test: refactor many test suites to use testUtils/initializeMocks (#2067)

Simplifies a bunch of test code by converting it to use our handy testUtils module.

None of the app code is change, just test code. And none of the test cases are modified in any meaningful way - this just simplifies the setup/cleanup significantly.
This commit is contained in:
Braden MacDonald
2025-06-03 13:52:56 -07:00
committed by GitHub
parent d806b6150d
commit 1dee2bba58
33 changed files with 279 additions and 892 deletions

View File

@@ -14,6 +14,5 @@ module.exports = createConfig('jest', {
'^CourseAuthoring/(.*)$': '<rootDir>/src/$1',
},
modulePathIgnorePatterns: [
'/src/pages-and-resources/utils.test.jsx',
],
});

View File

@@ -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(
<LearningAssistantSettings
onClose={onClose}
/>,
{
preloadedState: initialState,
},
);
initializeMocks({ initialState });
render(<LearningAssistantSettings onClose={onClose} />);
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 '

View File

@@ -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', () => {

View File

@@ -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('<CourseAuthoringRoutes>', () => {
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 () => {

View File

@@ -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(
<IntlProvider locale="en">
<AppProvider store={store}>
<AccessibilityPage />
</AppProvider>
</IntlProvider>,
);
};
const renderComponent = () => render(<AccessibilityPage />);
describe('<AccessibilityPolicyPage />', () => {
describe('renders', () => {
beforeEach(async () => {
initializeMockApp({
authenticatedUser: {
userId: 3,
username: 'abc123',
administrator: false,
roles: [],
},
});
store = initializeStore(initialState);
initializeMocks();
});
it('contains the policy body', () => {
renderComponent();

View File

@@ -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 = () => (
<AppProvider store={store}>
<IntlProvider locale="en" messages={{}}>
<AdvancedSettings intl={injectIntl} courseId={courseId} />
</IntlProvider>
</AppProvider>
const render = () => baseRender(
<AdvancedSettings courseId={courseId} />,
{ path: mockPathname },
);
describe('<AdvancedSettings />', () => {
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(<RootWrapper />);
const { getByText } = render();
await waitFor(() => {
expect(getByText(messages.headingSubtitle.defaultMessage)).toBeInTheDocument();
const advancedSettingsElement = getByText(messages.headingTitle.defaultMessage, {
@@ -72,7 +52,7 @@ describe('<AdvancedSettings />', () => {
});
});
it('should render setting element', async () => {
const { getByText, queryByText } = render(<RootWrapper />);
const { getByText, queryByText } = render();
await waitFor(() => {
const advancedModuleListTitle = getByText(/Advanced Module List/i);
expect(advancedModuleListTitle).toBeInTheDocument();
@@ -80,7 +60,7 @@ describe('<AdvancedSettings />', () => {
});
});
it('should change to onСhange', async () => {
const { getByLabelText } = render(<RootWrapper />);
const { getByLabelText } = render();
await waitFor(() => {
const textarea = getByLabelText(/Advanced Module List/i);
expect(textarea).toBeInTheDocument();
@@ -89,7 +69,7 @@ describe('<AdvancedSettings />', () => {
});
});
it('should display a warning alert', async () => {
const { getByLabelText, getByText } = render(<RootWrapper />);
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('<AdvancedSettings />', () => {
});
});
it('should display a tooltip on clicking on the icon', async () => {
const { getByLabelText, getByText } = render(<RootWrapper />);
const { getByLabelText, getByText } = render();
await waitFor(() => {
const button = getByLabelText(/Show help text/i);
fireEvent.click(button);
@@ -108,7 +88,7 @@ describe('<AdvancedSettings />', () => {
});
});
it('should change deprecated button text ', async () => {
const { getByText } = render(<RootWrapper />);
const { getByText } = render();
await waitFor(() => {
const showDeprecatedItemsBtn = getByText(/Show Deprecated Settings/i);
expect(showDeprecatedItemsBtn).toBeInTheDocument();
@@ -118,7 +98,7 @@ describe('<AdvancedSettings />', () => {
expect(getByText('Certificate web/html view enabled')).toBeInTheDocument();
});
it('should reset to default value on click on Cancel button', async () => {
const { getByLabelText, getByText } = render(<RootWrapper />);
const { getByLabelText, getByText } = render();
let textarea;
await waitFor(() => {
textarea = getByLabelText(/Advanced Module List/i);
@@ -129,7 +109,7 @@ describe('<AdvancedSettings />', () => {
expect(textarea.value).toBe('[]');
});
it('should update the textarea value and display the updated value after clicking "Change manually"', async () => {
const { getByLabelText, getByText } = render(<RootWrapper />);
const { getByLabelText, getByText } = render();
let textarea;
await waitFor(() => {
textarea = getByLabelText(/Advanced Module List/i);
@@ -141,7 +121,7 @@ describe('<AdvancedSettings />', () => {
expect(textarea.value).toBe('[3, 2, 1,');
});
it('should show success alert after save', async () => {
const { getByLabelText, getByText } = render(<RootWrapper />);
const { getByLabelText, getByText } = render();
let textarea;
await waitFor(() => {
textarea = getByLabelText(/Advanced Module List/i);

View File

@@ -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(
<AppProvider store={store} messages={{}}>
<IntlProvider locale="en">
<Certificates courseId={courseId} {...props} />
</IntlProvider>
</AppProvider>,
);
const renderComponent = (props) => render(<Certificates courseId={courseId} {...props} />);
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 () => {

View File

@@ -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(
<AppProvider store={store} messages={{}}>
<IntlProvider locale="en">
<CertificatesSidebar courseId={courseId} {...props} />
</IntlProvider>
</AppProvider>,
);
const renderComponent = (props) => render(<CertificatesSidebar courseId={courseId} {...props} />);
describe('CertificatesSidebar', () => {
beforeEach(() => {
initializeMockApp({
authenticatedUser: {
userId: 3,
username: 'abc123',
administrator: true,
roles: [],
},
});
store = initializeStore();
initializeMocks();
});
it('renders correctly', async () => {

View File

@@ -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', () => {

View File

@@ -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(
<AppProvider store={store} messages={{}}>
<IntlProvider locale="en">
<OutlineSidebar courseId={courseId} {...props} />
</IntlProvider>
</AppProvider>,
);
let axiosMock;
const mockPathname = '/foo-bar';
const courseId = '123';
const renderComponent = (props) => render(<OutlineSidebar courseId={courseId} {...props} />, { path: mockPathname });
describe('<OutlineSidebar />', () => {
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);

View File

@@ -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 = () => (
<MemoryRouter>
<AppProvider store={store} wrapWithRouter={false}>
<IntlProvider locale="en">
<CourseRerun intl={injectIntl} />
</IntlProvider>
</AppProvider>
</MemoryRouter>
);
describe('<CourseRerun />', () => {
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(<RootWrapper />);
const { getByText, getAllByRole } = render(<CourseRerun />);
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(<RootWrapper />);
const { getAllByRole } = render(<CourseRerun />);
const cancelButton = getAllByRole('button', { name: messages.cancelButton.defaultMessage })[0];
fireEvent.click(cancelButton);
@@ -78,13 +43,13 @@ describe('<CourseRerun />', () => {
it('shows the spinner before the query is complete', async () => {
useSelector.mockReturnValue({ organizationLoadingStatus: RequestStatus.IN_PROGRESS });
const { findByRole } = render(<RootWrapper />);
const { findByRole } = render(<CourseRerun />);
const spinner = await findByRole('status');
expect(spinner.textContent).toEqual('Loading...');
});
it('should show footer', async () => {
const { findByText } = render(<RootWrapper />);
const { findByText } = render(<CourseRerun />);
await findByText('Looking for help with Studio?');
const lmsElement = await findByText('LMS');
expect(lmsElement).toHaveAttribute('href', process.env.LMS_BASE_URL);

View File

@@ -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(
<AppProvider store={store} messages={{}}>
<IntlProvider locale="en">
<CourseRerunSideBar courseId={courseId} {...props} />
</IntlProvider>
</AppProvider>,
);
const renderComponent = (props) => render(<CourseRerunSideBar courseId={courseId} {...props} />);
describe('<CourseRerunSideBar />', () => {
beforeEach(() => {
initializeMockApp({
authenticatedUser: {
userId: 3,
username: 'abc123',
administrator: true,
roles: [],
},
});
store = initializeStore();
initializeMocks();
});
it('render CourseRerunSideBar successfully', () => {

View File

@@ -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;

View File

@@ -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 = () => (
<AppProvider store={store}>
<IntlProvider locale="en">
<CourseTeam courseId={courseId} />
</IntlProvider>
</AppProvider>
);
const render = () => baseRender(<CourseTeam courseId={courseId} />, { path: mockPathname });
describe('<CourseTeam />', () => {
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('<CourseTeam />', () => {
const {
getByText, getByRole, getByTestId, queryAllByTestId,
} = render(<RootWrapper />);
} = render();
await waitFor(() => {
expect(getByText(messages.headingTitle.defaultMessage)).toBeInTheDocument();
@@ -74,14 +47,13 @@ describe('<CourseTeam />', () => {
});
it('render CourseTeam component with 1 team member correctly', async () => {
cleanup();
axiosMock
.onGet(getCourseTeamApiUrl(courseId))
.reply(200, courseTeamWithOneUser);
const {
getByText, getByRole, getByTestId, getAllByTestId,
} = render(<RootWrapper />);
} = render();
await waitFor(() => {
expect(getByText(messages.headingTitle.defaultMessage)).toBeInTheDocument();
@@ -93,14 +65,13 @@ describe('<CourseTeam />', () => {
});
it('render CourseTeam component without team member correctly', async () => {
cleanup();
axiosMock
.onGet(getCourseTeamApiUrl(courseId))
.reply(200, courseTeamWithoutUsers);
const {
getByText, getByRole, getByTestId, queryAllByTestId,
} = render(<RootWrapper />);
} = render();
await waitFor(() => {
expect(getByText(messages.headingTitle.defaultMessage)).toBeInTheDocument();
@@ -112,12 +83,11 @@ describe('<CourseTeam />', () => {
});
it('render CourseTeam component with initial sidebar correctly', async () => {
cleanup();
axiosMock
.onGet(getCourseTeamApiUrl(courseId))
.reply(200, courseTeamWithoutUsers);
const { getByTestId, queryByTestId } = render(<RootWrapper />);
const { getByTestId, queryByTestId } = render();
await waitFor(() => {
expect(getByTestId('course-team-sidebar__initial')).toBeInTheDocument();
@@ -126,12 +96,11 @@ describe('<CourseTeam />', () => {
});
it('render CourseTeam component without initial sidebar correctly', async () => {
cleanup();
axiosMock
.onGet(getCourseTeamApiUrl(courseId))
.reply(200, courseTeamMock);
const { getByTestId, queryByTestId } = render(<RootWrapper />);
const { getByTestId, queryByTestId } = render();
await waitFor(() => {
expect(queryByTestId('course-team-sidebar__initial')).not.toBeInTheDocument();
@@ -140,12 +109,11 @@ describe('<CourseTeam />', () => {
});
it('displays AddUserForm when clicking the "Add New Member" button', async () => {
cleanup();
axiosMock
.onGet(getCourseTeamApiUrl(courseId))
.reply(200, courseTeamWithOneUser);
const { getByRole, queryByTestId } = render(<RootWrapper />);
const { getByRole, queryByTestId } = render();
await waitFor(() => {
expect(queryByTestId('add-user-form')).not.toBeInTheDocument();
@@ -156,12 +124,11 @@ describe('<CourseTeam />', () => {
});
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(<RootWrapper />);
const { getByRole, queryByTestId } = render();
await waitFor(() => {
expect(queryByTestId('add-user-form')).not.toBeInTheDocument();
@@ -172,7 +139,6 @@ describe('<CourseTeam />', () => {
});
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('<CourseTeam />', () => {
allowActions: false,
});
const { queryByRole, queryByTestId } = render(<RootWrapper />);
const { queryByRole, queryByTestId } = render();
await waitFor(() => {
expect(queryByRole('button', { name: messages.addNewMemberButton.defaultMessage })).not.toBeInTheDocument();
@@ -189,12 +155,11 @@ describe('<CourseTeam />', () => {
});
it('should delete user', async () => {
cleanup();
axiosMock
.onGet(getCourseTeamApiUrl(courseId))
.reply(200, courseTeamMock);
const { queryByText } = render(<RootWrapper />);
const { queryByText } = render();
axiosMock
.onDelete(updateCourseTeamUserApiUrl(courseId, 'staff@example.com'))
@@ -205,15 +170,14 @@ describe('<CourseTeam />', () => {
});
it('should change role user', async () => {
cleanup();
axiosMock
.onGet(getCourseTeamApiUrl(courseId))
.reply(200, courseTeamMock);
const { getAllByText } = render(<RootWrapper />);
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('<CourseTeam />', () => {
.onGet(getCourseTeamApiUrl(courseId))
.reply(403);
const { getByRole } = render(<RootWrapper />);
const { getByRole } = render();
await waitFor(() => {
expect(getByRole('alert')).toBeInTheDocument();
@@ -239,7 +203,7 @@ describe('<CourseTeam />', () => {
.onGet(getCourseTeamApiUrl(courseId))
.reply(404);
render(<RootWrapper />);
render();
await waitFor(() => {
const { loadingCourseTeamStatus } = store.getState().courseTeam;

View File

@@ -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(
<AppProvider store={store} messages={{}}>
<IntlProvider locale="en">
<CourseTeamSidebar courseId={courseId} {...props} />
</IntlProvider>
</AppProvider>,
);
const renderComponent = (props) => render(<CourseTeamSidebar courseId={courseId} {...props} />);
describe('<CourseTeamSidebar />', () => {
beforeEach(() => {
initializeMockApp({
authenticatedUser: {
userId: 3,
username: 'abc123',
administrator: true,
roles: [],
},
});
store = initializeStore();
initializeMocks();
});
it('render CourseTeamSidebar component correctly', () => {

View File

@@ -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(
<AppProvider store={store}>
<IntlProvider locale="en">
<IframeProvider>
<AddComponent
blockId={blockId}
isUnitVerticalType
parentLocator={blockId}
addComponentTemplateData={{}}
handleCreateNewCourseXBlock={handleCreateNewCourseXBlockMock}
{...props}
/>
</IframeProvider>
</IntlProvider>
</AppProvider>,
<IframeProvider>
<AddComponent
blockId={blockId}
isUnitVerticalType
parentLocator={blockId}
addComponentTemplateData={{}}
handleCreateNewCourseXBlock={handleCreateNewCourseXBlockMock}
{...props}
/>
</IframeProvider>,
);
describe('<AddComponent />', () => {
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);

View File

@@ -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();

View File

@@ -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 = () => (
<AppProvider store={store}>
<IntlProvider locale="en" messages={{}}>
<CourseExportPage intl={injectIntl} courseId={courseId} />
</IntlProvider>
</AppProvider>
);
const renderComponent = () => render(<CourseExportPage courseId={courseId} />);
describe('<CourseExportPage />', () => {
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('<CourseExportPage />', () => {
cookies.get.mockReturnValue(null);
});
it('should render page title correctly', async () => {
render(<RootWrapper />);
renderComponent();
await waitFor(() => {
const helmet = Helmet.peek();
expect(helmet.title).toEqual(
@@ -73,7 +60,7 @@ describe('<CourseExportPage />', () => {
});
});
it('should render without errors', async () => {
const { getByText } = render(<RootWrapper />);
const { getByText } = renderComponent();
await waitFor(() => {
expect(getByText(messages.headingSubtitle.defaultMessage)).toBeInTheDocument();
const exportPageElement = getByText(messages.headingTitle.defaultMessage, {
@@ -86,7 +73,7 @@ describe('<CourseExportPage />', () => {
});
});
it('should start exporting on click', async () => {
const { getByText, container } = render(<RootWrapper />);
const { getByText, container } = renderComponent();
const button = container.querySelector('.btn-primary');
fireEvent.click(button);
expect(getByText(stepperMessages.stepperPreparingDescription.defaultMessage)).toBeInTheDocument();
@@ -95,7 +82,7 @@ describe('<CourseExportPage />', () => {
axiosMock
.onGet(getExportStatusApiUrl(courseId))
.reply(200, { exportStatus: EXPORT_STAGES.EXPORTING, exportError: { rawErrorMsg: 'test error', editUnitUrl: 'http://test-url.test' } });
const { getByText, queryByText, container } = render(<RootWrapper />);
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('<CourseExportPage />', () => {
});
it('should fetch status without clicking when cookies has', async () => {
cookies.get.mockReturnValue({ date: 1679787000 });
const { getByText } = render(<RootWrapper />);
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(<RootWrapper />);
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(<RootWrapper />);
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(<RootWrapper />);
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('<CourseExportPage />', () => {
axiosMock
.onGet(getExportStatusApiUrl(courseId))
.reply(404);
const { container } = render(<RootWrapper />);
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 });
});
});

View File

@@ -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 = () => (
<AppProvider store={store}>
<IntlProvider locale="en" messages={{}}>
<ExportSidebar intl={{ formatMessage: jest.fn() }} courseId={courseId} />
</IntlProvider>
</AppProvider>
);
describe('<ExportSidebar />', () => {
beforeEach(() => {
initializeMockApp({
authenticatedUser: {
userId: 3,
username: 'abc123',
administrator: true,
roles: [],
},
});
store = initializeStore();
initializeMocks();
});
it('render sidebar correctly', () => {
const { getByText } = render(<RootWrapper />);
const { getByText } = render(<ExportSidebar courseId={courseId} />);
expect(getByText(messages.title1.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.exportedContentHeading.defaultMessage)).toBeInTheDocument();
});

View File

@@ -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) => (
<AppProvider store={store} messages={{}}>
<IntlProvider locale="en">
<HelpSidebar
{...props}
>
<p>Test children</p>
</HelpSidebar>
</IntlProvider>
</AppProvider>
const renderHelpSidebar = (props) => render(
<HelpSidebar {...props}>
<p>Test children</p>
</HelpSidebar>,
{ 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(<RootWrapper {...props} />);
const { getByText } = renderHelpSidebar(props);
expect(getByText('Test children')).toBeTruthy();
});
it('should render all sidebar links with correct text', () => {
const { getByText, queryByText } = render(<RootWrapper {...props} />);
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(<RootWrapper {...initialProps} />);
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(<RootWrapper {...initialProps} />);
const { getByText } = renderHelpSidebar(initialProps);
expect(getByText(messages.sidebarLinkToProctoredExamSettings.defaultMessage)).toBeTruthy();
});
});

View File

@@ -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(
<AppProvider store={store}>
<IntlProvider locale="en">
<GroupConfigurations courseId={courseId} />
</IntlProvider>
</AppProvider>,
);
const renderComponent = () => render(<GroupConfigurations courseId={courseId} />);
describe('<GroupConfigurations />', () => {
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);

View File

@@ -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(
<AppProvider store={store} messages={{}}>
<IntlProvider locale="en">
<GroupConfigurationSidebar courseId={courseId} {...props} />
</IntlProvider>,
</AppProvider>,
);
const renderComponent = (props) => render(<GroupConfigurationSidebar courseId={courseId} {...props} />);
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 () => {

View File

@@ -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 = () => (
<AppProvider store={store}>
<IntlProvider locale="en" messages={{}}>
<CourseImportPage intl={injectIntl} courseId={courseId} />
</IntlProvider>
</AppProvider>
);
const renderComponent = () => render(<CourseImportPage courseId={courseId} />);
describe('<CourseImportPage />', () => {
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('<CourseImportPage />', () => {
cookies.get.mockReturnValue(null);
});
it('should render page title correctly', async () => {
render(<RootWrapper />);
renderComponent();
await waitFor(() => {
const helmet = Helmet.peek();
expect(helmet.title).toEqual(
@@ -71,7 +52,7 @@ describe('<CourseImportPage />', () => {
});
});
it('should render without errors', async () => {
const { getByText } = render(<RootWrapper />);
const { getByText } = renderComponent();
await waitFor(() => {
expect(getByText(messages.headingSubtitle.defaultMessage)).toBeInTheDocument();
const importPageElement = getByText(messages.headingTitle.defaultMessage, {
@@ -85,7 +66,7 @@ describe('<CourseImportPage />', () => {
});
it('should fetch status without clicking when cookies has', async () => {
cookies.get.mockReturnValue({ date: 1679787000, completed: false, fileName: 'testFileName.test' });
const { getByText } = render(<RootWrapper />);
const { getByText } = renderComponent();
expect(getByText(stepperMessages.stepperUnpackingDescription.defaultMessage)).toBeInTheDocument();
});
it('should show error', async () => {
@@ -93,20 +74,20 @@ describe('<CourseImportPage />', () => {
.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(<RootWrapper />);
// 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(<RootWrapper />);
// 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('<CourseImportPage />', () => {
.onGet(getImportStatusApiUrl(courseId, 'testFileName.tar.gz'))
.reply(403);
cookies.get.mockReturnValue({ date: 1679787000, completed: false, fileName: 'testFileName.tar.gz' });
const { getByRole } = render(<RootWrapper />);
// 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('<CourseImportPage />', () => {
.onGet(getImportStatusApiUrl(courseId, 'testFileName.tar.gz'))
.reply(404);
cookies.get.mockReturnValue({ date: 1679787000, completed: false, fileName: 'testFileName.tar.gz' });
render(<RootWrapper />);
// 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 });
});
});

View File

@@ -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 = () => (
<AppProvider store={store}>
<IntlProvider locale="en" messages={{}}>
<ImportSidebar intl={{ formatMessage: jest.fn() }} courseId={courseId} />
</IntlProvider>
</AppProvider>
);
describe('<ImportSidebar />', () => {
beforeEach(() => {
initializeMockApp({
authenticatedUser: {
userId: 3,
username: 'abc123',
administrator: true,
roles: [],
},
});
store = initializeStore();
initializeMocks();
});
it('render sidebar correctly', () => {
const { getByText } = render(<RootWrapper />);
const { getByText } = render(<ImportSidebar courseId={courseId} />);
expect(getByText(messages.title1.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.importedContentHeading.defaultMessage)).toBeInTheDocument();
});

View File

@@ -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(
<PagesAndResources courseId={courseId} />,
{ preloadedState: initialState },
);
initializeMocks({ initialState });
render(<PagesAndResources courseId={courseId} />);
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(
<PagesAndResources courseId={courseId} />,
{ preloadedState: initialState },
);
initializeMocks({ initialState });
render(<PagesAndResources courseId={courseId} />);
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(
<PagesAndResources courseId={courseId} />,
{ preloadedState: initialState },
);
initializeMocks({ initialState });
render(<PagesAndResources courseId={courseId} />);
await waitFor(() => expect(screen.getByRole('heading', { name: 'Content permissions' })).toBeInTheDocument());
await waitFor(() => expect(screen.getByText('Xpert unit summaries')).toBeInTheDocument());

View File

@@ -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 () => {

View File

@@ -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 }) => (
<IntlProvider locale="en">
{/* <MemoryRouter initialEntries={['/pages-and-resources']}> */}
<PagesAndResourcesProvider courseId="course-v1:edX+TestX+Test_Course">
<BrowserRouter>
<Provider store={store}>{children}</Provider>
</BrowserRouter>
</PagesAndResourcesProvider>
{/* </MemoryRouter> */}
</IntlProvider>
);
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 };

View File

@@ -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) => (
<textarea {...props} onFocus={() => {}} onBlur={() => {}} />
)));
const RootWrapper = () => (
<AppProvider store={store}>
<IntlProvider locale="en" messages={{}}>
<ScheduleAndDetails intl={injectIntl} courseId={courseId} />
</IntlProvider>
</AppProvider>
);
describe('<ScheduleAndDetails />', () => {
beforeEach(() => {
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(getCourseDetailsApiUrl(courseId))
.reply(200, courseDetailsMock);
@@ -81,12 +62,9 @@ describe('<ScheduleAndDetails />', () => {
.onPut(getCourseDetailsApiUrl(courseId))
.reply(200);
});
afterEach(() => {
axiosMock.reset();
});
it('should render without errors', async () => {
const { getByText, getByRole, getAllByText } = render(<RootWrapper />);
const { getByText, getByRole, getAllByText } = render(<ScheduleAndDetails courseId={courseId} />);
await waitFor(() => {
const scheduleAndDetailElements = getAllByText(messages.headingTitle.defaultMessage);
const scheduleAndDetailTitle = scheduleAndDetailElements[0];
@@ -121,7 +99,7 @@ describe('<ScheduleAndDetails />', () => {
.onGet(getCourseSettingsApiUrl(courseId))
.reply(200, updatedResponse);
const { queryAllByText } = render(<RootWrapper />);
const { queryAllByText } = render(<ScheduleAndDetails courseId={courseId} />);
await waitFor(() => {
expect(
queryAllByText(creditMessages.creditTitle.defaultMessage).length,
@@ -131,12 +109,13 @@ describe('<ScheduleAndDetails />', () => {
it('should show save alert onChange ', async () => {
const { getAllByPlaceholderText, getByText } = render(
<RootWrapper />,
<ScheduleAndDetails courseId={courseId} />,
);
let inputs;
await waitFor(() => {
inputs = getAllByPlaceholderText(DATE_FORMAT.toLocaleUpperCase());
});
// @ts-ignore
fireEvent.change(inputs[0], { target: { value: '06/16/2023' } });
expect(
@@ -145,7 +124,7 @@ describe('<ScheduleAndDetails />', () => {
});
it('should display a success message when course details saves', async () => {
const { getByText } = render(<RootWrapper />);
const { getByText } = render(<ScheduleAndDetails courseId={courseId} />);
await waitFor(() => {
executeThunk(updateCourseDetailsQuery(courseId, 'DaTa'), store.dispatch);
});
@@ -156,7 +135,7 @@ describe('<ScheduleAndDetails />', () => {
axiosMock
.onGet(getCourseDetailsApiUrl(courseId))
.reply(404, 'error');
const { getByText } = render(<RootWrapper />);
const { getByText } = render(<ScheduleAndDetails courseId={courseId} />);
await waitFor(() => {
expect(getByText(messages.alertLoadFail.defaultMessage)).toBeInTheDocument();
});
@@ -166,7 +145,7 @@ describe('<ScheduleAndDetails />', () => {
axiosMock
.onGet(getCourseSettingsApiUrl(courseId))
.reply(404, 'error');
const { getByText } = render(<RootWrapper />);
const { getByText } = render(<ScheduleAndDetails courseId={courseId} />);
await waitFor(() => {
expect(getByText(messages.alertLoadFail.defaultMessage)).toBeInTheDocument();
});
@@ -176,7 +155,7 @@ describe('<ScheduleAndDetails />', () => {
axiosMock
.onPut(getCourseDetailsApiUrl(courseId))
.reply(404, 'error');
const { getByText } = render(<RootWrapper />);
const { getByText } = render(<ScheduleAndDetails courseId={courseId} />);
await act(async () => {
await executeThunk(updateCourseDetailsQuery(courseId, 'DaTa'), store.dispatch);
});

View File

@@ -1,12 +1,7 @@
import React from 'react';
import { useSelector } from 'react-redux';
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 { initializeMocks, render } from '../../testUtils';
import { COURSE_CREATOR_STATES } from '../../constants';
import initializeStore from '../../store';
import { studioHomeMock } from '../__mocks__';
import HomeSidebar from '.';
@@ -15,37 +10,20 @@ jest.mock('react-redux', () => ({
useSelector: jest.fn(),
}));
let store;
const {
studioName,
studioShortName,
} = studioHomeMock;
const RootWrapper = () => (
<AppProvider store={store}>
<IntlProvider locale="en" messages={{}}>
<HomeSidebar intl={{ formatMessage: jest.fn() }} />
</IntlProvider>
</AppProvider>
);
describe('<HomeSidebar />', () => {
beforeEach(() => {
initializeMockApp({
authenticatedUser: {
userId: 3,
username: 'abc123',
administrator: true,
roles: [],
},
});
store = initializeStore();
initializeMocks();
});
it('renders about and other sidebar titles correctly', () => {
useSelector.mockReturnValue(studioHomeMock);
const { getByText } = render(<RootWrapper />);
const { getByText } = render(<HomeSidebar />);
expect(getByText(`New to ${studioName}?`)).toBeInTheDocument();
expect(getByText(`Click "Looking for help with Studio" at the bottom of the page to access our continually updated documentation and other ${studioShortName} resources.`)).toBeInTheDocument();
});
@@ -58,7 +36,7 @@ describe('<HomeSidebar />', () => {
};
useSelector.mockReturnValue(studioHomeInitial);
const { getByText } = render(<RootWrapper />);
const { getByText } = render(<HomeSidebar />);
expect(getByText(`Can I create courses in ${studioName}?`)).toBeInTheDocument();
expect(getByText(`In order to create courses in ${studioName}, you must`)).toBeInTheDocument();
});
@@ -70,7 +48,7 @@ describe('<HomeSidebar />', () => {
};
useSelector.mockReturnValue(studioHomeInitial);
const { getByText } = render(<RootWrapper />);
const { getByText } = render(<HomeSidebar />);
expect(getByText(`Can I create courses in ${studioName}?`)).toBeInTheDocument();
expect(getByText(`In order to create courses in ${studioName}, you must have course creator privileges to create your own course.`)).toBeInTheDocument();
});
@@ -82,7 +60,7 @@ describe('<HomeSidebar />', () => {
};
useSelector.mockReturnValue(studioHomeInitial);
const { getByText } = render(<RootWrapper />);
const { getByText } = render(<HomeSidebar />);
expect(getByText(`Can I create courses in ${studioName}?`)).toBeInTheDocument();
expect(getByText(`Your request to author courses in ${studioName} has been denied.`, { exact: false })).toBeInTheDocument();
});

View File

@@ -1,11 +1,10 @@
import React from 'react';
import { initializeMockApp } from '@edx/frontend-platform';
import { fireEvent, render, screen } from '@testing-library/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { AppProvider } from '@edx/frontend-platform/react';
import {
fireEvent,
initializeMocks,
render,
screen,
} from '../../../testUtils';
import { COURSE_CREATOR_STATES } from '../../../constants';
import initializeStore from '../../../store';
import { studioHomeMock } from '../../__mocks__';
import { initialState } from '../../factories/mockApiResponses';
@@ -31,42 +30,27 @@ const renderComponent = (overrideProps = {}, studioHomeState = {}) => {
};
// Initialize the store with the custom initial state
const store = initializeStore(customInitialState);
const { reduxStore: store } = initializeMocks({ initialState: customInitialState });
return {
...render(
<AppProvider store={store}>
<IntlProvider locale="en" messages={{}}>
<CoursesTab
coursesDataItems={studioHomeMock.courses}
showNewCourseContainer={showNewCourseContainer}
onClickNewCourse={onClickNewCourse}
isShowProcessing={isShowProcessing}
isLoading={isLoading}
isFailed={isFailed}
numPages={numPages}
coursesCount={coursesCount}
{...overrideProps}
/>
</IntlProvider>
</AppProvider>,
<CoursesTab
coursesDataItems={studioHomeMock.courses}
showNewCourseContainer={showNewCourseContainer}
onClickNewCourse={onClickNewCourse}
isShowProcessing={isShowProcessing}
isLoading={isLoading}
isFailed={isFailed}
numPages={numPages}
coursesCount={coursesCount}
{...overrideProps}
/>,
),
store,
};
};
describe('<CoursesTab />', () => {
beforeEach(() => {
initializeMockApp({
authenticatedUser: {
userId: 3,
username: 'abc123',
administrator: true,
roles: [],
},
});
});
it('should render correctly', async () => {
renderComponent();
const coursesPaginationInfo = screen.getByTestId('pagination-info');

View File

@@ -1,61 +1,21 @@
import { render } from '@testing-library/react';
import { getAuthenticatedUser } 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';
import initializeStore from '../../store';
// @ts-check
import { initializeMocks, render } from '../../testUtils';
import messages from './messages';
import VerifyEmailLayout from '.';
let store;
const mockPathname = '/foo-bar';
const fakeAuthenticatedUser = {
userId: 7,
email: 'email@fake.com',
username: 'fake-user',
};
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useLocation: () => ({
pathname: mockPathname,
}),
}));
jest.mock('@edx/frontend-platform/auth');
getAuthenticatedUser.mockImplementation(() => fakeAuthenticatedUser);
const RootWrapper = () => (
<IntlProvider locale="en" messages={{}}>
<AppProvider store={store}>
<VerifyEmailLayout />
</AppProvider>
</IntlProvider>
);
describe('<VerifyEmailLayout />', () => {
beforeEach(async () => {
initializeMockApp({
authenticatedUser: {
userId: 3,
username: 'abc123',
administrator: false,
roles: [],
},
});
store = initializeStore({
courseDetail: {
courseId: 'id',
status: 'sucessful',
},
});
initializeMocks({ user: fakeAuthenticatedUser });
});
it('renders successfully', () => {
const { getByText } = render(<RootWrapper />);
const { getByText } = render(<VerifyEmailLayout />);
expect(
getByText(`Thanks for signing up, ${fakeAuthenticatedUser.username}!`, {

View File

@@ -1,13 +1,8 @@
import MockAdapter from 'axios-mock-adapter';
import { cleanup, render, 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 { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
// @ts-check
import userEvent from '@testing-library/user-event';
import { initializeMocks, render, waitFor } from '../testUtils';
import { RequestStatus } from '../data/constants';
import initializeStore from '../store';
import { executeThunk } from '../utils';
import { getTextbooksApiUrl } from './data/api';
import { fetchTextbooksQuery } from './data/thunk';
@@ -20,27 +15,14 @@ let store;
const courseId = 'course-v1:org+101+101';
const emptyTextbooksMock = { textbooks: [] };
const renderComponent = () => render(
<AppProvider store={store}>
<IntlProvider locale="en">
<Textbooks courseId={courseId} />
</IntlProvider>
</AppProvider>,
);
const renderComponent = () => render(<Textbooks courseId={courseId} />);
describe('<Textbooks />', () => {
beforeEach(async () => {
initializeMockApp({
authenticatedUser: {
userId: 3,
username: 'abc123',
administrator: true,
roles: [],
},
});
const mocks = initializeMocks();
store = initializeStore();
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
store = mocks.reduxStore;
axiosMock = mocks.axiosMock;
axiosMock
.onGet(getTextbooksApiUrl(courseId))
.reply(200, textbooksMock);
@@ -73,7 +55,6 @@ describe('<Textbooks />', () => {
});
it('renders Textbooks component with empty placeholder correctly', async () => {
cleanup();
axiosMock
.onGet(getTextbooksApiUrl(courseId))
.reply(200, emptyTextbooksMock);

View File

@@ -1,41 +1,17 @@
import MockAdapter from 'axios-mock-adapter';
import { render, 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 { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import initializeStore from '../../store';
// @ts-check
import { initializeMocks, render, waitFor } from '../../testUtils';
import { getHelpUrlsApiUrl } from '../../help-urls/data/api';
import { helpUrls } from '../../help-urls/__mocks__';
import TextbookSidebar from './TextbookSidebar';
import messages from './messages';
let axiosMock;
let store;
const courseId = 'course-v1:org+101+101';
const renderComponent = () => render(
<AppProvider store={store}>
<IntlProvider locale="en">
<TextbookSidebar courseId={courseId} />
</IntlProvider>
</AppProvider>,
);
const renderComponent = () => render(<TextbookSidebar courseId={courseId} />);
describe('<TextbookSidebar />', () => {
beforeEach(async () => {
initializeMockApp({
authenticatedUser: {
userId: 3,
username: 'abc123',
administrator: true,
roles: [],
},
});
store = initializeStore();
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
const { axiosMock } = initializeMocks();
axiosMock
.onGet(getHelpUrlsApiUrl())
.reply(200, helpUrls);