import { renderToStaticMarkup } from "react-dom/server";
import { describe, expect, it } from "vitest";
import { ContextInfoButton } from "../src/renderer/ui/ContextInfoButton";
import {
DataTable,
DataTableBody,
DataTableEmpty,
DataTableFooter,
DataTableHeader
} from "../src/renderer/ui/DataTable";
import { Icon } from "../src/renderer/ui/Icon";
import { Toolbar, ToolbarGroup, ToolbarSearch } from "../src/renderer/ui/Toolbar";
import { getThemeVariables, UI_FOCUS_RING_VARIABLE } from "../src/renderer/ui/theme";
const expectedThemes = {
dark: {
"--ui-canvas": "#0F0F0F",
"--ui-surface": "#232323",
"--ui-input": "#2B2B2B",
"--ui-table-header": "#313131",
"--ui-active": "#333436",
"--ui-hover": "#373535",
"--ui-tooltip": "#4F4D4D",
"--ui-border": "#3D3D3D",
"--ui-text": "#FFFFFF",
"--ui-text-secondary": "#EAEDF3",
"--ui-text-muted": "#919191",
"--ui-primary": "#BAD0FC",
"--ui-primary-hover": "#8AA5DC",
"--ui-accent": "#3886FF",
"--ui-warning": "#F1C786",
"--ui-danger": "#F06464",
"--ui-modal-secondary": "#35383D",
"--ui-overlay": "rgba(0, 0, 0, 0.60)"
},
light: {
"--ui-canvas": "#F3F4F6",
"--ui-surface": "#FFFFFF",
"--ui-input": "#F7F8FA",
"--ui-table-header": "#E7E9ED",
"--ui-active": "#DEE6F5",
"--ui-hover": "#E8ECF3",
"--ui-tooltip": "#35383D",
"--ui-border": "#D0D4DB",
"--ui-text": "#181A1F",
"--ui-text-secondary": "#343842",
"--ui-text-muted": "#667085",
"--ui-primary": "#A9C2F3",
"--ui-primary-hover": "#8AA5DC",
"--ui-accent": "#256FDB",
"--ui-warning": "#E8B85D",
"--ui-danger": "#D94747",
"--ui-modal-secondary": "#E7E9ED",
"--ui-overlay": "rgba(0, 0, 0, 0.45)"
}
} as const;
function relativeLuminance(color: string): number {
const channels = color.slice(1).match(/.{2}/g)?.map((value) => Number.parseInt(value, 16) / 255) ?? [];
const [red, green, blue] = channels.map((value) => value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4);
return 0.2126 * red + 0.7152 * green + 0.0722 * blue;
}
function contrastRatio(first: string, second: string): number {
const lighter = Math.max(relativeLuminance(first), relativeLuminance(second));
const darker = Math.min(relativeLuminance(first), relativeLuminance(second));
return (lighter + 0.05) / (darker + 0.05);
}
describe("semantic themes", () => {
it("exposes the exact frozen semantic roles for dark and light", () => {
const dark = getThemeVariables("dark");
const light = getThemeVariables("light");
expect(dark).toEqual(expectedThemes.dark);
expect(light).toEqual(expectedThemes.light);
expect(Object.keys(dark)).toEqual(Object.keys(light));
expect(Object.keys(dark)).toHaveLength(18);
expect(Object.isFrozen(dark)).toBe(true);
expect(Object.isFrozen(light)).toBe(true);
});
it("uses a focus ring role with at least 3 to 1 contrast on control surfaces", () => {
for (const theme of ["dark", "light"] as const) {
const variables = getThemeVariables(theme);
const focus = variables[UI_FOCUS_RING_VARIABLE];
for (const surface of ["--ui-canvas", "--ui-surface", "--ui-input", "--ui-active", "--ui-hover"] as const) {
expect(contrastRatio(focus, variables[surface]), `${theme} focus on ${surface}`).toBeGreaterThanOrEqual(3);
}
}
});
});
describe("new UI primitives", () => {
it("renders labelled current-color outline icons without emoji text", () => {
const html = renderToStaticMarkup();
expect(html).toContain("aria-label=\"Downloads\"");
expect(html).toContain("