pomwright 1.5.1 → 2.0.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/CHANGELOG.md +22 -0
- package/README.md +5 -5
- package/dist/index.d.mts +75 -970
- package/dist/index.d.ts +75 -970
- package/dist/index.js +585 -1872
- package/dist/index.mjs +598 -1880
- package/package.json +9 -11
- package/AGENTS.md +0 -37
- package/docs/v1/BaseApi-explanation.md +0 -63
- package/docs/v1/BasePage-explanation.md +0 -96
- package/docs/v1/LocatorSchema-explanation.md +0 -271
- package/docs/v1/LocatorSchemaPath-explanation.md +0 -165
- package/docs/v1/PlaywrightReportLogger-explanation.md +0 -56
- package/docs/v1/get-locator-methods-explanation.md +0 -250
- package/docs/v1/intro-to-using-pomwright.md +0 -899
- package/docs/v1/sessionStorage-methods-explanation.md +0 -38
- package/docs/v1/tips-folder-structure.md +0 -38
- package/docs/v1-to-v2-migration/bridge-migration-guide.md +0 -159
- package/docs/v1-to-v2-migration/direct-migration-guide.md +0 -238
- package/docs/v1-to-v2-migration/v1-to-v2-comparison.md +0 -547
- package/docs/v2/PageObject.md +0 -293
- package/docs/v2/composing-locator-modules.md +0 -93
- package/docs/v2/locator-registry.md +0 -695
- package/docs/v2/logging.md +0 -168
- package/docs/v2/overview.md +0 -515
- package/docs/v2/session-storage.md +0 -160
- package/index.ts +0 -75
- package/intTestV2/.env +0 -0
- package/intTestV2/fixtures/testApp.fixtures.ts +0 -43
- package/intTestV2/package.json +0 -22
- package/intTestV2/page-object-models/testApp/pages/iframe/iframe.locatorSchema.ts +0 -24
- package/intTestV2/page-object-models/testApp/pages/iframe/iframe.page.ts +0 -17
- package/intTestV2/page-object-models/testApp/pages/testPage.locatorSchema.ts +0 -32
- package/intTestV2/page-object-models/testApp/pages/testPage.page.ts +0 -119
- package/intTestV2/page-object-models/testApp/pages/testPath/[color]/color.locatorSchema.ts +0 -29
- package/intTestV2/page-object-models/testApp/pages/testPath/[color]/color.page.ts +0 -48
- package/intTestV2/page-object-models/testApp/pages/testPath/testPath.locatorSchema.ts +0 -9
- package/intTestV2/page-object-models/testApp/pages/testPath/testPath.page.ts +0 -23
- package/intTestV2/page-object-models/testApp/pages/testfilters/testfilters.locatorSchema.ts +0 -114
- package/intTestV2/page-object-models/testApp/pages/testfilters/testfilters.page.ts +0 -23
- package/intTestV2/page-object-models/testApp/testApp.base.ts +0 -20
- package/intTestV2/playwright.config.ts +0 -54
- package/intTestV2/server.js +0 -216
- package/intTestV2/test-data/staticPage/index.html +0 -280
- package/intTestV2/test-data/staticPage/w3images/avatar2.png +0 -0
- package/intTestV2/test-data/staticPage/w3images/avatar3.png +0 -0
- package/intTestV2/test-data/staticPage/w3images/avatar5.png +0 -0
- package/intTestV2/test-data/staticPage/w3images/avatar6.png +0 -0
- package/intTestV2/test-data/staticPage/w3images/forest.jpg +0 -0
- package/intTestV2/test-data/staticPage/w3images/lights.jpg +0 -0
- package/intTestV2/test-data/staticPage/w3images/mountains.jpg +0 -0
- package/intTestV2/test-data/staticPage/w3images/nature.jpg +0 -0
- package/intTestV2/test-data/staticPage/w3images/snow.jpg +0 -0
- package/intTestV2/tests/locatorRegistry/add/add.describe.spec.ts +0 -54
- package/intTestV2/tests/locatorRegistry/add/add.filter.spec.ts +0 -143
- package/intTestV2/tests/locatorRegistry/add/add.frameLocator.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/add/add.get.clone.spec.ts +0 -76
- package/intTestV2/tests/locatorRegistry/add/add.getByAltText.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/add/add.getById.spec.ts +0 -45
- package/intTestV2/tests/locatorRegistry/add/add.getByLabel.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/add/add.getByPlaceholder.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/add/add.getByRole.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/add/add.getByTestId.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/add/add.getByText.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/add/add.getByTitle.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/add/add.locator.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/add/add.reuseExisting.spec.ts +0 -107
- package/intTestV2/tests/locatorRegistry/add/add.reuseReusable.spec.ts +0 -311
- package/intTestV2/tests/locatorRegistry/add/add.spec.ts +0 -159
- package/intTestV2/tests/locatorRegistry/filter.cycle.spec.ts +0 -39
- package/intTestV2/tests/locatorRegistry/getLocator/getLocator.spec.ts +0 -253
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.clearSteps.spec.ts +0 -105
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.describe.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.filter.spec.ts +0 -368
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.getLocator.spec.ts +0 -56
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.getNestedLocator.spec.ts +0 -175
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.nth.spec.ts +0 -60
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.remove.spec.ts +0 -32
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.replace.spec.ts +0 -24
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.spec.ts +0 -110
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.update.spec.ts +0 -322
- package/intTestV2/tests/locatorRegistry/getNestedLocator/getNestedLocator.spec.ts +0 -412
- package/intTestV2/tests/locatorRegistry/registry/registry.binding.spec.ts +0 -50
- package/intTestV2/tests/locatorRegistry/validation/validation.locatorSchemaPath.spec.ts +0 -115
- package/intTestV2/tests/locatorRegistry/validation/validation.locatorSchemaPath.typecheck.ts +0 -86
- package/intTestV2/tests/locatorRegistry/validation/validation.sub-path.spec.ts +0 -45
- package/intTestV2/tests/step/step.spec.ts +0 -49
- package/intTestV2/tests/testApp/color.spec.ts +0 -15
- package/intTestV2/tests/testApp/iframe.spec.ts +0 -57
- package/intTestV2/tests/testApp/testFilters.spec.ts +0 -24
- package/intTestV2/tests/testApp/testPage.spec.ts +0 -161
- package/intTestV2/tests/testApp/testPath.spec.ts +0 -18
- package/pack-build.sh +0 -11
- package/pack-test-v2.sh +0 -36
- package/playwright.base.ts +0 -42
- package/skills/README.md +0 -56
- package/skills/pomwright-v1-5-bridge-migration/SKILL.md +0 -40
- package/skills/pomwright-v1-5-bridge-migration/references/call-site-migration.md +0 -178
- package/skills/pomwright-v1-5-bridge-migration/references/schema-translation.md +0 -183
- package/skills/pomwright-v2-migration/SKILL.md +0 -63
- package/skills/pomwright-v2-migration/references/call-site-migration.md +0 -265
- package/skills/pomwright-v2-migration/references/class-migration.md +0 -266
- package/skills/pomwright-v2-migration/references/fixture-and-helpers.md +0 -423
- package/skills/pomwright-v2-migration/references/locator-registration.md +0 -344
- package/srcV2/fixture/base.fixtures.ts +0 -23
- package/srcV2/helpers/navigation.ts +0 -153
- package/srcV2/helpers/playwrightReportLogger.ts +0 -196
- package/srcV2/helpers/sessionStorage.ts +0 -251
- package/srcV2/helpers/stepDecorator.ts +0 -106
- package/srcV2/locators/index.ts +0 -15
- package/srcV2/locators/locatorQueryBuilder.ts +0 -427
- package/srcV2/locators/locatorRegistrationBuilder.ts +0 -558
- package/srcV2/locators/locatorRegistry.ts +0 -583
- package/srcV2/locators/locatorUpdateBuilder.ts +0 -602
- package/srcV2/locators/reusableLocatorBuilder.ts +0 -200
- package/srcV2/locators/types.ts +0 -256
- package/srcV2/locators/utils.ts +0 -309
- package/srcV2/locators/v1SchemaTranslator.ts +0 -178
- package/srcV2/pageObject.ts +0 -105
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { expect, test } from "@fixtures-v2/testApp.fixtures";
|
|
2
|
-
|
|
3
|
-
const chainedPath = "fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText" as const;
|
|
4
|
-
|
|
5
|
-
test.describe("sub-path validation", () => {
|
|
6
|
-
test("addFilter rejects invalid root-level sub-paths", ({ testFilters }) => {
|
|
7
|
-
expect(() =>
|
|
8
|
-
testFilters
|
|
9
|
-
.getLocatorSchema("fictional.filter@hasNotText.filter@hasText")
|
|
10
|
-
// @ts-expect-error Testing invalid path handling
|
|
11
|
-
.filter("fictional", { hasText: "nope" }),
|
|
12
|
-
).toThrow('"fictional" is not a valid sub-path of "fictional.filter@hasNotText.filter@hasText".');
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
test("addFilter rejects invalid intermediate sub-paths", ({ testFilters }) => {
|
|
16
|
-
expect(() =>
|
|
17
|
-
testFilters
|
|
18
|
-
.getLocatorSchema(chainedPath)
|
|
19
|
-
// @ts-expect-error Testing invalid path handling
|
|
20
|
-
.filter("fictional.filter@hasNotText.filter@missing", { hasText: "nope" }),
|
|
21
|
-
).toThrow(
|
|
22
|
-
'"fictional.filter@hasNotText.filter@missing" is not a valid sub-path of "fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText".',
|
|
23
|
-
);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
test("update rejects missing/invalid subPaths", ({ testFilters }) => {
|
|
27
|
-
expect(() =>
|
|
28
|
-
testFilters
|
|
29
|
-
.getLocatorSchema("fictional.filter@hasNotText.filter@hasText")
|
|
30
|
-
// @ts-expect-error Testing invalid path handling
|
|
31
|
-
.update("fictional.filter@missing")
|
|
32
|
-
.getByText("nope"),
|
|
33
|
-
).toThrow('"fictional.filter@missing" is not a valid sub-path of "fictional.filter@hasNotText.filter@hasText".');
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test("update rejects a valid LocatorSchemaPath when it's an invalid subPath", ({ testFilters }) => {
|
|
37
|
-
expect(() =>
|
|
38
|
-
testFilters
|
|
39
|
-
.getLocatorSchema("body.section.heading")
|
|
40
|
-
// @ts-expect-error Testing invalid path handling
|
|
41
|
-
.update("body.section.button")
|
|
42
|
-
.locator("noop"),
|
|
43
|
-
).toThrow('"body.section.button" is not a valid sub-path of "body.section.heading"');
|
|
44
|
-
});
|
|
45
|
-
});
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { expect, test } from "@fixtures-v2/testApp.fixtures";
|
|
2
|
-
|
|
3
|
-
// These tests need to be verified manually by checking the test report
|
|
4
|
-
|
|
5
|
-
test("stepNoArgs should return the expected message", async ({ testPage }) => {
|
|
6
|
-
await expect(testPage.stepNoArgs()).resolves.toBe("Hello, World!");
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
test("stepWithTitle should return the expected message", async ({ testPage }) => {
|
|
10
|
-
await expect(testPage.stepWithTitle()).resolves.toBe("Hello, World!");
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
test("stepWithOptionBox should return the expected message", async ({ testPage }) => {
|
|
14
|
-
await expect(testPage.stepWithOptionBox()).resolves.toBe("Hello, World!");
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
test("stepWithOptionTimeout should return the expected message", async ({ testPage }) => {
|
|
18
|
-
await expect(testPage.stepWithOptionTimeout()).resolves.toBe("Hello, World!");
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
test("stepWithOptionLocation should return the expected message", async ({ testPage }) => {
|
|
22
|
-
await expect(testPage.stepWithOptionLocation()).resolves.toBe("Hello, World!");
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
test("stepWithTitleAndAllOptions should return the expected message", async ({ testPage }) => {
|
|
26
|
-
await expect(testPage.stepWithTitleAndAllOptions()).resolves.toBe("Hello, World!");
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
test("stepWithArgs should support required/default parameters", async ({ testPage }) => {
|
|
30
|
-
await expect(testPage.stepWithArgs("Starter"))
|
|
31
|
-
.resolves.toBe("Starter:1");
|
|
32
|
-
await expect(testPage.stepWithArgs("Starter", 3)).resolves.toBe("Starter:3");
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
test("stepWithArgsAndObjectReturn should preserve typed argument/return flows", async ({ testPage }) => {
|
|
36
|
-
await expect(testPage.stepWithArgsAndObjectReturn({ id: "abc" })).resolves.toEqual({
|
|
37
|
-
ok: true,
|
|
38
|
-
id: "abc",
|
|
39
|
-
mode: "basic",
|
|
40
|
-
retry: 0,
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
await expect(testPage.stepWithArgsAndObjectReturn({ id: "xyz", mode: "advanced" }, 2)).resolves.toEqual({
|
|
44
|
-
ok: true,
|
|
45
|
-
id: "xyz",
|
|
46
|
-
mode: "advanced",
|
|
47
|
-
retry: 2,
|
|
48
|
-
});
|
|
49
|
-
});
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { test } from "@fixtures-v2/testApp.fixtures";
|
|
2
|
-
|
|
3
|
-
test("Should validate navigation using RegExp-based urlPath and fullUrl", async ({ testPath, color }) => {
|
|
4
|
-
await testPath.page.goto(testPath.fullUrl);
|
|
5
|
-
|
|
6
|
-
await testPath.expectThisPage();
|
|
7
|
-
|
|
8
|
-
const linkToColorPage = testPath.getNestedLocator("body.link@color");
|
|
9
|
-
await linkToColorPage.waitFor({ state: "visible" });
|
|
10
|
-
|
|
11
|
-
await linkToColorPage.click();
|
|
12
|
-
|
|
13
|
-
await color.expectThisPage();
|
|
14
|
-
await color.validateColorPage();
|
|
15
|
-
});
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { expect, test } from "@fixtures-v2/testApp.fixtures";
|
|
2
|
-
|
|
3
|
-
const toggleStates = {
|
|
4
|
-
on: "On",
|
|
5
|
-
off: "Off",
|
|
6
|
-
} as const;
|
|
7
|
-
|
|
8
|
-
test.describe("iframe handling", () => {
|
|
9
|
-
test.beforeEach(async ({ iframePage }) => {
|
|
10
|
-
await iframePage.page.goto(iframePage.fullUrl);
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
test("getLocator finds iframeA and iframeB", async ({ iframePage }) => {
|
|
14
|
-
const iframeA = iframePage.getLocator("sectionA.frame");
|
|
15
|
-
const iframeB = iframePage.getLocator("sectionB.frame");
|
|
16
|
-
|
|
17
|
-
await expect(iframeA).toBeVisible();
|
|
18
|
-
await expect(iframeB).toBeVisible();
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
test("getLocator does not finds iframeC as it is inside iframeB", async ({ iframePage }) => {
|
|
22
|
-
const iframeC = iframePage.getLocator("sectionB.frame.innerFrame");
|
|
23
|
-
|
|
24
|
-
await expect(iframeC).not.toBeVisible();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
test("getNestedLocator finds iframeA and iframeB", async ({ iframePage }) => {
|
|
28
|
-
const iframeA = iframePage.getNestedLocator("sectionA.frame");
|
|
29
|
-
const iframeB = iframePage.getNestedLocator("sectionB.frame");
|
|
30
|
-
|
|
31
|
-
await expect(iframeA).toBeVisible();
|
|
32
|
-
await expect(iframeB).toBeVisible();
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
test("getNestedLocator finds nested iframeC", async ({ iframePage }) => {
|
|
36
|
-
const iframeC = iframePage.getNestedLocator("sectionB.frame.innerFrame");
|
|
37
|
-
await expect(iframeC).toBeVisible();
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
test("toggle elements inside each iframe", async ({ iframePage }) => {
|
|
41
|
-
const toggleA = iframePage.getNestedLocator("sectionA.frame.toggle");
|
|
42
|
-
const toggleB = iframePage.getNestedLocator("sectionB.frame.toggle");
|
|
43
|
-
const toggleC = iframePage.getNestedLocator("sectionB.frame.innerFrame.toggle");
|
|
44
|
-
|
|
45
|
-
await expect(toggleA).toHaveText(`Toggle A: ${toggleStates.off}`);
|
|
46
|
-
await expect(toggleB).toHaveText(`Toggle B: ${toggleStates.off}`);
|
|
47
|
-
await expect(toggleC).toHaveText(`Toggle C: ${toggleStates.off}`);
|
|
48
|
-
|
|
49
|
-
await toggleA.click();
|
|
50
|
-
await toggleB.click();
|
|
51
|
-
await toggleC.click();
|
|
52
|
-
|
|
53
|
-
await expect(toggleA).toHaveText(`Toggle A: ${toggleStates.on}`);
|
|
54
|
-
await expect(toggleB).toHaveText(`Toggle B: ${toggleStates.on}`);
|
|
55
|
-
await expect(toggleC).toHaveText(`Toggle C: ${toggleStates.on}`);
|
|
56
|
-
});
|
|
57
|
-
});
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { expect, test } from "@fixtures-v2/testApp.fixtures";
|
|
2
|
-
|
|
3
|
-
test("locator definitions with options should resolve playground interactions", async ({ testFilters }) => {
|
|
4
|
-
await testFilters.page.goto(testFilters.fullUrl);
|
|
5
|
-
|
|
6
|
-
const playgroundSection = testFilters.getNestedLocator("body.section@playground");
|
|
7
|
-
|
|
8
|
-
const playgroundBtnRed = testFilters.getNestedLocator("body.section@playground.button@red");
|
|
9
|
-
await playgroundBtnRed.click();
|
|
10
|
-
|
|
11
|
-
await expect(playgroundSection).toHaveCSS("background-color", "rgb(255, 0, 0)");
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
test("getLocatorSchema propagates registered filters and indices to getLocator", async ({ testFilters }) => {
|
|
15
|
-
await testFilters.page.goto(testFilters.fullUrl);
|
|
16
|
-
|
|
17
|
-
const locator = testFilters
|
|
18
|
-
.getLocatorSchema("body.section@playground.button@reset")
|
|
19
|
-
.filter("body.section@playground.button@reset", { hasText: /Reset/i })
|
|
20
|
-
.nth("body.section@playground.button@reset", "first")
|
|
21
|
-
.getLocator();
|
|
22
|
-
|
|
23
|
-
expect(`${locator}`).toEqual("getByRole('button', { name: 'Reset Color' }).filter({ hasText: /Reset/i }).first()");
|
|
24
|
-
});
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
import { expect, test } from "@fixtures-v2/testApp.fixtures";
|
|
2
|
-
import { SessionStorage } from "pomwright";
|
|
3
|
-
|
|
4
|
-
test("navigation.gotoThisPage runs post-navigation actions", async ({ testPage }) => {
|
|
5
|
-
expect(testPage.navigationActionCount.value).toBe(0);
|
|
6
|
-
|
|
7
|
-
await testPage.navigation.gotoThisPage();
|
|
8
|
-
|
|
9
|
-
expect(testPage.navigationActionCount.value).toBe(1);
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
test("topMenu should expose notification badge count", async ({ testPage }) => {
|
|
13
|
-
await testPage.page.goto(testPage.fullUrl);
|
|
14
|
-
|
|
15
|
-
const notificationBadge = testPage.getNestedLocator("topMenu.notifications.button.countBadge");
|
|
16
|
-
await expect(notificationBadge).toHaveText("3");
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
test("stepWithAdvancedReturnType should return the expected payload", async ({ testPage }) => {
|
|
20
|
-
await testPage.page.goto(testPage.fullUrl);
|
|
21
|
-
|
|
22
|
-
const result = await testPage.stepWithAdvancedReturnType();
|
|
23
|
-
|
|
24
|
-
expect(result.status).toBe("ready");
|
|
25
|
-
expect(result.payload[0]?.values[0]?.key).toBe("alpha");
|
|
26
|
-
expect(result.metadata.flags.has("beta")).toBe(true);
|
|
27
|
-
expect(result.metadata.versions.get("v2")?.hash).toBe("def456");
|
|
28
|
-
expect(result.startedAt.getTime()).toBe(0);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
test.describe("getLocator helpers on web app", () => {
|
|
32
|
-
test.afterEach(async ({ testPage }) => {
|
|
33
|
-
await testPage.page.goto(testPage.fullUrl);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test("getLocator should return the single locator the complete LocatorSchemaPath resolves to", async ({
|
|
37
|
-
testPage,
|
|
38
|
-
}) => {
|
|
39
|
-
const locator = testPage.getLocator("topMenu.notifications.dropdown.item");
|
|
40
|
-
expect(locator).not.toBeNull();
|
|
41
|
-
expect(locator).not.toBeUndefined();
|
|
42
|
-
expect(`${locator}`).toEqual("locator('.w3-bar-item')");
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
test("should be able to manually chain locators returned by getLocator", async ({ testPage }) => {
|
|
46
|
-
const topMenu = testPage.getLocator("topMenu");
|
|
47
|
-
|
|
48
|
-
const topMenuNotifications = topMenu.locator(testPage.getLocator("topMenu.notifications"));
|
|
49
|
-
|
|
50
|
-
const topMenuNotificationsDropdown = topMenuNotifications.locator(
|
|
51
|
-
testPage.getLocator("topMenu.notifications.dropdown"),
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
const topMenuNotificationsDropdownItem = topMenuNotificationsDropdown.locator(
|
|
55
|
-
testPage.getLocator("topMenu.notifications.dropdown.item"),
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
expect(topMenuNotificationsDropdownItem).not.toBeNull();
|
|
59
|
-
expect(topMenuNotificationsDropdownItem).not.toBeUndefined();
|
|
60
|
-
expect(`${topMenuNotificationsDropdownItem}`).toEqual(
|
|
61
|
-
"locator('.w3-top').locator(locator('.w3-dropdown-hover')).locator(locator('.w3-dropdown-content')).locator(locator('.w3-bar-item'))",
|
|
62
|
-
);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
test("getNestedLocator should support index overrides", async ({ testPage }) => {
|
|
67
|
-
await testPage.page.goto(testPage.fullUrl);
|
|
68
|
-
|
|
69
|
-
const secondNotification = testPage
|
|
70
|
-
.getLocatorSchema("topMenu.notifications.dropdown.item")
|
|
71
|
-
.nth("topMenu.notifications.dropdown.item", 1)
|
|
72
|
-
.getNestedLocator();
|
|
73
|
-
await expect(secondNotification).toHaveText("John Doe posted on your wall");
|
|
74
|
-
|
|
75
|
-
const lastNotification = testPage
|
|
76
|
-
.getLocatorSchema("topMenu.notifications.dropdown.item")
|
|
77
|
-
.nth("topMenu.notifications.dropdown.item", "last")
|
|
78
|
-
.getNestedLocator();
|
|
79
|
-
await expect(lastNotification).toHaveText("Jane likes your post");
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
test("setOnNextNavigation waits for a main-frame navigation", async ({ testPage }) => {
|
|
83
|
-
await testPage.page.goto(testPage.fullUrl);
|
|
84
|
-
await testPage.sessionStorage.clear();
|
|
85
|
-
|
|
86
|
-
await testPage.sessionStorage.setOnNextNavigation({ token: "abc" });
|
|
87
|
-
await expect(testPage.sessionStorage.get(["token"])).resolves.toEqual({});
|
|
88
|
-
|
|
89
|
-
await testPage.page.goto(testPage.fullUrl);
|
|
90
|
-
await expect(testPage.sessionStorage.get(["token"])).resolves.toEqual({ token: "abc" });
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
test("setOnNextNavigation merges multiple calls before navigation", async ({ testPage }) => {
|
|
94
|
-
await testPage.page.goto(testPage.fullUrl);
|
|
95
|
-
await testPage.sessionStorage.clear();
|
|
96
|
-
|
|
97
|
-
await testPage.sessionStorage.setOnNextNavigation({ token: "abc" });
|
|
98
|
-
await testPage.sessionStorage.setOnNextNavigation({ theme: "dark" });
|
|
99
|
-
await testPage.page.goto(testPage.fullUrl);
|
|
100
|
-
|
|
101
|
-
await expect(testPage.sessionStorage.get(["token", "theme"])).resolves.toEqual({
|
|
102
|
-
token: "abc",
|
|
103
|
-
theme: "dark",
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
test("clear can target specific keys", async ({ testPage }) => {
|
|
108
|
-
await testPage.page.goto(testPage.fullUrl);
|
|
109
|
-
await testPage.sessionStorage.set({ token: "abc", theme: "dark" });
|
|
110
|
-
|
|
111
|
-
await testPage.sessionStorage.clear("token");
|
|
112
|
-
await expect(testPage.sessionStorage.get(["token", "theme"])).resolves.toEqual({ theme: "dark" });
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
test("sessionStorage set/get supports waitForContext", async ({ testPage }) => {
|
|
116
|
-
await testPage.page.goto(testPage.fullUrl);
|
|
117
|
-
|
|
118
|
-
await testPage.sessionStorage.set({ token: "abc" }, { waitForContext: true });
|
|
119
|
-
|
|
120
|
-
const { token } = await testPage.sessionStorage.get(["token"], { waitForContext: true });
|
|
121
|
-
expect(token).toBe("abc");
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
test("sessionStorage clear supports keys and waitForContext", async ({ testPage }) => {
|
|
125
|
-
await testPage.page.goto(testPage.fullUrl);
|
|
126
|
-
|
|
127
|
-
await testPage.sessionStorage.set({ token: "abc", theme: "dark" }, { waitForContext: true });
|
|
128
|
-
await testPage.sessionStorage.clear("token", { waitForContext: true });
|
|
129
|
-
|
|
130
|
-
const data = await testPage.sessionStorage.get(["token", "theme"], { waitForContext: true });
|
|
131
|
-
expect(data.token).toBeUndefined();
|
|
132
|
-
expect(data.theme).toBe("dark");
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
test("sessionStorage set/get should write immediately on a loaded page", async ({ testPage }) => {
|
|
136
|
-
await testPage.page.goto(testPage.fullUrl);
|
|
137
|
-
|
|
138
|
-
await testPage.sessionStorage.set({ token: { value: "abc" } });
|
|
139
|
-
|
|
140
|
-
const data = await testPage.sessionStorage.get(["token"]);
|
|
141
|
-
expect(data.token).toEqual({ value: "abc" });
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
test("sessionStorage set with waitForContext should wait for navigation context", async ({ testPage }) => {
|
|
145
|
-
const freshPage = await testPage.page.context().newPage();
|
|
146
|
-
const sessionStorage = new SessionStorage(freshPage, { label: "SessionStorageTest" });
|
|
147
|
-
|
|
148
|
-
const navigation = freshPage.goto(testPage.fullUrl);
|
|
149
|
-
await sessionStorage.set({ token: { value: "waited" } }, { waitForContext: true });
|
|
150
|
-
await navigation;
|
|
151
|
-
|
|
152
|
-
const data = await sessionStorage.get(["token"]);
|
|
153
|
-
expect(data.token).toEqual({ value: "waited" });
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
test("sessionStorage operations reject without waitForContext when no context", async ({ testPage }) => {
|
|
157
|
-
const freshPage = await testPage.page.context().newPage();
|
|
158
|
-
const sessionStorage = new SessionStorage(freshPage, { label: "SessionStorageTest" });
|
|
159
|
-
|
|
160
|
-
await expect(sessionStorage.get(["token"])).rejects.toThrow("SessionStorage context is not available.");
|
|
161
|
-
});
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { expect, test } from "@fixtures-v2/testApp.fixtures";
|
|
2
|
-
|
|
3
|
-
test("navigation.goto prefixes baseUrl for URL paths", async ({ testPath }) => {
|
|
4
|
-
await testPath.navigation.goto("/testpath");
|
|
5
|
-
await testPath.navigation.expectThisPage();
|
|
6
|
-
|
|
7
|
-
expect(testPath.page.url()).toBe(testPath.fullUrl);
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
test("navigation.expectThisPage supports RegExp fullUrl", async ({ testPath, color }) => {
|
|
11
|
-
await testPath.navigation.gotoThisPage();
|
|
12
|
-
|
|
13
|
-
const linkToColorPage = testPath.getNestedLocator("body.link@color");
|
|
14
|
-
await linkToColorPage.waitFor({ state: "visible" });
|
|
15
|
-
await linkToColorPage.click();
|
|
16
|
-
|
|
17
|
-
await color.navigation.expectThisPage();
|
|
18
|
-
});
|
package/pack-build.sh
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
set -e
|
|
4
|
-
|
|
5
|
-
VERSION=$(node -pe "require('./package.json').version")
|
|
6
|
-
|
|
7
|
-
pnpm i --frozen-lockfile || { echo "Installation failed"; exit 1; }
|
|
8
|
-
pnpm build || { echo "Build failed"; exit 1; }
|
|
9
|
-
pnpm pack || { echo "Packaging failed"; exit 1; }
|
|
10
|
-
|
|
11
|
-
echo "Built pomwright-$VERSION.tgz"
|
package/pack-test-v2.sh
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
TEST_DIR="intTestV2"
|
|
4
|
-
|
|
5
|
-
cleanup() {
|
|
6
|
-
if [[ $(basename "$PWD") != "$TEST_DIR" ]]; then
|
|
7
|
-
echo "Not in ./$TEST_DIR directory. Trying to change to ./$TEST_DIR directory..."
|
|
8
|
-
if [[ -d "$TEST_DIR" ]]; then
|
|
9
|
-
cd $TEST_DIR || { echo "Failed to change to ./$TEST_DIR directory"; return 1; }
|
|
10
|
-
else
|
|
11
|
-
echo "The ./$TEST_DIR directory does not exist. Exiting cleanup."
|
|
12
|
-
return 1
|
|
13
|
-
fi
|
|
14
|
-
fi
|
|
15
|
-
|
|
16
|
-
# echo "Reverting to latest published version of POMWright in the ./$TEST_DIR directory..."
|
|
17
|
-
# pnpm i -D pomwright@latest || { echo "Failed to revert to latest POMWright version"; exit 1; }
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
trap cleanup EXIT
|
|
21
|
-
|
|
22
|
-
set -e
|
|
23
|
-
|
|
24
|
-
VERSION=$(node -pe "require('./package.json').version")
|
|
25
|
-
|
|
26
|
-
./pack-build.sh
|
|
27
|
-
|
|
28
|
-
cd $TEST_DIR || { echo "Changing directory failed"; exit 1; }
|
|
29
|
-
|
|
30
|
-
pnpm i -D "../pomwright-$VERSION.tgz" || { echo "Local package installation failed"; exit 1; }
|
|
31
|
-
|
|
32
|
-
pnpm i --frozen-lockfile || { echo "Installation failed"; exit 1; }
|
|
33
|
-
pnpm playwright install --with-deps || { echo "Playwright dependencies installation failed"; exit 1; }
|
|
34
|
-
pnpm playwright test --project=chromium || { echo "Tests failed"; exit 1; }
|
|
35
|
-
|
|
36
|
-
echo "Testing completed successfully."
|
package/playwright.base.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
// playwright.base.ts
|
|
2
|
-
import type { PlaywrightTestConfig } from "@playwright/test";
|
|
3
|
-
|
|
4
|
-
// this is only a *type* import → erased at runtime → does NOT trigger the double-load
|
|
5
|
-
// everything below is plain data the subprojects can spread in
|
|
6
|
-
|
|
7
|
-
export const baseConfig: PlaywrightTestConfig = {
|
|
8
|
-
// things you currently have in both configs:
|
|
9
|
-
globalTimeout: 60_000 * 5,
|
|
10
|
-
timeout: 60_000,
|
|
11
|
-
expect: {
|
|
12
|
-
timeout: 5_000,
|
|
13
|
-
},
|
|
14
|
-
fullyParallel: true,
|
|
15
|
-
forbidOnly: !!process.env.CI,
|
|
16
|
-
retries: process.env.CI ? 1 : 1,
|
|
17
|
-
workers: "50%",
|
|
18
|
-
reporter: process.env.CI
|
|
19
|
-
? [
|
|
20
|
-
["html", { open: "never" }],
|
|
21
|
-
["github", { printSteps: false }],
|
|
22
|
-
]
|
|
23
|
-
: [
|
|
24
|
-
["html", { open: "on-failure" }],
|
|
25
|
-
["list", { printSteps: false }],
|
|
26
|
-
],
|
|
27
|
-
use: {
|
|
28
|
-
actionTimeout: 5_000,
|
|
29
|
-
navigationTimeout: 10_000,
|
|
30
|
-
headless: true,
|
|
31
|
-
viewport: { width: 1280, height: 720 },
|
|
32
|
-
ignoreHTTPSErrors: false,
|
|
33
|
-
video: "retry-with-video",
|
|
34
|
-
screenshot: {
|
|
35
|
-
mode: "only-on-failure",
|
|
36
|
-
fullPage: true,
|
|
37
|
-
omitBackground: false,
|
|
38
|
-
},
|
|
39
|
-
trace: "on-all-retries",
|
|
40
|
-
testIdAttribute: "data-testid",
|
|
41
|
-
},
|
|
42
|
-
};
|
package/skills/README.md
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
# POMWright Migration Skills
|
|
2
|
-
|
|
3
|
-
AI assistant skills for migrating POMWright page objects between versions.
|
|
4
|
-
|
|
5
|
-
## Available Skills
|
|
6
|
-
|
|
7
|
-
### pomwright-v1-5-bridge-migration
|
|
8
|
-
Migrate v1 `BasePage` page objects to the v1.5 bridge (`BasePageV1toV2`) as an intermediate step toward v2.
|
|
9
|
-
|
|
10
|
-
**Triggered by:**
|
|
11
|
-
- "Migrate/convert/upgrade this page object from v1 to the bridge"
|
|
12
|
-
- "Use BasePageV1toV2 for this page object"
|
|
13
|
-
- Mentioning `BasePageV1toV2` or "bridge" in migration context
|
|
14
|
-
|
|
15
|
-
**Covers:**
|
|
16
|
-
- Changing class inheritance
|
|
17
|
-
- Translating `addSchema`/`initLocatorSchemas` to `defineLocators` with v2 DSL
|
|
18
|
-
- Updating call-site syntax (`getLocator`, `getNestedLocator`, `getLocatorSchema`)
|
|
19
|
-
- Updating filter/index/update/mutation patterns
|
|
20
|
-
|
|
21
|
-
### pomwright-v2-migration
|
|
22
|
-
Migrate page objects to v2 `PageObject` from either v1 `BasePage` or the v1.5 bridge.
|
|
23
|
-
|
|
24
|
-
**Triggered by:**
|
|
25
|
-
- "Migrate/convert/upgrade this page object to v2"
|
|
26
|
-
- "Use v2 PageObject for this page object"
|
|
27
|
-
- Mentioning `PageObject` or "v2" in migration context
|
|
28
|
-
|
|
29
|
-
**Covers:**
|
|
30
|
-
- Class inheritance and constructor signature changes
|
|
31
|
-
- Locator registration with v2 DSL
|
|
32
|
-
- Adding `pageActionsToPerformAfterNavigation`
|
|
33
|
-
- Call-site syntax updates
|
|
34
|
-
- Migrating fixtures, navigation, sessionStorage, and logging
|
|
35
|
-
- Adopting the `@step` decorator
|
|
36
|
-
|
|
37
|
-
## Installation
|
|
38
|
-
|
|
39
|
-
### For Claude Code
|
|
40
|
-
|
|
41
|
-
Copy the skill directories to your local Claude Code skills folder:
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
cp -r skills/pomwright-v1-5-bridge-migration ~/.claude/skills/
|
|
45
|
-
cp -r skills/pomwright-v2-migration ~/.claude/skills/
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
### For Other AI Assistants
|
|
49
|
-
|
|
50
|
-
These skills are structured as markdown-based instructions with reference documentation. Adapt the content from each `SKILL.md` file according to your AI assistant's skill/prompt format.
|
|
51
|
-
|
|
52
|
-
## Structure
|
|
53
|
-
|
|
54
|
-
Each skill includes:
|
|
55
|
-
- `SKILL.md` - Main skill instructions
|
|
56
|
-
- `references/` - Supporting documentation and examples
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: pomwright-v1-5-bridge-migration
|
|
3
|
-
description: >
|
|
4
|
-
Migrate POMWright v1 BasePage page objects to the v1.5 bridge (BasePageV1toV2) as an intermediate
|
|
5
|
-
step toward v2 PageObject. Use when asked to migrate, convert, or upgrade POMWright page objects
|
|
6
|
-
from v1 to the bridge, or when working with BasePageV1toV2. Covers: changing class inheritance,
|
|
7
|
-
translating addSchema/initLocatorSchemas to defineLocators with v2 DSL, updating call-site syntax
|
|
8
|
-
(getLocator, getNestedLocator, getLocatorSchema), and updating filter/index/update/mutation patterns.
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
# POMWright v1.5 Bridge Migration (BasePage -> BasePageV1toV2)
|
|
12
|
-
|
|
13
|
-
Migrate v1 `BasePage` page objects to `BasePageV1toV2` (the bridge), converting locator definitions
|
|
14
|
-
and call-site syntax to v2. The bridge is deprecated and will be removed in 2.0.0; it exists only
|
|
15
|
-
as a staged migration step.
|
|
16
|
-
|
|
17
|
-
## Workflow
|
|
18
|
-
|
|
19
|
-
1. **Inventory** the target POC file: identify `BasePage` extends, `initLocatorSchemas`, all `addSchema` calls, and call-sites using v1 accessors.
|
|
20
|
-
2. **Switch inheritance** from `BasePage` to `BasePageV1toV2`. Constructor signature stays the same (`page, testInfo, baseUrl, urlPath, pocName, pwrl`).
|
|
21
|
-
3. **Add `defineLocators()`** method stub.
|
|
22
|
-
4. **Translate each `addSchema` call** from `initLocatorSchemas` into `this.add(path).method(...)` inside `defineLocators()`. See [references/schema-translation.md](references/schema-translation.md) for the full mapping table and examples.
|
|
23
|
-
5. **Remove translated `addSchema` entries** from `initLocatorSchemas`. Once all are moved, keep `initLocatorSchemas()` as an empty method while still extending `BasePageV1toV2`.
|
|
24
|
-
6. **Update call-site syntax** in tests/helpers/fixtures that consume the POC. See [references/call-site-migration.md](references/call-site-migration.md).
|
|
25
|
-
7. **Verify** by running tests.
|
|
26
|
-
|
|
27
|
-
## Key rules
|
|
28
|
-
|
|
29
|
-
- Paths already registered in `defineLocators` are skipped when the bridge translates `initLocatorSchemas` - no duplicates.
|
|
30
|
-
- v1 schemas using `Locator` instances or missing selector fields cannot be auto-translated; rewrite those in `defineLocators` immediately.
|
|
31
|
-
- `BasePageV1toV2` narrows `this.locators` to `addSchema` only; no other v1 mutations are exposed.
|
|
32
|
-
- v2 accessors (`add`, `getLocator`, `getNestedLocator`, `getLocatorSchema`) are exposed on the bridge and should be used from this point forward.
|
|
33
|
-
- v2 `getLocator`/`getNestedLocator` are **synchronous** (no `await`). v1 versions were async.
|
|
34
|
-
- v2 `getNestedLocator` no longer accepts index maps. Use `getLocatorSchema(path).nth(subPath, index).getNestedLocator()` instead.
|
|
35
|
-
- `filter` replaces v1 `addFilter`. `nth` replaces v1 index maps. Both can be chained in any order.
|
|
36
|
-
|
|
37
|
-
## References
|
|
38
|
-
|
|
39
|
-
- **Schema translation mapping**: [references/schema-translation.md](references/schema-translation.md) - GetByMethod -> v2 DSL mapping table, filter/options translation, reusable locators, frameLocator changes
|
|
40
|
-
- **Call-site migration**: [references/call-site-migration.md](references/call-site-migration.md) - getLocator, getNestedLocator, getLocatorSchema, update, addFilter, index maps, SessionStorage changes
|