# SPDX-FileCopyrightText: © 2023 Christian BUHTZ <c.buhtz@posteo.jp>
#
# SPDX-License-Identifier: GPL-3.0-only
#
# This file is part of the program "Hyperorg" which is released under GNU
# General Public License v3 (GPLv3).
# See folder LICENSES or go to <https://www.gnu.org/licenses/#GPL>.
"""
Tests about generating HTML from elements, e.g. via using
_paragraph_to_html().
"""
import unittest
import html
from hyperorg import elements, exporter, textruns
from hyperorg.content import Node
from tests.helper import unify_html


def _reset_node_class():
    Node.nodes = {}
    Node.heading_ids = {}
    Node.input_dir = 'input_dir'
    exporter.Exporter.output_dir = 'output_dir'


class ParagraphToHtml(unittest.TestCase):
    """Test HTML from paragraphs"""
    # pylint: disable=protected-access

    def setUp(self):
        _reset_node_class()

    def test_links(self):
        """Paragraph with links"""
        self.maxDiff = None
        Node.nodes['123'] = Node([], 'bar_123.org')
        Node.nodes['456'] = Node([], 'foo_456.org')

        paragraph = elements.Paragraph([
            'Governments of the [[roam:Industrial World][IW]], you',
            '[[roam:weary giants]] of [[id:123][flesh]] and [[id:456]]',
            'steel, [[https://icke.world][I]] come',
            'from [[https://Cyberspace.net]], the new home of Mind.'])

        expect = '<p>Governments of the <span title="The link destination ' \
            'could not be found.&#xA;Unknown ROAM &quot;Industrial ' \
            'World&quot;.&#xA;Original link: [[roam:Industrial World][IW]">' \
            '👾<span class="invalid-link">IW</span></span>, you ' \
            '<span title="The link destination could not be found.' \
            '&#xA;Unknown ROAM &quot;weary giants&quot;.&#xA;Original ' \
            'link: [[roam:weary giants]]">👾<span class="invalid-link">' \
            'weary giants</span></span> of ' \
            '<a href="bar_123.html">flesh</a> and ' \
            '<a href="foo_456.html">456</a> steel, ' \
            '<a target="_blank" rel="noopener noreferrer" ' \
            'href="https://icke.world">I</a> come ' \
            'from <a target="_blank" rel="noopener noreferrer" ' \
            'href="https://Cyberspace.net">https://Cyberspace.net</a>' \
            ', the new home of Mind.</p>'

        self.assertEqual(
            unify_html(exporter.Exporter._paragraph_to_html(paragraph)),
            unify_html(expect))

    def test_multiline(self):
        """Paragraph with inline markup."""

        self.maxDiff = None

        paragraph = elements.Paragraph([
            'Multiline verbatim =start here and will go to the next line',
            'after 79 chars and we still weit=.'
        ])
        expect = '<p>Multiline verbatim <code class="verbatim">start here ' \
                 'and will go to the ' \
                 'next line after 79 chars and we still weit</code>.</p>'

        self.assertEqual(exporter.Exporter._paragraph_to_html(paragraph),
                         expect)

    def test_simple(self):
        """Paragraph to html"""

        test_data = [
            (['eins'], '<p>eins</p>'),
            (['eins', 'zwei'], '<p>eins zwei</p>'),
        ]

        for lines, target_html in test_data:
            paragraph = elements.Paragraph(lines)

            generated_html = exporter.Exporter._paragraph_to_html(paragraph)
            self.assertEqual(generated_html, target_html)

    def test_verbatim_first_marker(self):
        """Verbatim beginning marker."""

        test_data = [
            # no char before first marker
            ('=example=', '<code class="verbatim">example</code>'),
            # space before first marker
            ('lore =foobar= ipsum',
             'lore <code class="verbatim">foobar</code> ipsum'),

        ]

        for markup, expect in test_data:
            paragraph = elements.Paragraph([markup])
            self.assertEqual(
                exporter.Exporter._paragraph_to_html(paragraph),
                '<p>' + expect + '</p>')

    def test_verbatim_end_marker(self):
        """Verbatim end marker."""

        test_data = [
            # space and word at the end
            ('=foobar= ipsum', '<code class="verbatim">foobar</code> ipsum'),
            # nothing at the end
            ('lore =foobar=', 'lore <code class="verbatim">foobar</code>'),
            # not allowed char at the end
            ('lore =foobar=x', 'lore =foobar=x'),
        ]

        for markup, expect in test_data:
            paragraph = elements.Paragraph([markup])
            self.assertEqual(
                exporter.Exporter._paragraph_to_html(paragraph),
                '<p>' + expect + '</p>')

    def test_verbatim_multiple_markers(self):
        """Verbatim with = in it."""
        markup = '=foo X=1 Y=abc punkt.='
        expect = '<p><code class="verbatim">foo X=1 Y=abc punkt.</code></p>'

        paragraph = elements.Paragraph([markup])
        self.assertEqual(exporter.Exporter._paragraph_to_html(paragraph),
                         expect)

    def test_multiple_markups(self):
        """Multiple markup types"""
        test_data = [
            (' _example_ ', '<p> <u>example</u> </p>'),
            ('Underline _example_.', '<p>Underline <u>example</u>.</p>'),
            (' *example* ', '<p> <strong>example</strong> </p>'),
            ('Bold *example*.', '<p>Bold <strong>example</strong>.</p>'),
            (' /example/ ', '<p> <em>example</em> </p>'),
            ('Italic /example/.', '<p>Italic <em>example</em>.</p>'),
            (' +example+ ', '<p> <del>example</del> </p>'),
            ('Code +example+.', '<p>Code <del>example</del>.</p>'),
            (' ~example~ ', '<p> <code>example</code> </p>'),
            ('Code ~example~.', '<p>Code <code>example</code>.</p>'),
            ('Verbatim =example=.',
             '<p>Verbatim <code class="verbatim">example</code>.</p>'),
            (' =example= ',
             '<p> <code class="verbatim">example</code> </p>')
        ]

        for markup, expect in test_data:
            paragraph = elements.Paragraph([markup])
            self.assertEqual(
                exporter.Exporter._paragraph_to_html(paragraph),
                expect)

    def test_three_markers(self):
        """Line with three markers."""
        markup = 'One line with three =markers= and there= .'
        expect = '<p>One line with three <code class="verbatim">markers' \
                 '</code> and there= .</p>'

        paragraph = elements.Paragraph([markup])
        self.assertEqual(exporter.Exporter._paragraph_to_html(paragraph),
                         expect)

    def test_non_markers(self):
        """Marker chars not interpreted."""
        markup = 'Line with two but no-marsker: n =7 and x=9 .'
        expect = '<p>Line with two but no-marsker: n =7 and x=9 .</p>'

        paragraph = elements.Paragraph([markup])
        self.assertEqual(exporter.Exporter._paragraph_to_html(paragraph),
                         expect)

    def test_endings(self):
        """Chars allowed after marker.

        To find a pair of markers the first marker need to have a blank space
        as prefix. But the second marker can have other chars instead of a
        space; e.g. a period to finish a sentence.
        """

        self.maxDiff = None

        line = 'Allowed is =space= and =period=. and =question=? and ' \
               '=exclamation=! and =round=) and =curley=} and =double ' \
               'quotation=" and =single quotation=\' and ' \
               '=double point=: and =semicolon=; and =comma=, ' \
               'and =minus=- that is how it is.'

        expect = '<p>Allowed is <code class="verbatim">space</code> ' \
                 'and <code class="verbatim">period</code>. and ' \
                 '<code class="verbatim">question</code>? ' \
                 'and <code class="verbatim">exclamation</code>! and ' \
                 '<code class="verbatim">round</code>) ' \
                 'and <code class="verbatim">curley</code>} ' \
                 'and <code class="verbatim">double quotation</code>&quot; ' \
                 'and <code class="verbatim">single quotation</code>&#x27; ' \
                 'and <code class="verbatim">double point</code>: ' \
                 'and <code class="verbatim">semicolon</code>; ' \
                 'and <code class="verbatim">comma</code>, ' \
                 'and <code class="verbatim">minus</code>- ' \
                 'that is how it is.</p>'

        paragraph = elements.Paragraph([line])
        self.assertEqual(exporter.Exporter._paragraph_to_html(paragraph),
                         expect)

    def test_endings_for_all(self):
        """Allowed Endings for all inline markers."""

        allowed_endings = ' .?!)}"\':;,-'
        inline_and_html = [
            ('=', 'code class="verbatim"'),
            ('~', 'code'),
            ('/', 'em'),
            ('*', 'strong'),
            ('_', 'u'),
            ('+', 'del')
        ]

        for marker, tag in inline_and_html:

            for ending in allowed_endings:

                orgraw = f'{marker}foo bar{marker}{ending}'
                ending = html.escape(ending)
                expect = f'<{tag}>foo bar</{tag.split()[0]}>{ending}'

                # no prefixed/trailing whitespace
                paragraph = elements.Paragraph([orgraw])
                self.assertEqual(
                    exporter.Exporter._paragraph_to_html(paragraph),
                    '<p>' + expect + '</p>')

                # prefixed whitespace
                paragraph = elements.Paragraph([f' {orgraw}'])
                self.assertEqual(
                    exporter.Exporter._paragraph_to_html(paragraph),
                    f'<p> {expect}</p>')

                # trailing whitespace
                paragraph = elements.Paragraph([f'{orgraw} '])
                self.assertEqual(
                    exporter.Exporter._paragraph_to_html(paragraph),
                    f'<p>{expect} </p>')

                # prefixed & trailing whitespace
                paragraph = elements.Paragraph([f' {orgraw} '])
                self.assertEqual(
                    exporter.Exporter._paragraph_to_html(paragraph),
                    f'<p> {expect} </p>')

    def test_beginnings_for_all(self):
        """Allowed Beginnings for all inline markers.

        Dev note: Orgmode itself seems to make a difference between allowed
        characters in the beginning and at the end of an inline marker. Not
        sure why. The dot is allowed at the end but not in the beginning for
        example. But I don't care about this and allow the same characters at
        beginning and end
        """

        allowed_begins = ' .?!({"\':;,-'
        inline_and_html = [
            ('=', 'code class="verbatim"'),
            ('~', 'code'),
            ('/', 'em'),
            ('*', 'strong'),
            ('_', 'u'),
            ('+', 'del')
        ]

        for marker, tag in inline_and_html:

            for begin in allowed_begins:

                orgraw = f'{begin}{marker}foo bar{marker}'
                begin = html.escape(begin)
                expect = f'{begin}<{tag}>foo bar</{tag.split()[0]}>'

                # no prefixed/trailing whitespace
                paragraph = elements.Paragraph([orgraw])
                self.assertEqual(
                    exporter.Exporter._paragraph_to_html(paragraph),
                    '<p>' + expect + '</p>')

                # prefixed whitespace
                paragraph = elements.Paragraph([f' {orgraw}'])
                self.assertEqual(
                    exporter.Exporter._paragraph_to_html(paragraph),
                    f'<p> {expect}</p>')

                # trailing whitespace
                paragraph = elements.Paragraph([f'{orgraw} '])
                self.assertEqual(
                    exporter.Exporter._paragraph_to_html(paragraph),
                    f'<p>{expect} </p>')

                # prefixed & trailing whitespace
                paragraph = elements.Paragraph([f' {orgraw} '])
                self.assertEqual(
                    exporter.Exporter._paragraph_to_html(paragraph),
                    f'<p> {expect} </p>')

    def test_verbatim_in_brakets(self):
        """Verbatim enclosded by brackets"""
        verbatim = 'Foo (=C-c n l=) bar'
        expect = '<p>Foo (<code class="verbatim">C-c n l</code>) bar</p>'

        paragraph = elements.Paragraph([verbatim])
        self.assertEqual(
            exporter.Exporter._paragraph_to_html(paragraph), expect)


