diff --git a/src/editors/containers/VideoUploadEditor/__snapshots__/index.test.jsx.snap b/src/editors/containers/VideoUploadEditor/__snapshots__/index.test.jsx.snap index 975e6dcea..9e3efe377 100644 --- a/src/editors/containers/VideoUploadEditor/__snapshots__/index.test.jsx.snap +++ b/src/editors/containers/VideoUploadEditor/__snapshots__/index.test.jsx.snap @@ -1,166 +1,227 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`VideoUploadEditor renders without errors 1`] = ` +exports[`VideoUploader snapshots renders as expected with default behavior 1`] = `
`; -exports[`VideoUploader renders without errors 1`] = ` +exports[`VideoUploader snapshots renders as expected with error message 1`] = `
-
+
-
- -
+
+ + + + + + +
+
+ + OR + +
+
+ +
+
+
+ +
`; + +exports[`VideoUploaderEdirtor snapshots renders as expected with default behavior 1`] = ` + + + +`; diff --git a/src/editors/containers/VideoUploadEditor/hooks.test.js b/src/editors/containers/VideoUploadEditor/hooks.test.js index c1ef806df..23dc1e519 100644 --- a/src/editors/containers/VideoUploadEditor/hooks.test.js +++ b/src/editors/containers/VideoUploadEditor/hooks.test.js @@ -1,92 +1,31 @@ -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); +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); + 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(''); + }); }); - - 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); - }); - }); - - 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); - }); - - 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); - }); - - 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.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/data/redux/thunkActions/video.js b/src/editors/data/redux/thunkActions/video.js index 194ae5668..3e2c10ed5 100644 --- a/src/editors/data/redux/thunkActions/video.js +++ b/src/editors/data/redux/thunkActions/video.js @@ -388,7 +388,6 @@ export const uploadVideo = ({ supportedFiles, setLoadSpinner, postUploadRedirect 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; 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.'); + }); +});