import { render, screen, fireEvent } from '@testing-library/react'; import { describe, it, expect, vi, beforeEach } from 'vitest'; import { DeployFeedbackModal } from '../DeployFeedbackModal'; import type { DeployPanelState } from '@/context/DeployFeedbackContext'; const onPanelClose = vi.fn(); const onMinimize = vi.fn(); let mockStyle: 'modal' | 'inline'; let mockPanelState: DeployPanelState; vi.mock('@/context/DeployFeedbackContext', async (importOriginal) => { const actual = await importOriginal(); return { ...actual, useDeployFeedback: () => ({ panelState: mockPanelState, healthGate: null, logRows: [], lastOutputAt: 0, onTerminalReady: vi.fn(), onTerminalError: vi.fn(), onMessage: vi.fn(), onPanelClose, runWithLog: vi.fn(), minimized: false, setMinimized: vi.fn(), }), }; }); vi.mock('@/hooks/use-deploy-feedback-style', () => ({ useDeployFeedbackStyle: () => [mockStyle, vi.fn()], })); vi.mock('@/components/Terminal', () => ({ default: () =>
})); function panel(over: Partial = {}): DeployPanelState { return { isOpen: true, stackName: 'web', nodeId: null, action: 'update', status: 'streaming', progressUnavailable: false, deploySessionId: 'abc', sessionId: 1, ...over, }; } beforeEach(() => { onPanelClose.mockClear(); onMinimize.mockClear(); mockStyle = 'inline'; mockPanelState = panel(); }); function clickClose() { // Both the header icon and the footer button are named "Close" and route // through the same handler; either click exercises it. fireEvent.click(screen.getAllByRole('button', { name: 'Close' })[0]); } describe('DeployFeedbackModal Inline vs Modal style', () => { it('inline style: closing only hides the modal (keeps the session for the banner)', () => { mockStyle = 'inline'; render(); clickClose(); expect(onMinimize).toHaveBeenCalledTimes(1); expect(onPanelClose).not.toHaveBeenCalled(); }); it('inline style: closing a failed op ends the session (the banner has stepped aside)', () => { mockStyle = 'inline'; mockPanelState = panel({ status: 'failed' }); render(); clickClose(); expect(onPanelClose).toHaveBeenCalledTimes(1); expect(onMinimize).not.toHaveBeenCalled(); }); it('modal style: closing ends the session', () => { mockStyle = 'modal'; render(); clickClose(); expect(onPanelClose).toHaveBeenCalledTimes(1); expect(onMinimize).not.toHaveBeenCalled(); }); it('modal style owns the live terminal; inline style renders no terminal (single socket)', () => { mockStyle = 'modal'; const view = render(); expect(screen.getByTestId('modal-terminal')).toBeInTheDocument(); view.unmount(); mockStyle = 'inline'; render(); expect(screen.queryByTestId('modal-terminal')).toBeNull(); }); });