import { describe, expect, mock, test } from "bun:test"; import { getButtonOpeningTag, renderToolbarActionBoundary, registerAIToolbarMarkupHooks, renderToolbarMarkup, } from "./helpers/ai-toolbar-test-helpers"; registerAIToolbarMarkupHooks(); const makeEvent = (id = "event-1") => ({ id, title: "Event", start: "2026-04-22T09:00:00.000Z", }); describe("AI toolbar state rendering", () => { test("unauthenticated users see the sign-in-required callout", async () => { const markup = await renderToolbarMarkup({ isAuthenticated: false }); expect(markup).toContain("Sign in required to generate event drafts with AI"); expect(markup).toContain( "Sign in to turn natural language or flyers into event drafts", ); }); test("admin-disabled state explains that AI is disabled by the administrator", async () => { const markup = await renderToolbarMarkup({ adminAiEnabled: false, isAuthenticated: true, }); expect(markup).toContain("AI integrations are unavailable"); expect(markup).toContain( "AI integrations are currently disabled by the administrator.", ); }); test("browser-disabled state explains that AI was turned off from settings", async () => { const markup = await renderToolbarMarkup({ adminAiEnabled: true, aiEnabled: false, isAuthenticated: true, }); expect(markup).toContain("AI integrations are unavailable"); expect(markup).toContain( "AI has been turned off in this browser from Settings.", ); }); test("summary panel renders the summary body, updated timestamp, and dismiss affordance", async () => { const markup = await renderToolbarMarkup({ summary: "Three events need attention.", summaryUpdated: "Updated just now", }); expect(markup).toContain("AI Summary"); expect(markup).toContain("Three events need attention."); expect(markup).toContain("Updated just now"); expect(markup).toContain("Dismiss AI summary"); }); test("pending state renders loading skeletons instead of the live composer", async () => { const markup = await renderToolbarMarkup({ isPending: true }); expect(markup).toContain("h-[160px]"); expect(markup).toContain("h-12"); expect(markup).not.toContain("Type or paste event details..."); }); test("Summarize only appears when there are events to summarize", async () => { const emptyMarkup = await renderToolbarMarkup({ events: [] }); const populatedMarkup = await renderToolbarMarkup({ events: [makeEvent()], }); expect(emptyMarkup).not.toContain("Summarize"); expect(populatedMarkup).toContain("Summarize"); }); test("Generate event stays disabled while loading or when both prompt and images are absent", async () => { const emptyMarkup = await renderToolbarMarkup(); const loadingMarkup = await renderToolbarMarkup({ aiLoading: true, aiPrompt: "Draft a kickoff", }); const promptMarkup = await renderToolbarMarkup({ aiPrompt: "Draft a kickoff", }); const imageMarkup = await renderToolbarMarkup({ imagePreviews: ["blob:first"], }); expect(getButtonOpeningTag(emptyMarkup, "Generate event")).toContain( ' disabled=""', ); expect(getButtonOpeningTag(loadingMarkup, "Generating...")).toContain( ' disabled=""', ); expect(getButtonOpeningTag(promptMarkup, "Generate event")).not.toContain( " disabled=", ); expect(getButtonOpeningTag(imageMarkup, "Generate event")).not.toContain( " disabled=", ); }); test("Generate event button calls onAiCreate when the composer is actionable", async () => { const onAiCreate = mock(); const actions = await renderToolbarActionBoundary({ aiPrompt: "Draft a kickoff", onAiCreate, }); actions.getButtonByLabel("Generate event").onClick?.({} as never); expect(onAiCreate).toHaveBeenCalledTimes(1); }); test("AI unavailable state removes the composer so generate shortcuts are not exposed", async () => { const markup = await renderToolbarMarkup({ aiEnabled: false }); expect(markup).toContain("AI integrations are unavailable"); expect(markup).not.toContain("Type or paste event details..."); expect(markup).not.toContain("Generate event"); }); test("example prompt buttons call onAiTemplateSelect with the selected prompt", async () => { const onAiTemplateSelectMock = mock(); const actions = await renderToolbarActionBoundary({ onAiTemplateSelect: onAiTemplateSelectMock, }); const promptButton = actions.getButtonByLabel("Lunch with Maya next Thursday"); promptButton.onClick?.({} as never); expect(onAiTemplateSelectMock).toHaveBeenCalledTimes(1); expect(onAiTemplateSelectMock).toHaveBeenCalledWith( "Lunch with Maya next Thursday at 12:30pm at Toma, remind me 30 minutes before.", ); }); test("Summarize button calls onAiSummarize when events are present", async () => { const onAiSummarizeMock = mock(); const actions = await renderToolbarActionBoundary({ events: [makeEvent()], onAiSummarize: onAiSummarizeMock, }); actions .getButtonByLabel("Summarize") .onClick?.({} as never); expect(onAiSummarizeMock).toHaveBeenCalledTimes(1); }); test("summary dismiss button calls onSummaryDismiss", async () => { const onSummaryDismissMock = mock(); const actions = await renderToolbarActionBoundary({ summary: "Three events need attention.", onSummaryDismiss: onSummaryDismissMock, }); actions .getButtonByAriaLabel("Dismiss AI summary") .onClick?.({} as never); expect(onSummaryDismissMock).toHaveBeenCalledTimes(1); }); test("remove image button calls onImageRemove with the preview index", async () => { const onImageRemoveMock = mock(); const actions = await renderToolbarActionBoundary({ imagePreviews: ["blob:first", "blob:second"], onImageRemove: onImageRemoveMock, }); actions .getButtonByAriaLabel("Remove image 2") .onClick?.({} as never); expect(onImageRemoveMock).toHaveBeenCalledTimes(1); expect(onImageRemoveMock).toHaveBeenCalledWith(1); }); });