import { readFileSync } from "node:fs"; import { renderToStaticMarkup } from "react-dom/server"; import { describe, expect, it } from "vitest"; import { OverlayHost } from "../src/renderer/shell/OverlayHost"; import { UpdateExperience } from "../src/renderer/shell/UpdateExperience"; describe("OverlayHost", () => { it("renders every desktop overlay slot exactly once", () => { const slots = { confirm: confirm-slot, onlineBackup: backup-slot, diagnostics: diagnostics-slot, deleteConfirmation: delete-slot, conflict: conflict-slot, accountCreate: account-create-slot, accountEdit: account-edit-slot, keyStats: key-stats-slot, linkPopup: link-popup-slot, update: update-slot, toast: toast-slot, accountContextMenu: account-menu-slot, downloadContextMenu: download-menu-slot, columnContextMenu: column-menu-slot, historyContextMenu: history-menu-slot, dropOverlay: drop-slot }; const html = renderToStaticMarkup(); expect(html).toContain("id=\"md-overlay-host\""); for (const value of Object.values(slots)) { const label = String(value.props.children); expect(html.split(label)).toHaveLength(2); } }); it("does not render placeholders for empty slots", () => { const html = renderToStaticMarkup(sichtbar} />); expect(html).toContain("sichtbar"); expect(html).not.toContain("data-overlay-slot"); }); it("hosts the update surface through the shared dialog without duplicating the trigger", () => { const html = renderToStaticMarkup( {}} onInstall={() => {}} onLater={() => {}} onOpen={() => {}} open progress={0} releaseNotes="Changes" renderTrigger={false} state="prompt" /> )} /> ); expect(html).toContain("md-dialog-size-update"); expect(html).not.toContain("aria-label=\"Update verfügbar\""); expect(html.match(/role=\"dialog\"/g)).toHaveLength(1); }); it("defines a stacking-context-safe menu, tooltip, toast and modal layer order", () => { const css = readFileSync(new URL("../src/renderer/shell/shell.css", import.meta.url), "utf8"); expect(css).toMatch(/--md-layer-menu:\s*600/); expect(css).toMatch(/--md-layer-tooltip:\s*700/); expect(css).toMatch(/--md-layer-toast:\s*800/); expect(css).toMatch(/--md-layer-modal:\s*1000/); expect(css).toMatch(/\.md-context-menu\s*\{[^}]*z-index:\s*var\(--md-layer-menu\)/s); expect(css).toMatch(/\.md-update-tooltip\s*\{[^}]*z-index:\s*var\(--md-layer-tooltip\)/s); expect(css).toMatch(/\.md-toast\s*\{[^}]*z-index:\s*var\(--md-layer-toast\)/s); expect(css).toMatch(/\.md-dialog-backdrop\s*\{[^}]*z-index:\s*var\(--md-layer-modal\)/s); expect(css).toMatch(/\.md-drop-overlay\s*\{[^}]*pointer-events:\s*none/s); expect(css).toMatch(/\.md-overlay-host \.md-dialog-backdrop\s*\{[^}]*z-index:\s*var\(--md-layer-modal\)/s); expect(css).toMatch(/\.md-overlay-host \.md-context-menu\s*\{[^}]*z-index:\s*var\(--md-layer-menu\)/s); expect(css).toMatch(/\.md-overlay-host \.md-toast\s*\{[^}]*z-index:\s*var\(--md-layer-toast\)/s); expect(css).toMatch(/\.md-overlay-host \.md-dialog\s*\{[^}]*background:\s*var\(--ui-surface\)/s); }); });