1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
|
import assert from "node:assert";
import { describe, it } from "node:test";
import {
getGitHubBlob,
getGitHubIssues,
getGitHubRepo,
getGitHubTree,
GitHubHTTPError,
} from "./scraper.ts";
describe("GitHub scraper", () => {
describe("actions/deploy-pages repository", () => {
it("should fetch repository", async () => {
const data = await getGitHubRepo("actions", "deploy-pages");
assert.strictEqual(data.repo.owner, "actions");
assert.strictEqual(data.repo.name, "deploy-pages");
assert.strictEqual(data.branch, "main");
assert.strictEqual(data.path, "/");
assert.strictEqual(
data.info.description,
"GitHub Action to publish artifacts to GitHub Pages for deployments",
);
assert.strictEqual(data.info.website, "https://pages.github.com");
assert.strictEqual(data.info.stars?.length, 3);
assert.strictEqual(data.info.watchers?.length, 2);
assert.strictEqual(data.info.forks?.length, 3);
assert.strictEqual(data.info.numReleases, 36);
assert.ok(data.items.length > 0);
assert.ok(Object.keys(data.overviewHtml ?? {}).includes("README.md"));
});
it("should fetch tree", async () => {
const data = await getGitHubTree("actions", "deploy-pages", "main", "src");
assert.strictEqual(data.repo.owner, "actions");
assert.strictEqual(data.repo.name, "deploy-pages");
assert.strictEqual(data.branch, "main");
assert.strictEqual(data.path, "src");
assert.deepStrictEqual(data.items, [
{
name: "__tests__",
path: "src/__tests__",
contentType: "directory",
},
{
name: "internal",
path: "src/internal",
contentType: "directory",
},
{
name: "index.js",
path: "src/index.js",
contentType: "file",
},
]);
});
it("should fetch text blob", async () => {
const data = await getGitHubBlob("actions", "deploy-pages", "main", "LICENSE");
assert.strictEqual(data.repo.owner, "actions");
assert.strictEqual(data.repo.name, "deploy-pages");
assert.strictEqual(data.branch, "main");
assert.strictEqual(data.path, "LICENSE");
assert.strictEqual(data.size, "1.04 KB / 21 lines / 17 loc");
assert.strictEqual(data.language, "Text");
assert.strictEqual(data.image, false);
assert.strictEqual(data.textLines?.length, 21);
assert.strictEqual(data.textLines[0], "MIT License");
assert.strictEqual(data.htmlLines?.length, 21);
assert.strictEqual(data.htmlLines[0], "MIT License");
assert.strictEqual(data.htmlContent, null);
});
it("should fetch Markdown blob", async () => {
const data = await getGitHubBlob("actions", "deploy-pages", "main", "README.md");
assert.strictEqual(data.repo.owner, "actions");
assert.strictEqual(data.repo.name, "deploy-pages");
assert.strictEqual(data.branch, "main");
assert.strictEqual(data.path, "README.md");
assert.strictEqual(data.size, "8.95 KB / 133 lines / 94 loc");
assert.strictEqual(data.language, "Markdown");
assert.strictEqual(data.image, false);
assert.strictEqual(data.textLines, null);
assert.strictEqual(data.htmlLines, null);
const firstLine =
/^<article class="markdown-body entry-content container-lg" itemprop="text"><div class="markdown-heading" dir="auto"><h1 tabindex="-1" class="heading-element" dir="auto">deploy-pages 🚀<\/h1>/;
assert.match(data.htmlContent ?? "", firstLine);
});
it("should fetch code blob", async () => {
const data = await getGitHubBlob("actions", "deploy-pages", "main", ".gitattributes");
assert.strictEqual(data.repo.owner, "actions");
assert.strictEqual(data.repo.name, "deploy-pages");
assert.strictEqual(data.branch, "main");
assert.strictEqual(data.path, ".gitattributes");
assert.strictEqual(data.size, "39 Bytes / 1 lines / 1 loc");
assert.strictEqual(data.language, "Git Attributes");
assert.strictEqual(data.image, false);
assert.deepStrictEqual(data.textLines, ["dist/** -diff linguist-generated=true "]);
assert.deepStrictEqual(data.htmlLines, [
'\u003cspan class="pl-e"\u003edist\u003c/span\u003e/\u003cspan class="pl-k"\u003e**\u003c/span\u003e \u003cspan class="pl-k"\u003e-\u003c/span\u003e\u003cspan class="pl-v"\u003ediff\u003c/span\u003e \u003cspan class="pl-v"\u003elinguist-generated\u003c/span\u003e=\u003cspan class="pl-c1"\u003etrue\u003c/span\u003e ',
]);
assert.strictEqual(data.htmlContent, null);
});
it("should fetch issues", async () => {
const data = await getGitHubIssues("actions", "deploy-pages");
assert.strictEqual(data.repo.owner, "actions");
assert.strictEqual(data.repo.name, "deploy-pages");
const issue402 = data.issues.find((issue) => issue.number === 402);
assert.ok(issue402);
assert.strictEqual(issue402.title, "Dry Run");
assert.strictEqual(issue402.state, "OPEN");
assert.strictEqual(issue402.createdAt, "2025-07-03T19:07:08Z");
});
});
describe("rich file types", () => {
it("should support PNG", async () => {
const data = await getGitHubBlob("github", "docs", "main", "assets/images/site/logo.png");
assert.strictEqual(data.repo.owner, "github");
assert.strictEqual(data.repo.name, "docs");
assert.strictEqual(data.branch, "main");
assert.strictEqual(data.path, "assets/images/site/logo.png");
assert.strictEqual(data.size, "1.26 KB");
assert.strictEqual(data.language, null);
assert.strictEqual(data.image, true);
assert.strictEqual(data.textLines, null);
assert.strictEqual(data.htmlLines, null);
assert.strictEqual(data.htmlContent, null);
});
});
describe("error handling", () => {
it("should throw for non-existent repository", async () => {
await assert.rejects(getGitHubRepo("nosuchowner", "nosuchrepo"), (err) => {
assert(err instanceof GitHubHTTPError, "error should be an HTTPError");
assert.strictEqual(err.status, 404);
return true;
});
});
});
});
|