Skip to content
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 0 additions & 16 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,4 @@ const config = createConfig('jest', {

// delete config.testURL;

config.reporters = [...(config.reporters || []), ["jest-console-group-reporter", {
// change this setting if need to see less details for each test
// reportType: "summary" | "details",
// enable: true | false,
afterEachTest: {
enable: true,
filePaths: false,
reportType: "details",
},
afterAllTests: {
reportType: "summary",
enable: true,
filePaths: true,
},
}]];

module.exports = config;
21,836 changes: 12,641 additions & 9,195 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions src/courseware/course/new-sidebar/SidebarContextProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ const SidebarProvider: React.FC<Props> = ({
const { verifiedMode } = useModel('courseHomeMeta', courseId);
const topic = useModel('discussionTopics', unitId);
const windowWidth = useWindowSize().width ?? window.innerWidth;
const shouldDisplayFullScreen = windowWidth < breakpoints.large.minWidth;
const shouldDisplaySidebarOpen = windowWidth > breakpoints.medium.minWidth;
const shouldDisplayFullScreen = windowWidth < (breakpoints.large.minWidth ?? 992);
const shouldDisplaySidebarOpen = windowWidth > (breakpoints.medium.minWidth ?? 768);
const query = new URLSearchParams(window.location.search);
const isInitiallySidebarOpen = shouldDisplaySidebarOpen || query.get('sidebar') === 'true';
const sidebarKey = `sidebar.${courseId}`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe('NotificationsWidget', () => {
}

beforeEach(async () => {
global.innerWidth = breakpoints.large.minWidth;
global.innerWidth = breakpoints.large.minWidth ?? 992;
store = initializeStore();
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
axiosMock.onGet(courseMetadataUrl).reply(200, defaultMetadata);
Expand Down
51 changes: 51 additions & 0 deletions src/courseware/course/sequence/Unit/ContentIFrame.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,57 @@ jest.mock('@edx/frontend-platform/react', () => ({ ErrorPage: () => <div>ErrorPa

jest.mock('@src/generic/PageLoading', () => jest.fn(() => <div>PageLoading</div>));

jest.mock('@openedx/paragon', () => {
const actual = jest.requireActual('@openedx/paragon');
const PropTypes = jest.requireActual('prop-types');

const MockModalDialog = ({ children, isOpen, onClose }) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[important]: Is it possible to abandon the mock of this Paragon component?

if (!isOpen) { return null; }

return (
<div role="dialog" aria-modal="true" className="mock-modal">
<button
type="button"
data-testid="modal-backdrop"
onClick={onClose}
aria-label="Close"
>
</button>
<div className="mock-modal-content">
{children}
</div>
</div>
);
};

MockModalDialog.propTypes = {
children: PropTypes.node,
isOpen: PropTypes.bool,
onClose: PropTypes.func,
};

const createSubComponent = (baseClass) => {
const Component = ({ children, className }) => (
<div className={`${baseClass} ${className || ''}`}>{children}</div>
);
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(),
Expand Down
13 changes: 13 additions & 0 deletions src/courseware/data/pact-tests/lmsPact.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,19 @@ describe('Courseware Service', () => {
mergeConfig({
LMS_BASE_URL: 'http://localhost:8081',
}, 'Custom app config for pact tests');

// Mock Intl.DateTimeFormat to return a consistent timezone across all environments
// This ensures that the browser_timezone query parameter matches the pact file
const OriginalDateTimeFormat = Intl.DateTimeFormat;
jest.spyOn(Intl, 'DateTimeFormat').mockImplementation((...args) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[question]: What exactly caused this change? Why did the tests work without it before?

const dtf = new OriginalDateTimeFormat(...args);
const originalResolvedOptions = dtf.resolvedOptions.bind(dtf);
dtf.resolvedOptions = () => ({
...originalResolvedOptions(),
timeZone: 'Asia/Karachi',
});
return dtf;
});
});

describe('When a request to get a learning sequence outline is made', () => {
Expand Down
8 changes: 2 additions & 6 deletions src/plugin-slots/LearnerToolsSlot/index.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => <div data-testid="plugin-slot">Plugin Slot</div>),
}));

jest.mock('@edx/frontend-platform/auth', () => ({
getAuthenticatedUser: jest.fn(),
}));
Expand Down Expand Up @@ -98,7 +94,7 @@ describe('LearnerToolsSlot', () => {

render(<LearnerToolsSlot {...defaultProps} />);

// 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();
});
});
14 changes: 8 additions & 6 deletions src/setupTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
Expand Down
4 changes: 2 additions & 2 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`;
};
40 changes: 40 additions & 0 deletions webpack.dev.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const path = require('path');
const { createConfig } = require('@openedx/frontend-build');
const sass = require('sass');

const config = createConfig('webpack-dev');

Expand All @@ -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;
40 changes: 40 additions & 0 deletions webpack.prod.config.js
Original file line number Diff line number Diff line change
@@ -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');

Expand All @@ -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;
Loading