test(ai-toolbar): cover paste gating behavior
This commit is contained in:
@@ -94,6 +94,22 @@ interface AIToolbarProps {
|
||||
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 ────────────────────────────────────────────────────────────────
|
||||
|
||||
export const AIToolbar = ({
|
||||
@@ -154,7 +170,15 @@ export const AIToolbar = ({
|
||||
// focused element or OS clipboard model (X11/Wayland).
|
||||
// This is the approach used by Excalidraw's actionPaste.
|
||||
useEffect(() => {
|
||||
if (!isAuthenticated || isPending || !canUseAi || aiLoading) return;
|
||||
if (
|
||||
!shouldEnableGlobalPasteCapture({
|
||||
isAuthenticated,
|
||||
isPending,
|
||||
canUseAi,
|
||||
aiLoading,
|
||||
})
|
||||
)
|
||||
return;
|
||||
|
||||
// ── Handler 1: paste event (works when textarea is NOT focused) ───────
|
||||
const handleDocumentPaste = (e: ClipboardEvent) => {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { shouldEnableGlobalPasteCapture } from "@/components/ai-toolbar";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { getAiDisabledMessage } from "@/lib/ai-feature-flags";
|
||||
import { cn } from "@/lib/utils";
|
||||
@@ -325,14 +326,50 @@ describe("Keyboard shortcuts – toolbar integration contract", () => {
|
||||
|
||||
describe("Global paste capture – AI availability gate", () => {
|
||||
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(
|
||||
"if (!isAuthenticated || isPending || !canUseAi || aiLoading) return;",
|
||||
);
|
||||
expect(source).toContain(
|
||||
"}, [isAuthenticated, isPending, canUseAi, aiLoading]);",
|
||||
);
|
||||
expect(
|
||||
shouldEnableGlobalPasteCapture({
|
||||
isAuthenticated: false,
|
||||
isPending: false,
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user