import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { vi } from 'vitest'; import { http, HttpResponse } from 'msw'; import { withUserProvider } from '@/react/test-utils/withUserProvider'; import { withTestRouter } from '@/react/test-utils/withRouter'; import { withTestQueryProvider } from '@/react/test-utils/withTestQuery'; import { ContainerEngine } from '@/react/portainer/environments/types'; import { server } from '@/setup-tests/server'; import { createMockEnvironment } from '@/react-tools/test-mocks'; import { SecondaryActions } from './SecondaryActions'; const mockRecreateMutate = vi.fn(); vi.mock('@uirouter/react', async (importOriginal: () => Promise) => ({ ...(await importOriginal()), useRouter: vi.fn(() => ({ stateService: { go: vi.fn(), }, })), useCurrentStateAndParams: vi.fn(() => ({ params: { endpointId: 1 }, // Must match environmentId in test props and user authorizations })), })); vi.mock('./queries/useRecreateContainer', () => ({ useRecreateContainer: () => ({ mutate: mockRecreateMutate, isLoading: false, }), })); vi.mock( '@/react/hooks/useUser', async (importOriginal: () => Promise) => { const actual = await importOriginal(); return { ...actual, useIsEdgeAdmin: () => ({ isAdmin: true, }), }; } ); vi.mock('@/react/docker/proxy/queries/useInfo', () => ({ useIsSwarm: () => false, })); const mockConfirmContainerRecreation = vi.fn(); vi.mock('../ConfirmRecreationModal', () => ({ confirmContainerRecreation: (cannotPullImage: boolean) => mockConfirmContainerRecreation(cannotPullImage), })); describe('SecondaryActions', () => { beforeEach(() => { vi.clearAllMocks(); // Setup MSW handler for environment endpoint server.use( http.get('/api/endpoints/:id', () => HttpResponse.json( createMockEnvironment({ Id: 1, ContainerEngine: ContainerEngine.Docker, SecuritySettings: { allowContainerCapabilitiesForRegularUsers: true, allowBindMountsForRegularUsers: true, allowDeviceMappingForRegularUsers: true, allowSysctlSettingForRegularUsers: true, allowHostNamespaceForRegularUsers: true, allowPrivilegedModeForRegularUsers: true, allowVolumeBrowserForRegularUsers: true, allowStackManagementForRegularUsers: true, enableHostManagementFeatures: false, }, }) ) ) ); }); it('should render component when authorized', async () => { renderComponent(); // Wait for component to load environment data and render await waitFor(() => { expect( screen.getByTestId('recreate-container-button') ).toBeInTheDocument(); }); }); it('should show recreate button for Docker containers', async () => { renderComponent(); await waitFor(() => { expect(screen.getByTestId('recreate-container-button')).toBeVisible(); }); }); it('should show duplicate/edit button', async () => { renderComponent(); await waitFor(() => { expect( screen.getByTestId('duplicate-edit-container-button') ).toBeVisible(); }); }); it('should hide recreate button when container has AutoRemove enabled', () => { renderComponent({ containerAutoRemove: true }); expect( screen.queryByTestId('recreate-container-button') ).not.toBeInTheDocument(); }); it('should show confirmation modal and call recreate mutation when confirmed', async () => { const user = userEvent.setup(); mockConfirmContainerRecreation.mockResolvedValue({ pullLatest: true }); renderComponent(); const recreateButton = await screen.findByTestId( 'recreate-container-button' ); await user.click(recreateButton); await waitFor(() => { expect(mockConfirmContainerRecreation).toHaveBeenCalledWith(false); }); await waitFor(() => { expect(mockRecreateMutate).toHaveBeenCalledWith( { environmentId: 1, containerId: 'test-container-id', pullImage: true, nodeName: 'node1', }, expect.objectContaining({ onSuccess: expect.any(Function), }) ); }); }); it('should detect cannotPullImage when image starts with sha256', async () => { const user = userEvent.setup(); mockConfirmContainerRecreation.mockResolvedValue({ pullLatest: false }); renderComponent({ containerImage: 'sha256:1234567890abcdef', }); const recreateButton = await screen.findByTestId( 'recreate-container-button' ); await user.click(recreateButton); await waitFor(() => { expect(mockConfirmContainerRecreation).toHaveBeenCalledWith(true); }); }); it('should not call recreate mutation when confirmation is cancelled', async () => { const user = userEvent.setup(); mockConfirmContainerRecreation.mockResolvedValue(undefined); renderComponent(); const recreateButton = await screen.findByTestId( 'recreate-container-button' ); await user.click(recreateButton); await waitFor(() => { expect(mockConfirmContainerRecreation).toHaveBeenCalled(); }); expect(mockRecreateMutate).not.toHaveBeenCalled(); }); }); function renderComponent( props: Partial> = {} ) { const defaultProps: React.ComponentProps = { environmentId: 1, containerId: 'test-container-id', nodeName: 'node1', containerImage: 'nginx:latest', containerAutoRemove: false, isPortainer: false, ...props, }; const mockUser = { EndpointAuthorizations: { 1: { DockerContainerCreate: true }, }, PortainerAuthorizations: {}, Id: 1, Role: 1, Username: 'mock', UseCache: false, ThemeSettings: { color: 'auto' as const, subtleUpgradeButton: false, }, }; const Wrapper = withTestQueryProvider( withUserProvider(withTestRouter(SecondaryActions), mockUser) ); return render(); }