···3333- **Examples**: `CI=true pnpm test:examples`
3434- **Specific test suite**: `CI=true cd test/<test-folder> && pnpm test <test-file>`
3535- **Unit directory test**: `CI=true pnpm test <test-file>` (for `test/unit`)
3636-- **Browser tests**: `CI=true pnpm test:browser:playwright` or `CI=true pnpm test:browser:webdriverio`
3636+- **Browser tests**: `CI=true pnpm test:browser:playwright`
37373838**IMPORTANT: Do NOT use `--` when passing test filters to pnpm.**
3939Using `--` causes pnpm to drop the filter, resulting in a full test run instead of a filtered one.
···11-# @vitest/browser-webdriverio
22-33-[](https://npmx.dev/package/@vitest/browser-webdriverio)
44-55-Run your Vitest [browser tests](https://vitest.dev/guide/browser/) using [webdriverio](https://webdriver.io/docs/api/browser) API. Note that Vitest does not use webdriverio as a test runner, but only as a browser provider.
66-77-We recommend using this package if you are already using webdriverio in your project.
88-99-## Installation
1010-1111-Install the package with your favorite package manager:
1212-1313-```sh
1414-npm install -D @vitest/browser-webdriverio
1515-# or
1616-yarn add -D @vitest/browser-webdriverio
1717-# or
1818-pnpm add -D @vitest/browser-webdriverio
1919-```
2020-2121-Then specify it in the `browser.provider` field of your Vitest configuration:
2222-2323-```ts
2424-// vitest.config.ts
2525-import { defineConfig } from 'vitest/config'
2626-import { webdriverio } from '@vitest/browser-webdriverio'
2727-2828-export default defineConfig({
2929- test: {
3030- browser: {
3131- provider: webdriverio({
3232- // ...custom webdriverio options
3333- }),
3434- instances: [
3535- { browser: 'chrome' },
3636- ],
3737- },
3838- },
3939-})
4040-```
4141-4242-Then run Vitest in the browser mode:
4343-4444-```sh
4545-npx vitest --browser
4646-```
4747-4848-[GitHub](https://github.com/vitest-dev/vitest/tree/main/packages/browser-webdriverio) | [Documentation](https://vitest.dev/config/browser/webdriverio)
···1414pnpm run test-fixtures --root ./fixtures/locators
15151616# run specific fixture with selected provider and browser
1717-PROVIDER=webdriverio TEST_BROWSER=firefox pnpm run test-fixtures --root ./fixtures/locators
1717+PROVIDER=playwright TEST_BROWSER=firefox pnpm run test-fixtures --root ./fixtures/locators
1818```
19192020## Using docker playwright
···11-name: Setup WebDriverIO Browsers
22-description: Setup Chrome, ChromeDriver, and optional Firefox for WebDriverIO tests
33-44-inputs:
55- chrome-version:
66- description: Chrome for Testing version to install, or false to skip
77- # Pin to Chrome 148 while Chrome for Testing 149 Linux downloads/install are broken.
88- default: '148'
99- firefox-version:
1010- description: Firefox version to install, or false to skip
1111- default: latest
1212-1313-runs:
1414- using: composite
1515- steps:
1616- - uses: browser-actions/setup-chrome@4f8e94349a351df0f048634f25fec36c3c91eded # v2.1.1
1717- if: inputs.chrome-version != 'false'
1818- id: setup-chrome
1919- with:
2020- chrome-version: ${{ inputs.chrome-version }}
2121- install-chromedriver: true
2222-2323- - name: Export Chrome binaries
2424- if: inputs.chrome-version != 'false'
2525- shell: bash
2626- env:
2727- CHROME_PATH: ${{ steps.setup-chrome.outputs.chrome-path }}
2828- CHROMEDRIVER_PATH_VALUE: ${{ steps.setup-chrome.outputs.chromedriver-path }}
2929- run: | # zizmor: ignore[github-env] values come from pinned browser-actions/setup-chrome outputs.
3030- echo "CHROME_BIN=$CHROME_PATH" >> "$GITHUB_ENV"
3131- echo "CHROMEDRIVER_PATH=$CHROMEDRIVER_PATH_VALUE" >> "$GITHUB_ENV"
3232-3333- - name: Allow setup-chrome sandbox
3434- if: inputs.chrome-version != 'false' && runner.os == 'Linux'
3535- shell: bash
3636- env:
3737- CHROME_PATH: ${{ steps.setup-chrome.outputs.chrome-path }}
3838- run: |
3939- # Chrome for Testing is not covered by Ubuntu's AppArmor sandbox policy on CI.
4040- # https://pptr.dev/troubleshooting#issues-with-apparmor-on-ubuntu
4141- # https://chromium.googlesource.com/chromium/src/+/main/docs/security/apparmor-userns-restrictions.md
4242- # https://github.com/browser-actions/setup-chrome/issues/639
4343- cat <<EOF | sudo tee /etc/apparmor.d/chrome-for-testing
4444- abi <abi/4.0>,
4545- include <tunables/global>
4646-4747- profile chrome-for-testing "$CHROME_PATH" flags=(unconfined) {
4848- userns,
4949- include if exists <local/chrome>
5050- }
5151- EOF
5252- sudo apparmor_parser -r /etc/apparmor.d/chrome-for-testing
5353-5454- - uses: browser-actions/setup-firefox@fcf821c621167805dd63a29662bd7cb5676c81a8 # v1.7.1
5555- if: inputs.firefox-version != 'false'
5656- id: setup-firefox
5757- with:
5858- firefox-version: ${{ inputs.firefox-version }}
5959-6060- - name: Export Firefox binary
6161- if: inputs.firefox-version != 'false'
6262- shell: bash
6363- env:
6464- FIREFOX_PATH: ${{ steps.setup-firefox.outputs.firefox-path }}
6565- run: echo "FIREFOX_BIN=$FIREFOX_PATH" >> "$GITHUB_ENV" # zizmor: ignore[github-env] value comes from pinned browser-actions/setup-firefox output.
+2-2
docs/config/browser/webdriverio.md
···11# Configuring WebdriverIO
2233-::: info Playwright vs WebdriverIO
44-If you do not already use WebdriverIO in your project, we recommend starting with [Playwright](/config/browser/playwright) as it is easier to configure and has more flexible API.
33+::: info Community maintained
44+The WebdriverIO provider ([`@vitest/browser-webdriverio`](https://github.com/vitest-community/vitest-webdriverio)) is maintained by the Vitest community in the [`vitest-community`](https://github.com/vitest-community) organization, separately from the core Vitest packages. Please report provider-specific issues to its repository.
55:::
6677To run tests using WebdriverIO, you need to install the [`@vitest/browser-webdriverio`](https://npmx.dev/package/@vitest/browser-webdriverio) npm package and specify its `webdriverio` export in the `test.browser.provider` property of your config:
···11-import type { SerializedLocator } from '@vitest/browser'
22-import type { ScreenshotOptions } from 'vitest/browser'
33-import type { BrowserCommandContext } from 'vitest/node'
44-import crypto from 'node:crypto'
55-import { mkdir, rm } from 'node:fs/promises'
66-import { normalize as platformNormalize } from 'node:path'
77-import { resolveScreenshotPath } from '@vitest/browser'
88-import { dirname, normalize, resolve } from 'pathe'
99-1010-interface ScreenshotCommandOptions extends Omit<ScreenshotOptions, 'element' | 'mask'> {
1111- element?: SerializedLocator
1212- mask?: readonly SerializedLocator[]
1313- target?: 'element' | 'page'
1414-}
1515-1616-/**
1717- * Takes a screenshot using the provided browser context and returns a buffer and the expected screenshot path.
1818- *
1919- * **Note**: the returned `path` indicates where the screenshot *might* be found.
2020- * It is not guaranteed to exist, especially if `options.save` is `false`.
2121- *
2222- * @throws {Error} If the function is not called within a test or if the browser provider does not support screenshots.
2323- */
2424-export async function takeScreenshot(
2525- context: BrowserCommandContext,
2626- name: string,
2727- options: Omit<ScreenshotCommandOptions, 'base64'>,
2828-): Promise<{ buffer: Buffer<ArrayBufferLike>; path: string }> {
2929- if (!context.testPath) {
3030- throw new Error(`Cannot take a screenshot without a test path`)
3131- }
3232-3333- const path = resolveScreenshotPath(
3434- context.testPath,
3535- name,
3636- context.project.config,
3737- options.path,
3838- )
3939-4040- // playwright does not need a screenshot path if we don't intend to save it
4141- let savePath: string | undefined
4242-4343- if (options.save) {
4444- savePath = normalize(path)
4545-4646- await mkdir(dirname(savePath), { recursive: true })
4747- }
4848-4949- // webdriverio needs a path, so if one is not already set we create a temporary one
5050- if (savePath === undefined) {
5151- savePath = resolve(context.project.tmpDir, crypto.randomUUID())
5252-5353- await mkdir(context.project.tmpDir, { recursive: true })
5454- }
5555-5656- // webdriverio expects the path to contain the extension and only works with PNG files
5757- const savePathWithExtension = savePath.endsWith('.png') ? savePath : `${savePath}.png`
5858-5959- // there seems to be a bug in webdriverio, `X:/` gets appended to cwd, so we convert to `X:\`
6060- const normalizedSavePath = platformNormalize(savePathWithExtension)
6161- // `browser.saveScreenshot` captures the top-level page for `expect(page)`;
6262- // element and plain `page.screenshot()` calls keep the existing body fallback.
6363- const buffer = options.target === 'page'
6464- ? await context.browser.saveScreenshot(normalizedSavePath)
6565- : await context.browser.$(options.element?.selector ?? 'body').saveScreenshot(
6666- normalizedSavePath,
6767- )
6868-6969- if (!options.save) {
7070- await rm(savePathWithExtension, { force: true })
7171- }
7272-7373- return { buffer, path }
7474-}
···991010// ignore https errors due to self-signed certificate from plugin-basic-ssl
1111// https://playwright.dev/docs/api/class-browser#browser-new-context-option-ignore-https-errors
1212-// https://webdriver.io/docs/configuration/#strictssl and acceptInsecureCerts in https://webdriver.io/docs/api/browser/#properties
1312const configuredProvider = (function () {
1413 switch (provider.name) {
1514 case 'playwright': return providers.playwright()
1616- case 'webdriverio': return providers.webdriverio({ strictSSL: false, capabilities: { acceptInsecureCerts: true } })
1715 default: {
1816 throw new Error(`Invalid provider: ${provider.name}`)
1917 }
-4
test/browser/fixtures/trace/exotic.test.ts
···2323 const shadow = host.attachShadow({ mode: 'open' })
2424 shadow.innerHTML = '<button>Shadow button</button>'
2525 document.body.append(host)
2626- // TODO: webdriverio currently records this trace-view selector after it is
2727- // lowered to a provider-specific shadow DOM selector.
2826 await page.getByRole('button', { name: 'Shadow button' }).mark('shadow button rendered')
2927})
3028···4240 }
43414442 document.body.innerHTML = '<trace-widget></trace-widget>'
4545- // TODO: webdriverio currently records this trace-view selector after it is
4646- // lowered to a provider-specific shadow DOM selector.
4743 await page.getByRole('button', { name: 'Custom element button' }).mark('custom element rendered')
4844})
4945
-2
test/browser/fixtures/trace/expect.test.ts
···2828test('click fail', async () => {
2929 document.body.innerHTML = '<button>Hello</button>'
3030 try {
3131- // TODO: webdriverio currently records only the lifecycle failure for this case,
3232- // not the failed action trace entry with selectorResolution: "missing".
3331 await page.getByRole('button', { name: 'Missing' }).click({ timeout: 100 })
3432 }
3533 catch (e) {