diff --git a/docs/guide/browser/config.md b/docs/guide/browser/config.md index aa3095c37..18af8295f 100644 --- a/docs/guide/browser/config.md +++ b/docs/guide/browser/config.md @@ -4,12 +4,13 @@ You can change the browser configuration by updating the `test.browser` field in ```ts [vitest.config.ts] import { defineConfig } from 'vitest/config' +import { playwright } from '@vitest/browser/providers/playwright' export default defineConfig({ test: { browser: { enabled: true, - provider: 'playwright', + provider: playwright(), instances: [ { browser: 'chromium', @@ -53,15 +54,6 @@ Defines multiple browser setups. Every config has to have at least a `browser` f - [Configuring Playwright](/guide/browser/playwright) - [Configuring WebdriverIO](/guide/browser/webdriverio) -::: tip -To have a better type safety when using built-in providers, you should reference one of these types (for provider that you are using) in your [config file](/config/): - -```ts -/// -/// -``` -::: - In addition to that, you can also specify most of the [project options](/config/) (not marked with a icon) and some of the `browser` options like `browser.testerHtmlPath`. ::: warning @@ -100,26 +92,12 @@ List of available `browser` options: - [`browser.testerHtmlPath`](#browser-testerhtmlpath) - [`browser.screenshotDirectory`](#browser-screenshotdirectory) - [`browser.screenshotFailures`](#browser-screenshotfailures) +- [`browser.provider`](#browser-provider) By default, Vitest creates an array with a single element which uses the [`browser.name`](#browser-name) field as a `browser`. Note that this behaviour will be removed with Vitest 4. Under the hood, Vitest transforms these instances into separate [test projects](/advanced/api/test-project) sharing a single Vite server for better caching performance. -## browser.name deprecated {#browser-name} - -- **Type:** `string` -- **CLI:** `--browser=safari` - -::: danger DEPRECATED -This API is deprecated an will be removed in Vitest 4. Please, use [`browser.instances`](#browser-instances) option instead. -::: - -Run all tests in a specific browser. Possible options in different providers: - -- `webdriverio`: `firefox`, `chrome`, `edge`, `safari` -- `playwright`: `firefox`, `webkit`, `chromium` -- custom: any string that will be passed to the provider - ## browser.headless - **Type:** `boolean` @@ -150,70 +128,86 @@ A path to the HTML entry point. Can be relative to the root of the project. This Configure options for Vite server that serves code in the browser. Does not affect [`test.api`](#api) option. By default, Vitest assigns port `63315` to avoid conflicts with the development server, allowing you to run both in parallel. -## browser.provider {#browser-provider} +## browser.provider advanced {#browser-provider} -- **Type:** `'webdriverio' | 'playwright' | 'preview' | string` +- **Type:** `BrowserProviderOption` - **Default:** `'preview'` - **CLI:** `--browser.provider=playwright` -Path to a provider that will be used when running browser tests. Vitest provides three providers which are `preview` (default), `webdriverio` and `playwright`. Custom providers should be exported using `default` export and have this shape: +The return value of the provider factory. You can import the factory from `@vitest/browser/providers/` or make your own provider: -```ts -export interface BrowserProvider { - name: string - supportsParallelism: boolean - getSupportedBrowsers: () => readonly string[] - beforeCommand?: (command: string, args: unknown[]) => Awaitable - afterCommand?: (command: string, args: unknown[]) => Awaitable - getCommandsContext: (sessionId: string) => Record - openPage: (sessionId: string, url: string, beforeNavigate?: () => Promise) => Promise - getCDPSession?: (sessionId: string) => Promise - close: () => Awaitable - initialize( - ctx: TestProject, - options: BrowserProviderInitializationOptions - ): Awaitable -} -``` +```ts{8-10} +import { playwright } from '@vitest/browser/providers/playwright' +import { webdriverio } from '@vitest/browser/providers/webdriverio' +import { preview } from '@vitest/browser/providers/preview' -::: danger ADVANCED API -The custom provider API is highly experimental and can change between patches. If you just need to run tests in a browser, use the [`browser.instances`](#browser-instances) option instead. -::: - -## browser.providerOptions deprecated {#browser-provideroptions} - -- **Type:** `BrowserProviderOptions` - -::: danger DEPRECATED -This API is deprecated an will be removed in Vitest 4. Please, use [`browser.instances`](#browser-instances) option instead. -::: +export default defineConfig({ + test: { + browser: { + provider: playwright(), + provider: webdriverio(), + provider: preview(), // default + }, + }, +}) +``` -Options that will be passed down to provider when calling `provider.initialize`. +To configure how provider initializes the browser, you can pass down options to the factory function: -```ts -import { defineConfig } from 'vitest/config' +```ts{7-15,22-27} +import { playwright } from '@vitest/browser/providers/playwright' export default defineConfig({ test: { browser: { - providerOptions: { - launch: { - devtools: true, + // shared provider options between all instances + provider: playwright({ + launchOptions: { + slowMo: 50, + channel: 'chrome-beta', }, - }, + actionTimeout: 5_000, + }), + instances: [ + { browser: 'chromium' }, + { + browser: 'firefox', + // overriding options only for a single instance + // this will NOT merge options with the parent one + provider: playwright({ + launchOptions: { + firefoxUserPrefs: { + 'browser.startup.homepage': 'https://example.com', + }, + }, + }) + } + ], }, }, }) ``` -::: tip -To have a better type safety when using built-in providers, you should reference one of these types (for provider that you are using) in your [config file](/config/): +### Custom Provider + +::: danger ADVANCED API +The custom provider API is highly experimental and can change between patches. If you just need to run tests in a browser, use the [`browser.instances`](#browser-instances) option instead. +::: ```ts -/// -/// +export interface BrowserProvider { + name: string + mocker?: BrowserModuleMocker + /** + * @experimental opt-in into file parallelisation + */ + supportsParallelism: boolean + getCommandsContext: (sessionId: string) => Record + openPage: (sessionId: string, url: string) => Promise + getCDPSession?: (sessionId: string) => Promise + close: () => Awaitable +} ``` -::: ## browser.ui diff --git a/docs/guide/browser/index.md b/docs/guide/browser/index.md index dd2f57bb7..5ae2281a4 100644 --- a/docs/guide/browser/index.md +++ b/docs/guide/browser/index.md @@ -99,10 +99,12 @@ To activate browser mode in your Vitest configuration, set the `browser.enabled` ```ts [vitest.config.ts] import { defineConfig } from 'vitest/config' +import { playwright } from '@vitest/browser/providers/playwright' + export default defineConfig({ test: { browser: { - provider: 'playwright', // or 'webdriverio' + provider: playwright(), enabled: true, // at least one instance is required instances: [ @@ -125,13 +127,14 @@ If you have not used Vite before, make sure you have your framework's plugin ins ```ts [react] import { defineConfig } from 'vitest/config' import react from '@vitejs/plugin-react' +import { playwright } from '@vitest/browser/providers/playwright' export default defineConfig({ plugins: [react()], test: { browser: { enabled: true, - provider: 'playwright', + provider: playwright(), instances: [ { browser: 'chromium' }, ], @@ -141,6 +144,7 @@ export default defineConfig({ ``` ```ts [vue] import { defineConfig } from 'vitest/config' +import { playwright } from '@vitest/browser/providers/playwright' import vue from '@vitejs/plugin-vue' export default defineConfig({ @@ -148,7 +152,7 @@ export default defineConfig({ test: { browser: { enabled: true, - provider: 'playwright', + provider: playwright(), instances: [ { browser: 'chromium' }, ], @@ -159,13 +163,14 @@ export default defineConfig({ ```ts [svelte] import { defineConfig } from 'vitest/config' import { svelte } from '@sveltejs/vite-plugin-svelte' +import { playwright } from '@vitest/browser/providers/playwright' export default defineConfig({ plugins: [svelte()], test: { browser: { enabled: true, - provider: 'playwright', + provider: playwright(), instances: [ { browser: 'chromium' }, ], @@ -176,13 +181,14 @@ export default defineConfig({ ```ts [solid] import { defineConfig } from 'vitest/config' import solidPlugin from 'vite-plugin-solid' +import { playwright } from '@vitest/browser/providers/playwright' export default defineConfig({ plugins: [solidPlugin()], test: { browser: { enabled: true, - provider: 'playwright', + provider: playwright(), instances: [ { browser: 'chromium' }, ], @@ -193,13 +199,14 @@ export default defineConfig({ ```ts [marko] import { defineConfig } from 'vitest/config' import marko from '@marko/vite' +import { playwright } from '@vitest/browser/providers/playwright' export default defineConfig({ plugins: [marko()], test: { browser: { enabled: true, - provider: 'playwright', + provider: playwright(), instances: [ { browser: 'chromium' }, ], @@ -210,6 +217,7 @@ export default defineConfig({ ```ts [qwik] import { defineConfig } from 'vitest/config' import { qwikVite } from '@builder.io/qwik/optimizer' +import { playwright } from '@vitest/browser/providers/playwright' // optional, run the tests in SSR mode import { testSSR } from 'vitest-browser-qwik/ssr-plugin' @@ -219,7 +227,7 @@ export default defineConfig({ test: { browser: { enabled: true, - provider: 'playwright', + provider: playwright(), instances: [{ browser: 'chromium' }] }, }, @@ -370,10 +378,12 @@ Here's an example configuration enabling headless mode: ```ts [vitest.config.ts] import { defineConfig } from 'vitest/config' +import { playwright } from '@vitest/browser/providers/playwright' + export default defineConfig({ test: { browser: { - provider: 'playwright', + provider: playwright(), enabled: true, headless: true, }, diff --git a/docs/guide/browser/multiple-setups.md b/docs/guide/browser/multiple-setups.md index 811227f30..6113e71b7 100644 --- a/docs/guide/browser/multiple-setups.md +++ b/docs/guide/browser/multiple-setups.md @@ -10,11 +10,13 @@ You can use the `browser.instances` field to specify options for different brows ```ts [vitest.config.ts] import { defineConfig } from 'vitest/config' +import { playwright } from '@vitest/browser/providers/playwright' + export default defineConfig({ test: { browser: { enabled: true, - provider: 'playwright', + provider: playwright(), headless: true, instances: [ { browser: 'chromium' }, @@ -33,11 +35,13 @@ You can also specify different config options independently from the browser (al ::: code-group ```ts [vitest.config.ts] import { defineConfig } from 'vitest/config' +import { playwright } from '@vitest/browser/providers/playwright' + export default defineConfig({ test: { browser: { enabled: true, - provider: 'playwright', + provider: playwright(), headless: true, instances: [ { diff --git a/docs/guide/browser/playwright.md b/docs/guide/browser/playwright.md index 2601fcc66..c261f5fad 100644 --- a/docs/guide/browser/playwright.md +++ b/docs/guide/browser/playwright.md @@ -1,62 +1,59 @@ # Configuring Playwright -By default, TypeScript doesn't recognize providers options and extra `expect` properties. Make sure to reference `@vitest/browser/providers/playwright` so TypeScript can pick up definitions for custom options: +To run tests using playwright, you need to specify it in the `test.browser.provider` property in your config: -```ts [vitest.shims.d.ts] -/// -``` - -Alternatively, you can also add it to `compilerOptions.types` field in your `tsconfig.json` file. Note that specifying anything in this field will disable [auto loading](https://www.typescriptlang.org/tsconfig/#types) of `@types/*` packages. - -```json [tsconfig.json] -{ - "compilerOptions": { - "types": ["@vitest/browser/providers/playwright"] - } -} -``` - -Vitest opens a single page to run all tests in the same file. You can configure the `launch`, `connect` and `context` properties in `instances`: - -```ts{9-11} [vitest.config.ts] +```ts [vitest.config.js] +import { playwright } from '@vitest/browser/providers/playwright' import { defineConfig } from 'vitest/config' export default defineConfig({ test: { browser: { - instances: [ - { - browser: 'firefox', - launch: {}, - connect: {}, - context: {}, - }, - ], + provider: playwright(), + instances: [{ browser: 'chromium' }] }, }, }) ``` -::: warning -Before Vitest 3, these options were located on `test.browser.providerOptions` property: +Vitest opens a single page to run all tests in the same file. You can configure the `launch`, `connect` and `context` when calling `playwright` at the top level or inside instances: + +```ts{7-15,22-27} [vitest.config.js] +import { playwright } from '@vitest/browser/providers/playwright' +import { defineConfig } from 'vitest/config' -```ts [vitest.config.ts] export default defineConfig({ test: { browser: { - providerOptions: { - launch: {}, - context: {}, - }, + // shared provider options between all instances + provider: playwright({ + launchOptions: { + slowMo: 50, + channel: 'chrome-beta', + }, + actionTimeout: 5_000, + }), + instances: [ + { browser: 'chromium' }, + { + browser: 'firefox', + // overriding options only for a single instance + // this will NOT merge options with the parent one + provider: playwright({ + launchOptions: { + firefoxUserPrefs: { + 'browser.startup.homepage': 'https://example.com', + }, + }, + }) + } + ], }, }, }) ``` -`providerOptions` is deprecated in favour of `instances`. -::: - -## launch +## launchOptions These options are directly passed down to `playwright[browser].launch` command. You can read more about the command and available arguments in the [Playwright documentation](https://playwright.dev/docs/api/class-browsertype#browser-type-launch). @@ -66,7 +63,7 @@ Vitest will ignore `launch.headless` option. Instead, use [`test.browser.headles Note that Vitest will push debugging flags to `launch.args` if [`--inspect`](/guide/cli#inspect) is enabled. ::: -## connect 3.2.0 {#connect} +## connectOptions These options are directly passed down to `playwright[browser].connect` command. You can read more about the command and available arguments in the [Playwright documentation](https://playwright.dev/docs/api/class-browsertype#browser-type-connect). @@ -74,9 +71,9 @@ These options are directly passed down to `playwright[browser].connect` command. Since this command connects to an existing Playwright server, any `launch` options will be ignored. ::: -## context +## contextOptions -Vitest creates a new context for every test file by calling [`browser.newContext()`](https://playwright.dev/docs/api/class-browsercontext). You can configure this behaviour by specifying [custom arguments](https://playwright.dev/docs/api/class-apirequest#api-request-new-context). +Vitest creates a new context for every test file by calling [`browser.newContext()`](https://playwright.dev/docs/api/class-browsercontext). You can configure this behaviour by specifying [custom arguments](https://playwright.dev/docs/api/class-browser#browser-new-context). ::: tip Note that the context is created for every _test file_, not every _test_ like in playwright test runner. @@ -88,9 +85,9 @@ Vitest always sets `ignoreHTTPSErrors` to `true` in case your server is served v It is also recommended to use [`test.browser.viewport`](/guide/browser/config#browser-headless) instead of specifying it here as it will be lost when tests are running in headless mode. ::: -## `actionTimeout` 3.0.0 +## `actionTimeout` -- **Default:** no timeout, 1 second before 3.0.0 +- **Default:** no timeout This value configures the default timeout it takes for Playwright to wait until all accessibility checks pass and [the action](/guide/browser/interactivity-api) is actually done. diff --git a/docs/guide/browser/visual-regression-testing.md b/docs/guide/browser/visual-regression-testing.md index 544ea1de9..b6e3b7827 100644 --- a/docs/guide/browser/visual-regression-testing.md +++ b/docs/guide/browser/visual-regression-testing.md @@ -239,11 +239,14 @@ await page.viewport(1280, 720) ``` ```ts [vitest.config.ts] +import { playwright } from '@vitest/browser/providers/playwright' +import { defineConfig } from 'vitest/config' + export default defineConfig({ test: { browser: { enabled: true, - provider: 'playwright', + provider: playwright(), instances: [ { browser: 'chromium', @@ -588,6 +591,7 @@ The cleanest approach is using [Test Projects](/guide/projects): ```ts [vitest.config.ts] import { env } from 'node:process' import { defineConfig } from 'vitest/config' +import { playwright } from '@vitest/browser/providers/playwright' export default defineConfig({ // ...global Vite config @@ -610,7 +614,7 @@ export default defineConfig({ include: ['visual-regression-tests/**/*.test.ts?(x)'], browser: { enabled: true, - provider: 'playwright', + provider: playwright(), headless: true, instances: [ { diff --git a/docs/guide/browser/webdriverio.md b/docs/guide/browser/webdriverio.md index b0afdf789..409beb3a9 100644 --- a/docs/guide/browser/webdriverio.md +++ b/docs/guide/browser/webdriverio.md @@ -4,66 +4,59 @@ If you do not already use WebdriverIO in your project, we recommend starting with [Playwright](/guide/browser/playwright) as it is easier to configure and has more flexible API. ::: -By default, TypeScript doesn't recognize providers options and extra `expect` properties. Make sure to reference `@vitest/browser/providers/webdriverio` so TypeScript can pick up definitions for custom options: +To run tests using WebdriverIO, you need to specify it in the `test.browser.provider` property in your config: -```ts [vitest.shims.d.ts] -/// -``` - -Alternatively, you can also add it to `compilerOptions.types` field in your `tsconfig.json` file. Note that specifying anything in this field will disable [auto loading](https://www.typescriptlang.org/tsconfig/#types) of `@types/*` packages. - -```json [tsconfig.json] -{ - "compilerOptions": { - "types": ["@vitest/browser/providers/webdriverio"] - } -} -``` - -Vitest opens a single page to run all tests in the same file. You can configure any property specified in `RemoteOptions` in `instances`: - -```ts{9-12} [vitest.config.ts] +```ts [vitest.config.js] +import { webdriverio } from '@vitest/browser/providers/webdriverio' import { defineConfig } from 'vitest/config' export default defineConfig({ test: { browser: { - instances: [ - { - browser: 'chrome', - capabilities: { - browserVersion: 86, - platformName: 'Windows 10', - }, - }, - ], + provider: webdriverio(), + instances: [{ browser: 'chrome' }] }, }, }) ``` -::: warning -Before Vitest 3, these options were located on `test.browser.providerOptions` property: +Vitest opens a single page to run all tests in the same file. You can configure all the parameters that [`remote`](https://webdriver.io/docs/api/modules/#remoteoptions-modifier) function accepts: + +```ts{8-12,19-23} [vitest.config.js] +import { webdriverio } from '@vitest/browser/providers/webdriverio' +import { defineConfig } from 'vitest/config' -```ts [vitest.config.ts] export default defineConfig({ test: { browser: { - providerOptions: { - capabilities: {}, - }, + // shared provider options between all instances + provider: webdriverio({ + capabilities: { + browserVersion: '82', + }, + }), + instances: [ + { browser: 'chrome' }, + { + browser: 'firefox', + // overriding options only for a single instance + // this will NOT merge options with the parent one + provider: webdriverio({ + 'moz:firefoxOptions': { + args: ['--disable-gpu'], + }, + }) + } + ], }, }, }) ``` -`providerOptions` is deprecated in favour of `instances`. -::: - You can find most available options in the [WebdriverIO documentation](https://webdriver.io/docs/configuration/). Note that Vitest will ignore all test runner options because we only use `webdriverio`'s browser capabilities. ::: tip Most useful options are located on `capabilities` object. WebdriverIO allows nested capabilities, but Vitest will ignore those options because we rely on a different mechanism to spawn several browsers. -Note that Vitest will ignore `capabilities.browserName`. Use [`test.browser.instances.name`](/guide/browser/config#browser-capabilities-name) instead. +Note that Vitest will ignore `capabilities.browserName`. Use [`test.browser.instances.browser`](/guide/browser/config#browser-capabilities-name) instead. ::: diff --git a/docs/guide/debugging.md b/docs/guide/debugging.md index 0e0fd934d..144aa89f7 100644 --- a/docs/guide/debugging.md +++ b/docs/guide/debugging.md @@ -52,13 +52,14 @@ vitest --inspect-brk --browser --no-file-parallelism ``` ```ts [vitest.config.js] import { defineConfig } from 'vitest/config' +import { playwright } from '@vitest/browser/providers/playwright' export default defineConfig({ test: { inspectBrk: true, fileParallelism: false, browser: { - provider: 'playwright', + provider: playwright(), instances: [{ browser: 'chromium' }] }, }, diff --git a/docs/guide/migration.md b/docs/guide/migration.md index 5b97782ed..eaf92cfa9 100644 --- a/docs/guide/migration.md +++ b/docs/guide/migration.md @@ -229,6 +229,37 @@ export default defineWorkspace([ // [!code --] ``` ::: +### Browser Provider Accepts an Object + +In Vitest 4.0, the browser provider now accepts an object instead of a string (`'playwright'`, `'webdriverio'`). This makes it simpler to work with custom options and doesn't require adding `/// +import { playwright } from '@vitest/browser/providers/playwright' import { defineConfig } from 'vite' // https://vitejs.dev/config/ @@ -9,7 +10,7 @@ export default defineConfig({ // https://lit.dev/docs/tools/testing/#testing-in-the-browser browser: { enabled: true, - provider: 'playwright', + provider: playwright(), instances: [ { browser: 'chromium' }, ], diff --git a/packages/browser/README.md b/packages/browser/README.md index 1e5eac4cf..d6810bf7d 100644 --- a/packages/browser/README.md +++ b/packages/browser/README.md @@ -1,19 +1,7 @@ # @vitest/browser -[Browser runner](https://vitest.dev/guide/browser/) for Vitest. +[![NPM version](https://img.shields.io/npm/v/@vitest/browser?color=a1b858&label=)](https://www.npmjs.com/package/@vitest/browser) -> ⚠️ This package is **experimental**. While this package will be released along with other packages, it will not follow SemVer for breaking changes until we mark it as ready. +Running Vitest tests in the real browser. -## Development Setup - -At project root: - -```bash -cd test/browser -# runs relevant tests for the browser mode -# useful to confirm everything works fine -pnpm test -# runs tests as the browser mode -# useful during development -pnpm test-fixtures -``` +[GitHub](https://github.com/vitest-dev/vitest) | [Documentation](https://vitest.dev/guide/browser/) diff --git a/packages/browser/package.json b/packages/browser/package.json index 0240d11a2..355de79e8 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -20,9 +20,9 @@ "types": "./dist/index.d.ts", "default": "./dist/index.js" }, - "./providers": { - "types": "./providers.d.ts", - "default": "./dist/providers.js" + "./providers/*": { + "types": "./dist/providers/*.d.ts", + "default": "./dist/providers/*.js" }, "./context": { "types": "./context.d.ts", @@ -35,14 +35,6 @@ "types": "./matchers.d.ts", "default": "./dummy.js" }, - "./providers/webdriverio": { - "types": "./providers/webdriverio.d.ts", - "default": "./dummy.js" - }, - "./providers/playwright": { - "types": "./providers/playwright.d.ts", - "default": "./dummy.js" - }, "./locator": { "types": "./dist/locators/index.d.ts", "default": "./dist/locators/index.js" @@ -105,19 +97,14 @@ "@types/pngjs": "^6.0.5", "@types/ws": "catalog:", "@vitest/runner": "workspace:*", - "@vitest/ui": "workspace:*", - "@vitest/ws-client": "workspace:*", - "@wdio/protocols": "^9.16.2", "@wdio/types": "^9.16.2", "birpc": "catalog:", "flatted": "catalog:", "ivya": "^1.7.0", "mime": "^4.0.7", "pathe": "catalog:", - "periscopic": "^4.0.2", "playwright": "^1.54.1", "playwright-core": "^1.54.1", - "safaridriver": "^1.0.0", "vitest": "workspace:*", "webdriverio": "^9.18.4" } diff --git a/packages/browser/providers/playwright.d.ts b/packages/browser/providers/playwright.d.ts deleted file mode 100644 index 904d233b0..000000000 --- a/packages/browser/providers/playwright.d.ts +++ /dev/null @@ -1,97 +0,0 @@ -import type { - BrowserContext, - BrowserContextOptions, - Frame, - FrameLocator, - LaunchOptions, - Page, - CDPSession, - ConnectOptions -} from 'playwright' -import { Protocol } from 'playwright-core/types/protocol' -import '../matchers.js' -import type {} from "vitest/node" -import type { - Locator, - ScreenshotComparatorRegistry, - ScreenshotMatcherOptions, -} from "@vitest/browser/context" - -declare module 'vitest/node' { - export interface BrowserProviderOptions { - launch?: LaunchOptions - connect?: { - wsEndpoint: string - options?: ConnectOptions - } - context?: Omit< - BrowserContextOptions, - 'ignoreHTTPSErrors' | 'serviceWorkers' - > & { - /** - * The maximum time in milliseconds to wait for `userEvent` action to complete. - * @default 0 (no timeout) - */ - actionTimeout?: number - } - } - - export interface BrowserCommandContext { - page: Page - frame(): Promise - iframe: FrameLocator - context: BrowserContext - } - - export interface ToMatchScreenshotOptions - extends Omit< - ScreenshotMatcherOptions, - "comparatorName" | "comparatorOptions" - > {} - - export interface ToMatchScreenshotComparators - extends ScreenshotComparatorRegistry {} -} - -type PWHoverOptions = NonNullable[1]> -type PWClickOptions = NonNullable[1]> -type PWDoubleClickOptions = NonNullable[1]> -type PWFillOptions = NonNullable[2]> -type PWScreenshotOptions = NonNullable[0]> -type PWSelectOptions = NonNullable[2]> -type PWDragAndDropOptions = NonNullable[2]> -type PWSetInputFiles = NonNullable[2]> - -declare module '@vitest/browser/context' { - export interface UserEventHoverOptions extends PWHoverOptions {} - export interface UserEventClickOptions extends PWClickOptions {} - export interface UserEventDoubleClickOptions extends PWDoubleClickOptions {} - export interface UserEventTripleClickOptions extends PWClickOptions {} - export interface UserEventFillOptions extends PWFillOptions {} - export interface UserEventSelectOptions extends PWSelectOptions {} - export interface UserEventDragAndDropOptions extends PWDragAndDropOptions {} - export interface UserEventUploadOptions extends PWSetInputFiles {} - - export interface ScreenshotOptions extends Omit { - mask?: ReadonlyArray | undefined - } - - export interface CDPSession { - send( - method: T, - params?: Protocol.CommandParameters[T] - ): Promise - on( - event: T, - listener: (payload: Protocol.Events[T]) => void - ): this; - once( - event: T, - listener: (payload: Protocol.Events[T]) => void - ): this; - off( - event: T, - listener: (payload: Protocol.Events[T]) => void - ): this; - } -} diff --git a/packages/browser/providers/webdriverio.d.ts b/packages/browser/providers/webdriverio.d.ts deleted file mode 100644 index 66fdd93d2..000000000 --- a/packages/browser/providers/webdriverio.d.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { remote, ClickOptions, DragAndDropOptions } from 'webdriverio' -import '../matchers.js' -import type {} from "vitest/node" -import type { - ScreenshotComparatorRegistry, - ScreenshotMatcherOptions, -} from "@vitest/browser/context"; - -declare module 'vitest/node' { - export interface BrowserProviderOptions extends Partial< - Parameters[0] - > {} - - export interface UserEventClickOptions extends ClickOptions {} - - export interface UserEventDragOptions extends DragAndDropOptions { - sourceX?: number - sourceY?: number - targetX?: number - targetY?: number - } - - export interface BrowserCommandContext { - browser: WebdriverIO.Browser - } - - export interface ToMatchScreenshotOptions - extends Omit< - ScreenshotMatcherOptions, - "comparatorName" | "comparatorOptions" - > {} - - export interface ToMatchScreenshotComparators - extends ScreenshotComparatorRegistry {} -} diff --git a/packages/browser/rollup.config.js b/packages/browser/rollup.config.js index a87a86cad..6b56b7034 100644 --- a/packages/browser/rollup.config.js +++ b/packages/browser/rollup.config.js @@ -17,6 +17,7 @@ const external = [ 'worker_threads', 'node:worker_threads', 'vite', + 'playwright-core/types/protocol', ] const dtsUtils = createDtsUtils() @@ -38,8 +39,10 @@ const plugins = [ ] const input = { - index: './src/node/index.ts', - providers: './src/node/providers/index.ts', + 'index': './src/node/index.ts', + 'providers/playwright': './src/node/providers/playwright.ts', + 'providers/webdriverio': './src/node/providers/webdriverio.ts', + 'providers/preview': './src/node/providers/preview.ts', } export default () => @@ -135,7 +138,7 @@ export default () => ], }, { - input: dtsUtils.dtsInput(input.index), + input: dtsUtils.dtsInput(input), output: { dir: 'dist', entryFileNames: '[name].d.ts', diff --git a/packages/browser/src/client/tester/tester.ts b/packages/browser/src/client/tester/tester.ts index 2c7c6a148..e542e8033 100644 --- a/packages/browser/src/client/tester/tester.ts +++ b/packages/browser/src/client/tester/tester.ts @@ -84,7 +84,6 @@ channel.addEventListener('message', async (e) => { }) const url = new URL(location.href) -const reloadStart = url.searchParams.get('__reloadStart') const iframeId = url.searchParams.get('iframeId')! const commands = new CommandsManager() @@ -94,7 +93,7 @@ getBrowserState().iframeId = iframeId let contextSwitched = false async function prepareTestEnvironment(options: PrepareOptions) { - debug?.('trying to resolve runner', `${reloadStart}`) + debug?.('trying to resolve the runner') const config = getConfig() const rpc = createSafeRpc(client) diff --git a/packages/browser/src/node/commands/clear.ts b/packages/browser/src/node/commands/clear.ts index 9de4737ee..79f26644b 100644 --- a/packages/browser/src/node/commands/clear.ts +++ b/packages/browser/src/node/commands/clear.ts @@ -1,7 +1,7 @@ import type { UserEvent } from '../../../context' import type { UserEventCommand } from './utils' import { PlaywrightBrowserProvider } from '../providers/playwright' -import { WebdriverBrowserProvider } from '../providers/webdriver' +import { WebdriverBrowserProvider } from '../providers/webdriverio' export const clear: UserEventCommand = async ( context, diff --git a/packages/browser/src/node/commands/click.ts b/packages/browser/src/node/commands/click.ts index d804b043d..31047a013 100644 --- a/packages/browser/src/node/commands/click.ts +++ b/packages/browser/src/node/commands/click.ts @@ -1,7 +1,7 @@ import type { UserEvent } from '../../../context' import type { UserEventCommand } from './utils' import { PlaywrightBrowserProvider } from '../providers/playwright' -import { WebdriverBrowserProvider } from '../providers/webdriver' +import { WebdriverBrowserProvider } from '../providers/webdriverio' export const click: UserEventCommand = async ( context, diff --git a/packages/browser/src/node/commands/dragAndDrop.ts b/packages/browser/src/node/commands/dragAndDrop.ts index 6fc66bbb5..c665fd9ea 100644 --- a/packages/browser/src/node/commands/dragAndDrop.ts +++ b/packages/browser/src/node/commands/dragAndDrop.ts @@ -1,7 +1,7 @@ import type { UserEvent } from '../../../context' import type { UserEventCommand } from './utils' import { PlaywrightBrowserProvider } from '../providers/playwright' -import { WebdriverBrowserProvider } from '../providers/webdriver' +import { WebdriverBrowserProvider } from '../providers/webdriverio' export const dragAndDrop: UserEventCommand = async ( context, diff --git a/packages/browser/src/node/commands/fill.ts b/packages/browser/src/node/commands/fill.ts index b7651ce74..c3256cccb 100644 --- a/packages/browser/src/node/commands/fill.ts +++ b/packages/browser/src/node/commands/fill.ts @@ -1,7 +1,7 @@ import type { UserEvent } from '../../../context' import type { UserEventCommand } from './utils' import { PlaywrightBrowserProvider } from '../providers/playwright' -import { WebdriverBrowserProvider } from '../providers/webdriver' +import { WebdriverBrowserProvider } from '../providers/webdriverio' export const fill: UserEventCommand = async ( context, diff --git a/packages/browser/src/node/commands/hover.ts b/packages/browser/src/node/commands/hover.ts index 9d90d21df..7672eb839 100644 --- a/packages/browser/src/node/commands/hover.ts +++ b/packages/browser/src/node/commands/hover.ts @@ -1,7 +1,7 @@ import type { UserEvent } from '../../../context' import type { UserEventCommand } from './utils' import { PlaywrightBrowserProvider } from '../providers/playwright' -import { WebdriverBrowserProvider } from '../providers/webdriver' +import { WebdriverBrowserProvider } from '../providers/webdriverio' export const hover: UserEventCommand = async ( context, diff --git a/packages/browser/src/node/commands/keyboard.ts b/packages/browser/src/node/commands/keyboard.ts index 80f3ca8cf..6024e55ab 100644 --- a/packages/browser/src/node/commands/keyboard.ts +++ b/packages/browser/src/node/commands/keyboard.ts @@ -3,7 +3,7 @@ import type { UserEventCommand } from './utils' import { defaultKeyMap } from '@testing-library/user-event/dist/esm/keyboard/keyMap.js' import { parseKeyDef } from '@testing-library/user-event/dist/esm/keyboard/parseKeyDef.js' import { PlaywrightBrowserProvider } from '../providers/playwright' -import { WebdriverBrowserProvider } from '../providers/webdriver' +import { WebdriverBrowserProvider } from '../providers/webdriverio' export interface KeyboardState { unreleased: string[] diff --git a/packages/browser/src/node/commands/screenshot.ts b/packages/browser/src/node/commands/screenshot.ts index 11553473b..a87a09f41 100644 --- a/packages/browser/src/node/commands/screenshot.ts +++ b/packages/browser/src/node/commands/screenshot.ts @@ -4,7 +4,7 @@ import { mkdir, rm } from 'node:fs/promises' import { normalize } from 'node:path' import { basename, dirname, relative, resolve } from 'pathe' import { PlaywrightBrowserProvider } from '../providers/playwright' -import { WebdriverBrowserProvider } from '../providers/webdriver' +import { WebdriverBrowserProvider } from '../providers/webdriverio' interface ScreenshotCommandOptions extends Omit { element?: string diff --git a/packages/browser/src/node/commands/select.ts b/packages/browser/src/node/commands/select.ts index 22da023a1..b2910a317 100644 --- a/packages/browser/src/node/commands/select.ts +++ b/packages/browser/src/node/commands/select.ts @@ -2,7 +2,7 @@ import type { ElementHandle } from 'playwright' import type { UserEvent } from '../../../context' import type { UserEventCommand } from './utils' import { PlaywrightBrowserProvider } from '../providers/playwright' -import { WebdriverBrowserProvider } from '../providers/webdriver' +import { WebdriverBrowserProvider } from '../providers/webdriverio' export const selectOptions: UserEventCommand = async ( context, diff --git a/packages/browser/src/node/commands/tab.ts b/packages/browser/src/node/commands/tab.ts index 6c1f57b0d..3739841b9 100644 --- a/packages/browser/src/node/commands/tab.ts +++ b/packages/browser/src/node/commands/tab.ts @@ -1,7 +1,7 @@ import type { UserEvent } from '../../../context' import type { UserEventCommand } from './utils' import { PlaywrightBrowserProvider } from '../providers/playwright' -import { WebdriverBrowserProvider } from '../providers/webdriver' +import { WebdriverBrowserProvider } from '../providers/webdriverio' export const tab: UserEventCommand = async ( context, diff --git a/packages/browser/src/node/commands/type.ts b/packages/browser/src/node/commands/type.ts index 78ddc7a3f..189e7343b 100644 --- a/packages/browser/src/node/commands/type.ts +++ b/packages/browser/src/node/commands/type.ts @@ -1,7 +1,7 @@ import type { UserEvent } from '../../../context' import type { UserEventCommand } from './utils' import { PlaywrightBrowserProvider } from '../providers/playwright' -import { WebdriverBrowserProvider } from '../providers/webdriver' +import { WebdriverBrowserProvider } from '../providers/webdriverio' import { keyboardImplementation } from './keyboard' export const type: UserEventCommand = async ( diff --git a/packages/browser/src/node/commands/upload.ts b/packages/browser/src/node/commands/upload.ts index 81b2580c5..0893fa37a 100644 --- a/packages/browser/src/node/commands/upload.ts +++ b/packages/browser/src/node/commands/upload.ts @@ -2,7 +2,7 @@ import type { UserEventUploadOptions } from '@vitest/browser/context' import type { UserEventCommand } from './utils' import { resolve } from 'pathe' import { PlaywrightBrowserProvider } from '../providers/playwright' -import { WebdriverBrowserProvider } from '../providers/webdriver' +import { WebdriverBrowserProvider } from '../providers/webdriverio' export const upload: UserEventCommand<(element: string, files: Array + /** + * The maximum time in milliseconds to wait for `userEvent` action to complete. + * @default 0 (no timeout) + */ + actionTimeout?: number +} -export interface PlaywrightProviderOptions - extends BrowserProviderInitializationOptions { - browser: PlaywrightBrowser +export function playwright(options: PlaywrightProviderOptions = {}): BrowserProviderOption { + return { + name: 'playwright', + supportedBrowser: playwrightBrowsers, + factory(project) { + return new PlaywrightBrowserProvider(project, options) + }, + // --browser.provider=playwright + // @ts-expect-error hidden way to bypass importing playwright + _cli: true, + } } export class PlaywrightBrowserProvider implements BrowserProvider { @@ -38,39 +83,19 @@ export class PlaywrightBrowserProvider implements BrowserProvider { public browser: Browser | null = null - private browserName!: PlaywrightBrowser - private project!: TestProject - - private options?: { - launch?: LaunchOptions - connect?: { - wsEndpoint: string - options?: ConnectOptions - } - context?: BrowserContextOptions & { actionTimeout?: number } - } - public contexts: Map = new Map() public pages: Map = new Map() + public mocker: BrowserModuleMocker + public browserName: PlaywrightBrowser private browserPromise: Promise | null = null - - public mocker: BrowserModuleMocker | undefined - private closing = false - getSupportedBrowsers(): readonly string[] { - return playwrightBrowsers - } - - initialize( - project: TestProject, - { browser, options }: PlaywrightProviderOptions, - ): void { - this.closing = false - this.project = project - this.browserName = browser - this.options = options as any + constructor( + private project: TestProject, + private options: PlaywrightProviderOptions, + ) { + this.browserName = project.config.browser.name as PlaywrightBrowser this.mocker = this.createMocker() } @@ -92,22 +117,22 @@ export class PlaywrightBrowserProvider implements BrowserProvider { const playwright = await import('playwright') - if (this.options?.connect) { - if (this.options.launch) { + if (this.options.connectOptions) { + if (this.options.launchOptions) { this.project.vitest.logger.warn( c.yellow(`Found both ${c.bold(c.italic(c.yellow('connect')))} and ${c.bold(c.italic(c.yellow('launch')))} options in browser instance configuration. Ignoring ${c.bold(c.italic(c.yellow('launch')))} options and using ${c.bold(c.italic(c.yellow('connect')))} mode. You probably want to remove one of the two options and keep only the one you want to use.`), ) } - const browser = await playwright[this.browserName].connect(this.options.connect.wsEndpoint, this.options.connect.options) + const browser = await playwright[this.browserName].connect(this.options.connectOptions.wsEndpoint, this.options.connectOptions) this.browser = browser this.browserPromise = null return this.browser } const launchOptions = { - ...this.options?.launch, + ...this.options.launchOptions, headless: options.headless, } satisfies LaunchOptions @@ -288,7 +313,8 @@ export class PlaywrightBrowserProvider implements BrowserProvider { const browser = await this.openBrowser() await this._throwIfClosing(browser) - const { actionTimeout, ...contextOptions } = this.options?.context ?? {} + const actionTimeout = this.options.actionTimeout + const contextOptions = this.options.contextOptions ?? {} const options = { ...contextOptions, ignoreHTTPSErrors: true, @@ -298,7 +324,7 @@ export class PlaywrightBrowserProvider implements BrowserProvider { } const context = await browser.newContext(options) await this._throwIfClosing(context) - if (actionTimeout) { + if (actionTimeout != null) { context.setDefaultTimeout(actionTimeout) } debug?.('[%s][%s] the context is ready', sessionId, this.browserName) @@ -423,12 +449,12 @@ export class PlaywrightBrowserProvider implements BrowserProvider { async close(): Promise { debug?.('[%s] closing provider', this.browserName) this.closing = true - const browser = this.browser - this.browser = null if (this.browserPromise) { await this.browserPromise this.browserPromise = null } + const browser = this.browser + this.browser = null await Promise.all([...this.pages.values()].map(p => p.close())) this.pages.clear() await Promise.all([...this.contexts.values()].map(c => c.close())) @@ -476,3 +502,64 @@ const directRequestRE = /[?&]direct\b/ function isDirectCSSRequest(request: string): boolean { return isCSSRequest(request) && directRequestRE.test(request) } + +declare module 'vitest/node' { + export interface BrowserCommandContext { + page: Page + frame(): Promise + iframe: FrameLocator + context: BrowserContext + } + + export interface ToMatchScreenshotOptions + extends Omit< + ScreenshotMatcherOptions, + 'comparatorName' | 'comparatorOptions' + > {} + + export interface ToMatchScreenshotComparators + extends ScreenshotComparatorRegistry {} +} + +type PWHoverOptions = NonNullable[1]> +type PWClickOptions = NonNullable[1]> +type PWDoubleClickOptions = NonNullable[1]> +type PWFillOptions = NonNullable[2]> +type PWScreenshotOptions = NonNullable[0]> +type PWSelectOptions = NonNullable[2]> +type PWDragAndDropOptions = NonNullable[2]> +type PWSetInputFiles = NonNullable[2]> + +declare module '@vitest/browser/context' { + export interface UserEventHoverOptions extends PWHoverOptions {} + export interface UserEventClickOptions extends PWClickOptions {} + export interface UserEventDoubleClickOptions extends PWDoubleClickOptions {} + export interface UserEventTripleClickOptions extends PWClickOptions {} + export interface UserEventFillOptions extends PWFillOptions {} + export interface UserEventSelectOptions extends PWSelectOptions {} + export interface UserEventDragAndDropOptions extends PWDragAndDropOptions {} + export interface UserEventUploadOptions extends PWSetInputFiles {} + + export interface ScreenshotOptions extends Omit { + mask?: ReadonlyArray | undefined + } + + export interface CDPSession { + send( + method: T, + params?: Protocol.CommandParameters[T] + ): Promise + on( + event: T, + listener: (payload: Protocol.Events[T]) => void + ): this + once( + event: T, + listener: (payload: Protocol.Events[T]) => void + ): this + off( + event: T, + listener: (payload: Protocol.Events[T]) => void + ): this + } +} diff --git a/packages/browser/src/node/providers/preview.ts b/packages/browser/src/node/providers/preview.ts index 31eb96ff1..60ef88aca 100644 --- a/packages/browser/src/node/providers/preview.ts +++ b/packages/browser/src/node/providers/preview.ts @@ -1,4 +1,16 @@ -import type { BrowserProvider, TestProject } from 'vitest/node' +import type { BrowserProvider, BrowserProviderOption, TestProject } from 'vitest/node' + +export function preview(): BrowserProviderOption { + return { + name: 'preview', + factory(project) { + return new PreviewBrowserProvider(project) + }, + // --browser.provider=preview + // @ts-expect-error hidden way to bypass importing preview + _cli: true, + } +} export class PreviewBrowserProvider implements BrowserProvider { public name = 'preview' as const @@ -6,20 +18,7 @@ export class PreviewBrowserProvider implements BrowserProvider { private project!: TestProject private open = false - getSupportedBrowsers(): string[] { - // `none` is not restricted to certain browsers. - return [] - } - - isOpen(): boolean { - return this.open - } - - getCommandsContext() { - return {} - } - - async initialize(project: TestProject): Promise { + constructor(project: TestProject) { this.project = project this.open = false if (project.config.browser.headless) { @@ -30,6 +29,14 @@ export class PreviewBrowserProvider implements BrowserProvider { project.vitest.logger.printBrowserBanner(project) } + isOpen(): boolean { + return this.open + } + + getCommandsContext() { + return {} + } + async openPage(_sessionId: string, url: string): Promise { this.open = true if (!this.project.browser) { diff --git a/packages/browser/src/node/providers/webdriver.ts b/packages/browser/src/node/providers/webdriverio.ts similarity index 80% rename from packages/browser/src/node/providers/webdriver.ts rename to packages/browser/src/node/providers/webdriverio.ts index 5c75b6474..08d6fdc5e 100644 --- a/packages/browser/src/node/providers/webdriver.ts +++ b/packages/browser/src/node/providers/webdriverio.ts @@ -1,9 +1,15 @@ +import type { + ScreenshotComparatorRegistry, + ScreenshotMatcherOptions, +} from '@vitest/browser/context' import type { Capabilities } from '@wdio/types' import type { BrowserProvider, - BrowserProviderInitializationOptions, + BrowserProviderOption, TestProject, } from 'vitest/node' +import type { ClickOptions, DragAndDropOptions, remote } from 'webdriverio' + import { createDebugger } from 'vitest/node' const debug = createDebugger('vitest:browser:wdio') @@ -11,9 +17,21 @@ const debug = createDebugger('vitest:browser:wdio') const webdriverBrowsers = ['firefox', 'chrome', 'edge', 'safari'] as const type WebdriverBrowser = (typeof webdriverBrowsers)[number] -interface WebdriverProviderOptions - extends BrowserProviderInitializationOptions { - browser: WebdriverBrowser +interface WebdriverProviderOptions extends Partial< + Parameters[0] + > {} + +export function webdriverio(options: WebdriverProviderOptions = {}): BrowserProviderOption { + return { + name: 'webdriverio', + supportedBrowser: webdriverBrowsers, + factory(project) { + return new WebdriverBrowserProvider(project, options) + }, + // --browser.provider=webdriverio + // @ts-expect-error hidden way to bypass importing webdriverio + _cli: true, + } } export class WebdriverBrowserProvider implements BrowserProvider { @@ -25,7 +43,7 @@ export class WebdriverBrowserProvider implements BrowserProvider { private browserName!: WebdriverBrowser private project!: TestProject - private options?: Capabilities.WebdriverIOConfig + private options?: WebdriverProviderOptions private closing = false private iframeSwitched = false @@ -35,10 +53,10 @@ export class WebdriverBrowserProvider implements BrowserProvider { return webdriverBrowsers } - async initialize( + constructor( project: TestProject, - { browser, options }: WebdriverProviderOptions, - ): Promise { + options: WebdriverProviderOptions, + ) { // increase shutdown timeout because WDIO takes some extra time to kill the driver if (!project.vitest.state._data.timeoutIncreased) { project.vitest.state._data.timeoutIncreased = true @@ -47,8 +65,8 @@ export class WebdriverBrowserProvider implements BrowserProvider { this.closing = false this.project = project - this.browserName = browser - this.options = options as Capabilities.WebdriverIOConfig + this.browserName = project.config.browser.name as WebdriverBrowser + this.options = options } isIframeSwitched(): boolean { @@ -125,8 +143,8 @@ export class WebdriverBrowserProvider implements BrowserProvider { const { remote } = await import('webdriverio') const remoteOptions: Capabilities.WebdriverIOConfig = { + logLevel: 'silent', ...this.options, - logLevel: 'error', capabilities: this.buildCapabilities(), } @@ -208,3 +226,27 @@ export class WebdriverBrowserProvider implements BrowserProvider { this.browser = null } } + +declare module 'vitest/node' { + export interface UserEventClickOptions extends ClickOptions {} + + export interface UserEventDragOptions extends DragAndDropOptions { + sourceX?: number + sourceY?: number + targetX?: number + targetY?: number + } + + export interface BrowserCommandContext { + browser: WebdriverIO.Browser + } + + export interface ToMatchScreenshotOptions + extends Omit< + ScreenshotMatcherOptions, + 'comparatorName' | 'comparatorOptions' + > {} + + export interface ToMatchScreenshotComparators + extends ScreenshotComparatorRegistry {} +} diff --git a/packages/browser/src/node/rpc.ts b/packages/browser/src/node/rpc.ts index ed2777715..16b84aea2 100644 --- a/packages/browser/src/node/rpc.ts +++ b/packages/browser/src/node/rpc.ts @@ -5,7 +5,7 @@ import type { BrowserCommandContext, ResolveSnapshotPathHandlerContext, TestProj import type { WebSocket } from 'ws' import type { WebSocketBrowserEvents, WebSocketBrowserHandlers } from '../types' import type { ParentBrowserProject } from './projectParent' -import type { WebdriverBrowserProvider } from './providers/webdriver' +import type { WebdriverBrowserProvider } from './providers/webdriverio' import type { BrowserServerState } from './state' import { existsSync, promises as fs } from 'node:fs' import { AutomockedModule, AutospiedModule, ManualMockedModule, RedirectedModule } from '@vitest/mocker' diff --git a/packages/browser/src/node/serverOrchestrator.ts b/packages/browser/src/node/serverOrchestrator.ts index da2668ec5..9880d9713 100644 --- a/packages/browser/src/node/serverOrchestrator.ts +++ b/packages/browser/src/node/serverOrchestrator.ts @@ -36,7 +36,7 @@ export async function resolveOrchestrator( : await globalServer.injectorJs const injector = replacer(injectorJs, { - __VITEST_PROVIDER__: JSON.stringify(browserProject.config.browser.provider || 'preview'), + __VITEST_PROVIDER__: JSON.stringify(browserProject.config.browser.provider?.name || 'preview'), __VITEST_CONFIG__: JSON.stringify(browserProject.wrapSerializedConfig()), __VITEST_VITE_CONFIG__: JSON.stringify({ root: browserProject.vite.config.root, diff --git a/packages/browser/src/node/utils.ts b/packages/browser/src/node/utils.ts index f3e0f20da..40308aeb6 100644 --- a/packages/browser/src/node/utils.ts +++ b/packages/browser/src/node/utils.ts @@ -1,45 +1,46 @@ -import type { BrowserProviderModule, ResolvedBrowserOptions, TestProject } from 'vitest/node' +import type { BrowserProvider, ResolvedBrowserOptions, TestProject } from 'vitest/node' export function replacer(code: string, values: Record): string { return code.replace(/\{\s*(\w+)\s*\}/g, (_, key) => values[key] ?? _) } -const builtinProviders = ['webdriverio', 'playwright', 'preview'] - export async function getBrowserProvider( options: ResolvedBrowserOptions, project: TestProject, -): Promise { - if (options.provider == null || builtinProviders.includes(options.provider)) { - const providers = await import('./providers') - const provider = (options.provider || 'preview') as - | 'webdriverio' - | 'playwright' - | 'preview' - return providers[provider] - } - - let customProviderModule - - try { - customProviderModule = (await project.import<{ default: BrowserProviderModule }>( - options.provider, - )) - } - catch (error) { +): Promise { + const browser = project.config.browser.name + const name = project.name ? `[${project.name}] ` : '' + if (!browser) { throw new Error( - `Failed to load custom BrowserProvider from ${options.provider}`, - { cause: error }, + `${name}Browser name is required. Please, set \`test.browser.instances[].browser\` option manually.`, ) } - - if (customProviderModule.default == null) { + if ( + // nothing is provided by default + options.provider == null + // the provider is provided via `--browser.provider=playwright` + // or the config was serialized, but we can infer the factory by the name + || ('_cli' in options.provider && typeof options.provider.factory !== 'function') + ) { + const providers = await import('./providers/index') + const name = (options.provider?.name || 'preview') as 'preview' | 'webdriverio' | 'playwright' + if (!(name in providers)) { + throw new Error(`Unknown browser provider "${name}". Available providers: ${Object.keys(providers).join(', ')}.`) + } + return providers[name]().factory(project) + } + const supportedBrowsers = options.provider.supportedBrowser || [] + if (supportedBrowsers.length && !supportedBrowsers.includes(browser)) { throw new Error( - `Custom BrowserProvider loaded from ${options.provider} was not the default export`, + `${name}Browser "${browser}" is not supported by the browser provider "${ + options.provider.name + }". Supported browsers: ${supportedBrowsers.join(', ')}.`, ) } - - return customProviderModule.default + if (typeof options.provider.factory !== 'function') { + throw new TypeError(`The "${name}" browser provider does not provide a "factory" function. Received ${typeof options.provider.factory}.`) + } + return options.provider.factory(project) } export function slash(path: string): string { diff --git a/packages/ui/vite.config.ts b/packages/ui/vite.config.ts index ad9ee9016..f8fd979c1 100644 --- a/packages/ui/vite.config.ts +++ b/packages/ui/vite.config.ts @@ -106,10 +106,4 @@ export default defineConfig({ build: { outDir: './dist/client', }, - test: { - browser: { - name: 'chromium', - provider: 'playwright', - }, - }, }) diff --git a/packages/ui/vitest.config.ts b/packages/ui/vitest.config.ts new file mode 100644 index 000000000..1c3e3e320 --- /dev/null +++ b/packages/ui/vitest.config.ts @@ -0,0 +1,16 @@ +import { playwright } from '@vitest/browser/providers/playwright' +import { mergeConfig } from 'vite' +import { defineConfig } from 'vitest/config' +import viteConfig from './vite.config' + +export default mergeConfig( + viteConfig, + defineConfig({ + test: { + browser: { + provider: playwright(), + instances: [{ browser: 'chromium' }], + }, + }, + }), +) diff --git a/packages/vitest/src/create/browser/creator.ts b/packages/vitest/src/create/browser/creator.ts index 51eee017d..2a55884ca 100644 --- a/packages/vitest/src/create/browser/creator.ts +++ b/packages/vitest/src/create/browser/creator.ts @@ -295,6 +295,7 @@ async function generateFrameworkConfigFile(options: { const configContent = [ `import { defineConfig } from 'vitest/config'`, + `import { ${options.provider} } from '@vitest/browser/providers/${options.provider}'`, options.frameworkPlugin ? frameworkImport : null, ``, 'export default defineConfig({', @@ -302,7 +303,7 @@ async function generateFrameworkConfigFile(options: { ` test: {`, ` browser: {`, ` enabled: true,`, - ` provider: '${options.provider}',`, + ` provider: ${options.provider}(),`, options.provider !== 'preview' && ` // ${getProviderDocsLink(options.provider)}`, ` instances: [`, ...options.browsers.map(browser => ` { browser: '${browser}' },`), diff --git a/packages/vitest/src/node/cli/cli-config.ts b/packages/vitest/src/node/cli/cli-config.ts index d2ca85369..50385c9bc 100644 --- a/packages/vitest/src/node/cli/cli-config.ts +++ b/packages/vitest/src/node/cli/cli-config.ts @@ -380,12 +380,13 @@ export const cliOptionsConfig: VitestCLIOptions = { 'Provider used to run browser tests. Some browsers are only available for specific providers. Can be "webdriverio", "playwright", "preview", or the path to a custom provider. Visit [`browser.provider`](https://vitest.dev/guide/browser/config.html#browser-provider) for more information (default: `"preview"`)', argument: '', subcommands: null, // don't support custom objects - }, - providerOptions: { - description: - 'Options that are passed down to a browser provider. Visit [`browser.providerOptions`](https://vitest.dev/config/#browser-provideroptions) for more information', - argument: '', - subcommands: null, // don't support custom objects + transform(value) { + const supported = ['playwright', 'webdriverio', 'preview'] + if (typeof value !== 'string' || !supported.includes(value)) { + throw new Error(`Unsupported browser provider: ${value}. Supported providers are: ${supported.join(', ')}`) + } + return { name: value, _cli: true } + }, }, isolate: { description: diff --git a/packages/vitest/src/node/config/resolveConfig.ts b/packages/vitest/src/node/config/resolveConfig.ts index a915183f8..fdfd66ce3 100644 --- a/packages/vitest/src/node/config/resolveConfig.ts +++ b/packages/vitest/src/node/config/resolveConfig.ts @@ -275,19 +275,41 @@ export function resolveConfig( // Browser-mode "Playwright + Chromium" only features: if (browser.enabled && !playwrightChromiumOnly) { - const browserConfig = { - browser: { - provider: browser.provider, - name: browser.name, - instances: browser.instances?.map(i => ({ browser: i.browser })), - }, - } + const browserConfig = ` +{ + browser: { + provider: ${browser.provider?.name || 'preview'}(), + instances: [ + ${(browser.instances || []).map(i => `{ browser: '${i.browser}' }`).join(',\n ')} + ], + }, +} + `.trim() + + const correctExample = ` +{ + browser: { + provider: playwright(), + instances: [ + { browser: 'chromium' } + ], + }, +} + `.trim() if (resolved.coverage.enabled && resolved.coverage.provider === 'v8') { + const coverageExample = ` +{ + coverage: { + provider: 'istanbul', + }, +} + `.trim() + throw new Error( - `@vitest/coverage-v8 does not work with\n${JSON.stringify(browserConfig, null, 2)}\n` - + `\nUse either:\n${JSON.stringify({ browser: { provider: 'playwright', instances: [{ browser: 'chromium' }] } }, null, 2)}` - + `\n\n...or change your coverage provider to:\n${JSON.stringify({ coverage: { provider: 'istanbul' } }, null, 2)}\n`, + `@vitest/coverage-v8 does not work with\n${browserConfig}\n` + + `\nUse either:\n${correctExample}` + + `\n\n...or change your coverage provider to:\n${coverageExample}\n`, ) } @@ -295,8 +317,8 @@ export function resolveConfig( const inspectOption = `--inspect${resolved.inspectBrk ? '-brk' : ''}` throw new Error( - `${inspectOption} does not work with\n${JSON.stringify(browserConfig, null, 2)}\n` - + `\nUse either:\n${JSON.stringify({ browser: { provider: 'playwright', instances: [{ browser: 'chromium' }] } }, null, 2)}` + `${inspectOption} does not work with\n${browserConfig}\n` + + `\nUse either:\n${correctExample}` + `\n\n...or disable ${inspectOption}\n`, ) } @@ -712,7 +734,27 @@ export function resolveConfig( resolved.browser.screenshotDirectory, ) } - const isPreview = resolved.browser.provider === 'preview' + + resolved.browser.viewport ??= {} as any + resolved.browser.viewport.width ??= 414 + resolved.browser.viewport.height ??= 896 + + resolved.browser.locators ??= {} as any + resolved.browser.locators.testIdAttribute ??= 'data-testid' + + if (resolved.browser.enabled && stdProvider === 'stackblitz') { + resolved.browser.provider = undefined // reset to "preview" + } + + if (typeof resolved.browser.provider === 'string') { + const source = `@vitest/browser/providers/${resolved.browser.provider}` + throw new TypeError( + 'The `browser.provider` configuration was changed to accept a factory instead of a string. ' + + `Add an import of "${resolved.browser.provider}" from "${source}" instead. See: https://vitest.dev/guide/browser/config#provider`, + ) + } + + const isPreview = resolved.browser.provider?.name === 'preview' if (isPreview && resolved.browser.screenshotFailures === true) { console.warn(c.yellow( [ @@ -727,17 +769,6 @@ export function resolveConfig( resolved.browser.screenshotFailures ??= !isPreview && !resolved.browser.ui } - resolved.browser.viewport ??= {} as any - resolved.browser.viewport.width ??= 414 - resolved.browser.viewport.height ??= 896 - - resolved.browser.locators ??= {} as any - resolved.browser.locators.testIdAttribute ??= 'data-testid' - - if (resolved.browser.enabled && stdProvider === 'stackblitz') { - resolved.browser.provider = 'preview' - } - resolved.browser.api = resolveApiServerConfig( resolved.browser, defaultBrowserPort, @@ -804,7 +835,7 @@ export function resolveCoverageReporters(configReporters: NonNullable { + const provider = project.browser?.provider return { name: browser.name, headless: browser.headless, @@ -149,9 +150,9 @@ export function serializeConfig(project: TestProject): SerializedConfig { locators: { testIdAttribute: browser.locators.testIdAttribute, }, - providerOptions: browser.provider === 'playwright' + providerOptions: provider?.name === 'playwright' ? { - actionTimeout: (browser.providerOptions as any)?.context?.actionTimeout, + actionTimeout: (provider as any)?.options?.actionTimeout, } : {}, trackUnhandledErrors: browser.trackUnhandledErrors ?? true, diff --git a/packages/vitest/src/node/plugins/workspace.ts b/packages/vitest/src/node/plugins/workspace.ts index 658ff0916..e3210e914 100644 --- a/packages/vitest/src/node/plugins/workspace.ts +++ b/packages/vitest/src/node/plugins/workspace.ts @@ -1,6 +1,6 @@ import type { UserConfig as ViteConfig, Plugin as VitePlugin } from 'vite' import type { TestProject } from '../project' -import type { ResolvedConfig, TestProjectInlineConfiguration } from '../types/config' +import type { BrowserConfigOptions, ResolvedConfig, TestProjectInlineConfiguration } from '../types/config' import { existsSync, readFileSync } from 'node:fs' import { deepMerge } from '@vitest/utils' import { basename, dirname, relative, resolve } from 'pathe' @@ -66,7 +66,7 @@ export function WorkspaceVitestPlugin( const isBrowserEnabled = isUserBrowserEnabled ?? (viteConfig.test?.browser && project.vitest._cliOptions.browser?.enabled) // keep project names to potentially filter it out const workspaceNames = [name] - const browser = viteConfig.test!.browser || {} + const browser = (viteConfig.test!.browser || {}) as BrowserConfigOptions if (isBrowserEnabled && browser.name && !browser.instances?.length) { // vitest injects `instances` in this case later on workspaceNames.push(name ? `${name} (${browser.name})` : browser.name) diff --git a/packages/vitest/src/node/projects/resolveProjects.ts b/packages/vitest/src/node/projects/resolveProjects.ts index d21626452..5f596fed8 100644 --- a/packages/vitest/src/node/projects/resolveProjects.ts +++ b/packages/vitest/src/node/projects/resolveProjects.ts @@ -219,12 +219,6 @@ export async function resolveBrowserProjects( return } - if (project.config.browser.providerOptions) { - vitest.logger.warn( - withLabel('yellow', 'Vitest', `"providerOptions"${originalName ? ` in "${originalName}" project` : ''} is ignored because it's overridden by the configs. To hide this warning, remove the "providerOptions" property from the browser configuration.`), - ) - } - filteredInstances.forEach((config, index) => { const browser = config.browser if (!browser) { @@ -300,6 +294,7 @@ function cloneConfig(project: TestProject, { browser, ...config }: BrowserInstan // @ts-expect-error remove just in case browser: _browser, name, + provider, ...overrideConfig } = config const currentConfig = project.config.browser @@ -318,9 +313,9 @@ function cloneConfig(project: TestProject, { browser, ...config }: BrowserInstan screenshotDirectory: screenshotDirectory ?? currentConfig.screenshotDirectory, screenshotFailures: screenshotFailures ?? currentConfig.screenshotFailures, headless: headless ?? currentConfig.headless, + provider: provider ?? currentConfig.provider, name: browser, - providerOptions: config, - instances: undefined, // projects cannot spawn more configs + instances: [], // projects cannot spawn more configs }, // If there is no include or exclude or includeSource pattern in browser.instances[], we should use the that's pattern from the parent project include: (overrideConfig.include && overrideConfig.include.length > 0) ? [] : clonedConfig.include, diff --git a/packages/vitest/src/node/types/browser.ts b/packages/vitest/src/node/types/browser.ts index 74ee9c74f..65e881599 100644 --- a/packages/vitest/src/node/types/browser.ts +++ b/packages/vitest/src/node/types/browser.ts @@ -7,11 +7,6 @@ import type { BrowserTesterOptions } from '../../types/browser' import type { TestProject } from '../project' import type { ApiConfig, ProjectConfig } from './config' -export interface BrowserProviderInitializationOptions { - browser: string - options?: BrowserProviderOptions -} - export interface CDPSession { send: (method: string, params?: Record) => Promise on: (event: string, listener: (...args: unknown[]) => void) => void @@ -25,6 +20,12 @@ export interface BrowserModuleMocker { clear: (sessionId: string) => Promise } +export interface BrowserProviderOption { + name: string + supportedBrowser?: ReadonlyArray + factory: (project: TestProject) => BrowserProvider +} + export interface BrowserProvider { name: string mocker?: BrowserModuleMocker @@ -32,24 +33,12 @@ export interface BrowserProvider { * @experimental opt-in into file parallelisation */ supportsParallelism: boolean - getSupportedBrowsers: () => readonly string[] getCommandsContext: (sessionId: string) => Record - openPage: (sessionId: string, url: string, beforeNavigate?: () => Promise) => Promise + openPage: (sessionId: string, url: string) => Promise getCDPSession?: (sessionId: string) => Promise close: () => Awaitable - // eslint-disable-next-line ts/method-signature-style -- we want to allow extended options - initialize( - ctx: TestProject, - options: BrowserProviderInitializationOptions - ): Awaitable -} - -export interface BrowserProviderModule { - new (): BrowserProvider } -export interface BrowserProviderOptions {} - export type BrowserBuiltinProvider = 'webdriverio' | 'playwright' | 'preview' type UnsupportedProperties @@ -71,7 +60,7 @@ type UnsupportedProperties | 'benchmark' | 'name' -export interface BrowserInstanceOption extends BrowserProviderOptions, +export interface BrowserInstanceOption extends Omit, Pick< BrowserConfigOptions, @@ -81,6 +70,7 @@ export interface BrowserInstanceOption extends BrowserProviderOptions, | 'testerHtmlPath' | 'screenshotDirectory' | 'screenshotFailures' + | 'provider' > { /** * Name of the browser @@ -101,33 +91,19 @@ export interface BrowserConfigOptions { /** * Name of the browser * @deprecated use `instances` instead. if both are defined, this will filter `instances` by name. + * @internal */ name?: string /** * Configurations for different browser setups */ - instances?: BrowserInstanceOption[] + instances: BrowserInstanceOption[] /** * Browser provider - * - * @default 'preview' - */ - provider?: BrowserBuiltinProvider | (string & {}) - - /** - * Options that are passed down to a browser provider. - * To support type hinting, add one of the types to your tsconfig.json "compilerOptions.types" field: - * - * - for webdriverio: `@vitest/browser/providers/webdriverio` - * - for playwright: `@vitest/browser/providers/playwright` - * - * @example - * { playwright: { launch: { devtools: true } } - * @deprecated use `instances` instead */ - providerOptions?: BrowserProviderOptions + provider?: BrowserProviderOption /** * enable headless mode @@ -328,7 +304,6 @@ export interface BrowserScript { export interface ResolvedBrowserOptions extends BrowserConfigOptions { name: string - providerOptions?: BrowserProviderOptions enabled: boolean headless: boolean isolate: boolean diff --git a/packages/vitest/src/public/node.ts b/packages/vitest/src/public/node.ts index e8d55e3a8..0f2cfc903 100644 --- a/packages/vitest/src/public/node.ts +++ b/packages/vitest/src/public/node.ts @@ -67,9 +67,7 @@ export type { BrowserModuleMocker, BrowserOrchestrator, BrowserProvider, - BrowserProviderInitializationOptions, - BrowserProviderModule, - BrowserProviderOptions, + BrowserProviderOption, BrowserScript, BrowserServerState, BrowserServerStateSession, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 310b17823..5e9dd7b17 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -478,15 +478,6 @@ importers: '@vitest/runner': specifier: workspace:* version: link:../runner - '@vitest/ui': - specifier: workspace:* - version: link:../ui - '@vitest/ws-client': - specifier: workspace:* - version: link:../ws-client - '@wdio/protocols': - specifier: ^9.16.2 - version: 9.16.2 '@wdio/types': specifier: ^9.16.2 version: 9.16.2 @@ -505,18 +496,12 @@ importers: pathe: specifier: 'catalog:' version: 2.0.3 - periscopic: - specifier: ^4.0.2 - version: 4.0.2 playwright: specifier: ^1.54.1 version: 1.54.1 playwright-core: specifier: ^1.54.1 version: 1.54.1 - safaridriver: - specifier: ^1.0.0 - version: 1.0.0 vitest: specifier: workspace:* version: link:../vitest @@ -6343,9 +6328,6 @@ packages: is-reference@1.2.1: resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} - is-reference@3.0.2: - resolution: {integrity: sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==} - is-regex@1.1.4: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} @@ -7364,9 +7346,6 @@ packages: perfect-debounce@1.0.0: resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} - periscopic@4.0.2: - resolution: {integrity: sha512-sqpQDUy8vgB7ycLkendSKS6HnVz1Rneoc3Rc+ZBUCe2pbqlVuCC5vF52l0NJ1aiMg/r1qfYF9/myz8CZeI2rjA==} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -9133,9 +9112,6 @@ packages: resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==} engines: {node: '>=18'} - zimmerframe@1.1.0: - resolution: {integrity: sha512-+AmV37r9NPUy7KcuG0Fde9AAFSD88kN5pnqvD7Pkp5WLLK0jct7hAtIDXXFDCRk3l5Mc1r2Sth3gfP2ZLE+/Qw==} - zip-stream@6.0.1: resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} engines: {node: '>= 14'} @@ -14314,10 +14290,6 @@ snapshots: dependencies: '@types/estree': 1.0.8 - is-reference@3.0.2: - dependencies: - '@types/estree': 1.0.7 - is-regex@1.1.4: dependencies: call-bind: 1.0.7 @@ -15546,12 +15518,6 @@ snapshots: perfect-debounce@1.0.0: {} - periscopic@4.0.2: - dependencies: - '@types/estree': 1.0.7 - is-reference: 3.0.2 - zimmerframe: 1.1.0 - picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -17607,8 +17573,6 @@ snapshots: yoctocolors@2.1.1: {} - zimmerframe@1.1.0: {} - zip-stream@6.0.1: dependencies: archiver-utils: 5.0.2 diff --git a/test/browser/fixtures/browser-crash/vitest.config.ts b/test/browser/fixtures/browser-crash/vitest.config.ts index 099e62941..4aaf57d98 100644 --- a/test/browser/fixtures/browser-crash/vitest.config.ts +++ b/test/browser/fixtures/browser-crash/vitest.config.ts @@ -17,8 +17,7 @@ const forceCrash: BrowserCommand<[]> = async (context) => { throw new Error(`Browser crash not supported for ${browser}`) } if (context.provider.name === 'webdriverio') { - // @ts-expect-error not typed - const browser = context.browser as any + const browser = context.browser const name = context.project.config.browser.name if (name === 'chrome') { await browser.url('chrome://crash') diff --git a/test/browser/fixtures/inspect/vitest.config.ts b/test/browser/fixtures/inspect/vitest.config.ts index 44f91bdd5..2034fe691 100644 --- a/test/browser/fixtures/inspect/vitest.config.ts +++ b/test/browser/fixtures/inspect/vitest.config.ts @@ -1,3 +1,4 @@ +import { playwright } from '@vitest/browser/providers/playwright'; import { defineConfig } from 'vitest/config'; export default defineConfig({ @@ -6,7 +7,7 @@ export default defineConfig({ watch: false, browser: { enabled: true, - provider: "playwright", + provider: playwright(), instances: [{ browser: "chromium" }], headless: true, }, diff --git a/test/browser/fixtures/inspect/vitest.config.with-workspace.ts b/test/browser/fixtures/inspect/vitest.config.with-workspace.ts index ec6d2c097..24ceb3a1d 100644 --- a/test/browser/fixtures/inspect/vitest.config.with-workspace.ts +++ b/test/browser/fixtures/inspect/vitest.config.with-workspace.ts @@ -1,3 +1,4 @@ +import { playwright } from '@vitest/browser/providers/playwright'; import { defineConfig } from "vitest/config"; export default defineConfig({ @@ -10,7 +11,7 @@ export default defineConfig({ test: { name: "Browser in workspace", browser: { - provider: "playwright", + provider: playwright(), enabled: true, headless: true, instances: [{ browser: "chromium" }] diff --git a/test/browser/fixtures/multiple-different-configs/vitest.config.js b/test/browser/fixtures/multiple-different-configs/vitest.config.js index 8c0598179..0bfb3d846 100644 --- a/test/browser/fixtures/multiple-different-configs/vitest.config.js +++ b/test/browser/fixtures/multiple-different-configs/vitest.config.js @@ -1,20 +1,19 @@ import { defineConfig } from 'vitest/config'; import { fileURLToPath } from 'node:url' - -const provider = process.env.PROVIDER || 'playwright' +import { provider } from '../../settings' export default defineConfig({ clearScreen: false, cacheDir: fileURLToPath(new URL("./node_modules/.vite", import.meta.url)), test: { browser: { - provider: provider, + provider, enabled: true, headless: true, screenshotFailures: false, instances: [ { - browser: provider === 'playwright' ? 'chromium' : 'chrome', + browser: provider.name === 'playwright' ? 'chromium' : 'chrome', testerHtmlPath: './customTester.html', provide: { providedVar: true, diff --git a/test/browser/fixtures/playwright-connect/vitest.config.js b/test/browser/fixtures/playwright-connect/vitest.config.js index e86783018..8093a358c 100644 --- a/test/browser/fixtures/playwright-connect/vitest.config.js +++ b/test/browser/fixtures/playwright-connect/vitest.config.js @@ -1,14 +1,11 @@ import { defineConfig } from 'vitest/config'; import { fileURLToPath } from 'node:url' -const provider = process.env.PROVIDER || 'playwright' - export default defineConfig({ clearScreen: false, cacheDir: fileURLToPath(new URL("./node_modules/.vite", import.meta.url)), test: { browser: { - provider: provider, enabled: true, headless: true, screenshotFailures: false, diff --git a/test/browser/fixtures/server-url/vitest.config.ts b/test/browser/fixtures/server-url/vitest.config.ts index ad483f64e..78c525250 100644 --- a/test/browser/fixtures/server-url/vitest.config.ts +++ b/test/browser/fixtures/server-url/vitest.config.ts @@ -2,7 +2,7 @@ import path from 'node:path' import { fileURLToPath } from 'node:url' import { defineConfig } from 'vitest/config' import basicSsl from '@vitejs/plugin-basic-ssl' -import { instances, provider } from '../../settings' +import { instances, providers, provider } from '../../settings' // test https by // TEST_HTTPS=1 pnpm test-fixtures --root fixtures/server-url @@ -10,12 +10,15 @@ import { instances, provider } from '../../settings' // ignore https errors due to self-signed certificate from plugin-basic-ssl // https://playwright.dev/docs/api/class-browser#browser-new-context-option-ignore-https-errors // https://webdriver.io/docs/configuration/#strictssl and acceptInsecureCerts in https://webdriver.io/docs/api/browser/#properties -const providerOptions = (function () { - switch (provider) { - case 'playwright': return { page: { ignoreHTTPSErrors: true } } - case 'webdriverio': return { strictSSL: false, capabilities: { acceptInsecureCerts: true } } +const configuredProvider = (function () { + switch (provider.name) { + case 'playwright': return providers.playwright() + case 'webdriverio': return providers.webdriverio({ strictSSL: false, capabilities: { acceptInsecureCerts: true } }) + default: { + throw new Error(`Invalid provider: ${provider.name}`) + } } -})() as any +})() export default defineConfig({ plugins: [ @@ -25,11 +28,8 @@ export default defineConfig({ browser: { api: process.env.TEST_HTTPS ? 51122 : 51133, enabled: true, - provider, - instances: instances.map(instance => ({ - ...instance, - ...providerOptions, - })), + provider: configuredProvider, + instances, }, }, // separate cacheDir from test/browser/vite.config.ts diff --git a/test/browser/fixtures/timeout/vitest.config.ts b/test/browser/fixtures/timeout/vitest.config.ts index b482d8bb4..215dd0052 100644 --- a/test/browser/fixtures/timeout/vitest.config.ts +++ b/test/browser/fixtures/timeout/vitest.config.ts @@ -1,19 +1,16 @@ import { fileURLToPath } from 'node:url' import { defineConfig } from 'vitest/config' -import { instances, provider } from '../../settings' +import { instances, provider, providers } from '../../settings' export default defineConfig({ cacheDir: fileURLToPath(new URL("./node_modules/.vite", import.meta.url)), test: { browser: { enabled: true, - provider, - instances: instances.map(instance => ({ - ...instance, - context: { - actionTimeout: 500, - }, - })), + provider: provider.name === 'playwright' + ? providers.playwright({ actionTimeout: 500 }) + : provider, + instances, }, expect: { poll: { diff --git a/test/browser/fixtures/worker/vitest.config.ts b/test/browser/fixtures/worker/vitest.config.ts index 563c59b40..52962b06a 100644 --- a/test/browser/fixtures/worker/vitest.config.ts +++ b/test/browser/fixtures/worker/vitest.config.ts @@ -1,9 +1,6 @@ import { fileURLToPath } from 'node:url' import { defineConfig } from 'vitest/config' - -const provider = process.env.PROVIDER || 'playwright' -const browser = - process.env.BROWSER || (provider === 'playwright' ? 'chromium' : 'chrome') +import { provider, instances } from '../../settings' export default defineConfig({ cacheDir: fileURLToPath(new URL("./node_modules/.vite", import.meta.url)), @@ -11,7 +8,7 @@ export default defineConfig({ browser: { enabled: true, provider, - instances: [{ browser }], + instances, }, }, }) diff --git a/test/browser/settings.ts b/test/browser/settings.ts index 19022e7ba..3da09eed6 100644 --- a/test/browser/settings.ts +++ b/test/browser/settings.ts @@ -1,7 +1,17 @@ import type { BrowserInstanceOption } from 'vitest/node' +import { playwright } from '@vitest/browser/providers/playwright' +import { preview } from '@vitest/browser/providers/preview' +import { webdriverio } from '@vitest/browser/providers/webdriverio' -export const provider = process.env.PROVIDER || 'playwright' -export const browser = process.env.BROWSER || (provider !== 'playwright' ? 'chromium' : 'chrome') +const providerName = (process.env.PROVIDER || 'playwright') as 'playwright' | 'webdriverio' | 'preview' +export const providers = { + playwright, + preview, + webdriverio, +} + +export const provider = providers[providerName]() +export const browser = process.env.BROWSER || (provider.name !== 'playwright' ? 'chromium' : 'chrome') const devInstances: BrowserInstanceOption[] = [ { browser }, @@ -20,6 +30,6 @@ const webdriverioInstances: BrowserInstanceOption[] = [ export const instances = process.env.BROWSER ? devInstances - : provider === 'playwright' + : provider.name === 'playwright' ? playwrightInstances : webdriverioInstances diff --git a/test/browser/specs/multiple-different-configs.test.ts b/test/browser/specs/multiple-different-configs.test.ts index 48cd36e4a..eb25d39c3 100644 --- a/test/browser/specs/multiple-different-configs.test.ts +++ b/test/browser/specs/multiple-different-configs.test.ts @@ -2,7 +2,7 @@ import { expect, test } from 'vitest' import { provider } from '../settings' import { runBrowserTests } from './utils' -test.runIf(provider === 'playwright')('[playwright] runs multiple different configurations correctly', async () => { +test.runIf(provider.name === 'playwright')('[playwright] runs multiple different configurations correctly', async () => { const { stdout, exitCode, stderr } = await runBrowserTests({ root: './fixtures/multiple-different-configs', }) @@ -13,7 +13,7 @@ test.runIf(provider === 'playwright')('[playwright] runs multiple different conf expect(stdout).toContain('[firefox] HTML_INJECTED_VAR is undefined') }) -test.runIf(provider === 'webdriverio')('[webdriverio] runs multiple different configurations correctly', async () => { +test.runIf(provider.name === 'webdriverio')('[webdriverio] runs multiple different configurations correctly', async () => { const { stdout, exitCode, stderr } = await runBrowserTests({ root: './fixtures/multiple-different-configs', }) diff --git a/test/browser/specs/playwright-connect.test.ts b/test/browser/specs/playwright-connect.test.ts index ec5130c2e..1d3cf6555 100644 --- a/test/browser/specs/playwright-connect.test.ts +++ b/test/browser/specs/playwright-connect.test.ts @@ -1,9 +1,10 @@ +import { playwright } from '@vitest/browser/providers/playwright' import { chromium } from 'playwright' import { expect, test } from 'vitest' import { provider } from '../settings' import { runBrowserTests } from './utils' -test.runIf(provider === 'playwright')('[playwright] runs in connect mode', async () => { +test.runIf(provider.name === 'playwright')('[playwright] runs in connect mode', async () => { const browserServer = await chromium.launchServer() const wsEndpoint = browserServer.wsEndpoint() @@ -14,9 +15,11 @@ test.runIf(provider === 'playwright')('[playwright] runs in connect mode', async { browser: 'chromium', name: 'chromium', - connect: { - wsEndpoint, - }, + provider: playwright({ + connectOptions: { + wsEndpoint, + }, + }), }, ], }, @@ -29,7 +32,7 @@ test.runIf(provider === 'playwright')('[playwright] runs in connect mode', async expect(stderr).toBe('') }) -test.runIf(provider === 'playwright')('[playwright] warns if both connect and launch mode are configured', async () => { +test.runIf(provider.name === 'playwright')('[playwright] warns if both connect and launch mode are configured', async () => { const browserServer = await chromium.launchServer() const wsEndpoint = browserServer.wsEndpoint() @@ -40,10 +43,12 @@ test.runIf(provider === 'playwright')('[playwright] warns if both connect and la { browser: 'chromium', name: 'chromium', - connect: { - wsEndpoint, - }, - launch: {}, + provider: playwright({ + connectOptions: { + wsEndpoint, + }, + launchOptions: {}, + }), }, ], }, diff --git a/test/browser/specs/runner.test.ts b/test/browser/specs/runner.test.ts index 5a0d17b01..b3531742e 100644 --- a/test/browser/specs/runner.test.ts +++ b/test/browser/specs/runner.test.ts @@ -226,7 +226,7 @@ test('user-event', async () => { const { stdout, stderr } = await runBrowserTests({ root: './fixtures/user-event', }) - if (provider !== 'webdriverio') { + if (provider.name !== 'webdriverio') { expect(stderr).toBe('') } onTestFailed(() => console.error(stderr)) @@ -244,11 +244,11 @@ test('timeout settings', async () => { root: './fixtures/timeout', }) expect(stderr).toContain('Matcher did not succeed in time.') - if (provider === 'playwright') { + if (provider.name === 'playwright') { expect(stderr).toContain('locator.click: Timeout 500ms exceeded.') expect(stderr).toContain('locator.click: Timeout 345ms exceeded.') } - if (provider === 'webdriverio') { + if (provider.name === 'webdriverio') { expect(stderr).toContain('Cannot find element with locator') } }) @@ -290,7 +290,7 @@ test('in-source tests run correctly when filtered', async () => { expect(stdout).toContain(`Tests ${instances.length} passed`) }) -test.runIf(provider === 'playwright')('timeout hooks', async () => { +test.runIf(provider.name === 'playwright')('timeout hooks', async () => { const { stderr } = await runBrowserTests({ root: './fixtures/timeout-hooks', }) diff --git a/test/browser/specs/to-match-screenshot.test.ts b/test/browser/specs/to-match-screenshot.test.ts index 250392c16..887357abc 100644 --- a/test/browser/specs/to-match-screenshot.test.ts +++ b/test/browser/specs/to-match-screenshot.test.ts @@ -2,6 +2,7 @@ import type { ViteUserConfig } from 'vitest/config.js' import type { TestFsStructure } from '../../test-utils' import { platform } from 'node:os' import { resolve } from 'node:path' +import { playwright } from '@vitest/browser/providers/playwright' import { describe, expect, test } from 'vitest' import { runVitestCli, useFS } from '../../test-utils' import { extractToMatchScreenshotPaths } from '../fixtures/expect-dom/utils' @@ -40,7 +41,7 @@ export async function runInlineTests( browser: { enabled: true, screenshotFailures: false, - provider: 'playwright', + provider: playwright(), headless: true, instances: [{ browser }], }, diff --git a/test/browser/specs/worker.test.ts b/test/browser/specs/worker.test.ts index 060c4dc4e..4f7c54c64 100644 --- a/test/browser/specs/worker.test.ts +++ b/test/browser/specs/worker.test.ts @@ -2,9 +2,10 @@ import { expect, test } from 'vitest' import { runBrowserTests } from './utils' test('worker', async () => { - const { ctx } = await runBrowserTests({ + const { ctx, stderr } = await runBrowserTests({ root: './fixtures/worker', }) + expect(stderr).toBe('') expect(Object.fromEntries(ctx.state.getFiles().map(f => [f.name, f.result.state]))).toMatchInlineSnapshot(` { "src/basic.test.ts": "pass", diff --git a/test/browser/vitest.config.mts b/test/browser/vitest.config.mts index 2f5d7a033..bb01b2175 100644 --- a/test/browser/vitest.config.mts +++ b/test/browser/vitest.config.mts @@ -2,12 +2,20 @@ import type { BrowserCommand, BrowserInstanceOption } from 'vitest/node' import { dirname, resolve } from 'node:path' import { fileURLToPath } from 'node:url' import * as util from 'node:util' +import { playwright } from '@vitest/browser/providers/playwright' +import { preview } from '@vitest/browser/providers/preview' +import { webdriverio } from '@vitest/browser/providers/webdriverio' import { defineConfig } from 'vitest/config' const dir = dirname(fileURLToPath(import.meta.url)) -const provider = process.env.PROVIDER || 'playwright' -const browser = process.env.BROWSER || (provider === 'playwright' ? 'chromium' : 'chrome') +const providerName = process.env.PROVIDER || 'playwright' +const browser = process.env.BROWSER || (providerName === 'playwright' ? 'chromium' : 'chrome') +const provider = { + playwright, + preview, + webdriverio, +}[providerName] const myCustomCommand: BrowserCommand<[arg1: string, arg2: string]> = ({ testPath }, arg1, arg2) => { return { testPath, arg1, arg2 } @@ -57,10 +65,10 @@ export default defineConfig({ headless: false, instances: process.env.BROWSER ? devInstances - : provider === 'playwright' + : providerName === 'playwright' ? playwrightInstances : webdriverioInstances, - provider, + provider: provider(), // isolate: false, testerHtmlPath: './custom-tester.html', orchestratorScripts: [ diff --git a/test/cli/fixtures/browser-multiple/vitest.config.ts b/test/cli/fixtures/browser-multiple/vitest.config.ts index 3703112fe..1df074e7b 100644 --- a/test/cli/fixtures/browser-multiple/vitest.config.ts +++ b/test/cli/fixtures/browser-multiple/vitest.config.ts @@ -1,3 +1,4 @@ +import { playwright } from '@vitest/browser/providers/playwright'; import { resolve } from 'pathe'; import { defineConfig } from 'vitest/config'; @@ -13,7 +14,7 @@ export default defineConfig({ browser: { enabled: true, instances: [{ browser: 'chromium' }], - provider: 'playwright', + provider: playwright(), headless: true, } } @@ -27,7 +28,7 @@ export default defineConfig({ browser: { enabled: true, instances: [{ browser: 'chromium' }], - provider: 'playwright', + provider: playwright(), headless: true, } } diff --git a/test/cli/fixtures/config-loader/browser/vitest.config.ts b/test/cli/fixtures/config-loader/browser/vitest.config.ts index 7a387aaf2..4a0041e1b 100644 --- a/test/cli/fixtures/config-loader/browser/vitest.config.ts +++ b/test/cli/fixtures/config-loader/browser/vitest.config.ts @@ -1,11 +1,12 @@ import { defineConfig } from "vitest/config" import "@test/test-dep-linked/ts"; +import { playwright } from '@vitest/browser/providers/playwright'; export default defineConfig({ test: { browser: { enabled: true, - provider: 'playwright', + provider: playwright(), headless: true, instances: [ { diff --git a/test/cli/fixtures/list/vitest.config.ts b/test/cli/fixtures/list/vitest.config.ts index 3c5732b42..487485c4e 100644 --- a/test/cli/fixtures/list/vitest.config.ts +++ b/test/cli/fixtures/list/vitest.config.ts @@ -1,3 +1,4 @@ +import { playwright } from '@vitest/browser/providers/playwright' import { fileURLToPath } from 'node:url' import { defineConfig } from 'vitest/config' @@ -7,7 +8,7 @@ export default defineConfig({ include: ['basic.test.ts', 'math.test.ts'], browser: { instances: [{ browser: 'chromium' }], - provider: 'playwright', + provider: playwright(), headless: true, api: 7523, } diff --git a/test/cli/fixtures/public-api/vitest.config.ts b/test/cli/fixtures/public-api/vitest.config.ts index 5dc5b97d2..3fe34057d 100644 --- a/test/cli/fixtures/public-api/vitest.config.ts +++ b/test/cli/fixtures/public-api/vitest.config.ts @@ -1,9 +1,10 @@ +import { playwright } from '@vitest/browser/providers/playwright' import { defineConfig } from 'vitest/config' export default defineConfig({ test: { browser: { - provider: 'playwright', + provider: playwright(), instances: [{ browser: 'chromium' }], headless: true, }, diff --git a/test/cli/test/annotations.test.ts b/test/cli/test/annotations.test.ts index 8f9f4d6b3..414878805 100644 --- a/test/cli/test/annotations.test.ts +++ b/test/cli/test/annotations.test.ts @@ -1,4 +1,5 @@ import type { TestAnnotation } from 'vitest' +import { playwright } from '@vitest/browser/providers/playwright' import { describe, expect, test } from 'vitest' import { runInlineTests } from '../../test-utils' @@ -36,7 +37,7 @@ describe('API', () => { name: 'browser', browser: { enabled: true, - provider: 'playwright', + provider: playwright(), headless: true, instances: [ { browser: 'chromium' }, diff --git a/test/cli/test/fails.test.ts b/test/cli/test/fails.test.ts index 4ddfd41ef..7efc4b4f9 100644 --- a/test/cli/test/fails.test.ts +++ b/test/cli/test/fails.test.ts @@ -1,6 +1,7 @@ import type { TestCase } from 'vitest/node' -import { resolve } from 'pathe' +import { playwright } from '@vitest/browser/providers/playwright' +import { resolve } from 'pathe' import { glob } from 'tinyglobby' import { expect, it } from 'vitest' import { runInlineTests, runVitest, ts } from '../../test-utils' @@ -113,7 +114,7 @@ it('prints a warning if the assertion is not awaited in the browser mode', async browser: { enabled: true, instances: [{ browser: 'chromium' }], - provider: 'playwright', + provider: playwright(), headless: true, }, }, diff --git a/test/cli/test/init.test.ts b/test/cli/test/init.test.ts index 7cd78339d..c2c9f89ef 100644 --- a/test/cli/test/init.test.ts +++ b/test/cli/test/init.test.ts @@ -54,12 +54,13 @@ test('initializes project', async () => { expect(await getFileContent('/vitest.browser.config.ts')).toMatchInlineSnapshot(` "import { defineConfig } from 'vitest/config' + import { preview } from '@vitest/browser/providers/preview' export default defineConfig({ test: { browser: { enabled: true, - provider: 'preview', + provider: preview(), instances: [ ], }, diff --git a/test/cli/test/scoped-fixtures.test.ts b/test/cli/test/scoped-fixtures.test.ts index 6da4dd840..40ce7785f 100644 --- a/test/cli/test/scoped-fixtures.test.ts +++ b/test/cli/test/scoped-fixtures.test.ts @@ -4,6 +4,7 @@ import type { TestAPI } from 'vitest' import type { ViteUserConfig } from 'vitest/config' import type { TestSpecification, TestUserConfig } from 'vitest/node' import type { TestFsStructure } from '../../test-utils' +import { playwright } from '@vitest/browser/providers/playwright' import { runInlineTests } from '../../test-utils' interface TestContext { @@ -551,7 +552,7 @@ describe.for([ name: 'core', browser: { enabled: true, - provider: 'playwright', + provider: playwright(), headless: true, instances: [ { browser: 'chromium', name: '' }, @@ -669,7 +670,7 @@ describe('browser tests', () => { test: { browser: { enabled: true, - provider: 'playwright', + provider: playwright(), headless: true, isolate: false, instances: [ diff --git a/test/config/fixtures/bail/vitest.config.ts b/test/config/fixtures/bail/vitest.config.ts index cc4be76a8..fd4743a03 100644 --- a/test/config/fixtures/bail/vitest.config.ts +++ b/test/config/fixtures/bail/vitest.config.ts @@ -29,6 +29,7 @@ export default defineConfig({ }, browser: { headless: true, + instances: [], }, }, }) diff --git a/test/config/fixtures/browser-custom-html/vitest.config.correct.ts b/test/config/fixtures/browser-custom-html/vitest.config.correct.ts index bef458c6a..cc6645a06 100644 --- a/test/config/fixtures/browser-custom-html/vitest.config.correct.ts +++ b/test/config/fixtures/browser-custom-html/vitest.config.correct.ts @@ -1,3 +1,4 @@ +import { playwright } from '@vitest/browser/providers/playwright'; import { defineConfig } from 'vitest/config'; export default defineConfig({ @@ -7,7 +8,7 @@ export default defineConfig({ instances: [{ browser: 'chromium' }], enabled: true, headless: true, - provider: 'playwright', + provider: playwright(), testerHtmlPath: './custom-html.html' }, }, diff --git a/test/config/fixtures/browser-custom-html/vitest.config.custom-transformIndexHtml.ts b/test/config/fixtures/browser-custom-html/vitest.config.custom-transformIndexHtml.ts index ccb683258..136f218a6 100644 --- a/test/config/fixtures/browser-custom-html/vitest.config.custom-transformIndexHtml.ts +++ b/test/config/fixtures/browser-custom-html/vitest.config.custom-transformIndexHtml.ts @@ -1,3 +1,4 @@ +import { playwright } from '@vitest/browser/providers/playwright'; import { defineConfig } from 'vitest/config'; export default defineConfig({ @@ -33,7 +34,7 @@ export default defineConfig({ instances: [{ browser: 'chromium' }], enabled: true, headless: true, - provider: 'playwright', + provider: playwright(), testerHtmlPath: './custom-html.html', }, }, diff --git a/test/config/fixtures/browser-custom-html/vitest.config.default-transformIndexHtml.ts b/test/config/fixtures/browser-custom-html/vitest.config.default-transformIndexHtml.ts index d47640dff..f8fe90543 100644 --- a/test/config/fixtures/browser-custom-html/vitest.config.default-transformIndexHtml.ts +++ b/test/config/fixtures/browser-custom-html/vitest.config.default-transformIndexHtml.ts @@ -1,3 +1,4 @@ +import { playwright } from '@vitest/browser/providers/playwright'; import { defineConfig } from 'vitest/config'; export default defineConfig({ @@ -33,7 +34,7 @@ export default defineConfig({ instances: [{ browser: 'chromium' }], enabled: true, headless: true, - provider: 'playwright', + provider: playwright(), }, }, }) \ No newline at end of file diff --git a/test/config/fixtures/browser-custom-html/vitest.config.error-hook.ts b/test/config/fixtures/browser-custom-html/vitest.config.error-hook.ts index 9fbd6a978..4ad8f42e4 100644 --- a/test/config/fixtures/browser-custom-html/vitest.config.error-hook.ts +++ b/test/config/fixtures/browser-custom-html/vitest.config.error-hook.ts @@ -1,3 +1,4 @@ +import { playwright } from '@vitest/browser/providers/playwright'; import { defineConfig } from 'vitest/config'; export default defineConfig({ @@ -15,7 +16,7 @@ export default defineConfig({ instances: [{ browser: 'chromium' }], enabled: true, headless: true, - provider: 'playwright', + provider: playwright(), testerHtmlPath: './custom-html.html' }, }, diff --git a/test/config/fixtures/browser-custom-html/vitest.config.non-existing.ts b/test/config/fixtures/browser-custom-html/vitest.config.non-existing.ts index cdedec59b..8a259fc9f 100644 --- a/test/config/fixtures/browser-custom-html/vitest.config.non-existing.ts +++ b/test/config/fixtures/browser-custom-html/vitest.config.non-existing.ts @@ -1,3 +1,4 @@ +import { playwright } from '@vitest/browser/providers/playwright'; import { defineConfig } from 'vitest/config'; export default defineConfig({ @@ -6,7 +7,7 @@ export default defineConfig({ instances: [{ browser: 'chromium' }], enabled: true, headless: true, - provider: 'playwright', + provider: playwright(), testerHtmlPath: './some-non-existing-path' }, }, diff --git a/test/config/fixtures/browser-no-config/vitest.config.ts b/test/config/fixtures/browser-no-config/vitest.config.ts index 35b54686a..742cc1027 100644 --- a/test/config/fixtures/browser-no-config/vitest.config.ts +++ b/test/config/fixtures/browser-no-config/vitest.config.ts @@ -4,6 +4,6 @@ export default defineConfig({ test: { browser: { headless: true, - }, + } as any, // testing that instances is required }, }) \ No newline at end of file diff --git a/test/config/test/bail.test.ts b/test/config/test/bail.test.ts index 349fcee11..62f509dca 100644 --- a/test/config/test/bail.test.ts +++ b/test/config/test/bail.test.ts @@ -1,5 +1,6 @@ import type { TestUserConfig } from 'vitest/node' +import { playwright } from '@vitest/browser/providers/playwright' import { expect, test } from 'vitest' import { runVitest } from '../../test-utils' @@ -15,7 +16,7 @@ if (process.platform !== 'win32') { { browser: { enabled: true, - provider: 'playwright', + provider: playwright(), headless: true, fileParallelism: false, instances: [ @@ -26,7 +27,7 @@ if (process.platform !== 'win32') { { browser: { enabled: true, - provider: 'playwright', + provider: playwright(), headless: true, fileParallelism: true, instances: [ diff --git a/test/config/test/browser-configs.test.ts b/test/config/test/browser-configs.test.ts index bacbd3bb8..2056b36ca 100644 --- a/test/config/test/browser-configs.test.ts +++ b/test/config/test/browser-configs.test.ts @@ -2,6 +2,7 @@ import type { ViteUserConfig } from 'vitest/config' import type { TestUserConfig, VitestOptions } from 'vitest/node' import type { TestFsStructure } from '../../test-utils' import crypto from 'node:crypto' +import { playwright } from '@vitest/browser/providers/playwright' import { resolve } from 'pathe' import { describe, expect, onTestFinished, test } from 'vitest' import { createVitest } from 'vitest/node' @@ -192,7 +193,7 @@ test('coverage provider v8 works correctly in browser mode if instances are filt }, browser: { enabled: true, - provider: 'playwright', + provider: playwright(), instances: [ { browser: 'chromium' }, { browser: 'firefox' }, @@ -214,7 +215,7 @@ test('coverage provider v8 works correctly in workspaced browser mode if instanc name: 'browser', browser: { enabled: true, - provider: 'playwright', + provider: playwright(), instances: [ { browser: 'chromium' }, { browser: 'firefox' }, @@ -317,7 +318,7 @@ test('filter for the global browser project includes all browser instances', asy name: 'myproject', browser: { enabled: true, - provider: 'playwright', + provider: playwright(), headless: true, instances: [ { browser: 'chromium' }, @@ -347,6 +348,7 @@ test('can enable browser-cli options for multi-project workspace', async () => { browser: { enabled: true, headless: true, + instances: [], }, }, { @@ -360,7 +362,7 @@ test('can enable browser-cli options for multi-project workspace', async () => { test: { browser: { enabled: true, - provider: 'playwright', + provider: playwright(), instances: [ { browser: 'chromium', name: 'browser' }, ], @@ -460,7 +462,7 @@ describe('[e2e] workspace configs are affected by the CLI options', () => { browser: { enabled: true, headless: true, - provider: 'playwright', + provider: playwright(), instances: [ { browser: 'chromium', @@ -513,7 +515,7 @@ describe('[e2e] workspace configs are affected by the CLI options', () => { browser: { enabled: true, headless: true, - provider: 'playwright', + provider: playwright(), instances: [ { browser: 'chromium', @@ -573,7 +575,7 @@ describe('[e2e] workspace configs are affected by the CLI options', () => { browser: { enabled: true, headless: true, - provider: 'playwright', + provider: playwright(), instances: [ { browser: 'chromium', @@ -613,7 +615,7 @@ describe('[e2e] workspace configs are affected by the CLI options', () => { test('correctly resolves extended project', async () => { const { stdout } = await getCliConfig({ browser: { - provider: 'playwright', + provider: playwright(), headless: true, instances: [ { browser: 'chromium' }, @@ -632,6 +634,7 @@ describe('[e2e] workspace configs are affected by the CLI options', () => { name: 'browser', browser: { enabled: true, + instances: [], }, }, }, @@ -666,7 +669,7 @@ describe('[e2e] workspace configs are affected by the CLI options', () => { test('correctly overrides extended project', async () => { const { stdout } = await getCliConfig({ browser: { - provider: 'playwright', + provider: playwright(), headless: true, instances: [ { browser: 'chromium' }, @@ -685,6 +688,7 @@ describe('[e2e] workspace configs are affected by the CLI options', () => { name: 'browser', browser: { enabled: true, + instances: [], }, }, }, @@ -720,7 +724,7 @@ describe('[e2e] workspace configs are affected by the CLI options', () => { const { stdout } = await getCliConfig({ browser: { enabled: false, - provider: 'playwright', + provider: playwright(), headless: true, instances: [ { browser: 'chromium' }, diff --git a/test/config/test/failures.test.ts b/test/config/test/failures.test.ts index bac6c719a..8b8f01854 100644 --- a/test/config/test/failures.test.ts +++ b/test/config/test/failures.test.ts @@ -1,12 +1,19 @@ import type { UserConfig as ViteUserConfig } from 'vite' import type { TestUserConfig } from 'vitest/node' import type { VitestRunnerCLIOptions } from '../../test-utils' +import { playwright } from '@vitest/browser/providers/playwright' +import { preview } from '@vitest/browser/providers/preview' +import { webdriverio } from '@vitest/browser/providers/webdriverio' import { normalize, resolve } from 'pathe' import { beforeEach, expect, test } from 'vitest' import { version } from 'vitest/package.json' import * as testUtils from '../../test-utils' -const providers = ['playwright', 'webdriverio', 'preview'] as const +const providers = [ + playwright(), + webdriverio(), + preview(), +] as const const names = ['edge', 'chromium', 'webkit', 'chrome', 'firefox', 'safari'] as const const browsers = providers.map(provider => names.map(name => ({ name, provider }))).flat() @@ -75,7 +82,7 @@ test('inspect cannot be used with multi-threading', async () => { }) test('inspect in browser mode requires no-file-parallelism', async () => { - const { stderr } = await runVitest({ inspect: true, browser: { enabled: true, instances: [{ browser: 'chromium' }], provider: 'playwright' } }) + const { stderr } = await runVitest({ inspect: true, browser: { enabled: true, instances: [{ browser: 'chromium' }], provider: playwright() } }) expect(stderr).toMatch('Error: You cannot use --inspect without "--no-file-parallelism", "poolOptions.threads.singleThread" or "poolOptions.forks.singleFork"') }) @@ -87,7 +94,7 @@ test('inspect-brk cannot be used with multi processing', async () => { }) test('inspect-brk in browser mode requires no-file-parallelism', async () => { - const { stderr } = await runVitest({ inspectBrk: true, browser: { enabled: true, instances: [{ browser: 'chromium' }], provider: 'playwright' } }) + const { stderr } = await runVitest({ inspectBrk: true, browser: { enabled: true, instances: [{ browser: 'chromium' }], provider: playwright() } }) expect(stderr).toMatch('Error: You cannot use --inspect-brk without "--no-file-parallelism", "poolOptions.threads.singleThread" or "poolOptions.forks.singleFork"') }) @@ -97,7 +104,7 @@ test('inspect and --inspect-brk cannot be used when not playwright + chromium', const cli = `--inspect${option === 'inspectBrk' ? '-brk' : ''}` for (const { provider, name } of browsers) { - if (provider === 'playwright' && name === 'chromium') { + if (provider.name === 'playwright' && name === 'chromium') { continue } @@ -108,7 +115,7 @@ test('inspect and --inspect-brk cannot be used when not playwright + chromium', browser: { enabled: true, provider, - name, + instances: [{ browser: name }], }, }, }) @@ -116,22 +123,22 @@ test('inspect and --inspect-brk cannot be used when not playwright + chromium', expect(stderr).toMatch( `Error: ${cli} does not work with { - "browser": { - "provider": "${provider}", - "name": "${name}" - } + browser: { + provider: ${provider.name}(), + instances: [ + { browser: '${name}' } + ], + }, } Use either: { - "browser": { - "provider": "playwright", - "instances": [ - { - "browser": "chromium" - } - ] - } + browser: { + provider: playwright(), + instances: [ + { browser: 'chromium' } + ], + }, } ...or disable ${cli} @@ -141,60 +148,9 @@ Use either: } }) -test('v8 coverage provider throws when not playwright + chromium (browser.name)', async () => { +test('v8 coverage provider throws when not playwright + chromium', async () => { for (const { provider, name } of browsers) { - if (provider === 'playwright' && name === 'chromium') { - continue - } - - const { stderr } = await runVitest({}, { - test: { - coverage: { - enabled: true, - }, - browser: { - enabled: true, - provider, - name, - }, - }, - }) - - expect(stderr).toMatch( - `Error: @vitest/coverage-v8 does not work with -{ - "browser": { - "provider": "${provider}", - "name": "${name}" - } -} - -Use either: -{ - "browser": { - "provider": "playwright", - "instances": [ - { - "browser": "chromium" - } - ] - } -} - -...or change your coverage provider to: -{ - "coverage": { - "provider": "istanbul" - } -} -`, - ) - } -}) - -test('v8 coverage provider throws when not playwright + chromium (browser.instances)', async () => { - for (const { provider, name } of browsers) { - if (provider === 'playwright' && name === 'chromium') { + if (provider.name === 'playwright' && name === 'chromium') { continue } @@ -214,33 +170,29 @@ test('v8 coverage provider throws when not playwright + chromium (browser.instan expect(stderr).toMatch( `Error: @vitest/coverage-v8 does not work with { - "browser": { - "provider": "${provider}", - "instances": [ - { - "browser": "${name}" - } - ] - } + browser: { + provider: ${provider.name}(), + instances: [ + { browser: '${name}' } + ], + }, } Use either: { - "browser": { - "provider": "playwright", - "instances": [ - { - "browser": "chromium" - } - ] - } + browser: { + provider: playwright(), + instances: [ + { browser: 'chromium' } + ], + }, } ...or change your coverage provider to: { - "coverage": { - "provider": "istanbul" - } + coverage: { + provider: 'istanbul', + }, } `, ) @@ -256,7 +208,7 @@ test('v8 coverage provider throws when using chromium and other non-chromium bro browser: { enabled: true, headless: true, - provider: 'playwright', + provider: playwright(), instances: [ { browser: 'chromium' }, { browser: 'firefox' }, @@ -269,39 +221,31 @@ test('v8 coverage provider throws when using chromium and other non-chromium bro expect(stderr).toMatch( `Error: @vitest/coverage-v8 does not work with { - "browser": { - "provider": "playwright", - "instances": [ - { - "browser": "chromium" - }, - { - "browser": "firefox" - }, - { - "browser": "webkit" - } - ] - } + browser: { + provider: playwright(), + instances: [ + { browser: 'chromium' }, + { browser: 'firefox' }, + { browser: 'webkit' } + ], + }, } Use either: { - "browser": { - "provider": "playwright", - "instances": [ - { - "browser": "chromium" - } - ] - } + browser: { + provider: playwright(), + instances: [ + { browser: 'chromium' } + ], + }, } ...or change your coverage provider to: { - "coverage": { - "provider": "istanbul" - } + coverage: { + provider: 'istanbul', + }, }`, ) }) @@ -315,7 +259,7 @@ test('v8 coverage provider cannot be used in workspace without playwright + chro name: 'Browser project', browser: { enabled: true, - provider: 'webdriverio', + provider: webdriverio(), instances: [{ browser: 'chrome' }], }, }, @@ -325,14 +269,12 @@ test('v8 coverage provider cannot be used in workspace without playwright + chro expect(stderr).toMatch( `Error: @vitest/coverage-v8 does not work with { - "browser": { - "provider": "webdriverio", - "instances": [ - { - "browser": "chrome" - } - ] - } + browser: { + provider: webdriverio(), + instances: [ + { browser: 'chrome' } + ], + }, }`, ) }) @@ -451,7 +393,7 @@ test('browser.instances is empty', async () => { test: { browser: { enabled: true, - provider: 'playwright', + provider: playwright(), instances: [], }, }, @@ -483,7 +425,7 @@ test('browser.name filters all browser.instances are required', async () => { browser: { enabled: true, name: 'chromium', - provider: 'playwright', + provider: playwright(), instances: [ { browser: 'firefox' }, ], @@ -498,7 +440,7 @@ test('browser.instances throws an error if no custom name is provided', async () test: { browser: { enabled: true, - provider: 'playwright', + provider: playwright(), instances: [ { browser: 'firefox' }, { browser: 'firefox' }, @@ -515,7 +457,7 @@ test('browser.instances throws an error if no custom name is provided, but the c name: 'custom', browser: { enabled: true, - provider: 'playwright', + provider: playwright(), instances: [ { browser: 'firefox' }, { browser: 'firefox' }, @@ -535,7 +477,7 @@ test('throws an error if name conflicts with a workspace name', async () => { test: { browser: { enabled: true, - provider: 'playwright', + provider: playwright(), instances: [ { browser: 'firefox' }, ], @@ -553,7 +495,7 @@ test('throws an error if several browsers are headed in nonTTY mode', async () = test: { browser: { enabled: true, - provider: 'playwright', + provider: playwright(), headless: false, instances: [ { browser: 'chromium' }, diff --git a/test/coverage-test/test/isolation.test.ts b/test/coverage-test/test/isolation.test.ts index 9b53de697..beb625c58 100644 --- a/test/coverage-test/test/isolation.test.ts +++ b/test/coverage-test/test/isolation.test.ts @@ -27,6 +27,7 @@ for (const isolate of [true, false]) { browser: { isolate, + instances: [], }, }) diff --git a/test/coverage-test/utils.ts b/test/coverage-test/utils.ts index 57c467dc4..764f13e2e 100644 --- a/test/coverage-test/utils.ts +++ b/test/coverage-test/utils.ts @@ -7,6 +7,7 @@ import { unlink } from 'node:fs/promises' import { resolve } from 'node:path' import { fileURLToPath } from 'node:url' import { stripVTControlCharacters } from 'node:util' +import { playwright } from '@vitest/browser/providers/playwright' import libCoverage from 'istanbul-lib-coverage' import { normalize } from 'pathe' import { vi, describe as vitestDescribe, test as vitestTest } from 'vitest' @@ -56,7 +57,7 @@ export async function runVitest(config: TestUserConfig, options = { throwOnError enabled: process.env.COVERAGE_BROWSER === 'true', headless: true, instances: [{ browser: 'chromium' }], - provider: 'playwright', + provider: playwright(), }, }, }) diff --git a/test/dts-playwright/vite.config.ts b/test/dts-playwright/vite.config.ts index 250f59b25..0c32e8096 100644 --- a/test/dts-playwright/vite.config.ts +++ b/test/dts-playwright/vite.config.ts @@ -1,24 +1,26 @@ +import { playwright } from '@vitest/browser/providers/playwright' import { defineConfig } from 'vitest/config' export default defineConfig({ test: { browser: { - provider: 'playwright', - providerOptions: { - launch: { + provider: playwright({ + launchOptions: { timeout: 1234, // @ts-expect-error test type error slowMo: 'wrong', }, - }, + }), instances: [ { browser: 'chromium', - launch: { - timeout: 1234, - // @ts-expect-error test type error - slowMo: 'wrong', - }, + provider: playwright({ + launchOptions: { + timeout: 1234, + // @ts-expect-error test type error + slowMo: 'wrong', + }, + }), }, ], }, diff --git a/test/watch/test/config-watching.test.ts b/test/watch/test/config-watching.test.ts index bbec2996d..9edc214e3 100644 --- a/test/watch/test/config-watching.test.ts +++ b/test/watch/test/config-watching.test.ts @@ -1,3 +1,4 @@ +import { playwright } from '@vitest/browser/providers/playwright' import { expect, test } from 'vitest' import { runInlineTests } from '../../test-utils' @@ -15,7 +16,7 @@ test('rerun stops the previous browser server and restarts multiple times withou browser: { enabled: true, instances: [{ browser: 'chromium' }], - provider: 'playwright', + provider: playwright(), headless: true, }, }, diff --git a/test/watch/test/file-watching.test.ts b/test/watch/test/file-watching.test.ts index 74af818c6..451ec6aa3 100644 --- a/test/watch/test/file-watching.test.ts +++ b/test/watch/test/file-watching.test.ts @@ -1,6 +1,7 @@ import { existsSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs' -import { afterEach, describe, expect, test } from 'vitest' +import { webdriverio } from '@vitest/browser/providers/webdriverio' +import { afterEach, describe, expect, test } from 'vitest' import * as testUtils from '../../test-utils' const sourceFile = 'fixtures/math.ts' @@ -193,7 +194,7 @@ describe('browser', () => { ...options, browser: { instances: [{ browser: 'chromium' }], - provider: 'webdriverio', + provider: webdriverio(), enabled: true, headless: true, }, diff --git a/test/workspaces-browser/space_browser/vitest.config.ts b/test/workspaces-browser/space_browser/vitest.config.ts index 5b64428f2..246901f82 100644 --- a/test/workspaces-browser/space_browser/vitest.config.ts +++ b/test/workspaces-browser/space_browser/vitest.config.ts @@ -1,3 +1,4 @@ +import { playwright } from '@vitest/browser/providers/playwright' import { defineProject } from 'vitest/config' export default defineProject({ @@ -6,7 +7,7 @@ export default defineProject({ enabled: true, instances: [{ browser: process.env.BROWSER || 'chromium' }], headless: true, - provider: process.env.PROVIDER || 'playwright', + provider: playwright(), }, }, }) diff --git a/test/workspaces-browser/vitest.config.ts b/test/workspaces-browser/vitest.config.ts index 0e75c4df0..b550364fc 100644 --- a/test/workspaces-browser/vitest.config.ts +++ b/test/workspaces-browser/vitest.config.ts @@ -1,3 +1,4 @@ +import { playwright } from '@vitest/browser/providers/playwright' import { defineConfig } from 'vitest/config' if (process.env.TEST_WATCH) { @@ -22,7 +23,7 @@ export default defineConfig({ enabled: true, instances: [{ browser: process.env.BROWSER || 'chromium' }], headless: true, - provider: process.env.PROVIDER || 'playwright', + provider: playwright(), }, alias: { 'test-alias-from-vitest': new URL('./space_browser_inline/test-alias-to.ts', import.meta.url).pathname,