Skip to content

Commit dfbdd2c

Browse files
ntatoudfranky47
andauthored
chore: Vitest Browser Mode (#1254)
Co-authored-by: François Best <[email protected]>
1 parent 517a2b5 commit dfbdd2c

15 files changed

Lines changed: 498 additions & 448 deletions

.github/workflows/ci-cd.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ jobs:
8181
cache: pnpm
8282
- name: Install dependencies
8383
run: pnpm install --ignore-scripts --frozen-lockfile --filter nuqs...
84+
- name: Install Playwright Chromium
85+
run: ./node_modules/.bin/playwright install chromium
86+
working-directory: packages/nuqs
8487
- name: Run tests
8588
run: pnpm run test ${{ github.event_name == 'workflow_dispatch' && '--force' || '' }} --filter nuqs
8689
env:

packages/nuqs/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
size.json
2+
src/__screenshots__

packages/nuqs/package.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,9 @@
133133
"build": "tsdown",
134134
"build:size-json": "size-limit --json > size.json",
135135
"test": "pnpm run --stream '/^test:/'",
136-
"test:unit": "vitest run --typecheck",
136+
"test:unit": "vitest run --project unit",
137+
"test:browser": "vitest run --project browser --browser.headless",
138+
"test:types": "vitest run --project types",
137139
"test:size": "size-limit",
138140
"prepack": "./scripts/prepack.sh"
139141
},
@@ -168,27 +170,25 @@
168170
"devDependencies": {
169171
"@remix-run/react": "^2.17.0",
170172
"@size-limit/preset-small-lib": "^11.2.0",
171-
"@testing-library/dom": "^10.4.1",
172-
"@testing-library/jest-dom": "^6.8.0",
173-
"@testing-library/react": "^16.3.0",
174-
"@testing-library/user-event": "^14.6.1",
175173
"@types/node": "^24.3.0",
176174
"@types/react": "catalog:react19",
177175
"@types/react-dom": "catalog:react19",
178176
"@vitejs/plugin-react": "^5.0.4",
179-
"@vitest/coverage-v8": "^4.0.1",
177+
"@vitest/browser-playwright": "^4.0.15",
178+
"@vitest/coverage-v8": "^4.0.15",
180179
"arktype": "^2.1.20",
181180
"fast-check": "^4.2.0",
182-
"jsdom": "^27.0.1",
183181
"next": "16.0.7",
182+
"playwright": "catalog:e2e",
184183
"react": "catalog:react19",
185184
"react-dom": "catalog:react19",
186185
"react-router-dom": "6.30.1",
187186
"size-limit": "^11.2.0",
188187
"tsdown": "^0.15.9",
189188
"typescript": "^5.9.2",
190189
"valibot": "^1.2.0",
191-
"vitest": "^4.0.1",
190+
"vitest": "^4.0.15",
191+
"vitest-browser-react": "2.0.2",
192192
"vitest-package-exports": "^0.1.1",
193193
"zod": "^4.1.5"
194194
},

packages/nuqs/src/adapters/lib/patch-history.test.ts renamed to packages/nuqs/src/adapters/lib/patch-history.browser.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { describe, expect, it, vi } from 'vitest'
1+
import { describe, expect, it } from 'vitest'
22
import { getSearchParams } from './patch-history'
33

