pw-core 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +579 -0
- package/dist/component/table.d.ts +48 -0
- package/dist/component/table.js +112 -0
- package/dist/helpers.d.ts +15 -0
- package/dist/helpers.js +39 -0
- package/dist/page/actions/locator-actions.d.ts +10 -0
- package/dist/page/actions/locator-actions.js +60 -0
- package/dist/page/actions/page-actions.d.ts +39 -0
- package/dist/page/actions/page-actions.js +107 -0
- package/dist/page/assertions/expect.d.ts +2 -0
- package/dist/page/assertions/expect.js +7 -0
- package/dist/page/assertions/verify-chain.d.ts +63 -0
- package/dist/page/assertions/verify-chain.js +59 -0
- package/dist/page/assertions/verify-helpers.d.ts +22 -0
- package/dist/page/assertions/verify-helpers.js +28 -0
- package/dist/page/config.d.ts +31 -0
- package/dist/page/config.js +26 -0
- package/dist/page/index.d.ts +10 -0
- package/dist/page/index.js +26 -0
- package/dist/page/locators/resolver.d.ts +23 -0
- package/dist/page/locators/resolver.js +83 -0
- package/dist/page/registry.d.ts +20 -0
- package/dist/page/registry.js +34 -0
- package/dist/page/typed-page.d.ts +78 -0
- package/dist/page/typed-page.js +78 -0
- package/dist/page/types/proxy-methods.d.ts +53 -0
- package/dist/page/types/proxy-methods.js +2 -0
- package/dist/page/utils/formatter.d.ts +3 -0
- package/dist/page/utils/formatter.js +50 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 qualicore
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
# pw-core
|
|
2
|
+
|
|
3
|
+
A developer-first framework layer built on top of Playwright for creating readable, scalable, maintainable, and AI-friendly automation frameworks.
|
|
4
|
+
|
|
5
|
+
`pw-core` standardizes how pages, locators, components, assertions, and fixtures are defined so teams can focus on writing tests instead of maintaining framework code.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install pw-core
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### Requirements
|
|
16
|
+
|
|
17
|
+
- Existing Playwright project
|
|
18
|
+
- TypeScript recommended
|
|
19
|
+
- Compatible with supported Playwright versions defined by peer dependency requirements
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Core Philosophy
|
|
24
|
+
|
|
25
|
+
| Goal | Value |
|
|
26
|
+
| :--------------------- | :------------------------------------------------------ |
|
|
27
|
+
| Single Source of Truth | URLs, selectors, and testIds live in one place |
|
|
28
|
+
| Readability | Tests should read like business workflows |
|
|
29
|
+
| Scalability | Adding pages should not increase framework complexity |
|
|
30
|
+
| Reusability | Components should be defined once and reused everywhere |
|
|
31
|
+
| Type Safety | Catch mistakes during development instead of runtime |
|
|
32
|
+
| AI-Friendly | Standardized patterns reduce context and token usage |
|
|
33
|
+
| Maintainability | Update locators once, not across hundreds of tests |
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
# Why pw-core?
|
|
38
|
+
|
|
39
|
+
Most Playwright frameworks start small and manageable.
|
|
40
|
+
|
|
41
|
+
As projects grow, they often become difficult to maintain:
|
|
42
|
+
|
|
43
|
+
- Locators duplicated across files
|
|
44
|
+
- Massive Page Objects
|
|
45
|
+
- Repeated fixture definitions
|
|
46
|
+
- Components recreated on every page
|
|
47
|
+
- Multiple sources of truth
|
|
48
|
+
- Inconsistent coding patterns
|
|
49
|
+
|
|
50
|
+
The larger the framework becomes, the more expensive these problems become.
|
|
51
|
+
|
|
52
|
+
`pw-core` solves this by introducing:
|
|
53
|
+
|
|
54
|
+
- Configuration-driven pages
|
|
55
|
+
- Typed Page Objects
|
|
56
|
+
- Automatic Page Registry fixtures
|
|
57
|
+
- Reusable Components
|
|
58
|
+
- Chained Locators
|
|
59
|
+
- Built-in Assertions
|
|
60
|
+
- Browser Storage Helpers
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
# Traditional Playwright vs pw-core
|
|
65
|
+
|
|
66
|
+
## Traditional Approach
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
class LoginPage {
|
|
70
|
+
constructor(private page: Page) {}
|
|
71
|
+
|
|
72
|
+
username = this.page.getByTestId("username-input");
|
|
73
|
+
password = this.page.getByTestId("password-input");
|
|
74
|
+
loginButton = this.page.getByTestId("login-button");
|
|
75
|
+
errorMessage = this.page.getByTestId("login-error");
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Usage:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
await loginPage.username.fill(user);
|
|
83
|
+
await loginPage.password.fill(pass);
|
|
84
|
+
await loginPage.loginButton.click();
|
|
85
|
+
|
|
86
|
+
await expect(loginPage.errorMessage).toBeVisible();
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Problems:
|
|
90
|
+
|
|
91
|
+
- Locator definitions
|
|
92
|
+
- Large Page Objects
|
|
93
|
+
- Boilerplate code
|
|
94
|
+
- Multiple maintenance points
|
|
95
|
+
- Runtime locator mistakes
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## pw-core Approach
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
const config = createPageConfig({
|
|
103
|
+
url: "/login",
|
|
104
|
+
|
|
105
|
+
testIds: {
|
|
106
|
+
username: "username-input",
|
|
107
|
+
password: "password-input",
|
|
108
|
+
loginBtn: "login-button",
|
|
109
|
+
loginError: "login-error",
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
class LoginPage extends TypedPage<typeof config> {
|
|
114
|
+
constructor(page: Page) {
|
|
115
|
+
super(page, config);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Usage:
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
await loginPage.fill("username", "admin");
|
|
124
|
+
await loginPage.fill("password", "password");
|
|
125
|
+
await loginPage.click("loginBtn");
|
|
126
|
+
await login.verify("loginError");
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Benefits:
|
|
130
|
+
|
|
131
|
+
- Single source of truth
|
|
132
|
+
- Strong typing
|
|
133
|
+
- Less boilerplate
|
|
134
|
+
- Cleaner tests
|
|
135
|
+
- Easier maintenance
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
# Single Source of Truth
|
|
140
|
+
|
|
141
|
+
Everything begins with a page configuration.
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
const config = createPageConfig({
|
|
145
|
+
url: "/transactions",
|
|
146
|
+
|
|
147
|
+
testIds: {
|
|
148
|
+
table: "transactions-table",
|
|
149
|
+
next: "next-page",
|
|
150
|
+
prev: "prev-page",
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
selectors: {
|
|
154
|
+
pageTitle: "h1",
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
This configuration owns:
|
|
160
|
+
|
|
161
|
+
- URLs
|
|
162
|
+
- testIds
|
|
163
|
+
- selectors
|
|
164
|
+
|
|
165
|
+
Everything else consumes this configuration.
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
await page.goto();
|
|
169
|
+
|
|
170
|
+
await page.click("next");
|
|
171
|
+
|
|
172
|
+
await page.verify("table");
|
|
173
|
+
|
|
174
|
+
await page.verifyURL();
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
When a locator changes, update it once.
|
|
178
|
+
|
|
179
|
+
Not across dozens of tests and page objects.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
# TypedPage
|
|
184
|
+
|
|
185
|
+
`TypedPage` is the foundation of pw-core.
|
|
186
|
+
|
|
187
|
+
It transforms page configurations into fully typed automation objects.
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
class SettingsPage extends TypedPage<typeof config> {
|
|
191
|
+
async toggleLogo() {
|
|
192
|
+
await this.click("logoToggle");
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Automatically provides:
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
await page.goto();
|
|
201
|
+
|
|
202
|
+
await page.click("logoToggle");
|
|
203
|
+
|
|
204
|
+
await page.fill("username", "admin");
|
|
205
|
+
|
|
206
|
+
await page.check("rememberMe");
|
|
207
|
+
|
|
208
|
+
await page.hover("profile");
|
|
209
|
+
|
|
210
|
+
await page.textContent("message");
|
|
211
|
+
|
|
212
|
+
await page.verify("submit");
|
|
213
|
+
|
|
214
|
+
await page.verifyURL();
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
No locator declarations required.
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
# Readable Assertions
|
|
222
|
+
|
|
223
|
+
Traditional:
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
await expect(page.getByTestId("login-button")).toBeVisible();
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
pw-core:
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
await login.verify("loginBtn");
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
By default:
|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
await login.verify("loginBtn");
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
is equivalent to:
|
|
242
|
+
|
|
243
|
+
```ts
|
|
244
|
+
await expect(page.getByTestId("login-button")).toBeVisible();
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
Custom assertions remain available:
|
|
248
|
+
|
|
249
|
+
```ts
|
|
250
|
+
await login.verify("loginBtn").toBeEnabled();
|
|
251
|
+
|
|
252
|
+
await login.verify("errorMessage").toContainText("Invalid");
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
# Chained Locators
|
|
258
|
+
|
|
259
|
+
Complex nested locators are common in large applications.
|
|
260
|
+
|
|
261
|
+
Traditional:
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
await page
|
|
265
|
+
.getByTestId("transactions-table")
|
|
266
|
+
.getByTestId("transaction-row")
|
|
267
|
+
.nth(2)
|
|
268
|
+
.dblclick();
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
pw-core:
|
|
272
|
+
|
|
273
|
+
```ts
|
|
274
|
+
await transactions.dblclick("table.transactionRows", { nth: 2 });
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
Traditional:
|
|
278
|
+
|
|
279
|
+
```ts
|
|
280
|
+
await page
|
|
281
|
+
.locator('[data-testid="modal"]')
|
|
282
|
+
.locator('[data-testid="form"]')
|
|
283
|
+
.locator('[data-testid="submit"]')
|
|
284
|
+
.click();
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
pw-core:
|
|
288
|
+
|
|
289
|
+
```ts
|
|
290
|
+
await page.click("modal.form.submit");
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Assertions work the same way:
|
|
294
|
+
|
|
295
|
+
```ts
|
|
296
|
+
await page.verify("table.transactionRows").toHaveCount(10);
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Nested locator chains stay readable regardless of depth.
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
# Components
|
|
304
|
+
|
|
305
|
+
Most applications reuse the same UI patterns:
|
|
306
|
+
|
|
307
|
+
- Tables
|
|
308
|
+
- Sidebars
|
|
309
|
+
- Filters
|
|
310
|
+
- Menus
|
|
311
|
+
- Forms
|
|
312
|
+
- Modals
|
|
313
|
+
|
|
314
|
+
Traditional frameworks often recreate these components repeatedly.
|
|
315
|
+
|
|
316
|
+
```ts
|
|
317
|
+
class UsersPage {
|
|
318
|
+
usersTable = ...
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
class OrdersPage {
|
|
322
|
+
ordersTable = ...
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
class ProductsPage {
|
|
326
|
+
productsTable = ...
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Same component.
|
|
331
|
+
|
|
332
|
+
Different implementation.
|
|
333
|
+
|
|
334
|
+
Duplicate maintenance.
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
## Reusable Components
|
|
339
|
+
|
|
340
|
+
With pw-core, components become first-class citizens.
|
|
341
|
+
|
|
342
|
+
```ts
|
|
343
|
+
export class TransactionsPage extends TypedPage<typeof config> {
|
|
344
|
+
constructor(page: Page) {
|
|
345
|
+
super(page, config);
|
|
346
|
+
this.transactionsTable = new Table<Transaction>(tableLocator);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
Use the same component across:
|
|
352
|
+
|
|
353
|
+
- Users
|
|
354
|
+
- Orders
|
|
355
|
+
- Products
|
|
356
|
+
- Reports
|
|
357
|
+
|
|
358
|
+
Define once.
|
|
359
|
+
|
|
360
|
+
Same methods across pages, Reuse everywhere.
|
|
361
|
+
|
|
362
|
+
Exactly how frontend teams build reusable UI components.
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
# Page Registry
|
|
367
|
+
|
|
368
|
+
Not every page requires a custom Page Object.
|
|
369
|
+
|
|
370
|
+
Many pages only contain:
|
|
371
|
+
|
|
372
|
+
- URLs
|
|
373
|
+
- testIds
|
|
374
|
+
- selectors
|
|
375
|
+
|
|
376
|
+
For those pages, use `createPageRegistry`.
|
|
377
|
+
|
|
378
|
+
```ts
|
|
379
|
+
import { createPageRegistry } from "pw-core/page";
|
|
380
|
+
|
|
381
|
+
export const test = createPageRegistry({
|
|
382
|
+
loginPage: {
|
|
383
|
+
url: "/login",
|
|
384
|
+
|
|
385
|
+
testIds: {
|
|
386
|
+
username: "username-input",
|
|
387
|
+
password: "password-input",
|
|
388
|
+
submit: "login-button",
|
|
389
|
+
},
|
|
390
|
+
},
|
|
391
|
+
});
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
---
|
|
395
|
+
|
|
396
|
+
## Automatic Fixtures
|
|
397
|
+
|
|
398
|
+
Pages immediately become fixtures.
|
|
399
|
+
|
|
400
|
+
```ts
|
|
401
|
+
test("login", async ({ loginPage }) => {
|
|
402
|
+
await loginPage.fill("username", "admin");
|
|
403
|
+
|
|
404
|
+
await loginPage.fill("password", "password");
|
|
405
|
+
|
|
406
|
+
await loginPage.click("submit");
|
|
407
|
+
});
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
No Page Object.
|
|
411
|
+
|
|
412
|
+
No fixture setup.
|
|
413
|
+
|
|
414
|
+
No boilerplate.
|
|
415
|
+
|
|
416
|
+
---
|
|
417
|
+
|
|
418
|
+
# Extending Registry Pages
|
|
419
|
+
|
|
420
|
+
As pages become more complex, you can extend them.
|
|
421
|
+
|
|
422
|
+
```ts
|
|
423
|
+
class LoginPage extends test.classes.loginPage {
|
|
424
|
+
async login() {
|
|
425
|
+
await this.fill("username", "admin");
|
|
426
|
+
|
|
427
|
+
await this.fill("password", "password");
|
|
428
|
+
|
|
429
|
+
await this.click("submit");
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
Update the fixture:
|
|
435
|
+
|
|
436
|
+
```ts
|
|
437
|
+
export const test = registry.extend({
|
|
438
|
+
loginPage: LoginPage,
|
|
439
|
+
});
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
Usage:
|
|
443
|
+
|
|
444
|
+
```ts
|
|
445
|
+
await loginPage.login();
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
Start simple.
|
|
449
|
+
|
|
450
|
+
Extend only when needed.
|
|
451
|
+
|
|
452
|
+
---
|
|
453
|
+
|
|
454
|
+
# AI-Friendly Automation
|
|
455
|
+
|
|
456
|
+
Most automation frameworks are inconsistent.
|
|
457
|
+
|
|
458
|
+
One page uses:
|
|
459
|
+
|
|
460
|
+
```ts
|
|
461
|
+
page.locator(...)
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
Another:
|
|
465
|
+
|
|
466
|
+
```ts
|
|
467
|
+
page.getByTestId(...)
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
Another:
|
|
471
|
+
|
|
472
|
+
```ts
|
|
473
|
+
page.getByRole(...)
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
Another:
|
|
477
|
+
|
|
478
|
+
```ts
|
|
479
|
+
page.locator(...).locator(...)
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
AI agents must understand every pattern.
|
|
483
|
+
|
|
484
|
+
With pw-core:
|
|
485
|
+
|
|
486
|
+
```ts
|
|
487
|
+
page.click(...)
|
|
488
|
+
page.fill(...)
|
|
489
|
+
page.verify(...)
|
|
490
|
+
page.locator(...)
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
Everything follows the same structure.
|
|
494
|
+
|
|
495
|
+
Benefits:
|
|
496
|
+
|
|
497
|
+
- Reduced context size
|
|
498
|
+
- Reduced token usage
|
|
499
|
+
- Better AI-generated automation
|
|
500
|
+
- Easier reviews
|
|
501
|
+
- Faster onboarding
|
|
502
|
+
|
|
503
|
+
---
|
|
504
|
+
|
|
505
|
+
# Package Overview
|
|
506
|
+
|
|
507
|
+
| Import | Purpose |
|
|
508
|
+
| ------------------------- | -------------------------------------------------------- |
|
|
509
|
+
| `pw-core` | Browser storage helpers |
|
|
510
|
+
| `pw-core/page` | TypedPage, Page Registry, assertions, page configuration |
|
|
511
|
+
| `pw-core/component/table` | Reusable typed table component |
|
|
512
|
+
|
|
513
|
+
---
|
|
514
|
+
|
|
515
|
+
# Documentation
|
|
516
|
+
|
|
517
|
+
| Document | Description |
|
|
518
|
+
| -------------------- | ------------------------------------------------------ |
|
|
519
|
+
| `docs/page.md` | TypedPage, Page Registry, locator chaining, assertions |
|
|
520
|
+
| `docs/components.md` | Reusable UI components |
|
|
521
|
+
| `docs/helpers.md` | Browser storage helpers |
|
|
522
|
+
|
|
523
|
+
---
|
|
524
|
+
|
|
525
|
+
# When To Use TypedPage
|
|
526
|
+
|
|
527
|
+
Use TypedPage when:
|
|
528
|
+
|
|
529
|
+
- The page contains workflows
|
|
530
|
+
- The page contains business logic
|
|
531
|
+
- The page contains reusable actions
|
|
532
|
+
- Multiple tests interact with the page
|
|
533
|
+
|
|
534
|
+
Example:
|
|
535
|
+
|
|
536
|
+
```ts
|
|
537
|
+
await login.login();
|
|
538
|
+
|
|
539
|
+
await settings.toggleLogo("enable");
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
---
|
|
543
|
+
|
|
544
|
+
# When To Use Page Registry
|
|
545
|
+
|
|
546
|
+
Use Page Registry when:
|
|
547
|
+
|
|
548
|
+
- Pages only contain locators
|
|
549
|
+
- No custom methods are needed
|
|
550
|
+
- You want automatic fixtures
|
|
551
|
+
- You want minimal framework code
|
|
552
|
+
|
|
553
|
+
---
|
|
554
|
+
|
|
555
|
+
# Summary
|
|
556
|
+
|
|
557
|
+
pw-core is not a replacement for Playwright.
|
|
558
|
+
|
|
559
|
+
It is an architectural layer designed to create:
|
|
560
|
+
|
|
561
|
+
- Readable tests
|
|
562
|
+
- Reusable components
|
|
563
|
+
- Strongly typed pages
|
|
564
|
+
- Automatic fixtures
|
|
565
|
+
- Single source of truth configurations
|
|
566
|
+
- AI-friendly automation frameworks
|
|
567
|
+
- Scalable enterprise test suites
|
|
568
|
+
|
|
569
|
+
The larger the framework becomes, the more value pw-core provides.
|
|
570
|
+
|
|
571
|
+
---
|
|
572
|
+
|
|
573
|
+
More components, helpers, generators, and framework capabilities are planned.
|
|
574
|
+
|
|
575
|
+
Stay tuned.
|
|
576
|
+
|
|
577
|
+
## License
|
|
578
|
+
|
|
579
|
+
MIT
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Locator } from '@playwright/test';
|
|
2
|
+
/**
|
|
3
|
+
* A custom Array subclass representing a list of table rows.
|
|
4
|
+
* Inherits all standard Array methods while adding type-safe get and getAll capabilities.
|
|
5
|
+
*/
|
|
6
|
+
export declare class TableRows<T extends Record<string, any>> extends Array<T> {
|
|
7
|
+
constructor(...items: T[]);
|
|
8
|
+
/**
|
|
9
|
+
* Overloaded get method:
|
|
10
|
+
* 1. rows.get('id') -> returns the 'id' value of the first row (T[K] | undefined)
|
|
11
|
+
* 2. rows.get('id', 'value') -> returns the first matching row object (T | undefined)
|
|
12
|
+
*/
|
|
13
|
+
get<K extends keyof T>(key: K): T[K] | undefined;
|
|
14
|
+
get<K extends keyof T>(key: K, value: T[K]): T | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Overloaded getAll method:
|
|
17
|
+
* 1. rows.getAll('id') -> returns an array of all rows' 'id' values (T[K][])
|
|
18
|
+
* 2. rows.getAll('id', 'value') -> returns an array of all matching row objects (T[])
|
|
19
|
+
*/
|
|
20
|
+
getAll<K extends keyof T>(key: K): T[K][];
|
|
21
|
+
getAll<K extends keyof T>(key: K, value: T[K]): T[];
|
|
22
|
+
}
|
|
23
|
+
export declare class Table<T extends Record<string, any>> {
|
|
24
|
+
readonly root: Locator;
|
|
25
|
+
constructor(root: Locator);
|
|
26
|
+
/**
|
|
27
|
+
* Retrieves all the headers from the table as lowercase strings.
|
|
28
|
+
*/
|
|
29
|
+
getHeaders(): Promise<string[]>;
|
|
30
|
+
/**
|
|
31
|
+
* Retrieves all the rows of the table as typed objects.
|
|
32
|
+
* Dynamically maps table headers to object keys.
|
|
33
|
+
*/
|
|
34
|
+
getRows(): Promise<T[]>;
|
|
35
|
+
/**
|
|
36
|
+
* Retrieves all rows from the table, returned as a custom TableRows collection
|
|
37
|
+
* with type-safe finder helper methods.
|
|
38
|
+
*/
|
|
39
|
+
get(): Promise<TableRows<T>>;
|
|
40
|
+
/**
|
|
41
|
+
* Gets the total count of data rows in the table.
|
|
42
|
+
*/
|
|
43
|
+
getRowCount(): Promise<number>;
|
|
44
|
+
/**
|
|
45
|
+
* Retrieves the value of a specific cell by row index and column key.
|
|
46
|
+
*/
|
|
47
|
+
getCellValue(rowIndex: number, column: keyof T): Promise<string>;
|
|
48
|
+
}
|