import test from 'node:test'; import assert from 'node:assert/strict'; import React from 'react'; import { renderToStaticMarkup } from 'react-dom/server'; import { act, create, type ReactTestRenderer } from 'react-test-renderer'; import { I18nProvider } from '../../application/i18n/I18nProvider'; import { CodebuddyElicitationCard } from './CodebuddyElicitationCard'; (globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT: boolean }) .IS_REACT_ACT_ENVIRONMENT = true; test('CodebuddyElicitationCard renders MCP form fields and response actions', () => { const markup = renderToStaticMarkup( {}} /> , ); assert.match(markup, /CodeBuddy needs your input/); assert.match(markup, /Choose deployment settings/); assert.match(markup, /Environment \*/); assert.match(markup, /staging/); assert.match(markup, /Dry run/); assert.match(markup, /Decline/); assert.match(markup, /Continue/); assert.doesNotMatch(markup, /role="alert"/); }); test('CodebuddyElicitationCard renders constraint errors and blocks submission', () => { const markup = renderToStaticMarkup( {}} /> , ); assert.match(markup, /Retries must be at least 10\./); assert.match(markup, /Select at least 2 options for Regions\./); assert.match(markup, /aria-invalid="true"/); assert.match(markup, /]*disabled=""[^>]*>Continue<\/button>/); }); test('CodebuddyElicitationCard uses unique error ids across concurrent cards', () => { const makeCard = (elicitationId: string) => ( {}} /> ); const markup = renderToStaticMarkup( {makeCard('el-1')} {makeCard('el-2')} , ); const ids = Array.from(markup.matchAll(/id="([^"]+-field-0-error)"/g), (match) => match[1]); const describedByIds = Array.from( markup.matchAll(/aria-describedby="([^"]+-field-0-error)"/g), (match) => match[1], ); assert.equal(ids.length, 2); assert.equal(new Set(ids).size, 2); assert.deepEqual(describedByIds, ids); }); test('CodebuddyElicitationCard shows "must be whole number" for decimal integer input', () => { const markup = renderToStaticMarkup( {}} /> , ); assert.match(markup, /Retries must be a whole number\./); assert.match(markup, /aria-invalid="true"/); }); test('CodebuddyElicitationCard can clear optional constrained values back to omitted', async () => { const responses: Array<{ action: string; content?: Record; }> = []; let renderer: ReactTestRenderer; await act(async () => { renderer = create( { responses.push({ action, content }); }} />, ); }); const emailInput = renderer!.root.findByProps({ type: 'email' }); const regionInput = renderer!.root.findByProps({ type: 'checkbox' }); await act(async () => { emailInput.props.onChange({ target: { value: 'cat@example.com' } }); regionInput.props.onChange({ target: { checked: true } }); }); await act(async () => { emailInput.props.onChange({ target: { value: '' } }); regionInput.props.onChange({ target: { checked: false } }); }); const continueButton = renderer!.root .findAllByType('button') .find((button) => button.children.join('') === 'ai.codebuddy.elicitation.accept'); assert.ok(continueButton); assert.equal(continueButton.props.disabled, false); await act(async () => { continueButton.props.onClick(); await Promise.resolve(); }); assert.deepEqual(responses, [{ action: 'accept', content: {} }]); await act(async () => { renderer!.unmount(); }); }); test('CodebuddyElicitationCard submits required empty values when no size constraint forbids them', async () => { const responses: Array<{ action: string; content?: Record; }> = []; let renderer: ReactTestRenderer; await act(async () => { renderer = create( { responses.push({ action, content }); }} />, ); }); const noteInput = renderer!.root.findByProps({ type: 'text' }); const regionInput = renderer!.root.findByProps({ type: 'checkbox' }); await act(async () => { noteInput.props.onChange({ target: { value: '' } }); regionInput.props.onChange({ target: { checked: false } }); }); const continueButton = renderer!.root .findAllByType('button') .find((button) => button.children.join('') === 'ai.codebuddy.elicitation.accept'); assert.ok(continueButton); assert.equal(continueButton.props.disabled, false); await act(async () => { continueButton.props.onClick(); await Promise.resolve(); }); assert.deepEqual(responses, [{ action: 'accept', content: { note: '', regions: [], }, }]); await act(async () => { renderer!.unmount(); }); });