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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pomwright",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "POMWright is a complementary test framework for Playwright written in TypeScript.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -17,6 +17,12 @@
|
|
|
17
17
|
"main": "dist/index.js",
|
|
18
18
|
"module": "dist/index.mjs",
|
|
19
19
|
"types": "dist/index.d.ts",
|
|
20
|
+
"files": [
|
|
21
|
+
"dist/**",
|
|
22
|
+
"README.md",
|
|
23
|
+
"LICENSE",
|
|
24
|
+
"CHANGELOG.md"
|
|
25
|
+
],
|
|
20
26
|
"keywords": [
|
|
21
27
|
"Playwright",
|
|
22
28
|
"POM",
|
|
@@ -43,8 +49,7 @@
|
|
|
43
49
|
"@changesets/cli": "^2.29.7",
|
|
44
50
|
"@types/node": "^24.5.2",
|
|
45
51
|
"tsup": "^8.5.0",
|
|
46
|
-
"typescript": "^5.9.2"
|
|
47
|
-
"vitest": "^3.2.4"
|
|
52
|
+
"typescript": "^5.9.2"
|
|
48
53
|
},
|
|
49
54
|
"peerDependencies": {
|
|
50
55
|
"@playwright/test": ">=1.57.0 <2.0.0"
|
|
@@ -53,15 +58,8 @@
|
|
|
53
58
|
"build": "tsup index.ts --format cjs,esm --dts",
|
|
54
59
|
"release": "pnpm run build && changeset publish",
|
|
55
60
|
"lint": "biome check ./src",
|
|
56
|
-
"lint:v2": "biome check ./srcV2",
|
|
57
|
-
"lint:all": "pnpm lint && pnpm lint:v2",
|
|
58
61
|
"format": "biome format ./src --write",
|
|
59
|
-
"format:v2": "biome format ./srcV2 --write",
|
|
60
|
-
"format:all": "pnpm format && pnpm format:v2",
|
|
61
62
|
"pack-test": "bash pack-test.sh",
|
|
62
|
-
"
|
|
63
|
-
"test": "vitest run && bash pack-test.sh",
|
|
64
|
-
"test:v2": "bash pack-test-v2.sh",
|
|
65
|
-
"test:all": "pnpm test && pnpm test:v2"
|
|
63
|
+
"test": "pnpm pack-test"
|
|
66
64
|
}
|
|
67
65
|
}
|
package/AGENTS.md
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# Agent Guidelines for POMWright
|
|
2
|
-
|
|
3
|
-
This repository carries both the **v1** codepath (`src`, `intTest`) and the ongoing **v2** refactor (`srcV2`, `intTestV2`). Use the guidance below whenever you modify files in this repo.
|
|
4
|
-
|
|
5
|
-
## Development priorities
|
|
6
|
-
|
|
7
|
-
- Focus exclusively on the v2 codepath for implementation work. Do **not** change v1 (`src`, `intTest`); it is retained only for side-by-side comparison and regression awareness.
|
|
8
|
-
- Preserve the fluent locator registry API introduced in v2 (via `LocatorRegistry`, `bindLocatorAccessors`, and the thenable builders) and keep examples/tests aligned with it.
|
|
9
|
-
- Design v2 around the Page Object Model pattern expressed through functional programming: features should be as independent and composable as possible so users can adopt only what they need.
|
|
10
|
-
- Write documentation under `docsV2/` for v2 work while consulting v1 docs in `docs/` for context and completeness.
|
|
11
|
-
- Always consult the drift tracker at `docsV2/v1-v2-drift.md` before making changes, and update it immediately when you spot new breaking changes, regressions, or bug fixes.
|
|
12
|
-
- Prefer migration helpers/shims over strict runtime compatibility with v1 APIs; clear improvements in functionality and syntax take priority over backwards compatibility.
|
|
13
|
-
|
|
14
|
-
## Coding style
|
|
15
|
-
|
|
16
|
-
- Follow the `.editorconfig` (tabs, size 2) and existing TypeScript conventions in the project.
|
|
17
|
-
- Use pnpm for scripts and dependency management.
|
|
18
|
-
- Prefer descriptive naming for locator paths and avoid anonymous segments; the registry validates dot-delimited paths with no leading/trailing dots.
|
|
19
|
-
- Avoid `//biome-ignore` where possible by addressing the underlying lint/type issue; when necessary, include a short justification with the directive.
|
|
20
|
-
- Do not wrap imports in `try/catch` blocks.
|
|
21
|
-
|
|
22
|
-
## Testing and quality
|
|
23
|
-
|
|
24
|
-
- Always run and fix `pnpm pack-test:v2` and `pnpm lint:v2` before committing. Use `lint:v2` to avoid unrelated v1 noise.
|
|
25
|
-
- If you add or change locators or behavioural contracts, expand integration coverage in `intTestV2`; err on the side of more tests while keeping existing cases intact.
|
|
26
|
-
- Existing tests in `intTestV2` should be maintained and keep their scope/cases. If a change would require large edits to a case, prefer adding new tests instead.
|
|
27
|
-
|
|
28
|
-
## Commit and PR expectations
|
|
29
|
-
|
|
30
|
-
- Keep commits scoped and descriptive; call out the v2 focus explicitly.
|
|
31
|
-
- Keep docs and examples showing both v1 and v2 patterns where relevant to ease migration, but prioritize clear improvements in functionality and syntax over backwards compatibility.
|
|
32
|
-
|
|
33
|
-
## Notes on locator work
|
|
34
|
-
|
|
35
|
-
- Use v2’s builder DSL (`locators.add('path').getByRole(...)`, filters/index steps, frame handling) for new schemas and tests.
|
|
36
|
-
- Prefer migration helpers/shims over trying to keep strict runtime compatibility with v1 APIs.
|
|
37
|
-
- Document any intentionally breaking changes and provide migration tips where possible—capture them in `docsV2/v1-v2-drift.md` as they arise.
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
# BaseApi
|
|
2
|
-
|
|
3
|
-
The `BaseApi` class offers a minimal foundation for writing API helpers in POMWright. It wraps Playwright's `APIRequestContext` and integrates with the shared [`PlaywrightReportLogger`](./PlaywrightReportLogger-explanation.md).
|
|
4
|
-
|
|
5
|
-
## Constructor
|
|
6
|
-
|
|
7
|
-
```ts
|
|
8
|
-
constructor(baseUrl: string, apiName: string, context: APIRequestContext, pwrl: PlaywrightReportLogger)
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
* `baseUrl` – root address used to build request URLs.
|
|
12
|
-
* `apiName` – human‑readable identifier; also used as a logging prefix.
|
|
13
|
-
* `context` – Playwright [`APIRequestContext`](https://playwright.dev/docs/api/class-apirequestcontext).
|
|
14
|
-
* `pwrl` – logger instance supplied by the test fixtures. A child logger is created for the API class.
|
|
15
|
-
|
|
16
|
-
The class stores these values on the instance and exposes the `request` and `log` properties for use in subclasses.
|
|
17
|
-
|
|
18
|
-
## Example
|
|
19
|
-
|
|
20
|
-
```ts
|
|
21
|
-
import type { APIRequestContext } from "@playwright/test";
|
|
22
|
-
import { BaseApi, type PlaywrightReportLogger } from "pomwright";
|
|
23
|
-
|
|
24
|
-
class MyApi extends BaseApi {
|
|
25
|
-
constructor(baseUrl: string, context: APIRequestContext, pwrl: PlaywrightReportLogger) {
|
|
26
|
-
super(baseUrl, MyApi.name, context, pwrl);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
v1 = {
|
|
30
|
-
users: {
|
|
31
|
-
id: {
|
|
32
|
-
get: async (id: string, status: number = 200) => {
|
|
33
|
-
const response = await this.request.get(`/api/users/${id}`);
|
|
34
|
-
expect(response.status(), "should have status code: 200").toBe(status);
|
|
35
|
-
const body = await response.json();
|
|
36
|
-
return body;
|
|
37
|
-
},
|
|
38
|
-
//...etc.
|
|
39
|
-
}
|
|
40
|
-
},
|
|
41
|
-
products: {
|
|
42
|
-
//...etc.
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
Then in a test you'd do:
|
|
49
|
-
|
|
50
|
-
```ts
|
|
51
|
-
test("some test", { tag: ["@api", "@user"] }, async ({ myApi }) => {
|
|
52
|
-
// Create user
|
|
53
|
-
|
|
54
|
-
const existingUser = await myApi.v1.users.id.get(userId); // 200 (default) user found
|
|
55
|
-
|
|
56
|
-
// Delete user
|
|
57
|
-
|
|
58
|
-
const deletedUser = await myApi.v1.users.id.get(userId, 204); // 204 user not found successfully
|
|
59
|
-
|
|
60
|
-
})
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
`BaseApi` does not dictate how requests are made; you are free to add domain‑specific methods using the provided `request` context and logger.
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
# BasePage
|
|
2
|
-
|
|
3
|
-
`BasePage` is the foundation for all Page Object Classes (POCs) in POMWright. It provides common plumbing for Playwright pages, logging, locator management and session storage.
|
|
4
|
-
|
|
5
|
-
## Generics
|
|
6
|
-
|
|
7
|
-
```ts
|
|
8
|
-
BasePage<LocatorSchemaPathType, Options = { urlOptions: { baseUrlType: string; urlPathType: string } }, LocatorSubstring>
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
* **LocatorSchemaPathType** – union of valid locator paths for the POC.
|
|
12
|
-
* **Options** – optional configuration that allows the `baseUrl` or `urlPath` to be typed as a `RegExp` when dynamic values are required for mapping certain resource paths. See [intTest/page-object-models/testApp/with-options/base/baseWithOptions.page.ts](../intTest/page-object-models/testApp/with-options/base/baseWithOptions.page.ts)
|
|
13
|
-
* **LocatorSubstring** – internal type used when working with sub paths; normally left undefined.
|
|
14
|
-
|
|
15
|
-
## Constructor
|
|
16
|
-
|
|
17
|
-
```ts
|
|
18
|
-
constructor(
|
|
19
|
-
page: Page,
|
|
20
|
-
testInfo: TestInfo,
|
|
21
|
-
baseUrl: ExtractBaseUrlType<Options>,
|
|
22
|
-
urlPath: ExtractUrlPathType<Options>,
|
|
23
|
-
pocName: string,
|
|
24
|
-
pwrl: PlaywrightReportLogger
|
|
25
|
-
)
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
The base class stores these values and derives `fullUrl`. A child [`PlaywrightReportLogger`](./PlaywrightReportLogger-explanation.md) is created for the POC and a [`SessionStorage`](./sessionStorage-methods-explanation.md) helper is exposed as `sessionStorage`.
|
|
29
|
-
|
|
30
|
-
## Required implementation
|
|
31
|
-
|
|
32
|
-
Every POC extending `BasePage` must:
|
|
33
|
-
|
|
34
|
-
1. Define a `LocatorSchemaPath` union describing available locators.
|
|
35
|
-
2. Implement the `initLocatorSchemas()` method which adds schemas to the internal `GetLocatorBase` instance.
|
|
36
|
-
|
|
37
|
-
```ts
|
|
38
|
-
// login.page.ts
|
|
39
|
-
import type { Page, TestInfo } from "@playwright/test";
|
|
40
|
-
import { BasePage, type PlaywrightReportLogger } from "pomwright";
|
|
41
|
-
import { test } from "@fixtures/all.fixtures";
|
|
42
|
-
import { type LocatorSchemaPath, initLocatorSchemas } from "./login.locatorSchema";
|
|
43
|
-
|
|
44
|
-
export default class Login extends BasePage<LocatorSchemaPath> {
|
|
45
|
-
constructor(page: Page, testInfo: TestInfo, pwrl: PlaywrightReportLogger) {
|
|
46
|
-
super(page, testInfo, "https://example.com", "/login", Login.name, pwrl);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
protected initLocatorSchemas() {
|
|
50
|
-
initLocatorSchemas(this.locators);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
public async login(user: string, pass: string) {
|
|
54
|
-
await test.step(`${this.pocName}: Fill login form and login`, async () => {
|
|
55
|
-
|
|
56
|
-
await test.step(`${this.pocName}: Fill username field`, async () => {
|
|
57
|
-
const locator = await this.getNestedLocator("main.section@login.form.textbox@username");
|
|
58
|
-
await locator.fill(user);
|
|
59
|
-
await locator.blur();
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
await test.step(`${this.pocName}: Fill password field`, async () => {
|
|
63
|
-
const locator = await this.getNestedLocator("main.section@login.form.textbox@password");
|
|
64
|
-
await locator.fill(pass);
|
|
65
|
-
await locator.blur();
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
await test.step(`${this.pocName}: Click login button`, async () => {
|
|
69
|
-
const locator = await this.getNestedLocator("main.section@login.button@login");
|
|
70
|
-
await locator.click();
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* We probably don't want to await navigation here, instead we do it in the test,
|
|
75
|
-
* using the POC representing the page we are navigated to (e.g. /profile)
|
|
76
|
-
*/
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
## Helper methods
|
|
83
|
-
|
|
84
|
-
`BasePage` exposes a small API built around `LocatorSchema` definitions:
|
|
85
|
-
|
|
86
|
-
* `getLocatorSchema(path)` – returns a chainable object; see [locator methods](./get-locator-methods-explanation.md).
|
|
87
|
-
* `getLocator(path)` – wrapper method for `getLocatorSchema(path).getLocator();` - resolves the locator for the final segment of a path, e.g. the Locator created from the LocatorSchema the full LocatorSchemaPath references.
|
|
88
|
-
* `getNestedLocator(path, indices?)` – wrapper method for `getLocatorSchema(path).getNestedLocator();` - automatically chains locators along the path and optionally selects nth occurrences.
|
|
89
|
-
|
|
90
|
-
All locators are strongly typed, giving compile‑time suggestions and preventing typos in `LocatorSchemaPath` strings.
|
|
91
|
-
|
|
92
|
-
## Recommendation
|
|
93
|
-
|
|
94
|
-
Instead of extending each of your POC's with BasePage from POMWright, opt instead to create an abstract BasePage class for your domain and have it extend BasePage from POMWright. Then have each of your POC's extend your abstract POC instead. This allows us to easily sentralize common helper methods, LocatorSchema, etc. and reuse it across all our POC's for the given domain.
|
|
95
|
-
|
|
96
|
-
For examples see [intTest/page-object-models/testApp](../intTest/page-object-models/testApp)
|
|
@@ -1,271 +0,0 @@
|
|
|
1
|
-
# LocatorSchema
|
|
2
|
-
|
|
3
|
-
A `LocatorSchema` describes how to find an element in the DOM. Instead of storing Playwright `Locator` objects directly, POMWright keeps these schema definitions and resolves them only when a test requests the locator. The same schema can be reused across multiple page objects and test files without risking stale references.
|
|
4
|
-
|
|
5
|
-
Every schema must set `locatorMethod` to one of the [`GetByMethod`](../src/helpers/locatorSchema.interface.ts) enum values. The matching selector fields listed below determine which Playwright API call is executed under the hood. You can provide more than one selector field, but the method chosen by `locatorMethod` is the one that will be used when the locator is created.
|
|
6
|
-
|
|
7
|
-
When you register a schema with `GetLocatorBase.addSchema(path, schemaDetails)` the `locatorSchemaPath` is stored automatically and becomes part of the structured logs produced by POMWright.
|
|
8
|
-
|
|
9
|
-
## Selector strategies
|
|
10
|
-
|
|
11
|
-
> Note: You can define both "role" and "locator" on the same LocatorSchema, but which one is used is dictated by "locatorMethod". Too keep things clean I recommend only defining the actual type of Playwright Locator you'll be using.
|
|
12
|
-
|
|
13
|
-
Each selector strategy maps directly to a Playwright locator helper. The snippets below show the POMWright configuration alongside the equivalent raw Playwright call.
|
|
14
|
-
|
|
15
|
-
### `role` and `roleOptions`
|
|
16
|
-
|
|
17
|
-
Use [ARIA roles](https://playwright.dev/docs/api/class-page#page-get-by-role) as your primary selector. The optional `roleOptions` map to the options object accepted by `page.getByRole()`.
|
|
18
|
-
|
|
19
|
-
```ts
|
|
20
|
-
locators.addSchema("content.button@edit", {
|
|
21
|
-
role: "button",
|
|
22
|
-
roleOptions: { name: "Edit" },
|
|
23
|
-
locatorMethod: GetByMethod.role
|
|
24
|
-
});
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
Playwright equivalent:
|
|
28
|
-
|
|
29
|
-
```ts
|
|
30
|
-
page.getByRole("button", { name: "Edit" });
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
### `text` and `textOptions`
|
|
34
|
-
|
|
35
|
-
Match visible text using the same semantics as [`page.getByText()`](https://playwright.dev/docs/api/class-page#page-get-by-text).
|
|
36
|
-
|
|
37
|
-
```ts
|
|
38
|
-
locators.addSchema("content.link.help", {
|
|
39
|
-
text: "Need help?",
|
|
40
|
-
textOptions: { exact: true },
|
|
41
|
-
locatorMethod: GetByMethod.text
|
|
42
|
-
});
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
Playwright equivalent:
|
|
46
|
-
|
|
47
|
-
```ts
|
|
48
|
-
page.getByText("Need help?", { exact: true });
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### `label` and `labelOptions`
|
|
52
|
-
|
|
53
|
-
Reference form controls by their accessible label. `labelOptions` accepts the same arguments as [`page.getByLabel()`](https://playwright.dev/docs/api/class-page#page-get-by-label).
|
|
54
|
-
|
|
55
|
-
```ts
|
|
56
|
-
locators.addSchema("content.form.password", {
|
|
57
|
-
label: "Password",
|
|
58
|
-
locatorMethod: GetByMethod.label
|
|
59
|
-
});
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
Playwright equivalent:
|
|
63
|
-
|
|
64
|
-
```ts
|
|
65
|
-
page.getByLabel("Password");
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
### `placeholder` and `placeholderOptions`
|
|
69
|
-
|
|
70
|
-
Target elements by placeholder text via [`page.getByPlaceholder()`](https://playwright.dev/docs/api/class-page#page-get-by-placeholder).
|
|
71
|
-
|
|
72
|
-
```ts
|
|
73
|
-
locators.addSchema("content.form.search", {
|
|
74
|
-
placeholder: "Search",
|
|
75
|
-
placeholderOptions: { exact: true },
|
|
76
|
-
locatorMethod: GetByMethod.placeholder
|
|
77
|
-
});
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
Playwright equivalent:
|
|
81
|
-
|
|
82
|
-
```ts
|
|
83
|
-
page.getByPlaceholder("Search", { exact: true });
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
### `altText` and `altTextOptions`
|
|
87
|
-
|
|
88
|
-
Resolve images and other elements with alternative text via [`page.getByAltText()`](https://playwright.dev/docs/api/class-page#page-get-by-alt-text).
|
|
89
|
-
|
|
90
|
-
```ts
|
|
91
|
-
locators.addSchema("content.hero.image", {
|
|
92
|
-
altText: "Company logo",
|
|
93
|
-
locatorMethod: GetByMethod.altText
|
|
94
|
-
});
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
Playwright equivalent:
|
|
98
|
-
|
|
99
|
-
```ts
|
|
100
|
-
page.getByAltText("Company logo");
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### `title` and `titleOptions`
|
|
104
|
-
|
|
105
|
-
Tap into the [`title` attribute](https://playwright.dev/docs/api/class-page#page-get-by-title) of an element.
|
|
106
|
-
|
|
107
|
-
```ts
|
|
108
|
-
locators.addSchema("content.tooltip.info", {
|
|
109
|
-
title: "More information",
|
|
110
|
-
locatorMethod: GetByMethod.title
|
|
111
|
-
});
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
Playwright equivalent:
|
|
115
|
-
|
|
116
|
-
```ts
|
|
117
|
-
page.getByTitle("More information");
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
### `locator` and `locatorOptions`
|
|
121
|
-
|
|
122
|
-
Fallback to raw selectors or locator chaining with [`page.locator()`](https://playwright.dev/docs/api/class-page#page-locator). `locatorOptions` is passed straight through to the Playwright call.
|
|
123
|
-
|
|
124
|
-
```ts
|
|
125
|
-
locators.addSchema("content.main", {
|
|
126
|
-
locator: "main",
|
|
127
|
-
locatorOptions: { hasText: "Profile" },
|
|
128
|
-
locatorMethod: GetByMethod.locator
|
|
129
|
-
});
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
Playwright equivalent:
|
|
133
|
-
|
|
134
|
-
```ts
|
|
135
|
-
page.locator("main", { hasText: "Profile" });
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
### `frameLocator`
|
|
139
|
-
|
|
140
|
-
Create [`FrameLocator`](https://playwright.dev/docs/api/class-page#page-frame-locator) instances when your DOM interaction starts inside an iframe.
|
|
141
|
-
|
|
142
|
-
```ts
|
|
143
|
-
locators.addSchema("iframe.login", {
|
|
144
|
-
frameLocator: "#auth-frame",
|
|
145
|
-
locatorMethod: GetByMethod.frameLocator
|
|
146
|
-
});
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
Playwright equivalent:
|
|
150
|
-
|
|
151
|
-
```ts
|
|
152
|
-
page.frameLocator("#auth-frame");
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### `testId`
|
|
156
|
-
|
|
157
|
-
Call [`page.getByTestId()`](https://playwright.dev/docs/api/class-page#page-get-by-test-id) by setting the `testId` field.
|
|
158
|
-
|
|
159
|
-
```ts
|
|
160
|
-
locators.addSchema("content.button.reset", {
|
|
161
|
-
testId: "reset-btn",
|
|
162
|
-
locatorMethod: GetByMethod.testId
|
|
163
|
-
});
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
Playwright equivalent:
|
|
167
|
-
|
|
168
|
-
```ts
|
|
169
|
-
page.getByTestId("reset-btn");
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
### `dataCy`
|
|
173
|
-
|
|
174
|
-
`dataCy` is a POMWright convenience for the legacy `data-cy` attribute used in Cypress-based projects. Under the hood it still calls `page.locator()` with the [`data-cy=` selector engine](https://playwright.dev/docs/extensibility#custom-selector-engines) registered by `BasePage`.
|
|
175
|
-
|
|
176
|
-
```ts
|
|
177
|
-
locators.addSchema("content.card.actions", {
|
|
178
|
-
dataCy: "card-actions",
|
|
179
|
-
locatorMethod: GetByMethod.dataCy
|
|
180
|
-
});
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
Playwright equivalent:
|
|
184
|
-
|
|
185
|
-
```ts
|
|
186
|
-
page.locator("data-cy=card-actions");
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
If you prefer to control the selector string yourself you can pass the full expression (`"data-cy=card-actions"`) and it will be used verbatim.
|
|
190
|
-
|
|
191
|
-
### `id`
|
|
192
|
-
|
|
193
|
-
`id` is the second POMWright-specific helper. Provide either the raw id string or a `RegExp`. Strings are normalised to CSS id selectors, so `"details"`, `"#details"`, and `"id=details"` all resolve to the same element. A regular expression matches the start of the `id` attribute, allowing you to capture generated identifiers.
|
|
194
|
-
|
|
195
|
-
```ts
|
|
196
|
-
locators.addSchema("content.region.details", {
|
|
197
|
-
id: "profile-details",
|
|
198
|
-
locatorMethod: GetByMethod.id
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
locators.addSchema("content.region.dynamic", {
|
|
202
|
-
id: /^region-/,
|
|
203
|
-
locatorMethod: GetByMethod.id
|
|
204
|
-
});
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
Playwright equivalent:
|
|
208
|
-
|
|
209
|
-
```ts
|
|
210
|
-
page.locator("#profile-details");
|
|
211
|
-
page.locator('*[id^="region-"]');
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
### `filter`
|
|
215
|
-
|
|
216
|
-
Any schema can define a `filter` object with the same shape as [`locator.filter()`](https://playwright.dev/docs/api/class-locator#locator-filter). Filters are applied immediately after the initial Playwright locator is created.
|
|
217
|
-
|
|
218
|
-
```ts
|
|
219
|
-
locators.addSchema("content.list.item", {
|
|
220
|
-
role: "listitem",
|
|
221
|
-
locatorMethod: GetByMethod.role,
|
|
222
|
-
filter: { hasText: /Primary Colors/i }
|
|
223
|
-
});
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
Playwright equivalent:
|
|
227
|
-
|
|
228
|
-
```ts
|
|
229
|
-
page.getByRole("listitem").filter({ hasText: /Primary Colors/i });
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
## Putting it together
|
|
233
|
-
|
|
234
|
-
Schemas are typically registered inside `initLocatorSchemas()` of a `BasePage` subclass. The example below demonstrates reusable fragments and multiple selector strategies working together.
|
|
235
|
-
|
|
236
|
-
```ts
|
|
237
|
-
import { GetByMethod, type GetLocatorBase, type LocatorSchemaWithoutPath } from "pomwright";
|
|
238
|
-
type LocatorSchemaPath =
|
|
239
|
-
| "main"
|
|
240
|
-
| "main.button"
|
|
241
|
-
| "main.button@continue"
|
|
242
|
-
| "main.button@back";
|
|
243
|
-
|
|
244
|
-
export function initLocatorSchemas(locators: GetLocatorBase<LocatorSchemaPath>) {
|
|
245
|
-
locators.addSchema("main", {
|
|
246
|
-
locator: "main",
|
|
247
|
-
locatorMethod: GetByMethod.locator
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
const button: LocatorSchemaWithoutPath = {
|
|
251
|
-
role: "button",
|
|
252
|
-
locatorMethod: GetByMethod.role
|
|
253
|
-
};
|
|
254
|
-
|
|
255
|
-
locators.addSchema("main.button", {
|
|
256
|
-
...button
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
locators.addSchema("main.button@continue", {
|
|
260
|
-
...button,
|
|
261
|
-
roleOptions: { name: "Continue" }
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
locators.addSchema("main.button@back", {
|
|
265
|
-
...button,
|
|
266
|
-
roleOptions: { name: "Back" }
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
Once registered, the schemas can be consumed through the BasePage helpers documented in [`docs/get-locator-methods-explanation.md`](./get-locator-methods-explanation.md).
|
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
# LocatorSchemaPath
|
|
2
|
-
|
|
3
|
-
A `LocatorSchemaPath` is the unique key that identifies a `LocatorSchema`. Paths let POMWright build nested Playwright locators while keeping the definitions type‑safe and searchable.
|
|
4
|
-
|
|
5
|
-
## Path syntax
|
|
6
|
-
|
|
7
|
-
A `LocatorSchemaPath` is declared as a union of string literals.
|
|
8
|
-
|
|
9
|
-
```ts
|
|
10
|
-
type LocatorSchemaPath = "main";
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
Each word or segment of a string are separated by `.` (dot), where each dot represents a new element in the chain. Thus we end up with paths describing the hierarchy of elements on a page through dot notation.
|
|
14
|
-
|
|
15
|
-
```ts
|
|
16
|
-
type LocatorSchemaPath =
|
|
17
|
-
| "main"
|
|
18
|
-
| "main.heading";
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
In other words, each `.` (dot) separates a new segment in the chain. A LocatorSchemaPath string:
|
|
22
|
-
|
|
23
|
-
- Cannot be empty.
|
|
24
|
-
- The string cannot start or end with `.`.
|
|
25
|
-
- The string cannot contain consecutive dots.
|
|
26
|
-
|
|
27
|
-
A path must start and end with a "word", a word is any combination of characters except `.` (dot). The following paths will throw during runtime:
|
|
28
|
-
|
|
29
|
-
```ts
|
|
30
|
-
// These throw at runtime
|
|
31
|
-
type LocatorSchemaPath =
|
|
32
|
-
| "" // empty string
|
|
33
|
-
| ".main" // starting with a dot
|
|
34
|
-
| "main." // ending with a dot
|
|
35
|
-
| "main..heading" // consecutive dots
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
Every path represents a chain of locators that describe the hierarchy of elements on the page. POMWright enforces uniqueness, so registering the same path twice causes fixture initialisation to fail before any test using them can runs.
|
|
39
|
-
|
|
40
|
-
```ts
|
|
41
|
-
import { GetByMethod, type GetLocatorBase } from "pomwright";
|
|
42
|
-
|
|
43
|
-
export type LocatorSchemaPath =
|
|
44
|
-
| "main"
|
|
45
|
-
| "main.heading";
|
|
46
|
-
|
|
47
|
-
export function initLocatorSchemas(locators: GetLocatorBase<LocatorSchemaPath>) {
|
|
48
|
-
locators.addSchema("main.heading", { /* ... */ });
|
|
49
|
-
locators.addSchema("main.heading", { /* ... */ }); // duplicate – throws
|
|
50
|
-
}
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
## Referencing schemas
|
|
54
|
-
|
|
55
|
-
Each path declared in the type must be implemented with `addSchema` inside `initLocatorSchemas`. Missing implementations raise a `not implemented` error when the fixtures are created, helping you catch mistakes early.
|
|
56
|
-
|
|
57
|
-
Locator paths can be shared across Page Object Classes by re‑exporting the union of strings:
|
|
58
|
-
|
|
59
|
-
```ts
|
|
60
|
-
import { GetByMethod, type GetLocatorBase } from "pomwright";
|
|
61
|
-
import {
|
|
62
|
-
type LocatorSchemaPath as common,
|
|
63
|
-
initLocatorSchemas as initCommon
|
|
64
|
-
} from "../page-components/common.locatorSchema";
|
|
65
|
-
|
|
66
|
-
export type LocatorSchemaPath =
|
|
67
|
-
| common
|
|
68
|
-
| "main.heading";
|
|
69
|
-
|
|
70
|
-
export function initLocatorSchemas(locators: GetLocatorBase<LocatorSchemaPath>) {
|
|
71
|
-
initCommon(locators);
|
|
72
|
-
|
|
73
|
-
locators.addSchema("main.heading", {
|
|
74
|
-
role: "heading",
|
|
75
|
-
roleOptions: { name: "Welcome!" },
|
|
76
|
-
locatorMethod: GetByMethod.role
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
## Descriptive segments
|
|
82
|
-
|
|
83
|
-
A path may include segments that exist purely for readability. POMWright skips any missing intermediate keys when chaining the locator.
|
|
84
|
-
|
|
85
|
-
```ts
|
|
86
|
-
import { GetByMethod, type GetLocatorBase } from "pomwright";
|
|
87
|
-
|
|
88
|
-
type LocatorSchemaPath =
|
|
89
|
-
| "main"
|
|
90
|
-
| "main.button.continue"; // "button" communicates intent
|
|
91
|
-
|
|
92
|
-
export function initLocatorSchemas(locators: GetLocatorBase<LocatorSchemaPath>) {
|
|
93
|
-
|
|
94
|
-
locators.addSchema("main", {
|
|
95
|
-
locator: "main",
|
|
96
|
-
locatorMethod: GetByMethod.locator
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
locators.addSchema("main.button.continue", {
|
|
100
|
-
role: "button",
|
|
101
|
-
roleOptions: { name: "Continue" },
|
|
102
|
-
locatorMethod: GetByMethod.role
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
You can also suffix segments with a friendly name using `@`. POMWright treats `@` like any other character—it simply improves readability for humans. You're free to use any special character to improve readability.
|
|
108
|
-
|
|
109
|
-
```ts
|
|
110
|
-
import { GetByMethod, type GetLocatorBase, type LocatorSchemaWithoutPath } from "pomwright";
|
|
111
|
-
|
|
112
|
-
type LocatorSchemaPath =
|
|
113
|
-
| "main"
|
|
114
|
-
| "main.button"
|
|
115
|
-
| "main.button@continue"
|
|
116
|
-
| "main.button@back"; // "button" is here used for human context
|
|
117
|
-
|
|
118
|
-
export function initLocatorSchemas(locators: GetLocatorBase<LocatorSchemaPath>) {
|
|
119
|
-
|
|
120
|
-
locators.addSchema("main", {
|
|
121
|
-
locator: "main",
|
|
122
|
-
locatorMethod: GetByMethod.locator
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
const button: LocatorSchemaWithoutPath = {
|
|
126
|
-
role: "button",
|
|
127
|
-
locatorMethod: GetByMethod.role
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
locators.addSchema("main.button", {
|
|
131
|
-
...button,
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
locators.addSchema("main.button@continue", {
|
|
135
|
-
...button,
|
|
136
|
-
roleOptions: { name: "Continue" }
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
locators.addSchema("main.button@back", {
|
|
140
|
-
...button,
|
|
141
|
-
roleOptions: { name: "Back" }
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
> Use something like `main.button` when you need a broad locator (for example, to count buttons) and `main.button@continue` or `main.button@back` when you need a specific instance.
|
|
147
|
-
|
|
148
|
-
The portion before `@` usually describes the element type (`section`, `button`), while the part after `@` is a friendly identifier (`playground`, `reset`). This makes long chains readable while still conveying intent.
|
|
149
|
-
|
|
150
|
-
> **Note:** There is nothing special about the character `@`, in the eyes of POMWright it's just another character in a word. The only "special" character is `.` dot.
|
|
151
|
-
|
|
152
|
-
Remember: the goal is not to mirror the DOM structure 1:1. Just enough to ensure a descriptive and unique path to the elements you interact with and validate through the use of simple Locators.
|
|
153
|
-
|
|
154
|
-
## LocatorSchemaPath, Sub-paths and IntelliSense
|
|
155
|
-
|
|
156
|
-
Every dot‑delimited segment forms a sub path. Sub paths power IntelliSense and let you scope updates or filters to a specific part of the chain.
|
|
157
|
-
|
|
158
|
-
```ts
|
|
159
|
-
const resetBtn = await profile
|
|
160
|
-
.getLocatorSchema("body.section@playground.button@reset")
|
|
161
|
-
.addFilter("body.section@playground", { hasText: /Primary Colors/i })
|
|
162
|
-
.getNestedLocator();
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
The TypeScript union of all paths enables autocomplete, prevents typos, and makes refactors simple. Update a single `LocatorSchema` definition and every test using that path immediately benefits from the change.
|