@vitest/browser 4.0.0-beta.2 → 4.0.0-beta.4

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.
@@ -297,10 +297,10 @@ declare abstract class Locator {
297
297
  dropTo(target: Locator, options?: UserEventDragAndDropOptions): Promise<void>;
298
298
  selectOptions(value: HTMLElement | HTMLElement[] | Locator | Locator[] | string | string[], options?: UserEventSelectOptions): Promise<void>;
299
299
  screenshot(options: Omit<LocatorScreenshotOptions, "base64"> & {
300
- base64: true
300
+ base64: true;
301
301
  }): Promise<{
302
- path: string
303
- base64: string
302
+ path: string;
303
+ base64: string;
304
304
  }>;
305
305
  screenshot(options?: LocatorScreenshotOptions): Promise<string>;
306
306
  protected abstract locator(selector: string): Locator;
@@ -1 +1 @@
1
- import"@vitest/browser/context";import"../public-utils-Kx5DUGWa.js";export{L as Locator,s as selectorEngine}from"../index-W1MM53zC.js";import"vitest/internal/browser";
1
+ import"@vitest/browser/context";import"../public-utils-Kx5DUGWa.js";export{L as Locator,s as selectorEngine}from"../index-D_g_FMM5.js";import"vitest/internal/browser";
@@ -1 +1 @@
1
- import{page,server}from"@vitest/browser/context";import{g as getByTitleSelector,a as getByTextSelector,b as getByPlaceholderSelector,c as getByAltTextSelector,d as getByTestIdSelector,e as getByRoleSelector,f as getByLabelSelector}from"../public-utils-Kx5DUGWa.js";import{s as selectorEngine,L as Locator,p as processTimeoutOptions,g as getIframeScale}from"../index-W1MM53zC.js";import"vitest/internal/browser";page.extend({getByLabelText(e,_){return new PlaywrightLocator(getByLabelSelector(e,_))},getByRole(e,_){return new PlaywrightLocator(getByRoleSelector(e,_))},getByTestId(e){return new PlaywrightLocator(getByTestIdSelector(server.config.browser.locators.testIdAttribute,e))},getByAltText(e,_){return new PlaywrightLocator(getByAltTextSelector(e,_))},getByPlaceholder(e,_){return new PlaywrightLocator(getByPlaceholderSelector(e,_))},getByText(e,_){return new PlaywrightLocator(getByTextSelector(e,_))},getByTitle(e,_){return new PlaywrightLocator(getByTitleSelector(e,_))},_createLocator(e){return new PlaywrightLocator(e)},elementLocator(e){return new PlaywrightLocator(selectorEngine.generateSelectorSimple(e),e)}});class PlaywrightLocator extends Locator{constructor(e,_){super(),this.selector=e,this._container=_}click(e){return super.click(processTimeoutOptions(processClickOptions(e)))}dblClick(e){return super.dblClick(processTimeoutOptions(processClickOptions(e)))}tripleClick(e){return super.tripleClick(processTimeoutOptions(processClickOptions(e)))}selectOptions(e,_){return super.selectOptions(e,processTimeoutOptions(_))}clear(e){return super.clear(processTimeoutOptions(e))}hover(e){return super.hover(processTimeoutOptions(processHoverOptions(e)))}upload(e,_){return super.upload(e,processTimeoutOptions(_))}fill(e,_){return super.fill(e,processTimeoutOptions(_))}dropTo(e,_){return super.dropTo(e,processTimeoutOptions(processDragAndDropOptions(_)))}locator(e){return new PlaywrightLocator(`${this.selector} >> ${e}`,this._container)}elementLocator(e){return new PlaywrightLocator(selectorEngine.generateSelectorSimple(e),e)}}function processDragAndDropOptions(e){if(!e)return e;let _=e;return _.sourcePosition&&=processPlaywrightPosition(_.sourcePosition),_.targetPosition&&=processPlaywrightPosition(_.targetPosition),e}function processHoverOptions(e){if(!e)return e;let _=e;return _.position&&=processPlaywrightPosition(_.position),e}function processClickOptions(e){if(!e)return e;let _=e;return _.position&&=processPlaywrightPosition(_.position),_}function processPlaywrightPosition(e){let _=getIframeScale();return e.x!=null&&(e.x*=_),e.y!=null&&(e.y*=_),e}
1
+ import{page,server}from"@vitest/browser/context";import{g as getByTitleSelector,a as getByTextSelector,b as getByPlaceholderSelector,c as getByAltTextSelector,d as getByTestIdSelector,e as getByRoleSelector,f as getByLabelSelector}from"../public-utils-Kx5DUGWa.js";import{s as selectorEngine,L as Locator,p as processTimeoutOptions,g as getIframeScale}from"../index-D_g_FMM5.js";import"vitest/internal/browser";page.extend({getByLabelText(e,_){return new PlaywrightLocator(getByLabelSelector(e,_))},getByRole(e,_){return new PlaywrightLocator(getByRoleSelector(e,_))},getByTestId(e){return new PlaywrightLocator(getByTestIdSelector(server.config.browser.locators.testIdAttribute,e))},getByAltText(e,_){return new PlaywrightLocator(getByAltTextSelector(e,_))},getByPlaceholder(e,_){return new PlaywrightLocator(getByPlaceholderSelector(e,_))},getByText(e,_){return new PlaywrightLocator(getByTextSelector(e,_))},getByTitle(e,_){return new PlaywrightLocator(getByTitleSelector(e,_))},_createLocator(e){return new PlaywrightLocator(e)},elementLocator(e){return new PlaywrightLocator(selectorEngine.generateSelectorSimple(e),e)}});class PlaywrightLocator extends Locator{constructor(e,_){super(),this.selector=e,this._container=_}click(e){return super.click(processTimeoutOptions(processClickOptions(e)))}dblClick(e){return super.dblClick(processTimeoutOptions(processClickOptions(e)))}tripleClick(e){return super.tripleClick(processTimeoutOptions(processClickOptions(e)))}selectOptions(e,_){return super.selectOptions(e,processTimeoutOptions(_))}clear(e){return super.clear(processTimeoutOptions(e))}hover(e){return super.hover(processTimeoutOptions(processHoverOptions(e)))}upload(e,_){return super.upload(e,processTimeoutOptions(_))}fill(e,_){return super.fill(e,processTimeoutOptions(_))}dropTo(e,_){return super.dropTo(e,processTimeoutOptions(processDragAndDropOptions(_)))}locator(e){return new PlaywrightLocator(`${this.selector} >> ${e}`,this._container)}elementLocator(e){return new PlaywrightLocator(selectorEngine.generateSelectorSimple(e),e)}}function processDragAndDropOptions(e){if(!e)return e;let _=e;return _.sourcePosition&&=processPlaywrightPosition(_.sourcePosition),_.targetPosition&&=processPlaywrightPosition(_.targetPosition),e}function processHoverOptions(e){if(!e)return e;let _=e;return _.position&&=processPlaywrightPosition(_.position),e}function processClickOptions(e){if(!e)return e;let _=e;return _.position&&=processPlaywrightPosition(_.position),_}function processPlaywrightPosition(e){let _=getIframeScale();return e.x!=null&&(e.x*=_),e.y!=null&&(e.y*=_),e}
@@ -1 +1 @@
1
- import{page,server,userEvent}from"@vitest/browser/context";import{g as getByTitleSelector,a as getByTextSelector,b as getByPlaceholderSelector,c as getByAltTextSelector,d as getByTestIdSelector,e as getByRoleSelector,f as getByLabelSelector,h as getElementError}from"../public-utils-Kx5DUGWa.js";import{s as selectorEngine,L as Locator,c as convertElementToCssSelector}from"../index-W1MM53zC.js";import"vitest/internal/browser";page.extend({getByLabelText(e,m){return new PreviewLocator(getByLabelSelector(e,m))},getByRole(e,m){return new PreviewLocator(getByRoleSelector(e,m))},getByTestId(e){return new PreviewLocator(getByTestIdSelector(server.config.browser.locators.testIdAttribute,e))},getByAltText(e,m){return new PreviewLocator(getByAltTextSelector(e,m))},getByPlaceholder(e,m){return new PreviewLocator(getByPlaceholderSelector(e,m))},getByText(e,m){return new PreviewLocator(getByTextSelector(e,m))},getByTitle(e,m){return new PreviewLocator(getByTitleSelector(e,m))},_createLocator(e){return new PreviewLocator(e)},elementLocator(e){return new PreviewLocator(selectorEngine.generateSelectorSimple(e),e)}});class PreviewLocator extends Locator{constructor(e,m){super(),this._pwSelector=e,this._container=m}get selector(){let e=this.elements().map(e=>convertElementToCssSelector(e));if(!e.length)throw getElementError(this._pwSelector,this._container||document.body);return e.join(`, `)}click(){return userEvent.click(this.element())}dblClick(){return userEvent.dblClick(this.element())}tripleClick(){return userEvent.tripleClick(this.element())}hover(){return userEvent.hover(this.element())}unhover(){return userEvent.unhover(this.element())}async fill(e){return userEvent.fill(this.element(),e)}async upload(e){return userEvent.upload(this.element(),e)}selectOptions(e){return userEvent.selectOptions(this.element(),e)}clear(){return userEvent.clear(this.element())}locator(e){return new PreviewLocator(`${this._pwSelector} >> ${e}`,this._container)}elementLocator(e){return new PreviewLocator(selectorEngine.generateSelectorSimple(e),e)}}
1
+ import{page,server,userEvent}from"@vitest/browser/context";import{g as getByTitleSelector,a as getByTextSelector,b as getByPlaceholderSelector,c as getByAltTextSelector,d as getByTestIdSelector,e as getByRoleSelector,f as getByLabelSelector,h as getElementError}from"../public-utils-Kx5DUGWa.js";import{s as selectorEngine,L as Locator,c as convertElementToCssSelector}from"../index-D_g_FMM5.js";import"vitest/internal/browser";page.extend({getByLabelText(e,m){return new PreviewLocator(getByLabelSelector(e,m))},getByRole(e,m){return new PreviewLocator(getByRoleSelector(e,m))},getByTestId(e){return new PreviewLocator(getByTestIdSelector(server.config.browser.locators.testIdAttribute,e))},getByAltText(e,m){return new PreviewLocator(getByAltTextSelector(e,m))},getByPlaceholder(e,m){return new PreviewLocator(getByPlaceholderSelector(e,m))},getByText(e,m){return new PreviewLocator(getByTextSelector(e,m))},getByTitle(e,m){return new PreviewLocator(getByTitleSelector(e,m))},_createLocator(e){return new PreviewLocator(e)},elementLocator(e){return new PreviewLocator(selectorEngine.generateSelectorSimple(e),e)}});class PreviewLocator extends Locator{constructor(e,m){super(),this._pwSelector=e,this._container=m}get selector(){let e=this.elements().map(e=>convertElementToCssSelector(e));if(!e.length)throw getElementError(this._pwSelector,this._container||document.body);return e.join(`, `)}click(){return userEvent.click(this.element())}dblClick(){return userEvent.dblClick(this.element())}tripleClick(){return userEvent.tripleClick(this.element())}hover(){return userEvent.hover(this.element())}unhover(){return userEvent.unhover(this.element())}async fill(e){return userEvent.fill(this.element(),e)}async upload(e){return userEvent.upload(this.element(),e)}selectOptions(e){return userEvent.selectOptions(this.element(),e)}clear(){return userEvent.clear(this.element())}locator(e){return new PreviewLocator(`${this._pwSelector} >> ${e}`,this._container)}elementLocator(e){return new PreviewLocator(selectorEngine.generateSelectorSimple(e),e)}}
@@ -1 +1 @@
1
- import{page,server}from"@vitest/browser/context";import{g as getByTitleSelector,a as getByTextSelector,b as getByPlaceholderSelector,c as getByAltTextSelector,d as getByTestIdSelector,e as getByRoleSelector,f as getByLabelSelector,h as getElementError}from"../public-utils-Kx5DUGWa.js";import{s as selectorEngine,L as Locator,c as convertElementToCssSelector,a as getBrowserState,g as getIframeScale}from"../index-W1MM53zC.js";import"vitest/internal/browser";page.extend({getByLabelText(e,g){return new WebdriverIOLocator(getByLabelSelector(e,g))},getByRole(e,g){return new WebdriverIOLocator(getByRoleSelector(e,g))},getByTestId(e){return new WebdriverIOLocator(getByTestIdSelector(server.config.browser.locators.testIdAttribute,e))},getByAltText(e,g){return new WebdriverIOLocator(getByAltTextSelector(e,g))},getByPlaceholder(e,g){return new WebdriverIOLocator(getByPlaceholderSelector(e,g))},getByText(e,g){return new WebdriverIOLocator(getByTextSelector(e,g))},getByTitle(e,g){return new WebdriverIOLocator(getByTitleSelector(e,g))},_createLocator(e){return new WebdriverIOLocator(e)},elementLocator(e){return new WebdriverIOLocator(selectorEngine.generateSelectorSimple(e))}});class WebdriverIOLocator extends Locator{constructor(e,g){super(),this._pwSelector=e,this._container=g}get selector(){let e=this.elements().map(e=>convertElementToCssSelector(e));if(!e.length)throw getElementError(this._pwSelector,this._container||document.body);return e.join(`, `)}click(e){return super.click(processClickOptions(e))}dblClick(e){return super.dblClick(processClickOptions(e))}tripleClick(e){return super.tripleClick(processClickOptions(e))}selectOptions(e,g){let _=getWebdriverioSelectOptions(this.element(),e);return this.triggerCommand(`__vitest_selectOptions`,this.selector,_,g)}hover(e){return super.hover(processHoverOptions(e))}dropTo(e,g){return super.dropTo(e,processDragAndDropOptions(g))}locator(e){return new WebdriverIOLocator(`${this._pwSelector} >> ${e}`,this._container)}elementLocator(e){return new WebdriverIOLocator(selectorEngine.generateSelectorSimple(e),e)}}function getWebdriverioSelectOptions(e,g){let _=[...e.querySelectorAll(`option`)],v=Array.isArray(g)?g:[g];if(!v.length)return[];if(v.length>1)throw Error(`Provider "webdriverio" doesn't support selecting multiple values at once`);let y=v[0];if(typeof y!=`string`){let e=`element`in y?y.element():y,g=_.indexOf(e);if(g===-1)throw Error(`The element ${selectorEngine.previewNode(e)} was not found in the "select" options.`);return[{index:g}]}let b=_.findIndex(e=>e.value===y);if(b!==-1)return[{index:b}];let x=_.findIndex(e=>e.textContent?.trim()===y||e.ariaLabel===y);if(x===-1)throw Error(`The option "${y}" was not found in the "select" options.`);return[{index:x}]}function processClickOptions(e){if(!e||!getBrowserState().config.browser.ui)return e;let g=e;if(g.x!=null||g.y!=null){let e={};g.x!=null&&(g.x=scaleCoordinate(g.x,e)),g.y!=null&&(g.y=scaleCoordinate(g.y,e))}return e}function processHoverOptions(e){if(!e||!getBrowserState().config.browser.ui)return e;let g=e,_={};return g.xOffset!=null&&(g.xOffset=scaleCoordinate(g.xOffset,_)),g.yOffset!=null&&(g.yOffset=scaleCoordinate(g.yOffset,_)),e}function processDragAndDropOptions(e){if(!e||!getBrowserState().config.browser.ui)return e;let g={},_=e;return _.sourceX!=null&&(_.sourceX=scaleCoordinate(_.sourceX,g)),_.sourceY!=null&&(_.sourceY=scaleCoordinate(_.sourceY,g)),_.targetX!=null&&(_.targetX=scaleCoordinate(_.targetX,g)),_.targetY!=null&&(_.targetY=scaleCoordinate(_.targetY,g)),e}function scaleCoordinate(e,g){return Math.round(e*getCachedScale(g))}function getCachedScale(e){return e.scale??=getIframeScale()}
1
+ import{page,server}from"@vitest/browser/context";import{g as getByTitleSelector,a as getByTextSelector,b as getByPlaceholderSelector,c as getByAltTextSelector,d as getByTestIdSelector,e as getByRoleSelector,f as getByLabelSelector,h as getElementError}from"../public-utils-Kx5DUGWa.js";import{s as selectorEngine,L as Locator,c as convertElementToCssSelector,a as getBrowserState,g as getIframeScale}from"../index-D_g_FMM5.js";import"vitest/internal/browser";page.extend({getByLabelText(e,g){return new WebdriverIOLocator(getByLabelSelector(e,g))},getByRole(e,g){return new WebdriverIOLocator(getByRoleSelector(e,g))},getByTestId(e){return new WebdriverIOLocator(getByTestIdSelector(server.config.browser.locators.testIdAttribute,e))},getByAltText(e,g){return new WebdriverIOLocator(getByAltTextSelector(e,g))},getByPlaceholder(e,g){return new WebdriverIOLocator(getByPlaceholderSelector(e,g))},getByText(e,g){return new WebdriverIOLocator(getByTextSelector(e,g))},getByTitle(e,g){return new WebdriverIOLocator(getByTitleSelector(e,g))},_createLocator(e){return new WebdriverIOLocator(e)},elementLocator(e){return new WebdriverIOLocator(selectorEngine.generateSelectorSimple(e))}});class WebdriverIOLocator extends Locator{constructor(e,g){super(),this._pwSelector=e,this._container=g}get selector(){let e=this.elements().map(e=>convertElementToCssSelector(e));if(!e.length)throw getElementError(this._pwSelector,this._container||document.body);return e.join(`, `)}click(e){return super.click(processClickOptions(e))}dblClick(e){return super.dblClick(processClickOptions(e))}tripleClick(e){return super.tripleClick(processClickOptions(e))}selectOptions(e,g){let _=getWebdriverioSelectOptions(this.element(),e);return this.triggerCommand(`__vitest_selectOptions`,this.selector,_,g)}hover(e){return super.hover(processHoverOptions(e))}dropTo(e,g){return super.dropTo(e,processDragAndDropOptions(g))}locator(e){return new WebdriverIOLocator(`${this._pwSelector} >> ${e}`,this._container)}elementLocator(e){return new WebdriverIOLocator(selectorEngine.generateSelectorSimple(e),e)}}function getWebdriverioSelectOptions(e,g){let _=[...e.querySelectorAll(`option`)],v=Array.isArray(g)?g:[g];if(!v.length)return[];if(v.length>1)throw Error(`Provider "webdriverio" doesn't support selecting multiple values at once`);let y=v[0];if(typeof y!=`string`){let e=`element`in y?y.element():y,g=_.indexOf(e);if(g===-1)throw Error(`The element ${selectorEngine.previewNode(e)} was not found in the "select" options.`);return[{index:g}]}let b=_.findIndex(e=>e.value===y);if(b!==-1)return[{index:b}];let x=_.findIndex(e=>e.textContent?.trim()===y||e.ariaLabel===y);if(x===-1)throw Error(`The option "${y}" was not found in the "select" options.`);return[{index:x}]}function processClickOptions(e){if(!e||!getBrowserState().config.browser.ui)return e;let g=e;if(g.x!=null||g.y!=null){let e={};g.x!=null&&(g.x=scaleCoordinate(g.x,e)),g.y!=null&&(g.y=scaleCoordinate(g.y,e))}return e}function processHoverOptions(e){if(!e||!getBrowserState().config.browser.ui)return e;let g=e,_={};return g.xOffset!=null&&(g.xOffset=scaleCoordinate(g.xOffset,_)),g.yOffset!=null&&(g.yOffset=scaleCoordinate(g.yOffset,_)),e}function processDragAndDropOptions(e){if(!e||!getBrowserState().config.browser.ui)return e;let g={},_=e;return _.sourceX!=null&&(_.sourceX=scaleCoordinate(_.sourceX,g)),_.sourceY!=null&&(_.sourceY=scaleCoordinate(_.sourceY,g)),_.targetX!=null&&(_.targetX=scaleCoordinate(_.targetX,g)),_.targetY!=null&&(_.targetY=scaleCoordinate(_.targetY,g)),e}function scaleCoordinate(e,g){return Math.round(e*getCachedScale(g))}function getCachedScale(e){return e.scale??=getIframeScale()}
package/dist/providers.js CHANGED
@@ -1,4 +1,4 @@
1
- import { W as WebdriverBrowserProvider, P as PlaywrightBrowserProvider } from './webdriver-KA1WiV0q.js';
1
+ import { W as WebdriverBrowserProvider, P as PlaywrightBrowserProvider } from './webdriver-AHRa6U3j.js';
2
2
  import '@vitest/mocker/node';
3
3
  import 'tinyrainbow';
4
4
  import 'vitest/node';
@@ -0,0 +1,13 @@
1
+ import type { ScreenshotComparatorRegistry, ScreenshotMatcherOptions } from "../../../context.js";
2
+ export type ScreenshotMatcherArguments<ComparatorName extends keyof ScreenshotComparatorRegistry = keyof ScreenshotComparatorRegistry> = [name: string, testName: string, options: ScreenshotMatcherOptions<ComparatorName> & {
3
+ element: string;
4
+ }];
5
+ export type ScreenshotMatcherOutput = Promise<{
6
+ pass: false;
7
+ reference: string | null;
8
+ actual: string | null;
9
+ diff: string | null;
10
+ message: string;
11
+ } | {
12
+ pass: true;
13
+ }>;
package/dist/types.d.ts CHANGED
@@ -22,11 +22,11 @@ export interface WebSocketBrowserHandlers {
22
22
  resolveId: (id: string, importer?: string) => Promise<ServerIdResolution | null>;
23
23
  triggerCommand: <T>(sessionId: string, command: string, testPath: string | undefined, payload: unknown[]) => Promise<T>;
24
24
  resolveMock: (id: string, importer: string, options: {
25
- mock: "spy" | "factory" | "auto"
25
+ mock: "spy" | "factory" | "auto";
26
26
  }) => Promise<ServerMockResolution>;
27
27
  invalidate: (ids: string[]) => void;
28
28
  getBrowserFileSourceMap: (id: string) => SourceMap | null | {
29
- mappings: ""
29
+ mappings: "";
30
30
  } | undefined;
31
31
  wdioSwitchContext: (direction: "iframe" | "parent") => void;
32
32
  registerMock: (sessionId: string, mock: MockedModuleSerialized) => void;
@@ -52,9 +52,9 @@ export interface WebSocketBrowserEvents {
52
52
  cleanupTesters: () => Promise<void>;
53
53
  cdpEvent: (event: string, payload: unknown) => void;
54
54
  resolveManualMock: (url: string) => Promise<{
55
- url: string
56
- keys: string[]
57
- responseId: string
55
+ url: string;
56
+ keys: string[];
57
+ responseId: string;
58
58
  }>;
59
59
  }
60
60
  export type WebSocketBrowserRPC = BirpcReturn<WebSocketBrowserEvents, WebSocketBrowserHandlers>;
@@ -376,9 +376,14 @@ class WebdriverBrowserProvider {
376
376
  getSupportedBrowsers() {
377
377
  return webdriverBrowsers;
378
378
  }
379
- async initialize(ctx, { browser, options }) {
379
+ async initialize(project, { browser, options }) {
380
+ // increase shutdown timeout because WDIO takes some extra time to kill the driver
381
+ if (!project.vitest.state._data.timeoutIncreased) {
382
+ project.vitest.state._data.timeoutIncreased = true;
383
+ project.vitest.config.teardownTimeout += 1e4;
384
+ }
380
385
  this.closing = false;
381
- this.project = ctx;
386
+ this.project = project;
382
387
  this.browserName = browser;
383
388
  this.options = options;
384
389
  }
@@ -386,13 +391,13 @@ class WebdriverBrowserProvider {
386
391
  return this.iframeSwitched;
387
392
  }
388
393
  async switchToTestFrame() {
389
- const page = this.browser;
394
+ const browser = this.browser;
390
395
  // support wdio@9
391
- if (page.switchFrame) {
392
- await page.switchFrame(page.$("iframe[data-vitest]"));
396
+ if (browser.switchFrame) {
397
+ await browser.switchFrame(browser.$("iframe[data-vitest]"));
393
398
  } else {
394
- const iframe = await page.findElement("css selector", "iframe[data-vitest]");
395
- await page.switchToFrame(iframe);
399
+ const iframe = await browser.findElement("css selector", "iframe[data-vitest]");
400
+ await browser.switchToFrame(iframe);
396
401
  }
397
402
  this.iframeSwitched = true;
398
403
  }
@@ -497,10 +502,15 @@ class WebdriverBrowserProvider {
497
502
  async close() {
498
503
  debug?.("[%s] closing provider", this.browserName);
499
504
  this.closing = true;
500
- await Promise.all([this.browser?.sessionId ? this.browser?.deleteSession?.() : null]);
501
- // TODO: right now process can only exit with timeout, if we use browser
502
- // needs investigating
503
- process.exit();
505
+ const browser = this.browser;
506
+ const sessionId = browser?.sessionId;
507
+ if (!browser || !sessionId) {
508
+ return;
509
+ }
510
+ // https://github.com/webdriverio/webdriverio/blob/ab1a2e82b13a9c7d0e275ae87e7357e1b047d8d3/packages/wdio-runner/src/index.ts#L486
511
+ await browser.deleteSession();
512
+ browser.sessionId = undefined;
513
+ this.browser = null;
504
514
  }
505
515
  }
506
516
 
package/jest-dom.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  // Disable automatic exports.
2
2
 
3
3
  import { ARIARole } from './aria-role.ts'
4
+ import { ScreenshotComparatorRegistry, ScreenshotMatcherOptions } from './context.js'
4
5
 
5
6
  export interface TestingLibraryMatchers<E, R> {
6
7
  /**
@@ -14,6 +15,52 @@ export interface TestingLibraryMatchers<E, R> {
14
15
  * @see https://vitest.dev/guide/browser/assertion-api#tobeinthedocument
15
16
  */
16
17
  toBeInTheDocument(): R
18
+ /**
19
+ * @description
20
+ * Assert whether an element is within the viewport or not.
21
+ *
22
+ * An element is considered to be in the viewport if any part of it intersects with the current viewport bounds.
23
+ * This matcher calculates the intersection ratio between the element and the viewport, similar to the
24
+ * IntersectionObserver API.
25
+ *
26
+ * The element must be in the document and have visible dimensions. Elements with display: none or
27
+ * visibility: hidden are considered not in viewport.
28
+ * @example
29
+ * <div
30
+ * data-testid="visible-element"
31
+ * style="position: absolute; top: 10px; left: 10px; width: 50px; height: 50px;"
32
+ * >
33
+ * Visible Element
34
+ * </div>
35
+ *
36
+ * <div
37
+ * data-testid="hidden-element"
38
+ * style="position: fixed; top: -100px; left: 10px; width: 50px; height: 50px;"
39
+ * >
40
+ * Hidden Element
41
+ * </div>
42
+ *
43
+ * <div
44
+ * data-testid="large-element"
45
+ * style="height: 400vh;"
46
+ * >
47
+ * Large Element
48
+ * </div>
49
+ *
50
+ * // Check if any part of element is in viewport
51
+ * await expect.element(page.getByTestId('visible-element')).toBeInViewport()
52
+ *
53
+ * // Check if element is outside viewport
54
+ * await expect.element(page.getByTestId('hidden-element')).not.toBeInViewport()
55
+ *
56
+ * // Check if at least 50% of element is visible
57
+ * await expect.element(page.getByTestId('large-element')).toBeInViewport({ ratio: 0.5 })
58
+ *
59
+ * // Check if element is completely visible
60
+ * await expect.element(page.getByTestId('visible-element')).toBeInViewport({ ratio: 1 })
61
+ * @see https://vitest.dev/guide/browser/assertion-api#tobeinviewport
62
+ */
63
+ toBeInViewport(options?: { ratio?: number }): R
17
64
  /**
18
65
  * @description
19
66
  * This allows you to check if an element is currently visible to the user.
@@ -627,4 +674,51 @@ export interface TestingLibraryMatchers<E, R> {
627
674
  * @see https://vitest.dev/guide/browser/assertion-api#tohaveselection
628
675
  */
629
676
  toHaveSelection(selection?: string): R
677
+
678
+ /**
679
+ * @description
680
+ * This assertion allows you to perform visual regression testing by comparing
681
+ * screenshots of elements or pages against stored reference images.
682
+ *
683
+ * When differences are detected beyond the configured threshold, the test fails.
684
+ * To help identify the changes, the assertion generates:
685
+ *
686
+ * - The actual screenshot captured during the test
687
+ * - The expected reference screenshot
688
+ * - A diff image highlighting the differences (when possible)
689
+ *
690
+ * @example
691
+ * <button data-testid="button">Fancy Button</button>
692
+ *
693
+ * // basic usage, auto-generates screenshot name
694
+ * await expect.element(getByTestId('button')).toMatchScreenshot()
695
+ *
696
+ * // with custom name
697
+ * await expect.element(getByTestId('button')).toMatchScreenshot('fancy-button')
698
+ *
699
+ * // with options
700
+ * await expect.element(getByTestId('button')).toMatchScreenshot({
701
+ * comparatorName: 'pixelmatch',
702
+ * comparatorOptions: {
703
+ * allowedMismatchedPixelRatio: 0.01,
704
+ * },
705
+ * })
706
+ *
707
+ * // with both name and options
708
+ * await expect.element(getByTestId('button')).toMatchScreenshot('fancy-button', {
709
+ * comparatorName: 'pixelmatch',
710
+ * comparatorOptions: {
711
+ * allowedMismatchedPixelRatio: 0.01,
712
+ * },
713
+ * })
714
+ *
715
+ * @see https://vitest.dev/guide/browser/assertion-api#tomatchscreenshot
716
+ */
717
+ toMatchScreenshot<ComparatorName extends keyof ScreenshotComparatorRegistry>(
718
+ options?: ScreenshotMatcherOptions<ComparatorName>,
719
+ ): Promise<R>
720
+ toMatchScreenshot<ComparatorName extends keyof ScreenshotComparatorRegistry>(
721
+ name?: string,
722
+ options?: ScreenshotMatcherOptions<ComparatorName>,
723
+ ): Promise<R>
630
724
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vitest/browser",
3
3
  "type": "module",
4
- "version": "4.0.0-beta.2",
4
+ "version": "4.0.0-beta.4",
5
5
  "description": "Browser running for Vitest",
6
6
  "license": "MIT",
7
7
  "funding": "https://opencollective.com/vitest",
@@ -66,7 +66,7 @@
66
66
  "peerDependencies": {
67
67
  "playwright": "*",
68
68
  "webdriverio": "^7.0.0 || ^8.0.0 || ^9.0.0",
69
- "vitest": "4.0.0-beta.2"
69
+ "vitest": "4.0.0-beta.4"
70
70
  },
71
71
  "peerDependenciesMeta": {
72
72
  "playwright": {
@@ -83,30 +83,33 @@
83
83
  "@testing-library/dom": "^10.4.0",
84
84
  "@testing-library/user-event": "^14.6.1",
85
85
  "magic-string": "^0.30.17",
86
+ "pixelmatch": "7.1.0",
87
+ "pngjs": "^7.0.0",
86
88
  "sirv": "^3.0.1",
87
89
  "tinyrainbow": "^2.0.0",
88
- "ws": "^8.18.2",
89
- "@vitest/mocker": "4.0.0-beta.2",
90
- "@vitest/utils": "4.0.0-beta.2"
90
+ "ws": "^8.18.3",
91
+ "@vitest/mocker": "4.0.0-beta.4",
92
+ "@vitest/utils": "4.0.0-beta.4"
91
93
  },
92
94
  "devDependencies": {
95
+ "@types/pngjs": "^6.0.5",
93
96
  "@types/ws": "^8.18.1",
94
- "@wdio/protocols": "^9.15.0",
95
- "@wdio/types": "^9.15.0",
96
- "birpc": "2.4.0",
97
+ "@wdio/protocols": "^9.16.2",
98
+ "@wdio/types": "^9.16.2",
99
+ "birpc": "^2.5.0",
97
100
  "flatted": "^3.3.3",
98
101
  "ivya": "^1.7.0",
99
102
  "mime": "^4.0.7",
100
103
  "pathe": "^2.0.3",
101
104
  "periscopic": "^4.0.2",
102
- "playwright": "^1.53.1",
103
- "playwright-core": "^1.53.1",
105
+ "playwright": "^1.54.1",
106
+ "playwright-core": "^1.54.1",
104
107
  "safaridriver": "^1.0.0",
105
- "webdriverio": "^9.15.0",
106
- "@vitest/runner": "4.0.0-beta.2",
107
- "@vitest/ui": "4.0.0-beta.2",
108
- "@vitest/ws-client": "4.0.0-beta.2",
109
- "vitest": "4.0.0-beta.2"
108
+ "webdriverio": "^9.18.1",
109
+ "@vitest/runner": "4.0.0-beta.4",
110
+ "@vitest/ui": "4.0.0-beta.4",
111
+ "@vitest/ws-client": "4.0.0-beta.4",
112
+ "vitest": "4.0.0-beta.4"
110
113
  },
111
114
  "scripts": {
112
115
  "typecheck": "tsc -p ./src/client/tsconfig.json --noEmit",
@@ -11,6 +11,10 @@ import type {
11
11
  import { Protocol } from 'playwright-core/types/protocol'
12
12
  import '../matchers.js'
13
13
  import type {} from "vitest/node"
14
+ import type {
15
+ ScreenshotComparatorRegistry,
16
+ ScreenshotMatcherOptions,
17
+ } from "@vitest/browser/context"
14
18
 
15
19
  declare module 'vitest/node' {
16
20
  export interface BrowserProviderOptions {
@@ -37,6 +41,15 @@ declare module 'vitest/node' {
37
41
  iframe: FrameLocator
38
42
  context: BrowserContext
39
43
  }
44
+
45
+ export interface ToMatchScreenshotOptions
46
+ extends Omit<
47
+ ScreenshotMatcherOptions,
48
+ "comparatorName" | "comparatorOptions"
49
+ > {}
50
+
51
+ export interface ToMatchScreenshotComparators
52
+ extends ScreenshotComparatorRegistry {}
40
53
  }
41
54
 
42
55
  type PWHoverOptions = NonNullable<Parameters<Page['hover']>[1]>
@@ -1,6 +1,10 @@
1
1
  import type { remote, ClickOptions, DragAndDropOptions } from 'webdriverio'
2
2
  import '../matchers.js'
3
3
  import type {} from "vitest/node"
4
+ import type {
5
+ ScreenshotComparatorRegistry,
6
+ ScreenshotMatcherOptions,
7
+ } from "@vitest/browser/context";
4
8
 
5
9
  declare module 'vitest/node' {
6
10
  export interface BrowserProviderOptions extends Partial<
@@ -19,4 +23,13 @@ declare module 'vitest/node' {
19
23
  export interface BrowserCommandContext {
20
24
  browser: WebdriverIO.Browser
21
25
  }
26
+
27
+ export interface ToMatchScreenshotOptions
28
+ extends Omit<
29
+ ScreenshotMatcherOptions,
30
+ "comparatorName" | "comparatorOptions"
31
+ > {}
32
+
33
+ export interface ToMatchScreenshotComparators
34
+ extends ScreenshotComparatorRegistry {}
22
35
  }