pomwright 1.4.0 → 1.5.1

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/AGENTS.md +37 -0
  2. package/CHANGELOG.md +193 -0
  3. package/README.md +316 -34
  4. package/dist/index.d.mts +1049 -30
  5. package/dist/index.d.ts +1049 -30
  6. package/dist/index.js +2290 -65
  7. package/dist/index.mjs +2287 -67
  8. package/docs/v1-to-v2-migration/bridge-migration-guide.md +159 -0
  9. package/docs/v1-to-v2-migration/direct-migration-guide.md +238 -0
  10. package/docs/v1-to-v2-migration/v1-to-v2-comparison.md +547 -0
  11. package/docs/v2/PageObject.md +293 -0
  12. package/docs/v2/composing-locator-modules.md +93 -0
  13. package/docs/v2/locator-registry.md +695 -0
  14. package/docs/v2/logging.md +168 -0
  15. package/docs/v2/overview.md +515 -0
  16. package/docs/v2/session-storage.md +160 -0
  17. package/index.ts +61 -9
  18. package/intTestV2/.env +0 -0
  19. package/intTestV2/fixtures/testApp.fixtures.ts +43 -0
  20. package/intTestV2/package.json +22 -0
  21. package/intTestV2/page-object-models/testApp/pages/iframe/iframe.locatorSchema.ts +24 -0
  22. package/intTestV2/page-object-models/testApp/pages/iframe/iframe.page.ts +17 -0
  23. package/intTestV2/page-object-models/testApp/pages/testPage.locatorSchema.ts +32 -0
  24. package/intTestV2/page-object-models/testApp/pages/testPage.page.ts +119 -0
  25. package/intTestV2/page-object-models/testApp/pages/testPath/[color]/color.locatorSchema.ts +29 -0
  26. package/intTestV2/page-object-models/testApp/pages/testPath/[color]/color.page.ts +48 -0
  27. package/intTestV2/page-object-models/testApp/pages/testPath/testPath.locatorSchema.ts +9 -0
  28. package/intTestV2/page-object-models/testApp/pages/testPath/testPath.page.ts +23 -0
  29. package/intTestV2/page-object-models/testApp/pages/testfilters/testfilters.locatorSchema.ts +114 -0
  30. package/intTestV2/page-object-models/testApp/pages/testfilters/testfilters.page.ts +23 -0
  31. package/intTestV2/page-object-models/testApp/testApp.base.ts +20 -0
  32. package/intTestV2/playwright.config.ts +54 -0
  33. package/intTestV2/server.js +216 -0
  34. package/intTestV2/test-data/staticPage/index.html +280 -0
  35. package/intTestV2/test-data/staticPage/w3images/avatar2.png +0 -0
  36. package/intTestV2/test-data/staticPage/w3images/avatar3.png +0 -0
  37. package/intTestV2/test-data/staticPage/w3images/avatar5.png +0 -0
  38. package/intTestV2/test-data/staticPage/w3images/avatar6.png +0 -0
  39. package/intTestV2/test-data/staticPage/w3images/forest.jpg +0 -0
  40. package/intTestV2/test-data/staticPage/w3images/lights.jpg +0 -0
  41. package/intTestV2/test-data/staticPage/w3images/mountains.jpg +0 -0
  42. package/intTestV2/test-data/staticPage/w3images/nature.jpg +0 -0
  43. package/intTestV2/test-data/staticPage/w3images/snow.jpg +0 -0
  44. package/intTestV2/tests/locatorRegistry/add/add.describe.spec.ts +54 -0
  45. package/intTestV2/tests/locatorRegistry/add/add.filter.spec.ts +143 -0
  46. package/intTestV2/tests/locatorRegistry/add/add.frameLocator.spec.ts +23 -0
  47. package/intTestV2/tests/locatorRegistry/add/add.get.clone.spec.ts +76 -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 +107 -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.locatorSchemaPath.typecheck.ts +86 -0
  76. package/intTestV2/tests/locatorRegistry/validation/validation.sub-path.spec.ts +45 -0
  77. package/intTestV2/tests/step/step.spec.ts +49 -0
  78. package/intTestV2/tests/testApp/color.spec.ts +15 -0
  79. package/intTestV2/tests/testApp/iframe.spec.ts +57 -0
  80. package/intTestV2/tests/testApp/testFilters.spec.ts +24 -0
  81. package/intTestV2/tests/testApp/testPage.spec.ts +161 -0
  82. package/intTestV2/tests/testApp/testPath.spec.ts +18 -0
  83. package/pack-build.sh +11 -0
  84. package/pack-test-v2.sh +36 -0
  85. package/package.json +10 -3
  86. package/playwright.base.ts +42 -0
  87. package/skills/README.md +56 -0
  88. package/skills/pomwright-v1-5-bridge-migration/SKILL.md +40 -0
  89. package/skills/pomwright-v1-5-bridge-migration/references/call-site-migration.md +178 -0
  90. package/skills/pomwright-v1-5-bridge-migration/references/schema-translation.md +183 -0
  91. package/skills/pomwright-v2-migration/SKILL.md +63 -0
  92. package/skills/pomwright-v2-migration/references/call-site-migration.md +265 -0
  93. package/skills/pomwright-v2-migration/references/class-migration.md +266 -0
  94. package/skills/pomwright-v2-migration/references/fixture-and-helpers.md +423 -0
  95. package/skills/pomwright-v2-migration/references/locator-registration.md +344 -0
  96. package/srcV2/fixture/base.fixtures.ts +23 -0
  97. package/srcV2/helpers/navigation.ts +153 -0
  98. package/srcV2/helpers/playwrightReportLogger.ts +196 -0
  99. package/srcV2/helpers/sessionStorage.ts +251 -0
  100. package/srcV2/helpers/stepDecorator.ts +106 -0
  101. package/srcV2/locators/index.ts +15 -0
  102. package/srcV2/locators/locatorQueryBuilder.ts +427 -0
  103. package/srcV2/locators/locatorRegistrationBuilder.ts +558 -0
  104. package/srcV2/locators/locatorRegistry.ts +583 -0
  105. package/srcV2/locators/locatorUpdateBuilder.ts +602 -0
  106. package/srcV2/locators/reusableLocatorBuilder.ts +200 -0
  107. package/srcV2/locators/types.ts +256 -0
  108. package/srcV2/locators/utils.ts +309 -0
  109. package/srcV2/locators/v1SchemaTranslator.ts +178 -0
  110. package/srcV2/pageObject.ts +105 -0
  111. /package/docs/{BaseApi-explanation.md → v1/BaseApi-explanation.md} +0 -0
  112. /package/docs/{BasePage-explanation.md → v1/BasePage-explanation.md} +0 -0
  113. /package/docs/{LocatorSchema-explanation.md → v1/LocatorSchema-explanation.md} +0 -0
  114. /package/docs/{LocatorSchemaPath-explanation.md → v1/LocatorSchemaPath-explanation.md} +0 -0
  115. /package/docs/{PlaywrightReportLogger-explanation.md → v1/PlaywrightReportLogger-explanation.md} +0 -0
  116. /package/docs/{get-locator-methods-explanation.md → v1/get-locator-methods-explanation.md} +0 -0
  117. /package/docs/{intro-to-using-pomwright.md → v1/intro-to-using-pomwright.md} +0 -0
  118. /package/docs/{sessionStorage-methods-explanation.md → v1/sessionStorage-methods-explanation.md} +0 -0
  119. /package/docs/{tips-folder-structure.md → v1/tips-folder-structure.md} +0 -0
