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:
rachidatecs 2024-02-06 18:09:59 -05:00 committed by GitHub
commit 72ee511238
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 197 additions and 5 deletions

View file

@ -8,11 +8,13 @@
<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 %}
{% if this_step != "review" %}
<svg class="usa-icon text-green" aria-hidden="true" focsuable="false" role="img" width="24" height="24"> <svg class="usa-icon text-green" aria-hidden="true" focsuable="false" role="img" width="24" height="24">
<title id="checked-step__{{forloop.counter}}">Checked mark</title> <title id="checked-step__{{forloop.counter}}">Checked mark</title>
<use xlink:href="{%static 'img/sprite.svg'%}#check_circle"></use> <use xlink:href="{%static 'img/sprite.svg'%}#check_circle"></use>
</svg> </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 %}
class="usa-current" class="usa-current"

View file

@ -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}")

View file

@ -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:

View file

@ -159,6 +159,10 @@ 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
# 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 self.request.session.modified = True
return self.request.session.setdefault(self.prefix, {}) return self.request.session.setdefault(self.prefix, {})
@ -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,