Files
pikasTech-HWLAB/web/hwlab-cloud-web/src/components/workbench/WorkbenchMessageCard.test.ts
T

180 lines
6.3 KiB
TypeScript

// @vitest-environment jsdom
import { mount } from "@vue/test-utils";
import { describe, expect, it } from "vitest";
import WorkbenchMessageCard from "./WorkbenchMessageCard.vue";
function componentStubs() {
return {
TraceTimeline: { template: "<div />" },
LoadingState: { template: "<div />" },
MessageMarkdown: { template: "<div />" },
ApiErrorDiagnostic: { template: "<div />" },
StatusBadge: { template: "<div />" }
};
}
describe("WorkbenchMessageCard startup visibility", () => {
it("shows one startup label before the first Kafka event", () => {
const wrapper = mount(WorkbenchMessageCard, {
props: {
detailEnabled: false,
message: {
id: "msg_starting_agent",
messageId: "msg_starting_agent",
role: "agent",
title: "Code Agent",
text: "",
status: "running",
traceId: "trc_starting",
sessionId: "ses_starting",
createdAt: "2026-07-20T10:00:00.000Z",
}
},
global: {
stubs: {
...componentStubs(),
LoadingState: { props: ["label"], template: "<div class='loading-state'>{{ label }}</div>" },
}
}
});
expect(wrapper.text()).toContain("启动中...");
expect(wrapper.text()).not.toContain("思考中...");
expect(wrapper.findAll(".loading-state")).toHaveLength(1);
wrapper.unmount();
});
});
describe("WorkbenchMessageCard metadata layout", () => {
it("renders recent activity at the footer left and elapsed time at the footer right", () => {
const now = Date.now();
const wrapper = mount(WorkbenchMessageCard, {
props: {
detailEnabled: false,
message: {
id: "msg_timing_layout",
messageId: "msg_timing_layout",
role: "agent",
title: "Code Agent",
text: "",
status: "running",
traceId: "trc_timing_layout",
sessionId: "ses_timing_layout",
createdAt: new Date(now - 10_000).toISOString(),
timing: {
startedAt: new Date(now - 10_000).toISOString(),
lastEventAt: new Date(now - 4_000).toISOString()
}
}
},
global: { stubs: componentStubs() }
});
const footer = wrapper.get(".message-meta-footer");
expect(wrapper.find("article > header").exists()).toBe(false);
expect(footer.get(".message-activity-meta").text()).toMatch(/^最近更新:\d+ 秒前$/u);
expect(footer.get(".message-duration-meta").text()).toMatch(/^耗时 \d+ 秒$/u);
expect(footer.get(".message-footer-timing").element.firstElementChild).toBe(footer.get(".message-activity-meta").element);
expect(footer.get(".message-footer-timing").element.lastElementChild).toBe(footer.get(".message-duration-meta").element);
wrapper.unmount();
});
});
describe("WorkbenchMessageCard semantic retry visibility", () => {
it("shows Git mirror clone progress as runtime preparation", () => {
const wrapper = mount(WorkbenchMessageCard, {
props: {
detailEnabled: false,
message: {
id: "msg_fetch_agent",
messageId: "msg_fetch_agent",
role: "agent",
title: "Code Agent",
text: "",
status: "running",
traceId: "trc_fetch_banner",
sessionId: "ses_fetch_banner",
createdAt: "2026-07-20T10:00:00.000Z",
runnerTrace: {
traceId: "trc_fetch_banner",
eventSource: "hwlab-kafka-sse",
status: "running",
eventCount: 1,
events: [{
sourceEventId: "evt_fetch_banner",
sourceSeq: 5,
type: "backend",
status: "running",
failureDomain: "infrastructure",
failureCode: "git-mirror-fetch-in-progress",
summary: "正在从 Git mirror 获取 Runner 源码",
retryPhase: "initialAttempt",
retryAttempt: 1,
retryMaxAttempts: 3,
createdAt: new Date().toISOString()
}]
}
}
},
global: { stubs: componentStubs() }
});
expect(wrapper.text()).toContain("运行环境准备:正在从 Git mirror 获取 Runner 源码");
expect(wrapper.text()).toContain("第 1/3 次源码获取进行中");
expect(wrapper.text()).not.toContain("基础设施故障");
expect(wrapper.get(".semantic-failure-banner").attributes("data-emphasis")).toBe("quiet");
expect(wrapper.find(".semantic-failure-banner code").exists()).toBe(false);
wrapper.unmount();
});
it("shows an infrastructure retry banner without opening Trace details", () => {
const wrapper = mount(WorkbenchMessageCard, {
props: {
detailEnabled: false,
message: {
id: "msg_retry_agent",
messageId: "msg_retry_agent",
role: "agent",
title: "Code Agent",
text: "",
status: "running",
traceId: "trc_retry_banner",
sessionId: "ses_retry_banner",
createdAt: "2026-07-20T10:00:00.000Z",
runnerTrace: {
traceId: "trc_retry_banner",
eventSource: "hwlab-kafka-sse",
status: "running",
eventCount: 1,
events: [{
sourceEventId: "evt_retry_banner",
sourceSeq: 5,
type: "backend",
status: "retrying",
failureDomain: "infrastructure",
failureCode: "runner-image-registry-unreachable",
summary: "Runner 镜像仓库暂时不可达",
retryPhase: "retryScheduled",
retryAttempt: 2,
retryMaxAttempts: 5,
nextRetryAt: new Date(Date.now() + 12_000).toISOString(),
createdAt: new Date().toISOString()
}]
}
}
},
global: {
stubs: componentStubs()
}
});
const banner = wrapper.get(".semantic-failure-banner");
expect(banner.attributes("data-emphasis")).toBe("alert");
expect(banner.text()).toContain("基础设施故障:Runner 镜像仓库暂时不可达");
expect(banner.text()).toContain("第 2/5 次重试已安排");
expect(banner.text()).toMatch(/预计 \d+ 秒后开始/u);
expect(banner.text()).toContain("runner-image-registry-unreachable");
wrapper.unmount();
});
});