pomwright 1.5.0 → 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 (117) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/README.md +5 -5
  3. package/dist/index.d.mts +91 -989
  4. package/dist/index.d.ts +91 -989
  5. package/dist/index.js +627 -1887
  6. package/dist/index.mjs +633 -1888
  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 -693
  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.getByAltText.spec.ts +0 -23
  58. package/intTestV2/tests/locatorRegistry/add/add.getById.spec.ts +0 -45
  59. package/intTestV2/tests/locatorRegistry/add/add.getByLabel.spec.ts +0 -23
  60. package/intTestV2/tests/locatorRegistry/add/add.getByPlaceholder.spec.ts +0 -23
  61. package/intTestV2/tests/locatorRegistry/add/add.getByRole.spec.ts +0 -23
  62. package/intTestV2/tests/locatorRegistry/add/add.getByTestId.spec.ts +0 -23
  63. package/intTestV2/tests/locatorRegistry/add/add.getByText.spec.ts +0 -23
  64. package/intTestV2/tests/locatorRegistry/add/add.getByTitle.spec.ts +0 -23
  65. package/intTestV2/tests/locatorRegistry/add/add.locator.spec.ts +0 -23
  66. package/intTestV2/tests/locatorRegistry/add/add.reuseExisting.spec.ts +0 -66
  67. package/intTestV2/tests/locatorRegistry/add/add.reuseReusable.spec.ts +0 -311
  68. package/intTestV2/tests/locatorRegistry/add/add.spec.ts +0 -159
  69. package/intTestV2/tests/locatorRegistry/filter.cycle.spec.ts +0 -39
  70. package/intTestV2/tests/locatorRegistry/getLocator/getLocator.spec.ts +0 -253
  71. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.clearSteps.spec.ts +0 -105
  72. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.describe.spec.ts +0 -23
  73. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.filter.spec.ts +0 -368
  74. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.getLocator.spec.ts +0 -56
  75. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.getNestedLocator.spec.ts +0 -175
  76. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.nth.spec.ts +0 -60
  77. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.remove.spec.ts +0 -32
  78. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.replace.spec.ts +0 -24
  79. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.spec.ts +0 -110
  80. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.update.spec.ts +0 -322
  81. package/intTestV2/tests/locatorRegistry/getNestedLocator/getNestedLocator.spec.ts +0 -412
  82. package/intTestV2/tests/locatorRegistry/registry/registry.binding.spec.ts +0 -50
  83. package/intTestV2/tests/locatorRegistry/validation/validation.locatorSchemaPath.spec.ts +0 -115
  84. package/intTestV2/tests/locatorRegistry/validation/validation.sub-path.spec.ts +0 -45
  85. package/intTestV2/tests/step/step.spec.ts +0 -49
  86. package/intTestV2/tests/testApp/color.spec.ts +0 -15
  87. package/intTestV2/tests/testApp/iframe.spec.ts +0 -57
  88. package/intTestV2/tests/testApp/testFilters.spec.ts +0 -24
  89. package/intTestV2/tests/testApp/testPage.spec.ts +0 -161
  90. package/intTestV2/tests/testApp/testPath.spec.ts +0 -18
  91. package/pack-build.sh +0 -11
  92. package/pack-test-v2.sh +0 -36
  93. package/playwright.base.ts +0 -42
  94. package/skills/README.md +0 -56
  95. package/skills/pomwright-v1-5-bridge-migration/SKILL.md +0 -40
  96. package/skills/pomwright-v1-5-bridge-migration/references/call-site-migration.md +0 -178
  97. package/skills/pomwright-v1-5-bridge-migration/references/schema-translation.md +0 -183
  98. package/skills/pomwright-v2-migration/SKILL.md +0 -63
  99. package/skills/pomwright-v2-migration/references/call-site-migration.md +0 -265
  100. package/skills/pomwright-v2-migration/references/class-migration.md +0 -266
  101. package/skills/pomwright-v2-migration/references/fixture-and-helpers.md +0 -423
  102. package/skills/pomwright-v2-migration/references/locator-registration.md +0 -344
  103. package/srcV2/fixture/base.fixtures.ts +0 -23
  104. package/srcV2/helpers/navigation.ts +0 -153
  105. package/srcV2/helpers/playwrightReportLogger.ts +0 -196
  106. package/srcV2/helpers/sessionStorage.ts +0 -251
  107. package/srcV2/helpers/stepDecorator.ts +0 -106
  108. package/srcV2/locators/index.ts +0 -15
  109. package/srcV2/locators/locatorQueryBuilder.ts +0 -427
  110. package/srcV2/locators/locatorRegistrationBuilder.ts +0 -558
  111. package/srcV2/locators/locatorRegistry.ts +0 -541
  112. package/srcV2/locators/locatorUpdateBuilder.ts +0 -602
  113. package/srcV2/locators/reusableLocatorBuilder.ts +0 -200
  114. package/srcV2/locators/types.ts +0 -256
  115. package/srcV2/locators/utils.ts +0 -309
  116. package/srcV2/locators/v1SchemaTranslator.ts +0 -178
  117. package/srcV2/pageObject.ts +0 -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
