deer-flow/frontend/tests/unit/core/uploads/prompt-input-files.test.ts
yangzheli 4efc8d404f
feat(frontend): set up Vitest frontend testing infrastructure with CI workflow (#2147)
* feat: set up Vitest frontend testing infrastructure with CI workflow

Migrate existing 4 frontend test files from Node.js native test runner
(node:test + node:assert/strict) to Vitest, reorganize test directory
structure under tests/unit/ mirroring src/ layout, and add a dedicated
CI workflow for frontend unit tests.

- Add vitest as devDependency, remove tsx
- Create vitest.config.ts with @/ path alias
- Migrate tests to Vitest API (test/expect/vi)
- Rename .mjs test files to .ts
- Move tests from src/ to tests/unit/ (mirrors src/ layout)
- Add frontend/Makefile `test` target
- Add .github/workflows/frontend-unit-tests.yml (parallel to backend)
- Update CONTRIBUTING.md, README.md, AGENTS.md, CLAUDE.md

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style: fix the lint error

* style: fix the lint error

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-04-12 18:00:43 +08:00

121 lines
3.2 KiB
TypeScript

import { afterEach, expect, test, vi } from "vitest";
import {
type PromptInputFilePart,
promptInputFilePartToFile,
} from "@/core/uploads/prompt-input-files";
afterEach(() => {
vi.restoreAllMocks();
vi.unstubAllGlobals();
});
test("exports the prompt-input file conversion helper", () => {
expect(typeof promptInputFilePartToFile).toBe("function");
});
test("reuses the original File when a prompt attachment already has one", async () => {
const file = new File(["hello"], "note.txt", { type: "text/plain" });
vi.stubGlobal(
"fetch",
vi.fn(() => {
throw new Error("fetch should not run when File is already present");
}),
);
const converted = await promptInputFilePartToFile({
type: "file",
filename: file.name,
mediaType: file.type,
url: "blob:http://localhost:2026/stale-preview-url",
file,
});
expect(converted).toBe(file);
});
test("reconstructs a File from a data URL when no original File is present", async () => {
const converted = await promptInputFilePartToFile({
type: "file",
filename: "note.txt",
mediaType: "text/plain",
url: "data:text/plain;base64,aGVsbG8=",
});
expect(converted).toBeTruthy();
expect(converted!.name).toBe("note.txt");
expect(converted!.type).toBe("text/plain");
expect(await converted!.text()).toBe("hello");
});
test("rewraps the original File when the prompt metadata changes", async () => {
const file = new File(["hello"], "note.txt", { type: "text/plain" });
const converted = await promptInputFilePartToFile({
type: "file",
filename: "renamed.txt",
mediaType: "text/markdown",
file,
} as PromptInputFilePart);
expect(converted).toBeTruthy();
expect(converted).not.toBe(file);
expect(converted!.name).toBe("renamed.txt");
expect(converted!.type).toBe("text/markdown");
expect(await converted!.text()).toBe("hello");
});
test("returns null when upload preparation is missing required data", async () => {
const converted = await promptInputFilePartToFile({
type: "file",
mediaType: "text/plain",
} as PromptInputFilePart);
expect(converted).toBeNull();
});
test("returns null when the URL fallback fetch fails", async () => {
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => ({}));
vi.stubGlobal(
"fetch",
vi.fn(async () => {
throw new Error("network down");
}),
);
const converted = await promptInputFilePartToFile({
type: "file",
filename: "note.txt",
url: "blob:http://localhost:2026/missing-preview-url",
} as PromptInputFilePart);
expect(converted).toBeNull();
expect(warnSpy).toHaveBeenCalledOnce();
});
test("returns null when the URL fallback fetch response is non-ok", async () => {
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => ({}));
vi.stubGlobal(
"fetch",
vi.fn(
async () =>
new Response("missing", {
status: 404,
statusText: "Not Found",
}),
),
);
const converted = await promptInputFilePartToFile({
type: "file",
filename: "note.txt",
url: "blob:http://localhost:2026/missing-preview-url",
} as PromptInputFilePart);
expect(converted).toBeNull();
expect(warnSpy).toHaveBeenCalledOnce();
});