import assert from "node:assert/strict";
import { test } from "node:test";
import { Chalk } from "chalk";
import fetchMock from "fetch-mock";
import {
getAsciiArt,
getFaviconUrl,
Jimp,
readImage,
rgbTo256Color,
} from "./nodeinfo.ts";
const HTML_WITH_SMALL_ICON = `
Test Site
Test
`;
test("getFaviconUrl - small favicon.ico and apple-touch-icon.png", async () => {
fetchMock.spyGlobal();
fetchMock.get("https://example.com/", {
body: HTML_WITH_SMALL_ICON,
headers: { "Content-Type": "text/html" },
});
const result = await getFaviconUrl("https://example.com/");
assert.equal(result.href, "https://example.com/apple-touch-icon.png");
fetchMock.hardReset();
});
const HTML_WITH_ICON = `
Test Site
Test
`;
test("getFaviconUrl - favicon.ico and apple-touch-icon.png", async () => {
fetchMock.spyGlobal();
fetchMock.get("https://example.com/", {
body: HTML_WITH_ICON,
headers: { "Content-Type": "text/html" },
});
const result = await getFaviconUrl("https://example.com/");
assert.equal(result.href, "https://example.com/favicon.ico");
fetchMock.hardReset();
});
const HTML_WITH_SVG_ONLY = `
Test Site
Test
`;
test("getFaviconUrl - svg icons only falls back to /favicon.ico", async () => {
fetchMock.spyGlobal();
fetchMock.get("https://example.com/", {
body: HTML_WITH_SVG_ONLY,
headers: { "Content-Type": "text/html" },
});
const result = await getFaviconUrl("https://example.com/");
assert.equal(result.href, "https://example.com/favicon.ico");
fetchMock.hardReset();
});
const HTML_WITH_UPPERCASE_SVG_ONLY = `
Test Site
Test
`;
test("getFaviconUrl - uppercase svg icons only falls back to /favicon.ico", async () => {
fetchMock.spyGlobal();
fetchMock.get("https://example.com/", {
body: HTML_WITH_UPPERCASE_SVG_ONLY,
headers: { "Content-Type": "text/html" },
});
const result = await getFaviconUrl("https://example.com/");
assert.equal(result.href, "https://example.com/favicon.ico");
fetchMock.hardReset();
});
const HTML_WITH_UPPERCASE_SVG_WITH_QUERY = `
Test Site
Test
`;
test("getFaviconUrl - uppercase svg icons with query and hash fall back to /favicon.ico", async () => {
fetchMock.spyGlobal();
fetchMock.get("https://example.com/", {
body: HTML_WITH_UPPERCASE_SVG_WITH_QUERY,
headers: { "Content-Type": "text/html" },
});
const result = await getFaviconUrl("https://example.com/");
assert.equal(result.href, "https://example.com/favicon.ico");
fetchMock.hardReset();
});
const HTML_WITHOUT_ICON = `
Test Site
Test
`;
test("getFaviconUrl - falls back to /favicon.ico", async () => {
fetchMock.spyGlobal();
fetchMock.get("https://example.com/", {
body: HTML_WITHOUT_ICON,
headers: { "Content-Type": "text/html" },
});
const result = await getFaviconUrl("https://example.com/");
assert.equal(result.href, "https://example.com/favicon.ico");
fetchMock.hardReset();
});
test("rgbTo256Color - check RGB cube", () => {
const CUBE_VALUES = [0, 95, 135, 175, 215, 255];
const colors: Array<{ r: number; g: number; b: number }> = [];
for (let r = 0; r < 6; r++) {
for (let g = 0; g < 6; g++) {
for (let b = 0; b < 6; b++) {
colors.push({
r: CUBE_VALUES[r],
g: CUBE_VALUES[g],
b: CUBE_VALUES[b],
});
}
}
}
// Expected color indices for the above colors (16-231)
// RGB cube: 6x6x6 = 216 colors, indices 16-231
const expected_color_idx = Array.from(
{ length: colors.length },
(_, i) => 16 + i,
);
const results = colors.map((color) =>
rgbTo256Color(color.r, color.g, color.b)
);
assert.deepEqual(results, expected_color_idx);
});
test("rgbTo256Color - check grayscale", () => {
const grayscale = Array.from({ length: 24 }).map(
(_, idx) => ({
r: 8 + idx * 10,
g: 8 + idx * 10,
b: 8 + idx * 10,
}),
);
const expected_gray_idx = Array.from(
{ length: grayscale.length },
(_, i) => 232 + i,
);
const results = grayscale.map((GRAY) =>
rgbTo256Color(GRAY.r, GRAY.g, GRAY.b)
);
assert.deepEqual(results, expected_gray_idx);
});
async function createTestImage(
color: number,
): Promise>> {
const image = new Jimp({ width: 1, height: 1, color });
const imageBuffer = await image.getBuffer("image/png");
return readImage(imageBuffer);
}
test("getAsciiArt - Darkest Letter without color support", async () => {
const blackResult = getAsciiArt(
await createTestImage(0x000000ff),
1,
"none",
new Chalk({ level: 0 }),
);
assert.equal(blackResult, "â–ˆ");
});
test("getAsciiArt - Brightest Letter without color support", async () => {
const whiteResult = getAsciiArt(
await createTestImage(0xffffffff),
1,
"none",
new Chalk({ level: 0 }),
);
assert.equal(whiteResult, " ");
});
test("getAsciiArt - Darkest Letter with 256 color support", async () => {
const blackResult = getAsciiArt(
await createTestImage(0x000000ff),
1,
"256color",
new Chalk({ level: 2 }),
);
assert.equal(blackResult, "\u001b[38;5;16mâ–ˆ\u001b[39m");
});
test("getAsciiArt - Brightest Letter with 256 color support", async () => {
const whiteResult = getAsciiArt(
await createTestImage(0xffffffff),
1,
"256color",
new Chalk({ level: 2 }),
);
assert.equal(whiteResult, "\u001b[38;5;231m \u001b[39m");
});
test("getAsciiArt - Darkest Letter with true color support", async () => {
const blackResult = getAsciiArt(
await createTestImage(0x000000ff),
1,
"truecolor",
new Chalk({ level: 3 }),
);
assert.equal(blackResult, "\u001b[38;2;0;0;0mâ–ˆ\u001b[39m");
});
test("getAsciiArt - Brightest Letter with true color support", async () => {
const whiteResult = getAsciiArt(
await createTestImage(0xffffffff),
1,
"truecolor",
new Chalk({ level: 3 }),
);
assert.equal(whiteResult, "\u001b[38;2;255;255;255m \u001b[39m");
});