mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:20:43 +00:00
test(ui): split chat avatar render coverage
This commit is contained in:
87
ui/src/ui/chat/chat-avatar.test.ts
Normal file
87
ui/src/ui/chat/chat-avatar.test.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
/* @vitest-environment jsdom */
|
||||
|
||||
import { render } from "lit";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { renderChatAvatar } from "./chat-avatar.ts";
|
||||
|
||||
vi.mock("../views/agents-utils.ts", () => ({
|
||||
isRenderableControlUiAvatarUrl: (value: string) =>
|
||||
/^data:image\//i.test(value) || (value.startsWith("/") && !value.startsWith("//")),
|
||||
assistantAvatarFallbackUrl: () => "apple-touch-icon.png",
|
||||
resolveAssistantTextAvatar: (value: string | null | undefined) => {
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
return value.length <= 3 ? value : null;
|
||||
},
|
||||
resolveChatAvatarRenderUrl: (
|
||||
candidate: string | null | undefined,
|
||||
agent: { identity?: { avatar?: string; avatarUrl?: string } },
|
||||
) => {
|
||||
const isRenderableControlUiAvatarUrl = (value: string) =>
|
||||
/^data:image\//i.test(value) || (value.startsWith("/") && !value.startsWith("//"));
|
||||
if (typeof candidate === "string" && candidate.startsWith("blob:")) {
|
||||
return candidate;
|
||||
}
|
||||
for (const value of [candidate, agent.identity?.avatarUrl, agent.identity?.avatar]) {
|
||||
if (typeof value === "string" && isRenderableControlUiAvatarUrl(value)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
},
|
||||
}));
|
||||
|
||||
function renderAvatar(params: Parameters<typeof renderChatAvatar>) {
|
||||
const container = document.createElement("div");
|
||||
render(renderChatAvatar(...params), container);
|
||||
return container.querySelector<HTMLElement>(".chat-avatar");
|
||||
}
|
||||
|
||||
describe("renderChatAvatar", () => {
|
||||
it("uses the assistant fallback when no assistant avatar is configured", () => {
|
||||
const avatar = renderAvatar(["assistant"]);
|
||||
|
||||
expect(avatar).not.toBeNull();
|
||||
expect(avatar?.getAttribute("src")).toBe("apple-touch-icon.png");
|
||||
});
|
||||
|
||||
it("renders assistant fallback, blob image, and text avatars", () => {
|
||||
const remoteAvatar = renderAvatar([
|
||||
"assistant",
|
||||
{ avatar: "https://example.com/avatar.png", name: "Val" },
|
||||
]);
|
||||
expect(remoteAvatar?.getAttribute("src")).toBe("apple-touch-icon.png");
|
||||
|
||||
const blobAvatar = renderAvatar(["assistant", { avatar: "blob:managed-image", name: "Val" }]);
|
||||
expect(blobAvatar?.tagName).toBe("IMG");
|
||||
expect(blobAvatar?.getAttribute("src")).toBe("blob:managed-image");
|
||||
|
||||
const textAvatar = renderAvatar(["assistant", { avatar: "VC", name: "Val" }]);
|
||||
expect(textAvatar?.tagName).toBe("DIV");
|
||||
expect(textAvatar?.textContent).toContain("VC");
|
||||
expect(textAvatar?.getAttribute("aria-label")).toBe("Val");
|
||||
});
|
||||
|
||||
it("uses the assistant fallback while authenticated avatar routes are loading", () => {
|
||||
const avatar = renderAvatar([
|
||||
"assistant",
|
||||
{ avatar: "/avatar/main", name: "OpenClaw" },
|
||||
undefined,
|
||||
"",
|
||||
"session-token",
|
||||
]);
|
||||
|
||||
expect(avatar?.getAttribute("src")).toBe("apple-touch-icon.png");
|
||||
});
|
||||
|
||||
it("renders local user image and text avatars", () => {
|
||||
const imageAvatar = renderAvatar(["user", undefined, { name: "Buns", avatar: "/avatar/user" }]);
|
||||
expect(imageAvatar?.getAttribute("src")).toBe("/avatar/user");
|
||||
expect(imageAvatar?.getAttribute("alt")).toBe("Buns");
|
||||
|
||||
const textAvatar = renderAvatar(["user", undefined, { name: "Buns", avatar: "AB" }]);
|
||||
expect(textAvatar?.tagName).toBe("DIV");
|
||||
expect(textAvatar?.textContent).toContain("AB");
|
||||
});
|
||||
});
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
createSessionsListResult,
|
||||
DEFAULT_CHAT_MODEL_CATALOG,
|
||||
} from "../chat-model.test-helpers.ts";
|
||||
import { renderChatAvatar } from "../chat/chat-avatar.ts";
|
||||
import { renderChatQueue } from "../chat/chat-queue.ts";
|
||||
import { buildRawSidebarContent } from "../chat/chat-sidebar-raw.ts";
|
||||
import { renderWelcomeState } from "../chat/chat-welcome.ts";
|
||||
@@ -80,12 +79,6 @@ vi.mock("./agents-utils.ts", () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
function renderAvatar(params: Parameters<typeof renderChatAvatar>) {
|
||||
const container = document.createElement("div");
|
||||
render(renderChatAvatar(...params), container);
|
||||
return container.querySelector<HTMLElement>(".chat-avatar");
|
||||
}
|
||||
|
||||
function renderQueue(params: {
|
||||
queue: ChatQueueItem[];
|
||||
canAbort?: boolean;
|
||||
@@ -264,54 +257,6 @@ describe("chat queue", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("renderChatAvatar", () => {
|
||||
it("uses the assistant fallback when no assistant avatar is configured", () => {
|
||||
const avatar = renderAvatar(["assistant"]);
|
||||
|
||||
expect(avatar).not.toBeNull();
|
||||
expect(avatar?.getAttribute("src")).toBe("apple-touch-icon.png");
|
||||
});
|
||||
|
||||
it("renders assistant fallback, blob image, and text avatars", () => {
|
||||
const remoteAvatar = renderAvatar([
|
||||
"assistant",
|
||||
{ avatar: "https://example.com/avatar.png", name: "Val" },
|
||||
]);
|
||||
expect(remoteAvatar?.getAttribute("src")).toBe("apple-touch-icon.png");
|
||||
|
||||
const blobAvatar = renderAvatar(["assistant", { avatar: "blob:managed-image", name: "Val" }]);
|
||||
expect(blobAvatar?.tagName).toBe("IMG");
|
||||
expect(blobAvatar?.getAttribute("src")).toBe("blob:managed-image");
|
||||
|
||||
const textAvatar = renderAvatar(["assistant", { avatar: "VC", name: "Val" }]);
|
||||
expect(textAvatar?.tagName).toBe("DIV");
|
||||
expect(textAvatar?.textContent).toContain("VC");
|
||||
expect(textAvatar?.getAttribute("aria-label")).toBe("Val");
|
||||
});
|
||||
|
||||
it("uses the assistant fallback while authenticated avatar routes are loading", () => {
|
||||
const avatar = renderAvatar([
|
||||
"assistant",
|
||||
{ avatar: "/avatar/main", name: "OpenClaw" },
|
||||
undefined,
|
||||
"",
|
||||
"session-token",
|
||||
]);
|
||||
|
||||
expect(avatar?.getAttribute("src")).toBe("apple-touch-icon.png");
|
||||
});
|
||||
|
||||
it("renders local user image and text avatars", () => {
|
||||
const imageAvatar = renderAvatar(["user", undefined, { name: "Buns", avatar: "/avatar/user" }]);
|
||||
expect(imageAvatar?.getAttribute("src")).toBe("/avatar/user");
|
||||
expect(imageAvatar?.getAttribute("alt")).toBe("Buns");
|
||||
|
||||
const textAvatar = renderAvatar(["user", undefined, { name: "Buns", avatar: "AB" }]);
|
||||
expect(textAvatar?.tagName).toBe("DIV");
|
||||
expect(textAvatar?.textContent).toContain("AB");
|
||||
});
|
||||
});
|
||||
|
||||
describe("chat sidebar raw content", () => {
|
||||
it("keeps markdown raw text toggles idempotent", () => {
|
||||
const rawMarkdown = "```ts\nconst value = 1;\n```";
|
||||
|
||||
Reference in New Issue
Block a user