summaryrefslogtreecommitdiff
path: root/cgithub/src/app.test.ts
diff options
context:
space:
mode:
authorJoe Mou <dev@mou.fo>2026-09-03 00:08:06 -0400
committerJoe Mou <dev@mou.fo>2026-09-03 01:09:10 -0400
commit4c66a07a995c0d551cd284d6e72826b4f7748bab (patch)
tree35c53f0713a0903ed4c44d53ed17a4f47deaf4af /cgithub/src/app.test.ts
parent1b9d13fce99b9d798dd604f402699bb47cb1627e (diff)
Render repository wikis
Wikis are one of the last parts of GitHub still served as plain Rails HTML, with no embedded JSON to read, so this scrapes the markup: a page and any older revision of it, the _Sidebar and _Footer a wiki can define, and the page index. Every page is already in the sidebar of every page, since the "Show N more pages" button only unhides the rest, so linking the whole wiki costs no second request. Routes mirror GitHub's and resolve in its order, so an underscore name reserved for its own actions (_pages, _history, _new, _edit) can never be mistaken for a page; the ones we don't serve fall through to the usual redirect. A slug reaches us decoded and has to be encoded again rather than interpolated the way a ref is, or a page named C# would truncate at the fragment. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013rQG1io1i4GJQW5RFcXNGd
Diffstat (limited to 'cgithub/src/app.test.ts')
-rw-r--r--cgithub/src/app.test.ts98
1 files changed, 98 insertions, 0 deletions
diff --git a/cgithub/src/app.test.ts b/cgithub/src/app.test.ts
index e28c375..3ecebd0 100644
--- a/cgithub/src/app.test.ts
+++ b/cgithub/src/app.test.ts
@@ -178,6 +178,104 @@ describe("embedded files", () => {
});
});
+describe("wiki", () => {
+ const repo = { owner: "ariya", name: "phantomjs" };
+ const page = {
+ repo,
+ title: "Home",
+ updatedBy: "pixiuPL",
+ updatedAt: "2018-02-08T18:57:56Z",
+ bodyHtml: "<p>New to <strong>PhantomJS</strong>?</p>",
+ sidebarHtml: null,
+ footerHtml: null,
+ pages: [
+ { name: "Home", href: "/ariya/phantomjs/wiki" },
+ { name: "API Reference WebPage", href: "/ariya/phantomjs/wiki/API-Reference-WebPage" },
+ ],
+ };
+
+ it("should render the page beside who last edited it", () => {
+ const html = eta.render("wiki.eta", page);
+
+ assert.match(html, /<p>New to <strong>PhantomJS<\/strong>\?<\/p>/);
+ assert.match(html, /pixiuPL edited this page/);
+ assert.match(html, /<time title="2018-02-08T18:57:56Z">2018-02-08<\/time>/);
+ });
+
+ // The _Sidebar and _Footer pages a wiki can define, which GitHub renders
+ // alongside every page rather than as pages of their own.
+ it("should render the sidebar and footer only when the wiki has them", () => {
+ // The page's own content is the last thing in its box, and the box is the
+ // only one on the page.
+ const bare = eta.render("wiki.eta", page);
+ assert.match(bare, /New to <strong>PhantomJS<\/strong>\?<\/p>\s*<\/div>/);
+ assert.doesNotMatch(bare, /Sidebar/);
+
+ const html = eta.render("wiki.eta", {
+ ...page,
+ sidebarHtml: "<ul><li>nav</li></ul>",
+ footerHtml: "<p>fine print</p>",
+ });
+ assert.match(html, /<footer>\s*<p>fine print<\/p>/);
+ assert.match(html, /<header><span>Sidebar<\/span><\/header>\s*<ul><li>nav<\/li><\/ul>/);
+ });
+
+ it("should link every page of the wiki", () => {
+ const html = eta.render("wiki.eta", page);
+
+ assert.match(html, /<summary>2 pages<\/summary>/);
+ assert.match(
+ html,
+ /href="\/ariya\/phantomjs\/wiki\/API-Reference-WebPage">API Reference WebPage/,
+ );
+ });
+
+ it("should list the pages with when each was last updated", () => {
+ const html = eta.render("wikiPages.eta", {
+ repo,
+ pages: [
+ {
+ name: "Home",
+ href: "/ariya/phantomjs/wiki",
+ updatedAt: "2018-02-08T18:57:56Z",
+ },
+ ],
+ });
+
+ assert.match(html, /href="\/ariya\/phantomjs\/wiki">Home/);
+ assert.match(html, /<time title="2018-02-08T18:57:56Z">2018-02-08<\/time>/);
+ });
+
+ // The editor and the timestamp are scraped independently, so a page missing
+ // one of them should still show the other.
+ it("should render each part of the header meta it has", () => {
+ assert.doesNotMatch(eta.render("wiki.eta", { ...page, updatedAt: null }), /<time/);
+ assert.match(eta.render("wiki.eta", { ...page, updatedAt: null }), /pixiuPL edited this page/);
+ assert.doesNotMatch(eta.render("wiki.eta", { ...page, updatedBy: null }), /edited this page/);
+ });
+
+ // GitHub reserves underscore-prefixed names for its own actions, so a page
+ // can never be one; the rest of them are for signed-in users only.
+ it("should not scrape a reserved wiki name as a page", async () => {
+ const reserved = ["/ariya/phantomjs/wiki/_new", "/ariya/phantomjs/wiki/Home/_edit"];
+ for (const path of reserved) {
+ const res = await app.request(`http://cgithub.example${path}`);
+
+ assert.strictEqual(res.status, 200);
+ assert.match(await res.text(), new RegExp(`url=https://github.com${path}"`));
+ }
+ });
+
+ it("should only read a revision off a path that names one", async () => {
+ const res = await app.request("http://cgithub.example/ariya/phantomjs/wiki/Home/latest");
+
+ assert.match(
+ await res.text(),
+ /url=https:\/\/github.com\/ariya\/phantomjs\/wiki\/Home\/latest"/,
+ );
+ });
+});
+
describe("redirects to GitHub", () => {
// An HTTP redirect would be caught by the extension's declarativeNetRequest
// rules and bounced straight back here.