ErrorPa
jest.mock('@src/generic/PageLoading', () => jest.fn(() =>
PageLoading
));
+jest.mock('@openedx/paragon', () => {
+ const actual = jest.requireActual('@openedx/paragon');
+ const PropTypes = jest.requireActual('prop-types');
+
+ const MockModalDialog = ({ children, isOpen, onClose }) => {
+ if (!isOpen) { return null; }
+
+ return (
+
+
+
+ {children}
+
+
+ );
+ };
+
+ MockModalDialog.propTypes = {
+ children: PropTypes.node,
+ isOpen: PropTypes.bool,
+ onClose: PropTypes.func,
+ };
+
+ const createSubComponent = (baseClass) => {
+ const Component = ({ children, className }) => (
+
{children}
+ );
+ Component.propTypes = {
+ children: PropTypes.node,
+ className: PropTypes.string,
+ };
+ return Component;
+ };
+
+ MockModalDialog.Body = createSubComponent('mock-modal-body');
+ MockModalDialog.Header = createSubComponent('mock-modal-header');
+ MockModalDialog.Footer = createSubComponent('mock-modal-footer');
+
+ return {
+ ...actual,
+ ModalDialog: MockModalDialog,
+ };
+});
+
jest.mock('./hooks', () => ({
useIFrameBehavior: jest.fn(),
useModalIFrameData: jest.fn(),
diff --git a/src/plugin-slots/LearnerToolsSlot/index.test.jsx b/src/plugin-slots/LearnerToolsSlot/index.test.jsx
index 45091e41c4..47ea020015 100644
--- a/src/plugin-slots/LearnerToolsSlot/index.test.jsx
+++ b/src/plugin-slots/LearnerToolsSlot/index.test.jsx
@@ -5,10 +5,6 @@ import * as auth from '@edx/frontend-platform/auth';
import { LearnerToolsSlot } from './index';
-jest.mock('@openedx/frontend-plugin-framework', () => ({
- PluginSlot: jest.fn(() =>
Plugin Slot
),
-}));
-
jest.mock('@edx/frontend-platform/auth', () => ({
getAuthenticatedUser: jest.fn(),
}));
@@ -98,7 +94,7 @@ describe('LearnerToolsSlot', () => {
render(
);
- // The portal should render to document.body
- expect(document.body.querySelector('[data-testid="plugin-slot"]')).toBeInTheDocument();
+ // The portal should render to document.body with the id as testid
+ expect(document.body.querySelector('[data-testid="org.openedx.frontend.learning.learner_tools.v1"]')).toBeInTheDocument();
});
});
diff --git a/src/product-tours/ProductTours.test.jsx b/src/product-tours/ProductTours.test.jsx
index 0818b07937..3aa622227c 100644
--- a/src/product-tours/ProductTours.test.jsx
+++ b/src/product-tours/ProductTours.test.jsx
@@ -27,6 +27,11 @@ import { buildOutlineFromBlocks } from '../courseware/data/__factories__/learnin
import { UserMessagesProvider } from '../generic/user-messages';
import { DECODE_ROUTES } from '../constants';
+import {
+ DismissButtonFormattedMessage,
+ NextButtonFormattedMessage,
+ OkayButtonFormattedMessage,
+} from './GenericTourFormattedMessages';
initializeMockApp();
jest.mock('@edx/frontend-platform/analytics');
@@ -158,6 +163,18 @@ describe('Course Home Tours', () => {
expect(await screen.queryByRole('dialog')).not.toBeInTheDocument();
},
);
+
+ describe('GenericTourFormattedMessages', () => {
+ it('renders all formatted message components to satisfy coverage', () => {
+ render(
);
+ render(
);
+ render(
);
+
+ expect(screen.getByText('Dismiss')).toBeInTheDocument();
+ expect(screen.getByText('Next')).toBeInTheDocument();
+ expect(screen.getByText('Okay')).toBeInTheDocument();
+ });
+ });
});
jest.mock(
diff --git a/src/setupTest.js b/src/setupTest.js
index 60a87751e1..93b365a816 100755
--- a/src/setupTest.js
+++ b/src/setupTest.js
@@ -26,13 +26,15 @@ import { getCourseOutlineStructure } from './courseware/data/thunks';
import { appendBrowserTimezoneToUrl, executeThunk } from './utils';
import buildSimpleCourseAndSequenceMetadata from './courseware/data/__factories__/sequenceMetadata.factory';
import { buildOutlineFromBlocks } from './courseware/data/__factories__/learningSequencesOutline.factory';
-import MockedPluginSlot from './tests/MockedPluginSlot';
-jest.mock('@openedx/frontend-plugin-framework', () => ({
- ...jest.requireActual('@openedx/frontend-plugin-framework'),
- Plugin: () => 'Plugin',
- PluginSlot: MockedPluginSlot,
-}));
+jest.mock('@openedx/frontend-plugin-framework', () => {
+ const MockedPluginSlot = jest.requireActual('./tests/MockedPluginSlot').default;
+
+ return {
+ Plugin: () => 'Plugin',
+ PluginSlot: jest.fn(MockedPluginSlot),
+ };
+});
jest.mock('@src/generic/plugin-store', () => ({
...jest.requireActual('@src/generic/plugin-store'),
diff --git a/src/utils.ts b/src/utils.ts
index 5ea68509cd..35da3a0a5f 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -15,9 +15,9 @@ export const executeThunk = async (thunk, dispatch, getState = undefined) => {
*/
export const appendBrowserTimezoneToUrl = (url: string) => {
const browserTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
- const urlObject = new URL(url);
+ const urlObject = new URL(url, url.startsWith('http') ? undefined : 'http://localhost');
if (browserTimezone) {
urlObject.searchParams.append('browser_timezone', browserTimezone);
}
- return urlObject.href;
+ return url.startsWith('http') ? urlObject.href : `${urlObject.pathname}${urlObject.search}`;
};
diff --git a/webpack.dev.config.js b/webpack.dev.config.js
index ddf63def17..798daf67bf 100644
--- a/webpack.dev.config.js
+++ b/webpack.dev.config.js
@@ -1,5 +1,6 @@
const path = require('path');
const { createConfig } = require('@openedx/frontend-build');
+const sass = require('sass');
const config = createConfig('webpack-dev');
@@ -8,4 +9,43 @@ config.resolve.alias = {
'@src': path.resolve(__dirname, 'src'),
};
+// Fix for react-focus-on webpack 5 compatibility issue
+// The package has ES modules without file extensions in imports
+config.module.rules.push({
+ test: /\.m?js$/,
+ resolve: {
+ fullySpecified: false,
+ },
+});
+
+// Fix for sass-loader deprecation warnings
+config.module.rules.forEach((rule) => {
+ if (rule.oneOf) {
+ rule.oneOf.forEach((oneOfRule) => {
+ if (oneOfRule.use) {
+ oneOfRule.use.forEach((loaderConfig) => {
+ if (loaderConfig.loader && loaderConfig.loader.includes('sass-loader')) {
+ // eslint-disable-next-line no-param-reassign
+ loaderConfig.options = {
+ ...loaderConfig.options,
+ api: 'modern',
+ implementation: sass,
+ sassOptions: {
+ ...loaderConfig.options?.sassOptions,
+ silenceDeprecations: [
+ 'import',
+ 'abs-percent',
+ 'color-functions',
+ 'global-builtin',
+ 'legacy-js-api',
+ ],
+ },
+ };
+ }
+ });
+ }
+ });
+ }
+});
+
module.exports = config;
diff --git a/webpack.prod.config.js b/webpack.prod.config.js
index fbe410772d..8d2c0aac16 100644
--- a/webpack.prod.config.js
+++ b/webpack.prod.config.js
@@ -1,6 +1,7 @@
const path = require('path');
const { createConfig } = require('@openedx/frontend-build');
const CopyPlugin = require('copy-webpack-plugin');
+const sass = require('sass');
const config = createConfig('webpack-prod');
@@ -20,4 +21,43 @@ config.resolve.alias = {
'@src': path.resolve(__dirname, 'src'),
};
+// Fix for react-focus-on webpack 5 compatibility issue
+// The package has ES modules without file extensions in imports
+config.module.rules.push({
+ test: /\.m?js$/,
+ resolve: {
+ fullySpecified: false,
+ },
+});
+
+// Fix for sass-loader deprecation warnings
+config.module.rules.forEach((rule) => {
+ if (rule.oneOf) {
+ rule.oneOf.forEach((oneOfRule) => {
+ if (oneOfRule.use) {
+ oneOfRule.use.forEach((loaderConfig) => {
+ if (loaderConfig.loader && loaderConfig.loader.includes('sass-loader')) {
+ // eslint-disable-next-line no-param-reassign
+ loaderConfig.options = {
+ ...loaderConfig.options,
+ api: 'modern',
+ implementation: sass,
+ sassOptions: {
+ ...loaderConfig.options?.sassOptions,
+ silenceDeprecations: [
+ 'import',
+ 'abs-percent',
+ 'color-functions',
+ 'global-builtin',
+ 'legacy-js-api',
+ ],
+ },
+ };
+ }
+ });
+ }
+ });
+ }
+});
+
module.exports = config;