@oslokommune/punkt-elements
Version:
Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo
335 lines (259 loc) • 10.7 kB
text/typescript
import '@testing-library/jest-dom'
import { axe, toHaveNoViolations } from 'jest-axe'
import { fireEvent } from '@testing-library/dom'
import { vi } from 'vitest'
import { createElementTest, BaseTestConfig } from '../../tests/test-framework'
import { CustomElementFor } from '../../tests/component-registry'
import './alert'
expect.extend(toHaveNoViolations)
export interface AlertTestConfig extends BaseTestConfig {
skin?: string
compact?: boolean
title?: string
date?: string
closeAlert?: boolean
ariaLive?: string
}
// Use shared framework
export const createAlertTest = async (config: AlertTestConfig = {}) => {
const { container, element } = await createElementTest<
CustomElementFor<'pkt-alert'>,
AlertTestConfig
>('pkt-alert', config)
return {
container,
alert: element,
}
}
// Cleanup after each test
afterEach(() => {
document.body.innerHTML = ''
})
describe('PktAlert', () => {
describe('Rendering and basic functionality', () => {
test('renders without errors', async () => {
const { alert } = await createAlertTest({
content: 'Test alert content',
})
expect(alert).toBeInTheDocument()
await alert.updateComplete
expect(alert).toBeTruthy()
const alertDiv = alert.querySelector('.pkt-alert')
expect(alertDiv).toBeInTheDocument()
})
test('renders with correct structure', async () => {
const { alert } = await createAlertTest({
title: 'Test Title',
content: 'Test alert message',
})
await alert.updateComplete
const alertDiv = alert.querySelector('.pkt-alert')
const grid = alertDiv?.querySelector('.pkt-alert__grid')
const icon = alert.querySelector('.pkt-alert__icon')
const title = alert.querySelector('.pkt-alert__title')
const text = alert.querySelector('.pkt-alert__text')
expect(alertDiv).toHaveClass('pkt-alert')
expect(grid).toHaveClass('pkt-alert__grid')
expect(icon).toBeInTheDocument()
expect(title).toBeInTheDocument()
expect(text).toBeInTheDocument()
expect(title?.textContent).toContain('Test Title')
})
test('renders icon correctly based on skin', async () => {
const skins = ['error', 'success', 'warning', 'info'] as const
for (const skin of skins) {
const { alert } = await createAlertTest({ skin })
await alert.updateComplete
const icon = alert.querySelector('pkt-icon')
const expectedIconName = skin === 'info' ? 'alert-information' : `alert-${skin}`
expect(icon?.getAttribute('name')).toBe(expectedIconName)
expect(icon?.getAttribute('aria-hidden')).toBe('true')
}
})
})
describe('Properties and attributes', () => {
test('applies default properties correctly', async () => {
const { alert } = await createAlertTest()
await alert.updateComplete
expect(alert.skin).toBe('info')
expect(alert.compact).toBe(false)
expect(alert.closeAlert).toBe(false)
expect(alert.title).toBe('')
expect(alert.date).toBe(null)
expect(alert.role).toBe('status')
const alertDiv = alert.querySelector('.pkt-alert')
expect(alertDiv).toHaveClass('pkt-alert')
expect(alertDiv).toHaveClass('pkt-alert--info')
expect(alertDiv).not.toHaveClass('pkt-alert--compact')
})
test('applies different skin properties correctly', async () => {
const skins = ['error', 'success', 'warning', 'info'] as const
for (const skin of skins) {
const { alert } = await createAlertTest({ skin })
await alert.updateComplete
expect(alert.skin).toBe(skin)
expect(alert.getAttribute('skin')).toBe(skin)
const alertDiv = alert.querySelector('.pkt-alert')
expect(alertDiv).toHaveClass(`pkt-alert--${skin}`)
}
})
test('applies compact property correctly', async () => {
const { alert } = await createAlertTest({ compact: true })
await alert.updateComplete
expect(alert.compact).toBe(true)
const alertDiv = alert.querySelector('.pkt-alert')
expect(alertDiv).toHaveClass('pkt-alert--compact')
})
test('handles title property', async () => {
const { alert } = await createAlertTest({ title: 'Alert Title' })
await alert.updateComplete
expect(alert.title).toBe('Alert Title')
expect(alert.getAttribute('title')).toBe('Alert Title')
const title = alert.querySelector('.pkt-alert__title')
expect(title).toBeInTheDocument()
expect(title?.textContent).toBe('Alert Title')
// Test title updates
alert.title = 'Updated Title'
await alert.updateComplete
expect(title?.textContent).toBe('Updated Title')
})
test('handles date property', async () => {
const { alert } = await createAlertTest({ date: '2024-01-15' })
await alert.updateComplete
expect(alert.date).toBe('2024-01-15')
expect(alert.getAttribute('date')).toBe('2024-01-15')
const dateElement = alert.querySelector('.pkt-alert__date')
expect(dateElement).toBeInTheDocument()
expect(dateElement?.textContent).toContain('2024-01-15')
})
test('handles aria-live property', async () => {
const { alert } = await createAlertTest({ ariaLive: 'assertive' })
await alert.updateComplete
// The component uses the ariaLive property value that was set
expect(alert.ariaLiveAttr).toBe('assertive')
const alertDiv = alert.querySelector('.pkt-alert')
expect(alertDiv?.getAttribute('aria-live')).toBe('assertive')
})
})
describe('Close functionality', () => {
test('renders close button when closeAlert is true', async () => {
const { alert } = await createAlertTest({ closeAlert: true })
await alert.updateComplete
expect(alert.closeAlert).toBe(true)
const closeDiv = alert.querySelector('.pkt-alert__close')
const closeButton = alert.querySelector('pkt-button')
expect(closeDiv).toBeInTheDocument()
expect(closeButton).toBeInTheDocument()
expect(closeButton?.getAttribute('aria-label')).toBe('close')
expect(closeButton?.getAttribute('skin')).toBe('tertiary')
expect(closeButton?.getAttribute('variant')).toBe('icon-only')
})
test('does not render close button when closeAlert is false', async () => {
const { alert } = await createAlertTest({ closeAlert: false })
await alert.updateComplete
expect(alert.closeAlert).toBe(false)
const closeDiv = alert.querySelector('.pkt-alert__close')
const closeButton = alert.querySelector('pkt-button')
expect(closeDiv).not.toBeInTheDocument()
expect(closeButton).not.toBeInTheDocument()
})
test('closes alert when close button is clicked', async () => {
const { alert } = await createAlertTest({ closeAlert: true })
await alert.updateComplete
// Listen for close events
const closeSpy = vi.fn()
const onCloseSpy = vi.fn()
alert.addEventListener('close', closeSpy)
alert.addEventListener('on-close', onCloseSpy)
const closeButton = alert.querySelector('pkt-button')
expect(closeButton).toBeInTheDocument()
// Click the close button
fireEvent.click(closeButton!)
await alert.updateComplete
// Alert should be hidden
const alertDiv = alert.querySelector('.pkt-alert')
expect(alertDiv).toHaveClass('pkt-hide')
expect(alert).toHaveClass('pkt-hide')
// Events should be dispatched
expect(closeSpy).toHaveBeenCalledWith(
expect.objectContaining({
type: 'close',
detail: expect.objectContaining({
origin: expect.any(Object),
}),
}),
)
expect(onCloseSpy).toHaveBeenCalledWith(
expect.objectContaining({
type: 'on-close',
detail: expect.objectContaining({
origin: expect.any(Object),
}),
}),
)
})
})
describe('Grid layout classes', () => {
test('applies correct grid classes based on content', async () => {
// Test with no title and no date
const { alert: alert1 } = await createAlertTest()
await alert1.updateComplete
const grid1 = alert1.querySelector('.pkt-alert__grid')
expect(grid1).toHaveClass('pkt-alert__noTitle')
expect(grid1).toHaveClass('pkt-alert__noDate')
// Test with title but no date
const { alert: alert2 } = await createAlertTest({ title: 'Test Title' })
await alert2.updateComplete
const grid2 = alert2.querySelector('.pkt-alert__grid')
expect(grid2).not.toHaveClass('pkt-alert__noTitle')
expect(grid2).toHaveClass('pkt-alert__noDate')
// Test with both title and date
const { alert: alert3 } = await createAlertTest({ title: 'Test Title', date: '2024-01-15' })
await alert3.updateComplete
const grid3 = alert3.querySelector('.pkt-alert__grid')
expect(grid3).not.toHaveClass('pkt-alert__noTitle')
expect(grid3).not.toHaveClass('pkt-alert__noDate')
})
})
describe('Accessibility', () => {
test('has correct ARIA attributes', async () => {
const { alert } = await createAlertTest({ ariaLive: 'polite' })
await alert.updateComplete
const alertDiv = alert.querySelector('.pkt-alert')
const icon = alert.querySelector('pkt-icon')
expect(alertDiv?.getAttribute('aria-live')).toBe('polite')
expect(alert.role).toBe('status')
expect(icon?.getAttribute('aria-hidden')).toBe('true')
})
test('renders with no WCAG errors with axe - simple alert', async () => {
const { container } = await createAlertTest({
title: 'Test Alert',
content: 'This is a test alert message.',
})
const results = await axe(container)
expect(results).toHaveNoViolations()
})
test('renders with no WCAG errors with axe - complex alert', async () => {
const { container } = await createAlertTest({
skin: 'error',
title: 'Error Alert',
date: '2024-01-15',
closeAlert: true,
ariaLive: 'assertive',
content: '<p>This is an error message with <a href="#">a link</a>.</p>',
})
const results = await axe(container)
expect(results).toHaveNoViolations()
})
test('renders with no WCAG errors with axe - compact alert', async () => {
const { container } = await createAlertTest({
skin: 'success',
compact: true,
closeAlert: true,
content: 'Success message',
})
const results = await axe(container)
expect(results).toHaveNoViolations()
})
})
})