test(packages/tuono-router): improve test using mocks (#641)

This commit is contained in:
Marco Pasqualetti
2025-03-11 06:46:58 +01:00
committed by GitHub
parent 9f14689ca0
commit bfd0041c6a
6 changed files with 102 additions and 122 deletions
@@ -1,5 +1,5 @@
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { render, fireEvent, screen } from '@testing-library/react'
import { render, fireEvent, screen, cleanup } from '@testing-library/react'
import { Link } from './Link'
@@ -33,8 +33,9 @@ vi.mock('react-intersection-observer', () => ({
},
}))
describe('Link component', () => {
describe('<Link />', () => {
beforeEach(() => {
cleanup()
pushMock.mockReset()
preloadMock.mockReset()
intersectionObserverCallback = undefined
@@ -1,114 +1,93 @@
import type { HTMLAttributes, JSX } from 'react'
import type { JSX, ReactNode } from 'react'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { cleanup, render, screen } from '@testing-library/react'
import { cleanup, render } from '@testing-library/react'
import { Route } from '../route'
import type { RouteComponent, RouteProps } from '../types'
import type { RouteComponent } from '../types'
import type { RouterInstanceType } from '../router'
import { NotFound } from './NotFound'
import { RouteMatch } from './RouteMatch'
import { useRouterContext } from './RouterContext'
import { NotFoundDefaultContent } from './NotFoundDefaultContent'
function createRouteComponent(
routeType: string,
RouteComponentFn: (props: RouteProps) => JSX.Element,
): RouteComponent {
const RootComponent = RouteComponentFn as RouteComponent
RootComponent.preload = vi.fn()
RootComponent.displayName = routeType
return RootComponent
vi.mock('../components/RouterContext', () => ({
useRouterContext: vi.fn(),
}))
vi.mock('./RouteMatch', () => ({
RouteMatch: vi.fn(),
}))
vi.mock('./NotFoundDefaultContent', () => ({
NotFoundDefaultContent: vi.fn(),
}))
interface RouterMock {
router: Pick<RouterInstanceType, 'routesById'>
}
const useRouterContextMock = vi.mocked(useRouterContext as () => RouterMock)
const RouteMatchMock = vi.mocked(RouteMatch)
const NotFoundDefaultContentMock = vi.mocked(NotFoundDefaultContent)
const rootRouteComponentMock = vi
.fn<(props: { children: ReactNode }) => JSX.Element>()
.mockImplementation(({ children }) => <div>{children}</div>)
const root = new Route({
isRoot: true,
component: createRouteComponent('root', ({ children }) => (
<div data-testid="root">{children}</div>
)),
component: rootRouteComponentMock as unknown as RouteComponent,
})
vi.mock('./Link', () => ({
Link: (props: HTMLAttributes<HTMLAnchorElement>): JSX.Element => (
<a {...props} />
),
}))
vi.mock('../hooks/useServerPayloadData.ts', () => ({
useServerPayloadData: (): { data: unknown; isLoading: boolean } => {
return {
data: undefined,
isLoading: false,
}
},
}))
const { useRouterContext } = vi.hoisted(() => {
return { useRouterContext: vi.fn() }
})
vi.mock('../components/RouterContext', () => ({
useRouterContext,
}))
describe('<NotFound />', () => {
afterEach(cleanup)
afterEach(() => {
cleanup()
useRouterContextMock.mockReset()
RouteMatchMock.mockReset()
NotFoundDefaultContentMock.mockReset()
rootRouteComponentMock.mockClear()
})
describe('when a custom 404 page exists', () => {
it('should render the custom 404 page', () => {
useRouterContext.mockReturnValue({
const customRoute404 = new Route({
getParentRoute: (): Route => root,
component: vi.fn() as unknown as RouteComponent,
})
useRouterContextMock.mockReturnValue({
router: {
routesById: {
'/404': new Route({
getParentRoute: (): Route => root,
component: createRouteComponent('404', () => (
<div data-testid="404">custom 404</div>
)),
}),
'/404': customRoute404,
__root__: root,
},
},
})
render(<NotFound />)
expect(screen.getByTestId('root')).toMatchInlineSnapshot(
`
<div
data-testid="root"
>
<div
data-testid="404"
>
custom 404
</div>
</div>
`,
expect(RouteMatchMock).toHaveBeenCalledExactlyOnceWith(
{ route: customRoute404, serverInitialData: {} },
undefined, // deprecated react context parameter
)
expect(rootRouteComponentMock).not.toHaveBeenCalled()
expect(NotFoundDefaultContentMock).not.toHaveBeenCalled()
})
})
describe('when a custom 404 page does not exist', () => {
it('should render the default 404 page, wrapped by the root __layout', () => {
useRouterContext.mockReturnValue({
useRouterContextMock.mockReturnValue({
router: {
routesById: {
__root__: root,
},
},
})
render(<NotFound />)
expect(screen.getByTestId('root')).toMatchInlineSnapshot(
`
<div
data-testid="root"
>
<h1>
404 Not found
</h1>
<a
href="/"
>
Return home
</a>
</div>
`,
)
expect(RouteMatchMock).not.toHaveBeenCalled()
expect(rootRouteComponentMock).toHaveBeenCalled()
expect(NotFoundDefaultContentMock).toHaveBeenCalledOnce()
})
})
})
@@ -4,7 +4,7 @@ import { useRouterContext } from '../components/RouterContext'
import { ROOT_ROUTE_ID } from '../route'
import { RouteMatch } from './RouteMatch'
import { Link } from './Link'
import { NotFoundDefaultContent } from './NotFoundDefaultContent'
export function NotFound(): JSX.Element | null {
const { router } = useRouterContext()
@@ -22,8 +22,7 @@ export function NotFound(): JSX.Element | null {
return (
<RootLayout data={null} isLoading={false}>
<h1>404 Not found</h1>
<Link href="/">Return home</Link>
<NotFoundDefaultContent />
</RootLayout>
)
}
@@ -0,0 +1,12 @@
import type { JSX } from 'react'
import { Link } from './Link'
export function NotFoundDefaultContent(): JSX.Element {
return (
<>
<h1>Page Not Found</h1>
<Link href="/">Return to Homepage</Link>
</>
)
}
@@ -1,18 +1,22 @@
import type { JSX } from 'react'
import { afterEach, describe, expect, it, vi } from 'vitest'
import { cleanup, render, screen } from '@testing-library/react'
import { Route } from '../route'
import type { RouteComponent, RouteProps } from '../types'
import { useServerPayloadData } from '../hooks/useServerPayloadData'
import { RouteMatch } from './RouteMatch'
function createRouteComponent(
routeType: string,
RouteComponentFn: (props: RouteProps) => JSX.Element,
includeChildren: boolean,
): RouteComponent {
const RootComponent = RouteComponentFn as RouteComponent
const RootComponent = (({ children }: RouteProps) => (
<div data-testid={routeType}>
{`${routeType} route`}
{includeChildren ? children : null}
</div>
)) as RouteComponent
RootComponent.preload = vi.fn()
RootComponent.displayName = routeType
return RootComponent
@@ -20,35 +24,28 @@ function createRouteComponent(
const root = new Route({
isRoot: true,
component: createRouteComponent('root', ({ children }) => (
<div data-testid="root">root route {children}</div>
)),
component: createRouteComponent('root', true),
})
const parent = new Route({
component: createRouteComponent('parent', ({ children }) => (
<div data-testid="parent">parent route {children}</div>
)),
component: createRouteComponent('parent', true),
getParentRoute: (): Route => root,
})
const route = new Route({
component: createRouteComponent('route', () => (
<p data-testid="route">current route</p>
)),
component: createRouteComponent('current', false),
getParentRoute: (): Route => parent,
})
vi.mock('../hooks/useServerPayloadData.ts', () => ({
useServerPayloadData: (): { data: unknown; isLoading: boolean } => {
return {
data: undefined,
isLoading: false,
}
},
vi.mock('../hooks/useServerPayloadData', () => ({
useServerPayloadData: vi.fn(),
}))
vi.mocked(useServerPayloadData).mockReturnValue({
data: undefined,
isLoading: false,
})
describe('Test RouteMatch component', () => {
describe('<RouteMatch />', () => {
afterEach(cleanup)
it('should correctly render nested routes', () => {
@@ -59,26 +56,19 @@ describe('Test RouteMatch component', () => {
<div
data-testid="root"
>
root route
root route
<div
data-testid="parent"
>
parent route
<p
data-testid="route"
parent route
<div
data-testid="current"
>
current route
</p>
</div>
</div>
</div>
`,
)
expect(screen.getByTestId('route')).toMatchInlineSnapshot(`
<p
data-testid="route"
>
current route
</p>
`)
})
})
@@ -1,19 +1,18 @@
import { afterEach, describe, expect, it, vi } from 'vitest'
import { cleanup } from '@testing-library/react'
import { useRouterContext } from '../components/RouterContext'
import { useRoute } from './useRoute'
const { useRouterContextMock } = vi.hoisted(() => ({
useRouterContextMock: vi.fn<
() => {
router: { routesById: Record<string, { id: string }> }
}
>(),
vi.mock('../components/RouterContext.tsx', () => ({
useRouterContext: vi.fn(),
}))
vi.mock('../components/RouterContext.tsx', () => ({
useRouterContext: useRouterContextMock,
}))
interface RouterMock {
router: { routesById: Record<string, { id: string }> }
}
const useRouterContextMock = vi.mocked(useRouterContext as () => RouterMock)
describe('useRoute', () => {
afterEach(() => {