feat(frontend): registre icônes de marque légales avec métadonnées et fallback, monogrammes A-Z et filigrane CLI agrandi — #335 (QA verte)

This commit is contained in:
2026-09-12 12:29:35 +02:00
parent 460fad4e60
commit 4c69914817
8 changed files with 356 additions and 70 deletions

View File

@ -0,0 +1,92 @@
import { fireEvent, render, screen } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
import { ProjectIconPicker } from "./ProjectIconPicker";
import {
PROJECT_ICON_CATALOG,
ProjectIcon,
findProjectIcon,
resolveProjectIcon,
} from "./projectIcons";
describe("project icon catalogue", () => {
it("carries legal policy for every entry and an attribution for the vendored Angular artwork", () => {
expect(PROJECT_ICON_CATALOG.length).toBeGreaterThan(70);
for (const entry of PROJECT_ICON_CATALOG) {
expect(entry.legal.owner).toBeTruthy();
expect(entry.legal.license).toBeTruthy();
expect(typeof entry.legal.productUiAllowed).toBe("boolean");
expect(typeof entry.legal.watermarkAllowed).toBe("boolean");
}
const angular = findProjectIcon({ set: "simple-icons", name: "angular" });
expect(angular).toMatchObject({
group: "technologies",
legal: {
license: "CC BY 4.0",
productUiAllowed: true,
watermarkAllowed: true,
sourceUrl: "https://angular.dev/press-kit",
},
});
expect(angular?.assetSrc).toBeTruthy();
});
it.each([
["unity", "gamepad-2"],
["unrealengine", "gamepad-2"],
["windows", "terminal"],
["apple", "terminal"],
["google", "globe-2"],
["microsoft", "terminal"],
])("resolves the restricted %s brand to product-owned fallback artwork", (name, fallbackName) => {
const set = name === "windows" || name === "apple" ? "devicon" : "simple-icons";
const restricted = findProjectIcon({ set, name });
expect(restricted?.legal.productUiAllowed).toBe(false);
expect(restricted?.legal.watermarkAllowed).toBe(false);
expect(resolveProjectIcon({ set, name }, "product-ui")?.name).toBe(fallbackName);
expect(resolveProjectIcon({ set, name }, "watermark")?.name).toBe(fallbackName);
});
it("provides stable, strongly boxed A-Z monograms", () => {
const letters = PROJECT_ICON_CATALOG.filter((entry) => entry.group === "letters");
expect(letters.map((entry) => entry.glyph).join("")).toBe("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
const { getByRole } = render(<ProjectIcon icon={{ set: "idea", name: "letter-z" }} />);
expect(getByRole("img", { name: "Z" }).style.borderTopWidth).toBe("1px");
expect(getByRole("img", { name: "Z" }).style.borderRadius).toBe("18%");
});
});
describe("ProjectIconPicker", () => {
function renderPicker() {
render(<ProjectIconPicker value={null} onApply={vi.fn()} onCancel={vi.fn()} />);
}
it("shows the four catalogue groups", () => {
renderPicker();
expect(screen.getByText("Plateformes")).toBeTruthy();
expect(screen.getByText("Technologies / Frameworks")).toBeTruthy();
expect(screen.getByText("Symboles / Génériques")).toBeTruthy();
expect(screen.getByText("Lettres")).toBeTruthy();
});
it.each([
["unity", "Unity"],
["unreal", "Unreal Engine"],
["angular", "Angular"],
["windows", "Windows"],
])("finds %s using the English search metadata", (query, label) => {
renderPicker();
fireEvent.change(screen.getByLabelText("Search icons in English"), { target: { value: query } });
expect(screen.getByRole("option", { name: label })).toBeTruthy();
});
it("finds and selects an alphabet monogram", () => {
const onApply = vi.fn();
render(<ProjectIconPicker value={null} onApply={onApply} onCancel={vi.fn()} />);
fireEvent.change(screen.getByLabelText("Search icons in English"), { target: { value: "letter q" } });
fireEvent.click(screen.getByRole("option", { name: "Q" }));
fireEvent.click(screen.getByRole("button", { name: "Appliquer" }));
expect(onApply).toHaveBeenCalledWith({ set: "idea", name: "letter-q" });
});
});