import { describe, expect, it, vi } from 'vitest'
import { renderToStaticMarkup } from 'react-dom/server'
import type { ImageLoaderProps } from 'next/image'
import type { Media } from '~/data/schema'
vi.mock('next-intl', () => ({
useTranslations: () => (key: string) => key,
}))
vi.mock('next/image', () => ({
default: ({ loader, src, alt, sizes }: {
loader?: (args: ImageLoaderProps) => string
src: string
alt: string
sizes?: string
}) =>
,
}))
const { blockRegistry } = await import('~/components/blocks/registry')
const base: Media = {
id: '0b6f0f4a-1c1f-4a51-9b2f-1d0a41f00001',
projectId: '0b6f0f4a-1c1f-4a51-9b2f-1d0a41f00009',
kind: 'image',
originalFilename: 'schema.png',
path: 'projekt/bild/original.png',
mime: 'image/png',
width: 2000,
height: 1125,
byteSize: 12345,
variants: [
{ width: 480, format: 'webp', path: 'projekt/bild/w480.webp' },
{ width: 960, format: 'webp', path: 'projekt/bild/w960.webp' },
],
variantError: null,
alt: 'Ein Schema',
caption: 'Bildunterschrift',
uploadedBy: null,
createdAt: new Date('2026-07-30T07:00:00Z'),
}
function media(...items: Media[]): Map {
return new Map(items.map(item => [item.id, item]))
}
function render(type: string, data: Record, items: Map): string {
const Component = blockRegistry[type]
if (!Component) {
throw new Error(`kein Block für ${type}`)
}
return renderToStaticMarkup()
}
describe('Blöcke ohne Medium', () => {
it('bleibt bei jedem Typ ohne Daten leer statt kaputt', () => {
for (const type of Object.keys(blockRegistry)) {
expect(render(type, {}, new Map()), type).toBe('')
}
})
it('verliert kein Bild, wenn die Kennung ins Leere zeigt', () => {
const missing = { mediaId: base.id, mediaIds: [base.id], beforeMediaId: base.id, afterMediaId: base.id }
for (const type of ['image', 'gallery', 'before_after', 'video']) {
expect(render(type, missing, new Map()), type).toBe('')
}
})
it('zeigt den Videoverweis auch ohne Medium', () => {
const markup = render('video', { url: 'https://example.test/film' }, new Map())
expect(markup).toContain('https://example.test/film')
})
})
describe('Blöcke mit Medium', () => {
it('holt die passende Fassung über den Medienweg', () => {
const markup = render('image', { mediaId: base.id }, media(base))
expect(markup).toContain('/api/v1/media/file/projekt/bild/w960.webp')
expect(markup).toContain('Ein Schema')
})
it('nimmt das Original, wenn keine Fassung erzeugt wurde', () => {
const original: Media = { ...base, variants: [] }
const markup = render('image', { mediaId: original.id }, media(original))
expect(markup).toContain('/api/v1/media/file/projekt/bild/original.png')
})
it('stellt die Galerie mit Vorschaubildern auf', () => {
const second: Media = { ...base, id: '0b6f0f4a-1c1f-4a51-9b2f-1d0a41f00002', caption: null }
const markup = render('gallery', { mediaIds: [base.id, second.id] }, media(base, second))
expect(markup.match(/
{
const after: Media = { ...base, id: '0b6f0f4a-1c1f-4a51-9b2f-1d0a41f00003' }
const markup = render(
'before_after',
{ beforeMediaId: base.id, afterMediaId: after.id },
media(base, after),
)
expect(markup).toContain('type="range"')
expect(markup).toContain('clip-path')
})
it('zeigt nur eine Seite, wenn das Gegenstück fehlt', () => {
const markup = render('before_after', { beforeMediaId: base.id }, media(base))
expect(markup).not.toContain('type="range"')
expect(markup).toContain('before')
})
it('spielt ein Video als Video aus', () => {
const film: Media = { ...base, kind: 'video', mime: 'video/mp4', variants: [] }
const markup = render('video', { mediaId: film.id }, media(film))
expect(markup).toContain('