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.
Files changed (61) hide show
  1. package/README.md +1 -0
  2. package/dist/cli.js +145 -421
  3. package/dist/codegen/action-formatter.d.ts +19 -0
  4. package/dist/codegen/action-formatter.js +160 -0
  5. package/dist/codegen/action-processor.d.ts +23 -0
  6. package/dist/codegen/action-processor.js +112 -0
  7. package/dist/codegen/floating-panel/client.d.ts +14 -1
  8. package/dist/codegen/floating-panel/client.js +20 -25
  9. package/dist/codegen/floating-panel/manager.js +16 -22
  10. package/dist/codegen/generator/action.validator.d.ts +2 -1
  11. package/dist/codegen/generator/action.validator.js +2 -1
  12. package/dist/codegen/generator/candidate-scorer.d.ts +7 -0
  13. package/dist/codegen/generator/candidate-scorer.js +206 -0
  14. package/dist/codegen/generator/dom-scanner.d.ts +39 -0
  15. package/dist/codegen/generator/dom-scanner.js +429 -0
  16. package/dist/codegen/generator/id-stability.d.ts +6 -0
  17. package/dist/codegen/generator/id-stability.js +38 -0
  18. package/dist/codegen/generator/index.d.ts +8 -10
  19. package/dist/codegen/generator/index.js +23 -678
  20. package/dist/codegen/generator/key-builder.d.ts +13 -0
  21. package/dist/codegen/generator/key-builder.js +32 -0
  22. package/dist/codegen/hover-tracker/client.d.ts +5 -0
  23. package/dist/codegen/hover-tracker/client.js +14 -15
  24. package/dist/codegen/hover-tracker/manager.js +2 -6
  25. package/dist/codegen/index.d.ts +9 -32
  26. package/dist/codegen/index.js +9 -1128
  27. package/dist/codegen/key-utils.d.ts +16 -0
  28. package/dist/codegen/key-utils.js +29 -1
  29. package/dist/codegen/project-finder.d.ts +29 -0
  30. package/dist/codegen/project-finder.js +283 -0
  31. package/dist/codegen/registry-matcher.d.ts +27 -0
  32. package/dist/codegen/registry-matcher.js +254 -0
  33. package/dist/codegen/registry-store.d.ts +52 -0
  34. package/dist/codegen/registry-store.js +652 -0
  35. package/dist/codegen/selector-parser.d.ts +16 -0
  36. package/dist/codegen/selector-parser.js +121 -0
  37. package/dist/codegen/types.d.ts +18 -1
  38. package/dist/codegen/uniqueness.d.ts +3 -0
  39. package/dist/codegen/uniqueness.js +15 -0
  40. package/dist/component/table.d.ts +2 -2
  41. package/dist/component/table.js +7 -6
  42. package/dist/index.d.ts +3 -0
  43. package/dist/index.js +19 -0
  44. package/dist/page/actions/locator-actions.d.ts +6 -10
  45. package/dist/page/actions/locator-actions.js +24 -23
  46. package/dist/page/assertions/verify-chain.d.ts +11 -13
  47. package/dist/page/assertions/verify-chain.js +26 -7
  48. package/dist/page/assertions/verify-helpers.d.ts +5 -15
  49. package/dist/page/config.d.ts +25 -25
  50. package/dist/page/locators/dynamic-locator-resolver.js +22 -9
  51. package/dist/page/locators/resolver.d.ts +17 -11
  52. package/dist/page/locators/resolver.js +84 -78
  53. package/dist/page/registry.d.ts +35 -35
  54. package/dist/page/registry.js +86 -82
  55. package/dist/page/typed-page.d.ts +1 -0
  56. package/dist/page/typed-page.js +22 -12
  57. package/dist/page/types/proxy-methods.d.ts +11 -19
  58. package/dist/page/types/validation.d.ts +1 -1
  59. package/dist/page/utils/formatter.d.ts +3 -3
  60. package/dist/page/utils/formatter.js +5 -2
  61. package/package.json +6 -3
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getSelectorValue = getSelectorValue;
4
+ exports.extractNthFromSelector = extractNthFromSelector;
5
+ /**
6
+ * Parses a Playwright or CSS selector string and extracts its semantic strategy type and value.
7
+ */
8
+ function getSelectorValue(selector) {
9
+ // 1. Try to find data-testid first
10
+ const testIdRegexes = [
11
+ /internal:testid=\[data-testid="([^"]+)"[si]?\]/,
12
+ /data-testid="([^"]+)"[si]?/,
13
+ /\[data-testid="([^"]+)"[si]?\]/,
14
+ /data-testid=([^\[\]\s]+)/
15
+ ];
16
+ for (const regex of testIdRegexes) {
17
+ const match = selector.match(regex);
18
+ if (match) {
19
+ let val = match[1];
20
+ val = val.replace(/^["']|["']$/g, '');
21
+ return { type: 'testId', val };
22
+ }
23
+ }
24
+ // 2. Playwright getByRole
25
+ const roleRegex = /internal:role=([a-zA-Z0-9_-]+)\[name="([^"]+)"i?\]/;
26
+ const roleMatch = selector.match(roleRegex);
27
+ if (roleMatch) {
28
+ let type = roleMatch[1];
29
+ if (type === 'img')
30
+ type = 'altText';
31
+ return { type: type, val: roleMatch[2] };
32
+ }
33
+ const roleSimpleRegex = /role=([a-zA-Z0-9_-]+)/;
34
+ const roleSimpleMatch = selector.match(roleSimpleRegex);
35
+ if (roleSimpleMatch) {
36
+ return { type: roleSimpleMatch[1], val: roleSimpleMatch[1] };
37
+ }
38
+ // 3. Playwright getByLabel
39
+ const labelRegex = /internal:label="([^"]+)"i?/;
40
+ const labelMatch = selector.match(labelRegex);
41
+ if (labelMatch) {
42
+ return { type: 'label', val: labelMatch[1] };
43
+ }
44
+ // 4. Playwright getByPlaceholder
45
+ const placeholderRegexes = [
46
+ /internal:placeholder="([^"]+)"i?/,
47
+ /internal:attr=\[placeholder="([^"]+)"[si]?\]/
48
+ ];
49
+ for (const regex of placeholderRegexes) {
50
+ const placeholderMatch = selector.match(regex);
51
+ if (placeholderMatch) {
52
+ return { type: 'placeholder', val: placeholderMatch[1] };
53
+ }
54
+ }
55
+ // 5. Playwright getByAltText
56
+ const altRegexes = [
57
+ /internal:alt="([^"]+)"i?/,
58
+ /internal:attr=\[alt="([^"]+)"[si]?\]/
59
+ ];
60
+ for (const regex of altRegexes) {
61
+ const altMatch = selector.match(regex);
62
+ if (altMatch) {
63
+ return { type: 'altText', val: altMatch[1] };
64
+ }
65
+ }
66
+ // 6. Playwright getByTitle
67
+ const titleRegexes = [
68
+ /internal:title="([^"]+)"i?/,
69
+ /internal:attr=\[title="([^"]+)"[si]?\]/
70
+ ];
71
+ for (const regex of titleRegexes) {
72
+ const titleMatch = selector.match(regex);
73
+ if (titleMatch) {
74
+ return { type: 'title', val: titleMatch[1] };
75
+ }
76
+ }
77
+ // 7. Playwright getByText
78
+ const textRegex = /(?:internal:text|text)="([^"]+)"i?/;
79
+ const textMatch = selector.match(textRegex);
80
+ if (textMatch) {
81
+ return { type: 'text', val: textMatch[1] };
82
+ }
83
+ const textSimpleRegex = /(?:internal:text|text)=([^"\s]+)/;
84
+ const textSimpleMatch = selector.match(textSimpleRegex);
85
+ if (textSimpleMatch) {
86
+ return { type: 'text', val: textSimpleMatch[1] };
87
+ }
88
+ // 8. Try to find ID selector
89
+ const idRegexes = [/id=([a-zA-Z0-9_-]+)/, /#([a-zA-Z0-9_-]+)/];
90
+ for (const regex of idRegexes) {
91
+ const match = selector.match(regex);
92
+ if (match) {
93
+ return { type: 'selector', val: `#${match[1]}` };
94
+ }
95
+ }
96
+ // 9. Try to find class selectors
97
+ const classRegexes = [/\.([a-zA-Z0-9_-]+)/];
98
+ for (const regex of classRegexes) {
99
+ const match = selector.match(regex);
100
+ if (match) {
101
+ return { type: 'selector', val: `.${match[1]}` };
102
+ }
103
+ }
104
+ // 10. Fallback to raw selector
105
+ return { type: 'selector', val: selector };
106
+ }
107
+ /**
108
+ * Extracts any `>> nth=N` or `>> first()` suffix from a Playwright selector.
109
+ */
110
+ function extractNthFromSelector(selector) {
111
+ const nthMatch = selector.match(/\s*>>\s*nth=(\d+)/i);
112
+ if (nthMatch) {
113
+ const baseSelector = selector.replace(/\s*>>\s*nth=\d+/i, '').trim();
114
+ return { baseSelector, nth: parseInt(nthMatch[1], 10) };
115
+ }
116
+ if (selector.endsWith(' >> first()')) {
117
+ const baseSelector = selector.substring(0, selector.length - ' >> first()'.length).trim();
118
+ return { baseSelector, nth: 0 };
119
+ }
120
+ return { baseSelector: selector };
121
+ }
@@ -1,3 +1,5 @@
1
+ import type { StrategyType } from '../types/strategies';
2
+ export type { DynamicRegistryEntry, RegistryLocatorDictionary, RegistryPageConfig, RegistryRoot } from './registry-store';
1
3
  export interface DOMContext {
2
4
  parentText?: string;
3
5
  siblingText?: string;
@@ -6,10 +8,11 @@ export interface DOMContext {
6
8
  parentAttributes?: Record<string, string>;
7
9
  siblingAttributes?: Record<string, string>;
8
10
  }
11
+ export type SelectorStrategyType = StrategyType | 'role' | 'ariaLabel' | 'selector' | 'id' | 'class' | 'css' | 'xpath' | 'dataAttribute';
9
12
  export interface Candidate {
10
13
  selector: string;
11
14
  source: 'target' | 'child' | 'sibling' | 'parent' | 'ancestor';
12
- strategy: string;
15
+ strategy: SelectorStrategyType;
13
16
  depth: number;
14
17
  baseScore: number;
15
18
  semanticScore: number;
@@ -19,6 +22,16 @@ export interface Candidate {
19
22
  totalScore: number;
20
23
  unique: boolean;
21
24
  valueToScore?: string;
25
+ /** Attribute used to derive a data-attribute candidate. */
26
+ attrName?: string;
27
+ /** ARIA role used to derive a role candidate. */
28
+ roleVal?: string;
29
+ /** Additional score awarded for accessibility-oriented locators. */
30
+ accessibilityBonus?: number;
31
+ /** Additional score awarded for stable locator values. */
32
+ stabilityBonus?: number;
33
+ /** Penalties applied to unstable or non-semantic locator values. */
34
+ penalties?: number;
22
35
  }
23
36
  export interface LocatorCandidate extends Candidate {
24
37
  locator: string;
@@ -27,6 +40,10 @@ export interface LocatorCandidate extends Candidate {
27
40
  parentText?: string;
28
41
  accessibleName?: string;
29
42
  generatedKey?: string;
43
+ /** Stable target metadata consumed by registry matching. */
44
+ targetTestId?: string;
45
+ targetId?: string;
46
+ targetParentId?: string;
30
47
  }
31
48
  export interface LocatorStrategy {
32
49
  name: string;
@@ -0,0 +1,3 @@
1
+ import type { Page } from '@playwright/test';
2
+ import { LocatorCandidate } from './types';
3
+ export declare function checkUniqueness(page: Page, candidates: LocatorCandidate[]): Promise<LocatorCandidate[]>;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkUniqueness = checkUniqueness;
4
+ async function checkUniqueness(page, candidates) {
5
+ for (const candidate of candidates) {
6
+ try {
7
+ const count = await page.locator(candidate.selector).count();
8
+ candidate.unique = count === 1;
9
+ }
10
+ catch (e) {
11
+ candidate.unique = false;
12
+ }
13
+ }
14
+ return candidates;
15
+ }
@@ -3,7 +3,7 @@ import { Locator } from '@playwright/test';
3
3
  * A custom Array subclass representing a list of table rows.
4
4
  * Inherits all standard Array methods while adding type-safe get and getAll capabilities.
5
5
  */
6
- export declare class TableRows<T extends Record<string, any>> extends Array<T> {
6
+ export declare class TableRows<T extends object> extends Array<T> {
7
7
  constructor(...items: T[]);
8
8
  /**
9
9
  * Overloaded get method:
@@ -20,7 +20,7 @@ export declare class TableRows<T extends Record<string, any>> extends Array<T> {
20
20
  getAll<K extends keyof T>(key: K): T[K][];
21
21
  getAll<K extends keyof T>(key: K, value: T[K]): T[];
22
22
  }
23
- export declare class Table<T extends Record<string, any>> {
23
+ export declare class Table<T extends object> {
24
24
  readonly root: Locator;
25
25
  constructor(root: Locator);
26
26
  /**
@@ -32,6 +32,7 @@ class TableRows extends Array {
32
32
  }
33
33
  }
34
34
  exports.TableRows = TableRows;
35
+ const TABLE_DATA_ROW_SELECTOR = 'tbody tr, tr[data-testid="transaction-row"]';
35
36
  class Table {
36
37
  root;
37
38
  constructor(root) {
@@ -56,7 +57,7 @@ class Table {
56
57
  return test_1.test.step('Get table rows', async () => {
57
58
  const headers = await this.getHeaders();
58
59
  const rowsData = await this.root
59
- .locator('tbody tr, tr[data-testid="transaction-row"]')
60
+ .locator(TABLE_DATA_ROW_SELECTOR)
60
61
  .evaluateAll((trs, headers) => {
61
62
  return trs.map((tr) => {
62
63
  const cells = Array.from(tr.querySelectorAll('td')).map((td) => td.textContent?.trim() || '');
@@ -71,7 +72,7 @@ class Table {
71
72
  return rowData;
72
73
  });
73
74
  }, headers);
74
- return rowsData.filter((r) => r !== null);
75
+ return rowsData.filter((row) => row !== null);
75
76
  }, { box: true, location: (0, caller_location_1.getCallerLocation)() });
76
77
  }
77
78
  /**
@@ -87,7 +88,7 @@ class Table {
87
88
  */
88
89
  async getRowCount() {
89
90
  return test_1.test.step('Get table row count', async () => {
90
- return await this.root.locator('tbody tr, tr[data-testid="transaction-row"]').evaluateAll((trs) => {
91
+ return await this.root.locator(TABLE_DATA_ROW_SELECTOR).evaluateAll((trs) => {
91
92
  return trs.filter((tr) => tr.querySelectorAll('td').length > 0).length;
92
93
  });
93
94
  }, { box: true, location: (0, caller_location_1.getCallerLocation)() });
@@ -102,15 +103,15 @@ class Table {
102
103
  if (columnIndex === -1) {
103
104
  throw new Error(`Column "${String(column)}" not found in table headers: ${headers.join(', ')}`);
104
105
  }
105
- return await this.root.evaluate((tableEl, { rowIndex, columnIndex }) => {
106
- const trs = Array.from(tableEl.querySelectorAll('tbody tr, tr[data-testid="transaction-row"]'));
106
+ return await this.root.evaluate((tableEl, { rowIndex, columnIndex, rowSelector }) => {
107
+ const trs = Array.from(tableEl.querySelectorAll(rowSelector));
107
108
  const dataRows = trs.filter((tr) => tr.querySelectorAll('td').length > 0);
108
109
  if (rowIndex < 0 || rowIndex >= dataRows.length) {
109
110
  throw new Error(`Row index ${rowIndex} is out of bounds (0 to ${dataRows.length - 1})`);
110
111
  }
111
112
  const cell = dataRows[rowIndex].querySelectorAll('td')[columnIndex];
112
113
  return cell?.textContent?.trim() ?? '';
113
- }, { rowIndex, columnIndex });
114
+ }, { rowIndex, columnIndex, rowSelector: TABLE_DATA_ROW_SELECTOR });
114
115
  }, { box: true, location: (0, caller_location_1.getCallerLocation)() });
115
116
  }
116
117
  }
@@ -0,0 +1,3 @@
1
+ export * from './page/index';
2
+ export * from './component/table';
3
+ export * from './helpers';
package/dist/index.js ADDED
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./page/index"), exports);
18
+ __exportStar(require("./component/table"), exports);
19
+ __exportStar(require("./helpers"), exports);
@@ -1,12 +1,8 @@
1
1
  import { Locator } from '@playwright/test';
2
2
  import { AllowedMethodKeys } from '../config';
3
- export declare function executeAction(prop: AllowedMethodKeys, resolveLocatorFn: (target: any, options?: {
4
- nth?: number;
5
- hasText?: string | RegExp;
6
- raw?: boolean;
7
- }) => Locator, timeout: number | undefined, args: any[]): Promise<any>;
8
- export declare function defineActionMethods(instance: any, resolveLocatorFn: (target: any, options?: {
9
- nth?: number;
10
- hasText?: string | RegExp;
11
- raw?: boolean;
12
- }) => Locator, timeout: number | undefined): void;
3
+ import { LocatorResolutionOptions, LocatorTarget } from '../locators/resolver';
4
+ type LocatorResolver = (target: LocatorTarget, options?: LocatorResolutionOptions) => Locator;
5
+ type ActionArguments = unknown[];
6
+ export declare function executeAction(prop: AllowedMethodKeys, resolveLocatorFn: LocatorResolver, timeout: number | undefined, args: ActionArguments): Promise<unknown>;
7
+ export declare function defineActionMethods(instance: object, resolveLocatorFn: LocatorResolver, timeout: number | undefined): void;
8
+ export {};
@@ -6,31 +6,33 @@ const test_1 = require("@playwright/test");
6
6
  const config_1 = require("../config");
7
7
  const formatter_1 = require("../utils/formatter");
8
8
  const caller_location_1 = require("../utils/caller-location");
9
+ function isActionOptions(value) {
10
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
11
+ }
12
+ function getResolutionOptions(value) {
13
+ if (!isActionOptions(value))
14
+ return {};
15
+ return {
16
+ nth: typeof value.nth === 'number' ? value.nth : undefined,
17
+ hasText: typeof value.hasText === 'string' || value.hasText instanceof RegExp ? value.hasText : undefined
18
+ };
19
+ }
20
+ function removeLocatorStrategyOptions(options) {
21
+ const { exact, checked, disabled, expanded, includeHidden, level, pressed, selected, ...playwrightOptions } = options;
22
+ return playwrightOptions;
23
+ }
9
24
  function executeAction(prop, resolveLocatorFn, timeout, args) {
10
25
  const [locatorKey, ...methodArgs] = args;
11
- let optNth = undefined;
12
- let optHasText = undefined;
13
26
  const optionsIndex = (0, config_1.getOptionsArgumentIndex)(prop);
14
- if (optionsIndex !== -1 && methodArgs.length > optionsIndex) {
15
- const opts = methodArgs[optionsIndex];
16
- if (opts && typeof opts === 'object') {
17
- if ('nth' in opts) {
18
- optNth = opts.nth;
19
- }
20
- if ('hasText' in opts) {
21
- optHasText = opts.hasText;
22
- }
23
- }
24
- }
25
- const isCount = prop === 'count';
26
27
  const userOptions = optionsIndex !== -1 && methodArgs.length > optionsIndex ? methodArgs[optionsIndex] : undefined;
28
+ const resolutionOptions = getResolutionOptions(userOptions);
29
+ const isCount = prop === 'count';
27
30
  const locator = resolveLocatorFn(locatorKey, {
28
- nth: optNth,
29
- hasText: optHasText,
31
+ ...resolutionOptions,
30
32
  raw: isCount,
31
- ...userOptions
33
+ ...(isActionOptions(userOptions) ? userOptions : {})
32
34
  });
33
- const method = locator[prop];
35
+ const method = Reflect.get(locator, prop);
34
36
  if (typeof method !== 'function') {
35
37
  throw new Error(`Property '${prop}' does not exist on Locator.`);
36
38
  }
@@ -39,9 +41,8 @@ function executeAction(prop, resolveLocatorFn, timeout, args) {
39
41
  }
40
42
  if (optionsIndex !== -1 && methodArgs.length > optionsIndex) {
41
43
  const opts = methodArgs[optionsIndex];
42
- if (opts && typeof opts === 'object') {
43
- const { exact, checked, disabled, expanded, includeHidden, level, pressed, selected, ...rest } = opts;
44
- methodArgs[optionsIndex] = rest;
44
+ if (isActionOptions(opts)) {
45
+ methodArgs[optionsIndex] = removeLocatorStrategyOptions(opts);
45
46
  }
46
47
  }
47
48
  if (timeout !== undefined) {
@@ -56,7 +57,7 @@ function executeAction(prop, resolveLocatorFn, timeout, args) {
56
57
  const stepName = (0, formatter_1.formatStepDescription)(prop, locatorKey, methodArgs);
57
58
  if (prop === 'fill' && methodArgs.length > 1) {
58
59
  const opts = methodArgs[1];
59
- if (opts && typeof opts === 'object' && 'mask' in opts) {
60
+ if (isActionOptions(opts) && 'mask' in opts) {
60
61
  const { mask, ...playwrightOpts } = opts;
61
62
  methodArgs[1] = playwrightOpts;
62
63
  }
@@ -64,7 +65,7 @@ function executeAction(prop, resolveLocatorFn, timeout, args) {
64
65
  let shouldMask = false;
65
66
  if (prop === 'fill' && methodArgs.length > 0) {
66
67
  const opts = methodArgs[1];
67
- if (opts && typeof opts === 'object' && opts.mask !== undefined) {
68
+ if (isActionOptions(opts) && opts.mask !== undefined) {
68
69
  shouldMask = opts.mask === true;
69
70
  }
70
71
  else {
@@ -1,5 +1,6 @@
1
1
  import { Locator, expect as playwrightExpect } from '@playwright/test';
2
2
  import { PageKeys, GetStrategyOfKey } from '../config';
3
+ import { LocatorResolutionOptions, LocatorTarget } from '../locators/resolver';
3
4
  export type VerifyOptions = {
4
5
  timeout?: number;
5
6
  nth?: number;
@@ -7,25 +8,25 @@ export type VerifyOptions = {
7
8
  message?: string;
8
9
  };
9
10
  type PlaywrightLocatorMatchers = ReturnType<typeof playwrightExpect<Locator>>;
10
- type ModifyMatcherArgs<Args extends any[]> = Args extends [] ? [options?: {
11
+ type ModifyMatcherArgs<Args extends readonly unknown[]> = Args extends [] ? [options?: {
11
12
  nth?: number;
12
13
  hasText?: string | RegExp;
13
14
  message?: string;
14
- }] : Args extends [any, any?] ? [
15
- Args[0],
16
- (Exclude<Args[1], undefined> & {
15
+ }] : Args extends [infer First, (infer Second)?] ? [
16
+ First,
17
+ (Exclude<Second, undefined> & {
17
18
  nth?: number;
18
19
  hasText?: string | RegExp;
19
20
  message?: string;
20
21
  })?
21
- ] : Args extends [any?] ? Exclude<Args[0], undefined> extends object ? [
22
- (Exclude<Args[0], undefined> & {
22
+ ] : Args extends [(infer First)?] ? Exclude<First, undefined> extends object ? [
23
+ (Exclude<First, undefined> & {
23
24
  nth?: number;
24
25
  hasText?: string | RegExp;
25
26
  message?: string;
26
27
  })?
27
28
  ] : [
28
- Exclude<Args[0], undefined>,
29
+ Exclude<First, undefined>,
29
30
  options?: {
30
31
  nth?: number;
31
32
  hasText?: string | RegExp;
@@ -39,7 +40,7 @@ type ModifyMatcherArgs<Args extends any[]> = Args extends [] ? [options?: {
39
40
  }
40
41
  ];
41
42
  type DynamicallyModifiedMatchers<T, Target> = {
42
- [K in keyof PlaywrightLocatorMatchers]: K extends 'not' ? Omit<VerifyMatchers<T, Target>, 'not'> : K extends 'toBeChecked' ? Target extends Locator ? (...args: ModifyMatcherArgs<Parameters<PlaywrightLocatorMatchers[K]>>) => Promise<void> : Target extends string ? GetStrategyOfKey<T, Target> extends 'checkbox' | 'radio' ? (...args: ModifyMatcherArgs<Parameters<PlaywrightLocatorMatchers[K]>>) => Promise<void> : never : never : PlaywrightLocatorMatchers[K] extends (...args: infer Args) => any ? (...args: ModifyMatcherArgs<Args>) => Promise<void> : PlaywrightLocatorMatchers[K];
43
+ [K in keyof PlaywrightLocatorMatchers]: K extends 'not' ? Omit<VerifyMatchers<T, Target>, 'not'> : K extends 'toBeChecked' ? Target extends Locator ? (...args: ModifyMatcherArgs<Parameters<PlaywrightLocatorMatchers[K]>>) => Promise<void> : Target extends string ? GetStrategyOfKey<T, Target> extends 'checkbox' | 'radio' ? (...args: ModifyMatcherArgs<Parameters<PlaywrightLocatorMatchers[K]>>) => Promise<void> : never : never : PlaywrightLocatorMatchers[K] extends (...args: infer Args) => unknown ? (...args: ModifyMatcherArgs<Args>) => Promise<void> : PlaywrightLocatorMatchers[K];
43
44
  };
44
45
  export type VerifyMatchers<T, Target> = DynamicallyModifiedMatchers<T, Target> & PromiseLike<void> & {
45
46
  (options?: Parameters<PlaywrightLocatorMatchers['toBeVisible']>[0] & {
@@ -73,9 +74,6 @@ export type AssertionsMethod<T> = {
73
74
  nth?: number;
74
75
  }): Locator;
75
76
  };
76
- export declare function createVerifyChain<T>(resolveLocator: (target: any, options?: {
77
- nth?: number;
78
- hasText?: string | RegExp;
79
- raw?: boolean;
80
- }) => Locator, target: PageKeys<T> | Locator, verifyOptions: VerifyOptions | undefined, isSoft: boolean): VerifyMatchers<T, any>;
77
+ export type VerifyResolver = (target: LocatorTarget, options?: LocatorResolutionOptions) => Locator;
78
+ export declare function createVerifyChain<T, Target extends PageKeys<T> | Locator>(resolveLocator: VerifyResolver, target: Target, verifyOptions: VerifyOptions | undefined, isSoft: boolean): VerifyMatchers<T, Target>;
81
79
  export {};
@@ -4,6 +4,21 @@ exports.createVerifyChain = createVerifyChain;
4
4
  const test_1 = require("@playwright/test");
5
5
  const formatter_1 = require("../utils/formatter");
6
6
  const caller_location_1 = require("../utils/caller-location");
7
+ function isAssertionOptions(value) {
8
+ return typeof value === 'object' && value !== null && !Array.isArray(value) && !(value instanceof RegExp);
9
+ }
10
+ function getOptionNumber(options, key) {
11
+ const value = options?.[key];
12
+ return typeof value === 'number' ? value : undefined;
13
+ }
14
+ function getOptionText(options) {
15
+ const value = options?.hasText;
16
+ return typeof value === 'string' || value instanceof RegExp ? value : undefined;
17
+ }
18
+ function getOptionMessage(options) {
19
+ const value = options?.message;
20
+ return typeof value === 'string' ? value : undefined;
21
+ }
7
22
  function createVerifyChain(resolveLocator, target, verifyOptions, isSoft) {
8
23
  const defaultNth = verifyOptions?.nth;
9
24
  const defaultHasText = verifyOptions?.hasText;
@@ -24,7 +39,7 @@ function createVerifyChain(resolveLocator, target, verifyOptions, isSoft) {
24
39
  return new Proxy(baseFn, {
25
40
  get(targetObj, prop) {
26
41
  if (typeof prop === 'symbol') {
27
- return targetObj[prop];
42
+ return Reflect.get(targetObj, prop);
28
43
  }
29
44
  if (prop === 'not') {
30
45
  if (isNegated)
@@ -48,17 +63,17 @@ function createVerifyChain(resolveLocator, target, verifyOptions, isSoft) {
48
63
  'toLocaleString'
49
64
  ]);
50
65
  if (skippedProps.has(prop)) {
51
- return targetObj[prop];
66
+ return Reflect.get(targetObj, prop);
52
67
  }
53
68
  return async (...args) => {
54
69
  const lastArg = args[args.length - 1];
55
- const lastIsOptions = lastArg !== null && typeof lastArg === 'object' && !(lastArg instanceof RegExp) && !Array.isArray(lastArg);
70
+ const lastIsOptions = isAssertionOptions(lastArg);
56
71
  const valueArgs = lastIsOptions ? args.slice(0, args.length - 1) : args;
57
72
  const options = lastIsOptions ? lastArg : undefined;
58
- const nth = options && 'nth' in options ? options.nth : defaultNth;
59
- const hasText = options && 'hasText' in options ? options.hasText : defaultHasText;
73
+ const nth = getOptionNumber(options, 'nth') ?? defaultNth;
74
+ const hasText = getOptionText(options) ?? defaultHasText;
60
75
  const isHaveCount = prop === 'toHaveCount';
61
- const stepName = options?.message ?? defaultMessage ?? (0, formatter_1.formatAssertionDescription)(target, String(prop), isNegated, valueArgs);
76
+ const stepName = getOptionMessage(options) ?? defaultMessage ?? (0, formatter_1.formatAssertionDescription)(target, prop, isNegated, valueArgs);
62
77
  await test_1.test.step(stepName, async () => {
63
78
  const locator = resolveLocator(target, {
64
79
  nth,
@@ -67,7 +82,11 @@ function createVerifyChain(resolveLocator, target, verifyOptions, isSoft) {
67
82
  });
68
83
  const expectation = expectFn(locator, stepName);
69
84
  const match = isNegated ? expectation.not : expectation;
70
- await match[prop](...args);
85
+ const matcher = Reflect.get(match, prop);
86
+ if (typeof matcher !== 'function') {
87
+ throw new Error(`Matcher '${prop}' does not exist on Playwright expectations.`);
88
+ }
89
+ await matcher.apply(match, args);
71
90
  }, { box: true, location: (0, caller_location_1.getCallerLocation)() });
72
91
  };
73
92
  }
@@ -1,32 +1,22 @@
1
1
  import { Locator, expect as playwrightExpect } from '@playwright/test';
2
+ import { VerifyResolver } from './verify-chain';
3
+ import { LocatorTarget } from '../locators/resolver';
2
4
  type StepLocation = {
3
5
  file: string;
4
6
  line: number;
5
7
  column: number;
6
8
  };
7
- export declare function verifyHidden(resolveLocator: (target: any, options?: {
8
- nth?: number;
9
- hasText?: string | RegExp;
10
- raw?: boolean;
11
- }) => Locator, target: any, options?: Parameters<ReturnType<typeof playwrightExpect<Locator>>['toBeHidden']>[0] & {
9
+ export declare function verifyHidden(resolveLocator: VerifyResolver, target: LocatorTarget, options?: Parameters<ReturnType<typeof playwrightExpect<Locator>>['toBeHidden']>[0] & {
12
10
  nth?: number;
13
11
  hasText?: string | RegExp;
14
12
  message?: string;
15
13
  }, location?: StepLocation): Promise<void>;
16
- export declare function verifyEnabled(resolveLocator: (target: any, options?: {
17
- nth?: number;
18
- hasText?: string | RegExp;
19
- raw?: boolean;
20
- }) => Locator, target: any, options?: Parameters<ReturnType<typeof playwrightExpect<Locator>>['toBeEnabled']>[0] & {
14
+ export declare function verifyEnabled(resolveLocator: VerifyResolver, target: LocatorTarget, options?: Parameters<ReturnType<typeof playwrightExpect<Locator>>['toBeEnabled']>[0] & {
21
15
  nth?: number;
22
16
  hasText?: string | RegExp;
23
17
  message?: string;
24
18
  }, location?: StepLocation): Promise<void>;
25
- export declare function verifyDisabled(resolveLocator: (target: any, options?: {
26
- nth?: number;
27
- hasText?: string | RegExp;
28
- raw?: boolean;
29
- }) => Locator, target: any, options?: Parameters<ReturnType<typeof playwrightExpect<Locator>>['toBeDisabled']>[0] & {
19
+ export declare function verifyDisabled(resolveLocator: VerifyResolver, target: LocatorTarget, options?: Parameters<ReturnType<typeof playwrightExpect<Locator>>['toBeDisabled']>[0] & {
30
20
  nth?: number;
31
21
  hasText?: string | RegExp;
32
22
  message?: string;