class InlineImagesToHtml(unittest.TestCase):
    """More InlineImage stuff."""
    # pylint: disable=protected-access
    @classmethod
    def setUpClass(cls):
        def _mock_me(*args, **kwargs):  # pylint: disable=unused-argument
            pass
        cls._establish_file_link = exporter._establish_file_link
        exporter._establish_file_link = _mock_me

    @classmethod
    def tearDownClass(cls):
        exporter._establish_file_link = cls._establish_file_link

    def setUp(self):
        _reset_node_class()

    def test_simple(self):
        """Simple inline image."""
        sut = elements.Paragraph(['foo[[image.png]]bar'])

        self.assertEqual(
            exporter.Exporter._paragraph_to_html(sut),
            '<p>foo'
            '<img src="image.png" alt="image.png" />'
            'bar</p>'
        )

    def test_as_link_with_file_prefix(self):
        """Images as labels of Links are recognized with file: prefix"""
        sut = elements.Paragraph(['foo[[target.png][file:image.png]]bar'])

        self.assertEqual(
            exporter.Exporter._paragraph_to_html(sut),
            '<p>foo'
            '<a target="_blank" rel="noopener noreferrer" href="target.png">'
            '<img src="image.png" alt="image.png" />'
            '</a>bar</p>'
        )

    def test_as_link_with_https_prefix(self):
        """Images as labels of Links are recognized with https: prefix"""
        sut = elements.Paragraph(['foo[[target.png][https://image.png]]bar'])

        self.assertEqual(
            exporter.Exporter._paragraph_to_html(sut),
            '<p>foo'
            '<a target="_blank" rel="noopener noreferrer" href="target.png">'
            '<img src="https://image.png" alt="image.png" />'
            '</a>bar</p>'
        )

    def test_as_link_ignore_unsecure_http_prefix(self):
        """Images as link labels are ignored when from insecure HTTP source"""
        sut = elements.Paragraph(['foo[[target.png][http://image.png]]bar'])

        self.assertEqual(
            exporter.Exporter._paragraph_to_html(sut),
            '<p>foo'
            '<a target="_blank" rel="noopener noreferrer" href="target.png">'
            'http://image.png'
            '</a>bar</p>'
        )

    def test_as_link_ignore_without_prefix(self):
        """Images as link labels are ignored when missing "file:" or "https:"
        prefix"""
        sut = elements.Paragraph(['foo[[target.png][image.png]]bar'])

        self.assertEqual(
            exporter.Exporter._paragraph_to_html(sut),
            '<p>foo'
            '<a target="_blank" rel="noopener noreferrer" href="target.png">'
            'image.png'
            '</a>bar</p>'
        )


