UNPKG

@oslokommune/punkt-elements

Version:

Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo

244 lines (199 loc) 9.89 kB
import '@testing-library/jest-dom' import { axe, toHaveNoViolations } from 'jest-axe' import { fireEvent } from '@testing-library/dom' expect.extend(toHaveNoViolations) import './combobox' import { PktCombobox } from './combobox' import type { IPktComboboxOption } from './combobox' const waitForCustomElements = async () => { await customElements.whenDefined('pkt-combobox') } const createCombobox = async (comboboxProps = '') => { const container = document.createElement('div') container.innerHTML = ` <pkt-combobox ${comboboxProps}></pkt-combobox> ` document.body.appendChild(container) await waitForCustomElements() return container } const defaultOptions: IPktComboboxOption[] = [ { value: 'apple', label: 'Apple' }, { value: 'banana', label: 'Banana' }, { value: 'cherry', label: 'Cherry' }, ] afterEach(() => { document.body.innerHTML = '' }) describe('PktCombobox', () => { describe('Accessibility (axe)', () => { test('basic combobox has no accessibility violations', async () => { const container = await createCombobox('id="accessible" name="test" label="Choose a fruit"') const combobox = container.querySelector('pkt-combobox') as PktCombobox await combobox.updateComplete const results = await axe(combobox) expect(results).toHaveNoViolations() }) test('combobox with options has no accessibility violations', async () => { const container = await createCombobox('id="accessible" name="test" label="Choose a fruit"') const combobox = container.querySelector('pkt-combobox') as PktCombobox combobox.options = [...defaultOptions] await combobox.updateComplete const results = await axe(combobox) expect(results).toHaveNoViolations() }) test('combobox with text input has no accessibility violations', async () => { const container = await createCombobox( 'id="accessible" name="test" label="Choose a fruit" allow-user-input', ) const combobox = container.querySelector('pkt-combobox') as PktCombobox combobox.options = [...defaultOptions] await combobox.updateComplete const results = await axe(combobox) expect(results).toHaveNoViolations() }) test('combobox with typeahead has no accessibility violations', async () => { const container = await createCombobox( 'id="accessible" name="test" label="Choose a fruit" typeahead', ) const combobox = container.querySelector('pkt-combobox') as PktCombobox combobox.options = [...defaultOptions] await combobox.updateComplete const results = await axe(combobox) expect(results).toHaveNoViolations() }) test('multiple combobox has no accessibility violations', async () => { const container = await createCombobox( 'id="accessible" name="test" label="Choose fruits" multiple', ) const combobox = container.querySelector('pkt-combobox') as PktCombobox combobox.options = [...defaultOptions] await combobox.updateComplete const results = await axe(combobox) expect(results).toHaveNoViolations() }) test('disabled combobox has no accessibility violations', async () => { const container = await createCombobox( 'id="accessible" name="test" label="Choose a fruit" disabled', ) const combobox = container.querySelector('pkt-combobox') as PktCombobox await combobox.updateComplete const results = await axe(combobox) expect(results).toHaveNoViolations() }) test('combobox with error state has no accessibility violations', async () => { const container = await createCombobox( 'id="accessible" name="test" label="Choose a fruit" error-message="Required field"', ) const combobox = container.querySelector('pkt-combobox') as PktCombobox combobox.hasError = true await combobox.updateComplete const results = await axe(combobox) expect(results).toHaveNoViolations() }) }) describe('ARIA attributes', () => { test('select-only combobox has correct ARIA attributes', async () => { const container = await createCombobox('id="test-aria" name="test" label="Test Label"') const combobox = container.querySelector('pkt-combobox') as PktCombobox await combobox.updateComplete const comboboxInput = combobox.querySelector('.pkt-combobox__input') expect(comboboxInput?.getAttribute('role')).toBe('combobox') expect(comboboxInput?.getAttribute('aria-controls')).toBe('test-aria-listbox') expect(comboboxInput?.getAttribute('aria-haspopup')).toBe('listbox') expect(comboboxInput?.getAttribute('aria-expanded')).toBe('false') expect(comboboxInput?.getAttribute('aria-labelledby')).toBe('test-aria-combobox-label') }) test('arrow button aria-expanded updates when dropdown opens', async () => { const container = await createCombobox('id="test" name="test" label="Test"') const combobox = container.querySelector('pkt-combobox') as PktCombobox await combobox.updateComplete const arrowButton = combobox.querySelector('.pkt-combobox__input') expect(arrowButton?.getAttribute('aria-expanded')).toBe('false') fireEvent.click(arrowButton!) await combobox.updateComplete expect(arrowButton?.getAttribute('aria-expanded')).toBe('true') }) test('text input has correct ARIA attributes for allowUserInput', async () => { const container = await createCombobox( 'id="test-input" name="test" label="Test Label" allow-user-input', ) const combobox = container.querySelector('pkt-combobox') as PktCombobox await combobox.updateComplete const textInput = combobox.querySelector('input[type="text"]') expect(textInput?.getAttribute('role')).toBe('combobox') expect(textInput?.getAttribute('aria-controls')).toBe('test-input-listbox') expect(textInput?.getAttribute('aria-label')).toBe('Test Label') expect(textInput?.getAttribute('aria-autocomplete')).toBe('list') }) test('text input has correct ARIA attributes for typeahead', async () => { const container = await createCombobox( 'id="test-input" name="test" label="Test Label" typeahead', ) const combobox = container.querySelector('pkt-combobox') as PktCombobox await combobox.updateComplete const textInput = combobox.querySelector('input[type="text"]') expect(textInput?.getAttribute('aria-autocomplete')).toBe('both') }) test('text input sets aria-activedescendant when value is selected', async () => { const container = await createCombobox( 'id="test" name="test" label="Test" allow-user-input value="apple"', ) const combobox = container.querySelector('pkt-combobox') as PktCombobox combobox.options = [...defaultOptions] await combobox.updateComplete const textInput = combobox.querySelector('input[type="text"]') expect(textInput?.getAttribute('aria-activedescendant')).toBeTruthy() }) test('listbox has correct id for aria-controls reference', async () => { const container = await createCombobox('id="test-combo" name="test" label="Test"') const combobox = container.querySelector('pkt-combobox') as PktCombobox await combobox.updateComplete const listbox = combobox.querySelector('pkt-listbox') expect(listbox?.getAttribute('id')).toBe('test-combo-listbox') }) }) describe('Keyboard accessibility', () => { test('arrow button is focusable when not disabled', async () => { const container = await createCombobox('id="test" name="test" label="Test"') const combobox = container.querySelector('pkt-combobox') as PktCombobox await combobox.updateComplete const arrowButton = combobox.querySelector('.pkt-combobox__input') as HTMLElement expect(arrowButton.getAttribute('tabindex')).toBe('0') }) test('arrow button is not focusable when disabled', async () => { const container = await createCombobox('id="test" name="test" label="Test" disabled') const combobox = container.querySelector('pkt-combobox') as PktCombobox await combobox.updateComplete const arrowButton = combobox.querySelector('.pkt-combobox__input') as HTMLElement expect(arrowButton.getAttribute('tabindex')).toBe('-1') }) test('text input is part of tab order', async () => { const container = await createCombobox('id="test" name="test" label="Test" allow-user-input') const combobox = container.querySelector('pkt-combobox') as PktCombobox await combobox.updateComplete const textInput = combobox.querySelector('input[type="text"]') as HTMLElement expect(textInput).toBeInTheDocument() // Text inputs are naturally tabbable (no tabindex needed) expect(textInput.hasAttribute('tabindex')).toBe(false) }) }) describe('Label association', () => { test('input wrapper has correct forId for text input', async () => { const container = await createCombobox( 'id="test-id" name="test" label="Test Label" allow-user-input', ) const combobox = container.querySelector('pkt-combobox') as PktCombobox await combobox.updateComplete const wrapper = combobox.querySelector('pkt-input-wrapper') as any expect(wrapper?.forId).toBe('test-id-input') }) test('input wrapper has correct forId for combobox (no text input)', async () => { const container = await createCombobox('id="test-id" name="test" label="Test Label"') const combobox = container.querySelector('pkt-combobox') as PktCombobox await combobox.updateComplete const wrapper = combobox.querySelector('pkt-input-wrapper') as any expect(wrapper?.forId).toBe('test-id-combobox') }) }) })