mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
365 lines
11 KiB
TypeScript
365 lines
11 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
|
|
import { installMockBridge } from "../helpers/bridge";
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
await installMockBridge(page);
|
|
});
|
|
|
|
async function gotoApp(page: import("@playwright/test").Page) {
|
|
let lastError: unknown = null;
|
|
|
|
for (const attempt of [0, 1]) {
|
|
await page.goto("/", { waitUntil: "domcontentloaded" });
|
|
await waitForInvokeBridge(page);
|
|
|
|
try {
|
|
await expect(page.getByTestId("open-agents-view")).toBeVisible({
|
|
timeout: 10_000,
|
|
});
|
|
return;
|
|
} catch (error) {
|
|
lastError = error;
|
|
if (attempt === 1) {
|
|
throw error;
|
|
}
|
|
}
|
|
}
|
|
|
|
throw lastError;
|
|
}
|
|
|
|
async function getCatalogOrder(page: import("@playwright/test").Page) {
|
|
return page
|
|
.locator('[data-testid^="persona-catalog-card-target-"]')
|
|
.evaluateAll((elements) =>
|
|
elements.map((element) => element.getAttribute("data-testid") ?? ""),
|
|
);
|
|
}
|
|
|
|
async function waitForInvokeBridge(page: import("@playwright/test").Page) {
|
|
await page.waitForFunction(
|
|
() => {
|
|
const tauriWindow = window as Window & {
|
|
__SPROUT_E2E_INVOKE_MOCK_COMMAND__?: unknown;
|
|
__TAURI_INTERNALS__?: {
|
|
invoke?: unknown;
|
|
};
|
|
};
|
|
|
|
return (
|
|
typeof tauriWindow.__SPROUT_E2E_INVOKE_MOCK_COMMAND__ === "function" ||
|
|
typeof tauriWindow.__TAURI_INTERNALS__?.invoke === "function"
|
|
);
|
|
},
|
|
null,
|
|
{ timeout: 5_000 },
|
|
);
|
|
}
|
|
|
|
async function invokeTauri<T>(
|
|
page: import("@playwright/test").Page,
|
|
command: string,
|
|
payload?: Record<string, unknown>,
|
|
): Promise<T> {
|
|
await waitForInvokeBridge(page);
|
|
|
|
return page.evaluate(
|
|
async ({ command: targetCommand, payload: targetPayload }) => {
|
|
const tauriWindow = window as Window & {
|
|
__SPROUT_E2E_INVOKE_MOCK_COMMAND__?: (
|
|
command: string,
|
|
payload?: Record<string, unknown>,
|
|
) => Promise<unknown>;
|
|
__TAURI_INTERNALS__?: {
|
|
invoke?: (
|
|
command: string,
|
|
payload?: Record<string, unknown>,
|
|
) => Promise<unknown>;
|
|
};
|
|
};
|
|
|
|
const invoke =
|
|
tauriWindow.__SPROUT_E2E_INVOKE_MOCK_COMMAND__ ??
|
|
tauriWindow.__TAURI_INTERNALS__?.invoke;
|
|
if (!invoke) {
|
|
throw new Error("Mock invoke bridge is unavailable.");
|
|
}
|
|
|
|
return (await invoke(targetCommand, targetPayload)) as T;
|
|
},
|
|
{ command, payload },
|
|
);
|
|
}
|
|
|
|
async function invokeTauriExpectError(
|
|
page: import("@playwright/test").Page,
|
|
command: string,
|
|
payload?: Record<string, unknown>,
|
|
) {
|
|
await waitForInvokeBridge(page);
|
|
|
|
return page.evaluate(
|
|
async ({ command: targetCommand, payload: targetPayload }) => {
|
|
const tauriWindow = window as Window & {
|
|
__SPROUT_E2E_INVOKE_MOCK_COMMAND__?: (
|
|
command: string,
|
|
payload?: Record<string, unknown>,
|
|
) => Promise<unknown>;
|
|
__TAURI_INTERNALS__?: {
|
|
invoke?: (
|
|
command: string,
|
|
payload?: Record<string, unknown>,
|
|
) => Promise<unknown>;
|
|
};
|
|
};
|
|
|
|
const invoke =
|
|
tauriWindow.__SPROUT_E2E_INVOKE_MOCK_COMMAND__ ??
|
|
tauriWindow.__TAURI_INTERNALS__?.invoke;
|
|
if (!invoke) {
|
|
throw new Error("Mock invoke bridge is unavailable.");
|
|
}
|
|
|
|
try {
|
|
await invoke(targetCommand, targetPayload);
|
|
return null;
|
|
} catch (error) {
|
|
return error instanceof Error ? error.message : String(error);
|
|
}
|
|
},
|
|
{ command, payload },
|
|
);
|
|
}
|
|
|
|
test("built-in personas are chosen from the dialog and can be selected", async ({
|
|
page,
|
|
}) => {
|
|
await page.setViewportSize({ width: 1280, height: 420 });
|
|
await gotoApp(page);
|
|
await page.getByTestId("open-agents-view").click();
|
|
|
|
await expect(page.getByTestId("agents-library-personas")).toContainText(
|
|
"No agents yet",
|
|
);
|
|
await expect(page.getByTestId("agents-persona-catalog")).toHaveCount(0);
|
|
await page.getByTestId("open-persona-catalog").click();
|
|
await expect(page.getByTestId("persona-catalog-dialog")).toContainText(
|
|
"Reviewer",
|
|
);
|
|
await expect(page.getByTestId("persona-catalog-dialog-header")).toBeVisible();
|
|
await expect(
|
|
page.getByTestId("persona-catalog-dialog-scroll-area"),
|
|
).toBeVisible();
|
|
await expect(
|
|
page.getByTestId("persona-catalog-dialog-scroll-area"),
|
|
).toHaveCSS("overflow-y", "auto");
|
|
expect(
|
|
await page
|
|
.getByTestId("persona-catalog-dialog-scroll-area")
|
|
.evaluate(
|
|
(element) =>
|
|
element.scrollHeight > element.clientHeight &&
|
|
element.clientHeight > 0,
|
|
),
|
|
).toBe(true);
|
|
await expect(page.getByTestId("persona-catalog-dialog-footer")).toBeVisible();
|
|
await expect(page.getByRole("tooltip")).toHaveCount(0);
|
|
const initialCatalogOrder = await getCatalogOrder(page);
|
|
|
|
await page
|
|
.getByTestId("persona-catalog-card-target-builtin:reviewer")
|
|
.click();
|
|
await expect(
|
|
page.getByTestId("persona-catalog-feedback-notice"),
|
|
).toContainText("Selected Reviewer for My Agents.");
|
|
|
|
await expect(page.getByTestId("agents-library-personas")).toContainText(
|
|
"Reviewer",
|
|
);
|
|
await expect(
|
|
page.getByTestId("persona-catalog-card-target-builtin:reviewer"),
|
|
).toHaveAttribute("aria-pressed", "true");
|
|
await expect.poll(() => getCatalogOrder(page)).toEqual(initialCatalogOrder);
|
|
|
|
await page
|
|
.getByTestId("persona-catalog-card-target-builtin:reviewer")
|
|
.click();
|
|
await expect(
|
|
page.getByTestId("persona-catalog-feedback-notice"),
|
|
).toContainText("Deselected Reviewer from My Agents.");
|
|
await expect(page.getByTestId("agents-library-personas")).not.toContainText(
|
|
"Reviewer",
|
|
);
|
|
await expect.poll(() => getCatalogOrder(page)).toEqual(initialCatalogOrder);
|
|
});
|
|
|
|
test("persona catalog can reopen from the populated library header", async ({
|
|
page,
|
|
}) => {
|
|
await gotoApp(page);
|
|
await page.getByTestId("open-agents-view").click();
|
|
await page.getByTestId("open-persona-catalog").click();
|
|
|
|
await page
|
|
.getByTestId("persona-catalog-card-target-builtin:reviewer")
|
|
.click();
|
|
await expect(page.getByTestId("agents-library-personas")).toContainText(
|
|
"Reviewer",
|
|
);
|
|
|
|
await page.getByTestId("persona-catalog-dialog-done").click();
|
|
await page.getByTestId("open-persona-catalog").click();
|
|
|
|
await expect(page.getByTestId("persona-catalog-dialog")).toBeVisible();
|
|
await expect(
|
|
page.getByTestId("persona-catalog-card-target-builtin:reviewer"),
|
|
).toHaveAttribute("aria-pressed", "true");
|
|
});
|
|
|
|
test("persona catalog chooser order stays stable when selection changes", async ({
|
|
page,
|
|
}) => {
|
|
await gotoApp(page);
|
|
await page.getByTestId("open-agents-view").click();
|
|
await page.getByTestId("open-persona-catalog").click();
|
|
|
|
const before = await getCatalogOrder(page);
|
|
|
|
await page.getByTestId("persona-catalog-card-target-builtin:solo").click();
|
|
await expect(
|
|
page.getByTestId("persona-catalog-feedback-notice"),
|
|
).toContainText("Selected Solo for My Agents.");
|
|
|
|
expect(await getCatalogOrder(page)).toEqual(before);
|
|
});
|
|
|
|
test("catalog details sheet shows the full persona details", async ({
|
|
page,
|
|
}) => {
|
|
await gotoApp(page);
|
|
await page.getByTestId("open-agents-view").click();
|
|
await page.getByTestId("open-persona-catalog").click();
|
|
|
|
await page.getByTestId("persona-catalog-details-builtin:reviewer").click();
|
|
const detailSelectionTarget = page.getByTestId(
|
|
"persona-catalog-detail-selection-target-builtin:reviewer",
|
|
);
|
|
|
|
await expect(page.getByTestId("persona-catalog-details-sheet")).toContainText(
|
|
"Reviewer",
|
|
);
|
|
await expect(page.getByTestId("persona-catalog-details-sheet")).toContainText(
|
|
"You are Reviewer.",
|
|
);
|
|
await expect(
|
|
page.getByTestId("persona-catalog-detail-selection-title"),
|
|
).toHaveText("Available in Persona Catalog");
|
|
await expect(detailSelectionTarget).toHaveAttribute(
|
|
"aria-label",
|
|
"Select Reviewer in My Agents",
|
|
);
|
|
await expect(detailSelectionTarget).toHaveAttribute("aria-pressed", "false");
|
|
|
|
await detailSelectionTarget.click();
|
|
await expect(
|
|
page.getByTestId("persona-catalog-detail-selection-title"),
|
|
).toHaveText("Selected for My Agents");
|
|
await expect(detailSelectionTarget).toHaveAttribute(
|
|
"aria-label",
|
|
"Deselect Reviewer in My Agents",
|
|
);
|
|
await expect(detailSelectionTarget).toHaveAttribute("aria-pressed", "true");
|
|
await expect(page.getByTestId("agents-library-personas")).toContainText(
|
|
"Reviewer",
|
|
);
|
|
});
|
|
|
|
test("inactive built-ins cannot be used to create teams", async ({ page }) => {
|
|
await gotoApp(page);
|
|
|
|
const error = await invokeTauriExpectError(page, "create_team", {
|
|
input: {
|
|
name: "Reviewers",
|
|
personaIds: ["builtin:reviewer"],
|
|
},
|
|
});
|
|
|
|
expect(error).toBe(
|
|
"Reviewer is not in My Agents. Choose it from Persona Catalog first.",
|
|
);
|
|
});
|
|
|
|
test("built-in deselection failures show up in Persona Catalog", async ({
|
|
page,
|
|
}) => {
|
|
await gotoApp(page);
|
|
|
|
await page.getByTestId("open-agents-view").click();
|
|
await page.getByTestId("open-persona-catalog").click();
|
|
await page
|
|
.getByTestId("persona-catalog-card-target-builtin:reviewer")
|
|
.click();
|
|
|
|
await invokeTauri(page, "create_team", {
|
|
input: {
|
|
name: "Reviewers",
|
|
personaIds: ["builtin:reviewer"],
|
|
},
|
|
});
|
|
|
|
await page
|
|
.getByTestId("persona-catalog-card-target-builtin:reviewer")
|
|
.click();
|
|
|
|
await expect(
|
|
page.getByTestId("persona-catalog-feedback-error"),
|
|
).toContainText("Reviewer is still referenced by a team.");
|
|
});
|
|
|
|
test("channel quick add falls back to added personas when defaults are absent", async ({
|
|
page,
|
|
}) => {
|
|
await gotoApp(page);
|
|
await page.getByTestId("open-agents-view").click();
|
|
await page.getByTestId("open-persona-catalog").click();
|
|
await page
|
|
.getByTestId("persona-catalog-card-target-builtin:reviewer")
|
|
.click();
|
|
await page.getByTestId("persona-catalog-dialog-done").click();
|
|
|
|
await page.getByTestId("channel-random").click();
|
|
await expect(page.getByTestId("chat-title")).toHaveText("random");
|
|
await page.getByTestId("channel-add-bot-trigger").hover();
|
|
|
|
await expect(
|
|
page.getByRole("button", { name: "Add Reviewer" }),
|
|
).toBeVisible();
|
|
});
|
|
|
|
test("personas referenced by teams cannot be deleted", async ({ page }) => {
|
|
await gotoApp(page);
|
|
|
|
const persona = await invokeTauri<{ id: string }>(page, "create_persona", {
|
|
input: {
|
|
displayName: "Analyst",
|
|
systemPrompt: "You are Analyst.",
|
|
},
|
|
});
|
|
|
|
await invokeTauri(page, "create_team", {
|
|
input: {
|
|
name: "Analysts",
|
|
personaIds: [persona.id],
|
|
},
|
|
});
|
|
|
|
const error = await invokeTauriExpectError(page, "delete_persona", {
|
|
id: persona.id,
|
|
});
|
|
|
|
expect(error).toBe(
|
|
"Analyst is still referenced by a team. Remove it from those teams first.",
|
|
);
|
|
});
|