- const errMsg = "No locator schema registered for path";
8
-
9
- test("add getByRole to registry", async ({ page }) => {
10
- type LocatorSchemaPaths = "buttonByRole";
11
-
12
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
13
-
14
- expect(() => registry.get("buttonByRole")).toThrowError(`${errMsg} "buttonByRole".`);
15
-
16
- registry.add("buttonByRole").getByRole("button", { name: "Submit" });
17
-
18
- expect(registry.get("buttonByRole")).toEqual({
19
- definition: { role: "button", options: { name: "Submit" }, type: "role" },
20
- locatorSchemaPath: "buttonByRole",
21
- steps: [],
22
- });
23
- });
@@ -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
- const errMsg = "No locator schema registered for path";
8
-
9
- test("add getByTestId to registry", async ({ page }) => {
10
- type LocatorSchemaPaths = "elementByTestId";
11
-
12
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
13
-
14
- expect(() => registry.get("elementByTestId")).toThrowError(`${errMsg} "elementByTestId".`);
15
-
16
- registry.add("elementByTestId").getByTestId("login-button");
17
-
18
- expect(registry.get("elementByTestId")).toEqual({
19
- definition: { testId: "login-button", type: "testId" },
20
- locatorSchemaPath: "elementByTestId",
21
- steps: [],
22
- });
23
- });
@@ -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
- const errMsg = "No locator schema registered for path";
8
-
9
- test("add getByText to registry", async ({ page }) => {
10
- type LocatorSchemaPaths = "elementByText";
11
-
12
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
13
-
14
- expect(() => registry.get("elementByText")).toThrowError(`${errMsg} "elementByText".`);
15
-
16
- registry.add("elementByText").getByText("Welcome");
17
-
18
- expect(registry.get("elementByText")).toEqual({
19
- definition: { text: "Welcome", type: "text" },
20
- locatorSchemaPath: "elementByText",
21
- steps: [],
22
- });
23
- });
@@ -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
- const errMsg = "No locator schema registered for path";
8
-
9
- test("add getByTitle to registry", async ({ page }) => {
10
- type LocatorSchemaPaths = "elementByTitle";
11
-
12
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
13
-
14
- expect(() => registry.get("elementByTitle")).toThrowError(`${errMsg} "elementByTitle".`);
15
-
16
- registry.add("elementByTitle").getByTitle("Home Page");
17
-
18
- expect(registry.get("elementByTitle")).toEqual({
19
- definition: { text: "Home Page", type: "title" },
20
- locatorSchemaPath: "elementByTitle",
21
- steps: [],
22
- });
23
- });
@@ -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
- const errMsg = "No locator schema registered for path";
8
-
9
- test("add locator to registry", async ({ page }) => {
10
- type LocatorSchemaPaths = "body";
11
-
12
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
13
-
14
- expect(() => registry.get("body")).toThrowError(`${errMsg} "body".`);
15
-
16
- registry.add("body").locator("body");
17
-
18
- expect(registry.get("body")).toEqual({
19
- definition: { selector: "body", type: "locator" },
20
- locatorSchemaPath: "body",
21
- steps: [],
22
- });
23
- });
@@ -1,66 +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 reuses with existing record by path does not have chainable methods", async ({ page }) => {
8
- type LocatorSchemaPaths = "button" | "button.copy";
9
-
10
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
11
-
12
- registry.add("button").getByRole("button", { name: "Submit" }).filter({ hasText: "Submit" });
13
-
14
- const builder = registry.add("button.copy", { reuse: "button" });
15
-
16
- expect(builder).toBeUndefined();
17
- expect(registry.get("button.copy")).toEqual({
18
- definition: { role: "button", options: { name: "Submit" }, type: "role" },
19
- locatorSchemaPath: "button.copy",
20
- steps: [{ filter: { hasText: "Submit" }, kind: "filter" }],
21
- });
22
- });
23
-
24
- test("add reuse by path clones records so mutations do not leak", async ({ page }) => {
25
- type LocatorSchemaPaths = "button" | "button.copy";
26
-
27
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
28
-
29
- registry.add("button").getByRole("button", { name: "Submit" }).filter({ hasText: "Submit" });
30
-
31
- registry.add("button.copy", { reuse: "button" });
32
-
33
- expect(registry.get("button.copy")).toEqual({
34
- definition: { role: "button", options: { name: "Submit" }, type: "role" },
35
- locatorSchemaPath: "button.copy",
36
- steps: [{ filter: { hasText: "Submit" }, kind: "filter" }],
37
- });
38
-
39
- registry.replace("button.copy", {
40
- definition: { role: "link", options: { name: "Copy" }, type: "role" },
41
- locatorSchemaPath: "button.copy",
42
- steps: [{ filter: { hasText: "Copy" }, kind: "filter" }],
43
- });
44
-
45
- expect(registry.get("button.copy")).toEqual({
46
- definition: { role: "link", options: { name: "Copy" }, type: "role" },
47
- locatorSchemaPath: "button.copy",
48
- steps: [{ filter: { hasText: "Copy" }, kind: "filter" }],
49
- });
50
-
51
- expect(registry.get("button")).toEqual({
52
- definition: { role: "button", options: { name: "Submit" }, type: "role" },
53
- locatorSchemaPath: "button",
54
- steps: [{ filter: { hasText: "Submit" }, kind: "filter" }],
55
- });
56
- });
57
-
58
- test("add reuse by path throws when the source path is missing", async ({ page }) => {
59
- type LocatorSchemaPaths = "button" | "button.copy";
60
-
61
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
62
-
63
- expect(() => registry.add("button.copy", { reuse: "button" })).toThrowError(
64
- 'No locator schema registered for path "button".',
65
- );
66
- });
@@ -1,311 +0,0 @@
1
- import { expect, test } from "@fixtures-v2/testApp.fixtures";
2
- import type { Page } from "@playwright/test";
3
- import { type FilterDefinition, LocatorRegistryInternal } from "../../../../srcV2/locators";
4
-
5
- const createTestRegistry = <Paths extends string>(page: Page) => new LocatorRegistryInternal<Paths>(page);
6
-
7
- test("add can reuse a reusable locator definition", async ({ page }) => {
8
- type LocatorSchemaPaths = "heading" | "heading.first";
9
-
10
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
11
-
12
- const h2 = registry.createReusable.getByRole("heading", { level: 2 }).filter({ hasText: /Summary/ });
13
-
14
- registry.add("heading", { reuse: h2 });
15
- registry.add("heading.first", { reuse: h2 }).nth(0);
16
-
17
- expect(registry.get("heading")).toEqual({
18
- definition: { role: "heading", options: { level: 2 }, type: "role" },
19
- locatorSchemaPath: "heading",
20
- steps: [{ filter: { hasText: /Summary/ }, kind: "filter" }],
21
- });
22
-
23
- expect(registry.get("heading.first")).toEqual({
24
- definition: { role: "heading", options: { level: 2 }, type: "role" },
25
- locatorSchemaPath: "heading.first",
26
- steps: [
27
- { filter: { hasText: /Summary/ }, kind: "filter" },
28
- { index: 0, kind: "index" },
29
- ],
30
- });
31
- });
32
-
33
- test("reusable builder yields the same locator chain as a direct definition", async ({ page }) => {
34
- type LocatorSchemaPaths = "heading" | "heading.reused";
35
-
36
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
37
-
38
- const reusable = registry.createReusable.getByRole("heading", { level: 2 }).filter({ hasText: "Intro" }).nth(1);
39
-
40
- registry.add("heading").getByRole("heading", { level: 2 }).filter({ hasText: "Intro" }).nth(1);
41
- registry.add("heading.reused", { reuse: reusable });
42
-
43
- const direct = registry.getLocator("heading");
44
- const reused = registry.getLocator("heading.reused");
45
-
46
- expect(`${reused}`).toEqual(`${direct}`);
47
- });
48
-
49
- test("add reuse by reusable locator patches options while preserving selector", async ({ page }) => {
50
- type LocatorSchemaPaths = "errorMessage" | "error.invalidPassword";
51
-
52
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
53
-
54
- const errorMessage = registry.createReusable.locator("error-message");
55
- registry.add("errorMessage", { reuse: errorMessage });
56
- registry.add("error.invalidPassword", { reuse: errorMessage }).locator({ hasText: /invalid password/ });
57
-
58
- expect(registry.get("errorMessage")).toEqual({
59
- definition: { selector: "error-message", type: "locator" },
60
- locatorSchemaPath: "errorMessage",
61
- steps: [],
62
- });
63
-
64
- expect(registry.get("error.invalidPassword")).toEqual({
65
- definition: { options: { hasText: /invalid password/ }, selector: "error-message", type: "locator" },
66
- locatorSchemaPath: "error.invalidPassword",
67
- steps: [],
68
- });
69
- });
70
-
71
- test("add reuse by reusable locator inherits selector and discriminant", async ({ page }) => {
72
- type LocatorSchemaPaths = "errorMessage" | "error.invalidPassword";
73
-
74
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
75
-
76
- const errorMessage = registry.createReusable.locator("error-message", { hasText: /invalid password/ });
77
- registry.add("errorMessage", { reuse: errorMessage });
78
- registry.add("error.invalidPassword", { reuse: errorMessage }).locator({ hasText: /invalid email/ });
79
-
80
- expect(registry.get("errorMessage")).toEqual({
81
- definition: { options: { hasText: /invalid password/ }, selector: "error-message", type: "locator" },
82
- locatorSchemaPath: "errorMessage",
83
- steps: [],
84
- });
85
-
86
- expect(registry.get("error.invalidPassword")).toEqual({
87
- definition: { options: { hasText: /invalid email/ }, selector: "error-message", type: "locator" },
88
- locatorSchemaPath: "error.invalidPassword",
89
- steps: [],
90
- });
91
- });
92
-
93
- test("add reuse with a reusable locator does not mutate the reusable definition", async ({ page }) => {
94
- type LocatorSchemaPaths = "heading" | "heading.first";
95
-
96
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
97
-
98
- const reusable = registry.createReusable.getByRole("heading", { level: 2 }).filter({ hasText: /Summary/ });
99
-
100
- registry.add("heading", { reuse: reusable });
101
- registry.add("heading.first", { reuse: reusable }).nth(0);
102
-
103
- expect(reusable.steps).toEqual([{ filter: { hasText: /Summary/ }, kind: "filter" }]);
104
- expect(registry.get("heading")).toEqual({
105
- definition: { role: "heading", options: { level: 2 }, type: "role" },
106
- locatorSchemaPath: "heading",
107
- steps: [{ filter: { hasText: /Summary/ }, kind: "filter" }],
108
- });
109
-
110
- expect(registry.get("heading.first")).toEqual({
111
- definition: { role: "heading", options: { level: 2 }, type: "role" },
112
- locatorSchemaPath: "heading.first",
113
- steps: [
114
- { filter: { hasText: /Summary/ }, kind: "filter" },
115
- { index: 0, kind: "index" },
116
- ],
117
- });
118
- });
119
-
120
- test("add reuse enforces matching locator type overrides", async ({ page }) => {
121
- type LocatorSchemaPaths = "button" | "button.reuseLocator";
122
-
123
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
124
-
125
- const button = registry.createReusable.getByRole("button", { name: "Submit" });
126
-
127
- // @ts-expect-error mismatched locator strategies are not allowed when reusing a locator (getByText attempt on role locator)
128
- expect(() => registry.add("button", { reuse: button }).getByText("text")).toThrowError(
129
- 'must use the "role" strategy',
130
- );
131
-
132
- registry.add("button", { reuse: button });
133
-
134
- expect(registry.get("button")).toEqual({
135
- definition: { role: "button", options: { name: "Submit" }, type: "role" },
136
- locatorSchemaPath: "button",
137
- steps: [],
138
- });
139
-
140
- // @ts-expect-error mismatched locator strategies are not allowed when reusing a locator (getById attempt on role locator)
141
- expect(() => registry.add("button.reuseLocator", { reuse: button }).getById("Submit")).toThrowError(
142
- 'must use the "role" strategy',
143
- );
144
-
145
- registry.add("button.reuseLocator", { reuse: button }).getByRole({ name: "Submit" });
146
-
147
- expect(registry.get("button.reuseLocator")).toEqual({
148
- definition: { role: "button", options: { name: "Submit" }, type: "role" },
149
- locatorSchemaPath: "button.reuseLocator",
150
- steps: [],
151
- });
152
- });
153
-
154
- test("add reuse patches role options while preserving role value", async ({ page }) => {
155
- type LocatorSchemaPaths = "heading";
156
-
157
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
158
- const h2 = registry.createReusable.getByRole("heading", { level: 2 });
159
-
160
- registry.add("heading", { reuse: h2 }).getByRole({ name: "Summary" });
161
-
162
- expect(registry.get("heading")).toEqual({
163
- definition: { role: "heading", options: { level: 2, name: "Summary" }, type: "role" },
164
- locatorSchemaPath: "heading",
165
- steps: [],
166
- });
167
- });
168
-
169
- test("add reuse typing narrows override methods to the matching strategy", async ({ page }) => {
170
- type LocatorSchemaPaths = "button";
171
-
172
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
173
- const button = registry.createReusable.getByRole("button", { name: "Submit" });
174
- const builder = registry.add("button", { reuse: button });
175
-
176
- // @ts-expect-error mismatched locator strategies are not exposed when reusing a locator
177
- const _invalidOverrideMethod = builder.getByText;
178
-
179
- builder.getByRole({ name: "Submit" });
180
- });
181
-
182
- test("add reuse allows only one matching locator override", async ({ page }) => {
183
- type LocatorSchemaPaths = "heading";
184
-
185
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
186
- const h2 = registry.createReusable.getByRole("heading", { level: 2 });
187
-
188
- const builder = registry.add("heading", { reuse: h2 });
189
-
190
- builder.getByRole("heading", { name: "Summary" });
191
-
192
- expect(() => builder.getByRole("heading", { name: "Other" })).toThrowError("only one matching override is allowed");
193
- });
194
-
195
- test("createReusable.filter supports Playwright Locator instances for has/hasNot", async ({ page }) => {
196
- type LocatorSchemaPaths = "item";
197
-
198
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
199
-
200
- const seed = registry.createReusable
201
- .getByRole("button")
202
- .filter({ has: page.getByRole("heading", { level: 2 }) })
203
- .filter({ hasNot: page.getByRole("heading", { level: 3 }) });
204
-
205
- registry.add("item", { reuse: seed });
206
-
207
- const locator = registry.getLocator("item");
208
-
209
- expect(`${locator}`).toEqual(
210
- "getByRole('button').filter({ has: getByRole('heading', { level: 2 }) }).filter({ hasNot: getByRole('heading', { level: 3 }) })",
211
- );
212
- });
213
-
214
- test("createReusable.filter supports registry path strings for has/hasNot", async ({ page }) => {
215
- type LocatorSchemaPaths = "item" | "heading.primary" | "heading.secondary";
216
-
217
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
218
-
219
- registry.add("heading.primary").getByRole("heading", { level: 2 });
220
- registry.add("heading.secondary").getByRole("heading", { level: 3 });
221
-
222
- const seed = registry.createReusable
223
- .getByRole("button")
224
- .filter({ has: "heading.primary" })
225
- .filter({ hasNot: "heading.secondary" });
226
-
227
- registry.add("item", { reuse: seed });
228
-
229
- const locator = registry.getLocator("item");
230
-
231
- expect(`${locator}`).toEqual(
232
- "getByRole('button').filter({ has: getByRole('heading', { level: 2 }) }).filter({ hasNot: getByRole('heading', { level: 3 }) })",
233
- );
234
- });
235
-
236
- test("createReusable.filter rejects inline locator strategy definitions for has/hasNot", async ({ page }) => {
237
- type LocatorSchemaPaths = "item";
238
-
239
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
240
-
241
- registry.createReusable
242
- .getByRole("button")
243
- // @ts-expect-error inline locator definitions are no longer supported
244
- .filter({ has: { type: "locator", selector: "section" } });
245
-
246
- const unsafeFilter = {
247
- has: { type: "locator", selector: "section" },
248
- } as unknown as FilterDefinition<LocatorSchemaPaths, LocatorSchemaPaths>;
249
-
250
- const unsafeSeed = registry.createReusable.getByRole("button").filter(unsafeFilter);
251
-
252
- registry.add("item", { reuse: unsafeSeed });
253
-
254
- expect(() => registry.getLocator("item")).toThrow(/Unsupported filter reference/);
255
- });
256
-
257
- test("createReusable.filter rejects locator wrappers for has/hasNot", async ({ page }) => {
258
- type LocatorSchemaPaths = "item";
259
-
260
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
261
-
262
- registry.createReusable
263
- .getByRole("button")
264
- // @ts-expect-error locator wrapper is no longer supported
265
- .filter({ has: { locator: { type: "locator", selector: "section" } } });
266
-
267
- const unsafeFilter = {
268
- has: { locator: { type: "locator", selector: "section" } },
269
- } as unknown as FilterDefinition<LocatorSchemaPaths, LocatorSchemaPaths>;
270
-
271
- const unsafeSeed = registry.createReusable.getByRole("button").filter(unsafeFilter);
272
-
273
- registry.add("item", { reuse: unsafeSeed });
274
-
275
- expect(() => registry.getLocator("item")).toThrow(/Unsupported filter reference/);
276
- });
277
-
278
- test("createReusable.filter rejects locatorPath wrappers for has/hasNot", async ({ page }) => {
279
- type LocatorSchemaPaths = "item";
280
-
281
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
282
-
283
- registry.createReusable
284
- .getByRole("button")
285
- // @ts-expect-error locatorPath wrapper is no longer supported
286
- .filter({ has: { locatorPath: "item" } });
287
-
288
- const unsafeFilter = {
289
- has: { locatorPath: "item" },
290
- } as unknown as FilterDefinition<LocatorSchemaPaths, LocatorSchemaPaths>;
291
-
292
- const unsafeSeed = registry.createReusable.getByRole("button").filter(unsafeFilter);
293
-
294
- registry.add("item", { reuse: unsafeSeed });
295
-
296
- expect(() => registry.getLocator("item")).toThrow(/Unsupported filter reference/);
297
- });
298
-
299
- test("createReusable.filter supports visible true/false", async ({ page }) => {
300
- type LocatorSchemaPaths = "item";
301
-
302
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
303
-
304
- const seed = registry.createReusable.getByRole("button").filter({ visible: true }).filter({ visible: false });
305
-
306
- registry.add("item", { reuse: seed });
307
-
308
- const locator = registry.getLocator("item");
309
-
310
- expect(`${locator}`).toEqual("getByRole('button').filter({ visible: true }).filter({ visible: false })");
311
- });
@@ -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
- });