Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions packages/react-router/tests/ClientOnly.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import { afterEach, describe, expect, it, onTestFinished, vi } from 'vitest'
import React from 'react'
import ReactDOMServer from 'react-dom/server'
import { act, cleanup, render, screen } from '@testing-library/react'
Expand All @@ -12,7 +12,6 @@ import {
import { ClientOnly } from '../src/ClientOnly'

afterEach(() => {
vi.resetAllMocks()
cleanup()
})

Expand Down Expand Up @@ -70,7 +69,10 @@ describe('ClientOnly', () => {
await router.load()

// Mock useSyncExternalStore to simulate hydration
vi.spyOn(React, 'useSyncExternalStore').mockImplementation(() => true)
const useSyncExternalStore = vi
.spyOn(React, 'useSyncExternalStore')
.mockImplementation(() => true)
onTestFinished(() => useSyncExternalStore.mockRestore())

render(<RouterProvider router={router} />)

Expand All @@ -83,7 +85,10 @@ describe('ClientOnly', () => {
await router.load()

// Simulate hydration
vi.spyOn(React, 'useSyncExternalStore').mockImplementation(() => true)
const useSyncExternalStore = vi
.spyOn(React, 'useSyncExternalStore')
.mockImplementation(() => true)
onTestFinished(() => useSyncExternalStore.mockRestore())

// Re-render after hydration
render(<RouterProvider router={router} />)
Expand Down
4 changes: 2 additions & 2 deletions packages/react-router/tests/Matches.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { afterEach, describe, expect, test } from 'vitest'
import { afterEach, describe, expect, onTestFinished, test } from 'vitest'
import {
act,
cleanup,
Expand Down Expand Up @@ -267,6 +267,7 @@ test('legacy notFoundRoute drops a stale parent layout after navigation', async
})

const rendered = render(<RouterProvider router={router} />)
onTestFinished(() => rendered.unmount())
expect(await rendered.findByText('Parent layout')).toBeInTheDocument()
expect(await rendered.findByText('Legacy not found')).toBeInTheDocument()
expect(legacyLoads).toBe(1)
Expand All @@ -278,7 +279,6 @@ test('legacy notFoundRoute drops a stale parent layout after navigation', async
expect(rendered.queryByText('Parent layout')).not.toBeInTheDocument()
expect(await rendered.findByText('Legacy not found')).toBeInTheDocument()
expect(legacyLoads).toBe(1)
rendered.unmount()
})

describe('matching on different param types', () => {
Expand Down
7 changes: 2 additions & 5 deletions packages/react-router/tests/Scripts.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { afterEach, describe, expect, test, vi } from 'vitest'
import { afterEach, describe, expect, onTestFinished, test, vi } from 'vitest'
import {
act,
cleanup,
Expand Down Expand Up @@ -48,17 +48,14 @@ const createTestManifest = (
},
}) satisfies Manifest

const browserHistories: Array<ReturnType<typeof createBrowserHistory>> = []

const createTestBrowserHistory = () => {
const history = createBrowserHistory()
browserHistories.push(history)
onTestFinished(() => history.destroy())
return history
}

afterEach(() => {
cleanup()
browserHistories.splice(0).forEach((history) => history.destroy())
window.history.replaceState(null, 'root', '/')
delete window.$_TSR
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as React from 'react'
import { act } from 'react'
import { afterEach, expect, test, vi } from 'vitest'
import { afterEach, expect, onTestFinished, test, vi } from 'vitest'
import { cleanup, fireEvent, render, screen } from '@testing-library/react'
import { createControlledPromise } from '@tanstack/router-core'
import {
Expand All @@ -14,8 +14,6 @@ import {
import type { ErrorComponentProps } from '../src'

afterEach(() => {
vi.useRealTimers()
vi.restoreAllMocks()
cleanup()
})

Expand Down Expand Up @@ -52,7 +50,11 @@ test('delayed component preload reveals pending UI', async () => {
* ready and pendingMinMs has elapsed.
*/
test('component preload retry remains pending through pendingMinMs', async () => {
vi.spyOn(console, 'error').mockImplementation(() => {})
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {})
onTestFinished(() => {
vi.useRealTimers()
consoleError.mockRestore()
})

const retryChunk = createControlledPromise<void>()
let preloadAttempt = 0
Expand Down
12 changes: 7 additions & 5 deletions packages/react-router/tests/component-preload-retry.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react'
import { afterEach, expect, test, vi } from 'vitest'
import { afterEach, expect, onTestFinished, test, vi } from 'vitest'
import { cleanup, fireEvent, render, screen } from '@testing-library/react'
import { createControlledPromise } from '@tanstack/router-core'
import {
Expand All @@ -15,13 +15,13 @@ import type { ErrorComponentProps } from '../src'

afterEach(() => {
cleanup()
vi.restoreAllMocks()
vi.unstubAllGlobals()
sessionStorage.clear()
})

test('a successful server component download is reused', async () => {
vi.stubGlobal('window', undefined)
onTestFinished(() => {
vi.unstubAllGlobals()
})
const importer = vi.fn().mockResolvedValue({ default: () => null })
const Page = lazyRouteComponent(importer)

Expand All @@ -46,7 +46,8 @@ test('concurrent component preloads share the import', async () => {
})

test('a failed component download is retried from the route error UI', async () => {
vi.spyOn(console, 'error').mockImplementation(() => {})
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {})
onTestFinished(() => consoleError.mockRestore())

const PageContent = () => <div>Page content</div>
const importer = vi
Expand Down Expand Up @@ -97,6 +98,7 @@ test('a failed component download is retried from the route error UI', async ()
})

test('renders after retrying a module download that failed during preload', async () => {
onTestFinished(() => sessionStorage.clear())
const PageContent = () => <div>Page content</div>
const importer = vi
.fn<() => Promise<{ default: typeof PageContent }>>()
Expand Down
27 changes: 21 additions & 6 deletions packages/react-router/tests/errorComponent.test.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
import {
afterEach,
beforeEach,
describe,
expect,
onTestFinished,
test,
vi,
} from 'vitest'
import { act, cleanup, fireEvent, render, screen } from '@testing-library/react'

import {
Expand Down Expand Up @@ -48,7 +56,6 @@ beforeEach(() => {

afterEach(() => {
history.destroy()
vi.resetAllMocks()
window.history.replaceState(null, 'root', '/')
cleanup()
})
Expand Down Expand Up @@ -296,8 +303,12 @@ test('global catch boundary resets when a background child generation recovers',
routeTree: rootRoute.addChildren([childRoute]),
history,
})
vi.spyOn(console, 'warn').mockImplementation(() => {})
vi.spyOn(console, 'error').mockImplementation(() => {})
const consoleWarn = vi.spyOn(console, 'warn').mockImplementation(() => {})
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {})
onTestFinished(() => {
consoleWarn.mockRestore()
consoleError.mockRestore()
})

render(<RouterProvider router={router} />)
expect(
Expand Down Expand Up @@ -345,8 +356,12 @@ test('ancestor route errorComponent resets when a background child generation re
routeTree: rootRoute.addChildren([childRoute]),
history,
})
vi.spyOn(console, 'warn').mockImplementation(() => {})
vi.spyOn(console, 'error').mockImplementation(() => {})
const consoleWarn = vi.spyOn(console, 'warn').mockImplementation(() => {})
const consoleError = vi.spyOn(console, 'error').mockImplementation(() => {})
onTestFinished(() => {
consoleWarn.mockRestore()
consoleError.mockRestore()
})

let invalidation: Promise<void> | undefined
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as React from 'react'
import { act } from '@testing-library/react'
import { hydrateRoot } from 'react-dom/client'
import { renderToString } from 'react-dom/server'
import { afterEach, describe, expect, test, vi } from 'vitest'
import { describe, expect, onTestFinished, test, vi } from 'vitest'
import { createMemoryHistory } from '@tanstack/history'
import { dehydrateSsrMatchId } from '../../router-core/src/ssr/ssr-match-id'
import { hydrate } from '../src/ssr/client'
Expand All @@ -22,17 +22,6 @@ declare global {
}
}

const testCleanups: Array<() => void | Promise<void>> = []

afterEach(async () => {
while (testCleanups.length) {
await testCleanups.pop()!()
}
vi.restoreAllMocks()
window.$_TSR = undefined
document.body.innerHTML = ''
})

describe('hydrating a server-capped boundary lane', () => {
test('recovers a /404 payload against a missing browser URL', async () => {
function MissingPage() {
Expand Down Expand Up @@ -87,6 +76,9 @@ describe('hydrating a server-capped boundary lane', () => {
buffer: [],
initialized: false,
}
onTestFinished(() => {
window.$_TSR = undefined
})

await hydrate(clientRouter)

Expand All @@ -98,8 +90,9 @@ describe('hydrating a server-capped boundary lane', () => {
root = hydrateRoot(container, <RouterProvider router={clientRouter} />, {
onRecoverableError: () => {},
})
testCleanups.push(async () => {
onTestFinished(async () => {
await act(() => root.unmount())
container.remove()
})
await Promise.resolve()
})
Expand Down Expand Up @@ -229,6 +222,9 @@ describe('hydrating a server-capped boundary lane', () => {
buffer: [],
initialized: false,
}
onTestFinished(() => {
window.$_TSR = undefined
})

await hydrate(clientRouter)

Expand All @@ -238,11 +234,13 @@ describe('hydrating a server-capped boundary lane', () => {
const consoleError = vi
.spyOn(console, 'error')
.mockImplementation(() => {})
onTestFinished(() => consoleError.mockRestore())
let root!: ReturnType<typeof hydrateRoot>
await act(async () => {
root = hydrateRoot(container, <RouterProvider router={clientRouter} />)
testCleanups.push(async () => {
onTestFinished(async () => {
await act(() => root.unmount())
container.remove()
})
await Promise.resolve()
})
Expand Down
6 changes: 4 additions & 2 deletions packages/react-router/tests/hydration-terminal-lane.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { cleanup, render, screen } from '@testing-library/react'
import { afterEach, describe, expect, test, vi } from 'vitest'
import { afterEach, describe, expect, onTestFinished, test, vi } from 'vitest'
import { hydrate } from '@tanstack/router-core/ssr/client'
import { dehydrateSsrMatchId } from '../../router-core/src/ssr/ssr-match-id'
import {
Expand Down Expand Up @@ -44,11 +44,13 @@ function bootstrap(

afterEach(() => {
cleanup()
delete window.$_TSR
})

describe('hydration terminal lane', () => {
test('keeps server data while loading only the missing client suffix', async () => {
onTestFinished(() => {
delete window.$_TSR
})
const parentLoader = vi.fn(() => 'client-parent')
const childLoader = vi.fn(() => 'client-child')
const rootRoute = createRootRoute({ component: Outlet })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
screen,
waitFor,
} from '@testing-library/react'
import { afterEach, expect, test, vi } from 'vitest'
import { afterEach, expect, onTestFinished, test, vi } from 'vitest'
import {
HeadContent,
Link,
Expand All @@ -20,11 +20,13 @@ import {

afterEach(() => {
cleanup()
document.head.innerHTML = ''
})

// https://github.com/TanStack/router/issues/7635
test('#7635: a parent beforeLoad error replaces the previous child title', async () => {
onTestFinished(() => {
document.head.innerHTML = ''
})
const appError = new Error('App beforeLoad failed')
const appErrorRendered = vi.fn()
const childHead = vi.fn(() => ({
Expand Down
Loading
Loading