From 28e914ae1c82c7e9601803b84c20067d42ce9934 Mon Sep 17 00:00:00 2001 From: Brayan Ceron Date: Mon, 5 Aug 2024 16:30:23 -0500 Subject: [PATCH] test: solve some issues with some failing tests --- jest.config.js | 1 + .../course-apps/proctoring/Settings.test.jsx | 296 ++++++--------- .../header-buttons/HeaderButtons.test.jsx | 8 +- .../ChecklistSection/ChecklistItemComment.jsx | 7 +- src/course-rerun/CourseRerun.test.jsx | 12 +- .../files-page/FilesPage.test.jsx | 343 ++++++++---------- .../generic/table-components/GalleryCard.jsx | 2 +- .../videos-page/VideosPage.test.jsx | 284 ++++++--------- .../TranscriptSettings.test.jsx | 192 ++++------ .../CreateOrRerunCourseForm.test.jsx | 43 +-- .../modal-dropzone/ModalDropzone.test.jsx | 2 +- .../discussions/DiscussionsSettings.test.jsx | 180 ++++----- .../apps/shared/DiscussionRestriction.jsx | 2 +- .../RestrictDatesInput.jsx | 2 +- .../RestrictionSchedules.jsx | 2 +- .../discussions/app-list/AppList.jsx | 2 +- .../ScheduleAndDetails.test.jsx | 23 +- src/search-modal/SearchUI.test.jsx | 6 +- src/studio-home/StudioHome.test.jsx | 6 +- .../CollapsibleStateWithAction.test.jsx | 2 +- .../OrganizationSection.test.jsx | 2 +- .../course-item/CourseItem.test.jsx | 2 +- .../__snapshots__/index.test.jsx.snap | 22 +- src/taxonomy/TaxonomyLayout.test.jsx | 2 +- src/taxonomy/TaxonomyListPage.test.jsx | 8 +- .../taxonomy-card/TaxonomyCard.test.jsx | 2 +- .../TaxonomyDetailPage.test.jsx | 6 +- .../TaxonomyDetailSideCard.test.jsx | 2 +- 28 files changed, 632 insertions(+), 829 deletions(-) diff --git a/jest.config.js b/jest.config.js index 0af1e62af2..d6d17965b0 100644 --- a/jest.config.js +++ b/jest.config.js @@ -16,4 +16,5 @@ module.exports = createConfig('jest', { modulePathIgnorePatterns: [ '/src/pages-and-resources/utils.test.jsx', ], + testTimeout: 30000, }); diff --git a/plugins/course-apps/proctoring/Settings.test.jsx b/plugins/course-apps/proctoring/Settings.test.jsx index bd544ebb3b..46c7e7cac8 100644 --- a/plugins/course-apps/proctoring/Settings.test.jsx +++ b/plugins/course-apps/proctoring/Settings.test.jsx @@ -103,9 +103,7 @@ describe('ProctoredExamSettings', () => { screen.getByDisplayValue('mockproc'); }); const selectElement = screen.getByDisplayValue('mockproc'); - await act(async () => { - fireEvent.change(selectElement, { target: { value: 'proctortrack' } }); - }); + fireEvent.change(selectElement, { target: { value: 'proctortrack' } }); const zendeskTicketInput = screen.getByTestId('createZendeskTicketsNo'); expect(zendeskTicketInput.checked).toEqual(true); }); @@ -115,9 +113,7 @@ describe('ProctoredExamSettings', () => { screen.getByDisplayValue('mockproc'); }); const selectElement = screen.getByDisplayValue('mockproc'); - await act(async () => { - fireEvent.change(selectElement, { target: { value: 'software_secure' } }); - }); + fireEvent.change(selectElement, { target: { value: 'software_secure' } }); const zendeskTicketInput = screen.getByTestId('createZendeskTicketsYes'); expect(zendeskTicketInput.checked).toEqual(true); }); @@ -127,9 +123,7 @@ describe('ProctoredExamSettings', () => { screen.getByDisplayValue('mockproc'); }); const selectElement = screen.getByDisplayValue('mockproc'); - await act(async () => { - fireEvent.change(selectElement, { target: { value: 'mockproc' } }); - }); + fireEvent.change(selectElement, { target: { value: 'mockproc' } }); const zendeskTicketInput = screen.getByTestId('createZendeskTicketsYes'); expect(zendeskTicketInput.checked).toEqual(true); }); @@ -176,9 +170,7 @@ describe('ProctoredExamSettings', () => { let enabledProctoredExamCheck = screen.getAllByLabelText('Proctored exams', { exact: false })[0]; expect(enabledProctoredExamCheck.checked).toEqual(true); - await act(async () => { - fireEvent.click(enabledProctoredExamCheck, { target: { value: false } }); - }); + fireEvent.click(enabledProctoredExamCheck, { target: { value: false } }); enabledProctoredExamCheck = screen.getByLabelText('Proctored exams'); expect(enabledProctoredExamCheck.checked).toEqual(false); expect(screen.queryByText('Allow opting out of proctored exams')).toBeNull(); @@ -193,9 +185,7 @@ describe('ProctoredExamSettings', () => { screen.getByDisplayValue('mockproc'); }); const selectElement = screen.getByDisplayValue('mockproc'); - await act(async () => { - fireEvent.change(selectElement, { target: { value: 'test_lti' } }); - }); + fireEvent.change(selectElement, { target: { value: 'test_lti' } }); expect(screen.queryByTestId('allowOptingOutRadio')).toBeNull(); expect(screen.queryByTestId('createZendeskTicketsYes')).toBeNull(); expect(screen.queryByTestId('createZendeskTicketsNo')).toBeNull(); @@ -237,13 +227,9 @@ describe('ProctoredExamSettings', () => { screen.getByDisplayValue('proctortrack'); }); const selectEscalationEmailElement = screen.getByDisplayValue('test@example.com'); - await act(async () => { - fireEvent.change(selectEscalationEmailElement, { target: { value: '' } }); - }); + fireEvent.change(selectEscalationEmailElement, { target: { value: '' } }); const selectButton = screen.getByTestId('submissionButton'); - await act(async () => { - fireEvent.click(selectButton); - }); + fireEvent.click(selectButton); // verify alert content and focus management const escalationEmailError = screen.getByTestId('escalationEmailError'); @@ -252,9 +238,7 @@ describe('ProctoredExamSettings', () => { // verify alert link links to offending input const errorLink = screen.getByTestId('escalationEmailErrorLink'); - await act(async () => { - fireEvent.click(errorLink); - }); + fireEvent.click(errorLink); const escalationEmailInput = screen.getByTestId('escalationEmail'); expect(document.activeElement).toEqual(escalationEmailInput); }); @@ -265,18 +249,12 @@ describe('ProctoredExamSettings', () => { }); const selectElement = screen.getByDisplayValue('proctortrack'); - await act(async () => { - fireEvent.change(selectElement, { target: { value: provider } }); - }); + fireEvent.change(selectElement, { target: { value: provider } }); const selectEscalationEmailElement = screen.getByDisplayValue('test@example.com'); - await act(async () => { - fireEvent.change(selectEscalationEmailElement, { target: { value: 'foo.bar' } }); - }); - const selectButton = screen.getByTestId('submissionButton'); - await act(async () => { - fireEvent.click(selectButton); - }); + fireEvent.change(selectEscalationEmailElement, { target: { value: 'foo.bar' } }); + const proctoringForm = screen.getByTestId('proctoringForm'); + fireEvent.submit(proctoringForm); // verify alert content and focus management const escalationEmailError = screen.getByTestId('escalationEmailError'); @@ -286,9 +264,7 @@ describe('ProctoredExamSettings', () => { // verify alert link links to offending input const errorLink = screen.getByTestId('escalationEmailErrorLink'); - await act(async () => { - fireEvent.click(errorLink); - }); + fireEvent.click(errorLink); const escalationEmailInput = screen.getByTestId('escalationEmail'); expect(document.activeElement).toEqual(escalationEmailInput); }); @@ -298,15 +274,11 @@ describe('ProctoredExamSettings', () => { screen.getByDisplayValue('proctortrack'); }); const selectEscalationEmailElement = screen.getByDisplayValue('test@example.com'); - await act(async () => { - fireEvent.change(selectEscalationEmailElement, { target: { value: 'foo.bar' } }); - }); + fireEvent.change(selectEscalationEmailElement, { target: { value: 'foo.bar' } }); const enableProctoringElement = screen.getByText('Proctored exams'); - await act(async () => fireEvent.click(enableProctoringElement)); + fireEvent.click(enableProctoringElement); const selectButton = screen.getByTestId('submissionButton'); - await act(async () => { - fireEvent.click(selectButton); - }); + fireEvent.click(selectButton); // verify alert content and focus management const escalationEmailError = screen.getByTestId('escalationEmailError'); @@ -320,24 +292,22 @@ describe('ProctoredExamSettings', () => { screen.getByDisplayValue('proctortrack'); }); const selectEscalationEmailElement = screen.getByDisplayValue('test@example.com'); - await act(async () => { - fireEvent.change(selectEscalationEmailElement, { target: { value: '' } }); - }); + fireEvent.change(selectEscalationEmailElement, { target: { value: '' } }); const enableProctoringElement = screen.getByText('Proctored exams'); - await act(async () => fireEvent.click(enableProctoringElement)); + fireEvent.click(enableProctoringElement); const selectButton = screen.getByTestId('submissionButton'); - await act(async () => { - fireEvent.click(selectButton); - }); + fireEvent.click(selectButton); // verify there is no escalation email alert, and focus has been set on save success alert expect(screen.queryByTestId('escalationEmailError')).toBeNull(); - const errorAlert = screen.getByTestId('saveSuccess'); - expect(errorAlert.textContent).toEqual( - expect.stringContaining('Proctored exam settings saved successfully.'), - ); - expect(document.activeElement).toEqual(errorAlert); + await waitFor(() => { + const errorAlert = screen.getByTestId('saveSuccess'); + expect(errorAlert.textContent).toEqual( + expect.stringContaining('Proctored exam settings saved successfully.'), + ); + expect(document.activeElement).toEqual(errorAlert); + }); }); it(`Has no error when valid proctoring escalation email is provided with ${provider} selected`, async () => { @@ -345,22 +315,20 @@ describe('ProctoredExamSettings', () => { screen.getByDisplayValue('proctortrack'); }); const selectEscalationEmailElement = screen.getByDisplayValue('test@example.com'); - await act(async () => { - fireEvent.change(selectEscalationEmailElement, { target: { value: 'foo@bar.com' } }); - }); + fireEvent.change(selectEscalationEmailElement, { target: { value: 'foo@bar.com' } }); const selectButton = screen.getByTestId('submissionButton'); - await act(async () => { - fireEvent.click(selectButton); - }); + fireEvent.click(selectButton); // verify there is no escalation email alert, and focus has been set on save success alert expect(screen.queryByTestId('escalationEmailError')).toBeNull(); - const errorAlert = screen.getByTestId('saveSuccess'); - expect(errorAlert.textContent).toEqual( - expect.stringContaining('Proctored exam settings saved successfully.'), - ); - expect(document.activeElement).toEqual(errorAlert); + await waitFor(() => { + const errorAlert = screen.getByTestId('saveSuccess'); + expect(errorAlert.textContent).toEqual( + expect.stringContaining('Proctored exam settings saved successfully.'), + ); + expect(document.activeElement).toEqual(errorAlert); + }); }); it(`Escalation email field hidden when proctoring backend is not ${provider}`, async () => { @@ -370,9 +338,7 @@ describe('ProctoredExamSettings', () => { const proctoringBackendSelect = screen.getByDisplayValue('proctortrack'); const selectEscalationEmailElement = screen.getByTestId('escalationEmail'); expect(selectEscalationEmailElement.value).toEqual('test@example.com'); - await act(async () => { - fireEvent.change(proctoringBackendSelect, { target: { value: 'software_secure' } }); - }); + fireEvent.change(proctoringBackendSelect, { target: { value: 'software_secure' } }); expect(screen.queryByTestId('escalationEmail')).toBeNull(); }); @@ -382,13 +348,9 @@ describe('ProctoredExamSettings', () => { }); const proctoringBackendSelect = screen.getByDisplayValue('proctortrack'); let selectEscalationEmailElement = screen.getByTestId('escalationEmail'); - await act(async () => { - fireEvent.change(proctoringBackendSelect, { target: { value: 'software_secure' } }); - }); + fireEvent.change(proctoringBackendSelect, { target: { value: 'software_secure' } }); expect(screen.queryByTestId('escalationEmail')).toBeNull(); - await act(async () => { - fireEvent.change(proctoringBackendSelect, { target: { value: 'proctortrack' } }); - }); + fireEvent.change(proctoringBackendSelect, { target: { value: 'proctortrack' } }); expect(screen.queryByTestId('escalationEmail')).toBeDefined(); selectEscalationEmailElement = screen.getByTestId('escalationEmail'); expect(selectEscalationEmailElement.value).toEqual('test@example.com'); @@ -399,12 +361,8 @@ describe('ProctoredExamSettings', () => { screen.getByDisplayValue('proctortrack'); }); const selectEscalationEmailElement = screen.getByDisplayValue('test@example.com'); - await act(async () => { - fireEvent.change(selectEscalationEmailElement, { target: { value: '' } }); - }); - await act(async () => { - fireEvent.submit(selectEscalationEmailElement); - }); + fireEvent.change(selectEscalationEmailElement, { target: { value: '' } }); + fireEvent.submit(selectEscalationEmailElement); // if the error appears, the form has been submitted expect(screen.getByTestId('escalationEmailError')).toBeDefined(); }); @@ -572,7 +530,7 @@ describe('ProctoredExamSettings', () => { await act(async () => { render(intlWrapper()); // This expectation is _inside_ the `act` intentionally, so that it executes immediately. - const spinner = screen.getByRole('status'); + const spinner = await screen.findByRole('status'); expect(spinner.textContent).toEqual('Loading...'); }); }); @@ -628,9 +586,7 @@ describe('ProctoredExamSettings', () => { await act(async () => render(intlWrapper())); let submitButton = screen.getByTestId('submissionButton'); expect(screen.queryByTestId('saveInProgress')).toBeFalsy(); - act(() => { - fireEvent.click(submitButton); - }); + fireEvent.click(submitButton); submitButton = screen.getByTestId('submissionButton'); expect(submitButton).toHaveAttribute('disabled'); @@ -640,19 +596,13 @@ describe('ProctoredExamSettings', () => { await act(async () => render(intlWrapper())); // Make a change to the provider to proctortrack and set the email const selectElement = screen.getByDisplayValue('mockproc'); - await act(async () => { - fireEvent.change(selectElement, { target: { value: 'proctortrack' } }); - }); + fireEvent.change(selectElement, { target: { value: 'proctortrack' } }); const escalationEmail = screen.getByTestId('escalationEmail'); expect(escalationEmail.value).toEqual('test@example.com'); - await act(async () => { - fireEvent.change(escalationEmail, { target: { value: 'proctortrack@example.com' } }); - }); + fireEvent.change(escalationEmail, { target: { value: 'proctortrack@example.com' } }); expect(escalationEmail.value).toEqual('proctortrack@example.com'); const submitButton = screen.getByTestId('submissionButton'); - await act(async () => { - fireEvent.click(submitButton); - }); + fireEvent.click(submitButton); expect(axiosMock.history.post.length).toBe(1); expect(JSON.parse(axiosMock.history.post[0].data)).toEqual({ proctored_exam_settings: { @@ -664,11 +614,13 @@ describe('ProctoredExamSettings', () => { }, }); - const errorAlert = screen.getByTestId('saveSuccess'); - expect(errorAlert.textContent).toEqual( - expect.stringContaining('Proctored exam settings saved successfully.'), - ); - expect(document.activeElement).toEqual(errorAlert); + await waitFor(() => { + const errorAlert = screen.getByTestId('saveSuccess'); + expect(errorAlert.textContent).toEqual( + expect.stringContaining('Proctored exam settings saved successfully.'), + ); + expect(document.activeElement).toEqual(errorAlert); + }); }); it('Makes API call successfully without proctoring_escalation_email if not proctortrack', async () => { @@ -678,9 +630,7 @@ describe('ProctoredExamSettings', () => { expect(screen.getByDisplayValue('mockproc')).toBeDefined(); const submitButton = screen.getByTestId('submissionButton'); - await act(async () => { - fireEvent.click(submitButton); - }); + fireEvent.click(submitButton); expect(axiosMock.history.post.length).toBe(1); expect(JSON.parse(axiosMock.history.post[0].data)).toEqual({ proctored_exam_settings: { @@ -691,32 +641,28 @@ describe('ProctoredExamSettings', () => { }, }); - const errorAlert = screen.getByTestId('saveSuccess'); - expect(errorAlert.textContent).toEqual( - expect.stringContaining('Proctored exam settings saved successfully.'), - ); - expect(document.activeElement).toEqual(errorAlert); + await waitFor(() => { + const errorAlert = screen.getByTestId('saveSuccess'); + expect(errorAlert.textContent).toEqual( + expect.stringContaining('Proctored exam settings saved successfully.'), + ); + expect(document.activeElement).toEqual(errorAlert); + }); }); it('Successfully updates exam configuration and studio provider is set to "lti_external" for lti providers', async () => { await act(async () => render(intlWrapper())); // Make a change to the provider to test_lti and set the email const selectElement = screen.getByDisplayValue('mockproc'); - await act(async () => { - fireEvent.change(selectElement, { target: { value: 'test_lti' } }); - }); + fireEvent.change(selectElement, { target: { value: 'test_lti' } }); const escalationEmail = screen.getByTestId('escalationEmail'); expect(escalationEmail.value).toEqual('test@example.com'); - await act(async () => { - fireEvent.change(escalationEmail, { target: { value: 'test_lti@example.com' } }); - }); + fireEvent.change(escalationEmail, { target: { value: 'test_lti@example.com' } }); expect(escalationEmail.value).toEqual('test_lti@example.com'); const submitButton = screen.getByTestId('submissionButton'); - await act(async () => { - fireEvent.click(submitButton); - }); + fireEvent.click(submitButton); // update exam service config expect(axiosMock.history.patch.length).toBe(1); @@ -736,19 +682,19 @@ describe('ProctoredExamSettings', () => { }, }); - const errorAlert = screen.getByTestId('saveSuccess'); - expect(errorAlert.textContent).toEqual( - expect.stringContaining('Proctored exam settings saved successfully.'), - ); - expect(document.activeElement).toEqual(errorAlert); + await waitFor(() => { + const errorAlert = screen.getByTestId('saveSuccess'); + expect(errorAlert.textContent).toEqual( + expect.stringContaining('Proctored exam settings saved successfully.'), + ); + expect(document.activeElement).toEqual(errorAlert); + }); }); it('Sets exam service provider to null if a non-lti provider is selected', async () => { await act(async () => render(intlWrapper())); const submitButton = screen.getByTestId('submissionButton'); - await act(async () => { - fireEvent.click(submitButton); - }); + fireEvent.click(submitButton); // update exam service config expect(axiosMock.history.patch.length).toBe(1); expect(JSON.parse(axiosMock.history.patch[0].data)).toEqual({ @@ -766,11 +712,13 @@ describe('ProctoredExamSettings', () => { }, }); - const errorAlert = screen.getByTestId('saveSuccess'); - expect(errorAlert.textContent).toEqual( - expect.stringContaining('Proctored exam settings saved successfully.'), - ); - expect(document.activeElement).toEqual(errorAlert); + await waitFor(() => { + const errorAlert = screen.getByTestId('saveSuccess'); + expect(errorAlert.textContent).toEqual( + expect.stringContaining('Proctored exam settings saved successfully.'), + ); + expect(document.activeElement).toEqual(errorAlert); + }); }); it('Does not update exam service if lti is not enabled in studio', async () => { @@ -790,9 +738,7 @@ describe('ProctoredExamSettings', () => { await act(async () => render(intlWrapper())); const submitButton = screen.getByTestId('submissionButton'); - await act(async () => { - fireEvent.click(submitButton); - }); + fireEvent.click(submitButton); // does not update exam service config expect(axiosMock.history.patch.length).toBe(0); // does update studio @@ -806,11 +752,13 @@ describe('ProctoredExamSettings', () => { }, }); - const errorAlert = screen.getByTestId('saveSuccess'); - expect(errorAlert.textContent).toEqual( - expect.stringContaining('Proctored exam settings saved successfully.'), - ); - expect(document.activeElement).toEqual(errorAlert); + await waitFor(() => { + const errorAlert = screen.getByTestId('saveSuccess'); + expect(errorAlert.textContent).toEqual( + expect.stringContaining('Proctored exam settings saved successfully.'), + ); + expect(document.activeElement).toEqual(errorAlert); + }); }); it('Makes studio API call generated error', async () => { @@ -820,15 +768,15 @@ describe('ProctoredExamSettings', () => { await act(async () => render(intlWrapper())); const submitButton = screen.getByTestId('submissionButton'); - await act(async () => { - fireEvent.click(submitButton); - }); + fireEvent.click(submitButton); expect(axiosMock.history.post.length).toBe(1); - const errorAlert = screen.getByTestId('saveError'); - expect(errorAlert.textContent).toEqual( - expect.stringContaining('We encountered a technical error while trying to save proctored exam settings'), - ); - expect(document.activeElement).toEqual(errorAlert); + await waitFor(() => { + const errorAlert = screen.getByTestId('saveError'); + expect(errorAlert.textContent).toEqual( + expect.stringContaining('We encountered a technical error while trying to save proctored exam settings'), + ); + expect(document.activeElement).toEqual(errorAlert); + }); }); it('Makes exams API call generated error', async () => { @@ -838,15 +786,15 @@ describe('ProctoredExamSettings', () => { await act(async () => render(intlWrapper())); const submitButton = screen.getByTestId('submissionButton'); - await act(async () => { - fireEvent.click(submitButton); - }); + fireEvent.click(submitButton); expect(axiosMock.history.post.length).toBe(1); - const errorAlert = screen.getByTestId('saveError'); - expect(errorAlert.textContent).toEqual( - expect.stringContaining('We encountered a technical error while trying to save proctored exam settings'), - ); - expect(document.activeElement).toEqual(errorAlert); + await waitFor(() => { + const errorAlert = screen.getByTestId('saveError'); + expect(errorAlert.textContent).toEqual( + expect.stringContaining('We encountered a technical error while trying to save proctored exam settings'), + ); + expect(document.activeElement).toEqual(errorAlert); + }); }); it('Manages focus correctly after different save statuses', async () => { @@ -857,30 +805,30 @@ describe('ProctoredExamSettings', () => { await act(async () => render(intlWrapper())); const submitButton = screen.getByTestId('submissionButton'); - await act(async () => { - fireEvent.click(submitButton); - }); + fireEvent.click(submitButton); expect(axiosMock.history.post.length).toBe(1); - const errorAlert = screen.getByTestId('saveError'); - expect(errorAlert.textContent).toEqual( - expect.stringContaining('We encountered a technical error while trying to save proctored exam settings'), - ); - expect(document.activeElement).toEqual(errorAlert); + await waitFor(() => { + const errorAlert = screen.getByTestId('saveError'); + expect(errorAlert.textContent).toEqual( + expect.stringContaining('We encountered a technical error while trying to save proctored exam settings'), + ); + expect(document.activeElement).toEqual(errorAlert); + }); // now make a call that will allow for a successful save axiosMock.onPost( StudioApiService.getProctoredExamSettingsUrl(defaultProps.courseId), ).reply(200, 'success'); - await act(async () => { - fireEvent.click(submitButton); - }); + fireEvent.click(submitButton); expect(axiosMock.history.post.length).toBe(2); - const successAlert = screen.getByTestId('saveSuccess'); - expect(successAlert.textContent).toEqual( - expect.stringContaining('Proctored exam settings saved successfully.'), - ); - expect(document.activeElement).toEqual(successAlert); + await waitFor(() => { + const successAlert = screen.getByTestId('saveSuccess'); + expect(successAlert.textContent).toEqual( + expect.stringContaining('Proctored exam settings saved successfully.'), + ); + expect(document.activeElement).toEqual(successAlert); + }); }); it('Include Zendesk ticket in post request if user is not an admin', async () => { @@ -891,13 +839,9 @@ describe('ProctoredExamSettings', () => { await act(async () => render(intlWrapper())); // Make a change to the proctoring provider const selectElement = screen.getByDisplayValue('mockproc'); - await act(async () => { - fireEvent.change(selectElement, { target: { value: 'proctortrack' } }); - }); + fireEvent.change(selectElement, { target: { value: 'proctortrack' } }); const submitButton = screen.getByTestId('submissionButton'); - await act(async () => { - fireEvent.click(submitButton); - }); + fireEvent.click(submitButton); expect(axiosMock.history.post.length).toBe(1); expect(JSON.parse(axiosMock.history.post[0].data)).toEqual({ proctored_exam_settings: { diff --git a/src/certificates/layout/header-buttons/HeaderButtons.test.jsx b/src/certificates/layout/header-buttons/HeaderButtons.test.jsx index e774774d76..7adf1072d1 100644 --- a/src/certificates/layout/header-buttons/HeaderButtons.test.jsx +++ b/src/certificates/layout/header-buttons/HeaderButtons.test.jsx @@ -59,10 +59,10 @@ describe('HeaderButtons Component', () => { expect(previewLink).toHaveAttribute('href', expect.stringContaining(certificatesDataMock.courseModes[0])); const dropdownButton = getByRole('button', { name: certificatesDataMock.courseModes[0] }); - await userEvent.click(dropdownButton); + userEvent.click(dropdownButton); const verifiedMode = await getByRole('button', { name: certificatesDataMock.courseModes[1] }); - await userEvent.click(verifiedMode); + userEvent.click(verifiedMode); await waitFor(() => { expect(previewLink).toHaveAttribute('href', expect.stringContaining(certificatesDataMock.courseModes[1])); @@ -78,7 +78,7 @@ describe('HeaderButtons Component', () => { const { getByRole, queryByRole } = renderComponent(); const activationButton = getByRole('button', { name: messages.headingActionsActivate.defaultMessage }); - await userEvent.click(activationButton); + userEvent.click(activationButton); axiosMock.onPost( getUpdateCertificateApiUrl(courseId, certificatesDataMock.certificates[0].id), @@ -110,7 +110,7 @@ describe('HeaderButtons Component', () => { const { getByRole, queryByRole } = renderComponent(); const deactivateButton = getByRole('button', { name: messages.headingActionsDeactivate.defaultMessage }); - await userEvent.click(deactivateButton); + userEvent.click(deactivateButton); axiosMock.onPost( getUpdateCertificateApiUrl(courseId, certificatesDataMock.certificates[0].id), diff --git a/src/course-checklist/ChecklistSection/ChecklistItemComment.jsx b/src/course-checklist/ChecklistSection/ChecklistItemComment.jsx index b254a79c16..92fb83ea32 100644 --- a/src/course-checklist/ChecklistSection/ChecklistItemComment.jsx +++ b/src/course-checklist/ChecklistSection/ChecklistItemComment.jsx @@ -79,10 +79,9 @@ const ChecklistItemComment = ({
    {gradedAssignmentsOutsideDateRange.map(assignment => (
  • - + + {assignment.displayName} +
  • ))}
diff --git a/src/course-rerun/CourseRerun.test.jsx b/src/course-rerun/CourseRerun.test.jsx index 9c403b368b..319fde2727 100644 --- a/src/course-rerun/CourseRerun.test.jsx +++ b/src/course-rerun/CourseRerun.test.jsx @@ -79,15 +79,15 @@ describe('', () => { useSelector.mockReturnValue({ organizationLoadingStatus: RequestStatus.IN_PROGRESS }); await act(async () => { - const { getByRole } = render(); - const spinner = getByRole('status'); + const { findByRole } = render(); + const spinner = await findByRole('status'); expect(spinner.textContent).toEqual('Loading...'); }); }); - it('should show footer', () => { - const { getByText } = render(); - expect(getByText('Looking for help with Studio?')).toBeInTheDocument(); - expect(getByText('LMS')).toHaveAttribute('href', process.env.LMS_BASE_URL); + it('should show footer', async () => { + const { findByText } = render(); + expect(await findByText('Looking for help with Studio?')).toBeInTheDocument(); + expect(await findByText('LMS')).toHaveAttribute('href', process.env.LMS_BASE_URL); }); }); diff --git a/src/files-and-videos/files-page/FilesPage.test.jsx b/src/files-and-videos/files-page/FilesPage.test.jsx index 1a91280bc6..f64724cf92 100644 --- a/src/files-and-videos/files-page/FilesPage.test.jsx +++ b/src/files-and-videos/files-page/FilesPage.test.jsx @@ -1,6 +1,5 @@ import { render, - act, fireEvent, screen, waitFor, @@ -71,6 +70,15 @@ const mockStore = async ( } renderComponent(); await executeThunk(fetchAssets(courseId), store.dispatch); + + // Finish loading the expected files into the data table before returning, + // because loading new files can disrupt things like accessing file menus. + if (status === RequestStatus.SUCCESSFUL) { + const numFiles = skipNextPageFetch ? 13 : 15; + await waitFor(() => { + expect(screen.getByText(`Showing ${numFiles} of ${numFiles}`)).toBeInTheDocument(); + }); + } }; const emptyMockStore = async (status) => { @@ -126,15 +134,13 @@ describe('FilesAndUploads', () => { it('should upload a single file', async () => { await emptyMockStore(RequestStatus.SUCCESSFUL); const dropzone = screen.getByTestId('files-dropzone'); - await act(async () => { - axiosMock.onGet(`${getAssetsUrl(courseId)}?display_name=download.png&page_size=1`).reply(200, { assets: [] }); - axiosMock.onPost(getAssetsUrl(courseId)).reply(204, generateNewAssetApiResponse()); - Object.defineProperty(dropzone, 'files', { - value: [file], - }); - fireEvent.drop(dropzone); - await executeThunk(validateAssetFiles(courseId, [file]), store.dispatch); + axiosMock.onGet(`${getAssetsUrl(courseId)}?display_name=download.png&page_size=1`).reply(200, { assets: [] }); + axiosMock.onPost(getAssetsUrl(courseId)).reply(204, generateNewAssetApiResponse()); + Object.defineProperty(dropzone, 'files', { + value: [file], }); + fireEvent.drop(dropzone); + await executeThunk(validateAssetFiles(courseId, [file]), store.dispatch); const addStatus = store.getState().assets.addingStatus; expect(addStatus).toEqual(RequestStatus.SUCCESSFUL); @@ -185,9 +191,7 @@ describe('FilesAndUploads', () => { expect(screen.queryByRole('table')).toBeNull(); const listButton = screen.getByLabelText('List'); - await act(async () => { - fireEvent.click(listButton); - }); + fireEvent.click(listButton); expect(screen.queryByTestId('grid-card-mOckID1')).toBeNull(); expect(screen.getByRole('table')).toBeVisible(); @@ -200,16 +204,13 @@ describe('FilesAndUploads', () => { await mockStore(RequestStatus.SUCCESSFUL); axiosMock.onGet(`${getAssetsUrl(courseId)}?display_name=download.png&page_size=1`).reply(200, { assets: [] }); axiosMock.onPost(getAssetsUrl(courseId)).reply(200, generateNewAssetApiResponse()); - let addFilesButton; + const addFilesButton = screen.getByLabelText('file-input'); + userEvent.upload(addFilesButton, file); + await executeThunk(validateAssetFiles(courseId, [file]), store.dispatch); await waitFor(() => { - addFilesButton = screen.getByLabelText('file-input'); - }); - await act(async () => { - userEvent.upload(addFilesButton, file); - await executeThunk(validateAssetFiles(courseId, [file]), store.dispatch); + const addStatus = store.getState().assets.addingStatus; + expect(addStatus).toEqual(RequestStatus.SUCCESSFUL); }); - const addStatus = store.getState().assets.addingStatus; - expect(addStatus).toEqual(RequestStatus.SUCCESSFUL); }); it('should show duplicate file modal', async () => { @@ -219,14 +220,9 @@ describe('FilesAndUploads', () => { axiosMock.onGet( `${getAssetsUrl(courseId)}?display_name=mOckID6&page_size=1`, ).reply(200, { assets: [{ display_name: 'mOckID6' }] }); - let addFilesButton; - await waitFor(() => { - addFilesButton = screen.getByLabelText('file-input'); - }); - await act(async () => { - userEvent.upload(addFilesButton, file); - await executeThunk(validateAssetFiles(courseId, [file]), store.dispatch); - }); + const addFilesButton = screen.getByLabelText('file-input'); + userEvent.upload(addFilesButton, file); + await executeThunk(validateAssetFiles(courseId, [file]), store.dispatch); expect(screen.getByText(filesPageMessages.overwriteConfirmMessage.defaultMessage)).toBeVisible(); }); @@ -245,26 +241,21 @@ describe('FilesAndUploads', () => { }; axiosMock.onPost(getAssetsUrl(courseId)).reply(200, responseData); - let addFilesButton; - await waitFor(() => { - addFilesButton = screen.getByLabelText('file-input'); - }); - await act(async () => { - userEvent.upload(addFilesButton, file); - await executeThunk(validateAssetFiles(courseId, [file]), store.dispatch); - }); + const addFilesButton = screen.getByLabelText('file-input'); + userEvent.upload(addFilesButton, file); + await executeThunk(validateAssetFiles(courseId, [file]), store.dispatch); const overwriteButton = screen.getByText(filesPageMessages.confirmOverwriteButtonLabel.defaultMessage); - await act(async () => { - fireEvent.click(overwriteButton); - }); - - const assetData = store.getState().models.assets.mOckID6; - const { asset: responseAssetData } = responseData; - const [defaultData] = updateFileValues([camelCaseObject(responseAssetData)]); + fireEvent.click(overwriteButton); expect(screen.queryByText(filesPageMessages.overwriteConfirmMessage.defaultMessage)).toBeNull(); - expect(assetData).toEqual(defaultData); + await waitFor(() => { + const assetData = store.getState().models.assets.mOckID6; + const { asset: responseAssetData } = responseData; + const [defaultData] = updateFileValues([camelCaseObject(responseAssetData)]); + + expect(assetData).toEqual(defaultData); + }); }); it('should keep original file', async () => { @@ -274,19 +265,12 @@ describe('FilesAndUploads', () => { axiosMock.onGet( `${getAssetsUrl(courseId)}?display_name=mOckID6&page_size=1`, ).reply(200, { assets: [{ display_name: 'mOckID6' }] }); - let addFilesButton; - await waitFor(() => { - addFilesButton = screen.getByLabelText('file-input'); - }); - await act(async () => { - userEvent.upload(addFilesButton, file); - await executeThunk(validateAssetFiles(courseId, [file]), store.dispatch); - }); + const addFilesButton = screen.getByLabelText('file-input'); + userEvent.upload(addFilesButton, file); + await executeThunk(validateAssetFiles(courseId, [file]), store.dispatch); const cancelButton = screen.getByText(filesPageMessages.cancelOverwriteButtonLabel.defaultMessage); - await act(async () => { - fireEvent.click(cancelButton); - }); + fireEvent.click(cancelButton); const assetData = store.getState().models.assets.mOckID6; const defaultAssets = generateFetchAssetApiResponse().assets; @@ -299,12 +283,9 @@ describe('FilesAndUploads', () => { it('should have disabled action buttons', async () => { await mockStore(RequestStatus.SUCCESSFUL); - let actionsButton; - await waitFor(() => { - actionsButton = screen.getByText(messages.actionsButtonLabel.defaultMessage); - fireEvent.click(actionsButton); - }); + const actionsButton = await screen.getByText(messages.actionsButtonLabel.defaultMessage); + fireEvent.click(actionsButton); expect(screen.getByText(messages.downloadTitle.defaultMessage).closest('a')).toHaveClass('disabled'); expect(screen.getByText(messages.deleteTitle.defaultMessage).closest('a')).toHaveClass('disabled'); @@ -312,19 +293,14 @@ describe('FilesAndUploads', () => { it('delete button should be enabled and delete selected file', async () => { await mockStore(RequestStatus.SUCCESSFUL); - let selectCardButton; - await waitFor(() => { - [selectCardButton] = screen.getAllByTestId('datatable-select-column-checkbox-cell'); - fireEvent.click(selectCardButton); - }); + const [selectCardButton] = await screen.findAllByTestId('datatable-select-column-checkbox-cell'); + fireEvent.click(selectCardButton); const actionsButton = screen.getByText(messages.actionsButtonLabel.defaultMessage); expect(actionsButton).toBeVisible(); + fireEvent.click(actionsButton); - await waitFor(() => { - fireEvent.click(actionsButton); - }); const deleteButton = screen.getByText(messages.deleteTitle.defaultMessage).closest('a'); expect(deleteButton).not.toHaveClass('disabled'); @@ -332,24 +308,21 @@ describe('FilesAndUploads', () => { fireEvent.click(deleteButton); expect(screen.getByText('Delete mOckID1')).toBeVisible(); - await act(async () => { - userEvent.click(deleteButton); - }); + fireEvent.click(deleteButton); // Wait for the delete confirmation button to appear const confirmDeleteButton = await screen.findByRole('button', { name: messages.deleteFileButtonLabel.defaultMessage, }); - - await act(async () => { - userEvent.click(confirmDeleteButton); - }); + fireEvent.click(confirmDeleteButton); expect(screen.queryByText('Delete mOckID1')).toBeNull(); // Check if the asset is deleted in the store and UI - const deleteStatus = store.getState().assets.deletingStatus; - expect(deleteStatus).toEqual(RequestStatus.SUCCESSFUL); + await waitFor(() => { + const deleteStatus = store.getState().assets.deletingStatus; + expect(deleteStatus).toEqual(RequestStatus.SUCCESSFUL); + }); expect(screen.queryByTestId('grid-card-mOckID1')).toBeNull(); }); @@ -360,9 +333,7 @@ describe('FilesAndUploads', () => { const actionsButton = screen.getByText(messages.actionsButtonLabel.defaultMessage); expect(actionsButton).toBeVisible(); - await waitFor(() => { - fireEvent.click(actionsButton); - }); + fireEvent.click(actionsButton); const downloadButton = screen.getByText(messages.downloadTitle.defaultMessage).closest('a'); expect(downloadButton).not.toHaveClass('disabled'); @@ -378,9 +349,7 @@ describe('FilesAndUploads', () => { const actionsButton = screen.getByText(messages.actionsButtonLabel.defaultMessage); expect(actionsButton).toBeVisible(); - await waitFor(() => { - fireEvent.click(actionsButton); - }); + fireEvent.click(actionsButton); const mockResponseData = { ok: true, blob: () => 'Data' }; const mockFetchResponse = Promise.resolve(mockResponseData); const downloadButton = screen.getByText(messages.downloadTitle.defaultMessage).closest('a'); @@ -396,19 +365,18 @@ describe('FilesAndUploads', () => { const sortsButton = screen.getByText(messages.sortButtonLabel.defaultMessage); expect(sortsButton).toBeVisible(); + fireEvent.click(sortsButton); await waitFor(() => { - fireEvent.click(sortsButton); expect(screen.getByText(messages.sortModalTitleLabel.defaultMessage)).toBeVisible(); }); const sortNameAscendingButton = screen.getByText(messages.sortByNameAscending.defaultMessage); fireEvent.click(sortNameAscendingButton); + fireEvent.click(screen.getByText(messages.applySortButton.defaultMessage)); await waitFor(() => { - fireEvent.click(screen.getByText(messages.applySortButton.defaultMessage)); + expect(screen.queryByText(messages.sortModalTitleLabel.defaultMessage)).toBeNull(); }); - - expect(screen.queryByText(messages.sortModalTitleLabel.defaultMessage)).toBeNull(); }); it('sort button should be enabled and sort files by file size', async () => { @@ -416,30 +384,26 @@ describe('FilesAndUploads', () => { const sortsButton = screen.getByText(messages.sortButtonLabel.defaultMessage); expect(sortsButton).toBeVisible(); + fireEvent.click(sortsButton); await waitFor(() => { - fireEvent.click(sortsButton); expect(screen.getByText(messages.sortModalTitleLabel.defaultMessage)).toBeVisible(); }); const sortBySizeDescendingButton = screen.getByText(messages.sortBySizeDescending.defaultMessage); fireEvent.click(sortBySizeDescendingButton); + fireEvent.click(screen.getByText(messages.applySortButton.defaultMessage)); await waitFor(() => { - fireEvent.click(screen.getByText(messages.applySortButton.defaultMessage)); + expect(screen.queryByText(messages.sortModalTitleLabel.defaultMessage)).toBeNull(); }); - - expect(screen.queryByText(messages.sortModalTitleLabel.defaultMessage)).toBeNull(); }); }); describe('card menu actions', () => { it('should open asset info', async () => { await mockStore(RequestStatus.SUCCESSFUL); - let assetMenuButton; - await waitFor(() => { - [assetMenuButton] = screen.getAllByTestId('file-menu-dropdown-mOckID1'); - }); + const [assetMenuButton] = await screen.getAllByTestId('file-menu-dropdown-mOckID1'); axiosMock.onGet(`${getAssetsUrl(courseId)}mOckID1/usage`) .reply(201, { @@ -450,14 +414,15 @@ describe('FilesAndUploads', () => { }], }, }); + fireEvent.click(within(assetMenuButton).getByLabelText('file-menu-toggle')); + fireEvent.click(screen.getByText('Info')); + + await executeThunk(getUsagePaths({ + courseId, + asset: { id: 'mOckID1', displayName: 'mOckID1' }, + setSelectedRows: jest.fn(), + }), store.dispatch); await waitFor(() => { - fireEvent.click(within(assetMenuButton).getByLabelText('file-menu-toggle')); - fireEvent.click(screen.getByText('Info')); - executeThunk(getUsagePaths({ - courseId, - asset: { id: 'mOckID1', displayName: 'mOckID1' }, - setSelectedRows: jest.fn(), - }), store.dispatch); expect(screen.getAllByLabelText('mOckID1')[0]).toBeVisible(); }); @@ -472,23 +437,23 @@ describe('FilesAndUploads', () => { axiosMock.onPut(`${getAssetsUrl(courseId)}mOckID1`).reply(201, { locked: false }); axiosMock.onGet(`${getAssetsUrl(courseId)}mOckID1/usage`).reply(201, { usage_locations: { mOckID1: [] } }); + fireEvent.click(within(assetMenuButton).getByLabelText('file-menu-toggle')); + fireEvent.click(screen.getByText('Info')); + await executeThunk(getUsagePaths({ + courseId, + asset: { id: 'mOckID1', displayName: 'mOckID1' }, + setSelectedRows: jest.fn(), + }), store.dispatch); await waitFor(() => { - fireEvent.click(within(assetMenuButton).getByLabelText('file-menu-toggle')); - fireEvent.click(screen.getByText('Info')); - executeThunk(getUsagePaths({ - courseId, - asset: { id: 'mOckID1', displayName: 'mOckID1' }, - setSelectedRows: jest.fn(), - }), store.dispatch); expect(screen.getAllByLabelText('mOckID1')[0]).toBeVisible(); - - fireEvent.click(screen.getByLabelText('Checkbox')); - executeThunk(updateAssetLock({ - courseId, - assetId: 'mOckID1', - locked: false, - }), store.dispatch); }); + + fireEvent.click(screen.getByLabelText('Checkbox')); + await executeThunk(updateAssetLock({ + courseId, + assetId: 'mOckID1', + locked: false, + }), store.dispatch); expect(screen.getByText(messages.usageNotInUseMessage.defaultMessage)).toBeVisible(); const updateStatus = store.getState().assets.updatingStatus; @@ -500,18 +465,18 @@ describe('FilesAndUploads', () => { const assetMenuButton = screen.getAllByTestId('file-menu-dropdown-mOckID1')[0]; + axiosMock.onPut(`${getAssetsUrl(courseId)}mOckID1`).reply(201, { locked: false }); + fireEvent.click(within(assetMenuButton).getByLabelText('file-menu-toggle')); + fireEvent.click(screen.getByText('Unlock')); + await executeThunk(updateAssetLock({ + courseId, + assetId: 'mOckID1', + locked: false, + }), store.dispatch); await waitFor(() => { - axiosMock.onPut(`${getAssetsUrl(courseId)}mOckID1`).reply(201, { locked: false }); - fireEvent.click(within(assetMenuButton).getByLabelText('file-menu-toggle')); - fireEvent.click(screen.getByText('Unlock')); - executeThunk(updateAssetLock({ - courseId, - assetId: 'mOckID1', - locked: false, - }), store.dispatch); + const updateStatus = store.getState().assets.updatingStatus; + expect(updateStatus).toEqual(RequestStatus.SUCCESSFUL); }); - const updateStatus = store.getState().assets.updatingStatus; - expect(updateStatus).toEqual(RequestStatus.SUCCESSFUL); }); it('should lock asset', async () => { @@ -519,18 +484,18 @@ describe('FilesAndUploads', () => { const assetMenuButton = screen.getAllByTestId('file-menu-dropdown-mOckID3')[0]; + axiosMock.onPut(`${getAssetsUrl(courseId)}mOckID3`).reply(201, { locked: true }); + fireEvent.click(within(assetMenuButton).getByLabelText('file-menu-toggle')); + fireEvent.click(screen.getByText('Lock')); + await executeThunk(updateAssetLock({ + courseId, + assetId: 'mOckID3', + locked: true, + }), store.dispatch); await waitFor(() => { - axiosMock.onPut(`${getAssetsUrl(courseId)}mOckID3`).reply(201, { locked: true }); - fireEvent.click(within(assetMenuButton).getByLabelText('file-menu-toggle')); - fireEvent.click(screen.getByText('Lock')); - executeThunk(updateAssetLock({ - courseId, - assetId: 'mOckID3', - locked: true, - }), store.dispatch); + const updateStatus = store.getState().assets.updatingStatus; + expect(updateStatus).toEqual(RequestStatus.SUCCESSFUL); }); - const updateStatus = store.getState().assets.updatingStatus; - expect(updateStatus).toEqual(RequestStatus.SUCCESSFUL); }); it('download button should download file', async () => { @@ -538,10 +503,8 @@ describe('FilesAndUploads', () => { const assetMenuButton = screen.getAllByTestId('file-menu-dropdown-mOckID1')[0]; - await waitFor(() => { - fireEvent.click(within(assetMenuButton).getByLabelText('file-menu-toggle')); - fireEvent.click(screen.getByText('Download')); - }); + fireEvent.click(within(assetMenuButton).getByLabelText('file-menu-toggle')); + fireEvent.click(screen.getByText('Download')); expect(saveAs).toHaveBeenCalled(); }); @@ -550,17 +513,18 @@ describe('FilesAndUploads', () => { const assetMenuButton = screen.getAllByTestId('file-menu-dropdown-mOckID1')[0]; + axiosMock.onDelete(`${getAssetsUrl(courseId)}mOckID1`).reply(204); + fireEvent.click(within(assetMenuButton).getByLabelText('file-menu-toggle')); + fireEvent.click(screen.getByTestId('open-delete-confirmation-button')); await waitFor(() => { - axiosMock.onDelete(`${getAssetsUrl(courseId)}mOckID1`).reply(204); - fireEvent.click(within(assetMenuButton).getByLabelText('file-menu-toggle')); - fireEvent.click(screen.getByTestId('open-delete-confirmation-button')); expect(screen.getByText('Delete mOckID1')).toBeVisible(); + }); - fireEvent.click(screen.getByText(messages.deleteFileButtonLabel.defaultMessage)); + fireEvent.click(screen.getByText(messages.deleteFileButtonLabel.defaultMessage)); + await waitFor(() => { expect(screen.queryByText('Delete mOckID1')).toBeNull(); - - executeThunk(deleteAssetFile(courseId, 'mOckID1', 5), store.dispatch); }); + await executeThunk(deleteAssetFile(courseId, 'mOckID1', 5), store.dispatch); const deleteStatus = store.getState().assets.deletingStatus; expect(deleteStatus).toEqual(RequestStatus.SUCCESSFUL); @@ -572,9 +536,7 @@ describe('FilesAndUploads', () => { it('404 intitial fetch should show error', async () => { await mockStore(RequestStatus.FAILED); const { loadingStatus } = store.getState().assets; - await waitFor(() => { - expect(screen.getByText('Error')).toBeVisible(); - }); + expect(screen.getByText('Error')).toBeVisible(); expect(loadingStatus).toEqual(RequestStatus.FAILED); expect(screen.getByText('Failed to load all files.')).toBeVisible(); @@ -583,9 +545,7 @@ describe('FilesAndUploads', () => { it('404 intitial fetch should show error', async () => { await mockStore(RequestStatus.SUCCESSFUL, true); const { loadingStatus } = store.getState().assets; - await waitFor(() => { - expect(screen.getByText('Error')).toBeVisible(); - }); + expect(screen.getByText('Error')).toBeVisible(); expect(loadingStatus).toEqual(RequestStatus.PARTIAL_FAILURE); expect(screen.getByText('Failed to load remaining files.')).toBeVisible(); @@ -597,12 +557,11 @@ describe('FilesAndUploads', () => { axiosMock.onGet(`${getAssetsUrl(courseId)}?display_name=download.png&page_size=1`).reply(200, { assets: [] }); axiosMock.onPost(getAssetsUrl(courseId)).reply(413, { error: errorMessage }); const addFilesButton = screen.getByLabelText('file-input'); - await act(async () => { - userEvent.upload(addFilesButton, file); + userEvent.upload(addFilesButton, file); + await waitFor(() => { + const addStatus = store.getState().assets.addingStatus; + expect(addStatus).toEqual(RequestStatus.FAILED); }); - const addStatus = store.getState().assets.addingStatus; - expect(addStatus).toEqual(RequestStatus.FAILED); - expect(screen.getByText('Error')).toBeVisible(); }); @@ -610,10 +569,8 @@ describe('FilesAndUploads', () => { await mockStore(RequestStatus.SUCCESSFUL); axiosMock.onGet(`${getAssetsUrl(courseId)}?display_name=download.png&page_size=1`).reply(404); const addFilesButton = screen.getByLabelText('file-input'); - await act(async () => { - userEvent.upload(addFilesButton, file); - await executeThunk(addAssetFile(courseId, file, 1), store.dispatch); - }); + userEvent.upload(addFilesButton, file); + await executeThunk(addAssetFile(courseId, file, 1), store.dispatch); const addStatus = store.getState().assets.addingStatus; expect(addStatus).toEqual(RequestStatus.FAILED); @@ -625,10 +582,8 @@ describe('FilesAndUploads', () => { axiosMock.onGet(`${getAssetsUrl(courseId)}?display_name=download.png&page_size=1`).reply(200, { assets: [] }); axiosMock.onPost(getAssetsUrl(courseId)).reply(404); const addFilesButton = screen.getByLabelText('file-input'); - await act(async () => { - userEvent.upload(addFilesButton, file); - await executeThunk(addAssetFile(courseId, file, 1), store.dispatch); - }); + userEvent.upload(addFilesButton, file); + await executeThunk(addAssetFile(courseId, file, 1), store.dispatch); const addStatus = store.getState().assets.addingStatus; expect(addStatus).toEqual(RequestStatus.FAILED); @@ -640,17 +595,18 @@ describe('FilesAndUploads', () => { const assetMenuButton = screen.getAllByTestId('file-menu-dropdown-mOckID3')[0]; + axiosMock.onDelete(`${getAssetsUrl(courseId)}mOckID3`).reply(404); + fireEvent.click(within(assetMenuButton).getByLabelText('file-menu-toggle')); + fireEvent.click(screen.getByTestId('open-delete-confirmation-button')); await waitFor(() => { - axiosMock.onDelete(`${getAssetsUrl(courseId)}mOckID3`).reply(404); - fireEvent.click(within(assetMenuButton).getByLabelText('file-menu-toggle')); - fireEvent.click(screen.getByTestId('open-delete-confirmation-button')); expect(screen.getByText('Delete mOckID3')).toBeVisible(); + }); - fireEvent.click(screen.getByText(messages.deleteFileButtonLabel.defaultMessage)); + fireEvent.click(screen.getByText(messages.deleteFileButtonLabel.defaultMessage)); + await waitFor(() => { expect(screen.queryByText('Delete mOckID3')).toBeNull(); - - executeThunk(deleteAssetFile(courseId, 'mOckID3', 5), store.dispatch); }); + await executeThunk(deleteAssetFile(courseId, 'mOckID3', 5), store.dispatch); const deleteStatus = store.getState().assets.deletingStatus; expect(deleteStatus).toEqual(RequestStatus.FAILED); @@ -665,17 +621,17 @@ describe('FilesAndUploads', () => { const assetMenuButton = screen.getAllByTestId('file-menu-dropdown-mOckID3')[0]; axiosMock.onGet(`${getAssetsUrl(courseId)}mOckID3/usage`).reply(404); + fireEvent.click(within(assetMenuButton).getByLabelText('file-menu-toggle')); + fireEvent.click(screen.getByText('Info')); + await executeThunk(getUsagePaths({ + courseId, + asset: { id: 'mOckID3', displayName: 'mOckID3' }, + setSelectedRows: jest.fn(), + }), store.dispatch); await waitFor(() => { - fireEvent.click(within(assetMenuButton).getByLabelText('file-menu-toggle')); - fireEvent.click(screen.getByText('Info')); - executeThunk(getUsagePaths({ - courseId, - asset: { id: 'mOckID3', displayName: 'mOckID3' }, - setSelectedRows: jest.fn(), - }), store.dispatch); + const { usageStatus } = store.getState().assets; + expect(usageStatus).toEqual(RequestStatus.FAILED); }); - const { usageStatus } = store.getState().assets; - expect(usageStatus).toEqual(RequestStatus.FAILED); }); it('404 lock update should show error', async () => { @@ -683,19 +639,18 @@ describe('FilesAndUploads', () => { const assetMenuButton = screen.getAllByTestId('file-menu-dropdown-mOckID3')[0]; + axiosMock.onPut(`${getAssetsUrl(courseId)}mOckID3`).reply(404); + fireEvent.click(within(assetMenuButton).getByLabelText('file-menu-toggle')); + fireEvent.click(screen.getByText('Lock')); + await executeThunk(updateAssetLock({ + courseId, + assetId: 'mOckID3', + locked: true, + }), store.dispatch); await waitFor(() => { - axiosMock.onPut(`${getAssetsUrl(courseId)}mOckID3`).reply(404); - fireEvent.click(within(assetMenuButton).getByLabelText('file-menu-toggle')); - fireEvent.click(screen.getByText('Lock')); - executeThunk(updateAssetLock({ - courseId, - assetId: 'mOckID3', - locked: true, - }), store.dispatch); + const updateStatus = store.getState().assets.updatingStatus; + expect(updateStatus).toEqual(RequestStatus.FAILED); }); - const updateStatus = store.getState().assets.updatingStatus; - expect(updateStatus).toEqual(RequestStatus.FAILED); - expect(screen.getByText('Error')).toBeVisible(); }); @@ -707,17 +662,15 @@ describe('FilesAndUploads', () => { const actionsButton = screen.getByText(messages.actionsButtonLabel.defaultMessage); expect(actionsButton).toBeVisible(); - await waitFor(() => { - fireEvent.click(actionsButton); - }); + fireEvent.click(actionsButton); const mockResponseData = { ok: false }; const mockFetchResponse = Promise.resolve(mockResponseData); const downloadButton = screen.getByText(messages.downloadTitle.defaultMessage).closest('a'); expect(downloadButton).not.toHaveClass('disabled'); global.fetch = jest.fn().mockImplementation(() => mockFetchResponse); + fireEvent.click(downloadButton); await waitFor(() => { - fireEvent.click(downloadButton); expect(fetch).toHaveBeenCalledTimes(2); }); diff --git a/src/files-and-videos/generic/table-components/GalleryCard.jsx b/src/files-and-videos/generic/table-components/GalleryCard.jsx index 983a533f6d..fa21a182cf 100644 --- a/src/files-and-videos/generic/table-components/GalleryCard.jsx +++ b/src/files-and-videos/generic/table-components/GalleryCard.jsx @@ -104,7 +104,7 @@ GalleryCard.propTypes = { handleOpenDeleteConfirmation: PropTypes.func.isRequired, handleOpenFileInfo: PropTypes.func.isRequired, thumbnailPreview: PropTypes.func.isRequired, - fileType: PropTypes.func.isRequired, + fileType: PropTypes.string.isRequired, }; export default GalleryCard; diff --git a/src/files-and-videos/videos-page/VideosPage.test.jsx b/src/files-and-videos/videos-page/VideosPage.test.jsx index cc1b1f52af..4fc7923c89 100644 --- a/src/files-and-videos/videos-page/VideosPage.test.jsx +++ b/src/files-and-videos/videos-page/VideosPage.test.jsx @@ -70,6 +70,15 @@ const mockStore = async ( renderComponent(); await executeThunk(fetchVideos(courseId), store.dispatch); + + // Finish loading the expected files into the data table before returning, + // because loading new files can disrupt things like accessing file menus. + if (status === RequestStatus.SUCCESSFUL) { + const numFiles = 3; + await waitFor(() => { + expect(screen.getByText(`Showing ${numFiles} of ${numFiles}`)).toBeInTheDocument(); + }); + } }; const emptyMockStore = async (status) => { @@ -127,24 +136,24 @@ describe('Videos page', () => { it('should upload a single file', async () => { await emptyMockStore(RequestStatus.SUCCESSFUL); const dropzone = screen.getByTestId('files-dropzone'); - await act(async () => { - const mockResponseData = { status: '200', ok: true, blob: () => 'Data' }; - const mockFetchResponse = Promise.resolve(mockResponseData); - global.fetch = jest.fn().mockImplementation(() => mockFetchResponse); + const mockResponseData = { status: '200', ok: true, blob: () => 'Data' }; + const mockFetchResponse = Promise.resolve(mockResponseData); + global.fetch = jest.fn().mockImplementation(() => mockFetchResponse); + + axiosMock.onPost(getCourseVideosApiUrl(courseId)).reply(204, generateNewVideoApiResponse()); + axiosMock.onGet(getCourseVideosApiUrl(courseId)).reply(200, generateAddVideoApiResponse()); + Object.defineProperty(dropzone, 'files', { + value: [file], + }); + fireEvent.drop(dropzone); + await executeThunk(addVideoFile(courseId, file, [], { current: [] }), store.dispatch); - axiosMock.onPost(getCourseVideosApiUrl(courseId)).reply(204, generateNewVideoApiResponse()); - axiosMock.onGet(getCourseVideosApiUrl(courseId)).reply(200, generateAddVideoApiResponse()); - Object.defineProperty(dropzone, 'files', { - value: [file], - }); - fireEvent.drop(dropzone); - await executeThunk(addVideoFile(courseId, file, [], { current: [] }), store.dispatch); + await waitFor(() => { + const addStatus = store.getState().videos.addingStatus; + expect(addStatus).toEqual(RequestStatus.SUCCESSFUL); }); - const addStatus = store.getState().videos.addingStatus; - expect(addStatus).toEqual(RequestStatus.SUCCESSFUL); expect(screen.queryByTestId('files-dropzone')).toBeNull(); - expect(screen.getByTestId('files-data-table')).toBeVisible(); }); }); @@ -170,9 +179,7 @@ describe('Videos page', () => { const transcriptSettingsButton = screen.getByText(videoMessages.transcriptSettingsButtonLabel.defaultMessage); expect(transcriptSettingsButton).toBeVisible(); - await act(async () => { - fireEvent.click(transcriptSettingsButton); - }); + fireEvent.click(transcriptSettingsButton); expect(screen.getByLabelText('close settings')).toBeVisible(); }); @@ -200,9 +207,7 @@ describe('Videos page', () => { expect(screen.queryByRole('table')).toBeNull(); const listButton = screen.getByLabelText('List'); - await act(async () => { - fireEvent.click(listButton); - }); + fireEvent.click(listButton); expect(screen.queryByTestId('grid-card-mOckID1')).toBeNull(); expect(screen.getByRole('table')).toBeVisible(); @@ -213,10 +218,8 @@ describe('Videos page', () => { axiosMock.onPost(`${getApiBaseUrl()}/video_images/${courseId}/mOckID1`).reply(200, { image_url: 'url' }); const addThumbnailButton = screen.getByTestId('video-thumbnail-mOckID1'); const thumbnail = new File(['test'], 'sOMEUrl.jpg', { type: 'image/jpg' }); - await act(async () => { - fireEvent.click(addThumbnailButton); - await executeThunk(addVideoThumbnail({ file: thumbnail, videoId: 'mOckID1', courseId }), store.dispatch); - }); + fireEvent.click(addThumbnailButton); + await executeThunk(addVideoThumbnail({ file: thumbnail, videoId: 'mOckID1', courseId }), store.dispatch); const updateStatus = store.getState().videos.updatingStatus; expect(updateStatus).toEqual(RequestStatus.SUCCESSFUL); }); @@ -247,10 +250,8 @@ describe('Videos page', () => { const addFilesButton = screen.getAllByLabelText('file-input')[3]; const { videoIds } = store.getState().videos; - await act(async () => { - userEvent.upload(addFilesButton, file); - await executeThunk(addVideoFile(courseId, file, videoIds, { current: [] }), store.dispatch); - }); + userEvent.upload(addFilesButton, file); + await executeThunk(addVideoFile(courseId, file, videoIds, { current: [] }), store.dispatch); const addStatus = store.getState().videos.addingStatus; expect(addStatus).toEqual(RequestStatus.SUCCESSFUL); }); @@ -270,9 +271,7 @@ describe('Videos page', () => { uploadSpy.mockResolvedValue(new Promise(() => {})); const addFilesButton = screen.getAllByLabelText('file-input')[3]; - act(async () => { - userEvent.upload(addFilesButton, file); - }); + userEvent.upload(addFilesButton, file); await waitFor(() => { const addStatus = store.getState().videos.addingStatus; expect(addStatus).toEqual(RequestStatus.IN_PROGRESS); @@ -293,23 +292,24 @@ describe('Videos page', () => { await mockStore(RequestStatus.SUCCESSFUL); const actionsButton = screen.getByText(messages.actionsButtonLabel.defaultMessage); + fireEvent.click(actionsButton); await waitFor(() => { - fireEvent.click(actionsButton); - }); - expect(screen.getByText(messages.downloadTitle.defaultMessage).closest('a')).toHaveClass('disabled'); + expect(screen.getByText(messages.downloadTitle.defaultMessage).closest('a')).toHaveClass('disabled'); - expect(screen.getByText(messages.deleteTitle.defaultMessage).closest('a')).toHaveClass('disabled'); + expect(screen.getByText(messages.deleteTitle.defaultMessage).closest('a')).toHaveClass('disabled'); + }); }); it('delete button should be enabled and delete selected file', async () => { await mockStore(RequestStatus.SUCCESSFUL); - const selectCardButton = screen.getAllByTestId('datatable-select-column-checkbox-cell')[0]; + + const [selectCardButton] = await screen.findAllByTestId('datatable-select-column-checkbox-cell'); fireEvent.click(selectCardButton); + const actionsButton = screen.getByText(messages.actionsButtonLabel.defaultMessage); + expect(actionsButton).toBeVisible(); + fireEvent.click(actionsButton); - await waitFor(() => { - fireEvent.click(actionsButton); - }); const deleteButton = screen.getByText(messages.deleteTitle.defaultMessage).closest('a'); expect(deleteButton).not.toHaveClass('disabled'); @@ -317,45 +317,22 @@ describe('Videos page', () => { fireEvent.click(deleteButton); expect(screen.getByText('Delete mOckID1.mp4')).toBeVisible(); - await act(async () => { - userEvent.click(deleteButton); - }); + fireEvent.click(deleteButton); // Wait for the delete confirmation button to appear const confirmDeleteButton = await screen.findByRole('button', { name: messages.deleteFileButtonLabel.defaultMessage, }); - - await act(async () => { - userEvent.click(confirmDeleteButton); - }); + fireEvent.click(confirmDeleteButton); expect(screen.queryByText('Delete mOckID1.mp4')).toBeNull(); // Check if the video is deleted in the store and UI - const deleteStatus = store.getState().videos.deletingStatus; - expect(deleteStatus).toEqual(RequestStatus.SUCCESSFUL); - expect(screen.queryByTestId('grid-card-mOckID1')).toBeNull(); - }); - - it('download button should be enabled and download single selected file', async () => { - await mockStore(RequestStatus.SUCCESSFUL); - const selectCardButton = screen.getAllByTestId('datatable-select-column-checkbox-cell')[0]; - fireEvent.click(selectCardButton); - const actionsButton = screen.getByText(messages.actionsButtonLabel.defaultMessage); - await waitFor(() => { - fireEvent.click(actionsButton); - }); - const downloadButton = screen.getByText(messages.downloadTitle.defaultMessage).closest('a'); - expect(downloadButton).not.toHaveClass('disabled'); - - await act(async () => { - fireEvent.click(downloadButton); + const deleteStatus = store.getState().videos.deletingStatus; + expect(deleteStatus).toEqual(RequestStatus.SUCCESSFUL); }); - - const updateStatus = store.getState().videos.updatingStatus; - expect(updateStatus).toEqual(RequestStatus.SUCCESSFUL); + expect(screen.queryByTestId('grid-card-mOckID1')).toBeNull(); }); it('download button should be enabled and download multiple selected files', async () => { @@ -365,20 +342,18 @@ describe('Videos page', () => { fireEvent.click(selectCardButtons[1]); const actionsButton = screen.getByText(messages.actionsButtonLabel.defaultMessage); - await waitFor(() => { - fireEvent.click(actionsButton); - }); + fireEvent.click(actionsButton); axiosMock.onPut(`${getVideosUrl(courseId)}/download`).reply(200, null); const downloadButton = screen.getByText(messages.downloadTitle.defaultMessage).closest('a'); expect(downloadButton).not.toHaveClass('disabled'); - await act(async () => { - fireEvent.click(downloadButton); - }); + fireEvent.click(downloadButton); - const updateStatus = store.getState().videos.updatingStatus; - expect(updateStatus).toEqual(RequestStatus.SUCCESSFUL); + await waitFor(() => { + const updateStatus = store.getState().videos.updatingStatus; + expect(updateStatus).toEqual(RequestStatus.SUCCESSFUL); + }); }); describe('Sort and filter button', () => { @@ -386,9 +361,7 @@ describe('Videos page', () => { await mockStore(RequestStatus.SUCCESSFUL); const sortAndFilterButton = screen.getByText(messages.sortButtonLabel.defaultMessage); - await waitFor(() => { - fireEvent.click(sortAndFilterButton); - }); + fireEvent.click(sortAndFilterButton); }); describe('sort function', () => { @@ -396,22 +369,22 @@ describe('Videos page', () => { const sortNameAscendingButton = screen.getByText(messages.sortByNameAscending.defaultMessage); fireEvent.click(sortNameAscendingButton); + fireEvent.click(screen.getByText(messages.applySortButton.defaultMessage)); + await waitFor(() => { - fireEvent.click(screen.getByText(messages.applySortButton.defaultMessage)); + expect(screen.queryByText(messages.sortModalTitleLabel.defaultMessage)).toBeNull(); }); - - expect(screen.queryByText(messages.sortModalTitleLabel.defaultMessage)).toBeNull(); }); it('sort button should be enabled and sort files by file size', async () => { const sortBySizeDescendingButton = screen.getByText(messages.sortBySizeDescending.defaultMessage); fireEvent.click(sortBySizeDescendingButton); + fireEvent.click(screen.getByText(messages.applySortButton.defaultMessage)); + await waitFor(() => { - fireEvent.click(screen.getByText(messages.applySortButton.defaultMessage)); + expect(screen.queryByText(messages.sortModalTitleLabel.defaultMessage)).toBeNull(); }); - - expect(screen.queryByText(messages.sortModalTitleLabel.defaultMessage)).toBeNull(); }); }); @@ -425,13 +398,12 @@ describe('Videos page', () => { fireEvent.click(notTranscribedCheckboxFilter); fireEvent.click(transcribedCheckboxFilter); + fireEvent.click(screen.getByText(messages.applySortButton.defaultMessage)); + await waitFor(() => { - fireEvent.click(screen.getByText(messages.applySortButton.defaultMessage)); + const galleryCards = screen.getAllByTestId('grid-card', { exact: false }); + expect(galleryCards).toHaveLength(1); }); - - const galleryCards = screen.getAllByTestId('grid-card', { exact: false }); - - expect(galleryCards).toHaveLength(1); }); it('should clearAll selections', async () => { @@ -445,7 +417,7 @@ describe('Videos page', () => { fireEvent.click(transcribedCheckboxFilter); const clearAllButton = screen.getByText('Clear all'); - await waitFor(() => fireEvent.click(clearAllButton)); + fireEvent.click(clearAllButton); expect(transcribedCheckboxFilter).toHaveProperty('checked', false); @@ -460,11 +432,9 @@ describe('Videos page', () => { const transcribedCheckboxFilter = screen.getByText(videoMessages.transcribedCheckboxLabel.defaultMessage); fireEvent.click(transcribedCheckboxFilter); - await waitFor(() => { - fireEvent.click(screen.getByText(messages.applySortButton.defaultMessage)); - }); + fireEvent.click(screen.getByText(messages.applySortButton.defaultMessage)); - const imageFilterChip = screen.getByRole('button', { name: 'Remove this filter' }); + const imageFilterChip = await screen.findByRole('button', { name: 'Remove this filter' }); fireEvent.click(imageFilterChip); expect(screen.queryByText(videoMessages.transcribedCheckboxLabel.defaultMessage)).toBeNull(); @@ -488,13 +458,13 @@ describe('Videos page', () => { }], }); + fireEvent.click(within(videoMenuButton).getByLabelText('file-menu-toggle')); + fireEvent.click(screen.getByText('Info')); + await waitFor(() => { - fireEvent.click(within(videoMenuButton).getByLabelText('file-menu-toggle')); - fireEvent.click(screen.getByText('Info')); + expect(screen.getByText(messages.infoTitle.defaultMessage)).toBeVisible(); }); - expect(screen.getByText(messages.infoTitle.defaultMessage)).toBeVisible(); - const { usageStatus } = store.getState().videos; expect(usageStatus).toEqual(RequestStatus.SUCCESSFUL); @@ -507,13 +477,12 @@ describe('Videos page', () => { const videoMenuButton = screen.getByTestId('file-menu-dropdown-mOckID1'); axiosMock.onGet(`${getVideosUrl(courseId)}/mOckID1/usage`).reply(201, { usageLocations: [] }); + fireEvent.click(within(videoMenuButton).getByLabelText('file-menu-toggle')); + fireEvent.click(screen.getByText('Info')); await waitFor(() => { - fireEvent.click(within(videoMenuButton).getByLabelText('file-menu-toggle')); - fireEvent.click(screen.getByText('Info')); + expect(screen.getByText(messages.usageNotInUseMessage.defaultMessage)).toBeVisible(); }); - expect(screen.getByText(messages.usageNotInUseMessage.defaultMessage)).toBeVisible(); - const infoTab = screen.getAllByRole('tab')[0]; expect(infoTab).toBeVisible(); @@ -525,17 +494,14 @@ describe('Videos page', () => { const videoMenuButton = screen.getByTestId('file-menu-dropdown-mOckID1'); axiosMock.onGet(`${getVideosUrl(courseId)}/mOckID1/usage`).reply(201, { usageLocations: [] }); + fireEvent.click(within(videoMenuButton).getByLabelText('file-menu-toggle')); + fireEvent.click(screen.getByText('Info')); await waitFor(() => { - fireEvent.click(within(videoMenuButton).getByLabelText('file-menu-toggle')); - fireEvent.click(screen.getByText('Info')); + expect(screen.getByText(messages.usageNotInUseMessage.defaultMessage)).toBeVisible(); }); - expect(screen.getByText(messages.usageNotInUseMessage.defaultMessage)).toBeVisible(); - const transcriptTab = screen.getAllByRole('tab')[1]; - await act(async () => { - fireEvent.click(transcriptTab); - }); + fireEvent.click(transcriptTab); expect(transcriptTab).toBeVisible(); expect(transcriptTab).toHaveClass('active'); @@ -546,15 +512,11 @@ describe('Videos page', () => { const videoMenuButton = screen.getByTestId('file-menu-dropdown-mOckID3'); axiosMock.onGet(`${getVideosUrl(courseId)}/mOckID3/usage`).reply(201, { usageLocations: [] }); - await waitFor(() => { - fireEvent.click(within(videoMenuButton).getByLabelText('file-menu-toggle')); - fireEvent.click(screen.getByText('Info')); - }); + fireEvent.click(within(videoMenuButton).getByLabelText('file-menu-toggle')); + fireEvent.click(screen.getByText('Info')); - const transcriptTab = screen.getAllByRole('tab')[1]; - await act(async () => { - fireEvent.click(transcriptTab); - }); + const transcriptTab = await screen.getAllByRole('tab')[1]; + fireEvent.click(transcriptTab); expect(screen.getByText('Transcript (1)')).toBeVisible(); }); @@ -565,14 +527,13 @@ describe('Videos page', () => { const videoMenuButton = screen.getByTestId('file-menu-dropdown-mOckID1'); + fireEvent.click(within(videoMenuButton).getByLabelText('file-menu-toggle')); + fireEvent.click(screen.getByText('Download')); + await waitFor(() => { - fireEvent.click(within(videoMenuButton).getByLabelText('file-menu-toggle')); - fireEvent.click(screen.getByText('Download')); + const updateStatus = store.getState().videos.updatingStatus; + expect(updateStatus).toEqual(RequestStatus.SUCCESSFUL); }); - - const updateStatus = store.getState().videos.updatingStatus; - - expect(updateStatus).toEqual(RequestStatus.SUCCESSFUL); }); it('delete button should delete file', async () => { @@ -580,17 +541,18 @@ describe('Videos page', () => { const fileMenuButton = screen.getByTestId('file-menu-dropdown-mOckID1'); + axiosMock.onDelete(`${getCourseVideosApiUrl(courseId)}/mOckID1`).reply(204); + fireEvent.click(within(fileMenuButton).getByLabelText('file-menu-toggle')); + fireEvent.click(screen.getByTestId('open-delete-confirmation-button')); await waitFor(() => { - axiosMock.onDelete(`${getCourseVideosApiUrl(courseId)}/mOckID1`).reply(204); - fireEvent.click(within(fileMenuButton).getByLabelText('file-menu-toggle')); - fireEvent.click(screen.getByTestId('open-delete-confirmation-button')); expect(screen.getByText('Delete mOckID1.mp4')).toBeVisible(); + }); - fireEvent.click(screen.getByText(messages.deleteFileButtonLabel.defaultMessage)); + fireEvent.click(screen.getByText(messages.deleteFileButtonLabel.defaultMessage)); + await waitFor(() => { expect(screen.queryByText('Delete mOckID1.mp4')).toBeNull(); - - executeThunk(deleteVideoFile(courseId, 'mOckID1', 5), store.dispatch); }); + await executeThunk(deleteVideoFile(courseId, 'mOckID1', 5), store.dispatch); const deleteStatus = store.getState().videos.deletingStatus; expect(deleteStatus).toEqual(RequestStatus.SUCCESSFUL); @@ -614,10 +576,8 @@ describe('Videos page', () => { axiosMock.onPost(getCourseVideosApiUrl(courseId)).reply(413, { error: errorMessage }); const addFilesButton = screen.getAllByLabelText('file-input')[3]; - await act(async () => { - userEvent.upload(addFilesButton, file); - await executeThunk(addVideoFile(courseId, file, undefined, { current: [] }), store.dispatch); - }); + userEvent.upload(addFilesButton, file); + await executeThunk(addVideoFile(courseId, file, undefined, { current: [] }), store.dispatch); const addStatus = store.getState().videos.addingStatus; expect(addStatus).toEqual(RequestStatus.FAILED); @@ -629,10 +589,8 @@ describe('Videos page', () => { axiosMock.onPost(getCourseVideosApiUrl(courseId)).reply(404); const addFilesButton = screen.getAllByLabelText('file-input')[3]; - await act(async () => { - userEvent.upload(addFilesButton, file); - await executeThunk(addVideoFile(courseId, file, undefined, { current: [] }), store.dispatch); - }); + userEvent.upload(addFilesButton, file); + await executeThunk(addVideoFile(courseId, file, undefined, { current: [] }), store.dispatch); const addStatus = store.getState().videos.addingStatus; expect(addStatus).toEqual(RequestStatus.FAILED); @@ -645,10 +603,8 @@ describe('Videos page', () => { const addThumbnailButton = screen.getByTestId('video-thumbnail-mOckID1'); const thumbnail = new File(['test'], 'sOMEUrl.jpg', { type: 'image/jpg' }); - await act(async () => { - fireEvent.click(addThumbnailButton); - await executeThunk(addVideoThumbnail({ file: thumbnail, videoId: 'mOckID1', courseId }), store.dispatch); - }); + fireEvent.click(addThumbnailButton); + await executeThunk(addVideoThumbnail({ file: thumbnail, videoId: 'mOckID1', courseId }), store.dispatch); const updateStatus = store.getState().videos.updatingStatus; expect(updateStatus).toEqual(RequestStatus.FAILED); @@ -664,10 +620,8 @@ describe('Videos page', () => { axiosMock.onPost(getCourseVideosApiUrl(courseId)).reply(204, generateNewVideoApiResponse()); axiosMock.onGet(getCourseVideosApiUrl(courseId)).reply(200, generateAddVideoApiResponse()); const addFilesButton = screen.getAllByLabelText('file-input')[3]; - await act(async () => { - userEvent.upload(addFilesButton, file); - await executeThunk(addVideoFile(courseId, file, undefined, { current: [] }), store.dispatch); - }); + userEvent.upload(addFilesButton, file); + await executeThunk(addVideoFile(courseId, file, undefined, { current: [] }), store.dispatch); const addStatus = store.getState().videos.addingStatus; expect(addStatus).toEqual(RequestStatus.FAILED); @@ -679,16 +633,19 @@ describe('Videos page', () => { const videoMenuButton = screen.getByTestId('file-menu-dropdown-mOckID1'); + axiosMock.onDelete(`${getCourseVideosApiUrl(courseId)}/mOckID1`).reply(404); + fireEvent.click(within(videoMenuButton).getByLabelText('file-menu-toggle')); + fireEvent.click(screen.getByTestId('open-delete-confirmation-button')); await waitFor(async () => { - axiosMock.onDelete(`${getCourseVideosApiUrl(courseId)}/mOckID1`).reply(404); - fireEvent.click(within(videoMenuButton).getByLabelText('file-menu-toggle')); - fireEvent.click(screen.getByTestId('open-delete-confirmation-button')); expect(screen.getByText('Delete mOckID1.mp4')).toBeVisible(); + }); - fireEvent.click(screen.getByText(messages.deleteFileButtonLabel.defaultMessage)); + fireEvent.click(screen.getByText(messages.deleteFileButtonLabel.defaultMessage)); + await waitFor(async () => { expect(screen.queryByText('Delete mOckID1.mp4')).toBeNull(); }); - executeThunk(deleteVideoFile(courseId, 'mOckID1', 5), store.dispatch); + + await executeThunk(deleteVideoFile(courseId, 'mOckID1', 5), store.dispatch); await waitFor(() => { const deleteStatus = store.getState().videos.deletingStatus; expect(deleteStatus).toEqual(RequestStatus.FAILED); @@ -705,14 +662,12 @@ describe('Videos page', () => { const videoMenuButton = screen.getByTestId('file-menu-dropdown-mOckID3'); axiosMock.onGet(`${getVideosUrl(courseId)}/mOckID3/usage`).reply(404); - await waitFor(() => { - fireEvent.click(within(videoMenuButton).getByLabelText('file-menu-toggle')); - fireEvent.click(screen.getByText('Info')); - executeThunk(getUsagePaths({ - courseId, - video: { id: 'mOckID3', displayName: 'mOckID3' }, - }), store.dispatch); - }); + fireEvent.click(within(videoMenuButton).getByLabelText('file-menu-toggle')); + fireEvent.click(screen.getByText('Info')); + await executeThunk(getUsagePaths({ + courseId, + video: { id: 'mOckID3', displayName: 'mOckID3' }, + }), store.dispatch); await waitFor(() => { const { usageStatus } = store.getState().videos; expect(usageStatus).toEqual(RequestStatus.FAILED); @@ -728,18 +683,13 @@ describe('Videos page', () => { const actionsButton = screen.getByText(messages.actionsButtonLabel.defaultMessage); expect(actionsButton).toBeVisible(); - await waitFor(() => { - fireEvent.click(actionsButton); - }); - const downloadButton = screen.getByText(messages.downloadTitle.defaultMessage).closest('a'); + fireEvent.click(actionsButton); + const downloadButton = await screen.getByText(messages.downloadTitle.defaultMessage).closest('a'); expect(downloadButton).not.toHaveClass('disabled'); axiosMock.onPut(`${getVideosUrl(courseId)}/download`).reply(404); - - await waitFor(() => { - fireEvent.click(downloadButton); - executeThunk(fetchVideoDownload([{ original: { displayName: 'mOckID1', id: '2', downloadLink: 'test' } }]), store.dispatch); - }); + fireEvent.click(downloadButton); + await executeThunk(fetchVideoDownload([{ original: { displayName: 'mOckID1', id: '2', downloadLink: 'test' } }]), store.dispatch); const updateStatus = store.getState().videos.updatingStatus; expect(updateStatus).toEqual(RequestStatus.FAILED); diff --git a/src/files-and-videos/videos-page/transcript-settings/TranscriptSettings.test.jsx b/src/files-and-videos/videos-page/transcript-settings/TranscriptSettings.test.jsx index 65bb272044..55a97747a4 100644 --- a/src/files-and-videos/videos-page/transcript-settings/TranscriptSettings.test.jsx +++ b/src/files-and-videos/videos-page/transcript-settings/TranscriptSettings.test.jsx @@ -1,6 +1,6 @@ import { render, - act, + fireEvent, screen, waitFor, within, @@ -70,9 +70,7 @@ describe('TranscriptSettings', () => { it('should change view to order form', async () => { renderComponent(defaultProps); const orderButton = screen.getByText(messages.orderTranscriptsTitle.defaultMessage); - await act(async () => { - userEvent.click(orderButton); - }); + userEvent.click(orderButton); const selectableButtons = screen.getAllByLabelText('none radio')[0]; expect(selectableButtons).toBeVisible(); @@ -81,17 +79,14 @@ describe('TranscriptSettings', () => { it('should return to order transcript collapsible', async () => { renderComponent(defaultProps); const orderButton = screen.getByText(messages.orderTranscriptsTitle.defaultMessage); - await act(async () => { - userEvent.click(orderButton); - }); + userEvent.click(orderButton); const selectableButtons = screen.getAllByLabelText('none radio')[0]; expect(selectableButtons).toBeVisible(); const backButton = screen.getByLabelText('back button to main transcript settings view'); + userEvent.click(backButton); await waitFor(() => { - userEvent.click(backButton); - expect(screen.queryByLabelText('back button to main transcript settings view')).toBeNull(); }); }); @@ -99,13 +94,9 @@ describe('TranscriptSettings', () => { it('discard changes should call closeTranscriptSettings', async () => { renderComponent(defaultProps); const orderButton = screen.getByText(messages.orderTranscriptsTitle.defaultMessage); - await act(async () => { - userEvent.click(orderButton); - }); + userEvent.click(orderButton); const discardButton = screen.getByText(messages.discardSettingsLabel.defaultMessage); - await act(async () => { - userEvent.click(discardButton); - }); + userEvent.click(discardButton); expect(defaultProps.closeTranscriptSettings).toHaveBeenCalled(); }); @@ -145,9 +136,7 @@ describe('TranscriptSettings', () => { it('should load page with Cielo24 selected', async () => { const orderButton = screen.getByText(messages.orderTranscriptsTitle.defaultMessage); - await act(async () => { - userEvent.click(orderButton); - }); + userEvent.click(orderButton); const cielo24Button = screen.getByText(messages.cieloLabel.defaultMessage); expect(within(cielo24Button).getByLabelText('Cielo24 radio')).toHaveProperty('checked', true); @@ -185,38 +174,32 @@ describe('TranscriptSettings', () => { renderComponent(defaultProps); const orderButton = screen.getByText(messages.orderTranscriptsTitle.defaultMessage); - await act(async () => { - userEvent.click(orderButton); - }); + userEvent.click(orderButton); const noneButton = screen.getAllByLabelText('none radio')[0]; - await act(async () => { - userEvent.click(noneButton); - }); + userEvent.click(noneButton); }); it('api should succeed', async () => { const updateButton = screen.getByText(messages.updateSettingsLabel.defaultMessage); axiosMock.onDelete(`${getApiBaseUrl()}/transcript_preferences/${courseId}`).reply(204); + fireEvent.click(updateButton); await waitFor(() => { - userEvent.click(updateButton); + const { transcriptStatus } = store.getState().videos; + expect(transcriptStatus).toEqual(RequestStatus.SUCCESSFUL); }); - const { transcriptStatus } = store.getState().videos; - - expect(transcriptStatus).toEqual(RequestStatus.SUCCESSFUL); }); it('should show error alert', async () => { const updateButton = screen.getByText(messages.updateSettingsLabel.defaultMessage); axiosMock.onDelete(`${getApiBaseUrl()}/transcript_preferences/${courseId}`).reply(404); + fireEvent.click(updateButton); await waitFor(() => { - userEvent.click(updateButton); + const { transcriptStatus } = store.getState().videos; + expect(transcriptStatus).toEqual(RequestStatus.FAILED); }); - const { transcriptStatus } = store.getState().videos; - - expect(transcriptStatus).toEqual(RequestStatus.FAILED); expect(screen.getByText('Failed to update order transcripts settings.')).toBeVisible(); }); @@ -237,24 +220,18 @@ describe('TranscriptSettings', () => { renderComponent(defaultProps); const orderButton = screen.getByText(messages.orderTranscriptsTitle.defaultMessage); - await act(async () => { - userEvent.click(orderButton); - }); + userEvent.click(orderButton); }); it('should ask for Cielo24 or 3Play Media credentials', async () => { const cielo24Button = screen.getAllByLabelText('Cielo24 radio')[0]; - await act(async () => { - userEvent.click(cielo24Button); - }); + userEvent.click(cielo24Button); const cieloCredentialMessage = screen.getByTestId('cieloCredentialMessage'); expect(cieloCredentialMessage).toBeVisible(); const threePlayMediaButton = screen.getAllByLabelText('3PlayMedia radio')[0]; - await act(async () => { - userEvent.click(threePlayMediaButton); - }); + userEvent.click(threePlayMediaButton); const threePlayMediaCredentialMessage = screen.getByTestId('threePlayMediaCredentialMessage'); expect(threePlayMediaCredentialMessage).toBeVisible(); @@ -263,9 +240,7 @@ describe('TranscriptSettings', () => { describe('api succeeds', () => { it('should update cielo24 credentials ', async () => { const cielo24Button = screen.getAllByLabelText('Cielo24 radio')[0]; - await act(async () => { - userEvent.click(cielo24Button); - }); + userEvent.click(cielo24Button); const firstInput = screen.getByLabelText(messages.cieloApiKeyLabel.defaultMessage); const secondInput = screen.getByLabelText(messages.cieloUsernameLabel.defaultMessage); @@ -279,14 +254,12 @@ describe('TranscriptSettings', () => { }); axiosMock.onPost(`${getApiBaseUrl()}/transcript_credentials/${courseId}`).reply(200); + fireEvent.click(updateButton); await waitFor(() => { - userEvent.click(updateButton); + const { transcriptStatus } = store.getState().videos; + expect(transcriptStatus).toEqual(RequestStatus.SUCCESSFUL); }); - const { transcriptStatus } = store.getState().videos; - - expect(transcriptStatus).toEqual(RequestStatus.SUCCESSFUL); - expect(screen.queryByTestId('cieloCredentialMessage')).toBeNull(); expect(screen.getByText(messages.cieloFidelityLabel.defaultMessage)).toBeVisible(); @@ -294,9 +267,7 @@ describe('TranscriptSettings', () => { it('should update 3Play Media credentials', async () => { const threePlayButton = screen.getAllByLabelText('3PlayMedia radio')[0]; - await act(async () => { - userEvent.click(threePlayButton); - }); + userEvent.click(threePlayButton); const updateButton = screen.getByText(messages.updateSettingsLabel.defaultMessage); const firstInput = screen.getByLabelText(messages.threePlayMediaApiKeyLabel.defaultMessage); @@ -310,12 +281,12 @@ describe('TranscriptSettings', () => { }); axiosMock.onPost(`${getApiBaseUrl()}/transcript_credentials/${courseId}`).reply(200); + fireEvent.click(updateButton); + await waitFor(() => { - userEvent.click(updateButton); + const { transcriptStatus } = store.getState().videos; + expect(transcriptStatus).toEqual(RequestStatus.SUCCESSFUL); }); - const { transcriptStatus } = store.getState().videos; - - expect(transcriptStatus).toEqual(RequestStatus.SUCCESSFUL); expect(screen.queryByTestId('threePlayCredentialMessage')).toBeNull(); @@ -326,9 +297,7 @@ describe('TranscriptSettings', () => { describe('api fails', () => { it('should show error alert on Cielo24 credentials update', async () => { const cielo24Button = screen.getAllByLabelText('Cielo24 radio')[0]; - await act(async () => { - userEvent.click(cielo24Button); - }); + userEvent.click(cielo24Button); const firstInput = screen.getByLabelText(messages.cieloApiKeyLabel.defaultMessage); const secondInput = screen.getByLabelText(messages.cieloUsernameLabel.defaultMessage); @@ -342,21 +311,19 @@ describe('TranscriptSettings', () => { }); axiosMock.onPost(`${getApiBaseUrl()}/transcript_preferences/${courseId}`).reply(503); + fireEvent.click(updateButton); + await waitFor(() => { - userEvent.click(updateButton); + const { transcriptStatus } = store.getState().videos; + expect(transcriptStatus).toEqual(RequestStatus.FAILED); }); - const { transcriptStatus } = store.getState().videos; - - expect(transcriptStatus).toEqual(RequestStatus.FAILED); expect(screen.getByText('Failed to update Cielo24 credentials.')).toBeVisible(); }); it('should show error alert on 3PlayMedia credentials update', async () => { const threePlayButton = screen.getAllByLabelText('3PlayMedia radio')[0]; - await act(async () => { - userEvent.click(threePlayButton); - }); + userEvent.click(threePlayButton); const updateButton = screen.getByText(messages.updateSettingsLabel.defaultMessage); const firstInput = screen.getByLabelText(messages.threePlayMediaApiKeyLabel.defaultMessage); @@ -370,12 +337,12 @@ describe('TranscriptSettings', () => { }); axiosMock.onPost(`${getApiBaseUrl()}/transcript_preferences/${courseId}`).reply(404); + fireEvent.click(updateButton); + await waitFor(() => { - userEvent.click(updateButton); + const { transcriptStatus } = store.getState().videos; + expect(transcriptStatus).toEqual(RequestStatus.FAILED); }); - const { transcriptStatus } = store.getState().videos; - - expect(transcriptStatus).toEqual(RequestStatus.FAILED); expect(screen.getByText('Failed to update 3PlayMedia credentials.')).toBeVisible(); }); @@ -408,24 +375,18 @@ describe('TranscriptSettings', () => { axiosMock = new MockAdapter(getAuthenticatedHttpClient()); renderComponent(defaultProps); const orderButton = screen.getByText(messages.orderTranscriptsTitle.defaultMessage); - await act(async () => { - userEvent.click(orderButton); - }); + userEvent.click(orderButton); }); it('should not show credentials request for Cielo24 and 3Play Media', async () => { const cielo24Button = screen.getAllByLabelText('Cielo24 radio')[0]; - await act(async () => { - userEvent.click(cielo24Button); - }); + userEvent.click(cielo24Button); const cieloCredentialMessage = screen.queryByTestId('cieloCredentialMessage'); expect(cieloCredentialMessage).toBeNull(); const threePlayMediaButton = screen.getAllByLabelText('3PlayMedia radio')[0]; - await act(async () => { - userEvent.click(threePlayMediaButton); - }); + userEvent.click(threePlayMediaButton); const threePlayMediaCredentialMessage = screen.queryByTestId('threePlayMediaCredentialMessage'); expect(threePlayMediaCredentialMessage).toBeNull(); @@ -443,9 +404,7 @@ describe('TranscriptSettings', () => { }; const cielo24Button = screen.getAllByLabelText('Cielo24 radio')[0]; - await act(async () => { - userEvent.click(cielo24Button); - }); + userEvent.click(cielo24Button); const updateButton = screen.getByText(messages.updateSettingsLabel.defaultMessage); const turnaround = screen.getByText(messages.cieloTurnaroundPlaceholder.defaultMessage); const fidelity = screen.getByText(messages.cieloFidelityPlaceholder.defaultMessage); @@ -469,12 +428,13 @@ describe('TranscriptSettings', () => { expect(updateButton).not.toHaveAttribute('disabled'); axiosMock.onPost(`${getApiBaseUrl()}/transcript_preferences/${courseId}`).reply(200, apiResponse); + fireEvent.click(updateButton); + await waitFor(() => { - userEvent.click(updateButton); - }); - const { transcriptStatus } = store.getState().videos; + const { transcriptStatus } = store.getState().videos; - expect(transcriptStatus).toEqual(RequestStatus.SUCCESSFUL); + expect(transcriptStatus).toEqual(RequestStatus.SUCCESSFUL); + }); expect(screen.getByText(messages.cieloFidelityLabel.defaultMessage)).toBeVisible(); }); @@ -488,9 +448,7 @@ describe('TranscriptSettings', () => { global: false, }; const threePlayButton = screen.getAllByLabelText('3PlayMedia radio')[0]; - await act(async () => { - userEvent.click(threePlayButton); - }); + userEvent.click(threePlayButton); const updateButton = screen.getByText(messages.updateSettingsLabel.defaultMessage); const turnaround = screen.getByText(messages.threePlayMediaTurnaroundPlaceholder.defaultMessage); const source = screen.getByText(messages.threePlayMediaSourceLanguagePlaceholder.defaultMessage); @@ -512,12 +470,12 @@ describe('TranscriptSettings', () => { }); axiosMock.onPost(`${getApiBaseUrl()}/transcript_preferences/${courseId}`).reply(200, apiResponse); + fireEvent.click(updateButton); await waitFor(() => { - userEvent.click(updateButton); - }); - const { transcriptStatus } = store.getState().videos; + const { transcriptStatus } = store.getState().videos; - expect(transcriptStatus).toEqual(RequestStatus.SUCCESSFUL); + expect(transcriptStatus).toEqual(RequestStatus.SUCCESSFUL); + }); }); it('should update 3Play Media preferences with spanish as source language', async () => { @@ -529,9 +487,7 @@ describe('TranscriptSettings', () => { global: false, }; const threePlayButton = screen.getAllByLabelText('3PlayMedia radio')[0]; - await act(async () => { - userEvent.click(threePlayButton); - }); + userEvent.click(threePlayButton); const updateButton = screen.getByText(messages.updateSettingsLabel.defaultMessage); const turnaround = screen.getByText(messages.threePlayMediaTurnaroundPlaceholder.defaultMessage); const source = screen.getByText(messages.threePlayMediaSourceLanguagePlaceholder.defaultMessage); @@ -550,21 +506,19 @@ describe('TranscriptSettings', () => { expect(updateButton).not.toHaveAttribute('disabled'); axiosMock.onPost(`${getApiBaseUrl()}/transcript_preferences/${courseId}`).reply(200, apiResponse); + fireEvent.click(updateButton); await waitFor(() => { - userEvent.click(updateButton); - }); - const { transcriptStatus } = store.getState().videos; + const { transcriptStatus } = store.getState().videos; - expect(transcriptStatus).toEqual(RequestStatus.SUCCESSFUL); + expect(transcriptStatus).toEqual(RequestStatus.SUCCESSFUL); + }); }); }); describe('api fails', () => { it('should show error alert on Cielo24 preferences update', async () => { const cielo24Button = screen.getAllByLabelText('Cielo24 radio')[0]; - await act(async () => { - userEvent.click(cielo24Button); - }); + userEvent.click(cielo24Button); const updateButton = screen.getByText(messages.updateSettingsLabel.defaultMessage); const turnaround = screen.getByText(messages.cieloTurnaroundPlaceholder.defaultMessage); const fidelity = screen.getByText(messages.cieloFidelityPlaceholder.defaultMessage); @@ -588,21 +542,19 @@ describe('TranscriptSettings', () => { expect(updateButton).not.toHaveAttribute('disabled'); axiosMock.onPost(`${getApiBaseUrl()}/transcript_preferences/${courseId}`).reply(503); + fireEvent.click(updateButton); await waitFor(() => { - userEvent.click(updateButton); - }); - const { transcriptStatus } = store.getState().videos; + const { transcriptStatus } = store.getState().videos; - expect(transcriptStatus).toEqual(RequestStatus.FAILED); + expect(transcriptStatus).toEqual(RequestStatus.FAILED); + }); expect(screen.getByText('Failed to update Cielo24 transcripts settings.')).toBeVisible(); }); it('should show error alert with default message on 3PlayMedia preferences update', async () => { const threePlayButton = screen.getAllByLabelText('3PlayMedia radio')[0]; - await act(async () => { - userEvent.click(threePlayButton); - }); + userEvent.click(threePlayButton); const updateButton = screen.getByText(messages.updateSettingsLabel.defaultMessage); const turnaround = screen.getByText(messages.threePlayMediaTurnaroundPlaceholder.defaultMessage); const source = screen.getByText(messages.threePlayMediaSourceLanguagePlaceholder.defaultMessage); @@ -621,21 +573,19 @@ describe('TranscriptSettings', () => { expect(updateButton).not.toHaveAttribute('disabled'); axiosMock.onPost(`${getApiBaseUrl()}/transcript_preferences/${courseId}`).reply(404); + fireEvent.click(updateButton); await waitFor(() => { - userEvent.click(updateButton); - }); - const { transcriptStatus } = store.getState().videos; + const { transcriptStatus } = store.getState().videos; - expect(transcriptStatus).toEqual(RequestStatus.FAILED); + expect(transcriptStatus).toEqual(RequestStatus.FAILED); + }); expect(screen.getByText('Failed to update 3PlayMedia transcripts settings.')).toBeVisible(); }); it('should show error alert with default message on 3PlayMedia preferences update', async () => { const threePlayButton = screen.getAllByLabelText('3PlayMedia radio')[0]; - await act(async () => { - userEvent.click(threePlayButton); - }); + userEvent.click(threePlayButton); const updateButton = screen.getByText(messages.updateSettingsLabel.defaultMessage); const turnaround = screen.getByText(messages.threePlayMediaTurnaroundPlaceholder.defaultMessage); const source = screen.getByText(messages.threePlayMediaSourceLanguagePlaceholder.defaultMessage); @@ -654,12 +604,12 @@ describe('TranscriptSettings', () => { expect(updateButton).not.toHaveAttribute('disabled'); axiosMock.onPost(`${getApiBaseUrl()}/transcript_preferences/${courseId}`).reply(404, { error: 'Invalid turnaround.' }); + fireEvent.click(updateButton); await waitFor(() => { - userEvent.click(updateButton); - }); - const { transcriptStatus } = store.getState().videos; + const { transcriptStatus } = store.getState().videos; - expect(transcriptStatus).toEqual(RequestStatus.FAILED); + expect(transcriptStatus).toEqual(RequestStatus.FAILED); + }); expect(screen.getByText('Invalid turnaround.')).toBeVisible(); }); diff --git a/src/generic/create-or-rerun-course/CreateOrRerunCourseForm.test.jsx b/src/generic/create-or-rerun-course/CreateOrRerunCourseForm.test.jsx index 0383b789a7..efd2e91881 100644 --- a/src/generic/create-or-rerun-course/CreateOrRerunCourseForm.test.jsx +++ b/src/generic/create-or-rerun-course/CreateOrRerunCourseForm.test.jsx @@ -1,6 +1,5 @@ import React from 'react'; import { - act, fireEvent, screen, render, @@ -126,9 +125,7 @@ describe('', () => { render(); await mockStore(); const cancelBtn = screen.getByRole('button', { name: messages.cancelButton.defaultMessage }); - await act(async () => { - fireEvent.click(cancelBtn); - }); + fireEvent.click(cancelBtn); expect(onClickCancelMock).toHaveBeenCalled(); }); @@ -146,13 +143,11 @@ describe('', () => { const runInput = screen.getByPlaceholderText(messages.courseRunPlaceholder.defaultMessage); const createBtn = screen.getByRole('button', { name: messages.createButton.defaultMessage }); - await act(async () => { - userEvent.type(displayNameInput, 'foo course name'); - fireEvent.click(orgInput); - userEvent.type(numberInput, '777'); - userEvent.type(runInput, '1'); - userEvent.click(createBtn); - }); + userEvent.type(displayNameInput, 'foo course name'); + fireEvent.click(orgInput); + userEvent.type(numberInput, '777'); + userEvent.type(runInput, '1'); + userEvent.click(createBtn); await axiosMock.onPost(getCreateOrRerunCourseUrl()).reply(200, { url }); await executeThunk(updateCreateOrRerunCourseQuery({ org: 'testX', run: 'some' }), store.dispatch); @@ -170,13 +165,11 @@ describe('', () => { const createBtn = screen.getByRole('button', { name: messages.createButton.defaultMessage }); await axiosMock.onPost(getCreateOrRerunCourseUrl()).reply(200, { url, destinationCourseKey }); - await act(async () => { - userEvent.type(displayNameInput, 'foo course name'); - fireEvent.click(orgInput); - userEvent.type(numberInput, '777'); - userEvent.type(runInput, '1'); - userEvent.click(createBtn); - }); + userEvent.type(displayNameInput, 'foo course name'); + fireEvent.click(orgInput); + userEvent.type(numberInput, '777'); + userEvent.type(runInput, '1'); + userEvent.click(createBtn); await executeThunk(updateCreateOrRerunCourseQuery({ org: 'testX', run: 'some' }), store.dispatch); expect(window.location.assign).toHaveBeenCalledWith(`${process.env.STUDIO_BASE_URL}${url}${destinationCourseKey}`); @@ -207,12 +200,10 @@ describe('', () => { const numberInput = screen.getByPlaceholderText(messages.courseNumberPlaceholder.defaultMessage); const runInput = screen.getByPlaceholderText(messages.courseRunPlaceholder.defaultMessage); - await act(async () => { - fireEvent.change(displayNameInput, { target: { value: 'foo course name' } }); - fireEvent.click(orgInput); - fireEvent.change(numberInput, { target: { value: 'number with invalid (+) symbol' } }); - fireEvent.change(runInput, { target: { value: 'number with invalid (=) symbol' } }); - }); + fireEvent.change(displayNameInput, { target: { value: 'foo course name' } }); + fireEvent.click(orgInput); + fireEvent.change(numberInput, { target: { value: 'number with invalid (+) symbol' } }); + fireEvent.change(runInput, { target: { value: 'number with invalid (=) symbol' } }); waitFor(() => { expect(createBtn).toBeDisabled(); @@ -261,9 +252,7 @@ describe('', () => { await mockStore(); const numberInput = screen.getByPlaceholderText(messages.courseNumberPlaceholder.defaultMessage); - await act(async () => { - fireEvent.change(numberInput, { target: { value: 'number with invalid (+) symbol' } }); - }); + fireEvent.change(numberInput, { target: { value: 'number with invalid (+) symbol' } }); waitFor(() => { expect(screen.getByText(messages.noSpaceError)).toBeInTheDocument(); diff --git a/src/generic/modal-dropzone/ModalDropzone.test.jsx b/src/generic/modal-dropzone/ModalDropzone.test.jsx index 030c171518..71d1013cf7 100644 --- a/src/generic/modal-dropzone/ModalDropzone.test.jsx +++ b/src/generic/modal-dropzone/ModalDropzone.test.jsx @@ -116,7 +116,7 @@ describe('', () => { const dropzoneInput = getByRole('presentation', { hidden: true }).firstChild; const uploadButton = getByRole('button', { name: messages.uploadModal.defaultMessage }); - await userEvent.upload(dropzoneInput, file); + userEvent.upload(dropzoneInput, file); await waitFor(() => { expect(uploadButton).not.toBeDisabled(); diff --git a/src/pages-and-resources/discussions/DiscussionsSettings.test.jsx b/src/pages-and-resources/discussions/DiscussionsSettings.test.jsx index 2f8215ceff..60a257ee1f 100644 --- a/src/pages-and-resources/discussions/DiscussionsSettings.test.jsx +++ b/src/pages-and-resources/discussions/DiscussionsSettings.test.jsx @@ -5,13 +5,18 @@ import { import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; import { AppProvider, PageWrap } from '@edx/frontend-platform/react'; import { - act, findByRole, getByRole, queryByLabelText, queryByRole, queryByTestId, queryByText, render, - screen, waitFor, waitForElementToBeRemoved, + act, findByRole, fireEvent, getByRole, queryByLabelText, queryByRole, queryByTestId, queryByText, + render, screen, waitFor, waitForElementToBeRemoved, } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import MockAdapter from 'axios-mock-adapter'; import React from 'react'; -import { Routes, Route, MemoryRouter } from 'react-router-dom'; +import { + Routes, + Route, + MemoryRouter, + useLocation, +} from 'react-router-dom'; import { fetchCourseDetail } from '../../data/thunks'; import initializeStore from '../../store'; import { executeThunk } from '../../utils'; @@ -37,6 +42,11 @@ let container; // Modal creates a portal. Overriding ReactDOM.createPortal allows portals to be tested in jest. ReactDOM.createPortal = jest.fn(node => node); +const LocationDisplay = () => { + const location = useLocation(); + return
{location.pathname}
; +}; + function renderComponent(route) { const wrapper = render( @@ -52,6 +62,7 @@ function renderComponent(route) { element={} /> + , @@ -94,7 +105,7 @@ describe('DiscussionsSettings', () => { renderComponent(`/course/${courseId}/pages-and-resources/discussion`); // This is an important line that ensures the spinner has been removed - and thus our main // content has been loaded - prior to proceeding with our expectations. - await waitForElementToBeRemoved(screen.getByRole('status')); + await waitForElementToBeRemoved(screen.queryByRole('status')); expect(queryByTestId(container, 'appList')).toBeInTheDocument(); expect(queryByTestId(container, 'appConfigForm')).not.toBeInTheDocument(); @@ -104,7 +115,7 @@ describe('DiscussionsSettings', () => { renderComponent(`/course/${courseId}/pages-and-resources/discussion/configure/piazza`); // This is an important line that ensures the spinner has been removed - and thus our main // content has been loaded - prior to proceeding with our expectations. - await waitForElementToBeRemoved(screen.getByRole('status')); + await waitForElementToBeRemoved(screen.queryByRole('status')); expect(queryByTestId(container, 'appList')).not.toBeInTheDocument(); expect(queryByTestId(container, 'appConfigForm')).toBeInTheDocument(); @@ -114,7 +125,7 @@ describe('DiscussionsSettings', () => { renderComponent(`/course/${courseId}/pages-and-resources/discussion`); // This is an important line that ensures the spinner has been removed - and thus our main // content has been loaded - prior to proceeding with our expectations. - await waitForElementToBeRemoved(screen.getByRole('status')); + await waitForElementToBeRemoved(screen.queryByRole('status')); await waitFor(() => { userEvent.click(queryByLabelText(container, 'Select Piazza')); @@ -134,7 +145,7 @@ describe('DiscussionsSettings', () => { renderComponent(`/course/${courseId}/pages-and-resources/discussion`); // This is an important line that ensures the spinner has been removed - and thus our main // content has been loaded - prior to proceeding with our expectations. - await waitForElementToBeRemoved(screen.getByRole('status')); + await waitForElementToBeRemoved(screen.queryByRole('status')); await waitFor(() => { userEvent.click(queryByLabelText(container, 'Select edX')); @@ -151,11 +162,11 @@ describe('DiscussionsSettings', () => { renderComponent(`/course/${courseId}/pages-and-resources/discussion/configure/piazza`); // This is an important line that ensures the spinner has been removed - and thus our main // content has been loaded - prior to proceeding with our expectations. - await waitForElementToBeRemoved(screen.getByRole('status')); + await waitForElementToBeRemoved(screen.queryByRole('status')); expect(queryByTestId(container, 'appConfigForm')).toBeInTheDocument(); - await act(() => userEvent.click(queryByText(container, appMessages.backButton.defaultMessage))); + await waitFor(() => userEvent.click(queryByText(container, appMessages.backButton.defaultMessage))); await waitFor(() => { expect(queryByTestId(container, 'appList')).toBeInTheDocument(); @@ -167,7 +178,7 @@ describe('DiscussionsSettings', () => { renderComponent(`/course/${courseId}/pages-and-resources/discussion`); // This is an important line that ensures the spinner has been removed - and thus our main // content has been loaded - prior to proceeding with our expectations. - await waitForElementToBeRemoved(screen.getByRole('status')); + await waitForElementToBeRemoved(screen.queryByRole('status')); expect(queryByTestId(container, 'appList')).toBeInTheDocument(); @@ -183,22 +194,27 @@ describe('DiscussionsSettings', () => { // This is an important line that ensures the spinner has been removed - and thus our main // content has been loaded - prior to proceeding with our expectations. - await waitForElementToBeRemoved(screen.getByRole('status')); - - act(async () => { - userEvent.click(queryByLabelText(container, 'Select Piazza')); + await waitForElementToBeRemoved(screen.queryByRole('status')); - userEvent.click(getByRole(container, 'button', { name: 'Next' })); + userEvent.click(screen.getByLabelText('Select Piazza')); - userEvent.click(await findByRole(container, 'button', { name: 'Save' })); + // Have to use fireEvent.click with these Stepper buttons so that the + // onClick handler is triggered. (userEvent.click doesn't trigger onClick). + await act(async () => { + fireEvent.click(getByRole(container, 'button', { name: 'Next' })); + }); + await act(async () => { + fireEvent.click(getByRole(container, 'button', { name: 'Save' })); + }); - // This is an important line that ensures the Close button has been removed, which implies that - // the full screen modal has been closed following our click of Apply. Once this has happened, - // then it's safe to proceed with our expectations. - await waitFor(() => expect(screen.queryByRole(container, 'button', { name: 'Close' })).toBeNull()); + // This is an important line that ensures the Close button has been removed, which implies that + // the full screen modal has been closed following our click of Apply. Once this has happened, + // then it's safe to proceed with our expectations. + await waitFor(() => expect(screen.queryByRole(container, 'button', { name: 'Close' })).toBeNull()); - await waitFor(() => expect(window.location.pathname).toEqual(`/course/${courseId}/pages-and-resources`)); - }); + // Confirm route is correct + const locationDisplay = await screen.findByTestId('location-display'); + await waitFor(() => expect(locationDisplay.textContent).toEqual(`/course/${courseId}/pages-and-resources`)); }); test('requires confirmation if changing provider', async () => { @@ -208,20 +224,18 @@ describe('DiscussionsSettings', () => { renderComponent(`/course/${courseId}/pages-and-resources/discussion`); // This is an important line that ensures the spinner has been removed - and thus our main // content has been loaded - prior to proceeding with our expectations. - await waitForElementToBeRemoved(screen.getByRole('status')); + await waitForElementToBeRemoved(screen.queryByRole('status')); - act(async () => { - userEvent.click(getByRole(container, 'checkbox', { name: 'Select Discourse' })); - userEvent.click(getByRole(container, 'button', { name: 'Next' })); + userEvent.click(getByRole(container, 'checkbox', { name: 'Select Discourse' })); + userEvent.click(getByRole(container, 'button', { name: 'Next' })); - await findByRole(container, 'button', { name: 'Save' }); - userEvent.type(getByRole(container, 'textbox', { name: 'Consumer Key' }), 'key'); - userEvent.type(getByRole(container, 'textbox', { name: 'Consumer Secret' }), 'secret'); - userEvent.type(getByRole(container, 'textbox', { name: 'Launch URL' }), 'http://example.test'); - userEvent.click(getByRole(container, 'button', { name: 'Save' })); + await findByRole(container, 'button', { name: 'Save' }); + userEvent.type(getByRole(container, 'textbox', { name: 'Consumer Key' }), 'key'); + userEvent.type(getByRole(container, 'textbox', { name: 'Consumer Secret' }), 'secret'); + userEvent.type(getByRole(container, 'textbox', { name: 'Launch URL' }), 'http://example.test'); + userEvent.click(getByRole(container, 'button', { name: 'Save' })); - await waitFor(() => expect(queryByRole(container, 'dialog', { name: 'OK' })).toBeInTheDocument()); - }); + await waitFor(() => expect(queryByRole(container, 'dialog', { name: 'OK' })).toBeInTheDocument()); }); test('can cancel confirmation', async () => { @@ -231,7 +245,7 @@ describe('DiscussionsSettings', () => { renderComponent(`/course/${courseId}/pages-and-resources/discussion`); // This is an important line that ensures the spinner has been removed - and thus our main // content has been loaded - prior to proceeding with our expectations. - await waitForElementToBeRemoved(screen.getByRole('status')); + await waitForElementToBeRemoved(screen.queryByRole('status')); const discourseBox = getByRole(container, 'checkbox', { name: 'Select Discourse' }); expect(discourseBox).not.toBeDisabled(); @@ -241,20 +255,18 @@ describe('DiscussionsSettings', () => { await waitFor(() => expect(screen.queryByRole('status')).toBeNull()); - act(async () => { - expect(await findByRole(container, 'heading', { name: 'Discourse' })).toBeInTheDocument(); + expect(await findByRole(container, 'heading', { name: 'Discourse' })).toBeInTheDocument(); - userEvent.type(getByRole(container, 'textbox', { name: 'Consumer Key' }), 'a'); - userEvent.type(getByRole(container, 'textbox', { name: 'Consumer Secret' }), 'secret'); - userEvent.type(getByRole(container, 'textbox', { name: 'Launch URL' }), 'http://example.test'); - userEvent.click(getByRole(container, 'button', { name: 'Save' })); + userEvent.type(getByRole(container, 'textbox', { name: 'Consumer Key' }), 'a'); + userEvent.type(getByRole(container, 'textbox', { name: 'Consumer Secret' }), 'secret'); + userEvent.type(getByRole(container, 'textbox', { name: 'Launch URL' }), 'http://example.test'); + userEvent.click(getByRole(container, 'button', { name: 'Save' })); - waitFor(() => expect(getByRole(container, 'dialog', { name: 'OK' })).toBeInTheDocument()); - userEvent.click(getByRole(container, 'button', { name: 'Cancel' })); + await waitFor(() => expect(getByRole(container, 'dialog', { name: 'OK' })).toBeInTheDocument()); + userEvent.click(getByRole(container, 'button', { name: 'Cancel' })); - expect(queryByRole(container, 'dialog', { name: 'Confirm' })).not.toBeInTheDocument(); - expect(queryByRole(container, 'dialog', { name: 'Configure discussion' })); - }); + expect(queryByRole(container, 'dialog', { name: 'Confirm' })).not.toBeInTheDocument(); + expect(queryByRole(container, 'dialog', { name: 'Configure discussion' })); }); }); @@ -274,7 +286,7 @@ describe('DiscussionsSettings', () => { renderComponent(`/course/${courseId}/pages-and-resources/discussion`); // This is an important line that ensures the spinner has been removed - and thus our main // content has been loaded - prior to proceeding with our expectations. - await waitForElementToBeRemoved(screen.getByRole('status')); + await waitForElementToBeRemoved(screen.queryByRole('status')); const alert = queryByRole(container, 'alert'); expect(alert).toBeInTheDocument(); @@ -302,19 +314,17 @@ describe('DiscussionsSettings', () => { renderComponent(`/course/${courseId}/pages-and-resources/discussion/configure/piazza`); // This is an important line that ensures the spinner has been removed - and thus our main // content has been loaded - prior to proceeding with our expectations. - await waitForElementToBeRemoved(screen.getByRole('status')); + await waitForElementToBeRemoved(screen.queryByRole('status')); // Apply causes an async action to take place - act(async () => { - userEvent.click(queryByText(container, appMessages.saveButton.defaultMessage)); - await waitFor(() => expect(axiosMock.history.post.length).toBe(1)); - - expect(queryByTestId(container, 'appConfigForm')).toBeInTheDocument(); - const alert = await findByRole(container, 'alert'); - expect(alert).toBeInTheDocument(); - expect(alert.textContent).toEqual(expect.stringContaining('We encountered a technical error when applying changes.')); - expect(alert.innerHTML).toEqual(expect.stringContaining(getConfig().SUPPORT_URL)); - }); + userEvent.click(queryByText(container, appMessages.saveButton.defaultMessage)); + await waitFor(() => expect(axiosMock.history.post.length).toBe(1)); + + expect(queryByTestId(container, 'appConfigForm')).toBeInTheDocument(); + const alert = await findByRole(container, 'alert'); + expect(alert).toBeInTheDocument(); + expect(alert.textContent).toEqual(expect.stringContaining('We encountered a technical error when applying changes.')); + expect(alert.innerHTML).toEqual(expect.stringContaining(getConfig().SUPPORT_URL)); }); }); @@ -328,7 +338,7 @@ describe('DiscussionsSettings', () => { renderComponent(`/course/${courseId}/pages-and-resources/discussion`); // This is an important line that ensures the spinner has been removed - and thus our main // content has been loaded - prior to proceeding with our expectations. - await waitForElementToBeRemoved(screen.getByRole('status')); + await waitForElementToBeRemoved(screen.queryByRole('status')); const alert = queryByRole(container, 'alert'); expect(alert).toBeInTheDocument(); @@ -348,23 +358,23 @@ describe('DiscussionsSettings', () => { renderComponent(`/course/${courseId}/pages-and-resources/discussion/configure/piazza`); // This is an important line that ensures the spinner has been removed - and thus our main // content has been loaded - prior to proceeding with our expectations. - await waitForElementToBeRemoved(screen.getByRole('status')); + await waitForElementToBeRemoved(screen.queryByRole('status')); - act(async () => { - userEvent.click(getByRole(container, 'button', { name: 'Save' })); + userEvent.click(getByRole(container, 'button', { name: 'Save' })); - await waitFor(() => expect(axiosMock.history.post.length).toBe(1)); + await waitFor(() => expect(axiosMock.history.post.length).toBe(1)); - expect(queryByTestId(container, 'appList')).not.toBeInTheDocument(); - expect(queryByTestId(container, 'appConfigForm')).not.toBeInTheDocument(); + expect(queryByTestId(container, 'appList')).not.toBeInTheDocument(); + expect(queryByTestId(container, 'appConfigForm')).not.toBeInTheDocument(); - // We don't technically leave the route in this case, though the modal is hidden. - expect(window.location.pathname).toEqual(`/course/${courseId}/pages-and-resources/discussion/configure/piazza`); + // Confirm route is correct + // We don't technically leave the route in this case, though the modal is hidden. + const locationDisplay = await screen.findByTestId('location-display'); + await waitFor(() => expect(locationDisplay.textContent).toEqual(`/course/${courseId}/pages-and-resources/discussion/configure/piazza`)); - const alert = await findByRole(container, 'alert'); - expect(alert).toBeInTheDocument(); - expect(alert.textContent).toEqual(expect.stringContaining('You are not authorized to view this page.')); - }); + const alert = await findByRole(container, 'alert'); + expect(alert).toBeInTheDocument(); + expect(alert.textContent).toEqual(expect.stringContaining('You are not authorized to view this page.')); }); }); }); @@ -406,13 +416,13 @@ describe.each([ renderComponent(`/course/${courseId}/pages-and-resources/discussion`); // This is an important line that ensures the spinner has been removed - and thus our main // content has been loaded - prior to proceeding with our expectations. - waitForElementToBeRemoved(screen.getByRole('status')); + await waitForElementToBeRemoved(screen.queryByRole('status')); - act(async () => { - userEvent.click(await screen.findByLabelText('Select Piazza')); - userEvent.click(queryByText(container, messages.nextButton.defaultMessage)); - waitForElementToBeRemoved(screen.getByRole('status')); + userEvent.click(screen.getByLabelText('Select Piazza')); + userEvent.click(queryByText(container, messages.nextButton.defaultMessage)); + expect(screen.queryByRole('status')).not.toBeInTheDocument(); + await waitFor(() => { if (showLTIConfig) { expect(queryByText(container, ltiMessages.formInstructions.defaultMessage)).toBeInTheDocument(); expect(queryByTestId(container, 'ltiConfigFields')).toBeInTheDocument(); @@ -460,18 +470,16 @@ describe.each([ renderComponent(`/course/${courseId}/pages-and-resources/discussion`); // This is an important line that ensures the spinner has been removed - and thus our main // content has been loaded - prior to proceeding with our expectations. - waitForElementToBeRemoved(screen.getByRole('status')); + await waitForElementToBeRemoved(screen.queryByRole('status')); - act(async () => { - userEvent.click(await screen.findByLabelText('Select Piazza')); - userEvent.click(await screen.findByText(messages.nextButton.defaultMessage)); + userEvent.click(screen.getByLabelText('Select Piazza')); + userEvent.click(screen.getByText(messages.nextButton.defaultMessage)); - waitForElementToBeRemoved(screen.getByRole('status')); - if (enablePIISharing) { - expect(queryByTestId(container, 'piiSharingFields')).toBeInTheDocument(); - } else { - expect(queryByTestId(container, 'piiSharingFields')).not.toBeInTheDocument(); - } - }); + expect(screen.queryByRole('status')).not.toBeInTheDocument(); + if (enablePIISharing) { + expect(queryByTestId(container, 'piiSharingFields')).toBeInTheDocument(); + } else { + expect(queryByTestId(container, 'piiSharingFields')).not.toBeInTheDocument(); + } }); }); diff --git a/src/pages-and-resources/discussions/app-config-form/apps/shared/DiscussionRestriction.jsx b/src/pages-and-resources/discussions/app-config-form/apps/shared/DiscussionRestriction.jsx index fce616a4aa..3fbee41c10 100644 --- a/src/pages-and-resources/discussions/app-config-form/apps/shared/DiscussionRestriction.jsx +++ b/src/pages-and-resources/discussions/app-config-form/apps/shared/DiscussionRestriction.jsx @@ -40,7 +40,7 @@ const DiscussionRestriction = () => { const discussionRestrictionButtons = useMemo(() => discussionRestrictionOptions.map((restriction) => (