Repository: cisagov/manage.get.gov

Issue:
Make Domain Status more meaningful for user
### Issue description

During Analyst usability testing, some users did not understand the domain status, especially, what is the meaning of "On Hold." The thought (from the user) was that perhaps it was just waiting for something, and not that there was action required.

Implement solution per current design

user insight source: [Miro ](https://miro.com/app/board/uXjVNZhbw-s=/?openComment=3458764567841331692)(Heading: "Uncertainty about domain statuses")

### Acceptance criteria

- [ ] implement domain status per current design: see [Figma mockup](https://www.figma.com/file/aJbuDLJNNikqQObTuCNKQa/GetGov_Designs_ECS-Truss?type=design&node-id=2296-42994&mode=design) for style and [google doc](https://docs.google.com/document/d/152rI_svP_MB6LYrhmHv8-QyWbAbyJfnjXHuL6rlRlUk/edit?usp=sharing) for content

### Additional context

_No response_

### Links to other issues

🔄 Corresponding design ticket #1202


Discussion:
Need additional phrasing for the "expired" status. (Not a blocker, but needs to be added sooner or later) @Katherine-Osos 

Resolving patch:
diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py
--- a/src/registrar/models/domain.py
+++ b/src/registrar/models/domain.py
@@ -140,6 +140,24 @@ class State(models.TextChoices):
         # previously existed but has been deleted from the registry
         DELETED = "deleted", "Deleted"
 
+        @classmethod
+        def get_help_text(cls, state) -> str:
+            """Returns a help message for a desired state. If none is found, an empty string is returned"""
+            help_texts = {
+                # For now, unknown has the same message as DNS_NEEDED
+                cls.UNKNOWN: ("Before this domain can be used, " "you’ll need to add name server addresses."),
+                cls.DNS_NEEDED: ("Before this domain can be used, " "you’ll need to add name server addresses."),
+                cls.READY: "This domain has name servers and is ready for use.",
+                cls.ON_HOLD: (
+                    "This domain is administratively paused, "
+                    "so it can’t be edited and won’t resolve in DNS. "
+                    "Contact help@get.gov for details."
+                ),
+                cls.DELETED: ("This domain has been removed and " "is no longer registered to your organization."),
+            }
+
+            return help_texts.get(state, "")
+
     class Cache(property):
         """
         Python descriptor to turn class methods into properties.
@@ -1398,6 +1416,21 @@ def dns_needed(self):
         logger.info("Changing to DNS_NEEDED state")
         logger.info("able to transition to DNS_NEEDED state")
 
+    def get_state_help_text(self) -> str:
+        """Returns a str containing additional information about a given state.
+        Returns custom content for when the domain itself is expired."""
+
+        if self.is_expired() and self.state != self.State.UNKNOWN:
+            # Given expired is not a physical state, but it is displayed as such,
+            # We need custom logic to determine this message.
+            help_text = (
+                "This domain has expired, but it is still online. " "To renew this domain, contact help@get.gov."
+            )
+        else:
+            help_text = Domain.State.get_help_text(self.state)
+
+        return help_text
+
     def _disclose_fields(self, contact: PublicContact):
         """creates a disclose object that can be added to a contact Create using
         .disclose= <this function> on the command before sending.


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
@@ -1,5 +1,4 @@
 from django.test import Client, TestCase
-from django.urls import reverse
 from django.contrib.auth import get_user_model
 
 from .common import MockEppLib  # type: ignore
@@ -8,11 +7,7 @@
 from registrar.models import (
     DomainApplication,
     DomainInformation,
-    DraftDomain,
-    Contact,
-    User,
 )
-from .common import less_console_noise
 import logging
 
 logger = logging.getLogger(__name__)
@@ -55,252 +50,3 @@ def tearDown(self):
         DomainApplication.objects.all().delete()
         DomainInformation.objects.all().delete()
         self.user.delete()
-
-
-class LoggedInTests(TestWithUser):
-    def setUp(self):
-        super().setUp()
-        self.client.force_login(self.user)
-
-    def tearDown(self):
-        super().tearDown()
-        Contact.objects.all().delete()
-
-    def test_home_lists_domain_applications(self):
-        response = self.client.get("/")
-        self.assertNotContains(response, "igorville.gov")
-        site = DraftDomain.objects.create(name="igorville.gov")
-        application = DomainApplication.objects.create(creator=self.user, requested_domain=site)
-        response = self.client.get("/")
-
-        # count = 7 because of screenreader content
-        self.assertContains(response, "igorville.gov", count=7)
-
-        # clean up
-        application.delete()
-
-    def test_home_deletes_withdrawn_domain_application(self):
-        """Tests if the user can delete a DomainApplication in the 'withdrawn' status"""
-
-        site = DraftDomain.objects.create(name="igorville.gov")
-        application = DomainApplication.objects.create(
-            creator=self.user, requested_domain=site, status=DomainApplication.ApplicationStatus.WITHDRAWN
-        )
-
-        # Ensure that igorville.gov exists on the page
-        home_page = self.client.get("/")
-        self.assertContains(home_page, "igorville.gov")
-
-        # Check if the delete button exists. We can do this by checking for its id and text content.
-        self.assertContains(home_page, "Delete")
-        self.assertContains(home_page, "button-toggle-delete-domain-alert-1")
-
-        # Trigger the delete logic
-        response = self.client.post(reverse("application-delete", kwargs={"pk": application.pk}), follow=True)
-
-        self.assertNotContains(response, "igorville.gov")
-
-        # clean up
-        application.delete()
-
-    def test_home_deletes_started_domain_application(self):
-        """Tests if the user can delete a DomainApplication in the 'started' status"""
-
-        site = DraftDomain.objects.create(name="igorville.gov")
-        application = DomainApplication.objects.create(
-            creator=self.user, requested_domain=site, status=DomainApplication.ApplicationStatus.STARTED
-        )
-
-        # Ensure that igorville.gov exists on the page
-        home_page = self.client.get("/")
-        self.assertContains(home_page, "igorville.gov")
-
-        # Check if the delete button exists. We can do this by checking for its id and text content.
-        self.assertContains(home_page, "Delete")
-        self.assertContains(home_page, "button-toggle-delete-domain-alert-1")
-
-        # Trigger the delete logic
-        response = self.client.post(reverse("application-delete", kwargs={"pk": application.pk}), follow=True)
-
-        self.assertNotContains(response, "igorville.gov")
-
-        # clean up
-        application.delete()
-
-    def test_home_doesnt_delete_other_domain_applications(self):
-        """Tests to ensure the user can't delete Applications not in the status of STARTED or WITHDRAWN"""
-
-        # Given that we are including a subset of items that can be deleted while excluding the rest,
-        # subTest is appropriate here as otherwise we would need many duplicate tests for the same reason.
-        with less_console_noise():
-            draft_domain = DraftDomain.objects.create(name="igorville.gov")
-            for status in DomainApplication.ApplicationStatus:
-                if status not in [
-                    DomainApplication.ApplicationStatus.STARTED,
-                    DomainApplication.ApplicationStatus.WITHDRAWN,
-                ]:
-                    with self.subTest(status=status):
-                        application = DomainApplication.objects.create(
-                            creator=self.user, requested_domain=draft_domain, status=status
-                        )
-
-                        # Trigger the delete logic
-                        response = self.client.post(
-                            reverse("application-delete", kwargs={"pk": application.pk}), follow=True
-                        )
-
-                        # Check for a 403 error - the end user should not be allowed to do this
-                        self.assertEqual(response.status_code, 403)
-
-                        desired_application = DomainApplication.objects.filter(requested_domain=draft_domain)
-
-                        # Make sure the DomainApplication wasn't deleted
-                        self.assertEqual(desired_application.count(), 1)
-
-                        # clean up
-                        application.delete()
-
-    def test_home_deletes_domain_application_and_orphans(self):
-        """Tests if delete for DomainApplication deletes orphaned Contact objects"""
-
-        # Create the site and contacts to delete (orphaned)
-        contact = Contact.objects.create(
-            first_name="Henry",
-            last_name="Mcfakerson",
-        )
-        contact_shared = Contact.objects.create(
-            first_name="Relative",
-            last_name="Aether",
-        )
-
-        # 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])
-
-        # Create a second application to attach contacts to
-        site_2 = DraftDomain.objects.create(name="teaville.gov")
-        application_2 = DomainApplication.objects.create(
-            creator=self.user,
-            requested_domain=site_2,
-            status=DomainApplication.ApplicationStatus.STARTED,
-            authorizing_official=contact_2,
-            submitter=contact_shared,
-        )
-        application_2.other_contacts.set([contact_shared])
-
-        # Ensure that igorville.gov exists on the page
-        home_page = self.client.get("/")
-        self.assertContains(home_page, "igorville.gov")
-
-        # Trigger the delete logic
-        response = self.client.post(reverse("application-delete", kwargs={"pk": application.pk}), follow=True)
-
-        # igorville is now deleted
-        self.assertNotContains(response, "igorville.gov")
-
-        # Check if the orphaned contact was deleted
-        orphan = Contact.objects.filter(id=contact.id)
-        self.assertFalse(orphan.exists())
-
-        # All non-orphan contacts should still exist and are unaltered
-        try:
-            current_user = Contact.objects.filter(id=contact_user.id).get()
-        except Contact.DoesNotExist:
-            self.fail("contact_user (a non-orphaned contact) was deleted")
-
-        self.assertEqual(current_user, contact_user)
-        try:
-            edge_case = Contact.objects.filter(id=contact_2.id).get()
-        except Contact.DoesNotExist:
-            self.fail("contact_2 (a non-orphaned contact) was deleted")
-
-        self.assertEqual(edge_case, contact_2)
-
-    def test_home_deletes_domain_application_and_shared_orphans(self):
-        """Test the edge case for an object that will become orphaned after a delete
-        (but is not an orphan at the time of deletion)"""
-
-        # Create the site and contacts to delete (orphaned)
-        contact = Contact.objects.create(
-            first_name="Henry",
-            last_name="Mcfakerson",
-        )
-        contact_shared = Contact.objects.create(
-            first_name="Relative",
-            last_name="Aether",
-        )
-
-        # 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])
-
-        # Create a second application to attach contacts to
-        site_2 = DraftDomain.objects.create(name="teaville.gov")
-        application_2 = DomainApplication.objects.create(
-            creator=self.user,
-            requested_domain=site_2,
-            status=DomainApplication.ApplicationStatus.STARTED,
-            authorizing_official=contact_2,
-            submitter=contact_shared,
-        )
-        application_2.other_contacts.set([contact_shared])
-
-        home_page = self.client.get("/")
-        self.assertContains(home_page, "teaville.gov")
-
-        # Trigger the delete logic
-        response = self.client.post(reverse("application-delete", kwargs={"pk": application_2.pk}), follow=True)
-
-        self.assertNotContains(response, "teaville.gov")
-
-        # Check if the orphaned contact was deleted
-        orphan = Contact.objects.filter(id=contact_shared.id)
-        self.assertFalse(orphan.exists())
-
-    def test_application_form_view(self):
-        response = self.client.get("/request/", follow=True)
-        self.assertContains(
-            response,
-            "You’re about to start your .gov domain request.",
-        )
-
-    def test_domain_application_form_with_ineligible_user(self):
-        """Application form not accessible for an ineligible user.
-        This test should be solid enough since all application wizard
-        views share the same permissions class"""
-        self.user.status = User.RESTRICTED
-        self.user.save()
-
-        with less_console_noise():
-            response = self.client.get("/request/", follow=True)
-            self.assertEqual(response.status_code, 403)
diff --git a/src/registrar/tests/test_views_application.py b/src/registrar/tests/test_views_application.py
--- a/src/registrar/tests/test_views_application.py
+++ b/src/registrar/tests/test_views_application.py
@@ -3,6 +3,7 @@
 
 from django.conf import settings
 from django.urls import reverse
+from datetime import date
 
 from .common import MockSESClient, completed_application  # type: ignore
 from django_webtest import WebTest  # type: ignore
@@ -16,6 +17,7 @@
     Contact,
     User,
     Website,
+    UserDomainRole,
 )
 from registrar.views.application import ApplicationWizard, Step
 
@@ -2327,3 +2329,364 @@ def test_unlocked_steps_partial_application(self):
 
         else:
             self.fail(f"Expected a redirect, but got a different response: {response}")
+
+
+class HomeTests(TestWithUser):
+    """A series of tests that target the two tables on home.html"""
+
+    def setUp(self):
+        super().setUp()
+        self.client.force_login(self.user)
+
+    def tearDown(self):
+        super().tearDown()
+        Contact.objects.all().delete()
+
+    def test_home_lists_domain_applications(self):
+        response = self.client.get("/")
+        self.assertNotContains(response, "igorville.gov")
+        site = DraftDomain.objects.create(name="igorville.gov")
+        application = DomainApplication.objects.create(creator=self.user, requested_domain=site)
+        response = self.client.get("/")
+
+        # count = 7 because of screenreader content
+        self.assertContains(response, "igorville.gov", count=7)
+
+        # clean up
+        application.delete()
+
+    def test_state_help_text(self):
+        """Tests if each domain state has help text"""
+
+        # Get the expected text content of each state
+        deleted_text = "This domain has been removed and " "is no longer registered to your organization."
+        dns_needed_text = "Before this domain can be used, " "you’ll need to add name server addresses."
+        ready_text = "This domain has name servers and is ready for use."
+        on_hold_text = (
+            "This domain is administratively paused, "
+            "so it can’t be edited and won’t resolve in DNS. "
+            "Contact help@get.gov for details."
+        )
+        deleted_text = "This domain has been removed and " "is no longer registered to your organization."
+        # Generate a mapping of domain names, the state, and expected messages for the subtest
+        test_cases = [
+            ("deleted.gov", Domain.State.DELETED, deleted_text),
+            ("dnsneeded.gov", Domain.State.DNS_NEEDED, dns_needed_text),
+            ("unknown.gov", Domain.State.UNKNOWN, dns_needed_text),
+            ("onhold.gov", Domain.State.ON_HOLD, on_hold_text),
+            ("ready.gov", Domain.State.READY, ready_text),
+        ]
+        for domain_name, state, expected_message in test_cases:
+            with self.subTest(domain_name=domain_name, state=state, expected_message=expected_message):
+                # Create a domain and a UserRole with the given params
+                test_domain, _ = Domain.objects.get_or_create(name=domain_name, state=state)
+                test_domain.expiration_date = date.today()
+                test_domain.save()
+
+                user_role, _ = UserDomainRole.objects.get_or_create(
+                    user=self.user, domain=test_domain, role=UserDomainRole.Roles.MANAGER
+                )
+
+                # Grab the home page
+                response = self.client.get("/")
+
+                # Make sure the user can actually see the domain.
+                # We expect two instances because of SR content.
+                self.assertContains(response, domain_name, count=2)
+
+                # Check that we have the right text content.
+                self.assertContains(response, expected_message, count=1)
+
+                # Delete the role and domain to ensure we're testing in isolation
+                user_role.delete()
+                test_domain.delete()
+
+    def test_state_help_text_expired(self):
+        """Tests if each domain state has help text when expired"""
+        expired_text = "This domain has expired, but it is still online. " "To renew this domain, contact help@get.gov."
+        test_domain, _ = Domain.objects.get_or_create(name="expired.gov", state=Domain.State.READY)
+        test_domain.expiration_date = date(2011, 10, 10)
+        test_domain.save()
+
+        UserDomainRole.objects.get_or_create(user=self.user, domain=test_domain, role=UserDomainRole.Roles.MANAGER)
+
+        # Grab the home page
+        response = self.client.get("/")
+
+        # Make sure the user can actually see the domain.
+        # We expect two instances because of SR content.
+        self.assertContains(response, "expired.gov", count=2)
+
+        # Check that we have the right text content.
+        self.assertContains(response, expired_text, count=1)
+
+    def test_state_help_text_no_expiration_date(self):
+        """Tests if each domain state has help text when expiration date is None"""
+
+        # == Test a expiration of None for state ready. This should be expired. == #
+        expired_text = "This domain has expired, but it is still online. " "To renew this domain, contact help@get.gov."
+        test_domain, _ = Domain.objects.get_or_create(name="imexpired.gov", state=Domain.State.READY)
+        test_domain.expiration_date = None
+        test_domain.save()
+
+        UserDomainRole.objects.get_or_create(user=self.user, domain=test_domain, role=UserDomainRole.Roles.MANAGER)
+
+        # Grab the home page
+        response = self.client.get("/")
+
+        # Make sure the user can actually see the domain.
+        # We expect two instances because of SR content.
+        self.assertContains(response, "imexpired.gov", count=2)
+
+        # Make sure the expiration date is None
+        self.assertEqual(test_domain.expiration_date, None)
+
+        # Check that we have the right text content.
+        self.assertContains(response, expired_text, count=1)
+
+        # == Test a expiration of None for state unknown. This should not display expired text. == #
+        unknown_text = "Before this domain can be used, " "you’ll need to add name server addresses."
+        test_domain_2, _ = Domain.objects.get_or_create(name="notexpired.gov", state=Domain.State.UNKNOWN)
+        test_domain_2.expiration_date = None
+        test_domain_2.save()
+
+        UserDomainRole.objects.get_or_create(user=self.user, domain=test_domain_2, role=UserDomainRole.Roles.MANAGER)
+
+        # Grab the home page
+        response = self.client.get("/")
+
+        # Make sure the user can actually see the domain.
+        # We expect two instances because of SR content.
+        self.assertContains(response, "notexpired.gov", count=2)
+
+        # Make sure the expiration date is None
+        self.assertEqual(test_domain_2.expiration_date, None)
+
+        # Check that we have the right text content.
+        self.assertContains(response, unknown_text, count=1)
+
+    def test_home_deletes_withdrawn_domain_application(self):
+        """Tests if the user can delete a DomainApplication in the 'withdrawn' status"""
+
+        site = DraftDomain.objects.create(name="igorville.gov")
+        application = DomainApplication.objects.create(
+            creator=self.user, requested_domain=site, status=DomainApplication.ApplicationStatus.WITHDRAWN
+        )
+
+        # Ensure that igorville.gov exists on the page
+        home_page = self.client.get("/")
+        self.assertContains(home_page, "igorville.gov")
+
+        # Check if the delete button exists. We can do this by checking for its id and text content.
+        self.assertContains(home_page, "Delete")
+        self.assertContains(home_page, "button-toggle-delete-domain-alert-1")
+
+        # Trigger the delete logic
+        response = self.client.post(reverse("application-delete", kwargs={"pk": application.pk}), follow=True)
+
+        self.assertNotContains(response, "igorville.gov")
+
+        # clean up
+        application.delete()
+
+    def test_home_deletes_started_domain_application(self):
+        """Tests if the user can delete a DomainApplication in the 'started' status"""
+
+        site = DraftDomain.objects.create(name="igorville.gov")
+        application = DomainApplication.objects.create(
+            creator=self.user, requested_domain=site, status=DomainApplication.ApplicationStatus.STARTED
+        )
+
+        # Ensure that igorville.gov exists on the page
+        home_page = self.client.get("/")
+        self.assertContains(home_page, "igorville.gov")
+
+        # Check if the delete button exists. We can do this by checking for its id and text content.
+        self.assertContains(home_page, "Delete")
+        self.assertContains(home_page, "button-toggle-delete-domain-alert-1")
+
+        # Trigger the delete logic
+        response = self.client.post(reverse("application-delete", kwargs={"pk": application.pk}), follow=True)
+
+        self.assertNotContains(response, "igorville.gov")
+
+        # clean up
+        application.delete()
+
+    def test_home_doesnt_delete_other_domain_applications(self):
+        """Tests to ensure the user can't delete Applications not in the status of STARTED or WITHDRAWN"""
+
+        # Given that we are including a subset of items that can be deleted while excluding the rest,
+        # subTest is appropriate here as otherwise we would need many duplicate tests for the same reason.
+        with less_console_noise():
+            draft_domain = DraftDomain.objects.create(name="igorville.gov")
+            for status in DomainApplication.ApplicationStatus:
+                if status not in [
+                    DomainApplication.ApplicationStatus.STARTED,
+                    DomainApplication.ApplicationStatus.WITHDRAWN,
+                ]:
+                    with self.subTest(status=status):
+                        application = DomainApplication.objects.create(
+                            creator=self.user, requested_domain=draft_domain, status=status
+                        )
+
+                        # Trigger the delete logic
+                        response = self.client.post(
+                            reverse("application-delete", kwargs={"pk": application.pk}), follow=True
+                        )
+
+                        # Check for a 403 error - the end user should not be allowed to do this
+                        self.assertEqual(response.status_code, 403)
+
+                        desired_application = DomainApplication.objects.filter(requested_domain=draft_domain)
+
+                        # Make sure the DomainApplication wasn't deleted
+                        self.assertEqual(desired_application.count(), 1)
+
+                        # clean up
+                        application.delete()
+
+    def test_home_deletes_domain_application_and_orphans(self):
+        """Tests if delete for DomainApplication deletes orphaned Contact objects"""
+
+        # Create the site and contacts to delete (orphaned)
+        contact = Contact.objects.create(
+            first_name="Henry",
+            last_name="Mcfakerson",
+        )
+        contact_shared = Contact.objects.create(
+            first_name="Relative",
+            last_name="Aether",
+        )
+
+        # 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])
+
+        # Create a second application to attach contacts to
+        site_2 = DraftDomain.objects.create(name="teaville.gov")
+        application_2 = DomainApplication.objects.create(
+            creator=self.user,
+            requested_domain=site_2,
+            status=DomainApplication.ApplicationStatus.STARTED,
+            authorizing_official=contact_2,
+            submitter=contact_shared,
+        )
+        application_2.other_contacts.set([contact_shared])
+
+        # Ensure that igorville.gov exists on the page
+        home_page = self.client.get("/")
+        self.assertContains(home_page, "igorville.gov")
+
+        # Trigger the delete logic
+        response = self.client.post(reverse("application-delete", kwargs={"pk": application.pk}), follow=True)
+
+        # igorville is now deleted
+        self.assertNotContains(response, "igorville.gov")
+
+        # Check if the orphaned contact was deleted
+        orphan = Contact.objects.filter(id=contact.id)
+        self.assertFalse(orphan.exists())
+
+        # All non-orphan contacts should still exist and are unaltered
+        try:
+            current_user = Contact.objects.filter(id=contact_user.id).get()
+        except Contact.DoesNotExist:
+            self.fail("contact_user (a non-orphaned contact) was deleted")
+
+        self.assertEqual(current_user, contact_user)
+        try:
+            edge_case = Contact.objects.filter(id=contact_2.id).get()
+        except Contact.DoesNotExist:
+            self.fail("contact_2 (a non-orphaned contact) was deleted")
+
+        self.assertEqual(edge_case, contact_2)
+
+    def test_home_deletes_domain_application_and_shared_orphans(self):
+        """Test the edge case for an object that will become orphaned after a delete
+        (but is not an orphan at the time of deletion)"""
+
+        # Create the site and contacts to delete (orphaned)
+        contact = Contact.objects.create(
+            first_name="Henry",
+            last_name="Mcfakerson",
+        )
+        contact_shared = Contact.objects.create(
+            first_name="Relative",
+            last_name="Aether",
+        )
+
+        # 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])
+
+        # Create a second application to attach contacts to
+        site_2 = DraftDomain.objects.create(name="teaville.gov")
+        application_2 = DomainApplication.objects.create(
+            creator=self.user,
+            requested_domain=site_2,
+            status=DomainApplication.ApplicationStatus.STARTED,
+            authorizing_official=contact_2,
+            submitter=contact_shared,
+        )
+        application_2.other_contacts.set([contact_shared])
+
+        home_page = self.client.get("/")
+        self.assertContains(home_page, "teaville.gov")
+
+        # Trigger the delete logic
+        response = self.client.post(reverse("application-delete", kwargs={"pk": application_2.pk}), follow=True)
+
+        self.assertNotContains(response, "teaville.gov")
+
+        # Check if the orphaned contact was deleted
+        orphan = Contact.objects.filter(id=contact_shared.id)
+        self.assertFalse(orphan.exists())
+
+    def test_application_form_view(self):
+        response = self.client.get("/request/", follow=True)
+        self.assertContains(
+            response,
+            "You’re about to start your .gov domain request.",
+        )
+
+    def test_domain_application_form_with_ineligible_user(self):
+        """Application form not accessible for an ineligible user.
+        This test should be solid enough since all application wizard
+        views share the same permissions class"""
+        self.user.status = User.RESTRICTED
+        self.user.save()
+
+        with less_console_noise():
+            response = self.client.get("/request/", follow=True)
+            self.assertEqual(response.status_code, 403)