deerflow2/frontend/tests/e2e/theme-colors.spec.ts

171 lines
5.3 KiB
TypeScript

import { expect, test } from "@playwright/test";
import {
THREAD_WITH_ARTIFACTS,
THREAD_WITH_HISTORY,
openChat,
reuseThreadChatEntry,
setTheme,
skipIfMissingThread,
} from "./support/chat-helpers";
function isTransparent(color: string) {
const normalized = color.replace(/\s+/g, "").toLowerCase();
return normalized === "transparent" || normalized.endsWith(",0)");
}
test.describe("聊天工作台 / 主题颜色回归", () => {
test("DF-THEME-001 thread 页面在 light/dark 根容器颜色不同且非透明", async ({
page,
}, testInfo) => {
skipIfMissingThread(
testInfo,
THREAD_WITH_HISTORY,
"FRONTEND_E2E_THREAD_ID",
);
await openChat(page, reuseThreadChatEntry(THREAD_WITH_HISTORY!));
await setTheme(page, "light");
const lightState = await page.evaluate(() => {
const probe = document.createElement("div");
probe.className = "bg-background";
probe.style.position = "fixed";
probe.style.left = "-9999px";
probe.style.top = "-9999px";
document.body.appendChild(probe);
const bg = getComputedStyle(probe).backgroundColor;
probe.remove();
return {
bg,
rootBackground: getComputedStyle(document.documentElement)
.getPropertyValue("--background")
.trim(),
};
});
await setTheme(page, "dark");
const darkState = await page.evaluate(() => {
const probe = document.createElement("div");
probe.className = "bg-background";
probe.style.position = "fixed";
probe.style.left = "-9999px";
probe.style.top = "-9999px";
document.body.appendChild(probe);
const bg = getComputedStyle(probe).backgroundColor;
probe.remove();
return {
bg,
rootBackground: getComputedStyle(document.documentElement)
.getPropertyValue("--background")
.trim(),
};
});
expect(isTransparent(lightState.bg)).toBe(false);
expect(isTransparent(darkState.bg)).toBe(false);
expect(darkState.rootBackground).not.toBe(lightState.rootBackground);
});
test("DF-THEME-002 dark 模式下发送按钮 hover 前后颜色变化存在且可见", async ({
page,
}, testInfo) => {
skipIfMissingThread(
testInfo,
THREAD_WITH_HISTORY,
"FRONTEND_E2E_THREAD_ID",
);
await openChat(page, reuseThreadChatEntry(THREAD_WITH_HISTORY!));
await setTheme(page, "dark");
const textarea = page.locator("textarea[name='message']");
const submit = page.locator("button[aria-label='Submit']");
await textarea.fill("theme hover regression");
await expect(submit).toBeEnabled();
const before = await submit.evaluate((element) => {
const style = getComputedStyle(element);
return {
background: style.backgroundColor,
color: style.color,
border: style.borderTopColor,
};
});
await submit.hover();
const after = await submit.evaluate((element) => {
const style = getComputedStyle(element);
return {
background: style.backgroundColor,
color: style.color,
border: style.borderTopColor,
};
});
const changed =
before.background !== after.background ||
before.color !== after.color ||
before.border !== after.border;
expect(changed).toBe(true);
expect(isTransparent(after.background) && isTransparent(after.border)).toBe(
false,
);
expect(isTransparent(after.color)).toBe(false);
});
test("DF-THEME-003 artifact detail 面板在 light/dark 渲染 token 颜色", async ({
page,
}, testInfo) => {
skipIfMissingThread(
testInfo,
THREAD_WITH_ARTIFACTS,
"FRONTEND_E2E_ARTIFACTS_THREAD_ID",
);
await openChat(page, reuseThreadChatEntry(THREAD_WITH_ARTIFACTS!));
const openArtifacts = page.getByTestId("artifacts-open-button");
testInfo.skip(
(await openArtifacts.count()) === 0,
"当前线程未展示 artifacts 入口。",
);
await openArtifacts.click();
const firstCard = page.getByTestId("artifact-file-card").first();
testInfo.skip((await firstCard.count()) === 0, "当前线程没有 artifact 文件。");
await firstCard.click();
const detailRoot = page
.locator("div.bg-background.relative.h-full.overflow-hidden.rounded-2xl")
.first();
await expect(detailRoot).toBeVisible();
await setTheme(page, "light");
const light = await detailRoot.evaluate((element) => {
const style = getComputedStyle(element);
const header = element.querySelector("header");
const headerStyle = header ? getComputedStyle(header) : null;
return {
panelBg: style.backgroundColor,
headerBorder: headerStyle?.borderBottomColor ?? "",
};
});
await setTheme(page, "dark");
const dark = await detailRoot.evaluate((element) => {
const style = getComputedStyle(element);
const header = element.querySelector("header");
const headerStyle = header ? getComputedStyle(header) : null;
return {
panelBg: style.backgroundColor,
headerBorder: headerStyle?.borderBottomColor ?? "",
};
});
expect(isTransparent(light.panelBg)).toBe(false);
expect(isTransparent(dark.panelBg)).toBe(false);
expect(light.panelBg).not.toBe(dark.panelBg);
expect(light.headerBorder).not.toBe(dark.headerBorder);
});
});