File size: 2,962 Bytes
6aa76ed
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { describe, expect, it } from "vitest";

import {
  firstImageFromClipboard,
  imageFilesFromTransfer,
  transferMayContainImage,
} from "./chatImagePaste";

// Minimal DataTransfer stand-ins. jsdom's DataTransfer doesn't let us seed
// items/files, so we hand-roll the shape the helpers read.
function makeItem(kind: string, type: string, file: File | null) {
  return { kind, type, getAsFile: () => file } as unknown as DataTransferItem;
}

function makeData(opts: {
  items?: DataTransferItem[];
  files?: File[];
}): DataTransfer {
  const items = opts.items ?? [];
  const files = opts.files ?? [];
  const itemList: Record<string | number, unknown> = { length: items.length };
  items.forEach((it, i) => {
    itemList[i] = it;
  });
  const fileList: Record<string | number, unknown> = { length: files.length };
  files.forEach((f, i) => {
    fileList[i] = f;
  });
  return {
    items: itemList,
    files: fileList,
  } as unknown as DataTransfer;
}

const png = new File([new Uint8Array([1, 2, 3])], "x.png", {
  type: "image/png",
});
const gif = new File([new Uint8Array([4, 5])], "y.gif", {
  type: "image/gif",
});

describe("firstImageFromClipboard", () => {
  it("returns null for null clipboard data", () => {
    expect(firstImageFromClipboard(null)).toBeNull();
  });

  it("finds an image via items[].getAsFile()", () => {
    const data = makeData({ items: [makeItem("file", "image/png", png)] });
    expect(firstImageFromClipboard(data)).toBe(png);
  });

  it("ignores non-file and non-image items", () => {
    const data = makeData({
      items: [
        makeItem("string", "text/plain", null),
        makeItem("file", "application/pdf", new File([], "a.pdf")),
      ],
    });
    expect(firstImageFromClipboard(data)).toBeNull();
  });

  it("falls back to files[] when items are absent (Safari/Firefox)", () => {
    const data = makeData({ files: [png] });
    expect(firstImageFromClipboard(data)).toBe(png);
  });

  it("returns null when nothing image-like is present", () => {
    const data = makeData({
      files: [new File([], "notes.txt", { type: "text/plain" })],
    });
    expect(firstImageFromClipboard(data)).toBeNull();
  });
});

describe("imageFilesFromTransfer", () => {
  it("dedupes the same file when present in both items and files", () => {
    const data = makeData({
      items: [makeItem("file", "image/png", png)],
      files: [png, gif],
    });
    expect(imageFilesFromTransfer(data)).toEqual([png, gif]);
  });
});

describe("transferMayContainImage", () => {
  it("is true for image items even when type is empty (some browsers)", () => {
    const data = makeData({
      items: [makeItem("file", "", png)],
    });
    expect(transferMayContainImage(data)).toBe(true);
  });

  it("is false for text-only transfers", () => {
    const data = makeData({
      items: [makeItem("string", "text/plain", null)],
    });
    expect(transferMayContainImage(data)).toBe(false);
  });
});