Repository: cisagov/manage.get.gov

Issue:
Page jumping logic in domain request form
**User Story**

As a domain registrant, I want to be able to return to a previous step of the domain registration and then return to the latest step in the form so that i don't spend time clicking on screens uneccesarily

**Steps to Reproduce**

- Fill out the registration form up to the contact information step
- On the sidebar, click on the organization type
- Choose a new organization type

Expected result: user can click back to the contact information on the sidebar

Actual result: user has to click through all previous screens




Discussion:

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
@@ -12,6 +12,7 @@
 from django.urls import resolve
 
 from formtools.wizard.views import NamedUrlSessionWizardView  # type: ignore
+from formtools.wizard.storage.session import SessionStorage  # type: ignore
 
 from registrar.models import Contact, DomainApplication, Domain
 
@@ -382,6 +383,19 @@ class Step:
 }
 
 
+class TrackingStorage(SessionStorage):
+
+    """Storage subclass that keeps track of what the current_step has been."""
+
+    def _set_current_step(self, step):
+        super()._set_current_step(step)
+
+        step_history = self.extra_data.setdefault("step_history", [])
+        # can't serialize a set, so keep list entries unique
+        if step not in step_history:
+            step_history.append(step)
+
+
 class ApplicationWizard(LoginRequiredMixin, NamedUrlSessionWizardView):
 
     """Multi-page form ("wizard") for new domain applications.
@@ -397,6 +411,7 @@ class ApplicationWizard(LoginRequiredMixin, NamedUrlSessionWizardView):
     """
 
     form_list = FORMS
+    storage_name = "registrar.forms.application_wizard.TrackingStorage"
 
     def get_template_names(self):
         """Template for the current step.
@@ -418,6 +433,14 @@ def get_context_data(self, form, **kwargs):
         """Add title information to the context for all steps."""
         context = super().get_context_data(form=form, **kwargs)
         context["form_titles"] = TITLES
+
+        # Add information about which steps should be unlocked
+        # TODO: sometimes the first step doesn't get added to the step history
+        # so add it here
+        context["visited"] = self.storage.extra_data.get("step_history", []) + [
+            self.steps.first
+        ]
+
         if self.steps.current == Step.ORGANIZATION_CONTACT:
             context["is_federal"] = self._is_federal()
         if self.steps.current == Step.REVIEW:


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
@@ -8,7 +8,7 @@
 from django_webtest import WebTest  # type: ignore
 
 from registrar.models import DomainApplication, Domain, Contact, Website
-from registrar.forms.application_wizard import TITLES
+from registrar.forms.application_wizard import TITLES, Step
 
 from .common import less_console_noise
 
@@ -617,6 +617,34 @@ def test_application_form_conditional_elections(self):
         contact_page = election_result.follow()
         self.assertNotContains(contact_page, "Federal agency")
 
+    def test_application_form_section_skipping(self):
+        """Can skip forward and back in sections"""
+        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]
+
+        type_form = type_page.form
+        type_form["organization_type-organization_type"] = "federal"
+        self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
+        type_result = type_page.form.submit()
+
+        # follow first redirect
+        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
+        self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
+        new_page = federal_page.click(TITLES[Step.ORGANIZATION_TYPE], index=0)
+
+        # Should be a link to the organization_federal page
+        self.assertGreater(
+            len(new_page.html.find_all("a", href="/register/organization_federal/")),
+            0,
+        )
+
     @skip("WIP")
     def test_application_edit_restore(self):
         """