Merge pull request #2537 from cisagov/nl/2240-additional-details-completion-bug

Issue #2240 - Registrar step completion bug [backup]
This commit is contained in:
CuriousX 2024-08-16 16:05:22 -06:00 committed by GitHub
commit d2c4cf2bfa
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 76 additions and 19 deletions

View file

@ -679,7 +679,7 @@ class CisaRepresentativeYesNoForm(BaseYesNoForm):
field_name = "has_cisa_representative"
class AdditionalDetailsForm(BaseDeletableRegistrarForm):
class AnythingElseForm(BaseDeletableRegistrarForm):
anything_else = forms.CharField(
required=True,
label="Anything else?",
@ -698,7 +698,7 @@ class AdditionalDetailsForm(BaseDeletableRegistrarForm):
)
class AdditionalDetailsYesNoForm(BaseYesNoForm):
class AnythingElseYesNoForm(BaseYesNoForm):
"""Yes/no toggle for the anything else question on additional details"""
# Note that these can be set as functions/init if you need more fine-grained control.

View file

@ -296,23 +296,29 @@ class DomainInformation(TimeStampedModel):
"""Some yes/no forms use a db field to track whether it was checked or not.
We handle that here for def save().
"""
# Check if the firstname or lastname of cisa representative has any data.
# Then set the has_cisa_representative flag accordingly (so that it isn't
# "none", which indicates an incomplete form).
# This ensures that if we have prefilled data, the form is prepopulated
if self.cisa_representative_first_name is not None or self.cisa_representative_last_name is not None:
self.has_cisa_representative = (
self.cisa_representative_first_name != "" and self.cisa_representative_last_name != ""
)
# This check is required to ensure that the form doesn't start out checked
# Check for blank data and update has_cisa_representative accordingly (if it isn't None)
if self.has_cisa_representative is not None:
self.has_cisa_representative = (
self.cisa_representative_first_name != "" and self.cisa_representative_first_name is not None
) and (self.cisa_representative_last_name != "" and self.cisa_representative_last_name is not None)
# Check if anything_else has any data.
# Then set the has_anything_else_text flag accordingly (so that it isn't
# "none", which indicates an incomplete form).
# This ensures that if we have prefilled data, the form is prepopulated
if self.anything_else is not None:
self.has_anything_else_text = self.anything_else != ""
# This check is required to ensure that the form doesn't start out checked.
# Check for blank data and update has_anything_else_text accordingly (if it isn't None)
if self.has_anything_else_text is not None:
self.has_anything_else_text = self.anything_else != "" and self.anything_else is not None

View file

@ -645,23 +645,29 @@ class DomainRequest(TimeStampedModel):
"""Some yes/no forms use a db field to track whether it was checked or not.
We handle that here for def save().
"""
# Check if the firstname or lastname of cisa representative has any data.
# Then set the has_cisa_representative flag accordingly (so that it isn't
# "none", which indicates an incomplete form).
# This ensures that if we have prefilled data, the form is prepopulated
if self.cisa_representative_first_name is not None or self.cisa_representative_last_name is not None:
self.has_cisa_representative = (
self.cisa_representative_first_name != "" and self.cisa_representative_last_name != ""
)
# This check is required to ensure that the form doesn't start out checked
# Check for blank data and update has_cisa_representative accordingly (if it isn't None)
if self.has_cisa_representative is not None:
self.has_cisa_representative = (
self.cisa_representative_first_name != "" and self.cisa_representative_first_name is not None
) and (self.cisa_representative_last_name != "" and self.cisa_representative_last_name is not None)
# Check if anything_else has any data.
# Then set the has_anything_else_text flag accordingly (so that it isn't
# "none", which indicates an incomplete form).
# This ensures that if we have prefilled data, the form is prepopulated
if self.anything_else is not None:
self.has_anything_else_text = self.anything_else != ""
# This check is required to ensure that the form doesn't start out checked.
# Check for blank data and update has_anything_else_text accordingly (if it isn't None)
if self.has_anything_else_text is not None:
self.has_anything_else_text = self.anything_else != "" and self.anything_else is not None

View file

