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.
Files changed (96) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.d.ts +2 -0
  3. package/dist/cli.js +777 -0
  4. package/dist/codegen/config/context-weights.d.ts +8 -0
  5. package/dist/codegen/config/context-weights.js +11 -0
  6. package/dist/codegen/config/ignored-attributes.d.ts +1 -0
  7. package/dist/codegen/config/ignored-attributes.js +4 -0
  8. package/dist/codegen/config/ignored-classes.d.ts +1 -0
  9. package/dist/codegen/config/ignored-classes.js +144 -0
  10. package/dist/codegen/config/ignored-values.d.ts +1 -0
  11. package/dist/codegen/config/ignored-values.js +4 -0
  12. package/dist/codegen/config/index.d.ts +29 -0
  13. package/dist/codegen/config/index.js +17 -0
  14. package/dist/codegen/config/strategy-order.d.ts +1 -0
  15. package/dist/codegen/config/strategy-order.js +18 -0
  16. package/dist/codegen/config/weights.d.ts +15 -0
  17. package/dist/codegen/config/weights.js +18 -0
  18. package/dist/codegen/floating-panel/client.d.ts +1 -0
  19. package/dist/codegen/floating-panel/client.js +248 -0
  20. package/dist/codegen/floating-panel/index.d.ts +4 -0
  21. package/dist/codegen/floating-panel/index.js +20 -0
  22. package/dist/codegen/floating-panel/manager.d.ts +19 -0
  23. package/dist/codegen/floating-panel/manager.js +90 -0
  24. package/dist/codegen/floating-panel/template.d.ts +2 -0
  25. package/dist/codegen/floating-panel/template.js +58 -0
  26. package/dist/codegen/floating-panel/types.d.ts +7 -0
  27. package/dist/codegen/floating-panel/types.js +2 -0
  28. package/dist/codegen/generator/action.validator.d.ts +5 -0
  29. package/dist/codegen/generator/action.validator.js +27 -0
  30. package/dist/codegen/generator/index.d.ts +13 -0
  31. package/dist/codegen/generator/index.js +724 -0
  32. package/dist/codegen/generator/locator.restricted.d.ts +36 -0
  33. package/dist/codegen/generator/locator.restricted.js +127 -0
  34. package/dist/codegen/generator/locator.validator.d.ts +8 -0
  35. package/dist/codegen/generator/locator.validator.js +38 -0
  36. package/dist/codegen/hover-tracker/client.d.ts +1 -0
  37. package/dist/codegen/hover-tracker/client.js +75 -0
  38. package/dist/codegen/hover-tracker/index.d.ts +3 -0
  39. package/dist/codegen/hover-tracker/index.js +19 -0
  40. package/dist/codegen/hover-tracker/manager.d.ts +11 -0
  41. package/dist/codegen/hover-tracker/manager.js +28 -0
  42. package/dist/codegen/hover-tracker/types.d.ts +3 -0
  43. package/dist/codegen/hover-tracker/types.js +2 -0
  44. package/dist/codegen/index.d.ts +35 -0
  45. package/dist/codegen/index.js +1147 -0
  46. package/dist/codegen/key-utils.d.ts +23 -0
  47. package/dist/codegen/key-utils.js +68 -0
  48. package/dist/codegen/scorer/base-score.d.ts +1 -0
  49. package/dist/codegen/scorer/base-score.js +7 -0
  50. package/dist/codegen/scorer/context-score.d.ts +2 -0
  51. package/dist/codegen/scorer/context-score.js +27 -0
  52. package/dist/codegen/scorer/index.d.ts +5 -0
  53. package/dist/codegen/scorer/index.js +23 -0
  54. package/dist/codegen/scorer/locator.ranking.d.ts +15 -0
  55. package/dist/codegen/scorer/locator.ranking.js +83 -0
  56. package/dist/codegen/scorer/semantic-score.d.ts +1 -0
  57. package/dist/codegen/scorer/semantic-score.js +47 -0
  58. package/dist/codegen/types.d.ts +35 -0
  59. package/dist/codegen/types.js +2 -0
  60. package/dist/component/table.js +19 -14
  61. package/dist/constants/methods.d.ts +2 -0
  62. package/dist/constants/methods.js +33 -0
  63. package/dist/constants/strategies.d.ts +2 -0
  64. package/dist/constants/strategies.js +47 -0
  65. package/dist/helpers.d.ts +1 -1
  66. package/dist/helpers.js +21 -9
  67. package/dist/page/actions/locator-actions.js +46 -2
  68. package/dist/page/actions/page-actions.d.ts +11 -6
  69. package/dist/page/actions/page-actions.js +12 -12
  70. package/dist/page/assertions/verify-chain.d.ts +39 -32
  71. package/dist/page/assertions/verify-chain.js +21 -6
  72. package/dist/page/assertions/verify-helpers.d.ts +9 -3
  73. package/dist/page/assertions/verify-helpers.js +18 -9
  74. package/dist/page/config.d.ts +126 -38
  75. package/dist/page/config.js +10 -31
  76. package/dist/page/index.d.ts +1 -0
  77. package/dist/page/index.js +1 -0
  78. package/dist/page/locators/dynamic-locator-resolver.d.ts +49 -0
  79. package/dist/page/locators/dynamic-locator-resolver.js +187 -0
  80. package/dist/page/locators/resolver.d.ts +15 -14
  81. package/dist/page/locators/resolver.js +187 -30
  82. package/dist/page/registry.d.ts +51 -12
  83. package/dist/page/registry.js +90 -18
  84. package/dist/page/typed-page.d.ts +33 -28
  85. package/dist/page/typed-page.js +89 -12
  86. package/dist/page/types/proxy-methods.d.ts +69 -54
  87. package/dist/page/types/validation.d.ts +69 -0
  88. package/dist/page/types/validation.js +2 -0
  89. package/dist/page/utils/caller-location.d.ts +5 -0
  90. package/dist/page/utils/caller-location.js +82 -0
  91. package/dist/page/utils/formatter.js +14 -4
  92. package/dist/types/methods.d.ts +4 -0
  93. package/dist/types/methods.js +2 -0
  94. package/dist/types/strategies.d.ts +17 -0
  95. package/dist/types/strategies.js +2 -0
  96. package/package.json +8 -1
