Skip to content
Merged
215 changes: 215 additions & 0 deletions src/__tests__/renderer/components/ForcedParallelWarningModal.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
/**
* Tests for ForcedParallelWarningModal component
*
* Tests the one-time acknowledgment modal for forced parallel execution:
* - Rendering when open/closed
* - Confirm and Cancel button handlers
* - Layer stack integration
* - Warning content display
*/

import React from 'react';
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { ForcedParallelWarningModal } from '../../../renderer/components/ForcedParallelWarningModal';
import { LayerStackProvider } from '../../../renderer/contexts/LayerStackContext';
import type { Theme } from '../../../renderer/types';

// Mock lucide-react
vi.mock('lucide-react', () => ({
X: () => <svg data-testid="x-icon" />,
AlertTriangle: () => <svg data-testid="alert-triangle-icon" />,
}));

const testTheme: Theme = {
id: 'test-theme',
name: 'Test Theme',
mode: 'dark',
colors: {
bgMain: '#1e1e1e',
bgSidebar: '#252526',
bgActivity: '#333333',
textMain: '#d4d4d4',
textDim: '#808080',
accent: '#007acc',
border: '#404040',
error: '#f14c4c',
warning: '#cca700',
success: '#89d185',
info: '#3794ff',
textInverse: '#000000',
},
};

const renderWithLayerStack = (ui: React.ReactElement) => {
return render(<LayerStackProvider>{ui}</LayerStackProvider>);
};

