1. from django.contrib import admin
    
  2. from django.contrib.admin.tests import AdminSeleniumTestCase
    
  3. from django.contrib.auth.models import User
    
  4. from django.test import TestCase, override_settings
    
  5. from django.urls import path, reverse
    
  6. 
    
  7. from .models import Héllo
    
  8. 
    
  9. 
    
  10. class AdminSiteWithSidebar(admin.AdminSite):
    
  11.     pass
    
  12. 
    
  13. 
    
  14. class AdminSiteWithoutSidebar(admin.AdminSite):
    
  15.     enable_nav_sidebar = False
    
  16. 
    
  17. 
    
  18. site_with_sidebar = AdminSiteWithSidebar(name="test_with_sidebar")
    
  19. site_without_sidebar = AdminSiteWithoutSidebar(name="test_without_sidebar")
    
  20. 
    
  21. site_with_sidebar.register(User)
    
  22. site_with_sidebar.register(Héllo)
    
  23. 
    
  24. urlpatterns = [
    
  25.     path("test_sidebar/admin/", site_with_sidebar.urls),
    
  26.     path("test_wihout_sidebar/admin/", site_without_sidebar.urls),
    
  27. ]
    
  28. 
    
  29. 
    
  30. @override_settings(ROOT_URLCONF="admin_views.test_nav_sidebar")
    
  31. class AdminSidebarTests(TestCase):
    
  32.     @classmethod
    
  33.     def setUpTestData(cls):
    
  34.         cls.superuser = User.objects.create_superuser(
    
  35.             username="super",
    
  36.             password="secret",
    
  37.             email="[email protected]",
    
  38.         )
    
  39. 
    
  40.     def setUp(self):
    
  41.         self.client.force_login(self.superuser)
    
  42. 
    
  43.     def test_sidebar_not_on_index(self):
    
  44.         response = self.client.get(reverse("test_with_sidebar:index"))
    
  45.         self.assertContains(response, '<div class="main" id="main">')
    
  46.         self.assertNotContains(response, '<nav class="sticky" id="nav-sidebar">')
    
  47. 
    
  48.     def test_sidebar_disabled(self):
    
  49.         response = self.client.get(reverse("test_without_sidebar:index"))
    
  50.         self.assertNotContains(response, '<nav class="sticky" id="nav-sidebar">')
    
  51. 
    
  52.     def test_sidebar_unauthenticated(self):
    
  53.         self.client.logout()
    
  54.         response = self.client.get(reverse("test_with_sidebar:login"))
    
  55.         self.assertNotContains(response, '<nav class="sticky" id="nav-sidebar">')
    
  56. 
    
  57.     def test_sidebar_aria_current_page(self):
    
  58.         url = reverse("test_with_sidebar:auth_user_changelist")
    
  59.         response = self.client.get(url)
    
  60.         self.assertContains(response, '<nav class="sticky" id="nav-sidebar">')
    
  61.         self.assertContains(
    
  62.             response, '<a href="%s" aria-current="page">Users</a>' % url
    
  63.         )
    
  64. 
    
  65.     @override_settings(
    
  66.         TEMPLATES=[
    
  67.             {
    
  68.                 "BACKEND": "django.template.backends.django.DjangoTemplates",
    
  69.                 "DIRS": [],
    
  70.                 "APP_DIRS": True,
    
  71.                 "OPTIONS": {
    
  72.                     "context_processors": [
    
  73.                         "django.contrib.auth.context_processors.auth",
    
  74.                         "django.contrib.messages.context_processors.messages",
    
  75.                     ],
    
  76.                 },
    
  77.             }
    
  78.         ]
    
  79.     )
    
  80.     def test_sidebar_aria_current_page_missing_without_request_context_processor(self):
    
  81.         url = reverse("test_with_sidebar:auth_user_changelist")
    
  82.         response = self.client.get(url)
    
  83.         self.assertContains(response, '<nav class="sticky" id="nav-sidebar">')
    
  84.         # Does not include aria-current attribute.
    
  85.         self.assertContains(response, '<a href="%s">Users</a>' % url)
    
  86.         self.assertNotContains(response, "aria-current")
    
  87. 
    
  88.     @override_settings(DEBUG=True)
    
  89.     def test_included_app_list_template_context_fully_set(self):
    
  90.         # All context variables should be set when rendering the sidebar.
    
  91.         url = reverse("test_with_sidebar:auth_user_changelist")
    
  92.         with self.assertNoLogs("django.template", "DEBUG"):
    
  93.             self.client.get(url)
    
  94. 
    
  95.     def test_sidebar_model_name_non_ascii(self):
    
  96.         url = reverse("test_with_sidebar:admin_views_héllo_changelist")
    
  97.         response = self.client.get(url)
    
  98.         self.assertContains(
    
  99.             response, '<div class="app-admin_views module current-app">'
    
  100.         )
    
  101.         self.assertContains(response, '<tr class="model-héllo current-model">')
    
  102.         self.assertContains(
    
  103.             response,
    
  104.             '<th scope="row">'
    
  105.             '<a href="/test_sidebar/admin/admin_views/h%C3%A9llo/" aria-current="page">'
    
  106.             "Héllos</a></th>",
    
  107.         )
    
  108. 
    
  109. 
    
  110. @override_settings(ROOT_URLCONF="admin_views.test_nav_sidebar")
    
  111. class SeleniumTests(AdminSeleniumTestCase):
    
  112.     available_apps = ["admin_views"] + AdminSeleniumTestCase.available_apps
    
  113. 
    
  114.     def setUp(self):
    
  115.         self.superuser = User.objects.create_superuser(
    
  116.             username="super",
    
  117.             password="secret",
    
  118.             email="[email protected]",
    
  119.         )
    
  120.         self.admin_login(
    
  121.             username="super",
    
  122.             password="secret",
    
  123.             login_url=reverse("test_with_sidebar:index"),
    
  124.         )
    
  125.         self.selenium.execute_script(
    
  126.             "localStorage.removeItem('django.admin.navSidebarIsOpen')"
    
  127.         )
    
  128. 
    
  129.     def test_sidebar_starts_open(self):
    
  130.         from selenium.webdriver.common.by import By
    
  131. 
    
  132.         self.selenium.get(
    
  133.             self.live_server_url + reverse("test_with_sidebar:auth_user_changelist")
    
  134.         )
    
  135.         main_element = self.selenium.find_element(By.CSS_SELECTOR, "#main")
    
  136.         self.assertIn("shifted", main_element.get_attribute("class").split())
    
  137. 
    
  138.     def test_sidebar_can_be_closed(self):
    
  139.         from selenium.webdriver.common.by import By
    
  140. 
    
  141.         self.selenium.get(
    
  142.             self.live_server_url + reverse("test_with_sidebar:auth_user_changelist")
    
  143.         )
    
  144.         toggle_button = self.selenium.find_element(
    
  145.             By.CSS_SELECTOR, "#toggle-nav-sidebar"
    
  146.         )
    
  147.         self.assertEqual(toggle_button.tag_name, "button")
    
  148.         self.assertEqual(toggle_button.get_attribute("aria-label"), "Toggle navigation")
    
  149.         for link in self.selenium.find_elements(By.CSS_SELECTOR, "#nav-sidebar a"):
    
  150.             self.assertEqual(link.get_attribute("tabIndex"), "0")
    
  151.         filter_input = self.selenium.find_element(By.CSS_SELECTOR, "#nav-filter")
    
  152.         self.assertEqual(filter_input.get_attribute("tabIndex"), "0")
    
  153.         toggle_button.click()
    
  154.         # Hidden sidebar is not reachable via keyboard navigation.
    
  155.         for link in self.selenium.find_elements(By.CSS_SELECTOR, "#nav-sidebar a"):
    
  156.             self.assertEqual(link.get_attribute("tabIndex"), "-1")
    
  157.         filter_input = self.selenium.find_element(By.CSS_SELECTOR, "#nav-filter")
    
  158.         self.assertEqual(filter_input.get_attribute("tabIndex"), "-1")
    
  159.         main_element = self.selenium.find_element(By.CSS_SELECTOR, "#main")
    
  160.         self.assertNotIn("shifted", main_element.get_attribute("class").split())
    
  161. 
    
  162.     def test_sidebar_state_persists(self):
    
  163.         from selenium.webdriver.common.by import By
    
  164. 
    
  165.         self.selenium.get(
    
  166.             self.live_server_url + reverse("test_with_sidebar:auth_user_changelist")
    
  167.         )
    
  168.         self.assertIsNone(
    
  169.             self.selenium.execute_script(
    
  170.                 "return localStorage.getItem('django.admin.navSidebarIsOpen')"
    
  171.             )
    
  172.         )
    
  173.         toggle_button = self.selenium.find_element(
    
  174.             By.CSS_SELECTOR, "#toggle-nav-sidebar"
    
  175.         )
    
  176.         toggle_button.click()
    
  177.         self.assertEqual(
    
  178.             self.selenium.execute_script(
    
  179.                 "return localStorage.getItem('django.admin.navSidebarIsOpen')"
    
  180.             ),
    
  181.             "false",
    
  182.         )
    
  183.         self.selenium.get(
    
  184.             self.live_server_url + reverse("test_with_sidebar:auth_user_changelist")
    
  185.         )
    
  186.         main_element = self.selenium.find_element(By.CSS_SELECTOR, "#main")
    
  187.         self.assertNotIn("shifted", main_element.get_attribute("class").split())
    
  188. 
    
  189.         toggle_button = self.selenium.find_element(
    
  190.             By.CSS_SELECTOR, "#toggle-nav-sidebar"
    
  191.         )
    
  192.         # Hidden sidebar is not reachable via keyboard navigation.
    
  193.         for link in self.selenium.find_elements(By.CSS_SELECTOR, "#nav-sidebar a"):
    
  194.             self.assertEqual(link.get_attribute("tabIndex"), "-1")
    
  195.         filter_input = self.selenium.find_element(By.CSS_SELECTOR, "#nav-filter")
    
  196.         self.assertEqual(filter_input.get_attribute("tabIndex"), "-1")
    
  197.         toggle_button.click()
    
  198.         for link in self.selenium.find_elements(By.CSS_SELECTOR, "#nav-sidebar a"):
    
  199.             self.assertEqual(link.get_attribute("tabIndex"), "0")
    
  200.         filter_input = self.selenium.find_element(By.CSS_SELECTOR, "#nav-filter")
    
  201.         self.assertEqual(filter_input.get_attribute("tabIndex"), "0")
    
  202.         self.assertEqual(
    
  203.             self.selenium.execute_script(
    
  204.                 "return localStorage.getItem('django.admin.navSidebarIsOpen')"
    
  205.             ),
    
  206.             "true",
    
  207.         )
    
  208.         self.selenium.get(
    
  209.             self.live_server_url + reverse("test_with_sidebar:auth_user_changelist")
    
  210.         )
    
  211.         main_element = self.selenium.find_element(By.CSS_SELECTOR, "#main")
    
  212.         self.assertIn("shifted", main_element.get_attribute("class").split())
    
  213. 
    
  214.     def test_sidebar_filter_persists(self):
    
  215.         from selenium.webdriver.common.by import By
    
  216. 
    
  217.         self.selenium.get(
    
  218.             self.live_server_url + reverse("test_with_sidebar:auth_user_changelist")
    
  219.         )
    
  220.         filter_value_script = (
    
  221.             "return sessionStorage.getItem('django.admin.navSidebarFilterValue')"
    
  222.         )
    
  223.         self.assertIsNone(self.selenium.execute_script(filter_value_script))
    
  224.         filter_input = self.selenium.find_element(By.CSS_SELECTOR, "#nav-filter")
    
  225.         filter_input.send_keys("users")
    
  226.         self.assertEqual(self.selenium.execute_script(filter_value_script), "users")