feat!: move browser providers to @vitest/browser package (#4364)

This commit is contained in:
Vladimir 2023-10-27 09:02:11 +02:00 committed by GitHub
parent 2af2ba7a16
commit 5cdeb558c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 520 additions and 686 deletions

View File

@ -125,20 +125,23 @@ jobs:
- uses: browser-actions/setup-chrome@v1
- uses: browser-actions/setup-firefox@v1
- uses: browser-actions/setup-edge@v1
id: setup-edge
with:
edge-version: beta
edge-version: stable
- name: Install
run: pnpm i
- name: Install Playwright Dependencies
run: pnpx playwright install-deps
run: pnpx playwright install --with-deps
- name: Build
run: pnpm run build
- name: Test Browser (webdriverio)
run: pnpm run test:browser:webdriverio
env:
EDGEDRIVER_VERSION: ${{ steps.setup-edge.outputs.edge-version }}
- name: Test Browser (playwright)
run: pnpm run test:browser:playwright
@ -169,7 +172,7 @@ jobs:
run: pnpm i
- name: Install Playwright Dependencies
run: pnpx playwright install-deps
run: pnpx playwright install --with-deps
- name: Build
run: pnpm run build
@ -181,29 +184,3 @@ jobs:
run: pnpm run test:browser:playwright
env:
BROWSER: ${{ matrix.browser[1] }}
test-browser-safari:
runs-on: macos-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup-and-cache
with:
node-version: 20
- name: Install
run: sudo pnpm i --frozen-lockfile
- name: Build
run: sudo pnpm run build
- name: Enable
run: sudo safaridriver --enable
- name: Test Browser (webdriverio)
run: sudo BROWSER=safari pnpm run test:browser:webdriverio
- name: Test Browser (playwright)
run: sudo BROWSER=webkit pnpm run test:browser:playwright

View File

@ -1332,7 +1332,7 @@ Path to a provider that will be used when running browser tests. Vitest provides
export interface BrowserProvider {
name: string
getSupportedBrowsers(): readonly string[]
initialize(ctx: Vitest, options: { browser: string }): Awaitable<void>
initialize(ctx: Vitest, options: { browser: string; options?: BrowserProviderOptions }): Awaitable<void>
openPage(url: string): Awaitable<void>
close(): Awaitable<void>
}
@ -1342,6 +1342,42 @@ export interface BrowserProvider {
This is an advanced API for library authors. If you just need to run tests in a browser, use the [browser](/config/#browser) option.
:::
#### browser.providerOptions
- **Type:** `BrowserProviderOptions`
- **Version:** Since Vitest 1.0.0-beta.3
Options that will be passed down to provider when calling `provider.initialize`.
```ts
export default defineConfig({
test: {
browser: {
providerOptions: {
launch: {
devtools: true,
}
}
}
}
})
```
::: tip
To have a better type safety when using built-in providers, you can add one of these types (for provider that you are using) to your tsconfig's `compilerOptions.types` field:
```json
{
"compilerOptions": {
"types": [
"@vitest/browser/providers/webdriverio",
"@vitest/browser/providers/playwright"
]
}
}
```
:::
#### browser.slowHijackESM
- **Type:** `boolean`

View File

@ -7,9 +7,9 @@
"test:ui": "vite build && vitest --ui"
},
"devDependencies": {
"@playwright/test": "^1.28.0",
"@playwright/test": "^1.39.0",
"@vitest/ui": "latest",
"playwright": "^1.28.0",
"playwright": "^1.39.0",
"vite": "latest",
"vitest": "latest"
}

View File

@ -20,6 +20,16 @@
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
},
"./providers": {
"types": "./providers.d.ts",
"import": "./dist/providers.js"
},
"./providers/webdriverio": {
"types": "./dist/providers/webdriverio.d.ts"
},
"./providers/playwright": {
"types": "./dist/providers/playwright.d.ts"
},
"./*": "./*"
},
"main": "./dist/index.js",
@ -27,7 +37,7 @@
"types": "./dist/index.d.ts",
"files": [
"dist",
"stubs"
"providers"
],
"scripts": {
"build": "rimraf dist && pnpm build:node && pnpm build:client",
@ -39,7 +49,21 @@
"prepublishOnly": "pnpm build"
},
"peerDependencies": {
"vitest": "^1.0.0-0"
"playwright": "*",
"safaridriver": "*",
"vitest": "^1.0.0-0",
"webdriverio": "*"
},
"peerDependenciesMeta": {
"webdriverio": {
"optional": true
},
"safaridriver": {
"optional": true
},
"playwright": {
"optional": true
}
},
"dependencies": {
"estree-walker": "^3.0.3",
@ -52,7 +76,12 @@
"@vitest/runner": "workspace:*",
"@vitest/ui": "workspace:*",
"@vitest/ws-client": "workspace:*",
"@wdio/protocols": "^8.18.0",
"periscopic": "^3.1.0",
"vitest": "workspace:*"
"playwright": "^1.39.0",
"playwright-core": "^1.39.0",
"safaridriver": "^0.1.0",
"vitest": "workspace:*",
"webdriverio": "^8.20.0"
}
}