class ListElementToHtml(unittest.TestCase):
    """List Element to HTML"""
    # pylint: disable=protected-access

    def setUp(self):
        _reset_node_class()

    def test_simple_nested(self):
        """One list with a nested list"""
        listelem = elements.ListElement(item='- one', ordered=False)
        listelem.add_item('- two')
        listelem.add_item('  - blue')
        listelem.add_item('  - red')
        listelem.add_item('- three')

        expect = '''
            <ul>
                <li>one</li>
                <li>two
                    <ul>
                        <li>blue</li>
                        <li>red</li>
                    </ul></li>
                <li>three</li>
            </ul>'''

        self.assertEqual(
            unify_html(exporter.Exporter._list_to_html(listelem)),
            unify_html(expect))

    def test_complex(self):
        """Complex list (markup, links, markuped link-labels)"""
        self.maxDiff = None
        listelem = elements.ListElement(item='- one', ordered=False)
        listelem.add_item('- foo /two/')
        listelem.add_item('- blue [[https://bar.world][Bar]] link')
        listelem.add_item('- red [[https://bar.world][Big *Bar*]]')

        expect = '''
            <ul>
                <li>one</li>
                <li>foo <em>two</em></li>
                <li>blue <a target="_blank" rel="noopener noreferrer"
                    href="https://bar.world">Bar</a> link</li>
                <li>red <a target="_blank" rel="noopener noreferrer"
                    href="https://bar.world">Big <strong>Bar</strong></a></li>
            </ul>'''

        self.assertEqual(
            unify_html(exporter.Exporter._list_to_html(listelem)),
            unify_html(expect))

    def test_multiple_nested(self):
        """Some nestes lists."""
        test_data = [
            ([
                '- foo',
                '  - bar'],
             '''
             <ul>
                 <li>foo
                     <ul>
                         <li>bar</li>
                     </ul>
                </li>
             </ul>'''),
            (['- eins',
              '- zwei',
              '  - drei'],
             '''
             <ul>
                 <li>eins</li>
                 <li>zwei
                 <ul>
                 <li>drei</li>
                 </ul></li>
             </ul>'''),
            (['- eins',
              '     - zwei'],
             '''
             <ul>
                 <li>eins
                 <ul>
                     <li>zwei</li>
                 </ul></li>
             </ul>'''),
            (['- one',
              '- eins',
              '  - two',
              '  - zwei',
              '    - three',
              '  - foo'],
             '''
             <ul>
                 <li>one</li>
                 <li>eins
                     <ul>
                         <li>two</li>
                         <li>zwei
                             <ul>
                                 <li>three</li>
                             </ul></li>
                         <li>foo</li>
                     </ul>
                 </li>
             </ul>
             ''')
        ]

        for content, target_html in test_data:
            listelem = elements.ListElement(content[0], ordered=False)
            for item in content[1:]:
                listelem.add_item(item)

            generated_html = exporter.Exporter._list_to_html(listelem)

            self.assertEqual(
                unify_html(generated_html),
                unify_html(target_html))

    def test_inline_markup(self):
        """Inline markup."""

        self.maxDiff = None

        content = [
            '- example *bold*',
            '- =verbatim=',
            '- and ~code~ or',
            '- _underline_',
            '- +killme+',
            '- /emphasized/']
        listelem = elements.ListElement(content[0], ordered=False)
        for item in content[1:]:
            listelem.add_item(item)

        expected = '<ul>\n' \
            '<li>example <strong>bold</strong></li>\n' \
            '<li><code class="verbatim">verbatim</code></li>\n' \
            '<li>and <code>code</code> or</li>\n' \
            '<li><u>underline</u></li>\n' \
            '<li><del>killme</del></li>\n' \
            '<li><em>emphasized</em></li>\n' \
            '</ul>'

        generated_html = exporter.Exporter._list_to_html(listelem)
        self.assertEqual(generated_html, expected)

    def test_nested_inline_markup(self):
        """Nested list Inline markup."""

        self.maxDiff = None

        content = [
            '- example *bold*',
            '- =verbatim=',
            '  - and ~code~ or',
            '  - _underline_',
            '  - /emphasized/']
        listelem = elements.ListElement(content[0], ordered=False)
        for item in content[1:]:
            listelem.add_item(item)

        expected = '''
            <ul>
                <li>example <strong>bold</strong></li>
                <li><code class="verbatim">verbatim</code>
                <ul>
                    <li>and <code>code</code> or</li>
                    <li><u>underline</u></li>
                    <li><em>emphasized</em></li>
                </ul></li>
            </ul>'''

        generated_html = exporter.Exporter._list_to_html(listelem)
        self.assertEqual(unify_html(generated_html), unify_html(expected))

    def test_with_codeblock(self):
        """List with codeblock"""
        self.maxDiff = None

        # List with a code block in second item
        block = elements.Block(
                name='src', content_lines=['foo'], language='Language')
        listelem = elements.ListElement(item='- one', ordered=False)
        listelem.add_item('- two')
        listelem.append_to_last_item(block)

        expect = '''
            <ul>
                <li>one</li>
                <li>two
                    <pre title="src Language">
                         <span></span>
                         foo
                    </pre>
                </li>
            </ul>'''

        self.assertEqual(
            unify_html(exporter.Exporter._list_to_html(listelem), ''),
            unify_html(expect, ''))

    def test_with_two_codeblock(self):
        """List with two codeblock"""
        self.maxDiff = None

        # List with a code block in second item
        blocka = elements.Block(
            name='src', content_lines=['foo'], language='Language')
        blockb = elements.Block(
            name='src', content_lines=['bar'], language='Language')
        listelem = elements.ListElement(item='- one', ordered=False)
        listelem.add_item('- two')
        listelem.append_to_last_item(blocka)
        listelem.append_to_last_item('three')
        listelem.append_to_last_item(blockb)
        listelem.add_item('- four')

        expect = '''
            <ul>
                <li>one</li>
                <li>two
                    <pre title="src Language">
                         <span></span>
                         foo
                    </pre>
                    three
                    <pre title="src Language">
                         <span></span>
                         bar
                    </pre>
                </li>
                <li>four</li>
            </ul>'''

        self.assertEqual(
            unify_html(exporter.Exporter._list_to_html(listelem), ''),
            unify_html(expect, ''))