44
describe('patch-history/getSearchParams', () => {
@@ -13,7 +13,6 @@ describe('patch-history/getSearchParams', () => {
1313
expect(received).toEqual(expected)
1414
})
1515
it('extracts search params from a pathname', () => {
16-
vi.stubGlobal('location', { origin: 'http://example.com' })
1716
const received = getSearchParams('/?foo=bar')
1817
const expected = new URLSearchParams('?foo=bar')
1918
expect(received).toEqual(expected)

packages/nuqs/src/lib/queues/useSyncExternalStores.test.ts renamed to packages/nuqs/src/lib/queues/useSyncExternalStores.browser.test.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,31 @@
1-
import { act, renderHook } from '@testing-library/react'
21
import { useRef } from 'react'
32
import { describe, expect, it } from 'vitest'
3+
import { renderHook } from 'vitest-browser-react'
44
import { createEmitter } from '../emitter'
55
import { useSyncExternalStores } from './useSyncExternalStores'
66

77
describe('useSyncExternalStores', () => {
8-
it('should handle an empty array of keys', () => {
8+
it('should handle an empty array of keys', async () => {
99
const useTest = () =>
1010
useSyncExternalStores(
1111
[],
1212
(_, callback) => callback,
1313
_ => 'snapshot'
1414
)
15-
const { result } = renderHook(useTest)
15+
const { result } = await renderHook(useTest)
1616
expect(result.current).toEqual({})
1717
})
18-
it('should handle a single key', () => {
18+
it('should handle a single key', async () => {
1919
const useTest = () =>
2020
useSyncExternalStores(
2121
['a'],
2222
(_, callback) => callback,
2323
_ => 'snapshot'
2424
)
25-
const { result } = renderHook(useTest)
25+
const { result } = await renderHook(useTest)
2626
expect(result.current).toEqual({ a: 'snapshot' })
2727
})
28-
it('should be reactive to changes in the store', () => {
28+
it('should be reactive to changes in the store', async () => {
2929
const emitter = createEmitter()
3030
const store: Record<string, number> = {
3131
a: 0
@@ -39,7 +39,7 @@ describe('useSyncExternalStores', () => {
3939
},
4040
key => store[key]
4141
)
42-
const { result } = renderHook(useTest)
42+
const { result, act } = await renderHook(useTest)
4343
expect(result.current).toEqual({ a: 0 })
4444
// Update the store
4545
act(() => {
@@ -48,7 +48,7 @@ describe('useSyncExternalStores', () => {
4848
})
4949
expect(result.current).toEqual({ a: 1 })
5050
})
51-
it('should be reactive to changes in the keys', () => {
51+
it('should be reactive to changes in the keys', async () => {
5252
const emitter = createEmitter()
5353
const store: Record<string, number> = {
5454
a: 0,
@@ -63,12 +63,12 @@ describe('useSyncExternalStores', () => {
6363
},
6464
key => store[key]
6565
)
66-
const { result, rerender } = renderHook(useTest)
66+
const { result, rerender } = await renderHook(useTest)
6767
expect(result.current).toEqual({ a: 0 })
6868
rerender({ keys: ['b'] })
6969
expect(result.current).toEqual({ b: 0 })
7070
})
71-
it('should not re-render when a non-listened key changes', () => {
71+
it('should not re-render when a non-listened key changes', async () => {
7272
const emitter = createEmitter()
7373
const store: Record<string, number> = {
7474
a: 0,
@@ -89,7 +89,7 @@ describe('useSyncExternalStores', () => {
8989
result
9090
}
9191
}
92-
const { result } = renderHook(useTest)
92+
const { result, act } = await renderHook(useTest)
9393
expect(result.current.result).toEqual({ a: 0 })
9494
expect(result.current.renderCount).toBe(1)
9595
// Update another key

packages/nuqs/src/lib/sync.test.tsx renamed to packages/nuqs/src/lib/sync.browser.test.tsx

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { render, screen } from '@testing-library/react'
2-
import userEvent from '@testing-library/user-event'
31
import React from 'react'
42
import { describe, expect, it } from 'vitest'
3+
import { page, userEvent } from 'vitest/browser'
54
import { withNuqsTestingAdapter } from '../adapters/testing'
65
import { parseAsInteger, useQueryState, useQueryStates } from '../index'
6+
import { render } from 'vitest-browser-react'
77

88
type TestComponentProps = {
99
testId: string
@@ -24,7 +24,7 @@ describe('sync', () => {
2424
}
2525

2626
const user = userEvent.setup()
27-
render(
27+
await render(
2828
<>
2929
<TestComponent testId="a" />
3030
<TestComponent testId="b" />
@@ -34,14 +34,14 @@ describe('sync', () => {
3434
}
3535
)
3636
// Act
37-
const buttonA = screen.getByTestId('a')
38-
const buttonB = screen.getByTestId('b')
37+
const buttonA = page.getByTestId('a')
38+
const buttonB = page.getByTestId('b')
3939
await user.click(buttonA)
40-
expect(buttonA).toHaveTextContent('count is 1')
41-
expect(buttonB).toHaveTextContent('count is 1')
40+
await expect.element(buttonA).toHaveTextContent('count is 1')
41+
await expect.element(buttonB).toHaveTextContent('count is 1')
4242
await user.click(buttonB)
43-
expect(buttonA).toHaveTextContent('count is 2')
44-
expect(buttonB).toHaveTextContent('count is 2')
43+
await expect.element(buttonA).toHaveTextContent('count is 2')
44+
await expect.element(buttonB).toHaveTextContent('count is 2')
4545
})
4646

4747
it('should sync useQueryState and useQueryStates', async () => {
@@ -71,7 +71,7 @@ describe('sync', () => {
7171
}
7272

7373
const user = userEvent.setup()
74-
render(
74+
await render(
7575
<>
7676
<TestComponentA testId="a" />
7777
<TestComponentB testId="b" />
@@ -81,13 +81,13 @@ describe('sync', () => {
8181
}
8282
)
8383
// Act
84-
const buttonA = screen.getByTestId('a')
85-
const buttonB = screen.getByTestId('b')
84+
const buttonA = page.getByTestId('a')
85+
const buttonB = page.getByTestId('b')
8686
await user.click(buttonA)
87-
expect(buttonA).toHaveTextContent('count is 1')
88-
expect(buttonB).toHaveTextContent('count is 1')
87+
await expect.element(buttonA).toHaveTextContent('count is 1')
88+
await expect.element(buttonB).toHaveTextContent('count is 1')
8989
await user.click(buttonB)
90-
expect(buttonA).toHaveTextContent('count is 2')
91-
expect(buttonB).toHaveTextContent('count is 2')
90+
await expect.element(buttonA).toHaveTextContent('count is 2')
91+
await expect.element(buttonB).toHaveTextContent('count is 2')
9292
})
9393
})
File renamed without changes.

0 commit comments

Comments
 (0)