From b7fbac9161699f3bef4564c7a10a224e3f9351d1 Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Fri, 17 Jan 2025 18:53:50 -0500 Subject: [PATCH 01/10] feat: Enable transcripts for video library --- .../TranscriptWidget/LanguageSelector.jsx | 3 ++ .../components/TranscriptWidget/index.jsx | 11 +++- .../components/VideoPreviewWidget/index.jsx | 8 +-- .../components/VideoSettingsModal/index.tsx | 4 +- src/editors/data/constants/requests.ts | 1 + .../data/redux/thunkActions/requests.js | 52 ++++++++++++++----- src/editors/data/redux/thunkActions/video.js | 11 ++++ src/editors/data/redux/video/reducer.js | 1 + src/editors/data/redux/video/selectors.js | 1 + src/editors/data/services/cms/api.ts | 24 +++++++++ src/editors/data/services/cms/urls.ts | 8 +++ 11 files changed, 100 insertions(+), 24 deletions(-) diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.jsx index 90d3ec235b..e418e14fa2 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.jsx @@ -54,6 +54,7 @@ const LanguageSelector = ({ language, // Redux openLanguages, // Only allow those languages not already associated with a transcript to be selected + transcriptHandlerUrl, // intl intl, @@ -122,6 +123,7 @@ LanguageSelector.defaultProps = { LanguageSelector.propTypes = { openLanguages: PropTypes.arrayOf(PropTypes.string), + transcriptHandlerUrl: PropTypes.string.isRequired, index: PropTypes.number.isRequired, language: PropTypes.string.isRequired, intl: intlShape.isRequired, @@ -129,6 +131,7 @@ LanguageSelector.propTypes = { export const mapStateToProps = (state) => ({ openLanguages: selectors.video.openLanguages(state), + transcriptHandlerUrl: selectors.video.transcriptHandlerUrl(state), }); export const mapDispatchToProps = {}; 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 b734b31ff4..18be27f6a3 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/index.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/index.jsx @@ -1,5 +1,5 @@ import React from 'react'; -import { connect } from 'react-redux'; +import { connect, useDispatch } from 'react-redux'; import PropTypes from 'prop-types'; import { FormattedMessage, @@ -17,7 +17,7 @@ import { } from '@openedx/paragon'; import { Add, InfoOutline } from '@openedx/paragon/icons'; -import { actions, selectors } from '../../../../../../data/redux'; +import { thunkActions, actions, selectors } from '../../../../../../data/redux'; import messages from './messages'; import { RequestKeys } from '../../../../../../data/constants/requests'; @@ -90,6 +90,7 @@ const TranscriptWidget = ({ updateField, isUploadError, isDeleteError, + isLibrary, // injected intl, }) => { @@ -97,6 +98,10 @@ const TranscriptWidget = ({ const [showImportCard, setShowImportCard] = React.useState(true); const fullTextLanguages = module.hooks.transcriptLanguages(transcripts, intl); const hasTranscripts = module.hooks.hasTranscripts(transcripts); + const dispatch = useDispatch(); + if (isLibrary) { + dispatch(thunkActions.video.updateTranscriptHandlerUrl()); + } return ( ({ @@ -207,6 +213,7 @@ export const mapStateToProps = (state) => ({ allowTranscriptImport: selectors.video.allowTranscriptImport(state), isUploadError: selectors.requests.isFailed(state, { requestKey: RequestKeys.uploadTranscript }), isDeleteError: selectors.requests.isFailed(state, { requestKey: RequestKeys.deleteTranscript }), + isLibrary: selectors.app.isLibrary(state), }); export const mapDispatchToProps = (dispatch) => ({ diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoPreviewWidget/index.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoPreviewWidget/index.jsx index 3377e31f45..912b541dc1 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoPreviewWidget/index.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoPreviewWidget/index.jsx @@ -17,7 +17,6 @@ export const VideoPreviewWidget = ({ videoSource, transcripts, blockTitle, - isLibrary, intl, }) => { const imgRef = React.useRef(); @@ -47,10 +46,7 @@ export const VideoPreviewWidget = ({ />

{blockTitle}

- {!isLibrary && ( - // Since content libraries v2 don't support static assets yet, we can't include transcripts. - - )} + {videoType && ( ({ @@ -82,7 +77,6 @@ export const mapStateToProps = (state) => ({ videoSource: selectors.video.videoSource(state), thumbnail: selectors.video.thumbnail(state), blockTitle: selectors.app.blockTitle(state), - isLibrary: selectors.app.isLibrary(state), }); export default injectIntl(connect(mapStateToProps)(VideoPreviewWidget)); diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/index.tsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/index.tsx index 4761e39320..7edbcb8be2 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/index.tsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/index.tsx @@ -49,9 +49,7 @@ const VideoSettingsModal: React.FC = ({ )} - {!isLibrary && ( // Since content libraries v2 don't support static assets yet, we can't include transcripts. - - )} + diff --git a/src/editors/data/constants/requests.ts b/src/editors/data/constants/requests.ts index e12905d588..561965af86 100644 --- a/src/editors/data/constants/requests.ts +++ b/src/editors/data/constants/requests.ts @@ -27,4 +27,5 @@ export const RequestKeys = StrictDict({ uploadAsset: 'uploadAsset', fetchAdvancedSettings: 'fetchAdvancedSettings', fetchVideoFeatures: 'fetchVideoFeatures', + getHandlerUrl: 'getHandlerUrl', } as const); diff --git a/src/editors/data/redux/thunkActions/requests.js b/src/editors/data/redux/thunkActions/requests.js index edff3bf875..05cbad8396 100644 --- a/src/editors/data/redux/thunkActions/requests.js +++ b/src/editors/data/redux/thunkActions/requests.js @@ -4,6 +4,7 @@ import { RequestKeys } from '../../constants/requests'; import api, { loadImages } from '../../services/cms/api'; import { actions as requestsActions } from '../requests'; import { selectors as appSelectors } from '../app'; +import { selectors as videoSelectors } from '../video'; // This 'module' self-import hack enables mocking during tests. // See src/editors/decisions/0005-internal-editor-testability-decisions.md. The whole approach to how hooks are tested @@ -15,7 +16,7 @@ import { acceptedImgKeys } from '../../../sharedComponents/ImageUploadModal/Sele // Similar to `import { actions, selectors } from '..';` but avoid circular imports: const actions = { requests: requestsActions }; -const selectors = { app: appSelectors }; +const selectors = { app: appSelectors, video: videoSelectors }; /** * Wrapper around a network request promise, that sends actions to the redux store to @@ -257,17 +258,31 @@ export const uploadTranscript = ({ language, ...rest }) => (dispatch, getState) => { - dispatch(module.networkRequest({ - requestKey: RequestKeys.uploadTranscript, - promise: api.uploadTranscript({ - blockId: selectors.app.blockId(getState()), - transcript, - videoId, - language, - studioEndpointUrl: selectors.app.studioEndpointUrl(getState()), - }), - ...rest, - })); + const isLibrary = selectors.app.isLibrary(getState()); + if (isLibrary) { + dispatch(module.networkRequest({ + requestKey: RequestKeys.uploadTranscript, + promise: api.uploadTranscriptV2({ + handlerUrl: selectors.video.transcriptHandlerUrl(getState()), + transcript, + videoId, + language, + }), + ...rest, + })); + } else { + dispatch(module.networkRequest({ + requestKey: RequestKeys.uploadTranscript, + promise: api.uploadTranscript({ + blockId: selectors.app.blockId(getState()), + transcript, + videoId, + language, + studioEndpointUrl: selectors.app.studioEndpointUrl(getState()), + }), + ...rest, + })); + } }; export const updateTranscriptLanguage = ({ @@ -304,6 +319,18 @@ export const getTranscriptFile = ({ language, videoId, ...rest }) => (dispatch, })); }; +export const getHandlerlUrl = ({ handlerName, ...rest }) => (dispatch, getState) => { + dispatch(module.networkRequest({ + requestKey: RequestKeys.getHandlerlUrl, + promise: api.getHandlerUrl({ + studioEndpointUrl: selectors.app.studioEndpointUrl(getState()), + blockId: selectors.app.blockId(getState()), + handlerName, + }), + ...rest, + })); +}; + export const fetchCourseDetails = ({ ...rest }) => (dispatch, getState) => { dispatch(module.networkRequest({ requestKey: RequestKeys.fetchCourseDetails, @@ -368,4 +395,5 @@ export default StrictDict({ fetchAdvancedSettings, fetchVideoFeatures, uploadVideo, + getHandlerlUrl, }); diff --git a/src/editors/data/redux/thunkActions/video.js b/src/editors/data/redux/thunkActions/video.js index 352d989737..04c8b79b8b 100644 --- a/src/editors/data/redux/thunkActions/video.js +++ b/src/editors/data/redux/thunkActions/video.js @@ -383,6 +383,16 @@ export const updateTranscriptLanguage = ({ newLanguageCode, languageBeforeChange })); }; +export const updateTranscriptHandlerUrl = () => (dispatch, getState) => { + dispatch(requests.getHandlerlUrl({ + handlerName: 'studio_transcript', + onSuccess: (response) => { + const transcriptHandlerUrl = response.data.handler_url; + dispatch(actions.video.updateField({ transcriptHandlerUrl })); + }, + })); +}; + export const replaceTranscript = ({ newFile, newFilename, language }) => (dispatch, getState) => { const state = getState(); const { videoId } = state.video; @@ -456,4 +466,5 @@ export default { replaceTranscript, uploadHandout, uploadVideo, + updateTranscriptHandlerUrl, }; diff --git a/src/editors/data/redux/video/reducer.js b/src/editors/data/redux/video/reducer.js index 04bfbcd99c..8770c306fe 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: [], + transcriptHandlerUrl: '', selectedVideoTranscriptUrls: {}, allowTranscriptDownloads: false, duration: { diff --git a/src/editors/data/redux/video/selectors.js b/src/editors/data/redux/video/selectors.js index f71c014cf7..f8c21f0a39 100644 --- a/src/editors/data/redux/video/selectors.js +++ b/src/editors/data/redux/video/selectors.js @@ -27,6 +27,7 @@ export const simpleSelectors = [ stateKeys.allowVideoSharing, stateKeys.thumbnail, stateKeys.transcripts, + stateKeys.transcriptHandlerUrl, stateKeys.selectedVideoTranscriptUrls, stateKeys.allowTranscriptDownloads, stateKeys.duration, diff --git a/src/editors/data/services/cms/api.ts b/src/editors/data/services/cms/api.ts index e979eb4455..926b541fbb 100644 --- a/src/editors/data/services/cms/api.ts +++ b/src/editors/data/services/cms/api.ts @@ -246,6 +246,23 @@ export const apiMethods = { data, ); }, + uploadTranscriptV2: ({ + handlerUrl, + transcript, + videoId, + language, + newLanguage = null, + }) => { + const data = new FormData(); + data.append('file', transcript); + data.append('edx_video_id', videoId); + data.append('language_code', language); + data.append('new_language_code', newLanguage || language); + return post( + urls.uploadTrascriptXblockV2({ handlerUrl }), + data, + ); + }, normalizeContent: ({ blockId, blockType, @@ -345,6 +362,13 @@ export const apiMethods = { urls.courseVideos({ studioEndpointUrl, learningContextId }), data, ), + getHandlerUrl: ({ + studioEndpointUrl, + blockId, + handlerName, + }) => get( + urls.handlerUrl({ studioEndpointUrl, blockId, handlerName }), + ), }; export default apiMethods; diff --git a/src/editors/data/services/cms/urls.ts b/src/editors/data/services/cms/urls.ts index a137ffb9f8..fdd35e31da 100644 --- a/src/editors/data/services/cms/urls.ts +++ b/src/editors/data/services/cms/urls.ts @@ -111,3 +111,11 @@ export const videoFeatures = (({ studioEndpointUrl }) => ( export const courseVideos = (({ studioEndpointUrl, learningContextId }) => ( `${studioEndpointUrl}/videos/${learningContextId}` )) satisfies UrlFunction; + +export const handlerUrl = (({studioEndpointUrl, blockId, handlerName}) => ( + `${studioEndpointUrl}/api/xblock/v2/xblocks/${blockId}/handler_url/${handlerName}/` +)) satisfies UrlFunction; + +export const uploadTrascriptXblockV2 = (({ handlerUrl }) => ( + `${handlerUrl}translation` +)) satisfies UrlFunction; From 627b4af8eafdca47ab96b3bfc6af73f0913cfa21 Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Mon, 20 Jan 2025 16:43:45 -0500 Subject: [PATCH 02/10] test: Enable transcript in libraries --- .../TranscriptWidget/LanguageSelector.jsx | 3 - .../__snapshots__/index.test.jsx.snap | 2 + .../TranscriptWidget/index.test.jsx | 9 +++ .../VideoPreviewWidget/index.test.jsx | 4 +- .../data/redux/thunkActions/requests.js | 2 +- .../data/redux/thunkActions/requests.test.js | 56 ++++++++++++++++++- src/editors/data/redux/thunkActions/video.js | 2 +- src/editors/data/services/cms/api.test.ts | 31 ++++++++++ src/editors/data/services/cms/api.ts | 2 +- src/editors/data/services/cms/urls.test.ts | 17 ++++++ src/editors/data/services/cms/urls.ts | 6 +- 11 files changed, 122 insertions(+), 12 deletions(-) diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.jsx index e418e14fa2..90d3ec235b 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/LanguageSelector.jsx @@ -54,7 +54,6 @@ const LanguageSelector = ({ language, // Redux openLanguages, // Only allow those languages not already associated with a transcript to be selected - transcriptHandlerUrl, // intl intl, @@ -123,7 +122,6 @@ LanguageSelector.defaultProps = { LanguageSelector.propTypes = { openLanguages: PropTypes.arrayOf(PropTypes.string), - transcriptHandlerUrl: PropTypes.string.isRequired, index: PropTypes.number.isRequired, language: PropTypes.string.isRequired, intl: intlShape.isRequired, @@ -131,7 +129,6 @@ LanguageSelector.propTypes = { export const mapStateToProps = (state) => ({ openLanguages: selectors.video.openLanguages(state), - transcriptHandlerUrl: selectors.video.transcriptHandlerUrl(state), }); export const mapDispatchToProps = {}; 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 904d6433b2..4bfc289700 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 @@ -242,6 +242,8 @@ exports[`TranscriptWidget component snapshots snapshot: renders ErrorAlert with
`; +exports[`TranscriptWidget component snapshots snapshot: renders when isLibrary is true 1`] = `undefined`; + exports[`TranscriptWidget component snapshots snapshots: renders as expected with allowTranscriptDownloads true 1`] = ` ({ }, selectors: { + app: { + isLibrary: jest.fn(state => ({ isLibrary: state })), + }, video: { transcripts: jest.fn(state => ({ transcripts: state })), selectedVideoTranscriptUrls: jest.fn(state => ({ selectedVideoTranscriptUrls: state })), @@ -103,6 +106,7 @@ describe('TranscriptWidget', () => { updateField: jest.fn().mockName('args.updateField'), isUploadError: false, isDeleteError: false, + isLibrary: false, }; describe('snapshots', () => { @@ -151,6 +155,11 @@ describe('TranscriptWidget', () => { shallow().snapshot, ).toMatchSnapshot(); }); + test('snapshot: renders when isLibrary is true', () => { + expect( + shallow().snapshot, + ).toMatchSnapshot(); + }); }); describe('mapStateToProps', () => { const testState = { A: 'pple', B: 'anana', C: 'ucumber' }; diff --git a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoPreviewWidget/index.test.jsx b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoPreviewWidget/index.test.jsx index fc7f89a5cd..131c9b53ba 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoPreviewWidget/index.test.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/VideoPreviewWidget/index.test.jsx @@ -30,7 +30,7 @@ describe('VideoPreviewWidget', () => { expect(screen.queryByText('No transcripts added')).toBeInTheDocument(); }); - test('hides transcripts section in preview for libraries', () => { + test('renders transcripts section in preview for libraries', () => { render( { thumbnail="" />, ); - expect(screen.queryByText('No transcripts added')).not.toBeInTheDocument(); + expect(screen.queryByText('No transcripts added')).toBeInTheDocument(); }); }); }); diff --git a/src/editors/data/redux/thunkActions/requests.js b/src/editors/data/redux/thunkActions/requests.js index 05cbad8396..8230433c7d 100644 --- a/src/editors/data/redux/thunkActions/requests.js +++ b/src/editors/data/redux/thunkActions/requests.js @@ -321,7 +321,7 @@ export const getTranscriptFile = ({ language, videoId, ...rest }) => (dispatch, export const getHandlerlUrl = ({ handlerName, ...rest }) => (dispatch, getState) => { dispatch(module.networkRequest({ - requestKey: RequestKeys.getHandlerlUrl, + requestKey: RequestKeys.getHandlerUrl, promise: api.getHandlerUrl({ studioEndpointUrl: selectors.app.studioEndpointUrl(getState()), blockId: selectors.app.blockId(getState()), diff --git a/src/editors/data/redux/thunkActions/requests.test.js b/src/editors/data/redux/thunkActions/requests.test.js index ece5411780..8b5eec9fb7 100644 --- a/src/editors/data/redux/thunkActions/requests.test.js +++ b/src/editors/data/redux/thunkActions/requests.test.js @@ -6,6 +6,7 @@ import { actions, selectors } from '../index'; const testState = { some: 'data', + isLibrary: false, }; jest.mock('../app/selectors', () => ({ @@ -18,6 +19,11 @@ jest.mock('../app/selectors', () => ({ blockType: (state) => ({ blockType: state }), learningContextId: (state) => ({ learningContextId: state }), blockTitle: (state) => ({ title: state }), + isLibrary: (state) => (state.isLibrary), +})); + +jest.mock('../video/selectors', () => ({ + transcriptHandlerUrl: () => ('transcriptHandlerUrl'), })); jest.mock('../../services/cms/api', () => ({ @@ -35,6 +41,8 @@ jest.mock('../../services/cms/api', () => ({ uploadTranscript: (args) => args, deleteTranscript: (args) => args, getTranscript: (args) => args, + getHandlerUrl: (args) => args, + uploadTranscriptV2: (args) => args, checkTranscriptsForImport: (args) => args, importTranscript: (args) => args, fetchVideoFeatures: (args) => args, @@ -158,10 +166,11 @@ describe('requests thunkActions module', () => { args, expectedData, expectedString, + state, }) => { let dispatchedAction; beforeEach(() => { - action({ ...args, onSuccess, onFailure })(dispatch, () => testState); + action({ ...args, onSuccess, onFailure })(dispatch, () => state || testState); [[dispatchedAction]] = dispatch.mock.calls; }); it('dispatches networkRequest', () => { @@ -500,6 +509,23 @@ describe('requests thunkActions module', () => { }, }); }); + describe('getHandlerUrl', () => { + const handlerName = 'transcript'; + testNetworkRequestAction({ + action: requests.getHandlerlUrl, + args: { handlerName, ...fetchParams }, + expectedString: 'with getHandlerUrl promise', + expectedData: { + ...fetchParams, + requestKey: RequestKeys.getHandlerUrl, + promise: api.getHandlerUrl({ + studioEndpointUrl: selectors.app.studioEndpointUrl(testState), + blockId: selectors.app.blockId(testState), + handlerName, + }), + }, + }); + }); describe('updateTranscriptLanguage', () => { const languageBeforeChange = 'SoME laNGUage CoNtent As String'; const newLanguageCode = 'SoME NEW laNGUage CoNtent As String'; @@ -553,6 +579,34 @@ describe('requests thunkActions module', () => { }, }); }); + describe('uploadTranscript V2', () => { + const language = 'SoME laNGUage CoNtent As String'; + const videoId = 'SoME VidEOid CoNtent As String'; + const transcript = 'SoME tRANscRIPt CoNtent As String'; + testNetworkRequestAction({ + action: requests.uploadTranscript, + args: { + transcript, + language, + videoId, + ...fetchParams, + }, + expectedString: 'with uploadTranscript promise', + expectedData: { + ...fetchParams, + requestKey: RequestKeys.uploadTranscript, + promise: api.uploadTranscriptV2({ + handlerUrl: 'transcriptHandlerUrl', + transcript, + videoId, + language, + }), + }, + state: { + isLibrary: true, + }, + }); + }); describe('fetchVideoFeatures', () => { testNetworkRequestAction({ action: requests.fetchVideoFeatures, diff --git a/src/editors/data/redux/thunkActions/video.js b/src/editors/data/redux/thunkActions/video.js index 04c8b79b8b..0ea4d84cea 100644 --- a/src/editors/data/redux/thunkActions/video.js +++ b/src/editors/data/redux/thunkActions/video.js @@ -383,7 +383,7 @@ export const updateTranscriptLanguage = ({ newLanguageCode, languageBeforeChange })); }; -export const updateTranscriptHandlerUrl = () => (dispatch, getState) => { +export const updateTranscriptHandlerUrl = () => (dispatch) => { dispatch(requests.getHandlerlUrl({ handlerName: 'studio_transcript', onSuccess: (response) => { diff --git a/src/editors/data/services/cms/api.test.ts b/src/editors/data/services/cms/api.test.ts index c3df32ccbf..0eb8ef4771 100644 --- a/src/editors/data/services/cms/api.test.ts +++ b/src/editors/data/services/cms/api.test.ts @@ -23,6 +23,8 @@ jest.mock('./urls', () => ({ .mockImplementation( ({ studioEndpointUrl, learningContextId }) => `${studioEndpointUrl}/some_video_upload_url/${learningContextId}`, ), + handlerUrl: jest.fn().mockReturnValue('urls.handlerUrl'), + uploadTrascriptXblockV2: jest.fn().mockReturnValue('url.uploadTranscriptV2'), })); jest.mock('./utils', () => ({ @@ -152,6 +154,14 @@ describe('cms api', () => { }); }); + describe('getHandlerUrl', () => { + it('should call get with url.handlerUrl', () => { + const handlerName = 'transcript'; + apiMethods.getHandlerUrl({ studioEndpointUrl, blockId, handlerName }); + expect(get).toHaveBeenCalledWith(urls.handlerUrl({ studioEndpointUrl, blockId, handlerName })); + }); + }); + describe('normalizeContent', () => { test('return value for blockType: html', () => { const content = 'Im baby palo santo ugh celiac fashion axe. La croix lo-fi venmo whatever. Beard man braid migas single-origin coffee forage ramps.'; @@ -410,6 +420,27 @@ describe('cms api', () => { ); }); }); + describe('uploadTranscriptV2', () => { + const transcript = new Blob(['dAta']); + it('should call post with urls.uploadTranscriptV2 and transcript data', () => { + const mockFormdata = new FormData(); + const transcriptHandlerUrl = 'handlerUrl'; + mockFormdata.append('file', transcript); + mockFormdata.append('edx_video_id', videoId); + mockFormdata.append('language_code', language); + mockFormdata.append('new_language_code', language); + apiMethods.uploadTranscriptV2({ + handlerUrl: transcriptHandlerUrl, + transcript, + videoId, + language, + }); + expect(post).toHaveBeenCalledWith( + urls.uploadTrascriptXblockV2({ transcriptHandlerUrl }), + mockFormdata, + ); + }); + }); describe('transcript delete', () => { it('should call deleteObject with urls.videoTranscripts and transcript data', () => { const mockDeleteJSON = { data: { lang: language, edx_video_id: videoId } }; diff --git a/src/editors/data/services/cms/api.ts b/src/editors/data/services/cms/api.ts index 926b541fbb..a1ca8e2334 100644 --- a/src/editors/data/services/cms/api.ts +++ b/src/editors/data/services/cms/api.ts @@ -259,7 +259,7 @@ export const apiMethods = { data.append('language_code', language); data.append('new_language_code', newLanguage || language); return post( - urls.uploadTrascriptXblockV2({ handlerUrl }), + urls.uploadTrascriptXblockV2({ transcriptHandlerUrl: handlerUrl }), data, ); }, diff --git a/src/editors/data/services/cms/urls.test.ts b/src/editors/data/services/cms/urls.test.ts index 95a3968959..82696069c5 100644 --- a/src/editors/data/services/cms/urls.test.ts +++ b/src/editors/data/services/cms/urls.test.ts @@ -17,6 +17,8 @@ import { mediaTranscriptURL, videoFeatures, courseVideos, + handlerUrl, + uploadTrascriptXblockV2, } from './urls'; describe('cms url methods', () => { @@ -189,4 +191,19 @@ describe('cms url methods', () => { .toEqual(`${studioEndpointUrl}${transcriptUrl}`); }); }); + describe('handlerUrl', () => { + it('returns url with studioEndpointUrl, blockId and handlerName', () => { + const handlerName = 'transcript'; + expect(handlerUrl({ studioEndpointUrl, blockId, handlerName })) + .toEqual(`${studioEndpointUrl}/api/xblock/v2/xblocks/${blockId}/handler_url/transcript/`); + }); + }); + describe('uploadTrascriptXblockV2', () => { + it('returns url with transcriptHandlerUrl', () => { + const handlerName = 'transcript'; + const transcriptHandlerUrl = handlerUrl({ studioEndpointUrl, blockId, handlerName }); + expect(uploadTrascriptXblockV2({ transcriptHandlerUrl })) + .toEqual(`${transcriptHandlerUrl}translation`); + }); + }); }); diff --git a/src/editors/data/services/cms/urls.ts b/src/editors/data/services/cms/urls.ts index fdd35e31da..cca1b8bffe 100644 --- a/src/editors/data/services/cms/urls.ts +++ b/src/editors/data/services/cms/urls.ts @@ -112,10 +112,10 @@ export const courseVideos = (({ studioEndpointUrl, learningContextId }) => ( `${studioEndpointUrl}/videos/${learningContextId}` )) satisfies UrlFunction; -export const handlerUrl = (({studioEndpointUrl, blockId, handlerName}) => ( +export const handlerUrl = (({ studioEndpointUrl, blockId, handlerName }) => ( `${studioEndpointUrl}/api/xblock/v2/xblocks/${blockId}/handler_url/${handlerName}/` )) satisfies UrlFunction; -export const uploadTrascriptXblockV2 = (({ handlerUrl }) => ( - `${handlerUrl}translation` +export const uploadTrascriptXblockV2 = (({ transcriptHandlerUrl }) => ( + `${transcriptHandlerUrl}translation` )) satisfies UrlFunction; From a82e79624460493ccc06fc8282afb43a143340e2 Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Tue, 21 Jan 2025 15:43:33 -0500 Subject: [PATCH 03/10] test: Adding tests for updateTranscriptHandlerUrl --- .../data/redux/thunkActions/video.test.js | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/editors/data/redux/thunkActions/video.test.js b/src/editors/data/redux/thunkActions/video.test.js index 75a668d0bc..4c501cc447 100644 --- a/src/editors/data/redux/thunkActions/video.test.js +++ b/src/editors/data/redux/thunkActions/video.test.js @@ -33,6 +33,7 @@ jest.mock('./requests', () => ({ importTranscript: (args) => ({ importTranscript: args }), fetchVideoFeatures: (args) => ({ fetchVideoFeatures: args }), uploadVideo: (args) => ({ uploadVideo: args }), + getHandlerlUrl: (args) => ({ getHandlerlUrl: args }), })); jest.mock('../../../utils', () => ({ @@ -61,6 +62,12 @@ const mockVideoFeatures = { }; const mockSelectedVideoId = 'ThisIsAVideoId'; const mockSelectedVideoUrl = 'ThisIsAYoutubeUrl'; +const mockUpdateTranscriptHandlerUrl = 'ThisIsAHandler'; +const mockUpdateTranscriptHandlerUrlData = { + data: { + handler_url: mockUpdateTranscriptHandlerUrl, + }, +}; const testMetadata = { download_track: 'dOWNlOAdTraCK', @@ -669,6 +676,22 @@ describe('video thunkActions', () => { expect(dispatch).toHaveBeenCalledWith(actions.video.updateField({ transcripts: [] })); }); }); + describe('updateTranscriptHandlerUrl', () => { + beforeEach(() => { + thunkActions.updateTranscriptHandlerUrl()(dispatch); + [[dispatchedAction]] = dispatch.mock.calls; + }); + it('dispatches updateTranscriptHandlerUrl action', () => { + expect(dispatchedAction.getHandlerlUrl).not.toEqual(undefined); + }); + it('dispatches actions.video.updateField on success', () => { + dispatch.mockClear(); + dispatchedAction.getHandlerlUrl.onSuccess(mockUpdateTranscriptHandlerUrlData); + expect(dispatch).toHaveBeenCalledWith( + actions.video.updateField({ transcriptHandlerUrl: mockUpdateTranscriptHandlerUrl }), + ); + }); + }); describe('uploadTranscript', () => { beforeEach(() => { thunkActions.uploadTranscript({ From 9c4a930902973364599d8914ec455497b1a1f4c7 Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Mon, 27 Jan 2025 10:29:35 -0500 Subject: [PATCH 04/10] feat: Suppor delete and replace transcripts in library --- .../data/redux/thunkActions/requests.js | 33 +++++++++++++------ .../data/redux/thunkActions/requests.test.js | 26 +++++++++++++++ src/editors/data/services/cms/api.test.ts | 17 ++++++++-- src/editors/data/services/cms/api.ts | 13 +++++++- src/editors/data/services/cms/urls.test.ts | 6 ++-- src/editors/data/services/cms/urls.ts | 2 +- 6 files changed, 80 insertions(+), 17 deletions(-) diff --git a/src/editors/data/redux/thunkActions/requests.js b/src/editors/data/redux/thunkActions/requests.js index 8230433c7d..9d99be2ab1 100644 --- a/src/editors/data/redux/thunkActions/requests.js +++ b/src/editors/data/redux/thunkActions/requests.js @@ -240,16 +240,29 @@ export const importTranscript = ({ youTubeId, ...rest }) => (dispatch, getState) }; export const deleteTranscript = ({ language, videoId, ...rest }) => (dispatch, getState) => { - dispatch(module.networkRequest({ - requestKey: RequestKeys.deleteTranscript, - promise: api.deleteTranscript({ - blockId: selectors.app.blockId(getState()), - language, - videoId, - studioEndpointUrl: selectors.app.studioEndpointUrl(getState()), - }), - ...rest, - })); + const isLibrary = selectors.app.isLibrary(getState()); + if (isLibrary) { + dispatch(module.networkRequest({ + requestKey: RequestKeys.deleteTranscript, + promise: api.deleteTranscriptV2({ + language, + videoId, + handlerUrl: selectors.video.transcriptHandlerUrl(getState()), + }), + ...rest, + })); + } else { + dispatch(module.networkRequest({ + requestKey: RequestKeys.deleteTranscript, + promise: api.deleteTranscript({ + blockId: selectors.app.blockId(getState()), + language, + videoId, + studioEndpointUrl: selectors.app.studioEndpointUrl(getState()), + }), + ...rest, + })); + } }; export const uploadTranscript = ({ diff --git a/src/editors/data/redux/thunkActions/requests.test.js b/src/editors/data/redux/thunkActions/requests.test.js index 8b5eec9fb7..992147f6e3 100644 --- a/src/editors/data/redux/thunkActions/requests.test.js +++ b/src/editors/data/redux/thunkActions/requests.test.js @@ -40,6 +40,7 @@ jest.mock('../../services/cms/api', () => ({ uploadThumbnail: (args) => args, uploadTranscript: (args) => args, deleteTranscript: (args) => args, + deleteTranscriptV2: (args) => args, getTranscript: (args) => args, getHandlerUrl: (args) => args, uploadTranscriptV2: (args) => args, @@ -454,6 +455,31 @@ describe('requests thunkActions module', () => { }, }); }); + describe('deleteTranscript V2', () => { + const language = 'SoME laNGUage CoNtent As String'; + const videoId = 'SoME VidEOid CoNtent As String'; + testNetworkRequestAction({ + action: requests.deleteTranscript, + args: { + language, + videoId, + ...fetchParams, + }, + expectedString: 'with deleteTranscript promise', + expectedData: { + ...fetchParams, + requestKey: RequestKeys.deleteTranscript, + promise: api.deleteTranscriptV2({ + handlerUrl: 'transcriptHandlerUrl', + videoId, + language, + }), + }, + state: { + isLibrary: true, + }, + }); + }); describe('checkTranscriptsForImport', () => { const youTubeId = 'SoME yOUtUbEiD As String'; const videoId = 'SoME VidEOid As String'; diff --git a/src/editors/data/services/cms/api.test.ts b/src/editors/data/services/cms/api.test.ts index 0eb8ef4771..5bb04eb2f7 100644 --- a/src/editors/data/services/cms/api.test.ts +++ b/src/editors/data/services/cms/api.test.ts @@ -24,7 +24,7 @@ jest.mock('./urls', () => ({ ({ studioEndpointUrl, learningContextId }) => `${studioEndpointUrl}/some_video_upload_url/${learningContextId}`, ), handlerUrl: jest.fn().mockReturnValue('urls.handlerUrl'), - uploadTrascriptXblockV2: jest.fn().mockReturnValue('url.uploadTranscriptV2'), + trascriptXblockV2: jest.fn().mockReturnValue('url.transcriptXblockV2'), })); jest.mock('./utils', () => ({ @@ -436,7 +436,7 @@ describe('cms api', () => { language, }); expect(post).toHaveBeenCalledWith( - urls.uploadTrascriptXblockV2({ transcriptHandlerUrl }), + urls.trascriptXblockV2({ transcriptHandlerUrl }), mockFormdata, ); }); @@ -455,6 +455,19 @@ describe('cms api', () => { mockDeleteJSON, ); }); + it('should call deleteObject with urls.trascriptXblockV2 and transcript data', () => { + const mockDeleteJSON = { data: { lang: language, edx_video_id: videoId } }; + const transcriptHandlerUrl = 'handlerUrl'; + apiMethods.deleteTranscriptV2({ + handlerUrl: transcriptHandlerUrl, + videoId, + language, + }); + expect(deleteObject).toHaveBeenCalledWith( + urls.trascriptXblockV2({ transcriptHandlerUrl }), + mockDeleteJSON, + ); + }); }); describe('transcript get', () => { it('should call get with urls.videoTranscripts and transcript data', () => { diff --git a/src/editors/data/services/cms/api.ts b/src/editors/data/services/cms/api.ts index a1ca8e2334..04803ecda8 100644 --- a/src/editors/data/services/cms/api.ts +++ b/src/editors/data/services/cms/api.ts @@ -228,6 +228,17 @@ export const apiMethods = { deleteJSON, ); }, + deleteTranscriptV2: ({ + handlerUrl, + language, + videoId, + }) => { + const deleteJSON = { data: { lang: language, edx_video_id: videoId } }; + return deleteObject( + urls.trascriptXblockV2({ transcriptHandlerUrl: handlerUrl }), + deleteJSON, + ); + }, uploadTranscript: ({ blockId, studioEndpointUrl, @@ -259,7 +270,7 @@ export const apiMethods = { data.append('language_code', language); data.append('new_language_code', newLanguage || language); return post( - urls.uploadTrascriptXblockV2({ transcriptHandlerUrl: handlerUrl }), + urls.trascriptXblockV2({ transcriptHandlerUrl: handlerUrl }), data, ); }, diff --git a/src/editors/data/services/cms/urls.test.ts b/src/editors/data/services/cms/urls.test.ts index 82696069c5..10bbc7c02c 100644 --- a/src/editors/data/services/cms/urls.test.ts +++ b/src/editors/data/services/cms/urls.test.ts @@ -18,7 +18,7 @@ import { videoFeatures, courseVideos, handlerUrl, - uploadTrascriptXblockV2, + trascriptXblockV2, } from './urls'; describe('cms url methods', () => { @@ -198,11 +198,11 @@ describe('cms url methods', () => { .toEqual(`${studioEndpointUrl}/api/xblock/v2/xblocks/${blockId}/handler_url/transcript/`); }); }); - describe('uploadTrascriptXblockV2', () => { + describe('trascriptXblockV2', () => { it('returns url with transcriptHandlerUrl', () => { const handlerName = 'transcript'; const transcriptHandlerUrl = handlerUrl({ studioEndpointUrl, blockId, handlerName }); - expect(uploadTrascriptXblockV2({ transcriptHandlerUrl })) + expect(trascriptXblockV2({ transcriptHandlerUrl })) .toEqual(`${transcriptHandlerUrl}translation`); }); }); diff --git a/src/editors/data/services/cms/urls.ts b/src/editors/data/services/cms/urls.ts index cca1b8bffe..d1e40841b7 100644 --- a/src/editors/data/services/cms/urls.ts +++ b/src/editors/data/services/cms/urls.ts @@ -116,6 +116,6 @@ export const handlerUrl = (({ studioEndpointUrl, blockId, handlerName }) => ( `${studioEndpointUrl}/api/xblock/v2/xblocks/${blockId}/handler_url/${handlerName}/` )) satisfies UrlFunction; -export const uploadTrascriptXblockV2 = (({ transcriptHandlerUrl }) => ( +export const trascriptXblockV2 = (({ transcriptHandlerUrl }) => ( `${transcriptHandlerUrl}translation` )) satisfies UrlFunction; From 73c4bda0b987d083a0b3101e264b46aaa8bb5e6b Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Tue, 28 Jan 2025 19:55:47 -0500 Subject: [PATCH 05/10] feat: enable download, replace and update transcripts in library --- .../TranscriptWidget/TranscriptActionMenu.jsx | 13 +++- .../TranscriptActionMenu.test.jsx | 12 ++++ .../TranscriptActionMenu.test.jsx.snap | 54 ++++++++++++++ .../data/redux/thunkActions/requests.js | 72 +++++++++++++------ .../data/redux/thunkActions/requests.test.js | 55 +++++++++++++- src/editors/data/redux/video/selectors.js | 16 ++++- src/editors/data/services/cms/api.test.ts | 13 ++++ src/editors/data/services/cms/api.ts | 12 +++- src/editors/data/services/cms/urls.test.ts | 12 +++- src/editors/data/services/cms/urls.ts | 12 ++-- 10 files changed, 236 insertions(+), 35 deletions(-) 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 46f51180bc..4e0a0a1112 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/TranscriptActionMenu.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/TranscriptActionMenu.jsx @@ -33,10 +33,17 @@ const TranscriptActionMenu = ({ launchDeleteConfirmation, // redux getTranscriptDownloadUrl, + getTranscriptDownloadUrlV2, buildTranscriptUrl, + isLibrary, }) => { const input = fileInput({ onAddFile: module.hooks.replaceFileCallback({ language, dispatch: useDispatch() }) }); - const downloadLink = transcriptUrl ? buildTranscriptUrl({ transcriptUrl }) : getTranscriptDownloadUrl({ language }); + let downloadLink; + if (isLibrary) { + downloadLink = getTranscriptDownloadUrlV2({ language }); + } else { + downloadLink = transcriptUrl ? buildTranscriptUrl({ transcriptUrl }) : getTranscriptDownloadUrl({ language }); + } return ( ({ getTranscriptDownloadUrl: selectors.video.getTranscriptDownloadUrl(state), + getTranscriptDownloadUrlV2: selectors.video.getTranscriptDownloadUrlV2(state), buildTranscriptUrl: selectors.video.buildTranscriptUrl(state), + isLibrary: selectors.app.isLibrary(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 b23f5040e8..d31e4ae219 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 @@ -26,8 +26,12 @@ jest.mock('../../../../../../data/redux', () => ({ }, }, selectors: { + app: { + isLibrary: jest.fn(args => ({ isLibrary: args })).mockName('selectors.app.isLibrary'), + }, video: { getTranscriptDownloadUrl: jest.fn(args => ({ getTranscriptDownloadUrl: args })).mockName('selectors.video.getTranscriptDownloadUrl'), + getTranscriptDownloadUrlV2: jest.fn(args => ({ getTranscriptDownloadUrlV2: args })).mockName('selectors.video.getTranscriptDownloadUrlV2'), buildTranscriptUrl: jest.fn(args => ({ buildTranscriptUrl: args })).mockName('selectors.video.buildTranscriptUrl'), }, }, @@ -66,7 +70,9 @@ describe('TranscriptActionMenu', () => { launchDeleteConfirmation: jest.fn().mockName('launchDeleteConfirmation'), // redux getTranscriptDownloadUrl: jest.fn().mockName('selectors.video.getTranscriptDownloadUrl'), + getTranscriptDownloadUrlV2: jest.fn().mockName('selectors.video.getTranscriptDownloadUrlV2'), buildTranscriptUrl: jest.fn().mockName('selectors.video.buildTranscriptUrl'), + isLibrary: false, }; afterAll(() => { jest.clearAllMocks(); @@ -83,6 +89,12 @@ describe('TranscriptActionMenu', () => { shallow().snapshot, ).toMatchSnapshot(); }); + test('snapshots: renders as expected with isLibrary props: dont show confirm delete', () => { + jest.spyOn(module.hooks, 'replaceFileCallback').mockImplementationOnce(() => jest.fn().mockName('module.hooks.replaceFileCallback')); + expect( + shallow().snapshot, + ).toMatchSnapshot(); + }); }); describe('mapStateToProps', () => { const testState = { A: 'pple', B: 'anana', C: 'ucumber' }; 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 f9c0bc5336..ba0aa9cc26 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 @@ -54,6 +54,60 @@ exports[`TranscriptActionMenu Snapshots snapshots: renders as expected with defa `; +exports[`TranscriptActionMenu Snapshots snapshots: renders as expected with isLibrary props: dont show confirm delete 1`] = ` + + + + + + + + + + + + + + + +`; + exports[`TranscriptActionMenu Snapshots snapshots: renders as expected with transcriptUrl props: dont show confirm delete 1`] = ` (dispatch, getState) => { - dispatch(module.networkRequest({ - requestKey: RequestKeys.updateTranscriptLanguage, - promise: api.uploadTranscript({ - blockId: selectors.app.blockId(getState()), - transcript: file, - videoId, - language: languageBeforeChange, - newLanguage: newLanguageCode, - studioEndpointUrl: selectors.app.studioEndpointUrl(getState()), - }), - ...rest, - })); + const isLibrary = selectors.app.isLibrary(getState()); + if (isLibrary) { + dispatch(module.networkRequest({ + requestKey: RequestKeys.updateTranscriptLanguage, + promise: api.uploadTranscriptV2({ + handlerUrl: selectors.video.transcriptHandlerUrl(getState()), + transcript: file, + videoId, + language: languageBeforeChange, + newLanguage: newLanguageCode, + }), + ...rest, + })); + } else { + dispatch(module.networkRequest({ + requestKey: RequestKeys.updateTranscriptLanguage, + promise: api.uploadTranscript({ + blockId: selectors.app.blockId(getState()), + transcript: file, + videoId, + language: languageBeforeChange, + newLanguage: newLanguageCode, + studioEndpointUrl: selectors.app.studioEndpointUrl(getState()), + }), + ...rest, + })); + } }; export const getTranscriptFile = ({ language, videoId, ...rest }) => (dispatch, getState) => { - dispatch(module.networkRequest({ - requestKey: RequestKeys.getTranscriptFile, - promise: api.getTranscript({ - studioEndpointUrl: selectors.app.studioEndpointUrl(getState()), - blockId: selectors.app.blockId(getState()), - videoId, - language, - }), - ...rest, - })); + const isLibrary = selectors.app.isLibrary(getState()); + if (isLibrary) { + dispatch(module.networkRequest({ + requestKey: RequestKeys.getTranscriptFile, + promise: api.getTranscriptV2({ + handlerUrl: selectors.video.transcriptHandlerUrl(getState()), + videoId, + language, + }), + ...rest, + })); + } else { + dispatch(module.networkRequest({ + requestKey: RequestKeys.getTranscriptFile, + promise: api.getTranscript({ + studioEndpointUrl: selectors.app.studioEndpointUrl(getState()), + blockId: selectors.app.blockId(getState()), + videoId, + language, + }), + ...rest, + })); + } }; export const getHandlerlUrl = ({ handlerName, ...rest }) => (dispatch, getState) => { diff --git a/src/editors/data/redux/thunkActions/requests.test.js b/src/editors/data/redux/thunkActions/requests.test.js index 992147f6e3..27f288685c 100644 --- a/src/editors/data/redux/thunkActions/requests.test.js +++ b/src/editors/data/redux/thunkActions/requests.test.js @@ -42,6 +42,7 @@ jest.mock('../../services/cms/api', () => ({ deleteTranscript: (args) => args, deleteTranscriptV2: (args) => args, getTranscript: (args) => args, + getTranscriptV2: (args) => args, getHandlerUrl: (args) => args, uploadTranscriptV2: (args) => args, checkTranscriptsForImport: (args) => args, @@ -470,7 +471,7 @@ describe('requests thunkActions module', () => { ...fetchParams, requestKey: RequestKeys.deleteTranscript, promise: api.deleteTranscriptV2({ - handlerUrl: 'transcriptHandlerUrl', + handlerUrl: selectors.video.transcriptHandlerUrl(testState), videoId, language, }), @@ -535,6 +536,27 @@ describe('requests thunkActions module', () => { }, }); }); + describe('getTranscriptFile V2', () => { + const language = 'SoME laNGUage CoNtent As String'; + const videoId = 'SoME VidEOid CoNtent As String'; + testNetworkRequestAction({ + action: requests.getTranscriptFile, + args: { language, videoId, ...fetchParams }, + expectedString: 'with getTranscriptFile promise', + expectedData: { + ...fetchParams, + requestKey: RequestKeys.getTranscriptFile, + promise: api.getTranscriptV2({ + handlerUrl: selectors.video.transcriptHandlerUrl(testState), + language, + videoId, + }), + }, + state: { + isLibrary: true, + }, + }); + }); describe('getHandlerUrl', () => { const handlerName = 'transcript'; testNetworkRequestAction({ @@ -578,7 +600,34 @@ describe('requests thunkActions module', () => { }, }); }); - + describe('updateTranscriptLanguage V2', () => { + const languageBeforeChange = 'SoME laNGUage CoNtent As String'; + const newLanguageCode = 'SoME NEW laNGUage CoNtent As String'; + const videoId = 'SoME VidEOid CoNtent As String'; + testNetworkRequestAction({ + action: requests.updateTranscriptLanguage, + args: { + languageBeforeChange, + newLanguageCode, + videoId, + ...fetchParams, + }, + expectedString: 'with uploadTranscript promise', + expectedData: { + ...fetchParams, + requestKey: RequestKeys.updateTranscriptLanguage, + promise: api.uploadTranscriptV2({ + videoId, + language: languageBeforeChange, + newLanguage: newLanguageCode, + handlerUrl: selectors.video.transcriptHandlerUrl(testState), + }), + }, + state: { + isLibrary: true, + }, + }); + }); describe('uploadTranscript', () => { const language = 'SoME laNGUage CoNtent As String'; const videoId = 'SoME VidEOid CoNtent As String'; @@ -622,7 +671,7 @@ describe('requests thunkActions module', () => { ...fetchParams, requestKey: RequestKeys.uploadTranscript, promise: api.uploadTranscriptV2({ - handlerUrl: 'transcriptHandlerUrl', + handlerUrl: selectors.video.transcriptHandlerUrl(testState), transcript, videoId, language, diff --git a/src/editors/data/redux/video/selectors.js b/src/editors/data/redux/video/selectors.js index f8c21f0a39..eb2148b215 100644 --- a/src/editors/data/redux/video/selectors.js +++ b/src/editors/data/redux/video/selectors.js @@ -10,7 +10,12 @@ import { initialState } from './reducer'; // eslint-disable-next-line import/no-self-import import * as module from './selectors'; import * as AppSelectors from '../app/selectors'; -import { downloadVideoTranscriptURL, downloadVideoHandoutUrl, mediaTranscriptURL } from '../../services/cms/urls'; +import { + downloadVideoTranscriptURL, + downloadVideoHandoutUrl, + mediaTranscriptURL, + downloadVideoTranscriptURLV2, +} from '../../services/cms/urls'; const stateKeys = keyStore(initialState); @@ -63,6 +68,14 @@ export const getTranscriptDownloadUrl = createSelector( }), ); +export const getTranscriptDownloadUrlV2 = createSelector( + [simpleSelectors.transcriptHandlerUrl], + (transcriptHandlerUrl) => ({ language }) => downloadVideoTranscriptURLV2({ + transcriptHandlerUrl, + language, + }), +); + export const buildTranscriptUrl = createSelector( [AppSelectors.simpleSelectors.studioEndpointUrl], (studioEndpointUrl) => ({ transcriptUrl }) => mediaTranscriptURL({ @@ -135,6 +148,7 @@ export default { ...simpleSelectors, openLanguages, getTranscriptDownloadUrl, + getTranscriptDownloadUrlV2, buildTranscriptUrl, getHandoutDownloadUrl, videoSettings, diff --git a/src/editors/data/services/cms/api.test.ts b/src/editors/data/services/cms/api.test.ts index 5bb04eb2f7..39c3a20652 100644 --- a/src/editors/data/services/cms/api.test.ts +++ b/src/editors/data/services/cms/api.test.ts @@ -483,6 +483,19 @@ describe('cms api', () => { mockJSON, ); }); + it('should call get with urls.trascriptXblockV2 and transcript data', () => { + const mockJSON = { data: { lang: language, edx_video_id: videoId } }; + const transcriptHandlerUrl = 'handlerUrl'; + apiMethods.getTranscriptV2({ + handlerUrl: transcriptHandlerUrl, + videoId, + language, + }); + expect(get).toHaveBeenCalledWith( + `${urls.trascriptXblockV2({ transcriptHandlerUrl })}?language_code=${language}`, + mockJSON, + ); + }); }); }); describe('processVideoIds', () => { diff --git a/src/editors/data/services/cms/api.ts b/src/editors/data/services/cms/api.ts index 04803ecda8..1e2b77a25b 100644 --- a/src/editors/data/services/cms/api.ts +++ b/src/editors/data/services/cms/api.ts @@ -215,7 +215,17 @@ export const apiMethods = { getJSON, ); }, - + getTranscriptV2: ({ + handlerUrl, + language, + videoId, + }) => { + const getJSON = { data: { lang: language, edx_video_id: videoId } }; + return get( + `${urls.trascriptXblockV2({ transcriptHandlerUrl: handlerUrl })}?language_code=${language}`, + getJSON, + ); + }, deleteTranscript: ({ studioEndpointUrl, language, diff --git a/src/editors/data/services/cms/urls.test.ts b/src/editors/data/services/cms/urls.test.ts index 10bbc7c02c..d159937557 100644 --- a/src/editors/data/services/cms/urls.test.ts +++ b/src/editors/data/services/cms/urls.test.ts @@ -19,6 +19,7 @@ import { courseVideos, handlerUrl, trascriptXblockV2, + downloadVideoTranscriptURLV2, } from './urls'; describe('cms url methods', () => { @@ -34,6 +35,8 @@ describe('cms url methods', () => { const handout = '/aSSet@hANdoUt'; const videoId = '123-SOmeVidEOid-213'; const parameters = 'SomEParAMEterS'; + const handlerName = 'transcript'; + const transcriptHandlerUrl = handlerUrl({ studioEndpointUrl, blockId, handlerName }); describe('return to learning context urls', () => { const unitUrl = { @@ -193,17 +196,20 @@ describe('cms url methods', () => { }); describe('handlerUrl', () => { it('returns url with studioEndpointUrl, blockId and handlerName', () => { - const handlerName = 'transcript'; expect(handlerUrl({ studioEndpointUrl, blockId, handlerName })) .toEqual(`${studioEndpointUrl}/api/xblock/v2/xblocks/${blockId}/handler_url/transcript/`); }); }); describe('trascriptXblockV2', () => { it('returns url with transcriptHandlerUrl', () => { - const handlerName = 'transcript'; - const transcriptHandlerUrl = handlerUrl({ studioEndpointUrl, blockId, handlerName }); expect(trascriptXblockV2({ transcriptHandlerUrl })) .toEqual(`${transcriptHandlerUrl}translation`); }); }); + describe('downloadVideoTranscriptURLV2', () => { + it('returns url with transcriptHandlerUrl and language', () => { + expect(downloadVideoTranscriptURLV2({ transcriptHandlerUrl, language })) + .toEqual(`${transcriptHandlerUrl}translation?language_code=${language}`); + }); + }); }); diff --git a/src/editors/data/services/cms/urls.ts b/src/editors/data/services/cms/urls.ts index d1e40841b7..f349654c04 100644 --- a/src/editors/data/services/cms/urls.ts +++ b/src/editors/data/services/cms/urls.ts @@ -80,6 +80,14 @@ export const downloadVideoTranscriptURL = (({ studioEndpointUrl, blockId, langua `${videoTranscripts({ studioEndpointUrl, blockId })}?language_code=${language}` )) satisfies UrlFunction; +export const trascriptXblockV2 = (({ transcriptHandlerUrl }) => ( + `${transcriptHandlerUrl}translation` +)) satisfies UrlFunction; + +export const downloadVideoTranscriptURLV2 = (({ transcriptHandlerUrl, language }) => ( + `${trascriptXblockV2({ transcriptHandlerUrl })}?language_code=${language}` +)) satisfies UrlFunction; + export const mediaTranscriptURL = (({ studioEndpointUrl, transcriptUrl }) => ( `${studioEndpointUrl}${transcriptUrl}` )) satisfies UrlFunction; @@ -115,7 +123,3 @@ export const courseVideos = (({ studioEndpointUrl, learningContextId }) => ( export const handlerUrl = (({ studioEndpointUrl, blockId, handlerName }) => ( `${studioEndpointUrl}/api/xblock/v2/xblocks/${blockId}/handler_url/${handlerName}/` )) satisfies UrlFunction; - -export const trascriptXblockV2 = (({ transcriptHandlerUrl }) => ( - `${transcriptHandlerUrl}translation` -)) satisfies UrlFunction; From 9f74c0772fa85146c24cc0307ec4cdb8953aa89e Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Wed, 29 Jan 2025 17:18:18 -0500 Subject: [PATCH 06/10] refactor: use getState once in requests.js functions --- .../data/redux/thunkActions/requests.js | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/editors/data/redux/thunkActions/requests.js b/src/editors/data/redux/thunkActions/requests.js index a3ab4efcea..3dd4663477 100644 --- a/src/editors/data/redux/thunkActions/requests.js +++ b/src/editors/data/redux/thunkActions/requests.js @@ -240,14 +240,15 @@ export const importTranscript = ({ youTubeId, ...rest }) => (dispatch, getState) }; export const deleteTranscript = ({ language, videoId, ...rest }) => (dispatch, getState) => { - const isLibrary = selectors.app.isLibrary(getState()); + const state = getState(); + const isLibrary = selectors.app.isLibrary(state); if (isLibrary) { dispatch(module.networkRequest({ requestKey: RequestKeys.deleteTranscript, promise: api.deleteTranscriptV2({ language, videoId, - handlerUrl: selectors.video.transcriptHandlerUrl(getState()), + handlerUrl: selectors.video.transcriptHandlerUrl(state), }), ...rest, })); @@ -255,10 +256,10 @@ export const deleteTranscript = ({ language, videoId, ...rest }) => (dispatch, g dispatch(module.networkRequest({ requestKey: RequestKeys.deleteTranscript, promise: api.deleteTranscript({ - blockId: selectors.app.blockId(getState()), + blockId: selectors.app.blockId(state), language, videoId, - studioEndpointUrl: selectors.app.studioEndpointUrl(getState()), + studioEndpointUrl: selectors.app.studioEndpointUrl(state), }), ...rest, })); @@ -271,12 +272,13 @@ export const uploadTranscript = ({ language, ...rest }) => (dispatch, getState) => { - const isLibrary = selectors.app.isLibrary(getState()); + const state = getState(); + const isLibrary = selectors.app.isLibrary(state); if (isLibrary) { dispatch(module.networkRequest({ requestKey: RequestKeys.uploadTranscript, promise: api.uploadTranscriptV2({ - handlerUrl: selectors.video.transcriptHandlerUrl(getState()), + handlerUrl: selectors.video.transcriptHandlerUrl(state), transcript, videoId, language, @@ -287,11 +289,11 @@ export const uploadTranscript = ({ dispatch(module.networkRequest({ requestKey: RequestKeys.uploadTranscript, promise: api.uploadTranscript({ - blockId: selectors.app.blockId(getState()), + blockId: selectors.app.blockId(state), transcript, videoId, language, - studioEndpointUrl: selectors.app.studioEndpointUrl(getState()), + studioEndpointUrl: selectors.app.studioEndpointUrl(state), }), ...rest, })); @@ -305,12 +307,13 @@ export const updateTranscriptLanguage = ({ videoId, ...rest }) => (dispatch, getState) => { - const isLibrary = selectors.app.isLibrary(getState()); + const state = getState(); + const isLibrary = selectors.app.isLibrary(state); if (isLibrary) { dispatch(module.networkRequest({ requestKey: RequestKeys.updateTranscriptLanguage, promise: api.uploadTranscriptV2({ - handlerUrl: selectors.video.transcriptHandlerUrl(getState()), + handlerUrl: selectors.video.transcriptHandlerUrl(state), transcript: file, videoId, language: languageBeforeChange, @@ -322,12 +325,12 @@ export const updateTranscriptLanguage = ({ dispatch(module.networkRequest({ requestKey: RequestKeys.updateTranscriptLanguage, promise: api.uploadTranscript({ - blockId: selectors.app.blockId(getState()), + blockId: selectors.app.blockId(state), transcript: file, videoId, language: languageBeforeChange, newLanguage: newLanguageCode, - studioEndpointUrl: selectors.app.studioEndpointUrl(getState()), + studioEndpointUrl: selectors.app.studioEndpointUrl(state), }), ...rest, })); @@ -335,12 +338,13 @@ export const updateTranscriptLanguage = ({ }; export const getTranscriptFile = ({ language, videoId, ...rest }) => (dispatch, getState) => { - const isLibrary = selectors.app.isLibrary(getState()); + const state = getState(); + const isLibrary = selectors.app.isLibrary(state); if (isLibrary) { dispatch(module.networkRequest({ requestKey: RequestKeys.getTranscriptFile, promise: api.getTranscriptV2({ - handlerUrl: selectors.video.transcriptHandlerUrl(getState()), + handlerUrl: selectors.video.transcriptHandlerUrl(state), videoId, language, }), @@ -350,8 +354,8 @@ export const getTranscriptFile = ({ language, videoId, ...rest }) => (dispatch, dispatch(module.networkRequest({ requestKey: RequestKeys.getTranscriptFile, promise: api.getTranscript({ - studioEndpointUrl: selectors.app.studioEndpointUrl(getState()), - blockId: selectors.app.blockId(getState()), + studioEndpointUrl: selectors.app.studioEndpointUrl(state), + blockId: selectors.app.blockId(state), videoId, language, }), From 4961e732c21fae7202ea7d2f032f96a98548fbfd Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Wed, 29 Jan 2025 18:15:38 -0500 Subject: [PATCH 07/10] refactor: trascriptXblockV2 to transcriptXblockV2 in urls --- src/editors/data/services/cms/api.test.ts | 12 ++++++------ src/editors/data/services/cms/api.ts | 6 +++--- src/editors/data/services/cms/urls.test.ts | 6 +++--- src/editors/data/services/cms/urls.ts | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/editors/data/services/cms/api.test.ts b/src/editors/data/services/cms/api.test.ts index 39c3a20652..9ae6f9a379 100644 --- a/src/editors/data/services/cms/api.test.ts +++ b/src/editors/data/services/cms/api.test.ts @@ -24,7 +24,7 @@ jest.mock('./urls', () => ({ ({ studioEndpointUrl, learningContextId }) => `${studioEndpointUrl}/some_video_upload_url/${learningContextId}`, ), handlerUrl: jest.fn().mockReturnValue('urls.handlerUrl'), - trascriptXblockV2: jest.fn().mockReturnValue('url.transcriptXblockV2'), + transcriptXblockV2: jest.fn().mockReturnValue('url.transcriptXblockV2'), })); jest.mock('./utils', () => ({ @@ -436,7 +436,7 @@ describe('cms api', () => { language, }); expect(post).toHaveBeenCalledWith( - urls.trascriptXblockV2({ transcriptHandlerUrl }), + urls.transcriptXblockV2({ transcriptHandlerUrl }), mockFormdata, ); }); @@ -455,7 +455,7 @@ describe('cms api', () => { mockDeleteJSON, ); }); - it('should call deleteObject with urls.trascriptXblockV2 and transcript data', () => { + it('should call deleteObject with urls.transcriptXblockV2 and transcript data', () => { const mockDeleteJSON = { data: { lang: language, edx_video_id: videoId } }; const transcriptHandlerUrl = 'handlerUrl'; apiMethods.deleteTranscriptV2({ @@ -464,7 +464,7 @@ describe('cms api', () => { language, }); expect(deleteObject).toHaveBeenCalledWith( - urls.trascriptXblockV2({ transcriptHandlerUrl }), + urls.transcriptXblockV2({ transcriptHandlerUrl }), mockDeleteJSON, ); }); @@ -483,7 +483,7 @@ describe('cms api', () => { mockJSON, ); }); - it('should call get with urls.trascriptXblockV2 and transcript data', () => { + it('should call get with urls.transcriptXblockV2 and transcript data', () => { const mockJSON = { data: { lang: language, edx_video_id: videoId } }; const transcriptHandlerUrl = 'handlerUrl'; apiMethods.getTranscriptV2({ @@ -492,7 +492,7 @@ describe('cms api', () => { language, }); expect(get).toHaveBeenCalledWith( - `${urls.trascriptXblockV2({ transcriptHandlerUrl })}?language_code=${language}`, + `${urls.transcriptXblockV2({ transcriptHandlerUrl })}?language_code=${language}`, mockJSON, ); }); diff --git a/src/editors/data/services/cms/api.ts b/src/editors/data/services/cms/api.ts index 1e2b77a25b..ec4cd7d760 100644 --- a/src/editors/data/services/cms/api.ts +++ b/src/editors/data/services/cms/api.ts @@ -222,7 +222,7 @@ export const apiMethods = { }) => { const getJSON = { data: { lang: language, edx_video_id: videoId } }; return get( - `${urls.trascriptXblockV2({ transcriptHandlerUrl: handlerUrl })}?language_code=${language}`, + `${urls.transcriptXblockV2({ transcriptHandlerUrl: handlerUrl })}?language_code=${language}`, getJSON, ); }, @@ -245,7 +245,7 @@ export const apiMethods = { }) => { const deleteJSON = { data: { lang: language, edx_video_id: videoId } }; return deleteObject( - urls.trascriptXblockV2({ transcriptHandlerUrl: handlerUrl }), + urls.transcriptXblockV2({ transcriptHandlerUrl: handlerUrl }), deleteJSON, ); }, @@ -280,7 +280,7 @@ export const apiMethods = { data.append('language_code', language); data.append('new_language_code', newLanguage || language); return post( - urls.trascriptXblockV2({ transcriptHandlerUrl: handlerUrl }), + urls.transcriptXblockV2({ transcriptHandlerUrl: handlerUrl }), data, ); }, diff --git a/src/editors/data/services/cms/urls.test.ts b/src/editors/data/services/cms/urls.test.ts index d159937557..c1f9c1ecda 100644 --- a/src/editors/data/services/cms/urls.test.ts +++ b/src/editors/data/services/cms/urls.test.ts @@ -18,7 +18,7 @@ import { videoFeatures, courseVideos, handlerUrl, - trascriptXblockV2, + transcriptXblockV2, downloadVideoTranscriptURLV2, } from './urls'; @@ -200,9 +200,9 @@ describe('cms url methods', () => { .toEqual(`${studioEndpointUrl}/api/xblock/v2/xblocks/${blockId}/handler_url/transcript/`); }); }); - describe('trascriptXblockV2', () => { + describe('transcriptXblockV2', () => { it('returns url with transcriptHandlerUrl', () => { - expect(trascriptXblockV2({ transcriptHandlerUrl })) + expect(transcriptXblockV2({ transcriptHandlerUrl })) .toEqual(`${transcriptHandlerUrl}translation`); }); }); diff --git a/src/editors/data/services/cms/urls.ts b/src/editors/data/services/cms/urls.ts index f349654c04..6da1e22038 100644 --- a/src/editors/data/services/cms/urls.ts +++ b/src/editors/data/services/cms/urls.ts @@ -80,12 +80,12 @@ export const downloadVideoTranscriptURL = (({ studioEndpointUrl, blockId, langua `${videoTranscripts({ studioEndpointUrl, blockId })}?language_code=${language}` )) satisfies UrlFunction; -export const trascriptXblockV2 = (({ transcriptHandlerUrl }) => ( +export const transcriptXblockV2 = (({ transcriptHandlerUrl }) => ( `${transcriptHandlerUrl}translation` )) satisfies UrlFunction; export const downloadVideoTranscriptURLV2 = (({ transcriptHandlerUrl, language }) => ( - `${trascriptXblockV2({ transcriptHandlerUrl })}?language_code=${language}` + `${transcriptXblockV2({ transcriptHandlerUrl })}?language_code=${language}` )) satisfies UrlFunction; export const mediaTranscriptURL = (({ studioEndpointUrl, transcriptUrl }) => ( From 91b4a9d8d02f0474a5b7fd4de3ecd0aaa48ec5ce Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Wed, 29 Jan 2025 18:48:13 -0500 Subject: [PATCH 08/10] refactor: delete getTranscriptDownloadUrlV2 and add it to getTranscriptDownloadUrl --- .../TranscriptWidget/TranscriptActionMenu.jsx | 13 +- .../TranscriptActionMenu.test.jsx | 9 -- .../TranscriptActionMenu.test.jsx.snap | 54 -------- .../__snapshots__/index.test.jsx.snap | 119 +++++++++++++++++- .../TranscriptWidget/index.test.jsx | 1 + src/editors/data/redux/video/selectors.js | 34 ++--- 6 files changed, 139 insertions(+), 91 deletions(-) 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 4e0a0a1112..46f51180bc 100644 --- a/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/TranscriptActionMenu.jsx +++ b/src/editors/containers/VideoEditor/components/VideoSettingsModal/components/TranscriptWidget/TranscriptActionMenu.jsx @@ -33,17 +33,10 @@ const TranscriptActionMenu = ({ launchDeleteConfirmation, // redux getTranscriptDownloadUrl, - getTranscriptDownloadUrlV2, buildTranscriptUrl, - isLibrary, }) => { const input = fileInput({ onAddFile: module.hooks.replaceFileCallback({ language, dispatch: useDispatch() }) }); - let downloadLink; - if (isLibrary) { - downloadLink = getTranscriptDownloadUrlV2({ language }); - } else { - downloadLink = transcriptUrl ? buildTranscriptUrl({ transcriptUrl }) : getTranscriptDownloadUrl({ language }); - } + const downloadLink = transcriptUrl ? buildTranscriptUrl({ transcriptUrl }) : getTranscriptDownloadUrl({ language }); return ( ({ getTranscriptDownloadUrl: selectors.video.getTranscriptDownloadUrl(state), - getTranscriptDownloadUrlV2: selectors.video.getTranscriptDownloadUrlV2(state), buildTranscriptUrl: selectors.video.buildTranscriptUrl(state), - isLibrary: selectors.app.isLibrary(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 d31e4ae219..9ffcf33f54 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 @@ -31,7 +31,6 @@ jest.mock('../../../../../../data/redux', () => ({ }, video: { getTranscriptDownloadUrl: jest.fn(args => ({ getTranscriptDownloadUrl: args })).mockName('selectors.video.getTranscriptDownloadUrl'), - getTranscriptDownloadUrlV2: jest.fn(args => ({ getTranscriptDownloadUrlV2: args })).mockName('selectors.video.getTranscriptDownloadUrlV2'), buildTranscriptUrl: jest.fn(args => ({ buildTranscriptUrl: args })).mockName('selectors.video.buildTranscriptUrl'), }, }, @@ -70,9 +69,7 @@ describe('TranscriptActionMenu', () => { launchDeleteConfirmation: jest.fn().mockName('launchDeleteConfirmation'), // redux getTranscriptDownloadUrl: jest.fn().mockName('selectors.video.getTranscriptDownloadUrl'), - getTranscriptDownloadUrlV2: jest.fn().mockName('selectors.video.getTranscriptDownloadUrlV2'), buildTranscriptUrl: jest.fn().mockName('selectors.video.buildTranscriptUrl'), - isLibrary: false, }; afterAll(() => { jest.clearAllMocks(); @@ -89,12 +86,6 @@ describe('TranscriptActionMenu', () => { shallow().snapshot, ).toMatchSnapshot(); }); - test('snapshots: renders as expected with isLibrary props: dont show confirm delete', () => { - jest.spyOn(module.hooks, 'replaceFileCallback').mockImplementationOnce(() => jest.fn().mockName('module.hooks.replaceFileCallback')); - expect( - shallow().snapshot, - ).toMatchSnapshot(); - }); }); describe('mapStateToProps', () => { const testState = { A: 'pple', B: 'anana', C: 'ucumber' }; 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 ba0aa9cc26..f9c0bc5336 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 @@ -54,60 +54,6 @@ exports[`TranscriptActionMenu Snapshots snapshots: renders as expected with defa `; -exports[`TranscriptActionMenu Snapshots snapshots: renders as expected with isLibrary props: dont show confirm delete 1`] = ` - - - - - - - - - - - - - - - -`; - exports[`TranscriptActionMenu Snapshots snapshots: renders as expected with transcriptUrl props: dont show confirm delete 1`] = ` `; -exports[`TranscriptWidget component snapshots snapshot: renders when isLibrary is true 1`] = `undefined`; +exports[`TranscriptWidget component snapshots snapshot: renders when isLibrary is true 1`] = ` + + + + + + + + + + + + +
+ +
+
+ + + + } + placement="top" + > + + + +
+ +
+ +
+
+
+
+ +
+
+
+`; exports[`TranscriptWidget component snapshots snapshots: renders as expected with allowTranscriptDownloads true 1`] = ` ({ thunkActions: { video: { deleteTranscript: jest.fn().mockName('thunkActions.video.deleteTranscript'), + updateTranscriptHandlerUrl: jest.fn().mockName('thunkActions.video.updateTranscriptHandlerUrl'), }, }, diff --git a/src/editors/data/redux/video/selectors.js b/src/editors/data/redux/video/selectors.js index eb2148b215..474ac518a7 100644 --- a/src/editors/data/redux/video/selectors.js +++ b/src/editors/data/redux/video/selectors.js @@ -60,20 +60,25 @@ export const openLanguages = createSelector( ); export const getTranscriptDownloadUrl = createSelector( - [AppSelectors.simpleSelectors.studioEndpointUrl, AppSelectors.simpleSelectors.blockId], - (studioEndpointUrl, blockId) => ({ language }) => downloadVideoTranscriptURL({ - studioEndpointUrl, - blockId, - language, - }), -); - -export const getTranscriptDownloadUrlV2 = createSelector( - [simpleSelectors.transcriptHandlerUrl], - (transcriptHandlerUrl) => ({ language }) => downloadVideoTranscriptURLV2({ - transcriptHandlerUrl, - language, - }), + [ + AppSelectors.simpleSelectors.studioEndpointUrl, + AppSelectors.simpleSelectors.blockId, + AppSelectors.isLibrary, + simpleSelectors.transcriptHandlerUrl, + ], + (studioEndpointUrl, blockId, isLibrary, transcriptHandlerUrl) => ({ language }) => { + if (isLibrary) { + return downloadVideoTranscriptURLV2({ + transcriptHandlerUrl, + language, + }); + } + return downloadVideoTranscriptURL({ + studioEndpointUrl, + blockId, + language, + }); + }, ); export const buildTranscriptUrl = createSelector( @@ -148,7 +153,6 @@ export default { ...simpleSelectors, openLanguages, getTranscriptDownloadUrl, - getTranscriptDownloadUrlV2, buildTranscriptUrl, getHandoutDownloadUrl, videoSettings, From e23367e21ca3e75cf899191a65899a0448cf8db1 Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Wed, 29 Jan 2025 19:16:27 -0500 Subject: [PATCH 09/10] style: Fix coverage --- .../components/TranscriptWidget/TranscriptActionMenu.test.jsx | 3 --- src/editors/data/redux/video/selectors.js | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) 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 9ffcf33f54..b23f5040e8 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 @@ -26,9 +26,6 @@ jest.mock('../../../../../../data/redux', () => ({ }, }, selectors: { - app: { - isLibrary: jest.fn(args => ({ isLibrary: args })).mockName('selectors.app.isLibrary'), - }, video: { getTranscriptDownloadUrl: jest.fn(args => ({ getTranscriptDownloadUrl: args })).mockName('selectors.video.getTranscriptDownloadUrl'), buildTranscriptUrl: jest.fn(args => ({ buildTranscriptUrl: args })).mockName('selectors.video.buildTranscriptUrl'), diff --git a/src/editors/data/redux/video/selectors.js b/src/editors/data/redux/video/selectors.js index 474ac518a7..4829321835 100644 --- a/src/editors/data/redux/video/selectors.js +++ b/src/editors/data/redux/video/selectors.js @@ -59,6 +59,7 @@ export const openLanguages = createSelector( }, ); +/* istanbul ignore next */ export const getTranscriptDownloadUrl = createSelector( [ AppSelectors.simpleSelectors.studioEndpointUrl, From ab97463e6b0928e98aca29fba180f1d6a42859fd Mon Sep 17 00:00:00 2001 From: XnpioChV Date: Wed, 12 Feb 2025 17:57:56 -0500 Subject: [PATCH 10/10] refactor: useSelector to get isLibrary in TranscriptWidget --- .../__snapshots__/index.test.jsx.snap | 119 ------------------ .../components/TranscriptWidget/index.jsx | 6 +- .../TranscriptWidget/index.test.jsx | 6 - 3 files changed, 2 insertions(+), 129 deletions(-) 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 83114a4d19..904d6433b2 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 @@ -242,125 +242,6 @@ exports[`TranscriptWidget component snapshots snapshot: renders ErrorAlert with `; -exports[`TranscriptWidget component snapshots snapshot: renders when isLibrary is true 1`] = ` - - - - - - - - - - - - -
- -
-
- - - - } - placement="top" - > - - - -
- -
- -
-
-
-
- -
-
-
-`; - exports[`TranscriptWidget component snapshots snapshots: renders as expected with allowTranscriptDownloads true 1`] = ` { @@ -98,6 +97,7 @@ const TranscriptWidget = ({ const [showImportCard, setShowImportCard] = React.useState(true); const fullTextLanguages = module.hooks.transcriptLanguages(transcripts, intl); const hasTranscripts = module.hooks.hasTranscripts(transcripts); + const isLibrary = useSelector(selectors.app.isLibrary); const dispatch = useDispatch(); if (isLibrary) { dispatch(thunkActions.video.updateTranscriptHandlerUrl()); @@ -202,7 +202,6 @@ TranscriptWidget.propTypes = { updateField: PropTypes.func.isRequired, isUploadError: PropTypes.bool.isRequired, isDeleteError: PropTypes.bool.isRequired, - isLibrary: PropTypes.bool.isRequired, intl: PropTypes.shape(intlShape).isRequired, }; export const mapStateToProps = (state) => ({ @@ -213,7 +212,6 @@ export const mapStateToProps = (state) => ({ allowTranscriptImport: selectors.video.allowTranscriptImport(state), isUploadError: selectors.requests.isFailed(state, { requestKey: RequestKeys.uploadTranscript }), isDeleteError: selectors.requests.isFailed(state, { requestKey: RequestKeys.deleteTranscript }), - isLibrary: selectors.app.isLibrary(state), }); export const mapDispatchToProps = (dispatch) => ({ 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 8dfc63d9fb..70eb6f7751 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 @@ -107,7 +107,6 @@ describe('TranscriptWidget', () => { updateField: jest.fn().mockName('args.updateField'), isUploadError: false, isDeleteError: false, - isLibrary: false, }; describe('snapshots', () => { @@ -156,11 +155,6 @@ describe('TranscriptWidget', () => { shallow().snapshot, ).toMatchSnapshot(); }); - test('snapshot: renders when isLibrary is true', () => { - expect( - shallow().snapshot, - ).toMatchSnapshot(); - }); }); describe('mapStateToProps', () => { const testState = { A: 'pple', B: 'anana', C: 'ucumber' };