test(ai-toolbar): cover paste gating behavior
This commit is contained in:
@@ -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) => {
|
||||||
|
|||||||
@@ -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);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user