@cronn/playwright-utils 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -7,7 +7,7 @@ import { APIRequestContext, APIResponse, ConsoleMessage, Locator, Page, Request,
7
7
  *
8
8
  * Evaluated once when the module is loaded.
9
9
  */
10
- declare const isCI: boolean;
10
+ export declare const isCI: boolean;
11
11
  //#endregion
12
12
  //#region src/api/fetch-adapter.d.ts
13
13
  type FetchAPI = typeof fetch;
@@ -21,7 +21,7 @@ type FetchAPI = typeof fetch;
21
21
  * @param requestContext - Playwright request context to send requests with
22
22
  * @returns A function with the signature of the global `fetch`
23
23
  */
24
- declare function createFetchAdapter(requestContext: APIRequestContext): FetchAPI;
24
+ export declare function createFetchAdapter(requestContext: APIRequestContext): FetchAPI;
25
25
  //#endregion
26
26
  //#region src/capturing/console-captor.d.ts
27
27
  /**
@@ -57,7 +57,7 @@ type ConsoleMessageLevel = ReturnType<ConsoleMessage["type"]>;
57
57
  * ]);
58
58
  * ```
59
59
  */
60
- declare class ConsoleCaptor {
60
+ export declare class ConsoleCaptor {
61
61
  private readonly page;
62
62
  private readonly consoleListener;
63
63
  /**
@@ -112,6 +112,21 @@ declare class ConsoleCaptor {
112
112
  * The already captured {@link messages} are kept.
113
113
  */
114
114
  stopCapture(): void;
115
+ /**
116
+ * Remove all captured {@link messages}.
117
+ *
118
+ * @example
119
+ * ```ts
120
+ * captor.startCapture();
121
+ *
122
+ * await page.goto("/users");
123
+ * captor.clearMessages();
124
+ *
125
+ * await page.getByRole("button", { name: "Only enabled" }).click();
126
+ * expect(captor.messages).toHaveLength(1);
127
+ * ```
128
+ */
129
+ clearMessages(): void;
115
130
  /**
116
131
  * Collect the console messages reported while the callback runs.
117
132
  *
@@ -156,7 +171,7 @@ declare class ConsoleCaptor {
156
171
  * );
157
172
  * ```
158
173
  */
159
- declare function captureConsole(page: Page, levelOrFilter?: ConsoleMessageLevel | Array<ConsoleMessageLevel> | ConsoleMessageFilter): ConsoleCaptor;
174
+ export declare function captureConsole(page: Page, levelOrFilter?: ConsoleMessageLevel | Array<ConsoleMessageLevel> | ConsoleMessageFilter): ConsoleCaptor;
160
175
  /**
161
176
  * Tests whether the given console message has one of the specified log levels.
162
177
  *
@@ -166,7 +181,7 @@ declare function captureConsole(page: Page, levelOrFilter?: ConsoleMessageLevel
166
181
  * @param level - The log level
167
182
  * @returns True if the message has one of the given log levels
168
183
  */
169
- declare function hasLogLevel(message: ConsoleMessage, ...level: Array<ConsoleMessageLevel>): boolean;
184
+ export declare function hasLogLevel(message: ConsoleMessage, ...level: Array<ConsoleMessageLevel>): boolean;
170
185
  //#endregion
171
186
  //#region src/api/route-interceptor/filter.d.ts
172
187
  /**
@@ -196,7 +211,7 @@ interface RouteFilter {
196
211
  * });
197
212
  * ```
198
213
  */
199
- declare function matchPath(pattern: RegExp | string): RouteFilter;
214
+ export declare function matchPath(pattern: RegExp | string): RouteFilter;
200
215
  //#endregion
201
216
  //#region src/api/route-interceptor/response-handler.d.ts
202
217
  /**
@@ -221,7 +236,7 @@ type ResponseHandler = (response: APIResponse) => Promise<FulfillOptions> | Fulf
221
236
  * });
222
237
  * ```
223
238
  */
224
- declare function modifyJsonBody<T>(modifier: (body: T) => T): ResponseHandler;
239
+ export declare function modifyJsonBody<T>(modifier: (body: T) => T): ResponseHandler;
225
240
  /**
226
241
  * Helper to create a {@link ResponseHandler} that modifies the response body as a plaintext string.
227
242
  *
@@ -237,7 +252,7 @@ declare function modifyJsonBody<T>(modifier: (body: T) => T): ResponseHandler;
237
252
  * });
238
253
  * ```
239
254
  */
240
- declare function modifyTextBody(modifier: (body: string) => string): ResponseHandler;
255
+ export declare function modifyTextBody(modifier: (body: string) => string): ResponseHandler;
241
256
  //#endregion
242
257
  //#region src/api/route-interceptor/interceptor.d.ts
243
258
  type Action<T> = () => Promise<T> | T;
@@ -245,7 +260,7 @@ type FulfillOptions = Parameters<Route["fulfill"]>[0];
245
260
  /**
246
261
  * Entrypoint to create various network interceptors using the {@link Page#route} API.
247
262
  */
248
- declare class RouteInterceptor {
263
+ export declare class RouteInterceptor {
249
264
  readonly page: Page;
250
265
  readonly filter: RouteFilter;
251
266
  constructor(page: Page, filter: RouteFilter);
@@ -383,7 +398,7 @@ declare class ExecutableRouteInterceptor {
383
398
  * });
384
399
  * ```
385
400
  */
386
- declare function interceptRoute(page: Page, filter: RouteFilter): RouteInterceptor;
401
+ export declare function interceptRoute(page: Page, filter: RouteFilter): RouteInterceptor;
387
402
  /**
388
403
  * Base class to build re-usable route interceptors.
389
404
  *
@@ -413,7 +428,7 @@ declare function interceptRoute(page: Page, filter: RouteFilter): RouteIntercept
413
428
  * });
414
429
  * ```
415
430
  */
416
- declare class RouteInterceptorFixture {
431
+ export declare class RouteInterceptorFixture {
417
432
  private readonly page;
418
433
  constructor(page: Page);
419
434
  /**
@@ -445,7 +460,42 @@ declare class RouteInterceptorFixture {
445
460
  * @returns The absolute path to the resolved location
446
461
  * @throws If no package root can be found
447
462
  */
448
- declare function resolveFromPackageRoot(...paths: Array<string>): string;
463
+ export declare function resolveFromPackageRoot(...paths: Array<string>): string;
464
+ //#endregion
465
+ //#region src/page-object-model/locator-extensions.d.ts
466
+ type RoleLocators = Record<Role, RoleLocator>;
467
+ interface RoleLocator {
468
+ (options?: RoleLocatorOptions): Locator;
469
+ (name: string | RegExp, options?: RoleLocatorOptions): Locator;
470
+ }
471
+ type RoleLocatorOptions = Omit<GetByRoleOptions, "name">;
472
+ type Role = Parameters<Locator["getByRole"]>[0];
473
+ type GetByRoleOptions = Parameters<Locator["getByRole"]>[1];
474
+ /**
475
+ * Creates a set of locator functions for every ARIA role, so tests don't
476
+ * have to repeat `getByRole` and its role string throughout a page object.
477
+ *
478
+ * Every generated locator applies `exact: true` by default, keeping name
479
+ * matches from accidentally matching unrelated elements with a similar
480
+ * accessible name. This can be overridden per call through `options`, or
481
+ * replaced entirely for every locator by passing a custom `defaultOptions`.
482
+ *
483
+ * The accessible name can be omitted when an element should be located by
484
+ * role alone, in which case `options` can be passed as the first argument.
485
+ *
486
+ * @param locator - The page or locator to scope the role locators to
487
+ * @param defaultOptions - Optional. Options applied to every generated locator, overridden by per-call options; defaults to `{ exact: true }`
488
+ * @returns A `RoleLocators` object with one locator function per ARIA role
489
+ *
490
+ * @example
491
+ * ```ts
492
+ * const { button, textbox } = roleLocators(page);
493
+ *
494
+ * await textbox("Email").fill("user@example.com");
495
+ * await button("Submit").click();
496
+ * ```
497
+ */
498
+ export declare function roleLocators(locator: Pick<Locator, "getByRole">, defaultOptions?: GetByRoleOptions): RoleLocators;
449
499
  //#endregion
450
500
  //#region src/normalizers/mask-base-url.d.ts
451
501
  /**
@@ -454,7 +504,7 @@ declare function resolveFromPackageRoot(...paths: Array<string>): string;
454
504
  *
455
505
  * @param baseURL - Base URL to mask; if `undefined`, values are left unchanged
456
506
  */
457
- declare function maskBaseURL<TValue>(baseURL: string | undefined): Normalizer<TValue>;
507
+ export declare function maskBaseURL<TValue>(baseURL: string | undefined): Normalizer<TValue>;
458
508
  //#endregion
459
509
  //#region src/normalizers/masked-value.d.ts
460
510
  /**
@@ -463,14 +513,14 @@ declare function maskBaseURL<TValue>(baseURL: string | undefined): Normalizer<TV
463
513
  *
464
514
  * @param name - Name of the masked value
465
515
  */
466
- declare function maskedValue(name: string): string;
516
+ export declare function maskedValue(name: string): string;
467
517
  /**
468
518
  * Creates a factory for indexed masked placeholders, e.g. `[USER_1]`.
469
519
  *
470
520
  * @param name - Name of the masked value
471
521
  * @returns A function mapping an index to a masked placeholder
472
522
  */
473
- declare function maskedValueWithIndex(name: string): (index: number) => string;
523
+ export declare function maskedValueWithIndex(name: string): (index: number) => string;
474
524
  //#endregion
475
525
  //#region src/types/playwright.d.ts
476
526
  /**
@@ -479,4 +529,4 @@ declare function maskedValueWithIndex(name: string): (index: number) => string;
479
529
  */
480
530
  type PlaywrightTarget = Page | Locator;
481
531
  //#endregion
482
- export { ConsoleCaptor, type ConsoleMessageFilter, type ConsoleMessageLevel, type HttpMethod, type PlaywrightTarget, type ResponseHandler, type RouteFilter, RouteInterceptor, RouteInterceptorFixture, captureConsole, createFetchAdapter, hasLogLevel, interceptRoute, isCI, maskBaseURL, maskedValue, maskedValueWithIndex, matchPath, modifyJsonBody, modifyTextBody, resolveFromPackageRoot };
532
+ export type { ConsoleMessageFilter, ConsoleMessageLevel, HttpMethod, PlaywrightTarget, ResponseHandler, RoleLocators, RouteFilter };
package/dist/index.mjs CHANGED
@@ -148,6 +148,23 @@ var ConsoleCaptor = class {
148
148
  this.page.off("console", this.consoleListener);
149
149
  }
150
150
  /**
151
+ * Remove all captured {@link messages}.
152
+ *
153
+ * @example
154
+ * ```ts
155
+ * captor.startCapture();
156
+ *
157
+ * await page.goto("/users");
158
+ * captor.clearMessages();
159
+ *
160
+ * await page.getByRole("button", { name: "Only enabled" }).click();
161
+ * expect(captor.messages).toHaveLength(1);
162
+ * ```
163
+ */
164
+ clearMessages() {
165
+ this.messages.length = 0;
166
+ }
167
+ /**
151
168
  * Collect the console messages reported while the callback runs.
152
169
  *
153
170
  * The capturing is stopped once the action has finished, even if it throws.
@@ -592,6 +609,133 @@ function resolveFromPackageRoot(...paths) {
592
609
  return path.resolve(packageDir, ...paths);
593
610
  }
594
611
  //#endregion
612
+ //#region src/page-object-model/locator-extensions.ts
613
+ const ROLES = [
614
+ "alert",
615
+ "alertdialog",
616
+ "application",
617
+ "article",
618
+ "banner",
619
+ "blockquote",
620
+ "button",
621
+ "caption",
622
+ "cell",
623
+ "checkbox",
624
+ "code",
625
+ "columnheader",
626
+ "combobox",
627
+ "complementary",
628
+ "contentinfo",
629
+ "definition",
630
+ "deletion",
631
+ "dialog",
632
+ "directory",
633
+ "document",
634
+ "emphasis",
635
+ "feed",
636
+ "figure",
637
+ "form",
638
+ "generic",
639
+ "grid",
640
+ "gridcell",
641
+ "group",
642
+ "heading",
643
+ "img",
644
+ "insertion",
645
+ "link",
646
+ "list",
647
+ "listbox",
648
+ "listitem",
649
+ "log",
650
+ "main",
651
+ "marquee",
652
+ "math",
653
+ "meter",
654
+ "menu",
655
+ "menubar",
656
+ "menuitem",
657
+ "menuitemcheckbox",
658
+ "menuitemradio",
659
+ "navigation",
660
+ "none",
661
+ "note",
662
+ "option",
663
+ "paragraph",
664
+ "presentation",
665
+ "progressbar",
666
+ "radio",
667
+ "radiogroup",
668
+ "region",
669
+ "row",
670
+ "rowgroup",
671
+ "rowheader",
672
+ "scrollbar",
673
+ "search",
674
+ "searchbox",
675
+ "separator",
676
+ "slider",
677
+ "spinbutton",
678
+ "status",
679
+ "strong",
680
+ "subscript",
681
+ "superscript",
682
+ "switch",
683
+ "tab",
684
+ "table",
685
+ "tablist",
686
+ "tabpanel",
687
+ "term",
688
+ "textbox",
689
+ "time",
690
+ "timer",
691
+ "toolbar",
692
+ "tooltip",
693
+ "tree",
694
+ "treegrid",
695
+ "treeitem"
696
+ ];
697
+ const DEFAULT_OPTIONS = { exact: true };
698
+ /**
699
+ * Creates a set of locator functions for every ARIA role, so tests don't
700
+ * have to repeat `getByRole` and its role string throughout a page object.
701
+ *
702
+ * Every generated locator applies `exact: true` by default, keeping name
703
+ * matches from accidentally matching unrelated elements with a similar
704
+ * accessible name. This can be overridden per call through `options`, or
705
+ * replaced entirely for every locator by passing a custom `defaultOptions`.
706
+ *
707
+ * The accessible name can be omitted when an element should be located by
708
+ * role alone, in which case `options` can be passed as the first argument.
709
+ *
710
+ * @param locator - The page or locator to scope the role locators to
711
+ * @param defaultOptions - Optional. Options applied to every generated locator, overridden by per-call options; defaults to `{ exact: true }`
712
+ * @returns A `RoleLocators` object with one locator function per ARIA role
713
+ *
714
+ * @example
715
+ * ```ts
716
+ * const { button, textbox } = roleLocators(page);
717
+ *
718
+ * await textbox("Email").fill("user@example.com");
719
+ * await button("Submit").click();
720
+ * ```
721
+ */
722
+ function roleLocators(locator, defaultOptions = DEFAULT_OPTIONS) {
723
+ function getByRole(role) {
724
+ return (nameOrOptions, options) => {
725
+ if (typeof nameOrOptions === "string" || nameOrOptions instanceof RegExp) return locator.getByRole(role, {
726
+ name: nameOrOptions,
727
+ ...defaultOptions,
728
+ ...options
729
+ });
730
+ return locator.getByRole(role, {
731
+ ...defaultOptions,
732
+ ...nameOrOptions
733
+ });
734
+ };
735
+ }
736
+ return Object.fromEntries(ROLES.map((role) => [role, getByRole(role)]));
737
+ }
738
+ //#endregion
595
739
  //#region src/normalizers/masked-value.ts
596
740
  /**
597
741
  * Formats a name as a masked placeholder, e.g. `maskedValue("base url")`
@@ -627,4 +771,4 @@ function identity(value) {
627
771
  return value;
628
772
  }
629
773
  //#endregion
630
- export { ConsoleCaptor, RouteInterceptor, RouteInterceptorFixture, captureConsole, createFetchAdapter, hasLogLevel, interceptRoute, isCI, maskBaseURL, maskedValue, maskedValueWithIndex, matchPath, modifyJsonBody, modifyTextBody, resolveFromPackageRoot };
774
+ export { ConsoleCaptor, RouteInterceptor, RouteInterceptorFixture, captureConsole, createFetchAdapter, hasLogLevel, interceptRoute, isCI, maskBaseURL, maskedValue, maskedValueWithIndex, matchPath, modifyJsonBody, modifyTextBody, resolveFromPackageRoot, roleLocators };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cronn/playwright-utils",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Utilities for writing tests with Playwright",
5
5
  "keywords": [
6
6
  "playwright",
@@ -33,30 +33,29 @@
33
33
  },
34
34
  "devDependencies": {
35
35
  "@arethetypeswrong/core": "0.18.5",
36
- "@changesets/cli": "3.0.1",
37
- "@cronn/element-snapshot": "0.28.0",
38
- "@cronn/playwright-file-snapshots": "2.2.1",
39
- "@playwright/test": "1.62.1",
40
- "@types/node": "24.13.3",
36
+ "@changesets/cli": "3.0.2",
37
+ "@cronn/element-snapshot": "0.30.0",
38
+ "@cronn/playwright-file-snapshots": "2.2.2",
39
+ "@playwright/test": "1.63.0",
40
+ "@types/node": "24.13.4",
41
41
  "@typescript/native": "npm:typescript@^7.0.2",
42
- "@vitest/coverage-v8": "4.1.11",
43
- "eslint": "10.9.1",
42
+ "@vitest/coverage-v8": "5.0.0",
43
+ "eslint": "10.10.0",
44
44
  "eslint-config-prettier": "10.1.8",
45
45
  "eslint-plugin-check-file": "3.3.2",
46
46
  "eslint-plugin-unused-imports": "4.4.1",
47
- "knip": "6.33.0",
48
- "oxc-minify": "0.147.0",
49
- "oxfmt": "0.65.0",
47
+ "knip": "6.35.1",
48
+ "oxc-minify": "0.149.0",
49
+ "oxfmt": "0.67.0",
50
50
  "publint": "0.3.24",
51
51
  "rimraf": "6.1.3",
52
- "tsdown": "0.22.14",
52
+ "tsdown": "0.23.0",
53
53
  "turbo": "2.10.12",
54
54
  "typescript": "npm:@typescript/typescript6@^6.0.2",
55
- "typescript-eslint": "8.68.0",
56
- "vitepress": "2.0.0-alpha.19",
55
+ "typescript-eslint": "8.70.0",
56
+ "vitepress": "2.0.0-alpha.20",
57
57
  "vitepress-plugin-group-icons": "1.7.6",
58
- "vitest": "4.1.11",
59
- "node": "runtime:24.20.0"
58
+ "vitest": "5.0.0"
60
59
  },
61
60
  "peerDependencies": {
62
61
  "@cronn/playwright-file-snapshots": "^2",
@@ -65,12 +64,12 @@
65
64
  "devEngines": {
66
65
  "packageManager": {
67
66
  "name": "pnpm",
68
- "version": "11.25.0",
67
+ "version": "12.5.1",
69
68
  "onFail": "download"
70
69
  },
71
70
  "runtime": {
72
71
  "name": "node",
73
- "version": "24.20.0",
72
+ "version": "24.21.0",
74
73
  "onFail": "download"
75
74
  }
76
75
  },