ai-chatbot/tests/e2e/canvas-image-toolbar.test.ts

219 lines
7.9 KiB
TypeScript

import { expect, test } from "@playwright/test";
const storageKey = "canvas.image-toolbar.v1";
test("image toolbar customization preserves actions, saves preferences and respects enabled tools", async ({
page,
context,
}) => {
const errors: string[] = [];
page.on("pageerror", (error) => errors.push(error.message));
await context.addInitScript(() => localStorage.setItem("theme", "light"));
const settings = {
canvasEnabled: true,
agentEnabled: true,
layerSplitEnabled: true,
angleEditEnabled: true,
membershipEnabled: false,
canvasToolbarConfig: {} as Record<string, unknown>,
};
await context.route("**/api/**", async (route) => {
const path = new URL(route.request().url()).pathname;
let data: unknown = {};
if (path.includes("validate-session"))
data = { valid: true, success: true };
else if (path === "/api/auth/session")
data = {
user: { id: "toolbar-test-user", name: "设计师", type: "admin" },
expires: "2099-01-01",
};
else if (path === "/api/site-settings") data = settings;
else if (path === "/api/models")
data = {
models: [
{
id: "image-test",
modelId: "gpt-image-2",
providerType: "openai",
name: "GPT Image",
type: "image",
enabled: true,
supportsImageGen: true,
imageGenConfig: { creditCostPerImage: 15 },
},
],
};
else if (
path === "/api/canvas/toolbar-test" &&
route.request().method() === "GET"
)
data = {
success: true,
data: {
id: "toolbar-test",
title: "工具栏测试",
canvasData: {
nodes: [
{
id: "toolbar-image",
type: "image",
position: { x: 220, y: 160 },
size: { width: 640, height: 420 },
data: {
status: "completed",
src: new URL("/toolbar-test.svg", page.url()).href,
title: "工具栏测试图",
originalWidth: 640,
originalHeight: 420,
},
},
],
edges: [],
viewport: { x: 0, y: 0, zoom: 1 },
},
},
};
else if (path.startsWith("/api/canvas/")) data = { success: true };
else if (path.includes("sessions")) data = { sessions: [] };
else if (path.includes("messages")) data = { messages: [] };
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify(data),
});
});
await context.route("**/toolbar-test.svg", (route) =>
route.fulfill({
contentType: "image/svg+xml",
body: '<svg xmlns="http://www.w3.org/2000/svg" width="640" height="420"><rect width="640" height="420" fill="#43b9bd"/></svg>',
})
);
const node = page.locator('[data-canvas-node-id="toolbar-image"]');
const bar = page.locator('[data-selected-image-toolbar="true"]');
const dialog = page.getByRole("dialog");
const keys = () =>
bar
.locator("button[data-tool-key]")
.evaluateAll((buttons) =>
buttons.map((button) => button.getAttribute("data-tool-key"))
);
const selectImage = async () => {
await node.waitFor();
await node.click({ position: { x: 80, y: 80 } });
await expect(bar).toBeVisible();
};
const customize = async () => {
await bar.getByRole("button", { name: "更多操作", exact: true }).click();
await page
.getByRole("menuitem", { name: "自定义工具栏", exact: true })
.click();
await expect(dialog).toBeVisible();
};
await page.goto("/canvas/toolbar-test", { waitUntil: "commit" });
await selectImage();
const initial = await keys();
await expect(bar).toHaveAttribute("data-show-tool-names", "false");
await bar.getByRole("button", { name: "高清", exact: true }).hover();
await expect(page.getByRole("tooltip")).toContainText("高清");
await page.mouse.move(1000, 100);
await customize();
await dialog.getByRole("button", { name: "固定裁剪", exact: true }).click();
await dialog.getByRole("button", { name: "取消", exact: true }).click();
expect(await keys()).toEqual(initial);
await customize();
await dialog
.getByRole("button", { name: "取消固定高清", exact: true })
.click();
await dialog.getByRole("button", { name: "固定裁剪", exact: true }).click();
await dialog.getByRole("switch", { name: "显示工具名称" }).click();
await dialog
.getByRole("button", { name: "裁剪,按左右方向键调整顺序", exact: true })
.focus();
await page.keyboard.press("ArrowLeft");
await dialog.getByRole("button", { name: "保存", exact: true }).click();
const changed = await keys();
expect(changed).toContain("crop");
expect(changed).not.toContain("enhance");
expect(changed.at(-3)).toBe("crop");
await expect(bar).toHaveAttribute("data-show-tool-names", "true");
await bar.getByRole("button", { name: "更多操作", exact: true }).click();
await expect(
page.getByRole("menuitem", { name: "高清", exact: true })
).toBeVisible();
await page.keyboard.press("Escape");
await page.reload({ waitUntil: "commit" });
await selectImage();
expect(await keys()).toEqual(changed);
await bar.getByRole("button", { name: "裁剪", exact: true }).click();
await expect(
page.getByRole("heading", { name: "裁剪", exact: true })
).toBeVisible();
await page.getByRole("button", { name: "取消", exact: true }).click();
await expect(bar).toBeVisible();
await bar.getByRole("button", { name: "添加对话", exact: true }).click();
await expect(page.getByText("已添加到对话框", { exact: true })).toBeVisible();
await customize();
await dialog
.getByRole("button", { name: "恢复默认工具栏", exact: true })
.click();
await dialog.getByRole("button", { name: "保存", exact: true }).click();
expect(await keys()).toEqual(initial);
await page.evaluate((key) => {
localStorage.setItem(key, "invalid-json{");
window.dispatchEvent(
new StorageEvent("storage", { key, newValue: "invalid-json{" })
);
}, storageKey);
expect(await keys()).toEqual(initial);
await customize();
const firstHandle = dialog.getByRole("button", {
name: "识物,按左右方向键调整顺序",
exact: true,
});
const secondHandle = dialog.getByRole("button", {
name: "高清,按左右方向键调整顺序",
exact: true,
});
await firstHandle.dragTo(secondHandle);
await dialog.getByRole("button", { name: "保存", exact: true }).click();
const draggedOrder = await keys();
expect(draggedOrder.slice(1, 3)).toEqual(["enhance", "pickObject"]);
await customize();
await page.setViewportSize({ width: 390, height: 700 });
await expect
.poll(async () => (await dialog.boundingBox())?.width)
.toBeLessThanOrEqual(358);
await expect
.poll(async () => (await dialog.boundingBox())?.height)
.toBeLessThanOrEqual(652);
await expect(
dialog.getByRole("button", { name: "保存", exact: true })
).toBeInViewport();
await dialog.getByRole("button", { name: "取消", exact: true }).click();
await page.setViewportSize({ width: 1512, height: 793 });
settings.canvasToolbarConfig = {
selectedImageInlineActionCount: 3,
items: {
imageEnhance: {
label: "自定义高清",
iconUrl: "/icons/lovart/model-gpt.svg",
enabled: true,
},
imageRemoveBackground: { enabled: false },
},
};
await page.reload({ waitUntil: "commit" });
await selectImage();
const customIcon = bar
.getByRole("button", { name: "自定义高清", exact: true })
.locator('span[aria-hidden="true"]');
expect(
await customIcon.evaluate(
(element) => (element as HTMLElement).style.maskImage
)
).toContain("model-gpt.svg");
await customize();
await expect(dialog.getByRole("button", { name: /固定抠图/ })).toHaveCount(0);
expect(errors).toEqual([]);
});