diff options
Diffstat (limited to 'spec/frontend/static_site_editor/rich_content_editor/services/build_html_to_markdown_renderer_spec.js')
-rw-r--r-- | spec/frontend/static_site_editor/rich_content_editor/services/build_html_to_markdown_renderer_spec.js | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/spec/frontend/static_site_editor/rich_content_editor/services/build_html_to_markdown_renderer_spec.js b/spec/frontend/static_site_editor/rich_content_editor/services/build_html_to_markdown_renderer_spec.js new file mode 100644 index 00000000000..c9cba3e8689 --- /dev/null +++ b/spec/frontend/static_site_editor/rich_content_editor/services/build_html_to_markdown_renderer_spec.js @@ -0,0 +1,218 @@ +import buildHTMLToMarkdownRenderer from '~/static_site_editor/rich_content_editor/services/build_html_to_markdown_renderer'; +import { attributeDefinition } from './renderers/mock_data'; + +describe('rich_content_editor/services/html_to_markdown_renderer', () => { + let baseRenderer; + let htmlToMarkdownRenderer; + let fakeNode; + + beforeEach(() => { + baseRenderer = { + trim: jest.fn((input) => `trimmed ${input}`), + getSpaceCollapsedText: jest.fn((input) => `space collapsed ${input}`), + getSpaceControlled: jest.fn((input) => `space controlled ${input}`), + convert: jest.fn(), + }; + + fakeNode = { nodeValue: 'mock_node', dataset: {} }; + }); + + afterEach(() => { + htmlToMarkdownRenderer = null; + }); + + describe('TEXT_NODE visitor', () => { + it('composes getSpaceControlled, getSpaceCollapsedText, and trim services', () => { + htmlToMarkdownRenderer = buildHTMLToMarkdownRenderer(baseRenderer); + + expect(htmlToMarkdownRenderer.TEXT_NODE(fakeNode)).toBe( + `space controlled trimmed space collapsed ${fakeNode.nodeValue}`, + ); + }); + }); + + describe('LI OL, LI UL visitor', () => { + const oneLevelNestedList = '\n * List item 1\n * List item 2'; + const twoLevelNestedList = '\n * List item 1\n * List item 2'; + const spaceInContentList = '\n * List item 1\n * List item 2'; + + it.each` + list | indentSpaces | result + ${oneLevelNestedList} | ${2} | ${'\n * List item 1\n * List item 2'} + ${oneLevelNestedList} | ${3} | ${'\n * List item 1\n * List item 2'} + ${oneLevelNestedList} | ${6} | ${'\n * List item 1\n * List item 2'} + ${twoLevelNestedList} | ${4} | ${'\n * List item 1\n * List item 2'} + ${spaceInContentList} | ${1} | ${'\n * List item 1\n * List item 2'} + `('changes the list indentation to $indentSpaces spaces', ({ list, indentSpaces, result }) => { + htmlToMarkdownRenderer = buildHTMLToMarkdownRenderer(baseRenderer, { + subListIndentSpaces: indentSpaces, + }); + + baseRenderer.convert.mockReturnValueOnce(list); + + expect(htmlToMarkdownRenderer['LI OL, LI UL'](fakeNode, list)).toBe(result); + expect(baseRenderer.convert).toHaveBeenCalledWith(fakeNode, list); + }); + }); + + describe('UL LI visitor', () => { + it.each` + listItem | unorderedListBulletChar | result | bulletChar + ${'* list item'} | ${undefined} | ${'- list item'} | ${'default'} + ${' - list item'} | ${'*'} | ${' * list item'} | ${'*'} + ${' * list item'} | ${'-'} | ${' - list item'} | ${'-'} + `( + 'uses $bulletChar bullet char in unordered list items when $unorderedListBulletChar is set in config', + ({ listItem, unorderedListBulletChar, result }) => { + htmlToMarkdownRenderer = buildHTMLToMarkdownRenderer(baseRenderer, { + unorderedListBulletChar, + }); + baseRenderer.convert.mockReturnValueOnce(listItem); + + expect(htmlToMarkdownRenderer['UL LI'](fakeNode, listItem)).toBe(result); + expect(baseRenderer.convert).toHaveBeenCalledWith(fakeNode, listItem); + }, + ); + + it('detects attribute definitions and attaches them to the list item', () => { + const listItem = '- list item'; + const result = `${listItem}\n${attributeDefinition}\n`; + + fakeNode.dataset.attributeDefinition = attributeDefinition; + htmlToMarkdownRenderer = buildHTMLToMarkdownRenderer(baseRenderer); + baseRenderer.convert.mockReturnValueOnce(`${listItem}\n`); + + expect(htmlToMarkdownRenderer['UL LI'](fakeNode, listItem)).toBe(result); + }); + }); + + describe('OL LI visitor', () => { + it.each` + listItem | result | incrementListMarker | action + ${'2. list item'} | ${'1. list item'} | ${false} | ${'increments'} + ${' 3. list item'} | ${' 1. list item'} | ${false} | ${'increments'} + ${' 123. list item'} | ${' 1. list item'} | ${false} | ${'increments'} + ${'3. list item'} | ${'3. list item'} | ${true} | ${'does not increment'} + `( + '$action a list item counter when incrementListMaker is $incrementListMarker', + ({ listItem, result, incrementListMarker }) => { + const subContent = null; + + htmlToMarkdownRenderer = buildHTMLToMarkdownRenderer(baseRenderer, { + incrementListMarker, + }); + baseRenderer.convert.mockReturnValueOnce(listItem); + + expect(htmlToMarkdownRenderer['OL LI'](fakeNode, subContent)).toBe(result); + expect(baseRenderer.convert).toHaveBeenCalledWith(fakeNode, subContent); + }, + ); + }); + + describe('STRONG, B visitor', () => { + it.each` + input | strongCharacter | result + ${'**strong text**'} | ${'_'} | ${'__strong text__'} + ${'__strong text__'} | ${'*'} | ${'**strong text**'} + `( + 'converts $input to $result when strong character is $strongCharacter', + ({ input, strongCharacter, result }) => { + htmlToMarkdownRenderer = buildHTMLToMarkdownRenderer(baseRenderer, { + strong: strongCharacter, + }); + + baseRenderer.convert.mockReturnValueOnce(input); + + expect(htmlToMarkdownRenderer['STRONG, B'](fakeNode, input)).toBe(result); + expect(baseRenderer.convert).toHaveBeenCalledWith(fakeNode, input); + }, + ); + }); + + describe('EM, I visitor', () => { + it.each` + input | emphasisCharacter | result + ${'*strong text*'} | ${'_'} | ${'_strong text_'} + ${'_strong text_'} | ${'*'} | ${'*strong text*'} + `( + 'converts $input to $result when emphasis character is $emphasisCharacter', + ({ input, emphasisCharacter, result }) => { + htmlToMarkdownRenderer = buildHTMLToMarkdownRenderer(baseRenderer, { + emphasis: emphasisCharacter, + }); + + baseRenderer.convert.mockReturnValueOnce(input); + + expect(htmlToMarkdownRenderer['EM, I'](fakeNode, input)).toBe(result); + expect(baseRenderer.convert).toHaveBeenCalledWith(fakeNode, input); + }, + ); + }); + + describe('H1, H2, H3, H4, H5, H6 visitor', () => { + it('detects attribute definitions and attaches them to the heading', () => { + const heading = 'heading text'; + const result = `${heading.trimRight()}\n${attributeDefinition}\n\n`; + + fakeNode.dataset.attributeDefinition = attributeDefinition; + htmlToMarkdownRenderer = buildHTMLToMarkdownRenderer(baseRenderer); + baseRenderer.convert.mockReturnValueOnce(`${heading}\n\n`); + + expect(htmlToMarkdownRenderer['H1, H2, H3, H4, H5, H6'](fakeNode, heading)).toBe(result); + }); + }); + + describe('PRE CODE', () => { + let node; + const subContent = 'sub content'; + const originalConverterResult = 'base result'; + + beforeEach(() => { + node = document.createElement('PRE'); + + node.innerText = 'reference definition content'; + node.dataset.sseReferenceDefinition = true; + + baseRenderer.convert.mockReturnValueOnce(originalConverterResult); + htmlToMarkdownRenderer = buildHTMLToMarkdownRenderer(baseRenderer); + }); + + it('returns raw text when pre node has sse-reference-definitions class', () => { + expect(htmlToMarkdownRenderer['PRE CODE'](node, subContent)).toBe( + `\n\n${node.innerText}\n\n`, + ); + }); + + it('returns base result when pre node does not have sse-reference-definitions class', () => { + delete node.dataset.sseReferenceDefinition; + + expect(htmlToMarkdownRenderer['PRE CODE'](node, subContent)).toBe(originalConverterResult); + }); + }); + + describe('IMG', () => { + const originalSrc = 'path/to/image.png'; + const alt = 'alt text'; + let node; + + beforeEach(() => { + node = document.createElement('img'); + node.alt = alt; + node.src = originalSrc; + }); + + it('returns an image with its original src of the `original-src` attribute is preset', () => { + node.dataset.originalSrc = originalSrc; + node.src = 'modified/path/to/image.png'; + + htmlToMarkdownRenderer = buildHTMLToMarkdownRenderer(baseRenderer); + + expect(htmlToMarkdownRenderer.IMG(node)).toBe(`![${alt}](${originalSrc})`); + }); + + it('fallback to `src` if no `original-src` is specified on the image', () => { + htmlToMarkdownRenderer = buildHTMLToMarkdownRenderer(baseRenderer); + expect(htmlToMarkdownRenderer.IMG(node)).toBe(`![${alt}](${originalSrc})`); + }); + }); +}); |