Files
Reactive-Resume/packages/pdf/src/browser.test.tsx
T

131 lines
4.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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: "18421843",
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);
});
});