Files
olivv-cs-ComfyUI-FunPack/app/tests-layout/theme.spec.js
T
digital-garbageandClaude Opus 5 bd0c4be42e composer: Playwright, and the two layout faults it found in its first run
The plan asked for jsdom AND Playwright from the start and only jsdom was
ever built. jsdom has no layout engine: getBoundingClientRect answers zeros,
container queries never fire, a theme is an attribute rather than a computed
cascade. So every claim about POSITION or SIZE in this kit was unchecked, and
the unit suite could not have noticed either of these.

A popover bounded its height and not its width. `max-height: 60vh` was there
from the start; there was no max-inline-size at all, so a long unbroken
string -- a file path, a model filename -- made the popover wider than the
screen and anchor.js could not save it: it can move a box, not shrink one.
Measured at 1436px inside a 1280px viewport, off the right edge in all four
corners.

A horizontal split could not fit a narrow screen. The first pane carries an
inline size and `flex: 0 0 auto`, so it would not shrink, and the page
scrolled sideways at 360px -- 407px of content in a 360px viewport. It wraps
now, which stacks the panes, rather than a media query: the kit sizes itself
from its container and not from the window.

Nine layout tests: every overlay corner, an overlay taller than the viewport,
both themes resolving every token they use, light actually being lighter than
dark, and no horizontal scrollbar at five widths. `npm run test:all` runs both
suites.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 18:57:52 +03:00

65 lines
2.5 KiB
JavaScript

// Both themes, painted.
//
// jsdom treats a theme as an attribute: it never resolves a custom property,
// so "the light theme is light" is unverifiable there. A real browser computes
// the cascade, which is the only place a token that resolves to the wrong value
// -- or to nothing -- shows up.
import { test, expect } from "@playwright/test";
const rgb = (value) => value.match(/\d+/g).map(Number);
const luminance = ([r, g, b]) => (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255;
test.describe("themes", () => {
test.beforeEach(async ({ page }) => {
await page.goto("/funpack/");
await page.waitForFunction(() => window.FunPack !== undefined);
});
for (const theme of ["dark", "light"]) {
test(`${theme} resolves every token it uses`, async ({ page }) => {
await page.evaluate((t) => window.ComposerTheme.apply(t), theme);
const unresolved = await page.evaluate(() => {
const style = getComputedStyle(document.documentElement);
const names = [];
for (const sheet of document.styleSheets) {
let rules;
try { rules = sheet.cssRules; } catch { continue; }
for (const rule of rules) {
if (!rule.style) continue;
for (const prop of rule.style) {
if (prop.startsWith("--")) names.push(prop);
}
}
}
// A token that resolves to nothing is the failure this catches: the
// rule still applies and the colour silently comes from elsewhere.
return [...new Set(names)].filter((n) => style.getPropertyValue(n).trim() === "");
});
expect(unresolved, `tokens with no value in ${theme}`).toEqual([]);
});
}
test("light is actually lighter than dark", async ({ page }) => {
const read = async (theme) => {
await page.evaluate((t) => window.ComposerTheme.apply(t), theme);
return page.evaluate(() => getComputedStyle(document.body).backgroundColor);
};
const dark = luminance(rgb(await read("dark")));
const light = luminance(rgb(await read("light")));
expect(light, "the light theme is not lighter than the dark one")
.toBeGreaterThan(dark);
});
test("the page never scrolls sideways", async ({ page }) => {
for (const width of [1440, 1024, 768, 480, 360]) {
await page.setViewportSize({ width, height: 800 });
const overflows = await page.evaluate(() =>
document.documentElement.scrollWidth > document.documentElement.clientWidth + 1);
expect(overflows, `horizontal scrollbar at ${width}px`).toBe(false);
}
});
});