@rstest/browser 0.11.6 → 0.11.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/browser-container/container-static/css/index.43daaf6c93.css +1 -0
  2. package/dist/browser-container/container-static/js/260.3739fe815c.js +194 -0
  3. package/dist/browser-container/container-static/js/index.c0f87bfb3e.js +1 -0
  4. package/dist/browser-container/index.html +1 -1
  5. package/dist/browserRsbuild.d.ts +24 -4
  6. package/dist/browserV8Coverage.d.ts +15 -0
  7. package/dist/client/240~1.js +150 -0
  8. package/dist/{augmentExpect.d.ts → client/client/augmentExpect.d.ts} +1 -1
  9. package/dist/client/{browserRpc.d.ts → client/browserRpc.d.ts} +1 -1
  10. package/dist/client/{dispatchTransport.d.ts → client/dispatchTransport.d.ts} +8 -4
  11. package/dist/client/client/index.d.ts +2 -0
  12. package/dist/client/client/runIdentity.d.ts +12 -0
  13. package/dist/client/client/runner.d.ts +4 -0
  14. package/dist/client/client/snapshot.d.ts +27 -0
  15. package/dist/client/client/sourceMapSupport.d.ts +36 -0
  16. package/dist/{browser.js → client/index.js} +3 -102
  17. package/dist/client/protocol.d.ts +225 -0
  18. package/dist/client/rpcProtocol.d.ts +138 -0
  19. package/dist/client/runner.js +675 -0
  20. package/dist/client/sourceMap/sourceMapLoader.d.ts +25 -0
  21. package/dist/containerRpc.d.ts +27 -19
  22. package/dist/headedRunRegistry.d.ts +51 -0
  23. package/dist/headedScheduler.d.ts +10 -10
  24. package/dist/headlessScheduler.d.ts +5 -4
  25. package/dist/headlessTransport.d.ts +4 -2
  26. package/dist/hostPayloads.d.ts +1 -7
  27. package/dist/index.js +840 -330
  28. package/dist/protocol.d.ts +48 -8
  29. package/dist/providers/index.d.ts +21 -0
  30. package/dist/providers/playwright/v8Coverage.d.ts +2 -0
  31. package/dist/rpcProtocol.d.ts +1 -2
  32. package/dist/schedulerSeam.d.ts +5 -0
  33. package/dist/sourceMap/sourceMapLoader.d.ts +12 -1
  34. package/dist/watchRerunPlanner.d.ts +20 -9
  35. package/dist/watchSignals.d.ts +8 -1
  36. package/package.json +10 -10
  37. package/dist/33.js +0 -7
  38. package/dist/browser-container/container-static/css/index.0521ff95b1.css +0 -1
  39. package/dist/browser-container/container-static/js/683.4f821ce8f1.js +0 -194
  40. package/dist/browser-container/container-static/js/index.568aa7cb35.js +0 -1
  41. package/dist/browser.d.ts +0 -2
  42. package/src/augmentExpect.ts +0 -62
  43. package/src/browser.ts +0 -3
  44. package/src/browserExecutor.ts +0 -222
  45. package/src/browserRpcRegistry.ts +0 -57
  46. package/src/browserRsbuild.ts +0 -1927
  47. package/src/client/api.ts +0 -213
  48. package/src/client/browserRpc.ts +0 -86
  49. package/src/client/dispatchTransport.ts +0 -240
  50. package/src/client/entry.ts +0 -807
  51. package/src/client/locator.ts +0 -448
  52. package/src/client/snapshot.ts +0 -113
  53. package/src/client/sourceMapSupport.ts +0 -159
  54. package/src/concurrency.ts +0 -65
  55. package/src/configValidation.ts +0 -263
  56. package/src/containerRpc.ts +0 -206
  57. package/src/dispatchCapabilities.ts +0 -177
  58. package/src/dispatchRouter.ts +0 -82
  59. package/src/env.d.ts +0 -54
  60. package/src/headedScheduler.ts +0 -664
  61. package/src/headedSerialTaskQueue.ts +0 -23
  62. package/src/headlessScheduler.ts +0 -566
  63. package/src/headlessTransport.ts +0 -28
  64. package/src/hostController.ts +0 -1365
  65. package/src/hostPayloads.ts +0 -62
  66. package/src/index.ts +0 -51
  67. package/src/manifest.d.ts +0 -41
  68. package/src/protocol.ts +0 -225
  69. package/src/providers/index.ts +0 -126
  70. package/src/providers/playwright/compileLocator.ts +0 -130
  71. package/src/providers/playwright/dispatchBrowserRpc.ts +0 -372
  72. package/src/providers/playwright/expectUtils.ts +0 -57
  73. package/src/providers/playwright/implementation.ts +0 -35
  74. package/src/providers/playwright/index.ts +0 -1
  75. package/src/providers/playwright/runtime.ts +0 -159
  76. package/src/providers/playwright/textMatcher.ts +0 -10
  77. package/src/rpcProtocol.ts +0 -218
  78. package/src/runSession.ts +0 -110
  79. package/src/schedulerSeam.ts +0 -49
  80. package/src/sessionRegistry.ts +0 -89
  81. package/src/sourceMap/sourceMapLoader.ts +0 -96
  82. package/src/viewportPresets.ts +0 -69
  83. package/src/watchRerunPlanner.ts +0 -88
  84. package/src/watchRuntime.ts +0 -83
  85. package/src/watchSignals.ts +0 -93
  86. /package/dist/{rslib-runtime.js → 612~0.js} +0 -0
  87. /package/dist/client/{api.d.ts → client/api.d.ts} +0 -0
  88. /package/dist/client/{locator.d.ts → client/locator.d.ts} +0 -0
