Files
local-cal/tests/multi-image.test.ts
Dmytro Stanchiev 513aafcebc feat: support multiple image uploads for AI event generation 🖼️
- Updated OpenRouter integration to accept an array of image URLs
- Updated ImagePicker to use the `multiple` attribute natively
- Added `appendImagesDeduped` for handling client-side image deduplication
- Enhanced clipboard pasting to extract multiple images at once
- Rendered multiple images in a horizontal thumbnail strip in the AIToolbar
- Added tests to cover multi-image logic and AI request mapping
2026-04-08 20:46:43 -04:00

104 lines
4.0 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.

import { describe, expect, test } from "bun:test";
import {
appendImagesDeduped,
imageFileKey,
} from "@/lib/multi-image";
// ---------------------------------------------------------------------------
// Multi-image helpers behavioral tests
//
// These functions are pure; they own the "append + dedup" contract.
// Tests describe what the system DOES, not how it's implemented.
// ---------------------------------------------------------------------------
describe("imageFileKey stable identity for dedup", () => {
test("returns a string combining name and size", () => {
const file = new File(["hello"], "flyer.png", { type: "image/png" });
const key = imageFileKey(file);
expect(typeof key).toBe("string");
expect(key).toContain("flyer.png");
expect(key).toContain(String(file.size));
});
test("two files with the same name and size produce the same key", () => {
const a = new File(["hello"], "a.png", { type: "image/png" });
const b = new File(["hello"], "a.png", { type: "image/png" });
expect(imageFileKey(a)).toBe(imageFileKey(b));
});
test("two files with the same name but different content produce different keys", () => {
const a = new File(["hello"], "a.png", { type: "image/png" });
const b = new File(["hello world"], "a.png", { type: "image/png" });
expect(imageFileKey(a)).not.toBe(imageFileKey(b));
});
test("two files with different names but same content produce different keys", () => {
const a = new File(["hello"], "a.png", { type: "image/png" });
const b = new File(["hello"], "b.png", { type: "image/png" });
expect(imageFileKey(a)).not.toBe(imageFileKey(b));
});
});
describe("appendImagesDeduped append with deduplication", () => {
const makeFile = (name: string, content = "data") =>
new File([content], name, { type: "image/png" });
test("appends new files to an empty list", () => {
const result = appendImagesDeduped([], [makeFile("a.png")]);
expect(result).toHaveLength(1);
expect(result[0].name).toBe("a.png");
});
test("appends new files to an existing list", () => {
const existing = [makeFile("a.png")];
const incoming = [makeFile("b.png")];
const result = appendImagesDeduped(existing, incoming);
expect(result).toHaveLength(2);
expect(result.map((f) => f.name)).toEqual(["a.png", "b.png"]);
});
test("silently ignores incoming files that are exact duplicates (same name+size)", () => {
const existing = [makeFile("a.png")];
const incoming = [makeFile("a.png")]; // identical name + content = same size
const result = appendImagesDeduped(existing, incoming);
expect(result).toHaveLength(1);
expect(result[0].name).toBe("a.png");
});
test("appends non-duplicate files even when some duplicates are in the batch", () => {
const existing = [makeFile("a.png")];
const incoming = [makeFile("a.png"), makeFile("b.png")];
const result = appendImagesDeduped(existing, incoming);
expect(result).toHaveLength(2);
expect(result.map((f) => f.name)).toEqual(["a.png", "b.png"]);
});
test("preserves existing list order — new files are appended at the end", () => {
const existing = [makeFile("first.png"), makeFile("second.png")];
const incoming = [makeFile("third.png")];
const result = appendImagesDeduped(existing, incoming);
expect(result.map((f) => f.name)).toEqual([
"first.png",
"second.png",
"third.png",
]);
});
test("returns a new array (does not mutate the existing list)", () => {
const existing = [makeFile("a.png")];
const incoming = [makeFile("b.png")];
const result = appendImagesDeduped(existing, incoming);
expect(result).not.toBe(existing); // new reference
expect(existing).toHaveLength(1); // original untouched
});
test("handles multiple incoming files with internal duplicates", () => {
// Two identical files in the same incoming batch
const existing: File[] = [];
const incoming = [makeFile("a.png"), makeFile("a.png"), makeFile("b.png")];
const result = appendImagesDeduped(existing, incoming);
expect(result).toHaveLength(2);
expect(result.map((f) => f.name)).toEqual(["a.png", "b.png"]);
});
});