mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-07-27 10:24:48 +10:00
test: add unit and component tests across the monorepo
Adds ~1000 tests to bring the previously-untested packages and apps under coverage: - packages/utils — string, color, date, file, level, locale, sanitize, field, html, network-icons, rate-limit, url, url-security, monorepo, resume/patch, resume/docx/link-utils, style helpers (~97% on the testable utility files) - packages/ui — 28 component test files plus the use-controlled-state, use-mobile, use-confirm, use-prompt hooks (95% statements) - packages/pdf — shared template helpers (filtering, columns, picture, metrics, section-links, page-size, rich-text-html, section-title) - packages/schema — resumeDataSchema, page, templates, default - packages/fonts — expanded coverage on font helpers - packages/ai — resume sanitize and extraction template - packages/api — resume-access-policy - apps/web — error-message, locale, theme, pwa, dialogs/store, and the resume/move-item / section-actions / make-section-item helpers Adds jsdom polyfills (ResizeObserver, IntersectionObserver, scrollIntoView, matchMedia) and an explicit React Testing Library cleanup hook to vitest.setup.ts so portal- and overlay-based components work without per-test setup.
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* @vitest-environment jsdom
|
||||
*/
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { downloadFromUrl, 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>;
|
||||
let revokeObjectURLSpy: ReturnType<typeof vi.fn>;
|
||||
let originalCreate: typeof URL.createObjectURL;
|
||||
let originalRevoke: typeof URL.revokeObjectURL;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
originalCreate = URL.createObjectURL;
|
||||
originalRevoke = URL.revokeObjectURL;
|
||||
createObjectURLSpy = vi.fn(() => "blob:mock-url");
|
||||
revokeObjectURLSpy = vi.fn();
|
||||
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");
|
||||
});
|
||||
});
|
||||
|
||||
describe("downloadFromUrl", () => {
|
||||
let originalFetch: typeof global.fetch;
|
||||
let createObjectURLSpy: ReturnType<typeof vi.fn>;
|
||||
let revokeObjectURLSpy: ReturnType<typeof vi.fn>;
|
||||
let originalCreate: typeof URL.createObjectURL;
|
||||
let originalRevoke: typeof URL.revokeObjectURL;
|
||||
|
||||
beforeEach(() => {
|
||||
originalFetch = global.fetch;
|
||||
originalCreate = URL.createObjectURL;
|
||||
originalRevoke = URL.revokeObjectURL;
|
||||
createObjectURLSpy = vi.fn(() => "blob:mock-url");
|
||||
revokeObjectURLSpy = vi.fn();
|
||||
URL.createObjectURL = createObjectURLSpy;
|
||||
URL.revokeObjectURL = revokeObjectURLSpy;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
global.fetch = originalFetch;
|
||||
URL.createObjectURL = originalCreate;
|
||||
URL.revokeObjectURL = originalRevoke;
|
||||
});
|
||||
|
||||
it("fetches the URL and downloads the resulting blob", async () => {
|
||||
const mockBlob = new Blob(["data"], { type: "application/pdf" });
|
||||
global.fetch = vi.fn(() =>
|
||||
Promise.resolve(new Response(mockBlob, { status: 200 })),
|
||||
) as unknown as typeof global.fetch;
|
||||
vi.spyOn(HTMLAnchorElement.prototype, "click").mockImplementation(() => {});
|
||||
|
||||
await downloadFromUrl("https://example.com/file.pdf", "file.pdf");
|
||||
|
||||
expect(global.fetch).toHaveBeenCalledWith("https://example.com/file.pdf");
|
||||
expect(createObjectURLSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("propagates fetch errors", async () => {
|
||||
global.fetch = vi.fn(() => Promise.reject(new Error("network down"))) as unknown as typeof global.fetch;
|
||||
await expect(downloadFromUrl("https://example.com", "x.pdf")).rejects.toThrow("network down");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user