Skip to content

Commit 39f2a2f

Browse files
Migrate tests to Vitest 4 (#67)
* migrate tests to vitest 4 Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com> * Fix vitest alias path resolution Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com> * Fix stopProcess signal exit handling Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com> * Fix resend mock output capture Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com> * address vitest review feedback Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com> * Fix mock server in ai runtime tests Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com> * Fix ai runtime mock reset order Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com> * Fix wrangler test process helpers Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com> * address latest review feedback Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
1 parent 0758248 commit 39f2a2f

25 files changed

+508
-255
lines changed

.github/workflows/validate.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ jobs:
6868

6969
test:
7070
runs-on: ubuntu-latest
71-
name: Bun Tests
71+
name: Unit Tests
7272
if: >-
7373
github.event_name != 'pull_request' || github.event.pull_request.draft ==
7474
false
@@ -84,8 +84,8 @@ jobs:
8484
- name: 📥 Install Dependencies
8585
run: bun install --frozen-lockfile
8686

87-
- name: ✅ Run Bun Tests
88-
run: bun test ./server ./mock-servers
87+
- name: ✅ Run Unit Tests
88+
run: bun run test:unit
8989

9090
e2e:
9191
runs-on: ubuntu-latest

bun.lock

Lines changed: 125 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/app-session-refresh.test.ts

Lines changed: 62 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,34 @@
11
/// <reference types="bun" />
2-
import { expect, mock, test } from 'bun:test'
2+
import { expect, test, vi } from 'vitest'
33
import { type Handle } from 'remix/component'
44

55
type QueueTask = Parameters<Handle['queueTask']>[0]
66

77
const navigationListeners: Array<() => void> = []
88
const queuedSessionResponses: Array<{ email: string } | null> = []
9-
const fetchSessionInfoMock = mock(async () => {
9+
const fetchSessionInfoMock = vi.fn(async () => {
1010
return queuedSessionResponses.shift() ?? null
1111
})
1212

13-
mock.module('./client-router.tsx', () => ({
14-
routerEvents: new EventTarget(),
15-
listenToRouterNavigation: (_handle: Handle, listener: () => void) => {
16-
navigationListeners.push(listener)
17-
},
18-
getPathname: () => '/',
19-
navigate: () => {
20-
return
21-
},
22-
Router: () => () => null,
23-
}))
24-
25-
mock.module('./session.ts', () => ({
26-
fetchSessionInfo: fetchSessionInfoMock,
27-
}))
28-
29-
const { App } = await import('./app.tsx')
13+
async function loadApp() {
14+
vi.resetModules()
15+
vi.doMock('./client-router.tsx', () => ({
16+
routerEvents: new EventTarget(),
17+
listenToRouterNavigation: (_handle: Handle, listener: () => void) => {
18+
navigationListeners.push(listener)
19+
},
20+
getPathname: () => '/',
21+
navigate: () => {
22+
return
23+
},
24+
Router: () => () => null,
25+
}))
26+
vi.doMock('./session.ts', () => ({
27+
fetchSessionInfo: fetchSessionInfoMock,
28+
}))
29+
const { App } = await import('./app.tsx')
30+
return App
31+
}
3032

3133
async function runNextTask(tasks: Array<QueueTask>, aborted: boolean) {
3234
const task = tasks.shift()
@@ -40,6 +42,7 @@ test('aborted refresh does not erase a ready authenticated session', async () =>
4042
navigationListeners.length = 0
4143
queuedSessionResponses.length = 0
4244
queuedSessionResponses.push({ email: 'signed-in@example.com' }, null)
45+
fetchSessionInfoMock.mockClear()
4346

4447
const queuedTasks: Array<QueueTask> = []
4548
const handle = {
@@ -54,24 +57,56 @@ test('aborted refresh does not erase a ready authenticated session', async () =>
5457
},
5558
} as unknown as Handle
5659

60+
const App = await loadApp()
5761
const render = App(handle)
5862
expect(navigationListeners).toHaveLength(1)
5963

6064
// Initial bootstrap task enqueues the session fetch.
6165
await runNextTask(queuedTasks, false)
6266
await runNextTask(queuedTasks, false)
6367

64-
const authenticatedUi = Bun.inspect(render())
65-
expect(authenticatedUi).toContain('signed-in@example.com')
66-
expect(authenticatedUi).toContain('Log out')
68+
const authenticatedUi = render()
69+
const navChildren = (authenticatedUi.props as { children: Array<unknown> })
70+
.children[0] as {
71+
props: { children: Array<unknown> }
72+
}
73+
const navItems = navChildren.props.children as Array<unknown>
74+
const accountLink = navItems[2] as { props?: { children?: Array<unknown> } }
75+
const accountEntry = (accountLink.props?.children ?? [])[1] as {
76+
props?: { children?: string }
77+
}
78+
const logoutEntry = (accountLink.props?.children ?? [])[2] as {
79+
props?: { children?: { props?: { children?: string } } }
80+
}
81+
expect(accountEntry.props?.children).toBe('signed-in@example.com')
82+
expect(logoutEntry.props?.children?.props?.children).toBe('Log out')
6783

6884
// Re-run refresh via navigation, then abort in-flight fetch.
6985
navigationListeners[0]!()
7086
await runNextTask(queuedTasks, true)
7187

72-
const uiAfterAbort = Bun.inspect(render())
73-
expect(uiAfterAbort).toContain('signed-in@example.com')
74-
expect(uiAfterAbort).toContain('Log out')
75-
expect(uiAfterAbort).not.toContain('>Login<')
76-
expect(uiAfterAbort).not.toContain('>Signup<')
88+
const uiAfterAbort = render()
89+
const navAfterAbort = (uiAfterAbort.props as { children: Array<unknown> })
90+
.children[0] as {
91+
props: { children: Array<unknown> }
92+
}
93+
const navAfterAbortItems = navAfterAbort.props.children as Array<unknown>
94+
expect(navAfterAbortItems).toHaveLength(3)
95+
const accountLinkAfterAbort = navAfterAbortItems[2] as {
96+
props?: { children?: Array<unknown> }
97+
}
98+
const accountEntryAfterAbort = (accountLinkAfterAbort.props?.children ?? [])[1] as {
99+
props?: { children?: string }
100+
}
101+
const logoutEntryAfterAbort = (accountLinkAfterAbort.props?.children ?? [])[2] as {
102+
props?: { children?: { props?: { children?: string } } }
103+
}
104+
expect(accountEntryAfterAbort.props?.children).toBe('signed-in@example.com')
105+
expect(logoutEntryAfterAbort.props?.children?.props?.children).toBe('Log out')
106+
const navAfterAbortText = JSON.stringify(navAfterAbort)
107+
expect(navAfterAbortText).not.toContain('Login')
108+
expect(navAfterAbortText).not.toContain('Signup')
109+
110+
vi.doUnmock('./client-router.tsx')
111+
vi.doUnmock('./session.ts')
77112
})

client/mcp-apps/widget-host-bridge.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { afterEach, expect, test } from 'bun:test'
1+
import { afterEach, expect, test } from 'vitest'
22
import { createWidgetHostBridge } from './widget-host-bridge.ts'
33

44
type HostRequestMessage = {

docs/agents/testing-principles.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@ magic.
1717
- Write tests so they could run offline if necessary: avoid relying on the
1818
public internet and third-party services; prefer local fakes/fixtures.
1919
- Prefer fast unit tests for server logic; keep e2e tests focused on journeys.
20-
- Run server/unit tests with `bun test ./server ./mock-servers` to avoid
20+
- Run server/unit tests with `bun run test:unit` to avoid
2121
Playwright spec discovery and accidental matches like `mcp-server-e2e`.
2222

2323
## Examples
2424

2525
### `Symbol.dispose` with `using`
2626

2727
```ts
28-
import { test, expect } from 'bun:test'
28+
import { test, expect } from 'vitest'
2929

3030
const createTempFile = () => {
3131
const path = `/tmp/test-${crypto.randomUUID()}.txt`
@@ -53,7 +53,7 @@ test('reads a temp file', () => {
5353
### `Symbol.asyncDispose` with `await using`
5454

5555
```ts
56-
import { test, expect } from 'bun:test'
56+
import { test, expect } from 'vitest'
5757

5858
const createDisposableServer = async () => {
5959
const server = Bun.serve({

mcp/context.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/// <reference types="bun" />
2-
import { expect, test } from 'bun:test'
2+
import { expect, test } from 'vitest'
33
import { createMcpCallerContext, parseMcpCallerContext } from './context.ts'
44

55
test('createMcpCallerContext normalizes missing user to null', () => {

0 commit comments

Comments
 (0)