@oslokommune/punkt-elements
Version:
Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo
229 lines (179 loc) • 7.05 kB
text/typescript
import '@testing-library/jest-dom'
import { axe, toHaveNoViolations } from 'jest-axe'
import { vi } from 'vitest'
import { createElementTest, BaseTestConfig } from '../../tests/test-framework'
import { CustomElementFor } from '../../tests/component-registry'
import { type IPktSelect, type TSelectOption } from './select'
import './select'
export interface SelectTestConfig extends Partial<IPktSelect>, BaseTestConfig {
id?: string
label?: string
disabled?: boolean
required?: boolean
hasError?: boolean
errorMessage?: string
helptext?: string
fullwidth?: boolean
ariaLabelledby?: string | null
ariaDescribedBy?: string | null
counter?: boolean
inline?: boolean
useWrapper?: boolean | string
}
export const createSelectTest = async (config: SelectTestConfig = {}) => {
const { options, ...htmlConfig } = config
const { container, element } = await createElementTest<
CustomElementFor<'pkt-select'>,
Omit<SelectTestConfig, 'options'>
>('pkt-select', htmlConfig)
if (options) {
element.options = options
await element.updateComplete
}
return {
container,
select: element,
}
}
expect.extend(toHaveNoViolations)
describe('pkt-select', () => {
afterEach(() => {
document.body.innerHTML = ''
})
test('renders with basic properties', async () => {
const { select } = await createSelectTest({
label: 'Test Select',
id: 'test-select',
})
expect(select).toBeInTheDocument()
expect(select.getAttribute('id')).toBe('test-select')
expect(select.label).toBe('Test Select')
})
test('displays correct label in input wrapper', async () => {
const { select } = await createSelectTest({
label: 'Choose Option',
})
const inputWrapper = select.querySelector('pkt-input-wrapper')
expect(inputWrapper).toBeInTheDocument()
expect(inputWrapper?.getAttribute('label')).toBe('Choose Option')
})
test('renders select element with correct attributes', async () => {
const { select } = await createSelectTest({
id: 'my-select',
disabled: true,
value: 'option2',
})
const selectElement = select.querySelector('select')
expect(selectElement).toBeInTheDocument()
expect(selectElement?.getAttribute('id')).toBe('my-select-input')
expect(selectElement?.hasAttribute('disabled')).toBe(true)
expect(selectElement?.getAttribute('value')).toBe('option2')
})
test('handles options array property', async () => {
const options: TSelectOption[] = [
{ value: '1', label: 'Option 1' },
{ value: '2', label: 'Option 2' },
{ value: '3', label: 'Option 3', disabled: true },
]
const { select } = await createSelectTest({ options })
const optionElements = select.querySelectorAll('option')
expect(optionElements).toHaveLength(3)
expect(optionElements[0].value).toBe('1')
expect(optionElements[0].textContent?.trim()).toBe('Option 1')
expect(optionElements[2].hasAttribute('disabled')).toBe(true)
})
test('updates value on selection', async () => {
const options: TSelectOption[] = [
{ value: 'a', label: 'Option A' },
{ value: 'b', label: 'Option B' },
]
const { select } = await createSelectTest({ options })
const selectElement = select.querySelector('select') as HTMLSelectElement
expect(selectElement.options.length).toBe(2)
expect(selectElement.options[0].value).toBe('a')
expect(selectElement.options[1].value).toBe('b')
expect(select.value).toBe('a')
selectElement.selectedIndex = 1
selectElement.dispatchEvent(new Event('change', { bubbles: true }))
await select.updateComplete
expect(select.value).toBe('b')
expect(select.touched).toBe(true)
})
test('handles error state correctly', async () => {
const { select } = await createSelectTest({
hasError: true,
errorMessage: 'Selection required',
})
const inputWrapper = select.querySelector('pkt-input-wrapper')
expect(inputWrapper?.hasAttribute('hasError')).toBe(true)
expect(inputWrapper?.getAttribute('errorMessage')).toBe('Selection required')
const selectElement = select.querySelector('select')
expect(selectElement?.getAttribute('aria-invalid')).toBe('true')
})
test('renders helptext when provided', async () => {
const { select } = await createSelectTest({
helptext: 'Choose your preferred option',
})
const inputWrapper = select.querySelector('pkt-input-wrapper')
expect(inputWrapper?.getAttribute('helptext')).toBe('Choose your preferred option')
})
test('handles fullwidth styling', async () => {
const { select } = await createSelectTest({
fullwidth: true,
})
const selectElement = select.querySelector('select')
expect(selectElement?.className).toContain('pkt-input--fullwidth')
})
test('manages focus events correctly', async () => {
const { select } = await createSelectTest()
const focusSpy = vi.spyOn(select, 'onFocus')
const blurSpy = vi.spyOn(select, 'onBlur')
const selectElement = select.querySelector('select') as HTMLSelectElement
selectElement.dispatchEvent(new FocusEvent('focus'))
expect(focusSpy).toHaveBeenCalled()
selectElement.dispatchEvent(new FocusEvent('blur'))
expect(blurSpy).toHaveBeenCalled()
})
test('passes through accessibility attributes', async () => {
const { select } = await createSelectTest({
ariaLabelledby: 'external-label',
ariaDescribedBy: 'external-description',
})
const selectElement = select.querySelector('select')
expect(selectElement?.getAttribute('aria-labelledby')).toBe('external-label')
const inputWrapper = select.querySelector('pkt-input-wrapper')
expect(inputWrapper?.getAttribute('ariaDescribedBy')).toBe('external-description')
})
test('handles input wrapper properties', async () => {
const { select } = await createSelectTest({
counter: true,
inline: true,
})
const inputWrapper = select.querySelector('pkt-input-wrapper')
expect(inputWrapper?.hasAttribute('counter')).toBe(true)
expect(inputWrapper?.hasAttribute('inline')).toBe(true)
})
test('handles useWrapper false', async () => {
const { select } = await createSelectTest()
// Set useWrapper property directly on the element
select.useWrapper = false
await select.updateComplete
// When useWrapper is false, verify the behavior
expect(select.useWrapper).toBe(false)
// Confirm that there's a label with class pkt-sr-only in the markup
const srOnlyLabel = select.querySelector('label.pkt-sr-only')
expect(srOnlyLabel).toBeInTheDocument()
})
test('meets accessibility standards', async () => {
const options: TSelectOption[] = [
{ value: '1', label: 'Option 1' },
{ value: '2', label: 'Option 2' },
]
const { select } = await createSelectTest({
label: 'Accessible Select',
options,
})
const results = await axe(select)
expect(results).toHaveNoViolations()
})
})