syntaxai/tdd.md · main · e2e / git-content-browse.spec.ts

git-content-browse.spec.ts 122 lines · 4467 bytes raw
// E2E: every blog post in ALL_POSTS is reachable via /GIT/.
//
// Crawls the registry's slugs (lifted into a literal array here so
// the test file doesn't import server-side modules) and asserts:
//   1. /GIT/syntaxai/tdd.md/tree/main/content/blog lists each post
//   2. /GIT/syntaxai/tdd.md/blob/main/content/blog/<slug>.md renders
//      the post (markdown rendered via marked into the chrome)
//   3. /GIT/syntaxai/tdd.md/raw/main/content/blog/<slug>.md serves
//      the raw markdown
// Plus the tree home (/GIT/syntaxai/tdd.md/tree/main) shows the
// top-level directories (content/, src/, public/, scripts/, etc.).

import { test, expect } from "@playwright/test";
import * as fs from "fs";
import * as path from "path";

// Mirror of c31_blog.ts ALL_POSTS slugs. If a post is added there,
// add the slug here too. Kept inline to avoid pulling server code
// into the test process.
const BLOG_SLUGS = [
  "sama-meets-git-cms",
  "from-rules-to-checks",
  "agentic-coding-corpus-three-patterns",
  "claude-code-harness-postmortem",
  "three-constraints-agentic-coding",
  "tweag-handbook-tdd",
  "aider-tdd",
  "cursor-tdd",
  "claude-code-tdd",
];

const SCREENSHOT_DIR = "test-results/git-content-browse";

test.beforeAll(() => {
  fs.mkdirSync(SCREENSHOT_DIR, { recursive: true });
});

test.describe("/GIT browses the local bare repo", () => {
  test("repo root tree lists the top-level directories", async ({ page }) => {
    const res = await page.goto("/GIT/syntaxai/tdd.md/tree/main");
    expect(res?.status()).toBe(200);

    // Top-level dirs we expect after the dev tree was pushed.
    for (const dir of ["content", "src", "public", "scripts", "e2e"]) {
      await expect(
        page.locator(`a[href="/GIT/syntaxai/tdd.md/tree/main/${dir}"]`),
      ).toBeVisible();
    }
    // Top-level files
    await expect(
      page.locator('a[href="/GIT/syntaxai/tdd.md/blob/main/package.json"]'),
    ).toBeVisible();

    await page.screenshot({
      path: path.join(SCREENSHOT_DIR, "1-repo-root-tree.png"),
      fullPage: true,
    });
  });

  test("content/blog tree lists every post in ALL_POSTS", async ({ page }) => {
    const res = await page.goto("/GIT/syntaxai/tdd.md/tree/main/content/blog");
    expect(res?.status()).toBe(200);
    for (const slug of BLOG_SLUGS) {
      const link = page.locator(
        `a[href="/GIT/syntaxai/tdd.md/blob/main/content/blog/${slug}.md"]`,
      );
      await expect(link, `link to ${slug}.md must be present`).toBeVisible();
    }

    await page.screenshot({
      path: path.join(SCREENSHOT_DIR, "2-content-blog-tree.png"),
      fullPage: true,
    });
  });

  for (const slug of BLOG_SLUGS) {
    test(`blob view renders ${slug}.md as markdown via /GIT`, async ({ page }) => {
      const res = await page.goto(
        `/GIT/syntaxai/tdd.md/blob/main/content/blog/${slug}.md`,
      );
      expect(res?.status()).toBe(200);
      // The repo-blob-rendered container is what marked.parse output
      // lands in. It must exist + be non-empty.
      const rendered = page.locator(".repo-blob-rendered");
      await expect(rendered).toBeVisible();
      const text = (await rendered.textContent()) ?? "";
      expect(text.length).toBeGreaterThan(200);
      // The breadcrumb must show the file path so users can climb.
      await expect(page.locator(".commit-breadcrumb")).toContainText(`${slug}.md`);
    });

    test(`raw endpoint serves ${slug}.md as text/plain via /GIT`, async ({ request }) => {
      const res = await request.get(
        `/GIT/syntaxai/tdd.md/raw/main/content/blog/${slug}.md`,
      );
      expect(res.status()).toBe(200);
      expect(res.headers()["content-type"]).toMatch(/text\/plain/);
      const body = await res.text();
      // Frontmatter or first heading — every blog post has one.
      expect(body.length).toBeGreaterThan(200);
    });
  }

  test("path traversal is rejected", async ({ request }) => {
    for (const evil of [
      "/GIT/syntaxai/tdd.md/blob/main/../etc/passwd",
      "/GIT/syntaxai/tdd.md/blob/main/content/../../etc/passwd",
      "/GIT/syntaxai/tdd.md/tree/main//content",
    ]) {
      const res = await request.get(evil);
      expect(res.status(), `${evil} must 404`).toBe(404);
    }
  });

  test("non-allowed (owner, repo) 404s — only syntaxai/tdd.md is served", async ({
    request,
  }) => {
    const res = await request.get("/GIT/someone/random-repo/tree/main");
    expect(res.status()).toBe(404);
  });
});