class HeadingToHtml(unittest.TestCase):
    """Heading elements to HTML"""

    # pylint: disable=protected-access,missing-function-docstring

    def setUp(self):
        _reset_node_class()

    def test_simple(self):
        heading = elements.Heading('* Heading')

        expect = '<h2>Heading</h2>'

        self.assertEqual(
            unify_html(exporter.Exporter._heading_to_html(heading)),
            unify_html(expect))

    def test_complex(self):
        heading = elements.Heading('* Heading /italic/ and +strikethrough+ '
                                   'extra [[https://target.net][link]]')

        expect = ('<h2>Heading <em>italic</em> and <del>strikethrough</del>'
                  ' extra <a target="_blank" rel="noopener noreferrer" '
                  'href="https://target.net">link</a></h2>')

        self.assertEqual(
            unify_html(exporter.Exporter._heading_to_html(heading)),
            unify_html(expect))

    def test_levels(self):
        # pylint: disable=consider-using-f-string
        test_data = [
            ('* eins', '<h2>eins</h2>'),
            ('** eins', '<h3>eins</h3>'),
            ('*** eins', '<h4>eins</h4>'),
            ('**** eins', '<h5>eins</h5>'),
            ('{} eins'.format('*' * 10), '<h11>eins</h11>'),
            ('{} eins'.format('*' * 50), '<h51>eins</h51>'),
            ('{} eins'.format('*' * 100), '<h101>eins</h101>'),
        ]

        for content, target_html in test_data:
            sut = elements.Heading(content)

            generated_html = exporter.Exporter._heading_to_html(sut)
            self.assertEqual(generated_html, target_html)

    def test_with_markdown_verbatim(self):
        sut = elements.Heading('* =--squash=')
        expect = '<h2><code class="verbatim">--squash</code></h2>'
        self.assertEqual(exporter.Exporter._heading_to_html(sut), expect)

    def test_with_markdown_important(self):
        sut = elements.Heading('* *--squash*')
        expect = '<h2><strong>--squash</strong></h2>'
        self.assertEqual(exporter.Exporter._heading_to_html(sut), expect)


