`;
-exports[`VideoUploader renders without errors 1`] = `
+exports[`VideoUploader snapshots renders as expected with error message 1`] = `
-
+
-
-
-
-
-
-
- FormattedMessage
-
-
- FormattedMessage
-
-
-
-
- OR
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+ OR
+
+
+
+
+
+
`;
+
+exports[`VideoUploaderEdirtor snapshots renders as expected with default behavior 1`] = `
+
+
+
+`;
diff --git a/src/editors/containers/VideoUploadEditor/hooks.js b/src/editors/containers/VideoUploadEditor/hooks.js
index 7db83f47b..415e96992 100644
--- a/src/editors/containers/VideoUploadEditor/hooks.js
+++ b/src/editors/containers/VideoUploadEditor/hooks.js
@@ -1,66 +1,78 @@
-import * as requests from '../../data/redux/thunkActions/requests';
+import React from 'react';
import * as module from './hooks';
import { selectors } from '../../data/redux';
import store from '../../data/store';
import * as appHooks from '../../hooks';
+const extToMime = {
+ mp4: 'video/mp4',
+ mov: 'video/quicktime',
+};
+
export const {
navigateTo,
} = appHooks;
-export const uploadVideo = async ({ dispatch, supportedFiles }) => {
- const data = { files: [] };
- supportedFiles.forEach((file) => {
- data.files.push({
- file_name: file.name,
- content_type: file.type,
- });
- });
- const onFileUploadedHook = module.onFileUploaded();
- dispatch(await requests.uploadVideo({
- data,
- onSuccess: async (response) => {
- const { files } = response.data;
- await Promise.all(Object.values(files).map(async (fileObj) => {
- const fileName = fileObj.file_name;
- const edxVideoId = fileObj.edx_video_id;
- const uploadUrl = fileObj.upload_url;
- const uploadFile = supportedFiles.find((file) => file.name === fileName);
-
- if (!uploadFile) {
- console.error(`Could not find file object with name "${fileName}" in supportedFiles array.`);
- return;
- }
- const formData = new FormData();
- formData.append('uploaded-file', uploadFile);
- await fetch(uploadUrl, {
- method: 'PUT',
- body: formData,
- headers: {
- 'Content-Type': 'multipart/form-data',
- },
- })
- .then(() => onFileUploadedHook(edxVideoId))
- .catch((error) => console.error('Error uploading file:', error));
- }));
- },
- }));
+export const state = {
+ loading: (val) => React.useState(val),
+ errorMessage: (val) => React.useState(val),
+ textInputValue: (val) => React.useState(val),
};
-export const onFileUploaded = () => {
- const state = store.getState();
- const learningContextId = selectors.app.learningContextId(state);
- const blockId = selectors.app.blockId(state);
- return (edxVideoId) => navigateTo(`/course/${learningContextId}/editor/video/${blockId}?selectedVideoId=${edxVideoId}`);
+export const uploadEditor = () => {
+ const [loading, setLoading] = module.state.loading(false);
+ const [errorMessage, setErrorMessage] = module.state.errorMessage(null);
+ return {
+ loading,
+ setLoading,
+ errorMessage,
+ setErrorMessage,
+ };
};
-export const onUrlUploaded = () => {
- const state = store.getState();
- const learningContextId = selectors.app.learningContextId(state);
- const blockId = selectors.app.blockId(state);
+export const uploader = () => {
+ const [textInputValue, settextInputValue] = module.state.textInputValue('');
+ return {
+ textInputValue,
+ settextInputValue,
+ };
+};
+
+export const postUploadRedirect = (storeState) => {
+ const learningContextId = selectors.app.learningContextId(storeState);
+ const blockId = selectors.app.blockId(storeState);
return (videoUrl) => navigateTo(`/course/${learningContextId}/editor/video/${blockId}?selectedVideoUrl=${videoUrl}`);
};
-export default {
- uploadVideo,
+export const onVideoUpload = () => {
+ const storeState = store.getState();
+ return module.postUploadRedirect(storeState);
+};
+
+const getFileExtension = (filename) => filename.slice(Math.abs(filename.lastIndexOf('.') - 1) + 2);
+
+export const fileValidator = (setLoading, setErrorMessage, uploadVideo) => (file) => {
+ const supportedFormats = Object.keys(extToMime);
+ const ext = getFileExtension(file.name);
+ const type = extToMime[ext] || '';
+ const newFile = new File([file], file.name, { type });
+
+ if (supportedFormats.includes(ext)) {
+ uploadVideo({
+ supportedFiles: [newFile],
+ setLoadSpinner: setLoading,
+ postUploadRedirect: onVideoUpload(),
+ });
+ } else {
+ const errorMsg = 'Video must be an MP4 or MOV file';
+ setErrorMessage(errorMsg);
+ }
+};
+
+export default {
+ postUploadRedirect,
+ uploadEditor,
+ uploader,
+ onVideoUpload,
+ fileValidator,
};
diff --git a/src/editors/containers/VideoUploadEditor/hooks.test.js b/src/editors/containers/VideoUploadEditor/hooks.test.js
index c1ef806df..a78978601 100644
--- a/src/editors/containers/VideoUploadEditor/hooks.test.js
+++ b/src/editors/containers/VideoUploadEditor/hooks.test.js
@@ -1,92 +1,50 @@
-import hooks from './hooks';
-import * as requests from '../../data/redux/thunkActions/requests';
+import * as hooks from './hooks';
+import { MockUseState } from '../../../testUtils';
-jest.mock('../../data/redux/thunkActions/requests');
-
-describe('uploadVideo', () => {
- const dispatch = jest.fn();
- const supportedFiles = [
- new File(['content1'], 'file1.mp4', { type: 'video/mp4' }),
- new File(['content2'], 'file2.mov', { type: 'video/quicktime' }),
- ];
+const state = new MockUseState(hooks);
+const setLoading = jest.fn();
+const setErrorMessage = jest.fn();
+const uploadVideo = jest.fn();
+describe('Video Upload Editor hooks', () => {
beforeEach(() => {
- jest.resetAllMocks();
- jest.restoreAllMocks();
+ jest.clearAllMocks();
});
-
- it('should dispatch uploadVideo action with correct data and onSuccess callback', async () => {
- requests.uploadVideo.mockImplementation(() => 'requests.uploadVideo');
- const data = {
- files: [
- { file_name: 'file1.mp4', content_type: 'video/mp4' },
- { file_name: 'file2.mov', content_type: 'video/quicktime' },
- ],
- };
-
- await hooks.uploadVideo({ dispatch, supportedFiles });
-
- expect(requests.uploadVideo).toHaveBeenCalledWith({ data, onSuccess: expect.any(Function) });
- expect(dispatch).toHaveBeenCalledWith('requests.uploadVideo');
+ describe('state hooks', () => {
+ state.testGetter(state.keys.loading);
+ state.testGetter(state.keys.errorMessage);
+ state.testGetter(state.keys.textInputValue);
});
+ describe('using state', () => {
+ beforeEach(() => { state.mock(); });
+ afterEach(() => { state.restore(); });
- it('should call fetch with correct arguments for each file', async () => {
- const mockResponseData = { success: true };
- const mockFetchResponse = Promise.resolve({ data: mockResponseData });
- global.fetch = jest.fn().mockImplementation(() => mockFetchResponse);
- const response = {
- files: [
- { file_name: 'file1.mp4', upload_url: 'http://example.com/put_video1' },
- { file_name: 'file2.mov', upload_url: 'http://example.com/put_video2' },
- ],
- };
- const mockRequestResponse = { data: response };
- requests.uploadVideo.mockImplementation(async ({ onSuccess }) => {
- await onSuccess(mockRequestResponse);
- });
-
- await hooks.uploadVideo({ dispatch, supportedFiles });
-
- expect(fetch).toHaveBeenCalledTimes(2);
- response.files.forEach(({ upload_url: uploadUrl }, index) => {
- expect(fetch.mock.calls[index][0]).toEqual(uploadUrl);
- });
- supportedFiles.forEach((file, index) => {
- expect(fetch.mock.calls[index][1].body.get('uploaded-file')).toBe(file);
+ describe('Hooks for Video Upload', () => {
+ beforeEach(() => {
+ hooks.uploadEditor();
+ hooks.uploader();
+ });
+ it('initialize state with correct values', () => {
+ expect(state.stateVals.loading).toEqual(false);
+ expect(state.stateVals.errorMessage).toEqual(null);
+ expect(state.stateVals.textInputValue).toEqual('');
+ });
});
});
-
- it('should log an error if fetch failed to upload a file', async () => {
- const error = new Error('Uh-oh!');
- global.fetch = jest.fn().mockRejectedValue(error);
- const response = {
- files: [
- { file_name: 'file1.mp4', upload_url: 'http://example.com/put_video1' },
- { file_name: 'file2.mov', upload_url: 'http://example.com/put_video2' },
- ],
- };
- const mockRequestResponse = { data: response };
- requests.uploadVideo.mockImplementation(async ({ onSuccess }) => {
- await onSuccess(mockRequestResponse);
+ describe('File Validation', () => {
+ it('Checks with valid MIME type', () => {
+ const file = new File(['(⌐□_□)'], 'video.mp4', { type: 'video/mp4' });
+ const validator = hooks.fileValidator(setLoading, setErrorMessage, uploadVideo);
+ validator(file);
+ expect(uploadVideo).toHaveBeenCalled();
+ expect(setErrorMessage).not.toHaveBeenCalled();
});
-
- await hooks.uploadVideo({ dispatch, supportedFiles });
- });
-
- it('should log an error if file object is not found in supportedFiles array', () => {
- const response = {
- files: [
- { file_name: 'file2.mov', upload_url: 'http://example.com/put_video2' },
- ],
- };
- const mockRequestResponse = { data: response };
- const spyConsoleError = jest.spyOn(console, 'error');
- requests.uploadVideo.mockImplementation(({ onSuccess }) => {
- onSuccess(mockRequestResponse);
+ it('Checks with invalid MIME type', () => {
+ const file = new File(['(⌐□_□)'], 'video.gif', { type: 'video/mp4' });
+ const validator = hooks.fileValidator(setLoading, setErrorMessage, uploadVideo);
+ validator(file);
+ expect(uploadVideo).not.toHaveBeenCalled();
+ expect(setErrorMessage).toHaveBeenCalled();
});
-
- hooks.uploadVideo({ dispatch, supportedFiles: [supportedFiles[0]] });
-
- expect(spyConsoleError).toHaveBeenCalledWith('Could not find file object with name "file2.mov" in supportedFiles array.');
});
});
diff --git a/src/editors/containers/VideoUploadEditor/index.jsx b/src/editors/containers/VideoUploadEditor/index.jsx
index 54780e719..853b247da 100644
--- a/src/editors/containers/VideoUploadEditor/index.jsx
+++ b/src/editors/containers/VideoUploadEditor/index.jsx
@@ -1,19 +1,19 @@
-import React, { useState } from 'react';
+import React from 'react';
import PropTypes from 'prop-types';
import { useDropzone } from 'react-dropzone';
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
-import { Icon, IconButton } from '@edx/paragon';
-import './index.scss';
-import { useDispatch } from 'react-redux';
+import { Icon, IconButton, Spinner } from '@edx/paragon';
import { ArrowForward, Close, FileUpload } from '@edx/paragon/icons';
+import { connect } from 'react-redux';
+import { thunkActions } from '../../data/redux';
+import './index.scss';
import * as hooks from './hooks';
-import messages from '../../messages';
+import messages from './messages';
import * as editorHooks from '../EditorContainer/hooks';
export const VideoUploader = ({ onUpload, errorMessage }) => {
- const [, setUploadedFile] = useState();
- const [textInputValue, setTextInputValue] = useState('');
- const onUrlUpdatedHook = hooks.onUrlUploaded();
+ const { textInputValue, setTextInputValue } = hooks.uploader();
+ const onURLUpload = hooks.onVideoUpload();
const { getRootProps, getInputProps, isDragActive } = useDropzone({
accept: 'video/*',
@@ -21,7 +21,6 @@ export const VideoUploader = ({ onUpload, errorMessage }) => {
onDrop: (acceptedFiles) => {
if (acceptedFiles.length > 0) {
const uploadfile = acceptedFiles[0];
- setUploadedFile(uploadfile);
onUpload(uploadfile);
}
},
@@ -32,7 +31,7 @@ export const VideoUploader = ({ onUpload, errorMessage }) => {
};
const handleSaveButtonClick = () => {
- onUrlUpdatedHook(textInputValue);
+ onURLUpload(textInputValue);
};
if (errorMessage) {
@@ -85,52 +84,53 @@ VideoUploader.propTypes = {
intl: intlShape.isRequired,
};
-const VideoUploadEditor = ({ intl, onClose }) => {
- const dispatch = useDispatch();
- const [errorMessage, setErrorMessage] = useState(null);
- const handleCancel = () => {
- editorHooks.handleCancel({ onClose });
- };
+const VideoUploadEditor = (
+ {
+ intl,
+ onClose,
+ // Redux states
+ uploadVideo,
+ },
+) => {
+ const {
+ loading,
+ setLoading,
+ errorMessage,
+ setErrorMessage,
+ } = hooks.uploadEditor();
+ const handleCancel = editorHooks.handleCancel({ onClose });
const handleDrop = (file) => {
if (!file) {
console.log('No file selected.');
return;
}
-
- const extToMime = {
- mp4: 'video/mp4',
- mov: 'video/quicktime',
- };
- const supportedFormats = Object.keys(extToMime);
-
- function getFileExtension(filename) {
- return filename.slice(Math.abs(filename.lastIndexOf('.') - 1) + 2);
- }
-
- const ext = getFileExtension(file.name);
- const type = extToMime[ext] || '';
- const newFile = new File([file], file.name, { type });
-
- if (supportedFormats.includes(ext)) {
- hooks.uploadVideo({ dispatch, supportedFiles: [newFile] });
- } else {
- const errorMsg = 'Video must be an MP4 or MOV file';
- console.log(errorMsg);
- setErrorMessage(errorMsg);
- }
+ const validator = hooks.fileValidator(setLoading, setErrorMessage, uploadVideo);
+ validator(file);
};
return (
-
-
-
-
-
+
+ {(!loading) ? (
+
+ ) : (
+
+
+
+ )}
);
};
@@ -138,6 +138,13 @@ const VideoUploadEditor = ({ intl, onClose }) => {
VideoUploadEditor.propTypes = {
intl: intlShape.isRequired,
onClose: PropTypes.func.isRequired,
+ uploadVideo: PropTypes.func.isRequired,
};
-export default injectIntl(VideoUploadEditor);
+export const mapStateToProps = () => ({});
+
+export const mapDispatchToProps = {
+ uploadVideo: thunkActions.video.uploadVideo,
+};
+
+export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(VideoUploadEditor));
diff --git a/src/editors/containers/VideoUploadEditor/index.test.jsx b/src/editors/containers/VideoUploadEditor/index.test.jsx
index 8f256e457..3b6e97c90 100644
--- a/src/editors/containers/VideoUploadEditor/index.test.jsx
+++ b/src/editors/containers/VideoUploadEditor/index.test.jsx
@@ -1,142 +1,37 @@
import React from 'react';
-import { IntlProvider } from '@edx/frontend-platform/i18n';
-import { render, fireEvent, act } from '@testing-library/react';
import '@testing-library/jest-dom';
+import { shallow } from 'enzyme';
import VideoUploadEditor, { VideoUploader } from '.';
-import * as hooks from './hooks';
-import * as appHooks from '../../hooks';
-
-const mockDispatch = jest.fn();
-const mockOnUpload = jest.fn();
-
-jest.mock('react-redux', () => ({
- useDispatch: () => mockDispatch,
-}));
-jest.mock('../../hooks', () => ({
- ...jest.requireActual('../../hooks'),
- navigateTo: jest.fn((args) => ({ navigateTo: args })),
-}));
+import { formatMessage } from '../../../testUtils';
const defaultEditorProps = {
- intl: {},
+ onClose: jest.fn().mockName('props.onClose'),
+ intl: { formatMessage },
+ uploadVideo: jest.fn(),
};
const defaultUploaderProps = {
- onUpload: mockOnUpload,
- errorMessage: '',
- intl: {},
+ onUpload: jest.fn(),
+ errorMessage: null,
+ intl: { formatMessage },
};
-const renderEditorComponent = (props = defaultEditorProps) => render(
-
-
- ,
-);
-
-const renderUploaderComponent = (props = defaultUploaderProps) => render(
-
-
- ,
-);
-
-describe('VideoUploadEditor', () => {
- afterEach(() => {
- jest.clearAllMocks();
- });
-
- it('renders without errors', () => {
- const { container } = renderEditorComponent();
- expect(container).toMatchSnapshot();
- });
-
- it('updates the input field value when user types', () => {
- const { getByPlaceholderText } = renderEditorComponent();
- const input = getByPlaceholderText('Paste your video ID or URL');
-
- fireEvent.change(input, { target: { value: 'test value' } });
- expect(input.value).toBe('test value');
- });
-
- it('click on the save button', () => {
- const { getByPlaceholderText, getByTestId } = renderEditorComponent();
- const testValue = 'test vale';
- const input = getByPlaceholderText('Paste your video ID or URL');
- fireEvent.change(input, { target: { value: testValue } });
- const button = getByTestId('inputSaveButton');
- fireEvent.click(button);
- expect(appHooks.navigateTo).toHaveBeenCalled();
- });
-
- it('shows error message with unsupported files', async () => {
- const { getByTestId, findByText } = renderEditorComponent();
- const fileInput = getByTestId('fileInput');
-
- const unsupportedFile = new File(['(⌐□_□)'], 'unsupported.avi', { type: 'video/avi' });
- fireEvent.change(fileInput, { target: { files: [unsupportedFile] } });
-
- const errorMsg = await findByText('Video must be an MP4 or MOV file');
- expect(errorMsg).toBeInTheDocument();
- });
-
- it('calls uploadVideo with supported files', async () => {
- const uploadVideoSpy = jest.spyOn(hooks, 'uploadVideo');
- const { container } = renderEditorComponent();
- const dropzone = container.querySelector('.dropzone-middle');
-
- const supportedFile = new File(['(⌐□_□)'], 'supported.mp4', { type: 'video/mp4' });
-
- await act(async () => {
- fireEvent.drop(dropzone, {
- dataTransfer: {
- files: [supportedFile],
- types: ['Files'],
- },
- });
- });
-
- expect(uploadVideoSpy).toHaveBeenCalledWith(expect.objectContaining({
- dispatch: mockDispatch,
- supportedFiles: expect.arrayContaining([
- expect.objectContaining({
- name: supportedFile.name,
- type: supportedFile.type,
- size: supportedFile.size,
- }),
- ]),
- }));
- });
-});
-
describe('VideoUploader', () => {
- afterEach(() => {
- jest.clearAllMocks();
- });
-
- it('renders without errors', () => {
- const { container } = renderUploaderComponent();
- expect(container).toMatchSnapshot();
- });
-
- it('renders with an error message', () => {
- const errorMessage = 'Video must be an MP4 or MOV file';
- const { getByText } = renderUploaderComponent({ ...defaultUploaderProps, errorMessage });
- expect(getByText(errorMessage)).toBeInTheDocument();
- });
-
- it('calls the onUpload function when a supported file is dropped', async () => {
- const { container } = renderUploaderComponent();
- const dropzone = container.querySelector('.dropzone-middle');
- const file = new File(['(⌐□_□)'], 'video.mp4', { type: 'video/mp4' });
-
- await act(async () => {
- fireEvent.drop(dropzone, {
- dataTransfer: {
- files: [file],
- types: ['Files'],
- },
- });
+ describe('snapshots', () => {
+ test('renders as expected with default behavior', () => {
+ expect(shallow(
)).toMatchSnapshot();
+ });
+ test('renders as expected with error message', () => {
+ const defaultUploaderPropsWithError = { ...defaultUploaderProps, errorMessages: 'Some Error' };
+ expect(shallow(
)).toMatchSnapshot();
+ });
+ });
+});
+
+describe('VideoUploaderEdirtor', () => {
+ describe('snapshots', () => {
+ test('renders as expected with default behavior', () => {
+ expect(shallow(
)).toMatchSnapshot();
});
-
- expect(mockOnUpload).toHaveBeenCalledWith(file);
});
});
diff --git a/src/editors/containers/VideoUploadEditor/messages.js b/src/editors/containers/VideoUploadEditor/messages.js
new file mode 100644
index 000000000..134962cd4
--- /dev/null
+++ b/src/editors/containers/VideoUploadEditor/messages.js
@@ -0,0 +1,21 @@
+import { defineMessages } from '@edx/frontend-platform/i18n';
+
+const messages = defineMessages({
+ spinnerScreenReaderText: {
+ id: 'authoring.videoUpload.spinnerScreenReaderText',
+ defaultMessage: 'loading',
+ description: 'Loading message for spinner screenreader text.',
+ },
+ dropVideoFileHere: {
+ defaultMessage: 'Drag and drop video here or click to upload',
+ id: 'VideoUploadEditor.dropVideoFileHere',
+ description: 'Display message for Drag and Drop zone',
+ },
+ info: {
+ id: 'VideoUploadEditor.uploadInfo',
+ defaultMessage: 'Upload MP4 or MOV files (5 GB max)',
+ description: 'Info message for supported formats',
+ },
+});
+
+export default messages;
diff --git a/src/editors/data/redux/thunkActions/video.js b/src/editors/data/redux/thunkActions/video.js
index d407b9d48..3e2c10ed5 100644
--- a/src/editors/data/redux/thunkActions/video.js
+++ b/src/editors/data/redux/thunkActions/video.js
@@ -370,6 +370,45 @@ export const replaceTranscript = ({ newFile, newFilename, language }) => (dispat
}));
};
+export const uploadVideo = ({ supportedFiles, setLoadSpinner, postUploadRedirect }) => (dispatch) => {
+ const data = { files: [] };
+ setLoadSpinner(true);
+ supportedFiles.forEach((file) => {
+ data.files.push({
+ file_name: file.name,
+ content_type: file.type,
+ });
+ });
+ dispatch(requests.uploadVideo({
+ data,
+ onSuccess: async (response) => {
+ const { files } = response.data;
+ await Promise.all(Object.values(files).map(async (fileObj) => {
+ const fileName = fileObj.file_name;
+ const edxVideoId = fileObj.edx_video_id;
+ const uploadUrl = fileObj.upload_url;
+ const uploadFile = supportedFiles.find((file) => file.name === fileName);
+ if (!uploadFile) {
+ console.error(`Could not find file object with name "${fileName}" in supportedFiles array.`);
+ return;
+ }
+ const formData = new FormData();
+ formData.append('uploaded-file', uploadFile);
+ await fetch(uploadUrl, {
+ method: 'PUT',
+ body: formData,
+ headers: {
+ 'Content-Type': 'multipart/form-data',
+ },
+ })
+ .then(() => postUploadRedirect(edxVideoId))
+ .catch((error) => console.error('Error uploading file:', error));
+ }));
+ setLoadSpinner(false);
+ },
+ }));
+};
+
export default {
loadVideoData,
determineVideoSources,
@@ -382,4 +421,5 @@ export default {
updateTranscriptLanguage,
replaceTranscript,
uploadHandout,
+ uploadVideo,
};
diff --git a/src/editors/data/redux/thunkActions/video.test.js b/src/editors/data/redux/thunkActions/video.test.js
index 841cb2b36..a8b0b177f 100644
--- a/src/editors/data/redux/thunkActions/video.test.js
+++ b/src/editors/data/redux/thunkActions/video.test.js
@@ -32,6 +32,7 @@ jest.mock('./requests', () => ({
checkTranscriptsForImport: (args) => ({ checkTranscriptsForImport: args }),
importTranscript: (args) => ({ importTranscript: args }),
fetchVideoFeatures: (args) => ({ fetchVideoFeatures: args }),
+ uploadVideo: (args) => ({ uploadVideo: args }),
}));
jest.mock('../../../utils', () => ({
@@ -669,3 +670,79 @@ describe('video thunkActions', () => {
});
});
});
+
+describe('uploadVideo', () => {
+ let dispatch;
+ let setLoadSpinner;
+ let postUploadRedirect;
+ let dispatchedAction;
+ const supportedFiles = [
+ new File(['content1'], 'file1.mp4', { type: 'video/mp4' }),
+ new File(['content2'], 'file2.mov', { type: 'video/quicktime' }),
+ ];
+
+ beforeEach(() => {
+ dispatch = jest.fn((action) => ({ dispatch: action }));
+ setLoadSpinner = jest.fn();
+ postUploadRedirect = jest.fn();
+ jest.resetAllMocks();
+ jest.restoreAllMocks();
+ });
+
+ it('dispatch uploadVideo action with right data', async () => {
+ const data = {
+ files: [
+ { file_name: 'file1.mp4', content_type: 'video/mp4' },
+ { file_name: 'file2.mov', content_type: 'video/quicktime' },
+ ],
+ };
+
+ thunkActions.uploadVideo({ supportedFiles, setLoadSpinner, postUploadRedirect })(dispatch);
+ [[dispatchedAction]] = dispatch.mock.calls;
+ expect(dispatchedAction.uploadVideo).not.toEqual(undefined);
+ expect(setLoadSpinner).toHaveBeenCalled();
+ expect(dispatchedAction.uploadVideo.data).toEqual(data);
+ });
+
+ it('should call fetch with correct arguments for each file', async () => {
+ const mockResponseData = { success: true };
+ const mockFetchResponse = Promise.resolve({ data: mockResponseData });
+ global.fetch = jest.fn().mockImplementation(() => mockFetchResponse);
+ const response = {
+ files: [
+ { file_name: 'file1.mp4', upload_url: 'http://example.com/put_video1' },
+ { file_name: 'file2.mov', upload_url: 'http://example.com/put_video2' },
+ ],
+ };
+ const mockRequestResponse = { data: response };
+ thunkActions.uploadVideo({ supportedFiles, setLoadSpinner, postUploadRedirect })(dispatch);
+ [[dispatchedAction]] = dispatch.mock.calls;
+
+ dispatchedAction.uploadVideo.onSuccess(mockRequestResponse);
+
+ expect(fetch).toHaveBeenCalledTimes(2);
+ response.files.forEach(({ upload_url: uploadUrl }, index) => {
+ expect(fetch.mock.calls[index][0]).toEqual(uploadUrl);
+ });
+ supportedFiles.forEach((file, index) => {
+ expect(fetch.mock.calls[index][1].body.get('uploaded-file')).toBe(file);
+ });
+ });
+
+ it('should log an error if file object is not found in supportedFiles array', () => {
+ const mockResponseData = { success: true };
+ const mockFetchResponse = Promise.resolve({ data: mockResponseData });
+ global.fetch = jest.fn().mockImplementation(() => mockFetchResponse);
+ const response = {
+ files: [
+ { file_name: 'file2.gif', upload_url: 'http://example.com/put_video2' },
+ ],
+ };
+ const mockRequestResponse = { data: response };
+ const spyConsoleError = jest.spyOn(console, 'error');
+
+ thunkActions.uploadVideo({ supportedFiles: [supportedFiles[0]], setLoadSpinner, postUploadRedirect })(dispatch);
+ dispatchedAction.uploadVideo.onSuccess(mockRequestResponse);
+ expect(spyConsoleError).toHaveBeenCalledWith('Could not find file object with name "file2.gif" in supportedFiles array.');
+ });
+});