@ -15,7 +15,7 @@ from registrar.forms.domain_request_wizard import (
RequirementsForm,
TribalGovernmentForm,
PurposeForm,
AdditionalDetailsForm,
AnythingElseForm,
AboutYourOrganizationForm,
)
from registrar.forms.domain import ContactForm
@ -274,7 +274,7 @@ class TestFormValidation(MockEppLib):
def test_anything_else_form_about_your_organization_character_count_invalid(self):
"""Response must be less than 2000 characters."""
form = AdditionalDetailsForm(
form = AnythingElseForm(
data={
"anything_else": "Bacon ipsum dolor amet fatback strip steak pastrami"
"shankle, drumstick doner chicken landjaeger turkey andouille."

View file

@ -1017,20 +1017,27 @@ class DomainRequestTests(TestWithUser, WebTest):
type_page = intro_result.follow()
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
# fill out the organization type section then submit
type_form = type_page.forms[0]
type_form["generic_org_type-generic_org_type"] = "federal"
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
type_result = type_form.submit()
# follow first redirect
# follow first redirect to the next section
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
federal_page = type_result.follow()
# Now on federal type page, click back to the organization type
# we need to fill out the federal section so it stays unlocked
fed_branch_form = federal_page.forms[0]
fed_branch_form["organization_federal-federal_type"] = "executive"
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
fed_branch_form.submit()
# Now click back to the organization type
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
new_page = federal_page.click(str(self.TITLES["generic_org_type"]), index=0)
# Should be a link to the organization_federal page
# Should be a link to the organization_federal page since it is now unlocked
self.assertGreater(
len(new_page.html.find_all("a", href="/request/organization_federal/")),
0,
@ -2528,9 +2535,22 @@ class DomainRequestTests(TestWithUser, WebTest):
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
election_page = type_result.follow()
# Go back to SO page and test the dynamic text changed
# Navigate to the org page as that is the step right before senior_official
org_page = election_page.click(str(self.TITLES["organization_contact"]), index=0)
org_contact_form = org_page.forms[0]
org_contact_form["organization_contact-organization_name"] = "Testorg"
org_contact_form["organization_contact-address_line1"] = "address 1"
org_contact_form["organization_contact-address_line2"] = "address 2"
org_contact_form["organization_contact-city"] = "NYC"
org_contact_form["organization_contact-state_territory"] = "NY"
org_contact_form["organization_contact-zipcode"] = "10002"
org_contact_form["organization_contact-urbanization"] = "URB Royal Oaks"
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
so_page = election_page.click(str(self.TITLES["senior_official"]), index=0)
org_contact_result = org_contact_form.submit()
# Navigate back to the so page
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
so_page = org_contact_result.follow()
self.assertContains(so_page, "Domain requests from cities")
@less_console_noise_decorator
@ -2628,9 +2648,15 @@ class DomainRequestTests(TestWithUser, WebTest):
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
election_page = type_result.follow()
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
current_websites = election_page.click(str(self.TITLES["current_sites"]), index=0)
current_sites_form = current_websites.forms[0]
current_sites_form["current_sites-0-website"] = "www.city.com"
current_sites_result = current_sites_form.submit().follow()
# Go back to dotgov domain page to test the dynamic text changed
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
dotgov_page = election_page.click(str(self.TITLES["dotgov_domain"]), index=0)
dotgov_page = current_sites_result.click(str(self.TITLES["dotgov_domain"]), index=0)
self.assertContains(dotgov_page, "CityofEudoraKS.gov")
self.assertNotContains(dotgov_page, "medicare.gov")
@ -2984,6 +3010,9 @@ class TestWizardUnlockingSteps(TestWithUser, WebTest):
"""Test when all fields in the domain request are filled."""
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.STARTED, user=self.user)
domain_request.anything_else = False
domain_request.has_anything_else_text = False
domain_request.save()
response = self.app.get(f"/domain-request/{domain_request.id}/edit/")
# django-webtest does not handle cookie-based sessions well because it keeps

View file

@ -217,7 +217,6 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
if current_url == self.EDIT_URL_NAME and "id" in kwargs:
del self.storage
self.storage["domain_request_id"] = kwargs["id"]
self.storage["step_history"] = self.db_check_for_unlocking_steps()
# if accessing this class directly, redirect to either to an acknowledgement
# page or to the first step in the processes (if an edit rather than a new request);
@ -233,6 +232,9 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
else:
return self.goto(self.steps.first)
# refresh step_history to ensure we don't erroneously unlock unfinished
# steps just because we visited it
self.storage["step_history"] = self.db_check_for_unlocking_steps()
context = self.get_context_data()
self.steps.current = current_url
context["forms"] = self.get_forms()
@ -341,6 +343,17 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
"""Helper for get_context_data
Queries the DB for a domain request and returns a list of unlocked steps."""
# The way this works is as follows:
# Each step is assigned a true/false value to determine if it is
# "unlocked" or not. This dictionary of values is looped through
# at the end of this function and any step with a "true" value is
# added to a simple array that is returned at the end of this function.
# This array is eventually passed to the frontend context (eg. domain_request_sidebar.html),
# and is used to determine how steps appear in the side nav.
# It is worth noting that any step assigned "false" here will be EXCLUDED
# from the list of "unlocked" steps.
history_dict = {
"generic_org_type": self.domain_request.generic_org_type is not None,
"tribal_government": self.domain_request.tribe_name is not None,
@ -368,8 +381,11 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
or self.domain_request.no_other_contacts_rationale is not None
),
"additional_details": (
(self.domain_request.anything_else is not None and self.domain_request.has_cisa_representative)
or self.domain_request.is_policy_acknowledged is not None
# Additional details is complete as long as "has anything else" and "has cisa rep" are not None
(
self.domain_request.has_anything_else_text is not None
and self.domain_request.has_cisa_representative is not None
)
),
"requirements": self.domain_request.is_policy_acknowledged is not None,
"review": self.domain_request.is_policy_acknowledged is not None,
@ -626,8 +642,8 @@ class AdditionalDetails(DomainRequestWizard):
forms = [
forms.CisaRepresentativeYesNoForm,
forms.CisaRepresentativeForm,
forms.AdditionalDetailsYesNoForm,
forms.AdditionalDetailsForm,
forms.AnythingElseYesNoForm,
forms.AnythingElseForm,
]
def is_valid(self, forms: list) -> bool: