158 lines
5.9 KiB
TypeScript
158 lines
5.9 KiB
TypeScript
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()
|
|
})
|
|
})
|