mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-15 17:17:02 +02:00
Merge branch 'main' into za/1456-sorting-not-working-correctly
This commit is contained in:
commit
c01b241ea3
2 changed files with 261 additions and 10 deletions
|
@ -12,6 +12,7 @@ from django.http.response import HttpResponseRedirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from epplibwrapper.errors import ErrorCode, RegistryError
|
from epplibwrapper.errors import ErrorCode, RegistryError
|
||||||
from registrar.models.domain import Domain
|
from registrar.models.domain import Domain
|
||||||
|
from registrar.models.user import User
|
||||||
from registrar.utility import csv_export
|
from registrar.utility import csv_export
|
||||||
from registrar.views.utility.mixins import OrderableFieldsMixin
|
from registrar.views.utility.mixins import OrderableFieldsMixin
|
||||||
from django.contrib.admin.views.main import ORDER_VAR
|
from django.contrib.admin.views.main import ORDER_VAR
|
||||||
|
@ -628,6 +629,9 @@ class DomainInformationAdmin(ListHeaderAdmin):
|
||||||
# to activate the edit/delete/view buttons
|
# to activate the edit/delete/view buttons
|
||||||
filter_horizontal = ("other_contacts",)
|
filter_horizontal = ("other_contacts",)
|
||||||
|
|
||||||
|
# Table ordering
|
||||||
|
ordering = ["domain__name"]
|
||||||
|
|
||||||
def get_readonly_fields(self, request, obj=None):
|
def get_readonly_fields(self, request, obj=None):
|
||||||
"""Set the read-only state on form elements.
|
"""Set the read-only state on form elements.
|
||||||
We have 1 conditions that determine which fields are read-only:
|
We have 1 conditions that determine which fields are read-only:
|
||||||
|
@ -679,6 +683,27 @@ class DomainApplicationAdmin(ListHeaderAdmin):
|
||||||
|
|
||||||
"""Custom domain applications admin class."""
|
"""Custom domain applications admin class."""
|
||||||
|
|
||||||
|
class InvestigatorFilter(admin.SimpleListFilter):
|
||||||
|
"""Custom investigator filter that only displays users with the manager role"""
|
||||||
|
|
||||||
|
title = "investigator"
|
||||||
|
# Match the old param name to avoid unnecessary refactoring
|
||||||
|
parameter_name = "investigator__id__exact"
|
||||||
|
|
||||||
|
def lookups(self, request, model_admin):
|
||||||
|
"""Lookup reimplementation, gets users of is_staff.
|
||||||
|
Returns a list of tuples consisting of (user.id, user)
|
||||||
|
"""
|
||||||
|
privileged_users = User.objects.filter(is_staff=True).order_by("first_name", "last_name", "email")
|
||||||
|
return [(user.id, user) for user in privileged_users]
|
||||||
|
|
||||||
|
def queryset(self, request, queryset):
|
||||||
|
"""Custom queryset implementation, filters by investigator"""
|
||||||
|
if self.value() is None:
|
||||||
|
return queryset
|
||||||
|
else:
|
||||||
|
return queryset.filter(investigator__id__exact=self.value())
|
||||||
|
|
||||||
# Columns
|
# Columns
|
||||||
list_display = [
|
list_display = [
|
||||||
"requested_domain",
|
"requested_domain",
|
||||||
|
@ -696,7 +721,7 @@ class DomainApplicationAdmin(ListHeaderAdmin):
|
||||||
]
|
]
|
||||||
|
|
||||||
# Filters
|
# Filters
|
||||||
list_filter = ("status", "organization_type", "investigator")
|
list_filter = ("status", "organization_type", InvestigatorFilter)
|
||||||
|
|
||||||
# Search
|
# Search
|
||||||
search_fields = [
|
search_fields = [
|
||||||
|
@ -772,6 +797,23 @@ class DomainApplicationAdmin(ListHeaderAdmin):
|
||||||
|
|
||||||
filter_horizontal = ("current_websites", "alternative_domains", "other_contacts")
|
filter_horizontal = ("current_websites", "alternative_domains", "other_contacts")
|
||||||
|
|
||||||
|
# Table ordering
|
||||||
|
ordering = ["requested_domain__name"]
|
||||||
|
|
||||||
|
# lists in filter_horizontal are not sorted properly, sort them
|
||||||
|
# by website
|
||||||
|
def formfield_for_manytomany(self, db_field, request, **kwargs):
|
||||||
|
if db_field.name in ("current_websites", "alternative_domains"):
|
||||||
|
kwargs["queryset"] = models.Website.objects.all().order_by("website") # Sort websites
|
||||||
|
return super().formfield_for_manytomany(db_field, request, **kwargs)
|
||||||
|
|
||||||
|
def formfield_for_foreignkey(self, db_field, request, **kwargs):
|
||||||
|
# Removes invalid investigator options from the investigator dropdown
|
||||||
|
if db_field.name == "investigator":
|
||||||
|
kwargs["queryset"] = User.objects.filter(is_staff=True)
|
||||||
|
return db_field.formfield(**kwargs)
|
||||||
|
return super().formfield_for_foreignkey(db_field, request, **kwargs)
|
||||||
|
|
||||||
# Trigger action when a fieldset is changed
|
# Trigger action when a fieldset is changed
|
||||||
def save_model(self, request, obj, form, change):
|
def save_model(self, request, obj, form, change):
|
||||||
if obj and obj.creator.status != models.User.RESTRICTED:
|
if obj and obj.creator.status != models.User.RESTRICTED:
|
||||||
|
@ -961,6 +1003,9 @@ class DomainAdmin(ListHeaderAdmin):
|
||||||
change_list_template = "django/admin/domain_change_list.html"
|
change_list_template = "django/admin/domain_change_list.html"
|
||||||
readonly_fields = ["state", "expiration_date"]
|
readonly_fields = ["state", "expiration_date"]
|
||||||
|
|
||||||
|
# Table ordering
|
||||||
|
ordering = ["name"]
|
||||||
|
|
||||||
def export_data_type(self, request):
|
def export_data_type(self, request):
|
||||||
# match the CSV example with all the fields
|
# match the CSV example with all the fields
|
||||||
response = HttpResponse(content_type="text/csv")
|
response = HttpResponse(content_type="text/csv")
|
||||||
|
|
|
@ -15,14 +15,7 @@ from registrar.admin import (
|
||||||
DomainInformationAdmin,
|
DomainInformationAdmin,
|
||||||
UserDomainRoleAdmin,
|
UserDomainRoleAdmin,
|
||||||
)
|
)
|
||||||
from registrar.models import (
|
from registrar.models import Domain, DomainApplication, DomainInformation, User, DomainInvitation, Contact, Website
|
||||||
Domain,
|
|
||||||
DomainApplication,
|
|
||||||
DomainInformation,
|
|
||||||
User,
|
|
||||||
DomainInvitation,
|
|
||||||
Contact,
|
|
||||||
)
|
|
||||||
from registrar.models.user_domain_role import UserDomainRole
|
from registrar.models.user_domain_role import UserDomainRole
|
||||||
from .common import (
|
from .common import (
|
||||||
AuditedAdminMockData,
|
AuditedAdminMockData,
|
||||||
|
@ -325,6 +318,7 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
self.admin = DomainApplicationAdmin(model=DomainApplication, admin_site=self.site)
|
self.admin = DomainApplicationAdmin(model=DomainApplication, admin_site=self.site)
|
||||||
self.superuser = create_superuser()
|
self.superuser = create_superuser()
|
||||||
self.staffuser = create_user()
|
self.staffuser = create_user()
|
||||||
|
self.client = Client(HTTP_HOST="localhost:8080")
|
||||||
self.test_helper = GenericTestHelper(
|
self.test_helper = GenericTestHelper(
|
||||||
factory=self.factory,
|
factory=self.factory,
|
||||||
user=self.superuser,
|
user=self.superuser,
|
||||||
|
@ -924,12 +918,224 @@ class TestDomainApplicationAdmin(MockEppLib):
|
||||||
with self.assertRaises(DomainInformation.DoesNotExist):
|
with self.assertRaises(DomainInformation.DoesNotExist):
|
||||||
domain_information.refresh_from_db()
|
domain_information.refresh_from_db()
|
||||||
|
|
||||||
|
def test_has_correct_filters(self):
|
||||||
|
"""
|
||||||
|
This test verifies that DomainApplicationAdmin has the correct filters set up.
|
||||||
|
|
||||||
|
It retrieves the current list of filters from DomainApplicationAdmin
|
||||||
|
and checks that it matches the expected list of filters.
|
||||||
|
"""
|
||||||
|
request = self.factory.get("/")
|
||||||
|
request.user = self.superuser
|
||||||
|
|
||||||
|
# Grab the current list of table filters
|
||||||
|
readonly_fields = self.admin.get_list_filter(request)
|
||||||
|
expected_fields = ("status", "organization_type", DomainApplicationAdmin.InvestigatorFilter)
|
||||||
|
|
||||||
|
self.assertEqual(readonly_fields, expected_fields)
|
||||||
|
|
||||||
|
def test_table_sorted_alphabetically(self):
|
||||||
|
"""
|
||||||
|
This test verifies that the DomainApplicationAdmin table is sorted alphabetically
|
||||||
|
by the 'requested_domain__name' field.
|
||||||
|
|
||||||
|
It creates a list of DomainApplication instances in a non-alphabetical order,
|
||||||
|
then retrieves the queryset from the DomainApplicationAdmin and checks
|
||||||
|
that it matches the expected queryset,
|
||||||
|
which is sorted alphabetically by the 'requested_domain__name' field.
|
||||||
|
"""
|
||||||
|
# Creates a list of DomainApplications in scrambled order
|
||||||
|
multiple_unalphabetical_domain_objects("application")
|
||||||
|
|
||||||
|
request = self.factory.get("/")
|
||||||
|
request.user = self.superuser
|
||||||
|
|
||||||
|
# Get the expected list of alphabetically sorted DomainApplications
|
||||||
|
expected_order = DomainApplication.objects.order_by("requested_domain__name")
|
||||||
|
|
||||||
|
# Get the returned queryset
|
||||||
|
queryset = self.admin.get_queryset(request)
|
||||||
|
|
||||||
|
# Check the order
|
||||||
|
self.assertEqual(
|
||||||
|
list(queryset),
|
||||||
|
list(expected_order),
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_displays_investigator_filter(self):
|
||||||
|
"""
|
||||||
|
This test verifies that the investigator filter in the admin interface for
|
||||||
|
the DomainApplication model displays correctly.
|
||||||
|
|
||||||
|
It creates two DomainApplication instances, each with a different investigator.
|
||||||
|
It then simulates a staff user logging in and applying the investigator filter
|
||||||
|
on the DomainApplication admin page.
|
||||||
|
|
||||||
|
We then test if the page displays the filter we expect, but we do not test
|
||||||
|
if we get back the correct response in the table. This is to isolate if
|
||||||
|
the filter displays correctly, when the filter isn't filtering correctly.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Create a mock DomainApplication object, with a fake investigator
|
||||||
|
application: DomainApplication = generic_domain_object("application", "SomeGuy")
|
||||||
|
investigator_user = User.objects.filter(username=application.investigator.username).get()
|
||||||
|
investigator_user.is_staff = True
|
||||||
|
investigator_user.save()
|
||||||
|
|
||||||
|
p = "userpass"
|
||||||
|
self.client.login(username="staffuser", password=p)
|
||||||
|
response = self.client.get(
|
||||||
|
"/admin/registrar/domainapplication/",
|
||||||
|
{
|
||||||
|
"investigator__id__exact": investigator_user.id,
|
||||||
|
},
|
||||||
|
follow=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Then, test if the filter actually exists
|
||||||
|
self.assertIn("filters", response.context)
|
||||||
|
|
||||||
|
# Assert the content of filters and search_query
|
||||||
|
filters = response.context["filters"]
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
filters,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"parameter_name": "investigator",
|
||||||
|
"parameter_value": "SomeGuy first_name:investigator SomeGuy last_name:investigator",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_investigator_filter_filters_correctly(self):
|
||||||
|
"""
|
||||||
|
This test verifies that the investigator filter in the admin interface for
|
||||||
|
the DomainApplication model works correctly.
|
||||||
|
|
||||||
|
It creates two DomainApplication instances, each with a different investigator.
|
||||||
|
It then simulates a staff user logging in and applying the investigator filter
|
||||||
|
on the DomainApplication admin page.
|
||||||
|
|
||||||
|
It then verifies that it was applied correctly.
|
||||||
|
The test checks that the response contains the expected DomainApplication pbjects
|
||||||
|
in the table.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Create a mock DomainApplication object, with a fake investigator
|
||||||
|
application: DomainApplication = generic_domain_object("application", "SomeGuy")
|
||||||
|
investigator_user = User.objects.filter(username=application.investigator.username).get()
|
||||||
|
investigator_user.is_staff = True
|
||||||
|
investigator_user.save()
|
||||||
|
|
||||||
|
# Create a second mock DomainApplication object, to test filtering
|
||||||
|
application: DomainApplication = generic_domain_object("application", "BadGuy")
|
||||||
|
another_user = User.objects.filter(username=application.investigator.username).get()
|
||||||
|
another_user.is_staff = True
|
||||||
|
another_user.save()
|
||||||
|
|
||||||
|
p = "userpass"
|
||||||
|
self.client.login(username="staffuser", password=p)
|
||||||
|
response = self.client.get(
|
||||||
|
"/admin/registrar/domainapplication/",
|
||||||
|
{
|
||||||
|
"investigator__id__exact": investigator_user.id,
|
||||||
|
},
|
||||||
|
follow=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
expected_name = "SomeGuy first_name:investigator SomeGuy last_name:investigator"
|
||||||
|
# We expect to see this four times, two of them are from the html for the filter,
|
||||||
|
# and the other two are the html from the list entry in the table.
|
||||||
|
self.assertContains(response, expected_name, count=4)
|
||||||
|
|
||||||
|
# Check that we don't also get the thing we aren't filtering for.
|
||||||
|
# We expect to see this two times in the filter
|
||||||
|
unexpected_name = "BadGuy first_name:investigator BadGuy last_name:investigator"
|
||||||
|
self.assertContains(response, unexpected_name, count=2)
|
||||||
|
|
||||||
|
def test_investigator_dropdown_displays_only_staff(self):
|
||||||
|
"""
|
||||||
|
This test verifies that the dropdown for the 'investigator' field in the DomainApplicationAdmin
|
||||||
|
interface only displays users who are marked as staff.
|
||||||
|
|
||||||
|
It creates two DomainApplication instances, one with an investigator
|
||||||
|
who is a staff user and another with an investigator who is not a staff user.
|
||||||
|
|
||||||
|
It then retrieves the queryset for the 'investigator' dropdown from DomainApplicationAdmin
|
||||||
|
and checks that it matches the expected queryset, which only includes staff users.
|
||||||
|
"""
|
||||||
|
# Create a mock DomainApplication object, with a fake investigator
|
||||||
|
application: DomainApplication = generic_domain_object("application", "SomeGuy")
|
||||||
|
investigator_user = User.objects.filter(username=application.investigator.username).get()
|
||||||
|
investigator_user.is_staff = True
|
||||||
|
investigator_user.save()
|
||||||
|
|
||||||
|
# Create a mock DomainApplication object, with a user that is not staff
|
||||||
|
application_2: DomainApplication = generic_domain_object("application", "SomeOtherGuy")
|
||||||
|
investigator_user_2 = User.objects.filter(username=application_2.investigator.username).get()
|
||||||
|
investigator_user_2.is_staff = False
|
||||||
|
investigator_user_2.save()
|
||||||
|
|
||||||
|
p = "userpass"
|
||||||
|
self.client.login(username="staffuser", password=p)
|
||||||
|
|
||||||
|
request = self.factory.post("/admin/registrar/domainapplication/{}/change/".format(application.pk))
|
||||||
|
|
||||||
|
# Get the actual field from the model's meta information
|
||||||
|
investigator_field = DomainApplication._meta.get_field("investigator")
|
||||||
|
|
||||||
|
# We should only be displaying staff users, in alphabetical order
|
||||||
|
expected_dropdown = list(User.objects.filter(is_staff=True))
|
||||||
|
current_dropdown = list(self.admin.formfield_for_foreignkey(investigator_field, request).queryset)
|
||||||
|
|
||||||
|
self.assertEqual(expected_dropdown, current_dropdown)
|
||||||
|
|
||||||
|
# Non staff users should not be in the list
|
||||||
|
self.assertNotIn(application_2, current_dropdown)
|
||||||
|
|
||||||
|
def test_investigator_list_is_alphabetically_sorted(self):
|
||||||
|
"""
|
||||||
|
This test verifies that filter list for the 'investigator'
|
||||||
|
is displayed alphabetically
|
||||||
|
"""
|
||||||
|
# Create a mock DomainApplication object, with a fake investigator
|
||||||
|
application: DomainApplication = generic_domain_object("application", "SomeGuy")
|
||||||
|
investigator_user = User.objects.filter(username=application.investigator.username).get()
|
||||||
|
investigator_user.is_staff = True
|
||||||
|
investigator_user.save()
|
||||||
|
|
||||||
|
application_2: DomainApplication = generic_domain_object("application", "AGuy")
|
||||||
|
investigator_user_2 = User.objects.filter(username=application_2.investigator.username).get()
|
||||||
|
investigator_user_2.first_name = "AGuy"
|
||||||
|
investigator_user_2.is_staff = True
|
||||||
|
investigator_user_2.save()
|
||||||
|
|
||||||
|
application_3: DomainApplication = generic_domain_object("application", "FinalGuy")
|
||||||
|
investigator_user_3 = User.objects.filter(username=application_3.investigator.username).get()
|
||||||
|
investigator_user_3.first_name = "FinalGuy"
|
||||||
|
investigator_user_3.is_staff = True
|
||||||
|
investigator_user_3.save()
|
||||||
|
|
||||||
|
p = "userpass"
|
||||||
|
self.client.login(username="staffuser", password=p)
|
||||||
|
request = RequestFactory().get("/")
|
||||||
|
|
||||||
|
expected_list = list(User.objects.filter(is_staff=True).order_by("first_name", "last_name", "email"))
|
||||||
|
|
||||||
|
# Get the actual sorted list of investigators from the lookups method
|
||||||
|
actual_list = [item for _, item in self.admin.InvestigatorFilter.lookups(self, request, self.admin)]
|
||||||
|
|
||||||
|
self.assertEqual(expected_list, actual_list)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
super().tearDown()
|
super().tearDown()
|
||||||
Domain.objects.all().delete()
|
Domain.objects.all().delete()
|
||||||
DomainInformation.objects.all().delete()
|
DomainInformation.objects.all().delete()
|
||||||
DomainApplication.objects.all().delete()
|
DomainApplication.objects.all().delete()
|
||||||
User.objects.all().delete()
|
User.objects.all().delete()
|
||||||
|
Contact.objects.all().delete()
|
||||||
|
Website.objects.all().delete()
|
||||||
|
|
||||||
|
|
||||||
class DomainInvitationAdminTest(TestCase):
|
class DomainInvitationAdminTest(TestCase):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue