pomwright 1.3.0 → 1.5.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/AGENTS.md +37 -0
  2. package/CHANGELOG.md +193 -0
  3. package/README.md +316 -34
  4. package/dist/index.d.mts +1058 -132
  5. package/dist/index.d.ts +1058 -132
  6. package/dist/index.js +2309 -185
  7. package/dist/index.mjs +2304 -185
  8. package/docs/{get-locator-methods-explanation.md → v1/get-locator-methods-explanation.md} +0 -16
  9. package/docs/v1-to-v2-migration/bridge-migration-guide.md +159 -0
  10. package/docs/v1-to-v2-migration/direct-migration-guide.md +238 -0
  11. package/docs/v1-to-v2-migration/v1-to-v2-comparison.md +547 -0
  12. package/docs/v2/PageObject.md +293 -0
  13. package/docs/v2/composing-locator-modules.md +93 -0
  14. package/docs/v2/locator-registry.md +693 -0
  15. package/docs/v2/logging.md +168 -0
  16. package/docs/v2/overview.md +515 -0
  17. package/docs/v2/session-storage.md +160 -0
  18. package/index.ts +61 -9
  19. package/intTestV2/.env +0 -0
  20. package/intTestV2/fixtures/testApp.fixtures.ts +43 -0
  21. package/intTestV2/package.json +22 -0
  22. package/intTestV2/page-object-models/testApp/pages/iframe/iframe.locatorSchema.ts +24 -0
  23. package/intTestV2/page-object-models/testApp/pages/iframe/iframe.page.ts +17 -0
  24. package/intTestV2/page-object-models/testApp/pages/testPage.locatorSchema.ts +32 -0
  25. package/intTestV2/page-object-models/testApp/pages/testPage.page.ts +119 -0
  26. package/intTestV2/page-object-models/testApp/pages/testPath/[color]/color.locatorSchema.ts +29 -0
  27. package/intTestV2/page-object-models/testApp/pages/testPath/[color]/color.page.ts +48 -0
  28. package/intTestV2/page-object-models/testApp/pages/testPath/testPath.locatorSchema.ts +9 -0
  29. package/intTestV2/page-object-models/testApp/pages/testPath/testPath.page.ts +23 -0
  30. package/intTestV2/page-object-models/testApp/pages/testfilters/testfilters.locatorSchema.ts +114 -0
  31. package/intTestV2/page-object-models/testApp/pages/testfilters/testfilters.page.ts +23 -0
  32. package/intTestV2/page-object-models/testApp/testApp.base.ts +20 -0
  33. package/intTestV2/playwright.config.ts +54 -0
  34. package/intTestV2/server.js +216 -0
  35. package/intTestV2/test-data/staticPage/index.html +280 -0
  36. package/intTestV2/test-data/staticPage/w3images/avatar2.png +0 -0
  37. package/intTestV2/test-data/staticPage/w3images/avatar3.png +0 -0
  38. package/intTestV2/test-data/staticPage/w3images/avatar5.png +0 -0
  39. package/intTestV2/test-data/staticPage/w3images/avatar6.png +0 -0
  40. package/intTestV2/test-data/staticPage/w3images/forest.jpg +0 -0
  41. package/intTestV2/test-data/staticPage/w3images/lights.jpg +0 -0
  42. package/intTestV2/test-data/staticPage/w3images/mountains.jpg +0 -0
  43. package/intTestV2/test-data/staticPage/w3images/nature.jpg +0 -0
  44. package/intTestV2/test-data/staticPage/w3images/snow.jpg +0 -0
  45. package/intTestV2/tests/locatorRegistry/add/add.describe.spec.ts +54 -0
  46. package/intTestV2/tests/locatorRegistry/add/add.filter.spec.ts +143 -0
  47. package/intTestV2/tests/locatorRegistry/add/add.frameLocator.spec.ts +23 -0
  48. package/intTestV2/tests/locatorRegistry/add/add.getByAltText.spec.ts +23 -0
  49. package/intTestV2/tests/locatorRegistry/add/add.getById.spec.ts +45 -0
  50. package/intTestV2/tests/locatorRegistry/add/add.getByLabel.spec.ts +23 -0
  51. package/intTestV2/tests/locatorRegistry/add/add.getByPlaceholder.spec.ts +23 -0
  52. package/intTestV2/tests/locatorRegistry/add/add.getByRole.spec.ts +23 -0
  53. package/intTestV2/tests/locatorRegistry/add/add.getByTestId.spec.ts +23 -0
  54. package/intTestV2/tests/locatorRegistry/add/add.getByText.spec.ts +23 -0
  55. package/intTestV2/tests/locatorRegistry/add/add.getByTitle.spec.ts +23 -0
  56. package/intTestV2/tests/locatorRegistry/add/add.locator.spec.ts +23 -0
  57. package/intTestV2/tests/locatorRegistry/add/add.reuseExisting.spec.ts +66 -0
  58. package/intTestV2/tests/locatorRegistry/add/add.reuseReusable.spec.ts +311 -0
  59. package/intTestV2/tests/locatorRegistry/add/add.spec.ts +159 -0
  60. package/intTestV2/tests/locatorRegistry/filter.cycle.spec.ts +39 -0
  61. package/intTestV2/tests/locatorRegistry/getLocator/getLocator.spec.ts +253 -0
  62. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.clearSteps.spec.ts +105 -0
  63. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.describe.spec.ts +23 -0
  64. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.filter.spec.ts +368 -0
  65. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.getLocator.spec.ts +56 -0
  66. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.getNestedLocator.spec.ts +175 -0
  67. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.nth.spec.ts +60 -0
  68. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.remove.spec.ts +32 -0
  69. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.replace.spec.ts +24 -0
  70. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.spec.ts +110 -0
  71. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.update.spec.ts +322 -0
  72. package/intTestV2/tests/locatorRegistry/getNestedLocator/getNestedLocator.spec.ts +412 -0
  73. package/intTestV2/tests/locatorRegistry/registry/registry.binding.spec.ts +50 -0
  74. package/intTestV2/tests/locatorRegistry/validation/validation.locatorSchemaPath.spec.ts +115 -0
  75. package/intTestV2/tests/locatorRegistry/validation/validation.sub-path.spec.ts +45 -0
  76. package/intTestV2/tests/step/step.spec.ts +49 -0
  77. package/intTestV2/tests/testApp/color.spec.ts +15 -0
  78. package/intTestV2/tests/testApp/iframe.spec.ts +57 -0
  79. package/intTestV2/tests/testApp/testFilters.spec.ts +24 -0
  80. package/intTestV2/tests/testApp/testPage.spec.ts +161 -0
  81. package/intTestV2/tests/testApp/testPath.spec.ts +18 -0
  82. package/pack-build.sh +11 -0
  83. package/pack-test-v2.sh +36 -0
  84. package/package.json +10 -3
  85. package/playwright.base.ts +42 -0
  86. package/skills/README.md +56 -0
  87. package/skills/pomwright-v1-5-bridge-migration/SKILL.md +40 -0
  88. package/skills/pomwright-v1-5-bridge-migration/references/call-site-migration.md +178 -0
  89. package/skills/pomwright-v1-5-bridge-migration/references/schema-translation.md +183 -0
  90. package/skills/pomwright-v2-migration/SKILL.md +63 -0
  91. package/skills/pomwright-v2-migration/references/call-site-migration.md +265 -0
  92. package/skills/pomwright-v2-migration/references/class-migration.md +266 -0
  93. package/skills/pomwright-v2-migration/references/fixture-and-helpers.md +423 -0
  94. package/skills/pomwright-v2-migration/references/locator-registration.md +344 -0
  95. package/srcV2/fixture/base.fixtures.ts +23 -0
  96. package/srcV2/helpers/navigation.ts +153 -0
  97. package/srcV2/helpers/playwrightReportLogger.ts +196 -0
  98. package/srcV2/helpers/sessionStorage.ts +251 -0
  99. package/srcV2/helpers/stepDecorator.ts +106 -0
  100. package/srcV2/locators/index.ts +15 -0
  101. package/srcV2/locators/locatorQueryBuilder.ts +427 -0
  102. package/srcV2/locators/locatorRegistrationBuilder.ts +558 -0
  103. package/srcV2/locators/locatorRegistry.ts +541 -0
  104. package/srcV2/locators/locatorUpdateBuilder.ts +602 -0
  105. package/srcV2/locators/reusableLocatorBuilder.ts +200 -0
  106. package/srcV2/locators/types.ts +256 -0
  107. package/srcV2/locators/utils.ts +309 -0
  108. package/srcV2/locators/v1SchemaTranslator.ts +178 -0
  109. package/srcV2/pageObject.ts +105 -0
  110. /package/docs/{BaseApi-explanation.md → v1/BaseApi-explanation.md} +0 -0
  111. /package/docs/{BasePage-explanation.md → v1/BasePage-explanation.md} +0 -0
  112. /package/docs/{LocatorSchema-explanation.md → v1/LocatorSchema-explanation.md} +0 -0
  113. /package/docs/{LocatorSchemaPath-explanation.md → v1/LocatorSchemaPath-explanation.md} +0 -0
  114. /package/docs/{PlaywrightReportLogger-explanation.md → v1/PlaywrightReportLogger-explanation.md} +0 -0
  115. /package/docs/{intro-to-using-pomwright.md → v1/intro-to-using-pomwright.md} +0 -0
  116. /package/docs/{sessionStorage-methods-explanation.md → v1/sessionStorage-methods-explanation.md} +0 -0
  117. /package/docs/{tips-folder-structure.md → v1/tips-folder-structure.md} +0 -0
