Two real findings from adversarial review of the previous commit (the v4 UI port's route change), both fixed: - app/tests-layout/*.spec.js (20 files, 133 tests): every one of these navigated to /funpack/ and asserted on composer-specific DOM (.cx-workspace*, window.FunPack) that no longer exists there now that the ported v4 frontend is served at that URL instead. Left as-is they would silently time out on every run. Each file now carries an explicit test.skip(true, "...") with a comment explaining why and pointing at the plan's pixel-exactness step, which is what will eventually rewrite these against the new DOM -- declared and tracked, not quietly broken. - core/tests/test_registry.py's fixture-based tests never exercised the new category field through the real announcement_of()/scan() path -- only test_schema.py's direct validate() calls did, which bypass registry.py's FIELDS name-mapping entirely. A typo there (e.g. "Category" instead of "CATEGORY") would have passed every existing test. The "good module" fixture gained CATEGORY = "guidance" and test_the_good_module_loads now asserts on it, closing the gap. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
139 lines
7.0 KiB
JavaScript
139 lines
7.0 KiB
JavaScript
// The unified Settings window: one button, a searchable nav list, and every
|
|
// section mounted in place beside it -- picking one never leaves the window.
|
|
|
|
import { test, expect } from "@playwright/test";
|
|
|
|
// Skipped: / now serves the ported v4 frontend (app/legacy/), not composer --
|
|
// these specs assert on .cx-workspace*/window.FunPack, which no longer exist
|
|
// on the served page. Pending rewrite against the v4-derived DOM as part of
|
|
// the v4-UI-onto-v5-backend port's pixel-exactness verification step (see
|
|
// the plan file). Not deleted: the assertions are still the right SHAPE of
|
|
// check, just against selectors this page no longer has.
|
|
test.skip(true, "composer retired in favor of the v4 UI port -- rewrite against app/legacy's DOM");
|
|
|
|
async function app(page) {
|
|
await page.goto("/funpack/");
|
|
await page.waitForFunction(() => window.FunPack !== undefined);
|
|
}
|
|
|
|
test("opening Settings shows About, with the real version and branch", async ({ page }) => {
|
|
await app(page);
|
|
const real = await page.evaluate(async () => (await fetch("/funpack/api/git/status")).json());
|
|
|
|
await page.getByRole("button", { name: "Settings" }).click();
|
|
await expect(page.locator(".cx-modal")).toContainText("About FunPack");
|
|
if (real.ok) await expect(page.locator(".cx-modal")).toContainText(real.branch);
|
|
});
|
|
|
|
test("search narrows the section list to what matches", async ({ page }) => {
|
|
await app(page);
|
|
await page.getByRole("button", { name: "Settings" }).click();
|
|
await page.locator(".cx-modal .cx-search").fill("packs");
|
|
const rows = page.locator(".cx-modal .cx-filter-row");
|
|
await expect(rows).toHaveCount(1);
|
|
await expect(rows).toContainText("Node packs");
|
|
});
|
|
|
|
test("search also matches a section by its keywords, not only its title", async ({ page }) => {
|
|
await app(page);
|
|
await page.getByRole("button", { name: "Settings" }).click();
|
|
// "loaders" is on the pipeline section's keywords, not its visible title
|
|
// ("Models and pipeline") or subtitle ("What the run is made of.").
|
|
await page.locator(".cx-modal .cx-search").fill("loaders");
|
|
const rows = page.locator(".cx-modal .cx-filter-row");
|
|
await expect(rows).toHaveCount(1);
|
|
await expect(rows).toContainText("Models and pipeline");
|
|
});
|
|
|
|
test("picking a section swaps the content in place -- the window stays open, and there is only ever one", async ({ page }) => {
|
|
await app(page);
|
|
await page.getByRole("button", { name: "Settings" }).click();
|
|
await page.locator(".cx-filter-row", { hasText: "Node packs" }).click();
|
|
|
|
await expect(page.locator(".cx-modal")).toHaveCount(1);
|
|
// "Settings" is the window's own title throughout; the section names
|
|
// itself inside the body, beside the still-visible nav list.
|
|
await expect(page.locator(".cx-modal-title")).toHaveText("Settings");
|
|
await expect(page.locator(".cx-modal")).toContainText("Node packs");
|
|
await expect(page.locator(".cx-filter-row")).toHaveCount(7); // About + the six, still all there
|
|
});
|
|
|
|
test("switching sections tears down the last one -- its own polling stops", async ({ page }) => {
|
|
// The log window polls while mounted. Picking it, then picking something
|
|
// else, must stop that poll rather than leaving it running against a
|
|
// detached view -- proven by leaving the log mounted only briefly and
|
|
// confirming the window still works normally afterwards, requesting
|
|
// nothing further from a section no longer on screen.
|
|
await app(page);
|
|
await page.getByRole("button", { name: "Settings" }).click();
|
|
let logRequests = 0;
|
|
await page.route("**/api/log**", (route) => { logRequests += 1; route.fulfill({ json: { lines: ["hi"] } }); });
|
|
await page.locator(".cx-filter-row", { hasText: "ComfyUI log" }).click();
|
|
await expect.poll(() => logRequests).toBeGreaterThan(0);
|
|
|
|
await page.locator(".cx-filter-row", { hasText: "About FunPack" }).click();
|
|
const before = logRequests;
|
|
await page.waitForTimeout(2200); // longer than the log's own 2s poll
|
|
expect(logRequests, "the log kept polling after it was navigated away from").toBe(before);
|
|
});
|
|
|
|
test("the sidebar is a collapsed icon rail that expands on hover, matching v4", async ({ page }) => {
|
|
await app(page);
|
|
await page.getByRole("button", { name: "Settings" }).click();
|
|
|
|
const rail = page.locator(".cx-settings-rail");
|
|
const collapsed = await rail.boundingBox();
|
|
expect(collapsed.width, "the rail did not start collapsed").toBeLessThan(60);
|
|
|
|
// Labels are present (findable by name) but faded out while collapsed --
|
|
// opacity, not display/visibility, so Playwright's own visibility check
|
|
// (which ignores opacity) cannot tell the two states apart; read it directly.
|
|
const label = page.locator(".cx-filter-label", { hasText: "Node packs" });
|
|
await expect(label).toHaveCSS("opacity", "0");
|
|
|
|
await rail.hover();
|
|
await expect.poll(async () => (await rail.boundingBox()).width).toBeGreaterThan(200);
|
|
await expect(label).toHaveCSS("opacity", "1");
|
|
|
|
// And back, once the mouse leaves -- not stuck open.
|
|
await page.mouse.move(500, 400);
|
|
await expect.poll(async () => (await rail.boundingBox()).width).toBeLessThan(60);
|
|
});
|
|
|
|
test("the collapsed rail never scrolls sideways -- the full-width rows underneath it are clipped, not scrollable", async ({ page }) => {
|
|
// The bug this guards: .cx-filter-list sets overflow-y:auto and left
|
|
// overflow-x unset, which the CSS spec then computes as overflow-x:auto
|
|
// too -- so the rows (still their full un-collapsed width) put a
|
|
// horizontal scrollbar on the 52px rail instead of just being clipped.
|
|
await app(page);
|
|
await page.getByRole("button", { name: "Settings" }).click();
|
|
const overflowX = await page.evaluate(() =>
|
|
getComputedStyle(document.querySelector(".cx-settings-rail .cx-filter-list")).overflowX);
|
|
expect(overflowX).toBe("hidden");
|
|
|
|
const rail = await page.locator(".cx-settings-rail").boundingBox();
|
|
const list = await page.locator(".cx-settings-rail .cx-filter-list").boundingBox();
|
|
expect(list.x, "the row list starts outside the collapsed rail").toBeGreaterThanOrEqual(rail.x - 2);
|
|
expect(list.x + list.width, "the row list extends past the collapsed rail")
|
|
.toBeLessThanOrEqual(rail.x + rail.width + 2);
|
|
});
|
|
|
|
test("there is real space between the collapsed rail and the content beside it", async ({ page }) => {
|
|
await app(page);
|
|
await page.getByRole("button", { name: "Settings" }).click();
|
|
|
|
const rail = await page.locator(".cx-settings-rail").boundingBox();
|
|
const title = await page.getByRole("heading", { name: "About FunPack" }).boundingBox();
|
|
expect(title.x - (rail.x + rail.width), "the content sits flush against the rail").toBeGreaterThan(15);
|
|
});
|
|
|
|
test("closing and reopening always starts fresh, on About", async ({ page }) => {
|
|
await app(page);
|
|
await page.getByRole("button", { name: "Settings" }).click();
|
|
await page.locator(".cx-filter-row", { hasText: "Node packs" }).click();
|
|
await page.locator(".cx-modal .cx-icon-btn").click(); // the one close button, for the whole window
|
|
|
|
await page.getByRole("button", { name: "Settings" }).click();
|
|
await expect(page.locator(".cx-modal")).toContainText("About FunPack");
|
|
});
|