Skip to content

Commit ff696d9

Browse files
committed
test: enhance VideoEditorModal mock initialization
1 parent 1f717da commit ff696d9

3 files changed

Lines changed: 62 additions & 88 deletions

File tree

Lines changed: 46 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,98 +1,58 @@
1-
import { render, waitFor, act } from '@testing-library/react';
2-
import { configureStore } from '@reduxjs/toolkit';
3-
import { MemoryRouter } from 'react-router-dom';
4-
import { AppProvider } from '@edx/frontend-platform/react';
5-
import { IntlProvider } from '@edx/frontend-platform/i18n';
6-
import { initializeMockApp } from '@edx/frontend-platform';
7-
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
1+
import { thunkActions } from '@src/editors/data/redux';
2+
import { initializeMocks, waitFor, act } from '@src/testUtils';
3+
import editorRender, { getEditorStore, PartialEditorState } from '@src/editors/editorTestRender';
84
import VideoEditorModal from './VideoEditorModal';
9-
import { thunkActions } from '../../../data/redux';
105

11-
jest.mock('../../../data/redux', () => ({
12-
...jest.requireActual('../../../data/redux'),
13-
thunkActions: {
14-
video: {
15-
loadVideoData: jest
16-
.fn()
17-
.mockImplementation(() => ({ type: 'MOCK_ACTION' })),
18-
},
19-
},
20-
}));
6+
thunkActions.video.loadVideoData = jest.fn().mockImplementation(() => ({ type: 'MOCK_ACTION' }));
217

