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,45 +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 getById to registry", async ({ page }) => {
10
- type LocatorSchemaPaths = "stringId" | "stringId.#stringId" | "regExpId";
11
-
12
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
13
-
14
- expect(() => registry.get("stringId")).toThrowError(`${errMsg} "stringId".`);
15
- expect(() => registry.get("stringId.#stringId")).toThrowError(`${errMsg} "stringId.#stringId".`);
16
- expect(() => registry.get("regExpId")).toThrowError(`${errMsg} "regExpId".`);
17
-
18
- registry.add("stringId").getById("unique-element");
19
- registry.add("stringId.#stringId").getById("#unique-element");
20
-
21
- const expectedStringIdDefinition = {
22
- definition: { id: "unique-element", type: "id" },
23
- locatorSchemaPath: "stringId",
24
- steps: [],
25
- };
26
-
27
- const expectedHashIdDefinition = {
28
- ...expectedStringIdDefinition,
29
- locatorSchemaPath: "stringId.#stringId",
30
- };
31
-
32
- expect(registry.get("stringId")).toEqual(expectedStringIdDefinition);
33
- expect(registry.get("stringId.#stringId")).toEqual(expectedHashIdDefinition);
34
-
35
- const locator = registry.getLocator("stringId.#stringId");
36
- expect(`${locator}`).toEqual("locator('#unique-element')");
37
-
38
- registry.add("regExpId").getById(/unique-\w+/);
39
- const regExpDefinition = {
40
- definition: { id: /unique-\w+/, type: "id" },
41
- locatorSchemaPath: "regExpId",
42
- steps: [],
43
- };
44
- expect(registry.get("regExpId")).toEqual(regExpDefinition);
45
- });
@@ -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 getByLabel to registry", async ({ page }) => {
10
- type LocatorSchemaPaths = "elementByLabel";
11
-
12
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
13
-
14
- expect(() => registry.get("elementByLabel")).toThrowError(`${errMsg} "elementByLabel".`);
15
-
16
- registry.add("elementByLabel").getByLabel("Username");
17
-
18
- expect(registry.get("elementByLabel")).toEqual({
19
- definition: { text: "Username", type: "label" },
20
- locatorSchemaPath: "elementByLabel",
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 getByPlaceholder to registry", async ({ page }) => {
10
- type LocatorSchemaPaths = "elementByPlaceholder";
11
-
12
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
13
-
14
- expect(() => registry.get("elementByPlaceholder")).toThrowError(`${errMsg} "elementByPlaceholder".`);
15
-
16
- registry.add("elementByPlaceholder").getByPlaceholder("Enter your name");
17
-
18
- expect(registry.get("elementByPlaceholder")).toEqual({
19
- definition: { text: "Enter your name", type: "placeholder" },
20
- locatorSchemaPath: "elementByPlaceholder",
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 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,107 +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 with empty string does not silently skip reuse", async ({ page }) => {
25
- type LocatorSchemaPaths = "button";
26
-
27
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
28
-
29
- // @ts-expect-error - testing runtime error when reuse is an empty string
30
- expect(() => registry.add("button", { reuse: "" })).toThrowError('No locator schema registered for path "".');
31
- });
32
-
33
- test("add reuse by path throws when source path matches target path", async ({ page }) => {
34
- type LocatorSchemaPaths = "button";
35
-
36
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
37
-
38
- // @ts-expect-error - testing runtime error when reuse path is the same as registration path
39
- expect(() => registry.add("button", { reuse: "button" })).toThrowError(
40
- 'Locator reuse path cannot be the same as registration path: "button".',
41
- );
42
- });
43
-
44
- test("add reuse by path reports compile-time fallback reason when forced at runtime", async ({ page }) => {
45
- type LocatorSchemaPaths = "button" | "button.copy";
46
-
47
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
48
-
49
- const addUnsafe = registry.add as unknown as (
50
- path: string,
51
- options: {
52
- reuse: [string];
53
- },
54
- ) => void;
55
-
56
- expect(() =>
57
- addUnsafe("button.copy", {
58
- reuse: ["Invalid reuse path, reuse path cannot be the same as registration path: button.copy"],
59
- }),
60
- ).toThrowError(
61
- 'Invalid reuse path configuration for "button.copy": Invalid reuse path, reuse path cannot be the same as registration path: button.copy',
62
- );
63
- });
64
-
65
- test("add reuse by path clones records so mutations do not leak", async ({ page }) => {
66
- type LocatorSchemaPaths = "button" | "button.copy";
67
-
68
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
69
-
70
- registry.add("button").getByRole("button", { name: "Submit" }).filter({ hasText: "Submit" });
71
-
72
- registry.add("button.copy", { reuse: "button" });
73
-
74
- expect(registry.get("button.copy")).toEqual({
75
- definition: { role: "button", options: { name: "Submit" }, type: "role" },
76
- locatorSchemaPath: "button.copy",
77
- steps: [{ filter: { hasText: "Submit" }, kind: "filter" }],
78
- });
79
-
80
- registry.replace("button.copy", {
81
- definition: { role: "link", options: { name: "Copy" }, type: "role" },
82
- locatorSchemaPath: "button.copy",
83
- steps: [{ filter: { hasText: "Copy" }, kind: "filter" }],
84
- });
85
-
86
- expect(registry.get("button.copy")).toEqual({
87
- definition: { role: "link", options: { name: "Copy" }, type: "role" },
88
- locatorSchemaPath: "button.copy",
89
- steps: [{ filter: { hasText: "Copy" }, kind: "filter" }],
90
- });
91
-
92
- expect(registry.get("button")).toEqual({
93
- definition: { role: "button", options: { name: "Submit" }, type: "role" },
94
- locatorSchemaPath: "button",
95
- steps: [{ filter: { hasText: "Submit" }, kind: "filter" }],
96
- });
97
- });
98
-
99
- test("add reuse by path throws when the source path is missing", async ({ page }) => {
100
- type LocatorSchemaPaths = "button" | "button.copy";
101
-
102
- const registry = createTestRegistry<LocatorSchemaPaths>(page);
103
-
104
- expect(() => registry.add("button.copy", { reuse: "button" })).toThrowError(
105
- 'No locator schema registered for path "button".',
106
- );
107
- });
@@ -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
- });