pw-core 1.3.0 → 1.3.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/README.md +1 -0
- package/dist/cli.js +145 -421
- package/dist/codegen/action-formatter.d.ts +19 -0
- package/dist/codegen/action-formatter.js +160 -0
- package/dist/codegen/action-processor.d.ts +23 -0
- package/dist/codegen/action-processor.js +112 -0
- package/dist/codegen/floating-panel/client.d.ts +14 -1
- package/dist/codegen/floating-panel/client.js +20 -25
- package/dist/codegen/floating-panel/manager.js +16 -22
- package/dist/codegen/generator/action.validator.d.ts +2 -1
- package/dist/codegen/generator/action.validator.js +2 -1
- package/dist/codegen/generator/candidate-scorer.d.ts +7 -0
- package/dist/codegen/generator/candidate-scorer.js +206 -0
- package/dist/codegen/generator/dom-scanner.d.ts +39 -0
- package/dist/codegen/generator/dom-scanner.js +429 -0
- package/dist/codegen/generator/id-stability.d.ts +6 -0
- package/dist/codegen/generator/id-stability.js +38 -0
- package/dist/codegen/generator/index.d.ts +8 -10
- package/dist/codegen/generator/index.js +23 -678
- package/dist/codegen/generator/key-builder.d.ts +13 -0
- package/dist/codegen/generator/key-builder.js +32 -0
- package/dist/codegen/hover-tracker/client.d.ts +5 -0
- package/dist/codegen/hover-tracker/client.js +14 -15
- package/dist/codegen/hover-tracker/manager.js +2 -6
- package/dist/codegen/index.d.ts +9 -32
- package/dist/codegen/index.js +9 -1128
- package/dist/codegen/key-utils.d.ts +16 -0
- package/dist/codegen/key-utils.js +29 -1
- package/dist/codegen/project-finder.d.ts +29 -0
- package/dist/codegen/project-finder.js +283 -0
- package/dist/codegen/registry-matcher.d.ts +27 -0
- package/dist/codegen/registry-matcher.js +254 -0
- package/dist/codegen/registry-store.d.ts +52 -0
- package/dist/codegen/registry-store.js +652 -0
- package/dist/codegen/selector-parser.d.ts +16 -0
- package/dist/codegen/selector-parser.js +121 -0
- package/dist/codegen/types.d.ts +18 -1
- package/dist/codegen/uniqueness.d.ts +3 -0
- package/dist/codegen/uniqueness.js +15 -0
- package/dist/component/table.d.ts +2 -2
- package/dist/component/table.js +7 -6
- package/dist/index.d.ts +3 -0
- package/dist/index.js +19 -0
- package/dist/page/actions/locator-actions.d.ts +6 -10
- package/dist/page/actions/locator-actions.js +24 -23
- package/dist/page/assertions/verify-chain.d.ts +11 -13
- package/dist/page/assertions/verify-chain.js +26 -7
- package/dist/page/assertions/verify-helpers.d.ts +5 -15
- package/dist/page/config.d.ts +25 -25
- package/dist/page/locators/dynamic-locator-resolver.js +22 -9
- package/dist/page/locators/resolver.d.ts +17 -11
- package/dist/page/locators/resolver.js +84 -78
- package/dist/page/registry.d.ts +35 -35
- package/dist/page/registry.js +86 -82
- package/dist/page/typed-page.d.ts +1 -0
- package/dist/page/typed-page.js +22 -12
- package/dist/page/types/proxy-methods.d.ts +11 -19
- package/dist/page/types/validation.d.ts +1 -1
- package/dist/page/utils/formatter.d.ts +3 -3
- package/dist/page/utils/formatter.js +5 -2
- package/package.json +6 -3
package/dist/page/config.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { Locator } from '@playwright/test';
|
|
2
|
-
import type { DynamicTestIdEntry, DynamicSelectorEntry } from './locators/dynamic-locator-resolver
|
|
3
|
-
export { ProxyLocatorMethods } from './types/proxy-methods
|
|
4
|
-
export type { DynamicLocatorEntry, DynamicTestIdEntry, DynamicSelectorEntry } from './locators/dynamic-locator-resolver
|
|
5
|
-
import { AllowedMethodKeys } from '../types/methods';
|
|
6
|
-
import { StrategyType, LocatorStrategyOptions } from '../types/strategies';
|
|
7
|
-
import { ResolvedTestIdKeys, ValidateTestIds } from './types/validation
|
|
1
|
+
import type { Locator } from '@playwright/test';
|
|
2
|
+
import type { DynamicTestIdEntry, DynamicSelectorEntry } from './locators/dynamic-locator-resolver';
|
|
3
|
+
export { ProxyLocatorMethods } from './types/proxy-methods';
|
|
4
|
+
export type { DynamicLocatorEntry, DynamicTestIdEntry, DynamicSelectorEntry } from './locators/dynamic-locator-resolver';
|
|
5
|
+
import type { AllowedMethodKeys } from '../types/methods';
|
|
6
|
+
import type { StrategyType, LocatorStrategyOptions } from '../types/strategies';
|
|
7
|
+
import type { ResolvedTestIdKeys, ValidateTestIds } from './types/validation';
|
|
8
8
|
export { zeroArgMethodsList, oneArgMethodsList } from '../constants/methods';
|
|
9
9
|
export { rolesList, strategyList } from '../constants/strategies';
|
|
10
|
-
export { AllowedZeroArgMethods, AllowedOneArgMethods, AllowedMethodKeys } from '../types/methods';
|
|
11
|
-
export { RoleType, StrategyType, PlaywrightRoleOptions, PlaywrightTextOptions, LocatorStrategyOptions } from '../types/strategies';
|
|
10
|
+
export type { AllowedZeroArgMethods, AllowedOneArgMethods, AllowedMethodKeys } from '../types/methods';
|
|
11
|
+
export type { RoleType, StrategyType, PlaywrightRoleOptions, PlaywrightTextOptions, LocatorStrategyOptions } from '../types/strategies';
|
|
12
12
|
export type GetStrategyOfKey<T, K extends string> = K extends (T extends {
|
|
13
13
|
testId: infer I;
|
|
14
14
|
} ? ResolvedTestIdKeys<I> : never) ? 'testId' : K extends (T extends {
|
|
@@ -26,13 +26,13 @@ export type ModifyOptionsForTarget<T, Target, Options> = Options & {
|
|
|
26
26
|
} & (Target extends string ? LocatorStrategyOptions<GetStrategyOfKey<T, Target>> : {});
|
|
27
27
|
export type PageKeys<T> = (T extends {
|
|
28
28
|
testId: infer I;
|
|
29
|
-
} ? (I extends Record<string,
|
|
29
|
+
} ? (I extends Record<string, unknown> ? ResolvedTestIdKeys<I> : never) : never) | (T extends {
|
|
30
30
|
testIds: infer I;
|
|
31
|
-
} ? (I extends Record<string,
|
|
31
|
+
} ? (I extends Record<string, unknown> ? ResolvedTestIdKeys<I> : never) : never) | (T extends {
|
|
32
32
|
selector: infer S;
|
|
33
|
-
} ? (S extends Record<string,
|
|
33
|
+
} ? (S extends Record<string, unknown> ? ResolvedTestIdKeys<S> : never) : never) | (T extends {
|
|
34
34
|
selectors: infer S;
|
|
35
|
-
} ? (S extends Record<string,
|
|
35
|
+
} ? (S extends Record<string, unknown> ? ResolvedTestIdKeys<S> : never) : never) | {
|
|
36
36
|
[S in Exclude<StrategyType, 'testId' | 'selector'>]: T extends Record<S, readonly string[]> ? T[S][number] : never;
|
|
37
37
|
}[Exclude<StrategyType, 'testId' | 'selector'>];
|
|
38
38
|
export type CheckableLocatorKeys<T> = {
|
|
@@ -88,43 +88,43 @@ export type KeysOfOtherStrategies<T, S extends StrategyType> = {
|
|
|
88
88
|
}[Exclude<StrategyType, S>];
|
|
89
89
|
type GetTestIds<T> = T extends {
|
|
90
90
|
testId: infer I;
|
|
91
|
-
} ? (I extends Record<string,
|
|
91
|
+
} ? (I extends Record<string, unknown> ? I : {}) : T extends {
|
|
92
92
|
testIds: infer I;
|
|
93
|
-
} ? (I extends Record<string,
|
|
93
|
+
} ? (I extends Record<string, unknown> ? I : {}) : {};
|
|
94
94
|
type GetSelectors<T> = T extends {
|
|
95
95
|
selector: infer S;
|
|
96
|
-
} ? (S extends Record<string,
|
|
96
|
+
} ? (S extends Record<string, unknown> ? S : {}) : T extends {
|
|
97
97
|
selectors: infer S;
|
|
98
|
-
} ? (S extends Record<string,
|
|
98
|
+
} ? (S extends Record<string, unknown> ? S : {}) : {};
|
|
99
99
|
export type ValidatePageConfig<T> = {
|
|
100
100
|
url?: string;
|
|
101
101
|
testId?: T extends {
|
|
102
102
|
testId: infer I;
|
|
103
|
-
} ? I extends Record<string,
|
|
103
|
+
} ? I extends Record<string, unknown> ? ValidateTestIds<I, 'testId', GetSelectors<T>> & {
|
|
104
104
|
[K in keyof I]: K extends KeysOfOtherStrategies<T, 'testId'> ? {
|
|
105
105
|
[P in `Error: Duplicate key "${K & string}" is defined in multiple strategies`]: never;
|
|
106
|
-
} :
|
|
106
|
+
} : unknown;
|
|
107
107
|
} : TestIdMap : TestIdMap;
|
|
108
108
|
testIds?: T extends {
|
|
109
109
|
testIds: infer I;
|
|
110
|
-
} ? I extends Record<string,
|
|
110
|
+
} ? I extends Record<string, unknown> ? ValidateTestIds<I, 'testId', GetSelectors<T>> & {
|
|
111
111
|
[K in keyof I]: K extends KeysOfOtherStrategies<T, 'testId'> ? {
|
|
112
112
|
[P in `Error: Duplicate key "${K & string}" is defined in multiple strategies`]: never;
|
|
113
|
-
} :
|
|
113
|
+
} : unknown;
|
|
114
114
|
} : TestIdMap : TestIdMap;
|
|
115
115
|
selector?: T extends {
|
|
116
116
|
selector: infer S;
|
|
117
|
-
} ? S extends Record<string,
|
|
117
|
+
} ? S extends Record<string, unknown> ? ValidateTestIds<S, 'selector', GetTestIds<T>> & {
|
|
118
118
|
[K in keyof S]: K extends KeysOfOtherStrategies<T, 'selector'> ? {
|
|
119
119
|
[P in `Error: Duplicate key "${K & string}" is defined in multiple strategies`]: never;
|
|
120
|
-
} :
|
|
120
|
+
} : unknown;
|
|
121
121
|
} : SelectorMap : SelectorMap;
|
|
122
122
|
selectors?: T extends {
|
|
123
123
|
selectors: infer S;
|
|
124
|
-
} ? S extends Record<string,
|
|
124
|
+
} ? S extends Record<string, unknown> ? ValidateTestIds<S, 'selector', GetTestIds<T>> & {
|
|
125
125
|
[K in keyof S]: K extends KeysOfOtherStrategies<T, 'selector'> ? {
|
|
126
126
|
[P in `Error: Duplicate key "${K & string}" is defined in multiple strategies`]: never;
|
|
127
|
-
} :
|
|
127
|
+
} : unknown;
|
|
128
128
|
} : SelectorMap : SelectorMap;
|
|
129
129
|
} & {
|
|
130
130
|
[S in Exclude<StrategyType, 'testId' | 'selector'>]?: T extends Record<S, infer A> ? A extends readonly string[] ? {
|
|
@@ -63,6 +63,24 @@ function extractPlaceholders(pattern) {
|
|
|
63
63
|
}
|
|
64
64
|
return names;
|
|
65
65
|
}
|
|
66
|
+
function getPlaceholderValues(entry) {
|
|
67
|
+
const placeholders = {};
|
|
68
|
+
for (const [key, value] of Object.entries(entry)) {
|
|
69
|
+
if (key !== 'testId' && key !== 'selector') {
|
|
70
|
+
placeholders[key] = value;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return placeholders;
|
|
74
|
+
}
|
|
75
|
+
function collectPlaceholderArrays(placeholders, names) {
|
|
76
|
+
return names.map((name) => {
|
|
77
|
+
const value = placeholders[name];
|
|
78
|
+
if (!Array.isArray(value)) {
|
|
79
|
+
throw new Error(`Dynamic locator placeholder "${name}" must be an array.`);
|
|
80
|
+
}
|
|
81
|
+
return value;
|
|
82
|
+
});
|
|
83
|
+
}
|
|
66
84
|
/**
|
|
67
85
|
* Expand a single dynamic key pattern + entry into flat key → locator-value pairs.
|
|
68
86
|
* Validates the entry at runtime before expansion.
|
|
@@ -71,14 +89,12 @@ function extractPlaceholders(pattern) {
|
|
|
71
89
|
function expandSingleDynamic(pattern, entry) {
|
|
72
90
|
const testIdPattern = entry.testId;
|
|
73
91
|
const selectorPattern = entry.selector;
|
|
74
|
-
const targetPattern = (testIdPattern
|
|
75
|
-
const targetKey = testIdPattern !== undefined ? 'testId' : 'selector';
|
|
92
|
+
const targetPattern = (testIdPattern ?? selectorPattern);
|
|
76
93
|
if (targetPattern === undefined) {
|
|
77
94
|
throw new Error(`Dynamic locator "${pattern}": entry must contain either 'testId' or 'selector' property.`);
|
|
78
95
|
}
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
delete placeholders.selector;
|
|
96
|
+
const targetKey = testIdPattern !== undefined ? 'testId' : 'selector';
|
|
97
|
+
const placeholders = getPlaceholderValues(entry);
|
|
82
98
|
// ─── Validation ──────────────────────────────────────────────────────
|
|
83
99
|
const expectedNames = extractPlaceholders(pattern);
|
|
84
100
|
// 0.5. No duplicate placeholders in the pattern
|
|
@@ -114,10 +130,7 @@ function expandSingleDynamic(pattern, entry) {
|
|
|
114
130
|
// ─── Expansion ───────────────────────────────────────────────────────
|
|
115
131
|
// Sort placeholder names longest-first to avoid substring replacement issues
|
|
116
132
|
const names = Object.keys(placeholders).sort((a, b) => b.length - a.length);
|
|
117
|
-
const values = names
|
|
118
|
-
const v = placeholders[name];
|
|
119
|
-
return v;
|
|
120
|
-
});
|
|
133
|
+
const values = collectPlaceholderArrays(placeholders, names);
|
|
121
134
|
const result = {};
|
|
122
135
|
for (const combo of cartesianProduct(values)) {
|
|
123
136
|
// Build the *key* — replace {name} with Capitalize(value), then uncapitalize the first char
|
|
@@ -1,25 +1,31 @@
|
|
|
1
1
|
import { Page, Locator } from '@playwright/test';
|
|
2
|
-
|
|
2
|
+
import { PageConfig, StrategyType } from '../config';
|
|
3
|
+
export interface LocatorStrategyDefinition {
|
|
3
4
|
resolver: string;
|
|
4
5
|
role?: string;
|
|
5
|
-
}
|
|
6
|
+
}
|
|
7
|
+
export declare const locatorStrategies: Record<string, LocatorStrategyDefinition>;
|
|
6
8
|
export interface LocatorMetadata {
|
|
7
|
-
strategy:
|
|
8
|
-
value:
|
|
9
|
+
strategy: StrategyType;
|
|
10
|
+
value: string;
|
|
9
11
|
}
|
|
10
|
-
|
|
11
|
-
export
|
|
12
|
-
|
|
13
|
-
export
|
|
14
|
-
export declare function resolveLocator(context: Page | Locator, config: any, target: any, options?: {
|
|
12
|
+
/** A configured locator key or a raw Playwright locator. */
|
|
13
|
+
export type LocatorTarget = string | Locator;
|
|
14
|
+
/** Shared internal options understood by locator resolution. */
|
|
15
|
+
export type LocatorResolutionOptions = {
|
|
15
16
|
nth?: number;
|
|
16
17
|
raw?: boolean;
|
|
17
18
|
hasText?: string | RegExp;
|
|
18
|
-
} & Record<string,
|
|
19
|
+
} & Record<string, unknown>;
|
|
20
|
+
export declare function cleanKey(s: string): string;
|
|
21
|
+
export declare const lookupIndexCache: WeakMap<object, Map<string, LocatorMetadata>>;
|
|
22
|
+
export declare function buildLookupIndex(config: PageConfig): Map<string, LocatorMetadata>;
|
|
23
|
+
export declare function defineLocators(instance: object, context: Page | Locator, config: PageConfig): void;
|
|
24
|
+
export declare function resolveLocator(context: Page | Locator, config: PageConfig, target: LocatorTarget, options?: LocatorResolutionOptions): Locator;
|
|
19
25
|
/**
|
|
20
26
|
* Resolve a config key to a proxied Locator with step-wrapped Playwright methods.
|
|
21
27
|
*/
|
|
22
|
-
export declare function locator(context: Page | Locator, config:
|
|
28
|
+
export declare function locator(context: Page | Locator, config: PageConfig, target: LocatorTarget, options?: Parameters<Locator['filter']>[0] & {
|
|
23
29
|
nth?: number;
|
|
24
30
|
}): Locator;
|
|
25
31
|
export declare function wrapLocatorWithProxy(loc: Locator): Locator;
|
|
@@ -21,13 +21,27 @@ exports.locatorStrategies = {
|
|
|
21
21
|
placeholder: { resolver: 'getByPlaceholder' },
|
|
22
22
|
altText: { resolver: 'getByAltText' }
|
|
23
23
|
};
|
|
24
|
-
// Add all roles dynamically
|
|
25
24
|
for (const role of config_1.rolesList) {
|
|
26
25
|
exports.locatorStrategies[role] = { resolver: 'getByRole', role };
|
|
27
26
|
}
|
|
28
27
|
function cleanKey(s) {
|
|
29
28
|
let clean = s;
|
|
30
|
-
const suffixes = [
|
|
29
|
+
const suffixes = [
|
|
30
|
+
'Btn',
|
|
31
|
+
'Button',
|
|
32
|
+
'Link',
|
|
33
|
+
'Input',
|
|
34
|
+
'Checkbox',
|
|
35
|
+
'Option',
|
|
36
|
+
'Title',
|
|
37
|
+
'Label',
|
|
38
|
+
'Value',
|
|
39
|
+
'Text',
|
|
40
|
+
'Wrapper',
|
|
41
|
+
'Container',
|
|
42
|
+
'Card',
|
|
43
|
+
'Item'
|
|
44
|
+
];
|
|
31
45
|
for (const suffix of suffixes) {
|
|
32
46
|
clean = clean.replace(new RegExp(`[-_]?${suffix}$`, 'i'), '');
|
|
33
47
|
}
|
|
@@ -36,6 +50,14 @@ function cleanKey(s) {
|
|
|
36
50
|
return clean;
|
|
37
51
|
}
|
|
38
52
|
exports.lookupIndexCache = new WeakMap();
|
|
53
|
+
function getLookupIndex(config) {
|
|
54
|
+
let index = exports.lookupIndexCache.get(config);
|
|
55
|
+
if (!index) {
|
|
56
|
+
index = buildLookupIndex(config);
|
|
57
|
+
exports.lookupIndexCache.set(config, index);
|
|
58
|
+
}
|
|
59
|
+
return index;
|
|
60
|
+
}
|
|
39
61
|
function buildLookupIndex(config) {
|
|
40
62
|
const index = new Map();
|
|
41
63
|
const seenInStrategies = new Map();
|
|
@@ -90,11 +112,7 @@ function buildLookupIndex(config) {
|
|
|
90
112
|
return index;
|
|
91
113
|
}
|
|
92
114
|
function defineLocators(instance, context, config) {
|
|
93
|
-
|
|
94
|
-
if (!index) {
|
|
95
|
-
index = buildLookupIndex(config);
|
|
96
|
-
exports.lookupIndexCache.set(config, index);
|
|
97
|
-
}
|
|
115
|
+
const index = getLookupIndex(config);
|
|
98
116
|
for (const key of index.keys()) {
|
|
99
117
|
Object.defineProperty(instance, key, {
|
|
100
118
|
get: () => {
|
|
@@ -105,19 +123,63 @@ function defineLocators(instance, context, config) {
|
|
|
105
123
|
});
|
|
106
124
|
}
|
|
107
125
|
}
|
|
108
|
-
function
|
|
109
|
-
if (typeof target !== 'string')
|
|
110
|
-
return target;
|
|
111
|
-
const targetStr = target;
|
|
112
|
-
let index = exports.lookupIndexCache.get(config);
|
|
113
|
-
if (!index) {
|
|
114
|
-
index = buildLookupIndex(config);
|
|
115
|
-
exports.lookupIndexCache.set(config, index);
|
|
116
|
-
}
|
|
126
|
+
function resolvePlaywrightLocator(ctx, resolver, role, value, options) {
|
|
117
127
|
const toRegex = (val) => {
|
|
118
128
|
const escaped = val.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
119
129
|
return new RegExp(escaped.replace(/\s+/g, '\\s+'), 'i');
|
|
120
130
|
};
|
|
131
|
+
if (resolver === 'getByRole') {
|
|
132
|
+
const val = typeof value === 'string' ? toRegex(value) : value;
|
|
133
|
+
const roleOptions = { name: val };
|
|
134
|
+
if (options) {
|
|
135
|
+
const allowedOptions = [
|
|
136
|
+
'exact',
|
|
137
|
+
'checked',
|
|
138
|
+
'disabled',
|
|
139
|
+
'expanded',
|
|
140
|
+
'includeHidden',
|
|
141
|
+
'level',
|
|
142
|
+
'pressed',
|
|
143
|
+
'selected'
|
|
144
|
+
];
|
|
145
|
+
for (const opt of allowedOptions) {
|
|
146
|
+
if (opt in options && options[opt] !== undefined) {
|
|
147
|
+
roleOptions[opt] = options[opt];
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return ctx.getByRole(role, roleOptions);
|
|
152
|
+
}
|
|
153
|
+
if (resolver === 'locator') {
|
|
154
|
+
return ctx.locator(value);
|
|
155
|
+
}
|
|
156
|
+
const opt = {};
|
|
157
|
+
if (options && 'exact' in options && typeof options.exact === 'boolean') {
|
|
158
|
+
opt.exact = options.exact;
|
|
159
|
+
}
|
|
160
|
+
const val = typeof value === 'string' ? toRegex(value) : value;
|
|
161
|
+
switch (resolver) {
|
|
162
|
+
case 'getByTestId':
|
|
163
|
+
return ctx.getByTestId(val);
|
|
164
|
+
case 'getByLabel':
|
|
165
|
+
return ctx.getByLabel(val, opt);
|
|
166
|
+
case 'getByPlaceholder':
|
|
167
|
+
return ctx.getByPlaceholder(val, opt);
|
|
168
|
+
case 'getByAltText':
|
|
169
|
+
return ctx.getByAltText(val, opt);
|
|
170
|
+
case 'getByTitle':
|
|
171
|
+
return ctx.getByTitle(val, opt);
|
|
172
|
+
case 'getByText':
|
|
173
|
+
return ctx.getByText(val, opt);
|
|
174
|
+
default:
|
|
175
|
+
return typeof val === 'string' ? ctx.locator(val) : ctx.getByText(val, opt);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function resolveLocator(context, config, target, options) {
|
|
179
|
+
if (typeof target !== 'string')
|
|
180
|
+
return target;
|
|
181
|
+
const targetStr = target;
|
|
182
|
+
const index = getLookupIndex(config);
|
|
121
183
|
const resolveSingle = (key, ctx) => {
|
|
122
184
|
const cleaned = cleanKey(key);
|
|
123
185
|
const meta = index.get(cleaned);
|
|
@@ -129,30 +191,7 @@ function resolveLocator(context, config, target, options) {
|
|
|
129
191
|
throw new Error(`Unknown locator strategy '${meta.strategy}' for key '${key}'.`);
|
|
130
192
|
}
|
|
131
193
|
const { resolver, role } = strategy;
|
|
132
|
-
|
|
133
|
-
const val = typeof meta.value === 'string' ? toRegex(meta.value) : meta.value;
|
|
134
|
-
const roleOptions = { name: val };
|
|
135
|
-
if (options) {
|
|
136
|
-
const allowedOptions = ['exact', 'checked', 'disabled', 'expanded', 'includeHidden', 'level', 'pressed', 'selected'];
|
|
137
|
-
for (const opt of allowedOptions) {
|
|
138
|
-
if (opt in options) {
|
|
139
|
-
roleOptions[opt] = options[opt];
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
return ctx[resolver](role, roleOptions);
|
|
144
|
-
}
|
|
145
|
-
else if (resolver === 'locator') {
|
|
146
|
-
return ctx[resolver](meta.value);
|
|
147
|
-
}
|
|
148
|
-
else {
|
|
149
|
-
const opt = {};
|
|
150
|
-
if (options && 'exact' in options) {
|
|
151
|
-
opt.exact = options.exact;
|
|
152
|
-
}
|
|
153
|
-
const val = typeof meta.value === 'string' ? toRegex(meta.value) : meta.value;
|
|
154
|
-
return ctx[resolver](val, opt);
|
|
155
|
-
}
|
|
194
|
+
return resolvePlaywrightLocator(ctx, resolver, role, meta.value, options);
|
|
156
195
|
};
|
|
157
196
|
let loc;
|
|
158
197
|
const cleanedTarget = cleanKey(targetStr);
|
|
@@ -192,50 +231,17 @@ function locator(context, config, target, options) {
|
|
|
192
231
|
: filtered;
|
|
193
232
|
return wrapLocatorWithProxy(loc);
|
|
194
233
|
}
|
|
234
|
+
const ACTION_METHODS = new Set([...config_1.zeroArgMethodsList, ...config_1.oneArgMethodsList]);
|
|
195
235
|
function wrapLocatorWithProxy(loc) {
|
|
196
|
-
const actionMethods = [...config_1.zeroArgMethodsList, ...config_1.oneArgMethodsList];
|
|
197
236
|
return new Proxy(loc, {
|
|
198
237
|
get: (targetLoc, propKey, receiver) => {
|
|
199
238
|
const val = Reflect.get(targetLoc, propKey, receiver);
|
|
200
|
-
if (typeof val === 'function' &&
|
|
239
|
+
if (typeof val === 'function' && typeof propKey === 'string' && ACTION_METHODS.has(propKey)) {
|
|
201
240
|
return (...args) => {
|
|
202
241
|
const stepName = (0, formatter_1.formatStepDescription)(propKey, targetLoc, args);
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
if (options && typeof options === 'object' && options.mask !== undefined) {
|
|
207
|
-
shouldMask = options.mask === true;
|
|
208
|
-
}
|
|
209
|
-
else {
|
|
210
|
-
const targetStr = (0, formatter_1.formatTarget)(targetLoc);
|
|
211
|
-
const targetStrLower = targetStr.toLowerCase();
|
|
212
|
-
shouldMask = targetStrLower.includes('pass') || targetStrLower.includes('pw');
|
|
213
|
-
}
|
|
214
|
-
if (shouldMask) {
|
|
215
|
-
return test_1.test.step(stepName, async () => {
|
|
216
|
-
await targetLoc.focus();
|
|
217
|
-
await targetLoc.evaluate((el, val) => {
|
|
218
|
-
const inputEl = el;
|
|
219
|
-
const prototype = el.tagName === 'TEXTAREA'
|
|
220
|
-
? window.HTMLTextAreaElement.prototype
|
|
221
|
-
: window.HTMLInputElement.prototype;
|
|
222
|
-
const descriptor = Object.getOwnPropertyDescriptor(prototype, 'value');
|
|
223
|
-
if (descriptor && descriptor.set) {
|
|
224
|
-
descriptor.set.call(inputEl, val);
|
|
225
|
-
}
|
|
226
|
-
else {
|
|
227
|
-
inputEl.value = val;
|
|
228
|
-
}
|
|
229
|
-
inputEl.dispatchEvent(new Event('input', { bubbles: true }));
|
|
230
|
-
inputEl.dispatchEvent(new Event('change', { bubbles: true }));
|
|
231
|
-
}, args[0]);
|
|
232
|
-
}, { box: true, location: (0, caller_location_1.getCallerLocation)() });
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
return test_1.test.step(stepName, () => val.apply(targetLoc, args), {
|
|
236
|
-
box: true,
|
|
237
|
-
location: (0, caller_location_1.getCallerLocation)()
|
|
238
|
-
});
|
|
242
|
+
return test_1.test.step(stepName, async () => {
|
|
243
|
+
return await targetLoc[propKey](...args);
|
|
244
|
+
}, { box: true, location: (0, caller_location_1.getCallerLocation)() });
|
|
239
245
|
};
|
|
240
246
|
}
|
|
241
247
|
return val;
|
package/dist/page/registry.d.ts
CHANGED
|
@@ -5,12 +5,12 @@ export type PageRegistry = Record<string, PageConfig>;
|
|
|
5
5
|
export interface PageConstructor<C extends PageConfig> {
|
|
6
6
|
new (page: Page): TypedPage<C>;
|
|
7
7
|
}
|
|
8
|
-
export type PageRegistryTest<T extends Record<string, PageConfig>, P, W, O = {}> = TestType<P & {
|
|
9
|
-
[K in keyof T]: K extends keyof O ? O[K] extends new (page: Page, ...args:
|
|
8
|
+
export type PageRegistryTest<T extends Record<string, PageConfig>, P extends {}, W extends {}, O = {}> = TestType<P & {
|
|
9
|
+
[K in keyof T]: K extends keyof O ? O[K] extends new (page: Page, ...args: unknown[]) => infer R ? R : TypedPage<T[K]> : TypedPage<T[K]>;
|
|
10
10
|
} & {
|
|
11
|
-
[K in keyof T as K extends `worker${infer R}` ? Uncapitalize<R> : never]: K extends keyof O ? O[K] extends new (page: Page, ...args:
|
|
11
|
+
[K in keyof T as K extends `worker${infer R}` ? Uncapitalize<R> : never]: K extends keyof O ? O[K] extends new (page: Page, ...args: unknown[]) => infer R ? R : TypedPage<T[K]> : TypedPage<T[K]>;
|
|
12
12
|
}, W & {
|
|
13
|
-
[K in keyof T as K extends `worker${string}` ? K : `worker${Capitalize<K & string>}`]: K extends keyof O ? O[K] extends new (page: Page, ...args:
|
|
13
|
+
[K in keyof T as K extends `worker${string}` ? K : `worker${Capitalize<K & string>}`]: K extends keyof O ? O[K] extends new (page: Page, ...args: unknown[]) => infer R ? R : TypedPage<T[K]> : TypedPage<T[K]>;
|
|
14
14
|
} & {
|
|
15
15
|
workerPage: Page;
|
|
16
16
|
}> & {
|
|
@@ -29,8 +29,8 @@ export type PageRegistryTest<T extends Record<string, PageConfig>, P, W, O = {}>
|
|
|
29
29
|
[K in keyof T as K extends `worker${string}` ? K : `worker${Capitalize<K & string>}`]: K extends keyof O ? O[K] : PageConstructor<T[K]>;
|
|
30
30
|
};
|
|
31
31
|
extend<O2 extends Partial<{
|
|
32
|
-
[K in keyof T]: new (page: Page, ...args:
|
|
33
|
-
}>,
|
|
32
|
+
[K in keyof T]: new (page: Page, ...args: unknown[]) => unknown;
|
|
33
|
+
}>, CustomP extends {} = typeof test extends TestType<infer DefaultP, infer _DefaultW> ? DefaultP : {}, CustomW extends {} = typeof test extends TestType<infer _DefaultP, infer DefaultW> ? DefaultW : {}>(overrides: O2, base?: TestType<CustomP, CustomW>): PageRegistryTest<T, CustomP, CustomW, O2>;
|
|
34
34
|
};
|
|
35
35
|
/**
|
|
36
36
|
* Creates a page registry that registers page objects as Playwright fixtures,
|
|
@@ -43,35 +43,35 @@ export type PageRegistryTest<T extends Record<string, PageConfig>, P, W, O = {}>
|
|
|
43
43
|
*
|
|
44
44
|
* // returns the extended test runner directly
|
|
45
45
|
* const test = createPageRegistry({
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
46
|
+
codegenPage: {
|
|
47
|
+
url: '/',
|
|
48
|
+
selectors: {
|
|
49
|
+
'{item}': {
|
|
50
|
+
item: ['why-pw-core', 'why-not-playwright', 'registry', 'typed-page', 'features'],
|
|
51
|
+
selector: '[data-parent-id="{item}"]'
|
|
52
|
+
},
|
|
53
|
+
copyToClipboardBtn: '.page',
|
|
54
|
+
dynamicLocators: 'text="Dynamic Locators" >> nth=0',
|
|
55
|
+
rootrailthumbright: '#rootRailThumbRight',
|
|
56
|
+
'text{item}': {
|
|
57
|
+
item: ['Auto Steps', 'Capabilities >> nth=0', 'Secret Masking'],
|
|
58
|
+
selector: 'text="{item}"'
|
|
59
|
+
},
|
|
60
|
+
tshirts: '.title-title',
|
|
61
|
+
tShirts: 'internal:role=link[name="T-Shirts"s]'
|
|
62
|
+
},
|
|
63
|
+
testIds: {
|
|
64
|
+
qaWorkspaceBtn: 'active-workspace-btn',
|
|
65
|
+
wsOptionApp: 'ws-option-app'
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
blankPage: {
|
|
69
|
+
url: '/',
|
|
70
|
+
selectors: {
|
|
71
|
+
element: '[data-pw-cursor="pointer"]'
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
75
|
*
|
|
76
76
|
* // returns page classes only when accessing .pages
|
|
77
77
|
* const pages = createPageRegistry({
|