@oslokommune/punkt-elements
Version:
Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo
506 lines (418 loc) • 16.2 kB
text/typescript
import '@testing-library/jest-dom'
import { axe, toHaveNoViolations } from 'jest-axe'
import { createElementTest, BaseTestConfig } from '../../tests/test-framework'
import { CustomElementFor } from '../../tests/component-registry'
import './input-wrapper'
expect.extend(toHaveNoViolations)
export interface InputWrapperTestConfig extends BaseTestConfig {
label?: string
helptext?: string
errorMessage?: string
hasError?: boolean
counter?: boolean
counterCurrent?: number
counterMaxLength?: number
counterError?: boolean
inline?: boolean
useWrapper?: boolean
disabled?: boolean
optionalTag?: boolean
requiredTag?: boolean
hasFieldset?: boolean
inFieldset?: boolean
readonly?: boolean
forId?: string
ariaDescribedBy?: string
helptextDropdown?: string
helptextDropdownButton?: string
optionalText?: string
requiredText?: string
tagText?: string
role?: string
}
// Use shared framework
export const createInputWrapperTest = async (config: InputWrapperTestConfig = {}) => {
const { container, element } = await createElementTest<
CustomElementFor<'pkt-input-wrapper'>,
InputWrapperTestConfig
>('pkt-input-wrapper', config)
return {
container,
wrapper: element,
}
}
describe('PktInputWrapper', () => {
describe('Rendering and basic functionality', () => {
test('renders without errors', async () => {
const { wrapper } = await createInputWrapperTest()
expect(wrapper).toBeInTheDocument()
await wrapper.updateComplete
expect(wrapper).toBeTruthy()
})
test('renders with basic structure', async () => {
const { wrapper } = await createInputWrapperTest({ label: 'Test Label' })
await wrapper.updateComplete
expect(wrapper).toBeInTheDocument()
expect(wrapper.label).toBe('Test Label')
const label = wrapper.querySelector('label')
expect(label).toBeInTheDocument()
expect(label?.textContent).toContain('Test Label')
})
test('renders slotted content', async () => {
const { wrapper } = await createInputWrapperTest({
label: 'Test Label',
content: '<input type="text" placeholder="Custom input" />',
})
await wrapper.updateComplete
const input = wrapper.querySelector('input')
expect(input).toBeInTheDocument()
expect(input?.placeholder).toBe('Custom input')
})
})
describe('Properties and attributes', () => {
test('applies default properties correctly', async () => {
const { wrapper } = await createInputWrapperTest()
await wrapper.updateComplete
expect(wrapper.label).toBe('')
expect(wrapper.hasError).toBe(false)
expect(wrapper.disabled).toBe(false)
expect(wrapper.inline).toBe(false)
expect(wrapper.counter).toBe(false)
expect(wrapper.optionalTag).toBe(false)
expect(wrapper.requiredTag).toBe(false)
})
test('sets label property correctly', async () => {
const { wrapper } = await createInputWrapperTest({ label: 'Test Label' })
await wrapper.updateComplete
expect(wrapper.label).toBe('Test Label')
})
test('sets helptext property correctly', async () => {
const { wrapper } = await createInputWrapperTest({ helptext: 'This is help text' })
await wrapper.updateComplete
expect(wrapper.helptext).toBe('This is help text')
})
test('sets error state correctly', async () => {
const { wrapper } = await createInputWrapperTest({
hasError: true,
errorMessage: 'Error message',
})
await wrapper.updateComplete
expect(wrapper.hasError).toBe(true)
expect(wrapper.errorMessage).toBe('Error message')
})
test('sets disabled state correctly', async () => {
const { wrapper } = await createInputWrapperTest({ disabled: true })
await wrapper.updateComplete
expect(wrapper.disabled).toBe(true)
})
test('sets inline layout correctly', async () => {
const { wrapper } = await createInputWrapperTest({ inline: true })
await wrapper.updateComplete
expect(wrapper.inline).toBe(true)
})
test('sets forId property correctly', async () => {
const { wrapper } = await createInputWrapperTest({ forId: 'custom-id' })
await wrapper.updateComplete
expect(wrapper.forId).toBe('custom-id')
})
})
describe('Tag functionality', () => {
test('renders optional tag when enabled', async () => {
const { wrapper } = await createInputWrapperTest({
label: 'Test Label',
optionalTag: true,
})
await wrapper.updateComplete
expect(wrapper.optionalTag).toBe(true)
const tag = wrapper.querySelector('.pkt-tag')
expect(tag).toBeInTheDocument()
})
test('renders required tag when enabled', async () => {
const { wrapper } = await createInputWrapperTest({
label: 'Test Label',
requiredTag: true,
})
await wrapper.updateComplete
expect(wrapper.requiredTag).toBe(true)
const tag = wrapper.querySelector('.pkt-tag')
expect(tag).toBeInTheDocument()
})
test('renders custom tag text when provided', async () => {
const { wrapper } = await createInputWrapperTest({
label: 'Test Label',
tagText: 'Custom Tag',
})
await wrapper.updateComplete
expect(wrapper.tagText).toBe('Custom Tag')
const tag = wrapper.querySelector('.pkt-tag')
expect(tag).toBeInTheDocument()
expect(tag?.textContent).toContain('Custom Tag')
})
})
describe('Counter functionality', () => {
test('renders counter when enabled', async () => {
const { wrapper } = await createInputWrapperTest({
label: 'Test Label',
counter: true,
counterCurrent: 5,
counterMaxLength: 100,
})
await wrapper.updateComplete
expect(wrapper.counter).toBe(true)
expect(wrapper.counterCurrent).toBe(5)
expect(wrapper.counterMaxLength).toBe(100)
const counter = wrapper.querySelector('.pkt-input__counter')
expect(counter).toBeInTheDocument()
})
test('updates counter current value', async () => {
const { wrapper } = await createInputWrapperTest({
label: 'Test Label',
counter: true,
counterCurrent: 10,
counterMaxLength: 100,
})
await wrapper.updateComplete
wrapper.counterCurrent = 25
await wrapper.updateComplete
expect(wrapper.counterCurrent).toBe(25)
})
test('handles counter error state', async () => {
const { wrapper } = await createInputWrapperTest({
label: 'Test Label',
counter: true,
counterCurrent: 105,
counterMaxLength: 100,
})
await wrapper.updateComplete
// Counter should indicate error when current exceeds max
expect(wrapper.counterCurrent).toBe(105)
expect(wrapper.counterMaxLength).toBe(100)
})
})
describe('Helptext functionality', () => {
test('renders dropdown helptext', async () => {
const { wrapper } = await createInputWrapperTest({
label: 'Test Label',
helptextDropdown: 'Dropdown help content',
})
await wrapper.updateComplete
expect(wrapper.helptextDropdown).toBe('Dropdown help content')
const helptext = wrapper.querySelector('pkt-helptext')
expect(helptext).toBeInTheDocument()
})
test('sets dropdown button text', async () => {
const { wrapper } = await createInputWrapperTest({
label: 'Test Label',
helptextDropdown: 'Dropdown help',
helptextDropdownButton: 'Custom Button',
})
await wrapper.updateComplete
expect(wrapper.helptextDropdownButton).toBe('Custom Button')
})
test('renders helptext from slot', async () => {
const { wrapper } = await createInputWrapperTest({
label: 'Test Label',
content: '<input type="text" /><div slot="helptext">Slotted help content</div>',
})
await wrapper.updateComplete
const helptextEl = wrapper.querySelector('pkt-helptext')
expect(helptextEl).toBeInTheDocument()
expect(helptextEl?.textContent).toContain('Slotted help content')
})
})
describe('Error handling', () => {
test('displays error message when hasError is true', async () => {
const { wrapper } = await createInputWrapperTest({
label: 'Test Label',
hasError: true,
errorMessage: 'This field is required',
})
await wrapper.updateComplete
expect(wrapper.hasError).toBe(true)
expect(wrapper.errorMessage).toBe('This field is required')
const errorMessage = wrapper.querySelector('.pkt-alert--error')
expect(errorMessage).toBeInTheDocument()
expect(errorMessage?.textContent).toContain('This field is required')
})
test('does not display error message when hasError is false', async () => {
const { wrapper } = await createInputWrapperTest({ label: 'Test Label' })
await wrapper.updateComplete
expect(wrapper.hasError).toBe(false)
const errorMessage = wrapper.querySelector('.pkt-alert--error')
expect(errorMessage).not.toBeInTheDocument()
})
test('applies error styling when hasError is true', async () => {
const { wrapper } = await createInputWrapperTest({
label: 'Test Label',
hasError: true,
})
await wrapper.updateComplete
expect(wrapper.hasError).toBe(true)
// Check for error-related classes based on actual component implementation
const inputWrapper = wrapper.querySelector('.pkt-inputwrapper')
expect(inputWrapper?.classList.contains('pkt-inputwrapper--error')).toBe(true)
})
})
describe('Fieldset functionality', () => {
test('handles fieldset mode', async () => {
const { wrapper } = await createInputWrapperTest({
label: 'Test Label',
hasFieldset: true,
})
await wrapper.updateComplete
expect(wrapper.hasFieldset).toBe(true)
})
test('sets role attribute correctly', async () => {
const { wrapper } = await createInputWrapperTest({
label: 'Test Label',
role: 'radiogroup',
})
await wrapper.updateComplete
expect(wrapper.role).toBe('radiogroup')
expect(wrapper.getAttribute('role')).toBe('radiogroup')
})
})
describe('Wrapper functionality', () => {
test('handles useWrapper property values', async () => {
// Test true value
const { wrapper: wrapperTrue } = await createInputWrapperTest({
label: 'Test Label',
useWrapper: true,
})
await wrapperTrue.updateComplete
expect(wrapperTrue.useWrapper).toBe(true)
expect(wrapperTrue.getAttribute('useWrapper')).toBe('true')
// Test false value explicitly
const { wrapper: wrapperFalse } = await createInputWrapperTest()
wrapperFalse.setAttribute('useWrapper', 'false')
wrapperFalse.label = 'Test Label'
await wrapperFalse.updateComplete
expect(wrapperFalse.useWrapper).toBe(false)
expect(wrapperFalse.getAttribute('useWrapper')).toBe('false')
})
})
describe('ARIA and accessibility attributes', () => {
test('sets aria-describedby correctly', async () => {
const { wrapper } = await createInputWrapperTest({
label: 'Test Label',
ariaDescribedBy: 'help-text-id',
})
await wrapper.updateComplete
expect(wrapper.ariaDescribedby).toBe('help-text-id')
})
test('associates label with input using forId', async () => {
const { wrapper } = await createInputWrapperTest({
label: 'Test Label',
forId: 'test-input',
content: '<input id="test-input" type="text" />',
})
await wrapper.updateComplete
const label = wrapper.querySelector('label')
const input = wrapper.querySelector('input')
expect(label?.getAttribute('for')).toBe('test-input')
expect(input?.id).toBe('test-input')
})
})
describe('Dynamic updates', () => {
test('updates label dynamically', async () => {
const { wrapper } = await createInputWrapperTest({ label: 'Original Label' })
await wrapper.updateComplete
wrapper.label = 'Updated Label'
await wrapper.updateComplete
expect(wrapper.label).toBe('Updated Label')
const label = wrapper.querySelector('label')
expect(label?.textContent).toContain('Updated Label')
})
test('updates error state dynamically', async () => {
const { wrapper } = await createInputWrapperTest({ label: 'Test Label' })
await wrapper.updateComplete
wrapper.hasError = true
wrapper.errorMessage = 'Dynamic error'
await wrapper.updateComplete
expect(wrapper.hasError).toBe(true)
expect(wrapper.errorMessage).toBe('Dynamic error')
})
test('updates helptext dynamically', async () => {
const { wrapper } = await createInputWrapperTest({ label: 'Test Label' })
await wrapper.updateComplete
wrapper.helptext = 'Dynamic help text'
await wrapper.updateComplete
expect(wrapper.helptext).toBe('Dynamic help text')
})
})
describe('Accessibility', () => {
test('basic input wrapper is accessible', async () => {
const { container } = await createInputWrapperTest({
label: 'Accessible Label',
forId: 'test-input',
content: '<input id="test-input" type="text" />',
})
await new Promise((resolve) => setTimeout(resolve, 100))
const results = await axe(container)
expect(results).toHaveNoViolations()
})
test('input wrapper with helptext is accessible', async () => {
const { container } = await createInputWrapperTest({
label: 'Test Label',
helptext: 'This is helpful information',
forId: 'test-input-2',
content: '<input id="test-input-2" type="text" />',
})
await new Promise((resolve) => setTimeout(resolve, 100))
const results = await axe(container)
expect(results).toHaveNoViolations()
})
test('comprehensive input wrapper accessibility', async () => {
const { container } = await createInputWrapperTest({
label: 'Test Label',
helptext: 'Help text',
hasError: true,
errorMessage: 'Error message',
optionalTag: true,
forId: 'test-input-comprehensive',
content: '<input id="test-input-comprehensive" type="text" />',
})
await new Promise((resolve) => setTimeout(resolve, 100))
const results = await axe(container)
expect(results).toHaveNoViolations()
})
})
describe('Integration scenarios', () => {
test('works with complex form elements', async () => {
const { wrapper } = await createInputWrapperTest({
label: 'Complex Field',
helptext: 'Help text',
counter: true,
counterMaxLength: 100,
optionalTag: true,
content: '<textarea></textarea>',
})
await wrapper.updateComplete
expect(wrapper.label).toBe('Complex Field')
expect(wrapper.helptext).toBe('Help text')
expect(wrapper.counter).toBe(true)
expect(wrapper.optionalTag).toBe(true)
const textarea = wrapper.querySelector('textarea')
expect(textarea).toBeInTheDocument()
})
test('handles multiple input wrappers on same page', async () => {
const { container } = await createInputWrapperTest({
label: 'Field 1',
content: '<input type="text" />',
})
// Add additional wrappers to the container
container.innerHTML += `
<pkt-input-wrapper label="Field 2"><input type="email" /></pkt-input-wrapper>
<pkt-input-wrapper label="Field 3"><textarea></textarea></pkt-input-wrapper>
`
// Wait for all components to be defined
await customElements.whenDefined('pkt-input-wrapper')
const wrappers = container.querySelectorAll('pkt-input-wrapper')
expect(wrappers).toHaveLength(3)
for (const wrapper of wrappers) {
await (wrapper as any).updateComplete
expect(wrapper.querySelector('label')).toBeInTheDocument()
}
})
})
})