102 lines
3.5 KiB
TypeScript
102 lines
3.5 KiB
TypeScript
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);
|
|
});
|
|
});
|