From 5ddd6bc36534c6df9867993b2d9d9599e800f4af Mon Sep 17 00:00:00 2001 From: Nalla Prakash <81852531+prakashNalla@users.noreply.github.com> Date: Thu, 15 Oct 2026 21:42:09 +0530 Subject: [PATCH] test(templates): render every template with satori --- CONTRIBUTING.md | 2 +- tests/templates/render.test.ts | 44 ++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 tests/templates/render.test.ts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c520883..aaca5e4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -128,7 +128,7 @@ public/fonts/ # Bundled .woff fonts (Inter, Playfair Display, JetBrains 5. Run `npm run dev`, open `http://localhost:4321/create/{category}` and check your template with long text and with optional fields left empty 6. Run `npm run check` and `npm run test` -The tests check the template's shape and size but don't run Satori yet, so the live preview in step 5 is where layout problems show up. You don't need to update template counts in the README. +`tests/templates/render.test.ts` renders every template with Satori, once with its defaults and once with the optional text fields empty, so a div with several children and no `display: 'flex'` fails `npm run test`. The live preview in step 5 is still where you check how it looks. You don't need to update template counts in the README. Use `src/templates/blog/minimal-dark.ts` as a reference implementation. diff --git a/tests/templates/render.test.ts b/tests/templates/render.test.ts new file mode 100644 index 0000000..d72838f --- /dev/null +++ b/tests/templates/render.test.ts @@ -0,0 +1,44 @@ +import { describe, it, expect } from 'vitest'; +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; +import satori from 'satori'; +import { getAllTemplates } from '@/templates/registry'; + +const fontFiles: [file: string, name: string, weight: 400 | 500 | 600 | 700][] = [ + ['Inter-Regular.woff', 'Inter', 400], + ['Inter-Medium.woff', 'Inter', 500], + ['Inter-SemiBold.woff', 'Inter', 600], + ['Inter-Bold.woff', 'Inter', 700], + ['PlayfairDisplay-Regular.woff', 'Playfair Display', 400], + ['PlayfairDisplay-Bold.woff', 'Playfair Display', 700], + ['JetBrainsMono-Regular.woff', 'JetBrains Mono', 400], + ['JetBrainsMono-Bold.woff', 'JetBrains Mono', 700], +]; + +const fonts = fontFiles.map(([file, name, weight]) => ({ + name, + data: readFileSync(join(process.cwd(), 'public/fonts', file)), + weight, + style: 'normal' as const, +})); + +const render = (element: unknown) => satori(element as any, { width: 1200, height: 630, fonts }); + +describe('template render', () => { + it.each(getAllTemplates().map((t) => [t.id, t] as const))('renders %s with its defaults', async (_id, template) => { + const svg = await render(template.render(template.defaults)); + expect(svg).toMatch(/^ [t.id, t] as const))( + 'renders %s with optional text fields empty', + async (_id, template) => { + const params = { ...template.defaults }; + for (const field of template.fields) { + if (!field.required && (field.type === 'text' || field.type === 'textarea')) params[field.key] = ''; + } + const svg = await render(template.render(params)); + expect(svg).toMatch(/^