Skip to content

Commit

Permalink
feat!: move browser providers to @vitest/browser package (#4364)
Browse files Browse the repository at this point in the history
  • Loading branch information
sheremet-va committed Oct 27, 2023
1 parent 2af2ba7 commit 5cdeb55
Show file tree
Hide file tree
Showing 29 changed files with 500 additions and 666 deletions.
35 changes: 6 additions & 29 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
38 changes: 37 additions & 1 deletion docs/config/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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>
}
Expand All @@ -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`
Expand Down
4 changes: 2 additions & 2 deletions examples/playwright/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
Expand Down
35 changes: 32 additions & 3 deletions packages/browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,24 @@
"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",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"files": [
"dist",
"stubs"
"providers"
],
"scripts": {
"build": "rimraf dist && pnpm build:node && pnpm build:client",
Expand All @@ -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",
Expand All @@ -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 changes: 6 additions & 0 deletions packages/browser/providers.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { BrowserProvider } from 'vitest/nide'

declare var webdriverio: BrowserProvider
declare var playwright: BrowserProvider

export { webdriverio, playwright }
8 changes: 8 additions & 0 deletions packages/browser/providers/playwright.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { Browser, LaunchOptions } from 'playwright'

declare module 'vitest/node' {
interface BrowserProviderOptions {
launch?: LaunchOptions
page?: Parameters<Browser['newPage']>[0]
}
}
5 changes: 5 additions & 0 deletions packages/browser/providers/webdriverio.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type { RemoteOptions } from 'webdriverio'

declare module 'vitest/node' {
interface BrowserProviderOptions extends RemoteOptions {}
}
13 changes: 9 additions & 4 deletions packages/browser/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
]
Expand All @@ -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',
Expand All @@ -36,7 +41,7 @@ export default () => [
plugins,
},
{
input: './src/node/index.ts',
input: input.index,
output: {
file: 'dist/index.d.ts',
format: 'esm',
Expand Down
7 changes: 6 additions & 1 deletion packages/browser/src/client/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -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)
}
Expand Down
5 changes: 5 additions & 0 deletions packages/browser/src/node/providers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { PlaywrightBrowserProvider } from './playwright'
import { WebdriverBrowserProvider } from './webdriver'

export const webdriverio = WebdriverBrowserProvider
export const playwright = PlaywrightBrowserProvider
80 changes: 80 additions & 0 deletions packages/browser/src/node/providers/playwright.ts
Original file line number Diff line number Diff line change
@@ -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()
}
}

0 comments on commit 5cdeb55

Please sign in to comment.