mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-08-24 15:22:20 +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:
@@ -1,5 +1,20 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { fontList, getPdfCjkFallbackFontFamily, getWebFontSource } from "./index";
|
||||
import {
|
||||
buildResumeFontFamily,
|
||||
fontList,
|
||||
getFallbackWebFontFamilies,
|
||||
getFont,
|
||||
getFontDisplayName,
|
||||
getFontSearchKeywords,
|
||||
getLoadableWebFontWeights,
|
||||
getPdfCjkFallbackFontFamily,
|
||||
getWebFont,
|
||||
getWebFontSource,
|
||||
isStandardPdfFontFamily,
|
||||
standardFontList,
|
||||
webFontList,
|
||||
webFontMap,
|
||||
} from "./index";
|
||||
|
||||
const sortFontFamilies = (families: string[]) => {
|
||||
return [...families].sort((a, b) => a.localeCompare(b, undefined, { sensitivity: "base" }));
|
||||
@@ -8,18 +23,237 @@ const sortFontFamilies = (families: string[]) => {
|
||||
describe("fontList", () => {
|
||||
it("is ordered by font family name instead of localized display name", () => {
|
||||
const families = fontList.map((font) => font.family);
|
||||
|
||||
expect(families).toEqual(sortFontFamilies(families));
|
||||
});
|
||||
|
||||
it("contains standard PDF fonts", () => {
|
||||
const families = fontList.map((font) => font.family);
|
||||
expect(families).toContain("Helvetica");
|
||||
expect(families).toContain("Courier");
|
||||
expect(families).toContain("Times-Roman");
|
||||
});
|
||||
|
||||
it("merges standardFontList and webFontList without duplicates", () => {
|
||||
const expectedSize = standardFontList.length + webFontList.length;
|
||||
expect(fontList).toHaveLength(expectedSize);
|
||||
});
|
||||
});
|
||||
|
||||
describe("font source helpers", () => {
|
||||
describe("standardFontList", () => {
|
||||
it("contains only fonts not present in webFontMap", () => {
|
||||
for (const font of standardFontList) {
|
||||
expect(webFontMap.has(font.family)).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
it("marks all standard fonts with type='standard'", () => {
|
||||
for (const font of standardFontList) {
|
||||
expect(font.type).toBe("standard");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("getFont", () => {
|
||||
it("returns a font record by family", () => {
|
||||
expect(getFont("Helvetica")).toBeDefined();
|
||||
expect(getFont("Helvetica")?.family).toBe("Helvetica");
|
||||
});
|
||||
|
||||
it("returns undefined for unknown families", () => {
|
||||
expect(getFont("DefinitelyNotAFont")).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("getFontDisplayName", () => {
|
||||
it("returns localized name when available", () => {
|
||||
expect(getFontDisplayName("Noto Sans SC")).toBe("思源黑体");
|
||||
expect(getFontDisplayName("PingFang SC")).toBe("苹方");
|
||||
});
|
||||
|
||||
it("returns the family name when no localized name exists", () => {
|
||||
expect(getFontDisplayName("Helvetica")).toBe("Helvetica");
|
||||
expect(getFontDisplayName("Roboto")).toBe("Roboto");
|
||||
});
|
||||
});
|
||||
|
||||
describe("getFontSearchKeywords", () => {
|
||||
it("returns family for non-CJK fonts", () => {
|
||||
const keywords = getFontSearchKeywords("Helvetica");
|
||||
expect(keywords).toContain("Helvetica");
|
||||
expect(keywords).not.toContain("中文");
|
||||
});
|
||||
|
||||
it("includes localized display name and 中文 marker for CJK fonts", () => {
|
||||
const keywords = getFontSearchKeywords("Noto Sans SC");
|
||||
expect(keywords).toContain("Noto Sans SC");
|
||||
expect(keywords).toContain("思源黑体");
|
||||
expect(keywords).toContain("中文");
|
||||
});
|
||||
|
||||
it("deduplicates entries", () => {
|
||||
const keywords = getFontSearchKeywords("Helvetica");
|
||||
expect(new Set(keywords).size).toBe(keywords.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isStandardPdfFontFamily", () => {
|
||||
it("returns true for Helvetica, Courier, Times-Roman", () => {
|
||||
expect(isStandardPdfFontFamily("Helvetica")).toBe(true);
|
||||
expect(isStandardPdfFontFamily("Courier")).toBe(true);
|
||||
expect(isStandardPdfFontFamily("Times-Roman")).toBe(true);
|
||||
});
|
||||
|
||||
it("returns false for unknown families", () => {
|
||||
expect(isStandardPdfFontFamily("Not-A-Font")).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false for non-standard web fonts", () => {
|
||||
// Roboto is a web font, not a standard PDF font
|
||||
const roboto = getWebFont("Roboto");
|
||||
if (roboto) expect(isStandardPdfFontFamily("Roboto")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getWebFont", () => {
|
||||
it("returns webFont record for known web font family", () => {
|
||||
// Spot-check a font that should be in the list
|
||||
const font = getWebFont("Roboto");
|
||||
if (font) {
|
||||
expect(font.type).toBe("web");
|
||||
expect(font.family).toBe("Roboto");
|
||||
}
|
||||
});
|
||||
|
||||
it("returns undefined for non-web families", () => {
|
||||
expect(getWebFont("Helvetica")).toBeUndefined();
|
||||
expect(getWebFont("definitely-not-a-font")).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("getWebFontSource", () => {
|
||||
it("uses the full normal font source when an italic variant is unavailable", () => {
|
||||
expect(getWebFontSource("Noto Serif SC", "400", true)).toBe(getWebFontSource("Noto Serif SC", "400", false));
|
||||
});
|
||||
|
||||
it("returns CJK PDF fallbacks for standard PDF fonts", () => {
|
||||
it("returns null for unknown fonts", () => {
|
||||
expect(getWebFontSource("definitely-not-a-font", "400")).toBeNull();
|
||||
});
|
||||
|
||||
it("defaults to weight 400 when not specified", () => {
|
||||
const roboto = getWebFont("Roboto");
|
||||
if (roboto) {
|
||||
const source = getWebFontSource("Roboto");
|
||||
expect(source).toBeTruthy();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("getPdfCjkFallbackFontFamily", () => {
|
||||
it("returns Noto Sans SC for sans-serif/standard PDF fonts", () => {
|
||||
expect(getPdfCjkFallbackFontFamily("Helvetica")).toBe("Noto Sans SC");
|
||||
});
|
||||
|
||||
it("returns Noto Serif SC for serif fonts", () => {
|
||||
expect(getPdfCjkFallbackFontFamily("Times-Roman")).toBe("Noto Serif SC");
|
||||
});
|
||||
|
||||
it("returns null when family already is the CJK fallback", () => {
|
||||
expect(getPdfCjkFallbackFontFamily("Noto Sans SC")).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("getFallbackWebFontFamilies", () => {
|
||||
it("returns empty array for standard PDF fonts", () => {
|
||||
expect(getFallbackWebFontFamilies("Helvetica")).toEqual([]);
|
||||
expect(getFallbackWebFontFamilies("Courier")).toEqual([]);
|
||||
expect(getFallbackWebFontFamilies("Times-Roman")).toEqual([]);
|
||||
});
|
||||
|
||||
it("returns the primary CJK web font for non-standard, non-CJK families", () => {
|
||||
// e.g. Roboto (sans-serif) → Noto Sans SC fallback
|
||||
const roboto = getWebFont("Roboto");
|
||||
if (roboto) {
|
||||
expect(getFallbackWebFontFamilies("Roboto")).toEqual(["Noto Sans SC"]);
|
||||
}
|
||||
});
|
||||
|
||||
it("returns empty when family is already its primary CJK fallback", () => {
|
||||
expect(getFallbackWebFontFamilies("Noto Sans SC")).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getLoadableWebFontWeights", () => {
|
||||
it("returns empty array for unknown fonts", () => {
|
||||
expect(getLoadableWebFontWeights("definitely-not-a-font", ["400"])).toEqual([]);
|
||||
});
|
||||
|
||||
it("returns matching weights when preferred weights are available", () => {
|
||||
const roboto = getWebFont("Roboto");
|
||||
if (roboto) {
|
||||
const result = getLoadableWebFontWeights("Roboto", ["400", "700"]);
|
||||
for (const weight of result) {
|
||||
expect(roboto.weights).toContain(weight);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("falls back to default weights when no preferences match", () => {
|
||||
const roboto = getWebFont("Roboto");
|
||||
if (roboto) {
|
||||
const result = getLoadableWebFontWeights("Roboto", ["999"]);
|
||||
expect(result.length).toBeGreaterThan(0);
|
||||
}
|
||||
});
|
||||
|
||||
it("deduplicates preferred weights", () => {
|
||||
const roboto = getWebFont("Roboto");
|
||||
if (roboto?.weights.includes("400")) {
|
||||
const result = getLoadableWebFontWeights("Roboto", ["400", "400"]);
|
||||
expect(result).toEqual(["400"]);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("buildResumeFontFamily", () => {
|
||||
it("wraps the primary family in single quotes", () => {
|
||||
const result = buildResumeFontFamily("Roboto");
|
||||
expect(result.startsWith("'Roboto',")).toBe(true);
|
||||
});
|
||||
|
||||
it("includes generic sans-serif fallback by default", () => {
|
||||
const result = buildResumeFontFamily("Roboto");
|
||||
expect(result.endsWith("sans-serif")).toBe(true);
|
||||
});
|
||||
|
||||
it("uses serif fallback for serif-category fonts", () => {
|
||||
expect(buildResumeFontFamily("Times-Roman").endsWith("serif")).toBe(true);
|
||||
});
|
||||
|
||||
it("includes system-ui and Segoe UI fallbacks", () => {
|
||||
const result = buildResumeFontFamily("Roboto");
|
||||
expect(result).toContain("system-ui");
|
||||
expect(result).toContain("Segoe UI");
|
||||
});
|
||||
|
||||
it("includes CJK fallbacks for sans-serif fonts", () => {
|
||||
const result = buildResumeFontFamily("Roboto");
|
||||
expect(result).toContain("Noto Sans SC");
|
||||
});
|
||||
|
||||
it("includes CJK serif fallbacks for serif fonts", () => {
|
||||
const result = buildResumeFontFamily("Times-Roman");
|
||||
expect(result).toContain("Noto Serif SC");
|
||||
});
|
||||
|
||||
it("does not duplicate primary family in fallback list", () => {
|
||||
const result = buildResumeFontFamily("Noto Sans SC");
|
||||
// Family should appear once
|
||||
const occurrences = result.split("Noto Sans SC").length - 1;
|
||||
expect(occurrences).toBe(1);
|
||||
});
|
||||
|
||||
it("escapes single quotes in family names", () => {
|
||||
const result = buildResumeFontFamily("Bob's Font");
|
||||
expect(result).toContain("Bob\\'s Font");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user