From 3fe00d198030a058a634c4ba28afd89740c21440 Mon Sep 17 00:00:00 2001 From: Neil Martinsen-Burrell Date: Wed, 23 Nov 2022 13:25:11 -0600 Subject: [PATCH] Display federal and election questions conditionally --- src/registrar/config/urls.py | 6 +- src/registrar/forms/__init__.py | 4 +- src/registrar/forms/application_wizard.py | 69 +++++++++++----- src/registrar/tests/test_views.py | 96 +++++++++++++++++------ 4 files changed, 125 insertions(+), 50 deletions(-) diff --git a/src/registrar/config/urls.py b/src/registrar/config/urls.py index b69a4b679..d3a6d0a46 100644 --- a/src/registrar/config/urls.py +++ b/src/registrar/config/urls.py @@ -10,12 +10,14 @@ from django.urls import include, path from django.views.generic import RedirectView from registrar.views import health, index, profile, whoami -from registrar.forms import ApplicationWizard +from registrar.forms import ApplicationWizard, WIZARD_CONDITIONS from api.views import available APPLICATION_URL_NAME = "application_step" application_wizard = ApplicationWizard.as_view( - url_name=APPLICATION_URL_NAME, done_step_name="finished" + url_name=APPLICATION_URL_NAME, + done_step_name="finished", + condition_dict=WIZARD_CONDITIONS, ) urlpatterns = [ diff --git a/src/registrar/forms/__init__.py b/src/registrar/forms/__init__.py index e1dff5082..806a2309c 100644 --- a/src/registrar/forms/__init__.py +++ b/src/registrar/forms/__init__.py @@ -1,4 +1,4 @@ from .edit_profile import EditProfileForm -from .application_wizard import ApplicationWizard +from .application_wizard import ApplicationWizard, WIZARD_CONDITIONS -__all__ = ["EditProfileForm", "ApplicationWizard"] +__all__ = ["EditProfileForm", "ApplicationWizard", "WIZARD_CONDITIONS"] diff --git a/src/registrar/forms/application_wizard.py b/src/registrar/forms/application_wizard.py index c184770ca..311981042 100644 --- a/src/registrar/forms/application_wizard.py +++ b/src/registrar/forms/application_wizard.py @@ -1,5 +1,7 @@ """Forms Wizard for creating a new domain application.""" +from __future__ import annotations # allows forward references in annotations + import logging from django import forms @@ -52,19 +54,6 @@ class OrganizationTypeForm(RegistrarForm): ], widget=forms.RadioSelect, ) - federal_type = forms.ChoiceField( - required=False, - choices=DomainApplication.BRANCH_CHOICES, - widget=forms.RadioSelect, - ) - is_election_board = forms.ChoiceField( - required=False, - choices=[ - ("Yes", "Yes"), - ("No", "No"), - ], - widget=forms.RadioSelect(attrs={"class": "usa-radio__input"}), - ) class OrganizationFederalForm(RegistrarForm): @@ -82,7 +71,8 @@ class OrganizationElectionForm(RegistrarForm): (True, "Yes"), (False, "No"), ], - ) + ), + required=False ) @@ -297,6 +287,41 @@ TITLES = { } +def _get_organization_type(wizard: ApplicationWizard) -> Union[str, None]: + """Extract the answer to the organization type question from the wizard.""" + # using the step data from the storage is a workaround for this + # bug in django-formtools version 2.4 + # https://github.com/jazzband/django-formtools/issues/220 + type_data = wizard.storage.get_step_data("organization_type") + if type_data: + return type_data.get("organization_type-organization_type") + return None + + +def show_organization_federal(wizard: ApplicationWizard) -> Bool: + """Show this step if the answer to the first question was "federal".""" + return _get_organization_type(wizard) == "Federal" + + +def show_organization_election(wizard: ApplicationWizard) -> Bool: + """Show this step if the answer to the first question implies it. + + This shows for answers that aren't "Federal" or "Interstate". + """ + type_answer = _get_organization_type(wizard) + if type_answer and type_answer not in ("Federal", "Interstate"): + return True + return False + + +# We can use a dictionary with step names and callables that return booleans +# to show or hide particular steps based on the state of the process. +WIZARD_CONDITIONS = { + "organization_federal": show_organization_federal, + "organization_election": show_organization_election, +} + + class ApplicationWizard(LoginRequiredMixin, NamedUrlSessionWizardView): """Multi-page form ("wizard") for new domain applications. @@ -330,13 +355,17 @@ class ApplicationWizard(LoginRequiredMixin, NamedUrlSessionWizardView): organization_type_data = form_dict["organization_type"].cleaned_data application.organization_type = organization_type_data["organization_type"] - # federal branch information - federal_branch_data = form_dict["organization_federal"].cleaned_data - application.federal_branch = federal_branch_data["federal_type"] + # federal branch information may not exist + federal_branch_data = form_dict.get("organization_federal") + if federal_branch_data is not None: + federal_branch_data = federal_branch_data.cleaned_data + application.federal_branch = federal_branch_data["federal_type"] - # election board information - election_board_data = form_dict["organization_election"].cleaned_data - application.is_election_office = election_board_data["is_election_board"] + # election board information may not exist. + election_board_data = form_dict.get("organization_election") + if election_board_data is not None: + election_board_data = election_board_data.cleaned_data + application.is_election_office = election_board_data["is_election_board"] # contact information contact_data = form_dict["organization_contact"].cleaned_data diff --git a/src/registrar/tests/test_views.py b/src/registrar/tests/test_views.py index 13c2f4fa3..bd8439135 100644 --- a/src/registrar/tests/test_views.py +++ b/src/registrar/tests/test_views.py @@ -6,6 +6,7 @@ from django.contrib.auth import get_user_model from django_webtest import WebTest # type: ignore from registrar.models import DomainApplication +from registrar.forms.application_wizard import TITLES class TestViews(TestCase): @@ -96,15 +97,6 @@ class FormTests(TestWithUser, WebTest): result = page.form.submit() self.assertIn("What kind of government organization do you represent?", result) - def test_application_form_organization(self): - # 302 redirect to the first form - page = self.app.get(reverse("application")).follow() - form = page.form - form["organization_type-organization_type"] = "Federal" - result = page.form.submit().follow() - # Got the next form page - self.assertContains(result, "contact information") - def test_application_form_submission(self): """Can fill out the entire form and submit. As we add additional form pages, we need to include them here to make @@ -130,10 +122,10 @@ class FormTests(TestWithUser, WebTest): self.assertEquals(type_result.status_code, 302) self.assertEquals(type_result["Location"], "/register/organization_federal/") - # TODO: In the future this should be conditionally dispalyed based on org type # ---- FEDERAL BRANCH PAGE ---- # Follow the redirect to the next form page + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) federal_page = type_result.follow() federal_form = federal_page.form federal_form["organization_federal-federal_type"] = "Executive" @@ -144,26 +136,12 @@ class FormTests(TestWithUser, WebTest): self.assertEquals(federal_result.status_code, 302) self.assertEquals( - federal_result["Location"], "/register/organization_election/" - ) - - # ---- ELECTION BOARD BRANCH PAGE ---- - # Follow the redirect to the next form page - election_page = federal_result.follow() - election_form = election_page.form - election_form["organization_election-is_election_board"] = True - - self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) - election_result = election_form.submit() - - self.assertEquals(election_result.status_code, 302) - self.assertEquals( - election_result["Location"], "/register/organization_contact/" + federal_result["Location"], "/register/organization_contact/" ) # ---- ORG CONTACT PAGE ---- # Follow the redirect to the next form page - org_contact_page = election_result.follow() + org_contact_page = federal_result.follow() org_contact_form = org_contact_page.form org_contact_form["organization_contact-organization_name"] = "Testorg" org_contact_form["organization_contact-address_line1"] = "address 1" @@ -322,3 +300,69 @@ class FormTests(TestWithUser, WebTest): self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) final_result = review_result.follow() self.assertContains(final_result, "Thank you for your domain request") + + def test_application_form_conditional_federal(self): + """Federal branch question is shown for federal organizations.""" + type_page = self.app.get(reverse("application")).follow() + # django-webtest does not handle cookie-based sessions well because it keeps + # resetting the session key on each new request, thus destroying the concept + # of a "session". We are going to do it manually, saving the session ID here + # and then setting the cookie on each request. + session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] + + # ---- TYPE PAGE ---- + + # the conditional step titles shouldn't appear initially + self.assertNotContains(type_page, TITLES["organization_federal"]) + self.assertNotContains(type_page, TITLES["organization_election"]) + type_form = type_page.form + type_form["organization_type-organization_type"] = "Federal" + + # set the session ID before .submit() + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + type_result = type_form.submit() + + # the post request should return a redirect to the federal branch + # question + self.assertEquals(type_result.status_code, 302) + self.assertEquals(type_result["Location"], "/register/organization_federal/") + + # and the step label should appear in the sidebar of the resulting page + # but the step label for the elections page should not appear + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + federal_page = type_result.follow() + self.assertContains(federal_page, TITLES["organization_federal"]) + self.assertNotContains(federal_page, TITLES["organization_election"]) + + def test_application_form_conditional_elections(self): + """Election question is shown for other organizations.""" + type_page = self.app.get(reverse("application")).follow() + # django-webtest does not handle cookie-based sessions well because it keeps + # resetting the session key on each new request, thus destroying the concept + # of a "session". We are going to do it manually, saving the session ID here + # and then setting the cookie on each request. + session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] + + # ---- TYPE PAGE ---- + + # the conditional step titles shouldn't appear initially + self.assertNotContains(type_page, TITLES["organization_federal"]) + self.assertNotContains(type_page, TITLES["organization_election"]) + type_form = type_page.form + type_form["organization_type-organization_type"] = "County" + + # set the session ID before .submit() + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + type_result = type_form.submit() + + # the post request should return a redirect to the federal branch + # question + self.assertEquals(type_result.status_code, 302) + self.assertEquals(type_result["Location"], "/register/organization_election/") + + # and the step label should appear in the sidebar of the resulting page + # but the step label for the elections page should not appear + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + election_page = type_result.follow() + self.assertContains(election_page, TITLES["organization_election"]) + self.assertNotContains(election_page, TITLES["organization_federal"])