diff --git a/src/registrar/assets/js/get-gov-admin.js b/src/registrar/assets/js/get-gov-admin.js index f38afd252..702364cba 100644 --- a/src/registrar/assets/js/get-gov-admin.js +++ b/src/registrar/assets/js/get-gov-admin.js @@ -394,3 +394,38 @@ function initializeWidgetOnList(list, parentId) { observer.observe(targetElement); } })(); + +/** An IIFE for toggling the overflow styles on django-admin__model-description (the show more / show less button) */ +(function () { + function handleShowMoreButton(toggleButton, descriptionDiv){ + // Check the length of the text content in the description div + if (descriptionDiv.textContent.length < 200) { + // Hide the toggle button if text content is less than 200 characters + // This is a little over 160 characters to give us some wiggle room if we + // change the font size marginally. + toggleButton.classList.add('display-none'); + } else { + toggleButton.addEventListener('click', function() { + toggleShowMoreButton(toggleButton, descriptionDiv, 'dja__model-description--no-overflow') + }); + } + } + + function toggleShowMoreButton(toggleButton, descriptionDiv, showMoreClassName){ + // Toggle the class on the description div + descriptionDiv.classList.toggle(showMoreClassName); + + // Change the button text based on the presence of the class + if (descriptionDiv.classList.contains(showMoreClassName)) { + toggleButton.textContent = 'Show less'; + } else { + toggleButton.textContent = 'Show more'; + } + } + + let toggleButton = document.getElementById('dja-show-more-model-description'); + let descriptionDiv = document.querySelector('.dja__model-description'); + if (toggleButton && descriptionDiv) { + handleShowMoreButton(toggleButton, descriptionDiv) + } +})(); diff --git a/src/registrar/assets/sass/_theme/_admin.scss b/src/registrar/assets/sass/_theme/_admin.scss index b4b590acb..b4b5c657d 100644 --- a/src/registrar/assets/sass/_theme/_admin.scss +++ b/src/registrar/assets/sass/_theme/_admin.scss @@ -676,3 +676,35 @@ form .aligned p.help, form .aligned div.help { background: var(--primary); color: var(--header-link-color); } + +div.dja__model-description{ + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + + p, li { + font-size: medium; + color: var(--secondary); + } + + li { + list-style-type: disc; + font-family: Source Sans Pro Web,Helvetica Neue,Helvetica,Roboto,Arial,sans-serif; + } + + a, a:link, a:visited { + font-size: medium; + color: #005288 !important; + } + + &.dja__model-description--no-overflow { + display: block; + overflow: auto; + } + +} + +.text-underline { + text-decoration: underline !important; +} diff --git a/src/registrar/templates/admin/change_list.html b/src/registrar/templates/admin/change_list.html index 05c2d4e64..43abf0861 100644 --- a/src/registrar/templates/admin/change_list.html +++ b/src/registrar/templates/admin/change_list.html @@ -2,6 +2,10 @@ {% block content_title %}

{{ title }}

+ + {# Adds a model description #} + {% include "admin/model_descriptions.html" %} +

{{ cl.result_count }} {% if cl.get_ordering_field_columns %} diff --git a/src/registrar/templates/admin/model_descriptions.html b/src/registrar/templates/admin/model_descriptions.html new file mode 100644 index 000000000..c075e03a5 --- /dev/null +++ b/src/registrar/templates/admin/model_descriptions.html @@ -0,0 +1,37 @@ + +
+ {% if opts.model_name == 'domainrequest' %} + {% include "django/admin/includes/descriptions/domain_request_description.html" %} + {% elif opts.model_name == 'domaininformation' %} + {% include "django/admin/includes/descriptions/domain_information_description.html" %} + {% elif opts.model_name == 'domaininvitation' %} + {% include "django/admin/includes/descriptions/domain_invitation_description.html" %} + {% elif opts.model_name == 'contact' %} + {% include "django/admin/includes/descriptions/contact_description.html" %} + {% elif opts.model_name == 'logentry' %} + {% include "django/admin/includes/descriptions/logentry_description.html" %} + {% elif opts.model_name == 'domain'%} + {% include "django/admin/includes/descriptions/domain_description.html" %} + {% elif opts.model_name == 'draftdomain' %} + {% include "django/admin/includes/descriptions/draft_domain_description.html" %} + {% elif opts.model_name == 'host' %} + {% include "django/admin/includes/descriptions/host_description.html" %} + {% elif opts.model_name == 'publiccontact' %} + {% include "django/admin/includes/descriptions/public_contact_description.html" %} + {% elif opts.model_name == 'transitiondomain' %} + {% include "django/admin/includes/descriptions/transition_domain_description.html" %} + {% elif opts.model_name == 'userdomainrole' %} + {% include "django/admin/includes/descriptions/user_domain_role_description.html" %} + {% elif opts.model_name == 'usergroup' %} + {% include "django/admin/includes/descriptions/user_group_description.html" %} + {% elif opts.model_name == 'user' %} + {% include "django/admin/includes/descriptions/user_description.html" %} + {% elif opts.model_name == 'verifiedbystaff' %} + {% include "django/admin/includes/descriptions/verified_by_staff_description.html" %} + {% elif opts.model_name == 'website' %} + {% include "django/admin/includes/descriptions/website_description.html" %} + {% else %} +

This table does not have a description yet.

+ {% endif %} +
+ diff --git a/src/registrar/templates/django/admin/includes/descriptions/contact_description.html b/src/registrar/templates/django/admin/includes/descriptions/contact_description.html new file mode 100644 index 000000000..11141dca8 --- /dev/null +++ b/src/registrar/templates/django/admin/includes/descriptions/contact_description.html @@ -0,0 +1,10 @@ +

+Contacts include anyone who has access to the registrar (known as “users”) and anyone listed in a domain request, +including other employees and authorizing officials. +Only contacts who have access to the registrar will have +a corresponding record within the Users table. +

+ +

+Updating someone’s contact information here will not affect that person’s Login.gov information. +

diff --git a/src/registrar/templates/django/admin/includes/descriptions/domain_description.html b/src/registrar/templates/django/admin/includes/descriptions/domain_description.html new file mode 100644 index 000000000..5b8a79a4a --- /dev/null +++ b/src/registrar/templates/django/admin/includes/descriptions/domain_description.html @@ -0,0 +1,25 @@ +

+This table contains all approved domains in the .gov registrar. +In other words, with the exception of domains in the “Unknown”, ”On hold”, and “Deleted” states, +this table matches the list of domains in the .gov zone file. +

+ +

+Individual domain pages allow you to view registry-related details and edit the associated domain information. +

+ +

+Other actions available on the domain page include the ability to: +

+ + +

+To view a domain from a domain manager’s perspective, click the "Manage domain" button. +That will allow you to make changes (e.g., update DNS settings, invite people to manage the domain) +directly inside the registrar. +

diff --git a/src/registrar/templates/django/admin/includes/descriptions/domain_information_description.html b/src/registrar/templates/django/admin/includes/descriptions/domain_information_description.html new file mode 100644 index 000000000..36d197cf8 --- /dev/null +++ b/src/registrar/templates/django/admin/includes/descriptions/domain_information_description.html @@ -0,0 +1,19 @@ +

+Domain information represents the basic metadata for an approved domain and the organization that manages it. +It does not include any information specific to the registry (DNS name servers, security email). +Registry-related information +can be managed within the Domains table. +

+ +

+Updating values here will immediately update the corresponding values that users see in the registrar. +

+ +

+Domain information is similar to Domain requests, +and the fields are nearly identical, +but edits made to one are not made to the other. +Domain information exists so we don’t modify details of an approved request after adjudication +(since a domain request should be maintained as-adjudicated for records retention purposes). +Entries are created here upon approval of a domain request. +

diff --git a/src/registrar/templates/django/admin/includes/descriptions/domain_invitation_description.html b/src/registrar/templates/django/admin/includes/descriptions/domain_invitation_description.html new file mode 100644 index 000000000..7765b9203 --- /dev/null +++ b/src/registrar/templates/django/admin/includes/descriptions/domain_invitation_description.html @@ -0,0 +1,16 @@ +

+Domain invitations contain all individuals who have been invited to manage a .gov domain. +Invitations are sent via email, and the recipient must log in to the registrar to officially +accept and become a domain manager. +

+ +

+An “invited” status indicates that the recipient has not logged in to the registrar since the invitation was sent. +A “received” status indicates that the recipient has logged in. +

+ +

+If an invitation is created in this table, an email will not be sent. +To have an email sent, go to the domain in Domains, +click the “Manage domain” button, and add a domain manager. +

diff --git a/src/registrar/templates/django/admin/includes/descriptions/domain_request_description.html b/src/registrar/templates/django/admin/includes/descriptions/domain_request_description.html new file mode 100644 index 000000000..5adc07454 --- /dev/null +++ b/src/registrar/templates/django/admin/includes/descriptions/domain_request_description.html @@ -0,0 +1,11 @@ +

+This table contains all domain requests that have been started within the registrar and the status of those requests. +Updating values here will immediately update the corresponding values that users see in the registrar. +

+ +

+Once a domain request has been adjudicated, the details of that request should not be modified. +To update attributes (like an organization’s name) after a domain’s approval, +go to Domains. +Similar fields display on each Domain page, but edits made there will not affect the corresponding domain request. +

diff --git a/src/registrar/templates/django/admin/includes/descriptions/draft_domain_description.html b/src/registrar/templates/django/admin/includes/descriptions/draft_domain_description.html new file mode 100644 index 000000000..9e0ac9914 --- /dev/null +++ b/src/registrar/templates/django/admin/includes/descriptions/draft_domain_description.html @@ -0,0 +1,10 @@ +

+This table represents all “requested domains” that have been saved within a domain request form. +If a registrant changes the requested domain in their form, +the original name they listed and the new name will appear as separate records. +

+ +

+This table does not include “alternative domains,” +which are housed in the Websites table. +

diff --git a/src/registrar/templates/django/admin/includes/descriptions/host_description.html b/src/registrar/templates/django/admin/includes/descriptions/host_description.html new file mode 100644 index 000000000..a39519898 --- /dev/null +++ b/src/registrar/templates/django/admin/includes/descriptions/host_description.html @@ -0,0 +1,11 @@ +

+Entries in the Hosts table indicate the relationship between an approved domain and a name server address +(and, if applicable, the IP address for a name server address). +

+ +

+In general, you should not modify these values here. They should be updated directly inside the registrar. +To update a domain’s name servers and/or IP addresses, +in the registrar, go to the domain in Domains, +then click the "Manage domain" button. +

diff --git a/src/registrar/templates/django/admin/includes/descriptions/logentry_description.html b/src/registrar/templates/django/admin/includes/descriptions/logentry_description.html new file mode 100644 index 000000000..0dc3fe94e --- /dev/null +++ b/src/registrar/templates/django/admin/includes/descriptions/logentry_description.html @@ -0,0 +1,7 @@ +

+Log entries represent instances where something was created, updated, or deleted within the registrar or /admin. +The table on this page is useful for searching actions across all records. +To understand what happened to an individual record (like a domain request), +it’s better to go to that specific page +(Domain requests) and click on the “History” button. +

diff --git a/src/registrar/templates/django/admin/includes/descriptions/public_contact_description.html b/src/registrar/templates/django/admin/includes/descriptions/public_contact_description.html new file mode 100644 index 000000000..809b62a33 --- /dev/null +++ b/src/registrar/templates/django/admin/includes/descriptions/public_contact_description.html @@ -0,0 +1,18 @@ +

+Public contacts represent the three registry contact types (administrative, technical, and security) +and their fields that are exposed in WHOIS data. +

+ +

+We don’t currently allow registrants to publish real contact information to WHOIS, +but we must publish something to WHOIS. For each of the contact types, we use default values that are +associated with the program instead of the real contact information, +which we then redact in whole at the registry/WHOIS. +We do allow registrants to set a security contact email address, +which is published to WHOIS when a user sets one. +

+ +

+The public contacts in this table are a reflection of the data in the registry and should not be updated. +This information is primarily used by developers for validation purposes. +

diff --git a/src/registrar/templates/django/admin/includes/descriptions/transition_domain_description.html b/src/registrar/templates/django/admin/includes/descriptions/transition_domain_description.html new file mode 100644 index 000000000..331c7b18b --- /dev/null +++ b/src/registrar/templates/django/admin/includes/descriptions/transition_domain_description.html @@ -0,0 +1,4 @@ +

+This table represents the domains that were transitioned from the old registry in November 2023. +This data has been preserved for historical reference and should not be updated. +

diff --git a/src/registrar/templates/django/admin/includes/descriptions/user_description.html b/src/registrar/templates/django/admin/includes/descriptions/user_description.html new file mode 100644 index 000000000..2f1777169 --- /dev/null +++ b/src/registrar/templates/django/admin/includes/descriptions/user_description.html @@ -0,0 +1,16 @@ +

+A user is anyone who has access to the registrar. This includes request creators, domain managers, and CISA administrators. +Within each user record, you can review that user’s permissions and user status. +

+ +

+If a user has a domain request in ineligible status, then their user status will be “restricted.” +Users who are in restricted status cannot create/edit domain requests or approved domains, +and their existing requests are locked. They will see a 403 error if they try to take action in the registrar. +

+ +

+Each user record displays the associated “contact” info for that user, +which is the same info found in the Contacts table. +Updating these details on the user record will also update the corresponding contact record for that user. +

diff --git a/src/registrar/templates/django/admin/includes/descriptions/user_domain_role_description.html b/src/registrar/templates/django/admin/includes/descriptions/user_domain_role_description.html new file mode 100644 index 000000000..7066fcb93 --- /dev/null +++ b/src/registrar/templates/django/admin/includes/descriptions/user_domain_role_description.html @@ -0,0 +1,10 @@ +

+This table represents the managers who are assigned to each domain in the registrar. +There are separate records for each domain/manager combination. +Managers can update information related to a domain, such as DNS data and security contact. +

+ +

+The creator of an approved domain request automatically becomes a manager for that domain. +Anyone who retrieves a domain invitation is also assigned the manager role. +

diff --git a/src/registrar/templates/django/admin/includes/descriptions/user_group_description.html b/src/registrar/templates/django/admin/includes/descriptions/user_group_description.html new file mode 100644 index 000000000..610c8b430 --- /dev/null +++ b/src/registrar/templates/django/admin/includes/descriptions/user_group_description.html @@ -0,0 +1,10 @@ +

+Groups are a way to bundle admin permissions so they can be easily assigned to multiple users. +Once a group is created, it can be assigned to people via the Users table. +

+ +

+To maintain a single source of truth across all environments (stable, staging, etc.), +the groups and permissions are set and updated through the codebase. +Do not add, edit or delete user groups here. +

diff --git a/src/registrar/templates/django/admin/includes/descriptions/verified_by_staff_description.html b/src/registrar/templates/django/admin/includes/descriptions/verified_by_staff_description.html new file mode 100644 index 000000000..f7e4a58c9 --- /dev/null +++ b/src/registrar/templates/django/admin/includes/descriptions/verified_by_staff_description.html @@ -0,0 +1,10 @@ +

+This table contains users who have been allowed to bypass identity proofing through Login.gov after approval by a CISA representative. +Bypassing identity proofing means they will not be asked to provide a form of ID, PII, and so on. +

+ +

+Additions to this table should be rare, and only after obtaining confirmation of their identity directly (or from a trusted person). +Once a verified-by-staff user has been added as a domain manager, they can be removed from this list, +(However, if they are removed as a domain manager for all domains and they attempt to sign in again, they will be identity proofed by Login.gov). +

diff --git a/src/registrar/templates/django/admin/includes/descriptions/website_description.html b/src/registrar/templates/django/admin/includes/descriptions/website_description.html new file mode 100644 index 000000000..f6f5bdd1c --- /dev/null +++ b/src/registrar/templates/django/admin/includes/descriptions/website_description.html @@ -0,0 +1,8 @@ +

+This table lists all the “current websites” and “alternative domains” that users have submitted in domain requests since January 2024. +

+ +

+This does not include any “requested domains” that have appeared within the Domain requests table. +Those names are managed in the Draft domains table. +

diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 59aea4a0c..27825b9ea 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -21,6 +21,12 @@ from registrar.admin import ( MyHostAdmin, UserDomainRoleAdmin, VerifiedByStaffAdmin, + WebsiteAdmin, + DraftDomainAdmin, + FederalAgencyAdmin, + PublicContactAdmin, + TransitionDomainAdmin, + UserGroupAdmin, ) from registrar.models import ( Domain, @@ -33,6 +39,9 @@ from registrar.models import ( PublicContact, Host, Website, + FederalAgency, + UserGroup, + TransitionDomain, ) from registrar.models.user_domain_role import UserDomainRole from registrar.models.verified_by_staff import VerifiedByStaff @@ -95,6 +104,23 @@ class TestDomainAdmin(MockEppLib, WebTest): ) super().setUp() + @less_console_noise_decorator + def test_has_model_description(self): + """Tests if this model has a model description on the table view""" + p = "adminpass" + self.client.login(username="superuser", password=p) + response = self.client.get( + "/admin/registrar/domain/", + follow=True, + ) + + # Make sure that the page is loaded correctly + self.assertEqual(response.status_code, 200) + + # Test for a description snippet + self.assertContains(response, "This table contains all approved domains in the .gov registrar.") + self.assertContains(response, "Show more") + @less_console_noise_decorator def test_contact_fields_on_domain_change_form_have_detail_table(self): """Tests if the contact fields in the inlined Domain information have the detail table @@ -511,7 +537,7 @@ class TestDomainAdmin(MockEppLib, WebTest): # There are 4 template references to Federal (4) plus four references in the table # for our actual domain_request - self.assertContains(response, "Federal", count=36) + self.assertContains(response, "Federal", count=42) # This may be a bit more robust self.assertContains(response, 'Federal', count=1) # Now let's make sure the long description does not exist @@ -852,6 +878,23 @@ class TestDomainRequestAdmin(MockEppLib): ) self.mock_client = MockSESClient() + @less_console_noise_decorator + def test_has_model_description(self): + """Tests if this model has a model description on the table view""" + p = "adminpass" + self.client.login(username="superuser", password=p) + response = self.client.get( + "/admin/registrar/domainrequest/", + follow=True, + ) + + # Make sure that the page is loaded correctly + self.assertEqual(response.status_code, 200) + + # Test for a description snippet + self.assertContains(response, "This table contains all domain requests") + self.assertContains(response, "Show more") + @less_console_noise_decorator def test_helper_text(self): """ @@ -1267,7 +1310,7 @@ class TestDomainRequestAdmin(MockEppLib): response = self.client.get("/admin/registrar/domainrequest/?generic_org_type__exact=federal") # There are 2 template references to Federal (4) and two in the results data # of the request - self.assertContains(response, "Federal", count=34) + self.assertContains(response, "Federal", count=40) # This may be a bit more robust self.assertContains(response, 'Federal', count=1) # Now let's make sure the long description does not exist @@ -2492,7 +2535,7 @@ class TestDomainRequestAdmin(MockEppLib): self.mock_client.EMAILS_SENT.clear() -class DomainInvitationAdminTest(TestCase): +class TestDomainInvitationAdmin(TestCase): """Tests for the DomainInvitation page""" def setUp(self): @@ -2508,6 +2551,25 @@ class DomainInvitationAdminTest(TestCase): User.objects.all().delete() Contact.objects.all().delete() + @less_console_noise_decorator + def test_has_model_description(self): + """Tests if this model has a model description on the table view""" + p = "adminpass" + self.client.login(username="superuser", password=p) + response = self.client.get( + "/admin/registrar/domaininvitation/", + follow=True, + ) + + # Make sure that the page is loaded correctly + self.assertEqual(response.status_code, 200) + + # Test for a description snippet + self.assertContains( + response, "Domain invitations contain all individuals who have been invited to manage a .gov domain." + ) + self.assertContains(response, "Show more") + def test_get_filters(self): """Ensures that our filters are displaying correctly""" with less_console_noise(): @@ -2522,7 +2584,7 @@ class DomainInvitationAdminTest(TestCase): ) # Assert that the filters are added - self.assertContains(response, "invited", count=2) + self.assertContains(response, "invited", count=4) self.assertContains(response, "Invited", count=2) self.assertContains(response, "retrieved", count=2) self.assertContains(response, "Retrieved", count=2) @@ -2557,6 +2619,23 @@ class TestHostAdmin(TestCase): Host.objects.all().delete() Domain.objects.all().delete() + @less_console_noise_decorator + def test_has_model_description(self): + """Tests if this model has a model description on the table view""" + p = "adminpass" + self.client.login(username="superuser", password=p) + response = self.client.get( + "/admin/registrar/host/", + follow=True, + ) + + # Make sure that the page is loaded correctly + self.assertEqual(response.status_code, 200) + + # Test for a description snippet + self.assertContains(response, "Entries in the Hosts table indicate the relationship between an approved domain") + self.assertContains(response, "Show more") + @less_console_noise_decorator def test_helper_text(self): """ @@ -2635,6 +2714,23 @@ class TestDomainInformationAdmin(TestCase): Contact.objects.all().delete() User.objects.all().delete() + @less_console_noise_decorator + def test_has_model_description(self): + """Tests if this model has a model description on the table view""" + p = "adminpass" + self.client.login(username="superuser", password=p) + response = self.client.get( + "/admin/registrar/domaininformation/", + follow=True, + ) + + # Make sure that the page is loaded correctly + self.assertEqual(response.status_code, 200) + + # Test for a description snippet + self.assertContains(response, "Domain information represents the basic metadata") + self.assertContains(response, "Show more") + @less_console_noise_decorator def test_helper_text(self): """ @@ -2878,7 +2974,7 @@ class TestDomainInformationAdmin(TestCase): self.test_helper.assert_table_sorted("-4", ("-submitter__first_name", "-submitter__last_name")) -class UserDomainRoleAdminTest(TestCase): +class TestUserDomainRoleAdmin(TestCase): def setUp(self): """Setup environment for a mock admin user""" self.site = AdminSite() @@ -2900,6 +2996,25 @@ class UserDomainRoleAdminTest(TestCase): Domain.objects.all().delete() UserDomainRole.objects.all().delete() + @less_console_noise_decorator + def test_has_model_description(self): + """Tests if this model has a model description on the table view""" + p = "adminpass" + self.client.login(username="superuser", password=p) + response = self.client.get( + "/admin/registrar/userdomainrole/", + follow=True, + ) + + # Make sure that the page is loaded correctly + self.assertEqual(response.status_code, 200) + + # Test for a description snippet + self.assertContains( + response, "This table represents the managers who are assigned to each domain in the registrar" + ) + self.assertContains(response, "Show more") + def test_domain_sortable(self): """Tests if the UserDomainrole sorts by domain correctly""" with less_console_noise(): @@ -3096,6 +3211,23 @@ class TestMyUserAdmin(TestCase): super().tearDown() User.objects.all().delete() + @less_console_noise_decorator + def test_has_model_description(self): + """Tests if this model has a model description on the table view""" + p = "adminpass" + self.client.login(username="superuser", password=p) + response = self.client.get( + "/admin/registrar/user/", + follow=True, + ) + + # Make sure that the page is loaded correctly + self.assertEqual(response.status_code, 200) + + # Test for a description snippet + self.assertContains(response, "A user is anyone who has access to the registrar.") + self.assertContains(response, "Show more") + @less_console_noise_decorator def test_helper_text(self): """ @@ -3530,7 +3662,7 @@ class DomainSessionVariableTest(TestCase): ) -class ContactAdminTest(TestCase): +class TestContactAdmin(TestCase): def setUp(self): self.site = AdminSite() self.factory = RequestFactory() @@ -3539,6 +3671,23 @@ class ContactAdminTest(TestCase): self.superuser = create_superuser() self.staffuser = create_user() + @less_console_noise_decorator + def test_has_model_description(self): + """Tests if this model has a model description on the table view""" + p = "adminpass" + self.client.login(username="superuser", password=p) + response = self.client.get( + "/admin/registrar/contact/", + follow=True, + ) + + # Make sure that the page is loaded correctly + self.assertEqual(response.status_code, 200) + + # Test for a description snippet + self.assertContains(response, "Contacts include anyone who has access to the registrar (known as “users”)") + self.assertContains(response, "Show more") + def test_readonly_when_restricted_staffuser(self): with less_console_noise(): request = self.factory.get("/") @@ -3657,6 +3806,25 @@ class TestVerifiedByStaffAdmin(TestCase): VerifiedByStaff.objects.all().delete() User.objects.all().delete() + @less_console_noise_decorator + def test_has_model_description(self): + """Tests if this model has a model description on the table view""" + p = "adminpass" + self.client.login(username="superuser", password=p) + response = self.client.get( + "/admin/registrar/verifiedbystaff/", + follow=True, + ) + + # Make sure that the page is loaded correctly + self.assertEqual(response.status_code, 200) + + # Test for a description snippet + self.assertContains( + response, "This table contains users who have been allowed to bypass " "identity proofing through Login.gov" + ) + self.assertContains(response, "Show more") + @less_console_noise_decorator def test_helper_text(self): """ @@ -3699,3 +3867,204 @@ class TestVerifiedByStaffAdmin(TestCase): # Check that the user field is set to the request.user self.assertEqual(vip_instance.requestor, self.superuser) + + +class TestWebsiteAdmin(TestCase): + def setUp(self): + super().setUp() + self.site = AdminSite() + self.superuser = create_superuser() + self.admin = WebsiteAdmin(model=Website, admin_site=self.site) + self.factory = RequestFactory() + self.client = Client(HTTP_HOST="localhost:8080") + self.test_helper = GenericTestHelper(admin=self.admin) + + def tearDown(self): + super().tearDown() + Website.objects.all().delete() + User.objects.all().delete() + + @less_console_noise_decorator + def test_has_model_description(self): + """Tests if this model has a model description on the table view""" + p = "adminpass" + self.client.login(username="superuser", password=p) + response = self.client.get( + "/admin/registrar/website/", + follow=True, + ) + + # Make sure that the page is loaded correctly + self.assertEqual(response.status_code, 200) + + # Test for a description snippet + self.assertContains(response, "This table lists all the “current websites” and “alternative domains”") + self.assertContains(response, "Show more") + + +class TestDraftDomain(TestCase): + def setUp(self): + super().setUp() + self.site = AdminSite() + self.superuser = create_superuser() + self.admin = DraftDomainAdmin(model=DraftDomain, admin_site=self.site) + self.factory = RequestFactory() + self.client = Client(HTTP_HOST="localhost:8080") + self.test_helper = GenericTestHelper(admin=self.admin) + + def tearDown(self): + super().tearDown() + DraftDomain.objects.all().delete() + User.objects.all().delete() + + @less_console_noise_decorator + def test_has_model_description(self): + """Tests if this model has a model description on the table view""" + p = "adminpass" + self.client.login(username="superuser", password=p) + response = self.client.get( + "/admin/registrar/draftdomain/", + follow=True, + ) + + # Make sure that the page is loaded correctly + self.assertEqual(response.status_code, 200) + + # Test for a description snippet + self.assertContains( + response, "This table represents all “requested domains” that have been saved within a domain" + ) + self.assertContains(response, "Show more") + + +class TestFederalAgency(TestCase): + def setUp(self): + super().setUp() + self.site = AdminSite() + self.superuser = create_superuser() + self.admin = FederalAgencyAdmin(model=FederalAgency, admin_site=self.site) + self.factory = RequestFactory() + self.client = Client(HTTP_HOST="localhost:8080") + self.test_helper = GenericTestHelper(admin=self.admin) + + def tearDown(self): + super().tearDown() + FederalAgency.objects.all().delete() + User.objects.all().delete() + + @less_console_noise_decorator + def test_has_model_description(self): + """Tests if this model has a model description on the table view""" + p = "adminpass" + self.client.login(username="superuser", password=p) + response = self.client.get( + "/admin/registrar/federalagency/", + follow=True, + ) + + # Make sure that the page is loaded correctly + self.assertEqual(response.status_code, 200) + + # Test for a description snippet + self.assertContains(response, "This table does not have a description yet.") + self.assertContains(response, "Show more") + + +class TestPublicContact(TestCase): + def setUp(self): + super().setUp() + self.site = AdminSite() + self.superuser = create_superuser() + self.admin = PublicContactAdmin(model=PublicContact, admin_site=self.site) + self.factory = RequestFactory() + self.client = Client(HTTP_HOST="localhost:8080") + self.test_helper = GenericTestHelper(admin=self.admin) + + def tearDown(self): + super().tearDown() + PublicContact.objects.all().delete() + User.objects.all().delete() + + @less_console_noise_decorator + def test_has_model_description(self): + """Tests if this model has a model description on the table view""" + p = "adminpass" + self.client.login(username="superuser", password=p) + response = self.client.get( + "/admin/registrar/publiccontact/", + follow=True, + ) + + # Make sure that the page is loaded correctly + self.assertEqual(response.status_code, 200) + + # Test for a description snippet + self.assertContains(response, "Public contacts represent the three registry contact types") + self.assertContains(response, "Show more") + + +class TestTransitionDomain(TestCase): + def setUp(self): + super().setUp() + self.site = AdminSite() + self.superuser = create_superuser() + self.admin = TransitionDomainAdmin(model=TransitionDomain, admin_site=self.site) + self.factory = RequestFactory() + self.client = Client(HTTP_HOST="localhost:8080") + self.test_helper = GenericTestHelper(admin=self.admin) + + def tearDown(self): + super().tearDown() + PublicContact.objects.all().delete() + User.objects.all().delete() + + @less_console_noise_decorator + def test_has_model_description(self): + """Tests if this model has a model description on the table view""" + p = "adminpass" + self.client.login(username="superuser", password=p) + response = self.client.get( + "/admin/registrar/transitiondomain/", + follow=True, + ) + + # Make sure that the page is loaded correctly + self.assertEqual(response.status_code, 200) + + # Test for a description snippet + self.assertContains(response, "This table represents the domains that were transitioned from the old registry") + self.assertContains(response, "Show more") + + +class TestUserGroup(TestCase): + def setUp(self): + super().setUp() + self.site = AdminSite() + self.superuser = create_superuser() + self.admin = UserGroupAdmin(model=UserGroup, admin_site=self.site) + self.factory = RequestFactory() + self.client = Client(HTTP_HOST="localhost:8080") + self.test_helper = GenericTestHelper(admin=self.admin) + + def tearDown(self): + super().tearDown() + User.objects.all().delete() + + @less_console_noise_decorator + def test_has_model_description(self): + """Tests if this model has a model description on the table view""" + p = "adminpass" + self.client.login(username="superuser", password=p) + response = self.client.get( + "/admin/registrar/usergroup/", + follow=True, + ) + + # Make sure that the page is loaded correctly + self.assertEqual(response.status_code, 200) + + # Test for a description snippet + self.assertContains( + response, "Groups are a way to bundle admin permissions so they can be easily assigned to multiple users." + ) + self.assertContains(response, "Show more")