SPB Git

spb/autoscholar

Public
5.7 KB · 138 lines typescript
1import AxeBuilder from "@axe-core/playwright";2import { expect, test } from "@playwright/test";34const PAGES = [5  "/",6  "/research",7  "/topics",8  "/series",9  "/about",10  "/methodology",11  "/infrastructure",12  "/transparency",13  "/legal/privacy",14  "/legal/terms",15  "/legal/research-disclaimer",16];1718test.describe("public site", () => {19  for (const path of PAGES) {20    test(`renders ${path} with no serious a11y violations`, async ({ page }) => {21      const res = await page.goto(path);22      expect(res?.status()).toBe(200);23      await expect(page.locator("main")).toBeVisible();24      await expect(page.getByText(/MacLustr/).first()).toBeVisible();25      const axe = await new AxeBuilder({ page }).withTags(["wcag2a", "wcag2aa"]).analyze();26      const serious = axe.violations.filter((v) => v.impact === "serious" || v.impact === "critical");27      expect(28        serious,29        JSON.stringify(30          serious.map((v) => ({ id: v.id, nodes: v.nodes.length })),31          null,32          1,33        ),34      ).toEqual([]);35    });36  }3738  test("responsive screenshots", async ({ page }) => {39    for (const [name, w] of [40      ["mobile", 390],41      ["tablet", 820],42      ["desktop", 1440],43    ] as const) {44      await page.setViewportSize({ width: w, height: 900 });45      for (const [slug, path] of [46        ["home", "/"],47        ["research", "/research"],48        ["topics", "/topics"],49        ["paper", "/research/as-wp-2026-0000-staging-sample"],50      ] as const) {51        await page.goto(path);52        await page.screenshot({ path: `tests/e2e/screenshots/${slug}-${name}.png`, fullPage: true });53      }54    }55  });5657  test("dark mode toggle persists", async ({ page }) => {58    await page.goto("/");59    await page.getByRole("button", { name: /colour scheme/i }).click();60    await expect(page.locator("html")).toHaveClass(/dark/);61    await page.reload();62    await expect(page.locator("html")).toHaveClass(/dark/);63  });6465  test("keyboard: skip link and header nav are reachable", async ({ page }) => {66    await page.goto("/");67    await page.keyboard.press("Tab");68    await expect(page.locator(".skip-link")).toBeFocused();69    await page.keyboard.press("Enter");70    await expect(page.locator("#main")).toBeVisible();71  });7273  test("search, filters, pagination and empty state", async ({ page }) => {74    await page.goto("/research?q=zzzz-no-such-paper-zzzz");75    await expect(page.getByText(/No papers match these filters yet/)).toBeVisible();76    await page.goto("/research?discipline=interdisciplinary&mode=theoretical");77    await expect(page.locator("main")).toContainText(/paper/);78    await page.goto("/research?page=abc&pageSize=9999");79    await expect(page.locator('p[role="alert"]')).toContainText(/invalid/i);80    const r = await page.request.get("/api/public/papers?q=staging&sort=relevance");81    expect(r.status()).toBe(200);82    const j = (await r.json()) as { items: Array<{ publicId: string }>; total: number };83    expect(j.total).toBeGreaterThanOrEqual(1);84    expect(j.items[0]?.publicId).toBe("AS-WP-2026-0000");85  });8687  test("paper page, PDF route, citation exports and JSON-LD", async ({ page, request }) => {88    await page.goto("/research/as-wp-2026-0000-staging-sample");89    await expect(page.getByRole("heading", { level: 1 })).toContainText("STAGING SAMPLE");90    await expect(page.getByText("AutoScholar — AI-generated research").first()).toBeVisible();91    await expect(page.getByText(/not formally peer reviewed/).first()).toBeVisible();92    const ld = await page.locator('script[type="application/ld+json"]').textContent();93    expect(ld).toContain('"ScholarlyArticle"');94    expect(ld).not.toContain("doi");95    const pdf = await request.get("/papers/AS-WP-2026-0000/v1/manuscript/main.pdf");96    expect(pdf.status()).toBe(200);97    expect(pdf.headers()["content-type"]).toContain("application/pdf");98    expect(pdf.headers()["x-artifact-sha256"]).toHaveLength(64);99    const notListed = await request.get("/papers/AS-WP-2026-0000/v1/manuscript/secret.txt");100    expect(notListed.status()).toBe(404);101    const traversal = await request.get("/papers/AS-WP-2026-0000/v1/..%2F..%2Fetc%2Fpasswd");102    expect([400, 404]).toContain(traversal.status());103    for (const ext of ["bib", "ris", "txt"]) {104      const c = await request.get(`/research/as-wp-2026-0000-staging-sample/cite.${ext}`);105      expect(c.status()).toBe(200);106      expect(await c.text()).toContain("AS-WP-2026-0000");107    }108    await page.getByRole("tab", { name: "RIS" }).click();109    await expect(page.getByText("TY  - RPRT")).toBeVisible();110    await expect(page.getByText(/Version history/)).toBeVisible();111    const api = await request.get("/api/public/papers/as-wp-2026-0000-staging-sample");112    const body = await api.text();113    expect(api.status()).toBe(200);114    expect(body).not.toMatch(/tokenHash|storageKey|instructionText|fencing/);115    expect(await (await request.get("/research/does-not-exist")).status()).toBe(404);116  });117118  test("feeds, sitemap, robots and security headers", async ({ request }) => {119    const rss = await request.get("/feed.xml");120    expect(rss.status()).toBe(200);121    expect(rss.headers()["content-type"]).toContain("rss");122    expect(await rss.text()).toContain("<rss");123    const atom = await request.get("/feed.atom");124    expect(atom.status()).toBe(200);125    expect(await atom.text()).toContain("<feed");126    const robots = await request.get("/robots.txt");127    expect(robots.status()).toBe(200);128    expect(await robots.text()).toMatch(/Disallow/);129    const sm = await request.get("/sitemap.xml");130    expect(sm.status()).toBe(200);131    const home = await request.get("/");132    const h = home.headers();133    expect(h["content-security-policy"]).toContain("default-src 'self'");134    expect(h["x-content-type-options"]).toBe("nosniff");135    expect(h["x-powered-by"]).toBeUndefined();136  });137});138
Files · cbf31e64fd1db62a1819edf166ff2d903b6d3510
Menu

Theme