Repository: cisagov/manage.get.gov

Issue:
Implement frontend views from wireframes in the Django application
_Outcome_: Content and proposed views from #190 for the new registrant form are implemented in the Django application. 

This does not require functionality to manipulate Application state which requires #221 and #222 

Views to implement:
- [x] Type of organization  
- [x] Organization name and mailing address
- [x] Authorizing official
- [x] Website for your organization
- [x] .gov domain
- [x] Purpose of your domain
- [x] Contacts for your domain
- [x] Other contacts for your domain
- [x] Security email for public use
- [x] Anything else we should know?
- [x] Requirements for registration and operation of .gov domains
- [x] Review and submit your domain request


- [ ] Testing


Discussion:
11/3 - it make sense to break out into per-view ticket? Joining engineering huddle

Resolving patch:
diff --git a/src/registrar/forms/application_wizard.py b/src/registrar/forms/application_wizard.py
--- a/src/registrar/forms/application_wizard.py
+++ b/src/registrar/forms/application_wizard.py
@@ -15,7 +15,14 @@
 logger = logging.getLogger(__name__)
 
 
-class OrganizationForm(forms.Form):
+# Subclass used to remove the default colon suffix from all fields
+class RegistrarForm(forms.Form):
+    def __init__(self, *args, **kwargs):
+        kwargs.setdefault("label_suffix", "")
+        super(RegistrarForm, self).__init__(*args, **kwargs)
+
+
+class OrganizationTypeForm(RegistrarForm):
     organization_type = forms.ChoiceField(
         required=True,
         choices=[
@@ -47,11 +54,7 @@ class OrganizationForm(forms.Form):
     )
     federal_type = forms.ChoiceField(
         required=False,
-        choices=[
-            ("Executive", "Executive"),
-            ("Judicial", "Judicial"),
-            ("Legislative", "Legislative"),
-        ],
+        choices=DomainApplication.BRANCH_CHOICES,
         widget=forms.RadioSelect,
     )
     is_election_board = forms.ChoiceField(
@@ -60,34 +63,237 @@ class OrganizationForm(forms.Form):
             ("Yes", "Yes"),
             ("No", "No"),
         ],
+        widget=forms.RadioSelect(attrs={"class": "usa-radio__input"}),
+    )
+
+
+class OrganizationFederalForm(RegistrarForm):
+    federal_type = forms.ChoiceField(
+        required=False,
+        choices=DomainApplication.BRANCH_CHOICES,
         widget=forms.RadioSelect,
     )
 
 
-class ContactForm(forms.Form):
+class OrganizationElectionForm(RegistrarForm):
+    is_election_board = forms.BooleanField(
+        widget=forms.RadioSelect(
+            choices=[
+                (True, "Yes"),
+                (False, "No"),
+            ],
+        )
+    )
+
+
+class OrganizationContactForm(RegistrarForm):
     organization_name = forms.CharField(label="Organization Name")
