Skip to content

Commit 86600b7

Browse files
Phase 7 - Add comprehensive test suites for authentication components and services
- Implement test coverage for AuthErrorDisplay, AuthLoadingIndicator, and SignInForm components - Create test suite for useAuthOperations hook with detailed scenario testing - Add comprehensive tests for CognitoAuthService covering various authentication methods - Enhance test mocking for translations, hooks, and external services - Improve error handling and loading state validation in test scenarios
1 parent e383ae9 commit 86600b7

File tree

6 files changed

+946
-47
lines changed

6 files changed

+946
-47
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { render, screen } from '@testing-library/react';
2+
import { vi, expect } from 'vitest';
3+
import AuthErrorDisplay from '../AuthErrorDisplay';
4+
import { AuthError } from 'common/models/auth';
5+
6+
// Mock translations
7+
vi.mock('react-i18next', () => ({
8+
useTranslation: () => ({
9+
t: (key: string) => {
10+
// Return the key as the translation for testing
11+
if (key === 'error.details') return 'Error details';
12+
return key;
13+
}
14+
})
15+
}));
16+
17+
describe('AuthErrorDisplay', () => {
18+
it('should not render when error is null', () => {
19+
const { container } = render(<AuthErrorDisplay error={null} />);
20+
expect(container.firstChild).toEqual(null);
21+
});
22+
23+
it('should not render when error is undefined', () => {
24+
const { container } = render(<AuthErrorDisplay error={undefined} />);
25+
expect(container.firstChild).toEqual(null);
26+
});
27+
28+
it('should render string error message', () => {
29+
const errorMessage = 'Test error message';
30+
render(<AuthErrorDisplay error={errorMessage} />);
31+
32+
const element = screen.getByText(errorMessage);
33+
expect(element).toBeDefined();
34+
});
35+
36+
it('should render AuthError object with message', () => {
37+
const error: AuthError = {
38+
code: 'TestError',
39+
message: 'Test error object message',
40+
name: 'TestError'
41+
};
42+
43+
render(<AuthErrorDisplay error={error} />);
44+
45+
const element = screen.getByText(error.message);
46+
expect(element).toBeDefined();
47+
48+
// Should not show error details by default
49+
const details = screen.queryByText(/Error details/);
50+
expect(details).toEqual(null);
51+
});
52+
53+
it('should render error details when showDetails is true', () => {
54+
const error: AuthError = {
55+
code: 'DetailedError',
56+
message: 'Error with details',
57+
name: 'DetailedError'
58+
};
59+
60+
render(<AuthErrorDisplay error={error} showDetails={true} />);
61+
62+
const messageElement = screen.getByText(error.message);
63+
expect(messageElement).toBeDefined();
64+
65+
const detailsLabel = screen.getByText(/Error details/);
66+
expect(detailsLabel).toBeDefined();
67+
68+
const errorCode = screen.getByText(/DetailedError/);
69+
expect(errorCode).toBeDefined();
70+
});
71+
72+
it('should apply custom className', () => {
73+
const error = 'Test error';
74+
const customClass = 'custom-class';
75+
76+
render(<AuthErrorDisplay error={error} className={customClass} />);
77+
78+
const errorElement = screen.getByTestId('auth-error-display');
79+
expect(errorElement.className).toContain(customClass);
80+
});
81+
82+
it('should use custom testid', () => {
83+
const error = 'Test error';
84+
const customTestId = 'custom-error-display';
85+
86+
render(<AuthErrorDisplay error={error} testid={customTestId} />);
87+
88+
const element = screen.getByTestId(customTestId);
89+
expect(element).toBeDefined();
90+
});
91+
});
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import { render, screen } from '@testing-library/react';
2+
import { vi, expect } from 'vitest';
3+
import AuthLoadingIndicator from '../AuthLoadingIndicator';
4+
5+
// Mock translations
6+
vi.mock('react-i18next', () => ({
7+
useTranslation: () => ({
8+
t: (key: string) => key // Return the key as the translation for testing
9+
})
10+
}));
11+
12+
describe('AuthLoadingIndicator', () => {
13+
it('should not render when loading is false', () => {
14+
const { container } = render(<AuthLoadingIndicator isLoading={false} />);
15+
expect(container.firstChild).toEqual(null);
16+
});
17+
18+
it('should render spinner and default message when loading is true', () => {
19+
render(<AuthLoadingIndicator isLoading={true} />);
20+
21+
// Check that the component is rendered
22+
const loadingElement = screen.getByTestId('auth-loading-indicator');
23+
expect(loadingElement).toBeDefined();
24+
25+
// Check that the spinner is rendered
26+
const spinner = screen.getByTestId('loading-spinner');
27+
expect(spinner).toBeDefined();
28+
29+
// Check that the default message is displayed
30+
const message = screen.getByText('auth.processing');
31+
expect(message).toBeDefined();
32+
});
33+
34+
it('should render with custom message', () => {
35+
const customMessage = 'Custom loading message';
36+
render(<AuthLoadingIndicator isLoading={true} message={customMessage} />);
37+
38+
const message = screen.getByText(customMessage);
39+
expect(message).toBeDefined();
40+
});
41+
42+
it('should apply custom className', () => {
43+
const customClass = 'custom-class';
44+
render(<AuthLoadingIndicator isLoading={true} className={customClass} />);
45+
46+
const loadingElement = screen.getByTestId('auth-loading-indicator');
47+
expect(loadingElement.className).toContain(customClass);
48+
});
49+
50+
it('should use custom testid', () => {
51+
const customTestId = 'custom-loading-indicator';
52+
render(<AuthLoadingIndicator isLoading={true} testid={customTestId} />);
53+
54+
const element = screen.getByTestId(customTestId);
55+
expect(element).toBeDefined();
56+
});
57+
});
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import React from 'react';
2+
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
3+
import { vi, expect } from 'vitest';
4+
import SignInForm from 'pages/Auth/SignIn/components/SignInForm';
5+
6+
// Mock the useTranslation hook
7+
vi.mock('react-i18next', () => ({
8+
useTranslation: () => ({
9+
t: (key: string) => key
10+
})
11+
}));
12+
13+
// Mock the useAuthOperations hook
14+
const mockSignIn = vi.fn();
15+
vi.mock('common/hooks/useAuthOperations', () => ({
16+
useAuthOperations: () => ({
17+
signIn: mockSignIn,
18+
error: null,
19+
isLoading: false
20+
})
21+
}));
22+
23+
// Mock the useProgress hook
24+
vi.mock('common/hooks/useProgress', () => ({
25+
useProgress: () => ({
26+
start: vi.fn(),
27+
done: vi.fn(),
28+
active: false,
29+
progress: 0,
30+
animation: 'ease',
31+
className: '',
32+
startPosition: 0.3,
33+
initialPosition: 0.1
34+
})
35+
}));
36+
37+
describe('SignInForm', () => {
38+
beforeEach(() => {
39+
mockSignIn.mockReset();
40+
});
41+
42+
it('renders the form', () => {
43+
render(<SignInForm />);
44+
45+
const emailInput = screen.getByLabelText(/auth.email/i);
46+
const passwordInput = screen.getByLabelText(/auth.password/i);
47+
const submitButton = screen.getByRole('button', { name: /auth.signIn/i });
48+
49+
expect(emailInput).toBeDefined();
50+
expect(passwordInput).toBeDefined();
51+
expect(submitButton).toBeDefined();
52+
});
53+
54+
it('validates email and password inputs', async () => {
55+
render(<SignInForm />);
56+
57+
const emailInput = screen.getByLabelText(/auth.email/i);
58+
const passwordInput = screen.getByLabelText(/auth.password/i);
59+
const submitButton = screen.getByRole('button', { name: /auth.signIn/i });
60+
61+
// Submit with empty fields
62+
fireEvent.click(submitButton);
63+
64+
await waitFor(() => {
65+
// Should show validation errors
66+
expect(screen.getByText(/auth.validation.emailRequired/i)).toBeDefined();
67+
expect(screen.getByText(/auth.validation.passwordRequired/i)).toBeDefined();
68+
});
69+
70+
// Enter invalid email format
71+
fireEvent.change(emailInput, { target: { value: 'invalid-email' } });
72+
fireEvent.click(submitButton);
73+
74+
await waitFor(() => {
75+
expect(screen.getByText(/auth.validation.emailFormat/i)).toBeDefined();
76+
});
77+
78+
// Enter valid email but short password
79+
fireEvent.change(emailInput, { target: { value: '[email protected]' } });
80+
fireEvent.change(passwordInput, { target: { value: '123' } });
81+
fireEvent.click(submitButton);
82+
83+
await waitFor(() => {
84+
expect(screen.getByText(/auth.validation.passwordLength/i)).toBeDefined();
85+
});
86+
});
87+
88+
it('submits the form with valid data', async () => {
89+
render(<SignInForm />);
90+
91+
const emailInput = screen.getByLabelText(/auth.email/i);
92+
const passwordInput = screen.getByLabelText(/auth.password/i);
93+
const submitButton = screen.getByRole('button', { name: /auth.signIn/i });
94+
95+
// Enter valid credentials
96+
fireEvent.change(emailInput, { target: { value: '[email protected]' } });
97+
fireEvent.change(passwordInput, { target: { value: 'Password123' } });
98+
fireEvent.click(submitButton);
99+
100+
await waitFor(() => {
101+
expect(mockSignIn).toHaveBeenCalledWith('[email protected]', 'Password123');
102+
});
103+
});
104+
105+
it('applies custom className', () => {
106+
const customClass = 'custom-form-class';
107+
render(<SignInForm className={customClass} />);
108+
109+
const form = screen.getByTestId('sign-in-form');
110+
expect(form.className).toContain(customClass);
111+
});
112+
113+
it('accepts custom testid', () => {
114+
const customTestId = 'custom-sign-in-form';
115+
render(<SignInForm testid={customTestId} />);
116+
117+
const form = screen.getByTestId(customTestId);
118+
expect(form).toBeDefined();
119+
});
120+
});

0 commit comments

Comments
 (0)