muddle/tests/analysis.test.ts
2026-10-23 09:12:49 -04:00

84 lines
3 KiB
TypeScript

import { expect, test } from "bun:test";
import { analyzeHtml } from "../modules/analysis/index.ts";
import { App } from "../core/app.ts";
import {
terserIntegration,
htmlMinifyIntegration,
imageOptimizationIntegration,
} from "../integrations/optimize.ts";
test("accessibility scoring reports semantics, alpha contrast and visual-effect uncertainty", async () => {
const html =
'<html lang="en"><head><title>Audit</title><style>body{background:white;color:black}.faint{color:rgba(0,0,0,.1)}.blur{filter:blur(1px);backdrop-filter:blur(4px)}</style></head><body><input><p class="faint">Low contrast</p><p class="blur">Filtered text</p><img src="missing.png"></body></html>';
const [report] = await analyzeHtml(html, "accessible", {
viewports: [{ width: 390, height: 844 }],
});
expect(report!.findings.map((finding) => finding.rule)).toContain(
"control-name",
);
expect(report!.findings.map((finding) => finding.rule)).toContain(
"text-contrast",
);
expect(report!.findings.map((finding) => finding.rule)).toContain(
"visibility-effects",
);
expect(report!.score).toBeLessThan(100);
expect(
report!.findings.find((finding) => finding.rule === "visibility-effects")!
.severity,
).toBe("review");
});
test("overflow analysis distinguishes intentional scrolling and clipped content", async () => {
const html =
'<html><head><style>body{margin:0}.wide{width:700px}.scroll{width:200px;overflow:auto}.clip{width:100px;height:10px;overflow:hidden}</style></head><body><div class="wide">Wide</div><div class="scroll"><div class="wide">Scrollable</div></div><div class="clip"><div style="height:200px">Clipped</div></div></body></html>';
const reports = await analyzeHtml(html, "overflow");
expect(
reports[0]!.findings.filter(
(finding) => finding.rule === "viewport-overflow",
),
).toHaveLength(0);
expect(
reports[1]!.findings.filter(
(finding) => finding.rule === "viewport-overflow",
),
).toHaveLength(1);
expect(reports[1]!.findings.map((finding) => finding.rule)).toContain(
"clipping",
);
});
test("async optimization runs on staged assets and preserves binary image input", async () => {
let bytes: number[] = [];
const app = await App.create({
integrations: [
terserIntegration(),
htmlMinifyIntegration(),
imageOptimizationIntegration({
async optimize(input) {
bytes = [...input];
return new Uint8Array([9, 8]);
},
}),
],
});
const js = await app.transformAsset({
path: "app.js",
contentType: "text/javascript",
content: 'const url = "https://example.test"; console.log(url);',
});
expect(String(js.content)).toContain("https://example.test");
const html = await app.transformAsset({
path: "index.html",
contentType: "text/html",
content: "<html><body> <p>Text</p> </body></html>",
});
expect(String(html.content)).not.toContain(" ");
const image = await app.transformAsset({
path: "image.png",
contentType: "image/png",
content: new Uint8Array([0, 255, 128]),
});
expect(bytes).toEqual([0, 255, 128]);
expect([...(image.content as Uint8Array)]).toEqual([9, 8]);
});