// @vitest-environment jsdom /** * Field-control behavior: what a control renders for a staged draft, how an * overridden field offers its reset, and that a control never writes on its own. */ import { cleanup, fireEvent, render, screen } from '@testing-library/react' import { afterEach, describe, expect, it, vi } from 'vitest' import { SecretField, ValueField } from '../src/client/fields.tsx' afterEach(cleanup) const frame = { id: 'field', label: 'Command timeout', hint: 'How long one command may run.', overriddenLabel: 'Overridden', resetLabel: 'Reset to default', invalidLabel: 'Enter a number.', disabled: false, overridden: false, invalid: false, } describe('ValueField', () => { it('stages every keystroke without writing', () => { const onEdit = vi.fn() render() fireEvent.change(screen.getByLabelText('Command timeout'), { target: { value: '9000' } }) expect(onEdit).toHaveBeenCalledWith('9000') }) it('renders the staged text it is given rather than a draft of its own', () => { const { rerender } = render() expect(screen.getByLabelText('Command timeout')).toHaveProperty('value', '60000') rerender() expect(screen.getByLabelText('Command timeout')).toHaveProperty('value', '9000') }) it('offers the reset only while an override would stand', () => { const onReset = vi.fn() const { rerender } = render() expect(screen.queryByRole('button', { name: 'Reset to default' })).toBeNull() rerender() fireEvent.click(screen.getByRole('button', { name: 'Reset to default' })) expect(screen.getByText('Overridden')).toBeTruthy() expect(onReset).toHaveBeenCalledOnce() }) it('replaces the hint with the reason an invalid draft cannot be saved', () => { render() expect(screen.getByText('Enter a number.')).toBeTruthy() expect(screen.queryByText('How long one command may run.')).toBeNull() expect(screen.getByLabelText('Command timeout').getAttribute('aria-invalid')).toBe('true') }) it('hints a numeric keypad and renders a placeholder when asked', () => { render( , ) const input = screen.getByLabelText('Command timeout') expect(input.getAttribute('inputmode')).toBe('numeric') expect(input).toHaveProperty('placeholder', 'https://api.deepseek.com') }) it('disables the control and its reset while the document is read-only', () => { render() expect(screen.getByLabelText('Command timeout')).toHaveProperty('disabled', true) expect(screen.getByRole('button', { name: 'Reset to default' })).toHaveProperty('disabled', true) }) }) describe('SecretField', () => { const secret = { id: 'key', label: 'API key', hint: 'Stored outside the settings file.', disabled: false, } it('stages the draft and never renders it', () => { const onEdit = vi.fn() render( , ) const input = screen.getByLabelText('API key') fireEvent.change(input, { target: { value: 'ds-secret' } }) expect(onEdit).toHaveBeenCalledWith('ds-secret') expect(input).toHaveProperty('type', 'password') }) it('reports the configured state the Host holds', () => { const { rerender } = render( , ) expect(screen.getByText('No key is configured.')).toBeTruthy() rerender( , ) expect(screen.getByText('A key is configured.')).toBeTruthy() expect(screen.getByLabelText('API key')).toHaveProperty('value', 'ds-secret') }) it('disables the control when it is told to', () => { render( , ) expect(screen.getByLabelText('API key')).toHaveProperty('disabled', true) }) })