Files
bmp-website-2026/tests/int/payload-field-inventory.int.spec.ts
2026-07-31 15:21:21 +02:00

171 lines
6.1 KiB
TypeScript

import type { Field } from 'payload'
import { describe, expect, it } from 'vitest'
import { ApplicationForm } from '@/ApplicationForm/config'
import { ApplicationPhase } from '@/ApplicationPhase/config'
import { Footer } from '@/Footer/config'
import { Header } from '@/Header/config'
import { NewsletterForm } from '@/NewsletterForm/config'
import { SiteSettings } from '@/SiteSettings/config'
import { aboutFields } from '@/collections/Pages/aboutFields'
import { homeFields } from '@/collections/Pages/homeFields'
import { netzwerkFields } from '@/collections/Pages/netzwerkFields'
import { participationFields } from '@/collections/Pages/participationFields'
import { pressIndexFields } from '@/collections/Pages/pressIndexFields'
function fieldPaths(fields: Field[], prefix = ''): string[] {
return fields.flatMap((field) => {
if (!('name' in field) || typeof field.name !== 'string') {
return 'fields' in field && Array.isArray(field.fields) ? fieldPaths(field.fields, prefix) : []
}
const path = prefix ? `${prefix}.${field.name}` : field.name
const children = 'fields' in field && Array.isArray(field.fields) ? fieldPaths(field.fields, path) : []
return [path, ...children]
})
}
function findField(fields: Field[], path: string): Field | undefined {
const [name, ...rest] = path.split('.')
const field = fields.find((candidate) => 'name' in candidate && candidate.name === name)
if (!field || rest.length === 0) return field
if (!('fields' in field) || !Array.isArray(field.fields)) return undefined
return findField(field.fields, rest.join('.'))
}
describe('Payload field inventory', () => {
it('keeps obsolete page controls out of the schema', () => {
const paths = [
...fieldPaths(homeFields),
...fieldPaths(participationFields),
...fieldPaths(aboutFields),
...fieldPaths(pressIndexFields),
...fieldPaths(netzwerkFields),
]
expect(paths).not.toEqual(
expect.arrayContaining([
'home.application',
'home.form',
'participation.applicationForm',
'participation.form',
'participation.eligibility.secondaryCta',
'participation.eligibility.nudgeCta',
'participation.applicationWays.recommendedLabel',
'participation.applicationWays.fallbackCta',
'participation.applicationWays.ways.featured',
'participation.applicationWays.ways.cta',
'about.highlights',
'pressIndex.events.detailLabel',
'pressIndex.news.readMoreLabel',
'pressIndex.downloads.kitLabel',
'pressIndex.downloads.kitMeta',
'pressIndex.downloads.kitFile',
'pressIndex.downloads.kitUrl',
'pressIndex.downloads.kitDownloadName',
'pressIndex.accreditation',
'netzwerk.jury.note',
'netzwerk.partners.premiumLabel',
'netzwerk.partners.defaultLogoColor',
'netzwerk.sponsoring.footnotePrefix',
'netzwerk.sponsoring.footnoteLabel',
'netzwerk.sponsoring.footnoteUrl',
'netzwerk.sponsoring.formEyebrow',
'netzwerk.sponsoringForm.packages',
'netzwerk.sponsoringForm.step1Eyebrow',
'netzwerk.sponsoringForm.step1Heading',
'netzwerk.sponsoringForm.requiredPackageError',
]),
)
})
it('retains rendered page controls and authoritative relationships', () => {
const paths = [
...fieldPaths(homeFields),
...fieldPaths(participationFields),
...fieldPaths(aboutFields),
...fieldPaths(pressIndexFields),
...fieldPaths(netzwerkFields),
]
expect(paths).toEqual(
expect.arrayContaining([
'home.hero',
'home.winners.featured',
'home.videoModal.video',
'participation.process.steps',
'participation.awardsGrid.cards',
'about.testimonials.items',
'pressIndex.events.statusLabels',
'pressIndex.downloads.contactName',
'pressIndex.downloads.contactImage',
'pressIndex.downloads.contactImageAlt',
'netzwerk.jury.members',
'netzwerk.patronage.greetingVideo',
'netzwerk.patronage.greetingVideoButtonLabel',
'netzwerk.patronage.greetingVideoTitle',
'netzwerk.patronage.greetingVideoDescription',
'netzwerk.patronage.greetingVideoCloseLabel',
'netzwerk.partners.tiers',
'netzwerk.partners.modal.websiteLabel',
'netzwerk.sponsoringForm.steps',
'netzwerk.sponsoringForm.requiredFieldError',
]),
)
})
it('keeps shared multi-page and site-wide content in Globals', () => {
const globals = [ApplicationForm, ApplicationPhase, NewsletterForm, Header, Footer, SiteSettings]
const slugs = globals.map((global) => global.slug)
expect(slugs).toEqual([
'application-form',
'application-phase',
'newsletter-form',
'header',
'footer',
'site-settings',
])
expect(fieldPaths(ApplicationForm.fields)).toEqual(expect.arrayContaining(['section', 'form']))
expect(fieldPaths(ApplicationPhase.fields)).toEqual(expect.arrayContaining(['activePhase', 'phases']))
expect(fieldPaths(NewsletterForm.fields)).toEqual(expect.arrayContaining(['evaluation', 'completed']))
})
it('exposes exactly two editable sponsoring step labels', () => {
expect(findField(netzwerkFields, 'netzwerk.sponsoringForm.steps')).toMatchObject({
minRows: 2,
maxRows: 2,
})
})
it('limits the Netzwerk greeting upload to video media', () => {
const field = findField(netzwerkFields, 'netzwerk.patronage.greetingVideo')
expect(field).toMatchObject({
type: 'upload',
relationTo: 'media',
filterOptions: {
mimeType: {
contains: 'video',
},
},
})
expect(field).not.toHaveProperty('defaultValue')
})
it('limits the press contact upload to image media', () => {
const field = findField(pressIndexFields, 'pressIndex.downloads.contactImage')
expect(field).toMatchObject({
type: 'upload',
relationTo: 'media',
filterOptions: {
mimeType: {
contains: 'image',
},
},
})
expect(field).not.toHaveProperty('defaultValue')
})
})