Starts a job with a per-second countdown and waits for it to finish. Proves a long wait does not bloat the activity reel or the video.
asyncdemoslow version: 1.0 DEMO-006__long_job_wait.py
Runs
2
Pass rate
100%
2 passed / 0 failed
Avg duration
2m 02s
p95 2m 02s
Estimate
2m 02s
avg of last 10 judged runs
Flakiness
0.00
0 stable · 1 alternates
Current streak
2
passed
Duration per run point color = outcome; dashed = 7-run average; click a point to open the run
Step timing trends the same step compared across the last 2 runs — slowest steps first, one line each
Outcomes by target version
What this test does plain language, derived from the code
- page.goto(f"{ctx.base_url}?timer={JOB_SECONDS}")
- Note in the log: “phase 1: start the job (this part is worth watching)”
- Click #timer-btn
- Wait until #timer-panel:not(.hidden) appears
- Take a screenshot (“job-started”)
- ctx.log(f"phase 2: waiting up to {JOB_SECONDS}s for it to finish")
- ⏱ Timed phase: job wait
- page.wait_for_selector("#timer-done:not(.hidden)",
- timeout=(JOB_SECONDS + 60) * 1000)
- Note in the log: “phase 3: the job finished -- check the outcome”
- Take a screenshot (“job-finished”)
- Check #timer-done contains “finished”
- assert page.is_hidden("#timer-panel"), "the countdown should be gone"
Show the code
"""Wait for a long-running job, without recording the wait.
This is the pattern for "kick something off, then wait 30 minutes for it":
a nightly batch, a report build, a slow backend. The demo page's job shows
a countdown that repaints EVERY SECOND on purpose -- the hard case, because
content-hash de-duplication cannot collapse a clock (every tick genuinely
differs).
Two things to notice in the artifacts afterwards:
* the run took the full wait, but the ACTIVITY REEL only holds the busy
parts -- starting the job and checking the result. The idle stretch is
sampled sparsely and the replay jumps it with a "skipped idle" marker.
* the per-step timings show the wait as one labelled block, so you can
compare "how long did the job take" across runs on the trend chart.
Change JOB_SECONDS to 1800 for a real 30-minute rehearsal (and raise
timeout_seconds in the sidecar to match, plus a bit).
"""
JOB_SECONDS = 120
def run(page, ctx):
page.goto(f"{ctx.base_url}?timer={JOB_SECONDS}")
ctx.log("phase 1: start the job (this part is worth watching)")
page.click("#timer-btn")
page.wait_for_selector("#timer-panel:not(.hidden)")
ctx.screenshot("job-started")
ctx.log(f"phase 2: waiting up to {JOB_SECONDS}s for it to finish")
with ctx.timed("job wait"):
# One wait, not a polling loop: Playwright re-checks for us, and the
# harness treats an unattended page as idle for activity capture.
page.wait_for_selector("#timer-done:not(.hidden)",
timeout=(JOB_SECONDS + 60) * 1000)
ctx.log("phase 3: the job finished -- check the outcome")
ctx.screenshot("job-finished")
assert "finished" in page.inner_text("#timer-done").lower()
assert page.is_hidden("#timer-panel"), "the countdown should be gone"
All runs
| Run | Status | Queued | Duration | Version | Trigger | Batch |
|---|---|---|---|---|---|---|
| #461 | passed | 2026-09-25 00:27:40 | 2m 02s | 3.0.0 | cli-adopted | 2 tests (terminal) |
| #459 | passed | 2026-09-25 00:25:35 | 2m 02s | 3.0.0 | cli-adopted | 2 tests (terminal) |