-    street_address = forms.CharField(label="Street address")
+    address_line1 = forms.CharField(label="Address line 1")
+    address_line2 = forms.CharField(
+        required=False,
+        label="Address line 2",
+    )
+    us_state = forms.ChoiceField(
+        label="State",
+        choices=[
+            ("AL", "Alabama"),
+            ("AK", "Alaska"),
+            ("AZ", "Arizona"),
+            ("AR", "Arkansas"),
+            ("CA", "California"),
+            ("CO", "Colorado"),
+            ("CT", "Connecticut"),
+            ("DE", "Delaware"),
+            ("DC", "District of Columbia"),
+            ("FL", "Florida"),
+            ("GA", "Georgia"),
+            ("HI", "Hawaii"),
+            ("ID", "Idaho"),
+            ("IL", "Illinois"),
+            ("IN", "Indiana"),
+            ("IA", "Iowa"),
+            ("KS", "Kansas"),
+            ("KY", "Kentucky"),
+            ("LA", "Louisiana"),
+            ("ME", "Maine"),
+            ("MD", "Maryland"),
+            ("MA", "Massachusetts"),
+            ("MI", "Michigan"),
+            ("MN", "Minnesota"),
+            ("MS", "Mississippi"),
+            ("MO", "Missouri"),
+            ("MT", "Montana"),
+            ("NE", "Nebraska"),
+            ("NV", "Nevada"),
+            ("NH", "New Hampshire"),
+            ("NJ", "New Jersey"),
+            ("NM", "New Mexico"),
+            ("NY", "New York"),
+            ("NC", "North Carolina"),
+            ("ND", "North Dakota"),
+            ("OH", "Ohio"),
+            ("OK", "Oklahoma"),
+            ("OR", "Oregon"),
+            ("PA", "Pennsylvania"),
+            ("RI", "Rhode Island"),
+            ("SC", "South Carolina"),
+            ("SD", "South Dakota"),
+            ("TN", "Tennessee"),
+            ("TX", "Texas"),
+            ("UT", "Utah"),
+            ("VT", "Vermont"),
+            ("VA", "Virginia"),
+            ("WA", "Washington"),
+            ("WV", "West Virginia"),
+            ("WI", "Wisconsin"),
+            ("WY", "Wyoming"),
+            ("AS", "American Samoa"),
+            ("GU", "Guam"),
+            ("MP", "Northern Mariana Islands"),
+            ("PR", "Puerto Rico"),
+            ("VI", "Virgin Islands"),
+        ],
+    )
+    zipcode = forms.CharField(label="ZIP code")
+
+
+class AuthorizingOfficialForm(RegistrarForm):
+    first_name = forms.CharField(label="First name/given name")
+    middle_name = forms.CharField(
+        required=False,
+        label="Middle name (optional)",
+    )
+    last_name = forms.CharField(label="Last name/family name")
+    title = forms.CharField(label="Title or role in your organization")
+    email = forms.EmailField(label="Email")
+    phone = forms.CharField(label="Phone")
+
+
+class CurrentSitesForm(RegistrarForm):
+    current_site = forms.CharField(
+        required=False,
+        label="Enter your organization’s public website, if you have one. For example, "
+        "www.city.com.",
+    )
+
+
+class DotGovDomainForm(RegistrarForm):
+    dotgov_domain = forms.CharField(label="What .gov domain do you want?")
+    alternative_domain = forms.CharField(
+        required=False,
+        label="Are there other domains you’d like if we can’t give you your first "
+        "choice? Entering alternative domains is optional.",
+    )
+
+
+class PurposeForm(RegistrarForm):
+    purpose_field = forms.CharField(label="Purpose", widget=forms.Textarea())
+
+
+class YourContactForm(RegistrarForm):
+    first_name = forms.CharField(label="First name/given name")
+    middle_name = forms.CharField(
+        required=False,
+        label="Middle name (optional)",
+    )
+    last_name = forms.CharField(label="Last name/family name")
+    title = forms.CharField(label="Title or role in your organization")
+    email = forms.EmailField(label="Email")
+    phone = forms.CharField(label="Phone")
+
+
+class OtherContactsForm(RegistrarForm):
+    first_name = forms.CharField(label="First name/given name")
+    middle_name = forms.CharField(
+        required=False,
+        label="Middle name (optional)",
+    )
+    last_name = forms.CharField(label="Last name/family name")
+    title = forms.CharField(label="Title or role in your organization")
+    email = forms.EmailField(label="Email")
+    phone = forms.CharField(label="Phone")
+
+
+class SecurityEmailForm(RegistrarForm):
+    email = forms.EmailField(
+        required=False,
+        label="Security email",
+    )
+
+
+class AnythingElseForm(RegistrarForm):
+    anything_else = forms.CharField(
+        required=False, label="Anything else we should know", widget=forms.Textarea()
+    )
+
+
+class RequirementsForm(RegistrarForm):
+    agree_check = forms.BooleanField(
+        label="I read and agree to the .gov domain requirements."
+    )
+
+
+# Empty class for the review page which gets included as part of the form, but does not
+# have any form fields itself
+class ReviewForm(RegistrarForm):
+    pass
 
 
 # List of forms in our wizard. Each entry is a tuple of a name and a form
 # subclass
 FORMS = [
-    ("organization", OrganizationForm),
-    ("contact", ContactForm),
+    ("organization_type", OrganizationTypeForm),
+    ("organization_federal", OrganizationFederalForm),
+    ("organization_election", OrganizationElectionForm),
+    ("organization_contact", OrganizationContactForm),
+    ("authorizing_official", AuthorizingOfficialForm),
+    ("current_sites", CurrentSitesForm),
+    ("dotgov_domain", DotGovDomainForm),
+    ("purpose", PurposeForm),
+    ("your_contact", YourContactForm),
+    ("other_contacts", OtherContactsForm),
+    ("security_email", SecurityEmailForm),
+    ("anything_else", AnythingElseForm),
+    ("requirements", RequirementsForm),
+    ("review", ReviewForm),
 ]
 
 # Dict to match up the right template with the right step. Keys here must
 # match the first elements of the tuples in FORMS
 TEMPLATES = {
-    "organization": "application_organization.html",
-    "contact": "application_contact.html",
+    "organization_type": "application_org_type.html",
+    "organization_federal": "application_org_federal.html",
+    "organization_election": "application_org_election.html",
+    "organization_contact": "application_org_contact.html",
+    "authorizing_official": "application_authorizing_official.html",
+    "current_sites": "application_current_sites.html",
+    "dotgov_domain": "application_dotgov_domain.html",
+    "purpose": "application_purpose.html",
+    "your_contact": "application_your_contact.html",
+    "other_contacts": "application_other_contacts.html",
+    "security_email": "application_security_email.html",
+    "anything_else": "application_anything_else.html",
+    "requirements": "application_requirements.html",
+    "review": "application_review.html",
 }
 
 # We need to pass our page titles as context to the templates, indexed
 # by the step names
 TITLES = {
-    "organization": "About your organization",
-    "contact": "Your organization's contact information",
+    "organization_type": "Type of organization",
+    "organization_federal": "Type of organization — Federal",
+    "organization_election": "Type of organization — Election board",
+    "organization_contact": "Organization name and mailing address",
+    "authorizing_official": "Authorizing official",
+    "current_sites": "Organization website",
+    "dotgov_domain": ".gov domain",
+    "purpose": "Purpose of your domain",
+    "your_contact": "Your contact information",
+    "other_contacts": "Other contacts for your domain",
+    "security_email": "Security email for public use",
+    "anything_else": "Anything else we should know?",
+    "requirements": "Requirements for registration and operation of .gov domains",
+    "review": "Review and submit your domain request",
 }
 
 
