mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-07-13 22:37:14 +10:00
chore(schema): delete tautology test, add helpers, collapse styleRuleSlots
- Delete templates.test.ts: re-tests z.enum semantics with a hardcoded fixture that duplicates the template list in templates.ts. - default.ts: add 2-line section(icon) helper; 12 repeated 7-field blocks collapse to one-liners. Output is byte-identical. - data.ts: add 2-line itemSection<T> factory; 12 baseSectionSchema.extend() blocks collapse to one-liners. Inferred types unchanged. - data.ts: replace 15-field hand-listed styleRuleSlotsSchema with z.partialRecord(styleSlotSchema, styleIntentSchema); parse behaviour and TypeScript type are equivalent (unknown keys still rejected). Claude-Session: https://claude.ai/code/session_012Bnvt1MghwHj4qQRxuQUGa
This commit is contained in:
@@ -297,53 +297,28 @@ export const baseSectionSchema = z.object({
|
||||
startOnNewPage: z.boolean().catch(false).describe("If true, the section always begins on a new page."),
|
||||
});
|
||||
|
||||
const awardsSectionSchema = baseSectionSchema.extend({
|
||||
items: z.array(awardItemSchema).describe("The items to display in the awards section."),
|
||||
});
|
||||
// ponytail: 12 identical baseSectionSchema.extend({ items }) blocks collapsed to a factory
|
||||
const itemSection = <T extends z.ZodTypeAny>(itemSchema: T, description: string) =>
|
||||
baseSectionSchema.extend({ items: z.array(itemSchema).describe(description) });
|
||||
|
||||
const certificationsSectionSchema = baseSectionSchema.extend({
|
||||
items: z.array(certificationItemSchema).describe("The items to display in the certifications section."),
|
||||
});
|
||||
|
||||
const educationSectionSchema = baseSectionSchema.extend({
|
||||
items: z.array(educationItemSchema).describe("The items to display in the education section."),
|
||||
});
|
||||
|
||||
const experienceSectionSchema = baseSectionSchema.extend({
|
||||
items: z.array(experienceItemSchema).describe("The items to display in the experience section."),
|
||||
});
|
||||
|
||||
const interestsSectionSchema = baseSectionSchema.extend({
|
||||
items: z.array(interestItemSchema).describe("The items to display in the interests section."),
|
||||
});
|
||||
|
||||
const languagesSectionSchema = baseSectionSchema.extend({
|
||||
items: z.array(languageItemSchema).describe("The items to display in the languages section."),
|
||||
});
|
||||
|
||||
const profilesSectionSchema = baseSectionSchema.extend({
|
||||
items: z.array(profileItemSchema).describe("The items to display in the profiles section."),
|
||||
});
|
||||
|
||||
const projectsSectionSchema = baseSectionSchema.extend({
|
||||
items: z.array(projectItemSchema).describe("The items to display in the projects section."),
|
||||
});
|
||||
|
||||
const publicationsSectionSchema = baseSectionSchema.extend({
|
||||
items: z.array(publicationItemSchema).describe("The items to display in the publications section."),
|
||||
});
|
||||
|
||||
const referencesSectionSchema = baseSectionSchema.extend({
|
||||
items: z.array(referenceItemSchema).describe("The items to display in the references section."),
|
||||
});
|
||||
|
||||
const skillsSectionSchema = baseSectionSchema.extend({
|
||||
items: z.array(skillItemSchema).describe("The items to display in the skills section."),
|
||||
});
|
||||
|
||||
const volunteerSectionSchema = baseSectionSchema.extend({
|
||||
items: z.array(volunteerItemSchema).describe("The items to display in the volunteer section."),
|
||||
});
|
||||
const awardsSectionSchema = itemSection(awardItemSchema, "The items to display in the awards section.");
|
||||
const certificationsSectionSchema = itemSection(
|
||||
certificationItemSchema,
|
||||
"The items to display in the certifications section.",
|
||||
);
|
||||
const educationSectionSchema = itemSection(educationItemSchema, "The items to display in the education section.");
|
||||
const experienceSectionSchema = itemSection(experienceItemSchema, "The items to display in the experience section.");
|
||||
const interestsSectionSchema = itemSection(interestItemSchema, "The items to display in the interests section.");
|
||||
const languagesSectionSchema = itemSection(languageItemSchema, "The items to display in the languages section.");
|
||||
const profilesSectionSchema = itemSection(profileItemSchema, "The items to display in the profiles section.");
|
||||
const projectsSectionSchema = itemSection(projectItemSchema, "The items to display in the projects section.");
|
||||
const publicationsSectionSchema = itemSection(
|
||||
publicationItemSchema,
|
||||
"The items to display in the publications section.",
|
||||
);
|
||||
const referencesSectionSchema = itemSection(referenceItemSchema, "The items to display in the references section.");
|
||||
const skillsSectionSchema = itemSection(skillItemSchema, "The items to display in the skills section.");
|
||||
const volunteerSectionSchema = itemSection(volunteerItemSchema, "The items to display in the volunteer section.");
|
||||
|
||||
const sectionsSchema = z.object({
|
||||
profiles: profilesSectionSchema.describe("The section to display the profiles of the author."),
|
||||
@@ -573,24 +548,9 @@ const styleIntentSchema = z
|
||||
|
||||
export type StyleIntent = z.infer<typeof styleIntentSchema>;
|
||||
|
||||
// ponytail: 15 hand-listed optional slots collapsed to partialRecord; unknown keys still rejected
|
||||
const styleRuleSlotsSchema = z
|
||||
.strictObject({
|
||||
section: styleIntentSchema.optional(),
|
||||
heading: styleIntentSchema.optional(),
|
||||
item: styleIntentSchema.optional(),
|
||||
text: styleIntentSchema.optional(),
|
||||
secondaryText: styleIntentSchema.optional(),
|
||||
link: styleIntentSchema.optional(),
|
||||
icon: styleIntentSchema.optional(),
|
||||
level: styleIntentSchema.optional(),
|
||||
richParagraph: styleIntentSchema.optional(),
|
||||
richList: styleIntentSchema.optional(),
|
||||
richListItemRow: styleIntentSchema.optional(),
|
||||
richListItemContent: styleIntentSchema.optional(),
|
||||
richLink: styleIntentSchema.optional(),
|
||||
richBold: styleIntentSchema.optional(),
|
||||
richMark: styleIntentSchema.optional(),
|
||||
})
|
||||
.partialRecord(styleSlotSchema, styleIntentSchema)
|
||||
.refine((slots) => Object.values(slots).some(Boolean), {
|
||||
message: "At least one style slot must be configured.",
|
||||
});
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
import type { ResumeData } from "./data";
|
||||
|
||||
// ponytail: 12 identical 7-field section blocks collapsed to a helper
|
||||
const section = (icon: string) => ({
|
||||
title: "",
|
||||
icon,
|
||||
columns: 1,
|
||||
hidden: false,
|
||||
keepTogether: false,
|
||||
startOnNewPage: false,
|
||||
items: [] as never[],
|
||||
});
|
||||
|
||||
export const defaultResumeData: ResumeData = {
|
||||
picture: {
|
||||
hidden: false,
|
||||
@@ -32,114 +43,18 @@ export const defaultResumeData: ResumeData = {
|
||||
content: "",
|
||||
},
|
||||
sections: {
|
||||
profiles: {
|
||||
title: "",
|
||||
icon: "messenger-logo",
|
||||
columns: 1,
|
||||
hidden: false,
|
||||
keepTogether: false,
|
||||
startOnNewPage: false,
|
||||
items: [],
|
||||
},
|
||||
experience: {
|
||||
title: "",
|
||||
icon: "briefcase",
|
||||
columns: 1,
|
||||
hidden: false,
|
||||
keepTogether: false,
|
||||
startOnNewPage: false,
|
||||
items: [],
|
||||
},
|
||||
education: {
|
||||
title: "",
|
||||
icon: "graduation-cap",
|
||||
columns: 1,
|
||||
hidden: false,
|
||||
keepTogether: false,
|
||||
startOnNewPage: false,
|
||||
items: [],
|
||||
},
|
||||
projects: {
|
||||
title: "",
|
||||
icon: "code-simple",
|
||||
columns: 1,
|
||||
hidden: false,
|
||||
keepTogether: false,
|
||||
startOnNewPage: false,
|
||||
items: [],
|
||||
},
|
||||
skills: {
|
||||
title: "",
|
||||
icon: "compass-tool",
|
||||
columns: 1,
|
||||
hidden: false,
|
||||
keepTogether: false,
|
||||
startOnNewPage: false,
|
||||
items: [],
|
||||
},
|
||||
languages: {
|
||||
title: "",
|
||||
icon: "translate",
|
||||
columns: 1,
|
||||
hidden: false,
|
||||
keepTogether: false,
|
||||
startOnNewPage: false,
|
||||
items: [],
|
||||
},
|
||||
interests: {
|
||||
title: "",
|
||||
icon: "football",
|
||||
columns: 1,
|
||||
hidden: false,
|
||||
keepTogether: false,
|
||||
startOnNewPage: false,
|
||||
items: [],
|
||||
},
|
||||
awards: {
|
||||
title: "",
|
||||
icon: "trophy",
|
||||
columns: 1,
|
||||
hidden: false,
|
||||
keepTogether: false,
|
||||
startOnNewPage: false,
|
||||
items: [],
|
||||
},
|
||||
certifications: {
|
||||
title: "",
|
||||
icon: "certificate",
|
||||
columns: 1,
|
||||
hidden: false,
|
||||
keepTogether: false,
|
||||
startOnNewPage: false,
|
||||
items: [],
|
||||
},
|
||||
publications: {
|
||||
title: "",
|
||||
icon: "books",
|
||||
columns: 1,
|
||||
hidden: false,
|
||||
keepTogether: false,
|
||||
startOnNewPage: false,
|
||||
items: [],
|
||||
},
|
||||
volunteer: {
|
||||
title: "",
|
||||
icon: "hand-heart",
|
||||
columns: 1,
|
||||
hidden: false,
|
||||
keepTogether: false,
|
||||
startOnNewPage: false,
|
||||
items: [],
|
||||
},
|
||||
references: {
|
||||
title: "",
|
||||
icon: "phone",
|
||||
columns: 1,
|
||||
hidden: false,
|
||||
keepTogether: false,
|
||||
startOnNewPage: false,
|
||||
items: [],
|
||||
},
|
||||
profiles: section("messenger-logo"),
|
||||
experience: section("briefcase"),
|
||||
education: section("graduation-cap"),
|
||||
projects: section("code-simple"),
|
||||
skills: section("compass-tool"),
|
||||
languages: section("translate"),
|
||||
interests: section("football"),
|
||||
awards: section("trophy"),
|
||||
certifications: section("certificate"),
|
||||
publications: section("books"),
|
||||
volunteer: section("hand-heart"),
|
||||
references: section("phone"),
|
||||
},
|
||||
customSections: [],
|
||||
metadata: {
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { templateSchema } from "./templates";
|
||||
|
||||
describe("templateSchema", () => {
|
||||
it("accepts known template names", () => {
|
||||
const validTemplates = [
|
||||
"azurill",
|
||||
"bronzor",
|
||||
"chikorita",
|
||||
"ditgar",
|
||||
"ditto",
|
||||
"gengar",
|
||||
"glalie",
|
||||
"kakuna",
|
||||
"lapras",
|
||||
"leafish",
|
||||
"meowth",
|
||||
"onyx",
|
||||
"pikachu",
|
||||
"rhyhorn",
|
||||
"scizor",
|
||||
];
|
||||
for (const t of validTemplates) {
|
||||
expect(templateSchema.safeParse(t).success).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects unknown template names", () => {
|
||||
expect(templateSchema.safeParse("unknown").success).toBe(false);
|
||||
expect(templateSchema.safeParse("").success).toBe(false);
|
||||
expect(templateSchema.safeParse("ONYX").success).toBe(false); // case-sensitive
|
||||
});
|
||||
|
||||
it("rejects non-string values", () => {
|
||||
expect(templateSchema.safeParse(null).success).toBe(false);
|
||||
expect(templateSchema.safeParse(undefined).success).toBe(false);
|
||||
expect(templateSchema.safeParse(42).success).toBe(false);
|
||||
});
|
||||
|
||||
it("returns the exact value for a valid template", () => {
|
||||
const result = templateSchema.safeParse("onyx");
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.data).toBe("onyx");
|
||||
}
|
||||
});
|
||||
|
||||
it("includes 14 templates", () => {
|
||||
const validTemplates = templateSchema.options;
|
||||
expect(validTemplates).toHaveLength(15);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user