@@ -1,448 +0,0 @@
1
- import type {
2
- BrowserLocatorIR,
3
- BrowserLocatorText,
4
- BrowserRpcRequest,
5
- } from '../rpcProtocol';
6
- import { callBrowserRpc } from './browserRpc';
7
-
8
- export const serializeText = (value: string | RegExp): BrowserLocatorText => {
9
- if (typeof value === 'string') {
10
- return { type: 'string', value };
11
- }
12
- return { type: 'regexp', source: value.source, flags: value.flags };
13
- };
14
-
15
- export type LocatorGetByRoleOptions = {
16
- name?: string | RegExp;
17
- exact?: boolean;
18
- checked?: boolean;
19
- disabled?: boolean;
20
- expanded?: boolean;
21
- selected?: boolean;
22
- pressed?: boolean;
23
- includeHidden?: boolean;
24
- level?: number;
25
- };
26
-
27
- export type LocatorTextOptions = {
28
- exact?: boolean;
29
- };
30
-
31
- export type LocatorKeyboardModifier =
32
- 'Alt' | 'Control' | 'ControlOrMeta' | 'Meta' | 'Shift';
33
-
34
- export type LocatorMouseButton = 'left' | 'right' | 'middle';
35
-
36
- export type LocatorPosition = {
37
- x: number;
38
- y: number;
39
- };
40
-
41
- export type LocatorClickOptions = {
42
- button?: LocatorMouseButton;
43
- clickCount?: number;
44
- delay?: number;
45
- force?: boolean;
46
- modifiers?: LocatorKeyboardModifier[];
47
- position?: LocatorPosition;
48
- timeout?: number;
49
- trial?: boolean;
50
- };
51
-
52
- export type LocatorDblclickOptions = Omit<LocatorClickOptions, 'clickCount'>;
53
-
54
- export type LocatorHoverOptions = Pick<
55
- LocatorClickOptions,
56
- 'force' | 'modifiers' | 'position' | 'timeout' | 'trial'
57
- >;
58
-
59
- export type LocatorPressOptions = {
60
- delay?: number;
61
- timeout?: number;
62
- };
63
-
64
- export type LocatorFillOptions = {
65
- force?: boolean;
66
- timeout?: number;
67
- };
68
-
69
- export type LocatorCheckOptions = {
70
- force?: boolean;
71
- position?: LocatorPosition;
72
- timeout?: number;
73
- trial?: boolean;
74
- };
75
-
76
- export type LocatorFocusOptions = {
77
- timeout?: number;
78
- };
79
-
80
- export type LocatorBlurOptions = {
81
- timeout?: number;
82
- };
83
-
84
- export type LocatorScrollIntoViewIfNeededOptions = {
85
- timeout?: number;
86
- };
87
-
88
- export type LocatorWaitForOptions = {
89
- state?: 'attached' | 'detached' | 'visible' | 'hidden';
90
- timeout?: number;
91
- };
92
-
93
- export type BrowserSerializable =
94
- | null
95
- | boolean
96
- | number
97
- | string
98
- | BrowserSerializable[]
99
- | { [key: string]: BrowserSerializable };
100
-
101
- export type LocatorDispatchEventInit = BrowserSerializable;
102
-
103
- export type LocatorSelectOptionOptions = {
104
- force?: boolean;
105
- timeout?: number;
106
- };
107
-
108
- export type LocatorSetInputFilesOptions = {
109
- timeout?: number;
110
- };
111
-
112
- export type LocatorFilterOptions = {
113
- hasText?: string | RegExp;
114
- hasNotText?: string | RegExp;
115
- has?: Locator;
116
- hasNot?: Locator;
117
- };
118
-
119
- export class Locator {
120
- readonly ir: BrowserLocatorIR;
121
-
122
- constructor(ir: BrowserLocatorIR) {
123
- this.ir = ir;
124
- }
125
-
126
- getByRole(role: string, options?: LocatorGetByRoleOptions): Locator {
127
- const next = {
128
- steps: [
129
- ...this.ir.steps,
130
- {
131
- type: 'getByRole',
132
- role,
133
- options: options
134
- ? {
135
- ...options,
136
- name:
137
- options.name === undefined
138
- ? undefined
139
- : serializeText(options.name),
140
- }
141
- : undefined,
142
- },
143
- ],
144
- } satisfies BrowserLocatorIR;
145
- return new Locator(next);
146
- }
147
-
148
- locator(selector: string): Locator {
149
- return new Locator({
150
- steps: [...this.ir.steps, { type: 'locator', selector }],
151
- });
152
- }
153
-
154
- getByText(text: string | RegExp, options?: LocatorTextOptions): Locator {
155
- return new Locator({
156
- steps: [
157
- ...this.ir.steps,
158
- { type: 'getByText', text: serializeText(text), options },
159
- ],
160
- });
161
- }
162
-
163
- getByLabel(text: string | RegExp, options?: LocatorTextOptions): Locator {
164
- return new Locator({
165
- steps: [
166
- ...this.ir.steps,
167
- { type: 'getByLabel', text: serializeText(text), options },
168
- ],
169
- });
170
- }
171
-
172
- getByPlaceholder(
173
- text: string | RegExp,
174
- options?: LocatorTextOptions,
175
- ): Locator {
176
- return new Locator({
177
- steps: [
178
- ...this.ir.steps,
179
- { type: 'getByPlaceholder', text: serializeText(text), options },
180
- ],
181
- });
182
- }
183
-
184
- getByAltText(text: string | RegExp, options?: LocatorTextOptions): Locator {
185
- return new Locator({
186
- steps: [
187
- ...this.ir.steps,
188
- { type: 'getByAltText', text: serializeText(text), options },
189
- ],
190
- });
191
- }
192
-
193
- getByTitle(text: string | RegExp, options?: LocatorTextOptions): Locator {
194
- return new Locator({
195
- steps: [
196
- ...this.ir.steps,
197
- { type: 'getByTitle', text: serializeText(text), options },
198
- ],
199
- });
200
- }
201
-
202
- getByTestId(text: string | RegExp): Locator {
203
- return new Locator({
204
- steps: [
205
- ...this.ir.steps,
206
- { type: 'getByTestId', text: serializeText(text) },
207
- ],
208
- });
209
- }
210
-
211
- filter(options: LocatorFilterOptions): Locator {
212
- return new Locator({
213
- steps: [
214
- ...this.ir.steps,
215
- {
216
- type: 'filter',
217
- options: {
218
- hasText: options.hasText
219
- ? serializeText(options.hasText)
220
- : undefined,
221
- hasNotText: options.hasNotText
222
- ? serializeText(options.hasNotText)
223
- : undefined,
224
- has:
225
- options.has === undefined
226
- ? undefined
227
- : isLocator(options.has)
228
- ? options.has.ir
229
- : (() => {
230
- throw new TypeError(
231
- 'Locator.filter({ has }) expects a Locator returned from @rstest/browser page.getBy* APIs.',
232
- );
233
- })(),
234
- hasNot:
235
- options.hasNot === undefined
236
- ? undefined
237
- : isLocator(options.hasNot)
238
- ? options.hasNot.ir
239
- : (() => {
240
- throw new TypeError(
241
- 'Locator.filter({ hasNot }) expects a Locator returned from @rstest/browser page.getBy* APIs.',
242
- );
243
- })(),
244
- },
245
- },
246
- ],
247
- });
248
- }
249
-
250
- and(other: Locator): Locator {
251
- if (!isLocator(other)) {
252
- throw new TypeError(
253
- 'Locator.and() expects a Locator returned from @rstest/browser page.getBy* APIs.',
254
- );
255
- }
256
- return new Locator({
257
- steps: [...this.ir.steps, { type: 'and', locator: other.ir }],
258
- });
259
- }
260
-
261
- or(other: Locator): Locator {
262
- if (!isLocator(other)) {
263
- throw new TypeError(
264
- 'Locator.or() expects a Locator returned from @rstest/browser page.getBy* APIs.',
265
- );
266
- }
267
- return new Locator({
268
- steps: [...this.ir.steps, { type: 'or', locator: other.ir }],
269
- });
270
- }
271
-
272
- nth(index: number): Locator {
273
- return new Locator({ steps: [...this.ir.steps, { type: 'nth', index }] });
274
- }
275
-
276
- first(): Locator {
277
- return new Locator({ steps: [...this.ir.steps, { type: 'first' }] });
278
- }
279
-
280
- last(): Locator {
281
- return new Locator({ steps: [...this.ir.steps, { type: 'last' }] });
282
- }
283
-
284
- async click(options?: LocatorClickOptions): Promise<void> {
285
- await this.callLocator('click', options === undefined ? [] : [options]);
286
- }
287
-
288
- async dblclick(options?: LocatorDblclickOptions): Promise<void> {
289
- await this.callLocator('dblclick', options === undefined ? [] : [options]);
290
- }
291
-
292
- async fill(value: string, options?: LocatorFillOptions): Promise<void> {
293
- await this.callLocator(
294
- 'fill',
295
- options === undefined ? [value] : [value, options],
296
- );
297
- }
298
-
299
- async hover(options?: LocatorHoverOptions): Promise<void> {
300
- await this.callLocator('hover', options === undefined ? [] : [options]);
301
- }
302
-
303
- async press(key: string, options?: LocatorPressOptions): Promise<void> {
304
- await this.callLocator(
305
- 'press',
306
- options === undefined ? [key] : [key, options],
307
- );
308
- }
309
-
310
- async clear(): Promise<void> {
311
- await this.callLocator('clear', []);
312
- }
313
-
314
- async check(options?: LocatorCheckOptions): Promise<void> {
315
- await this.callLocator('check', options === undefined ? [] : [options]);
316
- }
317
-
318
- async uncheck(options?: LocatorCheckOptions): Promise<void> {
319
- await this.callLocator('uncheck', options === undefined ? [] : [options]);
320
- }
321
-
322
- async focus(options?: LocatorFocusOptions): Promise<void> {
323
- await this.callLocator('focus', options === undefined ? [] : [options]);
324
- }
325
-
326
- async blur(options?: LocatorBlurOptions): Promise<void> {
327
- await this.callLocator('blur', options === undefined ? [] : [options]);
328
- }
329
-
330
- async scrollIntoViewIfNeeded(
331
- options?: LocatorScrollIntoViewIfNeededOptions,
332
- ): Promise<void> {
333
- await this.callLocator(
334
- 'scrollIntoViewIfNeeded',
335
- options === undefined ? [] : [options],
336
- );
337
- }
338
-
339
- async waitFor(options?: LocatorWaitForOptions): Promise<void> {
340
- await this.callLocator('waitFor', options === undefined ? [] : [options]);
341
- }
342
-
343
- async dispatchEvent(
344
- type: string,
345
- eventInit?: LocatorDispatchEventInit,
346
- ): Promise<void> {
347
- if (typeof type !== 'string' || !type) {
348
- throw new TypeError(
349
- 'Locator.dispatchEvent() expects a non-empty event type string.',
350
- );
351
- }
352
- await this.callLocator(
353
- 'dispatchEvent',
354
- eventInit === undefined ? [type] : [type, eventInit],
355
- );
356
- }
357
-
358
- async selectOption(
359
- value: string | string[],
360
- options?: LocatorSelectOptionOptions,
361
- ): Promise<void> {
362
- if (
363
- typeof value !== 'string' &&
364
- !(Array.isArray(value) && value.every((v) => typeof v === 'string'))
365
- ) {
366
- throw new TypeError(
367
- 'Locator.selectOption() only supports string or string[] values in browser mode.',
368
- );
369
- }
370
- await this.callLocator(
371
- 'selectOption',
372
- options === undefined ? [value] : [value, options],
373
- );
374
- }
375
-
376
- async setInputFiles(
377
- files: string | string[],
378
- options?: LocatorSetInputFilesOptions,
379
- ): Promise<void> {
380
- if (
381
- typeof files !== 'string' &&
382
- !(Array.isArray(files) && files.every((v) => typeof v === 'string'))
383
- ) {
384
- throw new TypeError(
385
- 'Locator.setInputFiles() only supports file path string or string[] in browser mode.',
386
- );
387
- }
388
- await this.callLocator(
389
- 'setInputFiles',
390
- options === undefined ? [files] : [files, options],
391
- );
392
- }
393
-
394
- private async callLocator(method: string, args: unknown[]): Promise<void> {
395
- await callBrowserRpc<void>({
396
- kind: 'locator',
397
- locator: this.ir,
398
- method,
399
- args,
400
- } satisfies Omit<BrowserRpcRequest, 'id' | 'testPath' | 'runId'>);
401
- }
402
- }
403
-
404
- const browserPageQueryMethods = [
405
- 'locator',
406
- 'getByRole',
407
- 'getByText',
408
- 'getByLabel',
409
- 'getByPlaceholder',
410
- 'getByAltText',
411
- 'getByTitle',
412
- 'getByTestId',
413
- ] as const;
414
-
415
- type BrowserPageQueryMethod = (typeof browserPageQueryMethods)[number];
416
-
417
- export type BrowserPage = Pick<Locator, BrowserPageQueryMethod>;
418
-
419
- const rootLocator = new Locator({ steps: [] });
420
-
421
- const createBrowserPage = (): BrowserPage => {
422
- return Object.fromEntries(
423
- browserPageQueryMethods.map((methodName) => {
424
- return [methodName, rootLocator[methodName].bind(rootLocator)];
425
- }),
426
- ) as BrowserPage;
427
- };
428
-
429
- export const page: BrowserPage = createBrowserPage();
430
-
431
- export const isLocator = (value: unknown): value is Locator => {
432
- return value instanceof Locator;
433
- };
434
-
435
- /**
436
- * Configure the attribute used by `getByTestId()` queries.
437
- * Forwards to the host provider (e.g. Playwright `selectors.setTestIdAttribute()`).
438
- *
439
- * @default 'data-testid'
440
- */
441
- export const setTestIdAttribute = async (attribute: string): Promise<void> => {
442
- await callBrowserRpc<void>({
443
- kind: 'config',
444
- locator: { steps: [] },
445
- method: 'setTestIdAttribute',
446
- args: [attribute],
447
- } satisfies Omit<BrowserRpcRequest, 'id' | 'testPath' | 'runId'>);
448
- };
@@ -1,113 +0,0 @@
1
- import type { BrowserDispatchRequest, SnapshotRpcCall } from '../protocol';
2
- import { DISPATCH_NAMESPACE_SNAPSHOT } from '../protocol';
3
- import {
4
- createRequestId,
5
- dispatchRpc,
6
- getRpcTimeout,
7
- } from './dispatchTransport';
8
- import { SNAPSHOT_HEADER } from '@rstest/core/internal/browser-runtime';
9
- import { mapStackFrame } from './sourceMapSupport';
10
-
11
- const createSnapshotDispatchRequest = (
12
- requestId: string,
13
- call: SnapshotRpcCall,
14
- ): BrowserDispatchRequest => {
15
- // Snapshot is just one namespace on the shared dispatch RPC channel.
16
- // Keep this mapping explicit so new runner-side RPC clients can mirror it.
17
- return {
18
- requestId,
19
- namespace: DISPATCH_NAMESPACE_SNAPSHOT,
20
- method: call.method,
21
- args: call.args,
22
- };
23
- };
24
-
25
- /**
26
- * Send a snapshot RPC request to the container (parent window).
27
- * The container will forward it to the host via WebSocket RPC.
28
- *
29
- * `call` is a {@link SnapshotRpcCall}, so `method` and `args` are validated as a
30
- * pair — a mismatched argument shape fails to compile at the call site.
31
- */
32
- const sendRpcRequest = <T>(call: SnapshotRpcCall): Promise<T> => {
33
- const requestId = createRequestId('snapshot-rpc');
34
- const rpcTimeout = getRpcTimeout();
35
- const dispatchRequest = createSnapshotDispatchRequest(requestId, call);
36
-
37
- return dispatchRpc<T>({
38
- requestId,
39
- request: dispatchRequest,
40
- timeoutMs: rpcTimeout,
41
- staleMessage: 'Stale snapshot RPC request ignored.',
42
- timeoutMessage: `Snapshot RPC timeout after ${rpcTimeout / 1000}s: ${call.method}`,
43
- });
44
- };
45
-
46
- /**
47
- * Browser snapshot environment that proxies file operations to the host
48
- * via postMessage RPC through the container.
49
- */
50
- export class BrowserSnapshotEnvironment {
51
- getVersion(): string {
52
- return '1';
53
- }
54
-
55
- getHeader(): string {
56
- return `${SNAPSHOT_HEADER} v${this.getVersion()}`;
57
- }
58
-
59
- async resolveRawPath(_testPath: string, rawPath: string): Promise<string> {
60
- return rawPath;
61
- }
62
-
63
- async resolvePath(filepath: string): Promise<string> {
64
- return sendRpcRequest<string>({
65
- method: 'resolveSnapshotPath',
66
- args: { testPath: filepath },
67
- });
68
- }
69
-
70
- async prepareDirectory(): Promise<void> {
71
- // Directory creation is handled by saveSnapshotFile on the host
72
- }
73
-
74
- async saveSnapshotFile(filepath: string, snapshot: string): Promise<void> {
75
- await sendRpcRequest<void>({
76
- method: 'saveSnapshotFile',
77
- args: { filepath, content: snapshot },
78
- });
79
- }
80
-
81
- async readSnapshotFile(filepath: string): Promise<string | null> {
82
- return sendRpcRequest<string | null>({
83
- method: 'readSnapshotFile',
84
- args: { filepath },
85
- });
86
- }
87
-
88
- async removeSnapshotFile(filepath: string): Promise<void> {
89
- await sendRpcRequest<void>({
90
- method: 'removeSnapshotFile',
91
- args: { filepath },
92
- });
93
- }
94
-
95
- /**
96
- * Process stack trace for inline snapshots.
97
- * Maps bundled URLs back to original source file paths.
98
- */
99
- processStackTrace(stack: {
100
- file: string;
101
- line: number;
102
- column: number;
103
- method: string;
104
- }): { file: string; line: number; column: number; method: string } {
105
- const mapped = mapStackFrame(stack);
106
- return {
107
- file: mapped.file,
108
- line: mapped.line,
109
- column: mapped.column,
110
- method: mapped.method || stack.method,
111
- };
112
- }
113
- }
@@ -1,159 +0,0 @@
1
- import { originalPositionFor, TraceMap } from '@jridgewell/trace-mapping';
2
- import {
3
- loadSourceMapWithCache,
4
- normalizeJavaScriptUrl,
5
- type SourceMapPayload,
6
- } from '../sourceMap/sourceMapLoader';
7
-
8
- // Source map cache: JS URL → TraceMap
9
- const sourceMapCache = new Map<string, TraceMap | null>();
10
- const sourceMapPayloadCache = new Map<string, SourceMapPayload | null>();
11
-
12
- /**
13
- * Get TraceMap for specified URL (sync cache lookup)
14
- */
15
- const getSourceMap = (url: string): TraceMap | null => {
16
- return sourceMapCache.get(url) ?? null;
17
- };
18
-
19
- /**
20
- * Preload source map for specified JS URL.
21
- * First tries to extract inline source map from JS code,
22
- * then falls back to fetching external .map file.
23
- *
24
- * @param jsUrl - The URL of the JS file
25
- * @param force - If true, bypass cache and always fetch fresh source map
26
- */
27
- const preloadSourceMap = async (
28
- jsUrl: string,
29
- force = false,
30
- ): Promise<void> => {
31
- const normalizedUrl = normalizeJavaScriptUrl(jsUrl, {
32
- origin: window.location.origin,
33
- });
34
- if (!normalizedUrl) {
35
- return;
36
- }
37
-
38
- if (!force && sourceMapCache.has(normalizedUrl)) return;
39
-
40
- const sourceMap = await loadSourceMapWithCache({
41
- jsUrl: normalizedUrl,
42
- cache: sourceMapPayloadCache,
43
- force,
44
- origin: window.location.origin,
45
- });
46
-
47
- sourceMapCache.set(normalizedUrl, sourceMap ? new TraceMap(sourceMap) : null);
48
- };
49
-
50
- /**
51
- * Get all script URLs currently on the page.
52
- * Used to detect newly loaded chunk scripts.
53
- */
54
- export const getScriptUrls = (): Set<string> => {
55
- const scripts = document.querySelectorAll('script[src]');
56
- const urls = new Set<string>();
57
- for (const script of scripts) {
58
- const src = script.getAttribute('src');
59
- if (src) {
60
- // Normalize to full URL
61
- const fullUrl = src.startsWith('http')
62
- ? src
63
- : `${window.location.origin}${src.startsWith('/') ? '' : '/'}${src}`;
64
- urls.add(fullUrl);
65
- }
66
- }
67
- return urls;
68
- };
69
-
70
- /**
71
- * Find the newly added script URL by comparing script sets.
72
- * Returns the first new script URL found, or null if none.
73
- */
74
- export const findNewScriptUrl = (
75
- beforeUrls: Set<string>,
76
- afterUrls: Set<string>,
77
- ): string | null => {
78
- for (const url of afterUrls) {
79
- if (!beforeUrls.has(url)) {
80
- return url;
81
- }
82
- }
83
- return null;
84
- };
85
-
86
- /**
87
- * Preload source map for a test file's chunk URL.
88
- *
89
- * Always fetches fresh source map to handle file changes during watch mode.
90
- *
91
- * @param chunkUrl - The full URL of the chunk JS file
92
- */
93
- export const preloadTestFileSourceMap = async (
94
- chunkUrl: string,
95
- ): Promise<void> => {
96
- // Always force refresh to ensure we have the latest source map
97
- // This handles the case where user saves file during test execution
98
- await preloadSourceMap(chunkUrl, true);
99
- };
100
-
101
- /**
102
- * Preload source map for the runner.js file.
103
- *
104
- * This is essential for inline snapshot support because the snapshot code
105
- * runs in runner.js (which contains @rstest/core/internal/browser-runtime).
106
- * Without this, stack traces from inline snapshots cannot be mapped back
107
- * to the original source files.
108
- */
109
- export const preloadRunnerSourceMap = async (): Promise<void> => {
110
- const runnerUrl = `${window.location.origin}/static/js/runner.js`;
111
- await preloadSourceMap(runnerUrl);
112
- };
113
-
114
- /**
115
- * Stack frame interface matching @vitest/snapshot's format
116
- */
117
- interface StackFrame {
118
- file: string;
119
- line: number;
120
- column: number;
121
- method?: string;
122
- }
123
-
124
- /**
125
- * Map a stack frame from bundled URL to original source file.
126
- * This is used by BrowserSnapshotEnvironment.processStackTrace
127
- */
128
- export const mapStackFrame = (frame: StackFrame): StackFrame => {
129
- const { file, line, column } = frame;
130
-
131
- const fullUrl = normalizeJavaScriptUrl(file, {
132
- origin: window.location.origin,
133
- });
134
- if (!fullUrl) {
135
- return frame;
136
- }
137
-
138
- const traceMap = getSourceMap(fullUrl);
139
- if (!traceMap) {
140
- return frame;
141
- }
142
-
143
- const pos = originalPositionFor(traceMap, {
144
- line,
145
- column: column - 1, // source map uses 0-based column
146
- });
147
-
148
- if (pos.source && pos.line != null && pos.column != null) {
149
- return {
150
- ...frame,
151
- file: pos.source,
152
- line: pos.line,
153
- column: pos.column + 1, // convert back to 1-based
154
- method: pos.name || frame.method,
155
- };
156
- }
157
-
158
- return frame;
159
- };