pomwright 1.1.0 → 1.2.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 +589 -0
- package/README.md +228 -43
- package/dist/index.d.mts +524 -128
- package/dist/index.d.ts +524 -128
- package/dist/index.js +478 -277
- package/dist/index.mjs +467 -266
- package/index.ts +10 -4
- package/package.json +22 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,594 @@
|
|
|
1
1
|
# pomwright
|
|
2
2
|
|
|
3
|
+
## 1.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#31](https://github.com/DyHex/POMWright/pull/31) [`bc29062`](https://github.com/DyHex/POMWright/commit/bc2906258a88acde1dd1479d7a000d036912f45a) Thanks [@DyHex](https://github.com/DyHex)! - # LocatorSchema Enhancements: `filter` Property, `.addFilter()`, Updated `.update()`, and Enhanced `getNestedLocator()`
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
This release introduces several key enhancements to the POMWright. These improvements aim to provide greater flexibility, maintainability, and type safety when constructing and managing locators in your Page Object Models (POMs). The main updates include:
|
|
12
|
+
|
|
13
|
+
- **New `filter` Property**: Extends filtering capabilities beyond the `locator` method.
|
|
14
|
+
- **New `.addFilter()` Method**: Facilitates dynamic addition of filters to locators.
|
|
15
|
+
- **Updated `.update()` Method**: Replaces deprecated `.update` and `.updates` methods with a more intuitive interface (none-breaking).
|
|
16
|
+
- **Enhanced `getNestedLocator()` Method**: Shifts from index-based to subPath-based indexing for better readability and maintainability.
|
|
17
|
+
- **Export of `LocatorSchemaWithoutPath`**: Enables partial or full reuse of locator schemas across different contexts.
|
|
18
|
+
|
|
19
|
+
## Changes
|
|
20
|
+
|
|
21
|
+
### 1. New `filter` Property for `LocatorSchema`
|
|
22
|
+
|
|
23
|
+
#### Purpose
|
|
24
|
+
|
|
25
|
+
The existing `locatorOptions` property in `LocatorSchema` is specific to the `locator` method, limiting its applicability. The newly introduced `filter` property extends filtering capabilities to all locator types (e.g., `role`, `label`, `placeholder`, `testid`, `id`, etc.), excluding `frameLocator`.
|
|
26
|
+
|
|
27
|
+
#### Behavior
|
|
28
|
+
|
|
29
|
+
- **Global Applicability**: Unlike `locatorOptions`, the `filter` property can be applied to various locator methods, enhancing versatility.
|
|
30
|
+
- **Priority Application**: When a `filter` is defined, it is always applied and will always be the first filter applied to the locator created from that LocatorSchema, the only exception is `locatorOptions` used with `locator`. This ensures consistent and prioritized filtering across different locator types.
|
|
31
|
+
|
|
32
|
+
#### Usage Examples
|
|
33
|
+
|
|
34
|
+
**Before: Using `locatorOptions` (Limited to `locator` method)**
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
this.locators.addSchema("main.products.radio@junior", {
|
|
38
|
+
locator: "radio",
|
|
39
|
+
locatorOptions: { hasText: "some text" },
|
|
40
|
+
locatorMethod: GetByMethod.locator,
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**After: Using `filter` (Applicable to Multiple Locator Methods)**
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
this.locators.addSchema("main.products.radio@junior", {
|
|
48
|
+
locator: "input",
|
|
49
|
+
filter: { hasText: "some text" },
|
|
50
|
+
locatorMethod: GetByMethod.locator,
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**With `role` Locator Method:**
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
this.locators.addSchema("main.products.radio@junior", {
|
|
58
|
+
role: "radio",
|
|
59
|
+
filter: { hasText: "some text" },
|
|
60
|
+
locatorMethod: GetByMethod.role,
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Combined Example:**
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
this.locators.addSchema("main.subscription.form.item.section@header", {
|
|
68
|
+
role: "region",
|
|
69
|
+
roleOptions: { name: "Subscription Details" },
|
|
70
|
+
filter: { hasText: "Mobile 2 GB" },
|
|
71
|
+
locatorMethod: GetByMethod.role,
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
#### Benefits
|
|
76
|
+
|
|
77
|
+
- **Enhanced Flexibility**: Apply default filters to various LocatorSchema.
|
|
78
|
+
- **Improved Locator Precision**: Chain multiple filters to narrow down locators based on complex criteria.
|
|
79
|
+
- **Backward Compatibility**: Existing `locatorOptions` works the same way as before, ensuring no disruption to current implementations.
|
|
80
|
+
|
|
81
|
+
### 2. New `.addFilter()` Method for `.getLocatorSchema()`
|
|
82
|
+
|
|
83
|
+
#### Purpose
|
|
84
|
+
|
|
85
|
+
The `.addFilter()` method allows dynamic addition of filters to any part of the locator chain, enhancing flexibility in locator construction without modifying the original `LocatorSchema` at any point in a test.
|
|
86
|
+
|
|
87
|
+
#### Method Signature
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
.addFilter(
|
|
91
|
+
subPath: SubPaths<LocatorSchemaPathType, LocatorSubstring>,
|
|
92
|
+
filterData: FilterEntry
|
|
93
|
+
): LocatorSchemaWithMethods<LocatorSchemaPathType, LocatorSubstring>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
#### Parameters
|
|
97
|
+
|
|
98
|
+
- `subPath`: A valid sub-path within the `LocatorSchemaPath` argument to .getLocatorSchema().
|
|
99
|
+
- `filterData`: An object defining the filter criteria, which can include:
|
|
100
|
+
- `has?: Locator`
|
|
101
|
+
- `hasNot?: Locator`
|
|
102
|
+
- `hasText?: string | RegExp`
|
|
103
|
+
- `hasNotText?: string | RegExp`
|
|
104
|
+
|
|
105
|
+
#### Usage Examples
|
|
106
|
+
|
|
107
|
+
**Adding Filters to Specific Sub-Paths:**
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
const allCheckboxesForBrandFilters = await poc
|
|
111
|
+
.getLocatorSchema("main.products.searchControls.filterType.label.checkbox")
|
|
112
|
+
.addFilter("main.products.searchControls.filterType", {
|
|
113
|
+
hasText: /Producer/i,
|
|
114
|
+
})
|
|
115
|
+
.getNestedLocator();
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Chaining Multiple Filters:**
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
const refinedCheckboxes = await poc
|
|
122
|
+
.getLocatorSchema("main.products.searchControls.filterType.label.checkbox")
|
|
123
|
+
.addFilter("main.products.searchControls.filterType", {
|
|
124
|
+
hasText: /Producer/i,
|
|
125
|
+
hasNotText: /discontinued/i,
|
|
126
|
+
})
|
|
127
|
+
.addFilter("main.products.searchControls.filterType.label", {
|
|
128
|
+
hasText: "Samsung",
|
|
129
|
+
})
|
|
130
|
+
.addFilter("main.products.searchControls.filterType.label", {
|
|
131
|
+
has: poc.page.getByRole("checkbox"),
|
|
132
|
+
})
|
|
133
|
+
.getNestedLocator();
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Combining `filter` Property and `.addFilter()`:**
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
this.locators.addSchema("main.subscription.form.item.section@header", {
|
|
140
|
+
role: "region",
|
|
141
|
+
roleOptions: { name: "Subscription Details" },
|
|
142
|
+
filter: { hasText: "Mobile 2 GB" },
|
|
143
|
+
locatorMethod: GetByMethod.role,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
const specificSection = await poc
|
|
147
|
+
.getLocatorSchema("main.subscription.form.item.section@header")
|
|
148
|
+
.addFilter("main.subscription.form.item.section@header", {
|
|
149
|
+
hasText: "Additional Services",
|
|
150
|
+
})
|
|
151
|
+
.getNestedLocator();
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
#### Benefits
|
|
155
|
+
|
|
156
|
+
- **Dynamic Filtering**: Add or modify filters on-the-fly without altering the original schema definitions.
|
|
157
|
+
- **Chainability**: Easily chain multiple `.addFilter()` calls to build complex locator chains.
|
|
158
|
+
- **Error Handling**: Attempts to add filters to invalid sub-paths will throw descriptive errors (compile & run-time), ensuring only valid paths are modified.
|
|
159
|
+
|
|
160
|
+
### 3. Updated `.update()` Method
|
|
161
|
+
|
|
162
|
+
#### Purpose
|
|
163
|
+
|
|
164
|
+
The existing `.update` and `.updates` methods are deprecated and will be removed in version 2.0.0. `.update` could only update the last LocatorSchema in the chain and `.updates` relied on index-based updates, which posed maintainability challenges, especially when renaming `LocatorSchemaPath` strings. The new `.update` method leverages valid `subPaths` of `LocatorSchemaPath` instead of indices, enhancing readability and reducing manual maintenance.
|
|
165
|
+
|
|
166
|
+
#### New Method Signature
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
.update(
|
|
170
|
+
subPath: SubPaths<LocatorSchemaPathType, LocatorSubstring>,
|
|
171
|
+
modifiedSchema: Partial<LocatorSchemaWithoutPath>
|
|
172
|
+
): LocatorSchemaWithMethods<LocatorSchemaPathType, LocatorSubstring>
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
#### Deprecation of Old Methods
|
|
176
|
+
|
|
177
|
+
- **Old `.update(updates: Partial<LocatorSchemaWithoutPath>): LocatorSchemaWithMethods`**
|
|
178
|
+
- **Old `.updates(indexedUpdates: { [index: number]: Partial<LocatorSchemaWithoutPath> | null }): LocatorSchemaWithMethods`**
|
|
179
|
+
|
|
180
|
+
These methods are marked as deprecated and will be removed in version 2.0.0.
|
|
181
|
+
|
|
182
|
+
#### Usage Examples
|
|
183
|
+
|
|
184
|
+
**Case A: Update the Last `LocatorSchema` in the Chain**
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
const editBtn = await profile
|
|
188
|
+
.getLocatorSchema("content.region.details.button.edit")
|
|
189
|
+
.update("content.region.details.button.edit", {
|
|
190
|
+
roleOptions: { name: "new accessibility name" },
|
|
191
|
+
})
|
|
192
|
+
.getNestedLocator();
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**Case B: Update a `LocatorSchema` Earlier in the Chain**
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
const editBtn = await profile
|
|
199
|
+
.getLocatorSchema("content.region.details.button.edit")
|
|
200
|
+
.update("content.region.details", {
|
|
201
|
+
roleOptions: { name: "new accessibility name" },
|
|
202
|
+
})
|
|
203
|
+
.getNestedLocator();
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**Case C: Update Multiple `LocatorSchema` in the Chain**
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
const editBtn = await profile
|
|
210
|
+
.getLocatorSchema("content.region.details.button.edit")
|
|
211
|
+
.update("content", { roleOptions: { name: "new accessibility name" } })
|
|
212
|
+
.update("content.region", {
|
|
213
|
+
roleOptions: { name: "new accessibility name" },
|
|
214
|
+
})
|
|
215
|
+
.update("content.region.details", {
|
|
216
|
+
roleOptions: { name: "new accessibility name" },
|
|
217
|
+
})
|
|
218
|
+
.update("content.region.details.button", {
|
|
219
|
+
roleOptions: { name: "new accessibility name" },
|
|
220
|
+
})
|
|
221
|
+
.update("content.region.details.button.edit", {
|
|
222
|
+
roleOptions: { name: "new accessibility name" },
|
|
223
|
+
})
|
|
224
|
+
.getNestedLocator();
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
#### Benefits
|
|
228
|
+
|
|
229
|
+
- **Enhanced Readability**: Using `LocatorSchemaPath` strings makes the code more intuitive and easier to understand.
|
|
230
|
+
- **Reduced Maintenance**: Eliminates the need to manage index-based references, especially when `LocatorSchemaPath` strings are renamed or restructured.
|
|
231
|
+
- **Type Safety**: Leverages TypeScript's type system to ensure only valid `subPath` strings are used, enhancing developer experience with accurate Intellisense suggestions.
|
|
232
|
+
|
|
233
|
+
### 4. Enhanced `getNestedLocator()` Method
|
|
234
|
+
|
|
235
|
+
#### Purpose
|
|
236
|
+
|
|
237
|
+
The `getNestedLocator()` method has been updated to utilize valid `subPath`'s of `LocatorSchemaPath` instead of numeric indices when specifying `.nth(n)` occurrences within a locator chain. The old index-based method is deprecated and will be removed in version 2.0.0.
|
|
238
|
+
|
|
239
|
+
#### New Method Signature
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
getNestedLocator(
|
|
243
|
+
subPathIndices?: { [K in SubPaths<LocatorSchemaPathType, LocatorSubstring>]: number | null }
|
|
244
|
+
): Promise<Locator>
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
#### Deprecation of Old Method
|
|
248
|
+
|
|
249
|
+
- **Old `getNestedLocator(indices?: { [key: number]: number | null }): Promise<Locator>`**
|
|
250
|
+
|
|
251
|
+
This method is marked as deprecated and will be removed in version 2.0.0.
|
|
252
|
+
|
|
253
|
+
#### Usage Examples
|
|
254
|
+
|
|
255
|
+
**Old Usage (Deprecated):**
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
const saveBtn = await profile.getNestedLocator(
|
|
259
|
+
"content.region.details.button.save",
|
|
260
|
+
{ 4: 2 }
|
|
261
|
+
);
|
|
262
|
+
|
|
263
|
+
const editBtn = await profile
|
|
264
|
+
.getLocatorSchema("content.region.details.button.edit")
|
|
265
|
+
.getNestedLocator({ 2: index });
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
**New Usage:**
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
const saveBtn = await profile.getNestedLocator(
|
|
272
|
+
"content.region.details.button.save",
|
|
273
|
+
{
|
|
274
|
+
"content.region.details.button.save": 2,
|
|
275
|
+
}
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
const editBtn = await profile
|
|
279
|
+
.getLocatorSchema("content.region.details.button.edit")
|
|
280
|
+
.getNestedLocator({ "content.region.details": index });
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
#### Benefits
|
|
284
|
+
|
|
285
|
+
- **Improved Readability**: SubPath-based indexing is more intuitive and aligns with the hierarchical nature of locator paths.
|
|
286
|
+
- **Enhanced Maintainability**: Reduces confusion and manual updates when `LocatorSchemaPath` strings are modified.
|
|
287
|
+
- **Type Safety and Intellisense**: TypeScript provides accurate suggestions for valid `subPath` keys, enhancing the developer experience and reducing errors.
|
|
288
|
+
|
|
289
|
+
### 5. Export of `LocatorSchemaWithoutPath`
|
|
290
|
+
|
|
291
|
+
#### Purpose
|
|
292
|
+
|
|
293
|
+
The `LocatorSchemaWithoutPath` type is now exported through `index.ts`, enabling full or partial reuse of `LocatorSchema` definitions across different `addSchema` calls within the same or different `initLocatorSchemas` functions.
|
|
294
|
+
|
|
295
|
+
#### Usage Example
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
import { GetByMethod, LocatorSchemaWithoutPath } from "pomwright";
|
|
299
|
+
import { missingInputError } from "@common/page-components/errors.locatorSchema.ts";
|
|
300
|
+
|
|
301
|
+
export type LocatorSchemaPath =
|
|
302
|
+
| "body"
|
|
303
|
+
| "body.main"
|
|
304
|
+
| "body.main.section"
|
|
305
|
+
| "body.main.section@products"
|
|
306
|
+
| "body.main.section@userInfo"
|
|
307
|
+
| "body.main.section@userInfo.input@email"
|
|
308
|
+
| "body.main.section@userInfo.inputError"
|
|
309
|
+
| "body.main.section@deliveryInfo";
|
|
310
|
+
|
|
311
|
+
export function initLocatorSchemas(
|
|
312
|
+
locators: GetLocatorBase<LocatorSchemaPath>
|
|
313
|
+
) {
|
|
314
|
+
locators.addSchema("body", {
|
|
315
|
+
locator: "body",
|
|
316
|
+
locatorMethod: GetByMethod.locator,
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
locators.addSchema("body.main", {
|
|
320
|
+
locator: "main",
|
|
321
|
+
locatorMethod: GetByMethod.locator,
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
const region: LocatorSchemaWithoutPath = {
|
|
325
|
+
role: "region",
|
|
326
|
+
locatorMethod: GetByMethod.role,
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
locators.addSchema("body.main.section", {
|
|
330
|
+
...region,
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
locators.addSchema("body.main.section@products", {
|
|
334
|
+
...region,
|
|
335
|
+
roleOptions: { name: "Products" },
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
locators.addSchema("body.main.section@userInfo", {
|
|
339
|
+
...region,
|
|
340
|
+
roleOptions: { name: "Contact Info" },
|
|
341
|
+
filter: { hasText: /e-mail/i },
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
locators.addSchema("body.main.section@userInfo.input@email", {
|
|
345
|
+
role: "textbox",
|
|
346
|
+
roleOptions: { name: "Input your e-mail" },
|
|
347
|
+
locatorMethod: GetByMethod.role,
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
locators.addSchema("body.main.section@userInfo.inputError", {
|
|
351
|
+
...missingInputError,
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
locators.addSchema("body.main.section@deliveryInfo", {
|
|
355
|
+
...region,
|
|
356
|
+
roleOptions: { name: "Delivery Info" },
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
#### Benefits
|
|
362
|
+
|
|
363
|
+
- **Code Reusability**: Facilitates the reuse of common locator schema fragments across different contexts, reducing redundancy.
|
|
364
|
+
- **Modular Definitions**: Encourages a modular approach to defining locators, enhancing organization and scalability.
|
|
365
|
+
|
|
366
|
+
## Deprecations
|
|
367
|
+
|
|
368
|
+
### 1. Deprecated `.update` and `.updates` Methods
|
|
369
|
+
|
|
370
|
+
- **Old `.update(updates: Partial<LocatorSchemaWithoutPath>): LocatorSchemaWithMethods`**
|
|
371
|
+
- **Old `.updates(indexedUpdates: { [index: number]: Partial<LocatorSchemaWithoutPath> | null }): LocatorSchemaWithMethods`**
|
|
372
|
+
|
|
373
|
+
**Reason:**
|
|
374
|
+
`.updates` relied on index-based updates, which are prone to errors and require manual maintenance, especially when `LocatorSchemaPath` strings are renamed or restructured. While the old `.update` method could only update the last LocatorSchema in the path. Confusing with multiple methods instead of just one.
|
|
375
|
+
|
|
376
|
+
**Replacement:**
|
|
377
|
+
Use the new `.update(subPath, modifiedSchema)` method, which leverages `subpath`'s of valid `LocatorSchemaPath` strings for more intuitive and maintainable updates.
|
|
378
|
+
|
|
379
|
+
**Removal Schedule:**
|
|
380
|
+
These methods are deprecated and will be removed in version 2.0.0.
|
|
381
|
+
|
|
382
|
+
### 2. Deprecated Old `getNestedLocator` Method
|
|
383
|
+
|
|
384
|
+
- **Old `getNestedLocator(indices?: { [key: number]: number | null }): Promise<Locator>`**
|
|
385
|
+
|
|
386
|
+
**Reason:**
|
|
387
|
+
Index-based indexing is less readable and requires manual updates when `LocatorSchemaPath` strings change.
|
|
388
|
+
|
|
389
|
+
**Replacement:**
|
|
390
|
+
Use the updated `getNestedLocator(subPathIndices?: { [K in SubPaths<LocatorSchemaPathType, LocatorSubstring>]: number | null }): Promise<Locator>` method, which utilizes `LocatorSchemaPath` strings for indexing.
|
|
391
|
+
|
|
392
|
+
**Removal Schedule:**
|
|
393
|
+
This method is deprecated and will be removed in version 2.0.0.
|
|
394
|
+
|
|
395
|
+
## Migration Guide
|
|
396
|
+
|
|
397
|
+
### Updating `.update` and `.updates` Methods
|
|
398
|
+
|
|
399
|
+
**Old Usage:**
|
|
400
|
+
|
|
401
|
+
```typescript
|
|
402
|
+
const allCheckboxes = await poc
|
|
403
|
+
.getLocatorSchema("main.products.searchControls.filterType.label.checkbox")
|
|
404
|
+
.updates({ 3: { locatorOptions: { hasText: /Producer/i } } })
|
|
405
|
+
.getNestedLocator();
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
**New Usage:**
|
|
409
|
+
|
|
410
|
+
```typescript
|
|
411
|
+
const allCheckboxes = await poc
|
|
412
|
+
.getLocatorSchema("main.products.searchControls.filterType.label.checkbox")
|
|
413
|
+
.update("main.products.searchControls.filterType", {
|
|
414
|
+
locatorOptions: { hasText: /Producer/i },
|
|
415
|
+
})
|
|
416
|
+
.getNestedLocator();
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### Updating `getNestedLocator` Method
|
|
420
|
+
|
|
421
|
+
**Old Usage (Deprecated):**
|
|
422
|
+
|
|
423
|
+
```typescript
|
|
424
|
+
const saveBtn = await profile.getNestedLocator(
|
|
425
|
+
"content.region.details.button.save",
|
|
426
|
+
{ 4: 2 }
|
|
427
|
+
);
|
|
428
|
+
|
|
429
|
+
const editBtn = await profile
|
|
430
|
+
.getLocatorSchema("content.region.details.button.edit")
|
|
431
|
+
.getNestedLocator({ 2: index });
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
**New Usage:**
|
|
435
|
+
|
|
436
|
+
```typescript
|
|
437
|
+
const saveBtn = await profile.getNestedLocator(
|
|
438
|
+
"content.region.details.button.save",
|
|
439
|
+
{
|
|
440
|
+
"content.region.details.button.save": 2,
|
|
441
|
+
}
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
const editBtn = await profile
|
|
445
|
+
.getLocatorSchema("content.region.details.button.edit")
|
|
446
|
+
.getNestedLocator({ "content.region.details": index });
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
### Utilizing `LocatorSchemaPath` Instead of Indices
|
|
450
|
+
|
|
451
|
+
Transition from index-based to `LocatorSchemaPath`-based indexing to improve code readability and maintainability.
|
|
452
|
+
|
|
453
|
+
**Old Example:**
|
|
454
|
+
|
|
455
|
+
```typescript
|
|
456
|
+
const allCheckboxes = await poc
|
|
457
|
+
.getLocatorSchema("main.form.item.checkbox")
|
|
458
|
+
.updates({ 3: { locatorOptions: { hasText: /Producer/i } } })
|
|
459
|
+
.getNestedLocator();
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
**New Example:**
|
|
463
|
+
|
|
464
|
+
```typescript
|
|
465
|
+
const allCheckboxes = await poc
|
|
466
|
+
.getLocatorSchema("main.form.item.checkbox")
|
|
467
|
+
.update("main.form.item", { locatorOptions: { hasText: /Producer/i } })
|
|
468
|
+
.getNestedLocator();
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
## Example in Context
|
|
472
|
+
|
|
473
|
+
### Defining a LocatorSchema with `filter` and Using `.addFilter()`
|
|
474
|
+
|
|
475
|
+
```typescript
|
|
476
|
+
// Defining LocatorSchemas
|
|
477
|
+
this.locators.addSchema("body.main.section@userInfo", {
|
|
478
|
+
role: "region",
|
|
479
|
+
roleOptions: { name: "Contact Info" },
|
|
480
|
+
filter: { hasText: /e-mail/i },
|
|
481
|
+
locatorMethod: GetByMethod.role,
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
// Dynamically adding additional filters using `.addFilter()`
|
|
485
|
+
const specificSection = await poc
|
|
486
|
+
.getLocatorSchema("body.main.section@userInfo")
|
|
487
|
+
.addFilter("body.main.section@userInfo", { hasText: "Additional Services" })
|
|
488
|
+
.getNestedLocator();
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
### Updating LocatorSchemas with the New `.update()` Method
|
|
492
|
+
|
|
493
|
+
```typescript
|
|
494
|
+
const editBtn = await profile
|
|
495
|
+
.getLocatorSchema("content.region.details.button.edit")
|
|
496
|
+
.update("content.region.details.button.edit", {
|
|
497
|
+
roleOptions: { name: "new accessibility name" },
|
|
498
|
+
})
|
|
499
|
+
.getNestedLocator();
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
### Reusing LocatorSchemas with `LocatorSchemaWithoutPath`
|
|
503
|
+
|
|
504
|
+
```typescript
|
|
505
|
+
import { GetByMethod, LocatorSchemaWithoutPath } from "pomwright";
|
|
506
|
+
import { missingInputError } from "@common/page-components/errors.locatorSchema.ts";
|
|
507
|
+
|
|
508
|
+
export type LocatorSchemaPath =
|
|
509
|
+
| "body"
|
|
510
|
+
| "body.main"
|
|
511
|
+
| "body.main.section"
|
|
512
|
+
| "body.main.section@products"
|
|
513
|
+
| "body.main.section@userInfo"
|
|
514
|
+
| "body.main.section@userInfo.input@email"
|
|
515
|
+
| "body.main.section@userInfo.inputError"
|
|
516
|
+
| "body.main.section@deliveryInfo";
|
|
517
|
+
|
|
518
|
+
export function initLocatorSchemas(
|
|
519
|
+
locators: GetLocatorBase<LocatorSchemaPath>
|
|
520
|
+
) {
|
|
521
|
+
locators.addSchema("body", {
|
|
522
|
+
locator: "body",
|
|
523
|
+
locatorMethod: GetByMethod.locator,
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
locators.addSchema("body.main", {
|
|
527
|
+
locator: "main",
|
|
528
|
+
locatorMethod: GetByMethod.locator,
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
const region: LocatorSchemaWithoutPath = {
|
|
532
|
+
role: "region",
|
|
533
|
+
locatorMethod: GetByMethod.role,
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
locators.addSchema("body.main.section", {
|
|
537
|
+
...region,
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
locators.addSchema("body.main.section@products", {
|
|
541
|
+
...region,
|
|
542
|
+
roleOptions: { name: "Products" },
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
locators.addSchema("body.main.section@userInfo", {
|
|
546
|
+
...region,
|
|
547
|
+
roleOptions: { name: "Contact Info" },
|
|
548
|
+
filter: { hasText: /e-mail/i },
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
locators.addSchema("body.main.section@userInfo.input@email", {
|
|
552
|
+
role: "textbox",
|
|
553
|
+
roleOptions: { name: "Input your e-mail" },
|
|
554
|
+
locatorMethod: GetByMethod.role,
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
locators.addSchema("body.main.section@userInfo.inputError", {
|
|
558
|
+
...missingInputError,
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
locators.addSchema("body.main.section@deliveryInfo", {
|
|
562
|
+
...region,
|
|
563
|
+
roleOptions: { name: "Delivery Info" },
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
```
|
|
567
|
+
|
|
568
|
+
## Notes
|
|
569
|
+
|
|
570
|
+
- **Filter Application Order:**
|
|
571
|
+
The `filter` property in `LocatorSchema` is always applied first, followed by any filters added via `.addFilter()`. This ensures that predefined filters have priority over dynamically added ones.
|
|
572
|
+
|
|
573
|
+
- **Exclusion of `frameLocator`:**
|
|
574
|
+
The `filter` property does not apply to `frameLocator` locators. If you need to filter within frames, use the appropriate Playwright frame methods.
|
|
575
|
+
|
|
576
|
+
- **Type Safety and Intellisense:**
|
|
577
|
+
The new `.update()` and enhanced `getNestedLocator()` methods leverage TypeScript's type system to provide accurate Intellisense suggestions, enhancing developer experience and reducing errors.
|
|
578
|
+
|
|
579
|
+
## Conclusion
|
|
580
|
+
|
|
581
|
+
These enhancements to `LocatorSchema` and its associated methods significantly improve the flexibility, readability, and maintainability of locator management within your POMs. By adopting the new `filter` property, `.addFilter()` method, and updated `.update()` & `getNestedLocator()` methods, you can construct more precise and adaptable locators, facilitating robust and scalable test automation.
|
|
582
|
+
|
|
583
|
+
**Note:**
|
|
584
|
+
Ensure to migrate away from the deprecated `.update`, `.updates`, and old `getNestedLocator` methods before upgrading to version 2.0.0 to maintain compatibility and leverage the full benefits of these enhancements.
|
|
585
|
+
|
|
586
|
+
## 1.1.1
|
|
587
|
+
|
|
588
|
+
### Patch Changes
|
|
589
|
+
|
|
590
|
+
- [#29](https://github.com/DyHex/POMWright/pull/29) [`14b9bff`](https://github.com/DyHex/POMWright/commit/14b9bff99ec337a4d8b8940c9904620892faae3a) Thanks [@DyHex](https://github.com/DyHex)! - Exports ExtractFullUrlType, updates devDeps and adds additional keywords
|
|
591
|
+
|
|
3
592
|
## 1.1.0
|
|
4
593
|
|
|
5
594
|
### Minor Changes
|