@@ -120,16 +326,22 @@ def forms_to_object(self, form_dict: dict) -> DomainApplication:
         """Unpack the form responses onto the model object properties."""
         application = DomainApplication.objects.create(creator=self.request.user)
 
-        # organization information
-        organization_data = form_dict["organization"].cleaned_data
-        application.organization_type = organization_data["organization_type"]
-        application.federal_branch = organization_data["federal_type"]
-        application.is_election_office = organization_data["is_election_board"]
+        # organization type information
+        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"]
+
+        # election board  information
+        election_board_data = form_dict["organization_election"].cleaned_data
+        application.is_election_office = election_board_data["is_election_board"]
 
         # contact information
-        contact_data = form_dict["contact"].cleaned_data
+        contact_data = form_dict["organization_contact"].cleaned_data
         application.organization_name = contact_data["organization_name"]
-        application.street_address = contact_data["street_address"]
+        application.street_address = contact_data["address_line1"]
         # TODO: add the rest of these fields when they are created in the forms
 
         # This isn't really the requested_domain field


Test patch:
diff --git a/src/registrar/tests/test_views.py b/src/registrar/tests/test_views.py
--- a/src/registrar/tests/test_views.py
+++ b/src/registrar/tests/test_views.py
@@ -67,7 +67,9 @@ def test_edit_profile(self):
 
     def test_application_form_view(self):
         response = self.client.get("/register/", follow=True)
-        self.assertContains(response, "About your organization")
+        self.assertContains(
+            response, "What kind of government organization do you represent?"
+        )
 
 
 class FormTests(TestWithUser, WebTest):
