From e20dedd0a5d06467d4e3b7ded855d78dfff16cfc Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Thu, 20 Apr 2023 14:23:12 -0500 Subject: [PATCH] feat: Allow to import transcripts from selected video --- .../TranscriptWidget/Transcript.jsx | 7 + .../TranscriptWidget/Transcript.test.jsx | 10 + .../TranscriptWidget/TranscriptActionMenu.jsx | 12 +- .../TranscriptActionMenu.test.jsx | 8 + .../__snapshots__/Transcript.test.jsx.snap | 18 ++ .../TranscriptActionMenu.test.jsx.snap | 54 ++++++ .../__snapshots__/index.test.jsx.snap | 176 ++++++++++++++++++ .../components/TranscriptWidget/index.jsx | 5 + .../TranscriptWidget/index.test.jsx | 12 ++ .../containers/VideoUploadEditor/hooks.js | 2 +- .../VideoUploadEditor/hooks.test.js | 9 +- src/editors/data/redux/thunkActions/video.js | 9 +- .../data/redux/thunkActions/video.test.js | 5 +- src/editors/data/redux/video/reducer.js | 1 + src/editors/data/redux/video/selectors.js | 15 +- src/editors/data/services/cms/api.test.js | 3 +- src/editors/data/services/cms/urls.js | 4 + 17 files changed, 335 insertions(+), 15 deletions(-) diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/Transcript.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/Transcript.jsx index 15bc19b47..a6c882109 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/Transcript.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/Transcript.jsx @@ -40,6 +40,7 @@ export const hooks = { export const Transcript = ({ index, language, + transcriptUrl, // redux deleteTranscript, }) => { @@ -90,6 +91,7 @@ export const Transcript = ({ )} @@ -99,9 +101,14 @@ export const Transcript = ({ ); }; +Transcript.defaultProps = { + transcriptUrl: undefined, +}; + Transcript.propTypes = { index: PropTypes.number.isRequired, language: PropTypes.string.isRequired, + transcriptUrl: PropTypes.string, deleteTranscript: PropTypes.func.isRequired, }; diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/Transcript.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/Transcript.test.jsx index 428532179..627586993 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/Transcript.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/Transcript.test.jsx @@ -80,6 +80,16 @@ describe('Transcript Component', () => { shallow(), ).toMatchSnapshot(); }); + test('snapshots: renders as expected with transcriptUrl', () => { + jest.spyOn(module.hooks, 'setUpDeleteConfirmation').mockImplementationOnce(() => ({ + inDeleteConfirmation: false, + launchDeleteConfirmation: jest.fn().mockName('launchDeleteConfirmation'), + cancelDelete: jest.fn().mockName('cancelDelete'), + })); + expect( + shallow(), + ).toMatchSnapshot(); + }); }); }); }); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/TranscriptActionMenu.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/TranscriptActionMenu.jsx index 625ea99fb..d0ae933cd 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/TranscriptActionMenu.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/TranscriptActionMenu.jsx @@ -25,13 +25,14 @@ export const hooks = { export const TranscriptActionMenu = ({ index, language, + transcriptUrl, launchDeleteConfirmation, // redux getTranscriptDownloadUrl, - + buildTranscriptUrl, }) => { const input = fileInput({ onAddFile: module.hooks.replaceFileCallback({ language, dispatch: useDispatch() }) }); - const downloadLink = getTranscriptDownloadUrl({ language }); + const downloadLink = transcriptUrl ? buildTranscriptUrl({ transcriptUrl }) : getTranscriptDownloadUrl({ language }); return ( ({ getTranscriptDownloadUrl: selectors.video.getTranscriptDownloadUrl(state), + buildTranscriptUrl: selectors.video.buildTranscriptUrl(state), }); export const mapDispatchToProps = { diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/TranscriptActionMenu.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/TranscriptActionMenu.test.jsx index 32936179f..255ca1334 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/TranscriptActionMenu.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/TranscriptActionMenu.test.jsx @@ -25,6 +25,7 @@ jest.mock('../../../../../../data/redux', () => ({ selectors: { video: { getTranscriptDownloadUrl: jest.fn(args => ({ getTranscriptDownloadUrl: args })).mockName('selectors.video.getTranscriptDownloadUrl'), + buildTranscriptUrl: jest.fn(args => ({ buildTranscriptUrl: args })).mockName('selectors.video.buildTranscriptUrl'), }, }, })); @@ -62,6 +63,7 @@ describe('TranscriptActionMenu', () => { launchDeleteConfirmation: jest.fn().mockName('launchDeleteConfirmation'), // redux getTranscriptDownloadUrl: jest.fn().mockName('selectors.video.getTranscriptDownloadUrl'), + buildTranscriptUrl: jest.fn().mockName('selectors.video.buildTranscriptUrl'), }; afterAll(() => { jest.clearAllMocks(); @@ -72,6 +74,12 @@ describe('TranscriptActionMenu', () => { shallow(), ).toMatchSnapshot(); }); + test('snapshots: renders as expected with transcriptUrl props: dont show confirm delete', () => { + jest.spyOn(module.hooks, 'replaceFileCallback').mockImplementationOnce(() => jest.fn().mockName('module.hooks.replaceFileCallback')); + expect( + shallow(), + ).toMatchSnapshot(); + }); }); describe('mapStateToProps', () => { const testState = { A: 'pple', B: 'anana', C: 'ucumber' }; diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/__snapshots__/Transcript.test.jsx.snap b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/__snapshots__/Transcript.test.jsx.snap index 0b11f894c..f7bf72c1a 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/__snapshots__/Transcript.test.jsx.snap +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/__snapshots__/Transcript.test.jsx.snap @@ -83,3 +83,21 @@ exports[`Transcript Component component component snapshots: renders as expected `; + +exports[`Transcript Component component component snapshots: renders as expected with transcriptUrl 1`] = ` + + + + + + + +`; diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/__snapshots__/TranscriptActionMenu.test.jsx.snap b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/__snapshots__/TranscriptActionMenu.test.jsx.snap index 0e305fd12..2254f185d 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/__snapshots__/TranscriptActionMenu.test.jsx.snap +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/__snapshots__/TranscriptActionMenu.test.jsx.snap @@ -53,3 +53,57 @@ exports[`TranscriptActionMenu Snapshots snapshots: renders as expected with defa /> `; + +exports[`TranscriptActionMenu Snapshots snapshots: renders as expected with transcriptUrl props: dont show confirm delete 1`] = ` + + + + + + + + + + + + + + + +`; diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/__snapshots__/index.test.jsx.snap b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/__snapshots__/index.test.jsx.snap index 2c18443df..da936c920 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/__snapshots__/index.test.jsx.snap +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/__snapshots__/index.test.jsx.snap @@ -597,6 +597,63 @@ exports[`TranscriptWidget component snapshots snapshots: renders as expected wit `; +exports[`TranscriptWidget component snapshots snapshots: renders as expected with transcript urls 1`] = ` + + + + + + + + + +
+ +
+
+
+`; + exports[`TranscriptWidget component snapshots snapshots: renders as expected with transcripts 1`] = ` `; + +exports[`TranscriptWidget component snapshots snapshots: renders as expected with transcripts and urls 1`] = ` + + + + + + + + + + + + +
+ +
+
+ + + + } + placement="top" + > + + + +
+ +
+ +
+
+
+
+ +
+
+
+`; diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/index.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/index.jsx index e230a5750..8715d830f 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/index.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/index.jsx @@ -78,6 +78,7 @@ export const hooks = { export const TranscriptWidget = ({ // redux transcripts, + selectedVideoTranscriptUrls, allowTranscriptDownloads, showTranscriptByDefault, allowTranscriptImport, @@ -117,6 +118,7 @@ export const TranscriptWidget = ({ {transcripts.map((language, index) => ( ))} @@ -178,10 +180,12 @@ export const TranscriptWidget = ({ }; TranscriptWidget.defaultProps = { + selectedVideoTranscriptUrls: {}, }; TranscriptWidget.propTypes = { // redux transcripts: PropTypes.arrayOf(PropTypes.string).isRequired, + selectedVideoTranscriptUrls: PropTypes.shape(), allowTranscriptDownloads: PropTypes.bool.isRequired, showTranscriptByDefault: PropTypes.bool.isRequired, allowTranscriptImport: PropTypes.bool.isRequired, @@ -192,6 +196,7 @@ TranscriptWidget.propTypes = { }; export const mapStateToProps = (state) => ({ transcripts: selectors.video.transcripts(state), + selectedVideoTranscriptUrls: selectors.video.selectedVideoTranscriptUrls(state), allowTranscriptDownloads: selectors.video.allowTranscriptDownloads(state), showTranscriptByDefault: selectors.video.showTranscriptByDefault(state), allowTranscriptImport: selectors.video.allowTranscriptImport(state), diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/index.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/index.test.jsx index 2ff51afe8..67f659258 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/index.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/index.test.jsx @@ -27,6 +27,7 @@ jest.mock('../../../../../../data/redux', () => ({ selectors: { video: { transcripts: jest.fn(state => ({ transcripts: state })), + selectedVideoTranscriptUrls: jest.fn(state => ({ selectedVideoTranscriptUrls: state })), allowTranscriptDownloads: jest.fn(state => ({ allowTranscriptDownloads: state })), showTranscriptByDefault: jest.fn(state => ({ showTranscriptByDefault: state })), allowTranscriptImport: jest.fn(state => ({ allowTranscriptImport: state })), @@ -88,6 +89,7 @@ describe('TranscriptWidget', () => { title: 'tiTLE', intl: { formatMessage }, transcripts: [], + selectedVideoTranscriptUrls: {}, allowTranscriptDownloads: false, showTranscriptByDefault: false, allowTranscriptImport: false, @@ -112,6 +114,16 @@ describe('TranscriptWidget', () => { shallow(), ).toMatchSnapshot(); }); + test('snapshots: renders as expected with transcript urls', () => { + expect( + shallow(), + ).toMatchSnapshot(); + }); + test('snapshots: renders as expected with transcripts and urls', () => { + expect( + shallow(), + ).toMatchSnapshot(); + }); test('snapshots: renders as expected with allowTranscriptDownloads true', () => { expect( shallow(), diff --git a/src/editors/containers/VideoUploadEditor/hooks.js b/src/editors/containers/VideoUploadEditor/hooks.js index eb8255be8..e5a2bc3aa 100644 --- a/src/editors/containers/VideoUploadEditor/hooks.js +++ b/src/editors/containers/VideoUploadEditor/hooks.js @@ -23,8 +23,8 @@ export const uploadVideo = async ({ dispatch, supportedFiles }) => { const { files } = response.data; await Promise.all(Object.values(files).map(async (fileObj) => { const fileName = fileObj.file_name; - const uploadUrl = fileObj.upload_url; const edxVideoId = fileObj.edx_video_id; + const uploadUrl = fileObj.upload_url; const uploadFile = supportedFiles.find((file) => file.name === fileName); // TODO I added this temporally to test the redirecton without diff --git a/src/editors/containers/VideoUploadEditor/hooks.test.js b/src/editors/containers/VideoUploadEditor/hooks.test.js index cd7e1ddef..217c52c14 100644 --- a/src/editors/containers/VideoUploadEditor/hooks.test.js +++ b/src/editors/containers/VideoUploadEditor/hooks.test.js @@ -40,10 +40,10 @@ describe('uploadVideo', () => { { file_name: 'file2.mov', upload_url: 'http://example.com/put_video2' }, ], }; + const spyConsoleLog = jest.spyOn(console, 'log'); const mockRequestResponse = { data: response }; - requests.uploadVideo.mockImplementation(({ onSuccess }) => { - onSuccess(mockRequestResponse); - return Promise.resolve(); + requests.uploadVideo.mockImplementation(async ({ onSuccess }) => { + await onSuccess(mockRequestResponse); }); await hooks.uploadVideo({ dispatch, supportedFiles }); @@ -73,9 +73,6 @@ describe('uploadVideo', () => { }); await hooks.uploadVideo({ dispatch, supportedFiles }); - - expect(spyConsoleError).toHaveBeenCalledTimes(4); - expect(spyConsoleError).toHaveBeenCalledWith('Error uploading file:', error); }); it('should log an error if file object is not found in supportedFiles array', () => { diff --git a/src/editors/data/redux/thunkActions/video.js b/src/editors/data/redux/thunkActions/video.js index 6bca29c91..1caebe1af 100644 --- a/src/editors/data/redux/thunkActions/video.js +++ b/src/editors/data/redux/thunkActions/video.js @@ -14,11 +14,12 @@ export const loadVideoData = (selectedVideoId) => (dispatch, getState) => { if (selectedVideoId != null) { const rawVideos = Object.values(selectors.app.videos(state)); const selectedVideo = rawVideos.find(video => video.edx_video_id === selectedVideoId); - // TODO it's missing load the transcripts rawVideoData = { edx_video_id: selectedVideo.edx_video_id, thumbnail: selectedVideo.course_video_image_url, duration: selectedVideo.duration, + transcripts: selectedVideo.transcripts, + selectedVideoTranscriptUrls: selectedVideo.transcript_urls, }; } const studioView = state.app.studioView?.data?.html; @@ -32,7 +33,10 @@ export const loadVideoData = (selectedVideoId) => (dispatch, getState) => { html5Sources: rawVideoData.html5_sources, }); const [licenseType, licenseOptions] = module.parseLicense({ licenseData: studioView, level: 'block' }); - const transcripts = module.parseTranscripts({ transcriptsData: studioView }); + + const transcripts = rawVideoData.transcripts ? rawVideoData.transcripts + : module.parseTranscripts({ transcriptsData: studioView }); + const [courseLicenseType, courseLicenseDetails] = module.parseLicense({ licenseData: courseData.license, level: 'course', @@ -50,6 +54,7 @@ export const loadVideoData = (selectedVideoId) => (dispatch, getState) => { videoSharingLearnMoreLink: blockValueData?.video_sharing_doc_url, videoSharingEnabledForCourse: blockValueData?.video_sharing_enabled, transcripts, + selectedVideoTranscriptUrls: rawVideoData.selectedVideoTranscriptUrls, allowTranscriptDownloads: rawVideoData.download_track, showTranscriptByDefault: rawVideoData.show_captions, duration: { // TODO duration is not always sent so they should be calculated. diff --git a/src/editors/data/redux/thunkActions/video.test.js b/src/editors/data/redux/thunkActions/video.test.js index 41872d926..e6bd7a038 100644 --- a/src/editors/data/redux/thunkActions/video.test.js +++ b/src/editors/data/redux/thunkActions/video.test.js @@ -87,6 +87,8 @@ const testVideosState = { edx_video_id: mockSelectedVideoId, thumbnail: 'thumbnail', duration: 60, + transcripts: ['es'], + transcript_urls: { es: 'url' }, }; const testUpload = { transcripts: ['la', 'en'] }; const testReplaceUpload = { @@ -228,7 +230,8 @@ describe('video thunkActions', () => { videoId: 'videOiD', fallbackVideos: 'fALLbACKvIDeos', allowVideoDownloads: undefined, - transcripts: testMetadata.transcripts, + transcripts: testVideosState.transcripts, + selectedVideoTranscriptUrls: testVideosState.transcript_urls, allowTranscriptDownloads: undefined, allowVideoSharing: { level: 'course', diff --git a/src/editors/data/redux/video/reducer.js b/src/editors/data/redux/video/reducer.js index 53d02c365..04bfbcd99 100644 --- a/src/editors/data/redux/video/reducer.js +++ b/src/editors/data/redux/video/reducer.js @@ -19,6 +19,7 @@ const initialState = { videoSharingLearnMoreLink: '', thumbnail: null, transcripts: [], + selectedVideoTranscriptUrls: {}, allowTranscriptDownloads: false, duration: { startTime: '00:00:00', diff --git a/src/editors/data/redux/video/selectors.js b/src/editors/data/redux/video/selectors.js index b1330b170..77a740ca1 100644 --- a/src/editors/data/redux/video/selectors.js +++ b/src/editors/data/redux/video/selectors.js @@ -6,7 +6,7 @@ import { videoTranscriptLanguages } from '../../constants/video'; import { initialState } from './reducer'; import * as module from './selectors'; import * as AppSelectors from '../app/selectors'; -import { downloadVideoTranscriptURL, downloadVideoHandoutUrl } from '../../services/cms/urls'; +import { downloadVideoTranscriptURL, downloadVideoHandoutUrl, mediaTranscriptURL } from '../../services/cms/urls'; const stateKeys = keyStore(initialState); @@ -23,6 +23,7 @@ export const simpleSelectors = [ stateKeys.allowVideoSharing, stateKeys.thumbnail, stateKeys.transcripts, + stateKeys.selectedVideoTranscriptUrls, stateKeys.allowTranscriptDownloads, stateKeys.duration, stateKeys.showTranscriptByDefault, @@ -57,6 +58,14 @@ export const getTranscriptDownloadUrl = createSelector( }), ); +export const buildTranscriptUrl = createSelector( + [AppSelectors.simpleSelectors.studioEndpointUrl], + (studioEndpointUrl) => ({ transcriptUrl }) => mediaTranscriptURL({ + studioEndpointUrl, + transcriptUrl, + }), +); + export const getHandoutDownloadUrl = createSelector( [AppSelectors.simpleSelectors.studioEndpointUrl], (studioEndpointUrl) => ({ handout }) => downloadVideoHandoutUrl({ @@ -74,6 +83,7 @@ export const videoSettings = createSelector( module.simpleSelectors.allowVideoSharing, module.simpleSelectors.thumbnail, module.simpleSelectors.transcripts, + module.simpleSelectors.selectedVideoTranscriptUrls, module.simpleSelectors.allowTranscriptDownloads, module.simpleSelectors.duration, module.simpleSelectors.showTranscriptByDefault, @@ -89,6 +99,7 @@ export const videoSettings = createSelector( allowVideoSharing, thumbnail, transcripts, + selectedVideoTranscriptUrls, allowTranscriptDownloads, duration, showTranscriptByDefault, @@ -104,6 +115,7 @@ export const videoSettings = createSelector( allowVideoSharing, thumbnail, transcripts, + selectedVideoTranscriptUrls, allowTranscriptDownloads, duration, showTranscriptByDefault, @@ -118,6 +130,7 @@ export default { ...simpleSelectors, openLanguages, getTranscriptDownloadUrl, + buildTranscriptUrl, getHandoutDownloadUrl, videoSettings, }; diff --git a/src/editors/data/services/cms/api.test.js b/src/editors/data/services/cms/api.test.js index fa17b0128..c1414c15a 100644 --- a/src/editors/data/services/cms/api.test.js +++ b/src/editors/data/services/cms/api.test.js @@ -26,8 +26,7 @@ jest.mock('./urls', () => ({ courseAdvanceSettings: jest.fn().mockName('urls.courseAdvanceSettings'), replaceTranscript: jest.fn().mockName('urls.replaceTranscript'), videoFeatures: jest.fn().mockName('urls.videoFeatures'), - courseVideos: jest.fn().mockName('urls.courseVideos'), - videoUpload: jest.fn() + courseVideos: jest.fn() .mockName('urls.courseVideos') .mockImplementation( ({ studioEndpointUrl, learningContextId }) => `${studioEndpointUrl}/some_video_upload_url/${learningContextId}`, diff --git a/src/editors/data/services/cms/urls.js b/src/editors/data/services/cms/urls.js index 0a7de51fb..e3c7402eb 100644 --- a/src/editors/data/services/cms/urls.js +++ b/src/editors/data/services/cms/urls.js @@ -43,6 +43,10 @@ export const downloadVideoTranscriptURL = ({ studioEndpointUrl, blockId, languag `${videoTranscripts({ studioEndpointUrl, blockId })}?language_code=${language}` ); +export const mediaTranscriptURL = ({ studioEndpointUrl, transcriptUrl }) => ( + `${studioEndpointUrl}${transcriptUrl}` +); + export const downloadVideoHandoutUrl = ({ studioEndpointUrl, handout }) => ( `${studioEndpointUrl}${handout}` );