From 8833f7bfca605330d9a9b220274c035de6e4f240 Mon Sep 17 00:00:00 2001 From: Jesper Hodge <19345795+jesperhodge@users.noreply.github.com> Date: Thu, 4 Jan 2024 14:36:07 -0500 Subject: [PATCH] fix: library editor not working locally (#448) Internal issue: https://2u-internal.atlassian.net/browse/TNL-11299 In library-authoring, content blocks could not be edited locally due to some problem with the Accept header. This caused some 404s. Visually you would get an infinite loading spinner. This depends on openedx/frontend-app-library-authoring#399. --- src/editors/data/services/cms/api.js | 11 ++++ src/editors/data/services/cms/api.test.js | 77 +++++++++++++++++++---- 2 files changed, 75 insertions(+), 13 deletions(-) diff --git a/src/editors/data/services/cms/api.js b/src/editors/data/services/cms/api.js index b1e407ad6..b97ea81c1 100644 --- a/src/editors/data/services/cms/api.js +++ b/src/editors/data/services/cms/api.js @@ -5,12 +5,23 @@ import * as module from './api'; import * as mockApi from './mockApi'; import { durationStringFromValue } from '../../../containers/VideoEditor/components/VideoSettingsModal/components/DurationWidget/hooks'; +const fetchByUnitIdOptions = {}; + +// For some reason, the local webpack-dev-server of library-authoring does not accept the normal Accept header. +// This is a workaround only for that specific case; the idea is to only do this locally and only for library-authoring. +if (process.env.NODE_ENV === 'development' && process.env.MFE_NAME === 'frontend-app-library-authoring') { + fetchByUnitIdOptions.headers = { + Accept: '*/*', + }; +} + export const apiMethods = { fetchBlockById: ({ blockId, studioEndpointUrl }) => get( urls.block({ blockId, studioEndpointUrl }), ), fetchByUnitId: ({ blockId, studioEndpointUrl }) => get( urls.blockAncestor({ studioEndpointUrl, blockId }), + fetchByUnitIdOptions, ), fetchStudioView: ({ blockId, studioEndpointUrl }) => get( urls.blockStudioView({ studioEndpointUrl, blockId }), diff --git a/src/editors/data/services/cms/api.test.js b/src/editors/data/services/cms/api.test.js index 289ec8ed9..2edb7ea7b 100644 --- a/src/editors/data/services/cms/api.test.js +++ b/src/editors/data/services/cms/api.test.js @@ -15,18 +15,18 @@ jest.mock('../../../utils', () => { }); jest.mock('./urls', () => ({ - block: jest.fn().mockName('urls.block'), - blockAncestor: jest.fn().mockName('urls.blockAncestor'), - blockStudioView: jest.fn().mockName('urls.StudioView'), - courseAssets: jest.fn().mockName('urls.courseAssets'), - videoTranscripts: jest.fn().mockName('urls.videoTranscripts'), - allowThumbnailUpload: jest.fn().mockName('urls.allowThumbnailUpload'), - thumbnailUpload: jest.fn().mockName('urls.thumbnailUpload'), - checkTranscriptsForImport: jest.fn().mockName('urls.checkTranscriptsForImport'), - courseDetailsUrl: jest.fn().mockName('urls.courseDetailsUrl'), - courseAdvanceSettings: jest.fn().mockName('urls.courseAdvanceSettings'), - replaceTranscript: jest.fn().mockName('urls.replaceTranscript'), - videoFeatures: jest.fn().mockName('urls.videoFeatures'), + block: jest.fn().mockReturnValue('urls.block'), + blockAncestor: jest.fn().mockReturnValue('urls.blockAncestor'), + blockStudioView: jest.fn().mockReturnValue('urls.StudioView'), + courseAssets: jest.fn().mockReturnValue('urls.courseAssets'), + videoTranscripts: jest.fn().mockReturnValue('urls.videoTranscripts'), + allowThumbnailUpload: jest.fn().mockReturnValue('urls.allowThumbnailUpload'), + thumbnailUpload: jest.fn().mockReturnValue('urls.thumbnailUpload'), + checkTranscriptsForImport: jest.fn().mockReturnValue('urls.checkTranscriptsForImport'), + courseDetailsUrl: jest.fn().mockReturnValue('urls.courseDetailsUrl'), + courseAdvanceSettings: jest.fn().mockReturnValue('urls.courseAdvanceSettings'), + replaceTranscript: jest.fn().mockReturnValue('urls.replaceTranscript'), + videoFeatures: jest.fn().mockReturnValue('urls.videoFeatures'), courseVideos: jest.fn() .mockName('urls.courseVideos') .mockImplementation( @@ -64,7 +64,58 @@ describe('cms api', () => { describe('fetchByUnitId', () => { it('should call get with url.blockAncestor', () => { apiMethods.fetchByUnitId({ blockId, studioEndpointUrl }); - expect(get).toHaveBeenCalledWith(urls.blockAncestor({ studioEndpointUrl, blockId })); + expect(get).toHaveBeenCalledWith(urls.blockAncestor({ studioEndpointUrl, blockId }), {}); + }); + + describe('when called in different contexts', () => { + // To mock env variables, you need to use dynamic imports for the tested methods + // and then reset the env variables afterwards. + const OLD_ENV = process.env; + + beforeEach(() => { + jest.resetModules(); + process.env = { ...OLD_ENV }; + }); + + afterEach(() => { + process.env = OLD_ENV; + }); + + it('should call get with normal accept header for prod', async () => { + process.env.NODE_ENV = 'production'; + process.env.MFE_NAME = 'frontend-app-library-authoring'; + // eslint-disable-next-line no-shadow + const { apiMethods } = await import('./api'); + // eslint-disable-next-line no-shadow + const utils = await import('./utils'); + const getSpy = jest.spyOn(utils, 'get'); + apiMethods.fetchByUnitId({ blockId, studioEndpointUrl }); + expect(getSpy).toHaveBeenCalledWith(urls.blockAncestor({ studioEndpointUrl, blockId }), {}); + }); + + it('should call get with normal accept header for course-authoring', async () => { + process.env.NODE_ENV = 'development'; + process.env.MFE_NAME = 'frontend-app-course-authoring'; + // eslint-disable-next-line no-shadow + const { apiMethods } = await import('./api'); + // eslint-disable-next-line no-shadow + const utils = await import('./utils'); + const getSpy = jest.spyOn(utils, 'get'); + apiMethods.fetchByUnitId({ blockId, studioEndpointUrl }); + expect(getSpy).toHaveBeenCalledWith(urls.blockAncestor({ studioEndpointUrl, blockId }), {}); + }); + + it('should call get with special accept header "*/*" for course-authoring', async () => { + process.env.NODE_ENV = 'development'; + process.env.MFE_NAME = 'frontend-app-library-authoring'; + // eslint-disable-next-line no-shadow + const { apiMethods } = await import('./api'); + // eslint-disable-next-line no-shadow + const utils = await import('./utils'); + const getSpy = jest.spyOn(utils, 'get'); + apiMethods.fetchByUnitId({ blockId, studioEndpointUrl }); + expect(getSpy).toHaveBeenCalledWith(urls.blockAncestor({ studioEndpointUrl, blockId }), { headers: { Accept: '*/*' } }); + }); }); });