website

#astro#js#html#css

git clone https://git.pyrossh.dev/website

木 Personal website of pyrossh. Built with astrojs, shiki, vite.


tests/pages.test.js
09c6a56 1
import { after, before, describe, test } from "node:test";
09c6a56 2
import assert from "node:assert/strict";
09c6a56 3
import path from "node:path";
09c6a56 4
import { fileURLToPath } from "node:url";
09c6a56 5
import { chromium } from "playwright";
09c6a56 6
import { startStaticServer } from "./helpers/static-server.js";
09c6a56 7
09c6a56 8
const __dirname = path.dirname(fileURLToPath(import.meta.url));
09c6a56 9
const distDir = path.join(__dirname, "..", "dist");
09c6a56 10
09c6a56 11
let server;
09c6a56 12
let browser;
09c6a56 13
let page;
09c6a56 14
const consoleErrors = [];
09c6a56 15
09c6a56 16
before(async () => {
09c6a56 17
  server = await startStaticServer(distDir);
09c6a56 18
  browser = await chromium.launch();
09c6a56 19
  page = await browser.newPage();
09c6a56 20
  page.on("console", (msg) => {
09c6a56 21
    if (msg.type() === "error") consoleErrors.push(`[${page.url()}] ${msg.text()}`);
09c6a56 22
  });
09c6a56 23
  page.on("pageerror", (err) => {
09c6a56 24
    consoleErrors.push(`[${page.url()}] ${err.message}`);
09c6a56 25
  });
09c6a56 26
});
09c6a56 27
09c6a56 28
after(async () => {
09c6a56 29
  await browser?.close();
09c6a56 30
  await server?.close();
09c6a56 31
});
09c6a56 32
09c6a56 33
async function goto(urlPath) {
09c6a56 34
  const response = await page.goto(`${server.url}${urlPath}`, { waitUntil: "domcontentloaded" });
09c6a56 35
  return response;
09c6a56 36
}
09c6a56 37
09c6a56 38
describe("static pages", () => {
09c6a56 39
  test("home page renders", async () => {
09c6a56 40
    const response = await goto("/");
09c6a56 41
    assert.equal(response.status(), 200);
09c6a56 42
    await assert.doesNotReject(page.locator("h1.title").waitFor());
09c6a56 43
    assert.match(await page.title(), /pyrossh/);
09c6a56 44
  });
09c6a56 45
09c6a56 46
  test("cv page renders", async () => {
09c6a56 47
    const response = await goto("/cv/");
09c6a56 48
    assert.equal(response.status(), 200);
09c6a56 49
    await assert.doesNotReject(page.locator("h1").waitFor());
09c6a56 50
    assert.equal(await page.locator("img[alt='Resume']").count(), 1);
09c6a56 51
  });
09c6a56 52
09c6a56 53
  test("posts index lists posts", async () => {
09c6a56 54
    const response = await goto("/posts/");
09c6a56 55
    assert.equal(response.status(), 200);
09c6a56 56
    const links = page.locator("main a[href^='/posts/']");
09c6a56 57
    assert.ok((await links.count()) > 0, "expected at least one post link");
09c6a56 58
  });
09c6a56 59
09c6a56 60
  test("a post page renders", async () => {
09c6a56 61
    const response = await goto("/posts/react-powertools-swr/");
09c6a56 62
    assert.equal(response.status(), 200);
09c6a56 63
    assert.match(await page.title(), /React Powertools/);
09c6a56 64
  });
09c6a56 65
09c6a56 66
  test("only-bible-app pages render", async () => {
09c6a56 67
    for (const url of ["/only-bible-app/", "/only-bible-app/privacy-policy/", "/only-bible-app/terms-and-conditions/"]) {
09c6a56 68
      const response = await goto(url);
09c6a56 69
      assert.equal(response.status(), 200, `expected 200 for ${url}`);
09c6a56 70
    }
09c6a56 71
  });
09c6a56 72
09c6a56 73
  test("robots.txt and rss.xml are served", async () => {
09c6a56 74
    const robots = await goto("/robots.txt");
09c6a56 75
    assert.equal(robots.status(), 200);
09c6a56 76
    assert.match(await page.content(), /User-agent/);
09c6a56 77
09c6a56 78
    const rss = await goto("/rss.xml");
09c6a56 79
    assert.equal(rss.status(), 200);
09c6a56 80
  });
09c6a56 81
09c6a56 82
  test("unknown route returns the 404 page", async () => {
09c6a56 83
    const response = await goto("/this-page-does-not-exist/");
09c6a56 84
    assert.equal(response.status(), 404);
09c6a56 85
    assert.match(await page.locator("h1").textContent(), /404/);
09c6a56 86
  });
09c6a56 87
});
09c6a56 88
09c6a56 89
describe("repo pages", () => {
09c6a56 90
  const repoId = "website";
09c6a56 91
09c6a56 92
  test("repo readme page renders", async () => {
09c6a56 93
    const response = await goto(`/repos/${repoId}/`);
09c6a56 94
    assert.equal(response.status(), 200);
09c6a56 95
    await assert.doesNotReject(page.locator(".repo .header h1").waitFor());
09c6a56 96
    assert.equal(await page.locator(".nav a.active-tab").textContent(), "Readme");
09c6a56 97
  });
09c6a56 98
09c6a56 99
  test("repo commits page lists commits", async () => {
09c6a56 100
    const response = await goto(`/repos/${repoId}/commits/`);
09c6a56 101
    assert.equal(response.status(), 200);
09c6a56 102
    const commitLinks = page.locator(`a[href^='/repos/${repoId}/commits/']`);
09c6a56 103
    assert.ok((await commitLinks.count()) > 0, "expected at least one commit link");
09c6a56 104
  });
09c6a56 105
09c6a56 106
  test("a single commit page renders", async () => {
09c6a56 107
    await goto(`/repos/${repoId}/commits/`);
09c6a56 108
    const firstCommitHref = await page.locator(`a[href^='/repos/${repoId}/commits/']`).first().getAttribute("href");
09c6a56 109
    const response = await goto(firstCommitHref);
09c6a56 110
    assert.equal(response.status(), 200);
09c6a56 111
    assert.equal(await page.locator(".nav a.active-tab").textContent(), "Commits");
09c6a56 112
  });
09c6a56 113
09c6a56 114
  test("repo files index renders the tree and a default file", async () => {
09c6a56 115
    const response = await goto(`/repos/${repoId}/files/`);
09c6a56 116
    assert.equal(response.status(), 200);
09c6a56 117
    assert.ok((await page.locator(".file-explorer-tree").count()) > 0);
09c6a56 118
  });
09c6a56 119
09c6a56 120
  test("a specific file, its history and blame views render", async () => {
09c6a56 121
    const filePath = "eleventy.config.js";
09c6a56 122
    const fileRes = await goto(`/repos/${repoId}/files/${filePath}/`);
09c6a56 123
    assert.equal(fileRes.status(), 200);
09c6a56 124
09c6a56 125
    const historyRes = await goto(`/repos/${repoId}/files/${filePath}/history/`);
09c6a56 126
    assert.equal(historyRes.status(), 200);
09c6a56 127
09c6a56 128
    const blameRes = await goto(`/repos/${repoId}/files/${filePath}/blame/`);
09c6a56 129
    assert.equal(blameRes.status(), 200);
09c6a56 130
  });
09c6a56 131
09c6a56 132
  test("repo nav tabs swap content via htmx without a full reload", async () => {
09c6a56 133
    await goto(`/repos/${repoId}/`);
09c6a56 134
    await page.click(".nav a[href='/repos/website/files/']");
09c6a56 135
    await page.waitForURL(`${server.url}/repos/${repoId}/files/`);
09c6a56 136
    assert.equal(await page.locator(".nav a.active-tab").textContent(), "Code");
09c6a56 137
  });
09c6a56 138
});
09c6a56 139
09c6a56 140
test("no unexpected console errors were logged across visited pages", () => {
09c6a56 141
  // The deliberate 404 test above triggers a browser-level "failed to load
09c6a56 142
  // resource" console entry for its own navigation request — that's the
09c6a56 143
  // behavior under test there, not a bug, so it's excluded here.
09c6a56 144
  const unexpected = consoleErrors.filter((msg) => !msg.includes("this-page-does-not-exist"));
09c6a56 145
  assert.deepEqual(unexpected, []);
09c6a56 146
});