From 17e2b22dd018a9098a597d835bf45d1a7a3e73c8 Mon Sep 17 00:00:00 2001 From: hamed-bavar <54584195+hamed-bavar@users.noreply.github.com> Date: Wed, 9 Sep 2026 11:02:41 +0330 Subject: [PATCH 01/12] fix(snapshot): report obsolete keys next to skipped tests (fix #11157) (#11158) Co-authored-by: Hiroshi Ogawa <4232207+hi-ogawa@users.noreply.github.com> Co-authored-by: OpenCode (gpt-6-astra) --- packages/snapshot/src/port/state.ts | 5 +- test/e2e/snapshots/skip-test.test.ts | 91 +++++++++++++++++++++++++++- 2 files changed, 94 insertions(+), 2 deletions(-) diff --git a/packages/snapshot/src/port/state.ts b/packages/snapshot/src/port/state.ts index 2fec430b8..4c46373a4 100644 --- a/packages/snapshot/src/port/state.ts +++ b/packages/snapshot/src/port/state.ts @@ -150,7 +150,10 @@ export default class SnapshotState { // skip snapshots with following keys // testName n // testName > xxx n (this is for toMatchSnapshot("xxx") API) - if (/ \d+$| > /.test(uncheckedKey.slice(testName.length))) { + if ( + uncheckedKey.startsWith(testName) + && /^ \d+$|^ > /.test(uncheckedKey.slice(testName.length)) + ) { this._uncheckedKeys.delete(uncheckedKey) } }) diff --git a/test/e2e/snapshots/skip-test.test.ts b/test/e2e/snapshots/skip-test.test.ts index cd6ad0890..5252f05b9 100644 --- a/test/e2e/snapshots/skip-test.test.ts +++ b/test/e2e/snapshots/skip-test.test.ts @@ -1,7 +1,7 @@ import fs from 'node:fs' import path from 'node:path' import { expect, test } from 'vitest' -import { runVitest } from '../../test-utils' +import { runInlineTests, runVitest } from '../../test-utils' test('snapshots in skipped test/suite is not obsolete', async () => { // create snapshot on first run @@ -114,3 +114,92 @@ test('handle obsoleteness of toMatchSnapshot("custom message")', async () => { " `) }) + +test('obsolete snapshots are still reported when another test is skipped', async () => { + const structure = { + 'basic.test.ts': ` + import { expect, it } from 'vitest' + + it.skip('a', () => { + expect(0).toMatchSnapshot() + }) + + it('b', () => { + expect(1).toMatchSnapshot() + }) + `, + '__snapshots__/basic.test.ts.snap': `// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[\`a 1\`] = \`0\`; + +exports[\`b 1\`] = \`1\`; + +exports[\`removed test 1\`] = \`2\`; +`, + } + + const failed = await runInlineTests(structure, { update: 'none' }) + expect(failed.errorTree()).toMatchInlineSnapshot(` + { + "basic.test.ts": { + "__module_errors__": [ + "Obsolete snapshots found when no snapshot update is expected. + · removed test 1 + ", + ], + "a": "skipped", + "b": "passed", + }, + } + `) + + const updated = await runInlineTests(structure, { update: 'all' }) + expect(fs.readFileSync(path.join(updated.root, '__snapshots__/basic.test.ts.snap'), 'utf-8')).toMatchInlineSnapshot(` + "// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + + exports[\`a 1\`] = \`0\`; + + exports[\`b 1\`] = \`1\`; + " + `) +}) + +test('skipped test does not hide obsolete snapshots of a test with a longer name', async () => { + const result = await runInlineTests({ + 'basic.test.ts': ` + import { expect, it } from 'vitest' + + it.skip('foo', () => { + expect(0).toMatchSnapshot() + expect(0).toMatchSnapshot('custom') + }) + + it('bar', () => { + expect(1).toMatchSnapshot() + }) + `, + '__snapshots__/basic.test.ts.snap': `// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[\`bar 1\`] = \`1\`; + +exports[\`foo 1\`] = \`0\`; + +exports[\`foo > custom 1\`] = \`0\`; + +exports[\`foobar 1\`] = \`0\`; +`, + }, { update: 'none' }) + expect(result.errorTree()).toMatchInlineSnapshot(` + { + "basic.test.ts": { + "__module_errors__": [ + "Obsolete snapshots found when no snapshot update is expected. + · foobar 1 + ", + ], + "bar": "passed", + "foo": "skipped", + }, + } + `) +}) -- 2.51.2 From 41c9f6516af58e6ca825d62cac63060b0c0a4e3e Mon Sep 17 00:00:00 2001 From: James D Bloom <733179+jamesdbloom@users.noreply.github.com> Date: Wed, 9 Sep 2026 09:57:20 +0200 Subject: [PATCH 02/12] docs: update the benchmarking example to the Vitest 5 bench API (#11204) Co-authored-by: Vladimir --- docs/guide/features.md | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/docs/guide/features.md b/docs/guide/features.md index 3c73a69cc..f484995c8 100644 --- a/docs/guide/features.md +++ b/docs/guide/features.md @@ -194,30 +194,33 @@ if (import.meta.vitest) { Learn more at [In-source testing](/guide/in-source). -## Benchmarking {#benchmarking} +## Benchmarking {#benchmarking} -You can run benchmark tests with [`bench`](/api/test#bench) function via [Tinybench](https://github.com/tinylibs/tinybench) to compare performance results. +You can run benchmark tests with the [`bench`](/api/test#bench) fixture from the [test context](/guide/test-context#bench) via [Tinybench](https://github.com/tinylibs/tinybench) to compare performance results. ```ts [sort.bench.ts] -import { bench, describe } from 'vitest' - -describe('sort', () => { - bench('normal', () => { - const x = [1, 5, 4, 2, 3] - x.sort((a, b) => { - return a - b - }) - }) - - bench('reverse', () => { - const x = [1, 5, 4, 2, 3] - x.reverse().sort((a, b) => { - return a - b - }) - }) +import { test } from 'vitest' + +test('sort', async ({ bench }) => { + await bench.compare( + bench('normal', () => { + const x = [1, 5, 4, 2, 3] + x.sort((a, b) => { + return a - b + }) + }), + bench('reverse', () => { + const x = [1, 5, 4, 2, 3] + x.reverse().sort((a, b) => { + return a - b + }) + }), + ) }) ``` +Learn more at [Benchmarking](/guide/benchmarking). + Benchmark report Benchmark report -- 2.51.2 From 376dc3bc1c7ac099a7634b55c2e8f90cd6ec02a9 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 9 Sep 2026 16:58:23 +0900 Subject: [PATCH 03/12] feat(ui): add focused trace view layout mode (#11190) Co-authored-by: Hiroshi Ogawa <4232207+hi-ogawa@users.noreply.github.com> Co-authored-by: OpenCode (gpt-5.6-sol) Co-authored-by: Codex --- .../client/components/trace/TraceViewPane.vue | 41 ++++++++++++- packages/ui/client/composables/navigation.ts | 2 +- packages/ui/client/composables/params.ts | 6 +- packages/ui/client/pages/index.vue | 11 +++- test/ui/test/trace.spec.ts | 60 +++++++++++++++++++ 5 files changed, 113 insertions(+), 7 deletions(-) diff --git a/packages/ui/client/components/trace/TraceViewPane.vue b/packages/ui/client/components/trace/TraceViewPane.vue index df6e9d2a1..2ab407253 100644 --- a/packages/ui/client/components/trace/TraceViewPane.vue +++ b/packages/ui/client/components/trace/TraceViewPane.vue @@ -2,7 +2,9 @@ import type { TraceSelection } from '~/composables/trace-view' import { computed } from 'vue' import IconButton from '~/components/IconButton.vue' +import { layoutMode, params } from '~/composables/params' import { closeTrace, getSelectedTrace, getTraceAttemptLabel, getTraceAttemptMap, selectActiveTraceAttempt, showTraceSelectorHighlight } from '~/composables/trace-view' +import { getNames } from '../../../../vitest/src/utils/tasks.ts' import TraceView from './TraceView.vue' const props = defineProps<{ @@ -19,13 +21,34 @@ const selectedAttemptKey = computed({ get: () => props.selection.attemptKey ?? '0:0', set: selectActiveTraceAttempt, }) +const ancestorNames = computed(() => getNames(props.selection.test).slice(0, -1).join(' > ')) +const traceLayoutPageUrl = computed(() => { + const url = new URL(globalThis.location.href) + const focusedParams = new URLSearchParams() + for (const [key, value] of Object.entries(params)) { + if (value != null) { + focusedParams.set(key, String(value)) + } + } + focusedParams.set('layout', 'trace') + url.hash = `/?${focusedParams}` + return url.href +})