playwright-python / tests
AGENTS.md15k starsChanged 10 days agoApache-2.0View on GitHub
These rules say how to write tests/async/{page,library} and tests/sync/{page,library} from tests/page and tests/library in microsoft/playwright, at the commit in DRIVER_VERSION. Use them at each roll, for every new or changed upstream test. The upstream tests are in .upstream/, the checkout at that commit. The steps of a roll are in the playwright-roll skill. The port is line by line, with zero deviations. A reviewer reads the .spec.ts file and the two .py files side by side. Each upstream statement maps…
We split this AGENTS.md into the rules it gives an agent
2 rules. About 0 are common, 1 is distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 14
Never change a port to make it pass: No skip, condition, mark, wait or assertion that upstream does not have, for any reason: not a browser …
- Very specific: line 29
Every upstream test has an identifier, the first token of its title (it('CH-001 should check the box', ...)).
≈ “Common” is an estimate: at least 1 in 5 of the 36,368 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
How to port the upstream tests to Python
These rules say how to write tests/async/{page,library} and tests/sync/{page,library}
from tests/page and tests/library in microsoft/playwright, at the commit in
DRIVER_VERSION. Use them at each roll, for every new or changed upstream test. The
upstream tests are in .upstream/, the checkout at that commit. The steps of a roll are
in the playwright-roll skill.
The port is line by line, with zero deviations. A reviewer reads the .spec.ts file
and the two .py files side by side. Each upstream statement maps to one Python
statement, or to one of the fixed patterns in this document. The test does what the
upstream test does, in the same order, with the same values, and checks the same things.
Never change a port to make it pass. No skip, condition, mark, wait or assertion
that upstream does not have, for any reason: not a browser channel, not a CI image, not an
operating system, not flakiness. A port that fails shows a bug in the client, the driver
or the environment: fix that, or report it with the evidence. The one allowed mark is
@pytest.mark.xfail(reason="python: ...", strict=True), for a bug in the Python client
that is not fixed yet.
The rules of the client itself are in playwright/_impl/AGENTS.md.
1. Files and names
- The upstream file decides the Python files:
tests/page/page-check.spec.tsistests/async/page/test_page_check.pyandtests/sync/page/test_page_check.py;tests/library/chromium/oopif.spec.tsistests/async/library/chromium/test_oopif.pyand the same undertests/sync. The tests keep the upstream order. - Every upstream test has an identifier, the first token of its title
(
it('CH-001 should check the box', ...)).tests/upstream_registry.mdis a copy of upstream'stests/registry.mdand lists them all. The name of the port istest_, the identifier with_, thedescribetitles and the title, all in snake_case:test('EX-181 toHaveCount pass')intest.describe('toHaveCount')istest_EX_181_to_have_count_to_have_count_pass. - A test that a loop declares several times (
for (const kind of kinds) it(...)) is one function with@pytest.mark.parametrize, with the loop values as the ids. beforeEach,afterEachand the fixtures that a file defines (test.extend({...})) are pytest fixtures at the top of the file, with the same names in snake_case. Helpers of the file stay helpers of the file.- A test that can not exist in Python (a JavaScript-only API, Node.js client internals,
tools that run in the driver,
@playwright/testfeatures) is not ported: it goes intests/upstream_not_ported.txt, with the reason. Nothing else is left out. python scripts/check_test_registry.pychecks that every test in the registry is ported twice or listed as not ported, under the expected name.--missing <code or file glob>prints the names still to port.
2. The two flavors
The async port is the port. The sync port is the same test again, statement for statement, with the sync API:
await page.click()isawait page.click()intests/asyncandpage.click()intests/sync.const [a, b] = await Promise.all([p1, p2])isa, b = await asyncio.gather(p1, p2)in async. In sync it isa, b = sync_gather(lambda: ..., lambda: ...): thesync_gatherfixture runs each callable in a greenlet of its own and returns their results in order.const [event] = await Promise.all([page.waitForEvent('x'), action()])isasync with page.expect_event("x") as info: await action()thenawait info.value. In sync:with page.expect_event("x") as info: action()theninfo.value.const promise = page.waitForX(); ...; await promise(a wait that starts before the action and is awaited later) istask = asyncio.create_task(page.wait_for_x())in async. In sync, where the port needs a bare future,wait_for_future(page._loop, page._dispatcher_fiber, future)fromplaywright._impl._greenletsruns the event loop until it settles; the file keeps a small helper for it when it needs several.- A handler that upstream writes as
async route => { await route.fulfill() }isasync def handler(route): await route.fulfill()in async anddef handler(route): route.fulfill()in sync. The sync API runs it in a greenlet of its own, so it may block.
3. Fixtures
The upstream fixtures map to the pytest fixtures of tests/conftest.py,
tests/async/conftest.py and tests/sync/conftest.py:
| Upstream | Python |
page, context, browser, browserType | page, context, browser, browser_type |
server, httpsServer | server, https_server (tests/server.py, Twisted) |
browserName, channel | browser_name, browser_channel |
isWindows, isMac, isLinux, platform | is_win, is_mac, is_linux, platform |
headless, isHeadlessShell | headless, is_headless_shell |
browserVersion, browserMajorVersion | browser_version, browser_major_version |
launchPersistent, contextFactory, browserFactory | launch_persistent, context_factory, browser_factory |
createUserDataDir | create_user_data_dir |
asset('file') | assetdir / "file" |
expect(buffer).toMatchSnapshot('x.png') | assert_to_be_golden(buffer, "x.png") |
kTargetClosedErrorMessage | TARGET_CLOSED_ERROR_MESSAGE (tests/utils.py) |
startRemoteServer, connect, twoPages and the other fixtures of a file | fixtures of the same name in the file, or in tests/helpers/ when several files share them |
The server has the methods of upstream's TestServer in snake_case: set_route,
set_redirect, set_auth, set_csp, enable_gzip, wait_for_request,
expect_request, wait_for_web_socket. A route handler takes a TestServerRequest
(Twisted's Request): setHeader, setResponseCode, write, finish, getHeader,
post_body.
4. Statements
expect(a).toBe(b),toEqual,toContain,toBeTruthy,toBeGreaterThanareassert a == b,assert a == b,assert b in a,assert a,assert a > b. A matcher on a locator or a page (await expect(locator).toHaveText(...)) is the same matcher of the Python API:await expect(locator).to_have_text(...), andexpect(locator)...in sync.await expect(promise).rejects.toThrow('text')andconst error = await promise.catch(e => e); expect(error.message).toContain('text')arewith pytest.raises(Error) as exc_info: await promiseandassert "text" in exc_info.value.message. The class is the one upstream expects:Error,TimeoutError,TargetClosedErrorfrom the public API.it.skip(condition, reason)andtest.skip(...)areif condition: pytest.skip(reason)at the top of the test, with the same condition.it.fixme(condition, reason)is the same withpytest.skip("fixme"), or the reason when there is one: a fixme is a skip that upstream has, so the port has it too. A skip at the top of a file or adescribeis a condition in each test of it, orpytestmark.test.slow()andtest.setTimeout(test.info().timeout * 3)are@pytest.mark.slow; at the top of a file,pytestmark = pytest.mark.slow. The timeout of a test is 30 seconds, as upstream (pyproject.toml);slowtriples it.it('...', { annotation: ... }, ...): the annotation is not ported.page.evaluate(() => ...): the function stays JavaScript, in a string. A function with arguments takes one argument:page.evaluate("([a, b]) => a + b", [a, b]). A Node.js value that goes to the page is the Python value:undefinedandnullareNone, an object is a dict with the same keys, an array is a list, aBufferisbytes, aRegExpisre.compile(...), aDateis adatetime.- The options of an API call are keyword arguments in snake_case:
{ waitUntil: 'load' }iswait_until="load". The keys of the data that the page or the driver returns stay as they are (request.headers["content-type"],entry["response"]["headers"]). new Promise(resolve => ...)that the test settles itself is anasyncio.Futurethat the test settles;new Promise(f => setTimeout(f, 100))isasyncio.sleep(0.1)in async andpage.wait_for_timeout(100)ortime.sleep(0.1)in sync, as the test needs.console.logand comments stay, as comments. A comment that explains an upstream workaround stays with the code it explains.
5. Marks
The port has one mark of its own, # Python:, on the line before a statement whose form
the rules above change in a way that is not obvious: a value that Python can not express
as upstream does, a helper that stands for a JavaScript construct. Do not mark a plain
rename or a plain await. A mark is not a deviation: the test still does what upstream
does.
6. Python-only tests
A behavior that no upstream test covers (the sync API, asyncio, Python types, __repr__,
a regression of this repository) gets a test in tests/async/python/ or
tests/sync/python/, in the file named after the upstream file of the feature. These
are not ports: keep them few (Tests in AGENTS.md).
Trending
More talk lately.about the whole repository
Who's talking about microsoft/playwright-python
- Playwright“Playwright at PYCon US with Andrew Knight”YouTube · May 12, 2023 · 6.1k views
- Bee Solutions“Monitoramento de Serviços com Downdetector Atualizado com Zabbix e Grafana - (BEE 2026)”YouTube · Jan 7, 2026 · 2.9k views
- QA Mania“Playwright python - how to run browsers concurrently”YouTube · Dec 11, 2020 · 2.5k views
- 開発ライブラリ紹介“取得したら中身が空っぽ?|playwrightでJSサイトも丸ごと取得 #shorts”YouTube · Sep 5, 2026 · 1.1k views
- coursegurukul“What is Playwright? | The Future of Web Testing | Playwright Tutorial for Beginners | Course Gurukul”YouTube · Apr 26, 2025 · 258 views
The biggest 5 of 72 mentions linking to the repository that our crawl found. Each line is their own headline.
See all 72
Talked about on GitHub, Stack Overflow, YouTube, the web and Hacker News (72 times).
On GitHub · 50
- Feb 9, 2022[Feature] Directly set Proxy-Authorization in Chrome without configuring any other proxy settings — microsoft/playwright65 reactions · 12 comments
- Jan 12, 2021[Feature] Support for Raspberry PI — microsoft/playwright34 reactions · 8 comments
- Sep 13, 2020[Feature] Driver - allow to install only selected browsers — microsoft/playwright14 reactions · 3 comments
- Nov 10, 2023[BUG] Opening page with large number of IFrames crashes/hangs playwright — microsoft/playwright9 reactions · 7 comments
- Nov 9, 2023[BUG] Unable to access iframe in firefox (works with chromium and webkit) — microsoft/playwright9 reactions · 3 comments
45 more on GitHub
- Jan 30, 2026python3Packages.playwright: 1.57.0 -> 1.58.0 — NixOS/nixpkgs5 reactions · 1 comment
- Sep 17, 2023[Feature] Open Telemetry Tracing — microsoft/playwright5 reactions · 2 comments
- Jun 9, 2026Bump the uv-all group across 1 directory with 3 updates — Ven0m0/dot-termux3 reactions · 1 comment
- May 20, 2026Bump the pip-all group across 1 directory with 3 updates — Ven0m0/dot-termux3 reactions · 1 comment
- May 3, 2024[Feature]: Proper support for scrolling to the bottom of a page — microsoft/playwright3 reactions · 7 comments
- Dec 30, 2022Update playwright driver to 1.29.1 — YusukeIwaki/playwright-ruby-client3 reactions · 3 comments
- Jan 22, 2021[Feature](playwright-cli) Option to install system dependencies — microsoft/playwright3 reactions · 4 comments
- Jul 7, 2026Bump the uv-all group across 1 directory with 2 updates — Ven0m0/dot-termux2 reactions · 2 comments
- Jul 7, 2026Bump the pip-all group across 1 directory with 2 updates — Ven0m0/dot-termux2 reactions · 2 comments
- Jun 30, 2026Bump playwright from 1.60.0 to 1.61.0 in the uv-all group — Ven0m0/dot-termux2 reactions · 2 comments
- Jun 30, 2026Bump playwright from 1.60.0 to 1.61.0 in the pip-all group — Ven0m0/dot-termux2 reactions · 2 comments
- May 20, 2026chore(deps): update playwright requirement from >=1.40.0 to >=1.60.0 — EvezArt/evez-claw-extensions2 reactions · 2 comments
- May 12, 2026chore(deps): update playwright requirement from >=1.40.0 to >=1.59.0 — EvezArt/evez-claw2 reactions · 2 comments
- Mar 26, 2026new rule - suggest changing function parameter type to readonly alternative if parameter is not mutated inside the function — astral-sh/ruff2 reactions · 1 comment
- Jul 31, 2024Please update Playwright-python to 1.40 for Python 3.12 compatibility — daijro/hrequests2 reactions · 4 comments
- Aug 13, 2022Flaky test: CypressError: `cy.request()` timed out waiting `30000ms` for a response from your server — cypress-io/cypress2 reactions · 7 comments
- Feb 3, 2022Installation fails using PDM — pytorch/vision2 reactions · 3 comments
- Oct 25, 2021[Regression] Page.screenshot does not work inside Docker with BrowserServer — microsoft/playwright2 reactions · 6 comments
- Oct 16, 2020setting asyncclick to use asyncio.WindowsProactorEventLoopPolicy — python-trio/asyncclick2 reactions · 2 comments
- Oct 29, 2026build(deps-dev): bump playwright from 1.63.0 to 1.64.1 — paulkakell/timeboardapp1 reaction · 1 comment
- Oct 26, 2026chore(deps-pip): bump the pip-jade-sprint-minor-patch group in /mission-control/jade_temple/sprint with 2 updates — GBOGEB/pipeline-automation-hub1 reaction · 2 comments
- Oct 26, 2026chore(deps-dev): bump playwright from 1.64.0 to 1.64.1 — MoonLadderStudios/MoonMind1 reaction · 1 comment
- Oct 26, 2026deps(deps): bump the pip-minor-patch group with 5 updates — cyxc1124/cyxcbot1 reaction · 1 comment
- Oct 26, 2026[Docs]: python - release notes haven't been updated since 1.63 — microsoft/playwright1 reaction
- Oct 26, 2026Update playwright requirement from >=1.62.0 to >=1.64.1 — WhiteDevil-93/Hermes-pro1 reaction · 2 comments
- Oct 25, 2026chore(deps): bump the uv-minor-patch group across 1 directory with 6 updates — mzworthington/RoMini1 reaction · 1 comment
- Oct 23, 2026chore(deps): update playwright requirement from >=1.40 to >=1.64.0 — escoffier-labs/brigade1 reaction · 3 comments
- Oct 21, 2026Build(deps): update playwright requirement from >=1.63.0 to >=1.64.0 in /tests/e2e — leynos/axinite1 reaction · 3 comments
- Oct 20, 2026fix(deps-dev): bump playwright from 1.50.0 to 1.63.0 — openai/openai-agents-python1 reaction · 4 comments
- Oct 18, 2026deps(deps): bump the pip-minor-patch group across 1 directory with 7 updates — cyxc1124/cyxcbot1 reaction · 1 comment
- Oct 17, 2026build(deps-dev): bump playwright from 1.63.0 to 1.64.0 — paulkakell/timeboardapp1 reaction · 2 comments
- Oct 17, 2026deps: bump playwright from 1.63.0 to 1.64.0 — scope-impact/rdm1 reaction · 1 comment
- Oct 17, 2026chore(deps-dev): bump playwright from 1.63.0 to 1.64.0 — MoonLadderStudios/MoonMind1 reaction · 1 comment
- Oct 8, 2026build(deps): bump playwright from 1.57.0 to 1.63.0 — Kotodama-Project/Kotodama-project1 reaction · 3 comments
- Sep 23, 2026Build(deps): update playwright requirement from >=1.61.0 to >=1.63.0 in /tests/e2e — leynos/axinite1 reaction · 1 comment
- Sep 22, 2026Bump playwright from 1.62.0 to 1.63.0 — gvatsal60/resume-studio1 reaction · 2 comments
- Sep 21, 2026chore(deps): Bump the python-deps group across 1 directory with 3 updates — julianocosta89/sig-meeting-notes1 reaction · 1 comment
- Sep 21, 2026Bump playwright from 1.62.0 to 1.63.0 — MoonLadderStudios/MoonMind1 reaction
- Aug 18, 2026chore(deps): bump the python-deps group across 1 directory with 2 updates — jimc1682000/film-brain1 reaction · 2 comments
- Aug 10, 2026Build(deps): update playwright requirement from >=1.61.0 to >=1.62.0 in /tests/e2e — leynos/axinite1 reaction · 1 comment
- Aug 10, 2026Chore(deps-dev): bump playwright from 1.61.0 to 1.62.0 — MoonLadderStudios/MoonMind1 reaction
- Aug 4, 2026chore(deps): update playwright requirement from >=1.61.0 to >=1.62.0 in the python-deps group across 1 directory — jimc1682000/film-brain1 reaction · 5 comments
- Aug 4, 2026Bump playwright from 1.61.0 to 1.62.0 — gvatsal60/resume-studio1 reaction · 1 comment
- Jul 26, 2026deps: update playwright requirement from <2,>=1.45 to >=1.61.0,<2 — Jnapier2/image-downloader1 reaction · 2 comments
- Jul 16, 2026Build(deps): update playwright requirement from >=1.40 to >=1.61.0 in /tests/e2e — leynos/axinite1 reaction
On Stack Overflow · 11
- Aug 25, 2018python - webdriver and asyncio25 votes
- Apr 1, 2022How to use the Playwright library in a Jupyter notebook instead of using a regular .py script (on Windows)14 votes
- Oct 30, 2022Installing playwright in Docker image fails3 votes
- Jun 1, 2022Can I test Electron application using Playwright Python library?3 votes
- Jun 21, 2024How to save django db data in the middle of a playwright test?2 votes
6 more on Stack Overflow
- May 17, 2023Right mouse click and selecting the appropriate item using Playwright2 votes
- Feb 26, 2024PyInstaller with Playwright and Firefox0 votes
- Dec 27, 2022Playwright and PM2 Issue - hang while creating PlaywrightContextManager0 votes · 1 answer
- Nov 5, 2022Install playwright from a local directory0 votes
- Oct 16, 2024Encountering NotImplementedError in Asynchronous Code with Playwright: Understanding the Cause and Seeking Solutions-1 votes
- Feb 24, 2022Making sure my headless browser is in the right place in my Playwright + Python + Aws lambda setup-1 votes
On YouTube · 5
- May 12, 2023Playwright at PYCon US with Andrew Knight — Playwright6.1k views · 7 comments
- Jan 7, 2026Monitoramento de Serviços com Downdetector Atualizado com Zabbix e Grafana - (BEE 2026) — Bee Solutions2.9k views · 17 comments
- Dec 11, 2020Playwright python - how to run browsers concurrently — QA Mania2.5k views · 8 comments
- Sep 5, 2026取得したら中身が空っぽ?|playwrightでJSサイトも丸ごと取得 #shorts — 開発ライブラリ紹介1.1k views
- Apr 26, 2025What is Playwright? | The Future of Web Testing | Playwright Tutorial for Beginners | Course Gurukul — coursegurukulsays it tested it258 views
On the web · 4
- Mar 19, 2024Python Playwright doesn't work in my computer · Issue #2364 — google.com
- Aug 23, 2022Python Playwright docker issue #1529 — google.com
- Oct 17, 2021Microsoft has released a Playwright-Python — google.com
- May 12, 2021[FEATURE] Playwright on PyPy · Issue #687 — google.com
On Hacker News · 2
- Sep 30, 2020Python version of the Playwright testing and automation librarysays it tested it2 points
- Oct 5, 2020Playwright: Microsoft's end-to-end testing framework for Pythonsays it tested it1 point · 1 comment
Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.
Of the AGENTS.md files we read, the 28,693 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
In this repository5 other files it gives its agents.
- AGENTS.mdAGENTS.md
- playwright/_impl/AGENTS.mdAGENTS.md
- playwright-releaseSkill
- playwright-rollSkill
- playwright-triageSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

