@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.
- package/dist/browser-container/container-static/css/index.43daaf6c93.css +1 -0
- package/dist/browser-container/container-static/js/260.3739fe815c.js +194 -0
- package/dist/browser-container/container-static/js/index.c0f87bfb3e.js +1 -0
- package/dist/browser-container/index.html +1 -1
- package/dist/browserRsbuild.d.ts +24 -4
- package/dist/browserV8Coverage.d.ts +15 -0
- package/dist/client/240~1.js +150 -0
- package/dist/{augmentExpect.d.ts → client/client/augmentExpect.d.ts} +1 -1
- package/dist/client/{browserRpc.d.ts → client/browserRpc.d.ts} +1 -1
- package/dist/client/{dispatchTransport.d.ts → client/dispatchTransport.d.ts} +8 -4
- package/dist/client/client/index.d.ts +2 -0
- package/dist/client/client/runIdentity.d.ts +12 -0
- package/dist/client/client/runner.d.ts +4 -0
- package/dist/client/client/snapshot.d.ts +27 -0
- package/dist/client/client/sourceMapSupport.d.ts +36 -0
- package/dist/{browser.js → client/index.js} +3 -102
- package/dist/client/protocol.d.ts +225 -0
- package/dist/client/rpcProtocol.d.ts +138 -0
- package/dist/client/runner.js +675 -0
- package/dist/client/sourceMap/sourceMapLoader.d.ts +25 -0
- package/dist/containerRpc.d.ts +27 -19
- package/dist/headedRunRegistry.d.ts +51 -0
- package/dist/headedScheduler.d.ts +10 -10
- package/dist/headlessScheduler.d.ts +5 -4
- package/dist/headlessTransport.d.ts +4 -2
- package/dist/hostPayloads.d.ts +1 -7
- package/dist/index.js +840 -330
- package/dist/protocol.d.ts +48 -8
- package/dist/providers/index.d.ts +21 -0
- package/dist/providers/playwright/v8Coverage.d.ts +2 -0
- package/dist/rpcProtocol.d.ts +1 -2
- package/dist/schedulerSeam.d.ts +5 -0
- package/dist/sourceMap/sourceMapLoader.d.ts +12 -1
- package/dist/watchRerunPlanner.d.ts +20 -9
- package/dist/watchSignals.d.ts +8 -1
- package/package.json +10 -10
- package/dist/33.js +0 -7
- package/dist/browser-container/container-static/css/index.0521ff95b1.css +0 -1
- package/dist/browser-container/container-static/js/683.4f821ce8f1.js +0 -194
- package/dist/browser-container/container-static/js/index.568aa7cb35.js +0 -1
- package/dist/browser.d.ts +0 -2
- package/src/augmentExpect.ts +0 -62
- package/src/browser.ts +0 -3
- package/src/browserExecutor.ts +0 -222
- package/src/browserRpcRegistry.ts +0 -57
- package/src/browserRsbuild.ts +0 -1927
- package/src/client/api.ts +0 -213
- package/src/client/browserRpc.ts +0 -86
- package/src/client/dispatchTransport.ts +0 -240
- package/src/client/entry.ts +0 -807
- package/src/client/locator.ts +0 -448
- package/src/client/snapshot.ts +0 -113
- package/src/client/sourceMapSupport.ts +0 -159
- package/src/concurrency.ts +0 -65
- package/src/configValidation.ts +0 -263
- package/src/containerRpc.ts +0 -206
- package/src/dispatchCapabilities.ts +0 -177
- package/src/dispatchRouter.ts +0 -82
- package/src/env.d.ts +0 -54
- package/src/headedScheduler.ts +0 -664
- package/src/headedSerialTaskQueue.ts +0 -23
- package/src/headlessScheduler.ts +0 -566
- package/src/headlessTransport.ts +0 -28
- package/src/hostController.ts +0 -1365
- package/src/hostPayloads.ts +0 -62
- package/src/index.ts +0 -51
- package/src/manifest.d.ts +0 -41
- package/src/protocol.ts +0 -225
- package/src/providers/index.ts +0 -126
- package/src/providers/playwright/compileLocator.ts +0 -130
- package/src/providers/playwright/dispatchBrowserRpc.ts +0 -372
- package/src/providers/playwright/expectUtils.ts +0 -57
- package/src/providers/playwright/implementation.ts +0 -35
- package/src/providers/playwright/index.ts +0 -1
- package/src/providers/playwright/runtime.ts +0 -159
- package/src/providers/playwright/textMatcher.ts +0 -10
- package/src/rpcProtocol.ts +0 -218
- package/src/runSession.ts +0 -110
- package/src/schedulerSeam.ts +0 -49
- package/src/sessionRegistry.ts +0 -89
- package/src/sourceMap/sourceMapLoader.ts +0 -96
- package/src/viewportPresets.ts +0 -69
- package/src/watchRerunPlanner.ts +0 -88
- package/src/watchRuntime.ts +0 -83
- package/src/watchSignals.ts +0 -93
- /package/dist/{rslib-runtime.js → 612~0.js} +0 -0
- /package/dist/client/{api.d.ts → client/api.d.ts} +0 -0
- /package/dist/client/{locator.d.ts → client/locator.d.ts} +0 -0
package/src/client/locator.ts
DELETED
|
@@ -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
|
-
};
|
package/src/client/snapshot.ts
DELETED
|
@@ -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
|
-
};
|