@@ -0,0 +1,547 @@
1
+ # v1 ➜ v2 Feature Comparison
2
+
3
+ This document compares the **complete** v1 and v2 public feature sets, highlights differences, and provides example-by-example mappings.
4
+
5
+ ---
6
+
7
+ ## High-level feature matrix
8
+
9
+ | Area | v1 (BasePage + GetLocatorBase) | v2 (PageObject + LocatorRegistry) | Notes |
10
+ | --- | --- | --- | --- |
11
+ | Base class | `BasePage` (requires `Page`, `testInfo` and `PlaywrightReportLogger`) | `PageObject` (requires `Page`) | `BasePage` is deprecated. `PageObject` is the v2 replacement. `PageObject` comes with a few basic navigation helper methods (`goto`, `gotoThisPage`, `expectThisPage` and `expectAnotherPage`) |
12
+ | Usage | v1 requires you to import `test` through pomwright and all features (except `log`) are made available by extending a class with `BasePage` | The use of `PageObject` is for convenience and is **optional**, if you prefer, the following pomwright features bundled through `PageObject` can be implemented/used directly: `LocatorRegistry`, `SessionStorage`, `PlaywrightReportLogger` and `Step`. | |
13
+ | URL typing | `BasePageOptions.urlOptions` + `Extract*Type` helpers | `UrlTypeOptions` + `*FromOptions` helpers | v2 uses flattened UrlTypeOptions (`baseUrlType`, `urlPathType`). |
14
+ | Locator definitions | `locators.addSchema(path, LocatorSchemaObject)` | Fluent registry DSL (`add(path).getByRole(...)`) | v2 eliminates schema objects and replaces it with a fluent DSL resembling native playwright syntax |
15
+ | Locator registry | `GetLocatorBase` | `LocatorRegistry` via `createRegistryWithAccessors` | v2 is fluent and typed. `createRegistryWithAccessors` can be used to implement the LocatorRegistry feature functionally or as part of any class without the use of the `PageObject`. |
16
+ | Locator retrieval | `await getLocatorSchema(path).getLocator()`, `await getLocatorSchema(path).getNestedLocator()`, `await getLocator(path)`, `await getNestedLocator(path, indices?)` | `getLocatorSchema(path).getLocator()`, `getLocatorSchema(path).getNestedLocator()`, `getLocator(path)`, `getNestedLocator(path)` | v2 retrieval methods are synchronous while v1 retrieval methods are async. v2 uses `getLocatorSchema(...).nth(...)` for indexing, see mutation. |
17
+ | Locator mutation | `getLocatorSchema(...)` with `addFilter(...)` and `.update(...)` | `getLocatorSchema(...)` with `filter(...)`, `nth(...)`, `.update(...)`, `replace(...)`, `remove(...)` and `describe(...)` | v2 replaces `addFilter` with `filter` and adds `nth`, `replace`, `remove`, `clearSteps` and `describe`. In v1 filters are chained in call order followed by **one** optional nth call. In v2 both filters and indices are chained in call order, no limit. In comparison to the v1 update method, the update/replace/remove methods in v2 do not mutate steps (filter & nth), this is done through the new dedicated methods. |
18
+ | Reusable locators | `LocatorSchemaWithoutPath` object with `addSchema(path, locatorSchemaWithoutPath)` | `createReusable.getByRole(...)` with `add(path, { reuse: reusable }` or `add(path, { reuse: path} )` | v2 provides a fluent DSL resembling native playwright syntax for creating a reusable Locator to be used as the base for multiple variations or alternatively reusing a previously added Locator definiton from the registry to a new chain/scope by referencing its `Path`. Ensuring single definitions, eliminating code duplication. |
19
+ | Frame handling | `frameLocator` returned as `Locator` (cast) | A terminal (path) frame resolves to a `frameLocator.owner` locator, while none terminal frames resolve to a `locator.contentFrame`framelocator e.g.: `locator('#noneTerminalFrame').contentFrame().locator('#terminalFrame')` | v2 avoids returning a `FrameLocator` at the terminal node. |
20
+ | Session storage | `SessionStorage` (v1 signatures) | `SessionStorage` (v2 signatures) | v2 adds `waitForContext` and key-based `clear`. |
21
+ | Logging | Requires you to import test through pomwright as `BasePage` requires the `log` fixture it provides. BasePage initializes with a `log` child of the `log` fixture. | In v2 it is optional to import test through POMWright to aquire the `log` fixture, alternatively you can copy-paste the fixture code-snippet from `logging.md` instead. `PageObject` does not provide a `log` child, but you can add one if you want it though this will requires the `log` fixture. | v2 does not provide a logger through PageObject. The `log` fixture is optional in v2 and PageObject does not provide a `log` child unless you add it. |
22
+ | Step decorator | Not available | `step` decorator | New in v2. |
23
+ | Base API | `BaseApi` | Not provided | Must supply your own API base class. |
24
+ | `data-cy` | Custom selector engine in BasePage | Removed | Use `locator('[data-cy="..."]')` or add custom engine in Playwright. |
25
+ | Bridge | `BasePageV1toV2` | N/A | Transitional class in v1 to bridge to v2. |
26
+
27
+ ---
28
+
29
+ ## Method-by-method mapping
30
+
31
+ ### 1) Base class construction
32
+
33
+ **v1: `BasePage`**
34
+
35
+ ```ts
36
+ class LoginPage extends BasePage<Paths> {
37
+ constructor(page: Page, testInfo: TestInfo, log: PlaywrightReportLogger) {
38
+ super(page, testInfo, "https://example.com", "/login", "LoginPage", log);
39
+ }
40
+
41
+ protected initLocatorSchemas() {
42
+ // v1 schemas
43
+ }
44
+ }
45
+ ```
46
+
47
+ **v2: `PageObject`**
48
+
49
+ ```ts
50
+ class LoginPage extends PageObject<Paths> {
51
+ constructor(page: Page) {
52
+ super(page, "https://example.com", "/login", { label: "LoginPage" });
53
+ }
54
+
55
+ protected defineLocators() {
56
+ // v2 registry DSL
57
+ }
58
+
59
+ protected pageActionsToPerformAfterNavigation() {
60
+ return [];
61
+ }
62
+ }
63
+ ```
64
+
65
+ #### **Key changes**
66
+
67
+ - v2 removes `testInfo` and `PlaywrightReportLogger` from the base class.
68
+ - v2 requires `pageActionsToPerformAfterNavigation()`.
69
+ - v2 uses `label` instead of `pocName`.
70
+
71
+ ---
72
+
73
+ ### 2) URL typing
74
+
75
+ **v1: `BasePageOptions.urlOptions`**
76
+
77
+ ```ts
78
+ type Options = { urlOptions: { baseUrlType: string; urlPathType: RegExp } };
79
+
80
+ class ReceiptPage extends BasePage<Paths, Options> {
81
+ constructor(page: Page, testInfo: TestInfo, log: PlaywrightReportLogger) {
82
+ super(page, testInfo, "https://example.com", /\/^[A-Za-z0-9]{12}$/, "ReceiptPage", log);
83
+ }
84
+ }
85
+ ```
86
+
87
+ **v2: `UrlTypeOptions`**
88
+
89
+ ```ts
90
+ type Options = { baseUrlType: string; urlPathType: RegExp };
91
+
92
+ class ReceiptPage extends PageObject<Paths, Options> {
93
+ constructor(page: Page) {
94
+ super(page, "https://example.com", /\/^[A-Za-z0-9]{12}$/);
95
+ }
96
+ }
97
+ ```
98
+
99
+ ---
100
+
101
+ ### 3) Locator schema definitions
102
+
103
+ #### **v1: `LocatorSchema` + `GetByMethod`**
104
+
105
+ ```ts
106
+ type Paths = "main" | "main.button@login";
107
+
108
+ // initLocatorSchemas:
109
+ this.locators.addSchema("main", {
110
+ locatorMethod: GetByMethod.locator,
111
+ locator: "main",
112
+ });
113
+
114
+ this.locators.addSchema("main.button@login", {
115
+ locatorMethod: GetByMethod.role,
116
+ role: "button",
117
+ roleOptions: { name: "Login" },
118
+ });
119
+ ```
120
+
121
+ #### **v2: Fluent DSL**
122
+
123
+ ```ts
124
+ type Paths = "main" | "main.button@login";
125
+
126
+ // defineLocators:
127
+ this.add("main").locator("main");
128
+ this.add("main.button@login").getByRole("button", { name: "Login" });
129
+ ```
130
+
131
+ Similar to how you would do natively in playwright:
132
+
133
+ ```ts
134
+ readonly main: Locator;
135
+ readonly loginButton: Locator;
136
+
137
+ // constructor:
138
+ this.main = page.locator("main");
139
+ this.loginButton = this.main.getByRole("button", { name: "Login" });
140
+ ```
141
+
142
+ ---
143
+
144
+ ### 4) `getLocator` and `getNestedLocator`
145
+
146
+ #### **v1 (async, optional index map)**
147
+
148
+ ```ts
149
+ const login = await poc.getLocator("navbar.button@login");
150
+ await login.click();
151
+
152
+ const logout = await poc.getNestedLocator("navbar.button@logout");
153
+ await logout.click();
154
+
155
+ const submit = await poc.getNestedLocator("main.form.button@submit", { "main.form": 0 });
156
+ await submit.click();
157
+ ```
158
+
159
+ **v2 (sync, use `nth`)**
160
+
161
+ ```ts
162
+ await poc.getLocator("navbar.button@login").click();
163
+
164
+ await poc.getNestedLocator("navbar.button@logout").click();
165
+
166
+ await poc.getLocatorSchema("main.form.button@submit").nth("main.form", 0).getNestedLocator().click();
167
+ ```
168
+
169
+ ---
170
+
171
+ ### 5) Mutating locator definitions
172
+
173
+ #### **v1: update**
174
+
175
+ ```ts
176
+ const patched = await poc
177
+ .getLocatorSchema("main.button@login")
178
+ .update("main.button@login", { roleOptions: { name: "Sign in" } })
179
+ .getNestedLocator();
180
+ ```
181
+
182
+ **v1** has no equivalent to v2's `replace` and `remove`.
183
+
184
+ #### **v2: update/replace/remove**
185
+
186
+ ```ts
187
+ const patch = poc
188
+ .getLocatorSchema("main.button@login")
189
+ .update() // defaults to the terminal path when no path is provided
190
+ .getByRole({ name: "Sign in" })
191
+ .getNestedLocator();
192
+
193
+ const post = poc
194
+ .getLocatorSchema("main.button@login")
195
+ .replace() // defaults to the terminal path when no path is provided
196
+ .getByText("Sign in")
197
+ .getNestedLocator();
198
+
199
+ const delete = poc
200
+ .getLocatorSchema("main.button@login")
201
+ .remove("main")
202
+ .getNestedLocator();
203
+ ```
204
+
205
+ ---
206
+
207
+ ### 6) Filtering and indexing
208
+
209
+ **v1: `addFilter` + index map**
210
+
211
+ ```ts
212
+ const filtered = await poc
213
+ .getLocatorSchema("main.list.item")
214
+ .addFilter("main.list", { hasText: "list" })
215
+ .addFilter("main.list.item", { hasText: "Row" })
216
+ .getNestedLocator({
217
+ "main.list": 0,
218
+ "main.list.item": -1
219
+ });
220
+ ```
221
+
222
+ **v2: `filter` + `nth`**
223
+
224
+ ```ts
225
+ const filtered = poc
226
+ .getLocatorSchema("main.list.item")
227
+ .filter("main.list", { hasText: "list" })
228
+ .nth("main.list", 0)
229
+ .filter({ hasText: "Row" }) // not providing a path will default to the terminal-path
230
+ .nth(-1) // not providing a path will default to the terminal-path
231
+ .getNestedLocator();
232
+ ```
233
+
234
+ ---
235
+
236
+ ### 7) `describe` (v2 only)
237
+
238
+ **v1** has no equivalent.
239
+
240
+ **v2**:
241
+
242
+ ```ts
243
+ const changePasswordBtn = poc
244
+ .getLocatorSchema("main.region@security.button@edit")
245
+ .filter({ hasText: "Change password"})
246
+ .describe("Change password")
247
+ .getNestedLocator();
248
+ ```
249
+
250
+ See Playwright [locator.describe](https://playwright.dev/docs/api/class-locator#locator-describe).
251
+
252
+ ---
253
+
254
+ ### 8) Reusable locators (v2 only)
255
+
256
+ **v1**:
257
+
258
+ ```ts
259
+ import { GetByMethod, GetLocatorBase, LocatorSchemaWithoutPath } from "pomwright";
260
+
261
+ ...
262
+
263
+ const orderItemDetails: LocatorSchemaWithoutPath = {
264
+ locator: ".order-item-details",
265
+ locatorMethod: GetByMethod.locator
266
+ };
267
+
268
+ addSchema("main.confirmation.region@yourSubscriptions.orderItem.details", {
269
+ ...orderItemDetails
270
+ });
271
+
272
+ addSchema("main.confirmation.region@yourSubscriptions.orderItem.details@activationTime", {
273
+ ...orderItemDetails,
274
+ locatorOptions: {
275
+ hasText: "Activation date:"
276
+ }
277
+ });
278
+ ```
279
+
280
+ v1 does not support reusing a LocatorSchema from an existing path.
281
+
282
+ **v2** provides a reusable seed and reuse-by-path:
283
+
284
+ ```ts
285
+ import type { LocatorRegistry } from "pomwright";
286
+
287
+ ...
288
+ // createReusable:
289
+ const orderItemDetails = createReusable.locator(".order-item-details");
290
+
291
+ add("main.confirmation.region@yourSubscriptions.orderItem.details", { reuse: orderItemDetails });
292
+
293
+ add("main.confirmation.region@yourSubscriptions.orderItem.details@activationTime", { reuse: orderItemDetails })
294
+ .locator({ hasText: "Activation date:"})
295
+
296
+ // reuse a previously added locator definition by path reference
297
+ add("main.confirmation.region@yourSubscriptions.orderItem.details@activationTimeError", {
298
+ reuse: "errors.invalidActivationTime"
299
+ });
300
+ ```
301
+
302
+ ---
303
+
304
+ ### 9) Frame locators
305
+
306
+ **v1** returns a `frameLocator` cast as a `Locator` (for chaining), which might be surprising...
307
+
308
+ ```ts
309
+ this.locators.addSchema("main.frame@login", {
310
+ locatorMethod: GetByMethod.frameLocator,
311
+ frameLocator: "#auth",
312
+ });
313
+ ```
314
+
315
+ **v2**:
316
+
317
+ ```ts
318
+ this.add("main.frame@login").frameLocator("#auth");
319
+ this.add("main.frame@login.input@username").getByLabel("Username");
320
+ ```
321
+
322
+ When you register a frame locator using add("path.to.frame").frameLocator("iframe#id"), POMWright v2 stores a frameLocator strategy (Playwright frameLocator semantics). During resolution:
323
+
324
+ - Non‑terminal frame locator paths: if the frame locator is not the last segment in the path, the registry enters the frame by using the FrameLocator as the current target. The next segments are resolved inside that frame. This is why you can chain frameLocator → getByRole and reliably target elements inside the iframe.
325
+ - Terminal frame locator paths: if the frame locator is the last segment, the registry returns frameLocator.owner() — i.e., the iframe element locator — rather than a FrameLocator. This keeps the terminal locator a standard Locator and makes it safe to assert visibility or existence of the iframe itself.
326
+
327
+ In short: non‑terminal frame locators scope the chain into the frame; terminal frame locators return the iframe element. The chain is built directly with Playwright’s frameLocator(...) API and frameLocator.owner() for terminal paths.
328
+
329
+ ---
330
+
331
+ ### 10) `getById` behavior
332
+
333
+ #### **v1 getById**
334
+
335
+ ```ts
336
+ // string example
337
+ addSchema({
338
+ locatorMethod: GetByMethod.id,
339
+ locatorSchemaPath: "main.modal@close",
340
+ id: "close-modal",
341
+ });
342
+
343
+ // regex example
344
+ addSchema({
345
+ locatorMethod: GetByMethod.id,
346
+ locatorSchemaPath: "main.modal@close",
347
+ id: /close-modal/,
348
+ });
349
+ ```
350
+
351
+ #### **v2 getById**
352
+
353
+ ```ts
354
+ // string example
355
+ add("main.modal@close").getById("close-modal");
356
+
357
+ // regex example
358
+ add("main.modal@close").getById(/close-modal/);
359
+ ```
360
+
361
+ #### **v1 vs v2 regex ID mapping**
362
+
363
+ - v1 regex id mapping uses `*[id^="pattern"]`.
364
+ - v2 regex id mapping uses `[id*="pattern"]` and escapes special characters.
365
+
366
+ What v1 does (regex IDs)
367
+ In v1, GetByMethod.id uses a prefix match selector for regex IDs: *[id^="pattern"]. This means it matches elements whose id starts with the regex source string (anchored to the beginning of the attribute value). It does not escape special characters in the regex source before inserting it into the selector.
368
+
369
+ What v2 does (regex IDs)
370
+ In v2, getById uses a substring match selector: [id*="pattern"]. This means it matches elements whose id contains the regex source string anywhere, not just at the start. Importantly, v2 escapes special characters from the regex source before inserting it into the selector (via cssEscape), so characters like . or [ are treated as literal characters in the selector rather than CSS syntax.
371
+
372
+ Why this matters:
373
+
374
+ - v1 regex IDs are effectively prefix matches and can break if the regex source contains CSS‑special characters (since it is not escaped).
375
+ - v2 regex IDs are substring matches and are safer with special characters due to escaping, but can be broader because they match anywhere in the id value. So v2 is more robust but potentially less strict (unless you choose a more specific regex pattern or switch to string IDs for exact matches).
376
+
377
+ ---
378
+
379
+ ### 11) SessionStorage
380
+
381
+ #### **v1 SessionStorage**
382
+
383
+ ```ts
384
+ await poc.sessionStorage.set({ token: "abc" }, true);
385
+
386
+ await poc.sessionStorage.setOnNextNavigation({ theme: "dark" });
387
+
388
+ const data = await poc.sessionStorage.get(); // retrieves all key-value pairs
389
+
390
+ const data = await poc.sessionStorage.get(["token"]); // retrieves specified key-value pair only
391
+
392
+ await poc.sessionStorage.clear();
393
+ ```
394
+
395
+ #### **v2 SessionStorage**
396
+
397
+ ```ts
398
+ await poc.sessionStorage.set({ token: "abc" }, { reload: true });
399
+
400
+ await poc.sessionStorage.setOnNextNavigation({ theme: "dark" });
401
+
402
+ const data = await poc.sessionStorage.get(); // retrieves all key-value pairs
403
+
404
+ const data = await poc.sessionStorage.get(["token"], { waitForContext: true }); // only retrieves the specified key-value pair
405
+
406
+ await poc.sessionStorage.clear(); // deletes all key-value pairs
407
+
408
+ await poc.sessionStorage.clear(["token"], { waitForContext: true }); // deletes specified key-value pairs only
409
+ ```
410
+
411
+ #### v2 generic typing example
412
+
413
+ ```ts
414
+ type SessionState = { // or import the actual type
415
+ token: string;
416
+ theme: string;
417
+ };
418
+
419
+ await poc.sessionStorage.set<SessionState>({ token: "abc", theme: "dark" });
420
+
421
+ const all = await poc.sessionStorage.get<SessionState>();
422
+ // all: Record<string, SessionState> (all keys, typed as SessionState values)
423
+
424
+ const onlyToken = await poc.sessionStorage.get<SessionState>(["token"], { waitForContext: true });
425
+ // onlyToken: Record<string, SessionState>
426
+ ```
427
+
428
+ **Important**: the generic is not runtime validation. It only tells TypeScript what value shape you expect to store/retrieve. The underlying storage is still string‑serialized JSON, and invalid shapes won’t be rejected at runtime by SessionStorage itself.
429
+
430
+ #### What waitForContext does and when to use it
431
+
432
+ `waitForContext` tells the helper to wait for a main‑frame execution context if none exists yet (e.g., before the first navigation, during reloads, or after a context was destroyed). Without it, v2 throws to avoid silent failures. With it, v2 listens for a navigation and retries once a valid context is available.
433
+
434
+ - Use `waitForContext`: true when calling `get`, `set`, or `clear` around navigation boundaries.
435
+ - Keep it `false` (default) when you’re sure the page already has a valid execution context.
436
+
437
+ ---
438
+
439
+ ### 12) Logging
440
+
441
+ #### **v1: logger passed into BasePage**
442
+
443
+ ```ts
444
+ class LoginPage extends BasePage<Paths> {
445
+ constructor(page: Page, testInfo: TestInfo, log: PlaywrightReportLogger) {
446
+ super(page, testInfo, "https://example.com", "/login", "LoginPage", log);
447
+ }
448
+ }
449
+ ```
450
+
451
+ #### **v2: log fixture**
452
+
453
+ ```ts
454
+ import { test } from "pomwright";
455
+
456
+ test("login flow", async ({ page, log }) => {
457
+ log.info("starting login");
458
+ });
459
+ ```
460
+
461
+ ---
462
+
463
+ ### 13) Navigation
464
+
465
+ **v1** has no built-in navigation helper.
466
+
467
+ **v2** provides `navigation` on `PageObject`:
468
+
469
+ ```ts
470
+ await loginPage.navigation.goto("https://anotherDomain.com");
471
+ await loginPage.navigation.goto("/anotherResourcePath"); // prefixes with loginPage.baseUrl
472
+ await loginPage.navigation.gotoThisPage(); // navigates to loginPage.fullUrl
473
+ await loginPage.navigation.expectThisPage(); // url === loginPage.fullUrl
474
+ await loginPage.navigation.expectAnotherPage(); // url != loginPage.fullUrl after navigation
475
+ ```
476
+
477
+ - If `loginPage.fullUrl` is of type string then all four methods are available.
478
+ - If `loginPage.fullUrl` is of type RegExp then only `expectThisPage` and `expectAnotherPage` are available.
479
+
480
+ ---
481
+
482
+ ### 14) Step decorator (v2 only)
483
+
484
+ ```ts
485
+ import { step } from "pomwright";
486
+
487
+ class LoginPage {
488
+ @step()
489
+ async loginAsUser(user: User) {
490
+ // ...
491
+ }
492
+ }
493
+ ```
494
+
495
+ ---
496
+
497
+ ### 15) Base API
498
+
499
+ #### **v1 BaseAPI**
500
+
501
+ ```ts
502
+ class UsersApi extends BaseApi {
503
+ constructor(context: APIRequestContext, log: PlaywrightReportLogger) {
504
+ super("https://example.com", "UsersApi", context, log);
505
+ }
506
+ }
507
+ ```
508
+
509
+ #### **v2 BaseAPI**
510
+
511
+ There is no built-in `BaseApi`. Implement your own base class or wrapper.
512
+
513
+ ---
514
+
515
+ ### 16) Bridge class: `BasePageV1toV2`
516
+
517
+ `BasePageV1toV2` allows v1 schemas and v2 registry accessors to coexist:
518
+
519
+ ```ts
520
+ class LoginPage extends BasePageV1toV2<Paths> {
521
+ protected defineLocators() {
522
+ this.add("main.button@login").getByRole("button", { name: "Login" });
523
+ }
524
+
525
+ protected initLocatorSchemas() {
526
+ this.locators.addSchema({
527
+ locatorSchemaPath: "main.form@login",
528
+ locatorMethod: GetByMethod.role,
529
+ role: "form",
530
+ roleOptions: { name: "Login" },
531
+ });
532
+ }
533
+ }
534
+ ```
535
+
536
+ ---
537
+
538
+ ## Summary
539
+
540
+ - v2 is a complete redesign of the locator system around a fluent, typed registry.
541
+ - v1 features are largely replaced rather than extended.
542
+ - Use `PageObject` for new work and plan to fully remove `BasePage` and `BasePageV1toV2` before v2.0.0.
543
+
544
+ For step-by-step guides, see:
545
+
546
+ - `direct-migration-guide.md`
547
+ - `bridge-migration-guide.md`