Z ZPI 文档 v0.3.8
介绍页

源码

tests/desktop/pane-isolation.spec.ts

typescript147 行查看原文
import { once } from "node:events";
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
import { createServer } from "node:http";
import type { AddressInfo } from "node:net";
import { tmpdir } from "node:os";
import { join } from "node:path";
import type { ElectronApplication } from "@playwright/test";
import { chunk, done, fakeServer, send } from "../fake-server.ts";
import { runCleanup } from "../helpers/cleanup.ts";
import { launchDesktop } from "../helpers/desktop.ts";
import { expect, test } from "../helpers/test.ts";

test("task switching isolates files, terminals and browser guests and restores active/collapsed state", async () => {
  const dir = await mkdtemp(join(tmpdir(), "zpi-pane-isolation-"));
  const project = join(dir, "project");
  await mkdir(project);
  const file = join(project, "shared.txt");
  await writeFile(file, "same file, independent previews");
  const site = createServer((request, response) => {
    response.setHeader("Content-Type", "text/html");
    response.end(`<title>Task browser</title><p>${request.url}</p><input id="draft">`);
  });
  site.listen(0, "127.0.0.1");
  await once(site, "listening");
  const url = `http://127.0.0.1:${(site.address() as AddressInfo).port}`;
  const server = await fakeServer((_, response) => {
    send(response, chunk({ content: `[shared.txt](<${file}>)\n\n[Task browser](${url}/)` }));
    done(response);
  });
  let app: ElectronApplication | undefined;
  try {
    app = await launchDesktop({ dir, project, url: server.url });
    const page = await app.firstWindow();
    const errors: string[] = [];
    page.on("pageerror", (error) => errors.push(error.message));
    await page.getByRole("button", { name: "添加项目", exact: true }).first().click();
    // 等项目选择完成后再发送,避免添加项目的迟到结果把新任务切回草稿。
    await expect(page.getByRole("button", { name: "选择项目", exact: true })).toContainText("project");
    const editor = page.getByLabel("消息", { exact: true });
    await editor.fill("task A");
    await editor.press("Enter");
    await page.locator(".session-name").filter({ hasText: "task A" }).click();
    await expect(page.getByTestId("run")).toHaveAttribute("data-status", "completed");
    await page.locator(".answer").getByRole("button", { name: "shared.txt", exact: true }).click();
    await page.getByRole("button", { name: "文件预览菜单", exact: true }).click();
    await page.getByRole("menuitemcheckbox", { name: "自动换行", exact: true }).click();
    await page.getByRole("button", { name: "新增侧栏标签", exact: true }).click();
    await page
      .locator(".pane-empty-launcher:visible")
      .getByRole("button", { name: "终端", exact: true })
      .click();
    const terminal = page.locator(".right-pane .xterm-helper-textarea");
    await terminal.pressSequentially("export TASK_A_VALUE=retained; printf 'TERMINAL_A_OK\\n'", { delay: 1 });
    await terminal.press("Enter");
    await expect(page.locator(".xterm-rows")).toContainText("TERMINAL_A_OK");
    await page.getByRole("button", { name: "新增侧栏标签", exact: true }).click();
    await page
      .locator(".pane-empty-launcher:visible")
      .getByRole("button", { name: "审查", exact: true })
      .click();
    await page.locator(".answer").getByRole("button", { name: "Task browser", exact: true }).click();
    await expect(page.getByRole("tab", { name: "Task browser", exact: true })).toBeVisible();
    const native = () =>
      page
        .locator(".browser-webview:visible")
        .evaluateAll((views) => views.map((view) => (view as Electron.WebviewTag).getWebContentsId()));
    await expect.poll(native).toHaveLength(1);
    const guestId = (await native())?.[0];
    await app.evaluate(async ({ webContents }, id) => {
      await webContents
        .fromId(id ?? -1)
        ?.executeJavaScript("document.querySelector('#draft').value = 'A browser draft'");
    }, guestId);
    await page.getByLabel("收起右侧栏", { exact: true }).click();
    const taskA = await page.evaluate(() => localStorage.getItem("zpi.selectedSession"));
    await page.getByLabel("新建任务", { exact: true }).first().click();
    await expect.poll(() => page.evaluate(() => localStorage.getItem("zpi.selectedSession"))).not.toBe(taskA);
    await expect(page.locator(".draft-greeting")).toBeVisible();
    await editor.fill("task B");
    await editor.press("Enter");
    await page.locator(".session-name").filter({ hasText: "task B" }).click();
    await expect(page.getByTestId("run")).toHaveAttribute("data-status", "completed");
    await expect(page.locator(".right-pane")).toBeHidden();
    await expect.poll(native).toEqual([]);
    await page.getByLabel("展开右侧栏", { exact: true }).click();
    await expect(page.locator(".pane-empty-launcher")).toBeVisible();
    await expect(page.getByRole("tablist", { name: "侧栏标签" }).getByRole("tab")).toHaveText("新标签页");
    await page.locator(".answer").getByRole("button", { name: "shared.txt", exact: true }).click();
    await expect(page.getByRole("tablist", { name: "侧栏标签" }).getByRole("tab")).toHaveCount(1);
    await page.getByRole("button", { name: "文件预览菜单", exact: true }).click();
    await expect(page.getByRole("menuitemcheckbox", { name: "自动换行", exact: true })).toHaveAttribute(
      "aria-checked",
      "false",
    );
    await page.keyboard.press("Escape");
    await page.locator(".session-name").filter({ hasText: "task A" }).click();
    await expect(page.locator(".right-pane")).toBeHidden();
    await expect.poll(native).toEqual([]);
    await page.getByLabel("展开右侧栏", { exact: true }).click();
    await expect(page.getByRole("tablist", { name: "侧栏标签" }).getByRole("tab")).toHaveCount(4);
    await expect(page.getByRole("tab", { name: "Task browser", exact: true })).toHaveAttribute(
      "aria-selected",
      "true",
    );
    await expect.poll(native).toEqual([guestId]);
    expect(
      await app.evaluate(
        async ({ webContents }, id) =>
          webContents.fromId(id ?? -1)?.executeJavaScript("document.querySelector('#draft').value"),
        guestId,
      ),
    ).toBe("A browser draft");
    await page.getByRole("tab", { name: "shared.txt", exact: true }).click();
    await page.getByRole("button", { name: "文件预览菜单", exact: true }).click();
    await expect(page.getByRole("menuitemcheckbox", { name: "自动换行", exact: true })).toHaveAttribute(
      "aria-checked",
      "true",
    );
    await page.keyboard.press("Escape");
    await page.getByRole("tab", { name: "project", exact: true }).click();
    await terminal.pressSequentially("printf 'RESTORED_%s\\n' \"$TASK_A_VALUE\"", { delay: 1 });
    await terminal.press("Enter");
    await expect(page.locator(".xterm-rows")).toContainText("RESTORED_retained");
    await page.locator(".session-name").filter({ hasText: "task B" }).click();
    await expect(page.getByRole("tab", { name: "shared.txt", exact: true })).toHaveAttribute(
      "aria-selected",
      "true",
    );
    await page.getByLabel("关闭标签 shared.txt", { exact: true }).click();
    await expect(page.locator(".right-pane")).toBeHidden();
    await page.locator(".session-name").filter({ hasText: "task A" }).click();
    await expect(page.getByRole("tablist", { name: "侧栏标签" }).getByRole("tab")).toHaveCount(4);
    await expect(page.getByRole("tab", { name: "project", exact: true })).toHaveAttribute(
      "aria-selected",
      "true",
    );
    expect(errors).toEqual([]);
  } finally {
    await runCleanup([
      () => rm(dir, { recursive: true, force: true }),
      () => new Promise<void>((resolve) => site.close(() => resolve())),
      () => server.close(),
      () => app?.close(),
    ]);
  }
});