mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-07-11 13:35:13 +10:00
a4999c04af
- drop dotenv (Node 24 process.loadEnvFile) and dompurify (only used by dead code) - delete unused ui components/hooks (card, progress, checkbox, use-confirm, use-prompt) - delete dead sanitizeHtml/sanitizeCss, url-security helpers, patch-resume tool, schema/page, createResumePatches, patch-proposal preview builder, fonts fallback helpers - inline single-caller wrappers (flags service, auth getSession, pdf renderer passthrough) - deduplicate template color helpers into shared/color-helpers - unexport 50+ internal-only symbols, remove dead export-map entries - replace hand-rolled unique()/useIsMobile with Set spread and usehooks-ts
91 lines
2.9 KiB
TypeScript
91 lines
2.9 KiB
TypeScript
/**
|
|
* @vitest-environment happy-dom
|
|
*/
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { downloadWithAnchor, generateFilename } from "./file";
|
|
|
|
describe("generateFilename", () => {
|
|
it("slugifies the prefix without extension", () => {
|
|
expect(generateFilename("My Resume")).toBe("my-resume");
|
|
});
|
|
|
|
it("appends extension when provided", () => {
|
|
expect(generateFilename("My Resume", "pdf")).toBe("my-resume.pdf");
|
|
});
|
|
|
|
it("appends extension verbatim (no extra dot)", () => {
|
|
expect(generateFilename("Name", "json")).toBe("name.json");
|
|
});
|
|
|
|
it("handles empty extension as no extension", () => {
|
|
expect(generateFilename("foo", "")).toBe("foo");
|
|
});
|
|
|
|
it("strips diacritics", () => {
|
|
expect(generateFilename("Résumé", "pdf")).toBe("resume.pdf");
|
|
});
|
|
|
|
it("handles empty prefix", () => {
|
|
expect(generateFilename("", "pdf")).toBe(".pdf");
|
|
});
|
|
});
|
|
|
|
describe("downloadWithAnchor", () => {
|
|
let createObjectURLSpy: ReturnType<typeof vi.fn<typeof URL.createObjectURL>>;
|
|
let revokeObjectURLSpy: ReturnType<typeof vi.fn<typeof URL.revokeObjectURL>>;
|
|
let originalCreate: typeof URL.createObjectURL;
|
|
let originalRevoke: typeof URL.revokeObjectURL;
|
|
|
|
beforeEach(() => {
|
|
vi.useFakeTimers();
|
|
originalCreate = URL.createObjectURL;
|
|
originalRevoke = URL.revokeObjectURL;
|
|
createObjectURLSpy = vi.fn<typeof URL.createObjectURL>(() => "blob:mock-url");
|
|
revokeObjectURLSpy = vi.fn<typeof URL.revokeObjectURL>();
|
|
URL.createObjectURL = createObjectURLSpy;
|
|
URL.revokeObjectURL = revokeObjectURLSpy;
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.useRealTimers();
|
|
URL.createObjectURL = originalCreate;
|
|
URL.revokeObjectURL = originalRevoke;
|
|
});
|
|
|
|
it("creates an anchor with correct href, rel, and download attributes", () => {
|
|
const blob = new Blob(["hello"], { type: "text/plain" });
|
|
const clickSpy = vi.spyOn(HTMLAnchorElement.prototype, "click").mockImplementation(() => {});
|
|
|
|
downloadWithAnchor(blob, "test.txt");
|
|
|
|
expect(createObjectURLSpy).toHaveBeenCalledWith(blob);
|
|
expect(clickSpy).toHaveBeenCalledOnce();
|
|
|
|
clickSpy.mockRestore();
|
|
});
|
|
|
|
it("removes the anchor from the DOM after click", () => {
|
|
const blob = new Blob(["hello"]);
|
|
vi.spyOn(HTMLAnchorElement.prototype, "click").mockImplementation(() => {});
|
|
|
|
const beforeChildCount = document.body.childElementCount;
|
|
downloadWithAnchor(blob, "test.txt");
|
|
const afterChildCount = document.body.childElementCount;
|
|
|
|
expect(afterChildCount).toBe(beforeChildCount);
|
|
});
|
|
|
|
it("revokes the object URL after 5 seconds", () => {
|
|
const blob = new Blob(["hello"]);
|
|
vi.spyOn(HTMLAnchorElement.prototype, "click").mockImplementation(() => {});
|
|
|
|
downloadWithAnchor(blob, "test.txt");
|
|
|
|
expect(revokeObjectURLSpy).not.toHaveBeenCalled();
|
|
vi.advanceTimersByTime(4999);
|
|
expect(revokeObjectURLSpy).not.toHaveBeenCalled();
|
|
vi.advanceTimersByTime(1);
|
|
expect(revokeObjectURLSpy).toHaveBeenCalledWith("blob:mock-url");
|
|
});
|
|
});
|