Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
44 changes: 44 additions & 0 deletions tests/templates/render.test.ts
Original file line number Diff line number Diff line change
@@ -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(/^<svg/);
});

it.each(getAllTemplates().map((t) => [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(/^<svg/);
},
);
});