import { describe, expect, test } from "bun:test"; import { getMatchingClassTokens, registerAIToolbarMarkupHooks, renderToolbarMarkup, } from "./helpers/ai-toolbar-test-helpers"; registerAIToolbarMarkupHooks(); describe("AI toolbar layout contracts", () => { test("desktop composer uses a dedicated multi-column branch while mobile stays single-column", async () => { const desktopMarkup = await renderToolbarMarkup(); const mobileMarkup = await renderToolbarMarkup({}, { isMobile: true }); const desktopLayoutTokens = getMatchingClassTokens( desktopMarkup, (tokens) => tokens.includes("grid") && tokens.some((token) => token.startsWith("grid-cols-[minmax(0,0.7fr)")), ); const mobileLayoutTokens = getMatchingClassTokens( mobileMarkup, (tokens) => tokens.includes("grid") && tokens.includes("gap-3"), ); expect(desktopLayoutTokens).toHaveLength(1); expect(desktopMarkup).toContain("Keyboard shortcuts"); expect(desktopMarkup).toContain("Attachments"); expect(mobileLayoutTokens).toHaveLength(1); expect(mobileMarkup).not.toContain("Keyboard shortcuts"); expect( mobileLayoutTokens[0].some((token) => token.startsWith("grid-cols-")), ).toBe(false); }); test("example prompts render as a masonry-style cluster below the textarea", async () => { const markup = await renderToolbarMarkup(); const masonryColumns = getMatchingClassTokens( markup, (tokens) => tokens.some((token) => token.startsWith("columns-")), ); const masonryWrappers = getMatchingClassTokens( markup, (tokens) => tokens.includes("break-inside-avoid"), ); const promptButtons = getMatchingClassTokens( markup, (tokens) => tokens.includes("justify-start") && tokens.includes("text-left") && tokens.includes("whitespace-normal"), ); expect(markup).toContain("Try:"); expect(markup).toContain( "Lunch with Maya next Thursday at 12:30pm at Toma, remind me 30 minutes before.", ); expect(markup).toContain( "Project sync tomorrow from 9am to 10am on Google Meet with a weekly repeat.", ); expect(markup).toContain( "Dentist appointment on May 14 at 3pm at Smile Studio, add confirmation #A4821.", ); expect(masonryColumns).toHaveLength(1); expect(masonryColumns[0]).toEqual( expect.arrayContaining(["columns-2", "gap-2"]), ); expect(masonryWrappers).toHaveLength(3); expect(promptButtons).toHaveLength(3); }); test("attachments render as a separate surfaced panel with count badge, picker, and empty state", async () => { const markup = await renderToolbarMarkup(); expect(markup).toContain("Attachments"); expect(markup).toContain("0 files"); expect(markup).toContain("Attach images"); expect(markup).toContain( "Drop or paste images here to pair them with the prompt.", ); }); test("attachment previews render in a stacked grid instead of a multi-column strip", async () => { const markup = await renderToolbarMarkup({ imagePreviews: ["blob:first", "blob:second"], }); const previewGridTokens = getMatchingClassTokens( markup, (tokens) => tokens.includes("grid") && tokens.includes("gap-2"), ); const multiColumnPreviewPattern = /(?:^|\s)(?:[a-z]+:)*(?:grid-cols-(?:\[[^\]]+\]|\S+)|grid-flow-col|auto-cols-(?:\[[^\]]+\]|\S+)|columns-(?:\[[^\]]+\]|\S+)|overflow-x-auto|flex-row)/; expect(markup).toContain("Attached image 1"); expect(markup).toContain("Attached image 2"); expect( previewGridTokens.some( (tokens) => !tokens.join(" ").match(multiColumnPreviewPattern), ), ).toBe(true); }); });