diff --git a/frontend/src/components/Chat/ConverterPanel/ConverterParams.test.tsx b/frontend/src/components/Chat/ConverterPanel/ConverterParams.test.tsx new file mode 100644 index 0000000000..b467edad25 --- /dev/null +++ b/frontend/src/components/Chat/ConverterPanel/ConverterParams.test.tsx @@ -0,0 +1,54 @@ +import React from 'react' +import { render, screen } from '@testing-library/react' +import { FluentProvider, webLightTheme } from '@fluentui/react-components' +import type { ConverterCatalogEntry } from '../../../types' +import ConverterParams from './ConverterParams' + +const converter: ConverterCatalogEntry = { + converter_type: 'TestConverter', + supported_input_types: ['text'], + supported_output_types: ['text'], + is_llm_based: false, + parameters: [ + { name: 'key', type_name: 'str', required: true, default: null, description: 'Cipher key' }, + { name: 'append_description', type_name: 'bool', required: false, default: 'false' }, + { name: 'mode', type_name: 'str', required: false, default: 'safe', choices: ['safe', 'strict'] }, + { name: 'image_path', type_name: 'str', required: false, default: null, description: 'Image file path' }, + ], +} + +function renderParams(showValidation = false) { + return render( + + + , + ) +} + +describe('ConverterParams accessibility', () => { + it('associates visible parameter names with every control variant', () => { + renderParams() + + expect(screen.getByRole('textbox', { name: 'key' })).toBeInTheDocument() + expect(screen.getByRole('switch', { name: 'append_description' })).toBeInTheDocument() + expect(screen.getByRole('combobox', { name: 'mode' })).toBeInTheDocument() + expect(screen.getByRole('textbox', { name: 'image_path' })).toBeInTheDocument() + }) + + it('connects required validation text and state to the input', () => { + renderParams(true) + + const keyInput = screen.getByRole('textbox', { name: 'key' }) + expect(keyInput).toHaveAttribute('aria-required', 'true') + expect(keyInput).toHaveAttribute('aria-invalid', 'true') + expect(keyInput).toHaveAccessibleDescription(/Required/) + }) +}) diff --git a/frontend/src/components/Chat/ConverterPanel/ConverterParams.tsx b/frontend/src/components/Chat/ConverterPanel/ConverterParams.tsx index fbccf69a4c..ad1aa1687b 100644 --- a/frontend/src/components/Chat/ConverterPanel/ConverterParams.tsx +++ b/frontend/src/components/Chat/ConverterPanel/ConverterParams.tsx @@ -7,15 +7,20 @@ interface ParamInputProps { param: Parameter value: string isMissing: boolean + labelId: string + describedBy?: string onChange: (name: string, value: string) => void } -function ConverterParameterChoiceViewer({ param, value, onChange }: ParamInputProps) { +function ConverterParameterChoiceViewer({ param, value, labelId, describedBy, onChange }: ParamInputProps) { const stringDefault = typeof param.default === 'string' ? param.default : '' return (