feat: change image absolute urls to relative urls

This commit is contained in:
Kristin Aoki
2022-08-31 12:03:07 -04:00
committed by GitHub
parent 2d427da80f
commit e5ea0a096c
7 changed files with 80 additions and 22 deletions

View File

@@ -11,11 +11,45 @@ export const {
saveBlock,
} = appHooks;
export const setAssetToStaticUrl = (images, getContent) => {
/* For assets to remain usable across course instances, we convert their url to be course-agnostic.
* For example, /assets/course/<asset hash>/filename gets converted to /static/filename. This is
* important for rerunning courses and importing/exporting course as the /static/ part of the url
* allows the asset to be mapped to the new course run.
*/
let content = getContent();
const imageUrls = [];
const imgsArray = Object.values(images);
imgsArray.forEach(image => {
imageUrls.push({ portableUrl: image.portableUrl, displayName: image.displayName });
});
const imageSrcs = content.split('src="');
imageSrcs.forEach(src => {
if (src.startsWith('/asset') && imageUrls.length > 0) {
const nameFromEditorSrc = src.substring(src.lastIndexOf('@') + 1, src.indexOf('"'));
const nameFromStudioSrc = nameFromEditorSrc.substring(nameFromEditorSrc.indexOf('/') + 1);
let portableUrl;
imageUrls.forEach((url) => {
if (url.displayName === nameFromEditorSrc || url.displayName === nameFromStudioSrc) {
portableUrl = url.portableUrl;
}
});
if (portableUrl) {
const currentSrc = src.substring(0, src.indexOf('"'));
const updatedContent = content.replace(currentSrc, portableUrl);
content = updatedContent;
}
}
});
return content;
};
export const handleSaveClicked = ({ getContent, dispatch }) => {
const destination = useSelector(selectors.app.returnUrl);
const analytics = useSelector(selectors.app.analytics);
const images = useSelector(selectors.app.images);
return () => saveBlock({
content: getContent(),
content: setAssetToStaticUrl(images, getContent),
destination,
analytics,
dispatch,

View File

@@ -11,6 +11,7 @@ jest.mock('../../data/redux', () => ({
selectors: {
app: {
isInitialized: (state) => ({ isInitialized: state }),
images: (state) => ({ images: state }),
},
requests: {
isFailed: (...args) => ({ requestFailed: args }),
@@ -25,6 +26,20 @@ jest.mock('../../hooks', () => ({
const dispatch = jest.fn();
describe('EditorContainer hooks', () => {
describe('non-state hooks', () => {
describe('replaceStaticwithAsset', () => {
it('returns content with updated img links', () => {
const getContent = jest.fn(() => '<img src="/asset@asset-block/soMEImagEURl1"/> <img src="/asset@soMEImagEURl" />');
const images = [
{ portableUrl: '/static/soMEImagEURl', displayName: 'soMEImagEURl' },
{ portableUrl: '/static/soMEImagEURl1', displayName: 'soMEImagEURl1' },
];
const content = hooks.setAssetToStaticUrl(images, getContent);
expect(getContent).toHaveBeenCalled();
expect(content).toEqual('<img src="/static/soMEImagEURl1"/> <img src="/static/soMEImagEURl" />');
});
});
});
describe('forwarded hooks', () => {
it('forwards navigateCallback from app hooks', () => {
expect(hooks.navigateCallback).toEqual(appHooks.navigateCallback);
@@ -41,17 +56,19 @@ describe('EditorContainer hooks', () => {
jest.clearAllMocks();
});
describe('handleSaveClicked', () => {
it('returns callback to saveBlock with dispatch and content from getContent', () => {
it('returns callback to saveBlock with dispatch and content from setAssetToStaticUrl', () => {
const getContent = () => 'myTestContentValue';
const setAssetToStaticUrl = () => 'myTestContentValue';
const output = hooks.handleSaveClicked({
getContent,
images: { portableUrl: '/static/sOmEuiMAge.jpeg', displayName: 'sOmEuiMAge' },
destination: 'testDEsTURL',
analytics: 'soMEanALytics',
dispatch,
});
output();
expect(appHooks.saveBlock).toHaveBeenCalledWith({
content: getContent(),
content: setAssetToStaticUrl(reactRedux.useSelector(selectors.app.images), getContent),
destination: reactRedux.useSelector(selectors.app.returnUrl),
analytics: reactRedux.useSelector(selectors.app.analytics),
dispatch,

View File

@@ -11,7 +11,7 @@ export const propsString = (props) => (
);
export const imgProps = ({ settings, selection }) => ({
src: selection.externalUrl,
src: selection.url,
alt: settings.isDecorative ? '' : settings.altText,
width: settings.dimensions.width,
height: settings.dimensions.height,

View File

@@ -25,9 +25,9 @@ const settings = {
describe('ImageUploadModal', () => {
describe('hooks', () => {
describe('imgTag', () => {
const selection = { externalUrl: 'sOmEuRl.cOm' };
const selection = { url: 'sOmEuRl.cOm' };
const expected = {
src: selection.externalUrl,
src: selection.url,
alt: settings.altText,
width: settings.dimensions.width,
height: settings.dimensions.height,

View File

@@ -62,19 +62,21 @@ export const setupCustomBehavior = ({
export const replaceStaticwithAsset = (editor, imageUrls) => {
const content = editor.getContent();
const imageSrcs = content.split('img src="');
const imageSrcs = content.split('src="');
imageSrcs.forEach(src => {
if (src.startsWith('/static/') && imageUrls.length > 0) {
const imgName = src.substring(8, src.indexOf('"'));
let staticFullUrl;
imageUrls.forEach((url) => {
if (url.includes(imgName)) {
staticFullUrl = url;
if (imgName === url.displayName) {
staticFullUrl = url.staticFullUrl;
}
});
const currentSrc = src.substring(0, src.indexOf('"'));
const updatedContent = content.replace(currentSrc, staticFullUrl);
editor.setContent(updatedContent);
if (staticFullUrl) {
const currentSrc = src.substring(0, src.indexOf('"'));
const updatedContent = content.replace(currentSrc, staticFullUrl);
editor.setContent(updatedContent);
}
}
});
};
@@ -182,7 +184,7 @@ export const fetchImageUrls = (images) => {
const imageUrls = [];
const imgsArray = Object.values(images);
imgsArray.forEach(image => {
imageUrls.push(image.staticFullUrl);
imageUrls.push({ staticFullUrl: image.staticFullUrl, displayName: image.displayName });
});
return imageUrls;
};

View File

@@ -87,18 +87,22 @@ describe('TextEditor hooks', () => {
});
describe('replaceStaticwithAsset', () => {
const editor = { getContent: jest.fn(() => '<img src="/static/soMEImagEURl1.jpeg"/>'), setContent: jest.fn() };
const imageUrls = ['soMEImagEURl1.jpeg'];
module.replaceStaticwithAsset(editor, imageUrls);
expect(editor.getContent).toHaveBeenCalled();
expect(editor.setContent).toHaveBeenCalled();
test('it calls getContent and setContent', () => {
const editor = { getContent: jest.fn(() => '<img src="/static/soMEImagEURl1.jpeg"/>'), setContent: jest.fn() };
const imageUrls = [{ staticFullUrl: '/assets/soMEImagEURl1.jpeg', displayName: 'soMEImagEURl1.jpeg' }];
module.replaceStaticwithAsset(editor, imageUrls);
expect(editor.getContent).toHaveBeenCalled();
expect(editor.setContent).toHaveBeenCalled();
});
});
describe('checkRelativeUrl', () => {
const editor = { on: jest.fn() };
const imageUrls = ['soMEImagEURl1'];
module.checkRelativeUrl(imageUrls)(editor);
expect(editor.on).toHaveBeenCalled();
test('it calls editor.on', () => {
const editor = { on: jest.fn() };
const imageUrls = ['soMEImagEURl1'];
module.checkRelativeUrl(imageUrls)(editor);
expect(editor.on).toHaveBeenCalled();
});
});
describe('editorConfig', () => {

View File

@@ -42,6 +42,7 @@ describe('app selectors unit tests', () => {
simpleKeys.learningContextId,
simpleKeys.editorInitialized,
simpleKeys.saveResponse,
simpleKeys.lmsEndpointUrl,
simpleKeys.studioEndpointUrl,
simpleKeys.unitUrl,
simpleKeys.blockTitle,