pomwright 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/AGENTS.md +37 -0
  2. package/CHANGELOG.md +179 -0
  3. package/README.md +316 -34
  4. package/dist/index.d.mts +1052 -30
  5. package/dist/index.d.ts +1052 -30
  6. package/dist/index.js +2263 -65
  7. package/dist/index.mjs +2260 -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 +693 -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.getByAltText.spec.ts +23 -0
  48. package/intTestV2/tests/locatorRegistry/add/add.getById.spec.ts +45 -0
  49. package/intTestV2/tests/locatorRegistry/add/add.getByLabel.spec.ts +23 -0
  50. package/intTestV2/tests/locatorRegistry/add/add.getByPlaceholder.spec.ts +23 -0
  51. package/intTestV2/tests/locatorRegistry/add/add.getByRole.spec.ts +23 -0
  52. package/intTestV2/tests/locatorRegistry/add/add.getByTestId.spec.ts +23 -0
  53. package/intTestV2/tests/locatorRegistry/add/add.getByText.spec.ts +23 -0
  54. package/intTestV2/tests/locatorRegistry/add/add.getByTitle.spec.ts +23 -0
  55. package/intTestV2/tests/locatorRegistry/add/add.locator.spec.ts +23 -0
  56. package/intTestV2/tests/locatorRegistry/add/add.reuseExisting.spec.ts +66 -0
  57. package/intTestV2/tests/locatorRegistry/add/add.reuseReusable.spec.ts +311 -0
  58. package/intTestV2/tests/locatorRegistry/add/add.spec.ts +159 -0
  59. package/intTestV2/tests/locatorRegistry/filter.cycle.spec.ts +39 -0
  60. package/intTestV2/tests/locatorRegistry/getLocator/getLocator.spec.ts +253 -0
  61. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.clearSteps.spec.ts +105 -0
  62. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.describe.spec.ts +23 -0
  63. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.filter.spec.ts +368 -0
  64. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.getLocator.spec.ts +56 -0
  65. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.getNestedLocator.spec.ts +175 -0
  66. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.nth.spec.ts +60 -0
  67. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.remove.spec.ts +32 -0
  68. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.replace.spec.ts +24 -0
  69. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.spec.ts +110 -0
  70. package/intTestV2/tests/locatorRegistry/getLocatorSchema/getLocatorSchema.update.spec.ts +322 -0
  71. package/intTestV2/tests/locatorRegistry/getNestedLocator/getNestedLocator.spec.ts +412 -0
  72. package/intTestV2/tests/locatorRegistry/registry/registry.binding.spec.ts +50 -0
  73. package/intTestV2/tests/locatorRegistry/validation/validation.locatorSchemaPath.spec.ts +115 -0
  74. package/intTestV2/tests/locatorRegistry/validation/validation.sub-path.spec.ts +45 -0
  75. package/intTestV2/tests/step/step.spec.ts +49 -0
  76. package/intTestV2/tests/testApp/color.spec.ts +15 -0
  77. package/intTestV2/tests/testApp/iframe.spec.ts +57 -0
  78. package/intTestV2/tests/testApp/testFilters.spec.ts +24 -0
  79. package/intTestV2/tests/testApp/testPage.spec.ts +161 -0
  80. package/intTestV2/tests/testApp/testPath.spec.ts +18 -0
  81. package/pack-build.sh +11 -0
  82. package/pack-test-v2.sh +36 -0
  83. package/package.json +10 -3
  84. package/playwright.base.ts +42 -0
  85. package/skills/README.md +56 -0
  86. package/skills/pomwright-v1-5-bridge-migration/SKILL.md +40 -0
  87. package/skills/pomwright-v1-5-bridge-migration/references/call-site-migration.md +178 -0
  88. package/skills/pomwright-v1-5-bridge-migration/references/schema-translation.md +183 -0
  89. package/skills/pomwright-v2-migration/SKILL.md +63 -0
  90. package/skills/pomwright-v2-migration/references/call-site-migration.md +265 -0
  91. package/skills/pomwright-v2-migration/references/class-migration.md +266 -0
  92. package/skills/pomwright-v2-migration/references/fixture-and-helpers.md +423 -0
  93. package/skills/pomwright-v2-migration/references/locator-registration.md +344 -0
  94. package/srcV2/fixture/base.fixtures.ts +23 -0
  95. package/srcV2/helpers/navigation.ts +153 -0
  96. package/srcV2/helpers/playwrightReportLogger.ts +196 -0
  97. package/srcV2/helpers/sessionStorage.ts +251 -0
  98. package/srcV2/helpers/stepDecorator.ts +106 -0
  99. package/srcV2/locators/index.ts +15 -0
  100. package/srcV2/locators/locatorQueryBuilder.ts +427 -0
  101. package/srcV2/locators/locatorRegistrationBuilder.ts +558 -0
  102. package/srcV2/locators/locatorRegistry.ts +541 -0
  103. package/srcV2/locators/locatorUpdateBuilder.ts +602 -0
  104. package/srcV2/locators/reusableLocatorBuilder.ts +200 -0
  105. package/srcV2/locators/types.ts +256 -0
  106. package/srcV2/locators/utils.ts +309 -0
  107. package/srcV2/locators/v1SchemaTranslator.ts +178 -0
  108. package/srcV2/pageObject.ts +105 -0
  109. /package/docs/{BaseApi-explanation.md → v1/BaseApi-explanation.md} +0 -0
  110. /package/docs/{BasePage-explanation.md → v1/BasePage-explanation.md} +0 -0
  111. /package/docs/{LocatorSchema-explanation.md → v1/LocatorSchema-explanation.md} +0 -0
  112. /package/docs/{LocatorSchemaPath-explanation.md → v1/LocatorSchemaPath-explanation.md} +0 -0
  113. /package/docs/{PlaywrightReportLogger-explanation.md → v1/PlaywrightReportLogger-explanation.md} +0 -0
  114. /package/docs/{get-locator-methods-explanation.md → v1/get-locator-methods-explanation.md} +0 -0
  115. /package/docs/{intro-to-using-pomwright.md → v1/intro-to-using-pomwright.md} +0 -0
  116. /package/docs/{sessionStorage-methods-explanation.md → v1/sessionStorage-methods-explanation.md} +0 -0
  117. /package/docs/{tips-folder-structure.md → v1/tips-folder-structure.md} +0 -0