class BlockToHtml(unittest.TestCase):
    """Block elements to HTML"""

    # pylint: disable=protected-access,missing-function-docstring

    def setUp(self):
        _reset_node_class()

    def test_simple(self):
        sut = elements.Block('src', ['foo', 'bar'], 'Python')

        # syntax highlights via Pygments
        expect = '<pre title="src Python">\n' \
                 '<span></span><span class="n">foo</span>\n' \
                 '<span class="n">bar</span>\n' \
                 '</pre>'

        self.assertEqual(
            unify_html(exporter.Exporter._block_to_html(sut)),
            unify_html(expect))

    # @mock.patch('hyperorg.exporter.pygments', return_value=None)
    def test_without_pygments(self):
        hide_pygments = exporter.pygments
        exporter.pygments = None

        sut = elements.Block('src', ['foo', 'bar'], 'Python')

        # syntax highlights via Pygments
        expect = '<pre title="src Python">\n' \
                 'foo\n' \
                 'bar' \
                 '</pre>'

        self.assertEqual(
            unify_html(exporter.Exporter._block_to_html(sut)),
            unify_html(expect))

        exporter.pygments = hide_pygments

    def test_html_block_without_pygments(self):
        hide_pygments = exporter.pygments
        exporter.pygments = None

        sut = elements.Block(
            'src',
            ['<p>Worf & Diana</p>'],
            'doesntmatter'
        )

        # Html like escaped: <
        expect = '<pre title="src doesntmatter">\n' \
                 '&lt;p&gt;Worf &amp; Diana&lt;/p&gt;' \
                 '</pre>'

        self.assertEqual(
            unify_html(exporter.Exporter._block_to_html(sut)),
            unify_html(expect))

        exporter.pygments = hide_pygments


