@oslokommune/punkt-elements
Version:
Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo
684 lines (558 loc) • 21.1 kB
text/typescript
import '@testing-library/jest-dom'
import { axe, toHaveNoViolations } from 'jest-axe'
import { fireEvent } from '@testing-library/dom'
import { createElementTest, BaseTestConfig } from '../../tests/test-framework'
import { CustomElementFor } from '../../tests/component-registry'
import './checkbox'
expect.extend(toHaveNoViolations)
export interface CheckboxTestConfig extends BaseTestConfig {
id?: string
name?: string
label?: string
checked?: boolean
disabled?: boolean
required?: boolean
value?: string
checkHelptext?: string
hasTile?: boolean
hasError?: boolean
errorMessage?: string
optionalTag?: boolean
requiredTag?: boolean
tagText?: string
labelPosition?: string
hideLabel?: boolean
isSwitch?: boolean
}
// Use shared framework
export const createCheckboxTest = async (config: CheckboxTestConfig = {}) => {
const { container, element } = await createElementTest<
CustomElementFor<'pkt-checkbox'>,
CheckboxTestConfig
>('pkt-checkbox', config)
return {
container,
checkbox: element,
}
}
describe('PktCheckbox', () => {
describe('Rendering and basic functionality', () => {
test('renders without errors', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test-checkbox',
name: 'test',
label: 'Test Checkbox',
})
expect(checkbox).toBeInTheDocument()
await checkbox.updateComplete
expect(checkbox).toBeTruthy()
const inputElement = checkbox.querySelector('input[type="checkbox"]')
expect(inputElement).toBeInTheDocument()
})
test('renders proper structure', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
label: 'Test Label',
})
await checkbox.updateComplete
expect(checkbox).toBeInTheDocument()
// Check basic structure exists
const input = checkbox.querySelector('input[type="checkbox"]')
const label = checkbox.querySelector('label')
expect(input).toBeInTheDocument()
expect(label).toBeInTheDocument()
expect(label).toHaveTextContent('Test Label')
})
test('applies ids and names correctly', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test-id',
name: 'test-name',
value: 'test-value',
})
await checkbox.updateComplete
expect(checkbox.id).toBe('test-id')
expect(checkbox.name).toBe('test-name')
expect(checkbox.value).toBe('test-value')
})
test('renders input with correct attributes', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test-id',
name: 'test-name',
value: 'test-value',
})
await checkbox.updateComplete
const input = checkbox.querySelector('input[type="checkbox"]')
expect(input?.getAttribute('id')).toBe('test-id-internal')
expect(input?.getAttribute('name')).toBe('test-name-internal')
expect(input?.getAttribute('type')).toBe('checkbox')
})
})
describe('Properties and attributes', () => {
test('applies default properties correctly', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
})
await checkbox.updateComplete
expect(checkbox.value).toBe('')
expect(checkbox.checked).toBe(false)
expect(checkbox.defaultChecked).toBe(false)
expect(checkbox.hasTile).toBe(false)
expect(checkbox.isSwitch).toBe(false)
expect(checkbox.labelPosition).toBe('right')
expect(checkbox.hideLabel).toBe(false)
expect(checkbox.disabled).toBe(false)
expect(checkbox.optionalTag).toBe(false)
expect(checkbox.requiredTag).toBe(false)
const input = checkbox.querySelector('input[type="checkbox"]')
expect(input?.getAttribute('role')).toBe('checkbox')
})
test('handles value property correctly', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
value: 'test-value',
})
expect(checkbox.value).toBe('test-value')
expect(checkbox.getAttribute('value')).toBe('test-value')
// Test value updates
checkbox.value = 'updated-value'
await checkbox.updateComplete
expect(checkbox.value).toBe('updated-value')
})
test('handles checked state correctly', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
checked: true,
})
await checkbox.updateComplete
expect(checkbox.checked).toBe(true)
const input = checkbox.querySelector('input[type="checkbox"]') as HTMLInputElement
expect(input?.checked).toBe(true)
})
test('handles checked property changes', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
})
await checkbox.updateComplete
expect(checkbox.checked).toBe(false)
checkbox.checked = true
await checkbox.updateComplete
expect(checkbox.checked).toBe(true)
const input = checkbox.querySelector('input[type="checkbox"]') as HTMLInputElement
expect(input?.checked).toBe(true)
})
test('handles disabled state correctly', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
disabled: true,
})
await checkbox.updateComplete
expect(checkbox.disabled).toBe(true)
const input = checkbox.querySelector('input[type="checkbox"]') as HTMLInputElement
expect(input.disabled).toBe(true)
})
test('handles labelPosition property correctly', async () => {
// Test left position
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
label: 'Test',
})
checkbox.labelPosition = 'left'
await checkbox.updateComplete
expect(checkbox.labelPosition).toBe('left')
let label = checkbox.querySelector('.pkt-input-check__input-label')
expect(label).toHaveClass('pkt-input-check__input-label--left')
// Test right position (default)
checkbox.labelPosition = 'right'
await checkbox.updateComplete
// Re-query the label element after DOM change
label = checkbox.querySelector('.pkt-input-check__input-label')
expect(label).toHaveClass('pkt-input-check__input-label--right')
expect(label).not.toHaveClass('pkt-input-check__input-label--left')
})
test('handles hideLabel property correctly', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
label: 'Hidden Label',
})
checkbox.hideLabel = true
await checkbox.updateComplete
expect(checkbox.hideLabel).toBe(true)
const label = checkbox.querySelector('.pkt-input-check__input-label')
expect(label).toHaveClass('pkt-sr-only')
})
test('handles isSwitch property correctly', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
isSwitch: true,
})
expect(checkbox.isSwitch).toBe(true)
const input = checkbox.querySelector('input[type="checkbox"]')
expect(input?.getAttribute('role')).toBe('switch')
})
test('handles indeterminate property correctly', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
label: 'Test',
})
await checkbox.updateComplete
const input = checkbox.querySelector('input[type="checkbox"]') as HTMLInputElement
expect(input.indeterminate).toBe(false)
// Set indeterminate to true
checkbox.indeterminate = true
await checkbox.updateComplete
expect(checkbox.indeterminate).toBe(true)
expect(input.indeterminate).toBe(true)
// Set indeterminate back to false
checkbox.indeterminate = false
await checkbox.updateComplete
expect(checkbox.indeterminate).toBe(false)
expect(input.indeterminate).toBe(false)
})
test('handles indeterminate attribute changes', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
label: 'Test',
})
await checkbox.updateComplete
const input = checkbox.querySelector('input[type="checkbox"]') as HTMLInputElement
expect(input.indeterminate).toBe(false)
// Set via attribute
checkbox.setAttribute('indeterminate', '')
await checkbox.updateComplete
expect(checkbox.indeterminate).toBe(true)
expect(input.indeterminate).toBe(true)
// Remove attribute
checkbox.removeAttribute('indeterminate')
await checkbox.updateComplete
expect(checkbox.indeterminate).toBe(false)
expect(input.indeterminate).toBe(false)
})
test('clicking checkbox clears indeterminate state', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
label: 'Test',
})
checkbox.indeterminate = true
await checkbox.updateComplete
const input = checkbox.querySelector('input[type="checkbox"]') as HTMLInputElement
expect(input.indeterminate).toBe(true)
// Click the checkbox
fireEvent.click(input)
await checkbox.updateComplete
// Indeterminate should be cleared by the browser when clicked
expect(input.indeterminate).toBe(false)
expect(input.checked).toBe(true)
})
test('handles hasTile property correctly', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
hasTile: true,
})
expect(checkbox.hasTile).toBe(true)
const inputDiv = checkbox.querySelector('.pkt-input-check__input')
expect(inputDiv).toHaveClass('pkt-input-check__input--tile')
// Test disabled with tile
checkbox.disabled = true
await checkbox.updateComplete
expect(inputDiv).toHaveClass('pkt-input-check__input--tile-disabled')
})
})
describe('Label and helptext functionality', () => {
test('renders label correctly', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
label: 'Checkbox Label',
})
await checkbox.updateComplete
expect(checkbox.label).toBe('Checkbox Label')
const label = checkbox.querySelector('.pkt-input-check__input-label')
expect(label?.textContent?.trim()).toBe('Checkbox Label')
expect(label?.getAttribute('for')).toBe('test-internal')
})
test('renders checkHelptext when provided', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
label: 'Test',
checkHelptext: 'This is help text',
})
await checkbox.updateComplete
expect(checkbox.checkHelptext).toBe('This is help text')
const helptext = checkbox.querySelector('.pkt-input-check__input-helptext')
expect(helptext).toBeInTheDocument()
expect(helptext?.textContent).toBe('This is help text')
})
test('does not render helptext when not provided', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
label: 'Test',
})
await checkbox.updateComplete
const helptext = checkbox.querySelector('.pkt-input-check__input-helptext')
expect(helptext).not.toBeInTheDocument()
})
})
describe('Tag functionality', () => {
test('renders custom tagText when provided', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
label: 'Test',
tagText: 'Custom Tag',
})
await checkbox.updateComplete
expect(checkbox.tagText).toBe('Custom Tag')
const tagElement = checkbox.querySelector('.pkt-tag--gray')
expect(tagElement).toBeInTheDocument()
expect(tagElement?.textContent).toBe('Custom Tag')
})
test('renders optional tag when optionalTag is true', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
label: 'Test',
optionalTag: true,
})
await checkbox.updateComplete
expect(checkbox.optionalTag).toBe(true)
expect(checkbox.optionalText).toBe('Valgfritt')
const optionalTag = checkbox.querySelector('.pkt-tag--blue-light')
expect(optionalTag).toBeInTheDocument()
expect(optionalTag?.textContent).toBe('Valgfritt')
})
test('renders required tag when requiredTag is true', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
label: 'Test',
requiredTag: true,
})
await checkbox.updateComplete
expect(checkbox.requiredTag).toBe(true)
expect(checkbox.requiredText).toBe('Må fylles ut')
const requiredTag = checkbox.querySelector('.pkt-tag--beige')
expect(requiredTag).toBeInTheDocument()
expect(requiredTag?.textContent).toBe('Må fylles ut')
})
test('renders custom optional and required text', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
label: 'Test',
})
checkbox.optionalTag = true
checkbox.optionalText = 'Custom Optional'
checkbox.requiredTag = true
checkbox.requiredText = 'Custom Required'
await checkbox.updateComplete
expect(checkbox.optionalText).toBe('Custom Optional')
expect(checkbox.requiredText).toBe('Custom Required')
const optionalTag = checkbox.querySelector('.pkt-tag--blue-light')
const requiredTag = checkbox.querySelector('.pkt-tag--beige')
expect(optionalTag?.textContent).toBe('Custom Optional')
expect(requiredTag?.textContent).toBe('Custom Required')
})
test('renders multiple tags when multiple are enabled', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
label: 'Test',
})
checkbox.tagText = 'Custom'
checkbox.optionalTag = true
checkbox.requiredTag = true
await checkbox.updateComplete
const customTag = checkbox.querySelector('.pkt-tag--gray')
const optionalTag = checkbox.querySelector('.pkt-tag--blue-light')
const requiredTag = checkbox.querySelector('.pkt-tag--beige')
expect(customTag).toBeInTheDocument()
expect(optionalTag).toBeInTheDocument()
expect(requiredTag).toBeInTheDocument()
})
})
describe('User interaction', () => {
test('toggles checked state when clicked', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
label: 'Test',
})
await checkbox.updateComplete
const input = checkbox.querySelector('input[type="checkbox"]') as HTMLInputElement
expect(input.checked).toBe(false)
// Click to check
fireEvent.click(input)
await checkbox.updateComplete
expect(input.checked).toBe(true)
expect(checkbox.checked).toBe(true)
// Click to uncheck
fireEvent.click(input)
await checkbox.updateComplete
expect(input.checked).toBe(false)
expect(checkbox.checked).toBe(false)
})
test('does not toggle when disabled', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
label: 'Test',
disabled: true,
})
await checkbox.updateComplete
const input = checkbox.querySelector('input[type="checkbox"]') as HTMLInputElement
expect(input.checked).toBe(false)
expect(input.disabled).toBe(true)
// Try to click
fireEvent.click(input)
await checkbox.updateComplete
// Should remain unchecked
expect(input.checked).toBe(false)
expect(checkbox.checked).toBe(false)
})
test('handles focus and blur events', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
label: 'Test',
})
await checkbox.updateComplete
const input = checkbox.querySelector('input[type="checkbox"]') as HTMLInputElement
// Test focus
fireEvent.focus(input)
await checkbox.updateComplete
// Test blur
fireEvent.blur(input)
await checkbox.updateComplete
// These should not throw errors and the element should remain functional
expect(checkbox).toBeInTheDocument()
})
test('marks as touched when interacted with', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
label: 'Test',
})
await checkbox.updateComplete
const input = checkbox.querySelector('input[type="checkbox"]') as HTMLInputElement
// Initially not touched
expect(checkbox.touched).toBe(false)
// Click to interact
fireEvent.click(input)
await checkbox.updateComplete
// Should be marked as touched
expect(checkbox.touched).toBe(true)
})
})
describe('Label position and structure', () => {
test('places label on the right by default', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
label: 'Right Label',
})
await checkbox.updateComplete
const inputDiv = checkbox.querySelector('.pkt-input-check__input')
const children = Array.from(inputDiv?.children || [])
// Should have input first, then label
expect(children[0]).toHaveAttribute('type', 'checkbox')
expect(children[1]).toHaveClass('pkt-input-check__input-label')
})
test('places label on the left when labelPosition is left', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
label: 'Left Label',
})
checkbox.labelPosition = 'left'
await checkbox.updateComplete
const inputDiv = checkbox.querySelector('.pkt-input-check__input')
const children = Array.from(inputDiv?.children || [])
// Should have label first, then input
expect(children[0]).toHaveClass('pkt-input-check__input-label')
expect(children[1]).toHaveAttribute('type', 'checkbox')
})
})
describe('Error handling', () => {
test('applies error styling when hasError is true', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
label: 'Test',
})
checkbox.hasError = true
await checkbox.updateComplete
const input = checkbox.querySelector('.pkt-input-check__input-checkbox')
expect(input).toHaveClass('pkt-input-check__input-checkbox--error')
})
test('does not apply error styling when hasError is false', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
label: 'Test',
})
await checkbox.updateComplete
const input = checkbox.querySelector('.pkt-input-check__input-checkbox')
expect(input).not.toHaveClass('pkt-input-check__input-checkbox--error')
})
})
describe('Accessibility', () => {
test('has no accessibility violations', async () => {
const { checkbox } = await createCheckboxTest({
id: 'accessible-checkbox',
name: 'test',
label: 'Accessible Checkbox',
})
await checkbox.updateComplete
const results = await axe(checkbox)
expect(results).toHaveNoViolations()
})
test('associates label with input correctly', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test-association',
name: 'test',
label: 'Associated Label',
})
await checkbox.updateComplete
const input = checkbox.querySelector('input[type="checkbox"]')
const label = checkbox.querySelector('.pkt-input-check__input-label')
expect(input?.getAttribute('id')).toBe('test-association-internal')
expect(label?.getAttribute('for')).toBe('test-association-internal')
})
test('has correct role for switch when isSwitch is true', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
label: 'Test Switch',
})
checkbox.isSwitch = true
await checkbox.updateComplete
const input = checkbox.querySelector('input[type="checkbox"]')
expect(input?.getAttribute('role')).toBe('switch')
})
test('has correct role for checkbox by default', async () => {
const { checkbox } = await createCheckboxTest({
id: 'test',
name: 'test',
label: 'Test Checkbox',
})
await checkbox.updateComplete
const input = checkbox.querySelector('input[type="checkbox"]')
expect(input?.getAttribute('role')).toBe('checkbox')
})
})
})