Skip to content

perf(browser): map only first frame for trace locations - #11613

Merged
sheremet-va merged 3 commits into
vitest-dev:mainfrom
hi-ogawa:perf/trace-location-first-frame
Oct 16, 2026
Merged

sheremet-va merged 3 commits into
vitest-dev:mainfrom
hi-ogawa:perf/trace-location-first-frame

Conversation

@hi-ogawa

@hi-ogawa hi-ogawa commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

Motivation

Trace view records a timeline of steps, each with its timing and source location, and takes a DOM snapshot at every step. On real app pages the snapshot is usually the larger cost. But #10310 and #11592 move toward keeping the timeline while taking snapshots only on failure, so the timeline can be shown for every failure, including in terminal output. Then the cost every passing test pays is collecting the timeline itself, and that should be cheap.

Fix

Most of that cost was resolving each step's location. The server source-mapped every frame of the step's stack while the browser waited, although only the first usable frame is used, so the cost grew with stack depth (firefox stacks are about 40 frames per entry). This PR changes so it stops at the first frame that resolves and isn't filtered. Playwright browser.trace resolves action locations the same way, so it gets the same change.

Example

One test with 200 passing expect.element(...).toBeVisible() calls with traceView: true (median of 3 runs). The page is a single button, so the snapshot cost is small and the numbers mostly show the timeline collection:

browser trace view off main PR
chromium 11ms 544ms 349ms
firefox 21ms 5674ms 326ms
webkit 11ms 490ms 358ms

Per expect.element (two trace entries, start and end), dividing the totals above by 200, with the stack parsing part measured separately on the server:

browser trace view off main PR stack parsing, main → PR
chromium ~0.06ms ~2.7ms ~1.7ms ~1.4ms → ~0.3ms
firefox ~0.1ms ~28ms ~1.6ms ~29ms → ~0.4ms
webkit ~0.06ms ~2.5ms ~1.8ms ~0.4ms → ~0.2ms

The rest of the remaining trace view cost per assertion is the DOM snapshot and the two awaited round trips to the server.

📄 benchmark
// vitest.config.ts
import { playwright } from '@vitest/browser-playwright'
import { defineConfig } from 'vitest/config'

export default defineConfig({
  test: {
    browser: {
      enabled: true,
      provider: playwright(),
      instances: [{ browser: 'chromium' }, { browser: 'firefox' }, { browser: 'webkit' }],
      headless: true,
      ui: false,
      screenshotFailures: false,
      traceView: true,
    },
  },
})
// basic.test.ts
import { expect, test } from 'vitest'
import { page } from 'vitest/browser'

test('200 expect.element', async () => {
  document.body.innerHTML = '<button>ok</button>'
  for (let i = 0; i < 200; i++) {
    await expect.element(page.getByRole('button')).toBeVisible()
  }
})

hi-ogawa and others added 3 commits October 16, 2026 13:56
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
if (!position) {
return stack
}
function mapStackFrame(

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This just extracted a loop. See with "hide whitespace" https://github.com/vitest-dev/vitest/pull/11613/changes?w=1

@hi-ogawa
hi-ogawa marked this pull request as ready for review October 16, 2026 05:51
@sheremet-va
sheremet-va merged commit a967d8f into vitest-dev:main Oct 16, 2026
28 of 29 checks passed
@hi-ogawa
hi-ogawa deleted the perf/trace-location-first-frame branch October 19, 2026 01:51
@github-actions github-actions Bot locked and limited conversation to collaborators Nov 3, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants