UNPKG

datajunction-ui

Version:
406 lines (330 loc) 10.9 kB
import React from 'react'; import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import { CreateServiceAccountModal } from '../CreateServiceAccountModal'; describe('CreateServiceAccountModal', () => { const mockOnClose = vi.fn(); const mockOnCreate = vi.fn(); beforeEach(() => { vi.clearAllMocks(); }); it('does not render when isOpen is false', () => { render( <CreateServiceAccountModal isOpen={false} onClose={mockOnClose} onCreate={mockOnCreate} />, ); expect( screen.queryByText('Create Service Account'), ).not.toBeInTheDocument(); }); it('renders modal when isOpen is true', () => { render( <CreateServiceAccountModal isOpen={true} onClose={mockOnClose} onCreate={mockOnCreate} />, ); expect(screen.getByText('Create Service Account')).toBeInTheDocument(); expect(screen.getByLabelText('Name')).toBeInTheDocument(); expect(screen.getByText('Cancel')).toBeInTheDocument(); expect(screen.getByText('Create')).toBeInTheDocument(); }); it('calls onClose when close button is clicked', () => { render( <CreateServiceAccountModal isOpen={true} onClose={mockOnClose} onCreate={mockOnCreate} />, ); fireEvent.click(screen.getByTitle('Close')); expect(mockOnClose).toHaveBeenCalled(); }); it('calls onClose when Cancel button is clicked', () => { render( <CreateServiceAccountModal isOpen={true} onClose={mockOnClose} onCreate={mockOnCreate} />, ); fireEvent.click(screen.getByText('Cancel')); expect(mockOnClose).toHaveBeenCalled(); }); it('calls onClose when overlay is clicked', () => { render( <CreateServiceAccountModal isOpen={true} onClose={mockOnClose} onCreate={mockOnCreate} />, ); // Click on overlay (modal-overlay class) const overlay = document.querySelector('.modal-overlay'); fireEvent.click(overlay); expect(mockOnClose).toHaveBeenCalled(); }); it('does not close when modal content is clicked', () => { render( <CreateServiceAccountModal isOpen={true} onClose={mockOnClose} onCreate={mockOnCreate} />, ); const content = document.querySelector('.modal-content'); fireEvent.click(content); expect(mockOnClose).not.toHaveBeenCalled(); }); it('disables Create button when name is empty', () => { render( <CreateServiceAccountModal isOpen={true} onClose={mockOnClose} onCreate={mockOnCreate} />, ); const createButton = screen.getByText('Create'); expect(createButton).toBeDisabled(); }); it('enables Create button when name is entered', () => { render( <CreateServiceAccountModal isOpen={true} onClose={mockOnClose} onCreate={mockOnCreate} />, ); const input = screen.getByLabelText('Name'); fireEvent.change(input, { target: { value: 'my-new-account' } }); const createButton = screen.getByText('Create'); expect(createButton).not.toBeDisabled(); }); it('calls onCreate with trimmed name on submit', async () => { mockOnCreate.mockResolvedValue({ client_id: 'test-id' }); render( <CreateServiceAccountModal isOpen={true} onClose={mockOnClose} onCreate={mockOnCreate} />, ); const input = screen.getByLabelText('Name'); fireEvent.change(input, { target: { value: ' my-account ' } }); fireEvent.click(screen.getByText('Create')); await waitFor(() => { expect(mockOnCreate).toHaveBeenCalledWith('my-account'); }); }); it('shows credentials after successful creation', async () => { const credentials = { name: 'my-account', client_id: 'abc-123', client_secret: 'secret-xyz', }; mockOnCreate.mockResolvedValue(credentials); render( <CreateServiceAccountModal isOpen={true} onClose={mockOnClose} onCreate={mockOnCreate} />, ); const input = screen.getByLabelText('Name'); fireEvent.change(input, { target: { value: 'my-account' } }); fireEvent.click(screen.getByText('Create')); await waitFor(() => { expect(screen.getByText('Service Account Created!')).toBeInTheDocument(); }); expect(screen.getByText('abc-123')).toBeInTheDocument(); expect(screen.getByText('secret-xyz')).toBeInTheDocument(); expect( screen.getByText(/client secret will not be shown again/i), ).toBeInTheDocument(); expect(screen.getByText('Done')).toBeInTheDocument(); }); it('shows alert when creation returns error message', async () => { window.alert = vi.fn(); mockOnCreate.mockResolvedValue({ message: 'Account already exists' }); render( <CreateServiceAccountModal isOpen={true} onClose={mockOnClose} onCreate={mockOnCreate} />, ); const input = screen.getByLabelText('Name'); fireEvent.change(input, { target: { value: 'my-account' } }); fireEvent.click(screen.getByText('Create')); await waitFor(() => { expect(window.alert).toHaveBeenCalledWith('Account already exists'); }); }); it('shows alert when creation throws error', async () => { window.alert = vi.fn(); mockOnCreate.mockRejectedValue(new Error('Network error')); render( <CreateServiceAccountModal isOpen={true} onClose={mockOnClose} onCreate={mockOnCreate} />, ); const input = screen.getByLabelText('Name'); fireEvent.change(input, { target: { value: 'my-account' } }); fireEvent.click(screen.getByText('Create')); await waitFor(() => { expect(window.alert).toHaveBeenCalledWith( 'Failed to create service account', ); }); }); it('shows Creating... while request is in progress', async () => { let resolveCreate; mockOnCreate.mockImplementation( () => new Promise(resolve => (resolveCreate = resolve)), ); render( <CreateServiceAccountModal isOpen={true} onClose={mockOnClose} onCreate={mockOnCreate} />, ); const input = screen.getByLabelText('Name'); fireEvent.change(input, { target: { value: 'my-account' } }); fireEvent.click(screen.getByText('Create')); expect(screen.getByText('Creating...')).toBeInTheDocument(); // Resolve the promise resolveCreate({ client_id: 'test' }); await waitFor(() => { expect(screen.queryByText('Creating...')).not.toBeInTheDocument(); }); }); it('closes and resets state when Done is clicked after creation', async () => { const credentials = { name: 'my-account', client_id: 'abc-123', client_secret: 'secret-xyz', }; mockOnCreate.mockResolvedValue(credentials); render( <CreateServiceAccountModal isOpen={true} onClose={mockOnClose} onCreate={mockOnCreate} />, ); const input = screen.getByLabelText('Name'); fireEvent.change(input, { target: { value: 'my-account' } }); fireEvent.click(screen.getByText('Create')); await waitFor(() => { expect(screen.getByText('Done')).toBeInTheDocument(); }); fireEvent.click(screen.getByText('Done')); expect(mockOnClose).toHaveBeenCalled(); }); it('copies client ID to clipboard when copy button is clicked', async () => { const credentials = { name: 'my-account', client_id: 'abc-123', client_secret: 'secret-xyz', }; mockOnCreate.mockResolvedValue(credentials); Object.assign(navigator, { clipboard: { writeText: vi.fn().mockResolvedValue(), }, }); render( <CreateServiceAccountModal isOpen={true} onClose={mockOnClose} onCreate={mockOnCreate} />, ); const input = screen.getByLabelText('Name'); fireEvent.change(input, { target: { value: 'my-account' } }); fireEvent.click(screen.getByText('Create')); await waitFor(() => { expect(screen.getByText('abc-123')).toBeInTheDocument(); }); const copyButtons = screen.getAllByTitle('Copy'); fireEvent.click(copyButtons[0]); expect(navigator.clipboard.writeText).toHaveBeenCalledWith('abc-123'); }); it('copies client secret to clipboard when copy button is clicked', async () => { const credentials = { name: 'my-account', client_id: 'abc-123', client_secret: 'secret-xyz', }; mockOnCreate.mockResolvedValue(credentials); Object.assign(navigator, { clipboard: { writeText: vi.fn().mockResolvedValue(), }, }); render( <CreateServiceAccountModal isOpen={true} onClose={mockOnClose} onCreate={mockOnCreate} />, ); const input = screen.getByLabelText('Name'); fireEvent.change(input, { target: { value: 'my-account' } }); fireEvent.click(screen.getByText('Create')); await waitFor(() => { expect(screen.getByText('secret-xyz')).toBeInTheDocument(); }); // Click the second copy button (client secret) const copyButtons = screen.getAllByTitle('Copy'); fireEvent.click(copyButtons[1]); expect(navigator.clipboard.writeText).toHaveBeenCalledWith('secret-xyz'); }); it('does not call onCreate when form is submitted with only whitespace', async () => { render( <CreateServiceAccountModal isOpen={true} onClose={mockOnClose} onCreate={mockOnCreate} />, ); const input = screen.getByLabelText('Name'); // Enter only whitespace fireEvent.change(input, { target: { value: ' ' } }); // Get the form and submit it directly const form = document.querySelector('form'); fireEvent.submit(form); // onCreate should not be called for whitespace-only names expect(mockOnCreate).not.toHaveBeenCalled(); }); it('clears name input after successful creation', async () => { const credentials = { name: 'my-account', client_id: 'abc-123', client_secret: 'secret-xyz', }; mockOnCreate.mockResolvedValue(credentials); render( <CreateServiceAccountModal isOpen={true} onClose={mockOnClose} onCreate={mockOnCreate} />, ); const input = screen.getByLabelText('Name'); fireEvent.change(input, { target: { value: 'my-account' } }); fireEvent.click(screen.getByText('Create')); await waitFor(() => { expect(screen.getByText('Service Account Created!')).toBeInTheDocument(); }); // After showing credentials view, the name input is no longer visible // The name was cleared after successful creation (line 21) expect(mockOnCreate).toHaveBeenCalledWith('my-account'); }); });