import { readFileSync } from "node:fs"; import { fileURLToPath } from "node:url"; import { describe, expect, it } from "vitest"; const styles = read("../app/globals.css"); const workspaceSource = read("../components/seedream-workspace.tsx"); function read(relativePath: string): string { return readFileSync(fileURLToPath(new URL(relativePath, import.meta.url)), "utf8").replace(/\r\n/g, "\n"); } function cssRule(selector: string): string { const escapedSelector = selector.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); const match = styles.match(new RegExp(`${escapedSelector}\\s*\\{([^}]*)\\}`)); if (!match) throw new Error(`CSS rule not found: ${selector}`); return match[1]; } describe("Seedream workspace layout contract", () => { it("keeps vertical scrolling on the workspace instead of nesting it inside the canvas", () => { const workspace = cssRule(".seedream-workspace"); const viewport = cssRule(".seedream-canvas-viewport"); expect(workspace).toMatch(/overflow-x\s*:\s*hidden/); expect(workspace).toMatch(/overflow-y\s*:\s*auto/); expect(workspace).not.toMatch(/overflow\s*:\s*auto/); expect(viewport).toMatch(/overflow\s*:\s*hidden/); expect(viewport).not.toMatch(/overflow\s*:\s*(?:auto|scroll)/); }); it("caps the annotation canvas without making it consume the remaining column height", () => { const viewport = cssRule(".seedream-canvas-viewport"); const image = cssRule(".seedream-canvas-stage > img"); expect(viewport).toMatch(/--seedream-canvas-height\s*:\s*clamp\(280px,\s*38vh,\s*460px\)/); expect(viewport).toMatch(/height\s*:\s*var\(--seedream-canvas-height\)/); expect(viewport).toMatch(/flex\s*:\s*0\s+0\s+auto/); expect(image).toMatch(/width\s*:\s*100%/); expect(image).toMatch(/height\s*:\s*100%/); expect(workspaceSource).toContain("fitCanvasImage"); expect(workspaceSource).toContain("width: fittedImage.width, height: fittedImage.height"); }); it("uses the same editor in native fullscreen and keeps panning inside a clipped viewport", () => { expect(workspaceSource).toContain("workspaceRef.current.requestFullscreen()"); expect(workspaceSource).not.toContain("createPortal"); expect(cssRule(".seedream-workspace:fullscreen")).toMatch(/overflow\s*:\s*hidden/); expect(cssRule(".seedream-workspace:fullscreen .seedream-canvas-viewport")).toMatch(/flex\s*:\s*1\s+1\s+auto/); expect(workspaceSource).toContain('addEventListener("wheel", handleWheel, { passive: false })'); expect(workspaceSource).toContain('removeEventListener("wheel", handleWheel)'); }); it("measures cached images that finish loading before hydration", () => { expect(workspaceSource).toContain("image?.complete && image.naturalWidth > 0"); expect(workspaceSource).toContain("ref={imageRef}"); expect(workspaceSource).toContain("key: sourceKey, width: image.naturalWidth, height: image.naturalHeight"); }); it("does not render the redundant source picker for single-image layer decomposition", () => { expect(workspaceSource).toMatch(/\{mode === "interactive" && sources\.length \? \(\s*