@atomic-testing/playwright 0.89.0 → 0.91.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 +2 -2
- package/dist/index.cjs +12 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -4
- package/dist/index.d.mts +7 -4
- package/dist/index.mjs +12 -8
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/src/PlaywrightInteractor.ts +12 -8
- package/src/createTestEngine.ts +9 -2
package/README.md
CHANGED
|
@@ -17,5 +17,5 @@ See the [docs](https://atomic-testing.dev/) for configuration and usage details.
|
|
|
17
17
|
|
|
18
18
|
The stable surface of this package is its `.` barrel exports, frozen under
|
|
19
19
|
SemVer and machine-checked by the committed [API Extractor](https://api-extractor.com/)
|
|
20
|
-
report at [`etc/playwright.api.md`](etc/playwright.api.md). Exports tagged `@internal` are
|
|
21
|
-
not part of that guarantee. See the [1.0 API freeze & evolution policy](
|
|
20
|
+
report at [`etc/playwright.api.md`](https://github.com/atomic-testing/atomic-testing/blob/main/packages/playwright/etc/playwright.api.md). Exports tagged `@internal` are
|
|
21
|
+
not part of that guarantee. See the [1.0 API freeze & evolution policy](https://github.com/atomic-testing/atomic-testing/blob/main/agent-docs/adr/006-1.0-api-freeze-and-evolution.md).
|
package/dist/index.cjs
CHANGED
|
@@ -4,7 +4,7 @@ let _atomic_testing_core = require("@atomic-testing/core");
|
|
|
4
4
|
/**
|
|
5
5
|
* Implementation of the {@link Interactor} interface using Playwright.
|
|
6
6
|
*/
|
|
7
|
-
var PlaywrightInteractor = class
|
|
7
|
+
var PlaywrightInteractor = class {
|
|
8
8
|
/**
|
|
9
9
|
* @param page - Playwright page instance used to drive the browser.
|
|
10
10
|
*/
|
|
@@ -293,9 +293,6 @@ var PlaywrightInteractor = class PlaywrightInteractor {
|
|
|
293
293
|
const cssLocator = await _atomic_testing_core.locatorUtil.toCssSelector(locator, this);
|
|
294
294
|
await this.runMutation(locator, "activate", () => this.page.locator(cssLocator).dispatchEvent("click"));
|
|
295
295
|
}
|
|
296
|
-
wait(ms) {
|
|
297
|
-
return _atomic_testing_core.timingUtil.wait(ms);
|
|
298
|
-
}
|
|
299
296
|
async waitUntilComponentState(locator, option = _atomic_testing_core.defaultWaitForOption) {
|
|
300
297
|
return _atomic_testing_core.interactorUtil.interactorWaitUtil(locator, this, option);
|
|
301
298
|
}
|
|
@@ -357,6 +354,13 @@ var PlaywrightInteractor = class PlaywrightInteractor {
|
|
|
357
354
|
async isReadonly(locator) {
|
|
358
355
|
return await this.getAttribute(locator, "readonly") != null;
|
|
359
356
|
}
|
|
357
|
+
async isRequired(locator) {
|
|
358
|
+
if (await this.getAttribute(locator, "required") != null) return true;
|
|
359
|
+
return await this.getAttribute(locator, "aria-required") === "true";
|
|
360
|
+
}
|
|
361
|
+
async isError(locator) {
|
|
362
|
+
return await this.getAttribute(locator, "aria-invalid") === "true";
|
|
363
|
+
}
|
|
360
364
|
async isVisible(locator) {
|
|
361
365
|
if (!await this.exists(locator)) return false;
|
|
362
366
|
async function checkCssVisibility(prop, invisibleValue, interactor) {
|
|
@@ -384,9 +388,6 @@ var PlaywrightInteractor = class PlaywrightInteractor {
|
|
|
384
388
|
const cssLocator = await _atomic_testing_core.locatorUtil.toCssSelector(locator, this);
|
|
385
389
|
return this.page.locator(cssLocator).innerHTML();
|
|
386
390
|
}
|
|
387
|
-
clone() {
|
|
388
|
-
return new PlaywrightInteractor(this.page);
|
|
389
|
-
}
|
|
390
391
|
};
|
|
391
392
|
//#endregion
|
|
392
393
|
//#region src/createTestEngine.ts
|
|
@@ -396,9 +397,12 @@ var PlaywrightInteractor = class PlaywrightInteractor {
|
|
|
396
397
|
* @param page - Playwright page used for interaction.
|
|
397
398
|
* @param partDefinitions - Scene part definitions describing the scene
|
|
398
399
|
* structure for the engine.
|
|
400
|
+
* @param _option - Reserved for entry-point symmetry with the other adapters;
|
|
401
|
+
* currently ignored. `rootElement` is not applicable to Playwright, which drives
|
|
402
|
+
* a real browser page rather than mounting into a host element.
|
|
399
403
|
* @returns A configured {@link TestEngine} ready for use.
|
|
400
404
|
*/
|
|
401
|
-
function createTestEngine(page, partDefinitions) {
|
|
405
|
+
function createTestEngine(page, partDefinitions, _option) {
|
|
402
406
|
return new _atomic_testing_core.TestEngine([], new PlaywrightInteractor(page), { parts: partDefinitions });
|
|
403
407
|
}
|
|
404
408
|
//#endregion
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["ElementNotFoundError","locatorUtil","dateUtil","timingUtil","defaultWaitForOption","interactorUtil","TestEngine"],"sources":["../src/PlaywrightInteractor.ts","../src/createTestEngine.ts"],"sourcesContent":["import {\n BlurOption,\n BoundingRect,\n byCssSelector,\n ClickOption,\n CssProperty,\n dateUtil,\n defaultWaitForOption,\n ElementNotFoundError,\n EnterTextOption,\n FocusOption,\n HoverOption,\n Interactor,\n interactorUtil,\n locatorUtil,\n MouseEnterOption,\n MouseLeaveOption,\n MouseOutOption,\n MouseDownOption,\n MouseMoveOption,\n MouseUpOption,\n Optional,\n PartLocator,\n Point,\n PressKeyOption,\n timingUtil,\n WaitForOption,\n WaitUntilOption,\n} from '@atomic-testing/core';\nimport { Page } from '@playwright/test';\n\n/**\n * Implementation of the {@link Interactor} interface using Playwright.\n */\nexport class PlaywrightInteractor implements Interactor {\n /**\n * @param page - Playwright page instance used to drive the browser.\n */\n constructor(public readonly page: Page) {}\n\n /**\n * Run a Playwright mutation and normalize a \"locator matched nothing\" failure\n * into {@link ElementNotFoundError}, so a missing element throws the same error\n * class here as it does in `DOMInteractor`, regardless of environment (the\n * unified contract ratified in ADR-006).\n *\n * Playwright auto-waits for actionability and then throws its own\n * `TimeoutError`. We translate that to `ElementNotFoundError` ONLY when the\n * element genuinely does not exist (count 0) and otherwise rethrow the original\n * error — preserving Playwright's auto-wait for an element that exists but is\n * briefly not actionable (covered, disabled, animating). The trade-off is that\n * a truly-missing element waits out the page's action timeout before throwing;\n * bound it with `page.setDefaultTimeout` when fast failure matters.\n *\n * @param locator - Locator the mutation targets\n * @param action - Method name used in the error message (e.g. `'click'`)\n * @param run - The Playwright action to execute\n * @throws {ElementNotFoundError} If the action fails and the element is absent\n */\n private async runMutation<T>(locator: PartLocator, action: string, run: () => Promise<T>): Promise<T> {\n try {\n return await run();\n } catch (e) {\n if ((await this.exists(locator)) === false) {\n throw new ElementNotFoundError(locator, action);\n }\n throw e;\n }\n }\n\n /**\n * Select the given option values on a `<select>` element.\n *\n * @param locator - Locator to the `<select>` element.\n * @param values - Values to select.\n * @throws {ElementNotFoundError} If the element is not found\n */\n async selectOptionValue(locator: PartLocator, values: string[]): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'selectOptionValue', () => this.page.locator(cssLocator).selectOption(values));\n }\n\n /**\n * Set the selected files on a `<input type=\"file\">` element.\n *\n * Playwright's native `setInputFiles` reads the given paths from disk and\n * populates the input's `FileList`, firing the change event — the only way to\n * fill a file input, whose value cannot be set programmatically.\n *\n * @param locator - Locator of the `<input type=\"file\">` element\n * @param files - One or more filesystem paths to upload\n * @throws {ElementNotFoundError} If the element is not found\n */\n async setInputFiles(locator: PartLocator, files: string | string[]): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'setInputFiles', () => this.page.locator(cssLocator).setInputFiles(files));\n }\n\n /**\n * Scroll the located element into view, no-op if already visible.\n *\n * Delegates to Playwright's `scrollIntoViewIfNeeded`, which performs a real\n * layout-aware scroll in the browser.\n *\n * @param locator - Locator of the element to scroll into view\n * @throws {ElementNotFoundError} If the element is not found\n */\n async scrollIntoView(locator: PartLocator): Promise<void> {\n const css = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'scrollIntoView', () => this.page.locator(css).scrollIntoViewIfNeeded());\n }\n\n /**\n * Scroll the located element by the given pixel delta.\n *\n * The scroll is performed by evaluating `el.scrollBy(dx, dy)` on the element\n * itself rather than `page.mouse.wheel`. A wheel event scrolls whatever sits\n * under the pointer and is non-deterministic across chromium/firefox/webkit,\n * whereas evaluating `scrollBy` on the resolved element scrolls exactly that\n * element. This is a deliberate deviation from ADR 0001's per-engine table\n * (which lists `page.mouse.wheel`), taking the alternative the step-5 prompt\n * permits (\"or evaluate el.scrollBy\") for cross-browser determinism.\n *\n * @param locator - Locator of the scrollable element\n * @param delta - Pixel offset to scroll by\n * @throws {ElementNotFoundError} If the element is not found\n */\n async scrollBy(locator: PartLocator, delta: Point): Promise<void> {\n const css = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'scrollBy', () =>\n this.page.locator(css).evaluate((el, d) => el.scrollBy(d.x, d.y), { x: delta.x, y: delta.y })\n );\n }\n\n /**\n * Drag the source element and drop it onto the target element.\n *\n * Delegates to Playwright's native `Locator.dragTo`, which performs a real,\n * layout-aware drag gesture in the browser.\n *\n * @param source - Locator of the element to drag\n * @param target - Locator of the drop target\n * @throws {ElementNotFoundError} If either the source or target is not found\n */\n async dragTo(source: PartLocator, target: PartLocator): Promise<void> {\n const srcCss = await locatorUtil.toCssSelector(source, this);\n const tgtCss = await locatorUtil.toCssSelector(target, this);\n try {\n await this.page.locator(srcCss).dragTo(this.page.locator(tgtCss));\n } catch (e) {\n if ((await this.exists(source)) === false) {\n throw new ElementNotFoundError(source, 'dragTo');\n }\n if ((await this.exists(target)) === false) {\n throw new ElementNotFoundError(target, 'dragTo');\n }\n throw e;\n }\n }\n\n /**\n * Drag the located element by the given pixel delta from its center.\n *\n * The gesture is a single uninterrupted `move → down → move → up` sequence\n * computed from the element's center. It deliberately does NOT reuse\n * {@link mouseMove}/{@link mouseDown} — `mouseMove` resets the pointer with\n * `page.mouse.move(0, 0)` after hovering, which would corrupt the drag path\n * (see ADR 0001, option 5). The center comes from {@link getBoundingRect},\n * which throws `ElementNotFoundError` when the element has no box, so this\n * shares that \"element not found\" contract instead of re-deriving the box +\n * guard here.\n *\n * @param locator - Locator of the element to drag\n * @param delta - Pixel offset to drag by\n * @throws {ElementNotFoundError} If the element has no bounding box\n */\n async drag(locator: PartLocator, delta: Point): Promise<void> {\n const rect = await this.getBoundingRect(locator);\n const cx = rect.x + rect.width / 2;\n const cy = rect.y + rect.height / 2;\n await this.page.mouse.move(cx, cy);\n await this.page.mouse.down();\n await this.page.mouse.move(cx + delta.x, cy + delta.y, { steps: 8 });\n await this.page.mouse.up();\n }\n\n /**\n * Get the value of an `<input>` element.\n *\n * @param locator - Locator pointing to the input element.\n * @returns The current value of the input or `undefined` if not present.\n */\n async getInputValue(locator: PartLocator): Promise<Optional<string>> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n return this.page.locator(cssLocator).inputValue();\n }\n\n /**\n * Retrieve the values of selected options within a `<select>` element.\n *\n * @param locator - Locator to the `<select>` element.\n * @returns Array of selected option values or `undefined` when no option is selected.\n */\n async getSelectValues(locator: PartLocator): Promise<Optional<readonly string[]>> {\n const optionLocator: PartLocator = byCssSelector('option:checked');\n const selectedOptionLocator = locatorUtil.append(locator, optionLocator);\n const cssLocator = await locatorUtil.toCssSelector(selectedOptionLocator, this);\n const allOptions = await this.page.locator(cssLocator).all();\n const values: string[] = [];\n for (const option of allOptions) {\n const value = await option.getAttribute('value');\n if (value != null) {\n values.push(value);\n }\n }\n return values;\n }\n\n async getSelectLabels(locator: PartLocator): Promise<Optional<readonly string[]>> {\n const optionLocator: PartLocator = byCssSelector('option:checked');\n const selectedOptionLocator = locatorUtil.append(locator, optionLocator);\n const cssLocator = await locatorUtil.toCssSelector(selectedOptionLocator, this);\n const allOptions = await this.page.locator(cssLocator).all();\n const labels: string[] = [];\n for (const option of allOptions) {\n const label = await option.textContent();\n if (label != null) {\n labels.push(label);\n }\n }\n return labels;\n }\n\n async getStyleValue(locator: PartLocator, propertyName: CssProperty): Promise<Optional<string>> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n const elLocator = this.page.locator(cssLocator);\n const value = await elLocator.evaluate((element, prop) => {\n return window.getComputedStyle(element).getPropertyValue(prop as string);\n }, propertyName);\n return value;\n }\n\n async enterText(locator: PartLocator, text: string, option?: Optional<Partial<EnterTextOption>>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'enterText', async () => {\n if (!option?.append) {\n await this.page.locator(cssLocator).clear();\n }\n\n // If it is a date, time or datetime-local input, validate the date format\n const type = (await this.getAttribute(locator, 'type')) ?? '';\n if (dateUtil.isHtmlDateInputType(type)) {\n const result = dateUtil.validateHtmlDateInput(type, text);\n if (!result.valid) {\n throw new Error(\n `Invalid date format for type: ${type}, expected format: ${result.format}, example: ${result.example}`\n );\n }\n }\n await this.page.locator(cssLocator).fill(text);\n });\n }\n\n async setRangeValue(locator: PartLocator, value: number): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n // Playwright's `fill` rejects `<input type=\"range\">` (it is not a fillable\n // text control), so set the value in-page through the native value setter.\n // Calling the prototype setter both sanitizes the value to the input's step\n // (the browser snaps an off-step target to the nearest valid step) and lets\n // React's value tracker observe the change; the dispatched input/change\n // events then drive a controlled component (e.g. MUI Slider) to re-render.\n await this.runMutation(locator, 'setRangeValue', () =>\n this.page.locator(cssLocator).evaluate((el, nextValue) => {\n const input = el as HTMLInputElement;\n const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')?.set;\n setter?.call(input, nextValue);\n input.dispatchEvent(new Event('input', { bubbles: true }));\n input.dispatchEvent(new Event('change', { bubbles: true }));\n }, String(value))\n );\n }\n\n async click(locator: PartLocator, option?: Partial<ClickOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'click', () => this.page.locator(cssLocator).click({ position: option?.position }));\n }\n\n /**\n * Dispatch a right-click on the located element to open its context menu.\n *\n * Delegates to Playwright's native right-button click, which fires a real\n * `contextmenu` event in the browser.\n *\n * @param locator - Locator of the element to right-click\n * @throws {ElementNotFoundError} If the element is not found\n */\n async contextMenu(locator: PartLocator): Promise<void> {\n const css = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'contextMenu', () => this.page.locator(css).click({ button: 'right' }));\n }\n\n async hover(locator: PartLocator, option?: Partial<HoverOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'hover', () => this.page.locator(cssLocator).hover({ position: option?.position }));\n }\n\n async mouseMove(locator: PartLocator, option?: Partial<MouseMoveOption>): Promise<void> {\n await this.runMutation(locator, 'mouseMove', async () => {\n await this.hover(locator, { position: option?.position });\n await this.page.mouse.move(0, 0);\n });\n }\n\n async mouseDown(locator: PartLocator, option?: Partial<MouseDownOption>): Promise<void> {\n await this.runMutation(locator, 'mouseDown', async () => {\n await this.hover(locator, { position: option?.position });\n await this.page.mouse.down();\n });\n }\n\n async mouseUp(locator: PartLocator, option?: Partial<MouseUpOption>): Promise<void> {\n await this.runMutation(locator, 'mouseUp', async () => {\n await this.hover(locator, { position: option?.position });\n await this.page.mouse.up();\n });\n }\n\n async mouseOver(locator: PartLocator, option?: Partial<HoverOption>): Promise<void> {\n await this.runMutation(locator, 'mouseOver', () => this.hover(locator, option));\n }\n\n async mouseOut(locator: PartLocator, _option?: Partial<MouseOutOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'mouseOut', async () => {\n // First hover over the element to trigger mouseenter/mouseover\n await this.page.locator(cssLocator).hover();\n // Then dispatch mouseout event directly for cross-browser reliability\n await this.page.locator(cssLocator).dispatchEvent('mouseout');\n });\n }\n\n async mouseEnter(locator: PartLocator, _option?: Partial<MouseEnterOption>): Promise<void> {\n await this.runMutation(locator, 'mouseEnter', () => this.hover(locator));\n }\n\n async mouseLeave(locator: PartLocator, _option?: Partial<MouseLeaveOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'mouseLeave', async () => {\n // First hover over the element to trigger mouseenter/mouseover\n await this.page.locator(cssLocator).hover();\n // Dispatch mouseout which triggers both mouseout and mouseleave handlers in React\n await this.page.locator(cssLocator).dispatchEvent('mouseout');\n });\n }\n\n async focus(locator: PartLocator, _option?: Partial<FocusOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'focus', () => this.page.focus(cssLocator));\n }\n\n async blur(locator: PartLocator, _option?: Partial<BlurOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'blur', () => this.page.locator(cssLocator).blur());\n }\n\n async pressKey(locator: PartLocator, key: string, option?: Partial<PressKeyOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n // Compose Playwright's chord syntax — modifiers joined to the key by `+`, in\n // Playwright's accepted Control+Alt+Shift+Meta order — so the browser holds\n // those modifiers across the keypress and the event carries ctrlKey/etc.\n const modifiers: string[] = [];\n if (option?.ctrl) {\n modifiers.push('Control');\n }\n if (option?.alt) {\n modifiers.push('Alt');\n }\n if (option?.shift) {\n modifiers.push('Shift');\n }\n if (option?.meta) {\n modifiers.push('Meta');\n }\n const chord = modifiers.length > 0 ? `${modifiers.join('+')}+${key}` : key;\n // locator.press auto-focuses the element, then dispatches a real, trusted\n // KeyboardEvent — the browser equivalent of the DOM focus-first keyDown/keyUp.\n // Caveat: for Shift + a printable key the browser case-folds `event.key`\n // (`Shift+a` → `'A'`) whereas the jsdom path keeps `'a'` — only the modifier\n // flags are delivered identically across engines (see #924).\n await this.runMutation(locator, 'pressKey', () => this.page.locator(cssLocator).press(chord));\n }\n\n async activate(locator: PartLocator): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n // Geometry-free activation mirrors the mouseout dispatch precedent above: it\n // bypasses hit-testing to actuate a covered or zero-size input that\n // locator.click() (a real geometry hit-test) cannot reach.\n await this.runMutation(locator, 'activate', () => this.page.locator(cssLocator).dispatchEvent('click'));\n }\n\n //#region wait conditions\n wait(ms: number): Promise<void> {\n return timingUtil.wait(ms);\n }\n\n async waitUntilComponentState(\n locator: PartLocator,\n option: Partial<Readonly<WaitForOption>> = defaultWaitForOption\n ): Promise<void> {\n return interactorUtil.interactorWaitUtil(locator, this, option);\n }\n\n waitUntil<T>(option: WaitUntilOption<T>): Promise<T> {\n return timingUtil.waitUntil(option);\n }\n //#endregion\n\n async getAttribute(locator: PartLocator, name: string, isMultiple: true): Promise<readonly string[]>;\n async getAttribute(locator: PartLocator, name: string, isMultiple: false): Promise<Optional<string>>;\n async getAttribute(locator: PartLocator, name: string): Promise<Optional<string>>;\n async getAttribute(\n locator: PartLocator,\n name: string,\n isMultiple?: boolean\n ): Promise<Optional<string> | readonly string[]> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n const elLocator = this.page.locator(cssLocator);\n if (isMultiple) {\n const locators = await elLocator.all();\n const values: string[] = [];\n for (const locator of locators) {\n const value = await locator.getAttribute(name);\n if (value != null) {\n values.push(value);\n }\n }\n return values;\n }\n const value = await elLocator.getAttribute(name);\n return value ?? undefined;\n }\n\n async getText(locator: PartLocator): Promise<Optional<string>> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n const text = await this.page.locator(cssLocator).textContent();\n return text ?? undefined;\n }\n\n /**\n * Get the located element's bounding rectangle.\n *\n * `boundingBox()` returns `null` for a detached/invisible element rather than\n * auto-waiting, so this throws `ElementNotFoundError` directly (no auto-wait)\n * — matching the unified \"element not found\" contract (ADR-006).\n *\n * @param locator - Locator of the element to measure\n * @returns The element's bounding rectangle in CSS pixels\n * @throws {ElementNotFoundError} If the element has no bounding box\n */\n async getBoundingRect(locator: PartLocator): Promise<BoundingRect> {\n const css = await locatorUtil.toCssSelector(locator, this);\n const box = await this.page.locator(css).boundingBox();\n if (box == null) {\n throw new ElementNotFoundError(locator, 'getBoundingRect');\n }\n return { x: box.x, y: box.y, width: box.width, height: box.height };\n }\n\n async exists(locator: PartLocator): Promise<boolean> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n const count = await this.page.locator(cssLocator).count();\n return count > 0;\n }\n\n async isChecked(locator: PartLocator): Promise<boolean> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n const checked = await this.page.locator(cssLocator).isChecked();\n return checked;\n }\n\n async isDisabled(locator: PartLocator): Promise<boolean> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n const isDisabled = await this.page.locator(cssLocator).isDisabled();\n return isDisabled;\n }\n\n async isReadonly(locator: PartLocator): Promise<boolean> {\n const readonly = await this.getAttribute(locator, 'readonly');\n return readonly != null;\n }\n\n async isVisible(locator: PartLocator): Promise<boolean> {\n const exists = await this.exists(locator);\n if (!exists) {\n return false;\n }\n\n async function checkCssVisibility(\n prop: CssProperty,\n invisibleValue: string,\n interactor: PlaywrightInteractor\n ): Promise<boolean> {\n try {\n const value = await interactor.getStyleValue(locator, prop);\n return value !== invisibleValue;\n } catch (e) {\n // Element may disappear or detached while being checked because of animation\n // when it happens, an error is thrown. In this case, if indeed the element\n // is not visible, we return false. Otherwise, we re-throw the error.\n if ((await interactor.exists(locator)) === false) {\n return false;\n }\n throw e;\n }\n }\n\n if ((await checkCssVisibility('opacity', '0', this)) === false) {\n return false;\n }\n\n if ((await checkCssVisibility('visibility', 'hidden', this)) === false) {\n return false;\n }\n\n if ((await checkCssVisibility('display', 'none', this)) === false) {\n return false;\n }\n\n return true;\n }\n\n async hasCssClass(locator: PartLocator, className: string): Promise<boolean> {\n const classNames = await this.getAttribute(locator, 'class');\n if (classNames == null) {\n return false;\n }\n\n const names = classNames.split(/\\s+/);\n return names.includes(className);\n }\n\n async hasAttribute(locator: PartLocator, name: string): Promise<boolean> {\n const attrValue = await this.getAttribute(locator, name);\n return attrValue != null;\n }\n\n //#region\n async innerHTML(locator: PartLocator): Promise<string> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n return this.page.locator(cssLocator).innerHTML();\n }\n //#endregion\n\n clone(): Interactor {\n return new PlaywrightInteractor(this.page);\n }\n}\n","import { ScenePart, TestEngine } from '@atomic-testing/core';\nimport { Page } from '@playwright/test';\n\nimport { PlaywrightInteractor } from './PlaywrightInteractor';\n\n/**\n * Create a {@link TestEngine} instance backed by Playwright.\n *\n * @param page - Playwright page used for interaction.\n * @param partDefinitions - Scene part definitions describing the scene\n * structure for the engine.\n * @returns A configured {@link TestEngine} ready for use.\n */\nexport function createTestEngine<T extends ScenePart>(page: Page, partDefinitions: T): TestEngine<T> {\n const engine = new TestEngine([], new PlaywrightInteractor(page), {\n parts: partDefinitions,\n });\n\n return engine;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["ElementNotFoundError","locatorUtil","dateUtil","defaultWaitForOption","interactorUtil","timingUtil","TestEngine"],"sources":["../src/PlaywrightInteractor.ts","../src/createTestEngine.ts"],"sourcesContent":["import {\n BlurOption,\n BoundingRect,\n byCssSelector,\n ClickOption,\n CssProperty,\n dateUtil,\n defaultWaitForOption,\n ElementNotFoundError,\n EnterTextOption,\n FocusOption,\n HoverOption,\n Interactor,\n interactorUtil,\n locatorUtil,\n MouseEnterOption,\n MouseLeaveOption,\n MouseOutOption,\n MouseDownOption,\n MouseMoveOption,\n MouseUpOption,\n Optional,\n PartLocator,\n Point,\n PressKeyOption,\n timingUtil,\n WaitForOption,\n WaitUntilOption,\n} from '@atomic-testing/core';\nimport { Page } from '@playwright/test';\n\n/**\n * Implementation of the {@link Interactor} interface using Playwright.\n */\nexport class PlaywrightInteractor implements Interactor {\n /**\n * @param page - Playwright page instance used to drive the browser.\n */\n constructor(public readonly page: Page) {}\n\n /**\n * Run a Playwright mutation and normalize a \"locator matched nothing\" failure\n * into {@link ElementNotFoundError}, so a missing element throws the same error\n * class here as it does in `DOMInteractor`, regardless of environment (the\n * unified contract ratified in ADR-006).\n *\n * Playwright auto-waits for actionability and then throws its own\n * `TimeoutError`. We translate that to `ElementNotFoundError` ONLY when the\n * element genuinely does not exist (count 0) and otherwise rethrow the original\n * error — preserving Playwright's auto-wait for an element that exists but is\n * briefly not actionable (covered, disabled, animating). The trade-off is that\n * a truly-missing element waits out the page's action timeout before throwing;\n * bound it with `page.setDefaultTimeout` when fast failure matters.\n *\n * @param locator - Locator the mutation targets\n * @param action - Method name used in the error message (e.g. `'click'`)\n * @param run - The Playwright action to execute\n * @throws {ElementNotFoundError} If the action fails and the element is absent\n */\n private async runMutation<T>(locator: PartLocator, action: string, run: () => Promise<T>): Promise<T> {\n try {\n return await run();\n } catch (e) {\n if ((await this.exists(locator)) === false) {\n throw new ElementNotFoundError(locator, action);\n }\n throw e;\n }\n }\n\n /**\n * Select the given option values on a `<select>` element.\n *\n * @param locator - Locator to the `<select>` element.\n * @param values - Values to select.\n * @throws {ElementNotFoundError} If the element is not found\n */\n async selectOptionValue(locator: PartLocator, values: string[]): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'selectOptionValue', () => this.page.locator(cssLocator).selectOption(values));\n }\n\n /**\n * Set the selected files on a `<input type=\"file\">` element.\n *\n * Playwright's native `setInputFiles` reads the given paths from disk and\n * populates the input's `FileList`, firing the change event — the only way to\n * fill a file input, whose value cannot be set programmatically.\n *\n * @param locator - Locator of the `<input type=\"file\">` element\n * @param files - One or more filesystem paths to upload\n * @throws {ElementNotFoundError} If the element is not found\n */\n async setInputFiles(locator: PartLocator, files: string | string[]): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'setInputFiles', () => this.page.locator(cssLocator).setInputFiles(files));\n }\n\n /**\n * Scroll the located element into view, no-op if already visible.\n *\n * Delegates to Playwright's `scrollIntoViewIfNeeded`, which performs a real\n * layout-aware scroll in the browser.\n *\n * @param locator - Locator of the element to scroll into view\n * @throws {ElementNotFoundError} If the element is not found\n */\n async scrollIntoView(locator: PartLocator): Promise<void> {\n const css = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'scrollIntoView', () => this.page.locator(css).scrollIntoViewIfNeeded());\n }\n\n /**\n * Scroll the located element by the given pixel delta.\n *\n * The scroll is performed by evaluating `el.scrollBy(dx, dy)` on the element\n * itself rather than `page.mouse.wheel`. A wheel event scrolls whatever sits\n * under the pointer and is non-deterministic across chromium/firefox/webkit,\n * whereas evaluating `scrollBy` on the resolved element scrolls exactly that\n * element. This is a deliberate deviation from ADR 0001's per-engine table\n * (which lists `page.mouse.wheel`), taking the alternative the step-5 prompt\n * permits (\"or evaluate el.scrollBy\") for cross-browser determinism.\n *\n * @param locator - Locator of the scrollable element\n * @param delta - Pixel offset to scroll by\n * @throws {ElementNotFoundError} If the element is not found\n */\n async scrollBy(locator: PartLocator, delta: Point): Promise<void> {\n const css = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'scrollBy', () =>\n this.page.locator(css).evaluate((el, d) => el.scrollBy(d.x, d.y), { x: delta.x, y: delta.y })\n );\n }\n\n /**\n * Drag the source element and drop it onto the target element.\n *\n * Delegates to Playwright's native `Locator.dragTo`, which performs a real,\n * layout-aware drag gesture in the browser.\n *\n * @param source - Locator of the element to drag\n * @param target - Locator of the drop target\n * @throws {ElementNotFoundError} If either the source or target is not found\n */\n async dragTo(source: PartLocator, target: PartLocator): Promise<void> {\n const srcCss = await locatorUtil.toCssSelector(source, this);\n const tgtCss = await locatorUtil.toCssSelector(target, this);\n try {\n await this.page.locator(srcCss).dragTo(this.page.locator(tgtCss));\n } catch (e) {\n if ((await this.exists(source)) === false) {\n throw new ElementNotFoundError(source, 'dragTo');\n }\n if ((await this.exists(target)) === false) {\n throw new ElementNotFoundError(target, 'dragTo');\n }\n throw e;\n }\n }\n\n /**\n * Drag the located element by the given pixel delta from its center.\n *\n * The gesture is a single uninterrupted `move → down → move → up` sequence\n * computed from the element's center. It deliberately does NOT reuse\n * {@link mouseMove}/{@link mouseDown} — `mouseMove` resets the pointer with\n * `page.mouse.move(0, 0)` after hovering, which would corrupt the drag path\n * (see ADR 0001, option 5). The center comes from {@link getBoundingRect},\n * which throws `ElementNotFoundError` when the element has no box, so this\n * shares that \"element not found\" contract instead of re-deriving the box +\n * guard here.\n *\n * @param locator - Locator of the element to drag\n * @param delta - Pixel offset to drag by\n * @throws {ElementNotFoundError} If the element has no bounding box\n */\n async drag(locator: PartLocator, delta: Point): Promise<void> {\n const rect = await this.getBoundingRect(locator);\n const cx = rect.x + rect.width / 2;\n const cy = rect.y + rect.height / 2;\n await this.page.mouse.move(cx, cy);\n await this.page.mouse.down();\n await this.page.mouse.move(cx + delta.x, cy + delta.y, { steps: 8 });\n await this.page.mouse.up();\n }\n\n /**\n * Get the value of an `<input>` element.\n *\n * @param locator - Locator pointing to the input element.\n * @returns The current value of the input or `undefined` if not present.\n */\n async getInputValue(locator: PartLocator): Promise<Optional<string>> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n return this.page.locator(cssLocator).inputValue();\n }\n\n /**\n * Retrieve the values of selected options within a `<select>` element.\n *\n * @param locator - Locator to the `<select>` element.\n * @returns Array of selected option values or `undefined` when no option is selected.\n */\n async getSelectValues(locator: PartLocator): Promise<Optional<readonly string[]>> {\n const optionLocator: PartLocator = byCssSelector('option:checked');\n const selectedOptionLocator = locatorUtil.append(locator, optionLocator);\n const cssLocator = await locatorUtil.toCssSelector(selectedOptionLocator, this);\n const allOptions = await this.page.locator(cssLocator).all();\n const values: string[] = [];\n for (const option of allOptions) {\n const value = await option.getAttribute('value');\n if (value != null) {\n values.push(value);\n }\n }\n return values;\n }\n\n async getSelectLabels(locator: PartLocator): Promise<Optional<readonly string[]>> {\n const optionLocator: PartLocator = byCssSelector('option:checked');\n const selectedOptionLocator = locatorUtil.append(locator, optionLocator);\n const cssLocator = await locatorUtil.toCssSelector(selectedOptionLocator, this);\n const allOptions = await this.page.locator(cssLocator).all();\n const labels: string[] = [];\n for (const option of allOptions) {\n const label = await option.textContent();\n if (label != null) {\n labels.push(label);\n }\n }\n return labels;\n }\n\n async getStyleValue(locator: PartLocator, propertyName: CssProperty): Promise<Optional<string>> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n const elLocator = this.page.locator(cssLocator);\n const value = await elLocator.evaluate((element, prop) => {\n return window.getComputedStyle(element).getPropertyValue(prop as string);\n }, propertyName);\n return value;\n }\n\n async enterText(locator: PartLocator, text: string, option?: Optional<Partial<EnterTextOption>>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'enterText', async () => {\n if (!option?.append) {\n await this.page.locator(cssLocator).clear();\n }\n\n // If it is a date, time or datetime-local input, validate the date format\n const type = (await this.getAttribute(locator, 'type')) ?? '';\n if (dateUtil.isHtmlDateInputType(type)) {\n const result = dateUtil.validateHtmlDateInput(type, text);\n if (!result.valid) {\n throw new Error(\n `Invalid date format for type: ${type}, expected format: ${result.format}, example: ${result.example}`\n );\n }\n }\n await this.page.locator(cssLocator).fill(text);\n });\n }\n\n async setRangeValue(locator: PartLocator, value: number): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n // Playwright's `fill` rejects `<input type=\"range\">` (it is not a fillable\n // text control), so set the value in-page through the native value setter.\n // Calling the prototype setter both sanitizes the value to the input's step\n // (the browser snaps an off-step target to the nearest valid step) and lets\n // React's value tracker observe the change; the dispatched input/change\n // events then drive a controlled component (e.g. MUI Slider) to re-render.\n await this.runMutation(locator, 'setRangeValue', () =>\n this.page.locator(cssLocator).evaluate((el, nextValue) => {\n const input = el as HTMLInputElement;\n const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')?.set;\n setter?.call(input, nextValue);\n input.dispatchEvent(new Event('input', { bubbles: true }));\n input.dispatchEvent(new Event('change', { bubbles: true }));\n }, String(value))\n );\n }\n\n async click(locator: PartLocator, option?: Partial<ClickOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'click', () => this.page.locator(cssLocator).click({ position: option?.position }));\n }\n\n /**\n * Dispatch a right-click on the located element to open its context menu.\n *\n * Delegates to Playwright's native right-button click, which fires a real\n * `contextmenu` event in the browser.\n *\n * @param locator - Locator of the element to right-click\n * @throws {ElementNotFoundError} If the element is not found\n */\n async contextMenu(locator: PartLocator): Promise<void> {\n const css = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'contextMenu', () => this.page.locator(css).click({ button: 'right' }));\n }\n\n async hover(locator: PartLocator, option?: Partial<HoverOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'hover', () => this.page.locator(cssLocator).hover({ position: option?.position }));\n }\n\n async mouseMove(locator: PartLocator, option?: Partial<MouseMoveOption>): Promise<void> {\n await this.runMutation(locator, 'mouseMove', async () => {\n await this.hover(locator, { position: option?.position });\n await this.page.mouse.move(0, 0);\n });\n }\n\n async mouseDown(locator: PartLocator, option?: Partial<MouseDownOption>): Promise<void> {\n await this.runMutation(locator, 'mouseDown', async () => {\n await this.hover(locator, { position: option?.position });\n await this.page.mouse.down();\n });\n }\n\n async mouseUp(locator: PartLocator, option?: Partial<MouseUpOption>): Promise<void> {\n await this.runMutation(locator, 'mouseUp', async () => {\n await this.hover(locator, { position: option?.position });\n await this.page.mouse.up();\n });\n }\n\n async mouseOver(locator: PartLocator, option?: Partial<HoverOption>): Promise<void> {\n await this.runMutation(locator, 'mouseOver', () => this.hover(locator, option));\n }\n\n async mouseOut(locator: PartLocator, _option?: Partial<MouseOutOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'mouseOut', async () => {\n // First hover over the element to trigger mouseenter/mouseover\n await this.page.locator(cssLocator).hover();\n // Then dispatch mouseout event directly for cross-browser reliability\n await this.page.locator(cssLocator).dispatchEvent('mouseout');\n });\n }\n\n async mouseEnter(locator: PartLocator, _option?: Partial<MouseEnterOption>): Promise<void> {\n await this.runMutation(locator, 'mouseEnter', () => this.hover(locator));\n }\n\n async mouseLeave(locator: PartLocator, _option?: Partial<MouseLeaveOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'mouseLeave', async () => {\n // First hover over the element to trigger mouseenter/mouseover\n await this.page.locator(cssLocator).hover();\n // Dispatch mouseout which triggers both mouseout and mouseleave handlers in React\n await this.page.locator(cssLocator).dispatchEvent('mouseout');\n });\n }\n\n async focus(locator: PartLocator, _option?: Partial<FocusOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'focus', () => this.page.focus(cssLocator));\n }\n\n async blur(locator: PartLocator, _option?: Partial<BlurOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'blur', () => this.page.locator(cssLocator).blur());\n }\n\n async pressKey(locator: PartLocator, key: string, option?: Partial<PressKeyOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n // Compose Playwright's chord syntax — modifiers joined to the key by `+`, in\n // Playwright's accepted Control+Alt+Shift+Meta order — so the browser holds\n // those modifiers across the keypress and the event carries ctrlKey/etc.\n const modifiers: string[] = [];\n if (option?.ctrl) {\n modifiers.push('Control');\n }\n if (option?.alt) {\n modifiers.push('Alt');\n }\n if (option?.shift) {\n modifiers.push('Shift');\n }\n if (option?.meta) {\n modifiers.push('Meta');\n }\n const chord = modifiers.length > 0 ? `${modifiers.join('+')}+${key}` : key;\n // locator.press auto-focuses the element, then dispatches a real, trusted\n // KeyboardEvent — the browser equivalent of the DOM focus-first keyDown/keyUp.\n // Caveat: for Shift + a printable key the browser case-folds `event.key`\n // (`Shift+a` → `'A'`) whereas the jsdom path keeps `'a'` — only the modifier\n // flags are delivered identically across engines (see #924).\n await this.runMutation(locator, 'pressKey', () => this.page.locator(cssLocator).press(chord));\n }\n\n async activate(locator: PartLocator): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n // Geometry-free activation mirrors the mouseout dispatch precedent above: it\n // bypasses hit-testing to actuate a covered or zero-size input that\n // locator.click() (a real geometry hit-test) cannot reach.\n await this.runMutation(locator, 'activate', () => this.page.locator(cssLocator).dispatchEvent('click'));\n }\n\n //#region wait conditions\n async waitUntilComponentState(\n locator: PartLocator,\n option: Partial<Readonly<WaitForOption>> = defaultWaitForOption\n ): Promise<void> {\n return interactorUtil.interactorWaitUtil(locator, this, option);\n }\n\n waitUntil<T>(option: WaitUntilOption<T>): Promise<T> {\n return timingUtil.waitUntil(option);\n }\n //#endregion\n\n async getAttribute(locator: PartLocator, name: string, isMultiple: true): Promise<readonly string[]>;\n async getAttribute(locator: PartLocator, name: string, isMultiple: false): Promise<Optional<string>>;\n async getAttribute(locator: PartLocator, name: string): Promise<Optional<string>>;\n async getAttribute(\n locator: PartLocator,\n name: string,\n isMultiple?: boolean\n ): Promise<Optional<string> | readonly string[]> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n const elLocator = this.page.locator(cssLocator);\n if (isMultiple) {\n const locators = await elLocator.all();\n const values: string[] = [];\n for (const locator of locators) {\n const value = await locator.getAttribute(name);\n if (value != null) {\n values.push(value);\n }\n }\n return values;\n }\n const value = await elLocator.getAttribute(name);\n return value ?? undefined;\n }\n\n async getText(locator: PartLocator): Promise<Optional<string>> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n const text = await this.page.locator(cssLocator).textContent();\n return text ?? undefined;\n }\n\n /**\n * Get the located element's bounding rectangle.\n *\n * `boundingBox()` returns `null` for a detached/invisible element rather than\n * auto-waiting, so this throws `ElementNotFoundError` directly (no auto-wait)\n * — matching the unified \"element not found\" contract (ADR-006).\n *\n * @param locator - Locator of the element to measure\n * @returns The element's bounding rectangle in CSS pixels\n * @throws {ElementNotFoundError} If the element has no bounding box\n */\n async getBoundingRect(locator: PartLocator): Promise<BoundingRect> {\n const css = await locatorUtil.toCssSelector(locator, this);\n const box = await this.page.locator(css).boundingBox();\n if (box == null) {\n throw new ElementNotFoundError(locator, 'getBoundingRect');\n }\n return { x: box.x, y: box.y, width: box.width, height: box.height };\n }\n\n async exists(locator: PartLocator): Promise<boolean> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n const count = await this.page.locator(cssLocator).count();\n return count > 0;\n }\n\n async isChecked(locator: PartLocator): Promise<boolean> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n const checked = await this.page.locator(cssLocator).isChecked();\n return checked;\n }\n\n async isDisabled(locator: PartLocator): Promise<boolean> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n const isDisabled = await this.page.locator(cssLocator).isDisabled();\n return isDisabled;\n }\n\n async isReadonly(locator: PartLocator): Promise<boolean> {\n const readonly = await this.getAttribute(locator, 'readonly');\n return readonly != null;\n }\n\n async isRequired(locator: PartLocator): Promise<boolean> {\n const required = await this.getAttribute(locator, 'required');\n if (required != null) {\n return true;\n }\n return (await this.getAttribute(locator, 'aria-required')) === 'true';\n }\n\n async isError(locator: PartLocator): Promise<boolean> {\n return (await this.getAttribute(locator, 'aria-invalid')) === 'true';\n }\n\n async isVisible(locator: PartLocator): Promise<boolean> {\n const exists = await this.exists(locator);\n if (!exists) {\n return false;\n }\n\n async function checkCssVisibility(\n prop: CssProperty,\n invisibleValue: string,\n interactor: PlaywrightInteractor\n ): Promise<boolean> {\n try {\n const value = await interactor.getStyleValue(locator, prop);\n return value !== invisibleValue;\n } catch (e) {\n // Element may disappear or detached while being checked because of animation\n // when it happens, an error is thrown. In this case, if indeed the element\n // is not visible, we return false. Otherwise, we re-throw the error.\n if ((await interactor.exists(locator)) === false) {\n return false;\n }\n throw e;\n }\n }\n\n if ((await checkCssVisibility('opacity', '0', this)) === false) {\n return false;\n }\n\n if ((await checkCssVisibility('visibility', 'hidden', this)) === false) {\n return false;\n }\n\n if ((await checkCssVisibility('display', 'none', this)) === false) {\n return false;\n }\n\n return true;\n }\n\n async hasCssClass(locator: PartLocator, className: string): Promise<boolean> {\n const classNames = await this.getAttribute(locator, 'class');\n if (classNames == null) {\n return false;\n }\n\n const names = classNames.split(/\\s+/);\n return names.includes(className);\n }\n\n async hasAttribute(locator: PartLocator, name: string): Promise<boolean> {\n const attrValue = await this.getAttribute(locator, name);\n return attrValue != null;\n }\n\n //#region\n async innerHTML(locator: PartLocator): Promise<string> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n return this.page.locator(cssLocator).innerHTML();\n }\n //#endregion\n}\n","import { ITestEngineOption, ScenePart, TestEngine } from '@atomic-testing/core';\nimport { Page } from '@playwright/test';\n\nimport { PlaywrightInteractor } from './PlaywrightInteractor';\n\n/**\n * Create a {@link TestEngine} instance backed by Playwright.\n *\n * @param page - Playwright page used for interaction.\n * @param partDefinitions - Scene part definitions describing the scene\n * structure for the engine.\n * @param _option - Reserved for entry-point symmetry with the other adapters;\n * currently ignored. `rootElement` is not applicable to Playwright, which drives\n * a real browser page rather than mounting into a host element.\n * @returns A configured {@link TestEngine} ready for use.\n */\nexport function createTestEngine<T extends ScenePart>(\n page: Page,\n partDefinitions: T,\n _option?: ITestEngineOption\n): TestEngine<T> {\n const engine = new TestEngine([], new PlaywrightInteractor(page), {\n parts: partDefinitions,\n });\n\n return engine;\n}\n"],"mappings":"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"}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BlurOption, BoundingRect, ClickOption, CssProperty, EnterTextOption, FocusOption, HoverOption, Interactor, MouseDownOption, MouseEnterOption, MouseLeaveOption, MouseMoveOption, MouseOutOption, MouseUpOption, Optional, PartLocator, Point, PressKeyOption, ScenePart, TestEngine, WaitForOption, WaitUntilOption } from "@atomic-testing/core";
|
|
1
|
+
import { BlurOption, BoundingRect, ClickOption, CssProperty, EnterTextOption, FocusOption, HoverOption, ITestEngineOption, Interactor, MouseDownOption, MouseEnterOption, MouseLeaveOption, MouseMoveOption, MouseOutOption, MouseUpOption, Optional, PartLocator, Point, PressKeyOption, ScenePart, TestEngine, WaitForOption, WaitUntilOption } from "@atomic-testing/core";
|
|
2
2
|
import { Page } from "@playwright/test";
|
|
3
3
|
|
|
4
4
|
//#region src/createTestEngine.d.ts
|
|
@@ -8,9 +8,12 @@ import { Page } from "@playwright/test";
|
|
|
8
8
|
* @param page - Playwright page used for interaction.
|
|
9
9
|
* @param partDefinitions - Scene part definitions describing the scene
|
|
10
10
|
* structure for the engine.
|
|
11
|
+
* @param _option - Reserved for entry-point symmetry with the other adapters;
|
|
12
|
+
* currently ignored. `rootElement` is not applicable to Playwright, which drives
|
|
13
|
+
* a real browser page rather than mounting into a host element.
|
|
11
14
|
* @returns A configured {@link TestEngine} ready for use.
|
|
12
15
|
*/
|
|
13
|
-
declare function createTestEngine<T extends ScenePart>(page: Page, partDefinitions: T): TestEngine<T>;
|
|
16
|
+
declare function createTestEngine<T extends ScenePart>(page: Page, partDefinitions: T, _option?: ITestEngineOption): TestEngine<T>;
|
|
14
17
|
//#endregion
|
|
15
18
|
//#region src/PlaywrightInteractor.d.ts
|
|
16
19
|
/**
|
|
@@ -157,7 +160,6 @@ declare class PlaywrightInteractor implements Interactor {
|
|
|
157
160
|
blur(locator: PartLocator, _option?: Partial<BlurOption>): Promise<void>;
|
|
158
161
|
pressKey(locator: PartLocator, key: string, option?: Partial<PressKeyOption>): Promise<void>;
|
|
159
162
|
activate(locator: PartLocator): Promise<void>;
|
|
160
|
-
wait(ms: number): Promise<void>;
|
|
161
163
|
waitUntilComponentState(locator: PartLocator, option?: Partial<Readonly<WaitForOption>>): Promise<void>;
|
|
162
164
|
waitUntil<T>(option: WaitUntilOption<T>): Promise<T>;
|
|
163
165
|
getAttribute(locator: PartLocator, name: string, isMultiple: true): Promise<readonly string[]>;
|
|
@@ -180,11 +182,12 @@ declare class PlaywrightInteractor implements Interactor {
|
|
|
180
182
|
isChecked(locator: PartLocator): Promise<boolean>;
|
|
181
183
|
isDisabled(locator: PartLocator): Promise<boolean>;
|
|
182
184
|
isReadonly(locator: PartLocator): Promise<boolean>;
|
|
185
|
+
isRequired(locator: PartLocator): Promise<boolean>;
|
|
186
|
+
isError(locator: PartLocator): Promise<boolean>;
|
|
183
187
|
isVisible(locator: PartLocator): Promise<boolean>;
|
|
184
188
|
hasCssClass(locator: PartLocator, className: string): Promise<boolean>;
|
|
185
189
|
hasAttribute(locator: PartLocator, name: string): Promise<boolean>;
|
|
186
190
|
innerHTML(locator: PartLocator): Promise<string>;
|
|
187
|
-
clone(): Interactor;
|
|
188
191
|
}
|
|
189
192
|
//#endregion
|
|
190
193
|
export { PlaywrightInteractor, createTestEngine };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BlurOption, BoundingRect, ClickOption, CssProperty, EnterTextOption, FocusOption, HoverOption, Interactor, MouseDownOption, MouseEnterOption, MouseLeaveOption, MouseMoveOption, MouseOutOption, MouseUpOption, Optional, PartLocator, Point, PressKeyOption, ScenePart, TestEngine, WaitForOption, WaitUntilOption } from "@atomic-testing/core";
|
|
1
|
+
import { BlurOption, BoundingRect, ClickOption, CssProperty, EnterTextOption, FocusOption, HoverOption, ITestEngineOption, Interactor, MouseDownOption, MouseEnterOption, MouseLeaveOption, MouseMoveOption, MouseOutOption, MouseUpOption, Optional, PartLocator, Point, PressKeyOption, ScenePart, TestEngine, WaitForOption, WaitUntilOption } from "@atomic-testing/core";
|
|
2
2
|
import { Page } from "@playwright/test";
|
|
3
3
|
|
|
4
4
|
//#region src/createTestEngine.d.ts
|
|
@@ -8,9 +8,12 @@ import { Page } from "@playwright/test";
|
|
|
8
8
|
* @param page - Playwright page used for interaction.
|
|
9
9
|
* @param partDefinitions - Scene part definitions describing the scene
|
|
10
10
|
* structure for the engine.
|
|
11
|
+
* @param _option - Reserved for entry-point symmetry with the other adapters;
|
|
12
|
+
* currently ignored. `rootElement` is not applicable to Playwright, which drives
|
|
13
|
+
* a real browser page rather than mounting into a host element.
|
|
11
14
|
* @returns A configured {@link TestEngine} ready for use.
|
|
12
15
|
*/
|
|
13
|
-
declare function createTestEngine<T extends ScenePart>(page: Page, partDefinitions: T): TestEngine<T>;
|
|
16
|
+
declare function createTestEngine<T extends ScenePart>(page: Page, partDefinitions: T, _option?: ITestEngineOption): TestEngine<T>;
|
|
14
17
|
//#endregion
|
|
15
18
|
//#region src/PlaywrightInteractor.d.ts
|
|
16
19
|
/**
|
|
@@ -157,7 +160,6 @@ declare class PlaywrightInteractor implements Interactor {
|
|
|
157
160
|
blur(locator: PartLocator, _option?: Partial<BlurOption>): Promise<void>;
|
|
158
161
|
pressKey(locator: PartLocator, key: string, option?: Partial<PressKeyOption>): Promise<void>;
|
|
159
162
|
activate(locator: PartLocator): Promise<void>;
|
|
160
|
-
wait(ms: number): Promise<void>;
|
|
161
163
|
waitUntilComponentState(locator: PartLocator, option?: Partial<Readonly<WaitForOption>>): Promise<void>;
|
|
162
164
|
waitUntil<T>(option: WaitUntilOption<T>): Promise<T>;
|
|
163
165
|
getAttribute(locator: PartLocator, name: string, isMultiple: true): Promise<readonly string[]>;
|
|
@@ -180,11 +182,12 @@ declare class PlaywrightInteractor implements Interactor {
|
|
|
180
182
|
isChecked(locator: PartLocator): Promise<boolean>;
|
|
181
183
|
isDisabled(locator: PartLocator): Promise<boolean>;
|
|
182
184
|
isReadonly(locator: PartLocator): Promise<boolean>;
|
|
185
|
+
isRequired(locator: PartLocator): Promise<boolean>;
|
|
186
|
+
isError(locator: PartLocator): Promise<boolean>;
|
|
183
187
|
isVisible(locator: PartLocator): Promise<boolean>;
|
|
184
188
|
hasCssClass(locator: PartLocator, className: string): Promise<boolean>;
|
|
185
189
|
hasAttribute(locator: PartLocator, name: string): Promise<boolean>;
|
|
186
190
|
innerHTML(locator: PartLocator): Promise<string>;
|
|
187
|
-
clone(): Interactor;
|
|
188
191
|
}
|
|
189
192
|
//#endregion
|
|
190
193
|
export { PlaywrightInteractor, createTestEngine };
|
package/dist/index.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { ElementNotFoundError, TestEngine, byCssSelector, dateUtil, defaultWaitF
|
|
|
3
3
|
/**
|
|
4
4
|
* Implementation of the {@link Interactor} interface using Playwright.
|
|
5
5
|
*/
|
|
6
|
-
var PlaywrightInteractor = class
|
|
6
|
+
var PlaywrightInteractor = class {
|
|
7
7
|
/**
|
|
8
8
|
* @param page - Playwright page instance used to drive the browser.
|
|
9
9
|
*/
|
|
@@ -292,9 +292,6 @@ var PlaywrightInteractor = class PlaywrightInteractor {
|
|
|
292
292
|
const cssLocator = await locatorUtil.toCssSelector(locator, this);
|
|
293
293
|
await this.runMutation(locator, "activate", () => this.page.locator(cssLocator).dispatchEvent("click"));
|
|
294
294
|
}
|
|
295
|
-
wait(ms) {
|
|
296
|
-
return timingUtil.wait(ms);
|
|
297
|
-
}
|
|
298
295
|
async waitUntilComponentState(locator, option = defaultWaitForOption) {
|
|
299
296
|
return interactorUtil.interactorWaitUtil(locator, this, option);
|
|
300
297
|
}
|
|
@@ -356,6 +353,13 @@ var PlaywrightInteractor = class PlaywrightInteractor {
|
|
|
356
353
|
async isReadonly(locator) {
|
|
357
354
|
return await this.getAttribute(locator, "readonly") != null;
|
|
358
355
|
}
|
|
356
|
+
async isRequired(locator) {
|
|
357
|
+
if (await this.getAttribute(locator, "required") != null) return true;
|
|
358
|
+
return await this.getAttribute(locator, "aria-required") === "true";
|
|
359
|
+
}
|
|
360
|
+
async isError(locator) {
|
|
361
|
+
return await this.getAttribute(locator, "aria-invalid") === "true";
|
|
362
|
+
}
|
|
359
363
|
async isVisible(locator) {
|
|
360
364
|
if (!await this.exists(locator)) return false;
|
|
361
365
|
async function checkCssVisibility(prop, invisibleValue, interactor) {
|
|
@@ -383,9 +387,6 @@ var PlaywrightInteractor = class PlaywrightInteractor {
|
|
|
383
387
|
const cssLocator = await locatorUtil.toCssSelector(locator, this);
|
|
384
388
|
return this.page.locator(cssLocator).innerHTML();
|
|
385
389
|
}
|
|
386
|
-
clone() {
|
|
387
|
-
return new PlaywrightInteractor(this.page);
|
|
388
|
-
}
|
|
389
390
|
};
|
|
390
391
|
//#endregion
|
|
391
392
|
//#region src/createTestEngine.ts
|
|
@@ -395,9 +396,12 @@ var PlaywrightInteractor = class PlaywrightInteractor {
|
|
|
395
396
|
* @param page - Playwright page used for interaction.
|
|
396
397
|
* @param partDefinitions - Scene part definitions describing the scene
|
|
397
398
|
* structure for the engine.
|
|
399
|
+
* @param _option - Reserved for entry-point symmetry with the other adapters;
|
|
400
|
+
* currently ignored. `rootElement` is not applicable to Playwright, which drives
|
|
401
|
+
* a real browser page rather than mounting into a host element.
|
|
398
402
|
* @returns A configured {@link TestEngine} ready for use.
|
|
399
403
|
*/
|
|
400
|
-
function createTestEngine(page, partDefinitions) {
|
|
404
|
+
function createTestEngine(page, partDefinitions, _option) {
|
|
401
405
|
return new TestEngine([], new PlaywrightInteractor(page), { parts: partDefinitions });
|
|
402
406
|
}
|
|
403
407
|
//#endregion
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/PlaywrightInteractor.ts","../src/createTestEngine.ts"],"sourcesContent":["import {\n BlurOption,\n BoundingRect,\n byCssSelector,\n ClickOption,\n CssProperty,\n dateUtil,\n defaultWaitForOption,\n ElementNotFoundError,\n EnterTextOption,\n FocusOption,\n HoverOption,\n Interactor,\n interactorUtil,\n locatorUtil,\n MouseEnterOption,\n MouseLeaveOption,\n MouseOutOption,\n MouseDownOption,\n MouseMoveOption,\n MouseUpOption,\n Optional,\n PartLocator,\n Point,\n PressKeyOption,\n timingUtil,\n WaitForOption,\n WaitUntilOption,\n} from '@atomic-testing/core';\nimport { Page } from '@playwright/test';\n\n/**\n * Implementation of the {@link Interactor} interface using Playwright.\n */\nexport class PlaywrightInteractor implements Interactor {\n /**\n * @param page - Playwright page instance used to drive the browser.\n */\n constructor(public readonly page: Page) {}\n\n /**\n * Run a Playwright mutation and normalize a \"locator matched nothing\" failure\n * into {@link ElementNotFoundError}, so a missing element throws the same error\n * class here as it does in `DOMInteractor`, regardless of environment (the\n * unified contract ratified in ADR-006).\n *\n * Playwright auto-waits for actionability and then throws its own\n * `TimeoutError`. We translate that to `ElementNotFoundError` ONLY when the\n * element genuinely does not exist (count 0) and otherwise rethrow the original\n * error — preserving Playwright's auto-wait for an element that exists but is\n * briefly not actionable (covered, disabled, animating). The trade-off is that\n * a truly-missing element waits out the page's action timeout before throwing;\n * bound it with `page.setDefaultTimeout` when fast failure matters.\n *\n * @param locator - Locator the mutation targets\n * @param action - Method name used in the error message (e.g. `'click'`)\n * @param run - The Playwright action to execute\n * @throws {ElementNotFoundError} If the action fails and the element is absent\n */\n private async runMutation<T>(locator: PartLocator, action: string, run: () => Promise<T>): Promise<T> {\n try {\n return await run();\n } catch (e) {\n if ((await this.exists(locator)) === false) {\n throw new ElementNotFoundError(locator, action);\n }\n throw e;\n }\n }\n\n /**\n * Select the given option values on a `<select>` element.\n *\n * @param locator - Locator to the `<select>` element.\n * @param values - Values to select.\n * @throws {ElementNotFoundError} If the element is not found\n */\n async selectOptionValue(locator: PartLocator, values: string[]): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'selectOptionValue', () => this.page.locator(cssLocator).selectOption(values));\n }\n\n /**\n * Set the selected files on a `<input type=\"file\">` element.\n *\n * Playwright's native `setInputFiles` reads the given paths from disk and\n * populates the input's `FileList`, firing the change event — the only way to\n * fill a file input, whose value cannot be set programmatically.\n *\n * @param locator - Locator of the `<input type=\"file\">` element\n * @param files - One or more filesystem paths to upload\n * @throws {ElementNotFoundError} If the element is not found\n */\n async setInputFiles(locator: PartLocator, files: string | string[]): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'setInputFiles', () => this.page.locator(cssLocator).setInputFiles(files));\n }\n\n /**\n * Scroll the located element into view, no-op if already visible.\n *\n * Delegates to Playwright's `scrollIntoViewIfNeeded`, which performs a real\n * layout-aware scroll in the browser.\n *\n * @param locator - Locator of the element to scroll into view\n * @throws {ElementNotFoundError} If the element is not found\n */\n async scrollIntoView(locator: PartLocator): Promise<void> {\n const css = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'scrollIntoView', () => this.page.locator(css).scrollIntoViewIfNeeded());\n }\n\n /**\n * Scroll the located element by the given pixel delta.\n *\n * The scroll is performed by evaluating `el.scrollBy(dx, dy)` on the element\n * itself rather than `page.mouse.wheel`. A wheel event scrolls whatever sits\n * under the pointer and is non-deterministic across chromium/firefox/webkit,\n * whereas evaluating `scrollBy` on the resolved element scrolls exactly that\n * element. This is a deliberate deviation from ADR 0001's per-engine table\n * (which lists `page.mouse.wheel`), taking the alternative the step-5 prompt\n * permits (\"or evaluate el.scrollBy\") for cross-browser determinism.\n *\n * @param locator - Locator of the scrollable element\n * @param delta - Pixel offset to scroll by\n * @throws {ElementNotFoundError} If the element is not found\n */\n async scrollBy(locator: PartLocator, delta: Point): Promise<void> {\n const css = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'scrollBy', () =>\n this.page.locator(css).evaluate((el, d) => el.scrollBy(d.x, d.y), { x: delta.x, y: delta.y })\n );\n }\n\n /**\n * Drag the source element and drop it onto the target element.\n *\n * Delegates to Playwright's native `Locator.dragTo`, which performs a real,\n * layout-aware drag gesture in the browser.\n *\n * @param source - Locator of the element to drag\n * @param target - Locator of the drop target\n * @throws {ElementNotFoundError} If either the source or target is not found\n */\n async dragTo(source: PartLocator, target: PartLocator): Promise<void> {\n const srcCss = await locatorUtil.toCssSelector(source, this);\n const tgtCss = await locatorUtil.toCssSelector(target, this);\n try {\n await this.page.locator(srcCss).dragTo(this.page.locator(tgtCss));\n } catch (e) {\n if ((await this.exists(source)) === false) {\n throw new ElementNotFoundError(source, 'dragTo');\n }\n if ((await this.exists(target)) === false) {\n throw new ElementNotFoundError(target, 'dragTo');\n }\n throw e;\n }\n }\n\n /**\n * Drag the located element by the given pixel delta from its center.\n *\n * The gesture is a single uninterrupted `move → down → move → up` sequence\n * computed from the element's center. It deliberately does NOT reuse\n * {@link mouseMove}/{@link mouseDown} — `mouseMove` resets the pointer with\n * `page.mouse.move(0, 0)` after hovering, which would corrupt the drag path\n * (see ADR 0001, option 5). The center comes from {@link getBoundingRect},\n * which throws `ElementNotFoundError` when the element has no box, so this\n * shares that \"element not found\" contract instead of re-deriving the box +\n * guard here.\n *\n * @param locator - Locator of the element to drag\n * @param delta - Pixel offset to drag by\n * @throws {ElementNotFoundError} If the element has no bounding box\n */\n async drag(locator: PartLocator, delta: Point): Promise<void> {\n const rect = await this.getBoundingRect(locator);\n const cx = rect.x + rect.width / 2;\n const cy = rect.y + rect.height / 2;\n await this.page.mouse.move(cx, cy);\n await this.page.mouse.down();\n await this.page.mouse.move(cx + delta.x, cy + delta.y, { steps: 8 });\n await this.page.mouse.up();\n }\n\n /**\n * Get the value of an `<input>` element.\n *\n * @param locator - Locator pointing to the input element.\n * @returns The current value of the input or `undefined` if not present.\n */\n async getInputValue(locator: PartLocator): Promise<Optional<string>> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n return this.page.locator(cssLocator).inputValue();\n }\n\n /**\n * Retrieve the values of selected options within a `<select>` element.\n *\n * @param locator - Locator to the `<select>` element.\n * @returns Array of selected option values or `undefined` when no option is selected.\n */\n async getSelectValues(locator: PartLocator): Promise<Optional<readonly string[]>> {\n const optionLocator: PartLocator = byCssSelector('option:checked');\n const selectedOptionLocator = locatorUtil.append(locator, optionLocator);\n const cssLocator = await locatorUtil.toCssSelector(selectedOptionLocator, this);\n const allOptions = await this.page.locator(cssLocator).all();\n const values: string[] = [];\n for (const option of allOptions) {\n const value = await option.getAttribute('value');\n if (value != null) {\n values.push(value);\n }\n }\n return values;\n }\n\n async getSelectLabels(locator: PartLocator): Promise<Optional<readonly string[]>> {\n const optionLocator: PartLocator = byCssSelector('option:checked');\n const selectedOptionLocator = locatorUtil.append(locator, optionLocator);\n const cssLocator = await locatorUtil.toCssSelector(selectedOptionLocator, this);\n const allOptions = await this.page.locator(cssLocator).all();\n const labels: string[] = [];\n for (const option of allOptions) {\n const label = await option.textContent();\n if (label != null) {\n labels.push(label);\n }\n }\n return labels;\n }\n\n async getStyleValue(locator: PartLocator, propertyName: CssProperty): Promise<Optional<string>> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n const elLocator = this.page.locator(cssLocator);\n const value = await elLocator.evaluate((element, prop) => {\n return window.getComputedStyle(element).getPropertyValue(prop as string);\n }, propertyName);\n return value;\n }\n\n async enterText(locator: PartLocator, text: string, option?: Optional<Partial<EnterTextOption>>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'enterText', async () => {\n if (!option?.append) {\n await this.page.locator(cssLocator).clear();\n }\n\n // If it is a date, time or datetime-local input, validate the date format\n const type = (await this.getAttribute(locator, 'type')) ?? '';\n if (dateUtil.isHtmlDateInputType(type)) {\n const result = dateUtil.validateHtmlDateInput(type, text);\n if (!result.valid) {\n throw new Error(\n `Invalid date format for type: ${type}, expected format: ${result.format}, example: ${result.example}`\n );\n }\n }\n await this.page.locator(cssLocator).fill(text);\n });\n }\n\n async setRangeValue(locator: PartLocator, value: number): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n // Playwright's `fill` rejects `<input type=\"range\">` (it is not a fillable\n // text control), so set the value in-page through the native value setter.\n // Calling the prototype setter both sanitizes the value to the input's step\n // (the browser snaps an off-step target to the nearest valid step) and lets\n // React's value tracker observe the change; the dispatched input/change\n // events then drive a controlled component (e.g. MUI Slider) to re-render.\n await this.runMutation(locator, 'setRangeValue', () =>\n this.page.locator(cssLocator).evaluate((el, nextValue) => {\n const input = el as HTMLInputElement;\n const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')?.set;\n setter?.call(input, nextValue);\n input.dispatchEvent(new Event('input', { bubbles: true }));\n input.dispatchEvent(new Event('change', { bubbles: true }));\n }, String(value))\n );\n }\n\n async click(locator: PartLocator, option?: Partial<ClickOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'click', () => this.page.locator(cssLocator).click({ position: option?.position }));\n }\n\n /**\n * Dispatch a right-click on the located element to open its context menu.\n *\n * Delegates to Playwright's native right-button click, which fires a real\n * `contextmenu` event in the browser.\n *\n * @param locator - Locator of the element to right-click\n * @throws {ElementNotFoundError} If the element is not found\n */\n async contextMenu(locator: PartLocator): Promise<void> {\n const css = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'contextMenu', () => this.page.locator(css).click({ button: 'right' }));\n }\n\n async hover(locator: PartLocator, option?: Partial<HoverOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'hover', () => this.page.locator(cssLocator).hover({ position: option?.position }));\n }\n\n async mouseMove(locator: PartLocator, option?: Partial<MouseMoveOption>): Promise<void> {\n await this.runMutation(locator, 'mouseMove', async () => {\n await this.hover(locator, { position: option?.position });\n await this.page.mouse.move(0, 0);\n });\n }\n\n async mouseDown(locator: PartLocator, option?: Partial<MouseDownOption>): Promise<void> {\n await this.runMutation(locator, 'mouseDown', async () => {\n await this.hover(locator, { position: option?.position });\n await this.page.mouse.down();\n });\n }\n\n async mouseUp(locator: PartLocator, option?: Partial<MouseUpOption>): Promise<void> {\n await this.runMutation(locator, 'mouseUp', async () => {\n await this.hover(locator, { position: option?.position });\n await this.page.mouse.up();\n });\n }\n\n async mouseOver(locator: PartLocator, option?: Partial<HoverOption>): Promise<void> {\n await this.runMutation(locator, 'mouseOver', () => this.hover(locator, option));\n }\n\n async mouseOut(locator: PartLocator, _option?: Partial<MouseOutOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'mouseOut', async () => {\n // First hover over the element to trigger mouseenter/mouseover\n await this.page.locator(cssLocator).hover();\n // Then dispatch mouseout event directly for cross-browser reliability\n await this.page.locator(cssLocator).dispatchEvent('mouseout');\n });\n }\n\n async mouseEnter(locator: PartLocator, _option?: Partial<MouseEnterOption>): Promise<void> {\n await this.runMutation(locator, 'mouseEnter', () => this.hover(locator));\n }\n\n async mouseLeave(locator: PartLocator, _option?: Partial<MouseLeaveOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'mouseLeave', async () => {\n // First hover over the element to trigger mouseenter/mouseover\n await this.page.locator(cssLocator).hover();\n // Dispatch mouseout which triggers both mouseout and mouseleave handlers in React\n await this.page.locator(cssLocator).dispatchEvent('mouseout');\n });\n }\n\n async focus(locator: PartLocator, _option?: Partial<FocusOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'focus', () => this.page.focus(cssLocator));\n }\n\n async blur(locator: PartLocator, _option?: Partial<BlurOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'blur', () => this.page.locator(cssLocator).blur());\n }\n\n async pressKey(locator: PartLocator, key: string, option?: Partial<PressKeyOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n // Compose Playwright's chord syntax — modifiers joined to the key by `+`, in\n // Playwright's accepted Control+Alt+Shift+Meta order — so the browser holds\n // those modifiers across the keypress and the event carries ctrlKey/etc.\n const modifiers: string[] = [];\n if (option?.ctrl) {\n modifiers.push('Control');\n }\n if (option?.alt) {\n modifiers.push('Alt');\n }\n if (option?.shift) {\n modifiers.push('Shift');\n }\n if (option?.meta) {\n modifiers.push('Meta');\n }\n const chord = modifiers.length > 0 ? `${modifiers.join('+')}+${key}` : key;\n // locator.press auto-focuses the element, then dispatches a real, trusted\n // KeyboardEvent — the browser equivalent of the DOM focus-first keyDown/keyUp.\n // Caveat: for Shift + a printable key the browser case-folds `event.key`\n // (`Shift+a` → `'A'`) whereas the jsdom path keeps `'a'` — only the modifier\n // flags are delivered identically across engines (see #924).\n await this.runMutation(locator, 'pressKey', () => this.page.locator(cssLocator).press(chord));\n }\n\n async activate(locator: PartLocator): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n // Geometry-free activation mirrors the mouseout dispatch precedent above: it\n // bypasses hit-testing to actuate a covered or zero-size input that\n // locator.click() (a real geometry hit-test) cannot reach.\n await this.runMutation(locator, 'activate', () => this.page.locator(cssLocator).dispatchEvent('click'));\n }\n\n //#region wait conditions\n wait(ms: number): Promise<void> {\n return timingUtil.wait(ms);\n }\n\n async waitUntilComponentState(\n locator: PartLocator,\n option: Partial<Readonly<WaitForOption>> = defaultWaitForOption\n ): Promise<void> {\n return interactorUtil.interactorWaitUtil(locator, this, option);\n }\n\n waitUntil<T>(option: WaitUntilOption<T>): Promise<T> {\n return timingUtil.waitUntil(option);\n }\n //#endregion\n\n async getAttribute(locator: PartLocator, name: string, isMultiple: true): Promise<readonly string[]>;\n async getAttribute(locator: PartLocator, name: string, isMultiple: false): Promise<Optional<string>>;\n async getAttribute(locator: PartLocator, name: string): Promise<Optional<string>>;\n async getAttribute(\n locator: PartLocator,\n name: string,\n isMultiple?: boolean\n ): Promise<Optional<string> | readonly string[]> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n const elLocator = this.page.locator(cssLocator);\n if (isMultiple) {\n const locators = await elLocator.all();\n const values: string[] = [];\n for (const locator of locators) {\n const value = await locator.getAttribute(name);\n if (value != null) {\n values.push(value);\n }\n }\n return values;\n }\n const value = await elLocator.getAttribute(name);\n return value ?? undefined;\n }\n\n async getText(locator: PartLocator): Promise<Optional<string>> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n const text = await this.page.locator(cssLocator).textContent();\n return text ?? undefined;\n }\n\n /**\n * Get the located element's bounding rectangle.\n *\n * `boundingBox()` returns `null` for a detached/invisible element rather than\n * auto-waiting, so this throws `ElementNotFoundError` directly (no auto-wait)\n * — matching the unified \"element not found\" contract (ADR-006).\n *\n * @param locator - Locator of the element to measure\n * @returns The element's bounding rectangle in CSS pixels\n * @throws {ElementNotFoundError} If the element has no bounding box\n */\n async getBoundingRect(locator: PartLocator): Promise<BoundingRect> {\n const css = await locatorUtil.toCssSelector(locator, this);\n const box = await this.page.locator(css).boundingBox();\n if (box == null) {\n throw new ElementNotFoundError(locator, 'getBoundingRect');\n }\n return { x: box.x, y: box.y, width: box.width, height: box.height };\n }\n\n async exists(locator: PartLocator): Promise<boolean> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n const count = await this.page.locator(cssLocator).count();\n return count > 0;\n }\n\n async isChecked(locator: PartLocator): Promise<boolean> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n const checked = await this.page.locator(cssLocator).isChecked();\n return checked;\n }\n\n async isDisabled(locator: PartLocator): Promise<boolean> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n const isDisabled = await this.page.locator(cssLocator).isDisabled();\n return isDisabled;\n }\n\n async isReadonly(locator: PartLocator): Promise<boolean> {\n const readonly = await this.getAttribute(locator, 'readonly');\n return readonly != null;\n }\n\n async isVisible(locator: PartLocator): Promise<boolean> {\n const exists = await this.exists(locator);\n if (!exists) {\n return false;\n }\n\n async function checkCssVisibility(\n prop: CssProperty,\n invisibleValue: string,\n interactor: PlaywrightInteractor\n ): Promise<boolean> {\n try {\n const value = await interactor.getStyleValue(locator, prop);\n return value !== invisibleValue;\n } catch (e) {\n // Element may disappear or detached while being checked because of animation\n // when it happens, an error is thrown. In this case, if indeed the element\n // is not visible, we return false. Otherwise, we re-throw the error.\n if ((await interactor.exists(locator)) === false) {\n return false;\n }\n throw e;\n }\n }\n\n if ((await checkCssVisibility('opacity', '0', this)) === false) {\n return false;\n }\n\n if ((await checkCssVisibility('visibility', 'hidden', this)) === false) {\n return false;\n }\n\n if ((await checkCssVisibility('display', 'none', this)) === false) {\n return false;\n }\n\n return true;\n }\n\n async hasCssClass(locator: PartLocator, className: string): Promise<boolean> {\n const classNames = await this.getAttribute(locator, 'class');\n if (classNames == null) {\n return false;\n }\n\n const names = classNames.split(/\\s+/);\n return names.includes(className);\n }\n\n async hasAttribute(locator: PartLocator, name: string): Promise<boolean> {\n const attrValue = await this.getAttribute(locator, name);\n return attrValue != null;\n }\n\n //#region\n async innerHTML(locator: PartLocator): Promise<string> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n return this.page.locator(cssLocator).innerHTML();\n }\n //#endregion\n\n clone(): Interactor {\n return new PlaywrightInteractor(this.page);\n }\n}\n","import { ScenePart, TestEngine } from '@atomic-testing/core';\nimport { Page } from '@playwright/test';\n\nimport { PlaywrightInteractor } from './PlaywrightInteractor';\n\n/**\n * Create a {@link TestEngine} instance backed by Playwright.\n *\n * @param page - Playwright page used for interaction.\n * @param partDefinitions - Scene part definitions describing the scene\n * structure for the engine.\n * @returns A configured {@link TestEngine} ready for use.\n */\nexport function createTestEngine<T extends ScenePart>(page: Page, partDefinitions: T): TestEngine<T> {\n const engine = new TestEngine([], new PlaywrightInteractor(page), {\n parts: partDefinitions,\n });\n\n return engine;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/PlaywrightInteractor.ts","../src/createTestEngine.ts"],"sourcesContent":["import {\n BlurOption,\n BoundingRect,\n byCssSelector,\n ClickOption,\n CssProperty,\n dateUtil,\n defaultWaitForOption,\n ElementNotFoundError,\n EnterTextOption,\n FocusOption,\n HoverOption,\n Interactor,\n interactorUtil,\n locatorUtil,\n MouseEnterOption,\n MouseLeaveOption,\n MouseOutOption,\n MouseDownOption,\n MouseMoveOption,\n MouseUpOption,\n Optional,\n PartLocator,\n Point,\n PressKeyOption,\n timingUtil,\n WaitForOption,\n WaitUntilOption,\n} from '@atomic-testing/core';\nimport { Page } from '@playwright/test';\n\n/**\n * Implementation of the {@link Interactor} interface using Playwright.\n */\nexport class PlaywrightInteractor implements Interactor {\n /**\n * @param page - Playwright page instance used to drive the browser.\n */\n constructor(public readonly page: Page) {}\n\n /**\n * Run a Playwright mutation and normalize a \"locator matched nothing\" failure\n * into {@link ElementNotFoundError}, so a missing element throws the same error\n * class here as it does in `DOMInteractor`, regardless of environment (the\n * unified contract ratified in ADR-006).\n *\n * Playwright auto-waits for actionability and then throws its own\n * `TimeoutError`. We translate that to `ElementNotFoundError` ONLY when the\n * element genuinely does not exist (count 0) and otherwise rethrow the original\n * error — preserving Playwright's auto-wait for an element that exists but is\n * briefly not actionable (covered, disabled, animating). The trade-off is that\n * a truly-missing element waits out the page's action timeout before throwing;\n * bound it with `page.setDefaultTimeout` when fast failure matters.\n *\n * @param locator - Locator the mutation targets\n * @param action - Method name used in the error message (e.g. `'click'`)\n * @param run - The Playwright action to execute\n * @throws {ElementNotFoundError} If the action fails and the element is absent\n */\n private async runMutation<T>(locator: PartLocator, action: string, run: () => Promise<T>): Promise<T> {\n try {\n return await run();\n } catch (e) {\n if ((await this.exists(locator)) === false) {\n throw new ElementNotFoundError(locator, action);\n }\n throw e;\n }\n }\n\n /**\n * Select the given option values on a `<select>` element.\n *\n * @param locator - Locator to the `<select>` element.\n * @param values - Values to select.\n * @throws {ElementNotFoundError} If the element is not found\n */\n async selectOptionValue(locator: PartLocator, values: string[]): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'selectOptionValue', () => this.page.locator(cssLocator).selectOption(values));\n }\n\n /**\n * Set the selected files on a `<input type=\"file\">` element.\n *\n * Playwright's native `setInputFiles` reads the given paths from disk and\n * populates the input's `FileList`, firing the change event — the only way to\n * fill a file input, whose value cannot be set programmatically.\n *\n * @param locator - Locator of the `<input type=\"file\">` element\n * @param files - One or more filesystem paths to upload\n * @throws {ElementNotFoundError} If the element is not found\n */\n async setInputFiles(locator: PartLocator, files: string | string[]): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'setInputFiles', () => this.page.locator(cssLocator).setInputFiles(files));\n }\n\n /**\n * Scroll the located element into view, no-op if already visible.\n *\n * Delegates to Playwright's `scrollIntoViewIfNeeded`, which performs a real\n * layout-aware scroll in the browser.\n *\n * @param locator - Locator of the element to scroll into view\n * @throws {ElementNotFoundError} If the element is not found\n */\n async scrollIntoView(locator: PartLocator): Promise<void> {\n const css = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'scrollIntoView', () => this.page.locator(css).scrollIntoViewIfNeeded());\n }\n\n /**\n * Scroll the located element by the given pixel delta.\n *\n * The scroll is performed by evaluating `el.scrollBy(dx, dy)` on the element\n * itself rather than `page.mouse.wheel`. A wheel event scrolls whatever sits\n * under the pointer and is non-deterministic across chromium/firefox/webkit,\n * whereas evaluating `scrollBy` on the resolved element scrolls exactly that\n * element. This is a deliberate deviation from ADR 0001's per-engine table\n * (which lists `page.mouse.wheel`), taking the alternative the step-5 prompt\n * permits (\"or evaluate el.scrollBy\") for cross-browser determinism.\n *\n * @param locator - Locator of the scrollable element\n * @param delta - Pixel offset to scroll by\n * @throws {ElementNotFoundError} If the element is not found\n */\n async scrollBy(locator: PartLocator, delta: Point): Promise<void> {\n const css = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'scrollBy', () =>\n this.page.locator(css).evaluate((el, d) => el.scrollBy(d.x, d.y), { x: delta.x, y: delta.y })\n );\n }\n\n /**\n * Drag the source element and drop it onto the target element.\n *\n * Delegates to Playwright's native `Locator.dragTo`, which performs a real,\n * layout-aware drag gesture in the browser.\n *\n * @param source - Locator of the element to drag\n * @param target - Locator of the drop target\n * @throws {ElementNotFoundError} If either the source or target is not found\n */\n async dragTo(source: PartLocator, target: PartLocator): Promise<void> {\n const srcCss = await locatorUtil.toCssSelector(source, this);\n const tgtCss = await locatorUtil.toCssSelector(target, this);\n try {\n await this.page.locator(srcCss).dragTo(this.page.locator(tgtCss));\n } catch (e) {\n if ((await this.exists(source)) === false) {\n throw new ElementNotFoundError(source, 'dragTo');\n }\n if ((await this.exists(target)) === false) {\n throw new ElementNotFoundError(target, 'dragTo');\n }\n throw e;\n }\n }\n\n /**\n * Drag the located element by the given pixel delta from its center.\n *\n * The gesture is a single uninterrupted `move → down → move → up` sequence\n * computed from the element's center. It deliberately does NOT reuse\n * {@link mouseMove}/{@link mouseDown} — `mouseMove` resets the pointer with\n * `page.mouse.move(0, 0)` after hovering, which would corrupt the drag path\n * (see ADR 0001, option 5). The center comes from {@link getBoundingRect},\n * which throws `ElementNotFoundError` when the element has no box, so this\n * shares that \"element not found\" contract instead of re-deriving the box +\n * guard here.\n *\n * @param locator - Locator of the element to drag\n * @param delta - Pixel offset to drag by\n * @throws {ElementNotFoundError} If the element has no bounding box\n */\n async drag(locator: PartLocator, delta: Point): Promise<void> {\n const rect = await this.getBoundingRect(locator);\n const cx = rect.x + rect.width / 2;\n const cy = rect.y + rect.height / 2;\n await this.page.mouse.move(cx, cy);\n await this.page.mouse.down();\n await this.page.mouse.move(cx + delta.x, cy + delta.y, { steps: 8 });\n await this.page.mouse.up();\n }\n\n /**\n * Get the value of an `<input>` element.\n *\n * @param locator - Locator pointing to the input element.\n * @returns The current value of the input or `undefined` if not present.\n */\n async getInputValue(locator: PartLocator): Promise<Optional<string>> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n return this.page.locator(cssLocator).inputValue();\n }\n\n /**\n * Retrieve the values of selected options within a `<select>` element.\n *\n * @param locator - Locator to the `<select>` element.\n * @returns Array of selected option values or `undefined` when no option is selected.\n */\n async getSelectValues(locator: PartLocator): Promise<Optional<readonly string[]>> {\n const optionLocator: PartLocator = byCssSelector('option:checked');\n const selectedOptionLocator = locatorUtil.append(locator, optionLocator);\n const cssLocator = await locatorUtil.toCssSelector(selectedOptionLocator, this);\n const allOptions = await this.page.locator(cssLocator).all();\n const values: string[] = [];\n for (const option of allOptions) {\n const value = await option.getAttribute('value');\n if (value != null) {\n values.push(value);\n }\n }\n return values;\n }\n\n async getSelectLabels(locator: PartLocator): Promise<Optional<readonly string[]>> {\n const optionLocator: PartLocator = byCssSelector('option:checked');\n const selectedOptionLocator = locatorUtil.append(locator, optionLocator);\n const cssLocator = await locatorUtil.toCssSelector(selectedOptionLocator, this);\n const allOptions = await this.page.locator(cssLocator).all();\n const labels: string[] = [];\n for (const option of allOptions) {\n const label = await option.textContent();\n if (label != null) {\n labels.push(label);\n }\n }\n return labels;\n }\n\n async getStyleValue(locator: PartLocator, propertyName: CssProperty): Promise<Optional<string>> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n const elLocator = this.page.locator(cssLocator);\n const value = await elLocator.evaluate((element, prop) => {\n return window.getComputedStyle(element).getPropertyValue(prop as string);\n }, propertyName);\n return value;\n }\n\n async enterText(locator: PartLocator, text: string, option?: Optional<Partial<EnterTextOption>>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'enterText', async () => {\n if (!option?.append) {\n await this.page.locator(cssLocator).clear();\n }\n\n // If it is a date, time or datetime-local input, validate the date format\n const type = (await this.getAttribute(locator, 'type')) ?? '';\n if (dateUtil.isHtmlDateInputType(type)) {\n const result = dateUtil.validateHtmlDateInput(type, text);\n if (!result.valid) {\n throw new Error(\n `Invalid date format for type: ${type}, expected format: ${result.format}, example: ${result.example}`\n );\n }\n }\n await this.page.locator(cssLocator).fill(text);\n });\n }\n\n async setRangeValue(locator: PartLocator, value: number): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n // Playwright's `fill` rejects `<input type=\"range\">` (it is not a fillable\n // text control), so set the value in-page through the native value setter.\n // Calling the prototype setter both sanitizes the value to the input's step\n // (the browser snaps an off-step target to the nearest valid step) and lets\n // React's value tracker observe the change; the dispatched input/change\n // events then drive a controlled component (e.g. MUI Slider) to re-render.\n await this.runMutation(locator, 'setRangeValue', () =>\n this.page.locator(cssLocator).evaluate((el, nextValue) => {\n const input = el as HTMLInputElement;\n const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')?.set;\n setter?.call(input, nextValue);\n input.dispatchEvent(new Event('input', { bubbles: true }));\n input.dispatchEvent(new Event('change', { bubbles: true }));\n }, String(value))\n );\n }\n\n async click(locator: PartLocator, option?: Partial<ClickOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'click', () => this.page.locator(cssLocator).click({ position: option?.position }));\n }\n\n /**\n * Dispatch a right-click on the located element to open its context menu.\n *\n * Delegates to Playwright's native right-button click, which fires a real\n * `contextmenu` event in the browser.\n *\n * @param locator - Locator of the element to right-click\n * @throws {ElementNotFoundError} If the element is not found\n */\n async contextMenu(locator: PartLocator): Promise<void> {\n const css = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'contextMenu', () => this.page.locator(css).click({ button: 'right' }));\n }\n\n async hover(locator: PartLocator, option?: Partial<HoverOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'hover', () => this.page.locator(cssLocator).hover({ position: option?.position }));\n }\n\n async mouseMove(locator: PartLocator, option?: Partial<MouseMoveOption>): Promise<void> {\n await this.runMutation(locator, 'mouseMove', async () => {\n await this.hover(locator, { position: option?.position });\n await this.page.mouse.move(0, 0);\n });\n }\n\n async mouseDown(locator: PartLocator, option?: Partial<MouseDownOption>): Promise<void> {\n await this.runMutation(locator, 'mouseDown', async () => {\n await this.hover(locator, { position: option?.position });\n await this.page.mouse.down();\n });\n }\n\n async mouseUp(locator: PartLocator, option?: Partial<MouseUpOption>): Promise<void> {\n await this.runMutation(locator, 'mouseUp', async () => {\n await this.hover(locator, { position: option?.position });\n await this.page.mouse.up();\n });\n }\n\n async mouseOver(locator: PartLocator, option?: Partial<HoverOption>): Promise<void> {\n await this.runMutation(locator, 'mouseOver', () => this.hover(locator, option));\n }\n\n async mouseOut(locator: PartLocator, _option?: Partial<MouseOutOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'mouseOut', async () => {\n // First hover over the element to trigger mouseenter/mouseover\n await this.page.locator(cssLocator).hover();\n // Then dispatch mouseout event directly for cross-browser reliability\n await this.page.locator(cssLocator).dispatchEvent('mouseout');\n });\n }\n\n async mouseEnter(locator: PartLocator, _option?: Partial<MouseEnterOption>): Promise<void> {\n await this.runMutation(locator, 'mouseEnter', () => this.hover(locator));\n }\n\n async mouseLeave(locator: PartLocator, _option?: Partial<MouseLeaveOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'mouseLeave', async () => {\n // First hover over the element to trigger mouseenter/mouseover\n await this.page.locator(cssLocator).hover();\n // Dispatch mouseout which triggers both mouseout and mouseleave handlers in React\n await this.page.locator(cssLocator).dispatchEvent('mouseout');\n });\n }\n\n async focus(locator: PartLocator, _option?: Partial<FocusOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'focus', () => this.page.focus(cssLocator));\n }\n\n async blur(locator: PartLocator, _option?: Partial<BlurOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.runMutation(locator, 'blur', () => this.page.locator(cssLocator).blur());\n }\n\n async pressKey(locator: PartLocator, key: string, option?: Partial<PressKeyOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n // Compose Playwright's chord syntax — modifiers joined to the key by `+`, in\n // Playwright's accepted Control+Alt+Shift+Meta order — so the browser holds\n // those modifiers across the keypress and the event carries ctrlKey/etc.\n const modifiers: string[] = [];\n if (option?.ctrl) {\n modifiers.push('Control');\n }\n if (option?.alt) {\n modifiers.push('Alt');\n }\n if (option?.shift) {\n modifiers.push('Shift');\n }\n if (option?.meta) {\n modifiers.push('Meta');\n }\n const chord = modifiers.length > 0 ? `${modifiers.join('+')}+${key}` : key;\n // locator.press auto-focuses the element, then dispatches a real, trusted\n // KeyboardEvent — the browser equivalent of the DOM focus-first keyDown/keyUp.\n // Caveat: for Shift + a printable key the browser case-folds `event.key`\n // (`Shift+a` → `'A'`) whereas the jsdom path keeps `'a'` — only the modifier\n // flags are delivered identically across engines (see #924).\n await this.runMutation(locator, 'pressKey', () => this.page.locator(cssLocator).press(chord));\n }\n\n async activate(locator: PartLocator): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n // Geometry-free activation mirrors the mouseout dispatch precedent above: it\n // bypasses hit-testing to actuate a covered or zero-size input that\n // locator.click() (a real geometry hit-test) cannot reach.\n await this.runMutation(locator, 'activate', () => this.page.locator(cssLocator).dispatchEvent('click'));\n }\n\n //#region wait conditions\n async waitUntilComponentState(\n locator: PartLocator,\n option: Partial<Readonly<WaitForOption>> = defaultWaitForOption\n ): Promise<void> {\n return interactorUtil.interactorWaitUtil(locator, this, option);\n }\n\n waitUntil<T>(option: WaitUntilOption<T>): Promise<T> {\n return timingUtil.waitUntil(option);\n }\n //#endregion\n\n async getAttribute(locator: PartLocator, name: string, isMultiple: true): Promise<readonly string[]>;\n async getAttribute(locator: PartLocator, name: string, isMultiple: false): Promise<Optional<string>>;\n async getAttribute(locator: PartLocator, name: string): Promise<Optional<string>>;\n async getAttribute(\n locator: PartLocator,\n name: string,\n isMultiple?: boolean\n ): Promise<Optional<string> | readonly string[]> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n const elLocator = this.page.locator(cssLocator);\n if (isMultiple) {\n const locators = await elLocator.all();\n const values: string[] = [];\n for (const locator of locators) {\n const value = await locator.getAttribute(name);\n if (value != null) {\n values.push(value);\n }\n }\n return values;\n }\n const value = await elLocator.getAttribute(name);\n return value ?? undefined;\n }\n\n async getText(locator: PartLocator): Promise<Optional<string>> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n const text = await this.page.locator(cssLocator).textContent();\n return text ?? undefined;\n }\n\n /**\n * Get the located element's bounding rectangle.\n *\n * `boundingBox()` returns `null` for a detached/invisible element rather than\n * auto-waiting, so this throws `ElementNotFoundError` directly (no auto-wait)\n * — matching the unified \"element not found\" contract (ADR-006).\n *\n * @param locator - Locator of the element to measure\n * @returns The element's bounding rectangle in CSS pixels\n * @throws {ElementNotFoundError} If the element has no bounding box\n */\n async getBoundingRect(locator: PartLocator): Promise<BoundingRect> {\n const css = await locatorUtil.toCssSelector(locator, this);\n const box = await this.page.locator(css).boundingBox();\n if (box == null) {\n throw new ElementNotFoundError(locator, 'getBoundingRect');\n }\n return { x: box.x, y: box.y, width: box.width, height: box.height };\n }\n\n async exists(locator: PartLocator): Promise<boolean> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n const count = await this.page.locator(cssLocator).count();\n return count > 0;\n }\n\n async isChecked(locator: PartLocator): Promise<boolean> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n const checked = await this.page.locator(cssLocator).isChecked();\n return checked;\n }\n\n async isDisabled(locator: PartLocator): Promise<boolean> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n const isDisabled = await this.page.locator(cssLocator).isDisabled();\n return isDisabled;\n }\n\n async isReadonly(locator: PartLocator): Promise<boolean> {\n const readonly = await this.getAttribute(locator, 'readonly');\n return readonly != null;\n }\n\n async isRequired(locator: PartLocator): Promise<boolean> {\n const required = await this.getAttribute(locator, 'required');\n if (required != null) {\n return true;\n }\n return (await this.getAttribute(locator, 'aria-required')) === 'true';\n }\n\n async isError(locator: PartLocator): Promise<boolean> {\n return (await this.getAttribute(locator, 'aria-invalid')) === 'true';\n }\n\n async isVisible(locator: PartLocator): Promise<boolean> {\n const exists = await this.exists(locator);\n if (!exists) {\n return false;\n }\n\n async function checkCssVisibility(\n prop: CssProperty,\n invisibleValue: string,\n interactor: PlaywrightInteractor\n ): Promise<boolean> {\n try {\n const value = await interactor.getStyleValue(locator, prop);\n return value !== invisibleValue;\n } catch (e) {\n // Element may disappear or detached while being checked because of animation\n // when it happens, an error is thrown. In this case, if indeed the element\n // is not visible, we return false. Otherwise, we re-throw the error.\n if ((await interactor.exists(locator)) === false) {\n return false;\n }\n throw e;\n }\n }\n\n if ((await checkCssVisibility('opacity', '0', this)) === false) {\n return false;\n }\n\n if ((await checkCssVisibility('visibility', 'hidden', this)) === false) {\n return false;\n }\n\n if ((await checkCssVisibility('display', 'none', this)) === false) {\n return false;\n }\n\n return true;\n }\n\n async hasCssClass(locator: PartLocator, className: string): Promise<boolean> {\n const classNames = await this.getAttribute(locator, 'class');\n if (classNames == null) {\n return false;\n }\n\n const names = classNames.split(/\\s+/);\n return names.includes(className);\n }\n\n async hasAttribute(locator: PartLocator, name: string): Promise<boolean> {\n const attrValue = await this.getAttribute(locator, name);\n return attrValue != null;\n }\n\n //#region\n async innerHTML(locator: PartLocator): Promise<string> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n return this.page.locator(cssLocator).innerHTML();\n }\n //#endregion\n}\n","import { ITestEngineOption, ScenePart, TestEngine } from '@atomic-testing/core';\nimport { Page } from '@playwright/test';\n\nimport { PlaywrightInteractor } from './PlaywrightInteractor';\n\n/**\n * Create a {@link TestEngine} instance backed by Playwright.\n *\n * @param page - Playwright page used for interaction.\n * @param partDefinitions - Scene part definitions describing the scene\n * structure for the engine.\n * @param _option - Reserved for entry-point symmetry with the other adapters;\n * currently ignored. `rootElement` is not applicable to Playwright, which drives\n * a real browser page rather than mounting into a host element.\n * @returns A configured {@link TestEngine} ready for use.\n */\nexport function createTestEngine<T extends ScenePart>(\n page: Page,\n partDefinitions: T,\n _option?: ITestEngineOption\n): TestEngine<T> {\n const engine = new TestEngine([], new PlaywrightInteractor(page), {\n parts: partDefinitions,\n });\n\n return engine;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atomic-testing/playwright",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.91.0",
|
|
4
4
|
"description": "Atomic Testing Playwright Adapter",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"license": "MIT",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
}
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@atomic-testing/core": "0.
|
|
28
|
+
"@atomic-testing/core": "0.91.0"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
31
|
"@playwright/test": ">=1.50.0"
|
|
@@ -399,10 +399,6 @@ export class PlaywrightInteractor implements Interactor {
|
|
|
399
399
|
}
|
|
400
400
|
|
|
401
401
|
//#region wait conditions
|
|
402
|
-
wait(ms: number): Promise<void> {
|
|
403
|
-
return timingUtil.wait(ms);
|
|
404
|
-
}
|
|
405
|
-
|
|
406
402
|
async waitUntilComponentState(
|
|
407
403
|
locator: PartLocator,
|
|
408
404
|
option: Partial<Readonly<WaitForOption>> = defaultWaitForOption
|
|
@@ -489,6 +485,18 @@ export class PlaywrightInteractor implements Interactor {
|
|
|
489
485
|
return readonly != null;
|
|
490
486
|
}
|
|
491
487
|
|
|
488
|
+
async isRequired(locator: PartLocator): Promise<boolean> {
|
|
489
|
+
const required = await this.getAttribute(locator, 'required');
|
|
490
|
+
if (required != null) {
|
|
491
|
+
return true;
|
|
492
|
+
}
|
|
493
|
+
return (await this.getAttribute(locator, 'aria-required')) === 'true';
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
async isError(locator: PartLocator): Promise<boolean> {
|
|
497
|
+
return (await this.getAttribute(locator, 'aria-invalid')) === 'true';
|
|
498
|
+
}
|
|
499
|
+
|
|
492
500
|
async isVisible(locator: PartLocator): Promise<boolean> {
|
|
493
501
|
const exists = await this.exists(locator);
|
|
494
502
|
if (!exists) {
|
|
@@ -550,8 +558,4 @@ export class PlaywrightInteractor implements Interactor {
|
|
|
550
558
|
return this.page.locator(cssLocator).innerHTML();
|
|
551
559
|
}
|
|
552
560
|
//#endregion
|
|
553
|
-
|
|
554
|
-
clone(): Interactor {
|
|
555
|
-
return new PlaywrightInteractor(this.page);
|
|
556
|
-
}
|
|
557
561
|
}
|
package/src/createTestEngine.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ScenePart, TestEngine } from '@atomic-testing/core';
|
|
1
|
+
import { ITestEngineOption, ScenePart, TestEngine } from '@atomic-testing/core';
|
|
2
2
|
import { Page } from '@playwright/test';
|
|
3
3
|
|
|
4
4
|
import { PlaywrightInteractor } from './PlaywrightInteractor';
|
|
@@ -9,9 +9,16 @@ import { PlaywrightInteractor } from './PlaywrightInteractor';
|
|
|
9
9
|
* @param page - Playwright page used for interaction.
|
|
10
10
|
* @param partDefinitions - Scene part definitions describing the scene
|
|
11
11
|
* structure for the engine.
|
|
12
|
+
* @param _option - Reserved for entry-point symmetry with the other adapters;
|
|
13
|
+
* currently ignored. `rootElement` is not applicable to Playwright, which drives
|
|
14
|
+
* a real browser page rather than mounting into a host element.
|
|
12
15
|
* @returns A configured {@link TestEngine} ready for use.
|
|
13
16
|
*/
|
|
14
|
-
export function createTestEngine<T extends ScenePart>(
|
|
17
|
+
export function createTestEngine<T extends ScenePart>(
|
|
18
|
+
page: Page,
|
|
19
|
+
partDefinitions: T,
|
|
20
|
+
_option?: ITestEngineOption
|
|
21
|
+
): TestEngine<T> {
|
|
15
22
|
const engine = new TestEngine([], new PlaywrightInteractor(page), {
|
|
16
23
|
parts: partDefinitions,
|
|
17
24
|
});
|