@atomic-testing/playwright 0.80.0 → 0.84.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +17 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.mts +3 -1
- package/dist/index.mjs +16 -12
- package/dist/index.mjs.map +1 -1
- package/package.json +16 -16
- package/src/PlaywrightInteractor.ts +10 -8
- package/src/testRunnerAdapter.ts +19 -3
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
1
2
|
let _atomic_testing_core = require("@atomic-testing/core");
|
|
2
3
|
let _playwright_test = require("@playwright/test");
|
|
3
|
-
|
|
4
4
|
//#region src/PlaywrightInteractor.ts
|
|
5
5
|
/**
|
|
6
6
|
* Implementation of the {@link Interactor} interface using Playwright.
|
|
@@ -102,17 +102,17 @@ var PlaywrightInteractor = class PlaywrightInteractor {
|
|
|
102
102
|
return this.hover(locator, option);
|
|
103
103
|
}
|
|
104
104
|
async mouseOut(locator, _option) {
|
|
105
|
-
await
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
} });
|
|
109
|
-
await this.page.mouse.move(-10, -10);
|
|
105
|
+
const cssLocator = await _atomic_testing_core.locatorUtil.toCssSelector(locator, this);
|
|
106
|
+
await this.page.locator(cssLocator).hover();
|
|
107
|
+
await this.page.locator(cssLocator).dispatchEvent("mouseout");
|
|
110
108
|
}
|
|
111
109
|
async mouseEnter(locator, _option) {
|
|
112
110
|
return this.hover(locator);
|
|
113
111
|
}
|
|
114
112
|
async mouseLeave(locator, _option) {
|
|
115
|
-
|
|
113
|
+
const cssLocator = await _atomic_testing_core.locatorUtil.toCssSelector(locator, this);
|
|
114
|
+
await this.page.locator(cssLocator).hover();
|
|
115
|
+
await this.page.locator(cssLocator).dispatchEvent("mouseout");
|
|
116
116
|
}
|
|
117
117
|
async focus(locator, _option) {
|
|
118
118
|
const cssLocator = await _atomic_testing_core.locatorUtil.toCssSelector(locator, this);
|
|
@@ -137,8 +137,8 @@ var PlaywrightInteractor = class PlaywrightInteractor {
|
|
|
137
137
|
if (isMultiple) {
|
|
138
138
|
const locators = await elLocator.all();
|
|
139
139
|
const values = [];
|
|
140
|
-
for (const locator
|
|
141
|
-
const value = await locator
|
|
140
|
+
for (const locator of locators) {
|
|
141
|
+
const value = await locator.getAttribute(name);
|
|
142
142
|
if (value != null) values.push(value);
|
|
143
143
|
}
|
|
144
144
|
return values;
|
|
@@ -195,7 +195,6 @@ var PlaywrightInteractor = class PlaywrightInteractor {
|
|
|
195
195
|
return new PlaywrightInteractor(this.page);
|
|
196
196
|
}
|
|
197
197
|
};
|
|
198
|
-
|
|
199
198
|
//#endregion
|
|
200
199
|
//#region src/createTestEngine.ts
|
|
201
200
|
/**
|
|
@@ -209,7 +208,6 @@ var PlaywrightInteractor = class PlaywrightInteractor {
|
|
|
209
208
|
function createTestEngine(page, partDefinitions) {
|
|
210
209
|
return new _atomic_testing_core.TestEngine([], new PlaywrightInteractor(page), { parts: partDefinitions });
|
|
211
210
|
}
|
|
212
|
-
|
|
213
211
|
//#endregion
|
|
214
212
|
//#region src/testRunnerAdapter.ts
|
|
215
213
|
async function goto(url, fixture) {
|
|
@@ -225,8 +223,15 @@ function playwrightGetTestEngine(scenePart, fixture) {
|
|
|
225
223
|
const page = fixture.page;
|
|
226
224
|
return createTestEngine(page, scenePart);
|
|
227
225
|
}
|
|
226
|
+
/**
|
|
227
|
+
* Playwright adapter for the TestFrameworkMapper interface.
|
|
228
|
+
*/
|
|
228
229
|
const playWrightTestFrameworkMapper = {
|
|
229
230
|
assertEqual: (a, b) => (0, _playwright_test.expect)(a).toEqual(b),
|
|
231
|
+
assertNotEqual: (a, b) => (0, _playwright_test.expect)(a).not.toEqual(b),
|
|
232
|
+
assertTrue: (value) => (0, _playwright_test.expect)(value).toBe(true),
|
|
233
|
+
assertFalse: (value) => (0, _playwright_test.expect)(value).toBe(false),
|
|
234
|
+
assertApproxEqual: (actual, expected, tolerance) => (0, _playwright_test.expect)(Math.abs(actual - expected)).toBeLessThanOrEqual(tolerance),
|
|
230
235
|
describe: _playwright_test.test.describe,
|
|
231
236
|
beforeEach: _playwright_test.test.beforeEach,
|
|
232
237
|
afterEach: _playwright_test.test.afterEach,
|
|
@@ -244,7 +249,6 @@ function getTestRunnerInterface() {
|
|
|
244
249
|
goto
|
|
245
250
|
};
|
|
246
251
|
}
|
|
247
|
-
|
|
248
252
|
//#endregion
|
|
249
253
|
exports.PlaywrightInteractor = PlaywrightInteractor;
|
|
250
254
|
exports.createTestEngine = createTestEngine;
|
|
@@ -252,4 +256,5 @@ exports.getTestRunnerInterface = getTestRunnerInterface;
|
|
|
252
256
|
exports.goto = goto;
|
|
253
257
|
exports.playWrightTestFrameworkMapper = playWrightTestFrameworkMapper;
|
|
254
258
|
exports.playwrightGetTestEngine = playwrightGetTestEngine;
|
|
259
|
+
|
|
255
260
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["page: Page","locatorUtil","optionLocator: PartLocator","values: string[]","labels: string[]","dateUtil","timingUtil","defaultWaitForOption","interactorUtil","locator","TestEngine","playWrightTestFrameworkMapper: TestFrameworkMapper","test"],"sources":["../src/PlaywrightInteractor.ts","../src/createTestEngine.ts","../src/testRunnerAdapter.ts"],"sourcesContent":["import {\n BlurOption,\n byCssSelector,\n ClickOption,\n CssProperty,\n dateUtil,\n defaultWaitForOption,\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 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 * 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 */\n async selectOptionValue(locator: PartLocator, values: string[]): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.page.locator(cssLocator).selectOption(values);\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 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 async click(locator: PartLocator, option?: Partial<ClickOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.page.locator(cssLocator).click({ position: option?.position });\n }\n\n async hover(locator: PartLocator, option?: Partial<HoverOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.page.locator(cssLocator).hover({ position: option?.position });\n }\n\n async mouseMove(locator: PartLocator, option?: Partial<MouseMoveOption>): Promise<void> {\n await this.hover(locator, {\n position: option?.position,\n });\n await this.page.mouse.move(0, 0);\n }\n\n async mouseDown(locator: PartLocator, option?: Partial<MouseDownOption>): Promise<void> {\n await this.hover(locator, {\n position: option?.position,\n });\n await this.page.mouse.down();\n }\n\n async mouseUp(locator: PartLocator, option?: Partial<MouseUpOption>): Promise<void> {\n await this.hover(locator, {\n position: option?.position,\n });\n await this.page.mouse.up();\n }\n\n async mouseOver(locator: PartLocator, option?: Partial<HoverOption>): Promise<void> {\n return this.hover(locator, option);\n }\n\n async mouseOut(locator: PartLocator, _option?: Partial<MouseOutOption>): Promise<void> {\n await this.hover(locator, {\n position: {\n x: 0,\n y: 0,\n },\n });\n await this.page.mouse.move(-10, -10);\n }\n\n async mouseEnter(locator: PartLocator, _option?: Partial<MouseEnterOption>): Promise<void> {\n return this.hover(locator);\n }\n\n async mouseLeave(locator: PartLocator, _option?: Partial<MouseLeaveOption>): Promise<void> {\n return this.mouseOut(locator);\n }\n\n async focus(locator: PartLocator, _option?: Partial<FocusOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n return 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.page.locator(cssLocator).blur();\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 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","import { ScenePart, TestEngine } from '@atomic-testing/core';\nimport {\n E2eTestInterface,\n E2eTestRunEnvironmentFixture,\n TestFrameworkMapper,\n} from '@atomic-testing/internal-test-runner';\nimport { expect, Page, test } from '@playwright/test';\n\nimport { createTestEngine } from './createTestEngine';\n\n/**\n * Navigate the current Playwright page to the provided URL.\n *\n * @param url - Destination URL to load.\n * @param fixture - Optional test fixture supplying the Playwright page.\n */\nexport async function goto(url: string): Promise<void>;\nexport async function goto(url: string, fixture: E2eTestRunEnvironmentFixture): Promise<void>;\nexport async function goto(url: string, fixture?: E2eTestRunEnvironmentFixture): Promise<void> {\n const page = fixture!.page as Page;\n await page.goto(url);\n}\n\n/**\n * Create a {@link TestEngine} bound to the Playwright page in the given fixture.\n *\n * @param scenePart - Scene definition to drive.\n * @param fixture - Fixture providing the Playwright page.\n */\nexport function playwrightGetTestEngine<T extends ScenePart>(\n scenePart: T,\n fixture: E2eTestRunEnvironmentFixture\n): TestEngine<T> {\n const page = fixture.page as Page;\n return createTestEngine(page, scenePart);\n}\n\nexport const playWrightTestFrameworkMapper: TestFrameworkMapper = {\n assertEqual: (a, b) => expect(a).toEqual(b),\n // @ts-expect-error - expect type is not compatible with the type of the test framework\n describe: test.describe,\n\n beforeEach: test.beforeEach,\n afterEach: test.afterEach,\n beforeAll: test.beforeAll,\n afterAll: test.afterAll,\n\n // @ts-expect-error - expect type is not compatible with the type of the test framework\n test: test,\n\n // @ts-expect-error - expect type is not compatible with the type of the test framework\n it: test,\n};\n\n/**\n * Get a typed interface for running end-to-end tests with Playwright.\n */\nexport function getTestRunnerInterface<T extends ScenePart>(): E2eTestInterface<T> {\n return {\n getTestEngine: playwrightGetTestEngine,\n goto,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["locatorUtil","dateUtil","timingUtil","defaultWaitForOption","interactorUtil","TestEngine","test"],"sources":["../src/PlaywrightInteractor.ts","../src/createTestEngine.ts","../src/testRunnerAdapter.ts"],"sourcesContent":["import {\n BlurOption,\n byCssSelector,\n ClickOption,\n CssProperty,\n dateUtil,\n defaultWaitForOption,\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 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 * 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 */\n async selectOptionValue(locator: PartLocator, values: string[]): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.page.locator(cssLocator).selectOption(values);\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 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 async click(locator: PartLocator, option?: Partial<ClickOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.page.locator(cssLocator).click({ position: option?.position });\n }\n\n async hover(locator: PartLocator, option?: Partial<HoverOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.page.locator(cssLocator).hover({ position: option?.position });\n }\n\n async mouseMove(locator: PartLocator, option?: Partial<MouseMoveOption>): Promise<void> {\n await this.hover(locator, {\n position: option?.position,\n });\n await this.page.mouse.move(0, 0);\n }\n\n async mouseDown(locator: PartLocator, option?: Partial<MouseDownOption>): Promise<void> {\n await this.hover(locator, {\n position: option?.position,\n });\n await this.page.mouse.down();\n }\n\n async mouseUp(locator: PartLocator, option?: Partial<MouseUpOption>): Promise<void> {\n await this.hover(locator, {\n position: option?.position,\n });\n await this.page.mouse.up();\n }\n\n async mouseOver(locator: PartLocator, option?: Partial<HoverOption>): Promise<void> {\n return 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 // 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 async mouseEnter(locator: PartLocator, _option?: Partial<MouseEnterOption>): Promise<void> {\n return this.hover(locator);\n }\n\n async mouseLeave(locator: PartLocator, _option?: Partial<MouseLeaveOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\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 async focus(locator: PartLocator, _option?: Partial<FocusOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n return 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.page.locator(cssLocator).blur();\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 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","import { ScenePart, TestEngine } from '@atomic-testing/core';\nimport {\n E2eTestInterface,\n E2eTestRunEnvironmentFixture,\n TestFrameworkMapper,\n} from '@atomic-testing/internal-test-runner';\nimport { expect, Page, test } from '@playwright/test';\n\nimport { createTestEngine } from './createTestEngine';\n\n/**\n * Navigate the current Playwright page to the provided URL.\n *\n * @param url - Destination URL to load.\n * @param fixture - Optional test fixture supplying the Playwright page.\n */\nexport async function goto(url: string): Promise<void>;\nexport async function goto(url: string, fixture: E2eTestRunEnvironmentFixture): Promise<void>;\nexport async function goto(url: string, fixture?: E2eTestRunEnvironmentFixture): Promise<void> {\n const page = fixture!.page as Page;\n await page.goto(url);\n}\n\n/**\n * Create a {@link TestEngine} bound to the Playwright page in the given fixture.\n *\n * @param scenePart - Scene definition to drive.\n * @param fixture - Fixture providing the Playwright page.\n */\nexport function playwrightGetTestEngine<T extends ScenePart>(\n scenePart: T,\n fixture: E2eTestRunEnvironmentFixture\n): TestEngine<T> {\n const page = fixture.page as Page;\n return createTestEngine(page, scenePart);\n}\n\n/**\n * Playwright adapter for the TestFrameworkMapper interface.\n */\nexport const playWrightTestFrameworkMapper: TestFrameworkMapper = {\n /*\n * INTENTIONAL @ts-expect-error comments: Playwright's test functions have different type\n * signatures than the normalized TestFrameworkMapper interface. Playwright uses fixture-based\n * callbacks with destructuring ({ page, browser }) while our interface uses a union type for\n * Jest compatibility (done callback or fixture object). The functions are compatible at runtime\n * but TypeScript cannot verify this due to these fundamental signature differences.\n */\n\n assertEqual: (a, b) => expect(a).toEqual(b),\n assertNotEqual: (a, b) => expect(a).not.toEqual(b),\n assertTrue: value => expect(value).toBe(true),\n assertFalse: value => expect(value).toBe(false),\n assertApproxEqual: (actual, expected, tolerance) =>\n expect(Math.abs(actual - expected)).toBeLessThanOrEqual(tolerance),\n // @ts-expect-error - Playwright describe signature differs from TestFrameworkMapper.Describe\n describe: test.describe,\n\n beforeEach: test.beforeEach,\n afterEach: test.afterEach,\n beforeAll: test.beforeAll,\n afterAll: test.afterAll,\n\n // @ts-expect-error - Playwright test signature differs from TestFrameworkMapper.Test\n test: test,\n\n // @ts-expect-error - Playwright test signature differs from TestFrameworkMapper.Test\n it: test,\n};\n\n/**\n * Get a typed interface for running end-to-end tests with Playwright.\n */\nexport function getTestRunnerInterface<T extends ScenePart>(): E2eTestInterface<T> {\n return {\n getTestEngine: playwrightGetTestEngine,\n goto,\n };\n}\n"],"mappings":";;;;;;;AA8BA,IAAa,uBAAb,MAAa,qBAA2C;;;;CAItD,YAAY,MAA4B;EAAZ,KAAA,OAAA;CAAa;;;;;;;CAQzC,MAAM,kBAAkB,SAAsB,QAAiC;EAC7E,MAAM,aAAa,MAAMA,qBAAAA,YAAY,cAAc,SAAS,IAAI;EAChE,MAAM,KAAK,KAAK,QAAQ,UAAU,CAAC,CAAC,aAAa,MAAM;CACzD;;;;;;;CAQA,MAAM,cAAc,SAAiD;EACnE,MAAM,aAAa,MAAMA,qBAAAA,YAAY,cAAc,SAAS,IAAI;EAChE,OAAO,KAAK,KAAK,QAAQ,UAAU,CAAC,CAAC,WAAW;CAClD;;;;;;;CAQA,MAAM,gBAAgB,SAA4D;EAChF,MAAM,iBAAA,GAAA,qBAAA,cAAA,CAA2C,gBAAgB;EACjE,MAAM,wBAAwBA,qBAAAA,YAAY,OAAO,SAAS,aAAa;EACvE,MAAM,aAAa,MAAMA,qBAAAA,YAAY,cAAc,uBAAuB,IAAI;EAC9E,MAAM,aAAa,MAAM,KAAK,KAAK,QAAQ,UAAU,CAAC,CAAC,IAAI;EAC3D,MAAM,SAAmB,CAAC;EAC1B,KAAK,MAAM,UAAU,YAAY;GAC/B,MAAM,QAAQ,MAAM,OAAO,aAAa,OAAO;GAC/C,IAAI,SAAS,MACX,OAAO,KAAK,KAAK;EAErB;EACA,OAAO;CACT;CAEA,MAAM,gBAAgB,SAA4D;EAChF,MAAM,iBAAA,GAAA,qBAAA,cAAA,CAA2C,gBAAgB;EACjE,MAAM,wBAAwBA,qBAAAA,YAAY,OAAO,SAAS,aAAa;EACvE,MAAM,aAAa,MAAMA,qBAAAA,YAAY,cAAc,uBAAuB,IAAI;EAC9E,MAAM,aAAa,MAAM,KAAK,KAAK,QAAQ,UAAU,CAAC,CAAC,IAAI;EAC3D,MAAM,SAAmB,CAAC;EAC1B,KAAK,MAAM,UAAU,YAAY;GAC/B,MAAM,QAAQ,MAAM,OAAO,YAAY;GACvC,IAAI,SAAS,MACX,OAAO,KAAK,KAAK;EAErB;EACA,OAAO;CACT;CAEA,MAAM,cAAc,SAAsB,cAAsD;EAC9F,MAAM,aAAa,MAAMA,qBAAAA,YAAY,cAAc,SAAS,IAAI;EAKhE,OAAO,MAJW,KAAK,KAAK,QAAQ,UACR,CAAC,CAAC,UAAU,SAAS,SAAS;GACxD,OAAO,OAAO,iBAAiB,OAAO,CAAC,CAAC,iBAAiB,IAAc;EACzE,GAAG,YAAY;CAEjB;CAEA,MAAM,UAAU,SAAsB,MAAc,QAA4D;EAC9G,MAAM,aAAa,MAAMA,qBAAAA,YAAY,cAAc,SAAS,IAAI;EAChE,IAAI,CAAC,QAAQ,QACX,MAAM,KAAK,KAAK,QAAQ,UAAU,CAAC,CAAC,MAAM;EAI5C,MAAM,OAAQ,MAAM,KAAK,aAAa,SAAS,MAAM,KAAM;EAC3D,IAAIC,qBAAAA,SAAS,oBAAoB,IAAI,GAAG;GACtC,MAAM,SAASA,qBAAAA,SAAS,sBAAsB,MAAM,IAAI;GACxD,IAAI,CAAC,OAAO,OACV,MAAM,IAAI,MACR,iCAAiC,KAAK,qBAAqB,OAAO,OAAO,aAAa,OAAO,SAC/F;EAEJ;EACA,MAAM,KAAK,KAAK,QAAQ,UAAU,CAAC,CAAC,KAAK,IAAI;CAC/C;CAEA,MAAM,MAAM,SAAsB,QAA8C;EAC9E,MAAM,aAAa,MAAMD,qBAAAA,YAAY,cAAc,SAAS,IAAI;EAChE,MAAM,KAAK,KAAK,QAAQ,UAAU,CAAC,CAAC,MAAM,EAAE,UAAU,QAAQ,SAAS,CAAC;CAC1E;CAEA,MAAM,MAAM,SAAsB,QAA8C;EAC9E,MAAM,aAAa,MAAMA,qBAAAA,YAAY,cAAc,SAAS,IAAI;EAChE,MAAM,KAAK,KAAK,QAAQ,UAAU,CAAC,CAAC,MAAM,EAAE,UAAU,QAAQ,SAAS,CAAC;CAC1E;CAEA,MAAM,UAAU,SAAsB,QAAkD;EACtF,MAAM,KAAK,MAAM,SAAS,EACxB,UAAU,QAAQ,SACpB,CAAC;EACD,MAAM,KAAK,KAAK,MAAM,KAAK,GAAG,CAAC;CACjC;CAEA,MAAM,UAAU,SAAsB,QAAkD;EACtF,MAAM,KAAK,MAAM,SAAS,EACxB,UAAU,QAAQ,SACpB,CAAC;EACD,MAAM,KAAK,KAAK,MAAM,KAAK;CAC7B;CAEA,MAAM,QAAQ,SAAsB,QAAgD;EAClF,MAAM,KAAK,MAAM,SAAS,EACxB,UAAU,QAAQ,SACpB,CAAC;EACD,MAAM,KAAK,KAAK,MAAM,GAAG;CAC3B;CAEA,MAAM,UAAU,SAAsB,QAA8C;EAClF,OAAO,KAAK,MAAM,SAAS,MAAM;CACnC;CAEA,MAAM,SAAS,SAAsB,SAAkD;EACrF,MAAM,aAAa,MAAMA,qBAAAA,YAAY,cAAc,SAAS,IAAI;EAEhE,MAAM,KAAK,KAAK,QAAQ,UAAU,CAAC,CAAC,MAAM;EAE1C,MAAM,KAAK,KAAK,QAAQ,UAAU,CAAC,CAAC,cAAc,UAAU;CAC9D;CAEA,MAAM,WAAW,SAAsB,SAAoD;EACzF,OAAO,KAAK,MAAM,OAAO;CAC3B;CAEA,MAAM,WAAW,SAAsB,SAAoD;EACzF,MAAM,aAAa,MAAMA,qBAAAA,YAAY,cAAc,SAAS,IAAI;EAEhE,MAAM,KAAK,KAAK,QAAQ,UAAU,CAAC,CAAC,MAAM;EAE1C,MAAM,KAAK,KAAK,QAAQ,UAAU,CAAC,CAAC,cAAc,UAAU;CAC9D;CAEA,MAAM,MAAM,SAAsB,SAA+C;EAC/E,MAAM,aAAa,MAAMA,qBAAAA,YAAY,cAAc,SAAS,IAAI;EAChE,OAAO,KAAK,KAAK,MAAM,UAAU;CACnC;CAEA,MAAM,KAAK,SAAsB,SAA8C;EAC7E,MAAM,aAAa,MAAMA,qBAAAA,YAAY,cAAc,SAAS,IAAI;EAChE,MAAM,KAAK,KAAK,QAAQ,UAAU,CAAC,CAAC,KAAK;CAC3C;CAGA,KAAK,IAA2B;EAC9B,OAAOE,qBAAAA,WAAW,KAAK,EAAE;CAC3B;CAEA,MAAM,wBACJ,SACA,SAA2CC,qBAAAA,sBAC5B;EACf,OAAOC,qBAAAA,eAAe,mBAAmB,SAAS,MAAM,MAAM;CAChE;CAEA,UAAa,QAAwC;EACnD,OAAOF,qBAAAA,WAAW,UAAU,MAAM;CACpC;CAMA,MAAM,aACJ,SACA,MACA,YAC+C;EAC/C,MAAM,aAAa,MAAMF,qBAAAA,YAAY,cAAc,SAAS,IAAI;EAChE,MAAM,YAAY,KAAK,KAAK,QAAQ,UAAU;EAC9C,IAAI,YAAY;GACd,MAAM,WAAW,MAAM,UAAU,IAAI;GACrC,MAAM,SAAmB,CAAC;GAC1B,KAAK,MAAM,WAAW,UAAU;IAC9B,MAAM,QAAQ,MAAM,QAAQ,aAAa,IAAI;IAC7C,IAAI,SAAS,MACX,OAAO,KAAK,KAAK;GAErB;GACA,OAAO;EACT;EAEA,OAAO,MADa,UAAU,aAAa,IAAI,KAC/B,KAAA;CAClB;CAEA,MAAM,QAAQ,SAAiD;EAC7D,MAAM,aAAa,MAAMA,qBAAAA,YAAY,cAAc,SAAS,IAAI;EAEhE,OAAO,MADY,KAAK,KAAK,QAAQ,UAAU,CAAC,CAAC,YAAY,KAC9C,KAAA;CACjB;CAEA,MAAM,OAAO,SAAwC;EACnD,MAAM,aAAa,MAAMA,qBAAAA,YAAY,cAAc,SAAS,IAAI;EAEhE,OAAO,MADa,KAAK,KAAK,QAAQ,UAAU,CAAC,CAAC,MAAM,IACzC;CACjB;CAEA,MAAM,UAAU,SAAwC;EACtD,MAAM,aAAa,MAAMA,qBAAAA,YAAY,cAAc,SAAS,IAAI;EAEhE,OAAO,MADe,KAAK,KAAK,QAAQ,UAAU,CAAC,CAAC,UAAU;CAEhE;CAEA,MAAM,WAAW,SAAwC;EACvD,MAAM,aAAa,MAAMA,qBAAAA,YAAY,cAAc,SAAS,IAAI;EAEhE,OAAO,MADkB,KAAK,KAAK,QAAQ,UAAU,CAAC,CAAC,WAAW;CAEpE;CAEA,MAAM,WAAW,SAAwC;EAEvD,OAAO,MADgB,KAAK,aAAa,SAAS,UAAU,KACzC;CACrB;CAEA,MAAM,UAAU,SAAwC;EAEtD,IAAI,CAAC,MADgB,KAAK,OAAO,OAAO,GAEtC,OAAO;EAGT,eAAe,mBACb,MACA,gBACA,YACkB;GAClB,IAAI;IAEF,OAAO,MADa,WAAW,cAAc,SAAS,IAAI,MACzC;GACnB,SAAS,GAAG;IAIV,IAAK,MAAM,WAAW,OAAO,OAAO,MAAO,OACzC,OAAO;IAET,MAAM;GACR;EACF;EAEA,IAAK,MAAM,mBAAmB,WAAW,KAAK,IAAI,MAAO,OACvD,OAAO;EAGT,IAAK,MAAM,mBAAmB,cAAc,UAAU,IAAI,MAAO,OAC/D,OAAO;EAGT,IAAK,MAAM,mBAAmB,WAAW,QAAQ,IAAI,MAAO,OAC1D,OAAO;EAGT,OAAO;CACT;CAEA,MAAM,YAAY,SAAsB,WAAqC;EAC3E,MAAM,aAAa,MAAM,KAAK,aAAa,SAAS,OAAO;EAC3D,IAAI,cAAc,MAChB,OAAO;EAIT,OADc,WAAW,MAAM,KACpB,CAAC,CAAC,SAAS,SAAS;CACjC;CAEA,MAAM,aAAa,SAAsB,MAAgC;EAEvE,OAAO,MADiB,KAAK,aAAa,SAAS,IAAI,KACnC;CACtB;CAGA,MAAM,UAAU,SAAuC;EACrD,MAAM,aAAa,MAAMA,qBAAAA,YAAY,cAAc,SAAS,IAAI;EAChE,OAAO,KAAK,KAAK,QAAQ,UAAU,CAAC,CAAC,UAAU;CACjD;CAGA,QAAoB;EAClB,OAAO,IAAI,qBAAqB,KAAK,IAAI;CAC3C;AACF;;;;;;;;;;;ACtTA,SAAgB,iBAAsC,MAAY,iBAAmC;CAKnG,OAAO,IAJYK,qBAAAA,WAAW,CAAC,GAAG,IAAI,qBAAqB,IAAI,GAAG,EAChE,OAAO,gBACT,CAEY;AACd;;;ACDA,eAAsB,KAAK,KAAa,SAAuD;CAE7F,MADa,QAAS,KACX,KAAK,GAAG;AACrB;;;;;;;AAQA,SAAgB,wBACd,WACA,SACe;CACf,MAAM,OAAO,QAAQ;CACrB,OAAO,iBAAiB,MAAM,SAAS;AACzC;;;;AAKA,MAAa,gCAAqD;CAShE,cAAc,GAAG,OAAA,GAAA,iBAAA,OAAA,CAAa,CAAC,CAAC,CAAC,QAAQ,CAAC;CAC1C,iBAAiB,GAAG,OAAA,GAAA,iBAAA,OAAA,CAAa,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC;CACjD,aAAY,WAAA,GAAA,iBAAA,OAAA,CAAgB,KAAK,CAAC,CAAC,KAAK,IAAI;CAC5C,cAAa,WAAA,GAAA,iBAAA,OAAA,CAAgB,KAAK,CAAC,CAAC,KAAK,KAAK;CAC9C,oBAAoB,QAAQ,UAAU,eAAA,GAAA,iBAAA,OAAA,CAC7B,KAAK,IAAI,SAAS,QAAQ,CAAC,CAAC,CAAC,oBAAoB,SAAS;CAEnE,UAAUC,iBAAAA,KAAK;CAEf,YAAYA,iBAAAA,KAAK;CACjB,WAAWA,iBAAAA,KAAK;CAChB,WAAWA,iBAAAA,KAAK;CAChB,UAAUA,iBAAAA,KAAK;CAGf,MAAMA,iBAAAA;CAGN,IAAIA,iBAAAA;AACN;;;;AAKA,SAAgB,yBAAmE;CACjF,OAAO;EACL,eAAe;EACf;CACF;AACF"}
|
package/dist/index.d.cts
CHANGED
|
@@ -3,7 +3,6 @@ import { Page } from "@playwright/test";
|
|
|
3
3
|
import { E2eTestInterface, E2eTestRunEnvironmentFixture, TestFrameworkMapper } from "@atomic-testing/internal-test-runner";
|
|
4
4
|
|
|
5
5
|
//#region src/createTestEngine.d.ts
|
|
6
|
-
|
|
7
6
|
/**
|
|
8
7
|
* Create a {@link TestEngine} instance backed by Playwright.
|
|
9
8
|
*
|
|
@@ -93,6 +92,9 @@ declare function goto(url: string, fixture: E2eTestRunEnvironmentFixture): Promi
|
|
|
93
92
|
* @param fixture - Fixture providing the Playwright page.
|
|
94
93
|
*/
|
|
95
94
|
declare function playwrightGetTestEngine<T extends ScenePart>(scenePart: T, fixture: E2eTestRunEnvironmentFixture): TestEngine<T>;
|
|
95
|
+
/**
|
|
96
|
+
* Playwright adapter for the TestFrameworkMapper interface.
|
|
97
|
+
*/
|
|
96
98
|
declare const playWrightTestFrameworkMapper: TestFrameworkMapper;
|
|
97
99
|
/**
|
|
98
100
|
* Get a typed interface for running end-to-end tests with Playwright.
|
package/dist/index.d.mts
CHANGED
|
@@ -3,7 +3,6 @@ import { Page } from "@playwright/test";
|
|
|
3
3
|
import { E2eTestInterface, E2eTestRunEnvironmentFixture, TestFrameworkMapper } from "@atomic-testing/internal-test-runner";
|
|
4
4
|
|
|
5
5
|
//#region src/createTestEngine.d.ts
|
|
6
|
-
|
|
7
6
|
/**
|
|
8
7
|
* Create a {@link TestEngine} instance backed by Playwright.
|
|
9
8
|
*
|
|
@@ -93,6 +92,9 @@ declare function goto(url: string, fixture: E2eTestRunEnvironmentFixture): Promi
|
|
|
93
92
|
* @param fixture - Fixture providing the Playwright page.
|
|
94
93
|
*/
|
|
95
94
|
declare function playwrightGetTestEngine<T extends ScenePart>(scenePart: T, fixture: E2eTestRunEnvironmentFixture): TestEngine<T>;
|
|
95
|
+
/**
|
|
96
|
+
* Playwright adapter for the TestFrameworkMapper interface.
|
|
97
|
+
*/
|
|
96
98
|
declare const playWrightTestFrameworkMapper: TestFrameworkMapper;
|
|
97
99
|
/**
|
|
98
100
|
* Get a typed interface for running end-to-end tests with Playwright.
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { TestEngine, byCssSelector, dateUtil, defaultWaitForOption, interactorUtil, locatorUtil, timingUtil } from "@atomic-testing/core";
|
|
2
2
|
import { expect, test } from "@playwright/test";
|
|
3
|
-
|
|
4
3
|
//#region src/PlaywrightInteractor.ts
|
|
5
4
|
/**
|
|
6
5
|
* Implementation of the {@link Interactor} interface using Playwright.
|
|
@@ -102,17 +101,17 @@ var PlaywrightInteractor = class PlaywrightInteractor {
|
|
|
102
101
|
return this.hover(locator, option);
|
|
103
102
|
}
|
|
104
103
|
async mouseOut(locator, _option) {
|
|
105
|
-
await
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
} });
|
|
109
|
-
await this.page.mouse.move(-10, -10);
|
|
104
|
+
const cssLocator = await locatorUtil.toCssSelector(locator, this);
|
|
105
|
+
await this.page.locator(cssLocator).hover();
|
|
106
|
+
await this.page.locator(cssLocator).dispatchEvent("mouseout");
|
|
110
107
|
}
|
|
111
108
|
async mouseEnter(locator, _option) {
|
|
112
109
|
return this.hover(locator);
|
|
113
110
|
}
|
|
114
111
|
async mouseLeave(locator, _option) {
|
|
115
|
-
|
|
112
|
+
const cssLocator = await locatorUtil.toCssSelector(locator, this);
|
|
113
|
+
await this.page.locator(cssLocator).hover();
|
|
114
|
+
await this.page.locator(cssLocator).dispatchEvent("mouseout");
|
|
116
115
|
}
|
|
117
116
|
async focus(locator, _option) {
|
|
118
117
|
const cssLocator = await locatorUtil.toCssSelector(locator, this);
|
|
@@ -137,8 +136,8 @@ var PlaywrightInteractor = class PlaywrightInteractor {
|
|
|
137
136
|
if (isMultiple) {
|
|
138
137
|
const locators = await elLocator.all();
|
|
139
138
|
const values = [];
|
|
140
|
-
for (const locator
|
|
141
|
-
const value = await locator
|
|
139
|
+
for (const locator of locators) {
|
|
140
|
+
const value = await locator.getAttribute(name);
|
|
142
141
|
if (value != null) values.push(value);
|
|
143
142
|
}
|
|
144
143
|
return values;
|
|
@@ -195,7 +194,6 @@ var PlaywrightInteractor = class PlaywrightInteractor {
|
|
|
195
194
|
return new PlaywrightInteractor(this.page);
|
|
196
195
|
}
|
|
197
196
|
};
|
|
198
|
-
|
|
199
197
|
//#endregion
|
|
200
198
|
//#region src/createTestEngine.ts
|
|
201
199
|
/**
|
|
@@ -209,7 +207,6 @@ var PlaywrightInteractor = class PlaywrightInteractor {
|
|
|
209
207
|
function createTestEngine(page, partDefinitions) {
|
|
210
208
|
return new TestEngine([], new PlaywrightInteractor(page), { parts: partDefinitions });
|
|
211
209
|
}
|
|
212
|
-
|
|
213
210
|
//#endregion
|
|
214
211
|
//#region src/testRunnerAdapter.ts
|
|
215
212
|
async function goto(url, fixture) {
|
|
@@ -225,8 +222,15 @@ function playwrightGetTestEngine(scenePart, fixture) {
|
|
|
225
222
|
const page = fixture.page;
|
|
226
223
|
return createTestEngine(page, scenePart);
|
|
227
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* Playwright adapter for the TestFrameworkMapper interface.
|
|
227
|
+
*/
|
|
228
228
|
const playWrightTestFrameworkMapper = {
|
|
229
229
|
assertEqual: (a, b) => expect(a).toEqual(b),
|
|
230
|
+
assertNotEqual: (a, b) => expect(a).not.toEqual(b),
|
|
231
|
+
assertTrue: (value) => expect(value).toBe(true),
|
|
232
|
+
assertFalse: (value) => expect(value).toBe(false),
|
|
233
|
+
assertApproxEqual: (actual, expected, tolerance) => expect(Math.abs(actual - expected)).toBeLessThanOrEqual(tolerance),
|
|
230
234
|
describe: test.describe,
|
|
231
235
|
beforeEach: test.beforeEach,
|
|
232
236
|
afterEach: test.afterEach,
|
|
@@ -244,7 +248,7 @@ function getTestRunnerInterface() {
|
|
|
244
248
|
goto
|
|
245
249
|
};
|
|
246
250
|
}
|
|
247
|
-
|
|
248
251
|
//#endregion
|
|
249
252
|
export { PlaywrightInteractor, createTestEngine, getTestRunnerInterface, goto, playWrightTestFrameworkMapper, playwrightGetTestEngine };
|
|
253
|
+
|
|
250
254
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["page: Page","optionLocator: PartLocator","values: string[]","labels: string[]","locator","playWrightTestFrameworkMapper: TestFrameworkMapper"],"sources":["../src/PlaywrightInteractor.ts","../src/createTestEngine.ts","../src/testRunnerAdapter.ts"],"sourcesContent":["import {\n BlurOption,\n byCssSelector,\n ClickOption,\n CssProperty,\n dateUtil,\n defaultWaitForOption,\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 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 * 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 */\n async selectOptionValue(locator: PartLocator, values: string[]): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.page.locator(cssLocator).selectOption(values);\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 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 async click(locator: PartLocator, option?: Partial<ClickOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.page.locator(cssLocator).click({ position: option?.position });\n }\n\n async hover(locator: PartLocator, option?: Partial<HoverOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.page.locator(cssLocator).hover({ position: option?.position });\n }\n\n async mouseMove(locator: PartLocator, option?: Partial<MouseMoveOption>): Promise<void> {\n await this.hover(locator, {\n position: option?.position,\n });\n await this.page.mouse.move(0, 0);\n }\n\n async mouseDown(locator: PartLocator, option?: Partial<MouseDownOption>): Promise<void> {\n await this.hover(locator, {\n position: option?.position,\n });\n await this.page.mouse.down();\n }\n\n async mouseUp(locator: PartLocator, option?: Partial<MouseUpOption>): Promise<void> {\n await this.hover(locator, {\n position: option?.position,\n });\n await this.page.mouse.up();\n }\n\n async mouseOver(locator: PartLocator, option?: Partial<HoverOption>): Promise<void> {\n return this.hover(locator, option);\n }\n\n async mouseOut(locator: PartLocator, _option?: Partial<MouseOutOption>): Promise<void> {\n await this.hover(locator, {\n position: {\n x: 0,\n y: 0,\n },\n });\n await this.page.mouse.move(-10, -10);\n }\n\n async mouseEnter(locator: PartLocator, _option?: Partial<MouseEnterOption>): Promise<void> {\n return this.hover(locator);\n }\n\n async mouseLeave(locator: PartLocator, _option?: Partial<MouseLeaveOption>): Promise<void> {\n return this.mouseOut(locator);\n }\n\n async focus(locator: PartLocator, _option?: Partial<FocusOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n return 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.page.locator(cssLocator).blur();\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 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","import { ScenePart, TestEngine } from '@atomic-testing/core';\nimport {\n E2eTestInterface,\n E2eTestRunEnvironmentFixture,\n TestFrameworkMapper,\n} from '@atomic-testing/internal-test-runner';\nimport { expect, Page, test } from '@playwright/test';\n\nimport { createTestEngine } from './createTestEngine';\n\n/**\n * Navigate the current Playwright page to the provided URL.\n *\n * @param url - Destination URL to load.\n * @param fixture - Optional test fixture supplying the Playwright page.\n */\nexport async function goto(url: string): Promise<void>;\nexport async function goto(url: string, fixture: E2eTestRunEnvironmentFixture): Promise<void>;\nexport async function goto(url: string, fixture?: E2eTestRunEnvironmentFixture): Promise<void> {\n const page = fixture!.page as Page;\n await page.goto(url);\n}\n\n/**\n * Create a {@link TestEngine} bound to the Playwright page in the given fixture.\n *\n * @param scenePart - Scene definition to drive.\n * @param fixture - Fixture providing the Playwright page.\n */\nexport function playwrightGetTestEngine<T extends ScenePart>(\n scenePart: T,\n fixture: E2eTestRunEnvironmentFixture\n): TestEngine<T> {\n const page = fixture.page as Page;\n return createTestEngine(page, scenePart);\n}\n\nexport const playWrightTestFrameworkMapper: TestFrameworkMapper = {\n assertEqual: (a, b) => expect(a).toEqual(b),\n // @ts-expect-error - expect type is not compatible with the type of the test framework\n describe: test.describe,\n\n beforeEach: test.beforeEach,\n afterEach: test.afterEach,\n beforeAll: test.beforeAll,\n afterAll: test.afterAll,\n\n // @ts-expect-error - expect type is not compatible with the type of the test framework\n test: test,\n\n // @ts-expect-error - expect type is not compatible with the type of the test framework\n it: test,\n};\n\n/**\n * Get a typed interface for running end-to-end tests with Playwright.\n */\nexport function getTestRunnerInterface<T extends ScenePart>(): E2eTestInterface<T> {\n return {\n getTestEngine: playwrightGetTestEngine,\n goto,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/PlaywrightInteractor.ts","../src/createTestEngine.ts","../src/testRunnerAdapter.ts"],"sourcesContent":["import {\n BlurOption,\n byCssSelector,\n ClickOption,\n CssProperty,\n dateUtil,\n defaultWaitForOption,\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 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 * 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 */\n async selectOptionValue(locator: PartLocator, values: string[]): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.page.locator(cssLocator).selectOption(values);\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 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 async click(locator: PartLocator, option?: Partial<ClickOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.page.locator(cssLocator).click({ position: option?.position });\n }\n\n async hover(locator: PartLocator, option?: Partial<HoverOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n await this.page.locator(cssLocator).hover({ position: option?.position });\n }\n\n async mouseMove(locator: PartLocator, option?: Partial<MouseMoveOption>): Promise<void> {\n await this.hover(locator, {\n position: option?.position,\n });\n await this.page.mouse.move(0, 0);\n }\n\n async mouseDown(locator: PartLocator, option?: Partial<MouseDownOption>): Promise<void> {\n await this.hover(locator, {\n position: option?.position,\n });\n await this.page.mouse.down();\n }\n\n async mouseUp(locator: PartLocator, option?: Partial<MouseUpOption>): Promise<void> {\n await this.hover(locator, {\n position: option?.position,\n });\n await this.page.mouse.up();\n }\n\n async mouseOver(locator: PartLocator, option?: Partial<HoverOption>): Promise<void> {\n return 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 // 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 async mouseEnter(locator: PartLocator, _option?: Partial<MouseEnterOption>): Promise<void> {\n return this.hover(locator);\n }\n\n async mouseLeave(locator: PartLocator, _option?: Partial<MouseLeaveOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\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 async focus(locator: PartLocator, _option?: Partial<FocusOption>): Promise<void> {\n const cssLocator = await locatorUtil.toCssSelector(locator, this);\n return 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.page.locator(cssLocator).blur();\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 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","import { ScenePart, TestEngine } from '@atomic-testing/core';\nimport {\n E2eTestInterface,\n E2eTestRunEnvironmentFixture,\n TestFrameworkMapper,\n} from '@atomic-testing/internal-test-runner';\nimport { expect, Page, test } from '@playwright/test';\n\nimport { createTestEngine } from './createTestEngine';\n\n/**\n * Navigate the current Playwright page to the provided URL.\n *\n * @param url - Destination URL to load.\n * @param fixture - Optional test fixture supplying the Playwright page.\n */\nexport async function goto(url: string): Promise<void>;\nexport async function goto(url: string, fixture: E2eTestRunEnvironmentFixture): Promise<void>;\nexport async function goto(url: string, fixture?: E2eTestRunEnvironmentFixture): Promise<void> {\n const page = fixture!.page as Page;\n await page.goto(url);\n}\n\n/**\n * Create a {@link TestEngine} bound to the Playwright page in the given fixture.\n *\n * @param scenePart - Scene definition to drive.\n * @param fixture - Fixture providing the Playwright page.\n */\nexport function playwrightGetTestEngine<T extends ScenePart>(\n scenePart: T,\n fixture: E2eTestRunEnvironmentFixture\n): TestEngine<T> {\n const page = fixture.page as Page;\n return createTestEngine(page, scenePart);\n}\n\n/**\n * Playwright adapter for the TestFrameworkMapper interface.\n */\nexport const playWrightTestFrameworkMapper: TestFrameworkMapper = {\n /*\n * INTENTIONAL @ts-expect-error comments: Playwright's test functions have different type\n * signatures than the normalized TestFrameworkMapper interface. Playwright uses fixture-based\n * callbacks with destructuring ({ page, browser }) while our interface uses a union type for\n * Jest compatibility (done callback or fixture object). The functions are compatible at runtime\n * but TypeScript cannot verify this due to these fundamental signature differences.\n */\n\n assertEqual: (a, b) => expect(a).toEqual(b),\n assertNotEqual: (a, b) => expect(a).not.toEqual(b),\n assertTrue: value => expect(value).toBe(true),\n assertFalse: value => expect(value).toBe(false),\n assertApproxEqual: (actual, expected, tolerance) =>\n expect(Math.abs(actual - expected)).toBeLessThanOrEqual(tolerance),\n // @ts-expect-error - Playwright describe signature differs from TestFrameworkMapper.Describe\n describe: test.describe,\n\n beforeEach: test.beforeEach,\n afterEach: test.afterEach,\n beforeAll: test.beforeAll,\n afterAll: test.afterAll,\n\n // @ts-expect-error - Playwright test signature differs from TestFrameworkMapper.Test\n test: test,\n\n // @ts-expect-error - Playwright test signature differs from TestFrameworkMapper.Test\n it: test,\n};\n\n/**\n * Get a typed interface for running end-to-end tests with Playwright.\n */\nexport function getTestRunnerInterface<T extends ScenePart>(): E2eTestInterface<T> {\n return {\n getTestEngine: playwrightGetTestEngine,\n goto,\n };\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,28 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atomic-testing/playwright",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.84.1",
|
|
4
4
|
"description": "Atomic Testing Playwright Adapter",
|
|
5
|
-
"main": "dist/index.cjs",
|
|
6
|
-
"module": "dist/index.mjs",
|
|
7
|
-
"files": [
|
|
8
|
-
"dist",
|
|
9
|
-
"src"
|
|
10
|
-
],
|
|
11
|
-
"author": "Tianzhen Lin <tangent@usa.net>",
|
|
12
5
|
"keywords": [],
|
|
13
6
|
"license": "MIT",
|
|
7
|
+
"author": "Tianzhen Lin <tangent@usa.net>",
|
|
14
8
|
"repository": {
|
|
15
9
|
"type": "git",
|
|
16
10
|
"url": "https://github.com/atomic-testing/atomic-testing.git",
|
|
17
11
|
"directory": "packages/playwright"
|
|
18
12
|
},
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"src"
|
|
16
|
+
],
|
|
17
|
+
"main": "dist/index.cjs",
|
|
18
|
+
"module": "dist/index.mjs",
|
|
26
19
|
"types": "dist/index.d.cts",
|
|
27
20
|
"exports": {
|
|
28
21
|
".": {
|
|
@@ -31,8 +24,15 @@
|
|
|
31
24
|
"require": "./dist/index.cjs"
|
|
32
25
|
}
|
|
33
26
|
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"@atomic-testing/core": "0.84.1",
|
|
29
|
+
"@atomic-testing/internal-test-runner": "0.84.1"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"@playwright/test": ">=1.50.0"
|
|
33
|
+
},
|
|
34
34
|
"scripts": {
|
|
35
35
|
"build": "tsdown",
|
|
36
|
-
"check:type": "
|
|
36
|
+
"check:type": "tsgo --noEmit"
|
|
37
37
|
}
|
|
38
38
|
}
|
|
@@ -156,13 +156,11 @@ export class PlaywrightInteractor implements Interactor {
|
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
async mouseOut(locator: PartLocator, _option?: Partial<MouseOutOption>): Promise<void> {
|
|
159
|
-
await
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
});
|
|
165
|
-
await this.page.mouse.move(-10, -10);
|
|
159
|
+
const cssLocator = await locatorUtil.toCssSelector(locator, this);
|
|
160
|
+
// First hover over the element to trigger mouseenter/mouseover
|
|
161
|
+
await this.page.locator(cssLocator).hover();
|
|
162
|
+
// Then dispatch mouseout event directly for cross-browser reliability
|
|
163
|
+
await this.page.locator(cssLocator).dispatchEvent('mouseout');
|
|
166
164
|
}
|
|
167
165
|
|
|
168
166
|
async mouseEnter(locator: PartLocator, _option?: Partial<MouseEnterOption>): Promise<void> {
|
|
@@ -170,7 +168,11 @@ export class PlaywrightInteractor implements Interactor {
|
|
|
170
168
|
}
|
|
171
169
|
|
|
172
170
|
async mouseLeave(locator: PartLocator, _option?: Partial<MouseLeaveOption>): Promise<void> {
|
|
173
|
-
|
|
171
|
+
const cssLocator = await locatorUtil.toCssSelector(locator, this);
|
|
172
|
+
// First hover over the element to trigger mouseenter/mouseover
|
|
173
|
+
await this.page.locator(cssLocator).hover();
|
|
174
|
+
// Dispatch mouseout which triggers both mouseout and mouseleave handlers in React
|
|
175
|
+
await this.page.locator(cssLocator).dispatchEvent('mouseout');
|
|
174
176
|
}
|
|
175
177
|
|
|
176
178
|
async focus(locator: PartLocator, _option?: Partial<FocusOption>): Promise<void> {
|
package/src/testRunnerAdapter.ts
CHANGED
|
@@ -35,9 +35,25 @@ export function playwrightGetTestEngine<T extends ScenePart>(
|
|
|
35
35
|
return createTestEngine(page, scenePart);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Playwright adapter for the TestFrameworkMapper interface.
|
|
40
|
+
*/
|
|
38
41
|
export const playWrightTestFrameworkMapper: TestFrameworkMapper = {
|
|
42
|
+
/*
|
|
43
|
+
* INTENTIONAL @ts-expect-error comments: Playwright's test functions have different type
|
|
44
|
+
* signatures than the normalized TestFrameworkMapper interface. Playwright uses fixture-based
|
|
45
|
+
* callbacks with destructuring ({ page, browser }) while our interface uses a union type for
|
|
46
|
+
* Jest compatibility (done callback or fixture object). The functions are compatible at runtime
|
|
47
|
+
* but TypeScript cannot verify this due to these fundamental signature differences.
|
|
48
|
+
*/
|
|
49
|
+
|
|
39
50
|
assertEqual: (a, b) => expect(a).toEqual(b),
|
|
40
|
-
|
|
51
|
+
assertNotEqual: (a, b) => expect(a).not.toEqual(b),
|
|
52
|
+
assertTrue: value => expect(value).toBe(true),
|
|
53
|
+
assertFalse: value => expect(value).toBe(false),
|
|
54
|
+
assertApproxEqual: (actual, expected, tolerance) =>
|
|
55
|
+
expect(Math.abs(actual - expected)).toBeLessThanOrEqual(tolerance),
|
|
56
|
+
// @ts-expect-error - Playwright describe signature differs from TestFrameworkMapper.Describe
|
|
41
57
|
describe: test.describe,
|
|
42
58
|
|
|
43
59
|
beforeEach: test.beforeEach,
|
|
@@ -45,10 +61,10 @@ export const playWrightTestFrameworkMapper: TestFrameworkMapper = {
|
|
|
45
61
|
beforeAll: test.beforeAll,
|
|
46
62
|
afterAll: test.afterAll,
|
|
47
63
|
|
|
48
|
-
// @ts-expect-error -
|
|
64
|
+
// @ts-expect-error - Playwright test signature differs from TestFrameworkMapper.Test
|
|
49
65
|
test: test,
|
|
50
66
|
|
|
51
|
-
// @ts-expect-error -
|
|
67
|
+
// @ts-expect-error - Playwright test signature differs from TestFrameworkMapper.Test
|
|
52
68
|
it: test,
|
|
53
69
|
};
|
|
54
70
|
|