evOo-local/packages/ui-web/src/bot-avatar.test.tsx
cynarAI 6c7eed7580 Initial clean evOo-local from f00577b
Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline
(Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/*
packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
2026-09-20 17:15:16 +02:00

125 lines
4.8 KiB
TypeScript

import { ACTIVE_RUN_STATUSES } from "@rakazo/core";
import { renderToString } from "react-dom/server";
import { describe, expect, it } from "vitest";
import {
BotAvatar,
DEFAULT_GROK_BOT_COLOR,
GROK_BOT_COLORS,
GrokShapePreview,
parseBotAvatar,
resolvePersonaColorDef,
resolvePersonaShape,
} from "./bot-avatar.js";
describe("BotAvatar", () => {
it("renders distinct shape gradient IDs for concurrent working avatars", () => {
const html = renderToString(
<div>
<BotAvatar color="#8B5CF6" status="running" />
<BotAvatar color="#10B981" status="running" />
</div>,
);
const bodyGradIds = [...html.matchAll(/id="(evoo-body-[^"]+)"/g)].map((m) => m[1]);
expect(bodyGradIds).toHaveLength(2);
expect(new Set(bodyGradIds).size).toBe(2);
for (const id of bodyGradIds) {
expect(html).toContain(`url(#${id})`);
}
});
it.each([...ACTIVE_RUN_STATUSES])("marks active run status %s as working", (status) => {
const html = renderToString(<BotAvatar color="#3B82F6" status={status} />);
expect(html).toContain("<svg");
expect(html).toContain('data-working="true"');
});
it("keeps working attribute false when idle", () => {
const html = renderToString(<BotAvatar color="#F59E0B" status="idle" />);
expect(html).toContain('data-working="false"');
});
it("renders a geometric mascot for plain color values", () => {
const html = renderToString(
<BotAvatar color="#D9508A" identity="maya" size={28} status="running" />,
);
expect(html).toContain("<svg");
expect(html).toContain("<path");
expect(html).toContain("<ellipse");
expect(html).toContain('data-working="true"');
});
it("renders distinct shapes for distinct bot identities", () => {
const maya = renderToString(<BotAvatar color="#D9508A" identity="maya" />);
const github = renderToString(<BotAvatar color="#D9508A" identity="github" />);
expect(maya).not.toEqual(github);
});
it("parses shape indexes from encoded color values", () => {
const parsed = parseBotAvatar(`${DEFAULT_GROK_BOT_COLOR}::shape_3`);
expect(parsed.color).toBe(DEFAULT_GROK_BOT_COLOR);
expect(parsed.shapeIndex).toBe(3);
expect(parsed.isImage).toBe(false);
});
it("normalizes malformed shape suffixes to shape 0", () => {
expect(parseBotAvatar(`${DEFAULT_GROK_BOT_COLOR}::shape_-1`).shapeIndex).toBe(0);
expect(parseBotAvatar(`${DEFAULT_GROK_BOT_COLOR}::shape_3junk`).shapeIndex).toBe(0);
expect(parseBotAvatar(`${DEFAULT_GROK_BOT_COLOR}::shape_`).shapeIndex).toBe(0);
});
it("exposes the violet identity color as the shared default", () => {
expect(GROK_BOT_COLORS).toContain(DEFAULT_GROK_BOT_COLOR);
expect(parseBotAvatar(`${DEFAULT_GROK_BOT_COLOR}::shape_0`).color).toBe(DEFAULT_GROK_BOT_COLOR);
});
it("resolves explicit colors and shapes", () => {
expect(resolvePersonaColorDef("bot", "#10B981").hex.toLowerCase()).toBe("#10b981");
expect(resolvePersonaColorDef("bot", "#fff").hex).toBe("#fff");
expect(resolvePersonaShape("bot", "hex")).toContain("M");
expect(GROK_BOT_COLORS.length).toBeGreaterThan(0);
});
it("falls back to the identity palette for invalid custom hex", () => {
expect(resolvePersonaColorDef("bot", "#zzzzzz")).toEqual(resolvePersonaColorDef("bot"));
expect(resolvePersonaColorDef("bot", "#ggg")).toEqual(resolvePersonaColorDef("bot"));
});
it("renders uploaded images without the geometric svg", () => {
const html = renderToString(
<BotAvatar color="data:image/png;base64,abc" identity="maya" size={32} />,
);
expect(html).toContain("<img");
expect(html).not.toContain("<path");
expect(html).not.toContain("grok-character-eyes");
});
it("does not treat arbitrary http(s) color values as remote images", () => {
const parsed = parseBotAvatar("https://evil.example/track.png");
expect(parsed.isImage).toBe(false);
expect(parsed.imageUrl).toBeUndefined();
const html = renderToString(
<BotAvatar color="https://evil.example/track.png" identity="maya" size={32} />,
);
expect(html).not.toContain("<img");
expect(html).not.toContain("evil.example");
});
it("honors reduced-motion for the working mascot pulse class", () => {
const html = renderToString(
<BotAvatar color="#8B5CF6" identity="maya" size={32} status="running" />,
);
expect(html).toContain("animate-pulse");
expect(html).toContain("motion-reduce:animate-none");
});
it("exposes shape picker name and pressed state", () => {
const html = renderToString(
<GrokShapePreview shapeIndex={0} color="#8B5CF6" selected onClick={() => undefined} />,
);
expect(html).toContain('aria-label="EvOo"');
expect(html).toContain('aria-pressed="true"');
expect(html).toContain("focus-visible:ring-2");
});
});