mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-06 08:24:46 +00:00
* fix(app): preserve image attachment MIME types Image intake discarded clipboard and desktop path metadata, then guessed JPEG when a later file object had no type. Resolve MIME once at the platform boundary and require it downstream. * fix(app): honor native picker MIME metadata
130 lines
3.6 KiB
TypeScript
130 lines
3.6 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import type { DesktopDialogBridge, DesktopDialogOpenOptions } from "@/desktop/host";
|
|
import { normalizePickedImageAssets, pickImagesWithDesktopDialog } from "./image-attachment-picker";
|
|
|
|
function fakeDialogReturning(selection: string | string[] | null): {
|
|
dialog: DesktopDialogBridge;
|
|
recordedOptions: DesktopDialogOpenOptions[];
|
|
} {
|
|
const recordedOptions: DesktopDialogOpenOptions[] = [];
|
|
return {
|
|
dialog: {
|
|
open: async (options?: DesktopDialogOpenOptions) => {
|
|
if (options) {
|
|
recordedOptions.push(options);
|
|
}
|
|
return selection;
|
|
},
|
|
},
|
|
recordedOptions,
|
|
};
|
|
}
|
|
|
|
describe("image-attachment-picker", () => {
|
|
it("normalizes a picked File into a blob source", async () => {
|
|
const file = new File(["hello"], "picked.png", { type: "image/png" });
|
|
|
|
const result = await normalizePickedImageAssets([
|
|
{
|
|
uri: "blob:test",
|
|
mimeType: "image/png",
|
|
fileName: null,
|
|
file,
|
|
},
|
|
]);
|
|
|
|
expect(result).toHaveLength(1);
|
|
expect(result[0]?.source.kind).toBe("blob");
|
|
expect(result[0]?.fileName).toBe("picked.png");
|
|
expect(result[0]?.mimeType).toBe("image/png");
|
|
});
|
|
|
|
it("derives the type of a type-less picked File from its name", async () => {
|
|
const file = new File(["image"], "picked.png");
|
|
|
|
const result = await normalizePickedImageAssets([
|
|
{
|
|
uri: "blob:test",
|
|
mimeType: null,
|
|
fileName: null,
|
|
file,
|
|
},
|
|
]);
|
|
|
|
expect(result).toEqual([
|
|
{
|
|
source: { kind: "blob", blob: file },
|
|
mimeType: "image/png",
|
|
fileName: "picked.png",
|
|
},
|
|
]);
|
|
});
|
|
|
|
it("keeps filesystem picker results as file uris", async () => {
|
|
const result = await normalizePickedImageAssets([
|
|
{
|
|
uri: "file:///tmp/picked.png",
|
|
mimeType: "image/png",
|
|
fileName: "picked.png",
|
|
},
|
|
]);
|
|
|
|
expect(result).toEqual([
|
|
{
|
|
source: { kind: "file_uri", uri: "file:///tmp/picked.png" },
|
|
mimeType: "image/png",
|
|
fileName: "picked.png",
|
|
},
|
|
]);
|
|
});
|
|
|
|
it("converts data urls into blob sources when no file path exists", async () => {
|
|
const result = await normalizePickedImageAssets([
|
|
{
|
|
uri: "data:image/png;base64,AAEC",
|
|
mimeType: "image/png",
|
|
fileName: "inline.png",
|
|
},
|
|
]);
|
|
|
|
expect(result).toHaveLength(1);
|
|
expect(result[0]?.source.kind).toBe("blob");
|
|
expect(result[0]?.fileName).toBe("inline.png");
|
|
expect(result[0]?.mimeType).toBe("image/png");
|
|
});
|
|
|
|
it("uses the desktop dialog api when available", async () => {
|
|
const { dialog, recordedOptions } = fakeDialogReturning(["/tmp/one.png", "/tmp/two.jpg"]);
|
|
|
|
const result = await pickImagesWithDesktopDialog(dialog);
|
|
|
|
expect(recordedOptions).toHaveLength(1);
|
|
expect(recordedOptions[0]).toMatchObject({
|
|
multiple: true,
|
|
directory: false,
|
|
title: "Attach images",
|
|
});
|
|
expect(result).toEqual([
|
|
{
|
|
source: { kind: "file_uri", uri: "/tmp/one.png" },
|
|
mimeType: "image/png",
|
|
fileName: "one.png",
|
|
},
|
|
{
|
|
source: { kind: "file_uri", uri: "/tmp/two.jpg" },
|
|
mimeType: "image/jpeg",
|
|
fileName: "two.jpg",
|
|
},
|
|
]);
|
|
});
|
|
|
|
it("throws when desktop dialog API is not available", async () => {
|
|
await expect(pickImagesWithDesktopDialog(null)).rejects.toThrow(
|
|
"Desktop dialog API is not available.",
|
|
);
|
|
await expect(pickImagesWithDesktopDialog({})).rejects.toThrow(
|
|
"Desktop dialog API is not available.",
|
|
);
|
|
});
|
|
});
|