diff --git a/.github/workflows/selenium.yml b/.github/workflows/playwright.yml similarity index 76% rename from .github/workflows/selenium.yml rename to .github/workflows/playwright.yml index 7ac14710f1c3..a3f526154929 100644 --- a/.github/workflows/selenium.yml +++ b/.github/workflows/playwright.yml @@ -1,4 +1,4 @@ -name: Selenium Tests +name: Playwright Tests on: pull_request: @@ -14,8 +14,8 @@ permissions: contents: read jobs: - selenium-sqlite: - if: contains(github.event.pull_request.labels.*.name, 'selenium') + playwright-sqlite: + if: contains(github.event.pull_request.labels.*.name, 'playwright') runs-on: ubuntu-latest name: SQLite timeout-minutes: 60 @@ -37,13 +37,15 @@ jobs: - name: Install and upgrade packaging tools run: python -m pip install --upgrade pip wheel - run: python -m pip install -r tests/requirements/py3.txt -e . - - name: Run Selenium tests + - name: Install Playwright browsers + run: python -m playwright install --with-deps chromium + - name: Run Playwright tests working-directory: ./tests/ run: | - python -Wall runtests.py --verbosity 2 --noinput --selenium=chrome --headless --settings=test_sqlite --parallel 1 + python -Wall runtests.py --verbosity 2 --noinput --playwright=chromium --settings=test_sqlite --parallel 1 - selenium-postgresql: - if: contains(github.event.pull_request.labels.*.name, 'selenium') + playwright-postgresql: + if: contains(github.event.pull_request.labels.*.name, 'playwright') runs-on: ubuntu-latest name: PostgreSQL timeout-minutes: 60 @@ -79,9 +81,11 @@ jobs: - name: Install and upgrade packaging tools run: python -m pip install --upgrade pip wheel - run: python -m pip install -r tests/requirements/py3.txt -r tests/requirements/postgres.txt -e . + - name: Install Playwright browsers + run: python -m playwright install --with-deps chromium - name: Create PostgreSQL settings file run: mv ./.github/workflows/data/test_postgres.py.tpl ./tests/test_postgres.py - - name: Run Selenium tests + - name: Run Playwright tests working-directory: ./tests/ run: | - python -Wall runtests.py --verbosity 2 --noinput --selenium=chrome --headless --settings=test_postgres --parallel 1 + python -Wall runtests.py --verbosity 2 --noinput --playwright=chromium --settings=test_postgres --parallel 1 diff --git a/.github/workflows/schedule_tests.yml b/.github/workflows/schedule_tests.yml index 3cb894a7a093..e6ddfccabafd 100644 --- a/.github/workflows/schedule_tests.yml +++ b/.github/workflows/schedule_tests.yml @@ -91,9 +91,9 @@ jobs: - run: npm install - run: npm test - selenium-sqlite: + playwright-sqlite: runs-on: ubuntu-latest - name: Selenium tests, SQLite + name: Playwright tests, SQLite timeout-minutes: 60 steps: - name: Checkout @@ -113,14 +113,16 @@ jobs: - name: Install and upgrade packaging tools run: python -m pip install --upgrade pip wheel - run: python -m pip install -r tests/requirements/py3.txt -e . - - name: Run Selenium tests + - name: Install Playwright browsers + run: python -m playwright install --with-deps chromium + - name: Run Playwright tests working-directory: ./tests/ run: | - python -Wall runtests.py --verbosity 2 --noinput --selenium=chrome --headless --settings=test_sqlite --parallel 1 + python -Wall runtests.py --verbosity 2 --noinput --playwright=chromium --settings=test_sqlite --parallel 1 - selenium-postgresql: + playwright-postgresql: runs-on: ubuntu-latest - name: Selenium tests, PostgreSQL + name: Playwright tests, PostgreSQL timeout-minutes: 60 services: postgres: @@ -154,12 +156,14 @@ jobs: - name: Install and upgrade packaging tools run: python -m pip install --upgrade pip wheel - run: python -m pip install -r tests/requirements/py3.txt -r tests/requirements/postgres.txt -e . + - name: Install Playwright browsers + run: python -m playwright install --with-deps chromium - name: Create PostgreSQL settings file run: mv ./.github/workflows/data/test_postgres.py.tpl ./tests/test_postgres.py - - name: Run Selenium tests + - name: Run Playwright tests working-directory: ./tests/ run: | - python -Wall runtests.py --verbosity 2 --noinput --selenium=chrome --headless --settings=test_postgres --parallel 1 + python -Wall runtests.py --verbosity 2 --noinput --playwright=chromium --settings=test_postgres --parallel 1 postgresql: strategy: diff --git a/.github/workflows/screenshots.yml b/.github/workflows/screenshots.yml index c98aa00db6bc..30e2b7f0ef64 100644 --- a/.github/workflows/screenshots.yml +++ b/.github/workflows/screenshots.yml @@ -14,7 +14,7 @@ permissions: contents: read jobs: - selenium-screenshots: + playwright-screenshots: if: contains(join(github.event.pull_request.labels.*.name, '|'), 'screenshots') runs-on: ubuntu-latest name: Screenshots @@ -37,10 +37,12 @@ jobs: - name: Install and upgrade packaging tools run: python -m pip install --upgrade pip wheel - run: python -m pip install -r tests/requirements/py3.txt -e . + - name: Install Playwright browsers + run: python -m playwright install --with-deps chromium - - name: Run Selenium tests with screenshots + - name: Run Playwright tests with screenshots working-directory: ./tests/ - run: python -Wall runtests.py --verbosity=2 --noinput --selenium=chrome --headless --screenshots --settings=test_sqlite --parallel=1 + run: python -Wall runtests.py --verbosity=2 --noinput --playwright=chromium --screenshots --settings=test_sqlite --parallel=1 - name: Cache oxipng uses: actions/cache@v6 diff --git a/django/contrib/admin/tests.py b/django/contrib/admin/tests.py deleted file mode 100644 index bd9c76b5fd52..000000000000 --- a/django/contrib/admin/tests.py +++ /dev/null @@ -1,257 +0,0 @@ -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 trigger_resize(self): - width = self.selenium.get_window_size()["width"] - height = self.selenium.get_window_size()["height"] - self.selenium.set_window_size(width + 1, height) - self.wait_page_ready() - self.selenium.set_window_size(width, height) - self.wait_page_ready() - - 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