Update test coverage
This commit is contained in:
@@ -903,7 +903,6 @@ class ToDocX(Tokenizer):
|
||||
"""Populate document.xml."""
|
||||
rId = self._nextRelId()
|
||||
xRoot = xmlElement(_wTag("document"))
|
||||
xRoot.set("xmlns:w14", "http://schemas.microsoft.com/office/word/2010/wordml")
|
||||
xBody = xmlSubElem(xRoot, _wTag("body"))
|
||||
self._rels["document.xml"] = DocXXmlRel(
|
||||
rId=rId,
|
||||
|
||||
@@ -2065,12 +2065,8 @@ class GuiDocEditor(QPlainTextEdit):
|
||||
feature for French, Spanish, etc, so it doesn't insert a
|
||||
space before colons in meta data lines. See issue #1090.
|
||||
"""
|
||||
if char == ":" and len(text) > 1:
|
||||
if text[0] == "@":
|
||||
return False
|
||||
if text[0] == "%":
|
||||
if text[1:].lstrip()[:9].lower() == "synopsis:":
|
||||
return False
|
||||
if char == ":" and len(text) > 1 and text[0] == "@":
|
||||
return False
|
||||
return True
|
||||
|
||||
def _autoSelect(self) -> QTextCursor:
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<?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:number="urn:oasis:names:tc:opendocument:xmlns:datastyle: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:number="urn:oasis:names:tc:opendocument:xmlns:datastyle: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" xmlns:xlink="http://www.w3.org/1999/xlink" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text">
|
||||
<office:meta>
|
||||
<meta:creation-date>2024-10-29T09:33:22</meta:creation-date>
|
||||
<meta:creation-date>2024-10-29T23:24:19</meta:creation-date>
|
||||
<meta:generator>novelWriter/2.6a3</meta:generator>
|
||||
<meta:initial-creator>Jane Smith</meta:initial-creator>
|
||||
<meta:editing-cycles>1234</meta:editing-cycles>
|
||||
<meta:editing-duration>P42DT12H34M56S</meta:editing-duration>
|
||||
<dc:title>Test Project</dc:title>
|
||||
<dc:date>2024-10-29T09:33:22</dc:date>
|
||||
<dc:date>2024-10-29T23:24:19</dc:date>
|
||||
<dc:creator>Jane Smith</dc:creator>
|
||||
</office:meta>
|
||||
<office:font-face-decls>
|
||||
@@ -82,6 +82,12 @@
|
||||
<style:style style:name="P1" style:family="paragraph" style:parent-style-name="Heading_20_2">
|
||||
<style:paragraph-properties fo:break-before="page" />
|
||||
</style:style>
|
||||
<style:style style:name="T1" style:family="text">
|
||||
<style:text-properties fo:font-weight="bold" />
|
||||
</style:style>
|
||||
<style:style style:name="T2" style:family="text">
|
||||
<style:text-properties fo:color="#4271ae" style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color" />
|
||||
</style:style>
|
||||
</office:automatic-styles>
|
||||
<office:master-styles>
|
||||
<style:master-page style:name="Standard" style:page-layout-name="PM1">
|
||||
@@ -95,10 +101,30 @@
|
||||
</office:master-styles>
|
||||
<office:body>
|
||||
<office:text>
|
||||
<text:h text:style-name="Heading_20_2" text:outline-level="2">Chapter One</text:h>
|
||||
<text:p text:style-name="Text_20_body">Text</text:p>
|
||||
<text:user-field-decls>
|
||||
<text:user-field-decl office:value-type="float" office:value="3" text:name="ManuscriptTitleCount" />
|
||||
<text:user-field-decl office:value-type="float" office:value="3" text:name="ManuscriptParagraphCount" />
|
||||
<text:user-field-decl office:value-type="float" office:value="217" text:name="ManuscriptAllWords" />
|
||||
<text:user-field-decl office:value-type="float" office:value="211" text:name="ManuscriptTextWords" />
|
||||
<text:user-field-decl office:value-type="float" office:value="6" text:name="ManuscriptTitleWords" />
|
||||
<text:user-field-decl office:value-type="float" office:value="1471" text:name="ManuscriptAllChars" />
|
||||
<text:user-field-decl office:value-type="float" office:value="1441" text:name="ManuscriptTextChars" />
|
||||
<text:user-field-decl office:value-type="float" office:value="30" text:name="ManuscriptTitleChars" />
|
||||
<text:user-field-decl office:value-type="float" office:value="1258" text:name="ManuscriptAllWordChars" />
|
||||
<text:user-field-decl office:value-type="float" office:value="1231" text:name="ManuscriptTextWordChars" />
|
||||
<text:user-field-decl office:value-type="float" office:value="27" text:name="ManuscriptTitleWordChars" />
|
||||
</text:user-field-decls>
|
||||
<text:p text:style-name="Title">My Novel</text:p>
|
||||
<text:p text:style-name="Text_20_body"><text:span text:style-name="T1">Word Count: </text:span><text:span text:style-name="T1"><text:user-field-get style:data-style-name="N0" text:name="ManuscriptAllWords">0</text:user-field-get></text:span><text:line-break /><text:user-field-get style:data-style-name="N0" text:name="ManuscriptParagraphCount">0</text:user-field-get> paragrphs<text:line-break />Web: <text:a xlink:type="simple" xlink:href="http://example.com" text:style-name="T2">http://example.com</text:a></text:p>
|
||||
<text:h text:style-name="P1" text:outline-level="2">Chapter One</text:h>
|
||||
<text:p text:style-name="Text_20_body">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc maximus justo non dictum commodo. Curabitur lacinia tempor orci vel luctus. Phasellus porta metus eu massa luctus, eget euismod risus rhoncus. Vestibulum sed arcu nisi. Maecenas pretium facilisis velit, vel semper lacus aliquam sit amet. Vestibulum vulputate neque ligula, rhoncus blandit turpis consequat id. Mauris sagittis vehicula imperdiet. Duis sed nunc pretium, ornare purus vel, sodales augue. Maecenas a suscipit risus. Quisque volutpat justo eleifend est ullamcorper fermentum. Donec ullamcorper et tortor a laoreet. Nam id risus nisi. Vivamus non imperdiet erat, sit amet imperdiet felis. Mauris vitae neque et est aliquam scelerisque non non ipsum.</text:p>
|
||||
<text:h text:style-name="P1" text:outline-level="2">Chapter Two</text:h>
|
||||
<text:p text:style-name="Text_20_body">Text</text:p>
|
||||
<text:p text:style-name="Text_20_body">Nullam laoreet lorem nec malesuada vehicula. Vivamus tempus sodales lectus sed viverra. Aenean lacinia sollicitudin quam, quis tempus eros suscipit id. Duis sed rutrum nisi, ut pulvinar magna. Nam et cursus tortor. Phasellus ac odio tellus. Nullam in iaculis ipsum. Vivamus ante sem, ultricies sed varius quis, tristique nec tellus. Nullam eu urna vitae lacus hendrerit gravida. Quisque pulvinar erat ex, id efficitur velit sodales vitae. Proin vestibulum, sapien eget mattis euismod, tortor quam viverra risus, at congue mauris tortor eu nunc. Mauris pellentesque elit leo, quis eleifend sem placerat a. Vivamus iaculis dui eget tellus volutpat, ac varius nisi facilisis.<text:note text:id="ftn1" text:note-class="footnote">
|
||||
<text:note-citation>1</text:note-citation>
|
||||
<text:note-body>
|
||||
<text:p text:style-name="Footnote">Lorem ipsum</text:p>
|
||||
</text:note-body>
|
||||
</text:note></text:p>
|
||||
</office:text>
|
||||
</office:body>
|
||||
</office:document>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<office:document-content xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible: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:document-content xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible: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" xmlns:xlink="http://www.w3.org/1999/xlink" office:version="1.3">
|
||||
<office:font-face-decls>
|
||||
<style:font-face style:name="Liberation Serif" style:font-pitch="variable" />
|
||||
</office:font-face-decls>
|
||||
@@ -7,13 +7,47 @@
|
||||
<style:style style:name="P1" style:family="paragraph" style:parent-style-name="Heading_20_2">
|
||||
<style:paragraph-properties fo:break-before="page" />
|
||||
</style:style>
|
||||
<style:style style:name="T1" style:family="text">
|
||||
<style:text-properties fo:font-weight="bold" />
|
||||
</style:style>
|
||||
<style:style style:name="T2" style:family="text">
|
||||
<style:text-properties fo:color="#4271ae" style:text-underline-style="solid" style:text-underline-width="auto" style:text-underline-color="font-color" />
|
||||
</style:style>
|
||||
</office:automatic-styles>
|
||||
<office:body>
|
||||
<office:text>
|
||||
<text:h text:style-name="Heading_20_2" text:outline-level="2">Chapter One</text:h>
|
||||
<text:p text:style-name="Text_20_body">Text</text:p>
|
||||
<text:user-field-decls>
|
||||
<text:user-field-decl office:value-type="float" office:value="3" text:name="ManuscriptTitleCount" />
|
||||
<text:user-field-decl office:value-type="float" office:value="3" text:name="ManuscriptParagraphCount" />
|
||||
<text:user-field-decl office:value-type="float" office:value="217" text:name="ManuscriptAllWords" />
|
||||
<text:user-field-decl office:value-type="float" office:value="211" text:name="ManuscriptTextWords" />
|
||||
<text:user-field-decl office:value-type="float" office:value="6" text:name="ManuscriptTitleWords" />
|
||||
<text:user-field-decl office:value-type="float" office:value="1471" text:name="ManuscriptAllChars" />
|
||||
<text:user-field-decl office:value-type="float" office:value="1441" text:name="ManuscriptTextChars" />
|
||||
<text:user-field-decl office:value-type="float" office:value="30" text:name="ManuscriptTitleChars" />
|
||||
<text:user-field-decl office:value-type="float" office:value="1258" text:name="ManuscriptAllWordChars" />
|
||||
<text:user-field-decl office:value-type="float" office:value="1231" text:name="ManuscriptTextWordChars" />
|
||||
<text:user-field-decl office:value-type="float" office:value="27" text:name="ManuscriptTitleWordChars" />
|
||||
</text:user-field-decls>
|
||||
<text:p text:style-name="Title">My Novel</text:p>
|
||||
<text:p text:style-name="Text_20_body">
|
||||
<text:span text:style-name="T1">Word Count: </text:span>
|
||||
<text:span text:style-name="T1">
|
||||
<text:user-field-get style:data-style-name="N0" text:name="ManuscriptAllWords">0</text:user-field-get>
|
||||
</text:span>
|
||||
<text:line-break />
|
||||
<text:user-field-get style:data-style-name="N0" text:name="ManuscriptParagraphCount">0</text:user-field-get> paragrphs<text:line-break />Web: <text:a xlink:type="simple" xlink:href="http://example.com" text:style-name="T2">http://example.com</text:a>
|
||||
</text:p>
|
||||
<text:h text:style-name="P1" text:outline-level="2">Chapter One</text:h>
|
||||
<text:p text:style-name="Text_20_body">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc maximus justo non dictum commodo. Curabitur lacinia tempor orci vel luctus. Phasellus porta metus eu massa luctus, eget euismod risus rhoncus. Vestibulum sed arcu nisi. Maecenas pretium facilisis velit, vel semper lacus aliquam sit amet. Vestibulum vulputate neque ligula, rhoncus blandit turpis consequat id. Mauris sagittis vehicula imperdiet. Duis sed nunc pretium, ornare purus vel, sodales augue. Maecenas a suscipit risus. Quisque volutpat justo eleifend est ullamcorper fermentum. Donec ullamcorper et tortor a laoreet. Nam id risus nisi. Vivamus non imperdiet erat, sit amet imperdiet felis. Mauris vitae neque et est aliquam scelerisque non non ipsum.</text:p>
|
||||
<text:h text:style-name="P1" text:outline-level="2">Chapter Two</text:h>
|
||||
<text:p text:style-name="Text_20_body">Text</text:p>
|
||||
<text:p text:style-name="Text_20_body">Nullam laoreet lorem nec malesuada vehicula. Vivamus tempus sodales lectus sed viverra. Aenean lacinia sollicitudin quam, quis tempus eros suscipit id. Duis sed rutrum nisi, ut pulvinar magna. Nam et cursus tortor. Phasellus ac odio tellus. Nullam in iaculis ipsum. Vivamus ante sem, ultricies sed varius quis, tristique nec tellus. Nullam eu urna vitae lacus hendrerit gravida. Quisque pulvinar erat ex, id efficitur velit sodales vitae. Proin vestibulum, sapien eget mattis euismod, tortor quam viverra risus, at congue mauris tortor eu nunc. Mauris pellentesque elit leo, quis eleifend sem placerat a. Vivamus iaculis dui eget tellus volutpat, ac varius nisi facilisis.<text:note text:id="ftn1" text:note-class="footnote">
|
||||
<text:note-citation>1</text:note-citation>
|
||||
<text:note-body>
|
||||
<text:p text:style-name="Footnote">Lorem ipsum</text:p>
|
||||
</text:note-body>
|
||||
</text:note>
|
||||
</text:p>
|
||||
</office:text>
|
||||
</office:body>
|
||||
</office:document-content>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
%%~name: New Scene
|
||||
%%~path: 000000000000d/000000000000f
|
||||
%%~kind: NOVEL/DOCUMENT
|
||||
%%~hash: 0566024662fb6ed7ed645717addd64dabf058915
|
||||
%%~date: 2024-10-27 13:03:13/2024-10-27 13:03:18
|
||||
%%~hash: 89b54ddaec2fddfd220e91dd438c84fb3aef9fc2
|
||||
%%~date: 2024-10-30 00:07:21/2024-10-30 00:07:26
|
||||
# Novel
|
||||
|
||||
## Chapter
|
||||
@@ -40,7 +40,9 @@ Some “ double quoted text with spaces padded ”.
|
||||
|
||||
@object: NoSpaceAdded
|
||||
|
||||
% synopsis: No space before this colon.
|
||||
% synopsis : Space before this is OK.
|
||||
|
||||
%Footnote.abc : A simple footnote.
|
||||
|
||||
Add space before this colon : See?
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.6a3" hexVersion="0x020600a3" fileVersion="1.5" fileRevision="4" timeStamp="2024-10-27 13:01:24">
|
||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="3" autoCount="2" editTime="6">
|
||||
<novelWriterXML appVersion="2.6a3" hexVersion="0x020600a3" fileVersion="1.5" fileRevision="4" timeStamp="2024-10-30 00:06:45">
|
||||
<project id="d0f3fe10-c6e6-4310-8bfd-181eb4224eed" saveCount="3" autoCount="2" editTime="5">
|
||||
<name>New Project</name>
|
||||
<author>Jane Doe</author>
|
||||
</project>
|
||||
@@ -46,7 +46,7 @@
|
||||
<name status="s000000" import="i000004" active="yes">New Chapter</name>
|
||||
</item>
|
||||
<item handle="000000000000f" parent="000000000000d" root="0000000000008" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="no" heading="H1" charCount="918" wordCount="154" paraCount="17" cursorPos="1140" />
|
||||
<meta expanded="no" heading="H1" charCount="918" wordCount="154" paraCount="17" cursorPos="1174" />
|
||||
<name status="s000000" import="i000004" active="yes">New Scene</name>
|
||||
</item>
|
||||
<item handle="0000000000009" parent="None" root="0000000000009" order="1" type="ROOT" class="PLOT">
|
||||
|
||||
@@ -1490,5 +1490,6 @@ def testCoreIndex_processComment():
|
||||
# Padding with term
|
||||
assert processComment("%note.term: Hi") == (nwComment.NOTE, "term", "Hi", 6, 11)
|
||||
assert processComment("% note.term: Hi") == (nwComment.NOTE, "term", "Hi", 7, 12)
|
||||
assert processComment("% note.term : Hi") == (nwComment.NOTE, "term", "Hi", 7, 13)
|
||||
assert processComment("% note. term : Hi") == (nwComment.PLAIN, "", "note. term : Hi", 0, 0)
|
||||
assert processComment("% note . term : Hi") == (nwComment.PLAIN, "", "note . term : Hi", 0, 0)
|
||||
|
||||
@@ -32,28 +32,9 @@ from novelwriter.core.docbuild import NWBuildDocument
|
||||
from novelwriter.core.project import NWProject
|
||||
from novelwriter.enum import nwBuildFmt
|
||||
from novelwriter.formats.shared import BlockFmt, BlockTyp
|
||||
from novelwriter.formats.todocx import ToDocX, _mkTag, _wTag
|
||||
from novelwriter.formats.todocx import OOXML_SCM, ToDocX, _mkTag, _wTag
|
||||
|
||||
from tests.tools import DOCX_IGNORE, cmpFiles
|
||||
|
||||
OOXML_SCM = "http://schemas.openxmlformats.org"
|
||||
XML_NS = [
|
||||
f' xmlns:r="{OOXML_SCM}/officeDocument/2006/relationships"',
|
||||
f' xmlns:w="{OOXML_SCM}/wordprocessingml/2006/main"',
|
||||
f' xmlns:cp="{OOXML_SCM}/package/2006/metadata/core-properties"',
|
||||
' xmlns:dc="http://purl.org/dc/elements/1.1/"',
|
||||
' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"',
|
||||
' xmlns:xml="http://www.w3.org/XML/1998/namespace"',
|
||||
' xmlns:dcterms="http://purl.org/dc/terms/"',
|
||||
]
|
||||
|
||||
|
||||
def xmlToText(xElem):
|
||||
"""Get the text content of an XML element."""
|
||||
rTxt = ET.tostring(xElem, encoding="utf-8", xml_declaration=False).decode()
|
||||
for ns in XML_NS:
|
||||
rTxt = rTxt.replace(ns, "")
|
||||
return rTxt
|
||||
from tests.tools import DOCX_IGNORE, cmpFiles, xmlToText
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
@@ -610,6 +591,49 @@ def testFmtToDocX_Footnotes(mockGUI):
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testFmtToDocX_Fields(mockGUI):
|
||||
"""Test formatting of footnotes."""
|
||||
project = NWProject()
|
||||
doc = ToDocX(project)
|
||||
doc.initDocument()
|
||||
|
||||
# Field Builder
|
||||
xNode = doc._generateField("a:b", 0x00)
|
||||
assert isinstance(xNode, ET.Element)
|
||||
assert xmlToText(xNode) == "<w:r><w:rPr /><w:t>0</w:t></w:r>"
|
||||
assert doc._usedFields == [(xNode.find(_wTag("t")), "b")]
|
||||
|
||||
assert doc._generateField("a", 0x00) is None
|
||||
|
||||
# Full Processing
|
||||
doc._text = (
|
||||
"Word Count: [field:allWords]\n"
|
||||
"Character Count: [field:allChars]\n"
|
||||
"Chicken Count: [field:allChickens]\n"
|
||||
)
|
||||
doc.tokenizeText()
|
||||
doc.doConvert()
|
||||
doc.countStats()
|
||||
doc._documentXml(None, None)
|
||||
assert xmlToText(doc._files["document.xml"].xml) == (
|
||||
'<w:document><w:body><w:p><w:pPr><w:pStyle w:val="Normal" /></w:pPr>'
|
||||
'<w:r><w:rPr /><w:t xml:space="preserve">Word Count: </w:t></w:r>'
|
||||
'<w:r><w:rPr /><w:t>6</w:t></w:r>'
|
||||
'<w:r><w:rPr /><w:br /><w:t xml:space="preserve">Character Count: </w:t></w:r>'
|
||||
'<w:r><w:rPr /><w:t>46</w:t></w:r>'
|
||||
'<w:r><w:rPr /><w:br /><w:t xml:space="preserve">Chicken Count: </w:t></w:r>'
|
||||
'<w:r><w:rPr /><w:t>0</w:t></w:r>'
|
||||
'</w:p><w:sectPr>'
|
||||
'<w:footnotePr><w:numFmt w:val="decimal" /></w:footnotePr>'
|
||||
'<w:pgSz w:w="11905" w:h="16837" w:orient="portrait" />'
|
||||
'<w:pgMar w:top="1133" w:right="1133" w:bottom="1133" w:left="1133" '
|
||||
'w:header="566" w:footer="0" w:gutter="0" />'
|
||||
'<w:pgNumType w:start="1" w:fmt="decimal" /><w:titlePg />'
|
||||
'</w:sectPr></w:body></w:document>'
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testFmtToDocX_SaveDocument(mockGUI, prjLipsum, fncPath, tstPaths):
|
||||
"""Test document output."""
|
||||
|
||||
@@ -45,7 +45,7 @@ class BareTokenizer(Tokenizer):
|
||||
super().doConvert() # type: ignore (deliberate check)
|
||||
|
||||
def closeDocument(self):
|
||||
super().doConvert() # type: ignore (deliberate check)
|
||||
super().closeDocument() # type: ignore (deliberate check)
|
||||
|
||||
def saveDocument(self, path) -> None:
|
||||
super().saveDocument(path) # type: ignore (deliberate check)
|
||||
@@ -60,6 +60,9 @@ def testFmtToken_Abstracts(mockGUI, tstPaths):
|
||||
with pytest.raises(NotImplementedError):
|
||||
tokens.doConvert()
|
||||
|
||||
with pytest.raises(NotImplementedError):
|
||||
tokens.closeDocument()
|
||||
|
||||
with pytest.raises(NotImplementedError):
|
||||
tokens.saveDocument(tstPaths)
|
||||
|
||||
|
||||
@@ -35,26 +35,7 @@ from novelwriter.core.project import NWProject
|
||||
from novelwriter.formats.shared import BlockFmt, BlockTyp, TextFmt
|
||||
from novelwriter.formats.toodt import ODTParagraphStyle, ODTTextStyle, ToOdt, XMLParagraph, _mkTag
|
||||
|
||||
from tests.tools import ODT_IGNORE, cmpFiles
|
||||
|
||||
XML_NS = [
|
||||
' 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"',
|
||||
' xmlns:xlink="http://www.w3.org/1999/xlink"',
|
||||
]
|
||||
|
||||
|
||||
def xmlToText(xElem):
|
||||
"""Get the text content of an XML element."""
|
||||
rTxt = ET.tostring(xElem, encoding="utf-8", xml_declaration=False).decode()
|
||||
for ns in XML_NS:
|
||||
rTxt = rTxt.replace(ns, "")
|
||||
return rTxt
|
||||
from tests.tools import ODT_IGNORE, cmpFiles, xmlToText
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
@@ -237,6 +218,46 @@ def testFmtToOdt_TextFormatting(mockGUI):
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testFmtToOdt_Fields(mockGUI):
|
||||
"""Test formatting of footnotes."""
|
||||
project = NWProject()
|
||||
odt = ToOdt(project, True)
|
||||
odt.initDocument()
|
||||
|
||||
# Field Builder
|
||||
xNode = odt._generateField("a:allWords", 0x00)
|
||||
assert isinstance(xNode, ET.Element)
|
||||
assert xmlToText(xNode) == (
|
||||
'<text:user-field-getstyle:data-style-name="N0" text:name="ManuscriptAllWords">'
|
||||
'0</text:user-field-get>'
|
||||
)
|
||||
# assert odt._usedFields == [(xNode.find(_wTag("t")), "b")]
|
||||
|
||||
assert odt._generateField("a", 0x00) is None
|
||||
|
||||
# Full Processing
|
||||
odt._text = (
|
||||
"Word Count: [field:allWords]\n"
|
||||
"Character Count: [field:allChars]\n"
|
||||
"Chicken Count: [field:allChickens]\n"
|
||||
)
|
||||
odt.tokenizeText()
|
||||
odt.doConvert()
|
||||
odt.countStats()
|
||||
assert xmlToText(odt._xBody) == (
|
||||
'<office:body><office:text>'
|
||||
'<text:p text:style-name="Text_20_body">'
|
||||
'Word Count: <text:user-field-get style:data-style-name="N0" '
|
||||
'text:name="ManuscriptAllWords">0</text:user-field-get><text:line-break />'
|
||||
'Character Count: <text:user-field-get style:data-style-name="N0" '
|
||||
'text:name="ManuscriptAllChars">0</text:user-field-get><text:line-break />'
|
||||
'Chicken Count: <text:user-field-get style:data-style-name="N0" '
|
||||
'text:name="ManuscriptAllChickens">0</text:user-field-get></text:p>'
|
||||
'</office:text></office:body>'
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testFmtToOdt_DialogueFormatting(mockGUI):
|
||||
"""Test formatting of dialogue."""
|
||||
@@ -770,7 +791,7 @@ def testFmtToOdt_ConvertDirect(mockGUI):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testFmtToOdt_SaveFlat(mockGUI, fncPath, tstPaths):
|
||||
def testFmtToOdt_SaveFlat(mockGUI, fncPath, tstPaths, ipsumText):
|
||||
"""Test the document save functions."""
|
||||
project = NWProject()
|
||||
project.data.setAuthor("Jane Smith")
|
||||
@@ -796,14 +817,20 @@ def testFmtToOdt_SaveFlat(mockGUI, fncPath, tstPaths):
|
||||
assert odt._mDocRight == "1.500cm"
|
||||
|
||||
odt._text = (
|
||||
"#! My Novel\n\n"
|
||||
"**Word Count: [field:allWords]**\n"
|
||||
"[field:paragraphCount] paragrphs\n"
|
||||
"Web: http://example.com\n\n"
|
||||
"## Chapter One\n\n"
|
||||
"Text\n\n"
|
||||
f"{ipsumText[0]}\n\n"
|
||||
"## Chapter Two\n\n"
|
||||
"Text\n\n"
|
||||
f"{ipsumText[1]}[footnote:abc]\n\n"
|
||||
"%Footnote.abc: Lorem ipsum\n\n"
|
||||
)
|
||||
odt.tokenizeText()
|
||||
odt.initDocument()
|
||||
odt.doConvert()
|
||||
odt.countStats()
|
||||
odt.closeDocument()
|
||||
|
||||
flatFile = fncPath / "document.fodt"
|
||||
@@ -818,7 +845,7 @@ def testFmtToOdt_SaveFlat(mockGUI, fncPath, tstPaths):
|
||||
|
||||
|
||||
@pytest.mark.core
|
||||
def testFmtToOdt_SaveFull(mockGUI, fncPath, tstPaths):
|
||||
def testFmtToOdt_SaveFull(mockGUI, fncPath, tstPaths, ipsumText):
|
||||
"""Test the document save functions."""
|
||||
project = NWProject()
|
||||
project.data.setAuthor("Jane Smith")
|
||||
@@ -833,14 +860,20 @@ def testFmtToOdt_SaveFull(mockGUI, fncPath, tstPaths):
|
||||
odt.setHeaderFormat(f"{nwHeadFmt.DOC_PROJECT} - {nwHeadFmt.DOC_AUTHOR}", 0)
|
||||
|
||||
odt._text = (
|
||||
"#! My Novel\n\n"
|
||||
"**Word Count: [field:allWords]**\n"
|
||||
"[field:paragraphCount] paragrphs\n"
|
||||
"Web: http://example.com\n\n"
|
||||
"## Chapter One\n\n"
|
||||
"Text\n\n"
|
||||
f"{ipsumText[0]}\n\n"
|
||||
"## Chapter Two\n\n"
|
||||
"Text\n\n"
|
||||
f"{ipsumText[1]}[footnote:abc]\n\n"
|
||||
"%Footnote.abc: Lorem ipsum\n\n"
|
||||
)
|
||||
odt.tokenizeText()
|
||||
odt.initDocument()
|
||||
odt.doConvert()
|
||||
odt.countStats()
|
||||
odt.closeDocument()
|
||||
|
||||
fullFile = fncPath / "document.odt"
|
||||
|
||||
@@ -2184,27 +2184,3 @@ def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, prjLipsum):
|
||||
assert docEditor.textCursor().selectedText() == ""
|
||||
|
||||
# qtbot.stop()
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testGuiEditor_StaticMethods():
|
||||
"""Test the document editor's static methods."""
|
||||
# Check the method that decides if it is allowed to insert a space
|
||||
# before a colon using the French, Spanish, etc language feature
|
||||
assert GuiDocEditor._allowSpaceBeforeColon("", "") is True
|
||||
assert GuiDocEditor._allowSpaceBeforeColon("", ":") is True
|
||||
assert GuiDocEditor._allowSpaceBeforeColon("some text", ":") is True
|
||||
|
||||
assert GuiDocEditor._allowSpaceBeforeColon("@:", ":") is False
|
||||
assert GuiDocEditor._allowSpaceBeforeColon("@>", ">") is True
|
||||
|
||||
assert GuiDocEditor._allowSpaceBeforeColon("%", ":") is True
|
||||
assert GuiDocEditor._allowSpaceBeforeColon("%:", ":") is True
|
||||
assert GuiDocEditor._allowSpaceBeforeColon("%synopsis:", ":") is False
|
||||
assert GuiDocEditor._allowSpaceBeforeColon("%Synopsis:", ":") is False
|
||||
assert GuiDocEditor._allowSpaceBeforeColon("% synopsis:", ":") is False
|
||||
assert GuiDocEditor._allowSpaceBeforeColon("% Synopsis:", ":") is False
|
||||
assert GuiDocEditor._allowSpaceBeforeColon("% synopsis:", ":") is False
|
||||
assert GuiDocEditor._allowSpaceBeforeColon("% Synopsis:", ":") is False
|
||||
assert GuiDocEditor._allowSpaceBeforeColon("%synopsis :", ":") is True
|
||||
assert GuiDocEditor._allowSpaceBeforeColon("%Synopsis :", ":") is True
|
||||
|
||||
@@ -474,7 +474,7 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, projPath, tstPaths, mockRnd):
|
||||
# Special Formatting
|
||||
# ==================
|
||||
|
||||
# Insert spaces before colon, but ignore tags and synopsis
|
||||
# Insert spaces before colon, but ignore tags
|
||||
docEditor._typPadBefore = ":"
|
||||
|
||||
for c in "@object: NoSpaceAdded":
|
||||
@@ -482,7 +482,12 @@ def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, projPath, tstPaths, mockRnd):
|
||||
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
||||
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
||||
|
||||
for c in "% synopsis: No space before this colon.":
|
||||
for c in "% synopsis: Space before this is OK.":
|
||||
qtbot.keyClick(docEditor, c, delay=KEY_DELAY)
|
||||
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
||||
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
||||
|
||||
for c in "%Footnote.abc: A simple footnote.":
|
||||
qtbot.keyClick(docEditor, c, delay=KEY_DELAY)
|
||||
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
||||
qtbot.keyClick(docEditor, Qt.Key.Key_Return, delay=KEY_DELAY)
|
||||
|
||||
@@ -21,6 +21,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
from __future__ import annotations
|
||||
|
||||
import shutil
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
@@ -110,6 +111,15 @@ def cmpFiles(
|
||||
return not diffFound
|
||||
|
||||
|
||||
def xmlToText(xElem):
|
||||
"""Get the text content of an XML element."""
|
||||
text = ET.tostring(xElem, encoding="utf-8", xml_declaration=False).decode()
|
||||
bits = text.partition(">")
|
||||
node = bits[0].partition(" ")
|
||||
rest = " ".join(x for x in node[2].split() if not x.startswith("xmlns")).replace("/", " /")
|
||||
return f"{node[0]}{rest}{bits[1]}{bits[2]}"
|
||||
|
||||
|
||||
def readFile(fileName: str | Path):
|
||||
"""Returns the content of a file as a string."""
|
||||
with open(fileName, mode="r", encoding="utf-8") as inFile:
|
||||
|
||||
Reference in New Issue
Block a user