Files
codex/codex-cli/tests/drag-drop-attach-image.test.tsx
2025-04-26 23:16:47 -07:00

137 lines
3.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Dropping / pasting an image path into the chat input should immediately move
// that image into the attached-images preview and remove the path from the draft
// text.
import fs from "node:fs";
import path from "node:path";
import React from "react";
import { beforeAll, afterAll, describe, expect, it, vi } from "vitest";
import { renderTui } from "./ui-test-helpers.js";
// ---------------------------------------------------------------------------
// Mocks keep in sync with other TerminalChatInput UI tests
// ---------------------------------------------------------------------------
// mock without type annotations to avoid Vitest transform TS errors in JS test
const createInputItemMock = vi.fn(async () => ({}));
vi.mock("../src/utils/input-utils.js", () => ({
createInputItem: createInputItemMock,
imageFilenameByDataUrl: new Map(),
}));
vi.mock("../src/approvals.js", () => ({ isSafeCommand: () => null }));
vi.mock("../src/format-command.js", () => ({
formatCommandForDisplay: (c: Array<string>): string => c.join(" "),
}));
import TerminalChatInput from "../src/components/chat/terminal-chat-input.js";
async function type(
stdin: NodeJS.WritableStream & { write(str: string): void },
text: string,
flush: () => Promise<void>,
): Promise<void> {
stdin.write(text);
await flush();
}
function props() {
return {
isNew: true,
loading: false,
submitInput: () => {},
confirmationPrompt: null,
submitConfirmation: () => {},
setLastResponseId: () => {},
setItems: () => {},
contextLeftPercent: 100,
openOverlay: () => {},
openModelOverlay: () => {},
openApprovalOverlay: () => {},
openHelpOverlay: () => {},
interruptAgent: () => {},
active: true,
onCompact: () => {},
};
}
describe("Drag-and-drop image attachment", () => {
const TMP = path.join(process.cwd(), "drag-drop-image-test");
const IMG = path.join(TMP, "dropped.png");
beforeAll(() => {
fs.mkdirSync(TMP, { recursive: true });
fs.writeFileSync(IMG, "");
});
afterAll(() => {
fs.rmSync(TMP, { recursive: true, force: true });
});
it("moves pasted path to attachment preview", async () => {
process.env.DEBUG_TCI = "1";
const orig = process.cwd();
process.chdir(TMP);
const { stdin, flush, lastFrameStripped, cleanup } = renderTui(
React.createElement(TerminalChatInput, props()),
);
await flush(); // initial render
// Simulate user pasting the bare filename (as most terminals do when you
// drag a file).
await type(stdin, "dropped.png ", flush);
await flush();
// A second flush to allow state updates triggered asynchronously by
// setState inside the onChange handler.
await flush();
let frame = lastFrameStripped();
expect(frame.match(/dropped\.png/g)?.length ?? 0).toBe(1);
// Now submit the message.
await type(stdin, "\r", flush);
await flush();
// createInputItem should have been called with the dropped image path
expect(createInputItemMock).toHaveBeenCalled();
const calls = createInputItemMock.mock.calls;
const lastCall = calls[calls.length - 1];
expect(lastCall?.[1]).toEqual(["dropped.png"]);
cleanup();
process.chdir(orig);
});
it("does NOT show slash-command overlay for absolute paths", async () => {
const orig = process.cwd();
process.chdir(TMP);
const { stdin, flush, lastFrameStripped, cleanup } = renderTui(
React.createElement(TerminalChatInput, props()),
);
await flush();
// absolute path starting with '/'
const absPath = path.join(TMP, "dropped.png");
await type(stdin, `${absPath} `, flush);
await flush();
const frame = lastFrameStripped();
// Should contain attachment preview but NOT typical slash-command suggestion like "/help"
expect(frame).toContain("dropped.png");
expect(frame).not.toContain("/help");
cleanup();
process.chdir(orig);
});
});