package/AGENTS.md ADDED
@@ -0,0 +1,37 @@
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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,184 @@
1
1
  # pomwright
2
2
 
3
+ ## 1.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#35](https://github.com/DyHex/POMWright/pull/35) [`50fb6f4`](https://github.com/DyHex/POMWright/commit/50fb6f47ffd0745d81fffdf63cfbcf4c8b569bcf) Thanks [@DyHex](https://github.com/DyHex)! - # v1.5 Bridge Release
8
+
9
+ ## Summary
10
+
11
+ POMWright **v1.5** is a bridge release. It ships the legacy v1 API and the redesigned v2 API side-by-side so teams can migrate at their own pace.
12
+
13
+ For projects continuing to use only v1 patterns, upgrading from **v1.4 → v1.5** should require no behavioral changes by default. Migration starts when you opt into v2 APIs.
14
+
15
+ ## Philosophy and Motivation
16
+
17
+ The v2 redesign preserves the original POMWright principles from v1 (typed path-based locator modeling and automatic locator chaining), while modernizing the architecture around:
18
+
19
+ - better composability,
20
+ - clearer public API boundaries,
21
+ - stronger validation/error handling,
22
+ - and **Playwright-native ergonomics**.
23
+
24
+ The Playwright-native API shape (`getByRole`, `locator`, `filter`, `nth`, etc.) makes adoption easier for teams already fluent in Playwright, reducing cognitive overhead while improving long-term maintainability in larger test suites.
25
+
26
+ ## Migration Documentation
27
+
28
+ - [v1 to v2 comparison](../docs/v1-to-v2-migration/v1-to-v2-comparison.md)
29
+ - [Direct migration guide](../docs/v1-to-v2-migration/direct-migration-guide.md)
30
+ - [Bridge migration guide](../docs/v1-to-v2-migration/bridge-migration-guide.md)
31
+
32
+ ## v2 documentation
33
+
34
+ - [v2 Overview](../docs/v2/overview.md)
35
+ - [v2 PageObject](../docs/v2/PageObject.md)
36
+ - [v2 Locator Registry](../docs/v2/locator-registry.md)
37
+ - [v2 Composing Locator Modules](../docs/v2/composing-locator-modules.md)
38
+ - [v2 Session Storage](../docs/v2/session-storage.md)
39
+ - [v2 Logging](../docs/v2/logging.md)
40
+
41
+ ***
42
+
43
+ ## What changes when moving from v1 to v2
44
+
45
+ > Important: These are migration-time changes. They do **not** affect users who remain on v1 usage patterns in v1.5.
46
+
47
+ ### 1) Base class and architecture
48
+
49
+ - **v1**: `BasePage` is the core abstraction and requires `Page`, `testInfo`, and `PlaywrightReportLogger`.
50
+ - **v2**: `PageObject` is slimmer and composes navigation, locator registry accessors, and session storage without hard-coupling `testInfo` or logger.
51
+
52
+ ### 2) Modular adoption (“cherry-pick” usage)
53
+
54
+ v2 does not force inheritance from `PageObject`.
55
+
56
+ Adopters can use only what they need:
57
+
58
+ - `LocatorRegistry` (via `createRegistryWithAccessors`)
59
+ - `SessionStorage`
60
+ - `step` decorator
61
+ - logging fixture/logger
62
+ - or `PageObject` as a convenience composition.
63
+
64
+ ### 3) Locator definitions: schema objects → fluent builder DSL
65
+
66
+ - **v1**: `addSchema(path, { locatorMethod, ... })` + `GetByMethod` enum + `LocatorSchemaWithoutPath`.
67
+ - **v2**: `add(path).getByRole(...).filter(...).nth(...).describe(...)`.
68
+
69
+ This redesign improves readability and aligns locator registration with native Playwright style.
70
+
71
+ ### 4) Retrieval API behavior
72
+
73
+ - Conceptually preserved: `getLocator` (terminal) vs `getNestedLocator` (full chain).
74
+ - **v1** retrieval was async.
75
+ - **v2** retrieval is synchronous.
76
+
77
+ ### 5) Step handling (`filter` + `nth`) is significantly more expressive
78
+
79
+ - **v1**: filters could be chained, but indexing behavior was constrained and applied after filter composition.
80
+ - **v2**:
81
+ - `filter` and `nth` are both first-class steps,
82
+ - steps are applied in exact chain order,
83
+ - no practical chain-limit restrictions,
84
+ - `nth` can be set at registration time and query time,
85
+ - `filter has/hasNot` accepts both Playwright locators and registry path references.
86
+
87
+ ### 6) FrameLocator semantics are explicit and safer
88
+
89
+ - **v1** cast `FrameLocator` to `Locator` for compatibility.
90
+ - **v2** handles frame segments explicitly:
91
+ - non-terminal frame segments continue resolution inside frame context,
92
+ - terminal frame segments resolve to frame owner locator.
93
+
94
+ ### 7) Mutation model redesign
95
+
96
+ - **v1**: `update(...)` + `addFilter(...)` on schema clones.
97
+ - **v2** separates concerns:
98
+ - definition ops: `update`, `replace`, `remove`
99
+ - step ops: `filter`, `nth`, `clearSteps`
100
+ - locator annotation: `describe`
101
+
102
+ This improves intent clarity and reduces accidental side effects.
103
+
104
+ ### 8) Reuse model upgrade
105
+
106
+ - **v1** reuse centered around shared `LocatorSchemaWithoutPath` objects.
107
+ - **v2** introduces:
108
+ - typed reusable seeds (`createReusable`),
109
+ - controlled typed overrides,
110
+ - reuse-by-path cloning.
111
+
112
+ This reduces duplication while improving type guidance and safety.
113
+
114
+ ### 9) Navigation helper (new in v2)
115
+
116
+ `PageObject.navigation` is URL-type aware:
117
+
118
+ - string fullUrl support includes `goto`, `gotoThisPage`, `expectThisPage`, `expectAnotherPage`.
119
+ - RegExp fullUrl support narrows to assertion-oriented methods (`expectThisPage`, `expectAnotherPage`).
120
+
121
+ ### 10) SessionStorage evolution
122
+
123
+ SessionStorage is not removed; it is redesigned and strengthened:
124
+
125
+ - context-aware operations (`waitForContext`),
126
+ - key-targeted clear,
127
+ - clearer option signatures.
128
+
129
+ ### 11) Logging decoupling
130
+
131
+ - v1 tightly coupled logger usage through base class constructor patterns.
132
+ - v2 keeps logging available but optional; `PageObject` does not force logger coupling.
133
+
134
+ ### 12) Step decorator (new in v2)
135
+
136
+ v2 introduces a `@step` decorator for wrapping POM methods in `test.step` with typed arguments and returns.
137
+
138
+ ### 13) Error handling and validation improvements
139
+
140
+ v2 strengthens diagnostics and safety across registry operations:
141
+
142
+ - stricter path validation (compile-time + runtime alignment),
143
+ - improved sub-path validation,
144
+ - better reuse guardrails,
145
+ - explicit filter reference constraints,
146
+ - filter cycle detection,
147
+ - clearer frame/filter misuse errors.
148
+
149
+ ***
150
+
151
+ ## Breaking / Behavioral Notes (migration-scoped)
152
+
153
+ The following apply when adopting v2 APIs:
154
+
155
+ - retrieval methods are synchronous,
156
+ - v1 nested index-map style is replaced by ordered `nth(...)` steps,
157
+ - `addFilter(...)` is replaced by `filter(...)`,
158
+ - frame handling semantics are explicit (terminal frame resolves to owner locator),
159
+ - built-in `data-cy` selector engine behavior from v1 is removed from framework defaults,
160
+ - `BaseApi` is not part of v2.
161
+
162
+ ***
163
+
164
+ ## Additional improvements worth mentioning
165
+
166
+ - Cleaner public/internal API boundary for registry internals.
167
+ - Functional-friendly factory (`createRegistryWithAccessors`) for dependency injection and non-class usage.
168
+ - Clone-based query mutation semantics that avoid mutating canonical registry state.
169
+ - Better migration support via bridge mechanisms and v1 schema translation helpers.
170
+ - Better docs structure for v2 and migration workflows.
171
+
172
+ ## skills
173
+
174
+ In the repository ./skills folder you can find AI assistant migration skills to help upgrade POMWright page objects from v1 to v2. These skills provide step-by-step guidance for migrating BasePage to the v1.5 bridge (BasePageV1toV2) or directly to v2 PageObject.
175
+
176
+ > **Important:** Use at your own discretion. These migration skills are an optional aid intended to reduce manual effort when moving POMWright page objects from v1 to v2. They offer practical guidance for common migration paths, but they cannot account for every project-specific variation, custom abstraction, or edge case in existing v1 implementations. Treat them as a helpful starting point—not a guaranteed or complete migration solution. Always review and validate AI-generated changes before using them.
177
+
178
+ ## Bridge Positioning Statement
179
+
180
+ POMWright v1.5 is intentionally designed to let teams keep shipping on v1 while preparing migration to v2. The release provides both a staged bridge path and a direct migration path, with no forced v1 behavior changes unless v2 APIs are adopted.
181
+
3
182
  ## 1.4.0
