Skip to content

Commit 2190831

Browse files
authored
Merge pull request #414 from dodok8/dodok8-issue-399
2 parents 1fa4c29 + 1124ee2 commit 2190831

File tree

5 files changed

+873
-6
lines changed

5 files changed

+873
-6
lines changed

packages/cli/deno.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
"hono": "jsr:@hono/hono@^4.8.3",
1919
"@poppanator/http-constants": "npm:@poppanator/http-constants@^1.1.1",
2020
"shiki": "npm:shiki@^1.6.4",
21-
"srvx": "npm:srvx@^0.8.7"
21+
"srvx": "npm:srvx@^0.8.7",
22+
"fetch-mock": "npm:fetch-mock@^12.5.4",
23+
"icojs": "npm:icojs@^0.19.5"
2224
},
2325
"exclude": [
2426
"fedify-cli-*.tar.xz",

packages/cli/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
"@optique/core": "^0.4.0-dev.52+12ee9e92",
4040
"@optique/run": "^0.3.0",
4141
"cli-highlight": "^2.1.11",
42+
"fetch-mock": "^12.5.4",
43+
"icojs": "^0.19.5",
4244
"jimp": "^1.6.0",
4345
"@jimp/core": "^1.6.0",
4446
"@jimp/wasm-webp": "^1.6.0",

packages/cli/src/nodeinfo.test.ts

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
import { assertEquals } from "@std/assert";
2+
import fetchMock from "fetch-mock";
3+
import { getAsciiArt, getFaviconUrl, Jimp, rgbTo256Color } from "./nodeinfo.ts";
4+
5+
const HTML_WITH_SMALL_ICON = `
6+
<!DOCTYPE html>
7+
<html>
8+
<head>
9+
<title>Test Site</title>
10+
<link rel="icon" href="/favicon.ico" sizes="32x32">
11+
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
12+
</head>
13+
<body>Test</body>
14+
</html>
15+
`;
16+
17+
Deno.test("getFaviconUrl - small favicon.ico and apple-touch-icon.png", async () => {
18+
fetchMock.spyGlobal();
19+
20+
fetchMock.get("https://example.com/", {
21+
body: HTML_WITH_SMALL_ICON,
22+
headers: { "Content-Type": "text/html" },
23+
});
24+
25+
const result = await getFaviconUrl("https://example.com/");
26+
assertEquals(result.href, "https://example.com/apple-touch-icon.png");
27+
28+
fetchMock.hardReset();
29+
});
30+
31+
const HTML_WITH_ICON = `
32+
<!DOCTYPE html>
33+
<html>
34+
<head>
35+
<title>Test Site</title>
36+
<link rel="icon" href="/favicon.ico" sizes="64x64">
37+
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
38+
</head>
39+
<body>Test</body>
40+
</html>
41+
`;
42+
43+
Deno.test("getFaviconUrl - favicon.ico and apple-touch-icon.png", async () => {
44+
fetchMock.spyGlobal();
45+
46+
fetchMock.get("https://example.com/", {
47+
body: HTML_WITH_ICON,
48+
headers: { "Content-Type": "text/html" },
49+
});
50+
51+
const result = await getFaviconUrl("https://example.com/");
52+
assertEquals(result.href, "https://example.com/favicon.ico");
53+
54+
fetchMock.hardReset();
55+
});
56+
57+
const HTML_WITH_SVG_ONLY = `
58+
<!DOCTYPE html>
59+
<html>
60+
<head>
61+
<title>Test Site</title>
62+
<link rel="icon" href="/icon.svg" type="image/svg+xml">
63+
</head>
64+
<body>Test</body>
65+
</html>
66+
`;
67+
68+
Deno.test("getFaviconUrl - svg icons only falls back to /favicon.ico", async () => {
69+
fetchMock.spyGlobal();
70+
71+
fetchMock.get("https://example.com/", {
72+
body: HTML_WITH_SVG_ONLY,
73+
headers: { "Content-Type": "text/html" },
74+
});
75+
76+
const result = await getFaviconUrl("https://example.com/");
77+
assertEquals(result.href, "https://example.com/favicon.ico");
78+
79+
fetchMock.hardReset();
80+
});
81+
82+
const HTML_WITHOUT_ICON = `
83+
<!DOCTYPE html>
84+
<html>
85+
<head>
86+
<title>Test Site</title>
87+
</head>
88+
<body>Test</body>
89+
</html>
90+
`;
91+
92+
Deno.test("getFaviconUrl - falls back to /favicon.ico", async () => {
93+
fetchMock.spyGlobal();
94+
95+
fetchMock.get("https://example.com/", {
96+
body: HTML_WITHOUT_ICON,
97+
headers: { "Content-Type": "text/html" },
98+
});
99+
100+
const result = await getFaviconUrl("https://example.com/");
101+
assertEquals(result.href, "https://example.com/favicon.ico");
102+
103+
fetchMock.hardReset();
104+
});
105+
106+
Deno.test("rgbTo256Color - check RGB cube", () => {
107+
const CUBE_VALUES = [0, 95, 135, 175, 215, 255];
108+
const colors: Array<{ r: number; g: number; b: number }> = [];
109+
110+
for (let r = 0; r < 6; r++) {
111+
for (let g = 0; g < 6; g++) {
112+
for (let b = 0; b < 6; b++) {
113+
colors.push({
114+
r: CUBE_VALUES[r],
115+
g: CUBE_VALUES[g],
116+
b: CUBE_VALUES[b],
117+
});
118+
}
119+
}
120+
}
121+
122+
// Expected color indices for the above colors (16-231)
123+
// RGB cube: 6x6x6 = 216 colors, indices 16-231
124+
const expected_color_idx = Array.from(
125+
{ length: colors.length },
126+
(_, i) => 16 + i,
127+
);
128+
129+
const results = colors.map((color) =>
130+
rgbTo256Color(color.r, color.g, color.b)
131+
);
132+
assertEquals(results, expected_color_idx);
133+
});
134+
135+
Deno.test("rgbTo256Color - check grayscale", () => {
136+
const grayscale = Array.from({ length: 24 }).map(
137+
(_, idx) => ({
138+
r: 8 + idx * 10,
139+
g: 8 + idx * 10,
140+
b: 8 + idx * 10,
141+
}),
142+
);
143+
144+
const expected_gray_idx = Array.from(
145+
{ length: grayscale.length },
146+
(_, i) => 232 + i,
147+
);
148+
149+
const results = grayscale.map((GRAY) =>
150+
rgbTo256Color(GRAY.r, GRAY.g, GRAY.b)
151+
);
152+
assertEquals(results, expected_gray_idx);
153+
});
154+
155+
async function createTestImage(
156+
color: number,
157+
): Promise<Awaited<ReturnType<typeof Jimp.read>>> {
158+
const image = new Jimp({ width: 1, height: 1, color });
159+
const imageBuffer = await image.getBuffer("image/webp");
160+
return Jimp.read(imageBuffer);
161+
}
162+
163+
Deno.test("getAsciiArt - Darkest Letter without color support", async () => {
164+
const blackResult = getAsciiArt(
165+
await createTestImage(0x000000ff),
166+
1,
167+
"none",
168+
);
169+
170+
assertEquals(blackResult, "█");
171+
});
172+
173+
Deno.test("getAsciiArt - Brightest Letter without color support", async () => {
174+
const whiteResult = getAsciiArt(
175+
await createTestImage(0xffffffff),
176+
1,
177+
"none",
178+
);
179+
180+
assertEquals(whiteResult, " ");
181+
});
182+
183+
Deno.test("getAsciiArt - Darkest Letter with 256 color support", async () => {
184+
const blackResult = getAsciiArt(
185+
await createTestImage(0x000000ff),
186+
1,
187+
"256color",
188+
);
189+
190+
assertEquals(blackResult, "\u001b[38;5;16m█\u001b[39m");
191+
});
192+
193+
Deno.test("getAsciiArt - Brightest Letter with 256 color support", async () => {
194+
const whiteResult = getAsciiArt(
195+
await createTestImage(0xffffffff),
196+
1,
197+
"256color",
198+
);
199+
200+
assertEquals(whiteResult, "\u001b[38;5;231m \u001b[39m");
201+
});
202+
203+
Deno.test("getAsciiArt - Darkest Letter with true color support", async () => {
204+
const blackResult = getAsciiArt(
205+
await createTestImage(0x000000ff),
206+
1,
207+
"truecolor",
208+
);
209+
210+
assertEquals(blackResult, "\u001b[38;2;0;0;0m█\u001b[39m");
211+
});
212+
213+
Deno.test("getAsciiArt - Brightest Letter with true color support", async () => {
214+
const whiteResult = getAsciiArt(
215+
await createTestImage(0xffffffff),
216+
1,
217+
"truecolor",
218+
);
219+
220+
assertEquals(whiteResult, "\u001b[38;2;255;255;255m \u001b[39m");
221+
});

0 commit comments

Comments
 (0)