tests/e2e/flows-pane-theme-light.spec.ts
Rama main · réplica de lectura del repositorio original.
import { expect, test } from "@playwright/test";
import {
setupWorkflowsPage,
normalizeCssColors,
} from "./helpers/flows-pane-theme-fixtures";
test("workflows builder and portalled surfaces follow the light app theme", async ({
page,
}, testInfo) => {
await setupWorkflowsPage(page, "light");
await page.setViewportSize({ width: 1600, height: 900 });
await page.goto("/alex/workflows");
await page.waitForLoadState("networkidle");
// Prove the premise: the surrounding app really is in its light theme.
await expect(page.locator("html")).not.toHaveClass(/dark/);
// React Flow's own color mode has to track the app theme, not the OS.
await expect(page.locator(".react-flow.light")).toBeVisible();
await expect(page.locator(".react-flow.dark")).toHaveCount(0);
const paneStyles = await page.locator(".flows-pane").evaluate((node) => {
const styles = getComputedStyle(node);
return {
themeBackground: styles.getPropertyValue("--background").trim(),
backgroundColor: styles.backgroundColor,
color: styles.color,
colorScheme: styles.colorScheme,
};
});
const [
expectedLightBackground,
actualLightBackground,
expectedLightForeground,
actualLightForeground,
expectedLightCard,
] = await normalizeCssColors(page, [
"#f6f5f0",
paneStyles.backgroundColor,
"#10110d",
paneStyles.color,
"#fcfbf7",
]);
expect(paneStyles.themeBackground).not.toBe("");
expect(actualLightBackground).toBe(expectedLightBackground);
expect(actualLightForeground).toBe(expectedLightForeground);
expect(paneStyles.colorScheme).toBe("light");
const canvasThemeColors = await page
.locator(".flows-canvas")
.evaluate((node) => {
const styles = getComputedStyle(node);
return {
canvasBackground: styles
.getPropertyValue("--xy-background-color")
.trim(),
themeBackground: styles.getPropertyValue("--background").trim(),
};
});
expect(canvasThemeColors.canvasBackground).toBe(
canvasThemeColors.themeBackground
);
const canvasBg = await page
.locator(".react-flow__background")
.evaluate((node) => getComputedStyle(node).backgroundColor);
expect((await normalizeCssColors(page, [canvasBg]))[0]).toBe(
expectedLightBackground
);
// Node cards must sit on a light surface too — they are the one place that
// still paints its own background rather than inheriting the canvas.
const nodeCardBackgrounds = await page
.locator(".flows-node-card")
.evaluateAll((nodes) =>
nodes.map((node) => getComputedStyle(node).backgroundColor)
);
expect(nodeCardBackgrounds.length).toBeGreaterThan(0);
const normalizedLightNodeCards = await normalizeCssColors(
page,
nodeCardBackgrounds
);
expect(
normalizedLightNodeCards.every(
(background) => background === expectedLightCard
)
).toBe(true);
await page.screenshot({
path: testInfo.outputPath("flows-pane-light-theme.png"),
fullPage: true,
});
// The context menu portals to document.body, outside `.flows-pane`, and
// relies on `.flows-theme` to pick up the same palette.
await page
.locator(".react-flow__pane")
.click({ button: "right", position: { x: 200, y: 60 } });
const contextMenu = page.getByTestId("flow-context-menu");
await expect(contextMenu).toBeVisible();
const menuStyles = await contextMenu.evaluate((node) => {
const styles = getComputedStyle(node);
return {
insidePane: Boolean(node.closest(".flows-pane")),
hasFlowsTheme: node.classList.contains("flows-theme"),
themeBackground: styles.getPropertyValue("--background").trim(),
backgroundColor: styles.backgroundColor,
color: styles.color,
};
});
expect(menuStyles.insidePane).toBe(false);
expect(menuStyles.hasFlowsTheme).toBe(true);
expect(menuStyles.themeBackground).not.toBe("");
expect(menuStyles.backgroundColor).not.toBe("rgba(0, 0, 0, 0)");
expect((await normalizeCssColors(page, [menuStyles.color]))[0]).toBe(
expectedLightForeground
);
await page.keyboard.press("Escape");
await expect(contextMenu).not.toBeVisible();
// The run-details dialog also portals to document.body and must carry the
// light palette via `.flows-theme`.
await page.getByTestId("flows-runs-tab").click();
await page.getByTestId("flow-run-card-run-1").click();
const runDialog = page.locator('[data-slot="dialog-content"]');
await expect(runDialog).toBeVisible();
await expect(runDialog.getByText("Overview")).toBeVisible();
const dialogStyles = await runDialog.evaluate((node) => {
const styles = getComputedStyle(node);
return {
insidePane: Boolean(node.closest(".flows-pane")),
hasFlowsTheme: node.classList.contains("flows-theme"),
backgroundColor: styles.backgroundColor,
color: styles.color,
};
});
expect(dialogStyles.insidePane).toBe(false);
expect(dialogStyles.hasFlowsTheme).toBe(true);
const [dialogBackground, dialogForeground] = await normalizeCssColors(page, [
dialogStyles.backgroundColor,
dialogStyles.color,
]);
expect(dialogBackground).toBe(expectedLightBackground);
expect(dialogForeground).toBe(expectedLightForeground);
});