This project aims to modernize Django’s integration testing by introducing Playwright as an alternative to Selenium. The work includes integrating Playwright with Django’s test framework, migrating existing browser-based tests, and ensuring compatibility with Django’s CI infrastructure. The goal is to improve test reliability, performance, and developer experience for Django core contributors.
My plan is to update Django’s browser testing setup by moving from
Selenium to Playwright
Why Playwright is Better
● Playwright doesn’t need external WebDrivers
● Playwright has built-in auto-waiting for actions.
● Playwright is much simpler to write and read, lowering the cognitive load for
contributors.
● We can delete custom helper functions that we needed for Selenium.
Project Size: As of today, there are 106 selenium test cases in main branch that need to be converted.
Project Goals:
Modernize the testing infrastructure.
Rewrite existing tests
Boost CI execution performance.
Enhance test reliability.
Improve the developer experience with better tools
The project preserves Django’s testing architecture and only replaces the browser
automation layer (Selenium to Playwright).
Even though the translated tests use self.assertEqual(), some waiting is still needed, such as wait_for_load_state() or wait_for(), because a few tests are flaky.
I am leaning towards using Playwright’s expect() since it provides built-in auto-waiting and retrying. Also we could use inbuilt helpers in expect(). However, I don’t think expect() is needed for every test case. For example, tests without page navigation or tests that only verify backend/database changes can continue using self.assertEqual().
My concern is that having both approaches in the same test file may not be contributor friendly. I am also slightly concerned about importing expect in every test case (from playwright.sync_api import expect).
Another option could be to import expect at the top level, (this could be improved in the future with Python 3.15’s lazy imports) but I’m not sure whether ImportError handling is necessary, considering Playwright is already listed in our requirements and expect is available in playwright.sync_api.
Nice progress! You’ve outlined the reasons to use or not use expect(), and I don’t expect that to be too confusing for contributors. About the imports, what about consolidating them in one place in an instance method on the base class? Then test methods would call self.expect().
This Selenium test uses selenium.minimize_window() to minimize the browser window, whereas Playwright does not seem to have an API to minimize the browser.
We could use a CDP session for this, but that would be a Chromium-only solution, and we would need to skip this test on non-Chromium browsers.
For the web first locators, I would suggest that this is out of scope for the migration
We could chose to rework tests that do not involve testing translations, however tests decorated with @screenshot_cases(["rtl"]) are testing a translated page and we add this decorator to existing tests fairly frequently. Therefore, it is nice if the tests remain translation friendly
I agree web first locators test accessibility better but we can do any updates here aligned with accessibility testing tickets