Skip to content

[Bug]: video show.test leaves a stale overlay behind, duplicating caption lines in the recording #42796

Closed

Description

@someden

Version

1.63.0, also reproduces on main (07f1a61, 1.64.0-next)

Steps to reproduce

// playwright.config.ts
export default defineConfig({
  use: { video: { mode: 'on', show: { test: { level: 'step' } } } },
});
// a.test.ts
test('my test', async ({ page }) => {
  await test.step('first step', async () => {
    const lines = await page.locator('.x-pw-user-overlay').evaluateAll(
        els => els.map(el => el.innerText.split('\n')));
    console.log(lines);
  });
});

Expected behavior

Exactly one annotation overlay, showing the current file / test title / step.

Image

Actual behavior

Two overlays are present in the page at once:

[ ['a.test.ts', 'my test'],                 // stale, from before the step started
  ['a.test.ts', 'my test', 'first step'] ]  // current

Both are anchored to the same edge, so they are offset by exactly one line: the last line
of the stale overlay lands on the step line of the current one. In the recorded video the
previous caption line shows through as a ghost behind the current line — visible wherever
it is longer than the text drawn over it. It appears from the first step and stays for the
whole clip.

Shortening the test title moves the ghost up one line (the file name's tail then shows
behind the shorter line), which confirms it depends on the length of adjacent lines, not
on the step itself. It is in the .webm, not an artifact of how frames are extracted —
it is equally visible during plain playback.

Image

Additional context

Possible cause

installScreencastTitleUpdater in packages/playwright/src/index.ts updates the overlay
from two places: the test.step callbacks and the page event. The page handler is
fire-and-forget (void updateOverlay()), and updateOverlay reads overlays.get(page)
before awaiting page.screencast.showOverlay(...). When a step begins while the
page-event update is still in flight, the second update finds an empty map, removes
nothing and adds a second overlay. The reference to the first one is lost, so it stays in
the page until the context closes.

This makes the bug timing-dependent: an extra round trip before the first test.step
(any page call) is enough to hide it.

Environment

System:
    OS: macOS 26.6.2
    CPU: (10) arm64 Apple M1 Pro
    Memory: 1.62 GB / 32.00 GB
  Binaries:
    Node: 24.7.0 - ~/.nvm/versions/node/v24.7.0/bin/node
    npm: 11.5.1 - ~/.nvm/versions/node/v24.7.0/bin/npm
  Languages:
    Bash: 3.2.57 - /bin/bash
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions