test(web): add shell and navigation coverage
This commit is contained in:
parent
4da56d489d
commit
98144ab4ff
@ -58,7 +58,7 @@ Stack: Next.js 16 + React 19 + TypeScript
|
|||||||
- [ ] Performance pass
|
- [ ] Performance pass
|
||||||
- [ ] Accessibility pass
|
- [ ] Accessibility pass
|
||||||
- [ ] Token validation pass
|
- [ ] Token validation pass
|
||||||
- [ ] Production build passes
|
- [x] Production build passes
|
||||||
- [ ] UX polish pass
|
- [ ] UX polish pass
|
||||||
|
|
||||||
# High-Collision Areas
|
# High-Collision Areas
|
||||||
@ -108,6 +108,13 @@ Stack: Next.js 16 + React 19 + TypeScript
|
|||||||
- stronger focus-visible treatment for interactive controls
|
- stronger focus-visible treatment for interactive controls
|
||||||
- clearer active-nav semantics via `aria-current`
|
- clearer active-nav semantics via `aria-current`
|
||||||
- keyboard/accessibility guidance surfaced in navigation/settings
|
- keyboard/accessibility guidance surfaced in navigation/settings
|
||||||
|
- Added the first web UI test harness and coverage for:
|
||||||
|
- shared `AppShell` skip-link/main landmark behavior
|
||||||
|
- shared `Sidebar` primary-nav and active-page semantics
|
||||||
|
- Verified `web/` with:
|
||||||
|
- `npm test`
|
||||||
|
- `npm run typecheck`
|
||||||
|
- `npm run build`
|
||||||
|
|
||||||
# Open Questions
|
# Open Questions
|
||||||
|
|
||||||
@ -139,8 +146,6 @@ Stack: Next.js 16 + React 19 + TypeScript
|
|||||||
- Extraction-backed task review flows
|
- Extraction-backed task review flows
|
||||||
- Backend-backed agent activity timeline, approval queue, proposal diff review, and audit filtering
|
- Backend-backed agent activity timeline, approval queue, proposal diff review, and audit filtering
|
||||||
- Remaining dense/accessibility polish and performance hardening
|
- Remaining dense/accessibility polish and performance hardening
|
||||||
- Remaining verification:
|
|
||||||
- run `npm test`
|
|
||||||
|
|
||||||
# Done When
|
# Done When
|
||||||
|
|
||||||
|
|||||||
23
web/src/components/AppShell.test.tsx
Normal file
23
web/src/components/AppShell.test.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { render, screen } from "@testing-library/react";
|
||||||
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
import { AppShell } from "./AppShell";
|
||||||
|
|
||||||
|
vi.mock("@/components/Sidebar", () => ({
|
||||||
|
Sidebar: () => <div data-testid="sidebar">Sidebar</div>,
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe("AppShell", () => {
|
||||||
|
it("renders skip link, page title, actions, and main content landmark", () => {
|
||||||
|
render(
|
||||||
|
<AppShell title="Search" description="Find notes" actions={<button type="button">Run</button>}>
|
||||||
|
<section>Results</section>
|
||||||
|
</AppShell>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(screen.getByRole("link", { name: "Skip to main content" })).toHaveAttribute("href", "#main-content");
|
||||||
|
expect(screen.getByRole("heading", { level: 1, name: "Search" })).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole("button", { name: "Run" })).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole("main")).toHaveAttribute("id", "main-content");
|
||||||
|
expect(screen.getByText("Results")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
30
web/src/components/Sidebar.test.tsx
Normal file
30
web/src/components/Sidebar.test.tsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { render, screen } from "@testing-library/react";
|
||||||
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
import { Sidebar } from "./Sidebar";
|
||||||
|
|
||||||
|
const usePathnameMock = vi.fn();
|
||||||
|
|
||||||
|
vi.mock("next/navigation", () => ({
|
||||||
|
usePathname: () => usePathnameMock(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("next/link", () => ({
|
||||||
|
default: ({ href, children, ...props }: React.ComponentProps<"a"> & { href: string }) => (
|
||||||
|
<a href={href} {...props}>
|
||||||
|
{children}
|
||||||
|
</a>
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe("Sidebar", () => {
|
||||||
|
it("marks the active navigation item and exposes primary navigation landmarks", () => {
|
||||||
|
usePathnameMock.mockReturnValue("/search");
|
||||||
|
|
||||||
|
render(<Sidebar />);
|
||||||
|
|
||||||
|
expect(screen.getByLabelText("Primary")).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole("navigation", { name: "Primary navigation" })).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole("link", { name: "Search" })).toHaveAttribute("aria-current", "page");
|
||||||
|
expect(screen.getByText("Keyboard flow")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
1
web/src/test/setupTests.ts
Normal file
1
web/src/test/setupTests.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
import "@testing-library/jest-dom/vitest";
|
||||||
15
web/vitest.config.ts
Normal file
15
web/vitest.config.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import path from "node:path";
|
||||||
|
import { defineConfig } from "vitest/config";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
environment: "jsdom",
|
||||||
|
setupFiles: ["./src/test/setupTests.ts"],
|
||||||
|
globals: true,
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@": path.resolve(__dirname, "./src"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user