@oslokommune/punkt-elements
Version:
Komponentbiblioteket til Punkt, et designsystem laget av Oslo Origo
290 lines (233 loc) • 9.93 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 './textarea'
export interface TextareaTestConfig extends BaseTestConfig {
// From PktTextarea specific properties
value?: string
autocomplete?: string
rows?: number | null
// From PktInputElement base class (commonly used ones)
id?: string
label?: string
name?: string
disabled?: boolean
readonly?: boolean
required?: boolean
placeholder?: string | null
maxlength?: number | null
minlength?: number | null
hasError?: boolean
errorMessage?: string
helptext?: string
fullwidth?: boolean
counter?: boolean
inline?: boolean
ariaLabelledby?: string | null
ariaDescribedBy?: string | null
}
// Use shared framework
export const createTextareaTest = async (config: TextareaTestConfig = {}) => {
const { container, element } = await createElementTest<
CustomElementFor<'pkt-textarea'>,
TextareaTestConfig
>('pkt-textarea', config)
return {
container,
textarea: element,
}
}
expect.extend(toHaveNoViolations)
afterEach(() => {
document.body.innerHTML = ''
})
describe('PktTextarea', () => {
describe('Basic Rendering', () => {
test('renders without errors', async () => {
const { textarea } = await createTextareaTest()
expect(textarea).toBeInTheDocument()
})
test('renders with default properties', async () => {
const { textarea } = await createTextareaTest()
expect(textarea.value).toBe('')
expect(textarea.autocomplete).toBe('off')
expect(textarea.rows).toBe(null)
})
test('renders textarea element', async () => {
const { textarea } = await createTextareaTest()
const textareaElement = textarea.querySelector('textarea')
expect(textareaElement).toBeInTheDocument()
})
})
describe('Properties and Attributes', () => {
test('sets value correctly', async () => {
const value = 'Test textarea content'
const { textarea } = await createTextareaTest({ value })
expect(textarea.value).toBe(value)
const textareaElement = textarea.querySelector('textarea') as HTMLTextAreaElement
expect(textareaElement.value).toBe(value)
})
test('sets rows correctly', async () => {
const { textarea } = await createTextareaTest({ rows: 5 })
expect(textarea.rows).toBe(5)
const textareaElement = textarea.querySelector('textarea')
expect(textareaElement?.getAttribute('rows')).toBe('5')
})
test('sets autocomplete correctly', async () => {
const { textarea } = await createTextareaTest({ autocomplete: 'on' })
expect(textarea.autocomplete).toBe('on')
const textareaElement = textarea.querySelector('textarea')
expect(textareaElement?.getAttribute('autocomplete')).toBe('on')
})
test('handles disabled state', async () => {
const { textarea } = await createTextareaTest({ disabled: true })
const textareaElement = textarea.querySelector('textarea')
expect(textareaElement?.hasAttribute('disabled')).toBe(true)
})
test('handles readonly state', async () => {
const { textarea } = await createTextareaTest({ readonly: true })
const textareaElement = textarea.querySelector('textarea')
expect(textareaElement?.hasAttribute('readonly')).toBe(true)
})
test('handles required state', async () => {
const { textarea } = await createTextareaTest({ required: true })
const textareaElement = textarea.querySelector('textarea')
expect(textareaElement?.hasAttribute('required')).toBe(false) // Not set as attribute on textarea
const inputWrapper = textarea.querySelector('pkt-input-wrapper')
expect(inputWrapper?.hasAttribute('required')).toBe(true) // But passed to wrapper
})
})
describe('Input Wrapper Integration', () => {
test('displays label correctly', async () => {
const { textarea } = await createTextareaTest({ label: 'Comment' })
const inputWrapper = textarea.querySelector('pkt-input-wrapper')
expect(inputWrapper?.getAttribute('label')).toBe('Comment')
})
test('displays helptext correctly', async () => {
const { textarea } = await createTextareaTest({ helptext: 'Enter your message' })
// helptext is passed as a property, not attribute to input-wrapper
expect(textarea.helptext).toBe('Enter your message')
})
test('handles error state', async () => {
const { textarea } = await createTextareaTest({
hasError: true,
errorMessage: 'This field is required',
})
expect(textarea.hasError).toBe(true)
expect(textarea.errorMessage).toBe('This field is required')
const inputWrapper = textarea.querySelector('pkt-input-wrapper')
expect(inputWrapper?.hasAttribute('hasError')).toBe(true)
const textareaElement = textarea.querySelector('textarea')
expect(textareaElement?.getAttribute('aria-invalid')).toBe('true')
})
test('handles fullwidth styling', async () => {
const { textarea } = await createTextareaTest({ fullwidth: true })
const textareaElement = textarea.querySelector('textarea')
expect(textareaElement?.className).toContain('pkt-input--fullwidth')
})
})
describe('Character Counter', () => {
test('shows counter when enabled', async () => {
const { textarea } = await createTextareaTest({
counter: true,
maxlength: 100,
})
const inputWrapper = textarea.querySelector('pkt-input-wrapper')
expect(inputWrapper?.hasAttribute('counter')).toBe(true)
})
test('updates counter on value change', async () => {
const { textarea } = await createTextareaTest({
counter: true,
maxlength: 100,
value: 'Hello',
})
expect(textarea.counterCurrent).toBe(5)
})
})
describe('User Interaction', () => {
test('updates value on user input', async () => {
const { textarea } = await createTextareaTest()
const textareaElement = textarea.querySelector('textarea') as HTMLTextAreaElement
fireEvent.input(textareaElement, { target: { value: 'New content' } })
await textarea.updateComplete
expect(textarea.value).toBe('New content')
expect(textarea.touched).toBe(true)
})
test('handles focus and blur events', async () => {
const { textarea } = await createTextareaTest()
const textareaElement = textarea.querySelector('textarea') as HTMLTextAreaElement
// Focus and input to trigger touched state
fireEvent.focus(textareaElement)
fireEvent.input(textareaElement, { target: { value: 'test input' } })
await textarea.updateComplete
fireEvent.blur(textareaElement)
await textarea.updateComplete
// Test that input with value change sets touched state
expect(textarea.touched).toBe(true)
})
})
describe('Validation', () => {
test('respects maxlength constraint', async () => {
const { textarea } = await createTextareaTest({ maxlength: 10 })
const textareaElement = textarea.querySelector('textarea')
expect(textareaElement?.getAttribute('maxlength')).toBe('10')
})
test('respects minlength constraint', async () => {
const { textarea } = await createTextareaTest({ minlength: 5 })
const textareaElement = textarea.querySelector('textarea')
expect(textareaElement?.getAttribute('minlength')).toBe('5')
})
})
describe('Accessibility', () => {
test('passes through accessibility attributes', async () => {
const { textarea } = await createTextareaTest({
ariaLabelledby: 'external-label',
ariaDescribedBy: 'external-description',
})
const textareaElement = textarea.querySelector('textarea')
expect(textareaElement?.getAttribute('aria-labelledby')).toBe('external-label')
// ariaDescribedBy is passed as property to input-wrapper
expect(textarea.ariaDescribedBy).toBe('external-description')
})
test('textarea is accessible', async () => {
const { textarea } = await createTextareaTest({
label: 'Message',
helptext: 'Enter your message here',
required: true,
})
const results = await axe(textarea)
expect(results).toHaveNoViolations()
})
})
describe('Complex Configuration', () => {
test('renders with all properties set', async () => {
const config: TextareaTestConfig = {
label: 'Feedback',
value: 'Initial feedback text',
placeholder: 'Enter your feedback...',
rows: 6,
maxlength: 500,
counter: true,
required: true,
helptext: 'Please provide detailed feedback',
}
const { textarea } = await createTextareaTest(config)
expect(textarea.value).toBe(config.value)
expect(textarea.rows).toBe(config.rows)
expect(textarea.maxlength).toBe(config.maxlength)
expect(textarea.required).toBe(config.required)
const inputWrapper = textarea.querySelector('pkt-input-wrapper')
expect(inputWrapper?.getAttribute('label')).toBe(config.label)
expect(textarea.helptext).toBe(config.helptext) // Property, not attribute
expect(inputWrapper?.hasAttribute('counter')).toBe(true)
const textareaElement = textarea.querySelector('textarea')
expect(textareaElement?.getAttribute('placeholder')).toBe(config.placeholder)
expect(textareaElement?.getAttribute('rows')).toBe(String(config.rows))
expect(textareaElement?.getAttribute('maxlength')).toBe(String(config.maxlength))
// required is handled by input-wrapper, not set directly on textarea
expect(textarea.required).toBe(true)
})
})
})