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.
Files changed (119) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +5 -5
  3. package/dist/index.d.mts +75 -970
  4. package/dist/index.d.ts +75 -970
  5. package/dist/index.js +585 -1872
  6. package/dist/index.mjs +598 -1880
  7. package/package.json +9 -11
  8. package/AGENTS.md +0 -37
  9. package/docs/v1/BaseApi-explanation.md +0 -63
  10. package/docs/v1/BasePage-explanation.md +0 -96
  11. package/docs/v1/LocatorSchema-explanation.md +0 -271
  12. package/docs/v1/LocatorSchemaPath-explanation.md +0 -165
  13. package/docs/v1/PlaywrightReportLogger-explanation.md +0 -56
  14. package/docs/v1/get-locator-methods-explanation.md +0 -250
  15. package/docs/v1/intro-to-using-pomwright.md +0 -899
  16. package/docs/v1/sessionStorage-methods-explanation.md +0 -38
  17. package/docs/v1/tips-folder-structure.md +0 -38
  18. package/docs/v1-to-v2-migration/bridge-migration-guide.md +0 -159
  19. package/docs/v1-to-v2-migration/direct-migration-guide.md +0 -238
  20. package/docs/v1-to-v2-migration/v1-to-v2-comparison.md +0 -547
  21. package/docs/v2/PageObject.md +0 -293
  22. package/docs/v2/composing-locator-modules.md +0 -93
  23. package/docs/v2/locator-registry.md +0 -695
  24. package/docs/v2/logging.md +0 -168
  25. package/docs/v2/overview.md +0 -515
  26. package/docs/v2/session-storage.md +0 -160
  27. package/index.ts +0 -75
  28. package/intTestV2/.env +0 -0
  29. package/intTestV2/fixtures/testApp.fixtures.ts +0 -43
  30. package/intTestV2/package.json +0 -22
  31. package/intTestV2/page-object-models/testApp/pages/iframe/iframe.locatorSchema.ts +0 -24
  32. package/intTestV2/page-object-models/testApp/pages/iframe/iframe.page.ts +0 -17
  33. package/intTestV2/page-object-models/testApp/pages/testPage.locatorSchema.ts +0 -32
  34. package/intTestV2/page-object-models/testApp/pages/testPage.page.ts +0 -119
  35. package/intTestV2/page-object-models/testApp/pages/testPath/[color]/color.locatorSchema.ts +0 -29
  36. package/intTestV2/page-object-models/testApp/pages/testPath/[color]/color.page.ts +0 -48
  37. package/intTestV2/page-object-models/testApp/pages/testPath/testPath.locatorSchema.ts +0 -9
  38. package/intTestV2/page-object-models/testApp/pages/testPath/testPath.page.ts +0 -23
  39. package/intTestV2/page-object-models/testApp/pages/testfilters/testfilters.locatorSchema.ts +0 -114
  40. package/intTestV2/page-object-models/testApp/pages/testfilters/testfilters.page.ts +0 -23
  41. package/intTestV2/page-object-models/testApp/testApp.base.ts +0 -20
  42. package/intTestV2/playwright.config.ts +0 -54
  43. package/intTestV2/server.js +0 -216
  44. package/intTestV2/test-data/staticPage/index.html +0 -280
  45. package/intTestV2/test-data/staticPage/w3images/avatar2.png +0 -0
  46. package/intTestV2/test-data/staticPage/w3images/avatar3.png +0 -0
  47. package/intTestV2/test-data/staticPage/w3images/avatar5.png +0 -0
  48. package/intTestV2/test-data/staticPage/w3images/avatar6.png +0 -0
  49. package/intTestV2/test-data/staticPage/w3images/forest.jpg +0 -0
  50. package/intTestV2/test-data/staticPage/w3images/lights.jpg +0 -0
  51. package/intTestV2/test-data/staticPage/w3images/mountains.jpg +0 -0
  52. package/intTestV2/test-data/staticPage/w3images/nature.jpg +0 -0
  53. package/intTestV2/test-data/staticPage/w3images/snow.jpg +0 -0
  54. package/intTestV2/tests/locatorRegistry/add/add.describe.spec.ts +0 -54
  55. package/intTestV2/tests/locatorRegistry/add/add.filter.spec.ts +0 -143
  56. package/intTestV2/tests/locatorRegistry/add/add.frameLocator.spec.ts +0 -23
  57. package/intTestV2/tests/locatorRegistry/add/add.get.clone.spec.ts +0 -76
  58. package/intTestV2/tests/locatorRegistry/add/add.getByAltText.spec.ts +0 -23
  59. package/intTestV2/tests/locatorRegistry/add/add.getById.spec.ts +0 -45
  60. package/intTestV2/tests/locatorRegistry/add/add.getByLabel.spec.ts +0 -23
  61. package/intTestV2/tests/locatorRegistry/add/add.getByPlaceholder.spec.ts +0 -23
  62. package/intTestV2/tests/locatorRegistry/add/add.getByRole.spec.ts +0 -23
  63. package/intTestV2/tests/locatorRegistry/add/add.getByTestId.spec.ts +0 -23
  64. package/intTestV2/tests/locatorRegistry/add/add.getByText.spec.ts +0 -23
  65. package/intTestV2/tests/locatorRegistry/add/add.getByTitle.spec.ts +0 -23
  66. package/intTestV2/tests/locatorRegistry/add/add.locator.spec.ts +0 -23
  67. package/intTestV2/tests/locatorRegistry/add/add.reuseExisting.spec.ts +0 -107
  68. package/intTestV2/tests/locatorRegistry/add/add.reuseReusable.spec.ts +0 -311
  69. package/intTestV2/tests/locatorRegistry/add/add.spec.ts +0 -159
  70. package/intTestV2/tests/locatorRegistry/filter.cycle.spec.ts +0 -39
  71. package/intTestV2/tests/locatorRegistry/getLocator/getLocator.spec.ts +0 -253
  72. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.clearSteps.spec.ts +0 -105
  73. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.describe.spec.ts +0 -23
  74. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.filter.spec.ts +0 -368
  75. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.getLocator.spec.ts +0 -56
  76. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.getNestedLocator.spec.ts +0 -175
  77. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.nth.spec.ts +0 -60
  78. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.remove.spec.ts +0 -32
  79. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.replace.spec.ts +0 -24
  80. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.spec.ts +0 -110
  81. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.update.spec.ts +0 -322
  82. package/intTestV2/tests/locatorRegistry/getNestedLocator/getNestedLocator.spec.ts +0 -412
  83. package/intTestV2/tests/locatorRegistry/registry/registry.binding.spec.ts +0 -50
  84. package/intTestV2/tests/locatorRegistry/validation/validation.locatorSchemaPath.spec.ts +0 -115
  85. package/intTestV2/tests/locatorRegistry/validation/validation.locatorSchemaPath.typecheck.ts +0 -86
  86. package/intTestV2/tests/locatorRegistry/validation/validation.sub-path.spec.ts +0 -45
  87. package/intTestV2/tests/step/step.spec.ts +0 -49
  88. package/intTestV2/tests/testApp/color.spec.ts +0 -15
  89. package/intTestV2/tests/testApp/iframe.spec.ts +0 -57
  90. package/intTestV2/tests/testApp/testFilters.spec.ts +0 -24
  91. package/intTestV2/tests/testApp/testPage.spec.ts +0 -161
  92. package/intTestV2/tests/testApp/testPath.spec.ts +0 -18
  93. package/pack-build.sh +0 -11
  94. package/pack-test-v2.sh +0 -36
  95. package/playwright.base.ts +0 -42
  96. package/skills/README.md +0 -56
  97. package/skills/pomwright-v1-5-bridge-migration/SKILL.md +0 -40
  98. package/skills/pomwright-v1-5-bridge-migration/references/call-site-migration.md +0 -178
  99. package/skills/pomwright-v1-5-bridge-migration/references/schema-translation.md +0 -183
  100. package/skills/pomwright-v2-migration/SKILL.md +0 -63
  101. package/skills/pomwright-v2-migration/references/call-site-migration.md +0 -265
  102. package/skills/pomwright-v2-migration/references/class-migration.md +0 -266
  103. package/skills/pomwright-v2-migration/references/fixture-and-helpers.md +0 -423
  104. package/skills/pomwright-v2-migration/references/locator-registration.md +0 -344
  105. package/srcV2/fixture/base.fixtures.ts +0 -23
  106. package/srcV2/helpers/navigation.ts +0 -153
  107. package/srcV2/helpers/playwrightReportLogger.ts +0 -196
  108. package/srcV2/helpers/sessionStorage.ts +0 -251
  109. package/srcV2/helpers/stepDecorator.ts +0 -106
  110. package/srcV2/locators/index.ts +0 -15
  111. package/srcV2/locators/locatorQueryBuilder.ts +0 -427
  112. package/srcV2/locators/locatorRegistrationBuilder.ts +0 -558
  113. package/srcV2/locators/locatorRegistry.ts +0 -583
  114. package/srcV2/locators/locatorUpdateBuilder.ts +0 -602
  115. package/srcV2/locators/reusableLocatorBuilder.ts +0 -200
  116. package/srcV2/locators/types.ts +0 -256
  117. package/srcV2/locators/utils.ts +0 -309
  118. package/srcV2/locators/v1SchemaTranslator.ts +0 -178
  119. package/srcV2/pageObject.ts +0 -105
