Repository: cisagov/manage.get.gov

Issue:
Django Admin: Search "user domain role" by email address
### Issue description

As an Analyst
I want to search for a domain manager by their email address
so that I can locate their records more easily.


![Image](https://github.com/cisagov/manage.get.gov/assets/62573986/c1315cf8-42fe-4cd0-ad82-6d471949bb10)




### Acceptance criteria

- [ ] In the "User domain role" page search bar, analysts can search using name, email, domain, or role.
- [ ] Change "name" to "firstname, lastname"

### Additional context

[Miro research](https://miro.com/app/board/uXjVNZhbw-s=/?openComment=3458764569105248958)

### Links to other issues

Related to #1291 #1298 


Discussion:

Resolving patch:
diff --git a/src/registrar/admin.py b/src/registrar/admin.py
--- a/src/registrar/admin.py
+++ b/src/registrar/admin.py
@@ -324,6 +324,14 @@ class WebsiteAdmin(ListHeaderAdmin):
 class UserDomainRoleAdmin(ListHeaderAdmin):
     """Custom domain role admin class."""
 
+    class Meta:
+        """Contains meta information about this class"""
+
+        model = models.UserDomainRole
+        fields = "__all__"
+
+    _meta = Meta()
+
     # Columns
     list_display = [
         "user",
@@ -335,10 +343,11 @@ class UserDomainRoleAdmin(ListHeaderAdmin):
     search_fields = [
         "user__first_name",
         "user__last_name",
+        "user__email",
         "domain__name",
         "role",
     ]
-    search_help_text = "Search by user, domain, or role."
+    search_help_text = "Search by firstname, lastname, email, domain, or role."
 
 
 class DomainInvitationAdmin(ListHeaderAdmin):


Test patch:
diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py
--- a/src/registrar/tests/test_admin.py
+++ b/src/registrar/tests/test_admin.py
@@ -13,6 +13,7 @@
     MyUserAdmin,
     AuditedAdmin,
     ContactAdmin,
+    UserDomainRoleAdmin,
 )
 from registrar.models import (
     Domain,
@@ -21,6 +22,7 @@
     User,
     DomainInvitation,
 )
+from registrar.models.user_domain_role import UserDomainRole
 from .common import (
     completed_application,
     generic_domain_object,
@@ -886,6 +888,86 @@ def test_get_filters(self):
         self.assertContains(response, retrieved_html, count=1)
 
 
+class UserDomainRoleAdminTest(TestCase):
+    def setUp(self):
+        """Setup environment for a mock admin user"""
+        self.site = AdminSite()
+        self.factory = RequestFactory()
+        self.admin = ListHeaderAdmin(model=UserDomainRoleAdmin, admin_site=None)
+        self.client = Client(HTTP_HOST="localhost:8080")
+        self.superuser = create_superuser()
+
+    def tearDown(self):
+        """Delete all Users, Domains, and UserDomainRoles"""
+        User.objects.all().delete()
+        Domain.objects.all().delete()
+        UserDomainRole.objects.all().delete()
+
+    def test_email_not_in_search(self):
+        """Tests the search bar in Django Admin for UserDomainRoleAdmin.
+        Should return no results for an invalid email."""
+        # Have to get creative to get past linter
+        p = "adminpass"
+        self.client.login(username="superuser", password=p)
+
+        fake_user = User.objects.create(
+            username="dummyuser", first_name="Stewart", last_name="Jones", email="AntarcticPolarBears@example.com"
+        )
+        fake_domain = Domain.objects.create(name="test123")
+        UserDomainRole.objects.create(user=fake_user, domain=fake_domain, role="manager")
+        # Make the request using the Client class
+        # which handles CSRF
+        # Follow=True handles the redirect
+        response = self.client.get(
+            "/admin/registrar/userdomainrole/",
+            {
+                "q": "testmail@igorville.com",
+            },
+            follow=True,
+        )
+
+        # Assert that the query is added to the extra_context
+        self.assertIn("search_query", response.context)
+        # Assert the content of filters and search_query
+        search_query = response.context["search_query"]
+        self.assertEqual(search_query, "testmail@igorville.com")
+
+        # We only need to check for the end of the HTML string
+        self.assertNotContains(response, "Stewart Jones AntarcticPolarBears@example.com</a></th>")
+
+    def test_email_in_search(self):
+        """Tests the search bar in Django Admin for UserDomainRoleAdmin.
+        Should return results for an valid email."""
+        # Have to get creative to get past linter
+        p = "adminpass"
+        self.client.login(username="superuser", password=p)
+
+        fake_user = User.objects.create(
+            username="dummyuser", first_name="Joe", last_name="Jones", email="AntarcticPolarBears@example.com"
+        )
+        fake_domain = Domain.objects.create(name="fake")
+        UserDomainRole.objects.create(user=fake_user, domain=fake_domain, role="manager")
+        # Make the request using the Client class
+        # which handles CSRF
+        # Follow=True handles the redirect
+        response = self.client.get(
+            "/admin/registrar/userdomainrole/",
+            {
+                "q": "AntarcticPolarBears@example.com",
+            },
+            follow=True,
+        )
+
+        # Assert that the query is added to the extra_context
+        self.assertIn("search_query", response.context)
+
+        search_query = response.context["search_query"]
+        self.assertEqual(search_query, "AntarcticPolarBears@example.com")
+
+        # We only need to check for the end of the HTML string
+        self.assertContains(response, "Joe Jones AntarcticPolarBears@example.com</a></th>", count=1)
+
+
 class ListHeaderAdminTest(TestCase):
     def setUp(self):
         self.site = AdminSite()