@atomic-testing/playwright 0.88.0 → 0.89.0
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/README.md +7 -0
- package/dist/index.cjs +102 -100
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +38 -48
- package/dist/index.d.mts +38 -48
- package/dist/index.mjs +103 -97
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/PlaywrightInteractor.ts +127 -65
- package/src/index.ts +0 -1
- package/src/testRunnerAdapter.ts +0 -81
|
@@ -38,15 +38,46 @@ export class PlaywrightInteractor implements Interactor {
|
|
|
38
38
|
*/
|
|
39
39
|
constructor(public readonly page: Page) {}
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Run a Playwright mutation and normalize a "locator matched nothing" failure
|
|
43
|
+
* into {@link ElementNotFoundError}, so a missing element throws the same error
|
|
44
|
+
* class here as it does in `DOMInteractor`, regardless of environment (the
|
|
45
|
+
* unified contract ratified in ADR-006).
|
|
46
|
+
*
|
|
47
|
+
* Playwright auto-waits for actionability and then throws its own
|
|
48
|
+
* `TimeoutError`. We translate that to `ElementNotFoundError` ONLY when the
|
|
49
|
+
* element genuinely does not exist (count 0) and otherwise rethrow the original
|
|
50
|
+
* error — preserving Playwright's auto-wait for an element that exists but is
|
|
51
|
+
* briefly not actionable (covered, disabled, animating). The trade-off is that
|
|
52
|
+
* a truly-missing element waits out the page's action timeout before throwing;
|
|
53
|
+
* bound it with `page.setDefaultTimeout` when fast failure matters.
|
|
54
|
+
*
|
|
55
|
+
* @param locator - Locator the mutation targets
|
|
56
|
+
* @param action - Method name used in the error message (e.g. `'click'`)
|
|
57
|
+
* @param run - The Playwright action to execute
|
|
58
|
+
* @throws {ElementNotFoundError} If the action fails and the element is absent
|
|
59
|
+
*/
|
|
60
|
+
private async runMutation<T>(locator: PartLocator, action: string, run: () => Promise<T>): Promise<T> {
|
|
61
|
+
try {
|
|
62
|
+
return await run();
|
|
63
|
+
} catch (e) {
|
|
64
|
+
if ((await this.exists(locator)) === false) {
|
|
65
|
+
throw new ElementNotFoundError(locator, action);
|
|
66
|
+
}
|
|
67
|
+
throw e;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
41
71
|
/**
|
|
42
72
|
* Select the given option values on a `<select>` element.
|
|
43
73
|
*
|
|
44
74
|
* @param locator - Locator to the `<select>` element.
|
|
45
75
|
* @param values - Values to select.
|
|
76
|
+
* @throws {ElementNotFoundError} If the element is not found
|
|
46
77
|
*/
|
|
47
78
|
async selectOptionValue(locator: PartLocator, values: string[]): Promise<void> {
|
|
48
79
|
const cssLocator = await locatorUtil.toCssSelector(locator, this);
|
|
49
|
-
await this.page.locator(cssLocator).selectOption(values);
|
|
80
|
+
await this.runMutation(locator, 'selectOptionValue', () => this.page.locator(cssLocator).selectOption(values));
|
|
50
81
|
}
|
|
51
82
|
|
|
52
83
|
/**
|
|
@@ -54,31 +85,29 @@ export class PlaywrightInteractor implements Interactor {
|
|
|
54
85
|
*
|
|
55
86
|
* Playwright's native `setInputFiles` reads the given paths from disk and
|
|
56
87
|
* populates the input's `FileList`, firing the change event — the only way to
|
|
57
|
-
* fill a file input, whose value cannot be set programmatically.
|
|
58
|
-
* this layer's convention, no `ElementNotFoundError` is fabricated: a missing
|
|
59
|
-
* element surfaces through Playwright's own auto-wait timeout.
|
|
88
|
+
* fill a file input, whose value cannot be set programmatically.
|
|
60
89
|
*
|
|
61
90
|
* @param locator - Locator of the `<input type="file">` element
|
|
62
91
|
* @param files - One or more filesystem paths to upload
|
|
92
|
+
* @throws {ElementNotFoundError} If the element is not found
|
|
63
93
|
*/
|
|
64
94
|
async setInputFiles(locator: PartLocator, files: string | string[]): Promise<void> {
|
|
65
95
|
const cssLocator = await locatorUtil.toCssSelector(locator, this);
|
|
66
|
-
await this.page.locator(cssLocator).setInputFiles(files);
|
|
96
|
+
await this.runMutation(locator, 'setInputFiles', () => this.page.locator(cssLocator).setInputFiles(files));
|
|
67
97
|
}
|
|
68
98
|
|
|
69
99
|
/**
|
|
70
100
|
* Scroll the located element into view, no-op if already visible.
|
|
71
101
|
*
|
|
72
102
|
* Delegates to Playwright's `scrollIntoViewIfNeeded`, which performs a real
|
|
73
|
-
* layout-aware scroll in the browser.
|
|
74
|
-
* `ElementNotFoundError` is fabricated: a missing element surfaces through
|
|
75
|
-
* Playwright's own auto-wait timeout.
|
|
103
|
+
* layout-aware scroll in the browser.
|
|
76
104
|
*
|
|
77
105
|
* @param locator - Locator of the element to scroll into view
|
|
106
|
+
* @throws {ElementNotFoundError} If the element is not found
|
|
78
107
|
*/
|
|
79
108
|
async scrollIntoView(locator: PartLocator): Promise<void> {
|
|
80
109
|
const css = await locatorUtil.toCssSelector(locator, this);
|
|
81
|
-
await this.page.locator(css).scrollIntoViewIfNeeded();
|
|
110
|
+
await this.runMutation(locator, 'scrollIntoView', () => this.page.locator(css).scrollIntoViewIfNeeded());
|
|
82
111
|
}
|
|
83
112
|
|
|
84
113
|
/**
|
|
@@ -90,33 +119,43 @@ export class PlaywrightInteractor implements Interactor {
|
|
|
90
119
|
* whereas evaluating `scrollBy` on the resolved element scrolls exactly that
|
|
91
120
|
* element. This is a deliberate deviation from ADR 0001's per-engine table
|
|
92
121
|
* (which lists `page.mouse.wheel`), taking the alternative the step-5 prompt
|
|
93
|
-
* permits ("or evaluate el.scrollBy") for cross-browser determinism.
|
|
94
|
-
* {@link scrollIntoView}, no `ElementNotFoundError` is fabricated; a missing
|
|
95
|
-
* element surfaces through Playwright's own auto-wait timeout.
|
|
122
|
+
* permits ("or evaluate el.scrollBy") for cross-browser determinism.
|
|
96
123
|
*
|
|
97
124
|
* @param locator - Locator of the scrollable element
|
|
98
125
|
* @param delta - Pixel offset to scroll by
|
|
126
|
+
* @throws {ElementNotFoundError} If the element is not found
|
|
99
127
|
*/
|
|
100
128
|
async scrollBy(locator: PartLocator, delta: Point): Promise<void> {
|
|
101
129
|
const css = await locatorUtil.toCssSelector(locator, this);
|
|
102
|
-
await this.
|
|
130
|
+
await this.runMutation(locator, 'scrollBy', () =>
|
|
131
|
+
this.page.locator(css).evaluate((el, d) => el.scrollBy(d.x, d.y), { x: delta.x, y: delta.y })
|
|
132
|
+
);
|
|
103
133
|
}
|
|
104
134
|
|
|
105
135
|
/**
|
|
106
136
|
* Drag the source element and drop it onto the target element.
|
|
107
137
|
*
|
|
108
138
|
* Delegates to Playwright's native `Locator.dragTo`, which performs a real,
|
|
109
|
-
* layout-aware drag gesture in the browser.
|
|
110
|
-
* `ElementNotFoundError` is fabricated: a missing element surfaces through
|
|
111
|
-
* Playwright's own auto-wait timeout.
|
|
139
|
+
* layout-aware drag gesture in the browser.
|
|
112
140
|
*
|
|
113
141
|
* @param source - Locator of the element to drag
|
|
114
142
|
* @param target - Locator of the drop target
|
|
143
|
+
* @throws {ElementNotFoundError} If either the source or target is not found
|
|
115
144
|
*/
|
|
116
145
|
async dragTo(source: PartLocator, target: PartLocator): Promise<void> {
|
|
117
146
|
const srcCss = await locatorUtil.toCssSelector(source, this);
|
|
118
147
|
const tgtCss = await locatorUtil.toCssSelector(target, this);
|
|
119
|
-
|
|
148
|
+
try {
|
|
149
|
+
await this.page.locator(srcCss).dragTo(this.page.locator(tgtCss));
|
|
150
|
+
} catch (e) {
|
|
151
|
+
if ((await this.exists(source)) === false) {
|
|
152
|
+
throw new ElementNotFoundError(source, 'dragTo');
|
|
153
|
+
}
|
|
154
|
+
if ((await this.exists(target)) === false) {
|
|
155
|
+
throw new ElementNotFoundError(target, 'dragTo');
|
|
156
|
+
}
|
|
157
|
+
throw e;
|
|
158
|
+
}
|
|
120
159
|
}
|
|
121
160
|
|
|
122
161
|
/**
|
|
@@ -127,9 +166,9 @@ export class PlaywrightInteractor implements Interactor {
|
|
|
127
166
|
* {@link mouseMove}/{@link mouseDown} — `mouseMove` resets the pointer with
|
|
128
167
|
* `page.mouse.move(0, 0)` after hovering, which would corrupt the drag path
|
|
129
168
|
* (see ADR 0001, option 5). The center comes from {@link getBoundingRect},
|
|
130
|
-
* which throws `ElementNotFoundError` when the element has no box
|
|
131
|
-
*
|
|
132
|
-
*
|
|
169
|
+
* which throws `ElementNotFoundError` when the element has no box, so this
|
|
170
|
+
* shares that "element not found" contract instead of re-deriving the box +
|
|
171
|
+
* guard here.
|
|
133
172
|
*
|
|
134
173
|
* @param locator - Locator of the element to drag
|
|
135
174
|
* @param delta - Pixel offset to drag by
|
|
@@ -203,101 +242,125 @@ export class PlaywrightInteractor implements Interactor {
|
|
|
203
242
|
|
|
204
243
|
async enterText(locator: PartLocator, text: string, option?: Optional<Partial<EnterTextOption>>): Promise<void> {
|
|
205
244
|
const cssLocator = await locatorUtil.toCssSelector(locator, this);
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
245
|
+
await this.runMutation(locator, 'enterText', async () => {
|
|
246
|
+
if (!option?.append) {
|
|
247
|
+
await this.page.locator(cssLocator).clear();
|
|
248
|
+
}
|
|
209
249
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
250
|
+
// If it is a date, time or datetime-local input, validate the date format
|
|
251
|
+
const type = (await this.getAttribute(locator, 'type')) ?? '';
|
|
252
|
+
if (dateUtil.isHtmlDateInputType(type)) {
|
|
253
|
+
const result = dateUtil.validateHtmlDateInput(type, text);
|
|
254
|
+
if (!result.valid) {
|
|
255
|
+
throw new Error(
|
|
256
|
+
`Invalid date format for type: ${type}, expected format: ${result.format}, example: ${result.example}`
|
|
257
|
+
);
|
|
258
|
+
}
|
|
218
259
|
}
|
|
219
|
-
|
|
220
|
-
|
|
260
|
+
await this.page.locator(cssLocator).fill(text);
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
async setRangeValue(locator: PartLocator, value: number): Promise<void> {
|
|
265
|
+
const cssLocator = await locatorUtil.toCssSelector(locator, this);
|
|
266
|
+
// Playwright's `fill` rejects `<input type="range">` (it is not a fillable
|
|
267
|
+
// text control), so set the value in-page through the native value setter.
|
|
268
|
+
// Calling the prototype setter both sanitizes the value to the input's step
|
|
269
|
+
// (the browser snaps an off-step target to the nearest valid step) and lets
|
|
270
|
+
// React's value tracker observe the change; the dispatched input/change
|
|
271
|
+
// events then drive a controlled component (e.g. MUI Slider) to re-render.
|
|
272
|
+
await this.runMutation(locator, 'setRangeValue', () =>
|
|
273
|
+
this.page.locator(cssLocator).evaluate((el, nextValue) => {
|
|
274
|
+
const input = el as HTMLInputElement;
|
|
275
|
+
const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')?.set;
|
|
276
|
+
setter?.call(input, nextValue);
|
|
277
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
278
|
+
input.dispatchEvent(new Event('change', { bubbles: true }));
|
|
279
|
+
}, String(value))
|
|
280
|
+
);
|
|
221
281
|
}
|
|
222
282
|
|
|
223
283
|
async click(locator: PartLocator, option?: Partial<ClickOption>): Promise<void> {
|
|
224
284
|
const cssLocator = await locatorUtil.toCssSelector(locator, this);
|
|
225
|
-
await this.page.locator(cssLocator).click({ position: option?.position });
|
|
285
|
+
await this.runMutation(locator, 'click', () => this.page.locator(cssLocator).click({ position: option?.position }));
|
|
226
286
|
}
|
|
227
287
|
|
|
228
288
|
/**
|
|
229
289
|
* Dispatch a right-click on the located element to open its context menu.
|
|
230
290
|
*
|
|
231
291
|
* Delegates to Playwright's native right-button click, which fires a real
|
|
232
|
-
* `contextmenu` event in the browser.
|
|
233
|
-
* `ElementNotFoundError` is fabricated: a missing element surfaces through
|
|
234
|
-
* Playwright's own auto-wait timeout.
|
|
292
|
+
* `contextmenu` event in the browser.
|
|
235
293
|
*
|
|
236
294
|
* @param locator - Locator of the element to right-click
|
|
295
|
+
* @throws {ElementNotFoundError} If the element is not found
|
|
237
296
|
*/
|
|
238
297
|
async contextMenu(locator: PartLocator): Promise<void> {
|
|
239
298
|
const css = await locatorUtil.toCssSelector(locator, this);
|
|
240
|
-
await this.page.locator(css).click({ button: 'right' });
|
|
299
|
+
await this.runMutation(locator, 'contextMenu', () => this.page.locator(css).click({ button: 'right' }));
|
|
241
300
|
}
|
|
242
301
|
|
|
243
302
|
async hover(locator: PartLocator, option?: Partial<HoverOption>): Promise<void> {
|
|
244
303
|
const cssLocator = await locatorUtil.toCssSelector(locator, this);
|
|
245
|
-
await this.page.locator(cssLocator).hover({ position: option?.position });
|
|
304
|
+
await this.runMutation(locator, 'hover', () => this.page.locator(cssLocator).hover({ position: option?.position }));
|
|
246
305
|
}
|
|
247
306
|
|
|
248
307
|
async mouseMove(locator: PartLocator, option?: Partial<MouseMoveOption>): Promise<void> {
|
|
249
|
-
await this.
|
|
250
|
-
position: option?.position
|
|
308
|
+
await this.runMutation(locator, 'mouseMove', async () => {
|
|
309
|
+
await this.hover(locator, { position: option?.position });
|
|
310
|
+
await this.page.mouse.move(0, 0);
|
|
251
311
|
});
|
|
252
|
-
await this.page.mouse.move(0, 0);
|
|
253
312
|
}
|
|
254
313
|
|
|
255
314
|
async mouseDown(locator: PartLocator, option?: Partial<MouseDownOption>): Promise<void> {
|
|
256
|
-
await this.
|
|
257
|
-
position: option?.position
|
|
315
|
+
await this.runMutation(locator, 'mouseDown', async () => {
|
|
316
|
+
await this.hover(locator, { position: option?.position });
|
|
317
|
+
await this.page.mouse.down();
|
|
258
318
|
});
|
|
259
|
-
await this.page.mouse.down();
|
|
260
319
|
}
|
|
261
320
|
|
|
262
321
|
async mouseUp(locator: PartLocator, option?: Partial<MouseUpOption>): Promise<void> {
|
|
263
|
-
await this.
|
|
264
|
-
position: option?.position
|
|
322
|
+
await this.runMutation(locator, 'mouseUp', async () => {
|
|
323
|
+
await this.hover(locator, { position: option?.position });
|
|
324
|
+
await this.page.mouse.up();
|
|
265
325
|
});
|
|
266
|
-
await this.page.mouse.up();
|
|
267
326
|
}
|
|
268
327
|
|
|
269
328
|
async mouseOver(locator: PartLocator, option?: Partial<HoverOption>): Promise<void> {
|
|
270
|
-
|
|
329
|
+
await this.runMutation(locator, 'mouseOver', () => this.hover(locator, option));
|
|
271
330
|
}
|
|
272
331
|
|
|
273
332
|
async mouseOut(locator: PartLocator, _option?: Partial<MouseOutOption>): Promise<void> {
|
|
274
333
|
const cssLocator = await locatorUtil.toCssSelector(locator, this);
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
334
|
+
await this.runMutation(locator, 'mouseOut', async () => {
|
|
335
|
+
// First hover over the element to trigger mouseenter/mouseover
|
|
336
|
+
await this.page.locator(cssLocator).hover();
|
|
337
|
+
// Then dispatch mouseout event directly for cross-browser reliability
|
|
338
|
+
await this.page.locator(cssLocator).dispatchEvent('mouseout');
|
|
339
|
+
});
|
|
279
340
|
}
|
|
280
341
|
|
|
281
342
|
async mouseEnter(locator: PartLocator, _option?: Partial<MouseEnterOption>): Promise<void> {
|
|
282
|
-
|
|
343
|
+
await this.runMutation(locator, 'mouseEnter', () => this.hover(locator));
|
|
283
344
|
}
|
|
284
345
|
|
|
285
346
|
async mouseLeave(locator: PartLocator, _option?: Partial<MouseLeaveOption>): Promise<void> {
|
|
286
347
|
const cssLocator = await locatorUtil.toCssSelector(locator, this);
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
348
|
+
await this.runMutation(locator, 'mouseLeave', async () => {
|
|
349
|
+
// First hover over the element to trigger mouseenter/mouseover
|
|
350
|
+
await this.page.locator(cssLocator).hover();
|
|
351
|
+
// Dispatch mouseout which triggers both mouseout and mouseleave handlers in React
|
|
352
|
+
await this.page.locator(cssLocator).dispatchEvent('mouseout');
|
|
353
|
+
});
|
|
291
354
|
}
|
|
292
355
|
|
|
293
356
|
async focus(locator: PartLocator, _option?: Partial<FocusOption>): Promise<void> {
|
|
294
357
|
const cssLocator = await locatorUtil.toCssSelector(locator, this);
|
|
295
|
-
|
|
358
|
+
await this.runMutation(locator, 'focus', () => this.page.focus(cssLocator));
|
|
296
359
|
}
|
|
297
360
|
|
|
298
361
|
async blur(locator: PartLocator, _option?: Partial<BlurOption>): Promise<void> {
|
|
299
362
|
const cssLocator = await locatorUtil.toCssSelector(locator, this);
|
|
300
|
-
await this.page.locator(cssLocator).blur();
|
|
363
|
+
await this.runMutation(locator, 'blur', () => this.page.locator(cssLocator).blur());
|
|
301
364
|
}
|
|
302
365
|
|
|
303
366
|
async pressKey(locator: PartLocator, key: string, option?: Partial<PressKeyOption>): Promise<void> {
|
|
@@ -324,7 +387,7 @@ export class PlaywrightInteractor implements Interactor {
|
|
|
324
387
|
// Caveat: for Shift + a printable key the browser case-folds `event.key`
|
|
325
388
|
// (`Shift+a` → `'A'`) whereas the jsdom path keeps `'a'` — only the modifier
|
|
326
389
|
// flags are delivered identically across engines (see #924).
|
|
327
|
-
await this.page.locator(cssLocator).press(chord);
|
|
390
|
+
await this.runMutation(locator, 'pressKey', () => this.page.locator(cssLocator).press(chord));
|
|
328
391
|
}
|
|
329
392
|
|
|
330
393
|
async activate(locator: PartLocator): Promise<void> {
|
|
@@ -332,7 +395,7 @@ export class PlaywrightInteractor implements Interactor {
|
|
|
332
395
|
// Geometry-free activation mirrors the mouseout dispatch precedent above: it
|
|
333
396
|
// bypasses hit-testing to actuate a covered or zero-size input that
|
|
334
397
|
// locator.click() (a real geometry hit-test) cannot reach.
|
|
335
|
-
await this.page.locator(cssLocator).dispatchEvent('click');
|
|
398
|
+
await this.runMutation(locator, 'activate', () => this.page.locator(cssLocator).dispatchEvent('click'));
|
|
336
399
|
}
|
|
337
400
|
|
|
338
401
|
//#region wait conditions
|
|
@@ -387,9 +450,8 @@ export class PlaywrightInteractor implements Interactor {
|
|
|
387
450
|
* Get the located element's bounding rectangle.
|
|
388
451
|
*
|
|
389
452
|
* `boundingBox()` returns `null` for a detached/invisible element rather than
|
|
390
|
-
* auto-waiting, so this
|
|
391
|
-
*
|
|
392
|
-
* (ADR 0001).
|
|
453
|
+
* auto-waiting, so this throws `ElementNotFoundError` directly (no auto-wait)
|
|
454
|
+
* — matching the unified "element not found" contract (ADR-006).
|
|
393
455
|
*
|
|
394
456
|
* @param locator - Locator of the element to measure
|
|
395
457
|
* @returns The element's bounding rectangle in CSS pixels
|
package/src/index.ts
CHANGED
package/src/testRunnerAdapter.ts
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { ScenePart, TestEngine } from '@atomic-testing/core';
|
|
2
|
-
import {
|
|
3
|
-
E2eTestInterface,
|
|
4
|
-
E2eTestRunEnvironmentFixture,
|
|
5
|
-
TestFrameworkMapper,
|
|
6
|
-
} from '@atomic-testing/internal-test-runner';
|
|
7
|
-
import { expect, Page, test } from '@playwright/test';
|
|
8
|
-
|
|
9
|
-
import { createTestEngine } from './createTestEngine';
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Navigate the current Playwright page to the provided URL.
|
|
13
|
-
*
|
|
14
|
-
* @param url - Destination URL to load.
|
|
15
|
-
* @param fixture - Optional test fixture supplying the Playwright page.
|
|
16
|
-
*/
|
|
17
|
-
export async function goto(url: string): Promise<void>;
|
|
18
|
-
export async function goto(url: string, fixture: E2eTestRunEnvironmentFixture): Promise<void>;
|
|
19
|
-
export async function goto(url: string, fixture?: E2eTestRunEnvironmentFixture): Promise<void> {
|
|
20
|
-
const page = fixture!.page as Page;
|
|
21
|
-
await page.goto(url);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Create a {@link TestEngine} bound to the Playwright page in the given fixture.
|
|
26
|
-
*
|
|
27
|
-
* @param scenePart - Scene definition to drive.
|
|
28
|
-
* @param fixture - Fixture providing the Playwright page.
|
|
29
|
-
*/
|
|
30
|
-
export function playwrightGetTestEngine<T extends ScenePart>(
|
|
31
|
-
scenePart: T,
|
|
32
|
-
fixture: E2eTestRunEnvironmentFixture
|
|
33
|
-
): TestEngine<T> {
|
|
34
|
-
const page = fixture.page as Page;
|
|
35
|
-
return createTestEngine(page, scenePart);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Playwright adapter for the TestFrameworkMapper interface.
|
|
40
|
-
*/
|
|
41
|
-
export const playWrightTestFrameworkMapper: TestFrameworkMapper = {
|
|
42
|
-
/*
|
|
43
|
-
* INTENTIONAL @ts-expect-error comments: Playwright's test functions have different type
|
|
44
|
-
* signatures than the normalized TestFrameworkMapper interface. Playwright uses fixture-based
|
|
45
|
-
* callbacks with destructuring ({ page, browser }) while our interface uses a union type for
|
|
46
|
-
* Jest compatibility (done callback or fixture object). The functions are compatible at runtime
|
|
47
|
-
* but TypeScript cannot verify this due to these fundamental signature differences.
|
|
48
|
-
*/
|
|
49
|
-
|
|
50
|
-
assertEqual: (a, b) => expect(a).toEqual(b),
|
|
51
|
-
assertNotEqual: (a, b) => expect(a).not.toEqual(b),
|
|
52
|
-
assertTrue: value => expect(value).toBe(true),
|
|
53
|
-
assertFalse: value => expect(value).toBe(false),
|
|
54
|
-
assertApproxEqual: (actual, expected, tolerance) =>
|
|
55
|
-
expect(Math.abs(actual - expected)).toBeLessThanOrEqual(tolerance),
|
|
56
|
-
// @ts-expect-error - Playwright describe signature differs from TestFrameworkMapper.Describe
|
|
57
|
-
describe: test.describe,
|
|
58
|
-
|
|
59
|
-
beforeEach: test.beforeEach,
|
|
60
|
-
afterEach: test.afterEach,
|
|
61
|
-
beforeAll: test.beforeAll,
|
|
62
|
-
afterAll: test.afterAll,
|
|
63
|
-
|
|
64
|
-
// @ts-expect-error - Playwright test signature differs from TestFrameworkMapper.Test
|
|
65
|
-
test: test,
|
|
66
|
-
|
|
67
|
-
// @ts-expect-error - Playwright test signature differs from TestFrameworkMapper.Test
|
|
68
|
-
it: test,
|
|
69
|
-
|
|
70
|
-
hasLayout: true,
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Get a typed interface for running end-to-end tests with Playwright.
|
|
75
|
-
*/
|
|
76
|
-
export function getTestRunnerInterface<T extends ScenePart>(): E2eTestInterface<T> {
|
|
77
|
-
return {
|
|
78
|
-
getTestEngine: playwrightGetTestEngine,
|
|
79
|
-
goto,
|
|
80
|
-
};
|
|
81
|
-
}
|