nexus/ui/src/components/ChatMessageIdentityBar.test.tsx
Nexus Dev cf73f3481c feat(22-02): ChatMessageIdentityBar, ChatStreamingCursor, and extended ChatMessage
- Create ChatMessageIdentityBar with agent icon, name, timestamp, and streaming dot
- Create ChatStreamingCursor with animate-cursor-blink and aria-hidden
- Extend ChatMessage with agentName, agentIcon, agentRole, timestamp, isStreaming props
- Wrap assistant messages in group div for hover actions (Plan 03)
- Create agent-role-colors.ts with 11 distinct role colors (light + dark variants)
- Create ChatMarkdownMessage prerequisite from phase-21 base
- All 4 ChatMessageIdentityBar tests pass
2026-04-04 03:55:47 +00:00

66 lines
2 KiB
TypeScript

// @vitest-environment jsdom
import { act } from "react";
import { createRoot } from "react-dom/client";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { ChatMessageIdentityBar } from "./ChatMessageIdentityBar";
// Tell React this environment uses act() for event flushing.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true;
describe("ChatMessageIdentityBar", () => {
let container: HTMLDivElement;
let root: ReturnType<typeof createRoot> | null = null;
beforeEach(() => {
container = document.createElement("div");
document.body.appendChild(container);
root = null;
});
afterEach(() => {
if (root) {
act(() => {
root!.unmount();
});
root = null;
}
if (container.parentNode) {
container.remove();
}
});
function render(props: Parameters<typeof ChatMessageIdentityBar>[0]) {
root = createRoot(container);
act(() => {
root!.render(<ChatMessageIdentityBar {...props} />);
});
return container;
}
it("renders agent name in semibold text", () => {
render({ agentName: "PM Agent" });
const nameEl = container.querySelector(".font-semibold");
expect(nameEl).toBeDefined();
expect(nameEl?.textContent).toBe("PM Agent");
});
it("renders timestamp when provided", () => {
render({ agentName: "Test", timestamp: "2026-01-01T12:30:00Z" });
const allText = container.textContent ?? "";
expect(allText).toContain("12:30");
});
it("applies role-specific color class", () => {
render({ agentName: "PM", agentRole: "pm" });
const nameEl = container.querySelector(".font-semibold");
expect(nameEl?.className).toContain("text-blue-600");
expect(nameEl?.className).toContain("dark:text-blue-400");
});
it("shows streaming indicator dot when isStreaming", () => {
render({ agentName: "Test", isStreaming: true });
const dot = container.querySelector(".animate-pulse");
expect(dot).not.toBeNull();
});
});