diff --git a/packages/app/e2e/browser/mermaid-fullscreen.spec.ts b/packages/app/e2e/browser/mermaid-fullscreen.spec.ts index 27eacf1b7b..4b458c4d03 100644 --- a/packages/app/e2e/browser/mermaid-fullscreen.spec.ts +++ b/packages/app/e2e/browser/mermaid-fullscreen.spec.ts @@ -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 () => { diff --git a/packages/app/e2e/support/helpers/diagram.ts b/packages/app/e2e/support/helpers/diagram.ts index 82fd745de1..48ecc60086 100644 --- a/packages/app/e2e/support/helpers/diagram.ts +++ b/packages/app/e2e/support/helpers/diagram.ts @@ -148,3 +148,59 @@ export async function expectFullscreenDiagram( export async function expectFullscreenDiagramClosed(page: Page): Promise { 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 { + 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 { + 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(); +}