@@ -92,63 +94,238 @@ def test_application_form_empty_submit(self):
         page = self.app.get(reverse("application")).follow()
         # submitting should get back the same page if the required field is empty
         result = page.form.submit()
-        self.assertIn("About your organization", result)
+        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-organization_type"] = "Federal"
+        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
         this test work.
         """
-        page = self.app.get(reverse("application")).follow()
+        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]
 
-        form = page.form
-        form["organization-organization_type"] = "Federal"
-        form["organization-federal_type"] = "Executive"
+        # ---- TYPE PAGE  ----
+        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)
-        result = page.form.submit()
+        type_result = type_page.form.submit()
 
         # the post request should return a redirect to the next form in
         # the application
-        self.assertEquals(result.status_code, 302)
-        self.assertEquals(result["Location"], "/register/contact/")
+        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
-        next_page = result.follow()
-        contact_form = next_page.form
-        contact_form["contact-organization_name"] = "test"
-        contact_form["contact-street_address"] = "100 Main Street"
+        federal_page = type_result.follow()
+        federal_form = federal_page.form
+        federal_form["organization_federal-federal_type"] = "Executive"
+
         # set the session ID before .submit()
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
-        result = contact_form.submit()
+        federal_result = federal_form.submit()
+
+        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/"
+        )
+
+        # ---- ORG CONTACT PAGE  ----
+        # Follow the redirect to the next form page
+        org_contact_page = election_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"
+        org_contact_form["organization_contact-us_state"] = "NY"
+        org_contact_form["organization_contact-zipcode"] = "10002"
+
+        self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
+        org_contact_result = org_contact_form.submit()
+
+        self.assertEquals(org_contact_result.status_code, 302)
+        self.assertEquals(
+            org_contact_result["Location"], "/register/authorizing_official/"
+        )
+        # ---- AUTHORIZING OFFICIAL PAGE  ----
+        # Follow the redirect to the next form page
+        ao_page = org_contact_result.follow()
+        ao_form = ao_page.form
+        ao_form["authorizing_official-first_name"] = "Testy"
+        ao_form["authorizing_official-last_name"] = "Tester"
+        ao_form["authorizing_official-title"] = "Chief Tester"
+        ao_form["authorizing_official-email"] = "testy@town.com"
+        ao_form["authorizing_official-phone"] = "(555) 555 5555"
+
+        self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
+        ao_result = ao_form.submit()
+
+        self.assertEquals(ao_result.status_code, 302)
+        self.assertEquals(ao_result["Location"], "/register/current_sites/")
+
+        # ---- CURRENT SITES PAGE  ----
+        # Follow the redirect to the next form page
+        current_sites_page = ao_result.follow()
+        current_sites_form = current_sites_page.form
+        current_sites_form["current_sites-current_site"] = "www.city.com"
+
+        self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
+        current_sites_result = current_sites_form.submit()
+
+        self.assertEquals(current_sites_result.status_code, 302)
+        self.assertEquals(current_sites_result["Location"], "/register/dotgov_domain/")
+
+        # ---- DOTGOV DOMAIN PAGE  ----
+        # Follow the redirect to the next form page
+        dotgov_page = current_sites_result.follow()
+        dotgov_form = dotgov_page.form
+        dotgov_form["dotgov_domain-dotgov_domain"] = "city"
+
+        self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
+        dotgov_result = dotgov_form.submit()
+
+        self.assertEquals(dotgov_result.status_code, 302)
+        self.assertEquals(dotgov_result["Location"], "/register/purpose/")
+
+        # ---- PURPOSE DOMAIN PAGE  ----
+        # Follow the redirect to the next form page
+        purpose_page = dotgov_result.follow()
+        purpose_form = purpose_page.form
+        purpose_form["purpose-purpose_field"] = "Purpose of the site"
+
+        self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
+        purpose_result = purpose_form.submit()
+
+        self.assertEquals(purpose_result.status_code, 302)
+        self.assertEquals(purpose_result["Location"], "/register/your_contact/")
+
+        # ---- YOUR CONTACT INFO PAGE  ----
+        # Follow the redirect to the next form page
+        your_contact_page = purpose_result.follow()
+        your_contact_form = your_contact_page.form
+
+        your_contact_form["your_contact-first_name"] = "Testy you"
+        your_contact_form["your_contact-last_name"] = "Tester you"
+        your_contact_form["your_contact-title"] = "Admin Tester"
+        your_contact_form["your_contact-email"] = "testy-admin@town.com"
+        your_contact_form["your_contact-phone"] = "(555) 555 5556"
+
+        self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
+        your_contact_result = your_contact_form.submit()
+
+        self.assertEquals(your_contact_result.status_code, 302)
+        self.assertEquals(your_contact_result["Location"], "/register/other_contacts/")
+
+        # ---- OTHER CONTACTS PAGE  ----
+        # Follow the redirect to the next form page
+        other_contacts_page = your_contact_result.follow()
+        other_contacts_form = other_contacts_page.form
+
+        other_contacts_form["other_contacts-first_name"] = "Testy2"
+        other_contacts_form["other_contacts-last_name"] = "Tester2"
+        other_contacts_form["other_contacts-title"] = "Another Tester"
+        other_contacts_form["other_contacts-email"] = "testy2@town.com"
+        other_contacts_form["other_contacts-phone"] = "(555) 555 5557"
+
+        self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
+        other_contacts_result = other_contacts_form.submit()
+
+        self.assertEquals(other_contacts_result.status_code, 302)
+        self.assertEquals(
+            other_contacts_result["Location"], "/register/security_email/"
+        )
+
+        # ---- SECURITY EMAIL PAGE  ----
+        # Follow the redirect to the next form page
+        security_email_page = other_contacts_result.follow()
+        security_email_form = security_email_page.form
+
+        security_email_form["security_email-email"] = "security@city.com"
+
+        self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
+        security_email_result = security_email_form.submit()
+
+        self.assertEquals(security_email_result.status_code, 302)
+        self.assertEquals(security_email_result["Location"], "/register/anything_else/")
+
+        # ---- ANYTHING ELSE PAGE  ----
+        # Follow the redirect to the next form page
+        anything_else_page = security_email_result.follow()
+        anything_else_form = anything_else_page.form
+
+        anything_else_form["anything_else-anything_else"] = "No"
+
+        self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
+        anything_else_result = anything_else_form.submit()
+
+        self.assertEquals(anything_else_result.status_code, 302)
+        self.assertEquals(anything_else_result["Location"], "/register/requirements/")
+
+        # ---- REQUIREMENTS PAGE  ----
+        # Follow the redirect to the next form page
+        requirements_page = anything_else_result.follow()
+        requirements_form = requirements_page.form
+
+        requirements_form["requirements-agree_check"] = True
+
+        self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
+        requirements_result = requirements_form.submit()
+
+        self.assertEquals(requirements_result.status_code, 302)
+        self.assertEquals(requirements_result["Location"], "/register/review/")
+
+        # ---- REVIEW AND FINSIHED PAGES  ----
+        # Follow the redirect to the next form page
+        review_page = requirements_result.follow()
+        review_form = review_page.form
 
         # final submission results in a redirect to the "finished" URL
-        self.assertEquals(result.status_code, 302)
-        self.assertEquals(result["Location"], "/register/finished/")
+
+        self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
+        review_result = review_form.submit()
+
+        self.assertEquals(review_result.status_code, 302)
+        self.assertEquals(review_result["Location"], "/register/finished/")
 
         # the finished URL (for now) returns a redirect to /
         # following this redirect is a GET request, so include the cookie
         # here too.
+
         self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
-        next_result = result.follow()
-        self.assertEquals(next_result.status_code, 302)
-        self.assertEquals(next_result["Location"], "/")
+        root_page = review_result.follow()
+        self.assertEquals(root_page.status_code, 302)
+        self.assertEquals(root_page["Location"], "/")
+        self.assertContains(root_page.follow(), "Welcome to the .gov registrar")
 
-        self.assertContains(next_result.follow(), "Welcome to the .gov registrar")
         # TODO: when we have a page that lists applications, visit it and
         # make sure that the new one exists