Skip to content

Commit 13eea81

Browse files
fix(deps): regenerate package-lock.json (#1855)
* fix(deps): regenerate package-lock.json Co-Authored-By: Claude Code <noreply@anthropic.com> * fix(deps): regenerate package-lock.json Moved @openedx/frontend-build from dependencies to devDependencies. Removed direct jest devDependency which was causing ts-jest hoisting issues. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(tests): use require() for MockedPluginSlot in jest.mock Jest hoists jest.mock() calls to the top of the file, which caused MockedPluginSlot to be undefined when the mock factory executed. Using require() inside the factory ensures it loads at runtime. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(types): handle nullable breakpoint types Paragon's breakpoint types now have optional minWidth/maxWidth properties. Added non-null assertions since these values are always defined in practice. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(tests): add IntlProvider to ContentIFrame tests Paragon's ModalDialog now uses useIntl() (openedx/paragon#3624), requiring an IntlProvider in the component ancestry. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(tests): await async operations in Course tests Fixed dangling waitFor blocks that weren't awaited, causing tests to not actually wait for async operations. Changed to properly use await with screen.findBy*() queries. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(tests): use dynamic imports in LearnerToolsSlot tests Jest hoists mock calls but ES imports run before the test body. Using dynamic imports in beforeEach ensures mocks are set up before modules are loaded. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Code <noreply@anthropic.com>
1 parent 1d0ab11 commit 13eea81

File tree

8 files changed

+2690
-2111
lines changed

8 files changed

+2690
-2111
lines changed

package-lock.json

Lines changed: 2639 additions & 2073 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
"@fortawesome/free-regular-svg-icons": "5.15.4",
4545
"@fortawesome/free-solid-svg-icons": "5.15.4",
4646
"@fortawesome/react-fontawesome": "^0.1.4",
47-
"@openedx/frontend-build": "^14.6.2",
4847
"@openedx/frontend-plugin-framework": "^1.7.0",
4948
"@openedx/paragon": "^23.4.5",
5049
"@popperjs/core": "2.11.8",
@@ -74,14 +73,14 @@
7473
"truncate-html": "1.0.4"
7574
},
7675
"devDependencies": {
76+
"@openedx/frontend-build": "^14.6.2",
7777
"@pact-foundation/pact": "^13.0.0",
7878
"@testing-library/jest-dom": "^6.6.3",
7979
"@testing-library/react": "^16.2.0",
8080
"@testing-library/user-event": "14.6.1",
8181
"axios-mock-adapter": "2.1.0",
8282
"bundlewatch": "^0.4.0",
8383
"eslint-import-resolver-webpack": "^0.13.9",
84-
"jest": "^29.7.0",
8584
"jest-console-group-reporter": "^1.1.1",
8685
"jest-when": "^3.6.0",
8786
"rosie": "2.1.1"

src/courseware/course/Course.test.jsx

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -191,23 +191,22 @@ describe('Course', () => {
191191
const { rerender } = render(<Course {...testData} />, { store: testStore });
192192
loadUnit();
193193

194-
waitFor(() => {
195-
expect(screen.findByTestId('sidebar-DISCUSSIONS')).toBeInTheDocument();
196-
expect(screen.findByTestId('sidebar-DISCUSSIONS')).not.toHaveClass('d-none');
197-
});
194+
const sidebar = await screen.findByTestId('sidebar-DISCUSSIONS');
195+
expect(sidebar).toBeInTheDocument();
196+
expect(sidebar).not.toHaveClass('d-none');
198197

199198
rerender(null);
200199
});
201200

202201
it('handles click to open/close notification tray', async () => {
203202
await setupDiscussionSidebar();
204-
waitFor(() => {
205-
const notificationShowButton = screen.findByRole('button', { name: /Show notification tray/i });
206-
expect(screen.queryByRole('region', { name: /notification tray/i })).not.toBeInTheDocument();
207-
fireEvent.click(notificationShowButton);
208-
expect(screen.queryByRole('region', { name: /notification tray/i })).toBeInTheDocument();
209-
expect(screen.queryByRole('region', { name: /notification tray/i })).not.toHaveClass('d-none');
210-
});
203+
const notificationShowButton = await screen.findByRole('button', { name: /Show notification tray/i });
204+
expect(screen.queryByRole('region', { name: /notification tray/i })).not.toBeInTheDocument();
205+
fireEvent.click(notificationShowButton);
206+
207+
const notificationTray = await screen.findByRole('region', { name: /notification tray/i });
208+
expect(notificationTray).toBeInTheDocument();
209+
expect(notificationTray).not.toHaveClass('d-none');
211210
});
212211

213212
it('doesn\'t renders course breadcrumbs by default', async () => {

src/courseware/course/new-sidebar/SidebarContextProvider.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ const SidebarProvider: React.FC<Props> = ({
2626
const { verifiedMode } = useModel('courseHomeMeta', courseId);
2727
const topic = useModel('discussionTopics', unitId);
2828
const windowWidth = useWindowSize().width ?? window.innerWidth;
29-
const shouldDisplayFullScreen = windowWidth < breakpoints.large.minWidth;
30-
const shouldDisplaySidebarOpen = windowWidth > breakpoints.medium.minWidth;
29+
const shouldDisplayFullScreen = windowWidth < breakpoints.large.minWidth!;
30+
const shouldDisplaySidebarOpen = windowWidth > breakpoints.medium.minWidth!;
3131
const query = new URLSearchParams(window.location.search);
3232
const isInitiallySidebarOpen = shouldDisplaySidebarOpen || query.get('sidebar') === 'true';
3333
const sidebarKey = `sidebar.${courseId}`;

src/courseware/course/new-sidebar/sidebars/discussions-notifications/notifications/NotificationsWidget.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ describe('NotificationsWidget', () => {
4444
}
4545

4646
beforeEach(async () => {
47-
global.innerWidth = breakpoints.large.minWidth;
47+
global.innerWidth = breakpoints.large.minWidth!;
4848
store = initializeStore();
4949
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
5050
axiosMock.onGet(courseMetadataUrl).reply(200, defaultMetadata);

src/courseware/course/sequence/Unit/ContentIFrame.test.jsx

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
import { render, screen } from '@testing-library/react';
2+
import { IntlProvider } from '@edx/frontend-platform/i18n';
23

34
import * as hooks from './hooks';
45
import ContentIFrame, { IFRAME_FEATURE_POLICY } from './ContentIFrame';
56

7+
// eslint-disable-next-line react/prop-types
8+
const IntlWrapper = ({ children }) => (
9+
<IntlProvider locale="en">{children}</IntlProvider>
10+
);
11+
612
jest.mock('@edx/frontend-platform/react', () => ({ ErrorPage: () => <div>ErrorPage</div> }));
713

814
jest.mock('@src/generic/PageLoading', () => jest.fn(() => <div>PageLoading</div>));
@@ -59,7 +65,7 @@ describe('ContentIFrame Component', () => {
5965
});
6066
describe('behavior', () => {
6167
beforeEach(() => {
62-
render(<ContentIFrame {...props} />);
68+
render(<ContentIFrame {...props} />, { wrapper: IntlWrapper });
6369
});
6470
it('initializes iframe behavior hook', () => {
6571
expect(hooks.useIFrameBehavior).toHaveBeenCalledWith({
@@ -78,28 +84,28 @@ describe('ContentIFrame Component', () => {
7884
describe('if not hasLoaded', () => {
7985
it('displays errorPage if showError', () => {
8086
hooks.useIFrameBehavior.mockReturnValueOnce({ ...iframeBehavior, showError: true });
81-
render(<ContentIFrame {...props} />);
87+
render(<ContentIFrame {...props} />, { wrapper: IntlWrapper });
8288
const errorPage = screen.getByText('ErrorPage');
8389
expect(errorPage).toBeInTheDocument();
8490
});
8591
it('displays PageLoading component if not showError', () => {
86-
render(<ContentIFrame {...props} />);
92+
render(<ContentIFrame {...props} />, { wrapper: IntlWrapper });
8793
const pageLoading = screen.getByText('PageLoading');
8894
expect(pageLoading).toBeInTheDocument();
8995
});
9096
});
9197
describe('hasLoaded', () => {
9298
it('does not display PageLoading or ErrorPage', () => {
9399
hooks.useIFrameBehavior.mockReturnValueOnce({ ...iframeBehavior, hasLoaded: true });
94-
render(<ContentIFrame {...props} />);
100+
render(<ContentIFrame {...props} />, { wrapper: IntlWrapper });
95101
const pageLoading = screen.queryByText('PageLoading');
96102
expect(pageLoading).toBeNull();
97103
const errorPage = screen.queryByText('ErrorPage');
98104
expect(errorPage).toBeNull();
99105
});
100106
});
101107
it('display iframe with props from hooks', () => {
102-
render(<ContentIFrame {...props} />);
108+
render(<ContentIFrame {...props} />, { wrapper: IntlWrapper });
103109
const iframe = screen.getByTitle(props.title);
104110
expect(iframe).toBeInTheDocument();
105111
expect(iframe).toHaveAttribute('id', props.elementId);
@@ -112,14 +118,14 @@ describe('ContentIFrame Component', () => {
112118
});
113119
describe('if not shouldShowContent', () => {
114120
it('does not show PageLoading, ErrorPage, or unit-iframe-wrapper', () => {
115-
render(<ContentIFrame {...{ ...props, shouldShowContent: false }} />);
121+
render(<ContentIFrame {...{ ...props, shouldShowContent: false }} />, { wrapper: IntlWrapper });
116122
expect(screen.queryByText('PageLoading')).toBeNull();
117123
expect(screen.queryByText('ErrorPage')).toBeNull();
118124
expect(screen.queryByTitle(props.title)).toBeNull();
119125
});
120126
});
121127
it('does not display modal if modalOptions returns isOpen: false', () => {
122-
render(<ContentIFrame {...props} />);
128+
render(<ContentIFrame {...props} />, { wrapper: IntlWrapper });
123129
const modal = screen.queryByRole('dialog');
124130
expect(modal).toBeNull();
125131
});
@@ -138,7 +144,7 @@ describe('ContentIFrame Component', () => {
138144
...modalIFrameData,
139145
modalOptions: { ...modalOptions.withBody, isFullscreen: true },
140146
});
141-
render(<ContentIFrame {...props} />);
147+
render(<ContentIFrame {...props} />, { wrapper: IntlWrapper });
142148
});
143149
it('displays Modal with div wrapping provided body content if modal.body is provided', () => {
144150
const dialog = screen.getByRole('dialog');
@@ -155,7 +161,7 @@ describe('ContentIFrame Component', () => {
155161
...modalIFrameData,
156162
modalOptions: { ...modalOptions.withUrl, isFullscreen: true },
157163
});
158-
render(<ContentIFrame {...props} />);
164+
render(<ContentIFrame {...props} />, { wrapper: IntlWrapper });
159165
});
160166
it('displays Modal with iframe to provided url if modal.body is not provided', () => {
161167
const iframe = screen.getByTitle(modalOptions.withUrl.title);
@@ -169,7 +175,7 @@ describe('ContentIFrame Component', () => {
169175
describe('body modal', () => {
170176
beforeEach(() => {
171177
hooks.useModalIFrameData.mockReturnValueOnce({ ...modalIFrameData, modalOptions: modalOptions.withBody });
172-
render(<ContentIFrame {...props} />);
178+
render(<ContentIFrame {...props} />, { wrapper: IntlWrapper });
173179
});
174180
it('displays Modal with div wrapping provided body content if modal.body is provided', () => {
175181
const dialog = screen.getByRole('dialog');
@@ -182,7 +188,7 @@ describe('ContentIFrame Component', () => {
182188
describe('url modal', () => {
183189
beforeEach(() => {
184190
hooks.useModalIFrameData.mockReturnValueOnce({ ...modalIFrameData, modalOptions: modalOptions.withUrl });
185-
render(<ContentIFrame {...props} />);
191+
render(<ContentIFrame {...props} />, { wrapper: IntlWrapper });
186192
});
187193
it('displays Modal with iframe to provided url if modal.body is not provided', () => {
188194
const iframe = screen.getByTitle(modalOptions.withUrl.title);

src/plugin-slots/LearnerToolsSlot/index.test.jsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
import React from 'react';
22
import { render } from '@testing-library/react';
3-
import { PluginSlot } from '@openedx/frontend-plugin-framework';
4-
import * as auth from '@edx/frontend-platform/auth';
5-
6-
import { LearnerToolsSlot } from './index';
73

84
jest.mock('@openedx/frontend-plugin-framework', () => ({
95
PluginSlot: jest.fn(() => <div data-testid="plugin-slot">Plugin Slot</div>),
@@ -14,17 +10,26 @@ jest.mock('@edx/frontend-platform/auth', () => ({
1410
}));
1511

1612
describe('LearnerToolsSlot', () => {
13+
let auth;
14+
let PluginSlot;
15+
let LearnerToolsSlot;
16+
1717
const defaultProps = {
1818
courseId: 'course-v1:edX+DemoX+Demo_Course',
1919
unitId: 'block-v1:edX+DemoX+Demo_Course+type@vertical+block@unit1',
2020
isStaff: false,
2121
enrollmentMode: 'verified',
2222
};
2323

24-
beforeEach(() => {
24+
beforeEach(async () => {
25+
jest.resetModules();
2526
jest.clearAllMocks();
2627
// Mock document.body for createPortal
2728
document.body.innerHTML = '<div id="root"></div>';
29+
30+
auth = await import('@edx/frontend-platform/auth');
31+
({ PluginSlot } = await import('@openedx/frontend-plugin-framework'));
32+
({ LearnerToolsSlot } = await import('./index'));
2833
});
2934

3035
it('renders PluginSlot with correct props when user is authenticated', () => {

src/setupTest.js

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,17 @@ import { getCourseOutlineStructure } from './courseware/data/thunks';
2626
import { appendBrowserTimezoneToUrl, executeThunk } from './utils';
2727
import buildSimpleCourseAndSequenceMetadata from './courseware/data/__factories__/sequenceMetadata.factory';
2828
import { buildOutlineFromBlocks } from './courseware/data/__factories__/learningSequencesOutline.factory';
29-
import MockedPluginSlot from './tests/MockedPluginSlot';
3029

31-
jest.mock('@openedx/frontend-plugin-framework', () => ({
32-
...jest.requireActual('@openedx/frontend-plugin-framework'),
33-
Plugin: () => 'Plugin',
34-
PluginSlot: MockedPluginSlot,
35-
}));
30+
jest.mock('@openedx/frontend-plugin-framework', () => {
31+
// eslint-disable-next-line global-require
32+
const MockedPluginSlot = require('./tests/MockedPluginSlot').default;
33+
34+
return {
35+
...jest.requireActual('@openedx/frontend-plugin-framework'),
36+
Plugin: () => 'Plugin',
37+
PluginSlot: MockedPluginSlot,
38+
};
39+
});
3640

3741
jest.mock('@src/generic/plugin-store', () => ({
3842
...jest.requireActual('@src/generic/plugin-store'),

0 commit comments

Comments
 (0)