mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-08-23 06:42:15 +10:00
131 lines
4.0 KiB
TypeScript
131 lines
4.0 KiB
TypeScript
import type { ResumeData } from "@reactive-resume/schema/resume/data";
|
||
import type { SectionTitleResolver } from "./section-title";
|
||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||
import { sampleResumeData } from "@reactive-resume/schema/resume/sample";
|
||
|
||
const rendererMock = vi.hoisted(() => ({
|
||
pdf: vi.fn(() => ({
|
||
toBlob: vi.fn(async () => new Blob(["%PDF"], { type: "application/pdf" })),
|
||
})),
|
||
}));
|
||
|
||
vi.mock("#react-pdf-renderer", async (importOriginal) => ({
|
||
...(await importOriginal<typeof import("@react-pdf/renderer")>()),
|
||
pdf: rendererMock.pdf,
|
||
}));
|
||
|
||
vi.mock("./document", () => ({
|
||
ResumeDocument: () => null,
|
||
}));
|
||
|
||
const createLegacyRendererSafeResumeData = (): ResumeData =>
|
||
({
|
||
...structuredClone(sampleResumeData),
|
||
customSections: [
|
||
{
|
||
id: "custom-experience",
|
||
type: "experience",
|
||
title: "Experience",
|
||
icon: "",
|
||
columns: 1,
|
||
hidden: false,
|
||
keepTogether: false,
|
||
startOnNewPage: false,
|
||
items: [
|
||
{
|
||
id: "experience-item",
|
||
hidden: false,
|
||
company: "Analytical Engines",
|
||
position: "Programmer",
|
||
location: "London",
|
||
period: "1842–1843",
|
||
description: "<p>Wrote the first algorithm.</p>",
|
||
content: "<p>Compatible overlap</p>",
|
||
},
|
||
],
|
||
},
|
||
],
|
||
}) as unknown as ResumeData;
|
||
|
||
const createRendererUnsafeResumeData = (): ResumeData => {
|
||
const data = createLegacyRendererSafeResumeData();
|
||
const section = data.customSections[0];
|
||
if (!section) throw new Error("Expected a custom section fixture.");
|
||
section.items = [{ id: "summary-item", hidden: false, content: "<p>Missing company</p>" }] as never;
|
||
return data;
|
||
};
|
||
|
||
describe("createResumePdfBlob", () => {
|
||
beforeEach(() => {
|
||
rendererMock.pdf.mockClear();
|
||
});
|
||
|
||
it("renders ResumeDocument with data, template, and section title resolver", async () => {
|
||
const resolveSectionTitle: SectionTitleResolver = (input) => input.defaultEnglishTitle ?? input.sectionId;
|
||
const { createResumePdfBlob } = await import("./browser");
|
||
const data = createLegacyRendererSafeResumeData();
|
||
|
||
const blob = await createResumePdfBlob({
|
||
data,
|
||
template: "azurill",
|
||
resolveSectionTitle,
|
||
});
|
||
|
||
expect(blob.type).toBe("application/pdf");
|
||
expect(rendererMock.pdf).toHaveBeenCalledTimes(1);
|
||
expect(rendererMock.pdf).toHaveBeenCalledWith(
|
||
expect.objectContaining({
|
||
props: expect.objectContaining({
|
||
template: "azurill",
|
||
resolveSectionTitle,
|
||
data: expect.objectContaining({
|
||
customSections: [
|
||
expect.objectContaining({
|
||
items: [
|
||
expect.objectContaining({
|
||
content: "<p>Compatible overlap</p>",
|
||
roles: [],
|
||
website: { url: "", label: "", inlineLink: false },
|
||
}),
|
||
],
|
||
}),
|
||
],
|
||
}),
|
||
}),
|
||
}),
|
||
);
|
||
});
|
||
|
||
it("rejects renderer-unsafe data before browser PDF dispatch", async () => {
|
||
const { createResumePdfBlob } = await import("./browser");
|
||
|
||
const error = await createResumePdfBlob({ data: createRendererUnsafeResumeData() }).catch(
|
||
(caught: unknown) => caught,
|
||
);
|
||
|
||
expect(error).toHaveProperty("issues.0.path", ["customSections", 0, "items", 0, "company"]);
|
||
expect(rendererMock.pdf).not.toHaveBeenCalled();
|
||
});
|
||
|
||
it("returns a rejected Promise when the renderer fails synchronously", async () => {
|
||
rendererMock.pdf.mockImplementationOnce(() => {
|
||
throw new Error("renderer failed");
|
||
});
|
||
const { createResumePdfBlob } = await import("./browser");
|
||
|
||
const promise = createResumePdfBlob({ data: sampleResumeData });
|
||
|
||
expect(promise).toBeInstanceOf(Promise);
|
||
await expect(promise).rejects.toThrow("renderer failed");
|
||
});
|
||
|
||
it("renders with base styles when the source is fatal", async () => {
|
||
const data = structuredClone(sampleResumeData);
|
||
data.metadata.stylesheet = { mode: "semantic", source: { languageVersion: 2, text: "@version 2;" } };
|
||
const { createResumePdfBlob } = await import("./browser");
|
||
|
||
await expect(createResumePdfBlob({ data })).resolves.toHaveProperty("type", "application/pdf");
|
||
expect(rendererMock.pdf).toHaveBeenCalledTimes(1);
|
||
});
|
||
});
|