mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-08-04 00:42:16 +02:00
unit tests
This commit is contained in:
parent
211a4b52cf
commit
1130b5db8e
4 changed files with 200 additions and 100 deletions
|
@ -4,24 +4,27 @@
|
|||
<nav aria-label="Form steps,">
|
||||
<ul class="usa-sidenav">
|
||||
{% for this_step in steps.all %}
|
||||
{% if this_step in visited %}
|
||||
<li class="usa-sidenav__item sidenav__step--locked">
|
||||
<span>
|
||||
{% if not this_step == steps.current %}
|
||||
<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>
|
||||
<use xlink:href="{%static 'img/sprite.svg'%}#check_circle"></use>
|
||||
</svg>
|
||||
{% endif %}
|
||||
{% if this_step == steps.current %}
|
||||
<a href="{% namespaced_url 'application' this_step %}"
|
||||
{% if this_step == steps.current %}
|
||||
class="usa-current"
|
||||
{% else %}
|
||||
class="link_usa-checked"
|
||||
{% endif%}>
|
||||
>
|
||||
{{ form_titles|get_item:this_step }}
|
||||
</a>
|
||||
</span>
|
||||
{% elif this_step in visited %}
|
||||
<span>
|
||||
{% if this_step != "review" %}
|
||||
<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>
|
||||
<use xlink:href="{%static 'img/sprite.svg'%}#check_circle"></use>
|
||||
</svg>
|
||||
{% endif %}
|
||||
<a href="{% namespaced_url 'application' this_step %}"
|
||||
class="link_usa-checked"
|
||||
>
|
||||
{{ form_titles|get_item:this_step }}
|
||||
</a>
|
||||
</span>
|
||||
{% else %}
|
||||
<li class="usa-sidenav__item sidenav__step--locked">
|
||||
<span>
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
from unittest import skip
|
||||
from unittest.mock import MagicMock, ANY, patch
|
||||
from unittest.mock import MagicMock, ANY, Mock, patch
|
||||
|
||||
from django.conf import settings
|
||||
from django.test import Client, TestCase
|
||||
from django.urls import reverse
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from django.contrib.sessions.models import Session
|
||||
|
||||
from .common import MockEppLib, MockSESClient, completed_application, create_user # type: ignore
|
||||
from django_webtest import WebTest # type: ignore
|
||||
import boto3_mocking # type: ignore
|
||||
|
@ -2373,6 +2375,150 @@ class DomainApplicationTests(TestWithUser, WebTest):
|
|||
self.assertContains(review_page, "You are about to submit an incomplete request")
|
||||
|
||||
|
||||
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 = {
|
||||
'about_your_organization': False,
|
||||
'anything_else': False,
|
||||
'authorizing_official': False,
|
||||
'current_sites': False,
|
||||
'dotgov_domain': False,
|
||||
'organization_contact': False,
|
||||
'organization_election': False,
|
||||
'organization_federal': False,
|
||||
'organization_type': False,
|
||||
'other_contacts': False,
|
||||
'purpose': False,
|
||||
'requirements': False,
|
||||
'review': False,
|
||||
'tribal_government': False,
|
||||
'your_contact': False
|
||||
}
|
||||
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:
|
||||
# Handle any potential errors while following the redirect
|
||||
self.fail("Error following the redirect")
|
||||
|
||||
# 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("Expected a redirect, but got a different 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:
|
||||
# Handle any potential errors while following the redirect
|
||||
self.fail("Error following the redirect")
|
||||
|
||||
# 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("Expected a redirect, but got a different response")
|
||||
|
||||
|
||||
class TestWithDomainPermissions(TestWithUser):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
|
|
@ -159,7 +159,11 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
|
|||
def storage(self):
|
||||
# marking session as modified on every access
|
||||
# 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, {})
|
||||
|
||||
@storage.setter
|
||||
|
@ -224,10 +228,8 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
|
|||
if request.path_info == self.NEW_URL_NAME:
|
||||
return render(request, "application_intro.html")
|
||||
else:
|
||||
logger.info('get calling self.steps.first')
|
||||
return self.goto(self.steps.first)
|
||||
|
||||
logger.info('get setting current step')
|
||||
self.steps.current = current_url
|
||||
context = self.get_context_data()
|
||||
context["forms"] = self.get_forms()
|
||||
|
@ -256,7 +258,6 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
|
|||
|
||||
All arguments (**kwargs) are passed directly to `get_forms`.
|
||||
"""
|
||||
logger.info('get_all_forms gettig steps in self.steps')
|
||||
nested = (self.get_forms(step=step, **kwargs) for step in self.steps)
|
||||
flattened = [form for lst in nested for form in lst]
|
||||
return flattened
|
||||
|
@ -273,7 +274,6 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
|
|||
An empty form will be provided if neither of those are true.
|
||||
"""
|
||||
|
||||
logger.info('get_forms setting prefix to self.steps.current')
|
||||
kwargs = {
|
||||
"files": files,
|
||||
"prefix": self.steps.current,
|
||||
|
@ -335,64 +335,37 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
|
|||
return DomainApplication.objects.filter(creator=self.request.user, status__in=check_statuses)
|
||||
|
||||
def db_check_for_unlocking_steps(self):
|
||||
unlocked_steps = {}
|
||||
|
||||
if self.application.organization_type:
|
||||
unlocked_steps["organization_type"] = True
|
||||
|
||||
if self.application.tribe_name:
|
||||
unlocked_steps["tribal_government"] = True
|
||||
|
||||
if self.application.federal_agency:
|
||||
unlocked_steps["organization_federal"] = True
|
||||
|
||||
if self.application.is_election_board:
|
||||
unlocked_steps["organization_election"] = True
|
||||
|
||||
if (
|
||||
self.application.organization_name
|
||||
or self.application.address_line1
|
||||
or self.application.city
|
||||
or self.application.state_territory
|
||||
or self.application.zipcode
|
||||
or self.application.urbanization
|
||||
):
|
||||
unlocked_steps["organization_contact"] = True
|
||||
|
||||
if self.application.about_your_organization:
|
||||
unlocked_steps["about_your_organization"] = True
|
||||
|
||||
if self.application.authorizing_official:
|
||||
unlocked_steps["authorizing_official"] = True
|
||||
|
||||
# Since this field is optional, test to see if the next step has been touched
|
||||
if self.application.current_websites.exists() or self.application.requested_domain:
|
||||
unlocked_steps["current_sites"] = True
|
||||
|
||||
if self.application.requested_domain:
|
||||
unlocked_steps["dotgov_domain"] = True
|
||||
|
||||
if self.application.purpose:
|
||||
unlocked_steps["purpose"] = True
|
||||
|
||||
if self.application.submitter:
|
||||
unlocked_steps["your_contact"] = True
|
||||
|
||||
if self.application.other_contacts.exists() or self.application.no_other_contacts_rationale:
|
||||
unlocked_steps["other_contacts"] = True
|
||||
|
||||
# Since this field is optional, test to see if the next step has been touched
|
||||
if self.application.anything_else or self.application.is_policy_acknowledged:
|
||||
unlocked_steps["anything_else"] = True
|
||||
|
||||
if self.application.is_policy_acknowledged:
|
||||
unlocked_steps["requirements"] = True
|
||||
|
||||
if self.application.submission_date:
|
||||
unlocked_steps["review"] = True
|
||||
|
||||
return unlocked_steps
|
||||
|
||||
"""Helper for get_context_data
|
||||
|
||||
Queries the DB for an application and returns a dict for unlocked steps."""
|
||||
return {
|
||||
"organization_type": bool(self.application.organization_type),
|
||||
"tribal_government": bool(self.application.tribe_name),
|
||||
"organization_federal": bool(self.application.federal_type),
|
||||
"organization_election": bool(self.application.is_election_board),
|
||||
"organization_contact": (
|
||||
bool(self.application.federal_agency) or bool(self.application.organization_name) or
|
||||
bool(self.application.address_line1) or bool(self.application.city) or
|
||||
bool(self.application.state_territory) or bool(self.application.zipcode) or
|
||||
bool(self.application.urbanization)
|
||||
),
|
||||
"about_your_organization": bool(self.application.about_your_organization),
|
||||
"authorizing_official": bool(self.application.authorizing_official),
|
||||
"current_sites": (
|
||||
bool(self.application.current_websites.exists()) or bool(self.application.requested_domain)
|
||||
),
|
||||
"dotgov_domain": bool(self.application.requested_domain),
|
||||
"purpose": bool(self.application.purpose),
|
||||
"your_contact": bool(self.application.submitter),
|
||||
"other_contacts": (
|
||||
bool(self.application.other_contacts.exists()) or bool(self.application.no_other_contacts_rationale)
|
||||
),
|
||||
"anything_else": (
|
||||
bool(self.application.anything_else) or bool(self.application.is_policy_acknowledged)
|
||||
),
|
||||
"requirements": bool(self.application.is_policy_acknowledged),
|
||||
"review": bool(self.application.is_policy_acknowledged),
|
||||
}
|
||||
|
||||
def get_context_data(self):
|
||||
"""Define context for access on all wizard pages."""
|
||||
|
@ -404,10 +377,7 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
|
|||
else:
|
||||
modal_heading = "You are about to submit an incomplete request"
|
||||
|
||||
logger.info(f'get_context_data returning value for cisited equals to: {self.storage.get("step_history", [])}')
|
||||
|
||||
unlocked_steps_list = list(self.db_check_for_unlocking_steps().keys())
|
||||
|
||||
unlocked_steps_list = [key for key, value in self.db_check_for_unlocking_steps().items() if value]
|
||||
|
||||
return {
|
||||
"form_titles": self.TITLES,
|
||||
|
@ -431,7 +401,6 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
|
|||
return step_list
|
||||
|
||||
def goto(self, step):
|
||||
logger.info(f'goto sets self.steps.current to passed {step}')
|
||||
self.steps.current = step
|
||||
return redirect(reverse(f"{self.URL_NAMESPACE}:{step}"))
|
||||
|
||||
|
@ -440,7 +409,6 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
|
|||
next = self.steps.next
|
||||
if next:
|
||||
self.steps.current = next
|
||||
logger.info(f'goto sets self.goto_next_step.current to passed {self.steps.next}')
|
||||
return self.goto(next)
|
||||
else:
|
||||
raise Http404()
|
||||
|
@ -460,7 +428,6 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
|
|||
if button == "intro_acknowledge":
|
||||
if request.path_info == self.NEW_URL_NAME:
|
||||
del self.storage
|
||||
logger.info(f'post calling goto with {self.steps.first}')
|
||||
return self.goto(self.steps.first)
|
||||
|
||||
# if accessing this class directly, redirect to the first step
|
||||
|
@ -480,7 +447,6 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
|
|||
# return them to the page they were already on
|
||||
if button == "save":
|
||||
messages.success(request, "Your progress has been saved!")
|
||||
logger.info(f'post calling goto with {self.steps.current}')
|
||||
return self.goto(self.steps.current)
|
||||
# if user opted to save progress and return,
|
||||
# return them to the home page
|
||||
|
|
|
@ -44,35 +44,28 @@ class StepsHelper:
|
|||
"""
|
||||
|
||||
def __init__(self, wizard):
|
||||
logger.info(f"steps_helper __init__")
|
||||
self._wizard = wizard
|
||||
|
||||
def __dir__(self):
|
||||
logger.info(f"steps_helper __dir__ {self.all}")
|
||||
return self.all
|
||||
|
||||
def __len__(self):
|
||||
logger.info(f"steps_helper __len__ {self.count}")
|
||||
return self.count
|
||||
|
||||
def __repr__(self):
|
||||
logger.info(f"steps_helper __repr__ {self._wizard} {self.all}")
|
||||
return "<StepsHelper for %s (steps: %s)>" % (self._wizard, self.all)
|
||||
|
||||
def __getitem__(self, step):
|
||||
logger.info(f"steps_helper __getitem__ {self.all[step]}")
|
||||
return self.all[step]
|
||||
|
||||
@property
|
||||
def all(self):
|
||||
"""Returns the names of all steps."""
|
||||
logger.info(f"steps_helper all {self._wizard.get_step_list()}")
|
||||
return self._wizard.get_step_list()
|
||||
|
||||
@property
|
||||
def count(self):
|
||||
"""Returns the total number of steps/forms in this the wizard."""
|
||||
logger.info(f"steps_helper count {len(self.all)}")
|
||||
return len(self.all)
|
||||
|
||||
@property
|
||||
|
@ -86,14 +79,12 @@ class StepsHelper:
|
|||
current_url = resolve(self._wizard.request.path_info).url_name
|
||||
step = current_url if current_url in self.all else self.first
|
||||
self._wizard.storage["current_step"] = step
|
||||
logger.info(f"steps_helper current getter {step}")
|
||||
return step
|
||||
|
||||
@current.setter
|
||||
def current(self, step: str):
|
||||
"""Sets the current step. Updates step history."""
|
||||
if step in self.all:
|
||||
logger.info(f"steps_helper current setter {step}")
|
||||
self._wizard.storage["current_step"] = step
|
||||
else:
|
||||
logger.debug("Invalid step name %s given to StepHelper" % str(step))
|
||||
|
@ -106,13 +97,11 @@ class StepsHelper:
|
|||
@property
|
||||
def first(self):
|
||||
"""Returns the name of the first step."""
|
||||
logger.info(f"steps_helper first {self.all[0]}")
|
||||
return self.all[0]
|
||||
|
||||
@property
|
||||
def last(self):
|
||||
"""Returns the name of the last step."""
|
||||
logger.info(f"steps_helper last {self.all[-1]}")
|
||||
return self.all[-1]
|
||||
|
||||
@property
|
||||
|
@ -121,7 +110,6 @@ class StepsHelper:
|
|||
steps = self.all
|
||||
index = steps.index(self.current) + 1
|
||||
if index < self.count:
|
||||
logger.info(f"steps_helper next {steps[index]}")
|
||||
return steps[index]
|
||||
return None
|
||||
|
||||
|
@ -131,7 +119,6 @@ class StepsHelper:
|
|||
steps = self.all
|
||||
key = steps.index(self.current) - 1
|
||||
if key >= 0:
|
||||
logger.info(f"steps_helper prev {steps[key]}")
|
||||
return steps[key]
|
||||
return None
|
||||
|
||||
|
@ -140,12 +127,10 @@ class StepsHelper:
|
|||
"""Returns the index for the current step."""
|
||||
steps = self.all
|
||||
if self.current in steps:
|
||||
logger.info(f"steps_helper index {steps.index(self)}")
|
||||
return steps.index(self)
|
||||
return None
|
||||
|
||||
@property
|
||||
def history(self):
|
||||
"""Returns the list of already visited steps."""
|
||||
logger.info(f"steps_helper history {self._wizard.storage.setdefault('step_history', [])}")
|
||||
return self._wizard.storage.setdefault("step_history", [])
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue