from contextlib import contextmanager from django.contrib.staticfiles.testing import StaticLiveServerTestCase from django.test import modify_settings, override_settings from django.test.selenium import SeleniumTestCase from django.utils.csp import CSP from django.utils.translation import gettext as _ # Make unittest ignore frames in this module when reporting failures. __unittest = True @modify_settings( MIDDLEWARE={"append": "django.middleware.csp.ContentSecurityPolicyMiddleware"} ) @override_settings( SECURE_CSP={ "default-src": [CSP.NONE], "connect-src": [CSP.SELF], "img-src": [CSP.SELF], "script-src": [CSP.SELF], "style-src": [CSP.SELF], }, ) class AdminSeleniumTestCase(SeleniumTestCase, StaticLiveServerTestCase): available_apps = [ "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.sites", ] def tearDown(self): # Ensure that no CSP violations were logged in the browser. self.assertEqual(self.get_browser_logs(source="security"), []) super().tearDown() def wait_until(self, callback, timeout=10): """ Block the execution of the tests until the specified callback returns a value that is not falsy. This method can be called, for example, after clicking a link or submitting a form. See the other public methods that call this function for more details. """ from selenium.webdriver.support.wait import WebDriverWait WebDriverWait(self.selenium, timeout).until(callback) def wait_for_and_switch_to_popup(self, num_windows=2, timeout=10): """ Block until `num_windows` are present and are ready (usually 2, but can be overridden in the case of pop-ups opening other pop-ups). Switch the current window to the new pop-up. """ self.wait_until(lambda d: len(d.window_handles) == num_windows, timeout) self.selenium.switch_to.window(self.selenium.window_handles[-1]) self.wait_page_ready() def wait_for(self, css_selector, timeout=10): """ Block until a CSS selector is found on the page. """ from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as ec self.wait_until( ec.presence_of_element_located((By.CSS_SELECTOR, css_selector)), timeout ) def wait_for_text(self, css_selector, text, timeout=10): """ Block until the text is found in the CSS selector. """ from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as ec self.wait_until( ec.text_to_be_present_in_element((By.CSS_SELECTOR, css_selector), text), timeout, ) def wait_for_value(self, css_selector, text, timeout=10): """ Block until the value is found in the CSS selector. """ from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as ec self.wait_until( ec.text_to_be_present_in_element_value( (By.CSS_SELECTOR, css_selector), text ), timeout, ) def wait_until_visible(self, css_selector, timeout=10): """ Block until the element described by the CSS selector is visible. """ from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as ec self.wait_until( ec.visibility_of_element_located((By.CSS_SELECTOR, css_selector)), timeout ) def wait_until_invisible(self, css_selector, timeout=10): """ Block until the element described by the CSS selector is invisible. """ from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as ec self.wait_until( ec.invisibility_of_element_located((By.CSS_SELECTOR, css_selector)), timeout ) def wait_page_ready(self, timeout=10): """ Block until the page is ready. """ self.wait_until( lambda driver: driver.execute_script("return document.readyState;") == "complete", timeout, ) @contextmanager def wait_page_loaded(self, timeout=10): """ Block until a new page has loaded and is ready. """ from selenium.common.exceptions import WebDriverException from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as ec old_page = self.selenium.find_element(By.TAG_NAME, "html") yield # Wait for the next page to be loaded try: self.wait_until(ec.staleness_of(old_page), timeout=timeout) except WebDriverException: # Issue in version 113+ of Chrome driver where a WebDriverException # error is raised rather than a StaleElementReferenceException, see: # https://issues.chromium.org/issues/42323468 pass self.wait_page_ready(timeout=timeout) def admin_login(self, username, password, login_url="/admin/"): """ Log in to the admin. """ from selenium.webdriver.common.by import By self.selenium.get("%s%s" % (self.live_server_url, login_url)) username_input = self.selenium.find_element(By.NAME, "username") username_input.send_keys(username) password_input = self.selenium.find_element(By.NAME, "password") password_input.send_keys(password) login_text = _("Log in") with self.wait_page_loaded(): self.selenium.find_element( By.XPATH, '//input[@value="%s"]' % login_text ).click() def select_option(self, selector, value): """ Select the