mirror of
https://github.com/vitest-dev/vitest.git
synced 2026-02-01 17:36:51 +00:00
288 lines
11 KiB
Markdown
288 lines
11 KiB
Markdown
---
|
|
title: Browser Mode | Guide
|
|
---
|
|
|
|
# Browser Mode <Badge type="warning">Experimental</Badge> {#browser-mode}
|
|
|
|
This page provides information about the experimental browser mode feature in the Vitest API, which allows you to run your tests in the browser natively, providing access to browser globals like window and document. This feature is currently under development, and APIs may change in the future.
|
|
|
|
## Browser Compatibility
|
|
|
|
Vitest uses [Vite dev server](https://vitejs.dev/guide/#browser-support) to run your tests, so we only support features specified in the [`esbuild.target`](https://vitejs.dev/config/shared-options.html#esbuild) option (`esnext` by default).
|
|
|
|
By default, Vite targets browsers which support the native [ES Modules](https://caniuse.com/es6-module), native [ESM dynamic import](https://caniuse.com/es6-module-dynamic-import), and [`import.meta`](https://caniuse.com/mdn-javascript_operators_import_meta). On top of that, we utilize [`BroadcastChannel`](https://caniuse.com/?search=BroadcastChannel) to communicate between iframes:
|
|
|
|
- Chrome >=87
|
|
- Firefox >=78
|
|
- Safari >=15.4
|
|
- Edge >=88
|
|
|
|
## Motivation
|
|
|
|
We developed the Vitest browser mode feature to help improve testing workflows and achieve more accurate and reliable test results. This experimental addition to our testing API allows developers to run tests in a native browser environment. In this section, we'll explore the motivations behind this feature and its benefits for testing.
|
|
|
|
### Different Ways of Testing
|
|
|
|
There are different ways to test JavaScript code. Some testing frameworks simulate browser environments in Node.js, while others run tests in real browsers. In this context, [jsdom](https://www.npmjs.com/package/jsdom) is an example of a spec implementation that simulates a browser environment by being used with a test runner like Jest or Vitest, while other testing tools such as [WebdriverIO](https://webdriver.io/) or [Cypress](https://www.cypress.io/) allow developers to test their applications in a real browser or in case of [Playwright](https://playwright.dev/) provide you a browser engine.
|
|
|
|
### The Simulation Caveat
|
|
|
|
Testing JavaScript programs in simulated environments such as jsdom or happy-dom has simplified the test setup and provided an easy-to-use API, making them suitable for many projects and increasing confidence in test results. However, it is crucial to keep in mind that these tools only simulate a browser environment and not an actual browser, which may result in some discrepancies between the simulated environment and the real environment. Therefore, false positives or negatives in test results may occur.
|
|
|
|
To achieve the highest level of confidence in our tests, it's crucial to test in a real browser environment. This is why we developed the browser mode feature in Vitest, allowing developers to run tests natively in a browser and gain more accurate and reliable test results. With browser-level testing, developers can be more confident that their application will work as intended in a real-world scenario.
|
|
|
|
## Drawbacks
|
|
|
|
When using Vitest browser, it is important to consider the following drawbacks:
|
|
|
|
### Early Development
|
|
|
|
The browser mode feature of Vitest is still in its early stages of development. As such, it may not yet be fully optimized, and there may be some bugs or issues that have not yet been ironed out. It is recommended that users augment their Vitest browser experience with a standalone browser-side test runner like WebdriverIO, Cypress or Playwright.
|
|
|
|
### Longer Initialization
|
|
|
|
Vitest browser requires spinning up the provider and the browser during the initialization process, which can take some time. This can result in longer initialization times compared to other testing patterns.
|
|
|
|
## Configuration
|
|
|
|
To activate browser mode in your Vitest configuration, you can use the `--browser` flag or set the `browser.enabled` field to `true` in your Vitest configuration file. Here is an example configuration using the browser field:
|
|
|
|
```ts
|
|
export default defineConfig({
|
|
test: {
|
|
browser: {
|
|
enabled: true,
|
|
name: 'chrome', // browser name is required
|
|
},
|
|
}
|
|
})
|
|
```
|
|
|
|
## Browser Option Types
|
|
|
|
The browser option in Vitest depends on the provider. Vitest will fail, if you pass `--browser` and don't specify its name in the config file. Available options:
|
|
|
|
- `webdriverio` (default) supports these browsers:
|
|
- `firefox`
|
|
- `chrome`
|
|
- `edge`
|
|
- `safari`
|
|
- `playwright` supports these browsers:
|
|
- `firefox`
|
|
- `webkit`
|
|
- `chromium`
|
|
|
|
## Cross-Browser Testing
|
|
|
|
When you specify a browser name in the browser option, Vitest will try to run the specified browser using [WebdriverIO](https://webdriver.io/) by default, and then run the tests there. This feature makes cross-browser testing easy to use and configure in environments like a CI. If you don't want to use WebdriverIO, you can configure the custom browser provider by using `browser.provider` option.
|
|
|
|
To specify a browser using the CLI, use the `--browser` flag followed by the browser name, like this:
|
|
|
|
```sh
|
|
npx vitest --browser=chrome
|
|
```
|
|
|
|
Or you can provide browser options to CLI with dot notation:
|
|
|
|
```sh
|
|
npx vitest --browser.name=chrome --browser.headless
|
|
```
|
|
|
|
::: tip NOTE
|
|
When using the Safari browser option with WebdriverIO, the `safaridriver` needs to be activated by running `sudo safaridriver --enable` on your device.
|
|
|
|
Additionally, when running your tests, Vitest will attempt to install some drivers for compatibility with `safaridriver`.
|
|
:::
|
|
|
|
## Headless
|
|
|
|
Headless mode is another option available in the browser mode. In headless mode, the browser runs in the background without a user interface, which makes it useful for running automated tests. The headless option in Vitest can be set to a boolean value to enable or disable headless mode.
|
|
|
|
Here's an example configuration enabling headless mode:
|
|
|
|
```ts
|
|
export default defineConfig({
|
|
test: {
|
|
browser: {
|
|
enabled: true,
|
|
headless: true,
|
|
},
|
|
}
|
|
})
|
|
```
|
|
|
|
You can also set headless mode using the `--browser.headless` flag in the CLI, like this:
|
|
|
|
```sh
|
|
npx vitest --browser.name=chrome --browser.headless
|
|
```
|
|
|
|
In this case, Vitest will run in headless mode using the Chrome browser.
|
|
|
|
## Context
|
|
|
|
Vitest exposes a context module via `@vitest/browser/context` entry point. As of 2.0, it exposes a small set of utilities that might be useful to you in tests.
|
|
|
|
```ts
|
|
export const server: {
|
|
/**
|
|
* Platform the Vitest server is running on.
|
|
* The same as calling `process.platform` on the server.
|
|
*/
|
|
platform: Platform
|
|
/**
|
|
* Runtime version of the Vitest server.
|
|
* The same as calling `process.version` on the server.
|
|
*/
|
|
version: string
|
|
/**
|
|
* Available commands for the browser.
|
|
* @see {@link https://vitest.dev/guide/browser#commands}
|
|
*/
|
|
commands: BrowserCommands
|
|
}
|
|
|
|
/**
|
|
* Available commands for the browser.
|
|
* A shortcut to `server.commands`.
|
|
* @see {@link https://vitest.dev/guide/browser#commands}
|
|
*/
|
|
export const commands: BrowserCommands
|
|
|
|
export const page: {
|
|
/**
|
|
* Serialized test config.
|
|
*/
|
|
config: ResolvedConfig
|
|
}
|
|
```
|
|
|
|
## Commands
|
|
|
|
Command is a function that invokes another function on the server and passes down the result back to the browser. Vitest exposes several built-in commands you can use in your browser tests.
|
|
|
|
## Built-in Commands
|
|
|
|
### Files Handling
|
|
|
|
You can use `readFile`, `writeFile` and `removeFile` API to handle files inside your browser tests. All paths are resolved relative to the test file even if they are called in a helper function located in another file.
|
|
|
|
By default, Vitest uses `utf-8` encoding but you can override it with options.
|
|
|
|
::: tip
|
|
This API follows [`server.fs`](https://vitejs.dev/config/server-options.html#server-fs-allow) limitations for security reasons.
|
|
:::
|
|
|
|
```ts
|
|
import { server } from '@vitest/browser/context'
|
|
|
|
const { readFile, writeFile, removeFile } = server.commands
|
|
|
|
it('handles files', async () => {
|
|
const file = './test.txt'
|
|
|
|
await writeFile(file, 'hello world')
|
|
const content = await readFile(file)
|
|
|
|
expect(content).toBe('hello world')
|
|
|
|
await removeFile(file)
|
|
})
|
|
```
|
|
|
|
### Keyboard Interactions
|
|
|
|
Vitest also implements Web Test Runner's [`sendKeys` API](https://modern-web.dev/docs/test-runner/commands/#send-keys). It accepts an object with a single property:
|
|
|
|
- `type` - types a sequence of characters, this API _is not_ affected by modifier keys, so having `Shift` won't make letters uppercase
|
|
- `press` - presses a single key, this API _is_ affected by modifier keys, so having `Shift` will make subsequent characters uppercase
|
|
- `up` - holds down a key (supported only with `playwright` provider)
|
|
- `down` - releases a key (supported only with `playwright` provider)
|
|
|
|
```ts
|
|
interface TypePayload { type: string }
|
|
interface PressPayload { press: string }
|
|
interface DownPayload { down: string }
|
|
interface UpPayload { up: string }
|
|
|
|
type SendKeysPayload = TypePayload | PressPayload | DownPayload | UpPayload
|
|
|
|
declare function sendKeys(payload: SendKeysPayload): Promise<void>
|
|
```
|
|
|
|
This is just a simple wrapper around providers APIs. Please refer to their respective documentations for details:
|
|
|
|
- [Playwright Keyboard API](https://playwright.dev/docs/api/class-keyboard)
|
|
- [Webdriver Keyboard API](https://webdriver.io/docs/api/browser/keys/)
|
|
|
|
## Custom Commands
|
|
|
|
You can also add your own commands via [`browser.commands`](/config/#browser-commands) config option. If you develop a library, you can provide them via a `config` hook inside a plugin:
|
|
|
|
```ts
|
|
import type { Plugin } from 'vitest/config'
|
|
import type { BrowserCommand } from 'vitest/node'
|
|
|
|
const myCustomCommand: BrowserCommand<[arg1: string, arg2: string]> = ({
|
|
testPath,
|
|
provider
|
|
}, arg1, arg2) => {
|
|
if (provider.name === 'playwright') {
|
|
console.log(testPath, arg1, arg2)
|
|
return { someValue: true }
|
|
}
|
|
|
|
throw new Error(`provider ${provider.name} is not supported`)
|
|
}
|
|
|
|
export default function BrowserCommands(): Plugin {
|
|
return {
|
|
name: 'vitest:custom-commands',
|
|
config() {
|
|
return {
|
|
test: {
|
|
browser: {
|
|
commands: {
|
|
myCustomCommand,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Then you can call it inside your test by importing it from `@vitest/browser/context`:
|
|
|
|
```ts
|
|
import { commands } from '@vitest/browser/context'
|
|
import { expect, test } from 'vitest'
|
|
|
|
test('custom command works correctly', async () => {
|
|
const result = await commands.myCustomCommand('test1', 'test2')
|
|
expect(result).toEqual({ someValue: true })
|
|
})
|
|
|
|
// if you are using TypeScript, you can augment the module
|
|
declare module '@vitest/browser/context' {
|
|
interface BrowserCommands {
|
|
myCustomCommand: (arg1: string, arg2: string) => Promise<{
|
|
someValue: true
|
|
}>
|
|
}
|
|
}
|
|
```
|
|
|
|
::: warning
|
|
Custom functions will override built-in ones if they have the same name.
|
|
:::
|
|
|
|
## Limitations
|
|
|
|
### Thread Blocking Dialogs
|
|
|
|
When using Vitest Browser, it's important to note that thread blocking dialogs like `alert` or `confirm` cannot be used natively. This is because they block the web page, which means Vitest cannot continue communicating with the page, causing the execution to hang.
|
|
|
|
In such situations, Vitest provides default mocks with default returned values for these APIs. This ensures that if the user accidentally uses synchronous popup web APIs, the execution would not hang. However, it's still recommended for the user to mock these web APIs for better experience. Read more in [Mocking](/guide/mocking).
|