test(ai-toolbar): split coverage and add fallback failure cases
This commit is contained in:
213
tests/ai-toolbar-shortcuts.test.ts
Normal file
213
tests/ai-toolbar-shortcuts.test.ts
Normal file
@@ -0,0 +1,213 @@
|
||||
import { describe, expect, mock, test } from "bun:test";
|
||||
import {
|
||||
createTextareaKeydownEvent,
|
||||
registerAIToolbarTestHooks,
|
||||
renderToolbarBoundary,
|
||||
} from "./helpers/ai-toolbar-test-helpers";
|
||||
|
||||
registerAIToolbarTestHooks();
|
||||
|
||||
describe("AI toolbar keyboard shortcuts", () => {
|
||||
test("Ctrl+Enter triggers AI generation when the prompt has content", async () => {
|
||||
const onAiCreate = mock();
|
||||
|
||||
const { textareaProps } = await renderToolbarBoundary({
|
||||
aiPrompt: "Draft a kickoff",
|
||||
onAiCreate,
|
||||
});
|
||||
const event = createTextareaKeydownEvent({ ctrlKey: true });
|
||||
|
||||
textareaProps.onKeyDown?.(event);
|
||||
|
||||
expect(event.preventDefault).toHaveBeenCalledTimes(1);
|
||||
expect(onAiCreate).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test("Cmd+Enter triggers AI generation when images are attached", async () => {
|
||||
const onAiCreate = mock();
|
||||
|
||||
const { textareaProps } = await renderToolbarBoundary({
|
||||
imagePreviews: ["blob:first"],
|
||||
onAiCreate,
|
||||
});
|
||||
const event = createTextareaKeydownEvent({ metaKey: true });
|
||||
|
||||
textareaProps.onKeyDown?.(event);
|
||||
|
||||
expect(event.preventDefault).toHaveBeenCalledTimes(1);
|
||||
expect(onAiCreate).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test("Mod+Enter ignores extra modifiers so Shift+Ctrl+Enter does not generate", async () => {
|
||||
const onAiCreate = mock();
|
||||
|
||||
const { textareaProps } = await renderToolbarBoundary({
|
||||
aiPrompt: "Draft a kickoff",
|
||||
onAiCreate,
|
||||
});
|
||||
const event = createTextareaKeydownEvent({
|
||||
ctrlKey: true,
|
||||
shiftKey: true,
|
||||
});
|
||||
|
||||
textareaProps.onKeyDown?.(event);
|
||||
|
||||
expect(event.preventDefault).not.toHaveBeenCalled();
|
||||
expect(onAiCreate).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("Mod+Enter ignores combined Ctrl+Meta modifiers", async () => {
|
||||
const onAiCreate = mock();
|
||||
|
||||
const { textareaProps } = await renderToolbarBoundary({
|
||||
aiPrompt: "Draft a kickoff",
|
||||
onAiCreate,
|
||||
});
|
||||
const event = createTextareaKeydownEvent({
|
||||
ctrlKey: true,
|
||||
metaKey: true,
|
||||
});
|
||||
|
||||
textareaProps.onKeyDown?.(event);
|
||||
|
||||
expect(event.preventDefault).not.toHaveBeenCalled();
|
||||
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();
|
||||
|
||||
const { textareaProps } = await renderToolbarBoundary({
|
||||
aiPrompt: "Draft a kickoff",
|
||||
aiLoading: true,
|
||||
onAiCreate,
|
||||
});
|
||||
const event = createTextareaKeydownEvent({ ctrlKey: true });
|
||||
|
||||
textareaProps.onKeyDown?.(event);
|
||||
|
||||
expect(event.preventDefault).not.toHaveBeenCalled();
|
||||
expect(onAiCreate).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("Shift+Mod+A opens the image picker when the composer is idle", async () => {
|
||||
const { textareaProps, imageTriggerOpen } = await renderToolbarBoundary();
|
||||
const ctrlEvent = createTextareaKeydownEvent({
|
||||
key: "A",
|
||||
ctrlKey: true,
|
||||
shiftKey: true,
|
||||
});
|
||||
|
||||
textareaProps.onKeyDown?.(ctrlEvent);
|
||||
|
||||
expect(ctrlEvent.preventDefault).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 () => {
|
||||
const { textareaProps, imageTriggerOpen } = await renderToolbarBoundary();
|
||||
const metaEvent = createTextareaKeydownEvent({
|
||||
key: "A",
|
||||
metaKey: true,
|
||||
shiftKey: true,
|
||||
});
|
||||
|
||||
textareaProps.onKeyDown?.(metaEvent);
|
||||
|
||||
expect(metaEvent.preventDefault).toHaveBeenCalledTimes(1);
|
||||
expect(imageTriggerOpen).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test("Shift+Mod+A ignores Alt-modified submissions", async () => {
|
||||
const { textareaProps, imageTriggerOpen } = await renderToolbarBoundary();
|
||||
const event = createTextareaKeydownEvent({
|
||||
key: "A",
|
||||
ctrlKey: true,
|
||||
shiftKey: true,
|
||||
altKey: true,
|
||||
});
|
||||
|
||||
textareaProps.onKeyDown?.(event);
|
||||
|
||||
expect(event.preventDefault).not.toHaveBeenCalled();
|
||||
expect(imageTriggerOpen).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("Shift+Mod+A ignores combined Ctrl+Meta modifiers", async () => {
|
||||
const { textareaProps, imageTriggerOpen } = await renderToolbarBoundary();
|
||||
const event = createTextareaKeydownEvent({
|
||||
key: "A",
|
||||
ctrlKey: true,
|
||||
metaKey: true,
|
||||
shiftKey: true,
|
||||
});
|
||||
|
||||
textareaProps.onKeyDown?.(event);
|
||||
|
||||
expect(event.preventDefault).not.toHaveBeenCalled();
|
||||
expect(imageTriggerOpen).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("Shift+Mod+A stays disabled while generation is in progress", async () => {
|
||||
const { textareaProps, imageTriggerOpen } = await renderToolbarBoundary({
|
||||
aiLoading: true,
|
||||
});
|
||||
const ctrlEvent = createTextareaKeydownEvent({
|
||||
key: "A",
|
||||
ctrlKey: true,
|
||||
shiftKey: true,
|
||||
});
|
||||
|
||||
textareaProps.onKeyDown?.(ctrlEvent);
|
||||
|
||||
expect(ctrlEvent.preventDefault).not.toHaveBeenCalled();
|
||||
expect(imageTriggerOpen).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("Escape clears the prompt when the textarea has content", async () => {
|
||||
const setAiPrompt = mock();
|
||||
|
||||
const { textareaProps } = await renderToolbarBoundary({
|
||||
aiPrompt: "Draft a kickoff",
|
||||
setAiPrompt,
|
||||
});
|
||||
const event = createTextareaKeydownEvent({ key: "Escape" });
|
||||
|
||||
textareaProps.onKeyDown?.(event);
|
||||
|
||||
expect(event.preventDefault).toHaveBeenCalledTimes(1);
|
||||
expect(setAiPrompt).toHaveBeenCalledTimes(1);
|
||||
expect(setAiPrompt).toHaveBeenCalledWith("");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user