MeshChatX/tests/frontend/DownloadUtils.test.js
2026-07-20 16:00:05 -05:00

89 lines
3.6 KiB
JavaScript

import { describe, it, expect, vi, afterEach } from "vitest";
import DownloadUtils from "@/js/DownloadUtils";
describe("DownloadUtils", () => {
afterEach(() => {
delete window.MeshChatXAndroid;
vi.restoreAllMocks();
});
it("delegates base64 saves to Android bridge when present", () => {
const saveDownload = vi.fn();
window.MeshChatXAndroid = { saveDownload };
DownloadUtils.downloadFromBase64("test.mu", "QUJD");
expect(saveDownload).toHaveBeenCalledWith("test.mu", "QUJD");
});
it("downloadFile uses Android bridge when present", async () => {
const saveDownload = vi.fn();
window.MeshChatXAndroid = { saveDownload };
const blob = new Blob([new Uint8Array([1, 2, 3])]);
await DownloadUtils.downloadFile("f.bin", blob);
expect(saveDownload).toHaveBeenCalledTimes(1);
const [name, b64] = saveDownload.mock.calls[0];
expect(name).toBe("f.bin");
expect(typeof b64).toBe("string");
expect(b64.length).toBeGreaterThan(0);
});
it("parseFilenameFromContentDisposition prefers UTF-8 filename*", () => {
const header = "attachment; filename=\"fallback.bin\"; filename*=UTF-8''photo%2Epng";
expect(DownloadUtils.parseFilenameFromContentDisposition(header, "default.bin")).toBe("photo.png");
});
it("downloadFromApiResponse uses Android bridge when present", async () => {
const saveDownload = vi.fn();
window.MeshChatXAndroid = { saveDownload };
await DownloadUtils.downloadFromApiResponse(
{
data: new Uint8Array([9, 8, 7]),
headers: {
"content-disposition": 'attachment; filename="backup.zip"',
"content-type": "application/zip",
},
},
"fallback.zip"
);
expect(saveDownload).toHaveBeenCalledWith("backup.zip", expect.any(String));
});
it("downloadFile triggers anchor download when Android bridge is absent", async () => {
const click = vi.fn();
const append = vi.fn();
const remove = vi.fn();
const link = { click, remove, download: "", href: "", style: { display: "" } };
vi.spyOn(document, "createElement").mockReturnValue(link);
vi.spyOn(document.body, "append").mockImplementation(append);
URL.createObjectURL = vi.fn(() => "blob:mock");
await DownloadUtils.downloadFile("browser.bin", new Blob([new Uint8Array([1])]));
expect(click).toHaveBeenCalledTimes(1);
expect(link.download).toBe("browser.bin");
expect(link.href).toBe("blob:mock");
expect(append).toHaveBeenCalledWith(link);
expect(remove).toHaveBeenCalledTimes(1);
});
it("downloadFromApiResponse reads Fetch Headers via .get()", async () => {
const saveDownload = vi.fn();
window.MeshChatXAndroid = { saveDownload };
const headers = new Headers({
"content-disposition": 'attachment; filename="from-headers.zip"',
"content-type": "application/zip",
});
await DownloadUtils.downloadFromApiResponse(
{
data: new Uint8Array([1]),
headers,
},
"fallback.zip"
);
expect(saveDownload).toHaveBeenCalledWith("from-headers.zip", expect.any(String));
});
it("headerValue works for plain objects and Headers", () => {
expect(DownloadUtils.headerValue({ "Content-Type": "text/plain" }, "content-type")).toBe("text/plain");
expect(DownloadUtils.headerValue(new Headers({ "content-type": "a/b" }), "Content-Type")).toBe("a/b");
});
});