fix(participation): clarify eligibility hierarchy

This commit is contained in:
syntaxbullet
2026-07-31 18:07:05 +02:00
parent d7a720460f
commit 4bb265dac1
2 changed files with 206 additions and 43 deletions

View File

@@ -0,0 +1,157 @@
import { cleanup, render, screen, within } from '@testing-library/react'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { CmsRouteProvider, type CmsRouteData } from '@/spa/cmsRoute'
import Participation from '@/spa/pages/Participation'
const viewport = vi.hoisted(() => ({ mobile: false }))
vi.mock('@/spa/hooks/useIsMobile', () => ({
useIsMobile: () => viewport.mobile,
}))
vi.mock('next/navigation', () => ({
usePathname: () => '/teilnahme',
useRouter: () => ({ push: vi.fn(), replace: vi.fn() }),
}))
vi.mock('@/spa/components/WegZurAuszeichnungSection', () => ({
default: () => <section data-testid="timeline-stub" />,
}))
vi.mock('@/spa/components/AwardsGridSection', () => ({
default: () => <section data-testid="awards-stub" />,
}))
vi.mock('@/spa/components/forms/ApplicationFormSection', () => ({
ApplicationFormSection: () => <section data-testid="application-form-stub" />,
}))
vi.mock('@/spa/components/ui/UnoptimizedImage', () => ({
default: ({ alt }: { alt: string }) => <div role="img" aria-label={alt} />,
}))
const participationRoute: CmsRouteData = {
collection: 'pages',
doc: {
id: 'participation-page',
slug: 'teilnahme',
spaPath: '/teilnahme',
participation: {
eligibility: {
eyebrow: 'CMS eligibility eyebrow',
heading: 'CMS ELIGIBILITY\nHEADING',
description: 'CMS eligibility introduction.',
primaryCta: { label: 'CMS eligibility CTA', url: '/kontakt' },
criteria: [
{
num: 'A',
title: 'First CMS criterion',
body: 'First CMS criterion body.',
icon: 'mapPin',
},
{
num: 'B',
title: 'Second CMS criterion',
body: 'Second CMS criterion body.',
icon: 'users',
},
],
notes: [
{ title: 'First CMS note', body: 'First CMS note body.', icon: 'building2' },
{ title: 'Second CMS note', body: 'Second CMS note body.', icon: 'userCheck' },
],
nudgeText: 'CMS eligibility conclusion.',
},
datesBanner: {
heading: 'CMS important dates',
description: 'CMS dates description.',
mobileItems: [{ date: 'Mobile date', label: 'Mobile label' }],
desktopItems: [{ date: 'Desktop date', label: 'Desktop label' }],
},
},
},
}
const renderParticipation = () =>
render(
<CmsRouteProvider value={participationRoute}>
<Participation />
</CmsRouteProvider>,
)
describe('participation eligibility hierarchy', () => {
beforeEach(() => {
viewport.mobile = false
})
afterEach(() => cleanup())
it('keeps CMS eligibility content and its CTA authoritative inside semantic groups', () => {
renderParticipation()
const section = screen.getByTestId('eligibility-section')
const criteria = within(section).getAllByTestId('eligibility-criterion')
const notes = within(section).getAllByTestId('eligibility-note')
const cta = within(section).getByRole('link', { name: 'CMS eligibility CTA' })
expect(within(section).getByRole('heading', { level: 2 }).textContent).toBe(
'CMS ELIGIBILITYHEADING',
)
expect(within(section).getByText('CMS eligibility introduction.')).toBeTruthy()
expect(criteria.map((row) => row.textContent)).toEqual([
'AFirst CMS criterionFirst CMS criterion body.',
'BSecond CMS criterionSecond CMS criterion body.',
])
expect(criteria.every((row) => row.tagName === 'LI')).toBe(true)
expect(criteria[0]?.parentElement?.tagName).toBe('OL')
expect(notes.map((note) => note.textContent)).toEqual([
'First CMS noteFirst CMS note body.',
'Second CMS noteSecond CMS note body.',
])
expect(within(section).getByText('CMS eligibility conclusion.')).toBeTruthy()
expect(cta.getAttribute('href')).toBe('/kontakt')
expect(within(section).getAllByRole('link')).toHaveLength(1)
})
it('provides restrained group and dates-transition hooks on desktop', () => {
renderParticipation()
expect(screen.getByTestId('eligibility-header').style.background).toBe('rgb(255, 255, 255)')
expect(screen.getByTestId('eligibility-criteria-group').style.background).toBe(
'rgb(244, 246, 248)',
)
expect(screen.getByTestId('eligibility-summary').style.background).toBe('rgb(17, 29, 85)')
expect(screen.getByTestId('important-dates-transition').style.borderTopWidth).toBe('8px')
expect(screen.getByRole('heading', { name: 'CMS important dates', level: 3 })).toBeTruthy()
expect(screen.getByText('Desktop date')).toBeTruthy()
expect(screen.queryByText('Mobile date')).toBeNull()
})
it('keeps criteria, notes, and conclusion in a single readable mobile order', () => {
viewport.mobile = true
renderParticipation()
const section = screen.getByTestId('eligibility-section')
const criteriaGroup = within(section).getByTestId('eligibility-criteria-group')
const notesGroup = within(section).getByTestId('eligibility-notes-group')
const summary = within(section).getByTestId('eligibility-summary')
const rows = within(criteriaGroup).getAllByTestId('eligibility-criterion')
expect(rows.map((row) => row.getAttribute('data-order'))).toEqual(['1', '2'])
expect(rows.every((row) => row.style.gridTemplateColumns === '40px 1fr')).toBe(true)
expect(
criteriaGroup.compareDocumentPosition(notesGroup) & Node.DOCUMENT_POSITION_FOLLOWING,
).toBeTruthy()
expect(
notesGroup.compareDocumentPosition(summary) & Node.DOCUMENT_POSITION_FOLLOWING,
).toBeTruthy()
expect(screen.getByTestId('important-dates-transition').style.borderTopWidth).toBe('5px')
expect(screen.getByTestId('important-date-item').getAttribute('data-order')).toBe('1')
expect(screen.getByTestId('important-date-item').style.gridTemplateColumns).toBe(
'minmax(112px, 0.9fr) minmax(0, 1.1fr)',
)
expect(screen.getByText('Mobile date')).toBeTruthy()
expect(screen.queryByText('Desktop date')).toBeNull()
})
})