feat: add semantic CSS stylesheets (#3274)

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
This commit is contained in:
Amruth Pillai
2026-07-30 12:39:15 +02:00
committed by GitHub
co-authored by Cursor Agent
parent 4ac19f81b3
commit d2ffbf9618
320 changed files with 78393 additions and 2915 deletions
+105 -5
View File
@@ -1,3 +1,4 @@
import type { ResumeData } from "@reactive-resume/schema/resume/data";
import type { SectionTitleResolver } from "./section-title";
import { Buffer } from "node:buffer";
import { beforeEach, describe, expect, it, vi } from "vitest";
@@ -7,7 +8,54 @@ const rendererMock = vi.hoisted(() => ({
renderToBuffer: vi.fn(async () => Buffer.from("%PDF")),
}));
vi.mock("@react-pdf/renderer", async (importOriginal) => ({
const createRendererUnsafeResumeData = (): ResumeData => {
const data = structuredClone(sampleResumeData);
data.customSections = [
{
id: "custom-experience",
type: "experience",
title: "Experience",
icon: "",
columns: 1,
hidden: false,
keepTogether: false,
startOnNewPage: false,
items: [{ id: "summary-shaped-item", hidden: false, content: "<p>Missing company</p>" }],
} as never,
];
return data;
};
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;
vi.mock("#react-pdf-renderer", async (importOriginal) => ({
...(await importOriginal<typeof import("@react-pdf/renderer")>()),
renderToBuffer: rendererMock.renderToBuffer,
}));
@@ -24,9 +72,10 @@ describe("createResumePdfFile", () => {
it("renders ResumeDocument with data, filename, template, and section title resolver", async () => {
const resolveSectionTitle: SectionTitleResolver = (input) => input.defaultEnglishTitle ?? input.sectionId;
const { createResumePdfFile } = await import("./server");
const data = createLegacyRendererSafeResumeData();
const file = await createResumePdfFile({
data: sampleResumeData,
data,
filename: "resume.pdf",
template: "azurill",
resolveSectionTitle,
@@ -38,12 +87,63 @@ describe("createResumePdfFile", () => {
expect(rendererMock.renderToBuffer).toHaveBeenCalledTimes(1);
expect(rendererMock.renderToBuffer).toHaveBeenCalledWith(
expect.objectContaining({
props: {
data: sampleResumeData,
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("returns semantic diagnostics without rendering an invalid applied source", async () => {
const data = structuredClone(sampleResumeData);
const invalid = { languageVersion: 1, text: "@version 1; section { color: ; }" };
data.metadata.stylesheet = { mode: "semantic", source: invalid, applied: invalid };
const { createResumePdfFileResult } = await import("./server");
const result = await createResumePdfFileResult({ data, filename: "resume.pdf" });
expect(result).toMatchObject({
ok: false,
diagnostics: [expect.objectContaining({ severity: "error" })],
});
expect(rendererMock.renderToBuffer).not.toHaveBeenCalled();
});
it("rejects unchecked rendering instead of producing an unstyled PDF for semantic errors", async () => {
const data = structuredClone(sampleResumeData);
const invalid = { languageVersion: 1, text: "@version 1; section { color: ; }" };
data.metadata.stylesheet = { mode: "semantic", source: invalid, applied: invalid };
const { createResumePdfFile } = await import("./server");
await expect(createResumePdfFile({ data, filename: "resume.pdf" })).rejects.toMatchObject({
cause: [expect.objectContaining({ severity: "error" })],
});
expect(rendererMock.renderToBuffer).not.toHaveBeenCalled();
});
it("rejects renderer-unsafe data at the server boundary before React PDF dispatch", async () => {
const { createResumePdfFile } = await import("./server");
const error = await createResumePdfFile({
data: createRendererUnsafeResumeData(),
filename: "resume.pdf",
}).catch((caught: unknown) => caught);
expect(error).toHaveProperty("issues.0.path", ["customSections", 0, "items", 0, "company"]);
expect(rendererMock.renderToBuffer).not.toHaveBeenCalled();
});
});