mcp-connection-form.test.tsx4.0 KBView on GitHub import type { McpProbeStatus, McpToolRow } from '@/modules/integrations/mcp-tool-policy';
import { McpConnectionForm } from '@/modules/integrations/mcp-connection-form';
import { fireEvent, render, screen } from '@testing-library/react';
const noop = () => {};
function renderForm(
over: Partial<{
mode: 'add' | 'edit';
probeStatus: McpProbeStatus;
probeError: string | null;
tools: McpToolRow[];
onSubmit: () => void;
isSaving: boolean;
}> = {},
) {
const onSubmit = over.onSubmit ?? jest.fn();
render(
<McpConnectionForm
mode={over.mode ?? 'add'}
values={{
name: 'Stripe',
serverUrl: 'https://mcp.stripe.com',
authorizationHeader: '',
instructions: '',
}}
onChange={noop}
probeStatus={over.probeStatus ?? 'idle'}
probeError={over.probeError ?? null}
tools={over.tools ?? []}
onProbe={noop}
onToggleTool={noop}
onToolInstructionChange={noop}
onSubmit={onSubmit}
onCancel={noop}
isSaving={over.isSaving ?? false}
/>,
);
return { onSubmit };
}
describe('McpConnectionForm', () => {
it('lets the connection be saved after a failed probe', () => {
// A server that is asleep or slow to warm is a normal state. Blocking the save
// would strand the user with no connection to retry the probe from — and the
// saved connection grants nothing, because it is deny-by-default.
const { onSubmit } = renderForm({
probeStatus: 'failed',
probeError: 'fetch failed: ECONNREFUSED',
});
const connect = screen.getByRole('button', { name: 'Connect' });
expect(connect).toBeEnabled();
fireEvent.click(connect);
expect(onSubmit).toHaveBeenCalled();
});
it('shows the probe error and says saving is still safe', () => {
renderForm({ probeStatus: 'failed', probeError: 'fetch failed: ECONNREFUSED' });
const alert = screen.getByRole('alert');
expect(alert).toHaveTextContent('Could not reach this server');
expect(alert).toHaveTextContent('fetch failed: ECONNREFUSED');
expect(alert).toHaveTextContent(/no tool is enabled until you enable it/i);
});
it('shows the discovered tools as a checklist before the connection is saved', () => {
renderForm({
probeStatus: 'ready',
tools: [
{ name: 'create_payment_link', description: 'Create a link', allowed: false },
{ name: 'create_refund', description: 'Refund a charge', allowed: false },
],
});
expect(screen.getByText('0 of 2 tools enabled')).toBeInTheDocument();
expect(screen.getByRole('checkbox', { name: 'Allow create_payment_link' })).not.toBeChecked();
expect(screen.getByRole('checkbox', { name: 'Allow create_refund' })).not.toBeChecked();
});
it('grants nothing before anything is probed — no checklist, just the check', () => {
// This used to assert the sentence "New connections start with every tool denied", which
// sat under the label as a hint. The hint is gone (the button beside the label says what to
// do), so the guarantee is asserted where it actually lives: until a probe returns there is
// no tool list at all, and therefore nothing that could be enabled.
renderForm({ probeStatus: 'idle' });
expect(screen.getByRole('button', { name: /Check available tools/i })).toBeInTheDocument();
expect(screen.queryByRole('checkbox', { name: /^Allow / })).not.toBeInTheDocument();
expect(screen.queryByText(/tools enabled/i)).not.toBeInTheDocument();
});
it('blocks submission only while a probe or a save is in flight', () => {
renderForm({ probeStatus: 'probing' });
expect(screen.getByRole('button', { name: 'Connect' })).toBeDisabled();
});
it('does not offer setup-time discovery when editing — permissions live on the connection', () => {
renderForm({ mode: 'edit' });
expect(
screen.queryByRole('button', { name: /Check available tools/i }),
).not.toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Save changes' })).toBeEnabled();
});
});