From 363df38b1f30ac7f3d5e50c2821fd4f7a3d8c0f1 Mon Sep 17 00:00:00 2001 From: Maxim Beder Date: Mon, 20 Mar 2023 14:04:38 +0100 Subject: [PATCH 1/2] feat: offer to import youtube transcripts dynamically Before this commit, the popup message which offers to import transcripts from YouTube would only appear after saving the video settings, and then reopenning the editor. With this commit, the popup appears dynamically, i.e. whenever a URL is changed to a YouTube one, certain validations and check take place, after which, if possible to do so, user will be offered to import transcripts from the YouTube video. --- .../components/VideoSourceWidget/hooks.jsx | 22 ++++++++++++++++++- .../components/VideoSourceWidget/index.jsx | 2 +- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/hooks.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/hooks.jsx index 90a8fce8c..27d7cbfab 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/hooks.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/hooks.jsx @@ -1,7 +1,27 @@ import { actions } from '../../../../../../data/redux'; +import { parseYoutubeId } from '../../../../../../data/services/cms/api'; +import * as requests from '../../../../../../data/redux/thunkActions/requests'; export const sourceHooks = ({ dispatch }) => ({ - updateVideoURL: (e) => dispatch(actions.video.updateField({ videoSource: e.target.value })), + updateVideoURL: (e, videoId) => { + const videoUrl = e.target.value; + dispatch(actions.video.updateField({ videoSource: videoUrl })); + + const youTubeId = parseYoutubeId(videoUrl); + if (youTubeId) { + dispatch(requests.checkTranscriptsForImport({ + videoId, + youTubeId, + onSuccess: (response) => { + if (response.data.command === 'import') { + dispatch(actions.video.updateField({ + allowTranscriptImport: true, + })); + } + }, + })); + } + }, updateVideoId: (e) => dispatch(actions.video.updateField({ videoId: e.target.value })), }); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.jsx index 6a88d459f..2337c0adc 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.jsx @@ -77,7 +77,7 @@ export const VideoSourceWidget = ({ updateVideoURL(e, videoId.local)} value={source.local} /> From 6c8fca811331f24d2d54c44b9d6958433d3ed760 Mon Sep 17 00:00:00 2001 From: Maxim Beder Date: Mon, 20 Mar 2023 15:26:45 +0100 Subject: [PATCH 2/2] test: add tests for VideoSettingsModal --- .../VideoSourceWidget/hooks.test.jsx | 42 +++++++++++++++++++ .../VideoSourceWidget/index.test.jsx | 10 ++--- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/hooks.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/hooks.test.jsx index 34181e523..9315a1caa 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/hooks.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/hooks.test.jsx @@ -1,5 +1,6 @@ import { dispatch } from 'react-redux'; import { actions } from '../../../../../../data/redux'; +import * as requests from '../../../../../../data/redux/thunkActions/requests'; import * as hooks from './hooks'; jest.mock('react-redux', () => { @@ -20,6 +21,13 @@ jest.mock('../../../../../../data/redux', () => ({ }, })); +jest.mock('../../../../../../data/redux/thunkActions/requests', () => ({ + checkTranscriptsForImport: jest.fn(), +})); + +const youtubeId = 'yOuTuBEiD'; +const youtubeUrl = `https://youtu.be/${youtubeId}`; + describe('VideoEditorHandout hooks', () => { let hook; @@ -28,6 +36,9 @@ describe('VideoEditorHandout hooks', () => { beforeEach(() => { hook = hooks.sourceHooks({ dispatch }); }); + afterEach(() => { + jest.clearAllMocks(); + }); describe('updateVideoURL', () => { it('dispatches updateField action with new videoSource', () => { hook.updateVideoURL(e); @@ -37,6 +48,37 @@ describe('VideoEditorHandout hooks', () => { }), ); }); + it('dispatches checkTranscriptsForImport request with new YouTube videoSource', () => { + e.target.value = youtubeUrl; + hook.updateVideoURL(e, 'video-id'); + expect(requests.checkTranscriptsForImport).toHaveBeenCalledWith({ + videoId: 'video-id', + youTubeId: youtubeId, + onSuccess: expect.anything(), + }); + }); + it('dispatches updateField video action when checkTranscriptsForImport onSuccess command is import', () => { + e.target.value = youtubeUrl; + hook.updateVideoURL(e, 'video-id'); + + const { onSuccess } = requests.checkTranscriptsForImport.mock.calls[0][0]; + onSuccess({ data: { command: 'import' } }); + + expect(actions.video.updateField).toHaveBeenCalledWith({ + allowTranscriptImport: true, + }); + }); + it('does not dispatch updateField video action when checkTranscriptsForImport onSuccess command is not import', () => { + e.target.value = youtubeUrl; + hook.updateVideoURL(e, 'video-id'); + + const { onSuccess } = requests.checkTranscriptsForImport.mock.calls[0][0]; + onSuccess({ data: { command: 'anything else' } }); + + expect(actions.video.updateField).not.toHaveBeenCalledWith({ + allowTranscriptImport: true, + }); + }); }); describe('updateVideoId', () => { it('dispatches updateField action with new videoId', () => { diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.test.jsx index ac38ce3d9..427cdcf07 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoSourceWidget/index.test.jsx @@ -34,7 +34,7 @@ jest.mock('../hooks', () => ({ jest.mock('./hooks', () => ({ sourceHooks: jest.fn().mockReturnValue({ updateVideoId: (args) => ({ updateVideoId: args }), - updateVideoURL: (args) => ({ updateVideoURL: args }), + updateVideoURL: jest.fn().mockName('updateVideoURL'), }), fallbackHooks: jest.fn().mockReturnValue({ addFallbackVideo: jest.fn().mockName('addFallbackVideo'), @@ -92,11 +92,11 @@ describe('VideoSourceWidget', () => { .props().onBlur).toEqual(expected); }); test('updateVideoURL is tied to url field onBlur', () => { - const expected = hook.updateVideoURL; - expect(el + const { onBlur } = el // eslint-disable-next-line - .children().at(0).children().at(0).children().at(2) - .props().onBlur).toEqual(expected); + .children().at(0).children().at(0).children().at(2).props(); + onBlur('onBlur event'); + expect(hook.updateVideoURL).toHaveBeenCalledWith('onBlur event', ''); }); }); });