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.
- package/CHANGELOG.md +36 -0
- package/README.md +5 -5
- package/dist/index.d.mts +91 -989
- package/dist/index.d.ts +91 -989
- package/dist/index.js +627 -1887
- package/dist/index.mjs +633 -1888
- package/package.json +9 -11
- package/AGENTS.md +0 -37
- package/docs/v1/BaseApi-explanation.md +0 -63
- package/docs/v1/BasePage-explanation.md +0 -96
- package/docs/v1/LocatorSchema-explanation.md +0 -271
- package/docs/v1/LocatorSchemaPath-explanation.md +0 -165
- package/docs/v1/PlaywrightReportLogger-explanation.md +0 -56
- package/docs/v1/get-locator-methods-explanation.md +0 -250
- package/docs/v1/intro-to-using-pomwright.md +0 -899
- package/docs/v1/sessionStorage-methods-explanation.md +0 -38
- package/docs/v1/tips-folder-structure.md +0 -38
- package/docs/v1-to-v2-migration/bridge-migration-guide.md +0 -159
- package/docs/v1-to-v2-migration/direct-migration-guide.md +0 -238
- package/docs/v1-to-v2-migration/v1-to-v2-comparison.md +0 -547
- package/docs/v2/PageObject.md +0 -293
- package/docs/v2/composing-locator-modules.md +0 -93
- package/docs/v2/locator-registry.md +0 -693
- package/docs/v2/logging.md +0 -168
- package/docs/v2/overview.md +0 -515
- package/docs/v2/session-storage.md +0 -160
- package/index.ts +0 -75
- package/intTestV2/.env +0 -0
- package/intTestV2/fixtures/testApp.fixtures.ts +0 -43
- package/intTestV2/package.json +0 -22
- package/intTestV2/page-object-models/testApp/pages/iframe/iframe.locatorSchema.ts +0 -24
- package/intTestV2/page-object-models/testApp/pages/iframe/iframe.page.ts +0 -17
- package/intTestV2/page-object-models/testApp/pages/testPage.locatorSchema.ts +0 -32
- package/intTestV2/page-object-models/testApp/pages/testPage.page.ts +0 -119
- package/intTestV2/page-object-models/testApp/pages/testPath/[color]/color.locatorSchema.ts +0 -29
- package/intTestV2/page-object-models/testApp/pages/testPath/[color]/color.page.ts +0 -48
- package/intTestV2/page-object-models/testApp/pages/testPath/testPath.locatorSchema.ts +0 -9
- package/intTestV2/page-object-models/testApp/pages/testPath/testPath.page.ts +0 -23
- package/intTestV2/page-object-models/testApp/pages/testfilters/testfilters.locatorSchema.ts +0 -114
- package/intTestV2/page-object-models/testApp/pages/testfilters/testfilters.page.ts +0 -23
- package/intTestV2/page-object-models/testApp/testApp.base.ts +0 -20
- package/intTestV2/playwright.config.ts +0 -54
- package/intTestV2/server.js +0 -216
- package/intTestV2/test-data/staticPage/index.html +0 -280
- package/intTestV2/test-data/staticPage/w3images/avatar2.png +0 -0
- package/intTestV2/test-data/staticPage/w3images/avatar3.png +0 -0
- package/intTestV2/test-data/staticPage/w3images/avatar5.png +0 -0
- package/intTestV2/test-data/staticPage/w3images/avatar6.png +0 -0
- package/intTestV2/test-data/staticPage/w3images/forest.jpg +0 -0
- package/intTestV2/test-data/staticPage/w3images/lights.jpg +0 -0
- package/intTestV2/test-data/staticPage/w3images/mountains.jpg +0 -0
- package/intTestV2/test-data/staticPage/w3images/nature.jpg +0 -0
- package/intTestV2/test-data/staticPage/w3images/snow.jpg +0 -0
- package/intTestV2/tests/locatorRegistry/add/add.describe.spec.ts +0 -54
- package/intTestV2/tests/locatorRegistry/add/add.filter.spec.ts +0 -143
- package/intTestV2/tests/locatorRegistry/add/add.frameLocator.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/add/add.getByAltText.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/add/add.getById.spec.ts +0 -45
- package/intTestV2/tests/locatorRegistry/add/add.getByLabel.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/add/add.getByPlaceholder.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/add/add.getByRole.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/add/add.getByTestId.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/add/add.getByText.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/add/add.getByTitle.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/add/add.locator.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/add/add.reuseExisting.spec.ts +0 -66
- package/intTestV2/tests/locatorRegistry/add/add.reuseReusable.spec.ts +0 -311
- package/intTestV2/tests/locatorRegistry/add/add.spec.ts +0 -159
- package/intTestV2/tests/locatorRegistry/filter.cycle.spec.ts +0 -39
- package/intTestV2/tests/locatorRegistry/getLocator/getLocator.spec.ts +0 -253
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.clearSteps.spec.ts +0 -105
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.describe.spec.ts +0 -23
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.filter.spec.ts +0 -368
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.getLocator.spec.ts +0 -56
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.getNestedLocator.spec.ts +0 -175
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.nth.spec.ts +0 -60
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.remove.spec.ts +0 -32
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.replace.spec.ts +0 -24
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.spec.ts +0 -110
- package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.update.spec.ts +0 -322
- package/intTestV2/tests/locatorRegistry/getNestedLocator/getNestedLocator.spec.ts +0 -412
- package/intTestV2/tests/locatorRegistry/registry/registry.binding.spec.ts +0 -50
- package/intTestV2/tests/locatorRegistry/validation/validation.locatorSchemaPath.spec.ts +0 -115
- package/intTestV2/tests/locatorRegistry/validation/validation.sub-path.spec.ts +0 -45
- package/intTestV2/tests/step/step.spec.ts +0 -49
- package/intTestV2/tests/testApp/color.spec.ts +0 -15
- package/intTestV2/tests/testApp/iframe.spec.ts +0 -57
- package/intTestV2/tests/testApp/testFilters.spec.ts +0 -24
- package/intTestV2/tests/testApp/testPage.spec.ts +0 -161
- package/intTestV2/tests/testApp/testPath.spec.ts +0 -18
- package/pack-build.sh +0 -11
- package/pack-test-v2.sh +0 -36
- package/playwright.base.ts +0 -42
- package/skills/README.md +0 -56
- package/skills/pomwright-v1-5-bridge-migration/SKILL.md +0 -40
- package/skills/pomwright-v1-5-bridge-migration/references/call-site-migration.md +0 -178
- package/skills/pomwright-v1-5-bridge-migration/references/schema-translation.md +0 -183
- package/skills/pomwright-v2-migration/SKILL.md +0 -63
- package/skills/pomwright-v2-migration/references/call-site-migration.md +0 -265
- package/skills/pomwright-v2-migration/references/class-migration.md +0 -266
- package/skills/pomwright-v2-migration/references/fixture-and-helpers.md +0 -423
- package/skills/pomwright-v2-migration/references/locator-registration.md +0 -344
- package/srcV2/fixture/base.fixtures.ts +0 -23
- package/srcV2/helpers/navigation.ts +0 -153
- package/srcV2/helpers/playwrightReportLogger.ts +0 -196
- package/srcV2/helpers/sessionStorage.ts +0 -251
- package/srcV2/helpers/stepDecorator.ts +0 -106
- package/srcV2/locators/index.ts +0 -15
- package/srcV2/locators/locatorQueryBuilder.ts +0 -427
- package/srcV2/locators/locatorRegistrationBuilder.ts +0 -558
- package/srcV2/locators/locatorRegistry.ts +0 -541
- package/srcV2/locators/locatorUpdateBuilder.ts +0 -602
- package/srcV2/locators/reusableLocatorBuilder.ts +0 -200
- package/srcV2/locators/types.ts +0 -256
- package/srcV2/locators/utils.ts +0 -309
- package/srcV2/locators/v1SchemaTranslator.ts +0 -178
- package/srcV2/pageObject.ts +0 -105
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
# Class Migration: Inheritance, Constructor, Generics, Abstract Methods
|
|
2
|
-
|
|
3
|
-
## Inheritance change
|
|
4
|
-
|
|
5
|
-
```ts
|
|
6
|
-
// v1
|
|
7
|
-
import { BasePage } from "pomwright";
|
|
8
|
-
class MyPage extends BasePage<Paths> { ... }
|
|
9
|
-
|
|
10
|
-
// v1.5 bridge
|
|
11
|
-
import { BasePageV1toV2 } from "pomwright";
|
|
12
|
-
class MyPage extends BasePageV1toV2<Paths> { ... }
|
|
13
|
-
|
|
14
|
-
// v2
|
|
15
|
-
import { PageObject } from "pomwright";
|
|
16
|
-
class MyPage extends PageObject<Paths> { ... }
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## Constructor signature
|
|
20
|
-
|
|
21
|
-
v1/bridge constructor:
|
|
22
|
-
|
|
23
|
-
```ts
|
|
24
|
-
constructor(
|
|
25
|
-
page: Page,
|
|
26
|
-
testInfo: TestInfo,
|
|
27
|
-
baseUrl: string | RegExp,
|
|
28
|
-
urlPath: string | RegExp,
|
|
29
|
-
pocName: string,
|
|
30
|
-
pwrl: PlaywrightReportLogger,
|
|
31
|
-
)
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
v2 constructor:
|
|
35
|
-
|
|
36
|
-
```ts
|
|
37
|
-
constructor(
|
|
38
|
-
page: Page,
|
|
39
|
-
baseUrl: string | RegExp,
|
|
40
|
-
urlPath: string | RegExp,
|
|
41
|
-
options?: { label?: string; navOptions?: NavigationOptions },
|
|
42
|
-
)
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
Changes:
|
|
46
|
-
- `testInfo` removed entirely (not needed by PageObject).
|
|
47
|
-
- `pocName` replaced by `options.label`. Defaults to `this.constructor.name`, so it can usually be omitted unless the v1 pocName differed from the class name.
|
|
48
|
-
- `PlaywrightReportLogger` removed from constructor; use the `log` fixture from `pomwright` in tests instead.
|
|
49
|
-
- Optional `navOptions` added for configuring `waitUntil` and `waitForLoadState` defaults.
|
|
50
|
-
|
|
51
|
-
### Migration example: concrete page object
|
|
52
|
-
|
|
53
|
-
```ts
|
|
54
|
-
// v1
|
|
55
|
-
import type { Page, TestInfo } from "@playwright/test";
|
|
56
|
-
import { BasePage, type PlaywrightReportLogger } from "pomwright";
|
|
57
|
-
|
|
58
|
-
export default class LoginPage extends BasePage<Paths> {
|
|
59
|
-
constructor(page: Page, testInfo: TestInfo, pwrl: PlaywrightReportLogger) {
|
|
60
|
-
super(page, testInfo, "https://example.com", "/login", "LoginPage", pwrl);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// v2
|
|
65
|
-
import type { Page } from "@playwright/test";
|
|
66
|
-
import { PageObject } from "pomwright";
|
|
67
|
-
|
|
68
|
-
export default class LoginPage extends PageObject<Paths> {
|
|
69
|
-
constructor(page: Page) {
|
|
70
|
-
super(page, "https://example.com", "/login");
|
|
71
|
-
// label defaults to "LoginPage" (class name), so no need to pass it
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
If pocName differed from the class name:
|
|
77
|
-
|
|
78
|
-
```ts
|
|
79
|
-
// v1
|
|
80
|
-
super(page, testInfo, "https://example.com", "/login", "MyCustomLabel", pwrl);
|
|
81
|
-
|
|
82
|
-
// v2
|
|
83
|
-
super(page, "https://example.com", "/login", { label: "MyCustomLabel" });
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
### Migration example: abstract base class
|
|
87
|
-
|
|
88
|
-
v1 abstract base classes typically wrap `baseUrl` and forward the rest:
|
|
89
|
-
|
|
90
|
-
```ts
|
|
91
|
-
// v1
|
|
92
|
-
import type { Page, TestInfo } from "@playwright/test";
|
|
93
|
-
import { BasePage, type PlaywrightReportLogger } from "pomwright";
|
|
94
|
-
|
|
95
|
-
export default abstract class AppBase<Paths extends string> extends BasePage<Paths> {
|
|
96
|
-
constructor(page: Page, testInfo: TestInfo, urlPath: string, pocName: string, pwrl: PlaywrightReportLogger) {
|
|
97
|
-
super(page, testInfo, "http://localhost:8080", urlPath, pocName, pwrl);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// v2
|
|
102
|
-
import type { Page } from "@playwright/test";
|
|
103
|
-
import { type NavigationOptions, PageObject, type UrlPathTypeFromOptions, type UrlTypeOptions } from "pomwright";
|
|
104
|
-
|
|
105
|
-
type BaseOptions<Options extends UrlTypeOptions> = {
|
|
106
|
-
baseUrlType: string;
|
|
107
|
-
urlPathType: UrlPathTypeFromOptions<Options>;
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
export default abstract class AppBase<
|
|
111
|
-
Paths extends string,
|
|
112
|
-
Options extends UrlTypeOptions = { baseUrlType: string; urlPathType: string },
|
|
113
|
-
> extends PageObject<Paths, BaseOptions<Options>> {
|
|
114
|
-
protected constructor(
|
|
115
|
-
page: Page,
|
|
116
|
-
urlPath: UrlPathTypeFromOptions<BaseOptions<Options>>,
|
|
117
|
-
options?: { label?: string; navOptions?: NavigationOptions },
|
|
118
|
-
) {
|
|
119
|
-
super(page, "http://localhost:8080", urlPath, options);
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
Concrete subclass in v2:
|
|
125
|
-
|
|
126
|
-
```ts
|
|
127
|
-
import type { Page } from "@playwright/test";
|
|
128
|
-
import AppBase from "../base/appBase";
|
|
129
|
-
|
|
130
|
-
export default class LoginPage extends AppBase<Paths> {
|
|
131
|
-
constructor(page: Page) {
|
|
132
|
-
super(page, "/login");
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
## Generic type parameters
|
|
138
|
-
|
|
139
|
-
### Simple case (string baseUrl, string urlPath)
|
|
140
|
-
|
|
141
|
-
```ts
|
|
142
|
-
// v1
|
|
143
|
-
class MyPage extends BasePage<Paths> { ... }
|
|
144
|
-
|
|
145
|
-
// v2 (identical shape, just different base class)
|
|
146
|
-
class MyPage extends PageObject<Paths> { ... }
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
### With URL type options (e.g. RegExp urlPath)
|
|
150
|
-
|
|
151
|
-
```ts
|
|
152
|
-
// v1
|
|
153
|
-
import { type BasePageOptions, type ExtractUrlPathType, BasePage } from "pomwright";
|
|
154
|
-
|
|
155
|
-
class ColorPage extends BasePage<
|
|
156
|
-
Paths,
|
|
157
|
-
{ urlOptions: { baseUrlType: string; urlPathType: RegExp } }
|
|
158
|
-
> { ... }
|
|
159
|
-
|
|
160
|
-
// v2 (flattened options shape, renamed types)
|
|
161
|
-
import { type UrlTypeOptions, type UrlPathTypeFromOptions, PageObject } from "pomwright";
|
|
162
|
-
|
|
163
|
-
class ColorPage extends PageObject<
|
|
164
|
-
Paths,
|
|
165
|
-
{ baseUrlType: string; urlPathType: RegExp }
|
|
166
|
-
> { ... }
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
### URL type helper renames
|
|
170
|
-
|
|
171
|
-
| v1 | v2 |
|
|
172
|
-
|---|---|
|
|
173
|
-
| `BasePageOptions` | `UrlTypeOptions` |
|
|
174
|
-
| `ExtractBaseUrlType<T>` | `BaseUrlTypeFromOptions<T>` |
|
|
175
|
-
| `ExtractUrlPathType<T>` | `UrlPathTypeFromOptions<T>` |
|
|
176
|
-
| `ExtractFullUrlType<T>` | `FullUrlTypeFromOptions<T>` |
|
|
177
|
-
|
|
178
|
-
The options shape itself flattened:
|
|
179
|
-
|
|
180
|
-
```ts
|
|
181
|
-
// v1
|
|
182
|
-
{ urlOptions: { baseUrlType: string; urlPathType: RegExp } }
|
|
183
|
-
|
|
184
|
-
// v2
|
|
185
|
-
{ baseUrlType: string; urlPathType: RegExp }
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
## Required abstract methods
|
|
189
|
-
|
|
190
|
-
v2 `PageObject` requires two abstract methods:
|
|
191
|
-
|
|
192
|
-
### defineLocators()
|
|
193
|
-
|
|
194
|
-
Same as bridge. See [locator-registration.md](locator-registration.md) for the full DSL mapping.
|
|
195
|
-
|
|
196
|
-
```ts
|
|
197
|
-
protected defineLocators(): void {
|
|
198
|
-
this.add("main").locator("main");
|
|
199
|
-
this.add("main.button@login").getByRole("button", { name: "Login" });
|
|
200
|
-
}
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
### pageActionsToPerformAfterNavigation()
|
|
204
|
-
|
|
205
|
-
New in v2. Returns an array of async callbacks to run after `navigation.gotoThisPage()`, or `null` if none are needed.
|
|
206
|
-
|
|
207
|
-
```ts
|
|
208
|
-
// No post-navigation actions
|
|
209
|
-
protected pageActionsToPerformAfterNavigation(): (() => Promise<void>)[] | null {
|
|
210
|
-
return null;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// With post-navigation actions (e.g., wait for loading state)
|
|
214
|
-
protected pageActionsToPerformAfterNavigation(): (() => Promise<void>)[] | null {
|
|
215
|
-
return [
|
|
216
|
-
async () => {
|
|
217
|
-
await this.getNestedLocator("main.spinner").waitFor({ state: "hidden" });
|
|
218
|
-
},
|
|
219
|
-
];
|
|
220
|
-
}
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
If the v1 page object had post-navigation logic scattered in tests (e.g., waiting for elements after `page.goto`), consider moving it here.
|
|
224
|
-
|
|
225
|
-
## Properties available on PageObject
|
|
226
|
-
|
|
227
|
-
| Property | Description |
|
|
228
|
-
|---|---|
|
|
229
|
-
| `this.page` | Playwright `Page` instance |
|
|
230
|
-
| `this.baseUrl` | Base URL (string or RegExp) |
|
|
231
|
-
| `this.urlPath` | URL path (string or RegExp) |
|
|
232
|
-
| `this.fullUrl` | Composed full URL |
|
|
233
|
-
| `this.label` | PageObject identifier (defaults to class name) |
|
|
234
|
-
| `this.sessionStorage` | SessionStorage helper |
|
|
235
|
-
| `this.navigation` | Navigation helper (type depends on URL types) |
|
|
236
|
-
| `this.locatorRegistry` | Internal LocatorRegistry (for sharing with external `defineLocators` functions) |
|
|
237
|
-
| `this.add` | Locator registration accessor |
|
|
238
|
-
| `this.getLocator` | Terminal locator accessor |
|
|
239
|
-
| `this.getNestedLocator` | Chained locator accessor |
|
|
240
|
-
| `this.getLocatorSchema` | Schema query builder accessor |
|
|
241
|
-
|
|
242
|
-
## Removed v1 properties
|
|
243
|
-
|
|
244
|
-
| v1 property | v2 replacement |
|
|
245
|
-
|---|---|
|
|
246
|
-
| `this.testInfo` | Not available; use test fixture if needed |
|
|
247
|
-
| `this.log` | Not built-in; use `log` fixture from `pomwright` |
|
|
248
|
-
| `this.pocName` | `this.label` |
|
|
249
|
-
| `this.selector` | Not available; locator strategies are in the registry |
|
|
250
|
-
| `this.locators` (GetLocatorBase) | `this.locatorRegistry` / `this.add` / `this.getLocator` etc. |
|
|
251
|
-
|
|
252
|
-
## Import changes
|
|
253
|
-
|
|
254
|
-
```ts
|
|
255
|
-
// v1
|
|
256
|
-
import { BasePage, GetByMethod, type BasePageOptions, type ExtractUrlPathType,
|
|
257
|
-
type ExtractBaseUrlType, type ExtractFullUrlType, type GetLocatorBase,
|
|
258
|
-
type PlaywrightReportLogger, type LocatorSchemaWithoutPath } from "pomwright";
|
|
259
|
-
|
|
260
|
-
// v2
|
|
261
|
-
import { PageObject, type UrlTypeOptions, type UrlPathTypeFromOptions,
|
|
262
|
-
type BaseUrlTypeFromOptions, type FullUrlTypeFromOptions,
|
|
263
|
-
type LocatorRegistry, type NavigationOptions } from "pomwright";
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
Remove imports that no longer exist: `GetByMethod`, `BasePage`, `BasePageV1toV2`, `BasePageOptions`, `ExtractUrlPathType`, `ExtractBaseUrlType`, `ExtractFullUrlType`, `GetLocatorBase`, `LocatorSchemaWithoutPath`.
|
|
@@ -1,423 +0,0 @@
|
|
|
1
|
-
# Fixtures and Helpers: Navigation, SessionStorage, @step, Logging
|
|
2
|
-
|
|
3
|
-
## Fixture migration
|
|
4
|
-
|
|
5
|
-
v1 fixtures inject `page`, `log`, and `testInfo` into PageObject constructors. v2 fixtures only inject `page`.
|
|
6
|
-
|
|
7
|
-
```ts
|
|
8
|
-
// v1
|
|
9
|
-
import TestPage from "./testPage.page";
|
|
10
|
-
import { test as base } from "pomwright";
|
|
11
|
-
|
|
12
|
-
type Fixtures = {
|
|
13
|
-
testPage: TestPage;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const test = base.extend<Fixtures>({
|
|
17
|
-
testPage: async ({ page, log }, use, testInfo) => {
|
|
18
|
-
const testPage = new TestPage(page, testInfo, log);
|
|
19
|
-
await use(testPage);
|
|
20
|
-
},
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
export { test };
|
|
24
|
-
|
|
25
|
-
// v2
|
|
26
|
-
import TestPage from "./testPage.page";
|
|
27
|
-
import { test as base } from "pomwright";
|
|
28
|
-
|
|
29
|
-
type Fixtures = {
|
|
30
|
-
testPage: TestPage;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const test = base.extend<Fixtures>({
|
|
34
|
-
testPage: async ({ page }, use) => {
|
|
35
|
-
const testPage = new TestPage(page);
|
|
36
|
-
await use(testPage);
|
|
37
|
-
},
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
export { test };
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
Key changes:
|
|
44
|
-
- Remove `testInfo` parameter from fixture callback (no longer needed by PageObject).
|
|
45
|
-
- Remove `log` parameter from fixture callback (no longer injected into PageObject).
|
|
46
|
-
- Constructor call simplified to `new TestPage(page)`.
|
|
47
|
-
- The `log` fixture from `pomwright` is still available in tests if needed for test-level logging.
|
|
48
|
-
|
|
49
|
-
## Navigation helper
|
|
50
|
-
|
|
51
|
-
v2 `PageObject` exposes a `navigation` property with URL-based navigation methods. This replaces manual `page.goto()` patterns from v1.
|
|
52
|
-
|
|
53
|
-
### Available when fullUrl is string (baseUrl: string, urlPath: string)
|
|
54
|
-
|
|
55
|
-
```ts
|
|
56
|
-
// Navigate to the PageObject's own URL
|
|
57
|
-
await poc.navigation.gotoThisPage();
|
|
58
|
-
|
|
59
|
-
// Navigate to a different path under the same baseUrl
|
|
60
|
-
await poc.navigation.goto("/other-path");
|
|
61
|
-
|
|
62
|
-
// Assert current page matches this PageObject's URL
|
|
63
|
-
await poc.navigation.expectThisPage();
|
|
64
|
-
|
|
65
|
-
// Assert current page does NOT match this PageObject's URL
|
|
66
|
-
await poc.navigation.expectAnotherPage();
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
### Available when fullUrl is RegExp (baseUrl or urlPath is RegExp)
|
|
70
|
-
|
|
71
|
-
```ts
|
|
72
|
-
// Only assertion methods available (no goto - can't construct URL from RegExp)
|
|
73
|
-
await poc.navigation.expectThisPage();
|
|
74
|
-
await poc.navigation.expectAnotherPage();
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### Navigation options
|
|
78
|
-
|
|
79
|
-
```ts
|
|
80
|
-
// At construction time (defaults for all navigation calls)
|
|
81
|
-
super(page, "https://example.com", "/login", {
|
|
82
|
-
navOptions: {
|
|
83
|
-
waitUntil: "networkidle",
|
|
84
|
-
waitForLoadState: "domcontentloaded",
|
|
85
|
-
},
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
// Per navigation call (overrides defaults)
|
|
89
|
-
await poc.navigation.gotoThisPage({
|
|
90
|
-
waitUntil: "commit",
|
|
91
|
-
waitForLoadState: "load",
|
|
92
|
-
});
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
### Post-navigation actions
|
|
96
|
-
|
|
97
|
-
The `pageActionsToPerformAfterNavigation()` method runs automatically after `navigation.gotoThisPage()` and `navigation.expectThisPage()`:
|
|
98
|
-
|
|
99
|
-
```ts
|
|
100
|
-
protected pageActionsToPerformAfterNavigation(): (() => Promise<void>)[] | null {
|
|
101
|
-
return [
|
|
102
|
-
async () => {
|
|
103
|
-
// Wait for page to be ready
|
|
104
|
-
await this.getNestedLocator("main.spinner").waitFor({ state: "hidden" });
|
|
105
|
-
},
|
|
106
|
-
async () => {
|
|
107
|
-
// Additional setup
|
|
108
|
-
await this.getNestedLocator("main.content").waitFor({ state: "visible" });
|
|
109
|
-
},
|
|
110
|
-
];
|
|
111
|
-
}
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
### Migration from v1 navigation patterns
|
|
115
|
-
|
|
116
|
-
```ts
|
|
117
|
-
// v1 (manual navigation in tests)
|
|
118
|
-
test("login flow", async ({ loginPage }) => {
|
|
119
|
-
await loginPage.page.goto(loginPage.fullUrl);
|
|
120
|
-
await loginPage.page.waitForLoadState("networkidle");
|
|
121
|
-
// ... test logic
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
// v2 (navigation helper)
|
|
125
|
-
test("login flow", async ({ loginPage }) => {
|
|
126
|
-
await loginPage.navigation.gotoThisPage();
|
|
127
|
-
// post-navigation actions run automatically
|
|
128
|
-
// ... test logic
|
|
129
|
-
});
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
## SessionStorage helper
|
|
133
|
-
|
|
134
|
-
Available on PageObject as `this.sessionStorage`. API changed from v1.
|
|
135
|
-
|
|
136
|
-
### Constructor
|
|
137
|
-
|
|
138
|
-
```ts
|
|
139
|
-
// v1: SessionStorage was instantiated internally by BasePage
|
|
140
|
-
// v2: SessionStorage is instantiated internally by PageObject
|
|
141
|
-
// Both expose it as this.sessionStorage
|
|
142
|
-
|
|
143
|
-
// v2 also supports standalone usage:
|
|
144
|
-
import { SessionStorage } from "pomwright";
|
|
145
|
-
const sessionStorage = new SessionStorage(page, { label: "MyLabel" });
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
### Method signatures
|
|
149
|
-
|
|
150
|
-
```ts
|
|
151
|
-
// set: write items to sessionStorage
|
|
152
|
-
await poc.sessionStorage.set({ token: "abc", theme: "dark" });
|
|
153
|
-
await poc.sessionStorage.set({ token: "abc" }, { reload: true }); // reload page after
|
|
154
|
-
await poc.sessionStorage.set({ token: "abc" }, { waitForContext: true }); // wait for page context
|
|
155
|
-
|
|
156
|
-
// setOnNextNavigation: queue items to be written on next page navigation
|
|
157
|
-
await poc.sessionStorage.setOnNextNavigation({ token: "abc" });
|
|
158
|
-
|
|
159
|
-
// get: read items from sessionStorage
|
|
160
|
-
const all = await poc.sessionStorage.get(); // all items
|
|
161
|
-
const specific = await poc.sessionStorage.get(["token", "theme"]); // specific keys
|
|
162
|
-
const waited = await poc.sessionStorage.get(["token"], { waitForContext: true });
|
|
163
|
-
|
|
164
|
-
// clear: remove items from sessionStorage
|
|
165
|
-
await poc.sessionStorage.clear(); // all items
|
|
166
|
-
await poc.sessionStorage.clear("token"); // specific key
|
|
167
|
-
await poc.sessionStorage.clear(["token", "theme"]); // specific keys
|
|
168
|
-
await poc.sessionStorage.clear("token", { waitForContext: true }); // with wait
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
### v1 -> v2 signature changes
|
|
172
|
-
|
|
173
|
-
| v1 | v2 |
|
|
174
|
-
|---|---|
|
|
175
|
-
| `.set(states, reload: boolean)` | `.set(states, { reload?: boolean; waitForContext?: boolean })` |
|
|
176
|
-
| `.get()` | `.get(keys?, { waitForContext?: boolean })` |
|
|
177
|
-
| `.clear()` | `.clear(keys?, { waitForContext?: boolean })` |
|
|
178
|
-
|
|
179
|
-
## @step decorator
|
|
180
|
-
|
|
181
|
-
v2 provides a `@step` decorator that wraps methods in `test.step()` for Playwright reporting.
|
|
182
|
-
|
|
183
|
-
```ts
|
|
184
|
-
import { step } from "pomwright";
|
|
185
|
-
|
|
186
|
-
class LoginPage extends PageObject<Paths> {
|
|
187
|
-
// Step title defaults to "LoginPage.login"
|
|
188
|
-
@step
|
|
189
|
-
async login(username: string, password: string) {
|
|
190
|
-
await this.getNestedLocator("main.form.input@username").fill(username);
|
|
191
|
-
await this.getNestedLocator("main.form.input@password").fill(password);
|
|
192
|
-
await this.getNestedLocator("main.form.button@submit").click();
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Custom step title
|
|
196
|
-
@step("Perform logout")
|
|
197
|
-
async logout() {
|
|
198
|
-
await this.getNestedLocator("topMenu.button@logout").click();
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// With Playwright step options
|
|
202
|
-
@step({ box: true })
|
|
203
|
-
async waitForDashboard() {
|
|
204
|
-
await this.getNestedLocator("main.dashboard").waitFor({ state: "visible" });
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Custom title + options
|
|
208
|
-
@step("Submit login form", { box: true, timeout: 5000 })
|
|
209
|
-
async submitForm() {
|
|
210
|
-
await this.getNestedLocator("main.form.button@submit").click();
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
### Decorator forms
|
|
216
|
-
|
|
217
|
-
| Form | Description |
|
|
218
|
-
|---|---|
|
|
219
|
-
| `@step` | No parentheses; title defaults to `ClassName.methodName` |
|
|
220
|
-
| `@step()` | Empty call; same as `@step` |
|
|
221
|
-
| `@step("title")` | Custom step title |
|
|
222
|
-
| `@step({ box: true })` | Playwright step options |
|
|
223
|
-
| `@step("title", { box: true })` | Custom title + options |
|
|
224
|
-
|
|
225
|
-
### Migration from v1 manual test.step patterns
|
|
226
|
-
|
|
227
|
-
```ts
|
|
228
|
-
// v1 (manual step wrapping in the POC)
|
|
229
|
-
async login(username: string) {
|
|
230
|
-
await test.step("LoginPage.login", async () => {
|
|
231
|
-
const input = await this.getNestedLocator("main.form.input@username");
|
|
232
|
-
await input.fill(username);
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// v2 (decorator)
|
|
237
|
-
@step()
|
|
238
|
-
async login(username: string) {
|
|
239
|
-
await this.getNestedLocator("main.form.input@username").fill(username);
|
|
240
|
-
}
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
## Logging
|
|
244
|
-
|
|
245
|
-
### v1: Logger injected into constructor
|
|
246
|
-
|
|
247
|
-
```ts
|
|
248
|
-
// v1
|
|
249
|
-
class LoginPage extends BasePage<Paths> {
|
|
250
|
-
constructor(page: Page, testInfo: TestInfo, pwrl: PlaywrightReportLogger) {
|
|
251
|
-
super(page, testInfo, "https://example.com", "/login", "LoginPage", pwrl);
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
async doSomething() {
|
|
255
|
-
this.log.info("Doing something"); // this.log available on BasePage
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// v1 fixture
|
|
260
|
-
const test = base.extend<Fixtures>({
|
|
261
|
-
loginPage: async ({ page, log }, use, testInfo) => {
|
|
262
|
-
await use(new LoginPage(page, testInfo, log));
|
|
263
|
-
},
|
|
264
|
-
});
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
### v2: Logger decoupled from PageObject
|
|
268
|
-
|
|
269
|
-
PageObject does not have a built-in `this.log`. If logging is needed:
|
|
270
|
-
|
|
271
|
-
**Option A: Use the `log` fixture in tests (recommended)**
|
|
272
|
-
|
|
273
|
-
```ts
|
|
274
|
-
import { test } from "pomwright";
|
|
275
|
-
|
|
276
|
-
test("login flow", async ({ page, log }) => {
|
|
277
|
-
log.info("Starting login flow");
|
|
278
|
-
const loginPage = new LoginPage(page);
|
|
279
|
-
// ...
|
|
280
|
-
});
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
**Option B: Pass logger to PageObject manually**
|
|
284
|
-
|
|
285
|
-
```ts
|
|
286
|
-
import { type PlaywrightReportLogger, PageObject } from "pomwright";
|
|
287
|
-
|
|
288
|
-
class LoginPage extends PageObject<Paths> {
|
|
289
|
-
constructor(page: Page, private log: PlaywrightReportLogger) {
|
|
290
|
-
super(page, "https://example.com", "/login");
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
@step()
|
|
294
|
-
async doSomething() {
|
|
295
|
-
this.log.info("Doing something");
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// Fixture
|
|
300
|
-
const test = base.extend<Fixtures>({
|
|
301
|
-
loginPage: async ({ page, log }, use) => {
|
|
302
|
-
await use(new LoginPage(page, log));
|
|
303
|
-
},
|
|
304
|
-
});
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
**Option C: Create a child logger in the fixture**
|
|
308
|
-
|
|
309
|
-
```ts
|
|
310
|
-
const test = base.extend<Fixtures>({
|
|
311
|
-
loginPage: async ({ page, log }, use) => {
|
|
312
|
-
const childLog = log.getNewChildLogger("LoginPage");
|
|
313
|
-
await use(new LoginPage(page, childLog));
|
|
314
|
-
},
|
|
315
|
-
});
|
|
316
|
-
```
|
|
317
|
-
|
|
318
|
-
## Complete migration example
|
|
319
|
-
|
|
320
|
-
### v1 (before)
|
|
321
|
-
|
|
322
|
-
```ts
|
|
323
|
-
// loginPage.page.ts
|
|
324
|
-
import type { Page, TestInfo } from "@playwright/test";
|
|
325
|
-
import { BasePage, GetByMethod, type PlaywrightReportLogger } from "pomwright";
|
|
326
|
-
|
|
327
|
-
type Paths = "main" | "main.form" | "main.form.input@username" | "main.form.button@submit";
|
|
328
|
-
|
|
329
|
-
export default class LoginPage extends BasePage<Paths> {
|
|
330
|
-
constructor(page: Page, testInfo: TestInfo, pwrl: PlaywrightReportLogger) {
|
|
331
|
-
super(page, testInfo, "https://example.com", "/login", "LoginPage", pwrl);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
protected initLocatorSchemas() {
|
|
335
|
-
this.locators.addSchema("main", { locator: "main", locatorMethod: GetByMethod.locator });
|
|
336
|
-
this.locators.addSchema("main.form", { locator: "form.login", locatorMethod: GetByMethod.locator });
|
|
337
|
-
this.locators.addSchema("main.form.input@username", { label: "Username", locatorMethod: GetByMethod.label });
|
|
338
|
-
this.locators.addSchema("main.form.button@submit", {
|
|
339
|
-
role: "button", roleOptions: { name: "Submit" }, locatorMethod: GetByMethod.role,
|
|
340
|
-
});
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
async login(username: string) {
|
|
344
|
-
const input = await this.getNestedLocator("main.form.input@username");
|
|
345
|
-
await input.fill(username);
|
|
346
|
-
const btn = await this.getNestedLocator("main.form.button@submit");
|
|
347
|
-
await btn.click();
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
// fixture
|
|
352
|
-
const test = base.extend<Fixtures>({
|
|
353
|
-
loginPage: async ({ page, log }, use, testInfo) => {
|
|
354
|
-
await use(new LoginPage(page, testInfo, log));
|
|
355
|
-
},
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
// test
|
|
359
|
-
test("login", async ({ loginPage }) => {
|
|
360
|
-
await loginPage.page.goto(loginPage.fullUrl);
|
|
361
|
-
await loginPage.login("admin");
|
|
362
|
-
});
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
### v2 (after)
|
|
366
|
-
|
|
367
|
-
```ts
|
|
368
|
-
// loginPage.locatorSchema.ts
|
|
369
|
-
import type { LocatorRegistry } from "pomwright";
|
|
370
|
-
|
|
371
|
-
export type Paths = "main" | "main.form" | "main.form.input@username" | "main.form.button@submit";
|
|
372
|
-
|
|
373
|
-
export function defineLocators(registry: LocatorRegistry<Paths>) {
|
|
374
|
-
registry.add("main").locator("main");
|
|
375
|
-
registry.add("main.form").locator("form.login");
|
|
376
|
-
registry.add("main.form.input@username").getByLabel("Username");
|
|
377
|
-
registry.add("main.form.button@submit").getByRole("button", { name: "Submit" });
|
|
378
|
-
}
|
|
379
|
-
```
|
|
380
|
-
|
|
381
|
-
```ts
|
|
382
|
-
// loginPage.page.ts
|
|
383
|
-
import type { Page } from "@playwright/test";
|
|
384
|
-
import { PageObject, step } from "pomwright";
|
|
385
|
-
import { defineLocators, type Paths } from "./loginPage.locatorSchema";
|
|
386
|
-
|
|
387
|
-
export default class LoginPage extends PageObject<Paths> {
|
|
388
|
-
constructor(page: Page) {
|
|
389
|
-
super(page, "https://example.com", "/login");
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
protected defineLocators(): void {
|
|
393
|
-
defineLocators(this.locatorRegistry);
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
protected pageActionsToPerformAfterNavigation(): (() => Promise<void>)[] | null {
|
|
397
|
-
return null;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
@step()
|
|
401
|
-
async login(username: string) {
|
|
402
|
-
await this.getNestedLocator("main.form.input@username").fill(username);
|
|
403
|
-
await this.getNestedLocator("main.form.button@submit").click();
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
```
|
|
407
|
-
|
|
408
|
-
```ts
|
|
409
|
-
// fixture
|
|
410
|
-
const test = base.extend<Fixtures>({
|
|
411
|
-
loginPage: async ({ page }, use) => {
|
|
412
|
-
await use(new LoginPage(page));
|
|
413
|
-
},
|
|
414
|
-
});
|
|
415
|
-
```
|
|
416
|
-
|
|
417
|
-
```ts
|
|
418
|
-
// test
|
|
419
|
-
test("login", async ({ loginPage }) => {
|
|
420
|
-
await loginPage.navigation.gotoThisPage();
|
|
421
|
-
await loginPage.login("admin");
|
|
422
|
-
});
|
|
423
|
-
```
|