852 lines
25 KiB
Python
852 lines
25 KiB
Python
"""
|
||
novelWriter – ToHtml Class Tester
|
||
=================================
|
||
|
||
This file is a part of novelWriter
|
||
Copyright (C) 2020 Veronica Berglyd Olsen and novelWriter contributors
|
||
|
||
This program is free software: you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation, either version 3 of the License, or
|
||
(at your option) any later version.
|
||
|
||
This program is distributed in the hope that it will be useful, but
|
||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||
""" # noqa
|
||
from __future__ import annotations
|
||
|
||
import json
|
||
|
||
import pytest
|
||
|
||
from novelwriter import CONFIG
|
||
from novelwriter.constants import nwHeadFmt
|
||
from novelwriter.core.project import NWProject
|
||
from novelwriter.enum import nwComment
|
||
from novelwriter.formats.shared import BlockFmt, BlockTyp
|
||
from novelwriter.formats.tohtml import ToHtml
|
||
|
||
|
||
@pytest.mark.core
|
||
def testFmtToHtml_ConvertHeaders(mockGUI):
|
||
"""Test header formats in the ToHtml class."""
|
||
project = NWProject()
|
||
html = ToHtml(project)
|
||
html.initDocument()
|
||
|
||
# Novel Files Headers
|
||
# ===================
|
||
|
||
html._isNovel = True
|
||
html._isFirst = True
|
||
|
||
# Header 1
|
||
html._text = "# Title\n"
|
||
html.setPartitionFormat(f"Part{nwHeadFmt.BR}{nwHeadFmt.TITLE}")
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<h1 class='title' style='text-align: center;'>Part<br>Title</h1>\n"
|
||
)
|
||
|
||
# Header 2
|
||
html._text = "## Title\n"
|
||
html.setChapterFormat(f"Chapter {nwHeadFmt.CH_NUM}{nwHeadFmt.BR}{nwHeadFmt.TITLE}")
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<h1 style='page-break-before: always;'>Chapter 1<br>Title</h1>\n"
|
||
)
|
||
|
||
# Header 3
|
||
html._text = "### Title\n"
|
||
html.setSceneFormat(f"Scene {nwHeadFmt.SC_ABS}{nwHeadFmt.BR}{nwHeadFmt.TITLE}")
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == "<h2>Scene 1<br>Title</h2>\n"
|
||
|
||
# Header 4
|
||
html._text = "#### Title\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == "<h3>Title</h3>\n"
|
||
|
||
# Title
|
||
html._text = "#! Title\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<h1 class='title' style='text-align: center; page-break-before: always;'>Title</h1>\n"
|
||
)
|
||
|
||
# Unnumbered
|
||
html._text = "##! Prologue\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == "<h1 style='page-break-before: always;'>Prologue</h1>\n"
|
||
|
||
# Note Files Headers
|
||
# ==================
|
||
|
||
html._isNovel = False
|
||
html._isFirst = True
|
||
html._handle = "0000000000000"
|
||
html.setLinkHeadings(True)
|
||
|
||
# Header 1
|
||
html._text = "# Heading One\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == "<h1><a name='0000000000000:T0001'></a>Heading One</h1>\n"
|
||
|
||
# Header 2
|
||
html._text = "## Heading Two\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == "<h2><a name='0000000000000:T0001'></a>Heading Two</h2>\n"
|
||
|
||
# Header 3
|
||
html._text = "### Heading Three\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == "<h3><a name='0000000000000:T0001'></a>Heading Three</h3>\n"
|
||
|
||
# Header 4
|
||
html._text = "#### Heading Four\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == "<h4><a name='0000000000000:T0001'></a>Heading Four</h4>\n"
|
||
|
||
# Title
|
||
html._text = "#! Heading One\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<h1 class='title' style='text-align: center; page-break-before: always;'>"
|
||
"<a name='0000000000000:T0001'></a>Heading One</h1>\n"
|
||
)
|
||
|
||
# Unnumbered
|
||
html._text = "##! Heading Two\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == "<h2><a name='0000000000000:T0001'></a>Heading Two</h2>\n"
|
||
|
||
|
||
@pytest.mark.core
|
||
def testFmtToHtml_ConvertParagraphs(mockGUI):
|
||
"""Test paragraph formats in the ToHtml class."""
|
||
project = NWProject()
|
||
html = ToHtml(project)
|
||
html.initDocument()
|
||
html._isNovel = True
|
||
html._isFirst = True
|
||
|
||
# Text
|
||
html._text = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p>Some <strong>nested bold and <em>italic</em> and "
|
||
"<del>strikethrough</del> text</strong> here</p>\n"
|
||
)
|
||
|
||
# Shortcodes
|
||
html._text = (
|
||
"Some [b]bold[/b], [i]italic[/i], [s]strike[/s], [u]underline[/u], [m]mark[/m], "
|
||
"super[sup]script[/sup], sub[sub]script[/sub] here\n"
|
||
)
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p>Some <strong>bold</strong>, <em>italic</em>, <del>strike</del>, "
|
||
"<span style='text-decoration: underline;'>underline</span>, <mark>mark</mark>, "
|
||
"super<sup>script</sup>, sub<sub>script</sub> here</p>\n"
|
||
)
|
||
|
||
# Text w/Hard Break
|
||
html._text = "Line one\nLine two\nLine three\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p>Line one<br>Line two<br>Line three</p>\n"
|
||
)
|
||
|
||
# Synopsis, Short
|
||
html._text = "%synopsis: The synopsis ...\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == ""
|
||
|
||
html.setCommentType(nwComment.SYNOPSIS, True)
|
||
html._text = "%synopsis: The synopsis ...\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p class='comment'>"
|
||
"<strong><span style='color: #813709'>Synopsis:</span></strong> "
|
||
"<span style='color: #813709'>The synopsis ...</span>"
|
||
"</p>\n"
|
||
)
|
||
|
||
html.setCommentType(nwComment.SHORT, True)
|
||
html._text = "%short: A short description ...\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p class='comment'>"
|
||
"<strong><span style='color: #813709'>Short Description:</span></strong> "
|
||
"<span style='color: #813709'>A short description ...</span>"
|
||
"</p>\n"
|
||
)
|
||
|
||
# Comment
|
||
html._text = "% A comment ...\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == ""
|
||
|
||
html.setCommentType(nwComment.PLAIN, True)
|
||
html._text = "% A comment ...\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p class='comment'>"
|
||
"<strong><span style='color: #646464'>Comment:</span></strong> "
|
||
"<span style='color: #646464'>A comment ...</span>"
|
||
"</p>\n"
|
||
)
|
||
|
||
|
||
@pytest.mark.core
|
||
def testFmtToHtml_ConvertMeta(mockGUI):
|
||
"""Test meta data formats in the ToHtml class."""
|
||
project = NWProject()
|
||
html = ToHtml(project)
|
||
html.initDocument()
|
||
html._isNovel = True
|
||
html._isFirst = True
|
||
|
||
# Keywords Disabled
|
||
html.setKeywords(False)
|
||
html._text = "@char: Bod, Jane\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == ""
|
||
|
||
# Keywords Enabled
|
||
html.setKeywords(True)
|
||
html._text = "@char: Bod, Jane\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p class='meta meta-char'><strong><span style='color: #f5871f'>"
|
||
"Characters:</span></strong> "
|
||
"<span style='color: #4271ae'><a href='#tag_bod'>Bod</a></span>, "
|
||
"<span style='color: #4271ae'><a href='#tag_jane'>Jane</a></span></p>\n"
|
||
)
|
||
|
||
# Tags w/Colour
|
||
html.setStyles(True)
|
||
html._text = "@tag: Bod\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p class='meta meta-tag'><strong><span style='color: #f5871f'>Tag:</span></strong> "
|
||
"<span style='color: #4271ae'><a name='tag_bod'>Bod</a></span></p>\n"
|
||
)
|
||
|
||
html._text = "@tag: Bod | Nobody Owens\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p class='meta meta-tag'><strong><span style='color: #f5871f'>Tag:</span></strong> "
|
||
"<span style='color: #4271ae'><a name='tag_bod'>Bod</a></span> | "
|
||
"<span style='color: #4271ae'>Nobody Owens</span></p>\n"
|
||
)
|
||
|
||
# Tags wo/Colour
|
||
html.setStyles(False)
|
||
html._text = "@tag: Bod\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p class='meta meta-tag'><strong>Tag:</strong> <a name='tag_bod'>Bod</a></p>\n"
|
||
)
|
||
|
||
html._text = "@tag: Bod | Nobody Owens\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p class='meta meta-tag'><strong>Tag:</strong> "
|
||
"<a name='tag_bod'>Bod</a> | Nobody Owens</p>\n"
|
||
)
|
||
|
||
# Multiple Keywords w/Colour
|
||
html.setStyles(True)
|
||
html._isFirst = False
|
||
html.setKeywords(True)
|
||
html._text = "## Chapter\n\n@pov: Bod\n@plot: Main\n@location: Europe\n\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<h1 style='page-break-before: always;'>Chapter</h1>\n"
|
||
"<p class='meta meta-pov' style='margin-bottom: 0;'>"
|
||
"<strong><span style='color: #f5871f'>Point of View:</span></strong> "
|
||
"<span style='color: #4271ae'><a href='#tag_bod'>Bod</a></span></p>\n"
|
||
"<p class='meta meta-plot' style='margin-bottom: 0; margin-top: 0;'>"
|
||
"<strong><span style='color: #f5871f'>Plot:</span></strong> "
|
||
"<span style='color: #4271ae'><a href='#tag_main'>Main</a></span></p>\n"
|
||
"<p class='meta meta-location' style='margin-top: 0;'>"
|
||
"<strong><span style='color: #f5871f'>Locations:</span></strong> "
|
||
"<span style='color: #4271ae'><a href='#tag_europe'>Europe</a></span></p>\n"
|
||
)
|
||
|
||
# Multiple Keywords wo/Colour
|
||
html.setStyles(False)
|
||
html._isFirst = False
|
||
html.setKeywords(True)
|
||
html._text = "## Chapter\n\n@pov: Bod\n@plot: Main\n@location: Europe\n\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<h1 style='page-break-before: always;'>Chapter</h1>\n"
|
||
"<p class='meta meta-pov' style='margin-bottom: 0;'>"
|
||
"<strong>Point of View:</strong> <a href='#tag_bod'>Bod</a></p>\n"
|
||
"<p class='meta meta-plot' style='margin-bottom: 0; margin-top: 0;'>"
|
||
"<strong>Plot:</strong> <a href='#tag_main'>Main</a></p>\n"
|
||
"<p class='meta meta-location' style='margin-top: 0;'>"
|
||
"<strong>Locations:</strong> <a href='#tag_europe'>Europe</a></p>\n"
|
||
)
|
||
|
||
|
||
@pytest.mark.core
|
||
def testFmtToHtml_Alignment(mockGUI):
|
||
"""Test paragraph alignment in the ToHtml class."""
|
||
project = NWProject()
|
||
html = ToHtml(project)
|
||
html.initDocument()
|
||
|
||
# Left
|
||
html._text = "This is text <<\nspanning multiple\nlines"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p style='text-align: left;'>This is text<br>spanning multiple<br>lines</p>\n"
|
||
)
|
||
|
||
# Right
|
||
html._text = ">> This is text\nspanning multiple\nlines"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p style='text-align: right;'>This is text<br>spanning multiple<br>lines</p>\n"
|
||
)
|
||
|
||
# Centre
|
||
html._text = ">> This is text <<\nspanning multiple\nlines"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p style='text-align: center;'>This is text<br>spanning multiple<br>lines</p>\n"
|
||
)
|
||
|
||
# Left before Right
|
||
html._text = ">> This is text\nspanning multiple <<\nlines"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p style='text-align: left;'>This is text<br>spanning multiple<br>lines</p>\n"
|
||
)
|
||
|
||
# Right before Centre
|
||
html._text = ">> This is text <<\n>> spanning multiple\nlines"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p style='text-align: right;'>This is text<br>spanning multiple<br>lines</p>\n"
|
||
)
|
||
|
||
|
||
@pytest.mark.core
|
||
def testFmtToHtml_Dialog(mockGUI):
|
||
"""Test paragraph formats in the ToHtml class."""
|
||
CONFIG.altDialogOpen = "::"
|
||
CONFIG.altDialogClose = "::"
|
||
|
||
project = NWProject()
|
||
html = ToHtml(project)
|
||
html.initDocument()
|
||
html.setDialogHighlight(True)
|
||
html._isNovel = True
|
||
html._isFirst = True
|
||
|
||
# Dialog
|
||
html.setDialogHighlight(True)
|
||
html._text = "## Chapter\n\nThis text \u201chas dialogue\u201d in it.\n\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<h1>Chapter</h1>\n"
|
||
"<p>This text <span style='color: #4271ae'>“has dialogue”</span> in it.</p>\n"
|
||
)
|
||
|
||
# Alt Dialog
|
||
html._text = "## Chapter\n\nThis text ::has alt dialogue:: in it.\n\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<h1 style='page-break-before: always;'>Chapter</h1>\n"
|
||
"<p>This text <span style='color: #813709'>::has alt dialogue::</span> in it.</p>\n"
|
||
)
|
||
|
||
|
||
@pytest.mark.core
|
||
def testFmtToHtml_Footnotes(mockGUI):
|
||
"""Test paragraph formats in the ToHtml class."""
|
||
project = NWProject()
|
||
html = ToHtml(project)
|
||
html.initDocument()
|
||
html._isNovel = True
|
||
html._isFirst = True
|
||
|
||
html._text = (
|
||
"Text with one[footnote:fa] or two[footnote:fb] footnotes.\n\n"
|
||
"%footnote.fa: Footnote text A.\n\n"
|
||
)
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p>Text with one<sup><a href='#footnote_1'>1</a></sup> "
|
||
"or two<sup>ERR</sup> footnotes.</p>\n"
|
||
)
|
||
|
||
html.closeDocument()
|
||
assert html._pages[-2] == (
|
||
"<p>Text with one<sup><a href='#footnote_1'>1</a></sup> "
|
||
"or two<sup>ERR</sup> footnotes.</p>\n"
|
||
)
|
||
assert html._pages[-1] == (
|
||
"<h3>Footnotes</h3>\n"
|
||
"<ol>\n"
|
||
"<li id='footnote_1'><p>Footnote text A.</p></li>\n"
|
||
"</ol>\n"
|
||
)
|
||
|
||
|
||
@pytest.mark.core
|
||
def testFmtToHtml_CloseTags(mockGUI):
|
||
"""Test automatic closing of HTML tags for shortcodes."""
|
||
project = NWProject()
|
||
html = ToHtml(project)
|
||
html.initDocument()
|
||
|
||
html._isNovel = True
|
||
html._isFirst = True
|
||
|
||
# Unclosed Shortcodes
|
||
html._text = "Text [b][i][s][u][m][sup][sub]text text text.\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p>Text <strong><em><del><span style='text-decoration: underline;'><mark><sup><sub>"
|
||
"text text text.</strong></em></del></span></mark></sup></sub></p>\n"
|
||
)
|
||
|
||
# Double Shortcodes
|
||
html._text = "Text [b][i][s][u][m][sup][sub]text [b][i][s][u][m][sup][sub]text text.\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p>Text <strong><em><del><span style='text-decoration: underline;'><mark><sup><sub>"
|
||
"text text text.</strong></em></del></span></mark></sup></sub></p>\n"
|
||
)
|
||
|
||
# Redundant Close Shortcodes
|
||
html._text = "Text text [/b][/i][/s][/u][/m][/sup][/sub]text text.\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p>Text text text text.</p>\n"
|
||
)
|
||
|
||
|
||
@pytest.mark.core
|
||
def testFmtToHtml_ConvertDirect(mockGUI):
|
||
"""Test the converter directly using the ToHtml class."""
|
||
project = NWProject()
|
||
html = ToHtml(project)
|
||
html.initDocument()
|
||
|
||
html._isNovel = True
|
||
html._handle = "0000000000000"
|
||
html.setLinkHeadings(True)
|
||
|
||
tMeta = "0000000000000:T0001"
|
||
|
||
# Special Titles
|
||
# ==============
|
||
|
||
# Title
|
||
html._blocks = [
|
||
(BlockTyp.TITLE, tMeta, "A Title", [], BlockFmt.PBB | BlockFmt.CENTRE),
|
||
]
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<h1 class='title' style='text-align: center; page-break-before: always;'>"
|
||
"<a name='0000000000000:T0001'></a>A Title</h1>\n"
|
||
)
|
||
|
||
# Unnumbered
|
||
html._blocks = [
|
||
(BlockTyp.HEAD1, tMeta, "Prologue", [], BlockFmt.PBB),
|
||
]
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<h1 style='page-break-before: always;'>"
|
||
"<a name='0000000000000:T0001'></a>Prologue</h1>\n"
|
||
)
|
||
|
||
# Separators
|
||
# ==========
|
||
|
||
# Separator
|
||
html._blocks = [
|
||
(BlockTyp.SEP, tMeta, "* * *", [], BlockFmt.CENTRE),
|
||
]
|
||
html.doConvert()
|
||
assert html._pages[-1] == "<p class='sep' style='text-align: center;'>* * *</p>\n"
|
||
|
||
# Skip
|
||
html._blocks = [
|
||
(BlockTyp.SKIP, tMeta, "", [], BlockFmt.NONE),
|
||
]
|
||
html.doConvert()
|
||
assert html._pages[-1] == "<p> </p>\n"
|
||
|
||
# Alignment
|
||
# =========
|
||
|
||
html.setLinkHeadings(False)
|
||
html.setStyles(True)
|
||
|
||
# Align Left
|
||
html._blocks = [
|
||
(BlockTyp.PART, tMeta, "A Title", [], BlockFmt.LEFT),
|
||
]
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<h1 class='title' style='text-align: left;'>A Title</h1>\n"
|
||
)
|
||
|
||
# Align Right
|
||
html._blocks = [
|
||
(BlockTyp.PART, tMeta, "A Title", [], BlockFmt.RIGHT),
|
||
]
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<h1 class='title' style='text-align: right;'>A Title</h1>\n"
|
||
)
|
||
|
||
# Align Centre
|
||
html._blocks = [
|
||
(BlockTyp.PART, tMeta, "A Title", [], BlockFmt.CENTRE),
|
||
]
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<h1 class='title' style='text-align: center;'>A Title</h1>\n"
|
||
)
|
||
|
||
# Align Justify
|
||
html._blocks = [
|
||
(BlockTyp.PART, tMeta, "A Title", [], BlockFmt.JUSTIFY),
|
||
]
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<h1 class='title' style='text-align: justify;'>A Title</h1>\n"
|
||
)
|
||
|
||
# Page Break
|
||
# ==========
|
||
|
||
# Page Break Always
|
||
html._blocks = [
|
||
(BlockTyp.PART, tMeta, "A Title", [], BlockFmt.PBB | BlockFmt.PBA),
|
||
]
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<h1 class='title' "
|
||
"style='page-break-before: always; page-break-after: always;'>A Title</h1>\n"
|
||
)
|
||
|
||
# Indent
|
||
# ======
|
||
|
||
# Indent Left
|
||
html._blocks = [
|
||
(BlockTyp.TEXT, tMeta, "Some text ...", [], BlockFmt.IND_L),
|
||
]
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p style='margin-left: 4.00em;'>Some text ...</p>\n"
|
||
)
|
||
|
||
# Indent Right
|
||
html._blocks = [
|
||
(BlockTyp.TEXT, tMeta, "Some text ...", [], BlockFmt.IND_R),
|
||
]
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p style='margin-right: 4.00em;'>Some text ...</p>\n"
|
||
)
|
||
|
||
# Text Indent
|
||
html._blocks = [
|
||
(BlockTyp.TEXT, tMeta, "Some text ...", [], BlockFmt.IND_T),
|
||
]
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p style='text-indent: 1.40em;'>Some text ...</p>\n"
|
||
)
|
||
|
||
|
||
@pytest.mark.core
|
||
def testFmtToHtml_SpecialCases(mockGUI):
|
||
"""Test some special cases that have caused errors in the past."""
|
||
project = NWProject()
|
||
html = ToHtml(project)
|
||
html.initDocument()
|
||
html._isNovel = True
|
||
|
||
# Greater/Lesser than symbols
|
||
# ===========================
|
||
|
||
html._text = "Text with > and < with some **bold text** in it.\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p>Text with > and < with some <strong>bold text</strong> in it.</p>\n"
|
||
)
|
||
|
||
html._text = "Text with some <**bold text**> in it.\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p>Text with some <<strong>bold text</strong>> in it.</p>\n"
|
||
)
|
||
|
||
html._text = "Let's > be > _difficult **shall** > we_?\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p>Let's > be > <em>difficult <strong>shall</strong> > we</em>?</p>\n"
|
||
)
|
||
|
||
html._text = "Test > text _<**bold**>_ and more.\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p>Test > text <em><<strong>bold</strong>></em> and more.</p>\n"
|
||
)
|
||
|
||
# Test for issue #950
|
||
# ===================
|
||
# See: https://github.com/vkbo/novelWriter/issues/950
|
||
|
||
html.setCommentType(nwComment.PLAIN, True)
|
||
html._text = "% Test > text _<**bold**>_ and more.\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p class='comment'>"
|
||
"<strong><span style='color: #646464'>Comment:</span></strong> "
|
||
"<span style='color: #646464'>Test > text <em><<strong>bold</strong>></em> "
|
||
"and more.</span>"
|
||
"</p>\n"
|
||
)
|
||
|
||
html._isFirst = False
|
||
html._text = "## Heading <1>\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<h1 style='page-break-before: always;'>Heading <1></h1>\n"
|
||
)
|
||
|
||
# Test for issue #1412
|
||
# ====================
|
||
# See: https://github.com/vkbo/novelWriter/issues/1412
|
||
|
||
html._text = "Test text \\**_bold_** and more.\n"
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p>Test text **<em>bold</em>** and more.</p>\n"
|
||
)
|
||
|
||
|
||
@pytest.mark.core
|
||
def testFmtToHtml_Save(mockGUI, fncPath):
|
||
"""Test the save method of the ToHtml class."""
|
||
project = NWProject()
|
||
html = ToHtml(project)
|
||
html.initDocument()
|
||
html._isNovel = True
|
||
|
||
# Build Project
|
||
# =============
|
||
|
||
docText = [
|
||
"# My Novel\n**By Jane Doh**\n",
|
||
"## Chapter 1\n\nThe text of chapter one.\n",
|
||
"### Scene 1\n\nThe text of scene one.\n",
|
||
"#### A Section\n\nMore text in scene one.\n",
|
||
"## Chapter 2\n\nThe text of chapter two.\n",
|
||
"### Scene 2\n\nThe text of scene two.\n",
|
||
"#### A Section\n\n\tMore text in scene two.\n",
|
||
]
|
||
resText = [(
|
||
"<h1 class='title' style='text-align: center;'>My Novel</h1>\n"
|
||
"<p><strong>By Jane Doh</strong></p>\n"
|
||
), (
|
||
"<h1 style='page-break-before: always;'>Chapter 1</h1>\n"
|
||
"<p>The text of chapter one.</p>\n"
|
||
), (
|
||
"<h2>Scene 1</h2>\n"
|
||
"<p>The text of scene one.</p>\n"
|
||
), (
|
||
"<h3>A Section</h3>\n"
|
||
"<p>More text in scene one.</p>\n"
|
||
), (
|
||
"<h1 style='page-break-before: always;'>Chapter 2</h1>\n"
|
||
"<p>The text of chapter two.</p>\n"
|
||
), (
|
||
"<h2>Scene 2</h2>\n"
|
||
"<p>The text of scene two.</p>\n"
|
||
), (
|
||
"<h3>A Section</h3>\n"
|
||
"<p>\tMore text in scene two.</p>\n"
|
||
)]
|
||
|
||
for i in range(len(docText)):
|
||
html._text = docText[i]
|
||
html.doPreProcessing()
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
|
||
assert html._pages == resText
|
||
|
||
html.replaceTabs(nSpaces=2, spaceChar=" ")
|
||
resText[6] = "<h3>A Section</h3>\n<p> More text in scene two.</p>\n"
|
||
|
||
# Check Files
|
||
# ===========
|
||
hStyle = html.getStyleSheet()
|
||
|
||
# HTML w/Styles
|
||
html.setStyles(True)
|
||
htmlDocCSS = (
|
||
"<!DOCTYPE html>\n"
|
||
"<html>\n"
|
||
"<head>\n"
|
||
"<title></title>\n"
|
||
"<meta charset='utf-8'>\n"
|
||
"<meta name='viewport' content='width=device-width, initial-scale=1'>\n"
|
||
"<style>\n"
|
||
"{style}\n"
|
||
"</style>\n"
|
||
"</head>\n"
|
||
"<body>\n"
|
||
"{body}\n"
|
||
"</body>\n"
|
||
"</html>\n"
|
||
).format(
|
||
style="\n".join(hStyle),
|
||
body="".join(resText).rstrip()
|
||
)
|
||
|
||
saveFile = fncPath / "outFile.htm"
|
||
html.saveDocument(saveFile)
|
||
assert saveFile.read_text(encoding="utf-8") == htmlDocCSS
|
||
|
||
# HTML wo/Styles
|
||
html.setStyles(False)
|
||
htmlDocCSS = (
|
||
"<!DOCTYPE html>\n"
|
||
"<html>\n"
|
||
"<head>\n"
|
||
"<title></title>\n"
|
||
"<meta charset='utf-8'>\n"
|
||
"</head>\n"
|
||
"<body>\n"
|
||
"{body}\n"
|
||
"</body>\n"
|
||
"</html>\n"
|
||
).format(
|
||
body="".join(resText).rstrip()
|
||
)
|
||
|
||
saveFile = fncPath / "outFile.htm"
|
||
html.saveDocument(saveFile)
|
||
assert saveFile.read_text(encoding="utf-8") == htmlDocCSS
|
||
|
||
# JSON + HTML w/Styles
|
||
html.setStyles(True)
|
||
saveFile = fncPath / "outFile.json"
|
||
html.saveDocument(saveFile)
|
||
data = json.loads(saveFile.read_text(encoding="utf-8"))
|
||
assert data["meta"]["projectName"] == ""
|
||
assert data["meta"]["novelAuthor"] == ""
|
||
assert data["meta"]["buildTime"] > 0
|
||
assert data["meta"]["buildTimeStr"] != ""
|
||
assert data["text"]["css"] == hStyle
|
||
assert len(data["text"]["html"]) == len(resText)
|
||
|
||
|
||
@pytest.mark.core
|
||
def testFmtToHtml_Methods(mockGUI):
|
||
"""Test all the other methods of the ToHtml class."""
|
||
project = NWProject()
|
||
html = ToHtml(project)
|
||
html.initDocument()
|
||
|
||
# Auto-Replace, keep Unicode
|
||
docText = "Text with <brackets> & short–dash, long—dash …\n"
|
||
html._text = docText
|
||
html.setReplaceUnicode(False)
|
||
html.doPreProcessing()
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p>Text with <brackets> & short–dash, long—dash …</p>\n"
|
||
)
|
||
|
||
# Auto-Replace, replace Unicode
|
||
docText = "Text with <brackets> & short–dash, long—dash …\n"
|
||
html._text = docText
|
||
html.setReplaceUnicode(True)
|
||
html.doPreProcessing()
|
||
html.tokenizeText()
|
||
html.doConvert()
|
||
assert html._pages[-1] == (
|
||
"<p>Text with <brackets> & short–dash, long—dash …</p>\n"
|
||
)
|
||
|
||
# Result Size
|
||
assert html.getFullResultSize() == 147
|
||
|
||
# CSS
|
||
# ===
|
||
|
||
assert len(html.getStyleSheet()) > 1
|
||
assert "p {text-align: left;" in " ".join(html.getStyleSheet())
|
||
assert "p {text-align: justify;" not in " ".join(html.getStyleSheet())
|
||
|
||
html.setStyles(False)
|
||
assert html.getStyleSheet() == []
|