@oslokommune/punkt-elements
Version:
Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo
274 lines (218 loc) • 8.19 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 './radiobutton'
export interface RadioButtonTestConfig extends BaseTestConfig {
value?: string
checkHelptext?: string | null
defaultChecked?: boolean
hasTile?: boolean
checked?: boolean | string | null
type?: string
tagText?: string | null
optionalTag?: boolean
optionalText?: string
requiredTag?: boolean
requiredText?: string
id?: string
name?: string
label?: string
disabled?: boolean
readonly?: boolean
required?: boolean
hasError?: boolean
inline?: boolean
ariaDescribedBy?: string | null
ariaLabelledby?: string | null
}
export const createRadioButtonTest = async (config: RadioButtonTestConfig = {}) => {
const { container, element } = await createElementTest<
CustomElementFor<'pkt-radiobutton'>,
RadioButtonTestConfig
>('pkt-radiobutton', config)
if (config.label) {
element.label = config.label
await element.updateComplete
}
return {
container,
radiobutton: element,
}
}
expect.extend(toHaveNoViolations)
describe('pkt-radiobutton', () => {
afterEach(() => {
document.body.innerHTML = ''
})
test('renders with basic properties', async () => {
const { radiobutton } = await createRadioButtonTest({
id: 'test',
name: 'test',
value: 'test',
})
expect(radiobutton).toBeInTheDocument()
expect(radiobutton.tagName).toBe('PKT-RADIOBUTTON')
})
test('renders input element with correct attributes', async () => {
const { radiobutton } = await createRadioButtonTest({
id: 'test',
name: 'test',
value: 'test',
})
const input = radiobutton.querySelector('input[type="radio"]') as HTMLInputElement
expect(input).toBeInTheDocument()
expect(input.type).toBe('radio')
expect(input.id).toBe('test-internal')
expect(input.name).toBe('test-internal')
})
test('displays label correctly', async () => {
const { radiobutton } = await createRadioButtonTest({
id: 'test',
label: 'Test Radio',
})
const label = radiobutton.querySelector('label')
expect(label).toBeInTheDocument()
expect(label?.textContent?.trim()).toContain('Test Radio')
expect(label?.getAttribute('for')).toBe('test-internal')
})
test('handles checked state correctly', async () => {
const { radiobutton } = await createRadioButtonTest()
expect(radiobutton.checked).toBe(null)
radiobutton.checked = true
await radiobutton.updateComplete
const input = radiobutton.querySelector('input') as HTMLInputElement
expect(input.checked).toBe(true)
expect(radiobutton.checked).toBe(true)
})
test('updates checked state on user interaction', async () => {
const { radiobutton } = await createRadioButtonTest({
label: 'Test Option',
})
const input = radiobutton.querySelector('input') as HTMLInputElement
expect(input.checked).toBe(false)
expect(radiobutton.touched).toBe(false)
fireEvent.click(input)
await radiobutton.updateComplete
expect(input.checked).toBe(true)
expect(radiobutton.touched).toBe(true)
})
test('handles disabled state correctly', async () => {
const { radiobutton } = await createRadioButtonTest({
disabled: true,
})
const input = radiobutton.querySelector('input') as HTMLInputElement
expect(input.disabled).toBe(true)
const label = radiobutton.querySelector('.pkt-input-check__input-label')
expect(label).toHaveClass('pkt-input-check__input-label--disabled')
})
test('renders tile variant correctly', async () => {
const { radiobutton } = await createRadioButtonTest({
hasTile: true,
})
const container = radiobutton.querySelector('.pkt-input-check__input')
expect(container).toHaveClass('pkt-input-check__input--tile')
})
test('renders disabled tile variant correctly', async () => {
const { radiobutton } = await createRadioButtonTest({
hasTile: true,
disabled: true,
})
const container = radiobutton.querySelector('.pkt-input-check__input')
expect(container).toHaveClass('pkt-input-check__input--tile-disabled')
})
test('renders helptext when provided', async () => {
const { radiobutton } = await createRadioButtonTest({
checkHelptext: 'This is helpful information',
})
const helptext = radiobutton.querySelector('.pkt-input-check__input-helptext')
expect(helptext).toBeInTheDocument()
expect(helptext?.textContent).toBe('This is helpful information')
})
test('handles error state correctly', async () => {
const { radiobutton } = await createRadioButtonTest({
hasError: true,
})
const input = radiobutton.querySelector('input')
expect(input).toHaveClass('pkt-input-check__input-checkbox--error')
})
test('renders tag text when provided', async () => {
const { radiobutton } = await createRadioButtonTest({
tagText: 'Tag',
})
const tag = radiobutton.querySelector('.pkt-tag--gray')
expect(tag).toBeInTheDocument()
expect(tag?.textContent).toBe('Tag')
})
test('renders optional tag when enabled', async () => {
const { radiobutton } = await createRadioButtonTest({
optionalTag: true,
})
const tag = radiobutton.querySelector('.pkt-tag--blue-light')
expect(tag).toBeInTheDocument()
expect(tag?.textContent).toBe('Valgfritt')
})
test('renders required tag when enabled', async () => {
const { radiobutton } = await createRadioButtonTest({
requiredTag: true,
})
const tag = radiobutton.querySelector('.pkt-tag--beige')
expect(tag).toBeInTheDocument()
expect(tag?.textContent).toBe('Må fylles ut')
})
test('manages focus events correctly', async () => {
const { radiobutton } = await createRadioButtonTest()
const focusSpy = vi.fn()
const blurSpy = vi.fn()
radiobutton.addEventListener('focus', focusSpy)
radiobutton.addEventListener('blur', blurSpy)
const input = radiobutton.querySelector('input') as HTMLInputElement
fireEvent.focus(input)
expect(focusSpy).toHaveBeenCalled()
fireEvent.blur(input)
expect(blurSpy).toHaveBeenCalled()
})
test('works with radio button groups', async () => {
const container = document.createElement('form')
document.body.appendChild(container)
const radio1Container = document.createElement('div')
radio1Container.innerHTML = '<pkt-radiobutton name="group" value="1"></pkt-radiobutton>'
container.appendChild(radio1Container)
await customElements.whenDefined('pkt-radiobutton')
const radio1 = radio1Container.querySelector(
'pkt-radiobutton',
) as CustomElementFor<'pkt-radiobutton'>
radio1.label = 'Option 1'
await radio1.updateComplete
const radio2Container = document.createElement('div')
radio2Container.innerHTML = '<pkt-radiobutton name="group" value="2"></pkt-radiobutton>'
container.appendChild(radio2Container)
const radio2 = radio2Container.querySelector(
'pkt-radiobutton',
) as CustomElementFor<'pkt-radiobutton'>
radio2.label = 'Option 2'
await radio2.updateComplete
const input1 = radio1.querySelector('input') as HTMLInputElement
const input2 = radio2.querySelector('input') as HTMLInputElement
expect(input1.checked).toBe(false)
expect(input2.checked).toBe(false)
fireEvent.click(input1)
await radio1.updateComplete
expect(input1.checked).toBe(true)
expect(radio1.touched).toBe(true)
fireEvent.click(input2)
await radio2.updateComplete
expect(input2.checked).toBe(true)
expect(radio2.touched).toBe(true)
document.body.removeChild(container)
})
test('meets accessibility standards', async () => {
const { radiobutton } = await createRadioButtonTest({
label: 'Accessible Radio Button',
})
const results = await axe(radiobutton)
expect(results).toHaveNoViolations()
})
})