Files
frontend-app-learning/src/setupTest.js
Michael Terry 44f5132e2a fix: downgrade react and upgrade some other deps to align
Now that we are using node 16, peer dependencies are much more
strict about aligning between all of our dependencies.

This PR downgrades react from 17 to 16 (no changes) and upgrades
paragon and frontend-lib-special-exams to all be on the same
page about what peer dependency ranges are valid.
2022-04-08 16:00:29 -04:00

198 lines
6.6 KiB
JavaScript
Executable File

import 'core-js/stable';
import 'regenerator-runtime/runtime';
import '@testing-library/jest-dom';
import '@testing-library/jest-dom/extend-expect';
import './courseware/data/__factories__';
import './course-home/data/__factories__';
import { getConfig, mergeConfig } from '@edx/frontend-platform';
import { configure as configureI18n, IntlProvider } from '@edx/frontend-platform/i18n';
import { configure as configureLogging } from '@edx/frontend-platform/logging';
import { configure as configureAuth, getAuthenticatedHttpClient, MockAuthService } from '@edx/frontend-platform/auth';
import React from 'react';
import PropTypes from 'prop-types';
import { render as rtlRender } from '@testing-library/react';
import { configureStore } from '@reduxjs/toolkit';
import MockAdapter from 'axios-mock-adapter';
import AppProvider from '@edx/frontend-platform/react/AppProvider';
import { reducer as courseHomeReducer } from './course-home/data';
import { reducer as coursewareReducer } from './courseware/data/slice';
import { reducer as modelsReducer } from './generic/model-store';
import { UserMessagesProvider } from './generic/user-messages';
import appMessages from './i18n';
import { fetchCourse, fetchSequence } from './courseware/data';
import { appendBrowserTimezoneToUrl, executeThunk } from './utils';
import buildSimpleCourseAndSequenceMetadata from './courseware/data/__factories__/sequenceMetadata.factory';
import { buildOutlineFromBlocks } from './courseware/data/__factories__/learningSequencesOutline.factory';
class MockLoggingService {
// eslint-disable-next-line no-console
logInfo = jest.fn(infoString => console.log(infoString));
// eslint-disable-next-line no-console
logError = jest.fn(errorString => console.log(errorString));
}
window.getComputedStyle = jest.fn(() => ({
getPropertyValue: jest.fn(),
}));
// Mock Intersection Observer which is unavailable in the context of a test.
global.IntersectionObserver = jest.fn(function mockIntersectionObserver() {
this.observe = jest.fn();
this.disconnect = jest.fn();
});
export const authenticatedUser = {
userId: 'abc123',
username: 'MockUser',
roles: [],
administrator: false,
};
export function initializeMockApp() {
mergeConfig({
CONTACT_URL: process.env.CONTACT_URL || null,
DISCUSSIONS_MFE_BASE_URL: process.env.DISCUSSIONS_MFE_BASE_URL || null,
INSIGHTS_BASE_URL: process.env.INSIGHTS_BASE_URL || null,
STUDIO_BASE_URL: process.env.STUDIO_BASE_URL || null,
TWITTER_URL: process.env.TWITTER_URL || null,
authenticatedUser: {
userId: 'abc123',
username: 'MockUser',
roles: [],
administrator: false,
},
SUPPORT_URL_ID_VERIFICATION: 'http://example.com',
});
const loggingService = configureLogging(MockLoggingService, {
config: getConfig(),
});
const authService = configureAuth(MockAuthService, {
config: getConfig(),
loggingService,
});
// i18n doesn't have a service class to return.
configureI18n({
config: getConfig(),
loggingService,
messages: [appMessages],
});
return { loggingService, authService };
}
window.scrollTo = jest.fn();
// MessageEvent used for indicating that a unit has been loaded.
export const messageEvent = {
type: 'plugin.resize',
payload: {
height: 300,
},
};
// Send MessageEvent indicating that a unit has been loaded.
export function loadUnit(message = messageEvent) {
window.postMessage(message, '*');
}
// Helper function to log unhandled API requests to the console while running tests.
export function logUnhandledRequests(axiosMock) {
axiosMock.onAny().reply((config) => {
// eslint-disable-next-line no-console
console.log(config.method, config.url);
return [200, {}];
});
}
let globalStore;
export async function initializeTestStore(options = {}, overrideStore = true) {
const store = configureStore({
reducer: {
models: modelsReducer,
courseware: coursewareReducer,
courseHome: courseHomeReducer,
},
});
if (overrideStore) {
globalStore = store;
}
initializeMockApp();
const axiosMock = new MockAdapter(getAuthenticatedHttpClient());
axiosMock.reset();
const {
courseBlocks, sequenceBlocks, courseMetadata, sequenceMetadata, courseHomeMetadata,
} = buildSimpleCourseAndSequenceMetadata(options);
let courseMetadataUrl = `${getConfig().LMS_BASE_URL}/api/courseware/course/${courseMetadata.id}`;
courseMetadataUrl = appendBrowserTimezoneToUrl(courseMetadataUrl);
const learningSequencesUrlRegExp = new RegExp(`${getConfig().LMS_BASE_URL}/api/learning_sequences/v1/course_outline/*`);
let courseHomeMetadataUrl = `${getConfig().LMS_BASE_URL}/api/course_home/course_metadata/${courseMetadata.id}`;
const discussionConfigUrl = new RegExp(`${getConfig().LMS_BASE_URL}/api/discussion/v1/courses/*`);
courseHomeMetadataUrl = appendBrowserTimezoneToUrl(courseHomeMetadataUrl);
axiosMock.onGet(courseMetadataUrl).reply(200, courseMetadata);
axiosMock.onGet(courseHomeMetadataUrl).reply(200, courseHomeMetadata);
axiosMock.onGet(learningSequencesUrlRegExp).reply(200, buildOutlineFromBlocks(courseBlocks));
axiosMock.onGet(discussionConfigUrl).reply(200, { provider: 'legacy' });
sequenceMetadata.forEach(metadata => {
const sequenceMetadataUrl = `${getConfig().LMS_BASE_URL}/api/courseware/sequence/${metadata.item_id}`;
axiosMock.onGet(sequenceMetadataUrl).reply(200, metadata);
const proctoredExamApiUrl = `${getConfig().LMS_BASE_URL}/api/edx_proctoring/v1/proctored_exam/attempt/course_id/${courseMetadata.id}/content_id/${sequenceMetadata.item_id}?is_learning_mfe=true`;
axiosMock.onGet(proctoredExamApiUrl).reply(200, { exam: {}, active_attempt: {} });
});
logUnhandledRequests(axiosMock);
// eslint-disable-next-line no-unused-expressions
!options.excludeFetchCourse && await executeThunk(fetchCourse(courseMetadata.id), store.dispatch);
if (!options.excludeFetchSequence) {
await Promise.all(sequenceBlocks
.map(block => executeThunk(fetchSequence(block.id), store.dispatch)));
}
return store;
}
function render(
ui,
{
store = null,
...renderOptions
} = {},
) {
function Wrapper({ children }) {
return (
// eslint-disable-next-line react/jsx-filename-extension
<IntlProvider locale="en">
<AppProvider store={store || globalStore}>
<UserMessagesProvider>
{children}
</UserMessagesProvider>
</AppProvider>
</IntlProvider>
);
}
Wrapper.propTypes = {
children: PropTypes.node.isRequired,
};
return rtlRender(ui, { wrapper: Wrapper, ...renderOptions });
}
// Re-export everything.
export * from '@testing-library/react';
// Override `render` method.
export {
render,
};