diff --git a/src/lib/components/forms/DocumentUpload.svelte b/src/lib/components/forms/DocumentUpload.svelte index 9f9583a9e..8f3919b45 100644 --- a/src/lib/components/forms/DocumentUpload.svelte +++ b/src/lib/components/forms/DocumentUpload.svelte @@ -127,7 +127,11 @@ import * as documents from "$lib/api/documents"; import { DOCUMENT_TYPES } from "@/config/config.js"; - import { isSupported } from "@/lib/utils/validateFiles"; + import { + filenameToTitle, + getFileExtension, + isSupported, + } from "@/lib/utils/files"; let files: File[] = []; let projects: Project[] = []; @@ -167,18 +171,6 @@ files = files.filter((f, i) => i !== index); } - function formatFileType(filetype: string) { - if (filetype && filetype.includes("/")) { - return filetype.split("/")[1]; - } - return "unknown"; - } - - function filenameToTitle(filename: string): string { - const [name, ...ext] = filename.split("."); - return name.replace(/_+/g, " ").replace(/\s+/, " ").trim(); - } - // handle uploads client side instead of going through the server async function onSubmit(e: SubmitEvent) { loading = true; @@ -224,7 +216,7 @@ {#each files as file, index}

- {formatFileType(file.type)} / {filesize(file.size)} + {getFileExtension(file)} / {filesize(file.size)}