6
packages/browser/providers.d.ts vendored Normal file
View File

@ -0,0 +1,6 @@
import type { BrowserProvider } from 'vitest/nide'
declare var webdriverio: BrowserProvider
declare var playwright: BrowserProvider
export { webdriverio, playwright }

View File

@ -0,0 +1,8 @@
import type { Browser, LaunchOptions } from 'playwright'
declare module 'vitest/node' {
interface BrowserProviderOptions {
launch?: LaunchOptions
page?: Parameters<Browser['newPage']>[0]
}
}

View File

@ -0,0 +1,5 @@
import type { RemoteOptions } from 'webdriverio'
declare module 'vitest/node' {
interface BrowserProviderOptions extends RemoteOptions {}
}

View File

@ -8,6 +8,8 @@ import pkg from './package.json' assert { type: 'json' }
const external = [
...Object.keys(pkg.dependencies),
...Object.keys(pkg.peerDependencies || {}),
'vitest/node',
'vitest',
'worker_threads',
'node:worker_threads',
]
@ -23,11 +25,14 @@ const plugins = [
}),
]
const input = {
index: './src/node/index.ts',
providers: './src/node/providers/index.ts',
}
export default () => [
{
input: [
'./src/node/index.ts',
],
input,
output: {
dir: 'dist',
format: 'esm',
@ -36,7 +41,7 @@ export default () => [
plugins,
},
{
input: './src/node/index.ts',
input: input.index,
output: {
file: 'dist/index.d.ts',
format: 'esm',

View File

@ -70,6 +70,8 @@ async function defaultErrorReport(type: string, unhandledError: any) {
message: unhandledError.message,
stack: unhandledError.stack,
}
if (testId !== 'no-isolate')
error.VITEST_TEST_PATH = testId
await client.rpc.onUnhandledError(error, type)
await client.rpc.onDone(testId)
}
@ -81,7 +83,10 @@ let runningTests = false
async function reportUnexpectedError(rpc: typeof client.rpc, type: string, error: any) {
const { processError } = await importId('vitest/browser') as typeof import('vitest/browser')
await rpc.onUnhandledError(processError(error), type)
const processedError = processError(error)
if (testId !== 'no-isolate')
error.VITEST_TEST_PATH = testId
await rpc.onUnhandledError(processedError, type)
if (!runningTests)
await rpc.onDone(testId)
}

View File

@ -0,0 +1,5 @@
import { PlaywrightBrowserProvider } from './playwright'
import { WebdriverBrowserProvider } from './webdriver'
export const webdriverio = WebdriverBrowserProvider
export const playwright = PlaywrightBrowserProvider

View File

@ -0,0 +1,80 @@
import type { Browser, LaunchOptions, Page } from 'playwright'
import type { BrowserProvider, BrowserProviderInitializationOptions, WorkspaceProject } from 'vitest/node'
import { ensurePackageInstalled } from 'vitest/node'
type Awaitable<T> = T | PromiseLike<T>
export const playwrightBrowsers = ['firefox', 'webkit', 'chromium'] as const
export type PlaywrightBrowser = typeof playwrightBrowsers[number]
export interface PlaywrightProviderOptions extends BrowserProviderInitializationOptions {
browser: PlaywrightBrowser
}
export class PlaywrightBrowserProvider implements BrowserProvider {
public name = 'playwright'
private cachedBrowser: Browser | null = null
private cachedPage: Page | null = null
private browser!: PlaywrightBrowser
private ctx!: WorkspaceProject
private options?: {
launch?: LaunchOptions
page?: Parameters<Browser['newPage']>[0]
}
getSupportedBrowsers() {
return playwrightBrowsers
}
async initialize(ctx: WorkspaceProject, { browser, options }: PlaywrightProviderOptions) {
this.ctx = ctx
this.browser = browser
this.options = options as any
const root = this.ctx.config.root
if (!await ensurePackageInstalled('playwright', root))
throw new Error('Cannot find "playwright" package. Please install it manually.')
}
private async openBrowserPage() {
if (this.cachedPage)
return this.cachedPage
const options = this.ctx.config.browser
const playwright = await import('playwright')
const browser = await playwright[this.browser].launch({
...this.options?.launch,
headless: options.headless,
})
this.cachedBrowser = browser
this.cachedPage = await browser.newPage(this.options?.page)
this.cachedPage.on('close', () => {
browser.close()
})
return this.cachedPage
}
catchError(cb: (error: Error) => Awaitable<void>) {
this.cachedPage?.on('pageerror', cb)
return () => {
this.cachedPage?.off('pageerror', cb)
}
}
async openPage(url: string) {
const browserPage = await this.openBrowserPage()
await browserPage.goto(url)
}
async close() {
await this.cachedPage?.close()
await this.cachedBrowser?.close()
}
}

View File

@ -1,30 +1,34 @@
import type { Awaitable } from '@vitest/utils'
import type { BrowserProvider, BrowserProviderOptions } from '../../types/browser'
import { ensurePackageInstalled } from '../pkg'
import type { WorkspaceProject } from '../workspace'
import type { BrowserProvider, BrowserProviderInitializationOptions, WorkspaceProject } from 'vitest/node'
import { ensurePackageInstalled } from 'vitest/node'
import type { Browser, RemoteOptions } from 'webdriverio'
export const webdriverBrowsers = ['firefox', 'chrome', 'edge', 'safari'] as const
export type WebdriverBrowser = typeof webdriverBrowsers[number]
type Awaitable<T> = T | PromiseLike<T>
export interface WebdriverProviderOptions extends BrowserProviderOptions {
const webdriverBrowsers = ['firefox', 'chrome', 'edge', 'safari'] as const
type WebdriverBrowser = typeof webdriverBrowsers[number]
interface WebdriverProviderOptions extends BrowserProviderInitializationOptions {
browser: WebdriverBrowser
}
export class WebdriverBrowserProvider implements BrowserProvider {
public name = 'webdriverio'
private cachedBrowser: WebdriverIO.Browser | null = null
private cachedBrowser: Browser | null = null
private stopSafari: () => void = () => {}
private browser!: WebdriverBrowser
private ctx!: WorkspaceProject
private options?: RemoteOptions
getSupportedBrowsers() {
return webdriverBrowsers
}
async initialize(ctx: WorkspaceProject, { browser }: WebdriverProviderOptions) {
async initialize(ctx: WorkspaceProject, { browser, options }: WebdriverProviderOptions) {
this.ctx = ctx
this.browser = browser
this.options = options as RemoteOptions
const root = this.ctx.config.root
@ -42,6 +46,9 @@ export class WebdriverBrowserProvider implements BrowserProvider {
const options = this.ctx.config.browser
if (this.browser === 'safari') {
if (options.headless)
throw new Error('You\'ve enabled headless mode for Safari but it doesn\'t currently support it.')
const safaridriver = await import('safaridriver')
safaridriver.start({ diagnose: true })
this.stopSafari = () => safaridriver.stop()
@ -55,16 +62,38 @@ export class WebdriverBrowserProvider implements BrowserProvider {
// TODO: close everything, if browser is closed from the outside
this.cachedBrowser = await remote({
...this.options,
logLevel: 'error',
capabilities: {
'browserName': this.browser,
'wdio:devtoolsOptions': { headless: options.headless },
},
capabilities: this.buildCapabilities(),
})
return this.cachedBrowser
}
private buildCapabilities() {
const capabilities: RemoteOptions['capabilities'] = {
...this.options?.capabilities,
browserName: this.browser,
}
const headlessMap = {
chrome: ['goog:chromeOptions', ['headless', 'disable-gpu']],
firefox: ['moz:firefoxOptions', ['-headless']],
edge: ['ms:edgeOptions', ['--headless']],
} as const
const options = this.ctx.config.browser
const browser = this.browser
if (browser !== 'safari' && options.headless) {
const [key, args] = headlessMap[browser]
const currentValues = (this.options?.capabilities as any)?.[key] || {}
const newArgs = [...currentValues.args || [], ...args]
capabilities[key] = { ...currentValues, args: newArgs as any }
}
return capabilities
}
async openPage(url: string) {
const browserInstance = await this.openBrowser()
await browserInstance.url(url)

View File

@ -111,10 +111,7 @@
"@vitest/browser": "*",
"@vitest/ui": "*",
"happy-dom": "*",
"jsdom": "*",
"playwright": "*",
"safaridriver": "*",
"webdriverio": "*"
"jsdom": "*"
},
"peerDependenciesMeta": {
"@types/node": {
@ -132,15 +129,6 @@
"jsdom": {
"optional": true
},
"webdriverio": {
"optional": true
},
"safaridriver": {
"optional": true
},
"playwright": {
"optional": true
},
"@edge-runtime/vm": {
"optional": true
}
@ -198,12 +186,9 @@
"mlly": "^1.4.0",
"p-limit": "^4.0.0",
"pkg-types": "^1.0.3",
"playwright": "^1.35.1",
"pretty-format": "^29.5.0",
"prompts": "^2.4.2",
"safaridriver": "^0.0.5",
"strip-ansi": "^7.1.0",
"webdriverio": "^8.11.2",
"ws": "^8.13.0"
}
}

View File

@ -59,9 +59,6 @@ const external = [
'rollup',
'node:vm',
'inspector',
'webdriverio',
'safaridriver',
'playwright',
'vite-node/source-map',
'vite-node/client',
'vite-node/server',
@ -92,6 +89,7 @@ const plugins = [
export default ({ watch }) => defineConfig([
{
input: entries,
treeshake: true,
output: {
dir: 'dist',
format: 'esm',

View File

@ -1,28 +1,26 @@
import { PlaywrightBrowserProvider } from '../node/browser/playwright'
import { WebdriverBrowserProvider } from '../node/browser/webdriver'
import { ensurePackageInstalled } from '../node/pkg'
import type { BrowserProviderModule, ResolvedBrowserOptions } from '../types/browser'
interface Loader {
executeId: (id: string) => Promise<{ default: BrowserProviderModule }>
root: string
executeId: (id: string) => any
}
export async function getBrowserProvider(options: ResolvedBrowserOptions, loader: Loader): Promise<BrowserProviderModule> {
switch (options.provider) {
case undefined:
case 'webdriverio':
return WebdriverBrowserProvider
case 'playwright':
return PlaywrightBrowserProvider
default:
break
if (options.provider == null || options.provider === 'webdriverio' || options.provider === 'playwright') {
await ensurePackageInstalled('@vitest/browser', loader.root)
const providers = await loader.executeId('@vitest/browser/providers') as {
webdriverio: BrowserProviderModule
playwright: BrowserProviderModule
}
const provider = (options.provider || 'webdriverio') as 'webdriverio' | 'playwright'
return providers[provider]
}
let customProviderModule
try {
customProviderModule = await loader.executeId(options.provider)
customProviderModule = await loader.executeId(options.provider) as { default: BrowserProviderModule }
}
catch (error) {
throw new Error(`Failed to load custom BrowserProvider from ${options.provider}`, { cause: error })

View File

@ -1,9 +1,4 @@
import { normalize, resolve } from 'pathe'
import { resolvePath } from 'mlly'
import { ViteNodeRunner } from 'vite-node/client'
import type { ViteNodeRunnerOptions } from 'vite-node'
import type { BuiltinEnvironment, VitestEnvironment } from '../../types/config'
import type { Environment } from '../../types'
import type { VitestEnvironment } from '../../types/config'
import node from './node'
import jsdom from './jsdom'
import happy from './happy-dom'
@ -24,10 +19,6 @@ export const envPackageNames: Record<Exclude<keyof typeof environments, 'node'>,
'edge-runtime': '@edge-runtime/vm',
}
function isBuiltinEnvironment(env: VitestEnvironment): env is BuiltinEnvironment {
return env in environments
}
export function getEnvPackageName(env: VitestEnvironment) {
if (env === 'node')
return null
@ -37,39 +28,3 @@ export function getEnvPackageName(env: VitestEnvironment) {
return null
return `vitest-environment-${env}`
}
const _loaders = new Map<string, ViteNodeRunner>()
export async function createEnvironmentLoader(options: ViteNodeRunnerOptions) {
if (!_loaders.has(options.root)) {
const loader = new ViteNodeRunner(options)
await loader.executeId('/@vite/env')
_loaders.set(options.root, loader)
}
return _loaders.get(options.root)!
}
export async function loadEnvironment(name: VitestEnvironment, options: ViteNodeRunnerOptions): Promise<Environment> {
if (isBuiltinEnvironment(name))
return environments[name]
const loader = await createEnvironmentLoader(options)
const root = loader.root
const packageId = name[0] === '.' || name[0] === '/'
? resolve(root, name)
: await resolvePath(`vitest-environment-${name}`, { url: [root] }) ?? resolve(root, name)
const pkg = await loader.executeId(normalize(packageId))
if (!pkg || !pkg.default || typeof pkg.default !== 'object') {
throw new TypeError(
`Environment "${name}" is not a valid environment. `
+ `Path "${packageId}" should export default object with a "setup" or/and "setupVM" method.`,
)
}
const environment = pkg.default
if (environment.transformMode !== 'web' && environment.transformMode !== 'ssr') {
throw new TypeError(
`Environment "${name}" is not a valid environment. `
+ `Path "${packageId}" should export default object with a "transformMode" method equal to "ssr" or "web".`,
)
}
return environment
}

View File

@ -0,0 +1,47 @@
import { normalize, resolve } from 'pathe'
import { resolvePath } from 'mlly'
import { ViteNodeRunner } from 'vite-node/client'
import type { ViteNodeRunnerOptions } from 'vite-node'
import type { BuiltinEnvironment, VitestEnvironment } from '../../types/config'
import type { Environment } from '../../types'
import { environments } from './index'
function isBuiltinEnvironment(env: VitestEnvironment): env is BuiltinEnvironment {
return env in environments
}
const _loaders = new Map<string, ViteNodeRunner>()
export async function createEnvironmentLoader(options: ViteNodeRunnerOptions) {
if (!_loaders.has(options.root)) {
const loader = new ViteNodeRunner(options)
await loader.executeId('/@vite/env')
_loaders.set(options.root, loader)
}
return _loaders.get(options.root)!
}
export async function loadEnvironment(name: VitestEnvironment, options: ViteNodeRunnerOptions): Promise<Environment> {
if (isBuiltinEnvironment(name))
return environments[name]
const loader = await createEnvironmentLoader(options)
const root = loader.root
const packageId = name[0] === '.' || name[0] === '/'
? resolve(root, name)
: await resolvePath(`vitest-environment-${name}`, { url: [root] }) ?? resolve(root, name)
const pkg = await loader.executeId(normalize(packageId))
if (!pkg || !pkg.default || typeof pkg.default !== 'object') {
throw new TypeError(
`Environment "${name}" is not a valid environment. `
+ `Path "${packageId}" should export default object with a "setup" or/and "setupVM" method.`,
)
}
const environment = pkg.default
if (environment.transformMode !== 'web' && environment.transformMode !== 'ssr') {
throw new TypeError(
`Environment "${name}" is not a valid environment. `
+ `Path "${packageId}" should export default object with a "transformMode" method equal to "ssr" or "web".`,
)
}
return environment
}

View File

@ -1,71 +0,0 @@
import type { Page } from 'playwright'
import type { BrowserProvider, BrowserProviderOptions } from '../../types/browser'
import { ensurePackageInstalled } from '../pkg'
import type { WorkspaceProject } from '../workspace'
import type { Awaitable } from '../../types'
export const playwrightBrowsers = ['firefox', 'webkit', 'chromium'] as const
export type PlaywrightBrowser = typeof playwrightBrowsers[number]
export interface PlaywrightProviderOptions extends BrowserProviderOptions {
browser: PlaywrightBrowser
}
export class PlaywrightBrowserProvider implements BrowserProvider {
public name = 'playwright'
private cachedBrowser: Page | null = null
private browser!: PlaywrightBrowser
private ctx!: WorkspaceProject
getSupportedBrowsers() {
return playwrightBrowsers
}
async initialize(ctx: WorkspaceProject, { browser }: PlaywrightProviderOptions) {
this.ctx = ctx
this.browser = browser
const root = this.ctx.config.root
if (!await ensurePackageInstalled('playwright', root))
throw new Error('Cannot find "playwright" package. Please install it manually.')
}
async openBrowser() {
if (this.cachedBrowser)
return this.cachedBrowser
const options = this.ctx.config.browser
const playwright = await import('playwright')
const playwrightInstance = await playwright[this.browser].launch({ headless: options.headless })
this.cachedBrowser = await playwrightInstance.newPage()
this.cachedBrowser.on('close', () => {
playwrightInstance.close()
})
return this.cachedBrowser
}
catchError(cb: (error: Error) => Awaitable<void>) {
this.cachedBrowser?.on('pageerror', cb)
return () => {
this.cachedBrowser?.off('pageerror', cb)
}
}
async openPage(url: string) {
const browserInstance = await this.openBrowser()
await browserInstance.goto(url)
}
async close() {
await this.cachedBrowser?.close()
// TODO: right now process can only exit with timeout, if we use browser
// needs investigating
process.exit()
}
}

View File

@ -1,5 +1,5 @@
export type { Vitest } from './core'
export type { WorkspaceProject as VitestWorkspace } from './workspace'
export type { WorkspaceProject } from './workspace'
export { createVitest } from './create'
export { VitestPlugin } from './plugins'
export { startVitest } from './cli-api'
@ -8,3 +8,6 @@ export type { WorkspaceSpec } from './pool'
export type { TestSequencer, TestSequencerConstructor } from './sequencers/types'
export { BaseSequencer } from './sequencers/BaseSequencer'
export { ensurePackageInstalled } from './pkg'
export type { BrowserProviderInitializationOptions, BrowserProvider, BrowserProviderOptions } from '../types/browser'

View File

@ -6,7 +6,7 @@ export function createMethodsRPC(project: WorkspaceProject): RuntimeRPC {
const ctx = project.ctx
return {
async onWorkerExit(error, code) {
await ctx.logger.printError(error, { type: 'Unexpected Exit' })
await ctx.logger.printError(error, { type: 'Unexpected Exit', fullStack: true })
process.exit(code || 1)
},
snapshotSaved(snapshot) {

View File

@ -356,6 +356,7 @@ export class WorkspaceProject {
throw new Error(`[${this.getName()}] Browser name is required. Please, set \`test.browser.name\` option manually.`)
if (!supportedBrowsers.includes(browser))
throw new Error(`[${this.getName()}] Browser "${browser}" is not supported by the browser provider "${this.browserProvider.name}". Supported browsers: ${supportedBrowsers.join(', ')}.`)
await this.browserProvider.initialize(this, { browser })
const providerOptions = this.config.browser.providerOptions
await this.browserProvider.initialize(this, { browser, options: providerOptions })
}
}

View File

@ -8,7 +8,7 @@ import type { CancelReason } from '@vitest/runner'
import type { ResolvedConfig, WorkerGlobalState } from '../types'
import type { RunnerRPC, RuntimeRPC } from '../types/rpc'
import type { ChildContext } from '../types/child'
import { loadEnvironment } from '../integrations/env'
import { loadEnvironment } from '../integrations/env/loader'
import { mockMap, moduleCache, startViteNode } from './execute'
import { createSafeRpc, rpcDone } from './rpc'
import { setupInspect } from './inspector'

View File

@ -9,7 +9,7 @@ import { installSourcemapsSupport } from 'vite-node/source-map'
import type { CancelReason } from '@vitest/runner'
import type { RunnerRPC, RuntimeRPC, WorkerContext, WorkerGlobalState } from '../types'
import { distDir } from '../paths'
import { loadEnvironment } from '../integrations/env'
import { loadEnvironment } from '../integrations/env/loader'
import { startVitestExecutor } from './execute'
import { createCustomConsole } from './console'
import { createSafeRpc } from './rpc'

View File

@ -4,7 +4,7 @@ import { workerId as poolId } from 'tinypool'
import type { CancelReason } from '@vitest/runner'
import type { RunnerRPC, RuntimeRPC, WorkerContext, WorkerGlobalState } from '../types'
import { getWorkerState } from '../utils/global'
import { loadEnvironment } from '../integrations/env'
import { loadEnvironment } from '../integrations/env/loader'
import { mockMap, moduleCache, startViteNode } from './execute'
import { setupInspect } from './inspector'
import { createSafeRpc, rpcDone } from './rpc'

View File

@ -2,14 +2,15 @@ import type { Awaitable } from '@vitest/utils'
import type { WorkspaceProject } from '../node/workspace'
import type { ApiConfig } from './config'
export interface BrowserProviderOptions {
export interface BrowserProviderInitializationOptions {
browser: string
options?: BrowserProviderOptions
}
export interface BrowserProvider {
name: string
getSupportedBrowsers(): readonly string[]
initialize(ctx: WorkspaceProject, options: BrowserProviderOptions): Awaitable<void>
initialize(ctx: WorkspaceProject, options: BrowserProviderInitializationOptions): Awaitable<void>
openPage(url: string): Awaitable<void>
catchError(cb: (error: Error) => Awaitable<void>): () => Awaitable<void>
close(): Awaitable<void>
@ -19,6 +20,8 @@ export interface BrowserProviderModule {
new (): BrowserProvider
}
export interface BrowserProviderOptions {}
export interface BrowserConfigOptions {
/**
* if running tests in the browser should be the default
@ -33,12 +36,24 @@ export interface BrowserConfigOptions {
name: string
/**
* browser provider
* Browser provider
*
* @default 'webdriverio'
*/
provider?: 'webdriverio' | 'playwright' | (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 } }
*/
providerOptions?: BrowserProviderOptions
/**
* enable headless mode
*

658
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@ const configs: UserConfig[] = []
const pools: UserConfig[] = [{ pool: 'threads' }, { pool: 'forks' }, { pool: 'threads', poolOptions: { threads: { singleThread: true } } }]
if (process.platform !== 'win32')
pools.push({ browser: { enabled: true, name: 'chrome' } })
pools.push({ browser: { enabled: true, name: 'chromium', provider: 'playwright' } })
for (const isolate of [true, false]) {
for (const pool of pools) {

View File

@ -1,6 +1,6 @@
import type { Vitest } from 'vitest'
import { describe, expect, test, vi } from 'vitest'
import type { VitestWorkspace } from 'vitest/node'
import type { WorkspaceProject } from 'vitest/node'
import { RandomSequencer } from '../../../packages/vitest/src/node/sequencers/RandomSequencer'
import { BaseSequencer } from '../../../packages/vitest/src/node/sequencers/BaseSequencer'
import type { WorkspaceSpec } from '../../../packages/vitest/src/node/pool'
@ -20,7 +20,7 @@ function buildCtx() {
function buildWorkspace() {
return {
getName: () => 'test',
} as any as VitestWorkspace
} as any as WorkspaceProject
}
const workspace = buildWorkspace()

View File

@ -7,7 +7,7 @@
"devDependencies": {
"execa": "^6.1.0",
"fs-extra": "^10.1.0",
"playwright-chromium": "^1.27.0",
"playwright-chromium": "^1.39.0",
"vitest": "workspace:*"
}
}