@@ -0,0 +1,293 @@
1
+ # PageObject (v2)
2
+
3
+ ## Overview
4
+
5
+ `PageObject` is the core v2 abstraction that wires a Playwright `Page` to:
6
+
7
+ - A typed locator registry (`add`, `getLocator`, `getNestedLocator`, `getLocatorSchema`).
8
+ - The navigation helper (`navigation`).
9
+ - The session storage helper (`sessionStorage`).
10
+
11
+ It is intentionally minimal: you provide locator definitions and any post-navigation actions, then compose additional behavior on top.
12
+
13
+ ---
14
+
15
+ ## Constructor and generics
16
+
17
+ ```ts
18
+ export abstract class PageObject<
19
+ LocatorSchemaPathType extends string,
20
+ Options extends UrlTypeOptions = { baseUrlType: string; urlPathType: string },
21
+ > {
22
+ protected constructor(
23
+ page: Page,
24
+ baseUrl: BaseUrlTypeFromOptions<Options>,
25
+ urlPath: UrlPathTypeFromOptions<Options>,
26
+ options?: { label?: string; navOptions?: NavigationOptions },
27
+ )
28
+ }
29
+ ```
30
+
31
+ ### `LocatorSchemaPathType`
32
+
33
+ A literal union of dot-delimited locator paths. The registry validates this union at compile time and runtime.
34
+
35
+ ```ts
36
+ type Paths =
37
+ | "common.spinner"
38
+ | "main"
39
+ | "main.form@login"
40
+ | "main.form@login.input@username"
41
+ | "main.form@login.input@password"
42
+ | "main.button@login";
43
+ ```
44
+
45
+ ### `Options` (`UrlTypeOptions`)
46
+
47
+ `UrlTypeOptions` controls how `baseUrl`, `urlPath`, and `fullUrl` are typed and interpreted:
48
+
49
+ ```ts
50
+ type UrlTypeOptions = {
51
+ baseUrlType?: string | RegExp;
52
+ urlPathType?: string | RegExp;
53
+ };
54
+ ```
55
+
56
+ - If both are strings, `fullUrl` is a string and all navigation methods are available.
57
+ - If either is `RegExp`, `fullUrl` is a `RegExp`, and navigation is limited to URL assertions (`expectThisPage`, `expectAnotherPage`).
58
+
59
+ ### Constructor options
60
+
61
+ - `label`: Optional label used in navigation and session storage step titles. Defaults to the class name.
62
+ - `navOptions`: Default `NavigationOptions` (`waitUntil`, `waitForLoadState`) used by the navigation helper.
63
+
64
+ ---
65
+
66
+ ## Required abstract methods
67
+
68
+ ### `defineLocators()`
69
+
70
+ Define every locator path for the page using the v2 registry DSL (`add(...).getByRole(...)`, etc.). This is called in the constructor before navigation is created.
71
+
72
+ ### `pageActionsToPerformAfterNavigation()`
73
+
74
+ Return a list of async callbacks to run after `gotoThisPage()` and `expectThisPage()`.
75
+
76
+ - Return `[]` to run no actions.
77
+ - Return `null` to skip the actions list entirely.
78
+
79
+ ---
80
+
81
+ ## Public properties
82
+
83
+ | Property | Type | Purpose |
84
+ | --- | --- | --- |
85
+ | `page` | `Page` | Playwright `Page` instance. |
86
+ | `baseUrl` / `urlPath` / `fullUrl` | `string` or `RegExp` | URL values typed based on `UrlTypeOptions`. |
87
+ | `label` | `string` | Label used for navigation and session storage steps. |
88
+ | `sessionStorage` | `SessionStorage` | Session storage helper, labeled with `label`. |
89
+ | `navigation` | `ExtractNavigationType<FullUrlType>` | Navigation helper (methods vary by URL type). |
90
+ | `add` | `AddAccessor<Paths>` | Registry `add` method for locator definitions. |
91
+ | `getLocator` | `GetLocatorAccessor<Paths>` | Terminal locator resolver. |
92
+ | `getNestedLocator` | `GetNestedLocatorAccessor<Paths>` | Full chained locator resolver. |
93
+ | `getLocatorSchema` | `GetLocatorSchemaAccessor<Paths>` | Builder clone for filters/indices/updates. |
94
+
95
+ ---
96
+
97
+ ## Example: basic `PageObject` class
98
+
99
+ ```ts
100
+ import { expect, type Page } from "@playwright/test";
101
+ import { PageObject, step } from "pomwright";
102
+ import type { User } from "testData";
103
+
104
+ type Paths =
105
+ | "common.spinner"
106
+ | "main"
107
+ | "main.form@login"
108
+ | "main.form@login.input@username"
109
+ | "main.form@login.input@password"
110
+ | "main.button@login";
111
+
112
+ export class LoginPage extends PageObject<Paths> {
113
+ constructor(page: Page) {
114
+ super(page, "https://example.com", "/login", { label: "LoginPage" });
115
+ }
116
+
117
+ protected defineLocators(): void {
118
+ this.add("common.spinner").getByTestId("loading-spinner");
119
+ this.add("main").locator("main");
120
+ this.add("main.form@login").getByRole("form", { name: "Login" });
121
+ this.add("main.form@login.input@username").getByLabel("Username");
122
+ this.add("main.form@login.input@password").getByLabel("Password");
123
+ this.add("main.button@login").getByRole("button", { name: "Login" });
124
+ }
125
+
126
+ protected pageActionsToPerformAfterNavigation() {
127
+ return [
128
+ async () => {
129
+ await expect(this.getLocator("common.spinner")).toHaveCount(0);
130
+ await this.getNestedLocator("main.form@login").waitFor({ state: "visible" });
131
+ },
132
+ ];
133
+ }
134
+
135
+ @step()
136
+ async loginAsUser(user: User) {
137
+ await this.getNestedLocator("main.form@login.input@username").fill(user.username);
138
+ await this.getNestedLocator("main.form@login.input@password").fill(user.password);
139
+ await this.getNestedLocator("main.button@login").click();
140
+ }
141
+ }
142
+ ```
143
+
144
+ ---
145
+
146
+ ## Navigation helper details
147
+
148
+ The navigation helper is created automatically and exposed as `pageObject.navigation`.
149
+
150
+ ### When `fullUrl` is a string
151
+
152
+ Available methods:
153
+
154
+ - `goto(urlPathOrUrl, options?)` – resolves paths starting with `/` against `baseUrl`.
155
+ - `gotoThisPage(options?)` – navigates to `fullUrl` and runs `pageActionsToPerformAfterNavigation()`.
156
+ - `expectThisPage(options?)` – waits for `fullUrl` and runs actions.
157
+ - `expectAnotherPage(options?)` – waits for a non-matching URL.
158
+
159
+ ### When `fullUrl` is a `RegExp`
160
+
161
+ Available methods:
162
+
163
+ - `expectThisPage(options?)` – matches against the regex.
164
+ - `expectAnotherPage(options?)` – asserts URL does not match.
165
+
166
+ ### Default vs per-call options
167
+
168
+ You can pass `navOptions` into the `PageObject` constructor and override per call:
169
+
170
+ ```ts
171
+ class OrdersPage extends PageObject<"main"> {
172
+ constructor(page: Page) {
173
+ super(page, "https://example.com", "/orders", {
174
+ label: "OrdersPage",
175
+ navOptions: { waitUntil: "domcontentloaded", waitForLoadState: "domcontentloaded" },
176
+ });
177
+ }
178
+
179
+ protected defineLocators() {
180
+ this.add("main").locator("main");
181
+ }
182
+
183
+ protected pageActionsToPerformAfterNavigation() {
184
+ return [];
185
+ }
186
+ }
187
+
188
+ await ordersPage.navigation.gotoThisPage({ waitUntil: "load" });
189
+ ```
190
+
191
+ ---
192
+
193
+ ## URL typing examples
194
+
195
+ ### All-string URLs (full navigation support)
196
+
197
+ ```ts
198
+ class ProfilePage extends PageObject<"main"> {
199
+ constructor(page: Page) {
200
+ super(page, "https://example.com", "/profile", { label: "ProfilePage" });
201
+ }
202
+
203
+ protected defineLocators() {
204
+ this.add("main").locator("main");
205
+ }
206
+
207
+ protected pageActionsToPerformAfterNavigation() {
208
+ return [];
209
+ }
210
+ }
211
+ ```
212
+
213
+ ### RegExp URL path (assert-only navigation)
214
+
215
+ ```ts
216
+ type UrlOptions = { baseUrlType: string; urlPathType: RegExp };
217
+
218
+ class AccountPage extends PageObject<"main", UrlOptions> {
219
+ constructor(page: Page) {
220
+ super(page, "https://example.com", /\/account\/.+/, { label: "AccountPage" });
221
+ }
222
+
223
+ protected defineLocators() {
224
+ this.add("main").locator("main");
225
+ }
226
+
227
+ protected pageActionsToPerformAfterNavigation() {
228
+ return [];
229
+ }
230
+ }
231
+
232
+ await accountPage.navigation.expectThisPage();
233
+ ```
234
+
235
+ ---
236
+
237
+ ## Accessing locators
238
+
239
+ - `getLocator(path)` returns the **terminal** locator only (no ancestor chaining).
240
+ - `getNestedLocator(path)` returns the **fully chained** locator.
241
+ - `getLocatorSchema(path)` returns a mutable clone to add filters, indices, or update/replace definitions before resolving.
242
+
243
+ ```ts
244
+ const terminal = loginPage.getLocator("main.form@login.input@username");
245
+ const chained = loginPage.getNestedLocator("main.form@login.input@username");
246
+
247
+ const filtered = loginPage
248
+ .getLocatorSchema("main.form@login.input@username")
249
+ .filter("main.form@login.input@username", { hasText: /User/i })
250
+ .getNestedLocator();
251
+ ```
252
+
253
+ ---
254
+
255
+ ## SessionStorage helper
256
+
257
+ `PageObject` constructs `SessionStorage` with `label` as a prefix, so step titles are easy to track.
258
+
259
+ ```ts
260
+ await loginPage.sessionStorage.set({ token: "abc" }, { reload: true });
261
+ await loginPage.sessionStorage.setOnNextNavigation({ theme: "dark" });
262
+ const data = await loginPage.sessionStorage.get(["token", "theme"], { waitForContext: true });
263
+ await loginPage.sessionStorage.clear(["token"], { waitForContext: true });
264
+ ```
265
+
266
+ ---
267
+
268
+ ## Fixture wiring example
269
+
270
+ ```ts
271
+ import { test as base } from "@playwright/test";
272
+ import type { Page } from "@playwright/test";
273
+ import { LoginPage } from "./login.page";
274
+
275
+ type Fixtures = { loginPage: LoginPage };
276
+
277
+ export const test = base.extend<Fixtures>({
278
+ loginPage: async ({ page }, use) => {
279
+ await use(new LoginPage(page as Page));
280
+ },
281
+ });
282
+ ```
283
+
284
+ ---
285
+
286
+ ## Key differences vs v1
287
+
288
+ - `PageObject` replaces `BasePage` and does **not** carry Playwright `testInfo` or a logger.
289
+ - Registry APIs are fluent (`add(...).getByRole(...)`) instead of schema objects.
290
+ - `getLocator`/`getNestedLocator` are synchronous and do not accept index maps; use `getLocatorSchema(...).nth(...)`.
291
+ - Navigation is built into the base class and depends on URL typing.
292
+
293
+ See the migration docs in `docs/v1-to-v2-migration` for a step-by-step guide.
@@ -0,0 +1,93 @@
1
+ # Composing locator modules
2
+
3
+ When you split locator registration across `*.locators.ts` files, type child modules with
4
+ an inline generic so they accept parent registries with supersets of paths while keeping full fluent
5
+ type precision:
6
+
7
+ ```ts
8
+ // alert.locator.ts
9
+ import type { LocatorRegistry } from "pomwright";
10
+
11
+ type Paths = "common.alert" | "common.alert.message";
12
+
13
+ export function defineLocators<Extra extends string = never>(registry: LocatorRegistry<Paths | Extra>) {
14
+ registry.add("common.alert").locator(".alert-container");
15
+ registry.add("common.alert.message").locator(".alert-message");
16
+ }
17
+ ```
18
+
19
+ ```ts
20
+ // nav.locator.ts
21
+ ```
22
+
23
+ ```ts
24
+ // footer.locator.ts
25
+ ```
26
+
27
+ Intermediary parent modules can then pass their full registry directly without casts:
28
+
29
+ ```ts
30
+ // common.locators.ts
31
+ import type { LocatorRegistry } from "pomwright";
32
+ import { type Paths as Alert, defineLocators as addAlert } from "./alert.locators";
33
+ import { type Paths as Nav, defineLocators as addNav } from "./nav.locators";
34
+ import { type Paths as Footer, defineLocators as addFooter } from "./footer.locators";
35
+
36
+ type Paths = Alert | Nav | Footer | "main";
37
+
38
+ export function defineLocators<Extra extends string = never>(registry: LocatorRegistry<Paths | Extra>) {
39
+ addAlert(registry);
40
+ addNav(registry);
41
+ addFooter(registry);
42
+ registry.add("main").locator("main");
43
+ }
44
+ ```
45
+
46
+ Parent modules can then pass their full registry directly without casts:
47
+
48
+ ```ts
49
+ // login.locators.ts
50
+ import type { LocatorRegistry } from "pomwright";
51
+ import { type Paths as Common, defineLocators as addCommon } from "../common.locators";
52
+
53
+ export type Paths =
54
+ | Common
55
+ | "main.form@login"
56
+ | "main.form@login.input@username"
57
+ | "main.form@login.input@password"
58
+ | "main.button@login";
59
+
60
+ export function defineLocators(registry: LocatorRegistry<Paths>) {
61
+ addCommon(registry);
62
+ registry.add("main.form@login").getByRole("form", { name: "Login" });
63
+ registry.add("main.form@login.input@username").getByLabel("Username");
64
+ registry.add("main.form@login.input@password").getByLabel("Password");
65
+ registry.add("main.button@login").getByRole("button", { name: "Login" });
66
+ }
67
+ ```
68
+
69
+ ```ts
70
+ // login.page.ts
71
+ import { type Page, expect } from "@playwright/test";
72
+ import { PageObject } from "pomwright";
73
+ import { type Paths, defineLocators } from "./login.locators.ts";
74
+
75
+ export class LoginPage extends PageObject<Paths> {
76
+ constructor(page: Page) {
77
+ super(page, "https://example.com", "/login");
78
+ }
79
+
80
+ protected defineLocators(): void {
81
+ defineLocators(this.locatorRegistry);
82
+ }
83
+
84
+ protected pageActionsToPerformAfterNavigation() {
85
+ return [
86
+ async () => {
87
+ await this.getNestedLocator("common.nav.logo").waitFor({ state: "visible" });
88
+ await this.getNestedLocator("main.form@login").waitFor({ state: "visible" });
89
+ },
90
+ ];
91
+ }
92
+ }
93
+ ```