pw-core 1.1.0 → 1.2.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 -1
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +777 -0
- package/dist/codegen/config/context-weights.d.ts +8 -0
- package/dist/codegen/config/context-weights.js +11 -0
- package/dist/codegen/config/ignored-attributes.d.ts +1 -0
- package/dist/codegen/config/ignored-attributes.js +4 -0
- package/dist/codegen/config/ignored-classes.d.ts +1 -0
- package/dist/codegen/config/ignored-classes.js +144 -0
- package/dist/codegen/config/ignored-values.d.ts +1 -0
- package/dist/codegen/config/ignored-values.js +4 -0
- package/dist/codegen/config/index.d.ts +29 -0
- package/dist/codegen/config/index.js +17 -0
- package/dist/codegen/config/strategy-order.d.ts +1 -0
- package/dist/codegen/config/strategy-order.js +18 -0
- package/dist/codegen/config/weights.d.ts +15 -0
- package/dist/codegen/config/weights.js +18 -0
- package/dist/codegen/floating-panel/client.d.ts +1 -0
- package/dist/codegen/floating-panel/client.js +248 -0
- package/dist/codegen/floating-panel/index.d.ts +4 -0
- package/dist/codegen/floating-panel/index.js +20 -0
- package/dist/codegen/floating-panel/manager.d.ts +19 -0
- package/dist/codegen/floating-panel/manager.js +90 -0
- package/dist/codegen/floating-panel/template.d.ts +2 -0
- package/dist/codegen/floating-panel/template.js +58 -0
- package/dist/codegen/floating-panel/types.d.ts +7 -0
- package/dist/codegen/floating-panel/types.js +2 -0
- package/dist/codegen/generator/action.validator.d.ts +5 -0
- package/dist/codegen/generator/action.validator.js +27 -0
- package/dist/codegen/generator/index.d.ts +13 -0
- package/dist/codegen/generator/index.js +724 -0
- package/dist/codegen/generator/locator.restricted.d.ts +36 -0
- package/dist/codegen/generator/locator.restricted.js +127 -0
- package/dist/codegen/generator/locator.validator.d.ts +8 -0
- package/dist/codegen/generator/locator.validator.js +38 -0
- package/dist/codegen/hover-tracker/client.d.ts +1 -0
- package/dist/codegen/hover-tracker/client.js +75 -0
- package/dist/codegen/hover-tracker/index.d.ts +3 -0
- package/dist/codegen/hover-tracker/index.js +19 -0
- package/dist/codegen/hover-tracker/manager.d.ts +11 -0
- package/dist/codegen/hover-tracker/manager.js +28 -0
- package/dist/codegen/hover-tracker/types.d.ts +3 -0
- package/dist/codegen/hover-tracker/types.js +2 -0
- package/dist/codegen/index.d.ts +35 -0
- package/dist/codegen/index.js +1147 -0
- package/dist/codegen/key-utils.d.ts +23 -0
- package/dist/codegen/key-utils.js +68 -0
- package/dist/codegen/scorer/base-score.d.ts +1 -0
- package/dist/codegen/scorer/base-score.js +7 -0
- package/dist/codegen/scorer/context-score.d.ts +2 -0
- package/dist/codegen/scorer/context-score.js +27 -0
- package/dist/codegen/scorer/index.d.ts +5 -0
- package/dist/codegen/scorer/index.js +23 -0
- package/dist/codegen/scorer/locator.ranking.d.ts +15 -0
- package/dist/codegen/scorer/locator.ranking.js +83 -0
- package/dist/codegen/scorer/semantic-score.d.ts +1 -0
- package/dist/codegen/scorer/semantic-score.js +47 -0
- package/dist/codegen/types.d.ts +35 -0
- package/dist/codegen/types.js +2 -0
- package/dist/component/table.js +19 -14
- package/dist/constants/methods.d.ts +2 -0
- package/dist/constants/methods.js +33 -0
- package/dist/constants/strategies.d.ts +2 -0
- package/dist/constants/strategies.js +47 -0
- package/dist/helpers.d.ts +1 -1
- package/dist/helpers.js +21 -9
- package/dist/page/actions/locator-actions.js +46 -2
- package/dist/page/actions/page-actions.d.ts +11 -6
- package/dist/page/actions/page-actions.js +12 -12
- package/dist/page/assertions/verify-chain.d.ts +39 -32
- package/dist/page/assertions/verify-chain.js +21 -6
- package/dist/page/assertions/verify-helpers.d.ts +9 -3
- package/dist/page/assertions/verify-helpers.js +18 -9
- package/dist/page/config.d.ts +126 -38
- package/dist/page/config.js +10 -31
- package/dist/page/index.d.ts +1 -0
- package/dist/page/index.js +1 -0
- package/dist/page/locators/dynamic-locator-resolver.d.ts +49 -0
- package/dist/page/locators/dynamic-locator-resolver.js +187 -0
- package/dist/page/locators/resolver.d.ts +15 -14
- package/dist/page/locators/resolver.js +187 -30
- package/dist/page/registry.d.ts +51 -12
- package/dist/page/registry.js +90 -18
- package/dist/page/typed-page.d.ts +33 -28
- package/dist/page/typed-page.js +89 -12
- package/dist/page/types/proxy-methods.d.ts +69 -54
- package/dist/page/types/validation.d.ts +69 -0
- package/dist/page/types/validation.js +2 -0
- package/dist/page/utils/caller-location.d.ts +5 -0
- package/dist/page/utils/caller-location.js +82 -0
- package/dist/page/utils/formatter.js +14 -4
- package/dist/types/methods.d.ts +4 -0
- package/dist/types/methods.js +2 -0
- package/dist/types/strategies.d.ts +17 -0
- package/dist/types/strategies.js +2 -0
- package/package.json +8 -1
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracts placeholder names from a `{name}` pattern string.
|
|
3
|
+
*/
|
|
4
|
+
export type ExtractPlaceholders<S extends string> = S extends `${string}{${infer Name}}${infer Rest}` ? Name | ExtractPlaceholders<Rest> : never;
|
|
5
|
+
/**
|
|
6
|
+
* Checks recursively if a pattern string contains duplicate placeholders.
|
|
7
|
+
*/
|
|
8
|
+
export type HasDuplicatePlaceholders<S extends string, Seen extends string = never> = S extends `${string}{${infer Name}}${infer Rest}` ? Name extends Seen ? true : HasDuplicatePlaceholders<Rest, Seen | Name> : false;
|
|
9
|
+
/**
|
|
10
|
+
* Returns the subset of `Placeholders` whose names are NOT found as
|
|
11
|
+
* substrings of `TestId`. Evaluates to `never` when all are present.
|
|
12
|
+
*/
|
|
13
|
+
export type MissingInTestId<TestId extends string, Placeholders extends string> = Placeholders extends any ? TestId extends `${string}${Placeholders}${string}` ? never : Placeholders : never;
|
|
14
|
+
/**
|
|
15
|
+
* Validates a dynamic entry by mapping each of its properties to their expected types.
|
|
16
|
+
*
|
|
17
|
+
* By checking properties individually, TypeScript reports the error exactly on the offending line
|
|
18
|
+
* (e.g. on the wrong value or unknown key) instead of marking the entire object as invalid.
|
|
19
|
+
*/
|
|
20
|
+
export type ValidateDynamicEntryProperties<Pattern extends string, Entry, TargetKey extends 'testId' | 'selector'> = {
|
|
21
|
+
[Key in keyof Entry | ExtractPlaceholders<Pattern> | TargetKey]: Key extends TargetKey ? Entry extends Record<TargetKey, infer TPat> ? TPat extends string ? [MissingInTestId<TPat, ExtractPlaceholders<Pattern>>] extends [never] ? [Exclude<ExtractPlaceholders<TPat>, ExtractPlaceholders<Pattern>>] extends [never] ? string : {
|
|
22
|
+
[P in `Error: ${TargetKey} contains unknown placeholder(s): ${Exclude<ExtractPlaceholders<TPat>, ExtractPlaceholders<Pattern>> & string}`]: never;
|
|
23
|
+
} : {
|
|
24
|
+
[P in `Error: ${TargetKey} must contain all placeholder names: missing ${MissingInTestId<TPat, ExtractPlaceholders<Pattern>> & string}`]: never;
|
|
25
|
+
} : {
|
|
26
|
+
[P in `Error: ${TargetKey} must be a string`]: never;
|
|
27
|
+
} : {
|
|
28
|
+
[P in `Error: Dynamic entry must include a ${TargetKey} property`]: never;
|
|
29
|
+
} : Key extends ExtractPlaceholders<Pattern> ? Key extends keyof Entry ? Entry[Key] extends readonly string[] ? readonly string[] : {
|
|
30
|
+
[P in `Error: Placeholder key must be an array: ${Key & string}`]: never;
|
|
31
|
+
} : {
|
|
32
|
+
[P in `Error: Missing placeholder key: ${Key & string}`]: never;
|
|
33
|
+
} : {
|
|
34
|
+
[P in `Error: Only placeholder keys and '${TargetKey}' are allowed`]: never;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Replaces every `{Name}` placeholder in `Pattern` with `Capitalize<Value>`
|
|
39
|
+
* where `Value` is drawn from the corresponding array in `Entry`.
|
|
40
|
+
* Template-literal unions distribute automatically, producing the full cartesian product.
|
|
41
|
+
*/
|
|
42
|
+
export type ReplacePattern<Pattern extends string, Entry> = Pattern extends `${infer Before}{${infer Name}}${infer After}` ? Name extends keyof Entry ? Entry[Name] extends readonly string[] ? `${Before}${Capitalize<Entry[Name][number]>}${ReplacePattern<After, Entry> & string}` : never : never : Pattern;
|
|
43
|
+
/**
|
|
44
|
+
* Expands a dynamic key pattern into the union of all concrete (camelCased) keys.
|
|
45
|
+
*/
|
|
46
|
+
export type ExpandDynamicKey<Pattern extends string, Entry> = Uncapitalize<ReplacePattern<Pattern, Entry>>;
|
|
47
|
+
/**
|
|
48
|
+
* Resolves every key in a testIds record:
|
|
49
|
+
* - string values → the key itself (static locator)
|
|
50
|
+
* - object values → the expanded dynamic key union
|
|
51
|
+
*/
|
|
52
|
+
export type ResolvedTestIdKeys<I> = {
|
|
53
|
+
[K in keyof I & string]: I[K] extends string ? K : ExpandDynamicKey<K, I[K]>;
|
|
54
|
+
}[keyof I & string];
|
|
55
|
+
export type AllExpandedTestIdKeys<I> = {
|
|
56
|
+
[P in keyof I & string]: I[P] extends string ? P : ExpandDynamicKey<P, I[P]>;
|
|
57
|
+
}[keyof I & string];
|
|
58
|
+
export type AllOtherExpandedTestIdKeys<I, K extends keyof I> = {
|
|
59
|
+
[P in Exclude<keyof I, K> & string]: I[P] extends string ? P : ExpandDynamicKey<P, I[P]>;
|
|
60
|
+
}[Exclude<keyof I, K> & string];
|
|
61
|
+
export type ValidateTestIds<I, TargetKey extends 'testId' | 'selector', S> = {
|
|
62
|
+
[K in keyof I & string]: I[K] extends string ? K extends AllOtherExpandedTestIdKeys<I, K> | AllExpandedTestIdKeys<S> ? {
|
|
63
|
+
[P in `Error: Duplicate key "${K}" is defined in multiple entries`]: never;
|
|
64
|
+
} : string : ExpandDynamicKey<K, I[K]> & (AllOtherExpandedTestIdKeys<I, K> | AllExpandedTestIdKeys<S>) extends never ? K extends `${string}{${string}}${string}` ? HasDuplicatePlaceholders<K> extends true ? {
|
|
65
|
+
[P in 'Error: Pattern contains duplicate placeholders']: never;
|
|
66
|
+
} : ValidateDynamicEntryProperties<K, I[K], TargetKey> : I[K] : {
|
|
67
|
+
[P in `Error: Dynamic locator "${K}" expands to duplicate key(s): ${ExpandDynamicKey<K, I[K]> & (AllOtherExpandedTestIdKeys<I, K> | AllExpandedTestIdKeys<S>) & string}`]: never;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getCallerLocation = getCallerLocation;
|
|
4
|
+
// Global stack trace filter to remove pw-core internal frames from all error stacks.
|
|
5
|
+
// This ensures that Playwright's auto-generated steps and expectations are attributed
|
|
6
|
+
// directly to the user's project files instead of showing internal pw-core library paths.
|
|
7
|
+
const originalPrepare = Error.prepareStackTrace;
|
|
8
|
+
Error.prepareStackTrace = (err, stack) => {
|
|
9
|
+
const filteredStack = stack.filter((frame) => {
|
|
10
|
+
const file = frame.getFileName();
|
|
11
|
+
if (!file)
|
|
12
|
+
return true;
|
|
13
|
+
const normalized = file.replace(/\\/g, '/');
|
|
14
|
+
if (normalized.includes('pw-core/src') ||
|
|
15
|
+
normalized.includes('pw-core/dist') ||
|
|
16
|
+
normalized.includes('node_modules/pw-core')) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
return true;
|
|
20
|
+
});
|
|
21
|
+
if (originalPrepare) {
|
|
22
|
+
try {
|
|
23
|
+
return originalPrepare(err, filteredStack);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
// Fallback if originalPrepare fails on filtered stack
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const errString = err.toString ? err.toString() : String(err);
|
|
30
|
+
return (errString +
|
|
31
|
+
'\n' +
|
|
32
|
+
filteredStack
|
|
33
|
+
.map((frame) => {
|
|
34
|
+
try {
|
|
35
|
+
return ' at ' + frame.toString();
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return ' at <unknown>';
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
.join('\n'));
|
|
42
|
+
};
|
|
43
|
+
function getCallerLocation() {
|
|
44
|
+
const originalPrepareStackTrace = Error.prepareStackTrace;
|
|
45
|
+
try {
|
|
46
|
+
const err = {};
|
|
47
|
+
Error.prepareStackTrace = (_err, stack) => stack;
|
|
48
|
+
Error.captureStackTrace(err);
|
|
49
|
+
const stack = err.stack;
|
|
50
|
+
if (!stack)
|
|
51
|
+
return undefined;
|
|
52
|
+
for (let i = 1; i < stack.length; i++) {
|
|
53
|
+
const frame = stack[i];
|
|
54
|
+
const file = frame.getFileName();
|
|
55
|
+
if (!file)
|
|
56
|
+
continue;
|
|
57
|
+
const normalizedFile = file.replace(/\\/g, '/');
|
|
58
|
+
// Skip internal node_modules, node built-ins, and pw-core source/dist files
|
|
59
|
+
if (normalizedFile.includes('node_modules') ||
|
|
60
|
+
normalizedFile.includes('pw-core/src') ||
|
|
61
|
+
normalizedFile.includes('pw-core/dist') ||
|
|
62
|
+
normalizedFile.includes('playwright/lib') ||
|
|
63
|
+
normalizedFile.includes('@playwright') ||
|
|
64
|
+
normalizedFile.startsWith('node:') ||
|
|
65
|
+
!normalizedFile.includes('/')) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
const line = frame.getLineNumber();
|
|
69
|
+
const column = frame.getColumnNumber();
|
|
70
|
+
if (!line)
|
|
71
|
+
continue;
|
|
72
|
+
return { file, line, column: column ?? 1 };
|
|
73
|
+
}
|
|
74
|
+
return undefined;
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
Error.prepareStackTrace = originalPrepareStackTrace;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -15,7 +15,14 @@ function formatStepDescription(methodName, target, args) {
|
|
|
15
15
|
const targetStr = formatTarget(target);
|
|
16
16
|
if (methodName === 'fill' && args[0] !== undefined) {
|
|
17
17
|
const options = args[1];
|
|
18
|
-
|
|
18
|
+
let shouldMask = false;
|
|
19
|
+
if (options && typeof options === 'object' && options.mask !== undefined) {
|
|
20
|
+
shouldMask = options.mask === true;
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
const targetStrLower = targetStr.toLowerCase();
|
|
24
|
+
shouldMask = targetStrLower.includes('pass') || targetStrLower.includes('pw');
|
|
25
|
+
}
|
|
19
26
|
const displayValue = shouldMask ? '*'.repeat(String(args[0]).length) : args[0];
|
|
20
27
|
return `Fill "${targetStr}" with "${displayValue}"`;
|
|
21
28
|
}
|
|
@@ -30,7 +37,10 @@ function formatStepDescription(methodName, target, args) {
|
|
|
30
37
|
if (methodName === 'uncheck')
|
|
31
38
|
return `Uncheck "${targetStr}"`;
|
|
32
39
|
const formattedMethod = methodName.charAt(0).toUpperCase() + methodName.slice(1);
|
|
33
|
-
const cleanArgs = args
|
|
40
|
+
const cleanArgs = args
|
|
41
|
+
.filter((a) => typeof a === 'string' || typeof a === 'number')
|
|
42
|
+
.map((a) => `"${a}"`)
|
|
43
|
+
.join(', ');
|
|
34
44
|
return `${formattedMethod} "${targetStr}"${cleanArgs ? ' with ' + cleanArgs : ''}`;
|
|
35
45
|
}
|
|
36
46
|
function formatAssertionDescription(target, matcherName, isNegated, args) {
|
|
@@ -42,8 +52,8 @@ function formatAssertionDescription(target, matcherName, isNegated, args) {
|
|
|
42
52
|
toBeEnabled: isNegated ? 'is not enabled' : 'is enabled',
|
|
43
53
|
toBeDisabled: isNegated ? 'is not disabled' : 'is disabled',
|
|
44
54
|
toBeChecked: isNegated ? 'is not checked' : 'is checked',
|
|
45
|
-
toHaveText: (isNegated ? 'does not have text "' : 'has text "') + args.map(a => String(a)).join(', ') + '"',
|
|
46
|
-
toContainText: (isNegated ? 'does not contain "' : 'contains "') + args.map(a => String(a)).join(', ') + '"',
|
|
55
|
+
toHaveText: (isNegated ? 'does not have text "' : 'has text "') + args.map((a) => String(a)).join(', ') + '"',
|
|
56
|
+
toContainText: (isNegated ? 'does not contain "' : 'contains "') + args.map((a) => String(a)).join(', ') + '"',
|
|
47
57
|
toHaveCount: (isNegated ? 'does not have count of "' : 'has count of "') + args[0] + '"'
|
|
48
58
|
};
|
|
49
59
|
return `${prefix} ${matcherMap[matcherName] || `${isNegated ? 'not ' : ''}${matcherName}`}`;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { zeroArgMethodsList, oneArgMethodsList } from '../constants/methods';
|
|
2
|
+
export type AllowedZeroArgMethods = (typeof zeroArgMethodsList)[number];
|
|
3
|
+
export type AllowedOneArgMethods = (typeof oneArgMethodsList)[number];
|
|
4
|
+
export type AllowedMethodKeys = AllowedZeroArgMethods | AllowedOneArgMethods;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { rolesList, strategyList } from '../constants/strategies';
|
|
2
|
+
export type RoleType = (typeof rolesList)[number];
|
|
3
|
+
export type StrategyType = (typeof strategyList)[number];
|
|
4
|
+
export type PlaywrightRoleOptions = {
|
|
5
|
+
exact?: boolean;
|
|
6
|
+
checked?: boolean;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
expanded?: boolean;
|
|
9
|
+
includeHidden?: boolean;
|
|
10
|
+
level?: number;
|
|
11
|
+
pressed?: boolean;
|
|
12
|
+
selected?: boolean;
|
|
13
|
+
};
|
|
14
|
+
export type PlaywrightTextOptions = {
|
|
15
|
+
exact?: boolean;
|
|
16
|
+
};
|
|
17
|
+
export type LocatorStrategyOptions<S extends string> = S extends RoleType ? PlaywrightRoleOptions : S extends 'text' | 'label' | 'title' | 'placeholder' | 'altText' ? PlaywrightTextOptions : {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pw-core",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"workspaces": [
|
|
5
5
|
"examples",
|
|
6
6
|
"create-pw-core"
|
|
@@ -23,6 +23,9 @@
|
|
|
23
23
|
"typescript"
|
|
24
24
|
],
|
|
25
25
|
"main": "./dist/index.js",
|
|
26
|
+
"bin": {
|
|
27
|
+
"pw-core": "./dist/cli.js"
|
|
28
|
+
},
|
|
26
29
|
"types": "./dist/index.d.ts",
|
|
27
30
|
"files": [
|
|
28
31
|
"dist",
|
|
@@ -58,10 +61,14 @@
|
|
|
58
61
|
"devDependencies": {
|
|
59
62
|
"@playwright/test": "^1.61.0",
|
|
60
63
|
"@types/node": "^20.11.0",
|
|
64
|
+
"prettier": "^3.9.4",
|
|
61
65
|
"rimraf": "^6.0.1",
|
|
62
66
|
"typescript": "^5.3.3"
|
|
63
67
|
},
|
|
64
68
|
"publishConfig": {
|
|
65
69
|
"access": "public"
|
|
70
|
+
},
|
|
71
|
+
"dependencies": {
|
|
72
|
+
"playwright-core": "^1.61.1"
|
|
66
73
|
}
|
|
67
74
|
}
|