Alle Varianten aus dem Symbol der Vorgabe per Skript (pnpm marke); Texte als Pfade, damit Wortmarke und Banner ohne installierte Schrift gleich aussehen. Closes #017 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
69 lines
2.7 KiB
TypeScript
69 lines
2.7 KiB
TypeScript
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import { Resvg } from "@resvg/resvg-js";
|
|
import { describe, expect, it } from "vitest";
|
|
import manifest from "@/app/manifest";
|
|
import { metadata } from "@/app/layout";
|
|
|
|
const ORDNER = "public/brand";
|
|
|
|
/** Breite und Höhe aus dem IHDR-Block einer PNG-Datei. */
|
|
function pngGroesse(datei: string): [number, number] {
|
|
const daten = readFileSync(join(ORDNER, datei));
|
|
expect(daten.subarray(1, 4).toString()).toBe("PNG");
|
|
return [daten.readUInt32BE(16), daten.readUInt32BE(20)];
|
|
}
|
|
|
|
describe("Marken-Dateien (pnpm marke)", () => {
|
|
it("alle SVGs der Vorgabe sind da, renderbar und ohne kaputte Koordinaten", () => {
|
|
const svgs = readdirSync(ORDNER).filter((d) => d.endsWith(".svg"));
|
|
expect(svgs.sort()).toEqual(
|
|
[
|
|
"avatar.svg",
|
|
"banner.svg",
|
|
"icon-small.svg",
|
|
"logo-light.svg",
|
|
"logo.svg",
|
|
"symbol-light.svg",
|
|
"symbol.svg",
|
|
].sort(),
|
|
);
|
|
for (const datei of svgs) {
|
|
const text = readFileSync(join(ORDNER, datei), "utf8");
|
|
expect(text, datei).not.toMatch(/NaN|Infinity/);
|
|
const bild = new Resvg(text).render();
|
|
expect(bild.width, datei).toBeGreaterThan(0);
|
|
}
|
|
const banner = new Resvg(readFileSync(join(ORDNER, "banner.svg"), "utf8")).render();
|
|
expect([banner.width, banner.height]).toEqual([1280, 640]);
|
|
});
|
|
|
|
it("kein Proxmox-Orange und nur Markenfarben", () => {
|
|
const erlaubt = new Set(["#11161C", "#1A2129", "#2A333D", "#8B98A5", "#E6EDF3", "#2DD4BF", "#0F766E"]);
|
|
for (const datei of readdirSync(ORDNER).filter((d) => d.endsWith(".svg"))) {
|
|
for (const [farbe] of readFileSync(join(ORDNER, datei), "utf8").matchAll(/#[0-9A-Fa-f]{6}\b/g)) {
|
|
expect(erlaubt.has(farbe.toUpperCase()), `${datei}: ${farbe}`).toBe(true);
|
|
}
|
|
}
|
|
});
|
|
|
|
it("Rasterbilder haben die richtige Größe; Layout und Manifest zeigen nur auf vorhandene Dateien", () => {
|
|
expect(pngGroesse("favicon-16.png")).toEqual([16, 16]);
|
|
expect(pngGroesse("favicon-32.png")).toEqual([32, 32]);
|
|
expect(pngGroesse("apple-touch-icon.png")).toEqual([180, 180]);
|
|
expect(pngGroesse("icon-192.png")).toEqual([192, 192]);
|
|
expect(pngGroesse("icon-512.png")).toEqual([512, 512]);
|
|
expect(pngGroesse("icon-maskable-512.png")).toEqual([512, 512]);
|
|
expect(pngGroesse("banner.png")).toEqual([1280, 640]);
|
|
|
|
const pfade = [
|
|
...(manifest().icons ?? []).map((i) => i.src),
|
|
...JSON.stringify(metadata.icons)
|
|
.matchAll(/"url":"([^"]+)"/g)
|
|
.map((t) => t[1]!),
|
|
];
|
|
expect(pfade.length).toBeGreaterThan(5);
|
|
for (const pfad of pfade) expect(existsSync(join("public", pfad)), pfad).toBe(true);
|
|
});
|
|
});
|