test_nav_sidebar.py 8.8 KB

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