diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a25bb0e4609d..86e790a14312 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -20,7 +20,7 @@ env:
jobs:
lint:
- runs-on: ubuntu-latest
+ runs-on: macos-14
steps:
- uses: actions/checkout@v4
@@ -32,7 +32,27 @@ jobs:
- name: Lint
run: pnpm run lint
+ changed:
+ runs-on: macos-14
+ outputs:
+ should_skip: ${{ steps.changed-files.outputs.only_changed == 'true' }}
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Get changed files
+ id: changed-files
+ uses: tj-actions/changed-files@c65cd883420fd2eb864698a825fc4162dd94482c # v44.5.7
+ with:
+ files: |
+ docs/**
+ .github/**
+ !.github/workflows/ci.yml
+ **.md
+
test:
+ needs: changed
+ if: needs.changed.outputs.should_skip != 'true'
runs-on: ${{ matrix.os }}
timeout-minutes: 30
@@ -54,49 +74,34 @@ jobs:
steps:
- uses: actions/checkout@v4
- - name: Get changed files
- id: changed-files
- uses: tj-actions/changed-files@c65cd883420fd2eb864698a825fc4162dd94482c # v44.5.7
- with:
- files: |
- docs/**
- .github/**
- !.github/workflows/ci.yml
- **.md
-
- uses: ./.github/actions/setup-and-cache
- if: steps.changed-files.outputs.only_changed != 'true'
with:
node-version: ${{ matrix.node_version }}
- uses: browser-actions/setup-chrome@v1
- if: steps.changed-files.outputs.only_changed != 'true'
- name: Install
- if: steps.changed-files.outputs.only_changed != 'true'
run: pnpm i
- name: Install Playwright Dependencies
- if: steps.changed-files.outputs.only_changed != 'true'
run: pnpm exec playwright install chromium --with-deps
- name: Build
- if: steps.changed-files.outputs.only_changed != 'true'
run: pnpm run build
- name: Test
- if: steps.changed-files.outputs.only_changed != 'true'
run: pnpm run test:ci
- name: Test Examples
- if: steps.changed-files.outputs.only_changed != 'true'
run: pnpm run test:examples
- name: Unit Test UI
- if: steps.changed-files.outputs.only_changed != 'true'
run: pnpm run -C packages/ui test:ui
test-browser:
+ needs: changed
+ if: needs.changed.outputs.should_skip != 'true'
+
runs-on: ${{ matrix.os }}
strategy:
matrix:
@@ -114,28 +119,16 @@ jobs:
steps:
- uses: actions/checkout@v4
- - name: Get changed files
- id: changed-files
- uses: tj-actions/changed-files@c65cd883420fd2eb864698a825fc4162dd94482c # v44.5.7
- with:
- files: |
- docs/**
- .github/**
- !.github/workflows/ci.yml
- **.md
-
- uses: ./.github/actions/setup-and-cache
- if: steps.changed-files.outputs.only_changed != 'true'
with:
node-version: 20
- uses: browser-actions/setup-chrome@v1
- if: ${{ steps.changed-files.outputs.only_changed != 'true' && matrix.browser[0] == 'chromium' }}
+ if: ${{ matrix.browser[0] == 'chromium' }}
- uses: browser-actions/setup-firefox@v1
- if: ${{ steps.changed-files.outputs.only_changed != 'true' && matrix.browser[0] == 'firefox' }}
+ if: ${{ matrix.browser[0] == 'firefox' }}
- name: Install
- if: steps.changed-files.outputs.only_changed != 'true'
run: pnpm i
- name: Install Playwright Dependencies
@@ -143,17 +136,15 @@ jobs:
run: pnpm exec playwright install ${{ matrix.browser[0] }} --with-deps
- name: Build
- if: steps.changed-files.outputs.only_changed != 'true'
run: pnpm run build
- name: Test Browser (playwright)
- if: steps.changed-files.outputs.only_changed != 'true'
run: pnpm run test:browser:playwright
env:
BROWSER: ${{ matrix.browser[0] }}
- name: Test Browser (webdriverio)
run: pnpm run test:browser:webdriverio
- if: ${{ steps.changed-files.outputs.only_changed != 'true' && matrix.browser[1] }}
+ if: ${{ matrix.browser[1] }}
env:
BROWSER: ${{ matrix.browser[1] }}
diff --git a/docs/api/vi.md b/docs/api/vi.md
index 5f6963b21dc2..ea3780c1df9e 100644
--- a/docs/api/vi.md
+++ b/docs/api/vi.md
@@ -52,6 +52,10 @@ vi.mock(import('./path/to/module.js'), async (importOriginal) => {
Under the hood, Vitest still operates on a string and not a module object.
+If you are using TypeScript with `paths` aliases configured in `tsconfig.json` however, the compiler won't be able to correctly resolve import types.
+In order to make it work, make sure to replace all aliased imports, with their corresponding relative paths.
+Eg. use `import('./path/to/module.js')` instead of `import('@/module')`.
+
::: warning
`vi.mock` is hoisted (in other words, _moved_) to **top of the file**. It means that whenever you write it (be it inside `beforeEach` or `test`), it will actually be called before that.
diff --git a/docs/config/index.md b/docs/config/index.md
index 5d7bf73595be..6e9971a54627 100644
--- a/docs/config/index.md
+++ b/docs/config/index.md
@@ -1003,6 +1003,54 @@ afterEach(() => {
globalThis.resetBeforeEachTest = true
```
+### provide 2.1.0 {#provide}
+
+- **Type:** `Partial`
+
+Define values that can be accessed inside your tests using `inject` method.
+
+:::code-group
+```ts [vitest.config.js]
+import { defineConfig } from 'vitest/config'
+
+export default defineConfig({
+ test: {
+ provide: {
+ API_KEY: '123',
+ },
+ },
+})
+```
+```ts [my.test.js]
+import { expect, inject, test } from 'vitest'
+
+test('api key is defined', () => {
+ expect(inject('API_KEY')).toBe('123')
+})
+```
+:::
+
+::: warning
+Properties have to be strings and values need to be [serializable](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm#supported_types) because this object will be transferred between different processes.
+:::
+
+::: tip
+If you are using TypeScript, you will need to augment `ProvidedContext` type for type safe access:
+
+```ts
+// vitest.shims.d.ts
+
+declare module 'vitest' {
+ export interface ProvidedContext {
+ API_KEY: string
+ }
+}
+
+// mark this file as a module so augmentation works correctly
+export {}
+```
+:::
+
### globalSetup
- **Type:** `string | string[]`
@@ -1018,7 +1066,7 @@ Multiple globalSetup files are possible. setup and teardown are executed sequent
::: warning
Global setup runs only if there is at least one running test. This means that global setup might start running during watch mode after test file is changed (the test file will wait for global setup to finish before running).
-Beware that the global setup is running in a different global scope, so your tests don't have access to variables defined here. However, you can pass down serializable data to tests via `provide` method:
+Beware that the global setup is running in a different global scope, so your tests don't have access to variables defined here. However, you can pass down serializable data to tests via [`provide`](#provide) method:
:::code-group
```js [globalSetup.js]
@@ -1033,8 +1081,6 @@ export default function setup({ provide }: GlobalSetupContext) {
provide('wsPort', 3000)
}
-// You can also extend `ProvidedContext` type
-// to have type safe access to `provide/inject` methods:
declare module 'vitest' {
export interface ProvidedContext {
wsPort: number
diff --git a/docs/guide/browser/assertion-api.md b/docs/guide/browser/assertion-api.md
index c0a422a0bed5..e7cb87845a0e 100644
--- a/docs/guide/browser/assertion-api.md
+++ b/docs/guide/browser/assertion-api.md
@@ -68,14 +68,14 @@ Tests in the browser might fail inconsistently due to their asynchronous nature.
```ts
import { expect, test } from 'vitest'
-import { screen } from '@testing-library/dom'
+import { page } from '@vitest/browser/context'
test('error banner is rendered', async () => {
triggerError()
// @testing-library provides queries with built-in retry-ability
// It will try to find the banner until it's rendered
- const banner = await screen.findByRole('alert', {
+ const banner = page.getByRole('alert', {
name: /error/i,
})
diff --git a/docs/guide/browser/index.md b/docs/guide/browser/index.md
index ff3124abed8b..2ffec710811c 100644
--- a/docs/guide/browser/index.md
+++ b/docs/guide/browser/index.md
@@ -341,110 +341,107 @@ Headless mode is not available by default. You need to use either [`playwright`]
## Examples
-Browser Mode is framework agnostic so it doesn't provide any method to render your components. However, you should be able to use your framework's test utils packages.
+Vitest provides packages to render components for several popular frameworks out of the box:
-We recommend using `testing-library` packages depending on your framework:
+- [`vitest-browser-vue`](https://github.com/vitest-dev/vitest-browser-vue) to render [vue](https://vuejs.org) components
+- [`vitest-browser-svelte`](https://github.com/vitest-dev/vitest-browser-svelte) to render [svelte](https://svelte.dev) components
+- [`vitest-browser-react`](https://github.com/vitest-dev/vitest-browser-react) to render [react](https://react.dev) components
-- [`@testing-library/dom`](https://testing-library.com/docs/dom-testing-library/intro) if you don't use a framework
-- [`@testing-library/vue`](https://testing-library.com/docs/vue-testing-library/intro) to render [vue](https://vuejs.org) components
-- [`@testing-library/svelte`](https://testing-library.com/docs/svelte-testing-library/intro) to render [svelte](https://svelte.dev) components
-- [`@testing-library/react`](https://testing-library.com/docs/react-testing-library/intro) to render [react](https://react.dev) components
-- [`@testing-library/preact`](https://testing-library.com/docs/preact-testing-library/intro) to render [preact](https://preactjs.com) components
-- [`solid-testing-library`](https://testing-library.com/docs/solid-testing-library/intro) to render [solid](https://www.solidjs.com) components
-- [`@marko/testing-library`](https://testing-library.com/docs/marko-testing-library/intro) to render [marko](https://markojs.com) components
+If your framework is not represented, feel free to create your own package - it is a simple wrapper around the framework renderer and `page.elementLocator` API. We will add a link to it on this page. Make sure it has a name starting with `vitest-browser-`.
-Besides rendering components and querying elements using `@testing-library/your-framework`, you will also need to make assertions. Vitest bundles the [`@testing-library/jest-dom`](https://github.com/testing-library/jest-dom) library to provide a wide range of DOM assertions out of the box. Read more at the [Assertions API](/guide/browser/assertion-api).
+Besides rendering components and locating elements, you will also need to make assertions. Vitest bundles the [`@testing-library/jest-dom`](https://github.com/testing-library/jest-dom) library to provide a wide range of DOM assertions out of the box. Read more at the [Assertions API](/guide/browser/assertion-api).
```ts
import { expect } from 'vitest'
+import { page } from '@vitest/browser/context'
// element is rendered correctly
-await expect.element(screen.getByText('Hello World')).toBeInTheDocument()
+await expect.element(page.getByText('Hello World')).toBeInTheDocument()
```
Vitest exposes a [Context API](/guide/browser/context) with a small set of utilities that might be useful to you in tests. For example, if you need to make an interaction, like clicking an element or typing text into an input, you can use `userEvent` from `@vitest/browser/context`. Read more at the [Interactivity API](/guide/browser/interactivity-api).
```ts
-import { userEvent } from '@vitest/browser/context'
-await userEvent.type(screen.getByLabelText(/username/i), 'Alice')
+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')
```
-::: warning
-`testing-library` provides a package `@testing-library/user-event`. We do not recommend using it directly because it simulates events instead of actually triggering them - instead, use [`userEvent`](#interactivity-api) imported from `@vitest/browser/context` that uses Chrome DevTools Protocol or Webdriver (depending on the provider) under the hood.
-:::
-
::: code-group
```ts [vue]
-// based on @testing-library/vue example
-// https://testing-library.com/docs/vue-testing-library/examples
-
-import { userEvent } from '@vitest/browser/context'
-import { render, screen } from '@testing-library/vue'
+import { render } from 'vitest-browser-vue'
import Component from './Component.vue'
test('properly handles v-model', async () => {
- render(Component)
+ const screen = render(Component)
// Asserts initial state.
- expect(screen.getByText('Hi, my name is Alice')).toBeInTheDocument()
+ await expect.element(screen.getByText('Hi, my name is Alice')).toBeInTheDocument()
// Get the input DOM node by querying the associated label.
- const usernameInput = await screen.findByLabelText(/username/i)
+ const usernameInput = screen.getByLabelText(/username/i)
// Type the name into the input. This already validates that the input
// is filled correctly, no need to check the value manually.
- await userEvent.fill(usernameInput, 'Bob')
+ await usernameInput.fill('Bob')
- expect(screen.getByText('Hi, my name is Bob')).toBeInTheDocument()
+ await expect.element(screen.getByText('Hi, my name is Bob')).toBeInTheDocument()
})
```
```ts [svelte]
-// based on @testing-library/svelte
-// https://testing-library.com/docs/svelte-testing-library/example
-
-import { render, screen } from '@testing-library/svelte'
-import { userEvent } from '@vitest/browser/context'
+import { render } from 'vitest-browser-svelte'
import { expect, test } from 'vitest'
import Greeter from './greeter.svelte'
test('greeting appears on click', async () => {
- const user = userEvent.setup()
- render(Greeter, { name: 'World' })
+ const screen = render(Greeter, { name: 'World' })
const button = screen.getByRole('button')
- await user.click(button)
- const greeting = await screen.findByText(/hello world/iu)
+ await button.click()
+ const greeting = screen.getByText(/hello world/iu)
- expect(greeting).toBeInTheDocument()
+ await expect.element(greeting).toBeInTheDocument()
})
```
```tsx [react]
-// based on @testing-library/react example
-// https://testing-library.com/docs/react-testing-library/example-intro
-
-import { userEvent } from '@vitest/browser/context'
-import { render, screen } from '@testing-library/react'
+import { render } from 'vitest-browser-react'
import Fetch from './fetch'
test('loads and displays greeting', async () => {
// Render a React element into the DOM
- render()
+ const screen = render()
- await userEvent.click(screen.getByText('Load Greeting'))
+ await screen.getByText('Load Greeting').click()
// wait before throwing an error if it cannot find an element
- const heading = await screen.findByRole('heading')
+ const heading = screen.getByRole('heading')
// assert that the alert message is correct
- expect(heading).toHaveTextContent('hello there')
- expect(screen.getByRole('button')).toBeDisabled()
+ await expect.element(heading).toHaveTextContent('hello there')
+ await expect.element(screen.getByRole('button')).toBeDisabled()
})
```
+:::
+
+Vitest doesn't support all frameworks out of the box, but you can use external tools to run tests with these frameworks. We also encourage the community to create their own `vitest-browser` wrappers - if you have one, feel free to add it to the examples above.
+
+For unsupported frameworks, we recommend using `testing-library` packages:
+
+- [`@testing-library/preact`](https://testing-library.com/docs/preact-testing-library/intro) to render [preact](https://preactjs.com) components
+- [`solid-testing-library`](https://testing-library.com/docs/solid-testing-library/intro) to render [solid](https://www.solidjs.com) components
+- [`@marko/testing-library`](https://testing-library.com/docs/marko-testing-library/intro) to render [marko](https://markojs.com) components
+
+::: warning
+`testing-library` provides a package `@testing-library/user-event`. We do not recommend using it directly because it simulates events instead of actually triggering them - instead, use [`userEvent`](/guide/browser/interactivity-api) imported from `@vitest/browser/context` that uses Chrome DevTools Protocol or Webdriver (depending on the provider) under the hood.
+:::
+
+::: code-block
```tsx [preact]
// based on @testing-library/preact example
// https://testing-library.com/docs/preact-testing-library/example
import { h } from 'preact'
-import { userEvent } from '@vitest/browser/context'
+import { page } from '@vitest/browser/context'
import { render } from '@testing-library/preact'
import HiddenMessage from '../hidden-message'
@@ -452,19 +449,21 @@ import HiddenMessage from '../hidden-message'
test('shows the children when the checkbox is checked', async () => {
const testMessage = 'Test Message'
- const { queryByText, getByLabelText, getByText } = render(
+ const { baseElement } = render(
{testMessage},
)
- // query* functions will return the element or null if it cannot be found.
- // get* functions will return the element or throw an error if it cannot be found.
- expect(queryByText(testMessage)).not.toBeInTheDocument()
+ 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 userEvent.click(getByLabelText(/show/i))
+ await screen.getByLabelText(/show/i).click()
- expect(getByText(testMessage)).toBeInTheDocument()
+ await expect.element(screen.getByText(testMessage)).toBeInTheDocument()
})
```
```tsx [solid]
@@ -488,8 +487,10 @@ it('uses params', async () => {
Start
} />
>
)
- const { findByText } = render(() => , { location: 'ids/1234' })
- expect(await findByText('Id: 1234')).toBeInTheDocument()
+ const { baseElement } = render(() => , { location: 'ids/1234' })
+ const screen = page.elementLocator(baseElement)
+
+ await expect.screen(screen.getByText('Id: 1234')).toBeInTheDocument()
})
```
```ts [marko]
@@ -500,9 +501,10 @@ import { render, screen } from '@marko/testing-library'
import Greeting from './greeting.marko'
test('renders a message', async () => {
- const { container } = await render(Greeting, { name: 'Marko' })
- expect(screen.getByText(/Marko/)).toBeInTheDocument()
- expect(container.firstChild).toMatchInlineSnapshot(`
+ 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(`
Hello, Marko!
`)
})
diff --git a/docs/guide/browser/interactivity-api.md b/docs/guide/browser/interactivity-api.md
index 66bd614babba..537abc23d392 100644
--- a/docs/guide/browser/interactivity-api.md
+++ b/docs/guide/browser/interactivity-api.md
@@ -35,10 +35,6 @@ Almost every `userEvent` method inherits its provider options. To see all availa
```
:::
-::: warning
-This page uses `@testing-library/dom` in examples to query elements. If you are using a framework like Vue, React or any other, use `@testing-library/{framework-name}` instead. Simple examples are available on the [Browser Mode page](/guide/browser/#examples).
-:::
-
## userEvent.setup
- **Type:** `() => UserEvent`
@@ -69,13 +65,14 @@ This behaviour is more useful because we do not emulate the keyboard, we actuall
Click on an element. Inherits provider's options. Please refer to your provider's documentation for detailed explanation about how this method works.
```ts
-import { userEvent } from '@vitest/browser/context'
-import { screen } from '@testing-library/dom'
+import { page, userEvent } from '@vitest/browser/context'
test('clicks on an element', async () => {
- const logo = screen.getByRole('img', { name: /logo/ })
+ const logo = page.getByRole('img', { name: /logo/ })
await userEvent.click(logo)
+ // or you can access it directly on the locator
+ await logo.click()
})
```
@@ -94,13 +91,14 @@ Triggers a double click event on an element.
Please refer to your provider's documentation for detailed explanation about how this method works.
```ts
-import { userEvent } from '@vitest/browser/context'
-import { screen } from '@testing-library/dom'
+import { page, userEvent } from '@vitest/browser/context'
test('triggers a double click on an element', async () => {
- const logo = screen.getByRole('img', { name: /logo/ })
+ const logo = page.getByRole('img', { name: /logo/ })
await userEvent.dblClick(logo)
+ // or you can access it directly on the locator
+ await logo.dblClick()
})
```
@@ -119,11 +117,10 @@ Triggers a triple click event on an element. Since there is no `tripleclick` in
Please refer to your provider's documentation for detailed explanation about how this method works.
```ts
-import { userEvent } from '@vitest/browser/context'
-import { screen } from '@testing-library/dom'
+import { page, userEvent } from '@vitest/browser/context'
test('triggers a triple click on an element', async () => {
- const logo = screen.getByRole('img', { name: /logo/ })
+ const logo = page.getByRole('img', { name: /logo/ })
let tripleClickFired = false
logo.addEventListener('click', (evt) => {
if (evt.detail === 3) {
@@ -132,6 +129,9 @@ test('triggers a triple click on an element', async () => {
})
await userEvent.tripleClick(logo)
+ // or you can access it directly on the locator
+ await logo.tripleClick()
+
expect(tripleClickFired).toBe(true)
})
```
@@ -149,15 +149,17 @@ References:
Set a value to the `input/textarea/conteneditable` field. This will remove any existing text in the input before setting the new value.
```ts
-import { userEvent } from '@vitest/browser/context'
-import { screen } from '@testing-library/dom'
+import { page, userEvent } from '@vitest/browser/context'
test('update input', async () => {
- const input = screen.getByRole('input')
+ const input = page.getByRole('input')
await userEvent.fill(input, 'foo') // input.value == foo
await userEvent.fill(input, '{{a[[') // input.value == {{a[[
await userEvent.fill(input, '{Shift}') // input.value == {Shift}
+
+ // or you can access it directly on the locator
+ await input.fill('foo') // input.value == foo
})
```
@@ -208,11 +210,10 @@ References:
Sends a `Tab` key event. This is a shorthand for `userEvent.keyboard('{tab}')`.
```ts
-import { userEvent } from '@vitest/browser/context'
-import { screen } from '@testing-library/dom'
+import { page, userEvent } from '@vitest/browser/context'
test('tab works', async () => {
- const [input1, input2] = screen.getAllByRole('input')
+ const [input1, input2] = page.getByRole('input').elements()
expect(input1).toHaveFocus()
@@ -247,11 +248,10 @@ This function allows you to type characters into an input/textarea/conteneditabl
If you just need to press characters without an input, use [`userEvent.keyboard`](#userevent-keyboard) API.
```ts
-import { userEvent } from '@vitest/browser/context'
-import { screen } from '@testing-library/dom'
+import { page, userEvent } from '@vitest/browser/context'
test('update input', async () => {
- const input = screen.getByRole('input')
+ const input = page.getByRole('input')
await userEvent.type(input, 'foo') // input.value == foo
await userEvent.type(input, '{{a[[') // input.value == foo{a[
@@ -259,6 +259,10 @@ test('update input', async () => {
})
```
+::: note
+Vitest doesn't expose `.type` method on the locator like `input.type` because it exists only for compatiblity with the `userEvent` library. Consider using `.fill` instead as it is faster.
+:::
+
References:
- [Playwright `locator.press` API](https://playwright.dev/docs/api/class-locator#locator-press)
@@ -272,16 +276,18 @@ References:
This method clears the input element content.
```ts
-import { userEvent } from '@vitest/browser/context'
-import { screen } from '@testing-library/dom'
+import { page, userEvent } from '@vitest/browser/context'
test('clears input', async () => {
- const input = screen.getByRole('input')
+ const input = page.getByRole('input')
await userEvent.fill(input, 'foo')
expect(input).toHaveValue('foo')
await userEvent.clear(input)
+ // or you can access it directly on the locator
+ await input.clear()
+
expect(input).toHaveValue('')
})
```
@@ -305,21 +311,23 @@ Unlike `@testing-library`, Vitest doesn't support [listbox](https://developer.mo
:::
```ts
-import { userEvent } from '@vitest/browser/context'
-import { screen } from '@testing-library/dom'
+import { page, userEvent } from '@vitest/browser/context'
test('clears input', async () => {
- const select = screen.getByRole('select')
+ const select = page.getByRole('select')
await userEvent.selectOptions(select, 'Option 1')
+ // or you can access it directly on the locator
+ await select.selectOptions('Option 1')
+
expect(select).toHaveValue('option-1')
await userEvent.selectOptions(select, 'option-1')
expect(select).toHaveValue('option-1')
await userEvent.selectOptions(select, [
- screen.getByRole('option', { name: 'Option 1' }),
- screen.getByRole('option', { name: 'Option 2' }),
+ page.getByRole('option', { name: 'Option 1' }),
+ page.getByRole('option', { name: 'Option 2' }),
])
expect(select).toHaveValue(['option-1', 'option-2'])
})
@@ -348,13 +356,14 @@ If you are using `playwright` provider, the cursor moves to "some" visible point
:::
```ts
-import { userEvent } from '@vitest/browser/context'
-import { screen } from '@testing-library/dom'
+import { page, userEvent } from '@vitest/browser/context'
test('hovers logo element', async () => {
- const logo = screen.getByRole('img', { name: /logo/ })
+ const logo = page.getByRole('img', { name: /logo/ })
await userEvent.hover(logo)
+ // or you can access it directly on the locator
+ await page.hover()
})
```
@@ -375,13 +384,14 @@ By default, the cursor position is in "some" visible place (in `playwright` prov
:::
```ts
-import { userEvent } from '@vitest/browser/context'
-import { screen } from '@testing-library/dom'
+import { page, userEvent } from '@vitest/browser/context'
test('unhover logo element', async () => {
- const logo = screen.getByRole('img', { name: /logo/ })
+ const logo = page.getByRole('img', { name: /logo/ })
await userEvent.unhover(logo)
+ // or you can access it directly on the locator
+ await page.unhover()
})
```
@@ -398,14 +408,15 @@ References:
Drags the source element on top of the target element. Don't forget that the `source` element has to have the `draggable` attribute set to `true`.
```ts
-import { userEvent } from '@vitest/browser/context'
-import { screen } from '@testing-library/dom'
+import { page, userEvent } from '@vitest/browser/context'
test('drag and drop works', async () => {
- const source = screen.getByRole('img', { name: /logo/ })
- const target = screen.getByTestId('logo-target')
+ const source = page.getByRole('img', { name: /logo/ })
+ const target = page.getByTestId('logo-target')
await userEvent.dragAndDrop(source, target)
+ // or you can access it directly on the locator
+ await source.dropTo(target)
await expect.element(target).toHaveTextContent('Logo is processed')
})
diff --git a/docs/guide/reporters.md b/docs/guide/reporters.md
index e18e974d4116..65a9d98ad38f 100644
--- a/docs/guide/reporters.md
+++ b/docs/guide/reporters.md
@@ -505,4 +505,4 @@ Additionally, you can define your own [custom reporters](/advanced/reporters) an
npx vitest --reporter=./path/to/reporter.ts
```
-Custom reporters should implement the [Reporter interface](https://github.com/vitest-dev/vitest/blob/main/packages/vitest/src/types/reporter.ts).
+Custom reporters should implement the [Reporter interface](https://github.com/vitest-dev/vitest/blob/main/packages/vitest/src/node/types/reporter.ts).
diff --git a/packages/browser/src/client/tester/locators/index.ts b/packages/browser/src/client/tester/locators/index.ts
index 0816f2fd54dd..90779164c698 100644
--- a/packages/browser/src/client/tester/locators/index.ts
+++ b/packages/browser/src/client/tester/locators/index.ts
@@ -150,7 +150,7 @@ export abstract class Locator {
public element(): Element {
const element = this.query()
if (!element) {
- throw getElementError(this._pwSelector || this.selector, this._container || document.documentElement)
+ throw getElementError(this._pwSelector || this.selector, this._container || document.body)
}
return element
}
diff --git a/packages/browser/src/client/tester/locators/preview.ts b/packages/browser/src/client/tester/locators/preview.ts
index f1484af9bef9..567b42122f04 100644
--- a/packages/browser/src/client/tester/locators/preview.ts
+++ b/packages/browser/src/client/tester/locators/preview.ts
@@ -52,7 +52,7 @@ class PreviewLocator extends Locator {
override get selector() {
const selectors = this.elements().map(element => convertElementToCssSelector(element))
if (!selectors.length) {
- throw getElementError(this._pwSelector, this._container || document.documentElement)
+ throw getElementError(this._pwSelector, this._container || document.body)
}
return selectors.join(', ')
}
diff --git a/packages/browser/src/client/tester/locators/webdriverio.ts b/packages/browser/src/client/tester/locators/webdriverio.ts
index 7313d9493ff5..3a9b7b947d0c 100644
--- a/packages/browser/src/client/tester/locators/webdriverio.ts
+++ b/packages/browser/src/client/tester/locators/webdriverio.ts
@@ -48,7 +48,7 @@ class WebdriverIOLocator extends Locator {
override get selector() {
const selectors = this.elements().map(element => convertElementToCssSelector(element))
if (!selectors.length) {
- throw getElementError(this._pwSelector, this._container || document.documentElement)
+ throw getElementError(this._pwSelector, this._container || document.body)
}
return selectors.join(', ')
}
diff --git a/packages/browser/src/node/plugin.ts b/packages/browser/src/node/plugin.ts
index 38b72b87aa9b..749442364baa 100644
--- a/packages/browser/src/node/plugin.ts
+++ b/packages/browser/src/node/plugin.ts
@@ -205,6 +205,52 @@ export default (browserServer: BrowserServer, base = '/'): Plugin[] => {
}
}
+ const include = [
+ 'vitest > @vitest/snapshot > magic-string',
+ 'vitest > chai',
+ 'vitest > chai > loupe',
+ 'vitest > @vitest/utils > loupe',
+ '@vitest/browser > @testing-library/user-event',
+ '@vitest/browser > @testing-library/dom',
+ ]
+
+ const react = tryResolve('vitest-browser-react', [project.ctx.config.root])
+ if (react) {
+ include.push(react)
+ }
+ const vue = tryResolve('vitest-browser-react', [project.ctx.config.root])
+ if (vue) {
+ include.push(vue)
+ }
+
+ const exclude = [
+ 'vitest',
+ 'vitest/utils',
+ 'vitest/browser',
+ 'vitest/runners',
+ '@vitest/browser',
+ '@vitest/browser/client',
+ '@vitest/utils',
+ '@vitest/utils/source-map',
+ '@vitest/runner',
+ '@vitest/spy',
+ '@vitest/utils/error',
+ '@vitest/snapshot',
+ '@vitest/expect',
+ 'std-env',
+ 'tinybench',
+ 'tinyspy',
+ 'tinyrainbow',
+ 'pathe',
+ 'msw',
+ 'msw/browser',
+ ]
+
+ const svelte = tryResolve('vitest-browser-svelte', [project.ctx.config.root])
+ if (svelte) {
+ exclude.push(svelte)
+ }
+
return {
define,
resolve: {
@@ -212,36 +258,8 @@ export default (browserServer: BrowserServer, base = '/'): Plugin[] => {
},
optimizeDeps: {
entries,
- exclude: [
- 'vitest',
- 'vitest/utils',
- 'vitest/browser',
- 'vitest/runners',
- '@vitest/browser',
- '@vitest/browser/client',
- '@vitest/utils',
- '@vitest/utils/source-map',
- '@vitest/runner',
- '@vitest/spy',
- '@vitest/utils/error',
- '@vitest/snapshot',
- '@vitest/expect',
- 'std-env',
- 'tinybench',
- 'tinyspy',
- 'tinyrainbow',
- 'pathe',
- 'msw',
- 'msw/browser',
- ],
- include: [
- 'vitest > @vitest/snapshot > magic-string',
- 'vitest > chai',
- 'vitest > chai > loupe',
- 'vitest > @vitest/utils > loupe',
- '@vitest/browser > @testing-library/user-event',
- '@vitest/browser > @testing-library/dom',
- ],
+ exclude,
+ include,
},
}
},
diff --git a/packages/browser/src/node/serverTester.ts b/packages/browser/src/node/serverTester.ts
index 6768db439e4e..6dccc013d20f 100644
--- a/packages/browser/src/node/serverTester.ts
+++ b/packages/browser/src/node/serverTester.ts
@@ -76,11 +76,11 @@ export async function resolveTester(
``,
server.locatorsUrl ? `` : '',
].join('\n'),
- __VITEST_APPEND__:
- ``,
})
}
diff --git a/packages/browser/utils.d.ts b/packages/browser/utils.d.ts
index fa8790aeef97..81fb50bf0c6d 100644
--- a/packages/browser/utils.d.ts
+++ b/packages/browser/utils.d.ts
@@ -5,7 +5,7 @@
import { LocatorSelectors } from '@vitest/browser/context'
import { StringifyOptions } from 'vitest/utils'
-type PrettyDOMOptions = Omit
+export type PrettyDOMOptions = Omit
export declare function getElementLocatorSelectors(element: Element): LocatorSelectors
export declare function debug(
diff --git a/packages/vitest/src/create/browser/creator.ts b/packages/vitest/src/create/browser/creator.ts
index 7de4fb638333..df5903f16869 100644
--- a/packages/vitest/src/create/browser/creator.ts
+++ b/packages/vitest/src/create/browser/creator.ts
@@ -105,13 +105,13 @@ function getFramework(): prompt.Choice[] {
function getFrameworkTestPackage(framework: string) {
switch (framework) {
case 'vanilla':
- return '@testing-library/dom'
+ return null
case 'vue':
- return '@testing-library/vue'
+ return 'vitest-browser-vue'
case 'svelte':
- return '@testing-library/svelte'
+ return 'vitest-browser-svelte'
case 'react':
- return '@testing-library/react'
+ return 'vitest-browser-react'
case 'preact':
return '@testing-library/preact'
case 'solid':
@@ -431,9 +431,13 @@ export async function create() {
const dependenciesToInstall = [
'@vitest/browser',
- getFrameworkTestPackage(framework),
]
+ const frameworkPackage = getFrameworkTestPackage(framework)
+ if (frameworkPackage) {
+ dependenciesToInstall.push(frameworkPackage)
+ }
+
const providerPkg = getProviderPackageNames(provider)
if (providerPkg.pkg) {
dependenciesToInstall.push(providerPkg.pkg)
diff --git a/packages/vitest/src/create/browser/examples.ts b/packages/vitest/src/create/browser/examples.ts
index b492b89155c0..6be47158ca88 100644
--- a/packages/vitest/src/create/browser/examples.ts
+++ b/packages/vitest/src/create/browser/examples.ts
@@ -29,8 +29,7 @@ import HelloWorld from './HelloWorld.jsx'
test('renders name', () => {
const { getByText } = render()
- const element = getByText('Hello Vitest!')
- expect(element).toBeInTheDocument()
+ await expect.element(getByText('Hello Vitest!')).toBeInTheDocument()
})
`,
}
@@ -65,15 +64,14 @@ defineProps<{
`,
test: `
import { expect, test } from 'vitest'
-import { render } from '@testing-library/vue'
+import { render } from 'vitest-browser-vue'
import HelloWorld from './HelloWorld.vue'
test('renders name', () => {
const { getByText } = render(HelloWorld, {
props: { name: 'Vitest' },
})
- const element = getByText('Hello Vitest!')
- expect(element).toBeInTheDocument()
+ await expect.element(getByText('Hello Vitest!')).toBeInTheDocument()
})
`,
}
@@ -96,15 +94,12 @@ const svelteExample = {
`,
test: `
import { expect, test } from 'vitest'
-import { render } from '@testing-library/svelte'
+import { render } from 'vitest-browser-svelte'
import HelloWorld from './HelloWorld.svelte'
test('renders name', () => {
- const { getByText } = render(HelloWorld, {
- props: { name: 'Vitest' },
- })
- const element = getByText('Hello Vitest!')
- expect(element).toBeInTheDocument()
+ const { getByText } = render(HelloWorld, { name: 'Vitest' })
+ await expect.element(getByText('Hello Vitest!')).toBeInTheDocument()
})
`,
}
@@ -183,11 +178,15 @@ function getExampleTest(framework: string) {
switch (framework) {
case 'solid':
case 'preact':
- case 'react':
return {
...jsxExample,
test: jsxExample.test.replace('@testing-library/jsx', `@testing-library/${framework}`),
}
+ case 'react':
+ return {
+ ...jsxExample,
+ test: jsxExample.test.replace('@testing-library/jsx', 'vitest-browser-react'),
+ }
case 'vue':
return vueExample
case 'svelte':
diff --git a/packages/vitest/src/node/cli/cli-config.ts b/packages/vitest/src/node/cli/cli-config.ts
index 960a3dcb3829..b9bf3295262e 100644
--- a/packages/vitest/src/node/cli/cli-config.ts
+++ b/packages/vitest/src/node/cli/cli-config.ts
@@ -794,6 +794,7 @@ export const cliOptionsConfig: VitestCLIOptions = {
compare: null,
outputJson: null,
json: null,
+ provide: null,
}
export const benchCliOptionsConfig: Pick<
diff --git a/packages/vitest/src/node/config/resolveConfig.ts b/packages/vitest/src/node/config/resolveConfig.ts
index 939ab65ba7fd..e75e9d617ba0 100644
--- a/packages/vitest/src/node/config/resolveConfig.ts
+++ b/packages/vitest/src/node/config/resolveConfig.ts
@@ -140,6 +140,8 @@ export function resolveConfig(
mode,
} as any as ResolvedConfig
+ resolved.provide ??= {}
+
const inspector = resolved.inspect || resolved.inspectBrk
resolved.inspector = {
diff --git a/packages/vitest/src/node/reporters/blob.ts b/packages/vitest/src/node/reporters/blob.ts
index 4f780e93fa6f..961d9ed492f3 100644
--- a/packages/vitest/src/node/reporters/blob.ts
+++ b/packages/vitest/src/node/reporters/blob.ts
@@ -1,4 +1,4 @@
-import { mkdir, readFile, readdir, writeFile } from 'node:fs/promises'
+import { mkdir, readFile, readdir, stat, writeFile } from 'node:fs/promises'
import { existsSync } from 'node:fs'
import { parse, stringify } from 'flatted'
import { dirname, resolve } from 'pathe'
@@ -79,18 +79,37 @@ export async function readBlobs(
// using process.cwd() because --merge-reports can only be used in CLI
const resolvedDir = resolve(process.cwd(), blobsDirectory)
const blobsFiles = await readdir(resolvedDir)
- const promises = blobsFiles.map(async (file) => {
- const content = await readFile(resolve(resolvedDir, file), 'utf-8')
+ const promises = blobsFiles.map(async (filename) => {
+ const fullPath = resolve(resolvedDir, filename)
+ const stats = await stat(fullPath)
+ if (!stats.isFile()) {
+ throw new TypeError(
+ `vitest.mergeReports() expects all paths in "${blobsDirectory}" to be files generated by the blob reporter, but "${filename}" is not a file`,
+ )
+ }
+ const content = await readFile(fullPath, 'utf-8')
const [version, files, errors, moduleKeys, coverage] = parse(
content,
) as MergeReport
- return { version, files, errors, moduleKeys, coverage }
+ if (!version) {
+ throw new TypeError(
+ `vitest.mergeReports() expects all paths in "${blobsDirectory}" to be files generated by the blob reporter, but "${filename}" is not a valid blob file`,
+ )
+ }
+ return { version, files, errors, moduleKeys, coverage, file: filename }
})
const blobs = await Promise.all(promises)
if (!blobs.length) {
throw new Error(
- `vitest.mergeReports() requires at least one blob file paths in the config`,
+ `vitest.mergeReports() requires at least one blob file in "${blobsDirectory}" directory, but none were found`,
+ )
+ }
+
+ const versions = new Set(blobs.map(blob => blob.version))
+ if (versions.size > 1) {
+ throw new Error(
+ `vitest.mergeReports() requires all blob files to be generated by the same Vitest version, received\n\n${blobs.map(b => `- "${b.file}" uses v${b.version}`).join('\n')}`,
)
}
diff --git a/packages/vitest/src/node/types/config.ts b/packages/vitest/src/node/types/config.ts
index bf866ec4fa8a..019b0f6714f4 100644
--- a/packages/vitest/src/node/types/config.ts
+++ b/packages/vitest/src/node/types/config.ts
@@ -10,7 +10,7 @@ import type {
} from '../reporters'
import type { TestSequencerConstructor } from '../sequencers/types'
import type { ChaiConfig } from '../../integrations/chai/config'
-import type { Arrayable, ErrorWithDiff, ParsedStack } from '../../types/general'
+import type { Arrayable, ErrorWithDiff, ParsedStack, ProvidedContext } from '../../types/general'
import type { JSDOMOptions } from '../../types/jsdom-options'
import type { HappyDOMOptions } from '../../types/happy-dom-options'
import type { EnvironmentOptions } from '../../types/environment'
@@ -731,6 +731,27 @@ export interface InlineConfig {
waitForDebugger?: boolean
}
+ /**
+ * Define variables that will be returned from `inject` in the test environment.
+ * @example
+ * ```ts
+ * // vitest.config.ts
+ * export default defineConfig({
+ * test: {
+ * provide: {
+ * someKey: 'someValue'
+ * }
+ * }
+ * })
+ * ```
+ * ```ts
+ * // test file
+ * import { inject } from 'vitest'
+ * const value = inject('someKey') // 'someValue'
+ * ```
+ */
+ provide?: Partial
+
/**
* Configuration options for expect() matches.
*/
diff --git a/packages/vitest/src/node/workspace.ts b/packages/vitest/src/node/workspace.ts
index 40a6a21b5ed8..e85b5d1275e2 100644
--- a/packages/vitest/src/node/workspace.ts
+++ b/packages/vitest/src/node/workspace.ts
@@ -364,6 +364,14 @@ export class WorkspaceProject {
project.server = ctx.server
project.runner = ctx.runner
project.config = ctx.config
+ for (const _providedKey in ctx.config.provide) {
+ const providedKey = _providedKey as keyof ProvidedContext
+ // type is very strict here, so we cast it to any
+ (project.provide as (key: string, value: unknown) => void)(
+ providedKey,
+ ctx.config.provide[providedKey],
+ )
+ }
project.testProject = new TestProject(project)
return project
}
@@ -384,6 +392,15 @@ export class WorkspaceProject {
server.config,
this.ctx.logger,
)
+ for (const _providedKey in this.config.provide) {
+ const providedKey = _providedKey as keyof ProvidedContext
+ // type is very strict here, so we cast it to any
+ (this.provide as (key: string, value: unknown) => void)(
+ providedKey,
+ this.config.provide[providedKey],
+ )
+ }
+
this.testProject = new TestProject(this)
this.server = server
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 503be041058b..8d85c8b2c0fc 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1090,9 +1090,6 @@ importers:
'@vitest/injected-lib':
specifier: link:./injected-lib
version: link:injected-lib
- execa:
- specifier: ^7.1.1
- version: 7.1.1
playwright:
specifier: ^1.41.0
version: 1.41.0
@@ -1111,6 +1108,9 @@ importers:
vitest:
specifier: workspace:*
version: link:../../packages/vitest
+ vitest-browser-react:
+ specifier: ^0.0.1
+ version: 0.0.1(@types/react-dom@18.2.14)(@types/react@18.2.79)(@vitest/browser@packages+browser)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vitest@packages+vitest)
webdriverio:
specifier: ^8.32.2
version: 8.32.2(typescript@5.5.4)
@@ -1135,9 +1135,6 @@ importers:
debug:
specifier: ^4.3.4
version: 4.3.4
- execa:
- specifier: ^8.0.1
- version: 8.0.1
unplugin-swc:
specifier: ^1.4.4
version: 1.4.4(@swc/core@1.4.1)(rollup@4.20.0)
@@ -1293,7 +1290,7 @@ importers:
version: 3.4.37(typescript@5.5.4)
webdriverio:
specifier: latest
- version: 8.40.0
+ version: 8.40.2
test/global-setup:
devDependencies:
@@ -1385,9 +1382,6 @@ importers:
'@types/inquirer':
specifier: ^9.0.3
version: 9.0.3
- execa:
- specifier: ^6.1.0
- version: 6.1.0
inquirer:
specifier: ^9.2.7
version: 9.2.7
@@ -1411,7 +1405,7 @@ importers:
version: link:../../packages/vitest
webdriverio:
specifier: latest
- version: 8.40.0
+ version: 8.40.2
test/workspaces:
devDependencies:
@@ -4210,8 +4204,8 @@ packages:
resolution: {integrity: sha512-yNuGPMPibY91s936gnJCHWlStvIyDrwLwGfLC/NCdTin4F7HL4Gp5iJnHWkJFty1/DfFi8jjoIUBNLM8HEez+A==}
engines: {node: ^16.13 || >=18}
- '@wdio/config@8.40.0':
- resolution: {integrity: sha512-sE+sBXUz4ZggS253hLNVu64ZCpm5ZidQ/IJNeM9Exh5OcsuZEnSeuqCZnd4ytK68A2heyZk8r2OjYZriA4l/Sg==}
+ '@wdio/config@8.40.2':
+ resolution: {integrity: sha512-RED2vcdX5Zdd6r+K+aWcjK4douxjJY4LP/8YvvavgqM0TURd5PDI0Y7IEz7+BIJOT4Uh+3atZawIN9/3yWFeag==}
engines: {node: ^16.13 || >=18}
'@wdio/logger@8.28.0':
@@ -4248,8 +4242,8 @@ packages:
resolution: {integrity: sha512-jY+n6jlGeK+9Tx8T659PKLwMQTGpLW5H78CSEWgZLbjbVSr2LfGR8Lx0CRktNXxAtqEVZPj16Pi74OtAhvhE6Q==}
engines: {node: ^16.13 || >=18}
- '@wdio/utils@8.40.0':
- resolution: {integrity: sha512-P9b6XbRDRfCsZvdA70VYQrnsbkDVwEXlAGe4v4hcdgFxz81w+k4IX5bFUb7IB33E+3EZ/GhJWVU3QHgI9Y0u6w==}
+ '@wdio/utils@8.40.2':
+ resolution: {integrity: sha512-leYcCUSaAdLUCVKqRKNgMCASPOUo/VvOTKETiZ/qpdY2azCBt/KnLugtiycCzakeYg6Kp+VIjx5fkm0M7y4qhA==}
engines: {node: ^16.13 || >=18}
'@yeger/debounce@2.0.9':
@@ -5605,10 +5599,6 @@ packages:
resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
engines: {node: '>=10'}
- execa@6.1.0:
- resolution: {integrity: sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==}
- engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
-
execa@7.1.1:
resolution: {integrity: sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==}
engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0}
@@ -6151,10 +6141,6 @@ packages:
resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
engines: {node: '>=10.17.0'}
- human-signals@3.0.1:
- resolution: {integrity: sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==}
- engines: {node: '>=12.20.0'}
-
human-signals@4.3.0:
resolution: {integrity: sha512-zyzVyMjpGBX2+6cDVZeFPCdtOtdsxOeseRhB9tkQ6xXmGUNrcnBzdEKPy3VPNYz+4gy1oukVOXcrJCunSyc6QQ==}
engines: {node: '>=14.18.0'}
@@ -8908,6 +8894,22 @@ packages:
postcss:
optional: true
+ vitest-browser-react@0.0.1:
+ resolution: {integrity: sha512-075hxhouuuMeCqHRyX4POzZWEDD3aPxFxDcwENN+T2ZlJ4lh5vUlgbkV4C4A0KZWinHb/Wsj3pYEJlDgIO23CQ==}
+ engines: {node: ^18.0.0 || >=20.0.0}
+ peerDependencies:
+ '@types/react': '>18.0.0'
+ '@types/react-dom': '>18.0.0'
+ '@vitest/browser': ^2.1.0-beta.4
+ react: '>18.0.0'
+ react-dom: '>18.0.0'
+ vitest: workspace:*
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
vitest-environment-custom@file:test/core/vitest-environment-custom:
resolution: {directory: test/core/vitest-environment-custom, type: directory}
@@ -9012,8 +9014,8 @@ packages:
resolution: {integrity: sha512-Kc3+SfiH4ufyrIht683VT2vnJocx0pfH8rYdyPvEh1b2OYewtFTHK36k9rBDHZiBmk6jcSXs4M2xeFgOuon9Lg==}
engines: {node: ^16.13 || >=18}
- webdriver@8.40.0:
- resolution: {integrity: sha512-pAuU8FbFXox837UgxjC2yT4s+goLBcqohdCSZJ1f1wG/XMsgjDHmouU6+f1SHHG7/I0IDGEZIsRD01RM57F3OA==}
+ webdriver@8.40.2:
+ resolution: {integrity: sha512-GoRR94m3yL8tWC9Myf+xIBSdVK8fi1ilZgEZZaYT8+XIWewR02dvrC6rml+/2ZjXUQzeee0RFGDwk9IC7cyYrg==}
engines: {node: ^16.13 || >=18}
webdriverio@8.32.2:
@@ -9034,8 +9036,8 @@ packages:
devtools:
optional: true
- webdriverio@8.40.0:
- resolution: {integrity: sha512-AYFLdfVt3wcDdnyxRDBlysOgB3XryLZrZdmtjUU842IyMcoV4Cq3SdVgz9aj9tskSeIJ3G37KgDEf5znnd5f3Q==}
+ webdriverio@8.40.2:
+ resolution: {integrity: sha512-6yuzUlE064qNuMy98Du1+8QHbXk0st8qTWF7MDZRgYK19FGoy+KhQbaUv1wlFJuFHM0PiAYuduTURL4ub6HvzQ==}
engines: {node: ^16.13 || >=18}
peerDependencies:
devtools: ^8.14.0
@@ -12613,11 +12615,11 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@wdio/config@8.40.0':
+ '@wdio/config@8.40.2':
dependencies:
'@wdio/logger': 8.38.0
'@wdio/types': 8.39.0
- '@wdio/utils': 8.40.0
+ '@wdio/utils': 8.40.2
decamelize: 6.0.0
deepmerge-ts: 5.1.0
glob: 10.4.1
@@ -12691,9 +12693,9 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@wdio/utils@8.40.0':
+ '@wdio/utils@8.40.2':
dependencies:
- '@puppeteer/browsers': 1.7.0
+ '@puppeteer/browsers': 1.9.1
'@wdio/logger': 8.38.0
'@wdio/types': 8.39.0
decamelize: 6.0.0
@@ -14258,18 +14260,6 @@ snapshots:
signal-exit: 3.0.7
strip-final-newline: 2.0.0
- execa@6.1.0:
- dependencies:
- cross-spawn: 7.0.3
- get-stream: 6.0.1
- human-signals: 3.0.1
- is-stream: 3.0.0
- merge-stream: 2.0.0
- npm-run-path: 5.1.0
- onetime: 6.0.0
- signal-exit: 3.0.7
- strip-final-newline: 3.0.0
-
execa@7.1.1:
dependencies:
cross-spawn: 7.0.3
@@ -14967,8 +14957,6 @@ snapshots:
human-signals@2.1.0: {}
- human-signals@3.0.1: {}
-
human-signals@4.3.0: {}
human-signals@5.0.0: {}
@@ -18096,6 +18084,16 @@ snapshots:
- typescript
- universal-cookie
+ vitest-browser-react@0.0.1(@types/react-dom@18.2.14)(@types/react@18.2.79)(@vitest/browser@packages+browser)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(vitest@packages+vitest):
+ dependencies:
+ '@vitest/browser': link:packages/browser
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ vitest: link:packages/vitest
+ optionalDependencies:
+ '@types/react': 18.2.79
+ '@types/react-dom': 18.2.14
+
vitest-environment-custom@file:test/core/vitest-environment-custom: {}
vitest-sonar-reporter@0.4.1(vitest@packages+vitest):
@@ -18225,15 +18223,15 @@ snapshots:
- supports-color
- utf-8-validate
- webdriver@8.40.0:
+ webdriver@8.40.2:
dependencies:
'@types/node': 20.14.14
'@types/ws': 8.5.12
- '@wdio/config': 8.40.0
+ '@wdio/config': 8.40.2
'@wdio/logger': 8.38.0
'@wdio/protocols': 8.38.0
'@wdio/types': 8.39.0
- '@wdio/utils': 8.40.0
+ '@wdio/utils': 8.40.2
deepmerge-ts: 5.1.0
got: 12.6.1
ky: 0.33.3
@@ -18310,15 +18308,15 @@ snapshots:
- typescript
- utf-8-validate
- webdriverio@8.40.0:
+ webdriverio@8.40.2:
dependencies:
'@types/node': 20.14.13
- '@wdio/config': 8.40.0
+ '@wdio/config': 8.40.2
'@wdio/logger': 8.38.0
'@wdio/protocols': 8.38.0
'@wdio/repl': 8.24.12
'@wdio/types': 8.39.0
- '@wdio/utils': 8.40.0
+ '@wdio/utils': 8.40.2
archiver: 7.0.1
aria-query: 5.3.0
css-shorthand-properties: 1.1.1
@@ -18336,7 +18334,7 @@ snapshots:
resq: 1.11.0
rgb2hex: 0.2.5
serialize-error: 11.0.2
- webdriver: 8.40.0
+ webdriver: 8.40.2
transitivePeerDependencies:
- bufferutil
- encoding
diff --git a/test/browser/fixtures/locators/blog.test.tsx b/test/browser/fixtures/locators/blog.test.tsx
index 1a50c29e2aff..fc54b916e3c6 100644
--- a/test/browser/fixtures/locators/blog.test.tsx
+++ b/test/browser/fixtures/locators/blog.test.tsx
@@ -1,12 +1,9 @@
import { expect, test } from 'vitest'
-import { render } from '@testing-library/react'
import { page } from '@vitest/browser/context'
import Blog from '../../src/blog-app/blog'
test('renders blog posts', async () => {
- const { container } = render()
-
- const screen = page.elementLocator(container)
+ const screen = page.render()
await expect.element(screen.getByRole('heading', { name: 'Blog' })).toBeInTheDocument()
diff --git a/test/browser/fixtures/locators/vitest.config.ts b/test/browser/fixtures/locators/vitest.config.ts
index 8edb8f6cf049..e32545f4ab22 100644
--- a/test/browser/fixtures/locators/vitest.config.ts
+++ b/test/browser/fixtures/locators/vitest.config.ts
@@ -11,6 +11,7 @@ export default defineConfig({
},
cacheDir: fileURLToPath(new URL("./node_modules/.vite", import.meta.url)),
test: {
+ setupFiles: ['vitest-browser-react'],
browser: {
enabled: true,
provider,
diff --git a/test/browser/package.json b/test/browser/package.json
index 57609afb6308..9d7384c67a3b 100644
--- a/test/browser/package.json
+++ b/test/browser/package.json
@@ -25,13 +25,13 @@
"@vitest/browser": "workspace:*",
"@vitest/cjs-lib": "link:./cjs-lib",
"@vitest/injected-lib": "link:./injected-lib",
- "execa": "^7.1.1",
"playwright": "^1.41.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"strip-ansi": "^7.1.0",
"url": "^0.11.3",
"vitest": "workspace:*",
+ "vitest-browser-react": "^0.0.1",
"webdriverio": "^8.32.2"
}
}
diff --git a/test/browser/tsconfig.json b/test/browser/tsconfig.json
index 06bca217a45c..0468f44e9342 100644
--- a/test/browser/tsconfig.json
+++ b/test/browser/tsconfig.json
@@ -8,7 +8,8 @@
},
"types": [
"vite/client",
- "@vitest/browser/providers/playwright"
+ "@vitest/browser/providers/playwright",
+ "vitest-browser-react"
],
"esModuleInterop": true
}
diff --git a/test/cli/package.json b/test/cli/package.json
index 80e7e4629463..f8fa07c1f3c9 100644
--- a/test/cli/package.json
+++ b/test/cli/package.json
@@ -12,7 +12,6 @@
"@vitest/runner": "workspace:^",
"@vitest/utils": "workspace:*",
"debug": "^4.3.4",
- "execa": "^8.0.1",
"unplugin-swc": "^1.4.4",
"vite": "latest",
"vitest": "workspace:*",
diff --git a/test/vite-node/package.json b/test/vite-node/package.json
index 6bb382c179f7..69eb99cee32a 100644
--- a/test/vite-node/package.json
+++ b/test/vite-node/package.json
@@ -11,7 +11,6 @@
},
"devDependencies": {
"@types/inquirer": "^9.0.3",
- "execa": "^6.1.0",
"inquirer": "^9.2.7",
"vite-node": "workspace:*",
"vitest": "workspace:*"
diff --git a/test/workspaces/globalTest.ts b/test/workspaces/globalTest.ts
index 771dca6aed17..439a06a3dec5 100644
--- a/test/workspaces/globalTest.ts
+++ b/test/workspaces/globalTest.ts
@@ -7,6 +7,8 @@ declare module 'vitest' {
globalSetup: boolean
globalSetupOverriden: boolean
invalidValue: unknown
+ projectConfigValue: boolean
+ globalConfigValue: boolean
}
}
diff --git a/test/workspaces/space_3/global-provide.space-3-test.ts b/test/workspaces/space_3/global-provide.space-3-test.ts
index 4cf6b70dd48c..2c893c09a99d 100644
--- a/test/workspaces/space_3/global-provide.space-3-test.ts
+++ b/test/workspaces/space_3/global-provide.space-3-test.ts
@@ -3,5 +3,7 @@ import { expect, inject, test } from 'vitest'
test('global setup provides data correctly', () => {
expect(inject('globalSetup')).toBe(true)
expect(inject('globalSetupOverriden')).toBe(true)
+ expect(inject('projectConfigValue')).toBe(true)
+ expect(inject('globalConfigValue')).toBe(true)
expect(inject('invalidValue')).toBe(undefined)
})
diff --git a/test/workspaces/space_3/vitest.config.ts b/test/workspaces/space_3/vitest.config.ts
index 956f9e5c8c84..0d32cd5002de 100644
--- a/test/workspaces/space_3/vitest.config.ts
+++ b/test/workspaces/space_3/vitest.config.ts
@@ -5,5 +5,8 @@ export default defineProject({
include: ['**/*.space-3-test.ts'],
environment: 'node',
globalSetup: './localSetup.ts',
+ provide: {
+ projectConfigValue: true,
+ },
},
})
diff --git a/test/workspaces/vitest.config.ts b/test/workspaces/vitest.config.ts
index ae8998934730..d6bbb3a1a486 100644
--- a/test/workspaces/vitest.config.ts
+++ b/test/workspaces/vitest.config.ts
@@ -16,5 +16,8 @@ export default defineConfig({
CONFIG_VAR: 'root',
CONFIG_OVERRIDE: 'root',
},
+ provide: {
+ globalConfigValue: true,
+ },
},
})