1
2
3
4
5
6import { describe, expect, it } from "vitest";
7import { filterByName, sortPickerFiles, type PickerSort } from "../picker";
8import type { FileListItem } from "../api";
9
10function file(overrides: Partial<FileListItem>): FileListItem {
11 return {
12 id: "f1",
13 workspaceId: "ws",
14 blockId: null,
15 pageId: "p1",
16 name: "file",
17 size: 100,
18 mimeType: "image/png",
19 status: "READY",
20 metadata: {},
21 thumbnailKey: null,
22 thumbnailUrl: null,
23 scanReport: null,
24 uploadedBy: "u1",
25 version: 1,
26 createdAt: "2026-01-01T00:00:00.000Z",
27 updatedAt: "2026-01-01T00:00:00.000Z",
28 uploadedByName: "Owner",
29 pageName: "Page",
30 versionCount: 1,
31 ...overrides,
32 };
33}
34
35const FIXTURES: FileListItem[] = [
36 file({ id: "a", name: "alpha.png", size: 10, createdAt: "2026-01-01T00:00:00.000Z", updatedAt: "2026-01-05T00:00:00.000Z" }),
37 file({ id: "b", name: "Bravo.pdf", size: 30, createdAt: "2026-01-03T00:00:00.000Z", updatedAt: "2026-01-02T00:00:00.000Z" }),
38 file({ id: "c", name: "charlie.mp4", size: 20, createdAt: "2026-01-02T00:00:00.000Z", updatedAt: "2026-01-04T00:00:00.000Z" }),
39 file({ id: "d", name: "delta-notes.txt", size: 40, createdAt: "2026-01-04T00:00:00.000Z", updatedAt: "2026-01-01T00:00:00.000Z" }),
40];
41
42describe("filterByName (client search, spec §4)", () => {
43 it("empty / whitespace query returns everything, unmodified", () => {
44 expect(filterByName(FIXTURES, "")).toHaveLength(4);
45 expect(filterByName(FIXTURES, " ")).toHaveLength(4);
46
47 expect(filterByName(FIXTURES, "")).toBe(FIXTURES);
48 });
49
50 it("is a case-insensitive substring match on the name", () => {
51 expect(filterByName(FIXTURES, "BRAVO").map((f) => f.id)).toEqual(["b"]);
52 expect(filterByName(FIXTURES, "br").map((f) => f.id)).toEqual(["b"]);
53 expect(filterByName(FIXTURES, ".png").map((f) => f.id)).toEqual(["a"]);
54 });
55
56 it("returns [] when nothing matches (the picker shows its empty state)", () => {
57 expect(filterByName(FIXTURES, "zzz")).toEqual([]);
58 });
59
60 it("trims the query before matching", () => {
61 expect(filterByName(FIXTURES, " charlie ").map((f) => f.id)).toEqual(["c"]);
62 });
63});
64
65describe("sortPickerFiles (picker order)", () => {
66 const ids = (arr: FileListItem[]) => arr.map((f) => f.id);
67
68 it("updated (default): most recently updated first", () => {
69 expect(ids(sortPickerFiles(FIXTURES, "updated"))).toEqual(["a", "c", "b", "d"]);
70 });
71
72 it("date: newest createdAt first", () => {
73 expect(ids(sortPickerFiles(FIXTURES, "date"))).toEqual(["d", "b", "c", "a"]);
74 });
75
76 it("size: largest first", () => {
77 expect(ids(sortPickerFiles(FIXTURES, "size"))).toEqual(["d", "b", "c", "a"]);
78 });
79
80 it("name: locale-aware ascending (case-insensitive)", () => {
81 expect(ids(sortPickerFiles(FIXTURES, "name"))).toEqual(["a", "b", "c", "d"]);
82 });
83
84 it("returns a NEW array — the caller's list is not mutated", () => {
85 const before = [...FIXTURES];
86 sortPickerFiles(FIXTURES, "size");
87 expect(FIXTURES.map((f) => f.id)).toEqual(before.map((f) => f.id));
88 });
89
90 it("composes with filterByName: filter first, then sort (the picker pipeline)", () => {
91 const pipeline = (q: string, sort: PickerSort) => sortPickerFiles(filterByName(FIXTURES, q), sort);
92
93 expect(ids(pipeline("pdf", "size"))).toEqual(["b"]);
94
95 expect(ids(pipeline("e", "size"))).toEqual(["d", "c"]);
96
97 expect(ids(pipeline("e", "name"))).toEqual(["c", "d"]);
98 });
99});
100