[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
12

Configure Feed

Select the types of activity you want to include in your feed.

feat(browser): support custom `kind` in `page.mark` (#10302)

authored by

Ari Perkkiö and committed by
GitHub
(May 11, 2026, 5:42 PM +0200) 053e8b0d ea55eb36

+108 -12
+8
packages/browser/context.d.ts
··· 42 42 save?: boolean 43 43 } 44 44 45 + export type BrowserTraceEntryKind = 'action' | 'expect' | 'mark' | 'lifecycle' 46 + 45 47 export interface MarkOptions { 46 48 /** 47 49 * Optional stack string used to resolve marker location. 48 50 * Useful for wrapper libraries that need to forward the end-user callsite. 49 51 */ 50 52 stack?: string 53 + 54 + /** 55 + * Optional marker kind that's used to categorize the marker in the trace viewer. 56 + * @default 'mark' 57 + */ 58 + kind?: BrowserTraceEntryKind 51 59 } 52 60 53 61 interface StandardScreenshotComparators {
+7 -5
docs/api/browser/context.md
··· 82 82 /** 83 83 * Add a trace marker when browser tracing is enabled. 84 84 */ 85 - mark(name: string, options?: { stack?: string }): Promise<void> 85 + mark(name: string, options?: { stack?: string; kind?: BrowserTraceEntryKind }): Promise<void> 86 86 /** 87 87 * Group multiple operations under a trace marker when browser tracing is enabled. 88 88 */ 89 - mark<T>(name: string, body: () => T | Promise<T>, options?: { stack?: string }): Promise<T> 89 + mark<T>(name: string, body: () => T | Promise<T>, options?: { stack?: string; kind?: BrowserTraceEntryKind }): Promise<T> 90 90 /** 91 91 * Extend default `page` object with custom methods. 92 92 */ ··· 127 127 ### mark 128 128 129 129 ```ts 130 - function mark(name: string, options?: { stack?: string }): Promise<void> 130 + function mark(name: string, options?: { stack?: string; kind?: BrowserTraceEntryKind }): Promise<void> 131 131 function mark<T>( 132 132 name: string, 133 133 body: () => T | Promise<T>, 134 - options?: { stack?: string }, 134 + options?: { stack?: string; kind?: BrowserTraceEntryKind }, 135 135 ): Promise<T> 136 136 ``` 137 137 138 138 Adds a named marker to the trace timeline for the current test. 139 139 140 140 Pass `options.stack` to override the callsite location in trace metadata. This is useful for wrapper libraries that need to preserve the end-user source location. 141 + 142 + Pass `options.kind` to categorize your marker as specific type, for example as `'action'`. 141 143 142 144 If you pass a callback, Vitest creates a trace group with this name, runs the callback, and closes the group automatically. 143 145 ··· 151 153 await page.mark('submit flow', async () => { 152 154 await page.getByRole('textbox', { name: 'Email' }).fill('john@example.com') 153 155 await page.getByRole('button', { name: 'Submit' }).click() 154 - }) 156 + }, { kind: 'action' }) 155 157 ``` 156 158 157 159 ::: tip
+3 -1
docs/api/browser/locators.md
··· 846 846 ### mark 847 847 848 848 ```ts 849 - function mark(name: string, options?: { stack?: string }): Promise<void> 849 + function mark(name: string, options?: { stack?: string; kind?: BrowserTraceEntryKind }): Promise<void> 850 850 ``` 851 851 852 852 Adds a named marker to the trace timeline and uses the current locator as marker context. 853 853 854 854 Pass `options.stack` to override the callsite location in trace metadata. This is useful for wrapper libraries that need to preserve the end-user source location. 855 + 856 + Pass `options.kind` to categorize your marker as specific type, for example as `'action'`. 855 857 856 858 ```ts 857 859 import { page } from 'vitest/browser'
+75
test/browser/specs/trace.test.ts
··· 100 100 }, 101 101 "mark.test.ts": { 102 102 "helper": "passed", 103 + "kind": "passed", 103 104 "locator.mark": "passed", 104 105 "mark function": "passed", 105 106 "page.mark": "passed", ··· 370 371 { 371 372 "kind": "lifecycle", 372 373 "location": "mark.test.ts:23", 374 + "name": "vitest:onAfterRetryTask", 375 + "snapshot": {}, 376 + "status": "pass", 377 + }, 378 + ], 379 + }, 380 + ], 381 + "kind": [ 382 + { 383 + "entries": [ 384 + { 385 + "kind": "action", 386 + "location": "mark.test.ts:42", 387 + "name": "action marker", 388 + "snapshot": {}, 389 + }, 390 + { 391 + "kind": "expect", 392 + "location": "mark.test.ts:43", 393 + "name": "expect marker", 394 + "snapshot": {}, 395 + }, 396 + { 397 + "kind": "lifecycle", 398 + "location": "mark.test.ts:44", 399 + "name": "lifecycle group", 400 + "snapshot": {}, 401 + }, 402 + { 403 + "kind": "mark", 404 + "location": "mark.test.ts:45", 405 + "name": "lifecycle group", 406 + "snapshot": {}, 407 + }, 408 + { 409 + "kind": "lifecycle", 410 + "location": "mark.test.ts:39", 373 411 "name": "vitest:onAfterRetryTask", 374 412 "snapshot": {}, 375 413 "status": "pass", ··· 1423 1461 { 1424 1462 "kind": "lifecycle", 1425 1463 "location": "mark.test.ts:23", 1464 + "name": "vitest:onAfterRetryTask", 1465 + "snapshot": {}, 1466 + "status": "pass", 1467 + }, 1468 + ], 1469 + }, 1470 + ], 1471 + "kind": [ 1472 + { 1473 + "entries": [ 1474 + { 1475 + "kind": "action", 1476 + "location": "mark.test.ts:42", 1477 + "name": "action marker", 1478 + "snapshot": {}, 1479 + }, 1480 + { 1481 + "kind": "expect", 1482 + "location": "mark.test.ts:43", 1483 + "name": "expect marker", 1484 + "snapshot": {}, 1485 + }, 1486 + { 1487 + "kind": "lifecycle", 1488 + "location": "mark.test.ts:44", 1489 + "name": "lifecycle group", 1490 + "snapshot": {}, 1491 + }, 1492 + { 1493 + "kind": "mark", 1494 + "location": "mark.test.ts:45", 1495 + "name": "lifecycle group", 1496 + "snapshot": {}, 1497 + }, 1498 + { 1499 + "kind": "lifecycle", 1500 + "location": "mark.test.ts:39", 1426 1501 "name": "vitest:onAfterRetryTask", 1427 1502 "snapshot": {}, 1428 1503 "status": "pass",
+9
test/browser/fixtures/trace/mark.test.ts
··· 35 35 document.body.innerHTML = '<button>Hello</button>' 36 36 }) 37 37 }) 38 + 39 + test('kind', async () => { 40 + document.body.innerHTML = '<button>Hello</button>' 41 + 42 + await page.mark('action marker', { kind: 'action' }) 43 + await page.mark('expect marker', { kind: 'expect' }) 44 + await page.mark('lifecycle group', { kind: 'lifecycle' }) 45 + await page.mark('lifecycle group', { kind: 'mark' }) 46 + })
+2 -2
packages/browser/src/client/tester/context.ts
··· 391 391 // TODO: support nested trace 392 392 recordBrowserTraceEntry(currentTest, { 393 393 name, 394 - kind: 'mark', 394 + kind: options?.kind ?? 'mark', 395 395 status, 396 396 startTime, 397 397 duration: now() - startTime, ··· 413 413 if (hasActiveTraceView) { 414 414 recordBrowserTraceEntry(currentTest, { 415 415 name, 416 - kind: 'mark', 416 + kind: bodyOrOptions?.kind ?? 'mark', 417 417 stack: bodyOrOptions?.stack ?? error?.stack, 418 418 }) 419 419 }
+1 -1
packages/browser/src/client/tester/locators.ts
··· 222 222 if (hasActiveTraceView) { 223 223 recordBrowserTraceEntry(currentTest, { 224 224 name, 225 - kind: 'mark', 225 + kind: options?.kind ?? 'mark', 226 226 element: this.serialize(), 227 227 stack: options?.stack ?? error?.stack, 228 228 })
+1 -1
packages/browser/src/client/tester/trace.ts
··· 1 1 import type { Task } from '@vitest/runner' 2 + import type { BrowserTraceEntryKind } from 'vitest/browser' 2 3 import type { SerializedLocator } from './locators' 3 4 import { getBrowserState, now } from '../utils' 4 5 ··· 9 10 entries: BrowserTraceEntry[] 10 11 } 11 12 12 - export type BrowserTraceEntryKind = 'action' | 'expect' | 'mark' | 'lifecycle' 13 13 export type BrowserTraceEntryStatus = 'pass' | 'fail' 14 14 export type BrowserTraceSelectorResolution = 'matched' | 'missing' | 'error' 15 15
+2 -2
packages/ui/client/components/trace/TraceView.vue
··· 122 122 } 123 123 124 124 function formatStepName(step: BrowserTraceEntry) { 125 - if (step.kind === 'lifecycle' && step.name === 'vitest:onAfterRetryTask') { 125 + if (step.name === 'vitest:onAfterRetryTask') { 126 126 return 'test finished' 127 127 } 128 - if (step.kind === 'action' && step.name.startsWith('vitest:')) { 128 + if (step.name.startsWith('vitest:')) { 129 129 return step.name.slice('vitest:'.length) 130 130 } 131 131 return step.name