import { render, screen, fireEvent } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { NodeInspector } from '../NodeInspector'; import type { WfNode, NodeTypeDef, NodeExecView } from '../types'; const node: WfNode = { id: 'n1', name: 'Send email', type: 'action.email', parameters: { to: 'a@b.com', retries: 3, urgent: true }, }; const nodeType: NodeTypeDef & { parameterSchema?: Record } = { slug: 'action.email', name: 'Send email', category: 'action', parameterSchema: { type: 'object', properties: { to: { type: 'string', title: 'Recipient' }, retries: { type: 'number', title: 'Retries' }, urgent: { type: 'boolean', title: 'Urgent' }, }, }, }; describe('NodeInspector', () => { it('renders a field per schema property with current values', () => { render(); expect(screen.getByLabelText('Recipient')).toHaveValue('a@b.com'); expect(screen.getByLabelText('Retries')).toHaveValue(3); expect(screen.getByLabelText('Urgent')).toBeChecked(); }); it('emits parameter changes via onParametersChange', () => { const onParametersChange = jest.fn(); render( ); fireEvent.change(screen.getByLabelText('Recipient'), { target: { value: 'c@d.com' }, }); expect(onParametersChange).toHaveBeenCalledWith( 'n1', expect.objectContaining({ to: 'c@d.com' }) ); }); it('renders an empty state when no node is selected', () => { render(); expect(screen.getByText(/select a node/i)).toBeInTheDocument(); }); it('shows read-only input/output tabs when given an execution view', async () => { const exec: NodeExecView = { nodeId: 'n1', status: 'success', inputData: { foo: 'in' }, outputData: { foo: 'out' }, }; const user = userEvent.setup(); render(); // the exec tabs exist expect(screen.getByRole('tab', { name: /input/i })).toBeInTheDocument(); expect(screen.getByRole('tab', { name: /output/i })).toBeInTheDocument(); // switch to the Output tab await user.click(screen.getByRole('tab', { name: /output/i })); expect(screen.getByText(/"foo": "out"/)).toBeInTheDocument(); }); it('marks required fields (schema.required[]) with an indicator', () => { const requiredType: typeof nodeType = { ...nodeType, parameterSchema: { type: 'object', required: ['to'], properties: { to: { type: 'string', title: 'Recipient' }, retries: { type: 'number', title: 'Retries' }, }, }, }; render(); // required field exposes aria-required + a visible "*" marker expect(screen.getByLabelText(/Recipient/)).toHaveAttribute( 'aria-required', 'true' ); // non-required field is not marked expect(screen.getByLabelText('Retries')).not.toHaveAttribute( 'aria-required', 'true' ); expect(screen.getByText('*')).toBeInTheDocument(); }); });