Files
2026-09-11 14:51:32 +00:00

79 lines
2.6 KiB
TypeScript

import { cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react";
import { afterEach, describe, expect, it, vi } from "vitest";
import { CanvasPage } from "./canvas-page";
import { WEB_APP_STARTUP_RESULT_TYPE, WEB_APP_STARTUP_VERSION } from "./web-app-startup";
const responsive = { isMobile: false };
vi.mock("@/hooks/use-responsive-breakpoint", () => ({
useResponsiveBreakpoint: () => responsive,
}));
describe("CanvasPage", () => {
afterEach(() => {
cleanup();
responsive.isMobile = false;
});
it("renders a full-height host and forwards runtime and load lifecycle props", async () => {
const onLoad = vi.fn();
render(
<CanvasPage
runtimeUrl="/api/v1/plugins/web-apps/runtime/capability/"
title="Task board"
onLoad={onLoad}
/>,
);
const page = screen.getByTestId("canvas-page");
expect(page.className).toContain("h-full");
expect(page.className).toContain("min-h-0");
expect(page.className).toContain("min-w-0");
expect(page.className).toContain("overflow-hidden");
const frame = screen.getByTitle("Task board") as HTMLIFrameElement;
expect(frame.getAttribute("src")).toContain("/api/v1/plugins/web-apps/runtime/");
expect(screen.getByRole("status").closest("iframe")).toBeNull();
const postMessage = vi.fn();
Object.defineProperty(frame, "contentWindow", {
configurable: true,
value: { postMessage },
});
fireEvent.load(frame);
const probe = postMessage.mock.calls.find(
([value]) => value?.type === "kandev.web_app.startup_probe",
)?.[0];
expect(probe).toBeDefined();
window.dispatchEvent(
new MessageEvent("message", {
data: {
type: WEB_APP_STARTUP_RESULT_TYPE,
version: WEB_APP_STARTUP_VERSION,
nonce: probe.nonce,
result: "ready",
},
source: frame.contentWindow,
}),
);
await waitFor(() => expect(onLoad).toHaveBeenCalledOnce());
await waitFor(() => expect(screen.queryByRole("status")).toBeNull());
});
it("keeps the unavailable host state outside the iframe", () => {
render(<CanvasPage title="Canvas" />);
expect(screen.queryByTitle("Canvas")).toBeNull();
expect(screen.getByRole("alert").closest("iframe")).toBeNull();
});
it("delegates phone safe-area behavior to WebAppFrame", () => {
responsive.isMobile = true;
render(<CanvasPage runtimeUrl="/runtime/phone/" title="Canvas" />);
const frame = screen.getByTestId("web-app-frame");
expect(frame.dataset.mobile).toBe("true");
expect(frame.className).toContain("pb-[env(safe-area-inset-bottom)]");
});
});