@@ -1,22 +1,32 @@
1
1
  import test, { Page, TestType } from '@playwright/test';
2
- import { PageConfig } from './config';
2
+ import { PageConfig, ValidatePageConfig } from './config';
3
3
  import { TypedPage } from './typed-page';
4
4
  export type PageRegistry = Record<string, PageConfig>;
5
- export interface PageConstructor<C extends PageConfig<any>> {
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<any>>, P, W, O = {}> = TestType<P & {
9
- [K in keyof T]: K extends keyof O ? O[K] extends new (page: Page, ...args: any[]) => infer R ? R : NonNullable<T[K]['Class']> extends new (page: Page, ...args: any[]) => infer R2 ? R2 : TypedPage<T[K]> : NonNullable<T[K]['Class']> extends new (page: Page, ...args: any[]) => infer R ? R : TypedPage<T[K]>;
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: any[]) => infer R ? R : TypedPage<T[K]> : TypedPage<T[K]>;
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: any[]) => infer R ? R : TypedPage<T[K]> : TypedPage<T[K]>;
10
12
  }, W & {
11
- [K in keyof T as `worker${Capitalize<K & string>}`]: K extends keyof O ? O[K] extends new (page: Page, ...args: any[]) => infer R ? R : NonNullable<T[K]['Class']> extends new (page: Page, ...args: any[]) => infer R2 ? R2 : TypedPage<T[K]> : NonNullable<T[K]['Class']> extends new (page: Page, ...args: any[]) => infer R ? R : TypedPage<T[K]>;
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: any[]) => infer R ? R : TypedPage<T[K]> : TypedPage<T[K]>;
12
14
  } & {
13
15
  workerPage: Page;
14
16
  }> & {
15
17
  pages: {
16
- [K in keyof T]: K extends keyof O ? O[K] : NonNullable<T[K]['Class']> extends new (page: Page, ...args: any[]) => any ? NonNullable<T[K]['Class']> : PageConstructor<T[K]>;
18
+ [K in keyof T]: K extends keyof O ? O[K] : PageConstructor<T[K]>;
19
+ } & {
20
+ [K in keyof T as K extends `worker${infer R}` ? Uncapitalize<R> : never]: K extends keyof O ? O[K] : PageConstructor<T[K]>;
21
+ } & {
22
+ [K in keyof T as K extends `worker${string}` ? K : `worker${Capitalize<K & string>}`]: K extends keyof O ? O[K] : PageConstructor<T[K]>;
17
23
  };
18
24
  classes: {
19
- [K in keyof T]: K extends keyof O ? O[K] : NonNullable<T[K]['Class']> extends new (page: Page, ...args: any[]) => any ? NonNullable<T[K]['Class']> : PageConstructor<T[K]>;
25
+ [K in keyof T]: K extends keyof O ? O[K] : PageConstructor<T[K]>;
26
+ } & {
27
+ [K in keyof T as K extends `worker${infer R}` ? Uncapitalize<R> : never]: K extends keyof O ? O[K] : PageConstructor<T[K]>;
28
+ } & {
29
+ [K in keyof T as K extends `worker${string}` ? K : `worker${Capitalize<K & string>}`]: K extends keyof O ? O[K] : PageConstructor<T[K]>;
20
30
  };
21
31
  extend<O2 extends Partial<{
22
32
  [K in keyof T]: new (page: Page, ...args: any[]) => any;
@@ -33,8 +43,35 @@ export type PageRegistryTest<T extends Record<string, PageConfig<any>>, P, W, O
33
43
  *
34
44
  * // returns the extended test runner directly
35
45
  * const test = createPageRegistry({
36
- * login: { ...config, Class: LoginPage },
37
- * });
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
+ });
38
75
  *
39
76
  * // returns page classes only when accessing .pages
40
77
  * const pages = createPageRegistry({
@@ -51,6 +88,8 @@ export type PageRegistryTest<T extends Record<string, PageConfig<any>>, P, W, O
51
88
  * });
