Fix ToOdt test coverage

This commit is contained in:
Veronica Berglyd Olsen
2023-10-20 22:55:59 +02:00
parent dc50f4bb3b
commit d51bfec090
3 changed files with 394 additions and 300 deletions
+2 -1
View File
@@ -29,6 +29,7 @@ from __future__ import annotations
import logging import logging
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
from typing import Sequence
from hashlib import sha256 from hashlib import sha256
from pathlib import Path from pathlib import Path
from zipfile import ZipFile from zipfile import ZipFile
@@ -578,7 +579,7 @@ class ToOdt(Tokenizer):
def _addTextPar( def _addTextPar(
self, styleName: str, oStyle: ODTParagraphStyle, tText: str, self, styleName: str, oStyle: ODTParagraphStyle, tText: str,
tFmt: list[tuple[int, int]] = [], isHead: bool = False, oLevel: str | None = None tFmt: Sequence[tuple[int, int]] = [], isHead: bool = False, oLevel: str | None = None
) -> None: ) -> None:
"""Add a text paragraph to the text XML element.""" """Add a text paragraph to the text XML element."""
tAttr = {} tAttr = {}
@@ -1,13 +1,13 @@
<?xml version='1.0' encoding='utf-8'?> <?xml version='1.0' encoding='utf-8'?>
<office:document xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text"> <office:document xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
<office:meta> <office:meta>
<meta:creation-date>2023-07-19T23:55:29</meta:creation-date> <meta:creation-date>2023-10-20T22:51:30</meta:creation-date>
<meta:generator>novelWriter/2.1-beta1</meta:generator> <meta:generator>novelWriter/2.2-alpha1</meta:generator>
<meta:initial-creator>Jane Smith</meta:initial-creator> <meta:initial-creator>Jane Smith</meta:initial-creator>
<meta:editing-cycles>1234</meta:editing-cycles> <meta:editing-cycles>1234</meta:editing-cycles>
<meta:editing-duration>P42DT12H34M56S</meta:editing-duration> <meta:editing-duration>P42DT12H34M56S</meta:editing-duration>
<dc:title>Test Project</dc:title> <dc:title>Test Project</dc:title>
<dc:date>2023-07-19T23:55:29</dc:date> <dc:date>2023-10-20T22:51:30</dc:date>
<dc:creator>Jane Smith</dc:creator> <dc:creator>Jane Smith</dc:creator>
</office:meta> </office:meta>
<office:font-face-decls> <office:font-face-decls>
@@ -64,7 +64,7 @@
</office:styles> </office:styles>
<office:automatic-styles> <office:automatic-styles>
<style:page-layout style:name="PM1"> <style:page-layout style:name="PM1">
<style:page-layout-properties fo:page-width="21.0cm" fo:page-height="29.7cm" fo:margin-top="2.000cm" fo:margin-bottom="2.000cm" fo:margin-left="2.000cm" fo:margin-right="2.000cm" fo:print-orientation="portrait" /> <style:page-layout-properties fo:page-width="14.800cm" fo:page-height="21.000cm" fo:margin-top="2.000cm" fo:margin-bottom="1.800cm" fo:margin-left="1.700cm" fo:margin-right="1.500cm" fo:print-orientation="portrait" />
<style:header-style> <style:header-style>
<style:header-footer-properties fo:min-height="0.600cm" fo:margin-left="0.000cm" fo:margin-right="0.000cm" fo:margin-bottom="0.500cm" /> <style:header-footer-properties fo:min-height="0.600cm" fo:margin-left="0.000cm" fo:margin-right="0.000cm" fo:margin-bottom="0.500cm" />
</style:header-style> </style:header-style>
+388 -295
View File
@@ -53,53 +53,53 @@ def xmlToText(xElem):
@pytest.mark.core @pytest.mark.core
def testCoreToOdt_Init(mockGUI): def testCoreToOdt_Init(mockGUI):
"""Test initialisation of the ODT document.""" """Test initialisation of the ODT document."""
theProject = NWProject() project = NWProject()
# Flat Doc # Flat Doc
# ======== # ========
theDoc = ToOdt(theProject, isFlat=True) odt = ToOdt(project, isFlat=True)
theDoc.initDocument() odt.initDocument()
# Document XML # Document XML
assert theDoc._dFlat.tag == _mkTag("office", "document") assert odt._dFlat.tag == _mkTag("office", "document")
assert theDoc._dCont.tag == "" assert odt._dCont.tag == ""
assert theDoc._dMeta.tag == "" assert odt._dMeta.tag == ""
assert theDoc._dStyl.tag == "" assert odt._dStyl.tag == ""
# Content XML # Content XML
assert theDoc._xMeta.tag == _mkTag("office", "meta") assert odt._xMeta.tag == _mkTag("office", "meta")
assert theDoc._xFont.tag == _mkTag("office", "font-face-decls") assert odt._xFont.tag == _mkTag("office", "font-face-decls")
assert theDoc._xFnt2.tag == "" assert odt._xFnt2.tag == ""
assert theDoc._xStyl.tag == _mkTag("office", "styles") assert odt._xStyl.tag == _mkTag("office", "styles")
assert theDoc._xAuto.tag == _mkTag("office", "automatic-styles") assert odt._xAuto.tag == _mkTag("office", "automatic-styles")
assert theDoc._xAut2.tag == "" assert odt._xAut2.tag == ""
assert theDoc._xMast.tag == _mkTag("office", "master-styles") assert odt._xMast.tag == _mkTag("office", "master-styles")
assert theDoc._xBody.tag == _mkTag("office", "body") assert odt._xBody.tag == _mkTag("office", "body")
assert theDoc._xText.tag == _mkTag("office", "text") assert odt._xText.tag == _mkTag("office", "text")
# ODT Doc # ODT Doc
# ======= # =======
theDoc = ToOdt(theProject, isFlat=False) odt = ToOdt(project, isFlat=False)
theDoc.initDocument() odt.initDocument()
# Document XML # Document XML
assert theDoc._dFlat.tag == "" assert odt._dFlat.tag == ""
assert theDoc._dCont.tag == _mkTag("office", "document-content") assert odt._dCont.tag == _mkTag("office", "document-content")
assert theDoc._dMeta.tag == _mkTag("office", "document-meta") assert odt._dMeta.tag == _mkTag("office", "document-meta")
assert theDoc._dStyl.tag == _mkTag("office", "document-styles") assert odt._dStyl.tag == _mkTag("office", "document-styles")
# Content XML # Content XML
assert theDoc._xMeta.tag == _mkTag("office", "meta") assert odt._xMeta.tag == _mkTag("office", "meta")
assert theDoc._xFont.tag == _mkTag("office", "font-face-decls") assert odt._xFont.tag == _mkTag("office", "font-face-decls")
assert theDoc._xFnt2.tag == _mkTag("office", "font-face-decls") assert odt._xFnt2.tag == _mkTag("office", "font-face-decls")
assert theDoc._xStyl.tag == _mkTag("office", "styles") assert odt._xStyl.tag == _mkTag("office", "styles")
assert theDoc._xAuto.tag == _mkTag("office", "automatic-styles") assert odt._xAuto.tag == _mkTag("office", "automatic-styles")
assert theDoc._xAut2.tag == _mkTag("office", "automatic-styles") assert odt._xAut2.tag == _mkTag("office", "automatic-styles")
assert theDoc._xMast.tag == _mkTag("office", "master-styles") assert odt._xMast.tag == _mkTag("office", "master-styles")
assert theDoc._xBody.tag == _mkTag("office", "body") assert odt._xBody.tag == _mkTag("office", "body")
assert theDoc._xText.tag == _mkTag("office", "text") assert odt._xText.tag == _mkTag("office", "text")
# END Test testCoreToOdt_Init # END Test testCoreToOdt_Init
@@ -108,147 +108,142 @@ def testCoreToOdt_Init(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToOdt_TextFormatting(mockGUI): def testCoreToOdt_TextFormatting(mockGUI):
"""Test formatting of paragraphs.""" """Test formatting of paragraphs."""
theProject = NWProject() project = NWProject()
theDoc = ToOdt(theProject, isFlat=True) odt = ToOdt(project, isFlat=True)
theDoc.initDocument() odt.initDocument()
assert xmlToText(theDoc._xText) == "<office:text />" assert xmlToText(odt._xText) == "<office:text />"
# Paragraph Style # Paragraph Style
# =============== # ===============
oStyle = ODTParagraphStyle() oStyle = ODTParagraphStyle()
assert theDoc._paraStyle("stuff", oStyle) == "Standard" assert odt._paraStyle("stuff", oStyle) == "Standard"
assert theDoc._paraStyle("Text_20_body", oStyle) == "Text_20_body" assert odt._paraStyle("Text_20_body", oStyle) == "Text_20_body"
# Create new para style # Create new para style
oStyle.setTextAlign("center") oStyle.setTextAlign("center")
assert theDoc._paraStyle("Text_20_body", oStyle) == "P1" assert odt._paraStyle("Text_20_body", oStyle) == "P1"
# Return the same style on second call # Return the same style on second call
assert theDoc._paraStyle("Text_20_body", oStyle) == "P1" assert odt._paraStyle("Text_20_body", oStyle) == "P1"
assert list(theDoc._mainPara.keys()) == [ assert list(odt._mainPara.keys()) == [
"Text_20_body", "Text_20_Meta", "Title", "Separator", "Text_20_body", "Text_20_Meta", "Title", "Separator",
"Heading_20_1", "Heading_20_2", "Heading_20_3", "Heading_20_4", "Header", "Heading_20_1", "Heading_20_2", "Heading_20_3", "Heading_20_4", "Header",
] ]
theKey = "071d6b2e4764749f8c78d3c1ab9099fa04c07d2d53fd3de61eb1bdf1cb4845c3" theKey = "071d6b2e4764749f8c78d3c1ab9099fa04c07d2d53fd3de61eb1bdf1cb4845c3"
assert theDoc._autoPara[theKey][0] == "P1" assert odt._autoPara[theKey][0] == "P1"
assert isinstance(theDoc._autoPara[theKey][1], ODTParagraphStyle) assert isinstance(odt._autoPara[theKey][1], ODTParagraphStyle)
# Paragraph Formatting # Paragraph Formatting
# ==================== # ====================
oStyle = ODTParagraphStyle() oStyle = ODTParagraphStyle()
# No Text # No Text
theDoc.initDocument() odt.initDocument()
theDoc._addTextPar("Standard", oStyle, "") odt._addTextPar("Standard", oStyle, "")
assert xmlToText(theDoc._xText) == ( assert xmlToText(odt._xText) == (
'<office:text>' '<office:text>'
'<text:p text:style-name="Standard" />' '<text:p text:style-name="Standard" />'
'</office:text>' '</office:text>'
) )
# No Format # No Format
theDoc.initDocument() odt.initDocument()
theDoc._addTextPar("Standard", oStyle, "Hello World") odt._addTextPar("Standard", oStyle, "Hello World")
assert theDoc.errData == [] assert odt.errData == []
assert xmlToText(theDoc._xText) == ( assert xmlToText(odt._xText) == (
'<office:text>' '<office:text>'
'<text:p text:style-name="Standard">Hello World</text:p>' '<text:p text:style-name="Standard">Hello World</text:p>'
'</office:text>' '</office:text>'
) )
# Heading Level None # Heading Level None
theDoc.initDocument() odt.initDocument()
theDoc._addTextPar("Standard", oStyle, "Hello World", isHead=True) odt._addTextPar("Standard", oStyle, "Hello World", isHead=True)
assert theDoc.errData == [] assert odt.errData == []
assert xmlToText(theDoc._xText) == ( assert xmlToText(odt._xText) == (
'<office:text>' '<office:text>'
'<text:h text:style-name="Standard">Hello World</text:h>' '<text:h text:style-name="Standard">Hello World</text:h>'
'</office:text>' '</office:text>'
) )
# Heading Level 1 # Heading Level 1
theDoc.initDocument() odt.initDocument()
theDoc._addTextPar("Standard", oStyle, "Hello World", isHead=True, oLevel="1") odt._addTextPar("Standard", oStyle, "Hello World", isHead=True, oLevel="1")
assert theDoc.errData == [] assert odt.errData == []
assert xmlToText(theDoc._xText) == ( assert xmlToText(odt._xText) == (
'<office:text>' '<office:text>'
'<text:h text:style-name="Standard" text:outline-level="1">Hello World</text:h>' '<text:h text:style-name="Standard" text:outline-level="1">Hello World</text:h>'
'</office:text>' '</office:text>'
) )
# Formatted Text # Formatted Text
theDoc.initDocument() odt.initDocument()
theTxt = "A **few** _words_ from ~~our~~ sponsor" text = "A bold word"
theFmt = " _B b_ I i _S s_ " fmt = [(2, odt.FMT_B_B), (6, odt.FMT_B_E)]
theDoc._addTextPar("Standard", oStyle, theTxt, tFmt=theFmt) odt._addTextPar("Standard", oStyle, text, tFmt=fmt)
assert theDoc.errData == [] assert odt.errData == []
assert xmlToText(theDoc._xText) == ( assert xmlToText(odt._xText) == (
'<office:text>' '<office:text>'
'<text:p text:style-name="Standard">A <text:span text:style-name="T1">few</text:span> ' '<text:p text:style-name="Standard">A <text:span text:style-name="T1">bold</text:span> '
'<text:span text:style-name="T2">words</text:span> from <text:span text:style-name="T3">' 'word</text:p>'
'our</text:span> sponsor</text:p>'
'</office:text>' '</office:text>'
) )
# Incorrectly Formatted Text # Incorrectly Formatted Text
theDoc.initDocument() odt.initDocument()
theTxt = "A **few** _wordsXXX" text = "A few words"
theFmt = " _b b_ I XXX" fmt = [(2, odt.FMT_B_B), (5, odt.FMT_B_E), (7, 99999)]
theDoc._addTextPar("Standard", oStyle, theTxt, tFmt=theFmt) odt._addTextPar("Standard", oStyle, text, tFmt=fmt)
assert theDoc.errData == ["Unknown format tag encountered"] assert odt.errData == ["Unknown format tag encountered"]
assert xmlToText(theDoc._xText) == ( assert xmlToText(odt._xText) == (
'<office:text>' '<office:text>'
'<text:p text:style-name="Standard">' '<text:p text:style-name="Standard">A <text:span text:style-name="T1">few</text:span> '
'A few <text:span text:style-name="T2">words</text:span>' 'words</text:p>'
'</text:p>'
'</office:text>' '</office:text>'
) )
# Formatted Text # Unclosed format
theDoc.initDocument() odt.initDocument()
theTxt = "Hello\n\tWorld" text = "A bold word"
theFmt = " " fmt = [(2, odt.FMT_B_B)]
theDoc._addTextPar("Standard", oStyle, theTxt, tFmt=theFmt) odt._addTextPar("Standard", oStyle, text, tFmt=fmt)
assert theDoc.errData == [] assert odt.errData == []
assert xmlToText(theDoc._xText) == ( assert xmlToText(odt._xText) == (
'<office:text>'
'<text:p text:style-name="Standard">A '
'<text:span text:style-name="T1">bold word</text:span></text:p>'
'</office:text>'
)
# Tabs and Breaks
odt.initDocument()
text = "Hello\n\tWorld"
fmt = []
odt._addTextPar("Standard", oStyle, text, tFmt=fmt)
assert odt.errData == []
assert xmlToText(odt._xText) == (
'<office:text>' '<office:text>'
'<text:p text:style-name="Standard">Hello<text:line-break /><text:tab />World</text:p>' '<text:p text:style-name="Standard">Hello<text:line-break /><text:tab />World</text:p>'
'</office:text>' '</office:text>'
) )
# Test for issue #1412
# ====================
# See: https://github.com/vkbo/novelWriter/issues/1412
theDoc.initDocument()
theTxt = "Test text \\**_bold_** and more."
theFmt = " I i "
theDoc._addTextPar("Standard", oStyle, theTxt, tFmt=theFmt)
assert theDoc.errData == []
assert xmlToText(theDoc._xText) == (
'<office:text>'
'<text:p text:style-name="Standard">Test text **<text:span text:style-name="T2">'
'bold</text:span>** and more.</text:p>'
'</office:text>'
)
# END Test testCoreToOdt_TextFormatting # END Test testCoreToOdt_TextFormatting
@pytest.mark.core @pytest.mark.core
def testCoreToOdt_Convert(mockGUI): def testCoreToOdt_Convert(mockGUI):
"""Test the converter of the ToOdt class.""" """Test the converter of the ToOdt class."""
theProject = NWProject() project = NWProject()
theDoc = ToOdt(theProject, isFlat=True) odt = ToOdt(project, isFlat=True)
theDoc._isNovel = True odt._isNovel = True
def getStyle(styleName): def getStyle(styleName):
for aSet in theDoc._autoPara.values(): for aSet in odt._autoPara.values():
if aSet[0] == styleName: if aSet[0] == styleName:
return aSet[1] return aSet[1]
return None return None
@@ -257,78 +252,78 @@ def testCoreToOdt_Convert(mockGUI):
# ======= # =======
# Header 1 # Header 1
theDoc._text = "# Title\n" odt._text = "# Title\n"
theDoc.tokenizeText() odt.tokenizeText()
theDoc.initDocument() odt.initDocument()
theDoc.doConvert() odt.doConvert()
theDoc.closeDocument() odt.closeDocument()
assert theDoc.errData == [] assert odt.errData == []
assert xmlToText(theDoc._xText) == ( assert xmlToText(odt._xText) == (
'<office:text>' '<office:text>'
'<text:h text:style-name="P1" text:outline-level="1">Title</text:h>' '<text:h text:style-name="P1" text:outline-level="1">Title</text:h>'
'</office:text>' '</office:text>'
) )
# Header 2 # Header 2
theDoc._text = "## Chapter\n" odt._text = "## Chapter\n"
theDoc.tokenizeText() odt.tokenizeText()
theDoc.initDocument() odt.initDocument()
theDoc.doConvert() odt.doConvert()
theDoc.closeDocument() odt.closeDocument()
assert theDoc.errData == [] assert odt.errData == []
assert xmlToText(theDoc._xText) == ( assert xmlToText(odt._xText) == (
'<office:text>' '<office:text>'
'<text:h text:style-name="P2" text:outline-level="2">Chapter</text:h>' '<text:h text:style-name="P2" text:outline-level="2">Chapter</text:h>'
'</office:text>' '</office:text>'
) )
# Header 3 # Header 3
theDoc._text = "### Scene\n" odt._text = "### Scene\n"
theDoc.tokenizeText() odt.tokenizeText()
theDoc.initDocument() odt.initDocument()
theDoc.doConvert() odt.doConvert()
theDoc.closeDocument() odt.closeDocument()
assert theDoc.errData == [] assert odt.errData == []
assert xmlToText(theDoc._xText) == ( assert xmlToText(odt._xText) == (
'<office:text>' '<office:text>'
'<text:h text:style-name="Heading_20_3" text:outline-level="3">Scene</text:h>' '<text:h text:style-name="Heading_20_3" text:outline-level="3">Scene</text:h>'
'</office:text>' '</office:text>'
) )
# Header 4 # Header 4
theDoc._text = "#### Section\n" odt._text = "#### Section\n"
theDoc.tokenizeText() odt.tokenizeText()
theDoc.initDocument() odt.initDocument()
theDoc.doConvert() odt.doConvert()
theDoc.closeDocument() odt.closeDocument()
assert theDoc.errData == [] assert odt.errData == []
assert xmlToText(theDoc._xText) == ( assert xmlToText(odt._xText) == (
'<office:text>' '<office:text>'
'<text:h text:style-name="Heading_20_4" text:outline-level="4">Section</text:h>' '<text:h text:style-name="Heading_20_4" text:outline-level="4">Section</text:h>'
'</office:text>' '</office:text>'
) )
# Title # Title
theDoc._text = "#! Title\n" odt._text = "#! Title\n"
theDoc.tokenizeText() odt.tokenizeText()
theDoc.initDocument() odt.initDocument()
theDoc.doConvert() odt.doConvert()
theDoc.closeDocument() odt.closeDocument()
assert theDoc.errData == [] assert odt.errData == []
assert xmlToText(theDoc._xText) == ( assert xmlToText(odt._xText) == (
'<office:text>' '<office:text>'
'<text:p text:style-name="Title">Title</text:p>' '<text:p text:style-name="Title">Title</text:p>'
'</office:text>' '</office:text>'
) )
# Unnumbered chapter # Unnumbered chapter
theDoc._text = "##! Prologue\n" odt._text = "##! Prologue\n"
theDoc.tokenizeText() odt.tokenizeText()
theDoc.initDocument() odt.initDocument()
theDoc.doConvert() odt.doConvert()
theDoc.closeDocument() odt.closeDocument()
assert theDoc.errData == [] assert odt.errData == []
assert xmlToText(theDoc._xText) == ( assert xmlToText(odt._xText) == (
'<office:text>' '<office:text>'
'<text:h text:style-name="P2" text:outline-level="2">Prologue</text:h>' '<text:h text:style-name="P2" text:outline-level="2">Prologue</text:h>'
'</office:text>' '</office:text>'
@@ -337,14 +332,14 @@ def testCoreToOdt_Convert(mockGUI):
# Paragraphs # Paragraphs
# ========== # ==========
# Nested Text # Nested Markdown Text
theDoc._text = "Some ~~nested **bold** and _italics_ text~~ text." odt._text = "Some ~~nested **bold** and _italics_ text~~ text."
theDoc.tokenizeText() odt.tokenizeText()
theDoc.initDocument() odt.initDocument()
theDoc.doConvert() odt.doConvert()
theDoc.closeDocument() odt.closeDocument()
assert theDoc.errData == [] assert odt.errData == []
assert xmlToText(theDoc._xText) == ( assert xmlToText(odt._xText) == (
'<office:text>' '<office:text>'
'<text:p text:style-name="Text_20_body">Some ' '<text:p text:style-name="Text_20_body">Some '
'<text:span text:style-name="T1">nested </text:span>' '<text:span text:style-name="T1">nested </text:span>'
@@ -355,59 +350,94 @@ def testCoreToOdt_Convert(mockGUI):
'</office:text>' '</office:text>'
) )
# Nested Shortcode Text, Emphasis
odt._text = "Some [s]nested [b]bold[/b] [u]and[/u] [i]italics[/i] text[/s] text."
odt.tokenizeText()
odt.initDocument()
odt.doConvert()
odt.closeDocument()
assert odt.errData == []
assert xmlToText(odt._xText) == (
'<office:text>'
'<text:p text:style-name="Text_20_body">Some '
'<text:span text:style-name="T1">nested </text:span>'
'<text:span text:style-name="T2">bold</text:span>'
'<text:span text:style-name="T1"> </text:span>'
'<text:span text:style-name="T4">and</text:span>'
'<text:span text:style-name="T1"> </text:span>'
'<text:span text:style-name="T3">italics</text:span>'
'<text:span text:style-name="T1"> text</text:span> text.</text:p>'
'</office:text>'
)
# Nested Shortcode Text, Super/Subscript
odt._text = "Some super[sup]script[/sup] and sub[sub]script[/sub] text."
odt.tokenizeText()
odt.initDocument()
odt.doConvert()
odt.closeDocument()
assert odt.errData == []
assert xmlToText(odt._xText) == (
'<office:text>'
'<text:p text:style-name="Text_20_body">Some '
'super<text:span text:style-name="T5">script</text:span> and '
'sub<text:span text:style-name="T6">script</text:span> text.</text:p>'
'</office:text>'
)
# Hard Break # Hard Break
theDoc._text = "Some text.\nNext line\n" odt._text = "Some text.\nNext line\n"
theDoc.tokenizeText() odt.tokenizeText()
theDoc.initDocument() odt.initDocument()
theDoc.doConvert() odt.doConvert()
theDoc.closeDocument() odt.closeDocument()
assert theDoc.errData == [] assert odt.errData == []
assert xmlToText(theDoc._xText) == ( assert xmlToText(odt._xText) == (
'<office:text>' '<office:text>'
'<text:p text:style-name="Text_20_body">Some text.<text:line-break />Next line</text:p>' '<text:p text:style-name="Text_20_body">Some text.<text:line-break />Next line</text:p>'
'</office:text>' '</office:text>'
) )
# Tab # Tab
theDoc._text = "\tItem 1\tItem 2\n" odt._text = "\tItem 1\tItem 2\n"
theDoc.tokenizeText() odt.tokenizeText()
theDoc.initDocument() odt.initDocument()
theDoc.doConvert() odt.doConvert()
theDoc.closeDocument() odt.closeDocument()
assert theDoc.errData == [] assert odt.errData == []
assert xmlToText(theDoc._xText) == ( assert xmlToText(odt._xText) == (
'<office:text>' '<office:text>'
'<text:p text:style-name="Text_20_body"><text:tab />Item 1<text:tab />Item 2</text:p>' '<text:p text:style-name="Text_20_body"><text:tab />Item 1<text:tab />Item 2</text:p>'
'</office:text>' '</office:text>'
) )
# Tab in Format # Tab in Format
theDoc._text = "Some **bold\ttext**" odt._text = "Some **bold\ttext**"
theDoc.tokenizeText() odt.tokenizeText()
theDoc.initDocument() odt.initDocument()
theDoc.doConvert() odt.doConvert()
theDoc.closeDocument() odt.closeDocument()
assert theDoc.errData == [] assert odt.errData == []
assert xmlToText(theDoc._xText) == ( assert xmlToText(odt._xText) == (
'<office:text>' '<office:text>'
'<text:p text:style-name="Text_20_body">Some <text:span text:style-name="T4">' '<text:p text:style-name="Text_20_body">Some <text:span text:style-name="T7">'
'bold<text:tab />text</text:span></text:p>' 'bold<text:tab />text</text:span></text:p>'
'</office:text>' '</office:text>'
) )
# Multiple Spaces # Multiple Spaces
theDoc._text = ( odt._text = (
"### Scene\n\n" "### Scene\n\n"
"Hello World\n\n" "Hello World\n\n"
"Hello World\n\n" "Hello World\n\n"
"Hello World\n\n" "Hello World\n\n"
) )
theDoc.tokenizeText() odt.tokenizeText()
theDoc.initDocument() odt.initDocument()
theDoc.doConvert() odt.doConvert()
theDoc.closeDocument() odt.closeDocument()
assert theDoc.errData == [] assert odt.errData == []
assert xmlToText(theDoc._xText) == ( assert xmlToText(odt._xText) == (
'<office:text>' '<office:text>'
'<text:h text:style-name="Heading_20_3" text:outline-level="3">Scene</text:h>' '<text:h text:style-name="Heading_20_3" text:outline-level="3">Scene</text:h>'
'<text:p text:style-name="Text_20_body">Hello World</text:p>' '<text:p text:style-name="Text_20_body">Hello World</text:p>'
@@ -417,42 +447,42 @@ def testCoreToOdt_Convert(mockGUI):
) )
# Synopsis, Comment, Keywords # Synopsis, Comment, Keywords
theDoc._text = ( odt._text = (
"### Scene\n\n" "### Scene\n\n"
"@pov: Jane\n\n" "@pov: Jane\n\n"
"% synopsis: So it begins\n\n" "% synopsis: So it begins\n\n"
"% a plain comment\n\n" "% a plain comment\n\n"
) )
theDoc.setSynopsis(True) odt.setSynopsis(True)
theDoc.setComments(True) odt.setComments(True)
theDoc.setKeywords(True) odt.setKeywords(True)
theDoc.tokenizeText() odt.tokenizeText()
theDoc.initDocument() odt.initDocument()
theDoc.doConvert() odt.doConvert()
theDoc.closeDocument() odt.closeDocument()
assert theDoc.errData == [] assert odt.errData == []
assert xmlToText(theDoc._xText) == ( assert xmlToText(odt._xText) == (
'<office:text>' '<office:text>'
'<text:h text:style-name="Heading_20_3" text:outline-level="3">Scene</text:h>' '<text:h text:style-name="Heading_20_3" text:outline-level="3">Scene</text:h>'
'<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T4">' '<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T7">'
'Point of View:</text:span> Jane</text:p>' 'Point of View:</text:span> Jane</text:p>'
'<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T4">' '<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T7">'
'Synopsis:</text:span> So it begins</text:p>' 'Synopsis:</text:span> So it begins</text:p>'
'<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T4">' '<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T7">'
'Comment:</text:span> a plain comment</text:p>' 'Comment:</text:span> a plain comment</text:p>'
'</office:text>' '</office:text>'
) )
# Scene Separator # Scene Separator
theDoc._text = "### Scene One\n\nText\n\n### Scene Two\n\nText" odt._text = "### Scene One\n\nText\n\n### Scene Two\n\nText"
theDoc.setSceneFormat("* * *", False) odt.setSceneFormat("* * *", False)
theDoc.tokenizeText() odt.tokenizeText()
theDoc.doHeaders() odt.doHeaders()
theDoc.initDocument() odt.initDocument()
theDoc.doConvert() odt.doConvert()
theDoc.closeDocument() odt.closeDocument()
assert theDoc.errData == [] assert odt.errData == []
assert xmlToText(theDoc._xText) == ( assert xmlToText(odt._xText) == (
'<office:text>' '<office:text>'
'<text:p text:style-name="Separator">* * *</text:p>' '<text:p text:style-name="Separator">* * *</text:p>'
'<text:p text:style-name="Text_20_body">Text</text:p>' '<text:p text:style-name="Text_20_body">Text</text:p>'
@@ -462,15 +492,15 @@ def testCoreToOdt_Convert(mockGUI):
) )
# Scene Break # Scene Break
theDoc._text = "### Scene One\n\nText\n\n### Scene Two\n\nText" odt._text = "### Scene One\n\nText\n\n### Scene Two\n\nText"
theDoc.setSceneFormat("", False) odt.setSceneFormat("", False)
theDoc.tokenizeText() odt.tokenizeText()
theDoc.doHeaders() odt.doHeaders()
theDoc.initDocument() odt.initDocument()
theDoc.doConvert() odt.doConvert()
theDoc.closeDocument() odt.closeDocument()
assert theDoc.errData == [] assert odt.errData == []
assert xmlToText(theDoc._xText) == ( assert xmlToText(odt._xText) == (
'<office:text>' '<office:text>'
'<text:p text:style-name="Separator" />' '<text:p text:style-name="Separator" />'
'<text:p text:style-name="Text_20_body">Text</text:p>' '<text:p text:style-name="Text_20_body">Text</text:p>'
@@ -480,7 +510,7 @@ def testCoreToOdt_Convert(mockGUI):
) )
# Paragraph Styles # Paragraph Styles
theDoc._text = ( odt._text = (
"### Scene\n\n" "### Scene\n\n"
"@pov: Jane\n" "@pov: Jane\n"
"@char: John\n" "@char: John\n"
@@ -491,20 +521,20 @@ def testCoreToOdt_Convert(mockGUI):
"> Left indent\n\n" "> Left indent\n\n"
"Right indent <\n\n" "Right indent <\n\n"
) )
theDoc.setKeywords(True) odt.setKeywords(True)
theDoc.tokenizeText() odt.tokenizeText()
theDoc.initDocument() odt.initDocument()
theDoc.doConvert() odt.doConvert()
theDoc.closeDocument() odt.closeDocument()
assert theDoc.errData == [] assert odt.errData == []
assert xmlToText(theDoc._xText) == ( assert xmlToText(odt._xText) == (
'<office:text>' '<office:text>'
'<text:h text:style-name="Heading_20_3" text:outline-level="3">Scene</text:h>' '<text:h text:style-name="Heading_20_3" text:outline-level="3">Scene</text:h>'
'<text:p text:style-name="P3"><text:span text:style-name="T4">' '<text:p text:style-name="P3"><text:span text:style-name="T7">'
'Point of View:</text:span> Jane</text:p>' 'Point of View:</text:span> Jane</text:p>'
'<text:p text:style-name="P4"><text:span text:style-name="T4">' '<text:p text:style-name="P4"><text:span text:style-name="T7">'
'Characters:</text:span> John</text:p>' 'Characters:</text:span> John</text:p>'
'<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T4">' '<text:p text:style-name="Text_20_Meta"><text:span text:style-name="T7">'
'Plot:</text:span> Main</text:p>' 'Plot:</text:span> Main</text:p>'
'<text:p text:style-name="P5">Right align</text:p>' '<text:p text:style-name="P5">Right align</text:p>'
'<text:p text:style-name="Text_20_body">Left Align</text:p>' '<text:p text:style-name="Text_20_body">Left Align</text:p>'
@@ -513,28 +543,28 @@ def testCoreToOdt_Convert(mockGUI):
'<text:p text:style-name="P8">Right indent</text:p>' '<text:p text:style-name="P8">Right indent</text:p>'
'</office:text>' '</office:text>'
) )
assert getStyle("P3")._pAttr["margin-bottom"] == ["fo", "0.000cm"] assert getStyle("P3")._pAttr["margin-bottom"] == ["fo", "0.000cm"] # type: ignore
assert getStyle("P4")._pAttr["margin-bottom"] == ["fo", "0.000cm"] assert getStyle("P4")._pAttr["margin-bottom"] == ["fo", "0.000cm"] # type: ignore
assert getStyle("P4")._pAttr["margin-top"] == ["fo", "0.000cm"] assert getStyle("P4")._pAttr["margin-top"] == ["fo", "0.000cm"] # type: ignore
assert getStyle("P5")._pAttr["text-align"] == ["fo", "right"] assert getStyle("P5")._pAttr["text-align"] == ["fo", "right"] # type: ignore
assert getStyle("P6")._pAttr["text-align"] == ["fo", "center"] assert getStyle("P6")._pAttr["text-align"] == ["fo", "center"] # type: ignore
assert getStyle("P7")._pAttr["margin-left"] == ["fo", "1.693cm"] assert getStyle("P7")._pAttr["margin-left"] == ["fo", "1.693cm"] # type: ignore
assert getStyle("P8")._pAttr["margin-right"] == ["fo", "1.693cm"] assert getStyle("P8")._pAttr["margin-right"] == ["fo", "1.693cm"] # type: ignore
# Justified # Justified
theDoc._text = ( odt._text = (
"### Scene\n\n" "### Scene\n\n"
"Regular paragraph\n\n" "Regular paragraph\n\n"
"with\nbreak\n\n" "with\nbreak\n\n"
"Left Align <<\n\n" "Left Align <<\n\n"
) )
theDoc.setJustify(True) odt.setJustify(True)
theDoc.tokenizeText() odt.tokenizeText()
theDoc.initDocument() odt.initDocument()
theDoc.doConvert() odt.doConvert()
theDoc.closeDocument() odt.closeDocument()
assert theDoc.errData == [] assert odt.errData == []
assert xmlToText(theDoc._xText) == ( assert xmlToText(odt._xText) == (
'<office:text>' '<office:text>'
'<text:h text:style-name="Heading_20_3" text:outline-level="3">Scene</text:h>' '<text:h text:style-name="Heading_20_3" text:outline-level="3">Scene</text:h>'
'<text:p text:style-name="Text_20_body">Regular paragraph</text:p>' '<text:p text:style-name="Text_20_body">Regular paragraph</text:p>'
@@ -542,21 +572,21 @@ def testCoreToOdt_Convert(mockGUI):
'<text:p text:style-name="P9">Left Align</text:p>' '<text:p text:style-name="P9">Left Align</text:p>'
'</office:text>' '</office:text>'
) )
assert getStyle("P9")._pAttr["text-align"] == ["fo", "left"] assert getStyle("P9")._pAttr["text-align"] == ["fo", "left"] # type: ignore
# Page Breaks # Page Breaks
theDoc._text = ( odt._text = (
"## Chapter One\n\n" "## Chapter One\n\n"
"Text\n\n" "Text\n\n"
"## Chapter Two\n\n" "## Chapter Two\n\n"
"Text\n\n" "Text\n\n"
) )
theDoc.tokenizeText() odt.tokenizeText()
theDoc.initDocument() odt.initDocument()
theDoc.doConvert() odt.doConvert()
theDoc.closeDocument() odt.closeDocument()
assert theDoc.errData == [] assert odt.errData == []
assert xmlToText(theDoc._xText) == ( assert xmlToText(odt._xText) == (
'<office:text>' '<office:text>'
'<text:h text:style-name="P2" text:outline-level="2">Chapter One</text:h>' '<text:h text:style-name="P2" text:outline-level="2">Chapter One</text:h>'
'<text:p text:style-name="Text_20_body">Text</text:p>' '<text:p text:style-name="Text_20_body">Text</text:p>'
@@ -565,6 +595,23 @@ def testCoreToOdt_Convert(mockGUI):
'</office:text>' '</office:text>'
) )
# Test for issue #1412
# ====================
# See: https://github.com/vkbo/novelWriter/issues/1412
odt._text = "Test text \\**_bold_** and more."
odt.tokenizeText()
odt.initDocument()
odt.doConvert()
odt.closeDocument()
assert odt.errData == []
assert xmlToText(odt._xText) == (
'<office:text>'
'<text:p text:style-name="Text_20_body">Test text **<text:span text:style-name="T8">'
'bold</text:span>** and more.</text:p>'
'</office:text>'
)
# END Test testCoreToOdt_Convert # END Test testCoreToOdt_Convert
@@ -626,37 +673,45 @@ def testCoreToOdt_ConvertDirect(mockGUI):
@pytest.mark.core @pytest.mark.core
def testCoreToOdt_SaveFlat(mockGUI, fncPath, tstPaths): def testCoreToOdt_SaveFlat(mockGUI, fncPath, tstPaths):
"""Test the document save functions.""" """Test the document save functions."""
theProject = NWProject() project = NWProject()
theProject.data.setAuthor("Jane Smith") project.data.setAuthor("Jane Smith")
theProject.data.setName("Test Project") project.data.setName("Test Project")
theProject.data.setSaveCount(1234) project.data.setSaveCount(1234)
theProject.data.setEditTime(3674096) project.data.setEditTime(3674096)
theDoc = ToOdt(theProject, isFlat=True) odt = ToOdt(project, isFlat=True)
theDoc._isNovel = True odt._isNovel = True
theDoc._dLanguage = "" odt._dLanguage = ""
theDoc.setLanguage(None) odt.setLanguage(None) # type: ignore
assert theDoc._dLanguage == "" assert odt._dLanguage == ""
theDoc.setLanguage("nb_NO") odt.setLanguage("nb_NO")
assert theDoc._dLanguage == "nb" assert odt._dLanguage == "nb"
theDoc.setColourHeaders(True) odt.setColourHeaders(True)
theDoc._text = ( odt.setPageLayout(148, 210, 20, 18, 17, 15)
assert odt._mDocWidth == "14.800cm"
assert odt._mDocHeight == "21.000cm"
assert odt._mDocTop == "2.000cm"
assert odt._mDocBtm == "1.800cm"
assert odt._mDocLeft == "1.700cm"
assert odt._mDocRight == "1.500cm"
odt._text = (
"## Chapter One\n\n" "## Chapter One\n\n"
"Text\n\n" "Text\n\n"
"## Chapter Two\n\n" "## Chapter Two\n\n"
"Text\n\n" "Text\n\n"
) )
theDoc.tokenizeText() odt.tokenizeText()
theDoc.initDocument() odt.initDocument()
theDoc.doConvert() odt.doConvert()
theDoc.closeDocument() odt.closeDocument()
flatFile = fncPath / "document.fodt" flatFile = fncPath / "document.fodt"
testFile = tstPaths.outDir / "coreToOdt_SaveFlat_document.fodt" testFile = tstPaths.outDir / "coreToOdt_SaveFlat_document.fodt"
compFile = tstPaths.refDir / "coreToOdt_SaveFlat_document.fodt" compFile = tstPaths.refDir / "coreToOdt_SaveFlat_document.fodt"
theDoc.saveFlatXML(flatFile) odt.saveFlatXML(flatFile)
assert flatFile.exists() assert flatFile.exists()
copyfile(flatFile, testFile) copyfile(flatFile, testFile)
@@ -668,29 +723,29 @@ def testCoreToOdt_SaveFlat(mockGUI, fncPath, tstPaths):
@pytest.mark.core @pytest.mark.core
def testCoreToOdt_SaveFull(mockGUI, fncPath, tstPaths): def testCoreToOdt_SaveFull(mockGUI, fncPath, tstPaths):
"""Test the document save functions.""" """Test the document save functions."""
theProject = NWProject() project = NWProject()
theProject.data.setAuthor("Jane Smith") project.data.setAuthor("Jane Smith")
theProject.data.setName("Test Project") project.data.setName("Test Project")
theProject.data.setSaveCount(1234) project.data.setSaveCount(1234)
theProject.data.setEditTime(3674096) project.data.setEditTime(3674096)
theDoc = ToOdt(theProject, isFlat=False) odt = ToOdt(project, isFlat=False)
theDoc._isNovel = True odt._isNovel = True
theDoc._text = ( odt._text = (
"## Chapter One\n\n" "## Chapter One\n\n"
"Text\n\n" "Text\n\n"
"## Chapter Two\n\n" "## Chapter Two\n\n"
"Text\n\n" "Text\n\n"
) )
theDoc.tokenizeText() odt.tokenizeText()
theDoc.initDocument() odt.initDocument()
theDoc.doConvert() odt.doConvert()
theDoc.closeDocument() odt.closeDocument()
fullFile = fncPath / "document.odt" fullFile = fncPath / "document.odt"
theDoc.saveOpenDocText(fullFile) odt.saveOpenDocText(fullFile)
assert fullFile.exists() assert fullFile.exists()
assert zipfile.is_zipfile(fullFile) assert zipfile.is_zipfile(fullFile)
@@ -745,26 +800,22 @@ def testCoreToOdt_SaveFull(mockGUI, fncPath, tstPaths):
@pytest.mark.core @pytest.mark.core
def testCoreToOdt_Format(mockGUI): def testCoreToOdt_Format(mockGUI):
"""Test the formatters for the ToOdt class.""" """Test the formatters for the ToOdt class."""
theProject = NWProject() project = NWProject()
theDoc = ToOdt(theProject, isFlat=True) odt = ToOdt(project, isFlat=True)
assert theDoc._formatSynopsis("synopsis text") == ( assert odt._formatSynopsis("synopsis text") == (
"**Synopsis:** synopsis text", "Synopsis: synopsis text", [(0, ToOdt.FMT_B_B), (9, ToOdt.FMT_B_E)]
"_B b_ "
) )
assert theDoc._formatComments("comment text") == ( assert odt._formatComments("comment text") == (
"**Comment:** comment text", "Comment: comment text", [(0, ToOdt.FMT_B_B), (8, ToOdt.FMT_B_E)]
"_B b_ "
) )
assert theDoc._formatKeywords("") == ("", "") assert odt._formatKeywords("") == ("", [])
assert theDoc._formatKeywords("tag: Jane") == ( assert odt._formatKeywords("tag: Jane") == (
"**Tag:** Jane", "Tag: Jane", [(0, ToOdt.FMT_B_B), (4, ToOdt.FMT_B_E)]
"_B b_ "
) )
assert theDoc._formatKeywords("char: Bod, Jane") == ( assert odt._formatKeywords("char: Bod, Jane") == (
"**Characters:** Bod, Jane", "Characters: Bod, Jane", [(0, ToOdt.FMT_B_B), (11, ToOdt.FMT_B_E)]
"_B b_ "
) )
# END Test testCoreToOdt_Format # END Test testCoreToOdt_Format
@@ -1016,6 +1067,17 @@ def testCoreToOdt_ODTTextStyle():
txtStyle.setFontStyle("stuff") txtStyle.setFontStyle("stuff")
assert txtStyle._tAttr["font-style"] == ["fo", None] assert txtStyle._tAttr["font-style"] == ["fo", None]
# Text Position
assert txtStyle._tAttr["text-position"] == ["style", None]
txtStyle.setTextPosition("stuff")
assert txtStyle._tAttr["text-position"] == ["style", None]
txtStyle.setTextPosition("super")
assert txtStyle._tAttr["text-position"] == ["style", "super 58%"]
txtStyle.setTextPosition("sub")
assert txtStyle._tAttr["text-position"] == ["style", "sub 58%"]
txtStyle.setTextPosition("stuff")
assert txtStyle._tAttr["text-position"] == ["style", None]
# Line Through Style # Line Through Style
assert txtStyle._tAttr["text-line-through-style"] == ["style", None] assert txtStyle._tAttr["text-line-through-style"] == ["style", None]
txtStyle.setStrikeStyle("stuff") txtStyle.setStrikeStyle("stuff")
@@ -1040,18 +1102,49 @@ def testCoreToOdt_ODTTextStyle():
txtStyle.setStrikeType("stuff") txtStyle.setStrikeType("stuff")
assert txtStyle._tAttr["text-line-through-type"] == ["style", None] assert txtStyle._tAttr["text-line-through-type"] == ["style", None]
# Underline Style
assert txtStyle._tAttr["text-underline-style"] == ["style", None]
txtStyle.setUnderlineStyle("stuff")
assert txtStyle._tAttr["text-underline-style"] == ["style", None]
txtStyle.setUnderlineStyle("none")
assert txtStyle._tAttr["text-underline-style"] == ["style", "none"]
txtStyle.setUnderlineStyle("solid")
assert txtStyle._tAttr["text-underline-style"] == ["style", "solid"]
txtStyle.setUnderlineStyle("stuff")
assert txtStyle._tAttr["text-underline-style"] == ["style", None]
# Underline Width
assert txtStyle._tAttr["text-underline-width"] == ["style", None]
txtStyle.setUnderlineWidth("stuff")
assert txtStyle._tAttr["text-underline-width"] == ["style", None]
txtStyle.setUnderlineWidth("auto")
assert txtStyle._tAttr["text-underline-width"] == ["style", "auto"]
txtStyle.setUnderlineWidth("stuff")
assert txtStyle._tAttr["text-underline-width"] == ["style", None]
# Underline Colour
assert txtStyle._tAttr["text-underline-color"] == ["style", None]
txtStyle.setUnderlineColour("stuff")
assert txtStyle._tAttr["text-underline-color"] == ["style", None]
txtStyle.setUnderlineColour("font-color")
assert txtStyle._tAttr["text-underline-color"] == ["style", "font-color"]
txtStyle.setUnderlineColour("stuff")
assert txtStyle._tAttr["text-underline-color"] == ["style", None]
# Pack XML # Pack XML
# ======== # ========
txtStyle.setFontWeight("bold") txtStyle.setFontWeight("bold")
txtStyle.setFontStyle("italic") txtStyle.setFontStyle("italic")
txtStyle.setTextPosition("super")
txtStyle.setStrikeStyle("solid") txtStyle.setStrikeStyle("solid")
txtStyle.setStrikeType("single") txtStyle.setStrikeType("single")
xStyle = ET.Element("test") xStyle = ET.Element("test")
txtStyle.packXML(xStyle, "test") txtStyle.packXML(xStyle, "test")
assert xmlToText(xStyle) == ( assert xmlToText(xStyle) == (
'<test><style:style style:name="test" style:family="text"><style:text-properties ' '<test><style:style style:name="test" style:family="text"><style:text-properties '
'fo:font-weight="bold" fo:font-style="italic" style:text-line-through-style="solid" ' 'fo:font-weight="bold" fo:font-style="italic" style:text-position="super 58%" '
'style:text-line-through-type="single" /></style:style></test>' 'style:text-line-through-style="solid" style:text-line-through-type="single" />'
'</style:style></test>'
) )
# END Test testCoreToOdt_ODTTextStyle # END Test testCoreToOdt_ODTTextStyle