浏览器模式 实验性
此页面提供有关 Vitest API 中实验性浏览器模式功能的信息,该功能允许你在浏览器中本地运行测试,提供对窗口和文档等浏览器全局变量的访问。此功能目前正在开发中,API 未来可能会更改。


安装
为方便设置,可使用 vitest init browser 命令安装所需的依赖项并创建浏览器配置。
npx vitest init browseryarn exec vitest init browserpnpx vitest init browserbunx vitest init browser手动安装
您也可以手动安装软件包。默认情况下,浏览器模式不需要任何额外的 E2E provider 就能在本地运行测试,因为它会复用你现有的浏览器。
npm install -D vitest @vitest/browseryarn add -D vitest @vitest/browserpnpm add -D vitest @vitest/browserbun add -D vitest @vitest/browserWARNING
不过,要在 CI 中运行测试,您需要安装 playwright 或 webdriverio 。我们还建议在本地测试时切换到这两个选项中的一个,而不是使用默认的 preview 提供程序,因为它依赖于模拟事件而不是使用 Chrome DevTools 协议。
如果我们尚未使用这些工具中的任何一个,我们建议从 Playwright 开始,因为它支持并行执行,这可以使我们的测试运行得更快。此外,Playwright 使用的是 Chrome DevTools Protocol ,通常比 WebDriver 更快。
Playwright 是一个用于网络测试和自动化的框架。
npm install -D vitest @vitest/browser playwrightyarn add -D vitest @vitest/browser playwrightpnpm add -D vitest @vitest/browser playwrightbun add -D vitest @vitest/browser playwright配置
要在 Vitest 配置中激活浏览器模式,可以使用 --browser 标志,或在 Vitest 配置文件中将 browser.enabled 字段设为 true。下面是一个使用浏览器字段的配置示例:
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
browser: {
provider: 'playwright', // 或 'webdriverio'
enabled: true,
name: 'chromium', // 浏览器名称是必填的
},
}
})INFO
Vitest 默认分配端口号 63315 以避免与开发服务器冲突,允许我们同时并行运行两者。我们可以通过 browser.api 选项来更改这个端口号。
自 Vitest 2.1.5 版本起,命令行界面(CLI)不再自动打印 Vite 的 URL。当您在观察模式下运行时,可以通过按 "b" 键来打印 URL。
如果之前未使用过 Vite,请确保已安装框架插件并在配置中指定。有些框架可能需要额外配置才能运行,请查看其 Vite 相关文档以确定。
import { defineConfig } from 'vitest/config'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
test: {
browser: {
enabled: true,
provider: 'playwright',
name: 'chromium',
}
}
})import { defineConfig } from 'vitest/config'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
test: {
browser: {
enabled: true,
provider: 'playwright',
name: 'chromium',
}
}
})import { defineConfig } from 'vitest/config'
import { svelte } from '@sveltejs/vite-plugin-svelte'
export default defineConfig({
plugins: [svelte()],
test: {
browser: {
enabled: true,
provider: 'playwright',
name: 'chromium',
}
}
})import { defineConfig } from 'vitest/config'
import solidPlugin from 'vite-plugin-solid'
export default defineConfig({
plugins: [solidPlugin()],
test: {
browser: {
enabled: true,
provider: 'playwright',
name: 'chromium',
}
}
})import { defineConfig } from 'vitest/config'
import marko from '@marko/vite'
export default defineConfig({
plugins: [marko()],
test: {
browser: {
enabled: true,
provider: 'playwright',
name: 'chromium',
}
}
})如果我们需要使用基于 Node 的运行器来运行一些测试,可以定义一个 工作区 文件,其中包含不同测试策略的独立配置:
import { defineWorkspace } from 'vitest/config'
export default defineWorkspace([
{
test: {
// 基于文件命名约定的示例
// 非强制要求
include: [
'tests/unit/**/*.{test,spec}.ts',
'tests/**/*.unit.{test,spec}.ts',
],
name: 'unit',
environment: 'node',
},
},
{
test: {
// 基于文件命名约定的示例
// 非强制要求
include: [
'tests/browser/**/*.{test,spec}.ts',
'tests/**/*.browser.{test,spec}.ts',
],
name: 'browser',
browser: {
enabled: true,
name: 'chrome',
},
},
},
])Provider 配置
你可以通过 providerOptions字段配置 Vitest 如何 启动浏览器 和创建 页面上下文:
export default defineConfig({
test: {
browser: {
providerOptions: {
launch: {
devtools: true,
},
context: {
geolocation: {
latitude: 45,
longitude: -30,
},
reducedMotion: 'reduce',
},
},
},
},
})浏览器选项类型
Vitest 中的浏览器选项取决于provider。如果在配置文件中传递 --browser 且未指定其名称,则 Vitest 将失败。可用选项:
webdriverio支持这些浏览器:firefoxchromeedgesafari
playwright支持这些浏览器:firefoxwebkitchromium
浏览器兼容性
Vitest 使用 Vite dev server 来运行您的测试,因此我们只支持 esbuild.target选项(默认为 esnext)中指定的功能。
默认情况下,Vite 的目标浏览器支持本地 ES Modules、本地 ESM dynamic import 和 import.meta。此外,我们还利用 BroadcastChannel在 iframe 之间进行通信:
- Chrome >=87
- Firefox >=78
- Safari >=15.4
- Edge >=88
运行测试
要使用 CLI 指定浏览器,请使用 --browser 标志后跟浏览器名称,如下所示:
npx vitest --browser=chrome或者你可以使用点符号向 CLI 提供浏览器选项:
npx vitest --browser.name=chrome --browser.headless默认情况下,Vitest 会自动打开浏览器用户界面进行开发。您的测试将在中间的 iframe 中运行。您可以通过选择首选尺寸、在测试中调用 page.viewport 或在 the config 中设置默认值来配置视口。
无头模式
无头模式是浏览器模式下可用的另一个选项。在无头模式下,浏览器在没有用户界面的情况下在后台运行,这对于运行自动化测试非常有用。Vitest 中的 headless 选项可以设置为布尔值以启用或禁用无头模式。
在使用无头模式时,Vitest 不会自动打开用户界面。如果我们希望继续使用用户界面,同时让测试以 无头模式运行,我们可以安装 @vitest/ui 包,并在运行Vitest时传递 --ui 标志。
这是启用无头模式的示例配置:
import { defineConfig } from 'vitest/config'
import { playwright } from '@vitest/browser-playwright'
export default defineConfig({
test: {
browser: {
provider: playwright(),
enabled: true,
headless: true,
},
}
})你还可以在 CLI 中使用 --browser.headless 标志设置 headless 模式,如下所示:
npx vitest --browser.name=chrome --browser.headless在这种情况下,Vitest 将使用 Chrome 浏览器以 headless 模式运行。
WARNING
默认情况下无头模式不可用。我们需要使用 playwright 或 webdriverio 提供程序来启用此功能。
示例
Vitest 提供的软件包可为多个流行框架呈现开箱即用的组件:
vitest-browser-vue渲染 vue 组件vitest-browser-svelte渲染 svelte 组件vitest-browser-react渲染 react 组件
如果您的框架没有包含此功能,请随意创建自己的软件包--它是框架渲染器和 page.elementLocator API 的简单封装。我们将在本页添加指向它的链接。请确保它的名称以 vitest-browser- 开头。
除了使用 @testing-library/your-framework 渲染组件和查询元素外,你还需要进行断言。Vitest 捆绑了 @testing-library/jest-dom库,可提供各种开箱即用的 DOM 断言。更多信息请参阅 Assertions API。
import { expect } from 'vitest'
import { page } from '@vitest/browser/context'
// 元素渲染正确
await expect.element(page.getByText('Hello World')).toBeInTheDocument()Vitest 公开了一个Context API,其中包含一小套在测试中可能有用的实用程序。例如,如果您需要进行交互,如点击元素或在输入框中输入文本,您可以使用 @vitest/browser/context 中的 userEvent。更多信息请参阅 Interactivity API。
import { page, userEvent } from '@vitest/browser/context'
await userEvent.fill(page.getByLabelText(/username/i), 'Alice')
// or just locator.fill
await page.getByLabelText(/username/i).fill('Alice')import { render } from 'vitest-browser-vue'
import Component from './Component.vue'
test('properly handles v-model', async () => {
const screen = render(Component)
// 断言初始状态。
await expect.element(screen.getByText('Hi, my name is Alice')).toBeInTheDocument()
// 通过查询关联的标签获取输入的 DOM 节点。
const usernameInput = screen.getByLabelText(/username/i)
// 将名称输入到输入框中。
// 这已经验证了输入框中的值是正确的,无需手动检查其值。
await usernameInput.fill('Bob')
await expect.element(screen.getByText('Hi, my name is Bob')).toBeInTheDocument()
})import { render } from 'vitest-browser-svelte'
import { expect, test } from 'vitest'
import Greeter from './greeter.svelte'
test('greeting appears on click', async () => {
const screen = render(Greeter, { name: 'World' })
const button = screen.getByRole('button')
await button.click()
const greeting = screen.getByText(/hello world/iu)
await expect.element(greeting).toBeInTheDocument()
})import { render } from 'vitest-browser-react'
import Fetch from './fetch'
test('loads and displays greeting', async () => {
// Render a React element into the DOM
const screen = render(<Fetch url="/greeting" />)
await screen.getByText('Load Greeting').click()
// wait before throwing an error if it cannot find an element
const heading = screen.getByRole('heading')
// assert that the alert message is correct
await expect.element(heading).toHaveTextContent('hello there')
await expect.element(screen.getByRole('button')).toBeDisabled()
})Vitest 并不支持所有开箱即用的框架,但您可以使用外部工具来运行这些框架的测试。我们还鼓励社区创建他们自己的 vitest-browser 封装程序,如果您有这样的封装程序,请随时将其添加到上述示例中。
对于不支持的框架,我们建议使用 testing-library 软件包:
@testing-library/preact渲染 preact 组件@solidjs/testing-library渲染 solid 组件@marko/testing-library渲染 marko 组件
WARNING
testing-library 提供了一个软件包 @testing-library/user-event。我们不建议直接使用它,因为它会模拟事件而非实际触发事件--相反,请使用从 @vitest/browser/context导入的 userEvent,它在引擎盖下使用 Chrome DevTools 协议或 Webdriver(取决于provider)。
// based on @testing-library/preact example
// https://testing-library.com/docs/preact-testing-library/example
import { h } from 'preact'
import { page } from '@vitest/browser/context'
import { render } from '@testing-library/preact'
import HiddenMessage from '../hidden-message'
test('shows the children when the checkbox is checked', async () => {
const testMessage = 'Test Message'
const { baseElement } = render(
<HiddenMessage>{testMessage}</HiddenMessage>,
)
const screen = page.elementLocator(baseElement)
// .query() will return the element or null if it cannot be found.
// .element() will return the element or throw an error if it cannot be found.
expect(screen.getByText(testMessage).query()).not.toBeInTheDocument()
// The queries can accept a regex to make your selectors more
// resilient to content tweaks and changes.
await screen.getByLabelText(/show/i).click()
await expect.element(screen.getByText(testMessage)).toBeInTheDocument()
})// baed on @testing-library/solid API
// https://testing-library.com/docs/solid-testing-library/api
import { render } from '@testing-library/solid'
it('uses params', async () => {
const App = () => (
<>
<Route
path="/ids/:id"
component={() => (
<p>
Id:
{useParams()?.id}
</p>
)}
/>
<Route path="/" component={() => <p>Start</p>} />
</>
)
const { baseElement } = render(() => <App />, { location: 'ids/1234' })
const screen = page.elementLocator(baseElement)
await expect.screen(screen.getByText('Id: 1234')).toBeInTheDocument()
})// baed on @testing-library/marko API
// https://testing-library.com/docs/marko-testing-library/api
import { render, screen } from '@marko/testing-library'
import Greeting from './greeting.marko'
test('renders a message', async () => {
const { baseElement } = await render(Greeting, { name: 'Marko' })
const screen = page.elementLocator(baseElement)
await expect.element(screen.getByText(/Marko/)).toBeInTheDocument()
await expect.element(container.firstChild).toMatchInlineSnapshot(`
<h1>Hello, Marko!</h1>
`)
})限制
线程阻塞对话框
使用 Vitest 浏览器时,需要注意的是像 alert 或 confirm 这样的线程阻塞对话框不能在本地使用。这是因为它们阻塞了网页,这意味着 Vitest 无法继续与该页面通信,导致执行挂起。
在这种情况下,Vitest 为这些 API 提供默认模拟和默认返回值。这确保如果用户不小心使用了同步弹出式 Web API,执行不会挂起。但是,仍然建议用户模拟这些 Web API 以获得更好的体验。更多内容可参考 模拟 章节。