* feat: upgrade react router to v6 * fix: all test cases affected by react router upgrade * refactor: fix navigations * fix: test cases affectewd due to lib-special-exams * refactor: improve code coverage
201 lines
6.8 KiB
JavaScript
Executable File
201 lines
6.8 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 { reducer as learningAssistantReducer } from '@edx/frontend-lib-learning-assistant';
|
|
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 messages 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,
|
|
});
|
|
|
|
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,
|
|
learningAssistant: learningAssistantReducer,
|
|
},
|
|
});
|
|
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);
|
|
|
|
const provider = options?.provider || 'legacy';
|
|
|
|
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 });
|
|
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,
|
|
wrapWithRouter = false,
|
|
...renderOptions
|
|
} = {},
|
|
) {
|
|
const Wrapper = ({ children }) => (
|
|
// eslint-disable-next-line react/jsx-filename-extension
|
|
<IntlProvider locale="en">
|
|
<AppProvider store={store || globalStore} wrapWithRouter={wrapWithRouter}>
|
|
<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,
|
|
};
|