fix(ui): harden avatar fallback regressions
This commit is contained in:
@@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
agentLogoUrl,
|
||||
resolveConfiguredCronModelSuggestions,
|
||||
resolveAgentAvatarUrl,
|
||||
resolveEffectiveModelFallbacks,
|
||||
sortLocaleStrings,
|
||||
} from "./agents-utils.ts";
|
||||
@@ -110,3 +111,18 @@ describe("agentLogoUrl", () => {
|
||||
expect(agentLogoUrl("")).toBe("favicon.svg");
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveAgentAvatarUrl", () => {
|
||||
it("prefers a runtime avatar URL over non-URL identity avatars", () => {
|
||||
expect(
|
||||
resolveAgentAvatarUrl({ identity: { avatar: "A", avatarUrl: "/avatar/main" } }, {
|
||||
avatar: "A",
|
||||
} as { avatar: string }),
|
||||
).toBe("/avatar/main");
|
||||
});
|
||||
|
||||
it("returns null for initials or emoji avatar values without a URL", () => {
|
||||
expect(resolveAgentAvatarUrl({ identity: { avatar: "A" } })).toBeNull();
|
||||
expect(resolveAgentAvatarUrl({ identity: { avatar: "🦞" } })).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -200,15 +200,18 @@ export function resolveAgentAvatarUrl(
|
||||
agent: { identity?: { avatar?: string; avatarUrl?: string } },
|
||||
agentIdentity?: AgentIdentityResult | null,
|
||||
): string | null {
|
||||
const url =
|
||||
agentIdentity?.avatar?.trim() ??
|
||||
agent.identity?.avatarUrl?.trim() ??
|
||||
agent.identity?.avatar?.trim();
|
||||
if (!url) {
|
||||
return null;
|
||||
const candidates = [
|
||||
agentIdentity?.avatar?.trim(),
|
||||
agent.identity?.avatarUrl?.trim(),
|
||||
agent.identity?.avatar?.trim(),
|
||||
];
|
||||
for (const candidate of candidates) {
|
||||
if (!candidate) {
|
||||
continue;
|
||||
}
|
||||
if (AVATAR_URL_RE.test(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
if (AVATAR_URL_RE.test(url)) {
|
||||
return url;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -96,6 +96,55 @@ describe("chat view", () => {
|
||||
expect(logoImage?.getAttribute("src")).toBe("favicon.svg");
|
||||
});
|
||||
|
||||
it("keeps the welcome logo fallback under the mounted base path", () => {
|
||||
const container = document.createElement("div");
|
||||
render(
|
||||
renderChat(
|
||||
createProps({
|
||||
assistantName: "Assistant",
|
||||
assistantAvatar: "A",
|
||||
assistantAvatarUrl: null,
|
||||
basePath: "/openclaw/",
|
||||
}),
|
||||
),
|
||||
container,
|
||||
);
|
||||
|
||||
const logoImage = container.querySelector<HTMLImageElement>(
|
||||
".agent-chat__welcome .agent-chat__avatar--logo img",
|
||||
);
|
||||
expect(logoImage).not.toBeNull();
|
||||
expect(logoImage?.getAttribute("src")).toBe("/openclaw/favicon.svg");
|
||||
});
|
||||
|
||||
it("keeps grouped assistant avatar fallbacks under the mounted base path", () => {
|
||||
const container = document.createElement("div");
|
||||
render(
|
||||
renderChat(
|
||||
createProps({
|
||||
assistantName: "Assistant",
|
||||
assistantAvatar: "A",
|
||||
assistantAvatarUrl: null,
|
||||
basePath: "/openclaw/",
|
||||
messages: [
|
||||
{
|
||||
role: "assistant",
|
||||
content: "hello",
|
||||
timestamp: 1000,
|
||||
},
|
||||
],
|
||||
}),
|
||||
),
|
||||
container,
|
||||
);
|
||||
|
||||
const groupedLogo = container.querySelector<HTMLImageElement>(
|
||||
".chat-group.assistant .chat-avatar--logo",
|
||||
);
|
||||
expect(groupedLogo).not.toBeNull();
|
||||
expect(groupedLogo?.getAttribute("src")).toBe("/openclaw/favicon.svg");
|
||||
});
|
||||
|
||||
it("renders compacting indicator as a badge", () => {
|
||||
const container = document.createElement("div");
|
||||
render(
|
||||
|
||||
@@ -82,6 +82,7 @@ export default defineConfig({
|
||||
"src/**/*.test.ts",
|
||||
"extensions/**/*.test.ts",
|
||||
"test/**/*.test.ts",
|
||||
"ui/src/ui/app-chat.test.ts",
|
||||
"ui/src/ui/views/agents-utils.test.ts",
|
||||
"ui/src/ui/views/chat.test.ts",
|
||||
"ui/src/ui/views/usage-render-details.test.ts",
|
||||
|
||||
Reference in New Issue
Block a user