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", {
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you add some context as to why we are removing this?

Copy link
Author

Choose a reason for hiding this comment

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

After regenerating package-lock.json, the dependency updates caused the jest-console-group-reporter to fail with a TypeError: Cannot read properties of undefined (reading 'isSet') during the test run.
Since this is just a reporter utility for grouping console logs and its failure was blocking the test suite execution, I removed it to restore stability to the CI pipeline.

// 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;
Copy link
Contributor

Choose a reason for hiding this comment

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

It would be nice for all breakpoints we use as fallbacks to have constant variables, instead of hardcoded values.

Copy link
Author

Choose a reason for hiding this comment

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

fixed

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh wait, I just remembered that Paragon already exports a breakpoints object, so let's use that instead new constants

Copy link
Author

Choose a reason for hiding this comment

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

used breakpoints from Paragon

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?

Copy link
Author

Choose a reason for hiding this comment

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

this mock resolve third issue about Paragon modal issues in tests in this ticket

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, this mock simulates the behavior of a modal window with data-testid="modal-backdrop". However, is it now clear why we’re getting the error TestingLibraryElementError: Unable to find an element by: [data-testid="modal-backdrop"]?

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?

Copy link
Author

Choose a reason for hiding this comment

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

it was removed

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) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we do without this? I think there is nothing wrong with displaying warnings.

Copy link
Author

Choose a reason for hiding this comment

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

removed

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