class EscapeHTMLEntities(unittest.TestCase):
    """Escaping of HTML entities.

    Such an entity is a special character. For example "<" is converted into
    "&lt;" in HTML code. Or "&" into "&amp;".

    Development note: This escaping is part of HTML and shouldn't be in the
    content layer which is format independent by design. Rework that!
    """
    # pylint: disable=protected-access,missing-function-docstring
    org_string = "< > & \" '"
    html_string = "&lt; &gt; &amp; &quot; &#x27;"

    def test_angle_brackets(self):
        """Angle brackets in paragraph."""

        paragraph = elements.Paragraph(['Entry <entry@mail.com>'])
        result = exporter.Exporter._paragraph_to_html(paragraph)

        self.assertEqual(result, '<p>Entry &lt;entry@mail.com&gt;</p>')

    def test_paragraph(self):
        paragraph = elements.Paragraph(
            ['foo', EscapeHTMLEntities.org_string, 'bar'])

        self.assertEqual(
            exporter.Exporter._paragraph_to_html(paragraph),
            f'<p>foo {EscapeHTMLEntities.html_string} bar</p>')

    def test_link(self):
        paragraph = elements.Paragraph([
            f'[[https://123.net][{EscapeHTMLEntities.org_string}]]'
        ])

        self.assertEqual(
            exporter.Exporter._paragraph_to_html(paragraph),
            '<p><a target="_blank" rel="noopener noreferrer" '
            f'href="https://123.net">{EscapeHTMLEntities.html_string}'
            '</a></p>')

    def test_paragraph_bold(self):
        paragraph = elements.Paragraph(
            [f'Bold *{EscapeHTMLEntities.org_string}*'])

        self.assertEqual(
            exporter.Exporter._paragraph_to_html(paragraph),
            f'<p>Bold <strong>{EscapeHTMLEntities.html_string}</strong></p>')

    def test_paragraph_italic(self):
        paragraph = elements.Paragraph(
            [f'Italic /{EscapeHTMLEntities.org_string}/'])

        self.assertEqual(
            exporter.Exporter._paragraph_to_html(paragraph),
            f'<p>Italic <em>{EscapeHTMLEntities.html_string}</em></p>')

    def test_paragraph_underline(self):
        paragraph = elements.Paragraph(
            [f'Underline _{EscapeHTMLEntities.org_string}_'])

        self.assertEqual(
            exporter.Exporter._paragraph_to_html(paragraph),
            f'<p>Underline <u>{EscapeHTMLEntities.html_string}</u></p>')

    def test_paragraph_code_verbatim(self):
        paragraph = elements.Paragraph([
            f'Code ~{EscapeHTMLEntities.org_string}~',
            f'Verbatim ={EscapeHTMLEntities.org_string}='])

        self.assertEqual(
            exporter.Exporter._paragraph_to_html(paragraph),
            f'<p>Code <code>{EscapeHTMLEntities.html_string}</code> '
            'Verbatim <code class="verbatim">'
            f'{EscapeHTMLEntities.html_string}</code></p>'
        )

    def test_list(self):
        listelem = elements.ListElement(f'- {EscapeHTMLEntities.org_string}',
                                        False)

        self.assertEqual(
            unify_html(exporter.Exporter._list_to_html(listelem)),
            f'<ul><li>{EscapeHTMLEntities.html_string}</li></ul>'
        )

    def test_heading(self):
        heading = elements.Heading(f'* {EscapeHTMLEntities.org_string}')

        self.assertEqual(
            exporter.Exporter._heading_to_html(heading),
            f'<h2>{EscapeHTMLEntities.html_string}</h2>')