22-
const queryClient = new QueryClient({
23-
defaultOptions: {
24-
queries: {
25-
retry: false,
8+
const initialState: PartialEditorState = {
9+
app: {
10+
videos: [],
11+
learningContextId: 'course-v1:test+test+test',
12+
blockId: 'some-block-id',
13+
courseDetails: {},
14+
},
15+
requests: {
16+
uploadAsset: { status: 'inactive', response: {} as any },
17+
uploadTranscript: { status: 'inactive', response: {} as any },
18+
deleteTranscript: { status: 'inactive', response: {} as any },
19+
fetchVideos: { status: 'inactive', response: {} as any },
20+
},
21+
video: {
22+
videoSource: '',
23+
videoId: '',
24+
fallbackVideos: ['', ''],
25+
allowVideoDownloads: false,
26+
allowVideoSharing: { level: 'block', value: false },
27+
thumbnail: null,
28+
transcripts: [],
29+
selectedVideoTranscriptUrls: {},
30+
allowTranscriptDownloads: false,
31+
duration: {
32+
startTime: '00:00:00',
33+
stopTime: '00:00:00',
34+
total: '00:00:00',
2635
},
2736
},
28-
});
37+
};
2938

3039
describe('VideoUploader', () => {
31-
let store;
32-
3340
beforeEach(async () => {
34-
store = configureStore({
35-
reducer: (state, action) => (action && action.newState ? action.newState : state),
36-
preloadedState: {
37-
app: {
38-
videos: [],
39-
learningContextId: 'course-v1:test+test+test',
40-
blockId: 'some-block-id',
41-
courseDetails: {},
42-
},
43-
requests: {
44-
uploadAsset: { status: 'inactive' },
45-
uploadTranscript: { status: 'inactive' },
46-
deleteTranscript: { status: 'inactive' },
47-
fetchVideos: { status: 'inactive' },
48-
},
49-
video: {
50-
videoSource: '',
51-
videoId: '',
52-
fallbackVideos: ['', ''],
53-
allowVideoDownloads: false,
54-
allowVideoSharing: { level: 'block', value: false },
55-
thumbnail: null,
56-
transcripts: [],
57-
transcriptHandlerUrl: '',
58-
selectedVideoTranscriptUrls: {},
59-
allowTranscriptDownloads: false,
60-
duration: {
61-
startTime: '00:00:00',
62-
stopTime: '00:00:00',
63-
total: '00:00:00',
64-
},
65-
},
66-
},
67-
});
68-
initializeMockApp({
69-
authenticatedUser: {
70-
userId: 3,
71-
username: 'test-user',
72-
administrator: true,
73-
roles: [],
74-
},
75-
});
41+
initializeMocks();
7642
});
7743

78-
const renderComponent = async () => render(
79-
<AppProvider store={store} wrapWithRouter={false}>
80-
<IntlProvider locale="en">
81-
<QueryClientProvider client={queryClient}>
82-
<MemoryRouter
83-
initialEntries={[
84-
'/some/path?selectedVideoId=id_1&selectedVideoUrl=https://video.com',
85-
]}
86-
>
87-
<VideoEditorModal isLibrary={false} />
88-
</MemoryRouter>
89-
</QueryClientProvider>
90-
</IntlProvider>
91-
</AppProvider>,
44+
const renderComponent = () => editorRender(
45+
<VideoEditorModal isLibrary={false} />,
46+
{
47+
routerProps: {
48+
initialEntries: ['/some/path?selectedVideoId=id_1&selectedVideoUrl=https://video.com'],
49+
},
50+
initialState,
51+
},
9252
);
9353

9454
it('should render the component and call loadVideoData with correct parameters', async () => {
95-
await renderComponent();
55+
renderComponent();
9656
await waitFor(() => {
9757
expect(thunkActions.video.loadVideoData).toHaveBeenCalledWith(
9858
'id_1',
@@ -102,10 +62,11 @@ describe('VideoUploader', () => {
10262
});
10363

10464
it('should call loadVideoData again when isLoaded state changes', async () => {
105-
await renderComponent();
65+
renderComponent();
10666
await waitFor(() => {
107-
expect(thunkActions.video.loadVideoData).toHaveBeenCalledTimes(2);
67+
expect(thunkActions.video.loadVideoData).toHaveBeenCalledTimes(1);
10868
});
69+
const store = getEditorStore();
10970

11071
act(() => {
11172
store.dispatch({
@@ -121,7 +82,7 @@ describe('VideoUploader', () => {
12182
});
12283

12384
await waitFor(() => {
124-
expect(thunkActions.video.loadVideoData).toHaveBeenCalledTimes(3);
85+
expect(thunkActions.video.loadVideoData).toHaveBeenCalledTimes(2);
12586
});
12687
});
12788
});

src/editors/data/redux/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ const rootReducer = (state: any, action: any) => {
2626
return editorReducer(undefined, action);
2727
}
2828

29+
// For test purposes only:
30+
if (action.type === 'UPDATE_STATE') {
31+
return action.newState;
32+
}
33+
2934
return editorReducer(state, action);
3035
};
3136

src/editors/editorTestRender.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
import React from 'react';
22
import { Provider } from 'react-redux';
3+
import { Store } from 'redux';
34
import { render as baseRender, WrapperOptions } from '../testUtils';
45
import { EditorContextProvider } from './EditorContext';
5-
import { type EditorState, initializeStore } from './data/redux'; // adjust path if needed
6+
import { createStore } from './data/store';
7+
import { type EditorState } from './data/redux'; // adjust path if needed
68

79
type RecursivePartial<T> = {
810
[P in keyof T]?: RecursivePartial<T[P]>;
911
};
1012

1113
export type PartialEditorState = RecursivePartial<EditorState>;
1214

15+
let editorStore: Store<EditorState>;
16+
1317
/**
1418
* Custom render function for testing React components with the editor context and Redux store.
1519
*
@@ -27,16 +31,20 @@ export const editorRender = (
2731
) => {
2832
// We might need a way for the test cases to access this store directly. In that case we could allow either an
2933
// initialState parameter OR an editorStore parameter.
30-
const store = initializeStore(initialState as any);
34+
editorStore = createStore(initialState);
3135

3236
return baseRender(ui, {
3337
...options,
3438
extraWrapper: ({ children }) => (
3539
<EditorContextProvider learningContextId={learningContextId}>
36-
<Provider store={store}>
40+
<Provider store={editorStore}>
3741
{children}
3842
</Provider>
3943
</EditorContextProvider>
4044
),
4145
});
4246
};
47+
48+
export function getEditorStore() {
49+
return editorStore;
50+
}

0 commit comments

Comments
 (0)