/* * SPDX-FileCopyrightText: 2020 The HedgeDoc developers (see AUTHORS file) * * SPDX-License-Identifier: AGPL-3.0-only */ import { renderToStaticMarkup } from 'react-dom/server' import { convertHtmlToReact, ParserOptions } from './convertHtmlToReact.js' import { convertNodeToReactElement } from './convertNodeToReactElement.js' import { Document, isTag, isText } from 'domhandler' import { NodeToReactElementTransformer } from './NodeToReactElementTransformer.js' import React, { ReactElement } from 'react' import { describe, expect, it } from '@jest/globals' const expectSameHtml = function (html: string, options: ParserOptions = {}) { const actual = renderToStaticMarkup(
{convertHtmlToReact(html, options)}
) const expected = `
${html}
` expect(actual).toBe(expected) } const expectOtherHtml = function (html: string, override: string, options: ParserOptions = {}) { const actual = renderToStaticMarkup(
{convertHtmlToReact(html, options)}
) const expected = `
${override}
` expect(actual).toBe(expected) } describe('Integration tests: ', () => { it('should render a simple element', () => { expectSameHtml('
test
') }) it('should render multiple sibling elements', () => { expectSameHtml('
test1
test2') }) it('should render nested elements', () => { expectSameHtml('
test1
') }) it('should handle bad html', () => { expectOtherHtml( '
testtest
', '
testtest
' ) }) it('should ignore doctypes', () => { expectOtherHtml('
test
', '
test
') }) it('should ignore comments', () => { expectOtherHtml('
test1
test2
', '
test1
test2
') }) it('should ignore script tags', () => { expectOtherHtml('', '') }) it('should ignore event handlers', () => { expectOtherHtml('test', 'test') }) it('should handle attributes', () => { expectSameHtml('
test
') }) it('should handle inline styles', () => { expectSameHtml('
test
') }) it('should ignore inline styles that are empty strings', () => { expectOtherHtml('
test
', '
test
') }) it('should not allow nesting of void elements', () => { expectOtherHtml('

test

', '

test

') }) it('should convert boolean attribute values', () => { expectOtherHtml('', '') expectOtherHtml('', '') expectOtherHtml('', '') }) ;[ ['CONTENTEDITABLE', 'contentEditable'], ['LABEL', 'label'], ['iTemREF', 'itemRef'] ].forEach(([attr, prop]) => { it(`should convert attribute ${attr} to prop ${prop}`, () => { const nodes = convertHtmlToReact(`
`, {}) expect(nodes).toHaveLength(1) expect((nodes[0] as ReactElement).props).toHaveProperty(prop) }) }) it('should decode html entities by default', () => { expectOtherHtml('!', '!') }) it('should not decode html entities when the option is disabled', () => { expectOtherHtml('!', '!', { decodeEntities: false }) }) describe('transform function', () => { it('should use the response when it is not undefined', () => { expectOtherHtml('test
another
', '

transformed

transformed

', { transform(node, index) { return

transformed

} }) }) it('should not render elements and children when returning null', () => { expectOtherHtml('

testinner testbold child

', '

test

', { transform(node) { if (isTag(node) && node.type === 'tag' && node.name === 'span') { return null } } }) }) it('should allow modifying nodes', () => { expectOtherHtml('test link', 'test link', { transform(node, index) { if (isTag(node)) { node.attribs.href = '/changed' } return convertNodeToReactElement(node, index) } }) }) it('should allow passing the transform function down to children', () => { const transform: NodeToReactElementTransformer = (node, index) => { if (isTag(node)) { if (node.name === 'ul') { node.attribs.class = 'test' return convertNodeToReactElement(node, index, transform) } } else if (isText(node)) { return node.data.replace(/list/, 'changed') } else { return null } } expectOtherHtml( '', '', { transform } ) }) }) it('should not render invalid tags', () => { expectOtherHtml('
test', '
test
') }) it('should not render invalid attributes', () => { expectOtherHtml('
content
', '
content
') }) it('should preprocess nodes correctly', () => { expectOtherHtml('
preprocess test
', '
preprocess test
preprocess test
', { preprocessNodes(document) { return new Document([...document.childNodes, ...document.childNodes]) } }) }) })