@rstest/browser 0.11.5 → 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 (86) 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/browserExecutor.d.ts +7 -5
  6. package/dist/browserRsbuild.d.ts +139 -0
  7. package/dist/browserV8Coverage.d.ts +15 -0
  8. package/dist/client/240~1.js +150 -0
  9. package/dist/{augmentExpect.d.ts → client/client/augmentExpect.d.ts} +1 -1
  10. package/dist/client/{browserRpc.d.ts → client/browserRpc.d.ts} +1 -1
  11. package/dist/client/{dispatchTransport.d.ts → client/dispatchTransport.d.ts} +8 -4
  12. package/dist/client/client/index.d.ts +2 -0
  13. package/dist/client/client/runIdentity.d.ts +12 -0
  14. package/dist/client/client/runner.d.ts +4 -0
  15. package/dist/client/client/snapshot.d.ts +27 -0
  16. package/dist/client/client/sourceMapSupport.d.ts +36 -0
  17. package/dist/{browser.js → client/index.js} +3 -102
  18. package/dist/client/protocol.d.ts +225 -0
  19. package/dist/client/rpcProtocol.d.ts +138 -0
  20. package/dist/client/runner.js +675 -0
  21. package/dist/client/sourceMap/sourceMapLoader.d.ts +25 -0
  22. package/dist/containerRpc.d.ts +60 -0
  23. package/dist/dispatchCapabilities.d.ts +1 -2
  24. package/dist/headedRunRegistry.d.ts +51 -0
  25. package/dist/headedScheduler.d.ts +58 -0
  26. package/dist/headlessScheduler.d.ts +38 -0
  27. package/dist/headlessTransport.d.ts +4 -2
  28. package/dist/hostController.d.ts +12 -47
  29. package/dist/hostPayloads.d.ts +24 -0
  30. package/dist/index.js +2019 -1516
  31. package/dist/protocol.d.ts +48 -8
  32. package/dist/providers/index.d.ts +21 -0
  33. package/dist/providers/playwright/v8Coverage.d.ts +2 -0
  34. package/dist/rpcProtocol.d.ts +1 -2
  35. package/dist/schedulerSeam.d.ts +42 -0
  36. package/dist/sourceMap/sourceMapLoader.d.ts +12 -1
  37. package/dist/watchRerunPlanner.d.ts +20 -4
  38. package/dist/watchRuntime.d.ts +21 -0
  39. package/dist/watchSignals.d.ts +29 -0
  40. package/package.json +12 -12
  41. package/dist/33.js +0 -7
  42. package/dist/browser-container/container-static/css/index.0521ff95b1.css +0 -1
  43. package/dist/browser-container/container-static/js/506.b2f920fa67.js +0 -194
  44. package/dist/browser-container/container-static/js/index.d27fd63f5e.js +0 -1
  45. package/dist/browser.d.ts +0 -2
  46. package/dist/headlessLatestRerunScheduler.d.ts +0 -18
  47. package/src/augmentExpect.ts +0 -62
  48. package/src/browser.ts +0 -3
  49. package/src/browserExecutor.ts +0 -140
  50. package/src/browserRpcRegistry.ts +0 -57
  51. package/src/client/api.ts +0 -213
  52. package/src/client/browserRpc.ts +0 -86
  53. package/src/client/dispatchTransport.ts +0 -240
  54. package/src/client/entry.ts +0 -807
  55. package/src/client/locator.ts +0 -448
  56. package/src/client/snapshot.ts +0 -113
  57. package/src/client/sourceMapSupport.ts +0 -159
  58. package/src/concurrency.ts +0 -65
  59. package/src/configValidation.ts +0 -263
  60. package/src/dispatchCapabilities.ts +0 -182
  61. package/src/dispatchRouter.ts +0 -82
  62. package/src/env.d.ts +0 -54
  63. package/src/headedSerialTaskQueue.ts +0 -23
  64. package/src/headlessLatestRerunScheduler.ts +0 -76
  65. package/src/headlessTransport.ts +0 -28
  66. package/src/hostController.ts +0 -4633
  67. package/src/index.ts +0 -51
  68. package/src/manifest.d.ts +0 -41
  69. package/src/protocol.ts +0 -225
  70. package/src/providers/index.ts +0 -126
  71. package/src/providers/playwright/compileLocator.ts +0 -130
  72. package/src/providers/playwright/dispatchBrowserRpc.ts +0 -372
  73. package/src/providers/playwright/expectUtils.ts +0 -57
  74. package/src/providers/playwright/implementation.ts +0 -35
  75. package/src/providers/playwright/index.ts +0 -1
  76. package/src/providers/playwright/runtime.ts +0 -159
  77. package/src/providers/playwright/textMatcher.ts +0 -10
  78. package/src/rpcProtocol.ts +0 -218
  79. package/src/runSession.ts +0 -110
  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 -77
  84. /package/dist/{rslib-runtime.js → 612~0.js} +0 -0
  85. /package/dist/client/{api.d.ts → client/api.d.ts} +0 -0
  86. /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
- };