import '../styles/app.css';
import '@luke-ui/react/themes/tactile/stylesheet.css';
import { Provider } from '@luke-ui/react/provider';
import spriteSheetHref from '@luke-ui/react/spritesheet.svg?url&no-inline';
import {
createMemoryHistory,
createRootRoute,
createRouter,
RouterProvider,
} from '@tanstack/react-router';
import type { Root as PageTree } from 'fumadocs-core/page-tree';
import { RootProvider } from 'fumadocs-ui/provider/tanstack';
import type { ReactNode } from 'react';
import { act } from 'react';
import type { Root } from 'react-dom/client';
import { createRoot } from 'react-dom/client';
import { afterEach, expect, test } from 'vite-plus/test';
import { page, userEvent } from 'vite-plus/test/context';
import { DocsSiteNav } from './docs-site-nav.js';
import { NotFound } from './not-found.js';
import { DocsSearchProvider } from './search.js';
import { SiteNav } from './site-nav.js';
import { DocsThemeRoot } from './theme-controls.js';
const tree: PageTree = {
name: 'Docs',
children: [
{
type: 'folder',
name: 'Documentation',
root: true,
children: [{ type: 'page', name: 'Installation', url: '/docs/installation' }],
},
],
};
let container: HTMLElement | undefined;
let root: Root | undefined;
afterEach(async () => {
if (root) act(() => root?.unmount());
container?.remove();
localStorage.clear();
container = undefined;
root = undefined;
await page.viewport(1024, 800);
});
test('marks only the current destination on desktop and keeps search available', async () => {
await page.viewport(1024, 800);
await renderAt('/playground', );
await expect
.element(page.getByRole('link', { name: 'Playground' }))
.toHaveAttribute('aria-current', 'page');
expect(getCurrentLinks()).toHaveLength(1);
await expect.element(page.getByRole('button', { name: /Search/ })).toBeVisible();
});
for (const width of [768, 800]) {
test(`keeps the landing page header on one row at ${width}px`, async () => {
await page.viewport(width, 800);
await renderAt('/', );
const siteNav = page.getByRole('navigation', { name: 'Site' });
const search = page.getByRole('button', { name: /Search/ });
const themeProfile = page.getByRole('radiogroup', { name: 'Theme profile' });
await expect.element(siteNav).toBeVisible();
await expect.element(search).toBeVisible();
await expect.element(themeProfile).toBeVisible();
// Everything sharing a centre line proves the header did not wrap onto a second row.
const centres = [siteNav, search, themeProfile].map((locator) => {
const rect = locator.element().getBoundingClientRect();
return rect.top + rect.height / 2;
});
for (const centre of centres) {
expect(Math.abs(centre - (centres[0] ?? 0))).toBeLessThanOrEqual(2);
}
});
}
test('offers search and theme controls from the mobile bar with no destination active on the landing page', async () => {
await page.viewport(390, 800);
await renderAt('/', );
expect(getCurrentLinks()).toHaveLength(0);
await expect.element(page.getByRole('button', { name: 'Open Search' })).toBeVisible();
const themeTrigger = page.getByRole('button', { name: 'Theme' });
await expect.element(themeTrigger).toBeVisible();
await act(async () => {
await userEvent.click(themeTrigger);
});
await expect.element(page.getByRole('radiogroup', { name: 'Theme profile' })).toBeVisible();
});
test('leaves every destination inactive on the 404 page', async () => {
await renderAt('/missing', );
expect(getCurrentLinks()).toHaveLength(0);
});
test('opening search does not also open the docs navigation drawer, and vice versa', async () => {
await page.viewport(390, 800);
await renderAt('/docs/installation', );
await act(async () => {
await userEvent.click(page.getByRole('button', { name: 'Open Search' }));
});
await expect.element(page.getByRole('dialog', { name: 'Search documentation' })).toBeVisible();
await expect
.element(page.getByRole('dialog', { name: 'Docs navigation' }))
.not.toBeInTheDocument();
await act(async () => {
await userEvent.keyboard('{Escape}');
});
await expect.element(page.getByRole('dialog')).not.toBeInTheDocument();
await act(async () => {
await userEvent.click(page.getByRole('button', { name: 'Menu' }));
});
await expect.element(page.getByRole('dialog', { name: 'Docs navigation' })).toBeVisible();
await expect
.element(page.getByRole('dialog', { name: 'Search documentation' }))
.not.toBeInTheDocument();
});
async function renderAt(pathname: string, children: ReactNode) {
const rootRoute = createRootRoute({
component: () => (
{children}
),
});
const router = createRouter({
history: createMemoryHistory({ initialEntries: [pathname] }),
routeTree: rootRoute,
});
container = document.body.appendChild(document.createElement('div'));
root = createRoot(container);
await act(async () => {
root?.render();
await router.load();
});
}
function getCurrentLinks() {
return page
.getByRole('link')
.elements()
.filter((link) => link.getAttribute('aria-current') === 'page');
}