Initial commit
This commit is contained in:
commit
89053153cc
8152 files changed
+2086461
No files matched your search
@@ -0,0 +1,143 @@
|
||||
import { fireEvent, render, screen, within } from '@testing-library/react'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import App from '../src/App'
|
||||
import {
|
||||
AppStateProvider,
|
||||
PATTERNS_PATH,
|
||||
activeHref,
|
||||
navigationGroups,
|
||||
patternFromSearch,
|
||||
routeForPath,
|
||||
routes,
|
||||
} from '../src/app'
|
||||
|
||||
function renderAt(url: string) {
|
||||
window.history.pushState(null, '', url)
|
||||
return render(
|
||||
<AppStateProvider>
|
||||
<App />
|
||||
</AppStateProvider>,
|
||||
)
|
||||
}
|
||||
|
||||
/** The sidebar and the overview cards can link to the same page — scope the
|
||||
queries that must land on one of them. */
|
||||
const sidebar = () => within(document.querySelector('.sidebar') as HTMLElement)
|
||||
const overview = () => within(document.querySelector('.overview-grid') as HTMLElement)
|
||||
|
||||
beforeEach(() => {
|
||||
localStorage.clear()
|
||||
/* jsdom has no layout — the shell's scroll-to-top effect would hit the
|
||||
"not implemented" hook on every navigation. */
|
||||
vi.spyOn(window, 'scrollTo').mockImplementation(() => {})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
describe('route table', () => {
|
||||
it('keeps every sidebar entry pointed at a route that exists', () => {
|
||||
const known = new Set(routes.map(route => route.path))
|
||||
|
||||
for (const group of navigationGroups) {
|
||||
for (const item of group.items) {
|
||||
const [path] = item.href.split('?')
|
||||
expect(known.has(path!)).toBe(true)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('leaves no route unreachable from the sidebar', () => {
|
||||
const navPaths = new Set(
|
||||
navigationGroups.flatMap(group => group.items.map(item => item.href.split('?')[0]!)),
|
||||
)
|
||||
|
||||
for (const route of routes) {
|
||||
expect(navPaths.has(route.path)).toBe(true)
|
||||
}
|
||||
})
|
||||
|
||||
it('looks routes up by path and rejects unknown ones', () => {
|
||||
expect(routeForPath('/components/buttons')?.label).toBe('Buttons')
|
||||
expect(routeForPath('/nope')).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe('pattern URLs', () => {
|
||||
it('accepts a known pattern, falls back on anything else', () => {
|
||||
expect(patternFromSearch('?pattern=table')).toBe('table')
|
||||
expect(patternFromSearch('?pattern=space')).toBe('metrics')
|
||||
expect(patternFromSearch('')).toBe('metrics')
|
||||
})
|
||||
|
||||
it('marks the matching pattern entry active', () => {
|
||||
expect(activeHref(PATTERNS_PATH, '?pattern=deploy')).toBe('/patterns?pattern=deploy')
|
||||
expect(activeHref(PATTERNS_PATH, '')).toBe('/patterns?pattern=metrics')
|
||||
expect(activeHref('/components/icons', '')).toBe('/components/icons')
|
||||
})
|
||||
})
|
||||
|
||||
describe('routing', () => {
|
||||
it('renders the overview at /', () => {
|
||||
renderAt('/')
|
||||
|
||||
expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent('Build with clarity.')
|
||||
expect(overview().getByText('Foundations')).toBeInTheDocument()
|
||||
expect(overview().getByRole('link', { name: /Color tokens/ })).toHaveAttribute(
|
||||
'href',
|
||||
'/foundations/colors',
|
||||
)
|
||||
})
|
||||
|
||||
it('renders the page registered for a URL', () => {
|
||||
for (const route of routes) {
|
||||
const { unmount } = renderAt(route.path)
|
||||
expect(screen.getByText(route.title, { selector: '.section-heading h2' })).toBeInTheDocument()
|
||||
unmount()
|
||||
}
|
||||
})
|
||||
|
||||
it('marks the sidebar entry for the current URL as current', () => {
|
||||
renderAt('/foundations/colors')
|
||||
|
||||
expect(sidebar().getByRole('link', { name: 'Color tokens' })).toHaveAttribute('aria-current', 'page')
|
||||
expect(sidebar().getByRole('link', { name: 'Typography' })).not.toHaveAttribute('aria-current')
|
||||
})
|
||||
|
||||
it('navigates on a sidebar click without a full page load', () => {
|
||||
renderAt('/')
|
||||
const link = sidebar().getByRole('link', { name: 'Typography' })
|
||||
|
||||
fireEvent.click(link)
|
||||
|
||||
expect(window.location.pathname).toBe('/foundations/typography')
|
||||
expect(link).toHaveAttribute('aria-current', 'page')
|
||||
expect(screen.getByText('Typography', { selector: '.section-heading h2' })).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('opens the pattern named in the URL', () => {
|
||||
renderAt('/patterns?pattern=table')
|
||||
|
||||
expect(screen.getByRole('button', { name: /Service Monitor Table/ })).toHaveAttribute('aria-pressed', 'true')
|
||||
expect(screen.getByRole('button', { name: /Metrics Dashboard/ })).toHaveAttribute('aria-pressed', 'false')
|
||||
// Sidebar pattern entries share /patterns and differ only by the query.
|
||||
expect(sidebar().getByRole('link', { name: 'Service Monitor Table' })).toHaveAttribute(
|
||||
'aria-current',
|
||||
'page',
|
||||
)
|
||||
})
|
||||
|
||||
it('writes the chosen pattern into the URL', () => {
|
||||
renderAt('/patterns')
|
||||
fireEvent.click(screen.getByRole('button', { name: /Deployment Flow/ }))
|
||||
|
||||
expect(window.location.search).toBe('?pattern=deploy')
|
||||
})
|
||||
|
||||
it('falls back to a 404 page instead of a blank screen', () => {
|
||||
renderAt('/definitely/not/a/route')
|
||||
|
||||
expect(screen.getByText('Page not found')).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user