Z ZPI 文档 v0.3.8
介绍页

源码

tests/desktop/process-summary-layout.spec.ts

typescript148 行查看原文
import { mkdir, mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { emptyAssistant, type ToolCall } from "@zpi-dev/ai";
import { SessionManager } from "@zpi-dev/coding-agent";
import { HistoryIndex } from "../../packages/desktop/src/main/history-index.ts";
import { StateStore } from "../../packages/desktop/src/main/state-store.ts";
import { fakeModel } from "../fake-server.ts";
import { runCleanup } from "../helpers/cleanup.ts";
import { launchDesktop } from "../helpers/desktop.ts";
import { testStorageDirectory } from "../helpers/storage.ts";
import { expect, test } from "../helpers/test.ts";
import { seedHistory } from "../history-fixture.ts";

test("process summaries retain ZCode typography and single-line geometry when the right pane opens", async () => {
  const dir = await mkdtemp(join(tmpdir(), "zpi-process-summary-layout-"));
  const summary = "Count lines of code in snake.html and report the function statistics";
  const target = "agent_adfa8c3b-47e3-4296-a36a-95be27217598";
  const taskId = "exec_85d164be-c9fb-40b4-9806-499e0c708043";
  const command = 'lsof -nP -iTCP:8080 -sTCP:LISTEN 2>/dev/null || echo "port 8080 free"';
  const cwd = join(dir, "workspace");
  await mkdir(cwd);
  // 旧夹具直接创建根目录 JSONL,会被当前存储拒绝;使用桌面实际读取的 generation 与索引。
  const { file } = seedHistory(dir, cwd, 0);
  const state = new StateStore(join(testStorageDirectory(dir), "state.sqlite"));
  const index = new HistoryIndex(file, state.db);
  await index.load();
  const manager = SessionManager.fromStore(index);
  manager.appendCustomEntry("zpi.run", {
    phase: "start",
    runId: "layout",
    text: "检查过程摘要",
    startedAt: Date.now() - 6000,
    modelLabel: "Fake",
  });
  manager.appendMessage({ role: "user", content: "检查过程摘要", timestamp: Date.now() });
  const tools: ToolCall[] = [
    {
      type: "toolCall",
      id: "message",
      name: "SendMessage",
      arguments: { summary, to: target, message: "完整消息内容" },
    },
    { type: "toolCall", id: "output", name: "TaskOutput", arguments: { task_id: taskId } },
    { type: "toolCall", id: "stop", name: "TaskStop", arguments: { task_id: taskId } },
    { type: "toolCall", id: "bash", name: "bash", arguments: { command } },
  ];
  const assistant = emptyAssistant(fakeModel("http://127.0.0.1:1/v1"));
  manager.appendMessage({
    ...assistant,
    content: [{ type: "thinking", thinking: "先检查摘要的布局。" }, ...tools],
    stopReason: "toolUse",
  });
  for (const tool of tools) {
    manager.appendMessage({
      role: "toolResult",
      toolCallId: tool.id,
      toolName: tool.name,
      content: [
        {
          type: "text",
          text:
            tool.name === "TaskOutput"
              ? `<retrieval_status>success</retrieval_status>\n\n<task_id>${taskId}</task_id>\n\n<task_type>local_bash</task_type>\n\n<status>aborted</status>\n\n<output>\n任务输出正文\n</output>`
              : tool.name === "TaskStop"
                ? JSON.stringify({ command, message: "任务已停止" })
                : "完成",
        },
      ],
      isError: false,
      timestamp: Date.now(),
    });
  }
  manager.appendMessage({ ...assistant, content: [{ type: "text", text: "检查完成。" }] });
  manager.appendCustomEntry("zpi.run", {
    phase: "end",
    runId: "layout",
    status: "completed",
    endedAt: Date.now(),
  });
  state.close();
  let app: Awaited<ReturnType<typeof launchDesktop>>;
  try {
    app = await launchDesktop({ dir, url: "http://127.0.0.1:1/v1" });
    const page = await app.firstWindow();
    await app.evaluate(({ BrowserWindow }) => BrowserWindow.getAllWindows()[0].setSize(1800, 900));
    const main = page.locator("main");
    await page.locator(`.session-row[data-session-id="${manager.getSessionId()}"]`).click();
    const rows = main.locator(".block-toggle, .tool-summary-row");
    const measure = () =>
      rows.evaluateAll((elements) =>
        elements.map((element) => ({
          fontSize: getComputedStyle(element).fontSize,
          height: element.getBoundingClientRect().height,
          iconWidth: element.querySelector("svg")?.getBoundingClientRect().width,
          labelWeight: getComputedStyle(element.querySelector("strong") as Element).fontWeight,
          overflow: element.scrollWidth - element.clientWidth,
        })),
      );
    for (const fontSize of [14, 18]) {
      for (const theme of ["light", "dark"] as const) {
        await page.evaluate((preferences) => window.zpi.updatePreferences(preferences), { theme, fontSize });
        await page.reload();
        await expect(page.locator("html")).toHaveAttribute("data-theme", theme);
        await main.getByTestId("progress").click();
        await expect(rows).toHaveCount(5);
        await expect(rows.first()).toHaveCSS("font-size", `${fontSize}px`);
        const wide = await measure();
        await page.getByLabel("展开右侧栏", { exact: true }).click();
        await expect(page.locator(".right-pane")).toBeVisible();
        await expect(page.locator(".right-pane")).not.toHaveAttribute("data-animating", "true");
        for (const width of [1200, 900]) {
          await app.evaluate(
            ({ BrowserWindow }, width) => BrowserWindow.getAllWindows()[0].setSize(width, 900),
            width,
          );
          await expect.poll(() => page.evaluate(() => innerWidth)).toBe(width);
          const narrow = await measure();
          expect(narrow).toEqual(wide);
          expect(
            narrow.every((row) => row.iconWidth === 16 && row.labelWeight === "500" && row.overflow <= 1),
          ).toBe(true);
          await page.screenshot({
            path: test.info().outputPath(`process-${fontSize}-${theme}-${width}.png`),
          });
        }
        const message = main.locator('[data-tool-name="SendMessage"]');
        await message.getByRole("button", { name: "展开工具详情" }).click();
        await expect(message.locator("dd")).toHaveText([target, summary, "完整消息内容"]);
        await message.getByRole("button", { name: "收起工具详情" }).click();
        for (const name of ["TaskOutput", "TaskStop"]) {
          const tool = main.locator(`[data-tool-name="${name}"]`);
          await tool.getByRole("button", { name: "展开工具详情" }).click();
          await expect(tool.locator(".subagent-task-output")).toContainText(
            name === "TaskOutput" ? "任务输出正文" : command,
          );
          await tool.getByRole("button", { name: "收起工具详情" }).click();
        }
        await page.getByLabel("收起右侧栏", { exact: true }).click();
        await expect(page.locator(".right-pane")).toBeHidden();
        await app.evaluate(({ BrowserWindow }) => BrowserWindow.getAllWindows()[0].setSize(1800, 900));
      }
    }
  } finally {
    await runCleanup([() => rm(dir, { recursive: true, force: true }), () => app?.close()]);
  }
});