import Polyfills from "@delement/configs/polyfills"; import assert from "node:assert"; import { describe, it, beforeEach, mock, before, } from "node:test"; import type { IFilesUploaderItem } from "../config/types/index"; import { disableEvent, getConvertedFilesArrayToMap, getConvertedFileTypes, getFileExtension, getFileSizeInMb, getFilesWithPropsArr, getIfFilesValid, getShortFileType, isFilesSizeValid, setEachFileIntoInput, } from "./index"; describe("FilesUploader Utils", () => { before(() => { new Polyfills(); if (typeof DataTransfer === "undefined") { // @ts-expect-error (mock) global.DataTransfer = class DataTransfer { items: unknown; files: unknown[]; constructor() { this.files = []; this.items = { add: (file: File) => { this.files.push(file); }, }; } } as unknown; } }); describe("getFileExtension", () => { it("returns extension from filename with dot", () => { assert.strictEqual(getFileExtension("document.pdf"), "pdf"); assert.strictEqual(getFileExtension("image.jpg"), "jpg"); assert.strictEqual(getFileExtension("archive.tar.gz"), "gz"); }); it("returns lowercase extension", () => { assert.strictEqual(getFileExtension("Document.PDF"), "pdf"); assert.strictEqual(getFileExtension("IMAGE.JPG"), "jpg"); }); it("handles filename without extension", () => { assert.strictEqual(getFileExtension("README"), "readme"); assert.strictEqual(getFileExtension("noextension"), "noextension"); }); it("handles filename with multiple dots", () => { assert.strictEqual(getFileExtension("my.file.name.txt"), "txt"); assert.strictEqual(getFileExtension("archive.tar.gz"), "gz"); }); it("handles empty string", () => { assert.strictEqual(getFileExtension(""), ""); }); it("handles filename starting with dot", () => { assert.strictEqual(getFileExtension(".gitignore"), "gitignore"); assert.strictEqual(getFileExtension(".env"), "env"); }); }); describe("getShortFileType", () => { it("extracts type after slash", () => { assert.strictEqual(getShortFileType("image/jpeg"), "jpeg"); assert.strictEqual(getShortFileType("application/pdf"), "pdf"); assert.strictEqual(getShortFileType("text/plain"), "plain"); }); it("returns original type if no slash", () => { assert.strictEqual(getShortFileType("pdf"), "pdf"); assert.strictEqual(getShortFileType("jpeg"), "jpeg"); }); it("handles empty string", () => { assert.strictEqual(getShortFileType(""), ""); }); }); describe("getFilesWithPropsArr", () => { it("converts single object to array with required props", () => { const input: IFilesUploaderItem = { name: "test.pdf", size: 1024, href: "http://example.com/test.pdf", data: null, }; const result = getFilesWithPropsArr(input); assert.strictEqual(result.length, 1); assert.deepStrictEqual(result[0], { name: "test.pdf", size: 1024, href: "http://example.com/test.pdf", data: null, }); }); it("converts array of objects to array with required props", () => { const input: IFilesUploaderItem[] = [ { name: "file1.pdf", size: 100 }, { name: "file2.jpg", size: 200, href: "http://example.com" }, ]; const result = getFilesWithPropsArr(input); assert.strictEqual(result.length, 2); assert.deepStrictEqual(result[0], { name: "file1.pdf", size: 100, href: null, data: null, }); assert.deepStrictEqual(result[1], { name: "file2.jpg", size: 200, href: "http://example.com", data: null, }); }); it("handles File instances in array", () => { const file = new File([ "content" ], "test.txt", { type: "text/plain" }); const input = [ file, { name: "file2.pdf", size: 100 } ]; const result = getFilesWithPropsArr(input); assert.strictEqual(result.length, 2); assert.strictEqual(result[0], file); assert.deepStrictEqual(result[1], { name: "file2.pdf", size: 100, href: null, data: null, }); }); it("provides default values for missing props", () => { const input: IFilesUploaderItem = {}; const result = getFilesWithPropsArr(input); assert.deepStrictEqual(result[0], { name: "", size: 0, href: null, data: null, }); }); it("handles invalid name and size types", () => { const input = { name: 123, size: "invalid", } as unknown as IFilesUploaderItem; const result = getFilesWithPropsArr(input); assert.deepStrictEqual(result[0], { name: "", size: 0, href: null, data: null, }); }); it("returns empty array for null/undefined", () => { assert.deepStrictEqual(getFilesWithPropsArr(null as unknown as IFilesUploaderItem), []); assert.deepStrictEqual(getFilesWithPropsArr(undefined as unknown as IFilesUploaderItem), []); }); it("returns empty array for non-object values", () => { assert.deepStrictEqual(getFilesWithPropsArr("string" as unknown as IFilesUploaderItem), []); assert.deepStrictEqual(getFilesWithPropsArr(123 as unknown as IFilesUploaderItem), []); }); }); describe("getConvertedFileTypes", () => { it("converts array of types to comma-separated extensions", () => { assert.strictEqual(getConvertedFileTypes([ "pdf", "jpg", "png" ]), ".pdf,.jpg,.png"); }); it("returns * for empty array", () => { assert.strictEqual(getConvertedFileTypes([]), "*"); }); it("handles single type", () => { assert.strictEqual(getConvertedFileTypes([ "pdf" ]), ".pdf"); }); }); describe("disableEvent", () => { it("calls preventDefault and stopPropagation", () => { const preventDefault = mock.fn(); const stopPropagation = mock.fn(); const event = { preventDefault, stopPropagation, } as unknown as Event; disableEvent(event); assert.strictEqual(preventDefault.mock.calls.length, 1); assert.strictEqual(stopPropagation.mock.calls.length, 1); }); }); describe("getFileSizeInMb", () => { it("converts bytes to megabytes", () => { assert.strictEqual(getFileSizeInMb(1048576), 1); // 1 MB assert.strictEqual(getFileSizeInMb(2097152), 2); // 2 MB assert.strictEqual(getFileSizeInMb(5242880), 5); // 5 MB }); it("handles fractional megabytes", () => { assert.strictEqual(getFileSizeInMb(524288), 0.5); // 0.5 MB // Use closeTo for floating point const result = getFileSizeInMb(1572864); assert.ok(Math.abs(result - 1.5) < 1e-10); }); it("handles zero and undefined", () => { assert.strictEqual(getFileSizeInMb(0), 0); assert.strictEqual(getFileSizeInMb(), 0); }); it("handles small sizes", () => { const result = getFileSizeInMb(1024); assert.ok(Math.abs(result - 0.0009765625) < 1e-10); const result2 = getFileSizeInMb(512); assert.ok(Math.abs(result2 - 0.00048828125) < 1e-10); }); }); describe("getIfFilesValid", () => { const createFile = (name: string, size: number) => { return new File([ "a".repeat(size) ], name, { type: "text/plain" }); }; it("returns true for valid files within size and type constraints", () => { const files = [ createFile("test.pdf", 1024), createFile("image.jpg", 2048), ]; assert.strictEqual(getIfFilesValid(files, 1, [ "pdf", "jpg" ]), true); }); it("returns false if file exceeds max size", () => { const files = [ createFile("large.pdf", 2 * 1024 * 1024) ]; // 2 MB assert.strictEqual(getIfFilesValid(files, 1, [ "pdf" ]), false); }); it("returns false if file type not allowed", () => { const files = [ createFile("test.exe", 1024) ]; assert.strictEqual(getIfFilesValid(files, 10, [ "pdf", "jpg" ]), false); }); it("returns true for empty files array", () => { assert.strictEqual(getIfFilesValid([], 10, [ "pdf" ]), true); }); it("returns true when maxSize is 0 (unlimited)", () => { const files = [ createFile("huge.pdf", 1024) ]; assert.strictEqual(getIfFilesValid(files, 0, [ "pdf" ]), true); }); it("returns true when maxSize is Infinity", () => { const files = [ createFile("test.pdf", 1000000) ]; assert.strictEqual(getIfFilesValid(files, Infinity, [ "pdf" ]), true); }); it("handles mixed valid and invalid files", () => { const files = [ createFile("valid.pdf", 1024), createFile("invalid.exe", 1024), ]; assert.strictEqual(getIfFilesValid(files, 10, [ "pdf" ]), false); }); it("uses default values when not provided", () => { assert.strictEqual(getIfFilesValid(), true); }); it("validates maxSize by total files size by default", () => { const files = [ createFile("file1.pdf", 700 * 1024), createFile("file2.pdf", 800 * 1024), ]; assert.strictEqual(getIfFilesValid(files, 1, [ "pdf" ]), false); }); it("validates maxSize per file when maxSizeType is every", () => { const files = [ createFile("file1.pdf", 700 * 1024), createFile("file2.pdf", 800 * 1024), ]; assert.strictEqual(getIfFilesValid(files, 1, [ "pdf" ], "every"), true); }); }); describe("getConvertedFilesArrayToMap", () => { it("converts array to Map with filename as key", () => { const files: IFilesUploaderItem[] = [ { name: "file1.pdf", size: 100 }, { name: "file2.jpg", size: 200 }, ]; const result = getConvertedFilesArrayToMap(files); assert.strictEqual(result.size, 2); assert.deepStrictEqual(result.get("file1.pdf"), { name: "file1.pdf", size: 100, href: null, data: null, }); assert.deepStrictEqual(result.get("file2.jpg"), { name: "file2.jpg", size: 200, href: null, data: null, }); }); it("handles File instances", () => { const file = new File([ "content" ], "test.txt"); const result = getConvertedFilesArrayToMap([ file ]); assert.strictEqual(result.size, 1); assert.strictEqual(result.get("test.txt"), file); }); it("returns empty Map for empty array", () => { const result = getConvertedFilesArrayToMap([]); assert.strictEqual(result.size, 0); }); it("handles duplicate filenames (last one wins)", () => { const files: IFilesUploaderItem[] = [ { name: "duplicate.pdf", size: 100 }, { name: "duplicate.pdf", size: 200 }, ]; const result = getConvertedFilesArrayToMap(files); assert.strictEqual(result.size, 1); assert.strictEqual(result.get("duplicate.pdf")?.size, 200); }); it("uses default empty array when undefined", () => { const result = getConvertedFilesArrayToMap(); assert.strictEqual(result.size, 0); }); }); describe("isFilesSizeValid", () => { const createFileWithSize = (size: number): File => { return new File([ "a".repeat(size) ], "test.txt"); }; it("returns true when total size is within limit", () => { const files = [ createFileWithSize(500 * 1024), // 0.5 MB createFileWithSize(500 * 1024), // 0.5 MB ]; assert.strictEqual(isFilesSizeValid(files, 2), true); }); it("returns false when total size exceeds limit", () => { const files = [ createFileWithSize(0.8 * 1024 * 1024), // 0.8 MB createFileWithSize(1.5 * 1024 * 1024), // 1.5 MB ]; assert.strictEqual(isFilesSizeValid(files, 2), false); }); it("returns true for empty array", () => { assert.strictEqual(isFilesSizeValid([], 10), true); }); it("returns true when maxSize is 0 (unlimited)", () => { const files = [ createFileWithSize(10 * 1024 * 1024) ]; // 10 MB assert.strictEqual(isFilesSizeValid(files, 0), true); }); it("returns true when maxSize is Infinity", () => { const files = [ createFileWithSize(1000000) ]; assert.strictEqual(isFilesSizeValid(files, Infinity), true); }); it("handles IFilesUploaderItem objects", () => { const files: Required[] = [ { name: "file1.pdf", size: 500 * 1024, href: null, data: null, }, { name: "file2.jpg", size: 500 * 1024, href: null, data: null, }, ]; assert.strictEqual(isFilesSizeValid(files, 1.5), true); }); it("uses default values when args not provided", () => { assert.strictEqual(isFilesSizeValid(), true); }); }); describe("setEachFileIntoInput", () => { let input: HTMLInputElement; beforeEach(() => { input = document.createElement("input"); input.type = "file"; input.multiple = true; }); it("resolves successfully with File instances", async () => { const file1 = new File([ "content1" ], "file1.txt"); const file2 = new File([ "content2" ], "file2.txt"); const result = await setEachFileIntoInput([ file1, file2 ], input); assert.notStrictEqual(result, undefined); assert.notStrictEqual(result.unresolvedFiles, undefined); assert.strictEqual(result.unresolvedFiles.length, 0); }); it("rejects non-File items and returns them in unresolvedFiles", async () => { const file = new File([ "content" ], "file.txt"); const nonFile: IFilesUploaderItem = { name: "other.pdf", size: 100 }; const result = await setEachFileIntoInput([ file, nonFile ], input); assert.notStrictEqual(result, undefined); assert.notStrictEqual(result.unresolvedFiles, undefined); assert.strictEqual(result.unresolvedFiles.length, 1); }); it("returns rejected promise when input is null", async () => { const file = new File([ "content" ], "file.txt"); await assert.rejects(setEachFileIntoInput([ file ], null)); }); it("handles empty files array", async () => { const result = await setEachFileIntoInput([], input); assert.notStrictEqual(result, undefined); assert.strictEqual(result.unresolvedFiles.length, 0); }); it("handles mix of File instances and non-File objects", async () => { const file1 = new File([ "content1" ], "file1.txt"); const file2 = new File([ "content2" ], "file2.txt"); const obj: IFilesUploaderItem = { name: "notafile.pdf", size: 100 }; const result = await setEachFileIntoInput([ file1, obj, file2 ], input); assert.notStrictEqual(result, undefined); assert.notStrictEqual(result.unresolvedFiles, undefined); assert.strictEqual(result.unresolvedFiles.length, 1); }); it("provides valid input element to the function", async () => { const file = new File([ "content" ], "test.txt"); await assert.doesNotReject(setEachFileIntoInput([ file ], input)); }); }); });