@@ -1,159 +0,0 @@
1
- import { expect, test } from "@fixtures-v2/testApp.fixtures";
2
- import type { Page } from "@playwright/test";
3
- import { LocatorRegistryInternal } from "../../../../srcV2/locators";
4
-
5
- const createTestRegistry = <Paths extends string>(page: Page) => new LocatorRegistryInternal<Paths>(page);
6
-
7
- test("add regex-driven getBy* registrations to registry", async ({ page }) => {
8
- type LocatorSchemaPaths =
9
- | "regex.text"
10
- | "regex.label"
11
- | "regex.placeholder"
12
- | "regex.altText"
13
- | "regex.title"
14
- | "regex.reuse.text"
15
- | "regex.reusePath.text";
16
-
17
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
18
-
19
- registry.add("regex.text").getByText(/Welcome/i);
20
- registry.add("regex.label").getByLabel(/Username/i);
21
- registry.add("regex.placeholder").getByPlaceholder(/Enter your name/i);
22
- registry.add("regex.altText").getByAltText(/Sample Image/i);
23
- registry.add("regex.title").getByTitle(/Home Page/i);
24
-
25
- const reusableText = registry.createReusable.getByText(/Seeded/i);
26
- registry.add("regex.reuse.text", { reuse: reusableText }).getByText({ exact: true });
27
- registry.add("regex.reusePath.text", { reuse: "regex.text" });
28
-
29
- expect(registry.get("regex.text")).toEqual({
30
- definition: { text: /Welcome/i, type: "text" },
31
- locatorSchemaPath: "regex.text",
32
- steps: [],
33
- });
34
- expect(registry.get("regex.label")).toEqual({
35
- definition: { text: /Username/i, type: "label" },
36
- locatorSchemaPath: "regex.label",
37
- steps: [],
38
- });
39
- expect(registry.get("regex.placeholder")).toEqual({
40
- definition: { text: /Enter your name/i, type: "placeholder" },
41
- locatorSchemaPath: "regex.placeholder",
42
- steps: [],
43
- });
44
- expect(registry.get("regex.altText")).toEqual({
45
- definition: { text: /Sample Image/i, type: "altText" },
46
- locatorSchemaPath: "regex.altText",
47
- steps: [],
48
- });
49
- expect(registry.get("regex.title")).toEqual({
50
- definition: { text: /Home Page/i, type: "title" },
51
- locatorSchemaPath: "regex.title",
52
- steps: [],
53
- });
54
- expect(registry.get("regex.reuse.text")).toEqual({
55
- definition: { text: /Seeded/i, options: { exact: true }, type: "text" },
56
- locatorSchemaPath: "regex.reuse.text",
57
- steps: [],
58
- });
59
- expect(registry.get("regex.reusePath.text")).toEqual({
60
- definition: { text: /Welcome/i, type: "text" },
61
- locatorSchemaPath: "regex.reusePath.text",
62
- steps: [],
63
- });
64
- });
65
-
66
- test("add prevents multiple locator type definitions", async ({ page }) => {
67
- type LocatorSchemaPaths = "heading";
68
-
69
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
70
-
71
- const builder = registry.add("heading").getByRole("heading", { level: 2 });
72
-
73
- expect((builder as { getByText?: unknown }).getByText).toBeUndefined();
74
-
75
- builder.filter({ hasText: "Heading" }).nth(0);
76
- });
77
-
78
- test("add typing narrows locator methods after a definition is chosen", async ({ page }) => {
79
- type LocatorSchemaPaths = "heading";
80
-
81
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
82
- const postDefinition = registry.add("heading").getByRole("heading", { level: 2 });
83
-
84
- // @ts-expect-error additional locator methods are not exposed after a definition is set
85
- const _invalidLocatorMethod = postDefinition.getByText;
86
-
87
- postDefinition.filter({ hasText: "Heading" }).nth(0);
88
- });
89
-
90
- test("A LocatorSchemaPath can only be added once", async ({ page }) => {
91
- type LocatorSchemaPaths = "body";
92
-
93
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
94
-
95
- registry.add("body").locator("body");
96
-
97
- const errMsg = /A locator schema with the path "body" already exists*/;
98
-
99
- expect(() => registry.add("body").locator("someone elses body")).toThrowError(errMsg);
100
- });
101
-
102
- test("add getByRole overloads accept optional options and support chained steps", async ({ page }) => {
103
- type LocalPath = "root" | "root.options" | "root.minimal";
104
- const registry = new LocatorRegistryInternal<LocalPath>(page);
105
-
106
- registry.add("root").locator("body");
107
-
108
- registry.add("root.options").getByRole("button", { name: "opts" }).filter({ hasText: "filtered" }).nth("last");
109
- registry.add("root.minimal").getByRole("button");
110
-
111
- expect(registry.get("root.options")).toEqual({
112
- locatorSchemaPath: "root.options",
113
- definition: { type: "role", role: "button", options: { name: "opts" } },
114
- steps: [
115
- { kind: "filter", filter: { hasText: "filtered" } },
116
- { kind: "index", index: "last" },
117
- ],
118
- });
119
-
120
- expect(registry.get("root.minimal")).toEqual({
121
- locatorSchemaPath: "root.minimal",
122
- definition: { type: "role", role: "button" },
123
- steps: [],
124
- });
125
- });
126
-
127
- test("add overloads support other strategies with options and chained steps", async ({ page }) => {
128
- type LocalPath = "root.text" | "root.locator" | "root.frame";
129
-
130
- const registry = new LocatorRegistryInternal<LocalPath>(page);
131
-
132
- registry.add("root.text").getByText("needle", { exact: true }).filter({ hasText: "filtered" }).nth(0);
133
- registry.add("root.locator").locator(".selector", { hasText: "opt" }).filter({ hasText: "filtered" }).nth(0);
134
- registry.add("root.frame").frameLocator("iframe[name=child]");
135
-
136
- expect(registry.get("root.text")).toEqual({
137
- locatorSchemaPath: "root.text",
138
- definition: { type: "text", text: "needle", options: { exact: true } },
139
- steps: [
140
- { kind: "filter", filter: { hasText: "filtered" } },
141
- { kind: "index", index: 0 },
142
- ],
143
- });
144
-
145
- expect(registry.get("root.locator")).toEqual({
146
- locatorSchemaPath: "root.locator",
147
- definition: { type: "locator", selector: ".selector", options: { hasText: "opt" } },
148
- steps: [
149
- { kind: "filter", filter: { hasText: "filtered" } },
150
- { kind: "index", index: 0 },
151
- ],
152
- });
153
-
154
- expect(registry.get("root.frame")).toEqual({
155
- locatorSchemaPath: "root.frame",
156
- definition: { type: "frameLocator", selector: "iframe[name=child]" },
157
- steps: [],
158
- });
159
- });
@@ -1,39 +0,0 @@
1
- import { expect, test } from "@fixtures-v2/testApp.fixtures";
2
- import type { Page } from "@playwright/test";
3
- import { LocatorRegistryInternal } from "../../../srcV2/locators";
4
-
5
- const createTestRegistry = <Paths extends string>(page: Page) => new LocatorRegistryInternal<Paths>(page);
6
-
7
- test("filter cycle detection throws on direct self-reference", async ({ page }) => {
8
- type LocatorSchemaPaths = "main.h1";
9
-
10
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
11
-
12
- registry.add("main.h1").getByRole("heading", { level: 1 }).filter({ has: "main.h1" });
13
-
14
- expect(() => registry.getLocator("main.h1")).toThrow(/resolving "main\.h1": "main\.h1"/);
15
- });
16
-
17
- test("filter cycle detection throws on indirect reference loops", async ({ page }) => {
18
- type LocatorSchemaPaths = "a" | "b";
19
-
20
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
21
-
22
- registry.add("a").getByRole("button").filter({ has: "b" });
23
- registry.add("b").getByRole("button").filter({ has: "a" });
24
-
25
- expect(() => registry.getLocator("a")).toThrow(/resolving "a": "b"/);
26
- });
27
-
28
- test("filter cycle detection allows repeated non-cyclic references", async ({ page }) => {
29
- type LocatorSchemaPaths = "a" | "a.b" | "shared.heading";
30
-
31
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
32
-
33
- registry.add("shared.heading").getByRole("heading", { level: 2 });
34
- registry.add("a").getByRole("button").filter({ has: "shared.heading" });
35
- registry.add("a.b").getByRole("button").filter({ has: "shared.heading" });
36
-
37
- expect(() => registry.getLocator("a")).not.toThrow();
38
- expect(() => registry.getLocator("a.b")).not.toThrow();
39
- });
@@ -1,253 +0,0 @@
1
- import { expect, test } from "@fixtures-v2/testApp.fixtures";
2
- import type { Page } from "@playwright/test";
3
- import { LocatorRegistryInternal } from "../../../../srcV2/locators";
4
-
5
- const createTestRegistry = <Paths extends string>(page: Page) => new LocatorRegistryInternal<Paths>(page);
6
-
7
- test("getLocator resolves the terminal locator when ancestor paths are registered", async ({ page }) => {
8
- type LocatorSchemaPaths =
9
- | "topMenu"
10
- | "topMenu.notifications"
11
- | "topMenu.notifications.dropdown"
12
- | "topMenu.notifications.dropdown.item";
13
-
14
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
15
-
16
- registry.add("topMenu").locator(".w3-top");
17
- registry.add("topMenu.notifications").locator(".w3-dropdown-hover");
18
- registry.add("topMenu.notifications.dropdown").locator(".w3-dropdown-content");
19
- registry.add("topMenu.notifications.dropdown.item").locator(".w3-bar-item");
20
-
21
- const locator = registry.getLocator("topMenu.notifications.dropdown.item");
22
- const chained = page
23
- .locator(".w3-top")
24
- .locator(".w3-dropdown-hover")
25
- .locator(".w3-dropdown-content")
26
- .locator(".w3-bar-item");
27
-
28
- expect(`${locator}`).toEqual("locator('.w3-bar-item')");
29
- expect(`${locator}`).not.toEqual(`${chained}`);
30
- });
31
-
32
- test("getLocator uses terminal steps while ignoring ancestor filters", async ({ page }) => {
33
- type LocatorSchemaPaths = "panel" | "panel.row" | "panel.row.button";
34
-
35
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
36
-
37
- registry.add("panel").locator("section.panel").filter({ hasText: "panel" }).nth(2);
38
- registry.add("panel.row").locator("div.row").filter({ hasText: "row" });
39
- registry.add("panel.row.button").getByRole("button", { name: "Save" }).filter({ hasText: "terminal" });
40
-
41
- const locator = registry.getLocator("panel.row.button");
42
-
43
- expect(`${locator}`).toEqual("getByRole('button', { name: 'Save' }).filter({ hasText: 'terminal' })");
44
- });
45
-
46
- test("getLocator returns a fresh locator each time", async ({ page }) => {
47
- type LocatorSchemaPaths = "button";
48
-
49
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
50
-
51
- registry.add("button").getByRole("button", { name: "Submit" });
52
-
53
- const first = registry.getLocator("button");
54
- const second = registry.getLocator("button");
55
-
56
- expect(first).not.toBe(second);
57
- expect(`${first}`).toEqual(`${second}`);
58
- });
59
-
60
- test.describe("getLocator applies terminal filters for registered definitions", () => {
61
- const cases = [
62
- {
63
- label: "role",
64
- build: (registry: LocatorRegistryInternal<"terminal">) =>
65
- registry.add("terminal").getByRole("button").filter({ hasText: "filtered" }),
66
- expected: "getByRole('button').filter({ hasText: 'filtered' })",
67
- },
68
- {
69
- label: "text",
70
- build: (registry: LocatorRegistryInternal<"terminal">) =>
71
- registry.add("terminal").getByText("text").filter({ hasText: "filtered" }),
72
- expected: "getByText('text').filter({ hasText: 'filtered' })",
73
- },
74
- {
75
- label: "label",
76
- build: (registry: LocatorRegistryInternal<"terminal">) =>
77
- registry.add("terminal").getByLabel("label").filter({ hasText: "filtered" }),
78
- expected: "getByLabel('label').filter({ hasText: 'filtered' })",
79
- },
80
- {
81
- label: "placeholder",
82
- build: (registry: LocatorRegistryInternal<"terminal">) =>
83
- registry.add("terminal").getByPlaceholder("placeholder").filter({ hasText: "filtered" }),
84
- expected: "getByPlaceholder('placeholder').filter({ hasText: 'filtered' })",
85
- },
86
- {
87
- label: "altText",
88
- build: (registry: LocatorRegistryInternal<"terminal">) =>
89
- registry.add("terminal").getByAltText("altText").filter({ hasText: "filtered" }),
90
- expected: "getByAltText('altText').filter({ hasText: 'filtered' })",
91
- },
92
- {
93
- label: "title",
94
- build: (registry: LocatorRegistryInternal<"terminal">) =>
95
- registry.add("terminal").getByTitle("title").filter({ hasText: "filtered" }),
96
- expected: "getByTitle('title').filter({ hasText: 'filtered' })",
97
- },
98
- {
99
- label: "locator",
100
- build: (registry: LocatorRegistryInternal<"terminal">) =>
101
- registry.add("terminal").locator("locator").filter({ hasText: "filtered" }),
102
- expected: "locator('locator').filter({ hasText: 'filtered' })",
103
- },
104
- {
105
- label: "testId",
106
- build: (registry: LocatorRegistryInternal<"terminal">) =>
107
- registry.add("terminal").getByTestId("testId").filter({ hasText: "filtered" }),
108
- expected: "getByTestId('testId').filter({ hasText: 'filtered' })",
109
- },
110
- {
111
- label: "id",
112
- build: (registry: LocatorRegistryInternal<"terminal">) =>
113
- registry.add("terminal").getById("unique-id").filter({ hasText: "filtered" }),
114
- expected: "locator('#unique-id').filter({ hasText: 'filtered' })",
115
- },
116
- ];
117
-
118
- for (const { label, build, expected } of cases) {
119
- test(`definition ${label}: applies terminal filters`, async ({ page }) => {
120
- const registry = createTestRegistry<"terminal">(page);
121
-
122
- build(registry);
123
-
124
- const locator = registry.getLocator("terminal");
125
- expect(`${locator}`).toEqual(expected);
126
- });
127
- }
128
- });
129
-
130
- test("getLocator returns frame locators without chaining ancestor filters", async ({ page }) => {
131
- type LocatorSchemaPaths = "frame";
132
-
133
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
134
-
135
- registry.add("frame").frameLocator('iframe[title="name"]');
136
-
137
- const locator = registry.getLocator("frame");
138
- const manual = page.frameLocator('iframe[title="name"]').owner();
139
-
140
- expect(`${locator}`).toEqual(`${manual}`);
141
- });
142
-
143
- test("getLocator ignores filter options when all values are undefined", async ({ page }) => {
144
- type LocatorSchemaPaths = "fictional.filter@optionsUndefined";
145
-
146
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
147
-
148
- registry
149
- .add("fictional.filter@optionsUndefined")
150
- .getByRole("button")
151
- .filter({ has: undefined, hasNot: undefined, hasText: undefined, hasNotText: undefined });
152
-
153
- const locator = registry.getLocator("fictional.filter@optionsUndefined");
154
-
155
- expect(`${locator}`).toEqual("getByRole('button')");
156
- });
157
-
158
- test("getLocator applies terminal indices", async ({ page }) => {
159
- type LocatorSchemaPaths =
160
- | "fictional.filter@hasNotText"
161
- | "fictional.filter@hasNotText.filter@hasText"
162
- | "fictional.filter@hasNotText.filter@hasText.filter@hasNotText"
163
- | "fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText";
164
-
165
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
166
-
167
- registry.add("fictional.filter@hasNotText").getByRole("button").filter({ hasNotText: "hasNotText" }).nth(2);
168
- registry.add("fictional.filter@hasNotText.filter@hasText").getByRole("button").filter({ hasText: "hasText" });
169
- registry
170
- .add("fictional.filter@hasNotText.filter@hasText.filter@hasNotText")
171
- .getByRole("button")
172
- .filter({ hasNotText: "hasNotText" });
173
- registry
174
- .add("fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText")
175
- .getByRole("button")
176
- .filter({ hasText: "hasText" })
177
- .nth(2);
178
-
179
- const locator = registry.getLocator("fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText");
180
-
181
- expect(`${locator}`).toEqual("getByRole('button').filter({ hasText: 'hasText' }).nth(2)");
182
- });
183
-
184
- test("getLocator honors terminal filter and index ordering", async ({ page }) => {
185
- type LocatorSchemaPaths = "fictional.filter@hasText";
186
-
187
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
188
-
189
- registry
190
- .add("fictional.filter@hasText")
191
- .getByRole("button")
192
- .filter({ hasText: "hasText" })
193
- .filter({ hasText: "extra" })
194
- .nth(1)
195
- .filter({ hasNotText: "tail" });
196
-
197
- const locator = registry.getLocator("fictional.filter@hasText");
198
-
199
- expect(`${locator}`).toEqual(
200
- "getByRole('button').filter({ hasText: 'hasText' }).filter({ hasText: 'extra' }).nth(1).filter({ hasNotText: 'tail' })",
201
- );
202
- });
203
-
204
- test("getLocator supports explicit last() selection for the terminal path", async ({ page }) => {
205
- type LocatorSchemaPaths = "fictional.filter@hasText";
206
-
207
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
208
-
209
- registry.add("fictional.filter@hasText").getByRole("button").filter({ hasText: "hasText" }).nth("last");
210
-
211
- const locator = registry.getLocator("fictional.filter@hasText");
212
-
213
- expect(`${locator}`).toEqual("getByRole('button').filter({ hasText: 'hasText' }).last()");
214
- });
215
-
216
- test('getLocator accepts "first" and "last" selections for terminal indices', async ({ page }) => {
217
- type LocatorSchemaPaths = "fictional.filter@hasText";
218
-
219
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
220
- registry.add("fictional.filter@hasText").getByRole("button").filter({ hasText: "hasText" }).nth("first");
221
-
222
- const first = registry.getLocator("fictional.filter@hasText");
223
- expect(`${first}`).toContain("first()");
224
-
225
- const secondRegistry = createTestRegistry<LocatorSchemaPaths>(page);
226
- secondRegistry.add("fictional.filter@hasText").getByRole("button").filter({ hasText: "hasText" }).nth("last");
227
-
228
- const last = secondRegistry.getLocator("fictional.filter@hasText");
229
- expect(`${last}`).toContain("last()");
230
- });
231
-
232
- test("getLocator rejects missing terminal paths", async ({ page }) => {
233
- type LocatorSchemaPaths = "fictional.filter@hasText";
234
-
235
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
236
- registry.add("fictional.filter@hasText").getByRole("button");
237
-
238
- // @ts-expect-error Testing invalid argument
239
- expect(() => registry.getLocator("fictional")).toThrow('No locator schema registered for path "fictional".');
240
- });
241
-
242
- test("getLocator rejects partially matching paths", async ({ page }) => {
243
- type LocatorSchemaPaths = "fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText";
244
-
245
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
246
-
247
- registry.add("fictional.filter@hasNotText.filter@hasText.filter@hasNotText.filter@hasText").getByRole("button");
248
-
249
- // @ts-expect-error Testing invalid argument
250
- expect(() => registry.getLocator("fictional.filter@has")).toThrow(
251
- 'No locator schema registered for path "fictional.filter@has".',
252
- );
253
- });
@@ -1,105 +0,0 @@
1
- import { expect, test } from "@fixtures-v2/testApp.fixtures";
2
-
3
- const terminalPath = "body.section.button" as const;
4
-
5
- test("clearSteps clears existing filters for a sub-path", async ({ testFilters }) => {
6
- const schema = testFilters.getLocatorSchema("fictional.filter@hasText");
7
-
8
- const original = schema.getNestedLocator();
9
- expect(`${original}`).toEqual("getByRole('button').filter({ hasText: 'hasText' })");
10
-
11
- const noFilters = schema.clearSteps("fictional.filter@hasText").getNestedLocator();
12
- expect(`${noFilters}`).toEqual("getByRole('button')");
13
- });
14
-
15
- test("clearSteps allows re-adding filters after clearing filters added through filter", async ({ testFilters }) => {
16
- const original = testFilters.getNestedLocator("fictional.filter@hasText");
17
- expect(`${original}`).toEqual("getByRole('button').filter({ hasText: 'hasText' })");
18
-
19
- const locator = testFilters
20
- .getLocatorSchema("fictional.filter@hasText")
21
- .filter("fictional.filter@hasText", { hasText: "this filter will be removed" })
22
- .clearSteps("fictional.filter@hasText")
23
- .filter("fictional.filter@hasText", { hasText: /Re-added/i })
24
- .getNestedLocator();
25
-
26
- expect(`${locator}`).toEqual("getByRole('button').filter({ hasText: /Re-added/i })");
27
- });
28
-
29
- test("clearSteps does not remove filters defined via locator options", async ({ testFilters }) => {
30
- const locator = testFilters
31
- .getLocatorSchema("body.section@playground")
32
- .clearSteps("body.section@playground")
33
- .getNestedLocator();
34
-
35
- expect(`${locator}`).toEqual("locator('body').locator('section').filter({ hasText: /Playground/i })");
36
- });
37
-
38
- test("filter resolves path string and locator references after clearing steps", async ({ testFilters }) => {
39
- const locator = testFilters
40
- .getLocatorSchema("fictional.filter@hasNotText")
41
- .clearSteps("fictional.filter@hasNotText")
42
- .filter("fictional.filter@hasNotText", { has: "body.section.heading" })
43
- .filter("fictional.filter@hasNotText", { hasNot: testFilters.page.locator(".missing") })
44
- .getNestedLocator();
45
-
46
- expect(`${locator}`).toEqual(
47
- "getByRole('button').filter({ has: getByRole('heading', { level: 2 }) }).filter({ hasNot: locator('.missing') })",
48
- );
49
- });
50
-
51
- test("nth defaults to the terminal path when subPath is omitted", async ({ testFilters }) => {
52
- const explicit = testFilters.getLocatorSchema(terminalPath).nth(terminalPath, 0).getNestedLocator();
53
- const implicit = testFilters.getLocatorSchema(terminalPath).nth(0).getNestedLocator();
54
-
55
- expect(`${explicit}`).toEqual("locator('body').locator('section').getByRole('button').first()");
56
- expect(`${implicit}`).toEqual(`${explicit}`);
57
- });
58
-
59
- test("filter defaults to the terminal path when subPath is omitted", async ({ testFilters }) => {
60
- const explicit = testFilters
61
- .getLocatorSchema(terminalPath)
62
- .filter(terminalPath, { hasText: /click/i })
63
- .getNestedLocator();
64
- const implicit = testFilters.getLocatorSchema(terminalPath).filter({ hasText: /click/i }).getNestedLocator();
65
-
66
- expect(`${explicit}`).toEqual("locator('body').locator('section').getByRole('button').filter({ hasText: /click/i })");
67
- expect(`${implicit}`).toEqual(`${explicit}`);
68
- });
69
-
70
- test("clearSteps defaults to the terminal path when subPath is omitted", async ({ testFilters }) => {
71
- const locator = testFilters
72
- .getLocatorSchema(terminalPath)
73
- .filter({ hasText: /click/i })
74
- .nth(1)
75
- .clearSteps()
76
- .getNestedLocator();
77
-
78
- expect(`${locator}`).toEqual("locator('body').locator('section').getByRole('button')");
79
- });
80
-
81
- test("update defaults to the terminal path when subPath is omitted", async ({ testFilters }) => {
82
- const updated = testFilters.getLocatorSchema(terminalPath).update().getByRole({ name: "Reset Color" }).getLocator();
83
-
84
- expect(`${updated}`).toEqual("getByRole('button', { name: 'Reset Color' })");
85
- });
86
-
87
- test("replace defaults to the terminal path when subPath is omitted", async ({ testFilters }) => {
88
- const locator = testFilters
89
- .getLocatorSchema(terminalPath)
90
- .replace()
91
- .locator("button.replace", { hasText: /Replace/ })
92
- .getNestedLocator();
93
-
94
- expect(`${locator}`).toEqual(
95
- "locator('body').locator('section').locator('button.replace').filter({ hasText: /Replace/ })",
96
- );
97
- });
98
-
99
- test("remove defaults to the terminal path when subPath is omitted", async ({ testFilters }) => {
100
- const builder = testFilters.getLocatorSchema(terminalPath).remove();
101
-
102
- await expect(async () => builder.getNestedLocator()).rejects.toThrow(
103
- 'No locator schema registered for path "body.section.button".',
104
- );
105
- });
@@ -1,23 +0,0 @@
1
- import { expect, test } from "@fixtures-v2/testApp.fixtures";
2
- import type { Page } from "@playwright/test";
3
- import { LocatorRegistryInternal } from "../../../../srcV2/locators";
4
-
5
- const createTestRegistry = <Paths extends string>(page: Page) => new LocatorRegistryInternal<Paths>(page);
6
-
7
- test("describe on locator schema builder overrides the resolved description only", async ({ page }) => {
8
- type LocatorSchemaPaths = "panel";
9
-
10
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
11
-
12
- registry.add("panel").locator("section.panel").describe("Original panel");
13
-
14
- const locator = registry.getLocatorSchema("panel").describe("Override panel").getLocator();
15
-
16
- expect(locator.description()).toEqual("Override panel");
17
- expect(registry.get("panel")).toEqual({
18
- description: "Original panel",
19
- definition: { selector: "section.panel", type: "locator" },
20
- locatorSchemaPath: "panel",
21
- steps: [],
22
- });
23
- });