diff --git a/package.json b/package.json
index d540a842fc..163d141818 100644
--- a/package.json
+++ b/package.json
@@ -49,7 +49,6 @@
"@tanstack/typedoc-config": "0.3.1",
"@tanstack/vite-config": "0.4.1",
"@testing-library/jest-dom": "^6.8.0",
- "@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1",
"@testing-library/vue": "^8.1.0",
"@types/node": "^24.1.0",
diff --git a/packages/form-core/vite.config.ts b/packages/form-core/vite.config.ts
index 94d53421f1..715e7060ab 100644
--- a/packages/form-core/vite.config.ts
+++ b/packages/form-core/vite.config.ts
@@ -7,7 +7,6 @@ const config = defineConfig({
name: packageJson.name,
dir: './tests',
watch: false,
- environment: 'jsdom',
coverage: { enabled: true, provider: 'istanbul', include: ['src/**/*'] },
typecheck: { enabled: true },
},
diff --git a/packages/react-form-nextjs/tests/test-setup.ts b/packages/react-form-nextjs/tests/test-setup.ts
deleted file mode 100644
index 33e431061a..0000000000
--- a/packages/react-form-nextjs/tests/test-setup.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import '@testing-library/jest-dom/vitest'
-import { cleanup } from '@testing-library/react'
-import { afterEach } from 'vitest'
-
-// https://testing-library.com/docs/react-testing-library/api#cleanup
-afterEach(() => cleanup())
diff --git a/packages/react-form-nextjs/vite.config.ts b/packages/react-form-nextjs/vite.config.ts
index 15183ab228..10d048d1ce 100644
--- a/packages/react-form-nextjs/vite.config.ts
+++ b/packages/react-form-nextjs/vite.config.ts
@@ -9,8 +9,6 @@ const config = defineConfig({
name: packageJson.name,
dir: './tests',
watch: false,
- environment: 'jsdom',
- setupFiles: ['./tests/test-setup.ts'],
coverage: { enabled: true, provider: 'istanbul', include: ['src/**/*'] },
typecheck: { enabled: true },
},
diff --git a/packages/react-form-remix/tests/test-setup.ts b/packages/react-form-remix/tests/test-setup.ts
deleted file mode 100644
index 33e431061a..0000000000
--- a/packages/react-form-remix/tests/test-setup.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import '@testing-library/jest-dom/vitest'
-import { cleanup } from '@testing-library/react'
-import { afterEach } from 'vitest'
-
-// https://testing-library.com/docs/react-testing-library/api#cleanup
-afterEach(() => cleanup())
diff --git a/packages/react-form-remix/vite.config.ts b/packages/react-form-remix/vite.config.ts
index 15183ab228..10d048d1ce 100644
--- a/packages/react-form-remix/vite.config.ts
+++ b/packages/react-form-remix/vite.config.ts
@@ -9,8 +9,6 @@ const config = defineConfig({
name: packageJson.name,
dir: './tests',
watch: false,
- environment: 'jsdom',
- setupFiles: ['./tests/test-setup.ts'],
coverage: { enabled: true, provider: 'istanbul', include: ['src/**/*'] },
typecheck: { enabled: true },
},
diff --git a/packages/react-form-start/tests/test-setup.ts b/packages/react-form-start/tests/test-setup.ts
deleted file mode 100644
index 33e431061a..0000000000
--- a/packages/react-form-start/tests/test-setup.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import '@testing-library/jest-dom/vitest'
-import { cleanup } from '@testing-library/react'
-import { afterEach } from 'vitest'
-
-// https://testing-library.com/docs/react-testing-library/api#cleanup
-afterEach(() => cleanup())
diff --git a/packages/react-form-start/vite.config.ts b/packages/react-form-start/vite.config.ts
index 15183ab228..10d048d1ce 100644
--- a/packages/react-form-start/vite.config.ts
+++ b/packages/react-form-start/vite.config.ts
@@ -9,8 +9,6 @@ const config = defineConfig({
name: packageJson.name,
dir: './tests',
watch: false,
- environment: 'jsdom',
- setupFiles: ['./tests/test-setup.ts'],
coverage: { enabled: true, provider: 'istanbul', include: ['src/**/*'] },
typecheck: { enabled: true },
},
diff --git a/packages/react-form/package.json b/packages/react-form/package.json
index 872693cf5a..50ade6bfce 100644
--- a/packages/react-form/package.json
+++ b/packages/react-form/package.json
@@ -59,10 +59,12 @@
"@types/react": "~19.2.0",
"@types/react-dom": "^19.0.3",
"@vitejs/plugin-react": "^5.1.1",
+ "@vitest/browser-playwright": "^5.0.5",
"eslint-plugin-react-compiler": "19.1.0-rc.2",
"react": "19.1.0",
"react-dom": "19.1.0",
- "vite": "^7.2.2"
+ "vite": "^7.2.2",
+ "vitest-browser-react": "^2.3.0"
},
"peerDependencies": {
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
diff --git a/packages/react-form/tests/createFormHook.test.tsx b/packages/react-form/tests/createFormHook.test.tsx
index c93d0d725b..40dac54372 100644
--- a/packages/react-form/tests/createFormHook.test.tsx
+++ b/packages/react-form/tests/createFormHook.test.tsx
@@ -1,7 +1,7 @@
import { describe, expect, it } from 'vitest'
-import { render, waitFor } from '@testing-library/react'
+import { render } from 'vitest-browser-react'
import { formOptions } from '@tanstack/form-core'
-import userEvent from '@testing-library/user-event'
+import { userEvent } from 'vitest/browser'
import { createFormHook, createFormHookContexts, useStore } from '../src'
const user = userEvent.setup()
@@ -44,7 +44,7 @@ const { useAppForm, withForm, withFieldGroup, useTypedAppFormContext } =
})
describe('createFormHook', () => {
- it('should allow to set default value', () => {
+ it('should allow to set default value', async () => {
type Person = {
firstName: string
lastName: string
@@ -68,12 +68,12 @@ describe('createFormHook', () => {
)
}
- const { getByLabelText } = render()
- const input = getByLabelText('Testing')
- expect(input).toHaveValue('FirstName')
+ const { getByLabelText } = await render()
+ const input = getByLabelText('Testing', { exact: true })
+ await expect.element(input).toHaveValue('FirstName')
})
- it('should handle withForm types properly', () => {
+ it('should handle withForm types properly', async () => {
const formOpts = formOptions({
defaultValues: {
firstName: 'John',
@@ -111,13 +111,15 @@ describe('createFormHook', () => {
return
}
- const { getByLabelText, getByText } = render()
- const input = getByLabelText('First Name')
- expect(input).toHaveValue('John')
- expect(getByText('Testing')).toBeInTheDocument()
+ const { getByLabelText, getByText } = await render()
+ const input = getByLabelText('First Name', { exact: true })
+ await expect.element(input).toHaveValue('John')
+ await expect
+ .element(getByText('Testing', { exact: true }))
+ .toBeInTheDocument()
})
- it('should handle withFieldGroup types properly', () => {
+ it('should handle withFieldGroup types properly', async () => {
const formOpts = formOptions({
defaultValues: {
person: {
@@ -157,13 +159,15 @@ describe('createFormHook', () => {
return
}
- const { getByLabelText, getByText } = render()
- const input = getByLabelText('First Name')
- expect(input).toHaveValue('John')
- expect(getByText('Testing')).toBeInTheDocument()
+ const { getByLabelText, getByText } = await render()
+ const input = getByLabelText('First Name', { exact: true })
+ await expect.element(input).toHaveValue('John')
+ await expect
+ .element(getByText('Testing', { exact: true }))
+ .toBeInTheDocument()
})
- it('should use the correct field name in Field with withFieldGroup', () => {
+ it('should use the correct field name in Field with withFieldGroup', async () => {
const formOpts = formOptions({
defaultValues: {
person: {
@@ -234,17 +238,19 @@ describe('createFormHook', () => {
)
}
- const { getByLabelText, getByText } = render()
- const inputField1 = getByLabelText('person.firstName')
- const inputArray = getByLabelText('people[0].firstName')
- const inputField2 = getByLabelText('people[1].firstName')
- expect(inputField1).toHaveValue('John')
- expect(inputArray).toHaveValue('Jane')
- expect(inputField2).toHaveValue('Robert')
- expect(getByText('Testing')).toBeInTheDocument()
+ const { getByLabelText, getByText } = await render()
+ const inputField1 = getByLabelText('person.firstName', { exact: true })
+ const inputArray = getByLabelText('people[0].firstName', { exact: true })
+ const inputField2 = getByLabelText('people[1].firstName', { exact: true })
+ await expect.element(inputField1).toHaveValue('John')
+ await expect.element(inputArray).toHaveValue('Jane')
+ await expect.element(inputField2).toHaveValue('Robert')
+ await expect
+ .element(getByText('Testing', { exact: true }))
+ .toBeInTheDocument()
})
- it('should forward Field and Subscribe to the form', () => {
+ it('should forward Field and Subscribe to the form', async () => {
const formOpts = formOptions({
defaultValues: {
person: {
@@ -286,10 +292,10 @@ describe('createFormHook', () => {
return
}
- const { getByLabelText, getByText } = render()
- const input = getByLabelText('person.firstName')
- expect(input).toHaveValue('John')
- expect(getByText('Doe')).toBeInTheDocument()
+ const { getByLabelText, getByText } = await render()
+ const input = getByLabelText('person.firstName', { exact: true })
+ await expect.element(input).toHaveValue('John')
+ await expect.element(getByText('Doe', { exact: true })).toBeInTheDocument()
})
it('should not lose focus on update with withFieldGroup', async () => {
@@ -339,19 +345,19 @@ describe('createFormHook', () => {
return
}
- const { getByLabelText } = render()
+ const { getByLabelText } = await render()
- const input = getByLabelText('person.firstName')
- input.focus()
- expect(input).toHaveFocus()
+ const input = getByLabelText('person.firstName', { exact: true })
+ ;(input.element() as HTMLInputElement).focus()
+ await expect.element(input).toHaveFocus()
await user.clear(input)
await user.type(input, 'Something')
- expect(input).toHaveFocus()
+ await expect.element(input).toHaveFocus()
})
- it('should allow nesting withFieldGroup in other withFieldGroups', () => {
+ it('should allow nesting withFieldGroup in other withFieldGroups', async () => {
type Nested = {
firstName: string
}
@@ -404,12 +410,14 @@ describe('createFormHook', () => {
return
}
- const { getByText } = render()
+ const { getByText } = await render()
- expect(getByText('form.field.firstName')).toBeInTheDocument()
+ await expect
+ .element(getByText('form.field.firstName', { exact: true }))
+ .toBeInTheDocument()
})
- it('should allow mapping withFieldGroup to different values', () => {
+ it('should allow mapping withFieldGroup to different values', async () => {
const formOpts = formOptions({
defaultValues: {
unrelated: 'John',
@@ -447,12 +455,12 @@ describe('createFormHook', () => {
)
}
- const { getByLabelText } = render()
- const inputField1 = getByLabelText('unrelated')
- expect(inputField1).toHaveValue('John')
+ const { getByLabelText } = await render()
+ const inputField1 = getByLabelText('unrelated', { exact: true })
+ await expect.element(inputField1).toHaveValue('John')
})
- it('should remap FieldGroupApi.Field validators to the correct names', () => {
+ it('should remap FieldGroupApi.Field validators to the correct names', async () => {
const FieldGroupString = withFieldGroup({
defaultValues: { password: '', confirmPassword: '' },
render: function Render({ group }) {
@@ -533,7 +541,7 @@ describe('createFormHook', () => {
)
}
- render()
+ await render()
})
it('should accept formId and return it', async () => {
@@ -574,15 +582,15 @@ describe('createFormHook', () => {
)
}
- const { getByTestId } = render()
+ const { getByTestId } = await render()
const target = getByTestId('formId-target')
const result = getByTestId('formId-result')
await user.click(target)
- expect(result).toHaveTextContent('1')
+ await expect.element(result).toHaveTextContent('1')
})
- it('should allow using typed app form', () => {
+ it('should allow using typed app form', async () => {
type Person = {
firstName: string
lastName: string
@@ -620,12 +628,12 @@ describe('createFormHook', () => {
)
}
- const { getByLabelText } = render()
- const input = getByLabelText('Testing')
- expect(input).toHaveValue('FirstName')
+ const { getByLabelText } = await render()
+ const input = getByLabelText('Testing', { exact: true })
+ await expect.element(input).toHaveValue('FirstName')
})
- it('should throw if `useTypedAppFormContext` is used without AppForm', () => {
+ it('should throw if `useTypedAppFormContext` is used without AppForm', async () => {
type Person = {
firstName: string
lastName: string
@@ -659,10 +667,10 @@ describe('createFormHook', () => {
return
}
- expect(() => render()).toThrow()
+ await expect(render()).rejects.toThrow()
})
- it('should allow using typed app form with form components', () => {
+ it('should allow using typed app form with form components', async () => {
type Person = {
firstName: string
lastName: string
@@ -695,13 +703,13 @@ describe('createFormHook', () => {
)
}
- const { getByText } = render()
- const button = getByText('Testing')
- expect(button).toBeInTheDocument()
+ const { getByText } = await render()
+ const button = getByText('Testing', { exact: true })
+ await expect.element(button).toBeInTheDocument()
})
describe('extendForm', () => {
- it('should include both parent and extended field components', () => {
+ it('should include both parent and extended field components', async () => {
function ExtendedTextField({ label }: { label: string }) {
const field = useFieldContext()
return (
@@ -746,12 +754,14 @@ describe('createFormHook', () => {
)
}
- const { getByLabelText, getByTestId } = render()
- expect(getByLabelText('First Name')).toHaveValue('John')
- expect(getByTestId('extended-input')).toHaveValue('Doe')
+ const { getByLabelText, getByTestId } = await render()
+ await expect
+ .element(getByLabelText('First Name', { exact: true }))
+ .toHaveValue('John')
+ await expect.element(getByTestId('extended-input')).toHaveValue('Doe')
})
- it('should include both parent and extended form components', () => {
+ it('should include both parent and extended form components', async () => {
function ExtendedSubmit({ label }: { label: string }) {
const form = useFormContext()
return (
@@ -791,14 +801,16 @@ describe('createFormHook', () => {
)
}
- const { getByText, getByTestId } = render()
- expect(getByText('Submit')).toBeInTheDocument()
- expect(getByTestId('extended-submit')).toHaveTextContent(
- 'Extended Submit',
- )
+ const { getByText, getByTestId } = await render()
+ await expect
+ .element(getByText('Submit', { exact: true }))
+ .toBeInTheDocument()
+ await expect
+ .element(getByTestId('extended-submit'))
+ .toHaveTextContent('Extended Submit')
})
- it('should support chaining multiple extendForm calls', () => {
+ it('should support chaining multiple extendForm calls', async () => {
function FieldA({ label }: { label: string }) {
const field = useFieldContext()
return (
@@ -859,13 +871,19 @@ describe('createFormHook', () => {
)
}
- const { getByLabelText } = render()
- expect(getByLabelText('A')).toHaveValue('valueA')
- expect(getByLabelText('B')).toHaveValue('valueB')
- expect(getByLabelText('C')).toHaveValue('valueC')
+ const { getByLabelText } = await render()
+ await expect
+ .element(getByLabelText('A', { exact: true }))
+ .toHaveValue('valueA')
+ await expect
+ .element(getByLabelText('B', { exact: true }))
+ .toHaveValue('valueB')
+ await expect
+ .element(getByLabelText('C', { exact: true }))
+ .toHaveValue('valueC')
})
- it('should work with withForm after extendForm', () => {
+ it('should work with withForm after extendForm', async () => {
function ExtendedTextField({ label }: { label: string }) {
const field = useFieldContext()
return (
@@ -916,12 +934,16 @@ describe('createFormHook', () => {
return
}
- const { getByLabelText } = render()
- expect(getByLabelText('First Name')).toHaveValue('Jane')
- expect(getByLabelText('Last Name')).toHaveValue('Smith')
+ const { getByLabelText } = await render()
+ await expect
+ .element(getByLabelText('First Name', { exact: true }))
+ .toHaveValue('Jane')
+ await expect
+ .element(getByLabelText('Last Name', { exact: true }))
+ .toHaveValue('Smith')
})
- it('should return a new extendForm from the extended hook', () => {
+ it('should return a new extendForm from the extended hook', async () => {
function ExtendedTextField({ label }: { label: string }) {
const field = useFieldContext()
return (
@@ -999,14 +1021,14 @@ describe('createFormHook', () => {
return
}
- const { getByTestId } = render()
+ const { getByTestId } = await render()
const input = getByTestId('lastName')
const stateLastName = getByTestId('state-lastName')
- expect(stateLastName).toHaveTextContent('LastName')
+ await expect.element(stateLastName).toHaveTextContent('LastName')
await user.clear(input)
await user.type(input, 'Updated')
- await waitFor(() => expect(stateLastName).toHaveTextContent('Updated'))
+ await expect.element(stateLastName).toHaveTextContent('Updated')
})
})
diff --git a/packages/react-form/tests/onChangeListenTo.adapter.test.tsx b/packages/react-form/tests/onChangeListenTo.adapter.test.tsx
index 79defa0b44..f5b6fd597d 100644
--- a/packages/react-form/tests/onChangeListenTo.adapter.test.tsx
+++ b/packages/react-form/tests/onChangeListenTo.adapter.test.tsx
@@ -1,5 +1,6 @@
import { describe, expect, it, vi } from 'vitest'
-import { fireEvent, render } from '@testing-library/react'
+import { userEvent } from 'vitest/browser'
+import { render } from 'vitest-browser-react'
import { useSelector } from '@tanstack/react-store'
import { StrictMode } from 'react'
import { useForm } from '../src/useForm'
@@ -109,22 +110,22 @@ describe('React adapter - onChangeListenTo race', () => {
)
}
- const { getByTestId } = render(
+ const { getByTestId } = await render(
,
)
- const street = getByTestId('street') as HTMLInputElement
- const houseNo = getByTestId('houseNo') as HTMLInputElement
- const zipCode = getByTestId('zipCode') as HTMLInputElement
- const city = getByTestId('city') as HTMLInputElement
+ const street = getByTestId('street')
+ const houseNo = getByTestId('houseNo')
+ const zipCode = getByTestId('zipCode')
+ const city = getByTestId('city')
// Simulate rapid updates (autofill)
- fireEvent.change(street, { target: { value: 'Foo Street' } })
- fireEvent.change(houseNo, { target: { value: '2' } })
- fireEvent.change(zipCode, { target: { value: '12345' } })
- fireEvent.change(city, { target: { value: 'Barrington' } })
+ await userEvent.fill(street, 'Foo Street')
+ await userEvent.fill(houseNo, '2')
+ await userEvent.fill(zipCode, '12345')
+ await userEvent.fill(city, 'Barrington')
// Run debounce + async validation
await vi.runAllTimersAsync()
@@ -132,11 +133,15 @@ describe('React adapter - onChangeListenTo race', () => {
expect(validationFn).toHaveBeenCalledTimes(1)
// Verify validation flags are not stuck
- const isFieldsValidating = getByTestId('isFieldsValidating').textContent
- const streetValidating = getByTestId('street-validating').textContent
- const houseValidating = getByTestId('houseNo-validating').textContent
- const zipValidating = getByTestId('zipCode-validating').textContent
- const cityValidating = getByTestId('city-validating').textContent
+ const isFieldsValidating =
+ getByTestId('isFieldsValidating').element().textContent
+ const streetValidating =
+ getByTestId('street-validating').element().textContent
+ const houseValidating =
+ getByTestId('houseNo-validating').element().textContent
+ const zipValidating =
+ getByTestId('zipCode-validating').element().textContent
+ const cityValidating = getByTestId('city-validating').element().textContent
expect(isFieldsValidating).toBe('false')
expect(streetValidating).toBe('false')
diff --git a/packages/react-form/tests/test-setup.ts b/packages/react-form/tests/test-setup.ts
deleted file mode 100644
index 33e431061a..0000000000
--- a/packages/react-form/tests/test-setup.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import '@testing-library/jest-dom/vitest'
-import { cleanup } from '@testing-library/react'
-import { afterEach } from 'vitest'
-
-// https://testing-library.com/docs/react-testing-library/api#cleanup
-afterEach(() => cleanup())
diff --git a/packages/react-form/tests/useField.test.tsx b/packages/react-form/tests/useField.test.tsx
index 4454165947..d041f3bb54 100644
--- a/packages/react-form/tests/useField.test.tsx
+++ b/packages/react-form/tests/useField.test.tsx
@@ -1,7 +1,7 @@
/* eslint-disable react-compiler/react-compiler */
import { describe, expect, it, vi } from 'vitest'
-import { render, waitFor, within } from '@testing-library/react'
-import { userEvent } from '@testing-library/user-event'
+import { render } from 'vitest-browser-react'
+import { userEvent } from 'vitest/browser'
import { StrictMode, useState } from 'react'
import { useStore } from '@tanstack/react-store'
import { useForm } from '../src/index'
@@ -11,7 +11,7 @@ import type { AnyFieldApi } from '../src/index'
const user = userEvent.setup()
describe('useField', () => {
- it('should allow to set default value', () => {
+ it('should allow to set default value', async () => {
type Person = {
firstName: string
lastName: string
@@ -44,12 +44,12 @@ describe('useField', () => {
)
}
- const { getByTestId } = render()
+ const { getByTestId } = await render()
const input = getByTestId('fieldinput')
- expect(input).toHaveValue('FirstName')
+ await expect.element(input).toHaveValue('FirstName')
})
- it('should use field default value first', () => {
+ it('should use field default value first', async () => {
type Person = {
firstName: string
lastName: string
@@ -83,9 +83,9 @@ describe('useField', () => {
)
}
- const { getByTestId } = render()
+ const { getByTestId } = await render()
const input = getByTestId('fieldinput')
- expect(input).toHaveValue('otherName')
+ await expect.element(input).toHaveValue('otherName')
})
it('should not validate on change if isTouched is false', async () => {
@@ -131,10 +131,12 @@ describe('useField', () => {
)
}
- const { getByTestId, queryByText } = render()
+ const { getByTestId, getByText } = await render()
const input = getByTestId('fieldinput')
await user.type(input, 'other')
- expect(queryByText(error)).not.toBeInTheDocument()
+ await expect
+ .element(getByText(error, { exact: true }))
+ .not.toBeInTheDocument()
})
it('should validate on change if isTouched is true', async () => {
@@ -177,11 +179,13 @@ describe('useField', () => {
)
}
- const { getByTestId, getByText, queryByText } = render()
+ const { getByTestId, getByText } = await render()
const input = getByTestId('fieldinput')
- expect(queryByText(error)).not.toBeInTheDocument()
+ await expect
+ .element(getByText(error, { exact: true }))
+ .not.toBeInTheDocument()
await user.type(input, 'other')
- expect(getByText(error)).toBeInTheDocument()
+ await expect.element(getByText(error, { exact: true })).toBeInTheDocument()
})
it('should validate on change and on blur', async () => {
@@ -229,14 +233,22 @@ describe('useField', () => {
)
}
- const { getByTestId, getByText, queryByText } = render()
+ const { getByTestId, getByText } = await render()
const input = getByTestId('fieldinput')
- expect(queryByText(onChangeError)).not.toBeInTheDocument()
- expect(queryByText(onBlurError)).not.toBeInTheDocument()
+ await expect
+ .element(getByText(onChangeError, { exact: true }))
+ .not.toBeInTheDocument()
+ await expect
+ .element(getByText(onBlurError, { exact: true }))
+ .not.toBeInTheDocument()
await user.type(input, 'other')
- expect(getByText(onChangeError)).toBeInTheDocument()
+ await expect
+ .element(getByText(onChangeError, { exact: true }))
+ .toBeInTheDocument()
await user.click(document.body)
- expect(queryByText(onBlurError)).toBeInTheDocument()
+ await expect
+ .element(getByText(onBlurError, { exact: true }))
+ .toBeInTheDocument()
})
it('should properly update conditionally rendered fields', async () => {
@@ -311,21 +323,19 @@ describe('useField', () => {
)
}
- const { getByTestId } = render()
+ const { getByTestId } = await render()
const showFirstFieldInput = getByTestId('show-first-field')
await user.type(getByTestId('first-field'), 'hello')
- expect((getByTestId('first-field') as HTMLInputElement).value).toBe('hello')
+ await expect.element(getByTestId('first-field')).toHaveValue('hello')
await user.click(showFirstFieldInput)
await user.type(getByTestId('second-field'), 'world')
- expect((getByTestId('second-field') as HTMLInputElement).value).toBe(
- 'world',
- )
+ await expect.element(getByTestId('second-field')).toHaveValue('world')
await user.click(showFirstFieldInput)
- expect((getByTestId('first-field') as HTMLInputElement).value).toBe('hello')
+ await expect.element(getByTestId('first-field')).toHaveValue('hello')
})
it('should not keep hidden field submit errors after unmount', async () => {
@@ -395,7 +405,7 @@ describe('useField', () => {
)
}
- const { getByTestId, queryByTestId } = render(
+ const { getByTestId } = await render(
,
@@ -407,26 +417,23 @@ describe('useField', () => {
await user.type(getByTestId('last-name'), 'abc')
await user.click(submitButton)
- await waitFor(() => expect(submitButton).toBeDisabled())
+ await expect.element(submitButton).toBeDisabled()
expect(onSubmit).toHaveBeenCalledTimes(0)
await user.clear(getByTestId('first-name'))
await user.type(getByTestId('first-name'), 'b')
- await waitFor(() =>
- expect(queryByTestId('last-name')).not.toBeInTheDocument(),
- )
- await waitFor(() => expect(submitButton).toBeEnabled())
+ await expect.element(getByTestId('last-name')).not.toBeInTheDocument()
+ await expect.element(submitButton).toBeEnabled()
await user.click(submitButton)
- await waitFor(() => expect(onSubmit).toHaveBeenCalledTimes(1))
+ await vi.waitFor(() => expect(onSubmit).toHaveBeenCalledTimes(1))
await user.clear(getByTestId('first-name'))
await user.type(getByTestId('first-name'), 'a')
- const remountedLastName = await waitFor(() => getByTestId('last-name'))
- expect((remountedLastName as HTMLInputElement).value).toBe('abc')
- expect(submitButton).toBeEnabled()
+ await expect.element(getByTestId('last-name')).toHaveValue('abc')
+ await expect.element(submitButton).toBeEnabled()
})
it('should call onUnmount listener when a field is conditionally removed', async () => {
@@ -476,20 +483,20 @@ describe('useField', () => {
)
}
- const { getByTestId, queryByTestId } = render(
+ const { getByTestId } = await render(
,
)
- await waitFor(() => expect(getByTestId('name')).toBeInTheDocument())
+ await expect.element(getByTestId('name')).toBeInTheDocument()
const callsBefore = fieldUnmount.mock.calls.length
const formCallsBefore = formFieldUnmount.mock.calls.length
await user.click(getByTestId('toggle'))
- await waitFor(() => expect(queryByTestId('name')).not.toBeInTheDocument())
+ await expect.element(getByTestId('name')).not.toBeInTheDocument()
expect(fieldUnmount).toHaveBeenCalledTimes(callsBefore + 1)
expect(formFieldUnmount).toHaveBeenCalledTimes(formCallsBefore + 1)
})
@@ -537,12 +544,13 @@ describe('useField', () => {
)
}
- const { getByTestId, getByText, queryByText } = render()
+ const { getByTestId, getByText } = await render()
const input = getByTestId('fieldinput')
- expect(queryByText(error)).not.toBeInTheDocument()
+ await expect
+ .element(getByText(error, { exact: true }))
+ .not.toBeInTheDocument()
await user.type(input, 'other')
- await waitFor(() => getByText(error))
- expect(getByText(error)).toBeInTheDocument()
+ await expect.element(getByText(error, { exact: true })).toBeInTheDocument()
})
it('should validate async on change and async on blur', async () => {
@@ -594,17 +602,23 @@ describe('useField', () => {
)
}
- const { getByTestId, getByText, queryByText } = render()
+ const { getByTestId, getByText } = await render()
const input = getByTestId('fieldinput')
- expect(queryByText(onChangeError)).not.toBeInTheDocument()
- expect(queryByText(onBlurError)).not.toBeInTheDocument()
+ await expect
+ .element(getByText(onChangeError, { exact: true }))
+ .not.toBeInTheDocument()
+ await expect
+ .element(getByText(onBlurError, { exact: true }))
+ .not.toBeInTheDocument()
await user.type(input, 'other')
- await waitFor(() => getByText(onChangeError))
- expect(getByText(onChangeError)).toBeInTheDocument()
+ await expect
+ .element(getByText(onChangeError, { exact: true }))
+ .toBeInTheDocument()
await user.click(document.body)
- await waitFor(() => getByText(onBlurError))
- expect(getByText(onBlurError)).toBeInTheDocument()
+ await expect
+ .element(getByText(onBlurError, { exact: true }))
+ .toBeInTheDocument()
})
it('should validate async on change with debounce', async () => {
@@ -653,13 +667,12 @@ describe('useField', () => {
)
}
- const { getByTestId, getByText } = render()
+ const { getByTestId, getByText } = await render()
const input = getByTestId('fieldinput')
await user.type(input, 'other')
// mockFn will have been called 5 times without onChangeAsyncDebounceMs
expect(mockFn).toHaveBeenCalledTimes(0)
- await waitFor(() => getByText(error))
- expect(getByText(error)).toBeInTheDocument()
+ await expect.element(getByText(error, { exact: true })).toBeInTheDocument()
})
it('should handle strict mode properly with conditional fields', async () => {
@@ -754,17 +767,21 @@ describe('useField', () => {
)
}
- const { getByText, findByText, queryByText } = render(
+ const { getByText } = await render(
,
)
- await user.click(getByText('Submit'))
- expect(await findByText('A first name is required')).toBeInTheDocument()
- await user.click(getByText('Hide field'))
- await user.click(getByText('Submit'))
- expect(queryByText('A first name is required')).not.toBeInTheDocument()
+ await user.click(getByText('Submit', { exact: true }))
+ await expect
+ .element(getByText('A first name is required', { exact: true }))
+ .toBeInTheDocument()
+ await user.click(getByText('Hide field', { exact: true }))
+ await user.click(getByText('Submit', { exact: true }))
+ await expect
+ .element(getByText('A first name is required', { exact: true }))
+ .not.toBeInTheDocument()
})
it('should handle arrays with primitive values', async () => {
@@ -837,29 +854,39 @@ describe('useField', () => {
)
}
- const { getByText, findByLabelText, queryByText, findByText } = render(
- ,
- )
-
- expect(queryByText('Name for person 0')).not.toBeInTheDocument()
- expect(queryByText('Name for person 1')).not.toBeInTheDocument()
- await user.click(getByText('Add person'))
- const input = await findByLabelText('Name for person 0')
- expect(input).toBeInTheDocument()
+ const { getByText, getByLabelText } = await render()
+
+ await expect
+ .element(getByText('Name for person 0', { exact: true }))
+ .not.toBeInTheDocument()
+ await expect
+ .element(getByText('Name for person 1', { exact: true }))
+ .not.toBeInTheDocument()
+ await user.click(getByText('Add person', { exact: true }))
+ const input = getByLabelText('Name for person 0', { exact: true })
+ await expect.element(input).toBeInTheDocument()
await user.type(input, 'John')
- await user.click(getByText('Add person'))
- const input2 = await findByLabelText('Name for person 1')
- expect(input).toBeInTheDocument()
+ await user.click(getByText('Add person', { exact: true }))
+ const input2 = getByLabelText('Name for person 1', { exact: true })
+ await expect.element(input).toBeInTheDocument()
await user.type(input2, 'Jack')
- expect(queryByText('Name for person 0')).toBeInTheDocument()
- expect(queryByText('Name for person 1')).toBeInTheDocument()
- await user.click(getByText('Remove person 1'))
- expect(queryByText('Name for person 0')).toBeInTheDocument()
- expect(queryByText('Name for person 1')).not.toBeInTheDocument()
-
- await user.click(await findByText('Submit'))
+ await expect
+ .element(getByText('Name for person 0', { exact: true }))
+ .toBeInTheDocument()
+ await expect
+ .element(getByText('Name for person 1', { exact: true }))
+ .toBeInTheDocument()
+ await user.click(getByText('Remove person 1', { exact: true }))
+ await expect
+ .element(getByText('Name for person 0', { exact: true }))
+ .toBeInTheDocument()
+ await expect
+ .element(getByText('Name for person 1', { exact: true }))
+ .not.toBeInTheDocument()
+
+ await user.click(getByText('Submit', { exact: true }))
expect(fn).toHaveBeenCalledWith({ people: ['John'] })
})
@@ -936,29 +963,39 @@ describe('useField', () => {
)
}
- const { getByText, findByLabelText, queryByText, findByText } = render(
- ,
- )
-
- expect(queryByText('Name for person 0')).not.toBeInTheDocument()
- expect(queryByText('Name for person 1')).not.toBeInTheDocument()
- await user.click(getByText('Add person'))
- const input = await findByLabelText('Name for person 0')
- expect(input).toBeInTheDocument()
+ const { getByText, getByLabelText } = await render()
+
+ await expect
+ .element(getByText('Name for person 0', { exact: true }))
+ .not.toBeInTheDocument()
+ await expect
+ .element(getByText('Name for person 1', { exact: true }))
+ .not.toBeInTheDocument()
+ await user.click(getByText('Add person', { exact: true }))
+ const input = getByLabelText('Name for person 0', { exact: true })
+ await expect.element(input).toBeInTheDocument()
await user.type(input, 'John')
- await user.click(getByText('Add person'))
- const input2 = await findByLabelText('Name for person 1')
- expect(input).toBeInTheDocument()
+ await user.click(getByText('Add person', { exact: true }))
+ const input2 = getByLabelText('Name for person 1', { exact: true })
+ await expect.element(input).toBeInTheDocument()
await user.type(input2, 'Jack')
- expect(queryByText('Name for person 0')).toBeInTheDocument()
- expect(queryByText('Name for person 1')).toBeInTheDocument()
- await user.click(getByText('Remove person 1'))
- expect(queryByText('Name for person 0')).toBeInTheDocument()
- expect(queryByText('Name for person 1')).not.toBeInTheDocument()
-
- await user.click(await findByText('Submit'))
+ await expect
+ .element(getByText('Name for person 0', { exact: true }))
+ .toBeInTheDocument()
+ await expect
+ .element(getByText('Name for person 1', { exact: true }))
+ .toBeInTheDocument()
+ await user.click(getByText('Remove person 1', { exact: true }))
+ await expect
+ .element(getByText('Name for person 0', { exact: true }))
+ .toBeInTheDocument()
+ await expect
+ .element(getByText('Name for person 1', { exact: true }))
+ .not.toBeInTheDocument()
+
+ await user.click(getByText('Submit', { exact: true }))
expect(fn).toHaveBeenCalledWith({ people: [{ name: 'John', age: 0 }] })
})
@@ -1024,21 +1061,27 @@ describe('useField', () => {
)
}
- const { getByText, findByLabelText } = render()
+ const { getByText, getByLabelText } = await render()
- await user.click(getByText('Add section'))
- await user.click(getByText('Add section'))
+ await user.click(getByText('Add section', { exact: true }))
+ await user.click(getByText('Add section', { exact: true }))
- await findByLabelText('Title for section id-1')
- await findByLabelText('Title for section id-2')
+ await expect
+ .element(getByLabelText('Title for section id-1', { exact: true }))
+ .toBeInTheDocument()
+ await expect
+ .element(getByLabelText('Title for section id-2', { exact: true }))
+ .toBeInTheDocument()
observedValues.length = 0
- await user.click(getByText('Remove first section'))
+ await user.click(getByText('Remove first section', { exact: true }))
// The surviving item (previously `sections[1]`) shifts to `sections[0]`.
// Its value must remain "Section 2" and never render as `undefined`.
- const survivingInput = await findByLabelText('Title for section id-2')
- expect((survivingInput as HTMLInputElement).value).toBe('Section 2')
+ const survivingInput = getByLabelText('Title for section id-2', {
+ exact: true,
+ })
+ await expect.element(survivingInput).toHaveValue('Section 2')
expect(observedValues).not.toContain(undefined)
})
@@ -1103,15 +1146,21 @@ describe('useField', () => {
)
}
- const { findByLabelText, queryByText, findByText } = render()
+ const { getByLabelText, getByText } = await render()
- const passwordInput = await findByLabelText('Password')
- const confirmPasswordInput = await findByLabelText('Confirm Password')
+ const passwordInput = getByLabelText('Password', { exact: true })
+ const confirmPasswordInput = getByLabelText('Confirm Password', {
+ exact: true,
+ })
await user.type(passwordInput, 'password')
await user.type(confirmPasswordInput, 'password')
- expect(queryByText('Passwords do not match')).not.toBeInTheDocument()
+ await expect
+ .element(getByText('Passwords do not match', { exact: true }))
+ .not.toBeInTheDocument()
await user.type(confirmPasswordInput, '1')
- expect(await findByText('Passwords do not match')).toBeInTheDocument()
+ await expect
+ .element(getByText('Passwords do not match', { exact: true }))
+ .toBeInTheDocument()
})
it('should handle deeply nested values in StrictMode', async () => {
@@ -1130,14 +1179,16 @@ describe('useField', () => {
)
}
- const { queryByText, findByText } = render(
+ const { getByText } = await render(
,
)
- expect(queryByText('Test')).not.toBeInTheDocument()
- expect(await findByText('User')).toBeInTheDocument()
+ await expect
+ .element(getByText('Test', { exact: true }))
+ .not.toBeInTheDocument()
+ await expect.element(getByText('User', { exact: true })).toBeInTheDocument()
})
it('should validate async on submit without debounce', async () => {
@@ -1188,12 +1239,11 @@ describe('useField', () => {
)
}
- const { getByRole, getByText } = render()
+ const { getByRole, getByText } = await render()
await user.click(getByRole('button', { name: 'Submit' }))
expect(mockFn).toHaveBeenCalledTimes(1)
- await waitFor(() => getByText(error))
- expect(getByText(error)).toBeInTheDocument()
+ await expect.element(getByText(error, { exact: true })).toBeInTheDocument()
})
it('should validate allow pushvalue to implicitly set a default value', async () => {
@@ -1246,14 +1296,14 @@ describe('useField', () => {
)
}
- const { getByText, queryByText } = render(
+ const { getByText } = await render(
,
)
- expect(getByText('[]')).toBeInTheDocument()
- await user.click(getByText('Add person'))
- expect(getByText(`[""]`)).toBeInTheDocument()
+ await expect.element(getByText('[]', { exact: true })).toBeInTheDocument()
+ await user.click(getByText('Add person', { exact: true }))
+ await expect.element(getByText(`[""]`, { exact: true })).toBeInTheDocument()
})
it('should validate allow pushvalue to implicitly set a pushed default value', async () => {
@@ -1306,14 +1356,16 @@ describe('useField', () => {
)
}
- const { getByText, queryByText } = render(
+ const { getByText } = await render(
,
)
- expect(getByText('[]')).toBeInTheDocument()
- await user.click(getByText('Add person'))
- expect(getByText(`["Test"]`)).toBeInTheDocument()
+ await expect.element(getByText('[]', { exact: true })).toBeInTheDocument()
+ await user.click(getByText('Add person', { exact: true }))
+ await expect
+ .element(getByText(`["Test"]`, { exact: true }))
+ .toBeInTheDocument()
})
it('should handle removing element from array', async () => {
@@ -1384,7 +1436,7 @@ describe('useField', () => {
)
}
- const { getByText, queryByText, getByTestId } = render(
+ const { getByText, getByTestId } = await render(
,
@@ -1395,20 +1447,28 @@ describe('useField', () => {
fakePeople.molly,
fakePeople.george,
]
- exisingPeople.forEach((person) =>
- expect(getByText(person.name)).toBeInTheDocument(),
- )
+ for (const person of exisingPeople) {
+ await expect
+ .element(getByText(person.name, { exact: true }))
+ .toBeInTheDocument()
+ }
const container = getByTestId('container')
- expect(within(container).getAllByRole('textbox')).toHaveLength(3)
+ expect(container.getByRole('textbox').elements()).toHaveLength(3)
- await user.click(getByText('Remove person'))
+ await user.click(getByText('Remove person', { exact: true }))
- expect(within(container).getAllByRole('textbox')).toHaveLength(2)
+ await expect
+ .poll(() => container.getByRole('textbox').elements())
+ .toHaveLength(2)
exisingPeople = [fakePeople.jack, fakePeople.george]
- exisingPeople.forEach((person) =>
- expect(getByText(person.name)).toBeInTheDocument(),
- )
- expect(queryByText(fakePeople.molly.name)).not.toBeInTheDocument()
+ for (const person of exisingPeople) {
+ await expect
+ .element(getByText(person.name, { exact: true }))
+ .toBeInTheDocument()
+ }
+ await expect
+ .element(getByText(fakePeople.molly.name, { exact: true }))
+ .not.toBeInTheDocument()
})
it('should not rerender unrelated fields', async () => {
@@ -1455,7 +1515,7 @@ describe('useField', () => {
)
}
- const { getByTestId } = render(
+ const { getByTestId } = await render(
,
@@ -1522,7 +1582,7 @@ describe('useField', () => {
)
}
- const { getByTestId } = render(
+ const { getByTestId } = await render(
,
@@ -1540,7 +1600,7 @@ describe('useField', () => {
expect(renderCount.arrayField).toBe(arrayFieldInitialRender)
// Verify typing still works
- expect(getByTestId('person-0')).toHaveValue('Johnny')
+ await expect.element(getByTestId('person-0')).toHaveValue('Johnny')
// Now add a new item - this SHOULD trigger array field re-render
const arrayFieldBeforeAdd = renderCount.arrayField
@@ -1589,21 +1649,21 @@ describe('useField', () => {
)
}
- const { getByTestId } = render(
+ const { getByTestId } = await render(
,
)
- expect(getByTestId('item-0')).toHaveTextContent('John')
- expect(getByTestId('item-1')).toHaveTextContent('Jane')
+ await expect.element(getByTestId('item-0')).toHaveTextContent('John')
+ await expect.element(getByTestId('item-1')).toHaveTextContent('Jane')
const before = renderCount.arrayField
await user.click(getByTestId('swap'))
expect(renderCount.arrayField).toBeGreaterThan(before)
- expect(getByTestId('item-0')).toHaveTextContent('Jane')
- expect(getByTestId('item-1')).toHaveTextContent('John')
+ await expect.element(getByTestId('item-0')).toHaveTextContent('Jane')
+ await expect.element(getByTestId('item-1')).toHaveTextContent('John')
})
it('should rerender array field when async defaultValues resolve', async () => {
@@ -1636,12 +1696,14 @@ describe('useField', () => {
)
}
- const { getByTestId, rerender } = render()
- expect(getByTestId('list').children).toHaveLength(0)
+ const { getByTestId, rerender } = await render()
+ expect(getByTestId('list').element().children).toHaveLength(0)
- rerender()
- await waitFor(() => expect(getByTestId('list').children).toHaveLength(1))
- expect(getByTestId('item-0')).toHaveTextContent('Alice')
+ await rerender()
+ await expect
+ .poll(() => getByTestId('list').element().children)
+ .toHaveLength(1)
+ await expect.element(getByTestId('item-0')).toHaveTextContent('Alice')
})
it('should handle defaultValue without setstate-in-render error', async () => {
@@ -1684,7 +1746,7 @@ describe('useField', () => {
)
}
- const { getByTestId } = render()
+ const { getByTestId } = await render()
await user.type(getByTestId('fieldOne'), 'John')
// Should not log an error
@@ -1709,9 +1771,11 @@ describe('useField', () => {
)
}
- const { queryByText } = render()
+ const { getByText } = await render()
- expect(queryByText('a')).toBeInTheDocument()
- expect(queryByText('never')).not.toBeInTheDocument()
+ await expect.element(getByText('a', { exact: true })).toBeInTheDocument()
+ await expect
+ .element(getByText('never', { exact: true }))
+ .not.toBeInTheDocument()
})
})
diff --git a/packages/react-form/tests/useForm.test.tsx b/packages/react-form/tests/useForm.test.tsx
index b71094cddc..9ce4639dc9 100644
--- a/packages/react-form/tests/useForm.test.tsx
+++ b/packages/react-form/tests/useForm.test.tsx
@@ -1,6 +1,6 @@
import { describe, expect, it, vi } from 'vitest'
-import { render, waitFor } from '@testing-library/react'
-import { userEvent } from '@testing-library/user-event'
+import { render } from 'vitest-browser-react'
+import { userEvent } from 'vitest/browser'
import { useStore } from '@tanstack/react-store'
import { useCallback, useEffect, useReducer, useState } from 'react'
import { mergeForm, useForm } from '../src/index'
@@ -40,11 +40,13 @@ describe('useForm', () => {
)
}
- const { getByTestId, queryByText } = render()
+ const { getByTestId, getByText } = await render()
const input = getByTestId('fieldinput')
- expect(queryByText('FirstName')).not.toBeInTheDocument()
+ await expect
+ .element(getByText('FirstName', { exact: true }))
+ .not.toBeInTheDocument()
await user.type(input, 'FirstName')
- expect(input).toHaveValue('FirstName')
+ await expect.element(input).toHaveValue('FirstName')
})
it('should allow default values to be set', async () => {
@@ -73,9 +75,13 @@ describe('useForm', () => {
)
}
- const { findByText, queryByText } = render()
- expect(await findByText('FirstName')).toBeInTheDocument()
- expect(queryByText('LastName')).not.toBeInTheDocument()
+ const { getByText } = await render()
+ await expect
+ .element(getByText('FirstName', { exact: true }))
+ .toBeInTheDocument()
+ await expect
+ .element(getByText('LastName', { exact: true }))
+ .not.toBeInTheDocument()
})
it('should handle submitting properly', async () => {
@@ -114,14 +120,14 @@ describe('useForm', () => {
)
}
- const { findByPlaceholderText, getByText } = render()
- const input = await findByPlaceholderText('First name')
+ const { getByPlaceholder, getByText } = await render()
+ const input = getByPlaceholder('First name')
await user.clear(input)
await user.type(input, 'OtherName')
- await user.click(getByText('Submit'))
- await waitFor(() =>
- expect(getByText('Submitted data: OtherName')).toBeInTheDocument(),
- )
+ await user.click(getByText('Submit', { exact: true }))
+ await expect
+ .element(getByText('Submitted data: OtherName', { exact: true }))
+ .toBeInTheDocument()
})
it('should run on form mount', async () => {
@@ -154,9 +160,11 @@ describe('useForm', () => {
)
}
- const { getByText } = render()
- await user.click(getByText('Mount form'))
- await waitFor(() => expect(getByText('Form mounted')).toBeInTheDocument())
+ const { getByText } = await render()
+ await user.click(getByText('Mount form', { exact: true }))
+ await expect
+ .element(getByText('Form mounted', { exact: true }))
+ .toBeInTheDocument()
})
it('should validate async on change for the form', async () => {
@@ -198,12 +206,13 @@ describe('useForm', () => {
)
}
- const { getByTestId, getByText, queryByText } = render()
+ const { getByTestId, getByText } = await render()
const input = getByTestId('fieldinput')
- expect(queryByText(error)).not.toBeInTheDocument()
+ await expect
+ .element(getByText(error, { exact: true }))
+ .not.toBeInTheDocument()
await user.type(input, 'other')
- await waitFor(() => getByText(error))
- expect(getByText(error)).toBeInTheDocument()
+ await expect.element(getByText(error, { exact: true })).toBeInTheDocument()
})
it('should not validate on change if isTouched is false', async () => {
@@ -251,10 +260,12 @@ describe('useForm', () => {
)
}
- const { getByTestId, queryByText } = render()
+ const { getByTestId, getByText } = await render()
const input = getByTestId('fieldinput')
await user.type(input, 'other')
- expect(queryByText(error)).not.toBeInTheDocument()
+ await expect
+ .element(getByText(error, { exact: true }))
+ .not.toBeInTheDocument()
})
it('should validate on change if isTouched is true', async () => {
@@ -298,11 +309,13 @@ describe('useForm', () => {
)
}
- const { getByTestId, getByText, queryByText } = render()
+ const { getByTestId, getByText } = await render()
const input = getByTestId('fieldinput')
- expect(queryByText(error)).not.toBeInTheDocument()
+ await expect
+ .element(getByText(error, { exact: true }))
+ .not.toBeInTheDocument()
await user.type(input, 'other')
- expect(getByText(error)).toBeInTheDocument()
+ await expect.element(getByText(error, { exact: true })).toBeInTheDocument()
})
it('should validate on change and on blur', async () => {
@@ -349,14 +362,22 @@ describe('useForm', () => {
>
)
}
- const { getByTestId, getByText, queryByText } = render()
+ const { getByTestId, getByText } = await render()
const input = getByTestId('fieldinput')
- expect(queryByText(onChangeError)).not.toBeInTheDocument()
- expect(queryByText(onBlurError)).not.toBeInTheDocument()
+ await expect
+ .element(getByText(onChangeError, { exact: true }))
+ .not.toBeInTheDocument()
+ await expect
+ .element(getByText(onBlurError, { exact: true }))
+ .not.toBeInTheDocument()
await user.type(input, 'other')
- expect(getByText(onChangeError)).toBeInTheDocument()
+ await expect
+ .element(getByText(onChangeError, { exact: true }))
+ .toBeInTheDocument()
await user.click(document.body)
- expect(queryByText(onBlurError)).toBeInTheDocument()
+ await expect
+ .element(getByText(onBlurError, { exact: true }))
+ .toBeInTheDocument()
})
it("should set field errors from the field's validators", async () => {
@@ -410,24 +431,24 @@ describe('useForm', () => {
>
)
}
- const { getByTestId } = render()
+ const { getByTestId } = await render()
- expect(getByTestId('form-onchange')).toHaveTextContent('')
+ await expect.element(getByTestId('form-onchange')).toHaveTextContent('')
const input = getByTestId('fieldinput')
await user.type(input, 'other')
- expect(getByTestId('form-onchange')).toHaveTextContent(
- 'Something went wrong',
- )
- expect(getByTestId('field-onchange')).toHaveTextContent(
- 'Please enter a different value (onChangeError)',
- )
+ await expect
+ .element(getByTestId('form-onchange'))
+ .toHaveTextContent('Something went wrong')
+ await expect
+ .element(getByTestId('field-onchange'))
+ .toHaveTextContent('Please enter a different value (onChangeError)')
await user.click(document.body)
- expect(getByTestId('form-onblur')).toHaveTextContent(
- 'Please enter a different value (onBlurError)',
- )
+ await expect
+ .element(getByTestId('form-onblur'))
+ .toHaveTextContent('Please enter a different value (onBlurError)')
})
it('should validate async on change', async () => {
@@ -473,12 +494,13 @@ describe('useForm', () => {
)
}
- const { getByTestId, getByText, queryByText } = render()
+ const { getByTestId, getByText } = await render()
const input = getByTestId('fieldinput')
- expect(queryByText(error)).not.toBeInTheDocument()
+ await expect
+ .element(getByText(error, { exact: true }))
+ .not.toBeInTheDocument()
await user.type(input, 'other')
- await waitFor(() => getByText(error))
- expect(getByText(error)).toBeInTheDocument()
+ await expect.element(getByText(error, { exact: true })).toBeInTheDocument()
})
it("should set field errors from the form's onChangeAsync validator", async () => {
@@ -531,23 +553,23 @@ describe('useForm', () => {
)
}
- const { getByTestId } = render()
+ const { getByTestId } = await render()
const input = getByTestId('fieldinput')
const firstNameErrorElement = getByTestId('field-error')
const formErrorElement = getByTestId('form-error')
- expect(firstNameErrorElement).toBeEmptyDOMElement()
- expect(formErrorElement).toBeEmptyDOMElement()
+ await expect.element(firstNameErrorElement).toBeEmptyDOMElement()
+ await expect.element(formErrorElement).toBeEmptyDOMElement()
await user.type(input, 'other')
- await waitFor(() => {
- expect(firstNameErrorElement).not.toBeEmptyDOMElement()
- })
- expect(firstNameErrorElement).toHaveTextContent(
- 'First name cannot be "other"',
- )
- expect(formErrorElement).toHaveTextContent('Invalid form values')
+ await expect.element(firstNameErrorElement).not.toBeEmptyDOMElement()
+ await expect
+ .element(firstNameErrorElement)
+ .toHaveTextContent('First name cannot be "other"')
+ await expect
+ .element(formErrorElement)
+ .toHaveTextContent('Invalid form values')
})
it('should validate async on change and async on blur', async () => {
@@ -600,17 +622,23 @@ describe('useForm', () => {
)
}
- const { getByTestId, getByText, queryByText } = render()
+ const { getByTestId, getByText } = await render()
const input = getByTestId('fieldinput')
- expect(queryByText(onChangeError)).not.toBeInTheDocument()
- expect(queryByText(onBlurError)).not.toBeInTheDocument()
+ await expect
+ .element(getByText(onChangeError, { exact: true }))
+ .not.toBeInTheDocument()
+ await expect
+ .element(getByText(onBlurError, { exact: true }))
+ .not.toBeInTheDocument()
await user.type(input, 'other')
- await waitFor(() => getByText(onChangeError))
- expect(getByText(onChangeError)).toBeInTheDocument()
+ await expect
+ .element(getByText(onChangeError, { exact: true }))
+ .toBeInTheDocument()
await user.click(document.body)
- await waitFor(() => getByText(onBlurError))
- expect(getByText(onBlurError)).toBeInTheDocument()
+ await expect
+ .element(getByText(onBlurError, { exact: true }))
+ .toBeInTheDocument()
})
it('should validate async on change with debounce', async () => {
@@ -660,13 +688,12 @@ describe('useForm', () => {
)
}
- const { getByTestId, getByText } = render()
+ const { getByTestId, getByText } = await render()
const input = getByTestId('fieldinput')
await user.type(input, 'other')
// mockFn will have been called 5 times without onChangeAsyncDebounceMs
expect(mockFn).toHaveBeenCalledTimes(0)
- await waitFor(() => getByText(error))
- expect(getByText(error)).toBeInTheDocument()
+ await expect.element(getByText(error, { exact: true })).toBeInTheDocument()
})
it("should set errors on the fields from the form's onChange validator", async () => {
@@ -716,26 +743,28 @@ describe('useForm', () => {
)
}
- const { getByTestId, queryByText } = render()
+ const { getByTestId, getByText } = await render()
const input = getByTestId('fieldinput')
const fieldError = getByTestId('field-error')
const formError = getByTestId('form-error')
- expect(
- queryByText('Please enter a different value'),
- ).not.toBeInTheDocument()
- expect(queryByText('Something went wrong')).not.toBeInTheDocument()
+ await expect
+ .element(getByText('Please enter a different value', { exact: true }))
+ .not.toBeInTheDocument()
+ await expect
+ .element(getByText('Something went wrong', { exact: true }))
+ .not.toBeInTheDocument()
await user.type(input, 'Tom')
- await waitFor(() =>
- expect(fieldError.textContent).toBe('Please enter a different value'),
- )
- await waitFor(() =>
- expect(formError.textContent).toBe('Something went wrong'),
- )
+ await expect
+ .poll(() => fieldError.element().textContent)
+ .toBe('Please enter a different value')
+ await expect
+ .poll(() => formError.element().textContent)
+ .toBe('Something went wrong')
})
- it('should not cause infinite re-renders when listening to state.errors', () => {
+ it('should not cause infinite re-renders when listening to state.errors', async () => {
const fn = vi.fn()
function Comp() {
@@ -761,12 +790,12 @@ describe('useForm', () => {
return null
}
- render()
+ await render()
expect(fn).toHaveBeenCalledTimes(1)
})
- it('should not cause infinite re-renders when listening to state', () => {
+ it('should not cause infinite re-renders when listening to state', async () => {
const fn = vi.fn()
function Comp() {
@@ -790,7 +819,7 @@ describe('useForm', () => {
return null
}
- render()
+ await render()
expect(fn).toHaveBeenCalledTimes(1)
})
@@ -839,20 +868,20 @@ describe('useForm', () => {
)
}
- const { getByTestId } = render()
+ const { getByTestId } = await render()
const input = getByTestId('fieldinput')
const submit = getByTestId('submit')
const reset = getByTestId('reset')
await user.type(input, 'test')
- await waitFor(() => expect(input).toHaveValue('test'))
+ await expect.element(input).toHaveValue('test')
await user.click(reset)
- await waitFor(() => expect(input).toHaveValue(''))
+ await expect.element(input).toHaveValue('')
await user.type(input, 'another-test')
await user.click(submit)
- await waitFor(() => expect(input).toHaveValue('another-test'))
+ await expect.element(input).toHaveValue('another-test')
})
it('should accept formId and return it', async () => {
@@ -885,12 +914,12 @@ describe('useForm', () => {
)
}
- const { getByTestId } = render()
+ const { getByTestId } = await render()
const target = getByTestId('formId-target')
const result = getByTestId('formId-result')
await user.click(target)
- expect(result).toHaveTextContent('1')
+ await expect.element(result).toHaveTextContent('1')
})
it('should allow custom component keys for arrays', async () => {
@@ -932,7 +961,7 @@ describe('useForm', () => {
)
}
- const { getByTestId } = render()
+ const { getByTestId } = await render()
const target = getByTestId('removeField')
await user.click(target)
@@ -996,7 +1025,7 @@ describe('useForm', () => {
)
}
- const { getByTestId } = render()
+ const { getByTestId } = await render()
const removeButton = getByTestId('remove')
const input = getByTestId('input')
@@ -1034,8 +1063,10 @@ describe('useForm', () => {
)
}
- const { getByText } = render()
- getByText('What 1')
+ const { getByText } = await render()
+ await expect
+ .element(getByText('What 1', { exact: true }))
+ .toBeInTheDocument()
})
it('should handle stable transforms to update the base form on subsequent renders', async () => {
@@ -1070,10 +1101,14 @@ describe('useForm', () => {
)
}
- const { findByText, getByText } = render()
- await findByText('First')
- await user.click(getByText('Rerender'))
- await findByText('Another')
+ const { getByText } = await render()
+ await expect
+ .element(getByText('First', { exact: true }))
+ .toBeInTheDocument()
+ await user.click(getByText('Rerender', { exact: true }))
+ await expect
+ .element(getByText('Another', { exact: true }))
+ .toBeInTheDocument()
})
it('should render Subscribe without selector (default identity)', async () => {
@@ -1106,14 +1141,14 @@ describe('useForm', () => {
)
}
- const { getByTestId } = render()
+ const { getByTestId } = await render()
const input = getByTestId('input')
const stateValue = getByTestId('state-value')
- expect(stateValue).toHaveTextContent('test')
+ await expect.element(stateValue).toHaveTextContent('test')
await user.clear(input)
await user.type(input, 'updated')
- await waitFor(() => expect(stateValue).toHaveTextContent('updated'))
+ await expect.element(stateValue).toHaveTextContent('updated')
})
})
diff --git a/packages/react-form/tests/useFormGroup.test.tsx b/packages/react-form/tests/useFormGroup.test.tsx
index d8eabf34f3..82f3c73cba 100644
--- a/packages/react-form/tests/useFormGroup.test.tsx
+++ b/packages/react-form/tests/useFormGroup.test.tsx
@@ -1,6 +1,6 @@
import { describe, expect, it, vi } from 'vitest'
-import { render, waitFor } from '@testing-library/react'
-import { userEvent } from '@testing-library/user-event'
+import { render } from 'vitest-browser-react'
+import { userEvent } from 'vitest/browser'
import { useForm } from '../src/index'
const user = userEvent.setup()
@@ -48,10 +48,10 @@ describe('form.FormGroup', () => {
)
}
- const { getByTestId } = render()
+ const { getByTestId } = await render()
await user.click(getByTestId('submit-group'))
- await waitFor(() => expect(onGroupSubmit).toHaveBeenCalledTimes(1))
+ await vi.waitFor(() => expect(onGroupSubmit).toHaveBeenCalledTimes(1))
expect(onSubmit).not.toHaveBeenCalled()
})
@@ -87,15 +87,17 @@ describe('form.FormGroup', () => {
)
}
- const { getByTestId } = render()
- expect(getByTestId('group-value').textContent).toBe('{"name":"initial"}')
+ const { getByTestId } = await render()
+ expect(getByTestId('group-value').element().textContent).toBe(
+ '{"name":"initial"}',
+ )
await user.clear(getByTestId('step1-name'))
await user.type(getByTestId('step1-name'), 'updated')
- await waitFor(() =>
- expect(getByTestId('group-value').textContent).toBe('{"name":"updated"}'),
- )
+ await expect
+ .poll(() => getByTestId('group-value').element().textContent)
+ .toBe('{"name":"updated"}')
})
it('should call onGroupSubmitInvalid when group-level validation fails', async () => {
@@ -152,15 +154,17 @@ describe('form.FormGroup', () => {
)
}
- const { getByTestId } = render()
+ const { getByTestId } = await render()
await user.click(getByTestId('submit-group'))
- await waitFor(() => expect(onGroupSubmitInvalid).toHaveBeenCalledTimes(1))
+ await vi.waitFor(() =>
+ expect(onGroupSubmitInvalid).toHaveBeenCalledTimes(1),
+ )
expect(onGroupSubmit).not.toHaveBeenCalled()
expect(onSubmit).not.toHaveBeenCalled()
- await waitFor(() =>
- expect(getByTestId('group-error').textContent).toBe('Name is required'),
- )
+ await expect
+ .poll(() => getByTestId('group-error').element().textContent)
+ .toBe('Name is required')
})
it('should ignore form-level field errors outside the group when submitting the group', async () => {
@@ -215,10 +219,10 @@ describe('form.FormGroup', () => {
)
}
- const { getByTestId } = render()
+ const { getByTestId } = await render()
await user.click(getByTestId('submit-group'))
- await waitFor(() => expect(onGroupSubmit).toHaveBeenCalledTimes(1))
+ await vi.waitFor(() => expect(onGroupSubmit).toHaveBeenCalledTimes(1))
expect(onGroupSubmitInvalid).not.toHaveBeenCalled()
})
@@ -252,10 +256,10 @@ describe('form.FormGroup', () => {
)
}
- const { getByTestId } = render()
+ const { getByTestId } = await render()
await user.click(getByTestId('submit-group'))
- await waitFor(() => expect(onGroupSubmit).toHaveBeenCalledTimes(1))
+ await vi.waitFor(() => expect(onGroupSubmit).toHaveBeenCalledTimes(1))
expect(onGroupSubmit).toHaveBeenCalledWith(
expect.objectContaining({
value: { name: 'test' },
@@ -304,20 +308,20 @@ describe('form.FormGroup', () => {
)
}
- const { getByTestId } = render()
- const button = getByTestId('submit-group') as HTMLButtonElement
- expect(button.textContent).toBe('Continue')
- expect(button.disabled).toBe(false)
+ const { getByTestId } = await render()
+ const button = getByTestId('submit-group')
+ await expect.element(button).toHaveTextContent('Continue')
+ await expect.element(button).toBeEnabled()
await user.click(button)
- await waitFor(() => expect(button.textContent).toBe('Saving...'))
- expect(button.disabled).toBe(true)
+ await expect.element(button).toHaveTextContent('Saving...')
+ await expect.element(button).toBeDisabled()
resolveSubmit()
- await waitFor(() => expect(button.textContent).toBe('Continue'))
- expect(button.disabled).toBe(false)
+ await expect.element(button).toHaveTextContent('Continue')
+ await expect.element(button).toBeEnabled()
expect(onGroupSubmit).toHaveBeenCalledTimes(1)
})
})
diff --git a/packages/react-form/tsconfig.json b/packages/react-form/tsconfig.json
index f65fd3d402..99e8c0b59a 100644
--- a/packages/react-form/tsconfig.json
+++ b/packages/react-form/tsconfig.json
@@ -3,6 +3,7 @@
"compilerOptions": {
"jsx": "react-jsx",
"moduleResolution": "Bundler",
+ "types": ["vitest/browser"],
"paths": {
"@tanstack/form-core": ["../form-core/src"]
}
diff --git a/packages/react-form/vite.config.ts b/packages/react-form/vite.config.ts
index 8e68669a0c..23949c95ac 100644
--- a/packages/react-form/vite.config.ts
+++ b/packages/react-form/vite.config.ts
@@ -1,6 +1,7 @@
import { defineConfig, mergeConfig } from 'vitest/config'
import { tanstackViteConfig } from '@tanstack/vite-config'
import react from '@vitejs/plugin-react'
+import { playwright } from '@vitest/browser-playwright'
import packageJson from './package.json'
const config = defineConfig({
@@ -9,8 +10,14 @@ const config = defineConfig({
name: packageJson.name,
dir: './tests',
watch: false,
- environment: 'jsdom',
- setupFiles: ['./tests/test-setup.ts'],
+ setupFiles: ['vitest-browser-react'],
+ browser: {
+ enabled: true,
+ provider: playwright(
+ process.env.CI ? { launchOptions: { channel: 'chrome' } } : {},
+ ),
+ instances: [{ browser: 'chromium', headless: true }],
+ },
coverage: { enabled: true, provider: 'istanbul', include: ['src/**/*'] },
typecheck: { enabled: true },
},
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 8932ccbf97..cad7b4c986 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -193,9 +193,6 @@ importers:
'@testing-library/jest-dom':
specifier: ^6.8.0
version: 6.9.1
- '@testing-library/react':
- specifier: ^16.3.0
- version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@testing-library/user-event':
specifier: ^14.6.1
version: 14.6.1(@testing-library/dom@10.4.1)
@@ -264,7 +261,7 @@ importers:
version: 7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0)
vitest:
specifier: ^5.0.5
- version: 5.0.5(@types/node@24.12.4)(@vitest/coverage-istanbul@5.0.5)(jsdom@27.4.0(supports-color@7.2.0))(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))
+ version: 5.0.5(@types/node@24.12.4)(@vitest/browser-playwright@5.0.5)(@vitest/coverage-istanbul@5.0.5)(@vitest/ui@5.0.5)(jsdom@27.4.0(supports-color@7.2.0))(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))
examples/angular/array:
dependencies:
@@ -1906,6 +1903,9 @@ importers:
'@vitejs/plugin-react':
specifier: ^5.1.1
version: 5.2.0(supports-color@8.1.1)(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))
+ '@vitest/browser-playwright':
+ specifier: ^5.0.5
+ version: 5.0.5(playwright@1.64.1)(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.5)
eslint-plugin-react-compiler:
specifier: 19.1.0-rc.2
version: 19.1.0-rc.2(eslint@9.36.0(jiti@2.7.0)(supports-color@8.1.1))(supports-color@8.1.1)
@@ -1918,6 +1918,9 @@ importers:
vite:
specifier: ^7.2.2
version: 7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0)
+ vitest-browser-react:
+ specifier: ^2.3.0
+ version: 2.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(vitest@5.0.5)
packages/react-form-devtools:
dependencies:
@@ -3262,6 +3265,9 @@ packages:
resolution: {integrity: sha512-iXSSZAKQWmaeAwGtFw10v9fg5hUiUs0L+w+Z2K11kym3pDNK+SF0VcAQPsEFvWcTSZ7LMgClMXDBvTbH0IlICg==}
engines: {node: ^22.18.0 || >=24.11.0}
+ '@blazediff/core@1.10.0':
+ resolution: {integrity: sha512-AOQff0zgR7cGsZL+4E7hVkmujoPUpm0J9xzWGWZj5wCjd3gmxESXAPfKyuzs93VdpQNFhHlBhfOjrcZ+XTERtQ==}
+
'@changesets/apply-release-plan@8.1.1':
resolution: {integrity: sha512-nk2Hbq3M9NeHMdaIRBDvawKrQJzXB9sGyBUE5yWnIKtvdcNCPj75zBekJYr0gTT2ecJH3eFeTrc8eisPImcv0Q==}
engines: {node: ^22.11 || ^24 || >=26}
@@ -5966,6 +5972,9 @@ packages:
resolution: {integrity: sha512-pOr5+q1fLYKwFN3LAJuGZEnfXDcQ73zqgDHMtGy+K+uIoUqyY+6MeDCWFwfu+4EFuq76I5EPFofoNAI+Bmmq4A==}
engines: {node: '>=22.13'}
+ '@polka/url@1.0.0-next.29':
+ resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==}
+
'@popperjs/core@2.11.8':
resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==}
@@ -7326,21 +7335,6 @@ packages:
peerDependencies:
preact: '>=10 || ^10.0.0-alpha.0 || ^10.0.0-beta.0'
- '@testing-library/react@16.3.2':
- resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==}
- engines: {node: '>=18'}
- peerDependencies:
- '@testing-library/dom': ^10.0.0
- '@types/react': ^18.0.0 || ^19.0.0
- '@types/react-dom': ^18.0.0 || ^19.0.0
- react: ^18.0.0 || ^19.0.0
- react-dom: ^18.0.0 || ^19.0.0
- peerDependenciesMeta:
- '@types/react':
- optional: true
- '@types/react-dom':
- optional: true
-
'@testing-library/svelte-core@1.0.0':
resolution: {integrity: sha512-VkUePoLV6oOYwSUvX6ShA8KLnJqZiYMIbP2JW2t0GLWLkJxKGvuH5qrrZBV/X7cXFnLGuFQEC7RheYiZOW68KQ==}
engines: {node: '>=16'}
@@ -7809,6 +7803,17 @@ packages:
vite: ^5.0.0 || ^6.0.0
vue: ^3.2.25
+ '@vitest/browser-playwright@5.0.5':
+ resolution: {integrity: sha512-lcdThFUUcMV9mGGV1SUiZXPxfjgW4CssA7o3STxzh8WK0tT2r526kc12tZIBRZfoLMwkhxjLmMXkWXONzdsDQw==}
+ peerDependencies:
+ playwright: '*'
+ vitest: 5.0.5
+
+ '@vitest/browser@5.0.5':
+ resolution: {integrity: sha512-IDMoZX/iZ8rkilw7weaSgN8tJ7iZ9hUv0wYoKXyiyjWjWFwLSWAY707DcVTgxTYOudWC1E3HA9LYn7kVa9a7uQ==}
+ peerDependencies:
+ vitest: 5.0.5
+
'@vitest/coverage-istanbul@5.0.5':
resolution: {integrity: sha512-nmbE0+e1JSH9kYEoHGp/iKCQq6+TqwvQ4EcH2zKPULOIrY7rPHxDHsipYGGr+0xMDdzCy46F8zj3edz8dsqvdg==}
peerDependencies:
@@ -7841,9 +7846,20 @@ packages:
vite:
optional: true
+ '@vitest/pretty-format@5.0.5':
+ resolution: {integrity: sha512-24sSdIlMA9DVPXnYiB9siwU0bTd887Y9jX3+vabztCSsvHwo7ipvJ/a2L3Rljw8+2rK1f5fCj3h5U9I/Jsqgbw==}
+
'@vitest/spy@5.0.5':
resolution: {integrity: sha512-lwJRExuH417257Q5wv9QYugN9IMJkoxcrKrYxGPQkBzl7wlNpTUxD/5yNX4ESKVUIShKPgkGchYPFMWOOk0wzw==}
+ '@vitest/ui@5.0.5':
+ resolution: {integrity: sha512-dCoGUOdgmCaSB2g+WQ/spPCBkytAWdNUkFSXY4z+eYp2d8JEOY8v0uDRaJTG9qf/kTlTVZ4CuYMYMz/f1HS/qQ==}
+ peerDependencies:
+ vitest: 5.0.5
+
+ '@vitest/utils@5.0.5':
+ resolution: {integrity: sha512-/rBu7gzLDQI/SC79Tn98b5DwOgaHU/Gt4I9OVeP3YofzNaYtbImRCOJQmT1S6vOJg9gEIlWC+r3QxKkjuSa1nQ==}
+
'@volar/language-core@2.4.15':
resolution: {integrity: sha512-3VHw+QZU0ZG9IuQmzT68IyN4hZNd9GchGPhbD9+pa8CVv7rnoOZwo7T8weIbrRmihqy3ATpdfXFnqRrfPVK6CA==}
@@ -9934,6 +9950,9 @@ packages:
fetchdts@0.1.7:
resolution: {integrity: sha512-YoZjBdafyLIop9lSxXVI33oLD5kN31q4Td+CasofLLYeLXRFeOsuOw0Uo+XNRi9PZlbfdlN2GmRtm4tCEQ9/KA==}
+ fflate@0.8.3:
+ resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==}
+
figures@3.2.0:
resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==}
engines: {node: '>=8'}
@@ -9992,6 +10011,9 @@ packages:
flatted@3.4.2:
resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==}
+ flatted@3.4.4:
+ resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==}
+
flow-enums-runtime@0.0.6:
resolution: {integrity: sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==}
@@ -12332,6 +12354,16 @@ packages:
resolution: {integrity: sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==}
engines: {node: '>=16.0.0'}
+ playwright-core@1.64.1:
+ resolution: {integrity: sha512-BC4s8y6SEVA6Q1rSxZTto8hI1KUI3xXlHGZgvGqP4IqPdI/a6FX9H29IDNz7vKo9l21b6LfYosScPL6M3xxyDQ==}
+ engines: {node: '>=20'}
+ hasBin: true
+
+ playwright@1.64.1:
+ resolution: {integrity: sha512-HCrVhHr5QQuwYoswEUGTb7gGp522yflbv/ST8nF4sFWGuLjQCt17Q/EubpGGSNKYn1tsTCPZxqHhHCfVURu7UA==}
+ engines: {node: '>=20'}
+ hasBin: true
+
plist@3.1.1:
resolution: {integrity: sha512-ZIfcLJC+7E7FBFnDxm9MPmt7D+DidyQ26lewieO75AdhA2ayMtsJSES0iWzqJQbcVRSrTufQoy0DR94xHue0oA==}
engines: {node: '>=10.4.0'}
@@ -12340,6 +12372,10 @@ packages:
resolution: {integrity: sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==}
engines: {node: '>=4.0.0'}
+ pngjs@7.0.0:
+ resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==}
+ engines: {node: '>=14.19.0'}
+
possible-typed-array-names@1.1.0:
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
engines: {node: '>= 0.4'}
@@ -13343,6 +13379,10 @@ packages:
simple-swizzle@0.2.4:
resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==}
+ sirv@3.0.2:
+ resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==}
+ engines: {node: '>=18'}
+
sisteransi@1.0.5:
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
@@ -13851,6 +13891,10 @@ packages:
toml@3.0.0:
resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==}
+ totalist@3.0.1:
+ resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
+ engines: {node: '>=6'}
+
tough-cookie@6.0.1:
resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==}
engines: {node: '>=16'}
@@ -14510,6 +14554,20 @@ packages:
vite:
optional: true
+ vitest-browser-react@2.3.0:
+ resolution: {integrity: sha512-S5vdGHHA93j2Z6dbmIJI1wtxT3AXPTRlmC1Meo+ekG011yNnEutMAnHrwyj8qFlGhgBYA8ENGK6+o1Q+vnFz6A==}
+ peerDependencies:
+ '@types/react': ^18.0.0 || ^19.0.0
+ '@types/react-dom': ^18.0.0 || ^19.0.0
+ react: ^18.0.0 || ^19.0.0
+ react-dom: ^18.0.0 || ^19.0.0
+ vitest: ^4.0.0 || ^5.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
vitest@5.0.5:
resolution: {integrity: sha512-cPODOMF8YnkiiYtDdHo6n4wWmBrOBBbOIYH2d33M4l3CdNn72h+qMmO6uXffUN6A5AZFXNND/d+krS9mgkXRfg==}
engines: {node: ^22.12.0 || ^24.0.0 || >=26.0.0}
@@ -14828,6 +14886,18 @@ packages:
utf-8-validate:
optional: true
+ ws@8.22.0:
+ resolution: {integrity: sha512-Ydggc987+RO0AnWtZ/7Wq9FtNvcrL1b/RO0ud9mWjUPgDrsAAwQSF51sm2hm1XofbU/4jkpGEsLFsZZxU+1DOg==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: '>=5.0.2'
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
wsl-utils@0.1.0:
resolution: {integrity: sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==}
engines: {node: '>=18'}
@@ -15071,7 +15141,7 @@ snapshots:
'@analogjs/vite-plugin-angular': 2.9.2(81ea192f493565263f8b896c3e692134)
'@angular-devkit/architect': 0.2102.12(chokidar@5.0.0)
'@angular-devkit/schematics': 21.2.12(chokidar@5.0.0)
- vitest: 5.0.5(@types/node@24.12.4)(@vitest/coverage-istanbul@5.0.5)(jsdom@27.4.0(supports-color@8.1.1))(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))
+ vitest: 5.0.5(@types/node@24.12.4)(@vitest/browser-playwright@5.0.5)(@vitest/coverage-istanbul@5.0.5)(@vitest/ui@5.0.5)(jsdom@27.4.0(supports-color@8.1.1))(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))
optionalDependencies:
zone.js: 0.15.1
@@ -15548,7 +15618,7 @@ snapshots:
lmdb: 3.5.1
ng-packagr: 21.2.3(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.14)(supports-color@8.1.1)(typescript@5.9.3))(tslib@2.8.1)(typescript@5.9.3)
postcss: 8.5.12
- vitest: 5.0.5(@types/node@24.12.4)(@vitest/coverage-istanbul@5.0.5)(jsdom@27.4.0(supports-color@8.1.1))(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))
+ vitest: 5.0.5(@types/node@24.12.4)(@vitest/browser-playwright@5.0.5)(@vitest/coverage-istanbul@5.0.5)(@vitest/ui@5.0.5)(jsdom@27.4.0(supports-color@8.1.1))(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))
transitivePeerDependencies:
- '@emnapi/core'
- '@emnapi/runtime'
@@ -15605,7 +15675,7 @@ snapshots:
lmdb: 3.5.1
ng-packagr: 21.2.3(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.14)(supports-color@8.1.1)(typescript@5.9.3))(tslib@2.8.1)(typescript@5.9.3)
postcss: 8.5.12
- vitest: 5.0.5(@types/node@24.12.4)(@vitest/coverage-istanbul@5.0.5)(jsdom@27.4.0(supports-color@7.2.0))(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))
+ vitest: 5.0.5(@types/node@24.12.4)(@vitest/browser-playwright@5.0.5)(@vitest/coverage-istanbul@5.0.5)(@vitest/ui@5.0.5)(jsdom@27.4.0(supports-color@7.2.0))(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))
transitivePeerDependencies:
- '@emnapi/core'
- '@emnapi/runtime'
@@ -15661,7 +15731,7 @@ snapshots:
lmdb: 3.5.1
ng-packagr: 21.2.3(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.14)(supports-color@8.1.1)(typescript@5.9.3))(tslib@2.8.1)(typescript@5.9.3)
postcss: 8.5.12
- vitest: 5.0.5(@types/node@24.12.4)(@vitest/coverage-istanbul@5.0.5)(jsdom@27.4.0(supports-color@7.2.0))(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))
+ vitest: 5.0.5(@types/node@24.12.4)(@vitest/browser-playwright@5.0.5)(@vitest/coverage-istanbul@5.0.5)(@vitest/ui@5.0.5)(jsdom@27.4.0(supports-color@7.2.0))(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))
transitivePeerDependencies:
- '@emnapi/core'
- '@emnapi/runtime'
@@ -15717,7 +15787,7 @@ snapshots:
lmdb: 3.5.1
ng-packagr: 21.2.3(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.14)(supports-color@8.1.1)(typescript@5.9.3))(tslib@2.8.1)(typescript@5.9.3)
postcss: 8.5.12
- vitest: 5.0.5(@types/node@24.12.4)(@vitest/coverage-istanbul@5.0.5)(jsdom@27.4.0(supports-color@7.2.0))(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))
+ vitest: 5.0.5(@types/node@24.12.4)(@vitest/browser-playwright@5.0.5)(@vitest/coverage-istanbul@5.0.5)(@vitest/ui@5.0.5)(jsdom@27.4.0(supports-color@7.2.0))(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))
transitivePeerDependencies:
- '@emnapi/core'
- '@emnapi/runtime'
@@ -15773,7 +15843,7 @@ snapshots:
lmdb: 3.5.1
ng-packagr: 21.2.3(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.14)(supports-color@8.1.1)(typescript@5.9.3))(tslib@2.8.1)(typescript@5.9.3)
postcss: 8.5.15
- vitest: 5.0.5(@types/node@24.12.4)(@vitest/coverage-istanbul@5.0.5)(jsdom@27.4.0(supports-color@8.1.1))(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))
+ vitest: 5.0.5(@types/node@24.12.4)(@vitest/browser-playwright@5.0.5)(@vitest/coverage-istanbul@5.0.5)(@vitest/ui@5.0.5)(jsdom@27.4.0(supports-color@8.1.1))(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))
transitivePeerDependencies:
- '@emnapi/core'
- '@emnapi/runtime'
@@ -17228,6 +17298,8 @@ snapshots:
'@babel/helper-string-parser': 8.0.8
'@babel/helper-validator-identifier': 8.0.6
+ '@blazediff/core@1.10.0': {}
+
'@changesets/apply-release-plan@8.1.1':
dependencies:
'@changesets/config': 4.0.1
@@ -19772,6 +19844,8 @@ snapshots:
'@pnpm/deps.graph-sequencer@1100.0.1': {}
+ '@polka/url@1.0.0-next.29': {}
+
'@popperjs/core@2.11.8': {}
'@preact/preset-vite@2.10.5(@babel/core@7.29.7(supports-color@8.1.1))(preact@10.29.2)(rollup@4.60.4)(supports-color@8.1.1)(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))':
@@ -21273,16 +21347,6 @@ snapshots:
'@testing-library/dom': 8.20.1
preact: 10.29.2
- '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
- dependencies:
- '@babel/runtime': 7.29.7
- '@testing-library/dom': 10.4.1
- react: 19.1.0
- react-dom: 19.1.0(react@19.1.0)
- optionalDependencies:
- '@types/react': 19.2.17
- '@types/react-dom': 19.2.3(@types/react@19.2.17)
-
'@testing-library/svelte-core@1.0.0(svelte@5.55.9(@typescript-eslint/types@8.59.4))':
dependencies:
svelte: 5.55.9(@typescript-eslint/types@8.59.4)
@@ -21294,7 +21358,7 @@ snapshots:
svelte: 5.55.9(@typescript-eslint/types@8.59.4)
optionalDependencies:
vite: 7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0)
- vitest: 5.0.5(@types/node@24.12.4)(@vitest/coverage-istanbul@5.0.5)(jsdom@27.4.0(supports-color@8.1.1))(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))
+ vitest: 5.0.5(@types/node@24.12.4)(@vitest/browser-playwright@5.0.5)(@vitest/coverage-istanbul@5.0.5)(@vitest/ui@5.0.5)(jsdom@27.4.0(supports-color@8.1.1))(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))
'@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)':
dependencies:
@@ -21900,6 +21964,37 @@ snapshots:
vite: 7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0)
vue: 3.5.34(typescript@5.9.3)
+ '@vitest/browser-playwright@5.0.5(playwright@1.64.1)(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.5)':
+ dependencies:
+ '@vitest/browser': 5.0.5(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.5)
+ '@vitest/mocker': 5.0.5(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))
+ playwright: 1.64.1
+ tinyrainbow: 3.2.0
+ vitest: 5.0.5(@types/node@24.12.4)(@vitest/browser-playwright@5.0.5)(@vitest/coverage-istanbul@5.0.5)(@vitest/ui@5.0.5)(jsdom@27.4.0(supports-color@7.2.0))(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))
+ transitivePeerDependencies:
+ - bufferutil
+ - msw
+ - utf-8-validate
+ - vite
+
+ '@vitest/browser@5.0.5(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.5)':
+ dependencies:
+ '@blazediff/core': 1.10.0
+ '@vitest/mocker': 5.0.5(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))
+ '@vitest/ui': 5.0.5(vitest@5.0.5)
+ '@vitest/utils': 5.0.5
+ magic-string: 1.4.5
+ pngjs: 7.0.0
+ sirv: 3.0.2
+ tinyrainbow: 3.2.0
+ vitest: 5.0.5(@types/node@24.12.4)(@vitest/browser-playwright@5.0.5)(@vitest/coverage-istanbul@5.0.5)(@vitest/ui@5.0.5)(jsdom@27.4.0(supports-color@7.2.0))(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))
+ ws: 8.22.0
+ transitivePeerDependencies:
+ - bufferutil
+ - msw
+ - utf-8-validate
+ - vite
+
'@vitest/coverage-istanbul@5.0.5(vitest@5.0.5)':
dependencies:
'@jridgewell/gen-mapping': 0.3.13
@@ -21911,7 +22006,7 @@ snapshots:
magicast: 0.5.5
obug: 3.0.0
tinyrainbow: 3.2.0
- vitest: 5.0.5(@types/node@24.12.4)(@vitest/coverage-istanbul@5.0.5)(jsdom@27.4.0(supports-color@7.2.0))(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))
+ vitest: 5.0.5(@types/node@24.12.4)(@vitest/browser-playwright@5.0.5)(@vitest/coverage-istanbul@5.0.5)(@vitest/ui@5.0.5)(jsdom@27.4.0(supports-color@7.2.0))(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))
'@vitest/istanbul-lib-coverage@1.0.2': {}
@@ -21942,8 +22037,28 @@ snapshots:
optionalDependencies:
vite: 7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0)
+ '@vitest/pretty-format@5.0.5':
+ dependencies:
+ tinyrainbow: 3.2.0
+
'@vitest/spy@5.0.5': {}
+ '@vitest/ui@5.0.5(vitest@5.0.5)':
+ dependencies:
+ '@vitest/utils': 5.0.5
+ fflate: 0.8.3
+ flatted: 3.4.4
+ pathe: 2.0.3
+ sirv: 3.0.2
+ tinyrainbow: 3.2.0
+ vitest: 5.0.5(@types/node@24.12.4)(@vitest/browser-playwright@5.0.5)(@vitest/coverage-istanbul@5.0.5)(@vitest/ui@5.0.5)(jsdom@27.4.0(supports-color@7.2.0))(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))
+
+ '@vitest/utils@5.0.5':
+ dependencies:
+ '@vitest/pretty-format': 5.0.5
+ convert-source-map: 2.0.0
+ tinyrainbow: 3.2.0
+
'@volar/language-core@2.4.15':
dependencies:
'@volar/source-map': 2.4.15
@@ -24767,6 +24882,8 @@ snapshots:
fetchdts@0.1.7: {}
+ fflate@0.8.3: {}
+
figures@3.2.0:
dependencies:
escape-string-regexp: 1.0.5
@@ -24844,6 +24961,8 @@ snapshots:
flatted@3.4.2: {}
+ flatted@3.4.4: {}
+
flow-enums-runtime@0.0.6: {}
flru@1.0.2: {}
@@ -28001,6 +28120,12 @@ snapshots:
pvutils: 1.1.5
tslib: 2.8.1
+ playwright-core@1.64.1: {}
+
+ playwright@1.64.1:
+ dependencies:
+ playwright-core: 1.64.1
+
plist@3.1.1:
dependencies:
'@xmldom/xmldom': 0.9.10
@@ -28009,6 +28134,8 @@ snapshots:
pngjs@3.4.0: {}
+ pngjs@7.0.0: {}
+
possible-typed-array-names@1.1.0: {}
postcss-discard-duplicates@5.1.0(postcss@8.5.6):
@@ -29217,6 +29344,12 @@ snapshots:
dependencies:
is-arrayish: 0.3.4
+ sirv@3.0.2:
+ dependencies:
+ '@polka/url': 1.0.0-next.29
+ mrmime: 2.0.1
+ totalist: 3.0.1
+
sisteransi@1.0.5: {}
slash@3.0.0: {}
@@ -29775,6 +29908,8 @@ snapshots:
toml@3.0.0: {}
+ totalist@3.0.1: {}
+
tough-cookie@6.0.1:
dependencies:
tldts: 7.4.0
@@ -30497,7 +30632,16 @@ snapshots:
optionalDependencies:
vite: 7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0)
- vitest@5.0.5(@types/node@24.12.4)(@vitest/coverage-istanbul@5.0.5)(jsdom@27.4.0(supports-color@7.2.0))(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0)):
+ vitest-browser-react@2.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(vitest@5.0.5):
+ dependencies:
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ vitest: 5.0.5(@types/node@24.12.4)(@vitest/browser-playwright@5.0.5)(@vitest/coverage-istanbul@5.0.5)(@vitest/ui@5.0.5)(jsdom@27.4.0(supports-color@8.1.1))(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))
+ optionalDependencies:
+ '@types/react': 19.2.17
+ '@types/react-dom': 19.2.3(@types/react@19.2.17)
+
+ vitest@5.0.5(@types/node@24.12.4)(@vitest/browser-playwright@5.0.5)(@vitest/coverage-istanbul@5.0.5)(@vitest/ui@5.0.5)(jsdom@27.4.0(supports-color@7.2.0))(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0)):
dependencies:
'@types/chai': 5.2.3
'@vitest/mocker': 5.0.5(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))
@@ -30515,12 +30659,14 @@ snapshots:
why-is-node-running: 3.2.1
optionalDependencies:
'@types/node': 24.12.4
+ '@vitest/browser-playwright': 5.0.5(playwright@1.64.1)(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.5)
'@vitest/coverage-istanbul': 5.0.5(vitest@5.0.5)
+ '@vitest/ui': 5.0.5(vitest@5.0.5)
jsdom: 27.4.0(supports-color@7.2.0)
transitivePeerDependencies:
- msw
- vitest@5.0.5(@types/node@24.12.4)(@vitest/coverage-istanbul@5.0.5)(jsdom@27.4.0(supports-color@8.1.1))(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0)):
+ vitest@5.0.5(@types/node@24.12.4)(@vitest/browser-playwright@5.0.5)(@vitest/coverage-istanbul@5.0.5)(@vitest/ui@5.0.5)(jsdom@27.4.0(supports-color@8.1.1))(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0)):
dependencies:
'@types/chai': 5.2.3
'@vitest/mocker': 5.0.5(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))
@@ -30538,7 +30684,9 @@ snapshots:
why-is-node-running: 3.2.1
optionalDependencies:
'@types/node': 24.12.4
+ '@vitest/browser-playwright': 5.0.5(playwright@1.64.1)(vite@7.3.3(@types/node@24.12.4)(jiti@2.7.0)(less@4.6.4)(lightningcss@1.32.0)(sass@1.100.0)(sugarss@5.0.1(postcss@8.5.15))(terser@5.48.0)(yaml@2.9.0))(vitest@5.0.5)
'@vitest/coverage-istanbul': 5.0.5(vitest@5.0.5)
+ '@vitest/ui': 5.0.5(vitest@5.0.5)
jsdom: 27.4.0(supports-color@8.1.1)
transitivePeerDependencies:
- msw
@@ -31009,6 +31157,8 @@ snapshots:
ws@8.21.0: {}
+ ws@8.22.0: {}
+
wsl-utils@0.1.0:
dependencies:
is-wsl: 3.1.1