4
183
 
5
184
  ### Minor Changes
package/README.md CHANGED
@@ -7,32 +7,294 @@
7
7
  [![NPM dev or peer Dependency Version](https://img.shields.io/npm/dependency-version/pomwright/peer/%40playwright%2Ftest)](https://www.npmjs.com/package/playwright)
8
8
  [![Static Badge](https://img.shields.io/badge/created%40-ICE-ffcd00)](https://www.ice.no/)
9
9
 
10
- POMWright is a lightweight TypeScript framework that layers the Page Object Model on top of Playwright. It keeps locators, page objects, and fixtures organised so that UI tests stay readable and maintainable.
10
+ POMWright is a TypeScript companion framework for Playwright focused on Page Object Model workflows.
11
11
 
12
- POMWright provides a way of abstracting the implementation details of a web page and encapsulating them into a reusable page object. This approach makes the tests easier to read, write, and maintain, and helps reduce duplicated code by breaking down the code into smaller, reusable components, making the code more maintainable and organized.
12
+ It provides automatic locator chaining via a LocatorRegistry, a Session Storage API, a log fixture for report attachments, and a test.step decorator all of which can be used independently either through a functional approachs or as part of your custom POMs or quickly create new standardized POMS by extending a class with the provided the abstract PageObject class. PageObject also adds navigation helpers and URL typing which supports multiple base URLs and dynamic RegExp paths.
13
13
 
14
- ## Key capabilities
14
+ ---
15
15
 
16
- - Quickly build maintainable Page Object Classes.
17
- - Define any Playwright Locator through type-safe LocatorSchemas.
18
- - Automatic chaining of locators with dot-delimited paths that map directly to Playwright locator methods.
19
- - Auto-completion of LocatorSchemaPaths and sub-paths.
20
- - Adjust locators on the fly without mutating the original definitions.
21
- - Attach structured logs directly to the Playwright HTML report.
22
- - Manage `sessionStorage` for browser setup and state hand‑off.
23
- - Multiple Domains/BaseURLs
24
- - Validate elements position in DOM through chaining
16
+ ## Version status: v1.5 is a migration bridge
25
17
 
26
- ## Why POMWright?
18
+ POMWright v1.5 currently ships both:
27
19
 
28
- - **Stronger reliability** – centralised locator definitions reduce brittle inline selectors and make refactors visible at compile time.
29
- - **Faster authoring** strongly typed schema paths provide instant auto-complete, even for deeply nested segments and reusable fragments.
30
- - **Easier maintenance** – shared helper patterns keep page objects, fixtures, and API clients aligned so teams can extend coverage without duplicating boilerplate.
31
- - **Incremental adoption** – each helper sits on top of Playwright primitives, making it straightforward to migrate existing tests component by component.
20
+ - **v1 API** (legacy/deprecated)
21
+ - **v2 API** (current direction)
32
22
 
33
- ## Installation
23
+ **v1.5 is the bridge release** for migrating from v1 patterns to v2 patterns. If you are starting fresh, use **v2 (`PageObject` + `LocatorRegistry`)**. If you are on v1, use the migration docs to move incrementally.
24
+
25
+ ---
26
+
27
+ ## Why use POMWright instead of vanilla Playwright POM?
28
+
29
+ Playwright’s official best practices emphasize locator chaining for clarity and resilience. In practice, manually writing and maintaining those chains becomes tedious and fragile as pages grow. POMWright’s solution is the LocatorRegistry, which automatically builds locator chains from single locator definitions tied to unique paths. You register one locator per path (with Playwright‑like syntax), and POMWright composes the full chain for you.
30
+
31
+ This gives you the same Playwright primitives you already use — but with dramatically simpler maintenance, better structure, and safer refactors across small and large projects.
32
+
33
+ ### Chain everything
34
+
35
+ POMWright’s default behavior is to resolve locators by chaining all segments in a path. That brings two major benefits:
36
+
37
+ - **Robustness through scope‑aware uniqueness:** Chaining narrows selectors through the intended DOM structure, producing more robust locators that are less brittle to UI changes and therefore less prone to flake. A “Change” button tied to a password field under a “Credentials” region resolves differently than a “Change” button tied to an email field under a “Contact info” region. If an identical button is added elsewhere, existing tests keep working because their paths remain scoped. If the button is mistakenly added under the wrong section, tests will fail and reveal the mistake.
38
+
39
+ - **Implicit DOM structure validation:** Because chains traverse the DOM hierarchy, locator resolution validates that the UI is still arranged as expected — as strictly or loosely as you choose. You don’t need to map the entire DOM; just chain the “structural anchors” that matter. Often, user‑visible elements are good enough.
34
40
 
35
- Install POMWright alongside Playwright:
41
+ ### Highlights
42
+
43
+ - **Centralized locator definitions with automatic chaining** — one path defines an entire selector chain across the app, across all Playwright locator types.
44
+ - **Safer refactors** — update a locator once and all usages update automatically (including full chains).
45
+ - **Typed paths and sub‑paths** with contextual autocomplete and compile‑time validation, making structural updates as simple as find‑and‑replace.
46
+ - **Explicit control of chain depth** — choose terminal resolution (getLocator) or full chained resolution (`getNestedLocator`) at the call site.
47
+ - **Non‑mutating query overrides** — handle edge cases, variations, and iteration without changing base definitions (`getLocatorSchema(...).update/replace/remove/filter/nth/describe`).
48
+ - **Reusable locator seeds + typed path reuse** — share locator patterns or clone existing definitions across contexts without duplication.
49
+ - **Optional helpers** — locator registry, session storage, logging, and step decorator can be adopted independently.
50
+ - **Minimal base class** — `PageObject` provides URL + navigation + registry + session storage, leaving everything else to your own composition.
51
+
52
+ ---
53
+
54
+ ## Example: vanilla Playwright POM vs POMWright `PageObject`
55
+
56
+ ### Vanilla Playwright POM (typical)
57
+
58
+ ```ts
59
+ import { test, type Locator, type Page } from "@playwright/test";
60
+
61
+ export class LoginPage {
62
+ readonly page: Page;
63
+ readonly main: Locator;
64
+ readonly loginForm: Locator;
65
+ readonly usernameInput: Locator;
66
+ readonly passwordInput: Locator;
67
+ readonly submitButton: Locator;
68
+
69
+ constructor(page: Page) {
70
+ this.page = page;
71
+ this.main = page.locator("main");
72
+ this.loginForm = this.main.getByRole("form", { name: "Login" });
73
+ this.usernameInput = this.loginForm.getByLabel("Username");
74
+ this.passwordInput = this.loginForm.getByLabel("Password");
75
+ this.submitButton = this.main.getByRole("button", { name: "Login" });
76
+ }
77
+
78
+ async goto() {
79
+ await test.step("LoginPage.goto", async () => {
80
+ await this.page.goto("/login");
81
+ });
82
+ }
83
+
84
+ async login(username: string, password: string) {
85
+ await test.step("LoginPage.login", async () => {
86
+ await this.usernameInput.fill(username);
87
+ await this.passwordInput.fill(password);
88
+ await this.submitButton.click();
89
+ });
90
+ }
91
+ }
92
+ ```
93
+
94
+ ### Vanilla Playwright fixture
95
+
96
+ ```ts
97
+ import { test as base } from "@playwright/test";
98
+ import { LoginPage } from "./login.page";
99
+
100
+ type Fixtures = { loginPage: LoginPage };
101
+
102
+ export const test = base.extend<Fixtures>({
103
+ loginPage: async ({ page }, use) => {
104
+ await use(new LoginPage(page));
105
+ },
106
+ });
107
+ ```
108
+
109
+ ### Vanilla Playwright Test
110
+
111
+ ```ts
112
+ import { test } from "./fixtures";
113
+
114
+ test("login flow", async ({ loginPage }) => {
115
+ await loginPage.goto();
116
+ await loginPage.login("alice", "secret");
117
+ });
118
+ ```
119
+
120
+ ### POMWright `PageObject` (v2)
121
+
122
+ ```ts
123
+ import { type Page } from "@playwright/test";
124
+ import { PageObject, step } from "pomwright";
125
+
126
+ type Paths =
127
+ | "main"
128
+ | "main.form@login"
129
+ | "main.form@login.input@username"
130
+ | "main.form@login.input@password"
131
+ | "main.button@login";
132
+
133
+ export class LoginPage extends PageObject<Paths> {
134
+ constructor(page: Page) {
135
+ super(page, "https://example.com", "/login");
136
+ }
137
+
138
+ protected defineLocators(): void {
139
+ this.add("main").locator("main");
140
+ this.add("main.form@login").getByRole("form", { name: "Login" });
141
+ this.add("main.form@login.input@username").getByLabel("Username");
142
+ this.add("main.form@login.input@password").getByLabel("Password");
143
+ this.add("main.button@login").getByRole("button", { name: "Login" });
144
+ }
145
+
146
+ protected pageActionsToPerformAfterNavigation() {
147
+ return [
148
+ async () => {
149
+ await this.getNestedLocator("main.form@login").waitFor({ state: "visible" });
150
+ },
151
+ ];
152
+ }
153
+
154
+ @step()
155
+ async login(username: string, password: string) {
156
+ await this.getNestedLocator("main.form@login.input@username").fill(username);
157
+ await this.getNestedLocator("main.form@login.input@password").fill(password);
158
+ await this.getNestedLocator("main.button@login").click();
159
+ }
160
+ }
161
+ ```
162
+
163
+ **Tip:** If a Page Object grows large with many Paths and this.add(...) calls, move the locator definitions into a companion file (e.g., login.page.ts + login.locators.ts) to keep the class focused on behavior while keeping the registry definitions centralized and reusable. Simmilarily you can move all Paths and add calls for locator definitions common to all POMs for a given domain into a common.locators.ts file to share across your POMs.
164
+
165
+ ```ts
166
+ // login.locators.ts
167
+ import type { LocatorRegistry } from "pomwright";
168
+ import { Paths as Common, defineLocators as addCommon } from "../common.locators"; // errors, dialogs, navbar, main, etc.
169
+
170
+ export type Paths =
171
+ | Common
172
+ | "main.form@login"
173
+ | "main.form@login.input@username"
174
+ | "main.form@login.input@password"
175
+ | "main.button@login";
176
+
177
+ export function defineLocators(registry: LocatorRegistry<Paths>) {
178
+ addCommon(registry);
179
+ registry.add("main.form@login").getByRole("form", { name: "Login" });
180
+ registry.add("main.form@login.input@username").getByLabel("Username");
181
+ registry.add("main.form@login.input@password").getByLabel("Password");
182
+ registry.add("main.button@login").getByRole("button", { name: "Login" });
183
+ }
184
+ ```
185
+
186
+ ```ts
187
+ // login.page.ts
188
+ import { type Page } from "@playwright/test";
189
+ import { PageObject, step } from "pomwright";
190
+ import { type Paths, defineLocators } from "./login.locators.ts";
191
+
192
+ export class LoginPage extends PageObject<Paths> {
193
+ constructor(page: Page) {
194
+ super(page, "https://example.com", "/login");
195
+ }
196
+
197
+ protected defineLocators(): void {
198
+ defineLocators(this.locatorRegistry);
199
+ }
200
+
201
+ protected pageActionsToPerformAfterNavigation() {
202
+ return [
203
+ async () => {
204
+ await this.getNestedLocator("common.nav.logo").waitFor({ state: "visible" });
205
+ await this.getNestedLocator("main.form@login").waitFor({ state: "visible" });
206
+ },
207
+ ];
208
+ }
209
+
210
+ @step()
211
+ async login(username: string, password: string) {
212
+ await this.getNestedLocator("main.form@login.input@username").fill(username);
213
+ await this.getNestedLocator("main.form@login.input@password").fill(password);
214
+ await this.getNestedLocator("main.button@login").click();
215
+ }
216
+ }
217
+ ```
218
+
219
+ ### POMWright fixtures
220
+
221
+ ```ts
222
+ import { test as base } from "@playwright/test";
223
+ import { PlaywrightReportLogger, type LogEntry, type LogLevel } from "pomwright";
224
+ import { LoginPage } from "./login.page";
225
+
226
+ type Fixtures = {
227
+ loginPage: LoginPage,
228
+ log: PlaywrightReportLogger
229
+ };
230
+
231
+ export const test = base.extend<Fixtures>({
232
+ loginPage: async ({ page }, use) => {
233
+ await use(new LoginPage(page));
234
+ },
235
+ log: async ({}, use, testInfo) => { // or just import { test as base } from "pomwright";
236
+ const sharedLogEntry: LogEntry[] = [];
237
+ const sharedLogLevel: { current: LogLevel; initial: LogLevel } =
238
+ testInfo.retry === 0
239
+ ? { current: "warn", initial: "warn" }
240
+ : { current: "debug", initial: "debug" };
241
+
242
+ const log = new PlaywrightReportLogger(sharedLogLevel, sharedLogEntry, "TestCase");
243
+ await use(log);
244
+ log.attachLogsToTest(testInfo);
245
+ },
246
+ });
247
+ ```
248
+
249
+ ### POMWright test
250
+
251
+ ```ts
252
+ import { test } from "./fixtures";
253
+
254
+ test("login flow", async ({ loginPage, log }) => {
255
+ await loginPage.navigation.gotoThisPage();
256
+ await loginPage.login("alice", "secret");
257
+ log.info("Hellow World!");
258
+ });
259
+
260
+ ```
261
+
262
+ ### What improves in the POMWright version?
263
+
264
+ - **No manual chain construction in class properties**:
265
+ The vanilla POM has to build and store each chain manually (main → form → input). In POMWright, you register each locator once and chaining is automatic, so you avoid duplicated chain logic and drift.
266
+ - **Single source of truth for structure**:
267
+ The DOM structure is encoded in the registry paths, not scattered across class fields. This makes the page structure explicit and easier to reason about.
268
+ - **Refactors touch fewer places**:
269
+ In vanilla POM, changing a DOM structure often means editing multiple chained fields. In POMWright, you update the registry definitions once and all chains update together.
270
+ - **Less coupling between fields**:
271
+ The vanilla POM creates dependencies between stored locators (loginForm must exist before usernameInput). POMWright doesn’t rely on field chains — each locator is defined independently and assembled by the registry.
272
+ - **Clearer intent at call sites lowers cognitive load when scanning code**:
273
+ In POMWright, call sites read like semantic paths (`"main.form@login.input@username"`), which makes intent clearer than referencing nested locator properties. Semantic paths make it obvious what a locator represents, while the registry tells you how it’s built.
274
+ - **Explicit terminal vs chained resolution**:
275
+ You can choose terminal‑only (getLocator) or fully chained (getNestedLocator) resolution per call without creating extra fields for each variation.
276
+ - **Chaining consistency by default**:
277
+ Every getNestedLocator(...) resolves the same full chain, so you can’t accidentally skip a parent locator when implementing or refactoring methods.
278
+ - **Easier to introduce variations**:
279
+ In vanilla POM, you often need extra fields or conditional logic for small variations. POMWright’s getLocatorSchema lets you tweak chains or filters for edge cases without changing the base definitions.
280
+ - **Consistent fixture usage**:
281
+ The POMWright fixture exposes a uniform API (navigation, registry, session storage), whereas the vanilla fixture just provides a class instance with custom fields. This reduces ad‑hoc helpers over time.
282
+ - **Built‑in navigation flow**:
283
+ The POMWright version can express “loaded state” directly as post‑navigation actions. The vanilla version requires separate helper methods or repeated waits in tests.
284
+ - **Step and logging instrumentation are standardized**:
285
+ @step() and optional logging make action tracing consistent across pages instead of ad‑hoc test.step wrappers in individual methods.
286
+ - **Registry definitions are shareable across POCs**:
287
+ If multiple POMs share UI sections, the same locator paths can be reused directly rather than copied.
288
+ - **Better guardrails for large teams**:
289
+ Typed paths and consistent chaining help prevent “near‑miss” locators that work in one file but differ subtly elsewhere.
290
+ - **More predictable long‑term maintenance**:
291
+ Registry‑driven chains reduce the risk of subtle inconsistencies between methods or tests as the suite grows.
292
+ - **Easier onboarding for new contributors**:
293
+ Paths reveal structure and intent immediately, so newcomers don’t need to trace nested Locator fields to understand what’s happening.
294
+
295
+ ---
296
+
297
+ ## Installation
36
298
 
37
299
  ```bash
38
300
  pnpm add -D pomwright
@@ -40,28 +302,48 @@ pnpm add -D pomwright
40
302
  npm install --save-dev pomwright
41
303
  ```
42
304
 
43
- ## Documentation
305
+ ---
306
+
307
+ ## Documentation index
308
+
309
+ ### v2 documentation (recommended)
310
+
311
+ - [v2 Overview](./docs/v2/overview.md)
312
+ - [v2 PageObject](./docs/v2/PageObject.md)
313
+ - [v2 Locator Registry](./docs/v2/locator-registry.md)
314
+ - [v2 Composing Locator Modules](./docs/v2/composing-locator-modules.md)
315
+ - [v2 Session Storage](./docs/v2/session-storage.md)
316
+ - [v2 Logging](./docs/v2/logging.md)
317
+
318
+ ### v1 -> v2 migration guides
319
+
320
+ - [v1 to v2 Comparison](./docs/v1-to-v2-migration/v1-to-v2-comparison.md)
321
+ - [Direct Migration Guide](./docs/v1-to-v2-migration/direct-migration-guide.md)
322
+ - [Bridge Migration Guide](./docs/v1-to-v2-migration/bridge-migration-guide.md)
323
+
324
+ ### v1 documentation (legacy/deprecated)
44
325
 
45
- Start with the introduction and continue through the topics:
326
+ - [Intro to using POMWright (v1)](./docs/v1/intro-to-using-pomwright.md)
327
+ - [BasePage](./docs/v1/BasePage-explanation.md)
328
+ - [LocatorSchemaPath](./docs/v1/LocatorSchemaPath-explanation.md)
329
+ - [LocatorSchema](./docs/v1/LocatorSchema-explanation.md)
330
+ - [Locator helper methods](./docs/v1/get-locator-methods-explanation.md)
331
+ - [BaseApi](./docs/v1/BaseApi-explanation.md)
332
+ - [PlaywrightReportLogger](./docs/v1/PlaywrightReportLogger-explanation.md)
333
+ - [Session storage helpers](./docs/v1/sessionStorage-methods-explanation.md)
334
+ - [Tips for folder structure](./docs/v1/tips-folder-structure.md)
46
335
 
47
- 1. [Intro to using POMWright](./docs/intro-to-using-pomwright.md)
48
- 2. [BasePage](./docs/BasePage-explanation.md)
49
- 3. [LocatorSchemaPath](./docs/LocatorSchemaPath-explanation.md)
50
- 4. [LocatorSchema](./docs/LocatorSchema-explanation.md)
51
- 5. [Locator schema helper methods](./docs/get-locator-methods-explanation.md)
52
- 6. [BaseApi](./docs/BaseApi-explanation.md)
53
- 7. [PlaywrightReportLogger](./docs/PlaywrightReportLogger-explanation.md)
54
- 8. [Session storage helpers](./docs/sessionStorage-methods-explanation.md)
55
- 9. [Tips for structuring locator files](./docs/tips-folder-structure.md)
336
+ ---
56
337
 
57
- ## Troubleshooting and Support
338
+ ## Support
58
339
 
59
- If you encounter any issues or have questions, please check our [issues page](https://github.com/DyHex/POMWright/issues) or reach out to us directly.
340
+ If you run into issues or have questions, please use the
341
+ [GitHub issues page](https://github.com/DyHex/POMWright/issues).
60
342
 
61
343
  ## Contributing
62
344
 
63
- Pull Requests are welcome! Please open an issue or submit a pull request for any enhancements or bug fixes.
345
+ Pull requests are welcome!
64
346
 
65
347
  ## License
66
348
 
67
- POMWright is open-source software licensed under the [Apache-2.0 license](https://github.com/DyHex/POMWright/blob/main/LICENSE).
349
+ POMWright is open-source software licensed under [Apache-2.0](./LICENSE).