class DisplayMathToHtml(unittest.TestCase):
    """Test HTML out of Math blocks"""
    # pylint: disable=protected-access,missing-function-docstring

    def test_display(self):
        # self.maxDiff = None
        sut = elements.DisplayMath('e = mc^{2}')

        expected_html = r'<div class="math">\[e = mc^{2}\]</div>'

        self.assertEqual(
            exporter.Exporter._displaymath_to_html(sut),
            expected_html
        )


class BacklinksToHtml(unittest.TestCase):
    """Backlinks as HTML"""
    # pylint: disable=protected-access

    def setUp(self):
        _reset_node_class()

    def test_one_not_found(self):
        """One backlink with unknown orgid"""

        sut = elements.Backlinks()
        sut.add_link(textruns.Link(
            target='rainbow',
            link_type=textruns.Link.Type.ORGID,
            label_runs=['Over the rainbow']
        ))

        expect = ''.join([
            '</section>',
            '<section>',
            '<hr />',
            '<h2>Backlinks</h2>',
            '<ul>',
            '<li>',
            '<span title="The link destination could not be found.&#xA;'
            'Unknown OrgID &quot;rainbow&quot;.&#xA;'
            'Original link: [[id:rainbow][Over the rainbow]">',
            '⚠<span class="invalid-link">Over the rainbow</span>',
            '</span>',
            '</li>',
            '</ul>'
        ])
        self.maxDiff = None
        self.assertEqual(
            unify_html(exporter.Exporter._backlinks_to_html(sut)),
            unify_html(expect)
        )

    def test_one_valid(self):
        """One backlink with valid orgid"""

        target = 'rainbow'
        Node.nodes[target] = Node([], f'{target}.org')

        sut = elements.Backlinks()
        sut.add_link(textruns.Link(
            target=target,
            link_type=textruns.Link.Type.ORGID,
            label_runs=['Over the rainbow']
        ))

        expect = ''.join([
            '</section>',
            '<section>',
            '<hr />',
            '<h2>Backlinks</h2>',
            '<ul>',
            '<li>',
            f'<a href="{target}.html">Over the rainbow</a>',
            '</li>',
            '</ul>'
        ])
        self.maxDiff = None
        self.assertEqual(
            unify_html(exporter.Exporter._backlinks_to_html(sut)),
            unify_html(expect)
        )
