112 lines
3.7 KiB
TypeScript
112 lines
3.7 KiB
TypeScript
import { expect, test, type Locator } from "playwright/test";
|
|
|
|
test("agent header reveals the shell acrylic without nesting another filter", async ({ page }) => {
|
|
await page.goto("/", { waitUntil: "domcontentloaded" });
|
|
await page.waitForTimeout(250);
|
|
|
|
const topBar = page.locator("header.acrylic-navigation");
|
|
const agentPanel = page.locator('aside[aria-label="Agent 命令面板"]');
|
|
const agentHeader = agentPanel.locator(".agent-panel-header");
|
|
|
|
await expect(topBar).toBeVisible();
|
|
await expect(agentPanel).toBeVisible();
|
|
await expect(agentHeader).toBeVisible();
|
|
|
|
await expectAcrylicSurface(topBar);
|
|
await expectAcrylicSurface(agentPanel);
|
|
await expectTransparentAcrylicContext(agentHeader);
|
|
await expectLightweightContextSurfaces(agentPanel.locator(".agent-panel-conversation"));
|
|
await expectSolidInternalSurfaces(agentPanel.locator(".agent-panel-band"));
|
|
});
|
|
|
|
async function expectAcrylicSurface(shell: Locator) {
|
|
const composition = await shell.evaluate((element) => {
|
|
const style = getComputedStyle(element);
|
|
const filteredDescendants: string[] = [];
|
|
|
|
for (const descendant of element.querySelectorAll("*")) {
|
|
if (getComputedStyle(descendant).backdropFilter !== "none") {
|
|
filteredDescendants.push(descendant.className);
|
|
}
|
|
}
|
|
|
|
return {
|
|
backdropFilter: style.backdropFilter,
|
|
backgroundColor: style.backgroundColor,
|
|
filteredDescendants,
|
|
transform: style.transform,
|
|
willChange: style.willChange
|
|
};
|
|
});
|
|
|
|
expect(composition.backdropFilter).toContain("blur(");
|
|
expect(getAlpha(composition.backgroundColor)).toBeLessThan(1);
|
|
expect(composition.filteredDescendants).toEqual([]);
|
|
expect(composition.transform).toBe("none");
|
|
expect(composition.willChange).toBe("auto");
|
|
}
|
|
|
|
function getAlpha(color: string) {
|
|
const match = color.match(/^rgba\([^,]+,[^,]+,[^,]+,\s*([\d.]+)\)$/);
|
|
|
|
return match ? Number(match[1]) : 1;
|
|
}
|
|
|
|
async function expectTransparentAcrylicContext(header: Locator) {
|
|
const style = await header.evaluate((element) => {
|
|
const computedStyle = getComputedStyle(element);
|
|
|
|
return {
|
|
backdropFilter: computedStyle.backdropFilter,
|
|
backgroundColor: computedStyle.backgroundColor,
|
|
className: element.className,
|
|
webkitBackdropFilter: computedStyle.getPropertyValue("-webkit-backdrop-filter")
|
|
};
|
|
});
|
|
|
|
expect(style.backdropFilter).toBe("none");
|
|
expect(style.backgroundColor).toBe("rgba(0, 0, 0, 0)");
|
|
expect(["", "none"]).toContain(style.webkitBackdropFilter);
|
|
expect(String(style.className)).not.toContain("acrylic-");
|
|
}
|
|
|
|
async function expectLightweightContextSurfaces(surfaces: Locator) {
|
|
const surfaceStyles = await surfaces.evaluateAll((elements) =>
|
|
elements.map((element) => {
|
|
const style = getComputedStyle(element);
|
|
|
|
return {
|
|
backdropFilter: style.backdropFilter,
|
|
backgroundColor: style.backgroundColor
|
|
};
|
|
})
|
|
);
|
|
|
|
expect(surfaceStyles.length).toBeGreaterThan(0);
|
|
for (const style of surfaceStyles) {
|
|
expect(style.backdropFilter).toBe("none");
|
|
expect(getAlpha(style.backgroundColor)).toBeLessThanOrEqual(0.12);
|
|
}
|
|
}
|
|
|
|
async function expectSolidInternalSurfaces(surfaces: Locator) {
|
|
const surfaceStyles = await surfaces.evaluateAll((elements) =>
|
|
elements.map((element) => {
|
|
const style = getComputedStyle(element);
|
|
|
|
return {
|
|
backdropFilter: style.backdropFilter,
|
|
className: element.className,
|
|
webkitBackdropFilter: style.getPropertyValue("-webkit-backdrop-filter")
|
|
};
|
|
})
|
|
);
|
|
|
|
expect(surfaceStyles.length).toBeGreaterThan(0);
|
|
for (const style of surfaceStyles) {
|
|
expect(style.backdropFilter).toBe("none");
|
|
expect(["", "none"]).toContain(style.webkitBackdropFilter);
|
|
expect(String(style.className)).not.toContain("acrylic-");
|
|
}
|
|
}
|