describe('ForcedParallelWarningModal', () => {
beforeEach(() => {
vi.clearAllMocks();
});

afterEach(() => {
vi.restoreAllMocks();
});

describe('rendering', () => {
it('renders when isOpen is true', () => {
renderWithLayerStack(
<ForcedParallelWarningModal
isOpen={true}
onConfirm={vi.fn()}
onCancel={vi.fn()}
theme={testTheme}
/>
);

expect(screen.getByRole('dialog')).toBeInTheDocument();
expect(
screen.getByRole('heading', { name: 'Forced Parallel Execution' })
).toBeInTheDocument();
});

it('does not render when isOpen is false', () => {
renderWithLayerStack(
<ForcedParallelWarningModal
isOpen={false}
onConfirm={vi.fn()}
onCancel={vi.fn()}
theme={testTheme}
/>
);

expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
});

it('displays warning content', () => {
renderWithLayerStack(
<ForcedParallelWarningModal
isOpen={true}
onConfirm={vi.fn()}
onCancel={vi.fn()}
theme={testTheme}
/>
);

expect(
screen.getByText(/sends messages immediately, even when the agent is already working/i)
).toBeInTheDocument();
expect(
screen.getByText(/intended for advanced users who understand the risks/i)
).toBeInTheDocument();
});

it('displays alert triangle icon', () => {
renderWithLayerStack(
<ForcedParallelWarningModal
isOpen={true}
onConfirm={vi.fn()}
onCancel={vi.fn()}
theme={testTheme}
/>
);

expect(screen.getByTestId('alert-triangle-icon')).toBeInTheDocument();
});

it('displays confirm and cancel buttons', () => {
renderWithLayerStack(
<ForcedParallelWarningModal
isOpen={true}
onConfirm={vi.fn()}
onCancel={vi.fn()}
theme={testTheme}
/>
);

expect(screen.getByRole('button', { name: 'I understand, enable it' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Cancel' })).toBeInTheDocument();
});
});

describe('button handlers', () => {
it('calls onConfirm when confirm button is clicked', () => {
const onConfirm = vi.fn();
renderWithLayerStack(
<ForcedParallelWarningModal
isOpen={true}
onConfirm={onConfirm}
onCancel={vi.fn()}
theme={testTheme}
/>
);

fireEvent.click(screen.getByRole('button', { name: 'I understand, enable it' }));
expect(onConfirm).toHaveBeenCalledTimes(1);
});

it('calls onCancel when cancel button is clicked', () => {
const onCancel = vi.fn();
renderWithLayerStack(
<ForcedParallelWarningModal
isOpen={true}
onConfirm={vi.fn()}
onCancel={onCancel}
theme={testTheme}
/>
);

fireEvent.click(screen.getByRole('button', { name: 'Cancel' }));
expect(onCancel).toHaveBeenCalledTimes(1);
});

it('calls onCancel when X close button is clicked', () => {
const onCancel = vi.fn();
renderWithLayerStack(
<ForcedParallelWarningModal
isOpen={true}
onConfirm={vi.fn()}
onCancel={onCancel}
theme={testTheme}
/>
);

const closeButton = screen.getByTestId('x-icon').closest('button');
fireEvent.click(closeButton!);
expect(onCancel).toHaveBeenCalledTimes(1);
});
});

describe('focus management', () => {
it('focuses confirm button on mount', async () => {
renderWithLayerStack(
<ForcedParallelWarningModal
isOpen={true}
onConfirm={vi.fn()}
onCancel={vi.fn()}
theme={testTheme}
/>
);

await waitFor(() => {
expect(document.activeElement).toBe(
screen.getByRole('button', { name: 'I understand, enable it' })
);
});
});
});

describe('layer stack integration', () => {
it('registers and unregisters without errors', () => {
const { unmount } = renderWithLayerStack(
<ForcedParallelWarningModal
isOpen={true}
onConfirm={vi.fn()}
onCancel={vi.fn()}
theme={testTheme}
/>
);

expect(screen.getByRole('dialog')).toBeInTheDocument();
expect(() => unmount()).not.toThrow();
});
});
});
Comment thread
coderabbitai[bot] marked this conversation as resolved.
137 changes: 137 additions & 0 deletions src/__tests__/renderer/hooks/useInputKeyDown.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -892,6 +892,143 @@ describe('Tab completion trigger', () => {
});
});

// ============================================================================
// Forced parallel send shortcut
// ============================================================================

describe('Forced parallel send shortcut', () => {
it('Cmd+Shift+Enter calls processInput with forceParallel in AI mode', () => {
setActiveSession({ inputMode: 'ai' });
useSettingsStore.setState({
forcedParallelExecution: true,
shortcuts: {
...useSettingsStore.getState().shortcuts,
forcedParallelSend: {
id: 'forcedParallelSend',
label: 'Forced Parallel Send',
keys: ['Meta', 'Shift', 'Enter'],
},
},
} as any);
const deps = createMockDeps();
const { result } = renderHook(() => useInputKeyDown(deps));
const e = createKeyEvent('Enter', { metaKey: true, shiftKey: true });

act(() => {
result.current.handleInputKeyDown(e);
});

expect(e.preventDefault).toHaveBeenCalled();
expect(deps.processInput).toHaveBeenCalledWith(undefined, { forceParallel: true });
});

it('Ctrl+Shift+Enter calls processInput with forceParallel in AI mode', () => {
setActiveSession({ inputMode: 'ai' });
useSettingsStore.setState({
forcedParallelExecution: true,
shortcuts: {
...useSettingsStore.getState().shortcuts,
forcedParallelSend: {
id: 'forcedParallelSend',
label: 'Forced Parallel Send',
keys: ['Meta', 'Shift', 'Enter'],
},
},
} as any);
const deps = createMockDeps();
const { result } = renderHook(() => useInputKeyDown(deps));
const e = createKeyEvent('Enter', { ctrlKey: true, shiftKey: true });

act(() => {
result.current.handleInputKeyDown(e);
});

expect(e.preventDefault).toHaveBeenCalled();
expect(deps.processInput).toHaveBeenCalledWith(undefined, { forceParallel: true });
});

it('does NOT trigger forced parallel in terminal mode', () => {
setActiveSession({ inputMode: 'terminal' });
useSettingsStore.setState({
forcedParallelExecution: true,
shortcuts: {
...useSettingsStore.getState().shortcuts,
forcedParallelSend: {
id: 'forcedParallelSend',
label: 'Forced Parallel Send',
keys: ['Meta', 'Shift', 'Enter'],
},
},
} as any);
const deps = createMockDeps();
const { result } = renderHook(() => useInputKeyDown(deps));
const e = createKeyEvent('Enter', { metaKey: true, shiftKey: true });

act(() => {
result.current.handleInputKeyDown(e);
});

// Should NOT call processInput at all in terminal mode
expect(deps.processInput).not.toHaveBeenCalled();
});

it('does NOT trigger forced parallel when feature is disabled', () => {
setActiveSession({ inputMode: 'ai' });
useSettingsStore.setState({
forcedParallelExecution: false,
shortcuts: {
...useSettingsStore.getState().shortcuts,
forcedParallelSend: {
id: 'forcedParallelSend',
label: 'Forced Parallel Send',
keys: ['Meta', 'Shift', 'Enter'],
},
},
} as any);
const deps = createMockDeps();
const { result } = renderHook(() => useInputKeyDown(deps));
const e = createKeyEvent('Enter', { metaKey: true, shiftKey: true });

act(() => {
result.current.handleInputKeyDown(e);
});

// Should NOT call processInput with forceParallel when feature is disabled
expect(deps.processInput).not.toHaveBeenCalledWith(undefined, { forceParallel: true });
});
Comment thread
coderabbitai[bot] marked this conversation as resolved.

it('respects custom shortcut configuration', () => {
setActiveSession({ inputMode: 'ai' });
useSettingsStore.setState({
forcedParallelExecution: true,
shortcuts: {
...useSettingsStore.getState().shortcuts,
forcedParallelSend: {
id: 'forcedParallelSend',
label: 'Forced Parallel Send',
keys: ['Alt', 'Enter'],
},
},
} as any);
const deps = createMockDeps();
const { result } = renderHook(() => useInputKeyDown(deps));

// Default shortcut (Meta+Shift+Enter) should NOT trigger
const e1 = createKeyEvent('Enter', { metaKey: true, shiftKey: true });
act(() => {
result.current.handleInputKeyDown(e1);
});
expect(deps.processInput).not.toHaveBeenCalledWith(undefined, { forceParallel: true });

// Custom shortcut (Alt+Enter) SHOULD trigger
const e2 = createKeyEvent('Enter', { altKey: true });
act(() => {
result.current.handleInputKeyDown(e2);
});
expect(deps.processInput).toHaveBeenCalledWith(undefined, { forceParallel: true });
});
});

// ============================================================================
// Edge cases
// ============================================================================
Expand Down
Loading