test(app): verify Mermaid controls through user journeys

Keep source-toggle mechanics in diagram helpers and compare preview geometry with its screen and controls instead of requiring exact pixel heights.

Co-authored-by: luke <luopeihuan@gmail.com>
This commit is contained in:
paseo-bot[bot]
2026-11-06 11:19:29 +00:00
co-authored by luke
parent 81bc669e3a
commit 93a544c65a
2 changed files with 61 additions and 33 deletions
@@ -1,7 +1,8 @@
import { expect } from "@playwright/test";
import { test } from "../support/fixtures";
import {
closeDiagramFullscreen,
expectInlineDiagramFitsViewport,
toggleDiagramSourceAndVerifyControls,
closeDiagramFullscreenFromOutside,
expectCompletedDiagram,
expectFullscreenDiagram,
@@ -43,9 +44,7 @@ test("bounds a tall inline diagram", async ({ page }) => {
await requestDiagram(agent);
await waitForDiagramTurnToComplete(agent);
await expectCompletedDiagram(page, ["N0", "N30"]);
const viewport = page.getByTestId("mermaid-viewport").last();
expect((await viewport.boundingBox())!.height).toBe(480);
await page.screenshot({ path: test.info().outputPath("tall-preview.png") });
await expectInlineDiagramFitsViewport(page);
await openDiagramFullscreen(page);
await expectFullscreenDiagram(page, ["N0", "N30"]);
await closeDiagramFullscreen(page);
@@ -73,35 +72,8 @@ test("opens a Mermaid diagram fullscreen and closes it again", async ({ page })
await expectCompletedDiagram(page, ["Start", "Ship"]);
});
await test.step("Source toggles preserve the iframe and exclude hidden controls", async () => {
const viewport = page.getByTestId("mermaid-viewport").last();
const iframe = await viewport.locator("iframe").elementHandle();
for (let i = 0; i < 3; i++) {
await viewport.hover();
await page.getByRole("button", { name: "View source" }).last().click();
const viewDiagram = page.getByRole("button", { name: "View diagram" }).last();
if (i === 0) await page.screenshot({ path: test.info().outputPath("source.png") });
await viewDiagram.click();
await expect(viewDiagram).toHaveCount(0);
if (i === 0) await page.screenshot({ path: test.info().outputPath("diagram.png") });
await expect(page.getByTestId("mermaid-fullscreen-viewport")).toHaveCount(0);
expect(
await viewport.locator("iframe").evaluate((node, original) => node === original, iframe),
).toBe(true);
}
await viewport.hover();
await page.getByRole("button", { name: "View source" }).last().click();
await viewport.getByTestId("mermaid-fullscreen").focus();
await expect(viewport.getByTestId("mermaid-fullscreen")).not.toBeFocused();
await page.getByRole("button", { name: "View diagram" }).last().click();
const box = await viewport.boundingBox();
expect(box!.height).toBeGreaterThanOrEqual(56);
expect(box!.height).toBeLessThan(480);
await viewport.hover();
await page.getByRole("button", { name: "Zoom in" }).last().click();
await expect(page.getByRole("button", { name: "Reset view" }).last()).toBeEnabled();
await page.getByRole("button", { name: "Reset view" }).last().click();
await expect(page.getByRole("button", { name: "Reset view" }).last()).toBeDisabled();
await test.step("Source toggles preserve the diagram and exclude hidden controls", async () => {
await toggleDiagramSourceAndVerifyControls(page);
});
await test.step("The toolbar opens the diagram fullscreen", async () => {
@@ -148,3 +148,59 @@ export async function expectFullscreenDiagram(
export async function expectFullscreenDiagramClosed(page: Page): Promise<void> {
await expect(page.getByTestId(FULLSCREEN_VIEWPORT)).toHaveCount(0);
}
/** Tall diagrams stay within the screen so their controls remain reachable. */
export async function expectInlineDiagramFitsViewport(page: Page): Promise<void> {
const viewport = page.getByTestId("mermaid-viewport").last();
const box = await viewport.boundingBox();
expect(box, "the inline diagram has measurable layout").not.toBeNull();
const screenHeight = page.viewportSize()!.height;
expect(box!.height, "the inline diagram fits within the screen").toBeLessThan(screenHeight);
const contentHeight = await diagramSvg(page, "mermaid-viewport").evaluate(
(svg) => (svg as SVGSVGElement).viewBox.baseVal.height,
);
expect(box!.height, "a tall diagram is bounded rather than expanding the chat").toBeLessThan(
contentHeight,
);
}
/** Exercise pointer toggles and the controls encountered along that same journey. */
export async function toggleDiagramSourceAndVerifyControls(page: Page): Promise<void> {
const viewport = page.getByTestId("mermaid-viewport").last();
const iframe = await viewport.locator("iframe").elementHandle();
for (let iteration = 0; iteration < 3; iteration++) {
await viewport.hover();
await page.getByRole("button", { name: "View source" }).last().click();
const viewDiagram = page.getByRole("button", { name: "View diagram" }).last();
await viewDiagram.click({ timeout: 10_000 });
await expect(viewDiagram).toHaveCount(0);
await expectFullscreenDiagramClosed(page);
expect(
await viewport.locator("iframe").evaluate((node, original) => node === original, iframe),
"switching views preserves the rendered diagram",
).toBe(true);
}
await viewport.hover();
await page.getByRole("button", { name: "View source" }).last().click();
const hiddenFullscreen = viewport.getByRole("button", {
name: "Fullscreen",
includeHidden: true,
});
await hiddenFullscreen.focus();
await expect(hiddenFullscreen).not.toBeFocused();
await page.getByRole("button", { name: "View diagram" }).last().click();
await viewport.hover();
const fullscreen = viewport.getByRole("button", { name: "Fullscreen", exact: true });
const box = await viewport.boundingBox();
const control = await fullscreen.boundingBox();
expect(box).not.toBeNull();
expect(control).not.toBeNull();
expect(control!.y, "the toolbar starts inside the preview").toBeGreaterThanOrEqual(box!.y);
expect(control!.y + control!.height, "the toolbar fits inside the preview").toBeLessThanOrEqual(
box!.y + box!.height,
);
await viewport.getByRole("button", { name: "Zoom in" }).click();
await expect(viewport.getByRole("button", { name: "Reset view" })).toBeEnabled();
await viewport.getByRole("button", { name: "Reset view" }).click();
await expect(viewport.getByRole("button", { name: "Reset view" })).toBeDisabled();
}