Commit a59face3 authored by Enrique Alcantara's avatar Enrique Alcantara

Fix text escaping issues in Static Site Editor

The Static Site Editor underlying Rich Content Editor
incorrectly escapes text in Markdown document. This
commit removes all the incorrect text escaping calls
parent e5107a23
......@@ -12,6 +12,7 @@ import {
} from './constants';
import {
registerHTMLToMarkdownRenderer,
addCustomEventListener,
removeCustomEventListener,
addImage,
......@@ -87,6 +88,7 @@ export default {
onLoad(editorApi) {
this.editorApi = editorApi;
registerHTMLToMarkdownRenderer(editorApi);
addCustomEventListener(
this.editorApi,
CUSTOM_EVENTS.openAddImageModal,
......
const buildHTMLToMarkdownRender = baseRenderer => {
return {
TEXT_NODE(node) {
return baseRenderer.getSpaceControlled(
baseRenderer.trim(baseRenderer.getSpaceCollapsedText(node.nodeValue)),
node,
);
},
};
};
export default buildHTMLToMarkdownRender;
import Vue from 'vue';
import ToolbarItem from '../toolbar_item.vue';
import buildHtmlToMarkdownRenderer from './build_html_to_markdown_renderer';
const buildWrapper = propsData => {
const instance = new Vue({
......@@ -40,3 +41,16 @@ export const removeCustomEventListener = (editorApi, event, handler) =>
export const addImage = ({ editor }, image) => editor.exec('AddImage', image);
export const getMarkdown = editorInstance => editorInstance.invoke('getMarkdown');
/**
* This function allow us to extend Toast UI HTML to Markdown renderer. It is
* a temporary measure because Toast UI does not provide an API
* to achieve this goal.
*/
export const registerHTMLToMarkdownRenderer = editorApi => {
const { renderer } = editorApi.toMarkOptions;
Object.assign(editorApi.toMarkOptions, {
renderer: renderer.constructor.factory(renderer, buildHtmlToMarkdownRenderer(renderer)),
});
};
---
title: Fix incorrect text escaping in the Static Site Editor
merge_request: 35671
author:
type: fixed
......@@ -2,18 +2,35 @@ import {
generateToolbarItem,
addCustomEventListener,
removeCustomEventListener,
registerHTMLToMarkdownRenderer,
addImage,
getMarkdown,
} from '~/vue_shared/components/rich_content_editor/services/editor_service';
import buildHTMLToMarkdownRenderer from '~/vue_shared/components/rich_content_editor/services/build_html_to_markdown_renderer';
jest.mock('~/vue_shared/components/rich_content_editor/services/build_html_to_markdown_renderer');
describe('Editor Service', () => {
const mockInstance = {
eventManager: { addEventType: jest.fn(), removeEventHandler: jest.fn(), listen: jest.fn() },
editor: { exec: jest.fn() },
invoke: jest.fn(),
};
const event = 'someCustomEvent';
const handler = jest.fn();
let mockInstance;
let event;
let handler;
beforeEach(() => {
mockInstance = {
eventManager: { addEventType: jest.fn(), removeEventHandler: jest.fn(), listen: jest.fn() },
editor: { exec: jest.fn() },
invoke: jest.fn(),
toMarkOptions: {
renderer: {
constructor: {
factory: jest.fn(),
},
},
},
};
event = 'someCustomEvent';
handler = jest.fn();
});
describe('generateToolbarItem', () => {
const config = {
......@@ -74,4 +91,33 @@ describe('Editor Service', () => {
expect(mockInstance.invoke).toHaveBeenCalledWith('getMarkdown');
});
});
describe('registerHTMLToMarkdownRenderer', () => {
let baseRenderer;
const htmlToMarkdownRenderer = {};
const extendedRenderer = {};
beforeEach(() => {
baseRenderer = mockInstance.toMarkOptions.renderer;
buildHTMLToMarkdownRenderer.mockReturnValueOnce(htmlToMarkdownRenderer);
baseRenderer.constructor.factory.mockReturnValueOnce(extendedRenderer);
registerHTMLToMarkdownRenderer(mockInstance);
});
it('builds a new instance of the HTML to Markdown renderer', () => {
expect(buildHTMLToMarkdownRenderer).toHaveBeenCalledWith(baseRenderer);
});
it('extends base renderer with the HTML to Markdown renderer', () => {
expect(baseRenderer.constructor.factory).toHaveBeenCalledWith(
baseRenderer,
htmlToMarkdownRenderer,
);
});
it('replaces the default renderer with extended renderer', () => {
expect(mockInstance.toMarkOptions.renderer).toBe(extendedRenderer);
});
});
});
......@@ -13,6 +13,7 @@ import {
addCustomEventListener,
removeCustomEventListener,
addImage,
registerHTMLToMarkdownRenderer,
} from '~/vue_shared/components/rich_content_editor/services/editor_service';
jest.mock('~/vue_shared/components/rich_content_editor/services/editor_service', () => ({
......@@ -20,6 +21,7 @@ jest.mock('~/vue_shared/components/rich_content_editor/services/editor_service',
addCustomEventListener: jest.fn(),
removeCustomEventListener: jest.fn(),
addImage: jest.fn(),
registerHTMLToMarkdownRenderer: jest.fn(),
}));
describe('Rich Content Editor', () => {
......@@ -86,16 +88,24 @@ describe('Rich Content Editor', () => {
});
describe('when editor is loaded', () => {
it('adds the CUSTOM_EVENTS.openAddImageModal custom event listener', () => {
const mockEditorApi = { eventManager: { addEventType: jest.fn(), listen: jest.fn() } };
let mockEditorApi;
beforeEach(() => {
mockEditorApi = { eventManager: { addEventType: jest.fn(), listen: jest.fn() } };
findEditor().vm.$emit('load', mockEditorApi);
});
it('adds the CUSTOM_EVENTS.openAddImageModal custom event listener', () => {
expect(addCustomEventListener).toHaveBeenCalledWith(
mockEditorApi,
CUSTOM_EVENTS.openAddImageModal,
wrapper.vm.onOpenAddImageModal,
);
});
it('registers HTML to markdown renderer', () => {
expect(registerHTMLToMarkdownRenderer).toHaveBeenCalledWith(mockEditorApi);
});
});
describe('when editor is destroyed', () => {
......
import buildHTMLToMarkdownRenderer from '~/vue_shared/components/rich_content_editor/services/build_html_to_markdown_renderer';
describe('HTMLToMarkdownRenderer', () => {
let baseRenderer;
let htmlToMarkdownRenderer;
const NODE = { nodeValue: 'mock_node' };
beforeEach(() => {
baseRenderer = {
trim: jest.fn(input => `trimmed ${input}`),
getSpaceCollapsedText: jest.fn(input => `space collapsed ${input}`),
getSpaceControlled: jest.fn(input => `space controlled ${input}`),
};
htmlToMarkdownRenderer = buildHTMLToMarkdownRenderer(baseRenderer);
});
describe('TEXT_NODE visitor', () => {
it('composes getSpaceControlled, getSpaceCollapsedText, and trim services', () => {
expect(htmlToMarkdownRenderer.TEXT_NODE(NODE)).toBe(
`space controlled trimmed space collapsed ${NODE.nodeValue}`,
);
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment