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,32 +0,0 @@
|
|
|
1
|
-
import { expect, test } from "@fixtures-v2/testApp.fixtures";
|
|
2
|
-
import type { Page } from "@playwright/test";
|
|
3
|
-
import { createRegistryWithAccessors } from "pomwright";
|
|
4
|
-
|
|
5
|
-
const createRegistry = <Paths extends string>(page: Page) => createRegistryWithAccessors<Paths>(page);
|
|
6
|
-
|
|
7
|
-
test("remove deletes definition and causes resolution to fail", async ({ page }) => {
|
|
8
|
-
type Paths = "root" | "root.target";
|
|
9
|
-
|
|
10
|
-
const { add, getLocatorSchema } = createRegistry<Paths>(page);
|
|
11
|
-
|
|
12
|
-
add("root").locator("div.root");
|
|
13
|
-
add("root.target").locator(".child");
|
|
14
|
-
|
|
15
|
-
const builder = getLocatorSchema("root.target");
|
|
16
|
-
builder.remove("root.target");
|
|
17
|
-
|
|
18
|
-
expect(() => builder.getNestedLocator()).toThrowError('No locator schema registered for path "root.target".');
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
test("remove on non-terminal skips the segment while allowing resolution", async ({ page }) => {
|
|
22
|
-
type Paths = "root" | "root.child";
|
|
23
|
-
|
|
24
|
-
const { add, getLocatorSchema } = createRegistry<Paths>(page);
|
|
25
|
-
|
|
26
|
-
add("root").locator("div.root");
|
|
27
|
-
add("root.child").getByRole("button", { name: "Submit" });
|
|
28
|
-
|
|
29
|
-
const locator = getLocatorSchema("root.child").remove("root").getNestedLocator();
|
|
30
|
-
|
|
31
|
-
expect(`${locator}`).toEqual("getByRole('button', { name: 'Submit' })");
|
|
32
|
-
});
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { expect, test } from "@fixtures-v2/testApp.fixtures";
|
|
2
|
-
import type { Page } from "@playwright/test";
|
|
3
|
-
import { createRegistryWithAccessors } from "pomwright";
|
|
4
|
-
|
|
5
|
-
const createRegistry = <Paths extends string>(page: Page) => createRegistryWithAccessors<Paths>(page);
|
|
6
|
-
|
|
7
|
-
test("replace updates definition while retaining recorded steps", async ({ page }) => {
|
|
8
|
-
type Paths = "root" | "root.target";
|
|
9
|
-
|
|
10
|
-
const { add, getLocatorSchema } = createRegistry<Paths>(page);
|
|
11
|
-
|
|
12
|
-
add("root").locator("div.root");
|
|
13
|
-
add("root.target").locator(".old").filter({ hasText: "old" });
|
|
14
|
-
|
|
15
|
-
const locator = getLocatorSchema("root.target")
|
|
16
|
-
.replace("root.target")
|
|
17
|
-
.getByRole("button", { name: "New" })
|
|
18
|
-
.filter("root.target", { hasText: "patched" })
|
|
19
|
-
.getNestedLocator();
|
|
20
|
-
|
|
21
|
-
expect(`${locator}`).toEqual(
|
|
22
|
-
"locator('div.root').getByRole('button', { name: 'New' }).filter({ hasText: 'old' }).filter({ hasText: 'patched' })",
|
|
23
|
-
);
|
|
24
|
-
});
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import { expect, test } from "@fixtures-v2/testApp.fixtures";
|
|
2
|
-
|
|
3
|
-
test("shorthand getLocator returns the same none-nested locator as getLocatorSchema.getLocator", async ({
|
|
4
|
-
testPage,
|
|
5
|
-
}) => {
|
|
6
|
-
const builderLocator = testPage.getLocatorSchema("topMenu.notifications.dropdown.item").getLocator();
|
|
7
|
-
const shorthand = testPage.getLocator("topMenu.notifications.dropdown.item");
|
|
8
|
-
|
|
9
|
-
expect(`${shorthand}`).toEqual(`${builderLocator}`);
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
test("shorthand getNestedLocator returns the same nested locator as getLocatorSchema.getNestedLocator", async ({
|
|
13
|
-
testPage,
|
|
14
|
-
}) => {
|
|
15
|
-
const builderLocator = testPage.getLocatorSchema("topMenu.notifications.dropdown.item").getNestedLocator();
|
|
16
|
-
const shorthand = testPage.getNestedLocator("topMenu.notifications.dropdown.item");
|
|
17
|
-
|
|
18
|
-
expect(`${shorthand}`).toEqual(`${builderLocator}`);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
test("independent builders do not share state", async ({ testFilters }) => {
|
|
22
|
-
const modified = testFilters
|
|
23
|
-
.getLocatorSchema("body.section.heading")
|
|
24
|
-
.update("body.section.heading")
|
|
25
|
-
.getByRole({ name: "hello", level: 3, exact: true })
|
|
26
|
-
.getNestedLocator();
|
|
27
|
-
|
|
28
|
-
const untouched = testFilters.getLocatorSchema("body.section.heading").getNestedLocator();
|
|
29
|
-
|
|
30
|
-
expect(`${modified}`).toEqual(
|
|
31
|
-
"locator('body').locator('section').getByRole('heading', { name: 'hello', exact: true, level: 3 })",
|
|
32
|
-
);
|
|
33
|
-
expect(`${untouched}`).toEqual("locator('body').locator('section').getByRole('heading', { level: 2 })");
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test("demonstrate filter and index implementation in v2", async ({ page, testFilters }) => {
|
|
37
|
-
const schemaTwo = testFilters.getLocatorSchema("one.two");
|
|
38
|
-
|
|
39
|
-
const initialLocator = schemaTwo.getNestedLocator();
|
|
40
|
-
expect(`${initialLocator}`).toEqual("locator('div.one').locator('div.two').filter({ hasText: 'two' }).first()");
|
|
41
|
-
|
|
42
|
-
schemaTwo
|
|
43
|
-
.update("one.two")
|
|
44
|
-
.locator()
|
|
45
|
-
.clearSteps("one.two")
|
|
46
|
-
.filter("one.two", { hasText: "NewText" })
|
|
47
|
-
.filter("one.two", { hasText: "AdditionalText" })
|
|
48
|
-
.nth("one.two", "last");
|
|
49
|
-
const newLocator = schemaTwo.getNestedLocator();
|
|
50
|
-
expect(`${newLocator}`).toEqual(
|
|
51
|
-
"locator('div.one').locator('div.two').filter({ hasText: 'NewText' }).filter({ hasText: 'AdditionalText' }).last()",
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
schemaTwo
|
|
55
|
-
.clearSteps("one.two")
|
|
56
|
-
.filter("one.two", { hasText: "NewText" })
|
|
57
|
-
.filter("one.two", { hasText: "AdditionalText" })
|
|
58
|
-
.filter("one.two", { hasText: "AddedText" })
|
|
59
|
-
.filter("one.two", { hasText: "LastText" })
|
|
60
|
-
.nth("one", 0)
|
|
61
|
-
.nth("one.two", 1);
|
|
62
|
-
const updatedLocator = schemaTwo.getNestedLocator();
|
|
63
|
-
expect(`${updatedLocator}`).toEqual(
|
|
64
|
-
"locator('div.one').first().locator('div.two').filter({ hasText: 'NewText' }).filter({ hasText: 'AdditionalText' }).filter({ hasText: 'AddedText' }).filter({ hasText: 'LastText' }).nth(1)",
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
// But in vanilla Playwright we can do:
|
|
68
|
-
|
|
69
|
-
const manualLocator = page
|
|
70
|
-
.locator("div.one")
|
|
71
|
-
.locator("div.two")
|
|
72
|
-
.first()
|
|
73
|
-
.filter({ hasText: "NewText" })
|
|
74
|
-
.last()
|
|
75
|
-
.filter({ hasText: "AdditionalText" })
|
|
76
|
-
.filter({ hasText: "AddedText" })
|
|
77
|
-
.filter({ hasText: "LastText" })
|
|
78
|
-
.nth(1);
|
|
79
|
-
expect(`${manualLocator}`).toEqual(
|
|
80
|
-
"locator('div.one').locator('div.two').first().filter({ hasText: 'NewText' }).last().filter({ hasText: 'AdditionalText' }).filter({ hasText: 'AddedText' }).filter({ hasText: 'LastText' }).nth(1)",
|
|
81
|
-
);
|
|
82
|
-
|
|
83
|
-
const autoChainedLocator = testFilters
|
|
84
|
-
.getLocatorSchema("one.two")
|
|
85
|
-
.clearSteps("one.two")
|
|
86
|
-
.nth("one.two", 0)
|
|
87
|
-
.filter("one.two", { hasText: "NewText" })
|
|
88
|
-
.nth("one.two", -1)
|
|
89
|
-
.filter("one.two", { hasText: "AdditionalText" })
|
|
90
|
-
.filter("one.two", { hasText: "AddedText" })
|
|
91
|
-
.filter("one.two", { hasText: "LastText" })
|
|
92
|
-
.nth("one.two", 1)
|
|
93
|
-
.getNestedLocator();
|
|
94
|
-
|
|
95
|
-
expect(`${autoChainedLocator}`).toEqual(`${manualLocator}`);
|
|
96
|
-
|
|
97
|
-
const autoChainedLocatorWrapper = testFilters
|
|
98
|
-
.getLocatorSchema("one.two")
|
|
99
|
-
.clearSteps("one.two")
|
|
100
|
-
.nth("one.two", 0)
|
|
101
|
-
.filter("one.two", { hasText: "NewText" })
|
|
102
|
-
.nth("one.two", -1)
|
|
103
|
-
.filter("one.two", { hasText: "AdditionalText" })
|
|
104
|
-
.filter("one.two", { hasText: "AddedText" })
|
|
105
|
-
.filter("one.two", { hasText: "LastText" })
|
|
106
|
-
.nth("one.two", 1)
|
|
107
|
-
.getNestedLocator();
|
|
108
|
-
|
|
109
|
-
expect(`${autoChainedLocatorWrapper}`).toEqual(`${manualLocator}`);
|
|
110
|
-
});
|
|
@@ -1,322 +0,0 @@
|
|
|
1
|
-
import { expect, test } from "@fixtures-v2/testApp.fixtures";
|
|
2
|
-
import { LocatorRegistryInternal } from "../../../../srcV2/locators";
|
|
3
|
-
|
|
4
|
-
test("update replaces intermediate definitions without mutating registry", async ({ testFilters }) => {
|
|
5
|
-
const original = testFilters.getNestedLocator("body.section.heading");
|
|
6
|
-
expect(`${original}`).toEqual("locator('body').locator('section').getByRole('heading', { level: 2 })");
|
|
7
|
-
|
|
8
|
-
const replaced = testFilters
|
|
9
|
-
.getLocatorSchema("body.section.heading")
|
|
10
|
-
.update("body.section")
|
|
11
|
-
.getByRole("heading", { level: 1 })
|
|
12
|
-
.getNestedLocator();
|
|
13
|
-
|
|
14
|
-
expect(`${replaced}`).toEqual(
|
|
15
|
-
"locator('body').getByRole('heading', { level: 1 }).getByRole('heading', { level: 2 })",
|
|
16
|
-
);
|
|
17
|
-
|
|
18
|
-
const after = testFilters.getNestedLocator("body.section.heading");
|
|
19
|
-
expect(`${after}`).toEqual(`${original}`);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
test("update operations can be chained across sub-paths", async ({ testFilters }) => {
|
|
23
|
-
const chained = testFilters
|
|
24
|
-
.getLocatorSchema("body.section")
|
|
25
|
-
.update("body")
|
|
26
|
-
.locator("SOMEBODY")
|
|
27
|
-
.update("body.section")
|
|
28
|
-
.getByRole("button", { name: "Click me!" })
|
|
29
|
-
.getNestedLocator();
|
|
30
|
-
|
|
31
|
-
expect(`${chained}`).toEqual("locator('SOMEBODY').getByRole('button', { name: 'Click me!' })");
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
test("update merges options without requiring full definitions", async ({ testFilters }) => {
|
|
35
|
-
const merged = testFilters
|
|
36
|
-
.getLocatorSchema("body.section.heading")
|
|
37
|
-
.update("body.section.heading")
|
|
38
|
-
.getByRole({ name: "HEADING TEXT" })
|
|
39
|
-
.getNestedLocator();
|
|
40
|
-
|
|
41
|
-
expect(`${merged}`).toEqual(
|
|
42
|
-
"locator('body').locator('section').getByRole('heading', { name: 'HEADING TEXT', level: 2 })",
|
|
43
|
-
);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
test("update handles full and partial definitions from fresh builders", async ({ testFilters }) => {
|
|
47
|
-
const full = testFilters
|
|
48
|
-
.getLocatorSchema("body.section.heading")
|
|
49
|
-
.update("body.section.heading")
|
|
50
|
-
.getByRole("heading", { level: 3 })
|
|
51
|
-
.getNestedLocator();
|
|
52
|
-
|
|
53
|
-
const partial = testFilters
|
|
54
|
-
.getLocatorSchema("body.section.heading")
|
|
55
|
-
.update("body.section.heading")
|
|
56
|
-
.getByRole({ level: 3 })
|
|
57
|
-
.getNestedLocator();
|
|
58
|
-
|
|
59
|
-
expect(`${full}`).toEqual("locator('body').locator('section').getByRole('heading', { level: 3 })");
|
|
60
|
-
expect(`${partial}`).toEqual("locator('body').locator('section').getByRole('heading', { level: 3 })");
|
|
61
|
-
expect(full).not.toBe(partial);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
test("update preserves registered filters on untouched segments", async ({ testFilters }) => {
|
|
65
|
-
const locator = testFilters
|
|
66
|
-
.getLocatorSchema("fictional.filter@hasNotText.filter@hasText")
|
|
67
|
-
.update("fictional.filter@hasNotText")
|
|
68
|
-
.getByRole("button", { name: "roleOptions" })
|
|
69
|
-
.update("fictional.filter@hasNotText.filter@hasText")
|
|
70
|
-
.locator("locator")
|
|
71
|
-
.getNestedLocator();
|
|
72
|
-
|
|
73
|
-
expect(`${locator}`).toEqual(
|
|
74
|
-
"getByRole('button', { name: 'roleOptions' }).filter({ hasNotText: 'hasNotText' }).locator('locator').filter({ hasText: 'hasText' })",
|
|
75
|
-
);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
test("update rejects unknown sub-paths", ({ testFilters }) => {
|
|
79
|
-
expect(() =>
|
|
80
|
-
testFilters
|
|
81
|
-
.getLocatorSchema("body.section.heading")
|
|
82
|
-
// @ts-expect-error Testing invalid path handling
|
|
83
|
-
.update("body.section.missing")
|
|
84
|
-
.locator("noop"),
|
|
85
|
-
).toThrow('"body.section.missing" is not a valid sub-path of "body.section.heading"');
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
test("update can mix ancestor and descendant changes without mutating registry", async ({ testFilters }) => {
|
|
89
|
-
const original = testFilters.getNestedLocator("body.section.heading");
|
|
90
|
-
|
|
91
|
-
const chained = testFilters
|
|
92
|
-
.getLocatorSchema("body.section.heading")
|
|
93
|
-
.update("body")
|
|
94
|
-
.locator("SOMEBODY")
|
|
95
|
-
.update("body.section.heading")
|
|
96
|
-
.getByRole({ name: "HEADING TEXT" })
|
|
97
|
-
.getNestedLocator();
|
|
98
|
-
|
|
99
|
-
expect(`${chained}`).toEqual(
|
|
100
|
-
"locator('SOMEBODY').locator('section').getByRole('heading', { name: 'HEADING TEXT', level: 2 })",
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
const after = testFilters.getNestedLocator("body.section.heading");
|
|
104
|
-
expect(`${after}`).toEqual(`${original}`);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
test("update preserves filters on the target sub-path", async ({ testFilters }) => {
|
|
108
|
-
const locator = testFilters
|
|
109
|
-
.getLocatorSchema("fictional.filter@hasText")
|
|
110
|
-
.update("fictional.filter@hasText")
|
|
111
|
-
.locator("updated")
|
|
112
|
-
.getNestedLocator();
|
|
113
|
-
|
|
114
|
-
expect(`${locator}`).toEqual("locator('updated').filter({ hasText: 'hasText' })");
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
test("update patches definitions without altering chained filters or indices", async ({ testFilters }) => {
|
|
118
|
-
const updated = testFilters
|
|
119
|
-
.getLocatorSchema("body.section.button")
|
|
120
|
-
.filter("body.section.button", { hasText: /Click me!/ })
|
|
121
|
-
.nth("body.section", "first")
|
|
122
|
-
.update("body.section.button")
|
|
123
|
-
.getByRole("button", { name: "Click me!" })
|
|
124
|
-
.getNestedLocator();
|
|
125
|
-
|
|
126
|
-
expect(`${updated}`).toEqual(
|
|
127
|
-
"locator('body').locator('section').first().getByRole('button', { name: 'Click me!' }).filter({ hasText: /Click me!/ })",
|
|
128
|
-
);
|
|
129
|
-
|
|
130
|
-
const untouched = testFilters.getLocatorSchema("body.section.button").getNestedLocator();
|
|
131
|
-
expect(`${untouched}`).toEqual("locator('body').locator('section').getByRole('button')");
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
test("update can remove locator options filters", async ({ testFilters }) => {
|
|
135
|
-
const original = testFilters.getNestedLocator("body.section@playground");
|
|
136
|
-
expect(`${original}`).toEqual("locator('body').locator('section').filter({ hasText: /Playground/i })");
|
|
137
|
-
|
|
138
|
-
const locator = testFilters
|
|
139
|
-
.getLocatorSchema("body.section@playground")
|
|
140
|
-
.update("body.section@playground")
|
|
141
|
-
.locator({ hasText: undefined })
|
|
142
|
-
.getNestedLocator();
|
|
143
|
-
|
|
144
|
-
expect(`${locator}`).toEqual("locator('body').locator('section')");
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
test("update can switch locator strategies, caching all latest locator definitions and preserving state of filters and indices", async ({
|
|
148
|
-
page,
|
|
149
|
-
testFilters,
|
|
150
|
-
}) => {
|
|
151
|
-
const path = "body.section" as const;
|
|
152
|
-
const schema = testFilters.getLocatorSchema(path);
|
|
153
|
-
|
|
154
|
-
const initialLocator = schema.getNestedLocator();
|
|
155
|
-
expect(`${initialLocator}`).toEqual("locator('body').locator('section')");
|
|
156
|
-
|
|
157
|
-
const manualLocator = page.locator("body").locator("newSelector").filter({ hasText: "Text" }).first();
|
|
158
|
-
const locator = schema
|
|
159
|
-
.update(path)
|
|
160
|
-
.locator("newSelector")
|
|
161
|
-
.filter(path, { hasText: "Text" })
|
|
162
|
-
.nth(path, 0)
|
|
163
|
-
.getNestedLocator();
|
|
164
|
-
expect(`${locator}`).toEqual(`${manualLocator}`);
|
|
165
|
-
|
|
166
|
-
const manualRole = page
|
|
167
|
-
.locator("body")
|
|
168
|
-
.getByRole("region", { name: "Now a region" })
|
|
169
|
-
.filter({ hasText: "Text" })
|
|
170
|
-
.first();
|
|
171
|
-
const role = schema.update(path).getByRole("region", { name: "Now a region" }).getNestedLocator();
|
|
172
|
-
expect(`${role}`).toEqual(`${manualRole}`);
|
|
173
|
-
|
|
174
|
-
const manualText = page.locator("body").getByText("Text node").filter({ hasText: "Text" }).first();
|
|
175
|
-
const text = schema.update(path).getByText("Text node").getNestedLocator();
|
|
176
|
-
expect(`${text}`).toEqual(`${manualText}`);
|
|
177
|
-
|
|
178
|
-
const manualLabel = page.locator("body").getByLabel("Label").filter({ hasText: "Text" }).first();
|
|
179
|
-
const label = schema.update(path).getByLabel("Label").getNestedLocator();
|
|
180
|
-
expect(`${label}`).toEqual(`${manualLabel}`);
|
|
181
|
-
|
|
182
|
-
const manualPlaceholder = page.locator("body").getByPlaceholder("Placeholder").filter({ hasText: "Text" }).first();
|
|
183
|
-
const placeholder = schema.update(path).getByPlaceholder("Placeholder").getNestedLocator();
|
|
184
|
-
expect(`${placeholder}`).toEqual(`${manualPlaceholder}`);
|
|
185
|
-
|
|
186
|
-
const manualAltText = page.locator("body").getByAltText("Alt").filter({ hasText: "Text" }).first();
|
|
187
|
-
const altText = schema.update(path).getByAltText("Alt").getNestedLocator();
|
|
188
|
-
expect(`${altText}`).toEqual(`${manualAltText}`);
|
|
189
|
-
|
|
190
|
-
const manualTitle = page.locator("body").getByTitle("Title").filter({ hasText: "Text" }).first();
|
|
191
|
-
const title = schema.update(path).getByTitle("Title").getNestedLocator();
|
|
192
|
-
expect(`${title}`).toEqual(`${manualTitle}`);
|
|
193
|
-
|
|
194
|
-
const manualFrameLocator = page.locator("body").locator("iframe[name=child]");
|
|
195
|
-
const frameLocator = schema.update(path).frameLocator("iframe[name=child]").getNestedLocator();
|
|
196
|
-
expect(`${frameLocator}`).toEqual(`${manualFrameLocator}`);
|
|
197
|
-
|
|
198
|
-
const manualTestId = page.locator("body").getByTestId("new-test-id").filter({ hasText: "Text" }).first();
|
|
199
|
-
const testId = schema.update(path).getByTestId("new-test-id").getNestedLocator();
|
|
200
|
-
expect(`${testId}`).toEqual(`${manualTestId}`);
|
|
201
|
-
|
|
202
|
-
const manualId = page.locator("body").locator("#new-id").filter({ hasText: "Text" }).first();
|
|
203
|
-
const id = schema.update(path).getById("new-id").getNestedLocator();
|
|
204
|
-
expect(`${id}`).toEqual(`${manualId}`);
|
|
205
|
-
|
|
206
|
-
const manualDataCy = page.locator("body").locator('[data-cy="new-cy"]').filter({ hasText: "Text" }).first();
|
|
207
|
-
const dataCy = schema.update(path).locator('[data-cy="new-cy"]').getNestedLocator();
|
|
208
|
-
expect(`${dataCy}`).toEqual(`${manualDataCy}`);
|
|
209
|
-
|
|
210
|
-
const resetLocator = schema.update(path).locator().getNestedLocator();
|
|
211
|
-
expect(`${resetLocator}`).toEqual(`${dataCy}`);
|
|
212
|
-
|
|
213
|
-
const resetRole = schema.update(path).getByRole().getNestedLocator();
|
|
214
|
-
expect(`${resetRole}`).toEqual(`${role}`);
|
|
215
|
-
|
|
216
|
-
const resetText = schema.update(path).getByText().getNestedLocator();
|
|
217
|
-
expect(`${resetText}`).toEqual(`${text}`);
|
|
218
|
-
|
|
219
|
-
const resetLabel = schema.update(path).getByLabel().getNestedLocator();
|
|
220
|
-
expect(`${resetLabel}`).toEqual(`${label}`);
|
|
221
|
-
|
|
222
|
-
const resetPlaceholder = schema.update(path).getByPlaceholder().getNestedLocator();
|
|
223
|
-
expect(`${resetPlaceholder}`).toEqual(`${placeholder}`);
|
|
224
|
-
|
|
225
|
-
const resetAltText = schema.update(path).getByAltText().getNestedLocator();
|
|
226
|
-
expect(`${resetAltText}`).toEqual(`${altText}`);
|
|
227
|
-
|
|
228
|
-
const resetTitle = schema.update(path).getByTitle().getNestedLocator();
|
|
229
|
-
expect(`${resetTitle}`).toEqual(`${title}`);
|
|
230
|
-
|
|
231
|
-
const resetFrameLocator = schema.update(path).frameLocator().getNestedLocator();
|
|
232
|
-
expect(`${resetFrameLocator}`).toEqual(`${frameLocator}`);
|
|
233
|
-
|
|
234
|
-
const resetTestId = schema.update(path).getByTestId().getNestedLocator();
|
|
235
|
-
expect(`${resetTestId}`).toEqual(`${testId}`);
|
|
236
|
-
|
|
237
|
-
const resetId = schema.update(path).getById().getNestedLocator();
|
|
238
|
-
expect(`${resetId}`).toEqual(`${id}`);
|
|
239
|
-
|
|
240
|
-
const resetLocatorAgain = schema.update(path).locator().getNestedLocator();
|
|
241
|
-
expect(`${resetLocatorAgain}`).toEqual(`${dataCy}`);
|
|
242
|
-
|
|
243
|
-
const lastRoleClearSteps = schema
|
|
244
|
-
.update(path)
|
|
245
|
-
.getByRole()
|
|
246
|
-
.clearSteps("body")
|
|
247
|
-
.clearSteps("body.section")
|
|
248
|
-
.getNestedLocator();
|
|
249
|
-
expect(`${lastRoleClearSteps}`).toEqual("locator('body').getByRole('region', { name: 'Now a region' })");
|
|
250
|
-
|
|
251
|
-
const stillNoFiltersAndIndicesOnAdditionalSwitch = schema.update(path).locator().getNestedLocator();
|
|
252
|
-
expect(`${stillNoFiltersAndIndicesOnAdditionalSwitch}`).toEqual("locator('body').locator('[data-cy=\"new-cy\"]')");
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
test("update getByRole overloads preserve patch semantics without undefined placeholders", async ({ page }) => {
|
|
256
|
-
type LocalPath = "overload" | "overload.target";
|
|
257
|
-
const registry = new LocatorRegistryInternal<LocalPath>(page);
|
|
258
|
-
|
|
259
|
-
registry.add("overload").locator("body");
|
|
260
|
-
registry.add("overload.target").getByRole("button", { name: "initial" }).filter({ hasText: "initial" }).nth(0);
|
|
261
|
-
|
|
262
|
-
const withRoleAndOptions = await registry
|
|
263
|
-
.getLocatorSchema("overload.target")
|
|
264
|
-
.update("overload.target")
|
|
265
|
-
.getByRole("button", { name: "patched" })
|
|
266
|
-
.filter("overload.target", { hasText: "patched" })
|
|
267
|
-
.nth("overload.target", "last")
|
|
268
|
-
.getNestedLocator();
|
|
269
|
-
|
|
270
|
-
expect(`${withRoleAndOptions}`).toContain("getByRole('button', { name: 'patched' })");
|
|
271
|
-
expect(`${withRoleAndOptions}`).toContain("filter({ hasText: 'patched' })");
|
|
272
|
-
expect(`${withRoleAndOptions}`).toContain("last()");
|
|
273
|
-
|
|
274
|
-
const withOptionsOnly = await registry
|
|
275
|
-
.getLocatorSchema("overload.target")
|
|
276
|
-
.update("overload.target")
|
|
277
|
-
.getByRole({ name: "patched" })
|
|
278
|
-
.getNestedLocator();
|
|
279
|
-
|
|
280
|
-
expect(`${withOptionsOnly}`).toContain("getByRole('button', { name: 'patched' })");
|
|
281
|
-
expect(`${withOptionsOnly}`).toContain("filter({ hasText: 'initial' })");
|
|
282
|
-
expect(`${withOptionsOnly}`).toContain("first()");
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
test("update overloads cover multiple strategies and retain filter/index steps", async ({ page }) => {
|
|
286
|
-
type LocalPath = "update.text" | "update.locator" | "update.frame";
|
|
287
|
-
const registry = new LocatorRegistryInternal<LocalPath>(page);
|
|
288
|
-
|
|
289
|
-
registry.add("update.text").getByText("seed");
|
|
290
|
-
registry.add("update.locator").locator(".seed");
|
|
291
|
-
registry.add("update.frame").frameLocator("iframe[name=seed]");
|
|
292
|
-
|
|
293
|
-
const textPatched = await registry
|
|
294
|
-
.getLocatorSchema("update.text")
|
|
295
|
-
.update("update.text")
|
|
296
|
-
.getByText({ exact: true })
|
|
297
|
-
.filter("update.text", { hasText: "patched" })
|
|
298
|
-
.nth("update.text", 0)
|
|
299
|
-
.getNestedLocator();
|
|
300
|
-
|
|
301
|
-
expect(`${textPatched}`).toEqual("getByText('seed', { exact: true }).filter({ hasText: 'patched' }).first()");
|
|
302
|
-
|
|
303
|
-
const locatorPatched = await registry
|
|
304
|
-
.getLocatorSchema("update.locator")
|
|
305
|
-
.update("update.locator")
|
|
306
|
-
.locator({ hasText: "opt" })
|
|
307
|
-
.filter("update.locator", { hasText: "patched" })
|
|
308
|
-
.nth("update.locator", 0)
|
|
309
|
-
.getNestedLocator();
|
|
310
|
-
|
|
311
|
-
expect(`${locatorPatched}`).toEqual(
|
|
312
|
-
"locator('.seed').filter({ hasText: 'opt' }).filter({ hasText: 'patched' }).first()",
|
|
313
|
-
);
|
|
314
|
-
|
|
315
|
-
const framePatched = await registry
|
|
316
|
-
.getLocatorSchema("update.frame")
|
|
317
|
-
.update("update.frame")
|
|
318
|
-
.frameLocator()
|
|
319
|
-
.getNestedLocator();
|
|
320
|
-
|
|
321
|
-
expect(`${framePatched}`).toEqual("locator('iframe[name=seed]')");
|
|
322
|
-
});
|