|
1 | | -import { screen } from '@testing-library/react'; |
| 1 | +import { screen, waitFor } from '@testing-library/react'; |
2 | 2 | import userEvent from '@testing-library/user-event'; |
| 3 | +import { AxiosError } from 'axios'; |
3 | 4 | import Attempts from './Attempts'; |
4 | 5 | import { useAttempts, useResetAttempt } from '../data/apiHook'; |
5 | 6 | import { Attempt } from '../types'; |
6 | | -import { renderWithIntl } from '@src/testUtils'; |
| 7 | +import { renderWithAlertAndIntl } from '@src/testUtils'; |
7 | 8 | import messages from '../messages'; |
8 | 9 |
|
9 | 10 | jest.mock('react-router-dom', () => ({ |
@@ -42,27 +43,88 @@ describe('Attempts', () => { |
42 | 43 | }); |
43 | 44 | }); |
44 | 45 |
|
| 46 | + const clickReset = async () => { |
| 47 | + const actionsButton = screen.getByRole('button', { name: messages.actions.defaultMessage }); |
| 48 | + await userEvent.click(actionsButton); |
| 49 | + const resetButton = screen.getByRole('button', { name: messages.reset.defaultMessage }); |
| 50 | + await userEvent.click(resetButton); |
| 51 | + }; |
| 52 | + |
45 | 53 | it('renders AttemptsList component', () => { |
46 | | - renderWithIntl(<Attempts />); |
| 54 | + renderWithAlertAndIntl(<Attempts />); |
47 | 55 | const usernameCell = screen.getByRole('cell', { name: mockAttempt.user.username }); |
48 | 56 | expect(usernameCell).toBeInTheDocument(); |
49 | 57 | }); |
50 | 58 |
|
51 | 59 | it('calls useResetAttempt with courseId from route params', () => { |
52 | | - renderWithIntl(<Attempts />); |
| 60 | + renderWithAlertAndIntl(<Attempts />); |
53 | 61 | expect(useResetAttempt).toHaveBeenCalledWith('course-v1:edX+Test+2024'); |
54 | 62 | }); |
55 | 63 |
|
56 | 64 | it('calls resetAttempt with username and examId when onReset is triggered', async () => { |
57 | | - renderWithIntl(<Attempts />); |
58 | | - const actionsButton = screen.getByRole('button', { name: messages.actions.defaultMessage }); |
59 | | - await userEvent.click(actionsButton); |
60 | | - const resetButton = screen.getByRole('button', { name: messages.reset.defaultMessage }); |
61 | | - await userEvent.click(resetButton); |
| 65 | + renderWithAlertAndIntl(<Attempts />); |
| 66 | + await clickReset(); |
| 67 | + |
| 68 | + expect(mockResetAttempt).toHaveBeenCalledWith( |
| 69 | + { username: 'testuser', examId: 42 }, |
| 70 | + expect.objectContaining({ |
| 71 | + onSuccess: expect.any(Function), |
| 72 | + onError: expect.any(Function), |
| 73 | + }), |
| 74 | + ); |
| 75 | + }); |
| 76 | + |
| 77 | + it('shows a success toast when reset succeeds', async () => { |
| 78 | + mockResetAttempt.mockImplementation((_params, options) => { |
| 79 | + options.onSuccess(); |
| 80 | + }); |
| 81 | + |
| 82 | + renderWithAlertAndIntl(<Attempts />); |
| 83 | + await clickReset(); |
| 84 | + |
| 85 | + const successMessage = screen.getByText(messages.successOnReset.defaultMessage.replace('{student}', mockAttempt.user.username).replace('{examName}', mockAttempt.examName)); |
| 86 | + |
| 87 | + await waitFor(() => { |
| 88 | + expect(successMessage).toBeInTheDocument(); |
| 89 | + }); |
| 90 | + }); |
| 91 | + |
| 92 | + it('shows an error modal with API detail when reset fails with AxiosError', async () => { |
| 93 | + const axiosError = new AxiosError('Request failed'); |
| 94 | + axiosError.response = { |
| 95 | + data: { detail: 'Student has no active attempt.' }, |
| 96 | + status: 400, |
| 97 | + statusText: 'Bad Request', |
| 98 | + headers: {}, |
| 99 | + config: {} as any, |
| 100 | + }; |
| 101 | + |
| 102 | + mockResetAttempt.mockImplementation((_params, options) => { |
| 103 | + options.onError(axiosError); |
| 104 | + }); |
| 105 | + |
| 106 | + renderWithAlertAndIntl(<Attempts />); |
| 107 | + await clickReset(); |
| 108 | + |
| 109 | + await waitFor(() => { |
| 110 | + expect(screen.getByText('Student has no active attempt.')).toBeInTheDocument(); |
| 111 | + }); |
| 112 | + expect(screen.getByText(messages.close.defaultMessage)).toBeInTheDocument(); |
| 113 | + }); |
| 114 | + |
| 115 | + it('shows a generic error modal when reset fails with a non-Axios error', async () => { |
| 116 | + const genericError = new Error('Something went wrong'); |
| 117 | + |
| 118 | + mockResetAttempt.mockImplementation((_params, options) => { |
| 119 | + options.onError(genericError); |
| 120 | + }); |
| 121 | + |
| 122 | + renderWithAlertAndIntl(<Attempts />); |
| 123 | + await clickReset(); |
62 | 124 |
|
63 | | - expect(mockResetAttempt).toHaveBeenCalledWith({ |
64 | | - username: 'testuser', |
65 | | - examId: 42, |
| 125 | + await waitFor(() => { |
| 126 | + expect(screen.getByText(messages.errorOnReset.defaultMessage)).toBeInTheDocument(); |
66 | 127 | }); |
| 128 | + expect(screen.getByText(messages.close.defaultMessage)).toBeInTheDocument(); |
67 | 129 | }); |
68 | 130 | }); |
0 commit comments