@vitest/browser-playwright 4.1.0-beta.5 → 4.1.0-beta.6

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @vitest/browser-playwright
2
2
 
3
- [![NPM version](https://img.shields.io/npm/v/@vitest/browser-playwright?color=a1b858&label=)](https://www.npmjs.com/package/@vitest/browser-playwright)
3
+ [![NPM version](https://img.shields.io/npm/v/@vitest/browser-playwright?color=a1b858&label=)](https://npmx.dev/package/@vitest/browser-playwright)
4
4
 
5
5
  Run your Vitest [browser tests](https://vitest.dev/guide/browser/) using [playwright](https://playwright.dev/docs/api/class-playwright) API. Note that Vitest does not use playwright as a test runner, but only as a browser provider.
6
6
 
package/dist/index.d.ts CHANGED
@@ -102,7 +102,8 @@ type PWScreenshotOptions = NonNullable<Parameters<Page["screenshot"]>[0]>;
102
102
  type PWSelectOptions = NonNullable<Parameters<Page["selectOption"]>[2]>;
103
103
  type PWDragAndDropOptions = NonNullable<Parameters<Page["dragAndDrop"]>[2]>;
104
104
  type PWSetInputFiles = NonNullable<Parameters<Page["setInputFiles"]>[2]>;
105
- type PWCDPSession = CDPSession;
105
+ type PWCDPSession = Pick<CDPSession, "send" | "on" | "off" | "once">;
106
+
106
107
  declare module "vitest/browser" {
107
108
  interface UserEventHoverOptions extends PWHoverOptions {}
108
109
  interface UserEventClickOptions extends PWClickOptions {}
@@ -119,4 +120,4 @@ declare module "vitest/browser" {
119
120
  }
120
121
 
121
122
  export { PlaywrightBrowserProvider, playwright };
122
- export type { PlaywrightProviderOptions };
123
+ export type { PWCDPSession as CDPSession, PlaywrightProviderOptions };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { parseKeyDef, resolveScreenshotPath, defineBrowserProvider } from '@vitest/browser';
1
+ import { asLocator, parseKeyDef, resolveScreenshotPath, defineBrowserProvider } from '@vitest/browser';
2
2
  export { defineBrowserCommand } from '@vitest/browser';
3
3
  import { createManualModuleSource } from '@vitest/mocker/node';
4
4
  import c from 'tinyrainbow';
@@ -169,23 +169,28 @@ const basename = function(p, extension) {
169
169
  return extension && lastSegment.endsWith(extension) ? lastSegment.slice(0, -extension.length) : lastSegment;
170
170
  };
171
171
 
172
+ // strip iframe locator part from the trace description e.g.
173
+ // - locator('[data-vitest="true"]').contentFrame().getByRole('button')
174
+ // ⇓
175
+ // - getByRole('button')
176
+ function getDescribedLocator(context, selector) {
177
+ const locator = context.iframe.locator(selector);
178
+ return typeof locator.describe === "function" ? locator.describe(asLocator("javascript", selector)) : locator;
179
+ }
180
+
172
181
  const clear = async (context, selector) => {
173
- const { iframe } = context;
174
- const element = iframe.locator(selector);
182
+ const element = getDescribedLocator(context, selector);
175
183
  await element.clear();
176
184
  };
177
185
 
178
186
  const click = async (context, selector, options = {}) => {
179
- const tester = context.iframe;
180
- await tester.locator(selector).click(options);
187
+ await getDescribedLocator(context, selector).click(options);
181
188
  };
182
189
  const dblClick = async (context, selector, options = {}) => {
183
- const tester = context.iframe;
184
- await tester.locator(selector).dblclick(options);
190
+ await getDescribedLocator(context, selector).dblclick(options);
185
191
  };
186
192
  const tripleClick = async (context, selector, options = {}) => {
187
- const tester = context.iframe;
188
- await tester.locator(selector).click({
193
+ await getDescribedLocator(context, selector).click({
189
194
  ...options,
190
195
  clickCount: 3
191
196
  });
@@ -197,13 +202,12 @@ const dragAndDrop = async (context, source, target, options_) => {
197
202
  };
198
203
 
199
204
  const fill = async (context, selector, text, options = {}) => {
200
- const { iframe } = context;
201
- const element = iframe.locator(selector);
205
+ const element = getDescribedLocator(context, selector);
202
206
  await element.fill(text, options);
203
207
  };
204
208
 
205
209
  const hover = async (context, selector, options = {}) => {
206
- await context.iframe.locator(selector).hover(options);
210
+ await getDescribedLocator(context, selector).hover(options);
207
211
  };
208
212
 
209
213
  const keyboard = async (context, text, state) => {
@@ -512,10 +516,10 @@ async function takeScreenshot(context, name, options) {
512
516
  savePath = normalize(path);
513
517
  await mkdir(dirname(savePath), { recursive: true });
514
518
  }
515
- const mask = options.mask?.map((selector) => context.iframe.locator(selector));
519
+ const mask = options.mask?.map((selector) => getDescribedLocator(context, selector));
516
520
  if (options.element) {
517
521
  const { element: selector, ...config } = options;
518
- const element = context.iframe.locator(selector);
522
+ const element = getDescribedLocator(context, selector);
519
523
  const buffer = await element.screenshot({
520
524
  ...config,
521
525
  mask,
@@ -526,7 +530,7 @@ async function takeScreenshot(context, name, options) {
526
530
  path
527
531
  };
528
532
  }
529
- const buffer = await context.iframe.locator("body").screenshot({
533
+ const buffer = await getDescribedLocator(context, "body").screenshot({
530
534
  ...options,
531
535
  mask,
532
536
  path: savePath
@@ -539,13 +543,12 @@ async function takeScreenshot(context, name, options) {
539
543
 
540
544
  const selectOptions = async (context, selector, userValues, options = {}) => {
541
545
  const value = userValues;
542
- const { iframe } = context;
543
- const selectElement = iframe.locator(selector);
546
+ const selectElement = getDescribedLocator(context, selector);
544
547
  const values = await Promise.all(value.map(async (v) => {
545
548
  if (typeof v === "string") {
546
549
  return v;
547
550
  }
548
- const elementHandler = await iframe.locator(v.element).elementHandle();
551
+ const elementHandler = await getDescribedLocator(context, v.element).elementHandle();
549
552
  if (!elementHandler) {
550
553
  throw new Error(`Element not found: ${v.element}`);
551
554
  }
@@ -569,7 +572,7 @@ const startTracing = async ({ context, project, provider, sessionId }) => {
569
572
  await context.tracing.start({
570
573
  screenshots: options.screenshots ?? true,
571
574
  snapshots: options.snapshots ?? true,
572
- sources: false
575
+ sources: options.sources ?? true
573
576
  }).catch(() => {
574
577
  provider.tracingContexts.delete(sessionId);
575
578
  });
@@ -605,6 +608,68 @@ const stopChunkTrace = async (context, { name }) => {
605
608
  }
606
609
  throw new TypeError(`The ${context.provider.name} provider does not support tracing.`);
607
610
  };
611
+ const markTrace = async (context, payload) => {
612
+ if (isPlaywrightProvider(context.provider)) {
613
+ // skip if tracing is not active
614
+ // this is only safe guard and this isn't expected to happen since
615
+ // runner already checks if tracing is active before sending this command
616
+ if (!context.provider.tracingContexts.has(context.sessionId)) {
617
+ return;
618
+ }
619
+ const { name, selector, stack } = payload;
620
+ const location = parseLocation(context, stack);
621
+ // mark trace via group/groupEnd with dummy calls to force snapshot.
622
+ // https://github.com/microsoft/playwright/issues/39308
623
+ await context.context.tracing.group(name, { location });
624
+ try {
625
+ if (selector) {
626
+ const locator = getDescribedLocator(context, selector);
627
+ if (typeof locator._expect === "function") {
628
+ await locator._expect("to.be.attached", {
629
+ isNot: false,
630
+ timeout: 1
631
+ });
632
+ } else {
633
+ await context.page.evaluate(() => 0);
634
+ }
635
+ } else {
636
+ await context.page.evaluate(() => 0);
637
+ }
638
+ } catch {}
639
+ await context.context.tracing.groupEnd();
640
+ return;
641
+ }
642
+ throw new TypeError(`The ${context.provider.name} provider does not support tracing.`);
643
+ };
644
+ const groupTraceStart = async (context, payload) => {
645
+ if (isPlaywrightProvider(context.provider)) {
646
+ if (!context.provider.tracingContexts.has(context.sessionId)) {
647
+ return;
648
+ }
649
+ const { name, stack } = payload;
650
+ const location = parseLocation(context, stack);
651
+ await context.context.tracing.group(name, { location });
652
+ return;
653
+ }
654
+ throw new TypeError(`The ${context.provider.name} provider does not support tracing.`);
655
+ };
656
+ const groupTraceEnd = async (context) => {
657
+ if (isPlaywrightProvider(context.provider)) {
658
+ if (!context.provider.tracingContexts.has(context.sessionId)) {
659
+ return;
660
+ }
661
+ await context.context.tracing.groupEnd();
662
+ return;
663
+ }
664
+ throw new TypeError(`The ${context.provider.name} provider does not support tracing.`);
665
+ };
666
+ function parseLocation(context, stack) {
667
+ if (!stack) {
668
+ return;
669
+ }
670
+ const parsedStacks = context.project.browser.parseStacktrace(stack);
671
+ return parsedStacks[0];
672
+ }
608
673
  function resolveTracesPath({ testPath, project }, name) {
609
674
  if (!testPath) {
610
675
  throw new Error(`This command can only be called inside a test file.`);
@@ -665,8 +730,7 @@ function isPlaywrightProvider(provider) {
665
730
  const type = async (context, selector, text, options = {}) => {
666
731
  const { skipClick = false, skipAutoClose = false } = options;
667
732
  const unreleased = new Set(Reflect.get(options, "unreleased") ?? []);
668
- const { iframe } = context;
669
- const element = iframe.locator(selector);
733
+ const element = getDescribedLocator(context, selector);
670
734
  if (!skipClick) {
671
735
  await element.focus();
672
736
  }
@@ -680,7 +744,6 @@ const upload = async (context, selector, files, options) => {
680
744
  throw new Error(`Cannot upload files outside of a test`);
681
745
  }
682
746
  const root = context.project.config.root;
683
- const { iframe } = context;
684
747
  const playwrightFiles = files.map((file) => {
685
748
  if (typeof file === "string") {
686
749
  return resolve(root, file);
@@ -691,7 +754,7 @@ const upload = async (context, selector, files, options) => {
691
754
  buffer: Buffer.from(file.base64, "base64")
692
755
  };
693
756
  });
694
- await iframe.locator(selector).setInputFiles(playwrightFiles, options);
757
+ await getDescribedLocator(context, selector).setInputFiles(playwrightFiles, options);
695
758
  };
696
759
 
697
760
  const wheel = async (context, selector, options) => {
@@ -724,7 +787,10 @@ var commands = {
724
787
  __vitest_startChunkTrace: startChunkTrace,
725
788
  __vitest_startTracing: startTracing,
726
789
  __vitest_stopChunkTrace: stopChunkTrace,
727
- __vitest_annotateTraces: annotateTraces
790
+ __vitest_annotateTraces: annotateTraces,
791
+ __vitest_markTrace: markTrace,
792
+ __vitest_groupTraceStart: groupTraceStart,
793
+ __vitest_groupTraceEnd: groupTraceEnd
728
794
  };
729
795
 
730
796
  const pkgRoot = resolve(fileURLToPath(import.meta.url), "../..");
@@ -1090,18 +1156,17 @@ class PlaywrightBrowserProvider {
1090
1156
  const page = this.getPage(sessionid);
1091
1157
  const cdp = await page.context().newCDPSession(page);
1092
1158
  return {
1093
- async send(method, params) {
1094
- const result = await cdp.send(method, params);
1095
- return result;
1159
+ send(method, params) {
1160
+ return cdp.send(method, params);
1096
1161
  },
1097
1162
  on(event, listener) {
1098
- cdp.on(event, listener);
1163
+ return cdp.on(event, listener);
1099
1164
  },
1100
1165
  off(event, listener) {
1101
- cdp.off(event, listener);
1166
+ return cdp.off(event, listener);
1102
1167
  },
1103
1168
  once(event, listener) {
1104
- cdp.once(event, listener);
1169
+ return cdp.once(event, listener);
1105
1170
  }
1106
1171
  };
1107
1172
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vitest/browser-playwright",
3
3
  "type": "module",
4
- "version": "4.1.0-beta.5",
4
+ "version": "4.1.0-beta.6",
5
5
  "description": "Browser running for Vitest using playwright",
6
6
  "license": "MIT",
7
7
  "funding": "https://opencollective.com/vitest",
@@ -42,7 +42,7 @@
42
42
  ],
43
43
  "peerDependencies": {
44
44
  "playwright": "*",
45
- "vitest": "4.1.0-beta.5"
45
+ "vitest": "4.1.0-beta.6"
46
46
  },
47
47
  "peerDependenciesMeta": {
48
48
  "playwright": {
@@ -51,12 +51,12 @@
51
51
  },
52
52
  "dependencies": {
53
53
  "tinyrainbow": "^3.0.3",
54
- "@vitest/browser": "4.1.0-beta.5",
55
- "@vitest/mocker": "4.1.0-beta.5"
54
+ "@vitest/mocker": "4.1.0-beta.6",
55
+ "@vitest/browser": "4.1.0-beta.6"
56
56
  },
57
57
  "devDependencies": {
58
58
  "playwright": "^1.58.2",
59
- "vitest": "4.1.0-beta.5"
59
+ "vitest": "4.1.0-beta.6"
60
60
  },
61
61
  "scripts": {
62
62
  "build": "premove dist && pnpm rollup -c",