Fix and optimise code (#904)

* Fix bug in early error reporting in main init
* Update docstrings and optimise code in GuiMain
* Update docstrings and optimise code in Config
* Update docstrings and optimise code in common module
* Update docstrings and optimise code in main project classes
* Update the OptionState class
* Update the spell checker class
* Update the file converter classes and extend tests
* Update the about, merge, split and item editor classes and extend tests
* Update item editor test
* Update about dialog tests
* Some minor test cleanup
* Fix typo and add clarification in contributing guide
This commit is contained in:
Veronica Berglyd Olsen
2021-10-14 20:35:42 +01:00
committed by GitHub
parent 2a06db0a2b
commit 1c45331b0a
31 changed files with 1042 additions and 666 deletions
+3 -3
View File
@@ -73,7 +73,7 @@ def testCoreOptions_LoadSave(monkeypatch, mockGUI, tmpDir):
assert theOpts.loadSettings()
# Check that unwanted items have been removed
assert theOpts.theState == {
assert theOpts._theState == {
"GuiBuildNovel": {
"winWidth": 1000,
"winHeight": 700,
@@ -88,7 +88,7 @@ def testCoreOptions_LoadSave(monkeypatch, mockGUI, tmpDir):
# Load again to check we get the values back
assert theOpts.loadSettings()
assert theOpts.theState == {
assert theOpts._theState == {
"GuiBuildNovel": {
"winWidth": 1000,
"winHeight": 700,
@@ -129,7 +129,7 @@ def testCoreOptions_SetGet(mockGUI):
assert theOpts.getValue("GuiBuildNovel", "mockItem", None) is None
# Get type-specific
assert theOpts.getString("GuiBuildNovel", "winWidth", None) == "100"
assert theOpts.getString("GuiBuildNovel", "winWidth", None) is None
assert theOpts.getString("GuiBuildNovel", "mockItem", None) is None
assert theOpts.getInt("GuiBuildNovel", "winWidth", None) == 100
assert theOpts.getInt("GuiBuildNovel", "textFont", None) is None
+1 -1
View File
@@ -61,7 +61,7 @@ def testCoreSpell_Enchant(monkeypatch, tmpDir):
assert spChk._readProjectDictionary(None) is False
assert spChk._readProjectDictionary(wList) is True
assert spChk.projectDict == wList
assert spChk._projectDict == wList
# Cannot write to file
with monkeypatch.context() as mp:
+55 -55
View File
@@ -27,59 +27,6 @@ from tools import readFile
from novelwriter.core import NWProject, NWIndex, ToHtml
@pytest.mark.core
def testCoreToHtml_Format(mockGUI):
"""Test all the formatters for the ToHtml class.
"""
theProject = NWProject(mockGUI)
mockGUI.theIndex = NWIndex(theProject)
theHtml = ToHtml(theProject)
# Export Mode
# ===========
assert theHtml._formatSynopsis("synopsis text") == (
"<p class='synopsis'><strong>Synopsis:</strong> synopsis text</p>\n"
)
assert theHtml._formatComments("comment text") == (
"<p class='comment'><strong>Comment:</strong> comment text</p>\n"
)
assert theHtml._formatKeywords("") == ""
assert theHtml._formatKeywords("tag: Jane") == (
"<span class='tags'>Tag:</span> <a name='tag_Jane'>Jane</a>"
)
assert theHtml._formatKeywords("char: Bod, Jane") == (
"<span class='tags'>Characters:</span> "
"<a href='#tag_Bod'>Bod</a>, "
"<a href='#tag_Jane'>Jane</a>"
)
# Preview Mode
# ============
theHtml.setPreview(True, True)
assert theHtml._formatSynopsis("synopsis text") == (
"<p class='comment'><span class='synopsis'>Synopsis:</span> synopsis text</p>\n"
)
assert theHtml._formatComments("comment text") == (
"<p class='comment'>comment text</p>\n"
)
assert theHtml._formatKeywords("") == ""
assert theHtml._formatKeywords("tag: Jane") == (
"<span class='tags'>Tag:</span> <a name='tag_Jane'>Jane</a>"
)
assert theHtml._formatKeywords("char: Bod, Jane") == (
"<span class='tags'>Characters:</span> "
"<a href='#char=Bod'>Bod</a>, "
"<a href='#char=Jane'>Jane</a>"
)
# END Test testCoreToHtml_Format
@pytest.mark.core
def testCoreToHtml_ConvertFormat(mockGUI):
"""Test the tokenizer and converter chain using the ToHtml class.
@@ -433,7 +380,7 @@ def testCoreToHtml_ConvertDirect(mockGUI):
@pytest.mark.core
def testCoreToHtml_Complex(mockGUI, fncDir):
"""Test the ave method of the ToHtml class.
"""Test the save method of the ToHtml class.
"""
theProject = NWProject(mockGUI)
theHtml = ToHtml(theProject)
@@ -524,7 +471,7 @@ def testCoreToHtml_Complex(mockGUI, fncDir):
theHtml.saveHTML5(saveFile)
assert readFile(saveFile) == htmlDoc
# END Test testCoreToHtml_Save
# END Test testCoreToHtml_Complex
@pytest.mark.core
@@ -589,3 +536,56 @@ def testCoreToHtml_Methods(mockGUI):
assert theHtml.getStyleSheet() == []
# END Test testCoreToHtml_Methods
@pytest.mark.core
def testCoreToHtml_Format(mockGUI):
"""Test all the formatters for the ToHtml class.
"""
theProject = NWProject(mockGUI)
mockGUI.theIndex = NWIndex(theProject)
theHtml = ToHtml(theProject)
# Export Mode
# ===========
assert theHtml._formatSynopsis("synopsis text") == (
"<p class='synopsis'><strong>Synopsis:</strong> synopsis text</p>\n"
)
assert theHtml._formatComments("comment text") == (
"<p class='comment'><strong>Comment:</strong> comment text</p>\n"
)
assert theHtml._formatKeywords("") == ""
assert theHtml._formatKeywords("tag: Jane") == (
"<span class='tags'>Tag:</span> <a name='tag_Jane'>Jane</a>"
)
assert theHtml._formatKeywords("char: Bod, Jane") == (
"<span class='tags'>Characters:</span> "
"<a href='#tag_Bod'>Bod</a>, "
"<a href='#tag_Jane'>Jane</a>"
)
# Preview Mode
# ============
theHtml.setPreview(True, True)
assert theHtml._formatSynopsis("synopsis text") == (
"<p class='comment'><span class='synopsis'>Synopsis:</span> synopsis text</p>\n"
)
assert theHtml._formatComments("comment text") == (
"<p class='comment'>comment text</p>\n"
)
assert theHtml._formatKeywords("") == ""
assert theHtml._formatKeywords("tag: Jane") == (
"<span class='tags'>Tag:</span> <a name='tag_Jane'>Jane</a>"
)
assert theHtml._formatKeywords("char: Bod, Jane") == (
"<span class='tags'>Characters:</span> "
"<a href='#char=Bod'>Bod</a>, "
"<a href='#char=Jane'>Jane</a>"
)
# END Test testCoreToHtml_Format
+277
View File
@@ -0,0 +1,277 @@
"""
novelWriter ToMd Class Tester
===============================
This file is a part of novelWriter
Copyright 20182021, Veronica Berglyd Olsen
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/>.
"""
import os
import pytest
from tools import readFile
from novelwriter.core import NWProject, NWIndex, ToMarkdown
@pytest.mark.core
def testCoreToMarkdown_ConvertFormat(mockGUI):
"""Test the tokenizer and converter chain using the ToMarkdown class.
"""
theProject = NWProject(mockGUI)
mockGUI.theIndex = NWIndex(theProject)
theMD = ToMarkdown(theProject)
# Headers
# =======
theMD.isNovel = True
theMD.isNote = False
theMD.isFirst = True
# Header 1
theMD.theText = "# Partition\n"
theMD.tokenizeText()
theMD.doConvert()
assert theMD.theResult == "# Partition\n\n"
# Header 2
theMD.theText = "## Chapter Title\n"
theMD.tokenizeText()
theMD.doConvert()
assert theMD.theResult == "## Chapter Title\n\n"
# Header 3
theMD.theText = "### Scene Title\n"
theMD.tokenizeText()
theMD.doConvert()
assert theMD.theResult == "### Scene Title\n\n"
# Header 4
theMD.theText = "#### Section Title\n"
theMD.tokenizeText()
theMD.doConvert()
assert theMD.theResult == "#### Section Title\n\n"
# Title
theMD.theText = "#! Title\n"
theMD.tokenizeText()
theMD.doConvert()
assert theMD.theResult == "# Title\n\n"
# Unnumbered
theMD.theText = "##! Prologue\n"
theMD.tokenizeText()
theMD.doConvert()
assert theMD.theResult == "## Prologue\n\n"
# Paragraphs
# ==========
# Text for GitHub Markdown
theMD.setGitHubMarkdown()
theMD.theText = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n"
theMD.tokenizeText()
theMD.doConvert()
assert theMD.theResult == (
"Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n\n"
)
# Text for Standard Markdown
theMD.setStandardMarkdown()
theMD.theText = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n"
theMD.tokenizeText()
theMD.doConvert()
assert theMD.theResult == (
"Some **nested bold and _italic_ and strikethrough text** here\n\n"
)
# Text w/Hard Break
theMD.theText = "Line one \nLine two \nLine three\n"
theMD.tokenizeText()
theMD.doConvert()
assert theMD.theResult == "Line one \nLine two \nLine three\n\n"
# Synopsis
theMD.theText = "%synopsis: The synopsis ...\n"
theMD.tokenizeText()
theMD.doConvert()
assert theMD.theResult == ""
theMD.setSynopsis(True)
theMD.theText = "%synopsis: The synopsis ...\n"
theMD.tokenizeText()
theMD.doConvert()
assert theMD.theResult == "**Synopsis:** The synopsis ...\n\n"
# Comment
theMD.theText = "% A comment ...\n"
theMD.tokenizeText()
theMD.doConvert()
assert theMD.theResult == ""
theMD.setComments(True)
theMD.theText = "% A comment ...\n"
theMD.tokenizeText()
theMD.doConvert()
assert theMD.theResult == "**Comment:** A comment ...\n\n"
# Keywords
theMD.theText = "@char: Bod, Jane\n"
theMD.tokenizeText()
theMD.doConvert()
assert theMD.theResult == ""
theMD.setKeywords(True)
theMD.theText = "@char: Bod, Jane\n"
theMD.tokenizeText()
theMD.doConvert()
assert theMD.theResult == "**Characters:** Bod, Jane\n\n"
# Multiple Keywords
theMD.setKeywords(True)
theMD.theText = "## Chapter\n\n@pov: Bod\n@plot: Main\n@location: Europe\n\n"
theMD.tokenizeText()
theMD.doConvert()
assert theMD.theResult == (
"## Chapter\n\n"
"**Point of View:** Bod \n"
"**Plot:** Main \n"
"**Locations:** Europe\n\n"
)
# END Test testCoreToMarkdown_ConvertFormat
@pytest.mark.core
def testCoreToMarkdown_ConvertDirect(mockGUI):
"""Test the converter directly using the ToMarkdown class.
"""
theProject = NWProject(mockGUI)
mockGUI.theIndex = NWIndex(theProject)
theMD = ToMarkdown(theProject)
theMD.isNovel = True
theMD.isNote = False
# Special Titles
# ==============
# Title
theMD.theTokens = [
(theMD.T_TITLE, 1, "A Title", None, theMD.A_PBB | theMD.A_CENTRE),
(theMD.T_EMPTY, 1, "", None, theMD.A_NONE),
]
theMD.doConvert()
assert theMD.theResult == "# A Title\n\n"
# Unnumbered
theMD.theTokens = [
(theMD.T_UNNUM, 1, "Prologue", None, theMD.A_PBB),
(theMD.T_EMPTY, 1, "", None, theMD.A_NONE),
]
theMD.doConvert()
assert theMD.theResult == "## Prologue\n\n"
# Separators
# ==========
# Separator
theMD.theTokens = [
(theMD.T_SEP, 1, "* * *", None, theMD.A_CENTRE),
(theMD.T_EMPTY, 1, "", None, theMD.A_NONE),
]
theMD.doConvert()
assert theMD.theResult == "* * *\n\n"
# Skip
theMD.theTokens = [
(theMD.T_SKIP, 1, "", None, theMD.A_NONE),
(theMD.T_EMPTY, 1, "", None, theMD.A_NONE),
]
theMD.doConvert()
assert theMD.theResult == "\n\n\n"
# END Test testCoreToMarkdown_ConvertDirect
@pytest.mark.core
def testCoreToMarkdown_Complex(mockGUI, fncDir):
"""Test the save method of the ToMarkdown class.
"""
theProject = NWProject(mockGUI)
theMD = ToMarkdown(theProject)
theMD.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 = [
"# My Novel\n\n**By Jane Doh**\n\n",
"## Chapter 1\n\nThe text of chapter one.\n\n",
"### Scene 1\n\nThe text of scene one.\n\n",
"#### A Section\n\nMore text in scene one.\n\n",
"## Chapter 2\n\nThe text of chapter two.\n\n",
"### Scene 2\n\nThe text of scene two.\n\n",
"#### A Section\n\n\tMore text in scene two.\n\n",
]
for i in range(len(docText)):
theMD.theText = docText[i]
theMD.doPreProcessing()
theMD.tokenizeText()
theMD.doConvert()
assert theMD.theResult == resText[i]
assert theMD.fullMD == resText
assert theMD.getFullResultSize() == len("".join(resText))
theMD.replaceTabs(nSpaces=4, spaceChar=" ")
resText[6] = "#### A Section\n\n More text in scene two.\n\n"
# Check File
# ==========
saveFile = os.path.join(fncDir, "outFile.md")
theMD.saveMarkdown(saveFile)
assert readFile(saveFile) == "".join(resText)
# END Test testCoreToHtml_Complex
@pytest.mark.core
def testCoreToMarkdown_Format(mockGUI):
"""Test all the formatters for the ToMarkdown class.
"""
theProject = NWProject(mockGUI)
mockGUI.theIndex = NWIndex(theProject)
theMD = ToMarkdown(theProject)
assert theMD._formatKeywords("", theMD.A_NONE) == ""
assert theMD._formatKeywords("tag: Jane", theMD.A_NONE) == "**Tag:** Jane\n\n"
assert theMD._formatKeywords("tag: Jane, John", theMD.A_NONE) == "**Tag:** Jane, John\n\n"
assert theMD._formatKeywords("tag: Jane", theMD.A_Z_BTMMRG) == "**Tag:** Jane \n"
# END Test testCoreToMarkdown_Format
+86
View File
@@ -559,6 +559,62 @@ def testCoreToOdt_Convert(mockGUI):
# END Test testCoreToOdt_Convert
@pytest.mark.core
def testCoreToOdt_ConvertDirect(mockGUI):
"""Test the converter directly using the ToOdt class to reach some
otherwise hard to reach conditions.
"""
theProject = NWProject(mockGUI)
mockGUI.theIndex = NWIndex(theProject)
theDoc = ToOdt(theProject, isFlat=True)
theDoc.isNovel = True
# Justified
theDoc = ToOdt(theProject, isFlat=True)
theDoc.theTokens = [
(theDoc.T_TEXT, 1, "This is a paragraph", [], theDoc.A_JUSTIFY),
(theDoc.T_EMPTY, 1, "", None, theDoc.A_NONE),
]
theDoc.initDocument()
theDoc.doConvert()
theDoc.closeDocument()
assert (
'<style:style style:name="P1" style:family="paragraph" '
'style:parent-style-name="Text_Body">'
'<style:paragraph-properties fo:text-align="justify"/>'
'</style:style>'
) in xmlToText(theDoc._xAuto)
assert xmlToText(theDoc._xText) == (
'<office:text>'
'<text:p text:style-name="P1">This is a paragraph</text:p>'
'</office:text>'
)
# Page Break After
theDoc = ToOdt(theProject, isFlat=True)
theDoc.theTokens = [
(theDoc.T_TEXT, 1, "This is a paragraph", [], theDoc.A_PBA),
(theDoc.T_EMPTY, 1, "", None, theDoc.A_NONE),
]
theDoc.initDocument()
theDoc.doConvert()
theDoc.closeDocument()
assert (
'<style:style style:name="P1" style:family="paragraph" '
'style:parent-style-name="Text_Body">'
'<style:paragraph-properties fo:break-after="page"/>'
'</style:style>'
) in xmlToText(theDoc._xAuto)
assert xmlToText(theDoc._xText) == (
'<office:text>'
'<text:p text:style-name="P1">This is a paragraph</text:p>'
'</office:text>'
)
# END Test testCoreToOdt_ConvertDirect
@pytest.mark.core
def testCoreToOdt_SaveFlat(mockGUI, fncDir, outDir, refDir):
"""Test the document save functions.
@@ -676,6 +732,36 @@ def testCoreToOdt_SaveFull(mockGUI, fncDir, outDir, refDir):
# END Test testCoreToOdt_SaveFull
@pytest.mark.core
def testCoreToOdt_Format(mockGUI):
"""Test the formatters for the ToOdt class.
"""
theProject = NWProject(mockGUI)
mockGUI.theIndex = NWIndex(theProject)
theDoc = ToOdt(theProject, isFlat=True)
assert theDoc._formatSynopsis("synopsis text") == (
"**Synopsis:** synopsis text",
"_B b_ "
)
assert theDoc._formatComments("comment text") == (
"**Comment:** comment text",
"_B b_ "
)
assert theDoc._formatKeywords("") == ""
assert theDoc._formatKeywords("tag: Jane") == (
"**Tag:** Jane",
"_B b_ "
)
assert theDoc._formatKeywords("char: Bod, Jane") == (
"**Characters:** Bod, Jane",
"_B b_ "
)
# END Test testCoreToOdt_Format
@pytest.mark.core
def testCoreToOdt_ODTParagraphStyle():
"""Test the ODTParagraphStyle class.