test(ai-toolbar): cover normalized clipboard paste paths
This commit is contained in:
@@ -405,14 +405,17 @@ describe("AI toolbar paste capture", () => {
|
||||
const { textareaProps } = await renderToolbarBoundary({ onImagesSelect });
|
||||
const preventDefault = mock();
|
||||
|
||||
textareaProps.onPaste?.({
|
||||
await textareaProps.onPaste?.({
|
||||
clipboardData: createClipboardData([image]),
|
||||
preventDefault,
|
||||
} as unknown as React.ClipboardEvent<HTMLTextAreaElement>);
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
|
||||
expect(preventDefault).toHaveBeenCalledTimes(1);
|
||||
expect(onImagesSelect).toHaveBeenCalledTimes(1);
|
||||
expect(onImagesSelect).toHaveBeenCalledWith([image]);
|
||||
expect(onImagesSelect.mock.calls[0]?.[0][0]?.name).toMatch(
|
||||
/^clipboard-image-/,
|
||||
);
|
||||
});
|
||||
|
||||
test("textarea-targeted paste bypasses the document listeners so the focused textarea owns the paste", async () => {
|
||||
@@ -446,12 +449,15 @@ describe("AI toolbar paste capture", () => {
|
||||
expect(preventDefault).not.toHaveBeenCalled();
|
||||
expect(onImagesSelect).not.toHaveBeenCalled();
|
||||
|
||||
textareaProps.onPaste?.(
|
||||
await textareaProps.onPaste?.(
|
||||
pasteEvent as unknown as React.ClipboardEvent<HTMLTextAreaElement>,
|
||||
);
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
expect(preventDefault).toHaveBeenCalledTimes(1);
|
||||
expect(onImagesSelect).toHaveBeenCalledTimes(1);
|
||||
expect(onImagesSelect).toHaveBeenCalledWith([image]);
|
||||
expect(onImagesSelect.mock.calls[0]?.[0][0]?.name).toMatch(
|
||||
/^clipboard-image-/,
|
||||
);
|
||||
|
||||
handleDocumentPaste(pasteEvent as unknown as Event);
|
||||
expect(preventDefault).toHaveBeenCalledTimes(1);
|
||||
@@ -524,6 +530,24 @@ describe("AI toolbar paste capture", () => {
|
||||
expect(onAiCreate).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("Mod+Enter ignores Alt-modified submissions", async () => {
|
||||
const onAiCreate = mock();
|
||||
|
||||
const { textareaProps } = await renderToolbarBoundary({
|
||||
aiPrompt: "Draft a kickoff",
|
||||
onAiCreate,
|
||||
});
|
||||
const event = createTextareaKeydownEvent({
|
||||
ctrlKey: true,
|
||||
altKey: true,
|
||||
});
|
||||
|
||||
textareaProps.onKeyDown?.(event);
|
||||
|
||||
expect(event.preventDefault).not.toHaveBeenCalled();
|
||||
expect(onAiCreate).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("Mod+Enter does not trigger AI generation while loading", async () => {
|
||||
const onAiCreate = mock();
|
||||
|
||||
@@ -627,10 +651,13 @@ describe("AI toolbar paste capture", () => {
|
||||
clipboardData: createClipboardData([image]),
|
||||
preventDefault,
|
||||
} as unknown as Event);
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
|
||||
expect(preventDefault).toHaveBeenCalledTimes(1);
|
||||
expect(onImagesSelect).toHaveBeenCalledTimes(1);
|
||||
expect(onImagesSelect).toHaveBeenCalledWith([image]);
|
||||
expect(onImagesSelect.mock.calls[0]?.[0][0]?.name).toMatch(
|
||||
/^clipboard-image-/,
|
||||
);
|
||||
});
|
||||
|
||||
test("Ctrl/Cmd+V fallback forwards clipboard images for non-editable targets", async () => {
|
||||
@@ -731,6 +758,45 @@ describe("AI toolbar paste capture", () => {
|
||||
);
|
||||
});
|
||||
|
||||
test("clipboard fallback keeps the same synthesized identity across MIME aliases for identical content", async () => {
|
||||
const onImagesSelect = mock();
|
||||
const reads = [
|
||||
async () => [
|
||||
{
|
||||
types: ["image/png"],
|
||||
getType: async () => new Blob(["same-image"], { type: "image/png" }),
|
||||
},
|
||||
],
|
||||
async () => [
|
||||
{
|
||||
types: ["image/x-png"],
|
||||
getType: async () => new Blob(["same-image"], { type: "image/x-png" }),
|
||||
},
|
||||
],
|
||||
];
|
||||
const clipboardRead = mock(async () => (await reads.shift()?.()) ?? []);
|
||||
|
||||
(globalThis as { navigator?: Navigator }).navigator = {
|
||||
clipboard: { read: clipboardRead },
|
||||
} as Navigator;
|
||||
|
||||
await renderToolbar({ onImagesSelect });
|
||||
|
||||
const handleDocumentKeydown = getDocumentListener("keydown");
|
||||
|
||||
await handleDocumentKeydown(
|
||||
createDocumentKeydownEvent({ ctrlKey: true }),
|
||||
);
|
||||
await handleDocumentKeydown(
|
||||
createDocumentKeydownEvent({ ctrlKey: true }),
|
||||
);
|
||||
|
||||
expect(onImagesSelect).toHaveBeenCalledTimes(2);
|
||||
expect(onImagesSelect.mock.calls[0]?.[0][0]?.name).toBe(
|
||||
onImagesSelect.mock.calls[1]?.[0][0]?.name,
|
||||
);
|
||||
});
|
||||
|
||||
test("async clipboard fallback does not double-handle a paste already handled by the synchronous document paste flow", async () => {
|
||||
const onImagesSelect = mock();
|
||||
const clipboardRead = mock(async () => [
|
||||
@@ -778,7 +844,9 @@ describe("AI toolbar paste capture", () => {
|
||||
expect(preventDefault).toHaveBeenCalledTimes(1);
|
||||
expect(clipboardRead).not.toHaveBeenCalled();
|
||||
expect(onImagesSelect).toHaveBeenCalledTimes(1);
|
||||
expect(onImagesSelect).toHaveBeenCalledWith([image]);
|
||||
expect(onImagesSelect.mock.calls[0]?.[0][0]?.name).toMatch(
|
||||
/^clipboard-image-/,
|
||||
);
|
||||
});
|
||||
|
||||
test("global paste listeners only arm when AI paste capture is usable, and disarm on cleanup", async () => {
|
||||
|
||||
Reference in New Issue
Block a user