Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions tests/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,13 @@ If a race appears only on a loaded machine, make the ordering explicit with
the route before the gesture whose request it must catch. For initial navigation,
attach it through `primed` so no request is already in flight.

That rule is about the page. A fact the driver loses on its way out of the browser
is not a page state any route can arrange, and it has no second channel to be read
through: `opened_tab` makes the press again because Chromium made the tab every time
and Playwright reported none of the lost ones. Reach for a repeat only with that
evidence in hand — the browser's own record showing the subject did its part — and
say so where the repeat is written.

A handler that appends a route to `held` has established only that the browser made
the request. Before indexing `held`, wait for the corresponding `Traffic` edge, a
request event, or another fact named by the handler. Some resources are requested
Expand Down
50 changes: 50 additions & 0 deletions tests/render_harness.py
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,56 @@ def open_page(
return page, errors


def opened_tab(page, press, tries=3, each=10_000):
"""The tab a press opens, pressed again when the harness loses the one Chromium made.

Chromium makes the tab every time. After a press whose tab never arrives,
`Target.getTargets` holds a second page target in this browser context — attached,
loaded, titled, sitting at the href the press named — while `context.pages` still
holds one, and it stays that way for the life of the page: the targets pile up press
after press and Playwright reports none of them. So `expect_page` spends its whole
timeout waiting on a tab that already exists, and the test reads as though the press
had opened nothing.

A driver that loses the handle is not a page state a route can arrange, and there is
no second channel to reach an unreported tab through, so the press is made again
rather than waited on longer. This is instrument repair, not tolerance for a flaky
subject: the press itself is deterministic — the loss reaches every chord, though not
at one rate: 3, 10 and 1 of 60 presses lost for ⌃-click, ⌃⇧-click and ⇧-click on a
loaded machine — so a runtime that stopped leaving a real href for the platform to act
on opens no tab for any of the tries, and the last one says which wait went unanswered.

A press whose tab is lost still leaves that tab loaded and polling its server, and no
caller can close what it was never handed. A test that closes the tab it receives is
closing only the try that was reported; the rest stand until context teardown. That is
the standing cost of the repeat, not a leak to chase.

A press that refuses outright — an anchor hidden, covered, or disabled, the shape a
runtime regression takes — raises its own timeout from inside the wait, and Playwright's
`EventContextManager.__exit__` cancels the wait and lets it through. Repeating that
press would be exactly the tolerance this helper is not, so it is caught where it is
raised and named as the subject's refusal.
"""
for attempt in range(tries):
try:
with page.context.expect_page(timeout=each) as opened:
try:
press()
except PlaywrightTimeout as refused:
raise AssertionError(
"the press timed out before any tab could open: the subject "
"refused the gesture, which is not the loss this repeats for"
) from refused
return opened.value
except PlaywrightTimeout as lost:
if attempt == tries - 1:
raise AssertionError(
f"no tab after {tries} presses waiting {each}ms each: either the "
"press stopped leaving a real href, or Chromium holds a target "
"Playwright never reported"
) from lost


def primed(browser, prepare):
"""A browser whose pages reach the product with the suite's hands already on them.

Expand Down
2 changes: 2 additions & 0 deletions tests/render_support.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@
leaf_page,
navigate,
open_page,
opened_tab,
page_registry,
panel_settled,
post_event,
Expand Down Expand Up @@ -537,6 +538,7 @@
"navigate",
"one_reader",
"open_page",
"opened_tab",
"other_leaf",
"page_at_rest",
"page_registry",
Expand Down
11 changes: 5 additions & 6 deletions tests/test_render_controls.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
live_watcher,
mark_edges,
open_page,
opened_tab,
page_at_rest,
page_registry,
panel_settled,
Expand Down Expand Up @@ -1768,12 +1769,11 @@ def test_the_banner_opens_a_panel_of_the_machines_leaves(
f"The other leaf\n{tmp_path / 'other-work'}\nWorking — running the suite",
)
destination = link.get_attribute("href")
with page.context.expect_page() as opened:
link.click()
tab = opened_tab(page, link.click)
# The new tab keeps the other page's live root, authorized by the key its link
# carried, rather than being redirected onto one immutable version.
assert destination is not None and destination.startswith(f"{other_url}/?t=")
expect(opened.value).to_have_url(destination)
expect(tab).to_have_url(destination)
# The press left this tab alone, tray still standing.
expect(others_panel).to_be_visible()
page.keyboard.press("Escape")
Expand Down Expand Up @@ -1943,10 +1943,9 @@ def test_the_leaves_tray_takes_the_keyboard(browser, serve, live_leaf):
# Enter is the browser's own on a link, which is why the row is one.
page.keyboard.press("ArrowDown")
destination = rows.nth(1).get_attribute("href")
with page.context.expect_page() as opened:
page.keyboard.press("Enter")
tab = opened_tab(page, lambda: page.keyboard.press("Enter"))
assert destination is not None and destination.startswith(f"{other_url}/?t=")
expect(opened.value).to_have_url(destination)
expect(tab).to_have_url(destination)
page.keyboard.press("Escape")
expect(page.locator(".lf-others-panel")).not_to_be_visible()
# Closing while focus is inside would drop the reader on the body; it lands on
Expand Down
5 changes: 2 additions & 3 deletions tests/test_render_projection.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
leaf_page,
live_url,
open_page,
opened_tab,
page_registry,
painted,
panel_settled,
Expand Down Expand Up @@ -2354,9 +2355,7 @@ def test_a_message_reference_travels_or_says_it_cant(browser, serve):
# holds — ⌘ where it was written, ⌃ where CI runs it — so the press names the
# gesture and lets Playwright spell it. Named outright, the Linux press opened
# nothing at all and the wait for the tab ran its full 30s before saying so.
with page.context.expect_page() as opened:
live.click(modifiers=["ControlOrMeta"])
tab = opened.value
tab = opened_tab(page, lambda: live.click(modifiers=["ControlOrMeta"]))
tab.wait_for_function(BOTH_STAMPS)
tab.wait_for_function(
"""() => { const r = document.getElementById('p-bath').getBoundingClientRect();
Expand Down
Loading