diff --git a/src/lib/utils/files.test.ts b/src/lib/utils/files.test.ts new file mode 100644 index 000000000..1ebedb890 --- /dev/null +++ b/src/lib/utils/files.test.ts @@ -0,0 +1,100 @@ +import { describe, test, it, expect } from "vitest"; +import * as files from "./files"; + +describe("files.getFileExtensionFromType", () => { + it("returns the second half of a Mimetype", () => { + expect(files.getFileExtensionFromType("application/pdf")).toEqual("pdf"); + expect(files.getFileExtensionFromType("image/jpeg")).toEqual("jpeg"); + }); + it("returns the provided input if not a Mimetype", () => { + expect(files.getFileExtensionFromType("")).toEqual(""); + expect(files.getFileExtensionFromType("pdf")).toEqual("pdf"); + expect(files.getFileExtensionFromType(undefined)).toEqual(undefined); + }); +}); + +describe("files.getFileExtension", () => { + it("returns the extension from the end of the filename", () => { + const file1 = new File([], "document.pdf"); + const file2 = new File([], "image.jpg"); + expect(files.getFileExtension(file1)).toEqual("pdf"); + expect(files.getFileExtension(file2)).toEqual("jpg"); + }); + + it("returns the last extension", () => { + const file = new File([], "period.delimited.filename.pdf"); + expect(files.getFileExtension(file)).toEqual("pdf"); + }); + + it("checks the file type if it's provided", () => { + const file = new File([], "filename", { type: "application/pdf" }); + expect(files.getFileExtension(file)).toEqual("pdf"); + }); + + it("returns undefined if neither filename extension nor file type", () => { + const file = new File([], "filename"); + expect(files.getFileExtension(file)).toBeUndefined(); + }); +}); + +test("files.isSupported", () => { + const file1 = new File([], "document.pdf"); + const file2 = new File([], "invalid.zip"); + expect(files.isSupported(file1)).toBe(true); + expect(files.isSupported(file2)).toBe(false); +}); + +describe("files.removeUnsupportedTypes", () => { + it("evaluates the filename for the type", () => { + const file1 = new File([], "document.pdf"); + const file2 = new File([], "invalid.zip"); + expect(files.removeUnsupportedTypes([file1, file2])).toEqual([file1]); + }); + it("ignores files missing an extension", () => { + const file1 = new File([], "document.pdf"); + const file2 = new File([], "pdf"); + expect(files.removeUnsupportedTypes([file1, file2])).toEqual([file1]); + }); + it("normalizes filenames to lowercase", () => { + const file1 = new File([], "document.pdf"); + const file2 = new File([], "valid.PDF"); + expect(files.removeUnsupportedTypes([file1, file2])).toEqual([ + file1, + file2, + ]); + }); +}); + +describe("files.isWithinSizeLimit", () => { + it("enforces a 500MB max size for PDF files", () => { + let file = new File([new ArrayBuffer(128000)], "document.pdf"); + expect(files.isWithinSizeLimit(file)).toBe(true); + file = new File([new ArrayBuffer(525336576)], "document.pdf"); + expect(files.isWithinSizeLimit(file)).toBe(true); + file = new File([new ArrayBuffer(525336576)], "document"); + expect(files.isWithinSizeLimit(file)).toBe(false); + file = new File([new ArrayBuffer(525336577)], "document", { + type: "application/pdf", + }); + expect(files.isWithinSizeLimit(file)).toBe(false); + }); + it("enforces a 25MB max size for other file types", () => { + let file = new File([new ArrayBuffer(128000)], "image.png"); + expect(files.isWithinSizeLimit(file)).toBe(true); + file = new File([new ArrayBuffer(27262976)], "image", { + type: "image/png", + }); + expect(files.isWithinSizeLimit(file)).toBe(true); + file = new File([new ArrayBuffer(27262977)], "image"); + expect(files.isWithinSizeLimit(file)).toBe(false); + }); +}); + +test("files.filenameToTitle", () => { + expect(files.filenameToTitle("foobar.zip")).toEqual("foobar"); + expect(files.filenameToTitle("foo.bar.baz.zip")).toEqual("foo"); + expect(files.filenameToTitle("BIP bim_bAp")).toEqual("BIP bim bAp"); + expect(files.filenameToTitle("PDF_FINAL_FINAL 2")).toEqual( + "PDF FINAL FINAL 2", + ); +}); diff --git a/src/lib/utils/files.ts b/src/lib/utils/files.ts new file mode 100644 index 000000000..6d63172cd --- /dev/null +++ b/src/lib/utils/files.ts @@ -0,0 +1,47 @@ +import { + DOCUMENT_TYPES, + PDF_SIZE_LIMIT, + DOCUMENT_SIZE_LIMIT, +} from "@/config/config.js"; + +const types = new Set(DOCUMENT_TYPES); + +export function getFileExtensionFromType(filetype?: string) { + if (filetype?.includes("/")) { + return filetype.split("/")[1]; + } + return filetype; +} + +export function getFileExtension(file: File) { + if (file.name.includes(".")) { + return file.name.toLowerCase().trim().split(".").pop(); + } else if (file.type) { + return getFileExtensionFromType(file.type); + } +} + +export function isSupported(file: File) { + const extension = getFileExtension(file); + return types.has(extension); +} + +/** Returns an array of only files with supported types */ +export function removeUnsupportedTypes(files: File[]) { + return files.filter(isSupported); +} + +export function isWithinSizeLimit(file: File) { + const { size } = file; + const extension = getFileExtension(file); + if (extension === "pdf") { + return size <= PDF_SIZE_LIMIT; + } else { + return size <= DOCUMENT_SIZE_LIMIT; + } +} + +export function filenameToTitle(filename: string): string { + const [name, ...ext] = filename.split("."); + return name.replace(/_/g, " "); +} diff --git a/src/lib/utils/validateFiles.test.ts b/src/lib/utils/validateFiles.test.ts deleted file mode 100644 index b6d13e6ca..000000000 --- a/src/lib/utils/validateFiles.test.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { describe, it, expect } from "vitest"; -import { removeUnsupportedTypes } from "./validateFiles"; - -describe("removeUnsupportedTypes", () => { - it("evaluates the filename for the type", () => { - const file1 = new File([], "document.pdf"); - const file2 = new File([], "invalid.zip"); - expect(removeUnsupportedTypes([file1, file2])).toEqual([file1]); - }); - it("ignores files missing an extension", () => { - const file1 = new File([], "document.pdf"); - const file2 = new File([], "pdf"); - expect(removeUnsupportedTypes([file1, file2])).toEqual([file1]); - }); - it("normalizes filenames to lowercase", () => { - const file1 = new File([], "document.pdf"); - const file2 = new File([], "valid.PDF"); - expect(removeUnsupportedTypes([file1, file2])).toEqual([file1, file2]); - }); -}); diff --git a/src/lib/utils/validateFiles.ts b/src/lib/utils/validateFiles.ts deleted file mode 100644 index a246b3640..000000000 --- a/src/lib/utils/validateFiles.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { DOCUMENT_TYPES } from "@/config/config.js"; - -const types = new Set(DOCUMENT_TYPES); - -/** Returns an array of only files with supported types */ -export function removeUnsupportedTypes(files: File[]) { - return files.filter((file) => { - if (!file.name.includes(".")) return false; - const extension = file.name.toLowerCase().trim().split(".").pop(); - return DOCUMENT_TYPES.includes(extension); - }); -} - -export function isSupported(file: File) { - const extension = file.name.toLowerCase().trim().split(".").pop(); - return types.has(extension); -}