Files
meshcore-hub/e2e/tests/custom-page.spec.ts
Louis King c8cc049651 feat(web): React SPA shell, markdown migration, vitest coverage, v0.17 docs
- Migrate footer, error page, and announcements to React; slim Jinja2
  shell to SEO/head/config/fonts/theme-init only
- Replace server-side markdown rendering with react-markdown (removes
  Python `markdown` dependency); custom pages + announcements ship
  raw markdown to the client
- Add heading-anchor deep-links with CSS override for inherited colors
- Extract pure helpers from pages (messageHelpers, mapMath, routesHelpers,
  profileHelpers, packetHelpers, packetGroupHelpers) for unit testability
- Add comprehensive vitest suite: 59 test files, 315 tests covering all
  components, pages, and extracted helpers; global i18next mock + matchMedia
  stub + AbortController-aware apiMock in test infrastructure
- Fix SortableTable test DOM nesting (<th> inside proper <table> context)
- Update docs for v0.17.0: upgrading.md release notes, README.md project
  structure + build description, i18n.md path fix
- Delete REACT_MIGRATION.md (migration complete, unreferenced)
- Add announcements dismiss-persistence + custom-page 404 E2E specs
2026-07-22 22:55:20 +01:00

56 lines
2.1 KiB
TypeScript

import { expect, test } from "@playwright/test";
test.describe("custom pages", () => {
test("markdown content is rendered", async ({ page }) => {
await page.goto("/pages/about");
const prose = page.locator(".card-body .prose");
await expect(prose).toBeVisible();
await expect(
prose.getByRole("heading", { name: "About the E2E Network" }),
).toBeVisible();
await expect(prose.getByText("rendered from markdown")).toBeVisible();
await expect(
prose.getByText("Fetched by the SPA from /spa/pages/about"),
).toBeVisible();
});
test("custom page appears in the navigation", async ({ page }) => {
await page.goto("/");
const link = page.locator('[data-testid="nav-link"][data-nav-href="/pages/about"]');
await expect(link.first()).toBeVisible();
await link.first().click();
await expect(page).toHaveURL(/\/pages\/about/);
await expect(page.locator(".card-body .prose")).toBeVisible();
});
test("heading anchor updates the URL hash on click", async ({ page }) => {
await page.goto("/pages/about");
// rehype-slug assigns the id; rehype-autolink-headings (behavior: "wrap")
// wraps the heading text in an <a href="#getting-started">.
const anchor = page.locator("h2#getting-started a");
await expect(anchor).toHaveAttribute("href", "#getting-started");
await anchor.click();
await expect(page).toHaveURL(/#getting-started$/);
});
test("direct hash navigation scrolls the heading into view", async ({ page }) => {
// Exercises the async-load scroll effect in CustomPage.tsx: the heading is
// absent until /spa/pages/about resolves, then scrollIntoView fires.
await page.goto("/pages/about#getting-started");
const heading = page.getByRole("heading", { name: "Getting Started" });
await expect(heading).toBeInViewport();
});
test("unknown slug shows a not-found error", async ({ page }) => {
await page.goto("/pages/does-not-exist");
const alert = page.locator('[role="alert"]');
await expect(alert).toBeVisible();
await expect(alert).toContainText(/not found/i);
});
});