test(ai-toolbar): cover paste gating behavior

This commit is contained in:
2026-04-22 17:58:14 -04:00
parent bfb29d3986
commit 9f23597e53
2 changed files with 69 additions and 8 deletions

View File

@@ -94,6 +94,22 @@ interface AIToolbarProps {
events: CalendarEvent[]; events: CalendarEvent[];
} }
interface GlobalPasteCaptureGate {
isAuthenticated: boolean;
isPending: boolean;
canUseAi: boolean;
aiLoading: boolean;
}
export function shouldEnableGlobalPasteCapture({
isAuthenticated,
isPending,
canUseAi,
aiLoading,
}: GlobalPasteCaptureGate): boolean {
return isAuthenticated && !isPending && canUseAi && !aiLoading;
}
// ─── Component ──────────────────────────────────────────────────────────────── // ─── Component ────────────────────────────────────────────────────────────────
export const AIToolbar = ({ export const AIToolbar = ({
@@ -154,7 +170,15 @@ export const AIToolbar = ({
// focused element or OS clipboard model (X11/Wayland). // focused element or OS clipboard model (X11/Wayland).
// This is the approach used by Excalidraw's actionPaste. // This is the approach used by Excalidraw's actionPaste.
useEffect(() => { useEffect(() => {
if (!isAuthenticated || isPending || !canUseAi || aiLoading) return; if (
!shouldEnableGlobalPasteCapture({
isAuthenticated,
isPending,
canUseAi,
aiLoading,
})
)
return;
// ── Handler 1: paste event (works when textarea is NOT focused) ─────── // ── Handler 1: paste event (works when textarea is NOT focused) ───────
const handleDocumentPaste = (e: ClipboardEvent) => { const handleDocumentPaste = (e: ClipboardEvent) => {

View File

@@ -1,5 +1,6 @@
import { describe, expect, test } from "bun:test"; import { describe, expect, test } from "bun:test";
import { readFileSync } from "node:fs"; import { readFileSync } from "node:fs";
import { shouldEnableGlobalPasteCapture } from "@/components/ai-toolbar";
import { buttonVariants } from "@/components/ui/button"; import { buttonVariants } from "@/components/ui/button";
import { getAiDisabledMessage } from "@/lib/ai-feature-flags"; import { getAiDisabledMessage } from "@/lib/ai-feature-flags";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
@@ -325,14 +326,50 @@ describe("Keyboard shortcuts toolbar integration contract", () => {
describe("Global paste capture AI availability gate", () => { describe("Global paste capture AI availability gate", () => {
test("document-level paste handlers only arm when AI is actually usable", () => { test("document-level paste handlers only arm when AI is actually usable", () => {
const source = readToolbarSource(); expect(
shouldEnableGlobalPasteCapture({
isAuthenticated: true,
isPending: false,
canUseAi: true,
aiLoading: false,
}),
).toBe(true);
expect(source).toContain( expect(
"if (!isAuthenticated || isPending || !canUseAi || aiLoading) return;", shouldEnableGlobalPasteCapture({
); isAuthenticated: false,
expect(source).toContain( isPending: false,
"}, [isAuthenticated, isPending, canUseAi, aiLoading]);", canUseAi: true,
); aiLoading: false,
}),
).toBe(false);
expect(
shouldEnableGlobalPasteCapture({
isAuthenticated: true,
isPending: true,
canUseAi: true,
aiLoading: false,
}),
).toBe(false);
expect(
shouldEnableGlobalPasteCapture({
isAuthenticated: true,
isPending: false,
canUseAi: false,
aiLoading: false,
}),
).toBe(false);
expect(
shouldEnableGlobalPasteCapture({
isAuthenticated: true,
isPending: false,
canUseAi: true,
aiLoading: true,
}),
).toBe(false);
}); });
}); });