mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-22 04:19:26 +02:00
Merge pull request #1729 from cisagov/rjm/1624-1472-unlock-application-pages
Issues 1624 1472: Unlock application pages (RJM sandbox)
This commit is contained in:
commit
72ee511238
4 changed files with 197 additions and 5 deletions
|
@ -8,10 +8,12 @@
|
||||||
<li class="usa-sidenav__item sidenav__step--locked">
|
<li class="usa-sidenav__item sidenav__step--locked">
|
||||||
<span>
|
<span>
|
||||||
{% if not this_step == steps.current %}
|
{% if not this_step == steps.current %}
|
||||||
<svg class="usa-icon text-green" aria-hidden="true" focsuable="false" role="img" width="24" height="24">
|
{% if this_step != "review" %}
|
||||||
<title id="checked-step__{{forloop.counter}}">Checked mark</title>
|
<svg class="usa-icon text-green" aria-hidden="true" focsuable="false" role="img" width="24" height="24">
|
||||||
<use xlink:href="{%static 'img/sprite.svg'%}#check_circle"></use>
|
<title id="checked-step__{{forloop.counter}}">Checked mark</title>
|
||||||
</svg>
|
<use xlink:href="{%static 'img/sprite.svg'%}#check_circle"></use>
|
||||||
|
</svg>
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a href="{% namespaced_url 'application' this_step %}"
|
<a href="{% namespaced_url 'application' this_step %}"
|
||||||
{% if this_step == steps.current %}
|
{% if this_step == steps.current %}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from unittest import skip
|
from unittest import skip
|
||||||
|
from unittest.mock import Mock
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
@ -9,6 +10,7 @@ import boto3_mocking # type: ignore
|
||||||
|
|
||||||
from registrar.models import (
|
from registrar.models import (
|
||||||
DomainApplication,
|
DomainApplication,
|
||||||
|
DraftDomain,
|
||||||
Domain,
|
Domain,
|
||||||
DomainInformation,
|
DomainInformation,
|
||||||
Contact,
|
Contact,
|
||||||
|
@ -2197,3 +2199,131 @@ class DomainApplicationTestDifferentStatuses(TestWithUser, WebTest):
|
||||||
# domain object, so we do not expect to see 'city.gov'
|
# domain object, so we do not expect to see 'city.gov'
|
||||||
# in either the Domains or Requests tables.
|
# in either the Domains or Requests tables.
|
||||||
self.assertNotContains(home_page, "city.gov")
|
self.assertNotContains(home_page, "city.gov")
|
||||||
|
|
||||||
|
|
||||||
|
class TestWizardUnlockingSteps(TestWithUser, WebTest):
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
self.app.set_user(self.user.username)
|
||||||
|
self.wizard = ApplicationWizard()
|
||||||
|
# Mock the request object, its user, and session attributes appropriately
|
||||||
|
self.wizard.request = Mock(user=self.user, session={})
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super().tearDown()
|
||||||
|
|
||||||
|
def test_unlocked_steps_empty_application(self):
|
||||||
|
"""Test when all fields in the application are empty."""
|
||||||
|
unlocked_steps = self.wizard.db_check_for_unlocking_steps()
|
||||||
|
expected_dict = []
|
||||||
|
self.assertEqual(unlocked_steps, expected_dict)
|
||||||
|
|
||||||
|
def test_unlocked_steps_full_application(self):
|
||||||
|
"""Test when all fields in the application are filled."""
|
||||||
|
|
||||||
|
completed_application(status=DomainApplication.ApplicationStatus.STARTED, user=self.user)
|
||||||
|
# Make a request to the home page
|
||||||
|
home_page = self.app.get("/")
|
||||||
|
# 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]
|
||||||
|
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||||
|
|
||||||
|
# Assert that the response contains "city.gov"
|
||||||
|
self.assertContains(home_page, "city.gov")
|
||||||
|
|
||||||
|
# Click the "Edit" link
|
||||||
|
response = home_page.click("Edit", index=0)
|
||||||
|
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||||
|
|
||||||
|
# Check if the response is a redirect
|
||||||
|
if response.status_code == 302:
|
||||||
|
# Follow the redirect manually
|
||||||
|
try:
|
||||||
|
detail_page = response.follow()
|
||||||
|
|
||||||
|
self.wizard.get_context_data()
|
||||||
|
except Exception as err:
|
||||||
|
# Handle any potential errors while following the redirect
|
||||||
|
self.fail(f"Error following the redirect {err}")
|
||||||
|
|
||||||
|
# Now 'detail_page' contains the response after following the redirect
|
||||||
|
self.assertEqual(detail_page.status_code, 200)
|
||||||
|
|
||||||
|
# 10 unlocked steps, one active step, the review step will have link_usa but not check_circle
|
||||||
|
self.assertContains(detail_page, "#check_circle", count=10)
|
||||||
|
# Type of organization
|
||||||
|
self.assertContains(detail_page, "usa-current", count=1)
|
||||||
|
self.assertContains(detail_page, "link_usa-checked", count=11)
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.fail(f"Expected a redirect, but got a different response: {response}")
|
||||||
|
|
||||||
|
def test_unlocked_steps_partial_application(self):
|
||||||
|
"""Test when some fields in the application are filled."""
|
||||||
|
|
||||||
|
# Create the site and contacts to delete (orphaned)
|
||||||
|
contact = Contact.objects.create(
|
||||||
|
first_name="Henry",
|
||||||
|
last_name="Mcfakerson",
|
||||||
|
)
|
||||||
|
# Create two non-orphaned contacts
|
||||||
|
contact_2 = Contact.objects.create(
|
||||||
|
first_name="Saturn",
|
||||||
|
last_name="Mars",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Attach a user object to a contact (should not be deleted)
|
||||||
|
contact_user, _ = Contact.objects.get_or_create(user=self.user)
|
||||||
|
|
||||||
|
site = DraftDomain.objects.create(name="igorville.gov")
|
||||||
|
application = DomainApplication.objects.create(
|
||||||
|
creator=self.user,
|
||||||
|
requested_domain=site,
|
||||||
|
status=DomainApplication.ApplicationStatus.WITHDRAWN,
|
||||||
|
authorizing_official=contact,
|
||||||
|
submitter=contact_user,
|
||||||
|
)
|
||||||
|
application.other_contacts.set([contact_2])
|
||||||
|
|
||||||
|
# Make a request to the home page
|
||||||
|
home_page = self.app.get("/")
|
||||||
|
# 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]
|
||||||
|
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||||
|
|
||||||
|
# Assert that the response contains "city.gov"
|
||||||
|
self.assertContains(home_page, "igorville.gov")
|
||||||
|
|
||||||
|
# Click the "Edit" link
|
||||||
|
response = home_page.click("Edit", index=0)
|
||||||
|
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||||
|
|
||||||
|
# Check if the response is a redirect
|
||||||
|
if response.status_code == 302:
|
||||||
|
# Follow the redirect manually
|
||||||
|
try:
|
||||||
|
detail_page = response.follow()
|
||||||
|
|
||||||
|
self.wizard.get_context_data()
|
||||||
|
except Exception as err:
|
||||||
|
# Handle any potential errors while following the redirect
|
||||||
|
self.fail(f"Error following the redirect {err}")
|
||||||
|
|
||||||
|
# Now 'detail_page' contains the response after following the redirect
|
||||||
|
self.assertEqual(detail_page.status_code, 200)
|
||||||
|
|
||||||
|
# 5 unlocked steps (ao, domain, submitter, other contacts, and current sites
|
||||||
|
# which unlocks if domain exists), one active step, the review step is locked
|
||||||
|
self.assertContains(detail_page, "#check_circle", count=5)
|
||||||
|
# Type of organization
|
||||||
|
self.assertContains(detail_page, "usa-current", count=1)
|
||||||
|
self.assertContains(detail_page, "link_usa-checked", count=5)
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.fail(f"Expected a redirect, but got a different response: {response}")
|
||||||
|
|
|
@ -92,6 +92,12 @@ def parse_row(columns, domain_info: DomainInformation, security_emails_dict=None
|
||||||
"Deleted": domain.deleted,
|
"Deleted": domain.deleted,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# user_emails = [user.email for user in domain.permissions]
|
||||||
|
|
||||||
|
# Dynamically add user emails to the FIELDS dictionary
|
||||||
|
# for i, user_email in enumerate(user_emails, start=1):
|
||||||
|
# FIELDS[f"User{i} email"] = user_email
|
||||||
|
|
||||||
row = [FIELDS.get(column, "") for column in columns]
|
row = [FIELDS.get(column, "") for column in columns]
|
||||||
return row
|
return row
|
||||||
|
|
||||||
|
@ -127,6 +133,16 @@ def write_body(
|
||||||
else:
|
else:
|
||||||
logger.warning("csv_export -> Domain was none for PublicContact")
|
logger.warning("csv_export -> Domain was none for PublicContact")
|
||||||
|
|
||||||
|
# all_user_nums = 0
|
||||||
|
# for domain_info in all_domain_infos:
|
||||||
|
# user_num = len(domain_info.domain.permissions)
|
||||||
|
# all_user_nums.append(user_num)
|
||||||
|
|
||||||
|
# if user_num > highest_user_nums:
|
||||||
|
# highest_user_nums = user_num
|
||||||
|
|
||||||
|
# Build the header here passing to it highest_user_nums
|
||||||
|
|
||||||
# Reduce the memory overhead when performing the write operation
|
# Reduce the memory overhead when performing the write operation
|
||||||
paginator = Paginator(all_domain_infos, 1000)
|
paginator = Paginator(all_domain_infos, 1000)
|
||||||
for page_num in paginator.page_range:
|
for page_num in paginator.page_range:
|
||||||
|
|
|
@ -159,7 +159,11 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
|
||||||
def storage(self):
|
def storage(self):
|
||||||
# marking session as modified on every access
|
# marking session as modified on every access
|
||||||
# so that updates to nested keys are always saved
|
# so that updates to nested keys are always saved
|
||||||
self.request.session.modified = True
|
# Also - check that self.request.session has the attr
|
||||||
|
# modified to account for test environments calling
|
||||||
|
# view methods
|
||||||
|
if hasattr(self.request.session, "modified"):
|
||||||
|
self.request.session.modified = True
|
||||||
return self.request.session.setdefault(self.prefix, {})
|
return self.request.session.setdefault(self.prefix, {})
|
||||||
|
|
||||||
@storage.setter
|
@storage.setter
|
||||||
|
@ -211,6 +215,7 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
|
||||||
if current_url == self.EDIT_URL_NAME and "id" in kwargs:
|
if current_url == self.EDIT_URL_NAME and "id" in kwargs:
|
||||||
del self.storage
|
del self.storage
|
||||||
self.storage["application_id"] = kwargs["id"]
|
self.storage["application_id"] = kwargs["id"]
|
||||||
|
self.storage["step_history"] = self.db_check_for_unlocking_steps()
|
||||||
|
|
||||||
# if accessing this class directly, redirect to the first step
|
# if accessing this class directly, redirect to the first step
|
||||||
# in other words, if `ApplicationWizard` is called as view
|
# in other words, if `ApplicationWizard` is called as view
|
||||||
|
@ -269,6 +274,7 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
|
||||||
and from the database if `use_db` is True (provided that record exists).
|
and from the database if `use_db` is True (provided that record exists).
|
||||||
An empty form will be provided if neither of those are true.
|
An empty form will be provided if neither of those are true.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
kwargs = {
|
kwargs = {
|
||||||
"files": files,
|
"files": files,
|
||||||
"prefix": self.steps.current,
|
"prefix": self.steps.current,
|
||||||
|
@ -329,6 +335,43 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
|
||||||
]
|
]
|
||||||
return DomainApplication.objects.filter(creator=self.request.user, status__in=check_statuses)
|
return DomainApplication.objects.filter(creator=self.request.user, status__in=check_statuses)
|
||||||
|
|
||||||
|
def db_check_for_unlocking_steps(self):
|
||||||
|
"""Helper for get_context_data
|
||||||
|
|
||||||
|
Queries the DB for an application and returns a list of unlocked steps."""
|
||||||
|
history_dict = {
|
||||||
|
"organization_type": self.application.organization_type is not None,
|
||||||
|
"tribal_government": self.application.tribe_name is not None,
|
||||||
|
"organization_federal": self.application.federal_type is not None,
|
||||||
|
"organization_election": self.application.is_election_board is not None,
|
||||||
|
"organization_contact": (
|
||||||
|
self.application.federal_agency is not None
|
||||||
|
or self.application.organization_name is not None
|
||||||
|
or self.application.address_line1 is not None
|
||||||
|
or self.application.city is not None
|
||||||
|
or self.application.state_territory is not None
|
||||||
|
or self.application.zipcode is not None
|
||||||
|
or self.application.urbanization is not None
|
||||||
|
),
|
||||||
|
"about_your_organization": self.application.about_your_organization is not None,
|
||||||
|
"authorizing_official": self.application.authorizing_official is not None,
|
||||||
|
"current_sites": (
|
||||||
|
self.application.current_websites.exists() or self.application.requested_domain is not None
|
||||||
|
),
|
||||||
|
"dotgov_domain": self.application.requested_domain is not None,
|
||||||
|
"purpose": self.application.purpose is not None,
|
||||||
|
"your_contact": self.application.submitter is not None,
|
||||||
|
"other_contacts": (
|
||||||
|
self.application.other_contacts.exists() or self.application.no_other_contacts_rationale is not None
|
||||||
|
),
|
||||||
|
"anything_else": (
|
||||||
|
self.application.anything_else is not None or self.application.is_policy_acknowledged is not None
|
||||||
|
),
|
||||||
|
"requirements": self.application.is_policy_acknowledged is not None,
|
||||||
|
"review": self.application.is_policy_acknowledged is not None,
|
||||||
|
}
|
||||||
|
return [key for key, value in history_dict.items() if value]
|
||||||
|
|
||||||
def get_context_data(self):
|
def get_context_data(self):
|
||||||
"""Define context for access on all wizard pages."""
|
"""Define context for access on all wizard pages."""
|
||||||
# Build the submit button that we'll pass to the modal.
|
# Build the submit button that we'll pass to the modal.
|
||||||
|
@ -338,6 +381,7 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
|
||||||
modal_heading = "You are about to submit a domain request for " + str(self.application.requested_domain)
|
modal_heading = "You are about to submit a domain request for " + str(self.application.requested_domain)
|
||||||
else:
|
else:
|
||||||
modal_heading = "You are about to submit an incomplete request"
|
modal_heading = "You are about to submit an incomplete request"
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"form_titles": self.TITLES,
|
"form_titles": self.TITLES,
|
||||||
"steps": self.steps,
|
"steps": self.steps,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue