gitlab-org--gitlab-foss/spec/frontend/static_site_editor/rich_content_editor/services/build_html_to_markdown_rend...

219 lines
8.4 KiB
JavaScript

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})`);
});
});
});