mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-08-24 15:22:20 +10:00
feat: add semantic CSS stylesheets (#3274)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor Agent
parent
4ac19f81b3
commit
d2ffbf9618
@@ -0,0 +1,123 @@
|
||||
import type { ResumeData } from "@reactive-resume/schema/resume/data";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { defaultResumeData } from "@reactive-resume/schema/resume/default";
|
||||
import { createResumeData, hasRenderDataChanged, preserveServerStylesheet } from "./stylesheet-preservation";
|
||||
|
||||
const cloneData = (): ResumeData => structuredClone(defaultResumeData);
|
||||
|
||||
const semanticStylesheet = {
|
||||
mode: "semantic",
|
||||
source: { languageVersion: 1, text: "@version 1;\nresume { color: red; }\n" },
|
||||
applied: { languageVersion: 1, text: "@version 1;\nresume { color: red; }\n" },
|
||||
} as const;
|
||||
|
||||
describe("preserveServerStylesheet", () => {
|
||||
it("preserves the server-owned stylesheet when an old client omits it", () => {
|
||||
const serverData = cloneData();
|
||||
serverData.metadata.stylesheet = semanticStylesheet;
|
||||
const clientData = cloneData();
|
||||
|
||||
const merged = preserveServerStylesheet(serverData, clientData);
|
||||
|
||||
expect(merged.metadata.stylesheet).toEqual(semanticStylesheet);
|
||||
});
|
||||
|
||||
it("preserves the server-owned stylesheet when a client tries to replace it", () => {
|
||||
const serverData = cloneData();
|
||||
serverData.metadata.stylesheet = semanticStylesheet;
|
||||
const clientData = cloneData();
|
||||
clientData.metadata.stylesheet = {
|
||||
mode: "legacy",
|
||||
source: { languageVersion: 1, text: "@version 1;\n" },
|
||||
applied: { languageVersion: 1, text: "@version 1;\n" },
|
||||
};
|
||||
|
||||
expect(preserveServerStylesheet(serverData, clientData).metadata.stylesheet).toEqual(semanticStylesheet);
|
||||
});
|
||||
|
||||
it("does not let a generic update introduce a stylesheet", () => {
|
||||
const serverData = cloneData();
|
||||
const clientData = cloneData();
|
||||
clientData.metadata.stylesheet = semanticStylesheet;
|
||||
|
||||
expect(preserveServerStylesheet(serverData, clientData).metadata.stylesheet).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("hasRenderDataChanged", () => {
|
||||
it("detects content and base-design changes", () => {
|
||||
const before = cloneData();
|
||||
const changedContent = cloneData();
|
||||
changedContent.basics.name = "Changed";
|
||||
const changedDesign = cloneData();
|
||||
changedDesign.metadata.design.colors.primary = "#000000";
|
||||
|
||||
expect(hasRenderDataChanged(before, changedContent)).toBe(true);
|
||||
expect(hasRenderDataChanged(before, changedDesign)).toBe(true);
|
||||
});
|
||||
|
||||
it("ignores notes and stylesheet source or applied changes", () => {
|
||||
const before = cloneData();
|
||||
before.metadata.stylesheet = semanticStylesheet;
|
||||
const changedNotes = structuredClone(before);
|
||||
changedNotes.metadata.notes = "private note";
|
||||
const changedStylesheet = structuredClone(before);
|
||||
changedStylesheet.metadata.stylesheet = {
|
||||
...semanticStylesheet,
|
||||
source: { languageVersion: 1, text: "@version 1;\nresume { color: blue; }\n" },
|
||||
applied: { languageVersion: 1, text: "@version 1;\nresume { color: blue; }\n" },
|
||||
};
|
||||
|
||||
expect(hasRenderDataChanged(before, changedNotes)).toBe(false);
|
||||
expect(hasRenderDataChanged(before, changedStylesheet)).toBe(false);
|
||||
});
|
||||
|
||||
it("detects active legacy style-rule changes but ignores dormant rules in semantic mode", () => {
|
||||
const legacyBefore = cloneData();
|
||||
const legacyAfter = cloneData();
|
||||
legacyAfter.metadata.styleRules = [
|
||||
{
|
||||
id: "rule",
|
||||
label: "Rule",
|
||||
enabled: true,
|
||||
target: { scope: "global" },
|
||||
slots: { heading: { color: "#000000" } },
|
||||
},
|
||||
];
|
||||
const semanticBefore = cloneData();
|
||||
semanticBefore.metadata.stylesheet = semanticStylesheet;
|
||||
const semanticAfter = structuredClone(semanticBefore);
|
||||
semanticAfter.metadata.styleRules = legacyAfter.metadata.styleRules;
|
||||
|
||||
expect(hasRenderDataChanged(legacyBefore, legacyAfter)).toBe(true);
|
||||
expect(hasRenderDataChanged(semanticBefore, semanticAfter)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("createResumeData", () => {
|
||||
it("seeds empty semantic source only for the default-enabled cohort", () => {
|
||||
expect(createResumeData({ semanticCssDefault: true }).metadata.stylesheet).toEqual({
|
||||
mode: "semantic",
|
||||
source: { languageVersion: 1, text: "@version 1;\n" },
|
||||
applied: { languageVersion: 1, text: "@version 1;\n" },
|
||||
});
|
||||
expect(createResumeData({ semanticCssDefault: false }).metadata.stylesheet).toBeUndefined();
|
||||
});
|
||||
|
||||
it("clones normal and sample defaults instead of mutating shared data", () => {
|
||||
const normal = createResumeData({ semanticCssDefault: false, locale: "de-DE" });
|
||||
const sample = createResumeData({
|
||||
semanticCssDefault: true,
|
||||
withSampleData: true,
|
||||
name: "Sample Person",
|
||||
locale: "de-DE",
|
||||
});
|
||||
|
||||
normal.basics.name = "Mutated";
|
||||
sample.metadata.page.locale = "en-US";
|
||||
|
||||
expect(defaultResumeData.basics.name).toBe("");
|
||||
expect(defaultResumeData.metadata.page.locale).not.toBe("de-DE");
|
||||
expect(sample.basics.name).toBe("Sample Person");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user