test(ai-toolbar): close remaining modifier and identity gaps
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { renderToStaticMarkup } from "react-dom/server";
|
import { renderToStaticMarkup } from "react-dom/server";
|
||||||
|
import { imageFileKey } from "@/lib/multi-image";
|
||||||
|
|
||||||
type AIToolbarProps = {
|
type AIToolbarProps = {
|
||||||
adminAiEnabled: boolean;
|
adminAiEnabled: boolean;
|
||||||
@@ -586,6 +587,19 @@ describe("AI toolbar paste capture", () => {
|
|||||||
expect(imageTriggerOpen).toHaveBeenCalledTimes(1);
|
expect(imageTriggerOpen).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("Mod+A without Shift does not open the image picker", async () => {
|
||||||
|
const { textareaProps, imageTriggerOpen } = await renderToolbarBoundary();
|
||||||
|
const event = createTextareaKeydownEvent({
|
||||||
|
key: "A",
|
||||||
|
ctrlKey: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
textareaProps.onKeyDown?.(event);
|
||||||
|
|
||||||
|
expect(event.preventDefault).not.toHaveBeenCalled();
|
||||||
|
expect(imageTriggerOpen).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
test("Shift+Cmd+A also opens the image picker when the composer is idle", async () => {
|
test("Shift+Cmd+A also opens the image picker when the composer is idle", async () => {
|
||||||
const { textareaProps, imageTriggerOpen } = await renderToolbarBoundary();
|
const { textareaProps, imageTriggerOpen } = await renderToolbarBoundary();
|
||||||
const metaEvent = createTextareaKeydownEvent({
|
const metaEvent = createTextareaKeydownEvent({
|
||||||
@@ -748,6 +762,69 @@ describe("AI toolbar paste capture", () => {
|
|||||||
expect(onImagesSelect).toHaveBeenCalledTimes(1);
|
expect(onImagesSelect).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("Shift+Mod+V does not trigger the async clipboard fallback", async () => {
|
||||||
|
const onImagesSelect = mock();
|
||||||
|
const clipboardRead = mock(async () => []);
|
||||||
|
|
||||||
|
(globalThis as { navigator?: Navigator }).navigator = {
|
||||||
|
clipboard: { read: clipboardRead },
|
||||||
|
} as Navigator;
|
||||||
|
|
||||||
|
await renderToolbar({ onImagesSelect });
|
||||||
|
|
||||||
|
const handleDocumentKeydown = getDocumentListener("keydown");
|
||||||
|
|
||||||
|
await handleDocumentKeydown(
|
||||||
|
createDocumentKeydownEvent({ ctrlKey: true, shiftKey: true }),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(clipboardRead).not.toHaveBeenCalled();
|
||||||
|
expect(onImagesSelect).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Alt+Mod+V does not trigger the async clipboard fallback", async () => {
|
||||||
|
const onImagesSelect = mock();
|
||||||
|
const clipboardRead = mock(async () => []);
|
||||||
|
|
||||||
|
(globalThis as { navigator?: Navigator }).navigator = {
|
||||||
|
clipboard: { read: clipboardRead },
|
||||||
|
} as Navigator;
|
||||||
|
|
||||||
|
await renderToolbar({ onImagesSelect });
|
||||||
|
|
||||||
|
const handleDocumentKeydown = getDocumentListener("keydown");
|
||||||
|
|
||||||
|
await handleDocumentKeydown(
|
||||||
|
createDocumentKeydownEvent({ ctrlKey: true, altKey: true }),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(clipboardRead).not.toHaveBeenCalled();
|
||||||
|
expect(onImagesSelect).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("async clipboard fallback ignores editable targets", async () => {
|
||||||
|
const onImagesSelect = mock();
|
||||||
|
const clipboardRead = mock(async () => []);
|
||||||
|
|
||||||
|
(globalThis as { navigator?: Navigator }).navigator = {
|
||||||
|
clipboard: { read: clipboardRead },
|
||||||
|
} as Navigator;
|
||||||
|
|
||||||
|
await renderToolbar({ onImagesSelect });
|
||||||
|
|
||||||
|
const handleDocumentKeydown = getDocumentListener("keydown");
|
||||||
|
|
||||||
|
await handleDocumentKeydown(
|
||||||
|
createDocumentKeydownEvent({
|
||||||
|
ctrlKey: true,
|
||||||
|
target: { tagName: "DIV", isContentEditable: true },
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(clipboardRead).not.toHaveBeenCalled();
|
||||||
|
expect(onImagesSelect).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
test("Ctrl+Meta+V does not trigger the async clipboard fallback", async () => {
|
test("Ctrl+Meta+V does not trigger the async clipboard fallback", async () => {
|
||||||
const onImagesSelect = mock();
|
const onImagesSelect = mock();
|
||||||
const clipboardRead = mock(async () => []);
|
const clipboardRead = mock(async () => []);
|
||||||
@@ -918,6 +995,9 @@ describe("AI toolbar paste capture", () => {
|
|||||||
expect(syncOnImagesSelect.mock.calls[0]?.[0][0]?.lastModified).toBe(
|
expect(syncOnImagesSelect.mock.calls[0]?.[0][0]?.lastModified).toBe(
|
||||||
asyncOnImagesSelect.mock.calls[0]?.[0][0]?.lastModified,
|
asyncOnImagesSelect.mock.calls[0]?.[0][0]?.lastModified,
|
||||||
);
|
);
|
||||||
|
expect(
|
||||||
|
imageFileKey(syncOnImagesSelect.mock.calls[0]?.[0][0]),
|
||||||
|
).toBe(imageFileKey(asyncOnImagesSelect.mock.calls[0]?.[0][0]));
|
||||||
});
|
});
|
||||||
|
|
||||||
test("async clipboard fallback does not double-handle a paste already handled by the synchronous document paste flow", async () => {
|
test("async clipboard fallback does not double-handle a paste already handled by the synchronous document paste flow", async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user