52
89
  * ```
53
90
  */
54
- export declare function createPageRegistry<T extends {
55
- [K in keyof T]: PageConfig<T[K]>;
56
- } & Record<string, PageConfig<any>>>(registry: T): typeof test extends TestType<infer P, infer W> ? PageRegistryTest<T, P, W> : never;
91
+ export declare function createPageRegistry<const T extends Record<string, PageConfig>>(registry: [T] extends [{
92
+ [K in keyof T]: ValidatePageConfig<T[K]>;
93
+ }] ? T : {
94
+ [K in keyof T]: ValidatePageConfig<T[K]>;
95
+ }): typeof test extends TestType<infer P, infer W> ? PageRegistryTest<T, P, W> : never;
@@ -6,43 +6,86 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.createPageRegistry = createPageRegistry;
7
7
  const test_1 = __importDefault(require("@playwright/test"));
8
8
  const typed_page_1 = require("./typed-page");
9
+ const resolver_1 = require("./locators/resolver");
9
10
  function createPageRegistryWithClasses(registry, classes, base) {
10
11
  const fixtures = {
11
12
  // Shared worker context and page
12
- workerContext: [async ({ browser }, use) => {
13
+ workerContext: [
14
+ async ({ browser }, use) => {
13
15
  const context = await browser.newContext();
14
16
  await use(context);
15
17
  await context.close();
16
- }, { scope: 'worker' }],
17
- workerPage: [async ({ workerContext }, use) => {
18
+ },
19
+ { scope: 'worker' }
20
+ ],
21
+ workerPage: [
22
+ async ({ workerContext }, use) => {
18
23
  const page = await workerContext.newPage();
19
24
  await use(page);
20
- }, { scope: 'worker' }]
25
+ },
26
+ { scope: 'worker' }
27
+ ]
21
28
  };
22
29
  for (const key of Object.keys(registry)) {
30
+ // Page fixture for key
23
31
  fixtures[key] = async ({ page }, use) => {
24
32
  const PageClass = classes[key];
25
33
  const instance = new PageClass(page);
26
34
  await use(instance);
27
35
  };
28
- const workerKey = `worker${key.charAt(0).toUpperCase()}${key.slice(1)}`;
29
- fixtures[workerKey] = [async ({ workerPage }, use) => {
36
+ // Page fixture for un-prefixed key (if key starts with worker)
37
+ if (key.startsWith('worker')) {
38
+ const unPrefixed = key.slice(6).charAt(0).toLowerCase() + key.slice(7);
39
+ if (unPrefixed && !fixtures[unPrefixed]) {
40
+ fixtures[unPrefixed] = async ({ page }, use) => {
41
+ const PageClass = classes[key];
42
+ const instance = new PageClass(page);
43
+ await use(instance);
44
+ };
45
+ }
46
+ }
47
+ // Worker fixture for worker-prefixed key
48
+ const workerKey = key.startsWith('worker') ? key : `worker${key.charAt(0).toUpperCase()}${key.slice(1)}`;
49
+ fixtures[workerKey] = [
50
+ async ({ workerPage }, use) => {
30
51
  const PageClass = classes[key];
31
52
  const instance = new PageClass(workerPage);
32
53
  await use(instance);
33
- }, { scope: 'worker' }];
54
+ },
55
+ { scope: 'worker' }
56
+ ];
57
+ }
58
+ // Copy worker and non-worker aliases in pages and classes dictionaries
59
+ const extendedClasses = { ...classes };
60
+ for (const key of Object.keys(classes)) {
61
+ if (key.startsWith('worker')) {
62
+ const unPrefixed = key.slice(6).charAt(0).toLowerCase() + key.slice(7);
63
+ if (unPrefixed && !extendedClasses[unPrefixed]) {
64
+ extendedClasses[unPrefixed] = classes[key];
65
+ }
66
+ }
67
+ else {
68
+ const workerKey = `worker${key.charAt(0).toUpperCase()}${key.slice(1)}`;
69
+ if (!extendedClasses[workerKey]) {
70
+ extendedClasses[workerKey] = classes[key];
71
+ }
72
+ }
34
73
  }
35
74
  const testRunner = base.extend(fixtures);
36
- testRunner.pages = classes;
37
- testRunner.classes = classes;
75
+ testRunner.pages = extendedClasses;
76
+ testRunner.classes = extendedClasses;
38
77
  const originalExtend = testRunner.extend.bind(testRunner);
39
78
  testRunner.extend = (overrides, customBase = test_1.default) => {
40
79
  if (!overrides) {
41
80
  return originalExtend();
42
81
  }
43
- const isPlaywrightFixtures = Object.values(overrides).every(val => (typeof val === 'function' && !val.toString().startsWith('class')) || Array.isArray(val));
82
+ const isPlaywrightFixtures = Object.values(overrides).every((val) => (typeof val === 'function' && !val.toString().startsWith('class')) || Array.isArray(val));
44
83
  if (isPlaywrightFixtures) {
45
- return originalExtend(overrides);
84
+ const runner = originalExtend(overrides);
85
+ runner.pages = extendedClasses;
86
+ runner.classes = extendedClasses;
87
+ runner.extend = testRunner.extend;
88
+ return runner;
46
89
  }
47
90
  const newClasses = { ...classes };
48
91
  for (const key of Object.keys(overrides)) {
@@ -65,8 +108,35 @@ function createPageRegistryWithClasses(registry, classes, base) {
65
108
  *
66
109
  * // returns the extended test runner directly
67
110
  * const test = createPageRegistry({
68
- * login: { ...config, Class: LoginPage },
69
- * });
111
+ codegenPage: {
112
+ url: '/',
113
+ selectors: {
114
+ '{item}': {
115
+ item: ['why-pw-core', 'why-not-playwright', 'registry', 'typed-page', 'features'],
116
+ selector: '[data-parent-id="{item}"]'
117
+ },
118
+ copyToClipboardBtn: '.page',
119
+ dynamicLocators: 'text="Dynamic Locators" >> nth=0',
120
+ rootrailthumbright: '#rootRailThumbRight',
121
+ 'text{item}': {
122
+ item: ['Auto Steps', 'Capabilities >> nth=0', 'Secret Masking'],
123
+ selector: 'text="{item}"'
124
+ },
125
+ tshirts: '.title-title',
126
+ tShirts: 'internal:role=link[name="T-Shirts"s]'
127
+ },
128
+ testIds: {
129
+ qaWorkspaceBtn: 'active-workspace-btn',
130
+ wsOptionApp: 'ws-option-app'
131
+ }
132
+ },
133
+ blankPage: {
134
+ url: '/',
135
+ selectors: {
136
+ element: '[data-pw-cursor="pointer"]'
137
+ }
138
+ }
139
+ });
70
140
  *
71
141
  * // returns page classes only when accessing .pages
72
142
  * const pages = createPageRegistry({
@@ -87,11 +157,13 @@ function createPageRegistry(registry) {
87
157
  const classes = {};
88
158
  for (const key of Object.keys(registry)) {
89
159
  const config = registry[key];
90
- classes[key] = config.Class || (class extends typed_page_1.TypedPage {
91
- constructor(page) {
92
- super(page, config);
93
- }
94
- });
160
+ (0, resolver_1.buildLookupIndex)(config);
161
+ classes[key] =
162
+ class extends typed_page_1.TypedPage {
163
+ constructor(page) {
164
+ super(page, config);
165
+ }
166
+ };
95
167
  }
96
168
  return createPageRegistryWithClasses(registry, classes, test_1.default);
97
169
  }
@@ -1,11 +1,7 @@
1
1
  import { Page, Locator, expect as playwrightExpect } from '@playwright/test';
2
- import { TypedLocators, ProxyLocatorMethods, PageKeys, ChainedKeys } from './config';
2
+ import { PageConfig, TypedLocators, ProxyLocatorMethods, PageKeys, ChainedKeys, ModifyOptionsForTarget, UniversalKeys, SemanticKeys } from './config';
3
3
  import { VerifyFn, AssertionsMethod } from './assertions/verify-chain';
4
- declare class TypedPageClass<T extends {
5
- testIds?: Record<string, string>;
6
- selectors?: Record<string, string>;
7
- url?: string;
8
- }> {
4
+ declare class TypedPageClass<T extends PageConfig> {
9
5
  readonly page: Page;
10
6
  readonly context: Page | Locator;
11
7
  protected readonly config: T;
@@ -30,50 +26,59 @@ declare class TypedPageClass<T extends {
30
26
  waitForLoadState(state?: Parameters<Page['waitForLoadState']>[0], options?: Parameters<Page['waitForLoadState']>[1]): Promise<void>;
31
27
  /** Wait for navigation to a URL. Defaults to the page config URL. */
32
28
  waitForURL(urlOrOptions?: string | RegExp | Parameters<Page['waitForURL']>[1], options?: Parameters<Page['waitForURL']>[1]): Promise<void>;
33
- resolveLocator(target: PageKeys<T> | ChainedKeys<T> | Locator, options?: {
29
+ resolveLocator<Target extends PageKeys<T> | Locator>(target: Target, options?: ModifyOptionsForTarget<T, Target, {
34
30
  nth?: number;
35
31
  raw?: boolean;
36
32
  hasText?: string | RegExp;
37
- }): Locator;
33
+ }>): Locator;
38
34
  /** Resolve a config key to a proxied Locator with step-wrapped methods. */
39
- locator(target: PageKeys<T> | ChainedKeys<T> | Locator, options?: Parameters<Locator['filter']>[0] & {
35
+ locator(target: PageKeys<T> | Locator, options?: Parameters<Locator['filter']>[0] & {
36
+ nth?: number;
37
+ }): Locator;
38
+ /** Chain multiple locators sequentially up to depth 3. */
39
+ chain<P1 extends UniversalKeys<T>, P2 extends PageKeys<T>>(p1: P1, p2: P2, options?: {
40
40
  nth?: number;
41
+ raw?: boolean;
42
+ hasText?: string | RegExp;
43
+ }): Locator;
44
+ chain<P1 extends UniversalKeys<T>, P2 extends SemanticKeys<T>, P3 extends UniversalKeys<T>>(p1: P1, p2: P2, p3: P3, options?: {
45
+ nth?: number;
46
+ raw?: boolean;
47
+ hasText?: string | RegExp;
48
+ }): Locator;
49
+ chain<P1 extends UniversalKeys<T>, P2 extends UniversalKeys<T>, P3 extends PageKeys<T>>(p1: P1, p2: P2, p3: P3, options?: {
50
+ nth?: number;
51
+ raw?: boolean;
52
+ hasText?: string | RegExp;
53
+ }): Locator;
54
+ chain(path: ChainedKeys<T>, options?: {
55
+ nth?: number;
56
+ raw?: boolean;
57
+ hasText?: string | RegExp;
41
58
  }): Locator;
42
59
  /** Chainable assertions on config keys. Supports `.soft` for soft assertions. */
43
60
  get verify(): VerifyFn<T> & {
44
61
  soft: VerifyFn<T>;
45
62
  };
46
- verifyHidden(target: PageKeys<T> | ChainedKeys<T> | Locator, options?: Parameters<ReturnType<typeof playwrightExpect<Locator>>['toBeHidden']>[0] & {
63
+ verifyHidden(target: PageKeys<T> | Locator, options?: Parameters<ReturnType<typeof playwrightExpect<Locator>>['toBeHidden']>[0] & {
47
64
  nth?: number;
48
65
  message?: string;
49
66
  }): Promise<void>;
50
- verifyEnabled(target: PageKeys<T> | ChainedKeys<T> | Locator, options?: Parameters<ReturnType<typeof playwrightExpect<Locator>>['toBeEnabled']>[0] & {
67
+ verifyEnabled(target: PageKeys<T> | Locator, options?: Parameters<ReturnType<typeof playwrightExpect<Locator>>['toBeEnabled']>[0] & {
51
68
  nth?: number;
52
69
  message?: string;
53
70
  }): Promise<void>;
54
- verifyDisabled(target: PageKeys<T> | ChainedKeys<T> | Locator, options?: Parameters<ReturnType<typeof playwrightExpect<Locator>>['toBeDisabled']>[0] & {
71
+ verifyDisabled(target: PageKeys<T> | Locator, options?: Parameters<ReturnType<typeof playwrightExpect<Locator>>['toBeDisabled']>[0] & {
55
72
  nth?: number;
56
73
  message?: string;
57
74
  }): Promise<void>;
58
- expect(target: PageKeys<T> | ChainedKeys<T> | Locator, message?: string): ReturnType<typeof playwrightExpect<Locator>>;
75
+ expect(target: PageKeys<T> | Locator, message?: string): ReturnType<typeof playwrightExpect<Locator>>;
59
76
  }
60
- export type TypedPageType<T extends {
61
- testIds?: Record<string, string>;
62
- selectors?: Record<string, string>;
63
- url?: string;
64
- }> = TypedPageClass<T> & TypedLocators<T> & ProxyLocatorMethods<T> & AssertionsMethod<T>;
77
+ export type TypedPageType<T extends PageConfig> = TypedPageClass<T> & TypedLocators<T> & ProxyLocatorMethods<T> & AssertionsMethod<T>;
65
78
  export declare const TypedPage: {
66
- new <T extends {
67
- testIds?: Record<string, string>;
68
- selectors?: Record<string, string>;
69
- url?: string;
70
- }>(context: Page | Locator, config: T, options?: {
79
+ new <T extends PageConfig>(context: Page | Locator, config: T, options?: {
71
80
  timeout?: number;
72
81
  }): TypedPageType<T>;
73
82
  };
74
- export type TypedPage<T extends {
75
- testIds?: Record<string, string>;
76
- selectors?: Record<string, string>;
77
- url?: string;
78
- }> = TypedPageType<T>;
83
+ export type TypedPage<T extends PageConfig> = TypedPageType<T>;
79
84
  export {};
@@ -1,12 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TypedPage = void 0;
4
+ const test_1 = require("@playwright/test");
4
5
  const verify_chain_1 = require("./assertions/verify-chain");
5
6
  const verify_helpers_1 = require("./assertions/verify-helpers");
6
7
  const expect_1 = require("./assertions/expect");
7
8
  const page_actions_1 = require("./actions/page-actions");
8
9
  const locator_actions_1 = require("./actions/locator-actions");
9
10
  const resolver_1 = require("./locators/resolver");
11
+ const caller_location_1 = require("./utils/caller-location");
10
12
  class TypedPageClass {
11
13
  page;
12
14
  context;
@@ -15,38 +17,91 @@ class TypedPageClass {
15
17
  timeout;
16
18
  constructor(context, config, options) {
17
19
  this.context = context;
18
- this.page = typeof context.page === 'function'
19
- ? context.page()
20
- : context;
20
+ this.page = typeof context.page === 'function' ? context.page() : context;
21
21
  this.config = config;
22
22
  this.url = config.url;
23
23
  this.timeout = options?.timeout;
24
24
  (0, resolver_1.defineLocators)(this, this.context, this.config);
25
25
  (0, locator_actions_1.defineActionMethods)(this, this.resolveLocator.bind(this), this.timeout);
26
+ // Auto-wrap subclass methods with Playwright test.step
27
+ const builtInMethods = new Set([
28
+ 'constructor',
29
+ 'goto',
30
+ 'verifyURL',
31
+ 'verifyTitle',
32
+ 'reload',
33
+ 'waitForLoadState',
34
+ 'waitForURL',
35
+ 'resolveLocator',
36
+ 'locator',
37
+ 'verify',
38
+ 'verifyHidden',
39
+ 'verifyEnabled',
40
+ 'verifyDisabled',
41
+ 'expect'
42
+ ]);
43
+ let proto = Object.getPrototypeOf(this);
44
+ while (proto && proto !== Object.prototype && proto !== TypedPageClass.prototype) {
45
+ const propNames = Object.getOwnPropertyNames(proto);
46
+ for (const prop of propNames) {
47
+ const desc = Object.getOwnPropertyDescriptor(proto, prop);
48
+ if (desc && typeof desc.value === 'function' && !builtInMethods.has(prop) && !prop.startsWith('_')) {
49
+ const originalFn = desc.value;
50
+ Object.defineProperty(this, prop, {
51
+ value: function (...args) {
52
+ const words = prop.split(/(?=[A-Z])/).map((w) => w.toLowerCase());
53
+ if (words.length > 0) {
54
+ words[0] = words[0].charAt(0).toUpperCase() + words[0].slice(1);
55
+ }
56
+ const sentence = words.join(' ');
57
+ const formattedArgs = args.map((arg) => {
58
+ if (typeof arg === 'object' && arg !== null) {
59
+ try {
60
+ return JSON.stringify(arg);
61
+ }
62
+ catch {
63
+ return String(arg);
64
+ }
65
+ }
66
+ return String(arg);
67
+ });
68
+ const stepName = formattedArgs.length > 0 ? `${sentence} - ${formattedArgs.join(' - ')}` : sentence;
69
+ return test_1.test.step(stepName, () => {
70
+ return originalFn.apply(this, args);
71
+ });
72
+ },
73
+ writable: true,
74
+ configurable: true,
75
+ enumerable: true
76
+ });
77
+ }
78
+ }
79
+ proto = Object.getPrototypeOf(proto);
80
+ }
26
81
  }
27
82
  /** Navigate to the URL from page config. */
28
83
  async goto(options) {
29
- return (0, page_actions_1.goto)(this.page, this.url, this.constructor.name, options);
84
+ return (0, page_actions_1.goto)(this.page, this.url, this.constructor.name, options, (0, caller_location_1.getCallerLocation)());
30
85
  }
31
86
  /** Assert the current URL matches the page config URL or a custom pattern. */
32
87
  async verifyURL(urlOrOptions, options) {
33
- return (0, page_actions_1.verifyURL)(this.page, this.url, this.constructor.name, urlOrOptions, options);
88
+ return (0, page_actions_1.verifyURL)(this.page, this.url, this.constructor.name, urlOrOptions, options, (0, caller_location_1.getCallerLocation)());
34
89
  }
35
90
  /** Assert the page title matches the expected value. */
36
91
  async verifyTitle(title, options) {
37
- return (0, page_actions_1.verifyTitle)(this.page, title, options);
92
+ return (0, page_actions_1.verifyTitle)(this.page, title, options, (0, caller_location_1.getCallerLocation)());
38
93
  }
39
94
  /** Reload the current page. */
40
95
  async reload(options) {
41
- return (0, page_actions_1.reload)(this.page, options);
96
+ return (0, page_actions_1.reload)(this.page, options, (0, caller_location_1.getCallerLocation)());
42
97
  }
43
98
  /** Wait for the page to reach a load state. */
44
99
  async waitForLoadState(state, options) {
45
- return (0, page_actions_1.waitForLoadState)(this.page, state, options);
100
+ return (0, page_actions_1.waitForLoadState)(this.page, state, options, (0, caller_location_1.getCallerLocation)());
46
101
  }
47
102
  /** Wait for navigation to a URL. Defaults to the page config URL. */
48
103
  async waitForURL(urlOrOptions, options) {
49
- return (0, page_actions_1.waitForURL)(this.page, this.url, this.constructor.name, urlOrOptions, options);
104
+ return (0, page_actions_1.waitForURL)(this.page, this.url, this.constructor.name, urlOrOptions, options, (0, caller_location_1.getCallerLocation)());
50
105
  }
51
106
  resolveLocator(target, options) {
52
107
  return (0, resolver_1.resolveLocator)(this.context, this.config, target, options);
@@ -55,6 +110,28 @@ class TypedPageClass {
55
110
  locator(target, options) {
56
111
  return (0, resolver_1.locator)(this.context, this.config, target, options);
57
112
  }
113
+ chain(...args) {
114
+ let options = undefined;
115
+ const keys = [];
116
+ for (const arg of args) {
117
+ if (typeof arg === 'string') {
118
+ if (arg.includes('.')) {
119
+ keys.push(...arg.split('.'));
120
+ }
121
+ else {
122
+ keys.push(arg);
123
+ }
124
+ }
125
+ else if (typeof arg === 'object' && arg !== null) {
126
+ options = arg;
127
+ }
128
+ }
129
+ let loc = (0, resolver_1.resolveLocator)(this.context, this.config, keys[0], options);
130
+ for (let i = 1; i < keys.length; i++) {
131
+ loc = (0, resolver_1.resolveLocator)(loc, this.config, keys[i], options);
132
+ }
133
+ return loc;
134
+ }
58
135
  /** Chainable assertions on config keys. Supports `.soft` for soft assertions. */
59
136
  get verify() {
60
137
  const fn = (target, opts) => (0, verify_chain_1.createVerifyChain)(this.resolveLocator.bind(this), target, opts, false);
@@ -62,13 +139,13 @@ class TypedPageClass {
62
139
  return fn;
63
140
  }
64
141
  async verifyHidden(target, options) {
65
- return (0, verify_helpers_1.verifyHidden)(this.resolveLocator.bind(this), target, options);
142
+ return (0, verify_helpers_1.verifyHidden)(this.resolveLocator.bind(this), target, options, (0, caller_location_1.getCallerLocation)());
66
143
  }
67
144
  async verifyEnabled(target, options) {
68
- return (0, verify_helpers_1.verifyEnabled)(this.resolveLocator.bind(this), target, options);
145
+ return (0, verify_helpers_1.verifyEnabled)(this.resolveLocator.bind(this), target, options, (0, caller_location_1.getCallerLocation)());
69
146
  }
70
147
  async verifyDisabled(target, options) {
71
- return (0, verify_helpers_1.verifyDisabled)(this.resolveLocator.bind(this), target, options);
148
+ return (0, verify_helpers_1.verifyDisabled)(this.resolveLocator.bind(this), target, options, (0, caller_location_1.getCallerLocation)());
72
149
  }
73
150
  expect(target, message) {
74
151
  const resolved = typeof target !== 'string' ? target : this.resolveLocator(target);
@@ -1,60 +1,75 @@
1
1
  import { Locator } from '@playwright/test';
2
- import type { PageKeys, ChainedKeys } from '../config';
3
- /**
4
- * Playwright Locator methods proxied onto the page object.
5
- * Each method resolves a config key (or Locator) as the first argument.
6
- *
7
- * @example
8
- * ```ts
9
- * await page.click('submit');
10
- * await page.fill('username', 'alice', { nth: 1 });
11
- * ```
12
- *
13
- * pw-core adds `nth` to options — targets a specific match when multiple elements resolve.
14
- */
15
- type ProxyKeys = 'click' | 'dblclick' | 'hover' | 'focus' | 'blur' | 'check' | 'uncheck' | 'clear' | 'waitFor' | 'isChecked' | 'isDisabled' | 'isVisible' | 'textContent' | 'innerText' | 'allInnerTexts' | 'allTextContents' | 'count' | 'scrollIntoViewIfNeeded' | 'boundingBox' | 'press' | 'pressSequentially' | 'selectOption' | 'setInputFiles' | 'getAttribute';
16
- type ModifyProxyArgs<Args extends any[]> = Args extends [] ? [options?: {
17
- nth?: number;
18
- hasText?: string | RegExp;
19
- }] : Args extends [any, any?] ? [Args[0], (Exclude<Args[1], undefined> & {
20
- nth?: number;
21
- hasText?: string | RegExp;
22
- })?] : Args extends [any?] ? Exclude<Args[0], undefined> extends object ? [(Exclude<Args[0], undefined> & {
23
- nth?: number;
24
- hasText?: string | RegExp;
25
- })?] : [Exclude<Args[0], undefined>, options?: {
26
- nth?: number;
27
- hasText?: string | RegExp;
28
- }] : [options?: {
29
- nth?: number;
30
- hasText?: string | RegExp;
31
- }];
32
- type BaseProxyLocatorMethods<T> = {
33
- [K in keyof Locator as K extends ProxyKeys ? K : never]: (target: PageKeys<T> | ChainedKeys<T> | Locator, ...args: ModifyProxyArgs<Parameters<Locator[K]>>) => ReturnType<Locator[K]>;
34
- };
35
- /**
36
- * Playwright Locator methods proxied onto the page object.
37
- * Each method resolves a config key (or Locator) as the first argument.
38
- *
39
- * @example
40
- * ```ts
41
- * await page.click('submit');
42
- * await page.fill('username', 'alice', { nth: 1 });
43
- * ```
44
- *
45
- * pw-core adds `nth` and `hasText` to options — targets a specific match when multiple elements resolve.
46
- */
47
- export interface ProxyLocatorMethods<T> extends BaseProxyLocatorMethods<T> {
2
+ import type { PageKeys, CheckableLocatorKeys, CheckboxLocatorKeys, EditableLocatorKeys, SelectableLocatorKeys, ModifyOptionsForTarget } from '../config';
3
+ export interface ProxyLocatorMethods<T> {
4
+ /** @see {@link https://playwright.dev/docs/api/class-locator#locator-click Locator.click} */
5
+ click<Target extends PageKeys<T> | Locator>(target: Target, options?: ModifyOptionsForTarget<T, Target, Parameters<Locator['click']>[0]>): Promise<void>;
6
+ /** @see {@link https://playwright.dev/docs/api/class-locator#locator-dblclick Locator.dblclick} */
7
+ dblclick<Target extends PageKeys<T> | Locator>(target: Target, options?: ModifyOptionsForTarget<T, Target, Parameters<Locator['dblclick']>[0]>): Promise<void>;
8
+ /** @see {@link https://playwright.dev/docs/api/class-locator#locator-hover Locator.hover} */
9
+ hover<Target extends PageKeys<T> | Locator>(target: Target, options?: ModifyOptionsForTarget<T, Target, Parameters<Locator['hover']>[0]>): Promise<void>;
10
+ /** @see {@link https://playwright.dev/docs/api/class-locator#locator-focus Locator.focus} */
11
+ focus<Target extends PageKeys<T> | Locator>(target: Target, options?: ModifyOptionsForTarget<T, Target, Parameters<Locator['focus']>[0]>): Promise<void>;
12
+ /** @see {@link https://playwright.dev/docs/api/class-locator#locator-blur Locator.blur} */
13
+ blur<Target extends PageKeys<T> | Locator>(target: Target, options?: ModifyOptionsForTarget<T, Target, Parameters<Locator['blur']>[0]>): Promise<void>;
14
+ /** @see {@link https://playwright.dev/docs/api/class-locator#locator-check Locator.check} */
15
+ check<Target extends CheckableLocatorKeys<T> | Locator>(target: Target, options?: ModifyOptionsForTarget<T, Target, Parameters<Locator['check']>[0]>): Promise<void>;
16
+ /** @see {@link https://playwright.dev/docs/api/class-locator#locator-uncheck Locator.uncheck} */
17
+ uncheck<Target extends CheckboxLocatorKeys<T> | Locator>(target: Target, options?: ModifyOptionsForTarget<T, Target, Parameters<Locator['uncheck']>[0]>): Promise<void>;
18
+ /** @see {@link https://playwright.dev/docs/api/class-locator#locator-clear Locator.clear} */
19
+ clear<Target extends EditableLocatorKeys<T> | (string & {}) | Locator>(target: Target, options?: ModifyOptionsForTarget<T, Target, Parameters<Locator['clear']>[0]>): Promise<void>;
20
+ /** @see {@link https://playwright.dev/docs/api/class-locator#locator-wait-for Locator.waitFor} */
21
+ waitFor<Target extends PageKeys<T> | Locator>(target: Target, options?: ModifyOptionsForTarget<T, Target, Parameters<Locator['waitFor']>[0]>): Promise<void>;
22
+ /** @see {@link https://playwright.dev/docs/api/class-locator#locator-is-checked Locator.isChecked} */
23
+ isChecked<Target extends CheckableLocatorKeys<T> | Locator>(target: Target, options?: ModifyOptionsForTarget<T, Target, Parameters<Locator['isChecked']>[0]>): Promise<boolean>;
24
+ /** @see {@link https://playwright.dev/docs/api/class-locator#locator-is-disabled Locator.isDisabled} */
25
+ isDisabled<Target extends PageKeys<T> | Locator>(target: Target, options?: ModifyOptionsForTarget<T, Target, Parameters<Locator['isDisabled']>[0]>): Promise<boolean>;
26
+ /** @see {@link https://playwright.dev/docs/api/class-locator#locator-is-visible Locator.isVisible} */
27
+ isVisible<Target extends PageKeys<T> | Locator>(target: Target, options?: ModifyOptionsForTarget<T, Target, Parameters<Locator['isVisible']>[0]>): Promise<boolean>;
28
+ /** @see {@link https://playwright.dev/docs/api/class-locator#locator-text-content Locator.textContent} */
29
+ textContent<Target extends PageKeys<T> | Locator>(target: Target, options?: ModifyOptionsForTarget<T, Target, Parameters<Locator['textContent']>[0]>): Promise<string | null>;
30
+ /** @see {@link https://playwright.dev/docs/api/class-locator#locator-inner-text Locator.innerText} */
31
+ innerText<Target extends PageKeys<T> | Locator>(target: Target, options?: ModifyOptionsForTarget<T, Target, Parameters<Locator['innerText']>[0]>): Promise<string>;
32
+ /** @see {@link https://playwright.dev/docs/api/class-locator#locator-all-inner-texts Locator.allInnerTexts} */
33
+ allInnerTexts<Target extends PageKeys<T> | Locator>(target: Target): Promise<string[]>;
34
+ /** @see {@link https://playwright.dev/docs/api/class-locator#locator-all-text-contents Locator.allTextContents} */
35
+ allTextContents<Target extends PageKeys<T> | Locator>(target: Target): Promise<string[]>;
36
+ /** @see {@link https://playwright.dev/docs/api/class-locator#locator-count Locator.count} */
37
+ count<Target extends PageKeys<T> | Locator>(target: Target): Promise<number>;
38
+ /** @see {@link https://playwright.dev/docs/api/class-locator#locator-scroll-into-view-if-needed Locator.scrollIntoViewIfNeeded} */
39
+ scrollIntoViewIfNeeded<Target extends PageKeys<T> | Locator>(target: Target, options?: ModifyOptionsForTarget<T, Target, Parameters<Locator['scrollIntoViewIfNeeded']>[0]>): Promise<void>;
40
+ /** @see {@link https://playwright.dev/docs/api/class-locator#locator-bounding-box Locator.boundingBox} */
41
+ boundingBox<Target extends PageKeys<T> | Locator>(target: Target): Promise<{
42
+ x: number;
43
+ y: number;
44
+ width: number;
45
+ height: number;
46
+ } | null>;
47
+ /** @see {@link https://playwright.dev/docs/api/class-locator#locator-press Locator.press} */
48
+ press<Target extends EditableLocatorKeys<T> | (string & {}) | Locator>(target: Target, key: string, options?: ModifyOptionsForTarget<T, Target, Parameters<Locator['press']>[1]>): Promise<void>;
49
+ /** @see {@link https://playwright.dev/docs/api/class-locator#locator-press-sequentially Locator.pressSequentially} */
50
+ pressSequentially<Target extends EditableLocatorKeys<T> | (string & {}) | Locator>(target: Target, value: string, options?: ModifyOptionsForTarget<T, Target, Parameters<Locator['pressSequentially']>[1]>): Promise<void>;
51
+ /** @see {@link https://playwright.dev/docs/api/class-locator#locator-select-option Locator.selectOption} */
52
+ selectOption<Target extends SelectableLocatorKeys<T> | (string & {}) | Locator>(target: Target, values: string | string[] | {
53
+ value?: string;
54
+ label?: string;
55
+ index?: number;
56
+ } | null, options?: ModifyOptionsForTarget<T, Target, Parameters<Locator['selectOption']>[1]>): Promise<string[]>;
57
+ /** @see {@link https://playwright.dev/docs/api/class-locator#locator-set-input-files Locator.setInputFiles} */
58
+ setInputFiles<Target extends PageKeys<T> | Locator>(target: Target, files: string | string[] | {
59
+ name: string;
60
+ mimeType: string;
61
+ buffer: Buffer;
62
+ } | {
63
+ name: string;
64
+ mimeType: string;
65
+ buffer: Buffer;
66
+ }[], options?: ModifyOptionsForTarget<T, Target, Parameters<Locator['setInputFiles']>[1]>): Promise<void>;
67
+ /** @see {@link https://playwright.dev/docs/api/class-locator#locator-get-attribute Locator.getAttribute} */
68
+ getAttribute<Target extends PageKeys<T> | Locator>(target: Target, name: string, options?: ModifyOptionsForTarget<T, Target, Parameters<Locator['getAttribute']>[1]>): Promise<string | null>;
48
69
  /** @see {@link https://playwright.dev/docs/api/class-locator#locator-fill Locator.fill} */
49
- fill(target: PageKeys<T> | ChainedKeys<T> | Locator, value: string, options?: Parameters<Locator['fill']>[1] & {
50
- nth?: number;
51
- hasText?: string | RegExp;
70
+ fill<Target extends EditableLocatorKeys<T> | (string & {}) | Locator>(target: Target, value: string, options?: ModifyOptionsForTarget<T, Target, Parameters<Locator['fill']>[1]> & {
52
71
  mask?: boolean;
53
72
  }): Promise<void>;
54
73
  /** @see {@link https://playwright.dev/docs/api/class-locator#locator-drag-to Locator.dragTo} */
55
- dragTo(target: PageKeys<T> | ChainedKeys<T> | Locator, destination: PageKeys<T> | ChainedKeys<T> | Locator, options?: Parameters<Locator['dragTo']>[1] & {
56
- nth?: number;
57
- hasText?: string | RegExp;
58
- }): Promise<void>;
74
+ dragTo<Target extends PageKeys<T> | Locator>(target: Target, destination: PageKeys<T> | Locator, options?: ModifyOptionsForTarget<T, Target, Parameters<Locator['dragTo']>[1]>): Promise<void>;
59
75
  }
60
- export {};