diff --git a/jest.config.js b/jest.config.js index 0af1e62af..141b70a4c 100644 --- a/jest.config.js +++ b/jest.config.js @@ -14,6 +14,5 @@ module.exports = createConfig('jest', { '^CourseAuthoring/(.*)$': '/src/$1', }, modulePathIgnorePatterns: [ - '/src/pages-and-resources/utils.test.jsx', ], }); diff --git a/plugins/course-apps/learning_assistant/Settings.test.jsx b/plugins/course-apps/learning_assistant/Settings.test.jsx index a9bfa5820..a98ae8b6f 100644 --- a/plugins/course-apps/learning_assistant/Settings.test.jsx +++ b/plugins/course-apps/learning_assistant/Settings.test.jsx @@ -2,16 +2,12 @@ import React from 'react'; import { screen, waitFor } from '@testing-library/react'; import { RequestStatus } from 'CourseAuthoring/data/constants'; -import { render } from 'CourseAuthoring/pages-and-resources/utils.test'; +import { initializeMocks, render } from 'CourseAuthoring/testUtils'; import LearningAssistantSettings from './Settings'; const onClose = () => { }; describe('Learning Assistant Settings', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - it('renders', async () => { const initialState = { models: { @@ -38,14 +34,8 @@ describe('Learning Assistant Settings', () => { }, }; - render( - , - { - preloadedState: initialState, - }, - ); + initializeMocks({ initialState }); + render(); const toggleDescription = 'Reinforce learning concepts by sharing text-based course content ' + 'with OpenAI (via API) to power an in-course Learning Assistant. Learners can leave feedback about the quality ' diff --git a/src/CourseAuthoringPage.test.jsx b/src/CourseAuthoringPage.test.jsx index 3212b1761..6122056c2 100644 --- a/src/CourseAuthoringPage.test.jsx +++ b/src/CourseAuthoringPage.test.jsx @@ -4,7 +4,7 @@ import CourseAuthoringPage from './CourseAuthoringPage'; import PagesAndResources from './pages-and-resources/PagesAndResources'; import { executeThunk } from './utils'; import { fetchCourseApps } from './pages-and-resources/data/thunks'; -import { fetchCourseDetail, fetchWaffleFlags } from './data/thunks'; +import { fetchCourseDetail } from './data/thunks'; import { getApiWaffleFlagsUrl } from './data/api'; import { initializeMocks, render } from './testUtils'; @@ -26,7 +26,6 @@ beforeEach(async () => { axiosMock .onGet(getApiWaffleFlagsUrl(courseId)) .reply(200, {}); - await executeThunk(fetchWaffleFlags(courseId), store.dispatch); }); describe('Editor Pages Load no header', () => { diff --git a/src/CourseAuthoringRoutes.test.jsx b/src/CourseAuthoringRoutes.test.jsx index e2233559f..472862e80 100644 --- a/src/CourseAuthoringRoutes.test.jsx +++ b/src/CourseAuthoringRoutes.test.jsx @@ -1,7 +1,5 @@ import CourseAuthoringRoutes from './CourseAuthoringRoutes'; -import { executeThunk } from './utils'; import { getApiWaffleFlagsUrl } from './data/api'; -import { fetchWaffleFlags } from './data/thunks'; import { screen, initializeMocks, render, waitFor, } from './testUtils'; @@ -11,7 +9,6 @@ const pagesAndResourcesMockText = 'Pages And Resources'; const editorContainerMockText = 'Editor Container'; const videoSelectorContainerMockText = 'Video Selector Container'; const customPagesMockText = 'Custom Pages'; -let store; const mockComponentFn = jest.fn(); jest.mock('react-router-dom', () => ({ @@ -51,12 +48,10 @@ jest.mock('./custom-pages/CustomPages', () => (props) => { describe('', () => { beforeEach(async () => { - const { axiosMock, reduxStore } = initializeMocks(); - store = reduxStore; + const { axiosMock } = initializeMocks(); axiosMock .onGet(getApiWaffleFlagsUrl(courseId)) .reply(200, {}); - await executeThunk(fetchWaffleFlags(courseId), store.dispatch); }); it('renders the PagesAndResources component when the pages and resources route is active', async () => { diff --git a/src/accessibility-page/AccessibilityPage.test.jsx b/src/accessibility-page/AccessibilityPage.test.jsx index f686daf4d..185b5ce5e 100644 --- a/src/accessibility-page/AccessibilityPage.test.jsx +++ b/src/accessibility-page/AccessibilityPage.test.jsx @@ -1,42 +1,13 @@ -import { - render, - screen, -} from '@testing-library/react'; -import { AppProvider } from '@edx/frontend-platform/react'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; -import { initializeMockApp } from '@edx/frontend-platform'; -import initializeStore from '../store'; +// @ts-check +import { initializeMocks, render, screen } from '../testUtils'; import AccessibilityPage from './index'; -const initialState = { - accessibilityPage: { - status: {}, - }, -}; -let store; - -const renderComponent = () => { - render( - - - - - , - ); -}; +const renderComponent = () => render(); describe('', () => { describe('renders', () => { beforeEach(async () => { - initializeMockApp({ - authenticatedUser: { - userId: 3, - username: 'abc123', - administrator: false, - roles: [], - }, - }); - store = initializeStore(initialState); + initializeMocks(); }); it('contains the policy body', () => { renderComponent(); diff --git a/src/advanced-settings/AdvancedSettings.test.jsx b/src/advanced-settings/AdvancedSettings.test.jsx index cc5144c64..cc76c8e8f 100644 --- a/src/advanced-settings/AdvancedSettings.test.jsx +++ b/src/advanced-settings/AdvancedSettings.test.jsx @@ -1,12 +1,9 @@ -import React from 'react'; -import { initializeMockApp } from '@edx/frontend-platform'; -import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; -import { IntlProvider, injectIntl } from '@edx/frontend-platform/i18n'; -import { AppProvider } from '@edx/frontend-platform/react'; -import { render, fireEvent, waitFor } from '@testing-library/react'; -import MockAdapter from 'axios-mock-adapter'; - -import initializeStore from '../store'; +import { + render as baseRender, + fireEvent, + initializeMocks, + waitFor, +} from '../testUtils'; import { executeThunk } from '../utils'; import { advancedSettingsMock } from './__mocks__'; import { getCourseAdvancedSettingsApiUrl } from './data/api'; @@ -28,39 +25,22 @@ jest.mock('react-textarea-autosize', () => jest.fn((props) => ( /> ))); -jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useLocation: () => ({ - pathname: mockPathname, - }), -})); - -const RootWrapper = () => ( - - - - - +const render = () => baseRender( + , + { path: mockPathname }, ); describe('', () => { beforeEach(() => { - initializeMockApp({ - authenticatedUser: { - userId: 3, - username: 'abc123', - administrator: true, - roles: [], - }, - }); - store = initializeStore(); - axiosMock = new MockAdapter(getAuthenticatedHttpClient()); + const mocks = initializeMocks(); + store = mocks.reduxStore; + axiosMock = mocks.axiosMock; axiosMock .onGet(`${getCourseAdvancedSettingsApiUrl(courseId)}?fetch_all=0`) .reply(200, advancedSettingsMock); }); it('should render without errors', async () => { - const { getByText } = render(); + const { getByText } = render(); await waitFor(() => { expect(getByText(messages.headingSubtitle.defaultMessage)).toBeInTheDocument(); const advancedSettingsElement = getByText(messages.headingTitle.defaultMessage, { @@ -72,7 +52,7 @@ describe('', () => { }); }); it('should render setting element', async () => { - const { getByText, queryByText } = render(); + const { getByText, queryByText } = render(); await waitFor(() => { const advancedModuleListTitle = getByText(/Advanced Module List/i); expect(advancedModuleListTitle).toBeInTheDocument(); @@ -80,7 +60,7 @@ describe('', () => { }); }); it('should change to onДhange', async () => { - const { getByLabelText } = render(); + const { getByLabelText } = render(); await waitFor(() => { const textarea = getByLabelText(/Advanced Module List/i); expect(textarea).toBeInTheDocument(); @@ -89,7 +69,7 @@ describe('', () => { }); }); it('should display a warning alert', async () => { - const { getByLabelText, getByText } = render(); + const { getByLabelText, getByText } = render(); await waitFor(() => { const textarea = getByLabelText(/Advanced Module List/i); fireEvent.change(textarea, { target: { value: '[3, 2, 1]' } }); @@ -100,7 +80,7 @@ describe('', () => { }); }); it('should display a tooltip on clicking on the icon', async () => { - const { getByLabelText, getByText } = render(); + const { getByLabelText, getByText } = render(); await waitFor(() => { const button = getByLabelText(/Show help text/i); fireEvent.click(button); @@ -108,7 +88,7 @@ describe('', () => { }); }); it('should change deprecated button text ', async () => { - const { getByText } = render(); + const { getByText } = render(); await waitFor(() => { const showDeprecatedItemsBtn = getByText(/Show Deprecated Settings/i); expect(showDeprecatedItemsBtn).toBeInTheDocument(); @@ -118,7 +98,7 @@ describe('', () => { expect(getByText('Certificate web/html view enabled')).toBeInTheDocument(); }); it('should reset to default value on click on Cancel button', async () => { - const { getByLabelText, getByText } = render(); + const { getByLabelText, getByText } = render(); let textarea; await waitFor(() => { textarea = getByLabelText(/Advanced Module List/i); @@ -129,7 +109,7 @@ describe('', () => { expect(textarea.value).toBe('[]'); }); it('should update the textarea value and display the updated value after clicking "Change manually"', async () => { - const { getByLabelText, getByText } = render(); + const { getByLabelText, getByText } = render(); let textarea; await waitFor(() => { textarea = getByLabelText(/Advanced Module List/i); @@ -141,7 +121,7 @@ describe('', () => { expect(textarea.value).toBe('[3, 2, 1,'); }); it('should show success alert after save', async () => { - const { getByLabelText, getByText } = render(); + const { getByLabelText, getByText } = render(); let textarea; await waitFor(() => { textarea = getByLabelText(/Advanced Module List/i); diff --git a/src/certificates/Certificates.test.jsx b/src/certificates/Certificates.test.jsx index e04d50a21..78fd1791a 100644 --- a/src/certificates/Certificates.test.jsx +++ b/src/certificates/Certificates.test.jsx @@ -1,14 +1,9 @@ -import { render, waitFor } from '@testing-library/react'; +// @ts-check import userEvent from '@testing-library/user-event'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; -import { initializeMockApp } from '@edx/frontend-platform'; -import { AppProvider } from '@edx/frontend-platform/react'; -import MockAdapter from 'axios-mock-adapter'; -import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; +import { initializeMocks, render, waitFor } from '../testUtils'; import { RequestStatus } from '../data/constants'; import { executeThunk } from '../utils'; -import initializeStore from '../store'; import { getCertificatesApiUrl } from './data/api'; import { fetchCertificates } from './data/thunks'; import { certificatesDataMock } from './__mocks__'; @@ -19,26 +14,13 @@ let axiosMock; let store; const courseId = 'course-123'; -const renderComponent = (props) => render( - - - - - , -); +const renderComponent = (props) => render(); describe('Certificates', () => { beforeEach(async () => { - initializeMockApp({ - authenticatedUser: { - userId: 3, - username: 'abc123', - administrator: true, - roles: [], - }, - }); - store = initializeStore(); - axiosMock = new MockAdapter(getAuthenticatedHttpClient()); + const mocks = initializeMocks(); + store = mocks.reduxStore; + axiosMock = mocks.axiosMock; }); it('renders WithoutModes when there are certificates but no certificate modes', async () => { diff --git a/src/certificates/layout/certificates-sidebar/CertificatesSidebar.test.jsx b/src/certificates/layout/certificates-sidebar/CertificatesSidebar.test.jsx index eb00e888d..3dd87ad7e 100644 --- a/src/certificates/layout/certificates-sidebar/CertificatesSidebar.test.jsx +++ b/src/certificates/layout/certificates-sidebar/CertificatesSidebar.test.jsx @@ -1,14 +1,9 @@ -import { render, waitFor } from '@testing-library/react'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; -import { initializeMockApp } from '@edx/frontend-platform'; -import { AppProvider } from '@edx/frontend-platform/react'; - -import initializeStore from '../../../store'; +// @ts-check import CertificatesSidebar from './CertificatesSidebar'; import messages from './messages'; +import { initializeMocks, render, waitFor } from '../../../testUtils'; const courseId = 'course-123'; -let store; jest.mock('@edx/frontend-platform/i18n', () => ({ ...jest.requireActual('@edx/frontend-platform/i18n'), @@ -17,25 +12,11 @@ jest.mock('@edx/frontend-platform/i18n', () => ({ }), })); -const renderComponent = (props) => render( - - - - - , -); +const renderComponent = (props) => render(); describe('CertificatesSidebar', () => { beforeEach(() => { - initializeMockApp({ - authenticatedUser: { - userId: 3, - username: 'abc123', - administrator: true, - roles: [], - }, - }); - store = initializeStore(); + initializeMocks(); }); it('renders correctly', async () => { diff --git a/src/course-checklist/ChecklistSection/ChecklistSection.test.jsx b/src/course-checklist/ChecklistSection/ChecklistSection.test.jsx index 09aa81d33..2fc73e66d 100644 --- a/src/course-checklist/ChecklistSection/ChecklistSection.test.jsx +++ b/src/course-checklist/ChecklistSection/ChecklistSection.test.jsx @@ -4,9 +4,7 @@ import { initializeMocks, render, screen, within, } from '../../testUtils'; import { getApiWaffleFlagsUrl } from '../../data/api'; -import { fetchWaffleFlags } from '../../data/thunks'; import { generateCourseLaunchData } from '../factories/mockApiResponses'; -import { executeThunk } from '../../utils'; import { checklistItems } from './utils/courseChecklistData'; import messages from './messages'; @@ -34,7 +32,7 @@ const renderComponent = (props) => { describe('ChecklistSection', () => { beforeEach(async () => { - const { axiosMock, reduxStore } = initializeMocks(); + const { axiosMock } = initializeMocks(); axiosMock .onGet(getApiWaffleFlagsUrl(courseId)) .reply(200, { @@ -43,7 +41,6 @@ describe('ChecklistSection', () => { useNewScheduleDetailsPage: true, useNewCourseOutlinePage: true, }); - await executeThunk(fetchWaffleFlags(courseId), reduxStore.dispatch); }); it('a heading using the dataHeading prop', () => { diff --git a/src/course-outline/outline-sidebar/OutlineSidebar.test.jsx b/src/course-outline/outline-sidebar/OutlineSidebar.test.jsx index 0df0095bb..d9af4a979 100644 --- a/src/course-outline/outline-sidebar/OutlineSidebar.test.jsx +++ b/src/course-outline/outline-sidebar/OutlineSidebar.test.jsx @@ -1,29 +1,10 @@ -import React from 'react'; -import { render, waitFor } from '@testing-library/react'; -import MockAdapter from 'axios-mock-adapter'; -import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; -import { initializeMockApp } from '@edx/frontend-platform'; -import { AppProvider } from '@edx/frontend-platform/react'; - +// @ts-check +import { initializeMocks, render, waitFor } from '../../testUtils'; import { helpUrls } from '../../help-urls/__mocks__'; import { getHelpUrlsApiUrl } from '../../help-urls/data/api'; -import initializeStore from '../../store'; import OutlineSidebar from './OutlineSidebar'; import messages from './messages'; -let axiosMock; -let store; -const mockPathname = '/foo-bar'; -const courseId = '123'; - -jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useLocation: () => ({ - pathname: mockPathname, - }), -})); - jest.mock('@edx/frontend-platform/i18n', () => ({ ...jest.requireActual('@edx/frontend-platform/i18n'), useIntl: () => ({ @@ -31,26 +12,16 @@ jest.mock('@edx/frontend-platform/i18n', () => ({ }), })); -const renderComponent = (props) => render( - - - - - , -); +let axiosMock; +const mockPathname = '/foo-bar'; +const courseId = '123'; + +const renderComponent = (props) => render(, { path: mockPathname }); describe('', () => { beforeEach(() => { - initializeMockApp({ - authenticatedUser: { - userId: 3, - username: 'abc123', - administrator: true, - roles: [], - }, - }); - store = initializeStore(); - axiosMock = new MockAdapter(getAuthenticatedHttpClient()); + const mocks = initializeMocks(); + axiosMock = mocks.axiosMock; axiosMock .onGet(getHelpUrlsApiUrl()) .reply(200, helpUrls); diff --git a/src/course-rerun/CourseRerun.test.jsx b/src/course-rerun/CourseRerun.test.jsx index bb02822b7..6f5cc97d4 100644 --- a/src/course-rerun/CourseRerun.test.jsx +++ b/src/course-rerun/CourseRerun.test.jsx @@ -1,72 +1,37 @@ -import React from 'react'; import { useSelector } from 'react-redux'; -import { MemoryRouter } from 'react-router-dom'; -import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; -import { initializeMockApp } from '@edx/frontend-platform'; -import { IntlProvider, injectIntl } from '@edx/frontend-platform/i18n'; -import { AppProvider } from '@edx/frontend-platform/react'; -import { - fireEvent, render, waitFor, -} from '@testing-library/react'; -import MockAdapter from 'axios-mock-adapter'; -import initializeStore from '../store'; +import { + initializeMocks, + fireEvent, + render, + waitFor, +} from '../testUtils'; import { studioHomeMock } from '../studio-home/__mocks__'; import { getStudioHomeApiUrl } from '../studio-home/data/api'; import { RequestStatus } from '../data/constants'; import messages from './messages'; import CourseRerun from '.'; -let axiosMock; -let store; -const mockPathname = '/foo-bar'; - jest.mock('react-redux', () => ({ ...jest.requireActual('react-redux'), useSelector: jest.fn(), })); -jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useLocation: () => ({ - pathname: mockPathname, - }), -})); - -const RootWrapper = () => ( - - - - - - - -); - describe('', () => { beforeEach(() => { - initializeMockApp({ - authenticatedUser: { - userId: 3, - username: 'abc123', - administrator: true, - roles: [], - }, - }); - store = initializeStore(); - axiosMock = new MockAdapter(getAuthenticatedHttpClient()); + const { axiosMock } = initializeMocks(); axiosMock.onGet(getStudioHomeApiUrl()).reply(200, studioHomeMock); useSelector.mockReturnValue(studioHomeMock); }); it('should render successfully', () => { - const { getByText, getAllByRole } = render(); + const { getByText, getAllByRole } = render(); expect(getByText(messages.rerunTitle.defaultMessage)); expect(getAllByRole('button', { name: messages.cancelButton.defaultMessage }).length).toBe(2); }); it('should navigate to /home on cancel button click', () => { - const { getAllByRole } = render(); + const { getAllByRole } = render(); const cancelButton = getAllByRole('button', { name: messages.cancelButton.defaultMessage })[0]; fireEvent.click(cancelButton); @@ -78,13 +43,13 @@ describe('', () => { it('shows the spinner before the query is complete', async () => { useSelector.mockReturnValue({ organizationLoadingStatus: RequestStatus.IN_PROGRESS }); - const { findByRole } = render(); + const { findByRole } = render(); const spinner = await findByRole('status'); expect(spinner.textContent).toEqual('Loading...'); }); it('should show footer', async () => { - const { findByText } = render(); + const { findByText } = render(); await findByText('Looking for help with Studio?'); const lmsElement = await findByText('LMS'); expect(lmsElement).toHaveAttribute('href', process.env.LMS_BASE_URL); diff --git a/src/course-rerun/course-rerun-sidebar/CourseRerunSidebar.test.jsx b/src/course-rerun/course-rerun-sidebar/CourseRerunSidebar.test.jsx index d497d873f..ad97a4638 100644 --- a/src/course-rerun/course-rerun-sidebar/CourseRerunSidebar.test.jsx +++ b/src/course-rerun/course-rerun-sidebar/CourseRerunSidebar.test.jsx @@ -1,43 +1,15 @@ -import React from 'react'; -import { render } from '@testing-library/react'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; -import { initializeMockApp } from '@edx/frontend-platform'; -import { AppProvider } from '@edx/frontend-platform/react'; - -import initializeStore from '../../store'; +// @ts-check +import { initializeMocks, render } from '../../testUtils'; import CourseRerunSideBar from '.'; import messages from './messages'; -let store; -const mockPathname = '/foo-bar'; const courseId = '123'; -jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useLocation: () => ({ - pathname: mockPathname, - }), -})); - -const renderComponent = (props) => render( - - - - - , -); +const renderComponent = (props) => render(); describe('', () => { beforeEach(() => { - initializeMockApp({ - authenticatedUser: { - userId: 3, - username: 'abc123', - administrator: true, - roles: [], - }, - }); - store = initializeStore(); + initializeMocks(); }); it('render CourseRerunSideBar successfully', () => { diff --git a/src/course-team/CourseTeam.jsx b/src/course-team/CourseTeam.jsx index 29ad5b80f..20b594af2 100644 --- a/src/course-team/CourseTeam.jsx +++ b/src/course-team/CourseTeam.jsx @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { useIntl, injectIntl } from '@edx/frontend-platform/i18n'; +import { useIntl } from '@edx/frontend-platform/i18n'; import { Button, Container, @@ -175,4 +175,4 @@ CourseTeam.propTypes = { courseId: PropTypes.string.isRequired, }; -export default injectIntl(CourseTeam); +export default CourseTeam; diff --git a/src/course-team/CourseTeam.test.jsx b/src/course-team/CourseTeam.test.jsx index 046e8ef95..71239a198 100644 --- a/src/course-team/CourseTeam.test.jsx +++ b/src/course-team/CourseTeam.test.jsx @@ -1,16 +1,4 @@ -import { - render, - fireEvent, - cleanup, - waitFor, -} from '@testing-library/react'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; -import { AppProvider } from '@edx/frontend-platform/react'; -import { initializeMockApp } from '@edx/frontend-platform'; -import MockAdapter from 'axios-mock-adapter'; -import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; - -import initializeStore from '../store'; +// @ts-check import { courseTeamMock, courseTeamWithOneUser, courseTeamWithoutUsers } from './__mocks__'; import { getCourseTeamApiUrl, updateCourseTeamUserApiUrl } from './data/api'; import CourseTeam from './CourseTeam'; @@ -19,40 +7,25 @@ import { USER_ROLES } from '../constants'; import { executeThunk } from '../utils'; import { RequestStatus } from '../data/constants'; import { changeRoleTeamUserQuery, deleteCourseTeamQuery } from './data/thunk'; +import { + fireEvent, + initializeMocks, + render as baseRender, + waitFor, +} from '../testUtils'; let axiosMock; let store; const mockPathname = '/foo-bar'; const courseId = '123'; -jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useLocation: () => ({ - pathname: mockPathname, - }), -})); - -const RootWrapper = () => ( - - - - - -); +const render = () => baseRender(, { path: mockPathname }); describe('', () => { beforeEach(() => { - initializeMockApp({ - authenticatedUser: { - userId: 3, - username: 'abc123', - administrator: true, - roles: [], - }, - }); - - store = initializeStore(); - axiosMock = new MockAdapter(getAuthenticatedHttpClient()); + const mocks = initializeMocks(); + store = mocks.reduxStore; + axiosMock = mocks.axiosMock; }); it('render CourseTeam component with 3 team members correctly', async () => { @@ -62,7 +35,7 @@ describe('', () => { const { getByText, getByRole, getByTestId, queryAllByTestId, - } = render(); + } = render(); await waitFor(() => { expect(getByText(messages.headingTitle.defaultMessage)).toBeInTheDocument(); @@ -74,14 +47,13 @@ describe('', () => { }); it('render CourseTeam component with 1 team member correctly', async () => { - cleanup(); axiosMock .onGet(getCourseTeamApiUrl(courseId)) .reply(200, courseTeamWithOneUser); const { getByText, getByRole, getByTestId, getAllByTestId, - } = render(); + } = render(); await waitFor(() => { expect(getByText(messages.headingTitle.defaultMessage)).toBeInTheDocument(); @@ -93,14 +65,13 @@ describe('', () => { }); it('render CourseTeam component without team member correctly', async () => { - cleanup(); axiosMock .onGet(getCourseTeamApiUrl(courseId)) .reply(200, courseTeamWithoutUsers); const { getByText, getByRole, getByTestId, queryAllByTestId, - } = render(); + } = render(); await waitFor(() => { expect(getByText(messages.headingTitle.defaultMessage)).toBeInTheDocument(); @@ -112,12 +83,11 @@ describe('', () => { }); it('render CourseTeam component with initial sidebar correctly', async () => { - cleanup(); axiosMock .onGet(getCourseTeamApiUrl(courseId)) .reply(200, courseTeamWithoutUsers); - const { getByTestId, queryByTestId } = render(); + const { getByTestId, queryByTestId } = render(); await waitFor(() => { expect(getByTestId('course-team-sidebar__initial')).toBeInTheDocument(); @@ -126,12 +96,11 @@ describe('', () => { }); it('render CourseTeam component without initial sidebar correctly', async () => { - cleanup(); axiosMock .onGet(getCourseTeamApiUrl(courseId)) .reply(200, courseTeamMock); - const { getByTestId, queryByTestId } = render(); + const { getByTestId, queryByTestId } = render(); await waitFor(() => { expect(queryByTestId('course-team-sidebar__initial')).not.toBeInTheDocument(); @@ -140,12 +109,11 @@ describe('', () => { }); it('displays AddUserForm when clicking the "Add New Member" button', async () => { - cleanup(); axiosMock .onGet(getCourseTeamApiUrl(courseId)) .reply(200, courseTeamWithOneUser); - const { getByRole, queryByTestId } = render(); + const { getByRole, queryByTestId } = render(); await waitFor(() => { expect(queryByTestId('add-user-form')).not.toBeInTheDocument(); @@ -156,12 +124,11 @@ describe('', () => { }); it('displays AddUserForm when clicking the "Add a New Team member" button', async () => { - cleanup(); axiosMock .onGet(getCourseTeamApiUrl(courseId)) .reply(200, courseTeamWithOneUser); - const { getByRole, queryByTestId } = render(); + const { getByRole, queryByTestId } = render(); await waitFor(() => { expect(queryByTestId('add-user-form')).not.toBeInTheDocument(); @@ -172,7 +139,6 @@ describe('', () => { }); it('not displays "Add New Member" and AddTeamMember component when isAllowActions is false', async () => { - cleanup(); axiosMock .onGet(getCourseTeamApiUrl(courseId)) .reply(200, { @@ -180,7 +146,7 @@ describe('', () => { allowActions: false, }); - const { queryByRole, queryByTestId } = render(); + const { queryByRole, queryByTestId } = render(); await waitFor(() => { expect(queryByRole('button', { name: messages.addNewMemberButton.defaultMessage })).not.toBeInTheDocument(); @@ -189,12 +155,11 @@ describe('', () => { }); it('should delete user', async () => { - cleanup(); axiosMock .onGet(getCourseTeamApiUrl(courseId)) .reply(200, courseTeamMock); - const { queryByText } = render(); + const { queryByText } = render(); axiosMock .onDelete(updateCourseTeamUserApiUrl(courseId, 'staff@example.com')) @@ -205,15 +170,14 @@ describe('', () => { }); it('should change role user', async () => { - cleanup(); axiosMock .onGet(getCourseTeamApiUrl(courseId)) .reply(200, courseTeamMock); - const { getAllByText } = render(); + const { getAllByText } = render(); axiosMock - .onPut(updateCourseTeamUserApiUrl(courseId, 'staff@example.com', { role: USER_ROLES.admin })) + .onPut(updateCourseTeamUserApiUrl(courseId, 'staff@example.com')) .reply(200, { role: USER_ROLES.admin }); await executeThunk(changeRoleTeamUserQuery(courseId, 'staff@example.com', { role: USER_ROLES.admin }), store.dispatch); @@ -225,7 +189,7 @@ describe('', () => { .onGet(getCourseTeamApiUrl(courseId)) .reply(403); - const { getByRole } = render(); + const { getByRole } = render(); await waitFor(() => { expect(getByRole('alert')).toBeInTheDocument(); @@ -239,7 +203,7 @@ describe('', () => { .onGet(getCourseTeamApiUrl(courseId)) .reply(404); - render(); + render(); await waitFor(() => { const { loadingCourseTeamStatus } = store.getState().courseTeam; diff --git a/src/course-team/course-team-sidebar/CourseTeamSideBar.test.jsx b/src/course-team/course-team-sidebar/CourseTeamSideBar.test.jsx index 7218ba40b..daab5fb7e 100644 --- a/src/course-team/course-team-sidebar/CourseTeamSideBar.test.jsx +++ b/src/course-team/course-team-sidebar/CourseTeamSideBar.test.jsx @@ -1,35 +1,15 @@ -import React from 'react'; -import { render } from '@testing-library/react'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; -import { initializeMockApp } from '@edx/frontend-platform'; -import { AppProvider } from '@edx/frontend-platform/react'; - +// @ts-check +import { initializeMocks, render } from '../../testUtils'; import CourseTeamSidebar from './CourseTeamSidebar'; import messages from './messages'; -import initializeStore from '../../store'; const courseId = 'course-123'; -let store; -const renderComponent = (props) => render( - - - - - , -); +const renderComponent = (props) => render(); describe('', () => { beforeEach(() => { - initializeMockApp({ - authenticatedUser: { - userId: 3, - username: 'abc123', - administrator: true, - roles: [], - }, - }); - store = initializeStore(); + initializeMocks(); }); it('render CourseTeamSidebar component correctly', () => { diff --git a/src/course-unit/add-component/AddComponent.test.jsx b/src/course-unit/add-component/AddComponent.test.jsx index d3c31ec7b..cab41c0a9 100644 --- a/src/course-unit/add-component/AddComponent.test.jsx +++ b/src/course-unit/add-component/AddComponent.test.jsx @@ -1,16 +1,14 @@ /* eslint-disable react/prop-types */ -import MockAdapter from 'axios-mock-adapter'; -import { - act, render, screen, waitFor, within, -} from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { initializeMockApp } from '@edx/frontend-platform'; -import { AppProvider } from '@edx/frontend-platform/react'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; -import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; - -import initializeStore from '../../store'; +import { + act, + render, + screen, + waitFor, + within, + initializeMocks, +} from '../../testUtils'; import { executeThunk } from '../../utils'; import { fetchCourseSectionVerticalData } from '../data/thunk'; import { getCourseSectionVerticalApiUrl } from '../data/api'; @@ -59,35 +57,23 @@ jest.mock('../../generic/hooks/context/hooks', () => ({ })); const renderComponent = (props) => render( - - - - - - - , + + + , ); describe('', () => { beforeEach(async () => { - initializeMockApp({ - authenticatedUser: { - userId: 3, - username: 'abc123', - administrator: true, - roles: [], - }, - }); - - store = initializeStore(); - axiosMock = new MockAdapter(getAuthenticatedHttpClient()); + const mocks = initializeMocks(); + axiosMock = mocks.axiosMock; + store = mocks.reduxStore; axiosMock .onGet(getCourseSectionVerticalApiUrl(blockId)) .reply(200, courseSectionVerticalMock); diff --git a/src/custom-pages/CustomPages.test.jsx b/src/custom-pages/CustomPages.test.jsx index de57e25b8..a5f2c4407 100644 --- a/src/custom-pages/CustomPages.test.jsx +++ b/src/custom-pages/CustomPages.test.jsx @@ -10,7 +10,6 @@ import { import { executeThunk } from '../utils'; import { RequestStatus } from '../data/constants'; import { getApiWaffleFlagsUrl } from '../data/api'; -import { fetchWaffleFlags } from '../data/thunks'; import CustomPages from './CustomPages'; import { generateFetchPageApiResponse, @@ -62,7 +61,6 @@ describe('CustomPages', () => { useNewScheduleDetailsPage: true, useNewCourseOutlinePage: true, }); - await executeThunk(fetchWaffleFlags(courseId), store.dispatch); }); it('should ', async () => { renderComponent(); diff --git a/src/export-page/CourseExportPage.test.jsx b/src/export-page/CourseExportPage.test.jsx index 4c0a01d5e..466a5434f 100644 --- a/src/export-page/CourseExportPage.test.jsx +++ b/src/export-page/CourseExportPage.test.jsx @@ -1,13 +1,13 @@ -import { getConfig, initializeMockApp } from '@edx/frontend-platform'; -import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; -import { IntlProvider, injectIntl } from '@edx/frontend-platform/i18n'; -import { AppProvider } from '@edx/frontend-platform/react'; -import { fireEvent, render, waitFor } from '@testing-library/react'; -import MockAdapter from 'axios-mock-adapter'; +import { getConfig } from '@edx/frontend-platform'; import { Helmet } from 'react-helmet'; import Cookies from 'universal-cookie'; -import initializeStore from '../store'; +import { + initializeMocks, + fireEvent, + render, + waitFor, +} from '../testUtils'; import { RequestStatus } from '../data/constants'; import stepperMessages from './export-stepper/messages'; import modalErrorMessages from './export-modal-error/messages'; @@ -37,26 +37,13 @@ jest.mock('universal-cookie', () => { return jest.fn(() => mCookie); }); -const RootWrapper = () => ( - - - - - -); +const renderComponent = () => render(); describe('', () => { beforeEach(() => { - initializeMockApp({ - authenticatedUser: { - userId: 3, - username: 'abc123', - administrator: true, - roles: [], - }, - }); - store = initializeStore(); - axiosMock = new MockAdapter(getAuthenticatedHttpClient()); + const mocks = initializeMocks(); + store = mocks.reduxStore; + axiosMock = mocks.axiosMock; axiosMock .onGet(postExportCourseApiUrl(courseId)) .reply(200, exportPageMock); @@ -64,7 +51,7 @@ describe('', () => { cookies.get.mockReturnValue(null); }); it('should render page title correctly', async () => { - render(); + renderComponent(); await waitFor(() => { const helmet = Helmet.peek(); expect(helmet.title).toEqual( @@ -73,7 +60,7 @@ describe('', () => { }); }); it('should render without errors', async () => { - const { getByText } = render(); + const { getByText } = renderComponent(); await waitFor(() => { expect(getByText(messages.headingSubtitle.defaultMessage)).toBeInTheDocument(); const exportPageElement = getByText(messages.headingTitle.defaultMessage, { @@ -86,7 +73,7 @@ describe('', () => { }); }); it('should start exporting on click', async () => { - const { getByText, container } = render(); + const { getByText, container } = renderComponent(); const button = container.querySelector('.btn-primary'); fireEvent.click(button); expect(getByText(stepperMessages.stepperPreparingDescription.defaultMessage)).toBeInTheDocument(); @@ -95,7 +82,7 @@ describe('', () => { axiosMock .onGet(getExportStatusApiUrl(courseId)) .reply(200, { exportStatus: EXPORT_STAGES.EXPORTING, exportError: { rawErrorMsg: 'test error', editUnitUrl: 'http://test-url.test' } }); - const { getByText, queryByText, container } = render(); + const { getByText, queryByText, container } = renderComponent(); const startExportButton = container.querySelector('.btn-primary'); fireEvent.click(startExportButton); // eslint-disable-next-line no-promise-executor-return @@ -108,45 +95,45 @@ describe('', () => { }); it('should fetch status without clicking when cookies has', async () => { cookies.get.mockReturnValue({ date: 1679787000 }); - const { getByText } = render(); + const { getByText } = renderComponent(); expect(getByText(stepperMessages.stepperPreparingDescription.defaultMessage)).toBeInTheDocument(); }); it('should show download path for relative path', async () => { axiosMock .onGet(getExportStatusApiUrl(courseId)) .reply(200, { exportStatus: EXPORT_STAGES.SUCCESS, exportOutput: '/test-download-path.test' }); - const { getByText, container } = render(); + const { getByText, container } = renderComponent(); const startExportButton = container.querySelector('.btn-primary'); fireEvent.click(startExportButton); - // eslint-disable-next-line no-promise-executor-return - await new Promise((r) => setTimeout(r, 3500)); - const downloadButton = getByText(stepperMessages.downloadCourseButtonTitle.defaultMessage); - expect(downloadButton).toBeInTheDocument(); - expect(downloadButton.getAttribute('href')).toEqual(`${getConfig().STUDIO_BASE_URL}/test-download-path.test`); + await waitFor(() => { + const downloadButton = getByText(stepperMessages.downloadCourseButtonTitle.defaultMessage); + expect(downloadButton).toBeInTheDocument(); + expect(downloadButton.getAttribute('href')).toEqual(`${getConfig().STUDIO_BASE_URL}/test-download-path.test`); + }, { timeout: 4_000 }); }); it('should show download path for absolute path', async () => { axiosMock .onGet(getExportStatusApiUrl(courseId)) .reply(200, { exportStatus: EXPORT_STAGES.SUCCESS, exportOutput: 'http://test-download-path.test' }); - const { getByText, container } = render(); + const { getByText, container } = renderComponent(); const startExportButton = container.querySelector('.btn-primary'); fireEvent.click(startExportButton); - // eslint-disable-next-line no-promise-executor-return - await new Promise((r) => setTimeout(r, 3500)); - const downloadButton = getByText(stepperMessages.downloadCourseButtonTitle.defaultMessage); - expect(downloadButton).toBeInTheDocument(); - expect(downloadButton.getAttribute('href')).toEqual('http://test-download-path.test'); + await waitFor(() => { + const downloadButton = getByText(stepperMessages.downloadCourseButtonTitle.defaultMessage); + expect(downloadButton).toBeInTheDocument(); + expect(downloadButton.getAttribute('href')).toEqual('http://test-download-path.test'); + }, { timeout: 4_000 }); }); it('displays an alert and sets status to DENIED when API responds with 403', async () => { axiosMock .onGet(getExportStatusApiUrl(courseId)) .reply(403); - const { getByRole, container } = render(); + const { getByRole, container } = renderComponent(); const startExportButton = container.querySelector('.btn-primary'); fireEvent.click(startExportButton); - // eslint-disable-next-line no-promise-executor-return - await new Promise((r) => setTimeout(r, 3500)); - expect(getByRole('alert')).toBeInTheDocument(); + await waitFor(() => { + expect(getByRole('alert')).toBeInTheDocument(); + }, { timeout: 4_000 }); const { loadingStatus } = store.getState().courseExport; expect(loadingStatus).toEqual(RequestStatus.DENIED); }); @@ -155,12 +142,12 @@ describe('', () => { axiosMock .onGet(getExportStatusApiUrl(courseId)) .reply(404); - const { container } = render(); + const { container } = renderComponent(); const startExportButton = container.querySelector('.btn-primary'); fireEvent.click(startExportButton); - // eslint-disable-next-line no-promise-executor-return - await new Promise((r) => setTimeout(r, 3500)); - const { loadingStatus } = store.getState().courseExport; - expect(loadingStatus).toEqual(RequestStatus.FAILED); + await waitFor(() => { + const { loadingStatus } = store.getState().courseExport; + expect(loadingStatus).toEqual(RequestStatus.FAILED); + }, { timeout: 4_000 }); }); }); diff --git a/src/export-page/export-sidebar/ExportSidebar.test.jsx b/src/export-page/export-sidebar/ExportSidebar.test.jsx index d770f4b15..7fe777425 100644 --- a/src/export-page/export-sidebar/ExportSidebar.test.jsx +++ b/src/export-page/export-sidebar/ExportSidebar.test.jsx @@ -1,38 +1,16 @@ -import React from 'react'; -import { render } from '@testing-library/react'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; -import { initializeMockApp } from '@edx/frontend-platform'; -import { AppProvider } from '@edx/frontend-platform/react'; - -import initializeStore from '../../store'; +// @ts-check +import { initializeMocks, render } from '../../testUtils'; import messages from './messages'; import ExportSidebar from './ExportSidebar'; const courseId = 'course-123'; -let store; - -const RootWrapper = () => ( - - - - - -); describe('', () => { beforeEach(() => { - initializeMockApp({ - authenticatedUser: { - userId: 3, - username: 'abc123', - administrator: true, - roles: [], - }, - }); - store = initializeStore(); + initializeMocks(); }); it('render sidebar correctly', () => { - const { getByText } = render(); + const { getByText } = render(); expect(getByText(messages.title1.defaultMessage)).toBeInTheDocument(); expect(getByText(messages.exportedContentHeading.defaultMessage)).toBeInTheDocument(); }); diff --git a/src/generic/help-sidebar/HelpSidebar.test.jsx b/src/generic/help-sidebar/HelpSidebar.test.jsx index fe8b03db2..5e530be30 100644 --- a/src/generic/help-sidebar/HelpSidebar.test.jsx +++ b/src/generic/help-sidebar/HelpSidebar.test.jsx @@ -1,32 +1,16 @@ -import React from 'react'; -import { render } from '@testing-library/react'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; -import { AppProvider } from '@edx/frontend-platform/react'; -import { initializeMockApp } from '@edx/frontend-platform'; -import initializeStore from '../../store'; +// @ts-check + +import { initializeMocks, render } from '../../testUtils'; import messages from './messages'; import { HelpSidebar } from '.'; const mockPathname = '/foo-bar'; -let store; -jest.mock('react-router-dom', () => ({ - ...jest.requireActual('react-router-dom'), - useLocation: () => ({ - pathname: mockPathname, - }), -})); - -const RootWrapper = (props) => ( - - - -

Test children

-
-
-
+const renderHelpSidebar = (props) => render( + +

Test children

+
, + { path: mockPathname }, ); const props = { @@ -37,25 +21,16 @@ const props = { describe('HelpSidebar', () => { beforeEach(() => { - initializeMockApp({ - authenticatedUser: { - userId: 3, - username: 'abc123', - administrator: true, - roles: [], - }, - }); - - store = initializeStore(); + initializeMocks(); }); it('renders children correctly', () => { - const { getByText } = render(); + const { getByText } = renderHelpSidebar(props); expect(getByText('Test children')).toBeTruthy(); }); it('should render all sidebar links with correct text', () => { - const { getByText, queryByText } = render(); + const { getByText, queryByText } = renderHelpSidebar(props); expect(getByText(messages.sidebarTitleOther.defaultMessage)).toBeTruthy(); expect(getByText(messages.sidebarLinkToScheduleAndDetails.defaultMessage)).toBeTruthy(); expect(getByText(messages.sidebarLinkToGrading.defaultMessage)).toBeTruthy(); @@ -67,7 +42,7 @@ describe('HelpSidebar', () => { it('should hide other settings url if showOtherSettings disabled', () => { const initialProps = { ...props, showOtherSettings: false }; - const { queryByText } = render(); + const { queryByText } = renderHelpSidebar(initialProps); expect(queryByText(messages.sidebarTitleOther.defaultMessage)).toBeFalsy(); expect(queryByText(messages.sidebarLinkToScheduleAndDetails.defaultMessage)).toBeFalsy(); expect(queryByText(messages.sidebarLinkToGrading.defaultMessage)).toBeFalsy(); @@ -78,7 +53,7 @@ describe('HelpSidebar', () => { it('should render proctored mfe url only if passed not empty value', () => { const initialProps = { ...props, showOtherSettings: true, proctoredExamSettingsUrl: 'http:/link-to' }; - const { getByText } = render(); + const { getByText } = renderHelpSidebar(initialProps); expect(getByText(messages.sidebarLinkToProctoredExamSettings.defaultMessage)).toBeTruthy(); }); }); diff --git a/src/group-configurations/GroupConfigurations.test.jsx b/src/group-configurations/GroupConfigurations.test.jsx index 0cfec7b1d..b35784ae8 100644 --- a/src/group-configurations/GroupConfigurations.test.jsx +++ b/src/group-configurations/GroupConfigurations.test.jsx @@ -1,12 +1,10 @@ -import MockAdapter from 'axios-mock-adapter'; -import { render, waitFor, within } from '@testing-library/react'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; -import { AppProvider } from '@edx/frontend-platform/react'; -import { initializeMockApp } from '@edx/frontend-platform'; -import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; - +import { + initializeMocks, + render, + waitFor, + within, +} from '../testUtils'; import { RequestStatus } from '../data/constants'; -import initializeStore from '../store'; import { executeThunk } from '../utils'; import { getContentStoreApiUrl } from './data/api'; import { fetchGroupConfigurationsQuery } from './data/thunk'; @@ -22,27 +20,13 @@ const courseId = 'course-v1:org+101+101'; const enrollmentTrackGroups = groupConfigurationResponseMock.allGroupConfigurations[0]; const contentGroups = groupConfigurationResponseMock.allGroupConfigurations[1]; -const renderComponent = () => render( - - - - - , -); +const renderComponent = () => render(); describe('', () => { beforeEach(async () => { - initializeMockApp({ - authenticatedUser: { - userId: 3, - username: 'abc123', - administrator: true, - roles: [], - }, - }); - - store = initializeStore(); - axiosMock = new MockAdapter(getAuthenticatedHttpClient()); + const mocks = initializeMocks(); + store = mocks.reduxStore; + axiosMock = mocks.axiosMock; axiosMock .onGet(getContentStoreApiUrl(courseId)) .reply(200, groupConfigurationResponseMock); diff --git a/src/group-configurations/group-configuration-sidebar/GroupConfigurationSidebar.test.jsx b/src/group-configurations/group-configuration-sidebar/GroupConfigurationSidebar.test.jsx index eb5cb9988..3ffe15103 100644 --- a/src/group-configurations/group-configuration-sidebar/GroupConfigurationSidebar.test.jsx +++ b/src/group-configurations/group-configuration-sidebar/GroupConfigurationSidebar.test.jsx @@ -1,44 +1,18 @@ -import { render } from '@testing-library/react'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; -import { initializeMockApp } from '@edx/frontend-platform'; -import { AppProvider } from '@edx/frontend-platform/react'; - -import initializeStore from '../../store'; +// @ts-check +import { initializeMocks, render } from '../../testUtils'; import GroupConfigurationSidebar from '.'; import messages from './messages'; -let store; const courseId = 'course-123'; const enrollmentTrackTitle = messages.about_3_title.defaultMessage; const contentGroupTitle = messages.aboutTitle.defaultMessage; const experimentGroupTitle = messages.about_2_title.defaultMessage; -jest.mock('@edx/frontend-platform/i18n', () => ({ - ...jest.requireActual('@edx/frontend-platform/i18n'), - useIntl: () => ({ - formatMessage: (message) => message.defaultMessage, - }), -})); - -const renderComponent = (props) => render( - - - - , - , -); +const renderComponent = (props) => render(); describe('GroupConfigurationSidebar', () => { beforeEach(() => { - initializeMockApp({ - authenticatedUser: { - userId: 3, - username: 'abc123', - administrator: true, - roles: [], - }, - }); - store = initializeStore(); + initializeMocks(); }); it('renders all groups when all props are true', async () => { diff --git a/src/import-page/CourseImportPage.test.jsx b/src/import-page/CourseImportPage.test.jsx index 646eadbe6..14727728b 100644 --- a/src/import-page/CourseImportPage.test.jsx +++ b/src/import-page/CourseImportPage.test.jsx @@ -1,13 +1,7 @@ -import { initializeMockApp } from '@edx/frontend-platform'; -import { IntlProvider, injectIntl } from '@edx/frontend-platform/i18n'; -import { AppProvider } from '@edx/frontend-platform/react'; -import { render, waitFor } from '@testing-library/react'; import { Helmet } from 'react-helmet'; -import MockAdapter from 'axios-mock-adapter'; -import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; - import Cookies from 'universal-cookie'; -import initializeStore from '../store'; + +import { initializeMocks, render, waitFor } from '../testUtils'; import { RequestStatus } from '../data/constants'; import messages from './messages'; import CourseImportPage from './CourseImportPage'; @@ -35,26 +29,13 @@ jest.mock('universal-cookie', () => { return jest.fn(() => Cookie); }); -const RootWrapper = () => ( - - - - - -); +const renderComponent = () => render(); describe('', () => { beforeEach(() => { - initializeMockApp({ - authenticatedUser: { - userId: 3, - username: 'abc123', - administrator: true, - roles: [], - }, - }); - store = initializeStore(); - axiosMock = new MockAdapter(getAuthenticatedHttpClient()); + const mocks = initializeMocks(); + store = mocks.reduxStore; + axiosMock = mocks.axiosMock; axiosMock .onGet(getImportStatusApiUrl(courseId, 'testFileName.test')) .reply(200, { importStatus: 1, message: '' }); @@ -62,7 +43,7 @@ describe('', () => { cookies.get.mockReturnValue(null); }); it('should render page title correctly', async () => { - render(); + renderComponent(); await waitFor(() => { const helmet = Helmet.peek(); expect(helmet.title).toEqual( @@ -71,7 +52,7 @@ describe('', () => { }); }); it('should render without errors', async () => { - const { getByText } = render(); + const { getByText } = renderComponent(); await waitFor(() => { expect(getByText(messages.headingSubtitle.defaultMessage)).toBeInTheDocument(); const importPageElement = getByText(messages.headingTitle.defaultMessage, { @@ -85,7 +66,7 @@ describe('', () => { }); it('should fetch status without clicking when cookies has', async () => { cookies.get.mockReturnValue({ date: 1679787000, completed: false, fileName: 'testFileName.test' }); - const { getByText } = render(); + const { getByText } = renderComponent(); expect(getByText(stepperMessages.stepperUnpackingDescription.defaultMessage)).toBeInTheDocument(); }); it('should show error', async () => { @@ -93,20 +74,20 @@ describe('', () => { .onGet(getImportStatusApiUrl(courseId, 'testFileName.tar.gz')) .reply(200, { importStatus: -IMPORT_STAGES.UPDATING, message: '' }); cookies.get.mockReturnValue({ date: 1679787000, completed: false, fileName: 'testFileName.tar.gz' }); - const { getByText } = render(); - // eslint-disable-next-line no-promise-executor-return - await new Promise((r) => setTimeout(r, 3500)); - expect(getByText(stepperMessages.defaultErrorMessage.defaultMessage)).toBeInTheDocument(); + const { getByText } = renderComponent(); + await waitFor(() => { + expect(getByText(stepperMessages.defaultErrorMessage.defaultMessage)).toBeInTheDocument(); + }, { timeout: 4000 }); }); it('should show success button', async () => { axiosMock .onGet(getImportStatusApiUrl(courseId, 'testFileName.tar.gz')) .reply(200, { importStatus: IMPORT_STAGES.SUCCESS, message: '' }); cookies.get.mockReturnValue({ date: 1679787000, completed: false, fileName: 'testFileName.tar.gz' }); - const { getByText } = render(); - // eslint-disable-next-line no-promise-executor-return - await new Promise((r) => setTimeout(r, 3500)); - expect(getByText(stepperMessages.viewOutlineButton.defaultMessage)).toBeInTheDocument(); + const { getByText } = renderComponent(); + await waitFor(() => { + expect(getByText(stepperMessages.viewOutlineButton.defaultMessage)).toBeInTheDocument(); + }, { timeout: 4000 }); }); it('displays an alert and sets status to DENIED when API responds with 403', async () => { @@ -114,10 +95,10 @@ describe('', () => { .onGet(getImportStatusApiUrl(courseId, 'testFileName.tar.gz')) .reply(403); cookies.get.mockReturnValue({ date: 1679787000, completed: false, fileName: 'testFileName.tar.gz' }); - const { getByRole } = render(); - // eslint-disable-next-line no-promise-executor-return - await new Promise((r) => setTimeout(r, 3500)); - expect(getByRole('alert')).toBeInTheDocument(); + const { getByRole } = renderComponent(); + await waitFor(() => { + expect(getByRole('alert')).toBeInTheDocument(); + }, { timeout: 4000 }); const { loadingStatus } = store.getState().courseImport; expect(loadingStatus).toEqual(RequestStatus.DENIED); }); @@ -127,10 +108,10 @@ describe('', () => { .onGet(getImportStatusApiUrl(courseId, 'testFileName.tar.gz')) .reply(404); cookies.get.mockReturnValue({ date: 1679787000, completed: false, fileName: 'testFileName.tar.gz' }); - render(); - // eslint-disable-next-line no-promise-executor-return - await new Promise((r) => setTimeout(r, 3500)); - const { loadingStatus } = store.getState().courseImport; - expect(loadingStatus).toEqual(RequestStatus.FAILED); + renderComponent(); + await waitFor(() => { + const { loadingStatus } = store.getState().courseImport; + expect(loadingStatus).toEqual(RequestStatus.FAILED); + }, { timeout: 4000 }); }); }); diff --git a/src/import-page/import-sidebar/ImportSidebar.test.jsx b/src/import-page/import-sidebar/ImportSidebar.test.jsx index 1fb3c2e28..d80357f2b 100644 --- a/src/import-page/import-sidebar/ImportSidebar.test.jsx +++ b/src/import-page/import-sidebar/ImportSidebar.test.jsx @@ -1,38 +1,16 @@ -import React from 'react'; -import { render } from '@testing-library/react'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; -import { initializeMockApp } from '@edx/frontend-platform'; -import { AppProvider } from '@edx/frontend-platform/react'; - -import initializeStore from '../../store'; +// @ts-check +import { initializeMocks, render } from '../../testUtils'; import messages from './messages'; import ImportSidebar from './ImportSidebar'; const courseId = 'course-123'; -let store; - -const RootWrapper = () => ( - - - - - -); describe('', () => { beforeEach(() => { - initializeMockApp({ - authenticatedUser: { - userId: 3, - username: 'abc123', - administrator: true, - roles: [], - }, - }); - store = initializeStore(); + initializeMocks(); }); it('render sidebar correctly', () => { - const { getByText } = render(); + const { getByText } = render(); expect(getByText(messages.title1.defaultMessage)).toBeInTheDocument(); expect(getByText(messages.importedContentHeading.defaultMessage)).toBeInTheDocument(); }); diff --git a/src/pages-and-resources/PagesAndResources.test.jsx b/src/pages-and-resources/PagesAndResources.test.jsx index 0e662e3f8..9888586cf 100644 --- a/src/pages-and-resources/PagesAndResources.test.jsx +++ b/src/pages-and-resources/PagesAndResources.test.jsx @@ -1,9 +1,10 @@ +// @ts-check import { screen, waitFor } from '@testing-library/react'; import { getConfig, setConfig } from '@edx/frontend-platform'; import { PLUGIN_OPERATIONS, DIRECT_PLUGIN } from '@openedx/frontend-plugin-framework'; import { PagesAndResources } from '.'; -import { render } from './utils.test'; +import { initializeMocks, render } from '../testUtils'; const mockPlugin = (identifier) => ({ plugins: [ @@ -43,10 +44,8 @@ describe('PagesAndResources', () => { }, }; - render( - , - { preloadedState: initialState }, - ); + initializeMocks({ initialState }); + render(); await waitFor(() => expect(screen.queryByRole('heading', { name: 'Content permissions' })).not.toBeInTheDocument()); await waitFor(() => expect(screen.queryByTestId('additional_course_plugin')).toBeInTheDocument()); @@ -75,10 +74,8 @@ describe('PagesAndResources', () => { }, }; - render( - , - { preloadedState: initialState }, - ); + initializeMocks({ initialState }); + render(); await waitFor(() => expect(screen.getByRole('heading', { name: 'Content permissions' })).toBeInTheDocument()); await waitFor(() => expect(screen.getByText('Learning Assistant')).toBeInTheDocument()); @@ -110,10 +107,8 @@ describe('PagesAndResources', () => { }, }; - render( - , - { preloadedState: initialState }, - ); + initializeMocks({ initialState }); + render(); await waitFor(() => expect(screen.getByRole('heading', { name: 'Content permissions' })).toBeInTheDocument()); await waitFor(() => expect(screen.getByText('Xpert unit summaries')).toBeInTheDocument()); diff --git a/src/pages-and-resources/pages/PageCard.test.jsx b/src/pages-and-resources/pages/PageCard.test.jsx index 6a9c193fa..56a2deae0 100644 --- a/src/pages-and-resources/pages/PageCard.test.jsx +++ b/src/pages-and-resources/pages/PageCard.test.jsx @@ -7,14 +7,11 @@ import { waitFor, } from '../../testUtils'; import PageGrid from './PageGrid'; -import { executeThunk } from '../../utils'; import { getApiWaffleFlagsUrl } from '../../data/api'; -import { fetchWaffleFlags } from '../../data/thunks'; import PagesAndResourcesProvider from '../PagesAndResourcesProvider'; let container; -let store; let axiosMock; const courseId = '123'; const mockPageConfig = [ @@ -50,7 +47,6 @@ const renderComponent = () => { describe('LiveSettings', () => { beforeEach(async () => { const mocks = initializeMocks(); - store = mocks.reduxStore; axiosMock = mocks.axiosMock; axiosMock .onGet(getApiWaffleFlagsUrl(courseId)) @@ -60,7 +56,6 @@ describe('LiveSettings', () => { useNewScheduleDetailsPage: true, useNewCourseOutlinePage: true, }); - await executeThunk(fetchWaffleFlags(courseId), store.dispatch); }); it('should render three cards', async () => { diff --git a/src/pages-and-resources/utils.test.jsx b/src/pages-and-resources/utils.test.jsx deleted file mode 100644 index debd09158..000000000 --- a/src/pages-and-resources/utils.test.jsx +++ /dev/null @@ -1,48 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -import { render } from '@testing-library/react'; -import { BrowserRouter } from 'react-router-dom'; -import { configureStore } from '@reduxjs/toolkit'; -import { IntlProvider } from '@edx/frontend-platform/i18n'; -import { Provider } from 'react-redux'; - -import { reducer as modelsReducer } from '../generic/model-store'; -import { reducer as pagesAndResourcesReducer } from './data/slice'; -import PagesAndResourcesProvider from './PagesAndResourcesProvider'; - -function renderWithProviders( - ui, - { - preloadedState = {}, - // Automatically create a store instance if no store was passed in - store = configureStore( - { - reducer: { pagesAndResources: pagesAndResourcesReducer, models: modelsReducer }, preloadedState, - }, - ), - ...renderOptions - } = {}, -) { - const Wrapper = ({ children }) => ( - - {/* */} - - - {children} - - - {/* */} - - ); - - Wrapper.propTypes = { - children: PropTypes.node.isRequired, - }; - - // Return an object with the store and all of RTL's query functions - return { store, ...render(ui, { wrapper: Wrapper, ...renderOptions }) }; -} - -// We may add additional exports to this file over time, so we will not export render as the default. -export { renderWithProviders as render }; diff --git a/src/schedule-and-details/ScheduleAndDetails.test.jsx b/src/schedule-and-details/ScheduleAndDetails.test.jsx index 0afee52fe..400700efe 100644 --- a/src/schedule-and-details/ScheduleAndDetails.test.jsx +++ b/src/schedule-and-details/ScheduleAndDetails.test.jsx @@ -1,14 +1,11 @@ -import React from 'react'; -import { initializeMockApp } from '@edx/frontend-platform'; -import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; -import { IntlProvider, injectIntl } from '@edx/frontend-platform/i18n'; -import { AppProvider } from '@edx/frontend-platform/react'; +// @ts-check import { - act, render, waitFor, fireEvent, -} from '@testing-library/react'; -import MockAdapter from 'axios-mock-adapter'; - -import initializeStore from '../store'; + act, + initializeMocks, + render, + waitFor, + fireEvent, +} from '../testUtils'; import { executeThunk } from '../utils'; import { courseDetailsMock, courseSettingsMock } from './__mocks__'; import { getCourseDetailsApiUrl, getCourseSettingsApiUrl } from './data/api'; @@ -50,27 +47,11 @@ jest.mock('react-textarea-autosize', () => jest.fn((props) => (