Merge Novel Layouts (#837)

* Merge novel layouts and simplify the Tokenizer class
* Fix core tests
* Delete auto-layout code
* Remove no longer needed item layouts
* Remove references to deleted item layouts in test
* Make some minor changes to Tokenizer class and update tests
* Update test reference files
* Update sample project
* Fix a few issues with the Tokenizer
* Centre some text in the sample documents
* Make some minor changes to how new projects are generated
* Add support for New Page and VSpace commands to the Tokenizer
* Remove Title and Page layouts and add codes for page break and vertical space
* Add document layout and fix some issues in index class with new title formats
* Update tests and reference files
* Bump the project file version and fix a minor issue in items class
* Update tests and test coverage
* Clean up some warnings and issues in tests
This commit is contained in:
Veronica Berglyd Olsen
2021-08-02 23:44:35 +02:00
committed by GitHub
parent c6973043e6
commit bb1a778277
92 changed files with 1574 additions and 1121 deletions
+3 -3
View File
@@ -79,7 +79,7 @@ def testCoreDocument_LoadSave(monkeypatch, mockGUI, nwMinimal):
assert readFile(docPath) == (
"%%~name: New File\n"
f"%%~path: a508bb932959c/{xHandle}\n"
"%%~kind: NOVEL/SCENE\n"
"%%~kind: NOVEL/DOCUMENT\n"
"### Test File\n\n"
"Text ...\n\n"
)
@@ -145,14 +145,14 @@ def testCoreDocument_Methods(mockGUI, nwMinimal):
assert theName == "New Scene"
assert theParent == "a6d311a93600a"
assert theClass == nwItemClass.NOVEL
assert theLayout == nwItemLayout.SCENE
assert theLayout == nwItemLayout.DOCUMENT
# Add meta data garbage
assert theDoc.writeDocument("%%~ stuff\n### Test File\n\nText ...\n\n")
assert readFile(docPath) == (
"%%~name: New Scene\n"
f"%%~path: a6d311a93600a/{sHandle}\n"
"%%~kind: NOVEL/SCENE\n"
"%%~kind: NOVEL/DOCUMENT\n"
"%%~ stuff\n"
"### Test File\n\n"
"Text ...\n\n"
+90 -32
View File
@@ -267,7 +267,7 @@ def testCoreIndex_ScanText(nwMinimal, mockGUI):
assert theIndex.scanText(dHandle, "Hello World!") is False
assert theIndex.scanText(xHandle, "Hello World!") is False
xItem.setLayout(nwItemLayout.SCENE)
xItem.setLayout(nwItemLayout.DOCUMENT)
xItem.setParent(None)
assert theIndex.scanText(xHandle, "Hello World!") is False
@@ -284,11 +284,15 @@ def testCoreIndex_ScanText(nwMinimal, mockGUI):
assert theIndex.scanText(xHandle, "Hello World!") is False
# Make some usable items
tHandle = theProject.newFile("Title", nwItemClass.NOVEL, "a508bb932959c")
pHandle = theProject.newFile("Page", nwItemClass.NOVEL, "a508bb932959c")
nHandle = theProject.newFile("Hello", nwItemClass.NOVEL, "a508bb932959c")
cHandle = theProject.newFile("Jane", nwItemClass.CHARACTER, "afb3043c7b2b3")
sHandle = theProject.newFile("Scene", nwItemClass.NOVEL, "a508bb932959c")
# Text Indexing
# =============
# Index correct text
assert theIndex.scanText(cHandle, (
"# Jane Smith\n"
@@ -305,7 +309,10 @@ def testCoreIndex_ScanText(nwMinimal, mockGUI):
assert theIndex._tagIndex == {"Jane": [2, cHandle, "CHARACTER", "T000001"]}
assert theIndex.getNovelData(nHandle, "T000001")["title"] == "Hello World!"
# Check that title sections are indexed properly
# Title Indexing
# ==============
# Document File
assert theIndex.scanText(nHandle, (
"# Title One\n\n"
"% synopsis: Synopsis One.\n\n"
@@ -322,7 +329,7 @@ def testCoreIndex_ScanText(nwMinimal, mockGUI):
"##### Title Five\n\n" # Not interpreted as a title, the hashes are counted as a word
"Paragraph Five.\n\n"
))
assert cHandle not in theIndex._refIndex
assert nHandle not in theIndex._refIndex
assert theIndex._fileIndex[nHandle]["T000001"]["level"] == "H1"
assert theIndex._fileIndex[nHandle]["T000007"]["level"] == "H2"
@@ -334,10 +341,10 @@ def testCoreIndex_ScanText(nwMinimal, mockGUI):
assert theIndex._fileIndex[nHandle]["T000013"]["title"] == "Title Three"
assert theIndex._fileIndex[nHandle]["T000019"]["title"] == "Title Four"
assert theIndex._fileIndex[nHandle]["T000001"]["layout"] == "SCENE"
assert theIndex._fileIndex[nHandle]["T000007"]["layout"] == "SCENE"
assert theIndex._fileIndex[nHandle]["T000013"]["layout"] == "SCENE"
assert theIndex._fileIndex[nHandle]["T000019"]["layout"] == "SCENE"
assert theIndex._fileIndex[nHandle]["T000001"]["layout"] == "DOCUMENT"
assert theIndex._fileIndex[nHandle]["T000007"]["layout"] == "DOCUMENT"
assert theIndex._fileIndex[nHandle]["T000013"]["layout"] == "DOCUMENT"
assert theIndex._fileIndex[nHandle]["T000019"]["layout"] == "DOCUMENT"
assert theIndex._fileIndex[nHandle]["T000001"]["cCount"] == 23
assert theIndex._fileIndex[nHandle]["T000007"]["cCount"] == 23
@@ -359,6 +366,7 @@ def testCoreIndex_ScanText(nwMinimal, mockGUI):
assert theIndex._fileIndex[nHandle]["T000013"]["synopsis"] == "Synopsis Three."
assert theIndex._fileIndex[nHandle]["T000019"]["synopsis"] == "Synopsis Four."
# Note File
assert theIndex.scanText(cHandle, (
"# Title One\n\n"
"@tag: One\n\n"
@@ -375,6 +383,7 @@ def testCoreIndex_ScanText(nwMinimal, mockGUI):
assert theIndex._fileIndex[cHandle]["T000001"]["pCount"] == 1
assert theIndex._fileIndex[cHandle]["T000001"]["synopsis"] == "Synopsis One."
# Valid and Invalid References
assert theIndex.scanText(sHandle, (
"# Title One\n\n"
"@pov: One\n\n" # Valid
@@ -387,15 +396,48 @@ def testCoreIndex_ScanText(nwMinimal, mockGUI):
[[3, "@pov", "One"], [5, "@char", "Two"]]
)
# Special Titles
# ==============
assert theIndex.scanText(tHandle, (
"#! My Project\n\n"
">> By Jane Doe <<\n\n"
))
assert tHandle not in theIndex._refIndex
assert theIndex._fileIndex[tHandle]["T000001"]["level"] == "H1"
assert theIndex._fileIndex[tHandle]["T000001"]["title"] == "My Project"
assert theIndex._fileIndex[tHandle]["T000001"]["layout"] == "DOCUMENT"
assert theIndex._fileIndex[tHandle]["T000001"]["cCount"] == 21
assert theIndex._fileIndex[tHandle]["T000001"]["wCount"] == 5
assert theIndex._fileIndex[tHandle]["T000001"]["pCount"] == 1
assert theIndex._fileIndex[tHandle]["T000001"]["synopsis"] == ""
assert theIndex.scanText(tHandle, (
"##! Prologue\n\n"
"In the beginning there was time ...\n\n"
))
assert tHandle not in theIndex._refIndex
assert theIndex._fileIndex[tHandle]["T000001"]["level"] == "H2"
assert theIndex._fileIndex[tHandle]["T000001"]["title"] == "Prologue"
assert theIndex._fileIndex[tHandle]["T000001"]["layout"] == "DOCUMENT"
assert theIndex._fileIndex[tHandle]["T000001"]["cCount"] == 43
assert theIndex._fileIndex[tHandle]["T000001"]["wCount"] == 8
assert theIndex._fileIndex[tHandle]["T000001"]["pCount"] == 1
assert theIndex._fileIndex[tHandle]["T000001"]["synopsis"] == ""
# Page wo/Title
theProject.projTree[pHandle].itemLayout = nwItemLayout.PAGE
# =============
theProject.projTree[pHandle].itemLayout = nwItemLayout.DOCUMENT
assert theIndex.scanText(pHandle, (
"This is a page with some text on it.\n\n"
))
assert pHandle in theIndex._fileIndex
assert theIndex._fileIndex[pHandle]["T000000"]["level"] == "H0"
assert theIndex._fileIndex[pHandle]["T000000"]["title"] == ""
assert theIndex._fileIndex[pHandle]["T000000"]["layout"] == "PAGE"
assert theIndex._fileIndex[pHandle]["T000000"]["layout"] == "DOCUMENT"
assert theIndex._fileIndex[pHandle]["T000000"]["cCount"] == 36
assert theIndex._fileIndex[pHandle]["T000000"]["wCount"] == 9
assert theIndex._fileIndex[pHandle]["T000000"]["pCount"] == 1
@@ -587,9 +629,9 @@ def testCoreIndex_ExtractData(nwMinimal, mockGUI):
sHandle = theProject.newFile("Scene One", nwItemClass.NOVEL, "a508bb932959c")
tHandle = theProject.newFile("Scene Two", nwItemClass.NOVEL, "a508bb932959c")
theProject.projTree[hHandle].itemLayout == nwItemLayout.CHAPTER
theProject.projTree[sHandle].itemLayout == nwItemLayout.SCENE
theProject.projTree[tHandle].itemLayout == nwItemLayout.SCENE
theProject.projTree[hHandle].itemLayout == nwItemLayout.DOCUMENT
theProject.projTree[sHandle].itemLayout == nwItemLayout.DOCUMENT
theProject.projTree[tHandle].itemLayout == nwItemLayout.DOCUMENT
assert theIndex.scanText(hHandle, "## Chapter One\n\n")
assert theIndex.scanText(sHandle, "### Scene One\n\n")
@@ -797,8 +839,8 @@ def testCoreIndex_CheckRefIndex(mockGUI):
@pytest.mark.core
def testCoreIndex_CheckNovelNoteIndex(mockGUI):
"""Test the novel and note index checkers.
def testCoreIndex_CheckFileIndex(mockGUI):
"""Test the file index checker.
"""
theProject = NWProject(mockGUI)
theIndex = NWIndex(theProject)
@@ -809,7 +851,7 @@ def testCoreIndex_CheckNovelNoteIndex(mockGUI):
"T000001": {
"level": "H1",
"title": "My Novel",
"layout": "TITLE",
"layout": "DOCUMENT",
"cCount": 72,
"wCount": 15,
"pCount": 2,
@@ -826,7 +868,7 @@ def testCoreIndex_CheckNovelNoteIndex(mockGUI):
"T000001": {
"level": "H1",
"title": "My Novel",
"layout": "TITLE",
"layout": "DOCUMENT",
"cCount": 72,
"wCount": 15,
"pCount": 2,
@@ -843,7 +885,7 @@ def testCoreIndex_CheckNovelNoteIndex(mockGUI):
"INVALID": {
"level": "H1",
"title": "My Novel",
"layout": "TITLE",
"layout": "DOCUMENT",
"cCount": 72,
"wCount": 15,
"pCount": 2,
@@ -860,7 +902,7 @@ def testCoreIndex_CheckNovelNoteIndex(mockGUI):
"T000001": {
"level": "H1",
"title": "My Novel",
"layout": "TITLE",
"layout": "DOCUMENT",
"cCount": 72,
"wCount": 15,
"pCount": 2,
@@ -881,7 +923,7 @@ def testCoreIndex_CheckNovelNoteIndex(mockGUI):
"T000001": {
"stuff": "H1",
"title": "My Novel",
"layout": "TITLE",
"layout": "DOCUMENT",
"cCount": 72,
"wCount": 15,
"pCount": 2,
@@ -898,7 +940,7 @@ def testCoreIndex_CheckNovelNoteIndex(mockGUI):
"T000001": {
"level": "H1",
"stuff": "My Novel",
"layout": "TITLE",
"layout": "DOCUMENT",
"cCount": 72,
"wCount": 15,
"pCount": 2,
@@ -915,7 +957,7 @@ def testCoreIndex_CheckNovelNoteIndex(mockGUI):
"T000001": {
"level": "H1",
"title": "My Novel",
"stuff": "TITLE",
"stuff": "DOCUMENT",
"cCount": 72,
"wCount": 15,
"pCount": 2,
@@ -932,7 +974,7 @@ def testCoreIndex_CheckNovelNoteIndex(mockGUI):
"T000001": {
"level": "H1",
"title": "My Novel",
"layout": "TITLE",
"layout": "DOCUMENT",
"stuff": 72,
"wCount": 15,
"pCount": 2,
@@ -949,7 +991,7 @@ def testCoreIndex_CheckNovelNoteIndex(mockGUI):
"T000001": {
"level": "H1",
"title": "My Novel",
"layout": "TITLE",
"layout": "DOCUMENT",
"cCount": 72,
"stuff": 15,
"pCount": 2,
@@ -966,7 +1008,7 @@ def testCoreIndex_CheckNovelNoteIndex(mockGUI):
"T000001": {
"level": "H1",
"title": "My Novel",
"layout": "TITLE",
"layout": "DOCUMENT",
"cCount": 72,
"wCount": 15,
"stuff": 2,
@@ -983,7 +1025,7 @@ def testCoreIndex_CheckNovelNoteIndex(mockGUI):
"T000001": {
"level": "H1",
"title": "My Novel",
"layout": "TITLE",
"layout": "DOCUMENT",
"cCount": 72,
"wCount": 15,
"pCount": 2,
@@ -1003,7 +1045,7 @@ def testCoreIndex_CheckNovelNoteIndex(mockGUI):
"T000001": {
"level": "XX",
"title": "My Novel",
"layout": "TITLE",
"layout": "DOCUMENT",
"cCount": 72,
"wCount": 15,
"pCount": 2,
@@ -1020,7 +1062,7 @@ def testCoreIndex_CheckNovelNoteIndex(mockGUI):
"T000001": {
"level": "H1",
"title": 12345678,
"layout": "TITLE",
"layout": "DOCUMENT",
"cCount": 72,
"wCount": 15,
"pCount": 2,
@@ -1054,7 +1096,7 @@ def testCoreIndex_CheckNovelNoteIndex(mockGUI):
"T000001": {
"level": "H1",
"title": "My Novel",
"layout": "TITLE",
"layout": "DOCUMENT",
"cCount": "72",
"wCount": 15,
"pCount": 2,
@@ -1071,7 +1113,7 @@ def testCoreIndex_CheckNovelNoteIndex(mockGUI):
"T000001": {
"level": "H1",
"title": "My Novel",
"layout": "TITLE",
"layout": "DOCUMENT",
"cCount": 72,
"wCount": "15",
"pCount": 2,
@@ -1088,7 +1130,7 @@ def testCoreIndex_CheckNovelNoteIndex(mockGUI):
"T000001": {
"level": "H1",
"title": "My Novel",
"layout": "TITLE",
"layout": "DOCUMENT",
"cCount": 72,
"wCount": 15,
"pCount": "2",
@@ -1105,7 +1147,7 @@ def testCoreIndex_CheckNovelNoteIndex(mockGUI):
"T000001": {
"level": "H1",
"title": "My Novel",
"layout": "TITLE",
"layout": "DOCUMENT",
"cCount": 72,
"wCount": 15,
"pCount": 2,
@@ -1116,7 +1158,7 @@ def testCoreIndex_CheckNovelNoteIndex(mockGUI):
with pytest.raises(ValueError):
theIndex._checkFileIndex()
# END Test testCoreIndex_CheckNovelNoteIndex
# END Test testCoreIndex_CheckFileIndex
@pytest.mark.core
@@ -1235,4 +1277,20 @@ def testCoreIndex_CountWords():
assert wC == 9
assert pC == 4
# Formatting Codes
cC, wC, pC = countWords((
"Some text\n\n"
"[NEWPAGE]\n\n"
"more text\n\n"
"[NEW PAGE]]\n\n"
"even more text\n\n"
"[VSPACE]\n\n"
"and some final text\n\n"
"[VSPACE:4]\n\n"
"THE END\n\n"
))
assert cC == 58
assert wC == 13
assert pC == 5
# END Test testCoreIndex_CountWords
+25 -17
View File
@@ -73,7 +73,7 @@ def testCoreItem_Setters(mockGUI):
theItem.setOrder(1)
assert theItem.itemOrder == 1
# Status
# Importance
theItem.setStatus("Nonsense")
assert theItem.itemStatus == "New"
theItem.setStatus("New")
@@ -85,7 +85,7 @@ def testCoreItem_Setters(mockGUI):
theItem.setStatus("Main")
assert theItem.itemStatus == "Main"
# Importance
# Status
theItem.itemClass = nwItemClass.NOVEL
theItem.setStatus("Nonsense")
assert theItem.itemStatus == "New"
@@ -244,29 +244,37 @@ def testCoreItem_LayoutSetter(mockGUI):
theProject = NWProject(mockGUI)
theItem = NWItem(theProject)
# Layout
# Faulty Layouts
theItem.setLayout(None)
assert theItem.itemLayout == nwItemLayout.NO_LAYOUT
theItem.setLayout("NONSENSE")
assert theItem.itemLayout == nwItemLayout.NO_LAYOUT
# Current Layouts
theItem.setLayout("NO_LAYOUT")
assert theItem.itemLayout == nwItemLayout.NO_LAYOUT
theItem.setLayout("TITLE")
assert theItem.itemLayout == nwItemLayout.TITLE
theItem.setLayout("BOOK")
assert theItem.itemLayout == nwItemLayout.BOOK
theItem.setLayout("PAGE")
assert theItem.itemLayout == nwItemLayout.PAGE
theItem.setLayout("PARTITION")
assert theItem.itemLayout == nwItemLayout.PARTITION
theItem.setLayout("UNNUMBERED")
assert theItem.itemLayout == nwItemLayout.UNNUMBERED
theItem.setLayout("CHAPTER")
assert theItem.itemLayout == nwItemLayout.CHAPTER
theItem.setLayout("SCENE")
assert theItem.itemLayout == nwItemLayout.SCENE
theItem.setLayout("DOCUMENT")
assert theItem.itemLayout == nwItemLayout.DOCUMENT
theItem.setLayout("NOTE")
assert theItem.itemLayout == nwItemLayout.NOTE
# Deprecated Layouts
theItem.setLayout("TITLE")
assert theItem.itemLayout == nwItemLayout.DOCUMENT
theItem.setLayout("PAGE")
assert theItem.itemLayout == nwItemLayout.DOCUMENT
theItem.setLayout("BOOK")
assert theItem.itemLayout == nwItemLayout.DOCUMENT
theItem.setLayout("PARTITION")
assert theItem.itemLayout == nwItemLayout.DOCUMENT
theItem.setLayout("UNNUMBERED")
assert theItem.itemLayout == nwItemLayout.DOCUMENT
theItem.setLayout("CHAPTER")
assert theItem.itemLayout == nwItemLayout.DOCUMENT
theItem.setLayout("SCENE")
assert theItem.itemLayout == nwItemLayout.DOCUMENT
# Alternatives
theItem.setLayout(nwItemLayout.NOTE)
assert theItem.itemLayout == nwItemLayout.NOTE
+27 -3
View File
@@ -398,7 +398,7 @@ def testCoreProject_Open(monkeypatch, nwMinimal, mockGUI):
))
assert theProject.openProject(nwMinimal) is False
# Larger hex version
# Update file version
writeFile(rName, (
"<?xml version='1.0' encoding='utf-8'?>\n"
"<novelWriterXML "
@@ -410,6 +410,22 @@ def testCoreProject_Open(monkeypatch, nwMinimal, mockGUI):
))
mockGUI.askResponse = False
assert theProject.openProject(nwMinimal) is False
assert mockGUI.lastQuestion[0] == "File Version"
mockGUI.undo()
# Larger hex version
writeFile(rName, (
"<?xml version='1.0' encoding='utf-8'?>\n"
"<novelWriterXML "
"appVersion=\"1.0\" "
"hexVersion=\"0xffffffff\" "
"fileVersion=\"%s\" "
"timeStamp=\"2020-01-01 00:00:00\">\n"
"</novelWriterXML>\n"
) % theProject.FILE_VERSION)
mockGUI.askResponse = False
assert theProject.openProject(nwMinimal) is False
assert mockGUI.lastQuestion[0] == "Version Conflict"
mockGUI.undo()
# Test skipping XML entries
@@ -672,12 +688,12 @@ def testCoreProject_Methods(monkeypatch, nwMinimal, mockGUI, tmpDir):
assert theProject.projPath == os.path.expanduser("~")
# Create a new folder and populate it
projPath = os.path.join(nwMinimal, "dummy1")
projPath = os.path.join(nwMinimal, "mock1")
assert theProject.setProjectPath(projPath, newProject=True)
# Make os.mkdir fail
monkeypatch.setattr("os.mkdir", causeOSError)
projPath = os.path.join(nwMinimal, "dummy2")
projPath = os.path.join(nwMinimal, "mock2")
assert not theProject.setProjectPath(projPath, newProject=True)
# Set back
@@ -753,6 +769,14 @@ def testCoreProject_Methods(monkeypatch, nwMinimal, mockGUI, tmpDir):
assert theProject.projSpell == "en_GB"
assert theProject.projChanged
# Project Language
theProject.projChanged = False
theProject.projLang = "en"
assert theProject.setProjectLang(None) is True
assert theProject.projLang is None
assert theProject.setProjectLang("en_GB") is True
assert theProject.projLang == "en_GB"
# Automatic outline update
theProject.projChanged = False
assert theProject.setAutoOutline(True)
+11 -7
View File
@@ -51,6 +51,8 @@ def testCoreSpell_Super(monkeypatch, tmpDir):
with monkeypatch.context() as mp:
mp.setattr("builtins.open", causeOSError)
assert spChk._readProjectDictionary(wList) is False
assert spChk._readProjectDictionary(None) is False
assert spChk._readProjectDictionary(wList) is True
assert spChk.projectDict == wList
@@ -85,22 +87,24 @@ def testCoreSpell_Enchant(monkeypatch, tmpDir):
spChk.setLanguage("en", wList)
assert spChk.setLanguage("", "") is None
assert spChk.checkWord("")
assert spChk.checkWord("") is True
assert spChk.suggestWords("") == []
assert spChk.listDictionaries() == []
assert spChk.describeDict() == ("", "")
# Load the proper enchant package
# Load the proper enchant package (twice)
spChk = NWSpellEnchant()
spChk.setLanguage("en", wList)
spChk.setLanguage("en", wList)
assert spChk.checkWord("a_word")
assert spChk.checkWord("b_word")
assert spChk.checkWord("c_word")
assert not spChk.checkWord("d_word")
# Check words
assert spChk.checkWord("a_word") is True
assert spChk.checkWord("b_word") is True
assert spChk.checkWord("c_word") is True
assert spChk.checkWord("d_word") is False
spChk.addWord("d_word")
assert spChk.checkWord("d_word")
assert spChk.checkWord("d_word") is True
wSuggest = spChk.suggestWords("wrod")
assert len(wSuggest) > 0
+143 -36
View File
@@ -81,29 +81,35 @@ def testCoreToHtml_Format(mockGUI):
@pytest.mark.core
def testCoreToHtml_Convert(mockGUI):
"""Test the converter of the ToHtml class.
def testCoreToHtml_ConvertFormat(mockGUI):
"""Test the tokenizer and converter chain using the ToHtml class.
"""
theProject = NWProject(mockGUI)
mockGUI.theIndex = NWIndex(theProject)
theHtml = ToHtml(theProject)
# Export Mode
# ===========
# Novel Files Headers
# ===================
theHtml.isNovel = True
theHtml.isNote = False
theHtml.isFirst = True
# Header 1
theHtml.theText = "# Title\n"
theHtml.theText = "# Partition\n"
theHtml.tokenizeText()
theHtml.doConvert()
assert theHtml.theResult == "<h1 class='title'>Title</h1>\n"
assert theHtml.theResult == (
"<h1 class='title' style='text-align: center;'>Partition</h1>\n"
)
# Header 2
theHtml.theText = "## Chapter Title\n"
theHtml.tokenizeText()
theHtml.doConvert()
assert theHtml.theResult == "<h1>Chapter Title</h1>\n"
assert theHtml.theResult == (
"<h1 style='page-break-before: always;'>Chapter Title</h1>\n"
)
# Header 3
theHtml.theText = "### Scene Title\n"
@@ -117,7 +123,26 @@ def testCoreToHtml_Convert(mockGUI):
theHtml.doConvert()
assert theHtml.theResult == "<h3>Section Title</h3>\n"
# Title
theHtml.theText = "#! Title\n"
theHtml.tokenizeText()
theHtml.doConvert()
assert theHtml.theResult == (
"<h1 class='title' style='text-align: center; page-break-before: always;'>Title</h1>\n"
)
# Unnumbered
theHtml.theText = "##! Prologue\n"
theHtml.tokenizeText()
theHtml.doConvert()
assert theHtml.theResult == "<h1 style='page-break-before: always;'>Prologue</h1>\n"
# Note Files Headers
# ==================
theHtml.isNovel = False
theHtml.isNote = True
theHtml.isFirst = True
theHtml.setLinkHeaders(True)
# Header 1
@@ -144,6 +169,23 @@ def testCoreToHtml_Convert(mockGUI):
theHtml.doConvert()
assert theHtml.theResult == "<h4><a name='T000001'></a>Heading Four</h4>\n"
# Title
theHtml.theText = "#! Heading One\n"
theHtml.tokenizeText()
theHtml.doConvert()
assert theHtml.theResult == (
"<h1 style='text-align: center;'><a name='T000001'></a>Heading One</h1>\n"
)
# Unnumbered
theHtml.theText = "##! Heading Two\n"
theHtml.tokenizeText()
theHtml.doConvert()
assert theHtml.theResult == "<h2><a name='T000001'></a>Heading Two</h2>\n"
# Paragraphs
# ==========
# Text
theHtml.theText = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n"
theHtml.tokenizeText()
@@ -223,22 +265,64 @@ def testCoreToHtml_Convert(mockGUI):
"</p>\n"
)
# Direct Tests
# Preview Mode
# ============
theHtml.setPreview(True, True)
# Text (HTML4)
theHtml.theText = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n"
theHtml.tokenizeText()
theHtml.doConvert()
assert theHtml.theResult == (
"<p>Some <b>nested bold and <i>italic</i> and "
"<span style='text-decoration: line-through;'>strikethrough</span> "
"text</b> here</p>\n"
)
# END Test testCoreToHtml_ConvertFormat
@pytest.mark.core
def testCoreToHtml_ConvertDirect(mockGUI):
"""Test the converter directly using the ToHtml class.
"""
theProject = NWProject(mockGUI)
mockGUI.theIndex = NWIndex(theProject)
theHtml = ToHtml(theProject)
theHtml.isNovel = True
theHtml.isNote = False
theHtml.setLinkHeaders(True)
# Special Titles
# ==============
# Title
theHtml.theTokens = [
(theHtml.T_TITLE, 1, "A Title", None, theHtml.A_PBB_AUT | theHtml.A_CENTRE),
(theHtml.T_TITLE, 1, "A Title", None, theHtml.A_PBB | theHtml.A_CENTRE),
(theHtml.T_EMPTY, 1, "", None, theHtml.A_NONE),
]
theHtml.doConvert()
assert theHtml.theResult == (
"<h1 class='title' style='text-align: center; page-break-before: auto;'>"
"<h1 class='title' style='text-align: center; page-break-before: always;'>"
"<a name='T000001'></a>A Title</h1>\n"
)
# Unnumbered
theHtml.theTokens = [
(theHtml.T_UNNUM, 1, "Prologue", None, theHtml.A_PBB),
(theHtml.T_EMPTY, 1, "", None, theHtml.A_NONE),
]
theHtml.doConvert()
assert theHtml.theResult == (
"<h1 style='page-break-before: always;'>"
"<a name='T000001'></a>Prologue</h1>\n"
)
# Separators
# ==========
# Separator
theHtml.theTokens = [
(theHtml.T_SEP, 1, "* * *", None, theHtml.A_CENTRE),
@@ -255,8 +339,8 @@ def testCoreToHtml_Convert(mockGUI):
theHtml.doConvert()
assert theHtml.theResult == "<p class='skip'>&nbsp;</p>\n"
# Styles
# ======
# Alignment
# =========
theHtml.setLinkHeaders(False)
@@ -308,6 +392,9 @@ def testCoreToHtml_Convert(mockGUI):
"<h1 class='title' style='text-align: justify;'>A Title</h1>\n"
)
# Page Break
# ==========
# Page Break Always
theHtml.theTokens = [
(theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_PBB | theHtml.A_PBA),
@@ -318,32 +405,30 @@ def testCoreToHtml_Convert(mockGUI):
"style='page-break-before: always; page-break-after: always;'>A Title</h1>\n"
)
# Page Break Auto
# Indent
# ======
# Indent Left
theHtml.theTokens = [
(theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_PBB_AUT | theHtml.A_PBA_AUT),
(theHtml.T_TEXT, 1, "Some text ...", [], theHtml.A_IND_L),
(theHtml.T_EMPTY, 2, "", None, theHtml.A_NONE),
]
theHtml.doConvert()
assert theHtml.theResult == (
"<h1 class='title' "
"style='page-break-before: auto; page-break-after: auto;'>A Title</h1>\n"
"<p style='margin-left: 40px;'>Some text ...</p>\n"
)
# Preview Mode
# ============
theHtml.setPreview(True, True)
# Text (HTML4)
theHtml.theText = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n"
theHtml.tokenizeText()
# Indent Right
theHtml.theTokens = [
(theHtml.T_TEXT, 1, "Some text ...", [], theHtml.A_IND_R),
(theHtml.T_EMPTY, 2, "", None, theHtml.A_NONE),
]
theHtml.doConvert()
assert theHtml.theResult == (
"<p>Some <b>nested bold and <i>italic</i> and "
"<span style='text-decoration: line-through;'>strikethrough</span> "
"text</b> here</p>\n"
"<p style='margin-right: 40px;'>Some text ...</p>\n"
)
# END Test testCoreToHtml_Convert
# END Test testCoreToHtml_ConvertDirect
@pytest.mark.core
@@ -352,6 +437,7 @@ def testCoreToHtml_Complex(mockGUI, fncDir):
"""
theProject = NWProject(mockGUI)
theHtml = ToHtml(theProject)
theHtml.isNovel = True
# Build Project
# =============
@@ -366,13 +452,34 @@ def testCoreToHtml_Complex(mockGUI, fncDir):
"#### A Section\n\n\tMore text in scene two.\n",
]
resText = [
"<h1>My Novel</h1>\n<p><strong>By Jane Doh</strong></p>\n",
"<h2>Chapter 1</h2>\n<p>The text of chapter one.</p>\n",
"<h3>Scene 1</h3>\n<p>The text of scene one.</p>\n",
"<h4>A Section</h4>\n<p>More text in scene one.</p>\n",
"<h2>Chapter 2</h2>\n<p>The text of chapter two.</p>\n",
"<h3>Scene 2</h3>\n<p>The text of scene two.</p>\n",
"<h4>A Section</h4>\n<p>\tMore text in scene two.</p>\n",
(
"<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)):
@@ -385,7 +492,7 @@ def testCoreToHtml_Complex(mockGUI, fncDir):
assert theHtml.fullHTML == resText
theHtml.replaceTabs(nSpaces=2, spaceChar="&nbsp;")
resText[6] = "<h4>A Section</h4>\n<p>&nbsp;&nbsp;More text in scene two.</p>\n"
resText[6] = "<h3>A Section</h3>\n<p>&nbsp;&nbsp;More text in scene two.</p>\n"
# Check File
# ==========
+502 -135
View File
@@ -45,6 +45,7 @@ def testCoreToken_Setters(mockGUI):
assert theToken.textSize == 11
assert theToken.textFixed is False
assert theToken.lineHeight == 1.15
assert theToken.blockIndent == 4.0
assert theToken.doJustify is False
assert theToken.marginTitle == (1.000, 0.500)
assert theToken.marginHead1 == (1.000, 0.500)
@@ -68,7 +69,8 @@ def testCoreToken_Setters(mockGUI):
theToken.setSceneFormat("S: %title%", True)
theToken.setSectionFormat("X: %title%", True)
theToken.setFont("Monospace", 10, True)
theToken.setLineHeight(2)
theToken.setLineHeight(2.0)
theToken.setBlockIndent(6.0)
theToken.setJustify(True)
theToken.setTitleMargins(2.0, 2.0)
theToken.setHead1Margins(2.0, 2.0)
@@ -93,6 +95,7 @@ def testCoreToken_Setters(mockGUI):
assert theToken.textSize == 10
assert theToken.textFixed is True
assert theToken.lineHeight == 2.0
assert theToken.blockIndent == 6.0
assert theToken.doJustify is True
assert theToken.marginTitle == (2.0, 2.0)
assert theToken.marginHead1 == (2.0, 2.0)
@@ -109,6 +112,17 @@ def testCoreToken_Setters(mockGUI):
assert theToken.doComments is True
assert theToken.doKeywords is True
# Check Limits
theToken.setLineHeight(0.0)
assert theToken.lineHeight == 0.5
theToken.setLineHeight(10.0)
assert theToken.lineHeight == 5.0
theToken.setBlockIndent(-6.0)
assert theToken.blockIndent == 0.0
theToken.setBlockIndent(60.0)
assert theToken.blockIndent == 10.0
# END Test testCoreToken_Setters
@@ -145,13 +159,25 @@ def testCoreToken_TextOps(monkeypatch, nwMinimal, mockGUI):
assert theProject.saveProject()
# Root heading
# Root Heading
assert theToken.addRootHeading("stuff") is False
assert theToken.addRootHeading(sHandle) is False
# First Page
assert theToken.addRootHeading("7695ce551d265") is True
assert theToken.theMarkdown[-1] == "# Notes: Plot\n\n"
assert theToken.theTokens[-1] == (
Tokenizer.T_TITLE, 0, "Notes: Plot", None, Tokenizer.A_CENTRE
)
# Set text
# Not First Page
assert theToken.addRootHeading("7695ce551d265") is True
assert theToken.theMarkdown[-1] == "# Notes: Plot\n\n"
assert theToken.theTokens[-1] == (
Tokenizer.T_TITLE, 0, "Notes: Plot", None, Tokenizer.A_CENTRE | Tokenizer.A_PBB
)
# Set Text
assert theToken.setText("stuff") is False
assert theToken.setText(sHandle) is True
assert theToken.theText == docText
@@ -168,15 +194,8 @@ def testCoreToken_TextOps(monkeypatch, nwMinimal, mockGUI):
assert theToken.theText == docText
assert theToken.isNone is False
assert theToken.isTitle is False
assert theToken.isBook is False
assert theToken.isPage is False
assert theToken.isPart is False
assert theToken.isUnNum is False
assert theToken.isChap is False
assert theToken.isScene is True
assert theToken.isNote is False
assert theToken.isNovel is True
assert theToken.isNote is False
# Pre Processing
theToken.doPreProcessing()
@@ -190,39 +209,115 @@ def testCoreToken_TextOps(monkeypatch, nwMinimal, mockGUI):
# Save File
savePath = os.path.join(nwMinimal, "dump.nwd")
theToken.saveRawMarkdown(savePath)
assert readFile(savePath) == "# Notes: Plot\n\n"
assert readFile(savePath) == (
"# Notes: Plot\n\n"
"# Notes: Plot\n\n"
)
# END Test testCoreToken_TextOps
@pytest.mark.core
def testCoreToken_Tokenize(mockGUI):
"""Test the tokenization of the Tokenizer class.
def testCoreToken_HeaderFormat(mockGUI):
"""Test the tokenization of header formats in the Tokenizer class.
"""
theProject = NWProject(mockGUI)
theToken = Tokenizer(theProject)
theToken.setKeepMarkdown(True)
# Header 1
theToken.theText = "# Novel Title\n"
# Title
# =====
# Story File
theToken.isNovel = True
theToken.isNote = False
theToken.isFirst = True
theToken.theText = "#! Novel Title\n"
theToken.tokenizeText()
assert theToken.theTokens == [
(Tokenizer.T_HEAD1, 1, "Novel Title", None, Tokenizer.A_NONE),
(Tokenizer.T_TITLE, 1, "Novel Title", None, Tokenizer.A_CENTRE),
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
]
assert theToken.theMarkdown[-1] == "#! Novel Title\n\n"
# Note File
theToken.isNovel = False
theToken.isNote = True
theToken.isFirst = True
theToken.theText = "#! Note Title\n"
theToken.tokenizeText()
assert theToken.theTokens == [
(Tokenizer.T_HEAD1, 1, "Note Title", None, Tokenizer.A_CENTRE),
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
]
assert theToken.theMarkdown[-1] == "#! Note Title\n\n"
# Header 1
# ========
# Story File
theToken.isNovel = True
theToken.isNote = False
theToken.isFirst = True
theToken.theText = "# Novel Title\n"
theToken.tokenizeText()
assert theToken.theTokens == [
(Tokenizer.T_HEAD1, 1, "Novel Title", None, Tokenizer.A_CENTRE),
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
]
assert theToken.theMarkdown[-1] == "# Novel Title\n\n"
# Header 2
theToken.theText = "## Chapter One\n"
# Note File
theToken.isNovel = False
theToken.isNote = True
theToken.isFirst = True
theToken.theText = "# Note Title\n"
theToken.tokenizeText()
assert theToken.theTokens == [
(Tokenizer.T_HEAD2, 1, "Chapter One", None, Tokenizer.A_NONE),
(Tokenizer.T_HEAD1, 1, "Note Title", None, Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
]
assert theToken.theMarkdown[-1] == "# Note Title\n\n"
# Header 2
# ========
# Story File
theToken.isNovel = True
theToken.isNote = False
theToken.theText = "## Chapter One\n"
theToken.tokenizeText()
assert theToken.theTokens == [
(Tokenizer.T_HEAD2, 1, "Chapter One", None, Tokenizer.A_PBB),
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
]
assert theToken.theMarkdown[-1] == "## Chapter One\n\n"
# Note File
theToken.isNovel = False
theToken.isNote = True
theToken.theText = "## Heading 2\n"
theToken.tokenizeText()
assert theToken.theTokens == [
(Tokenizer.T_HEAD2, 1, "Heading 2", None, Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
]
assert theToken.theMarkdown[-1] == "## Heading 2\n\n"
# Header 3
# ========
# Story File
theToken.isNovel = True
theToken.isNote = False
theToken.theText = "### Scene One\n"
theToken.tokenizeText()
assert theToken.theTokens == [
(Tokenizer.T_HEAD3, 1, "Scene One", None, Tokenizer.A_NONE),
@@ -230,8 +325,26 @@ def testCoreToken_Tokenize(mockGUI):
]
assert theToken.theMarkdown[-1] == "### Scene One\n\n"
# Note File
theToken.isNovel = False
theToken.isNote = True
theToken.theText = "### Heading 3\n"
theToken.tokenizeText()
assert theToken.theTokens == [
(Tokenizer.T_HEAD3, 1, "Heading 3", None, Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
]
assert theToken.theMarkdown[-1] == "### Heading 3\n\n"
# Header 4
# ========
# Story File
theToken.isNovel = True
theToken.isNote = False
theToken.theText = "#### A Section\n"
theToken.tokenizeText()
assert theToken.theTokens == [
(Tokenizer.T_HEAD4, 1, "A Section", None, Tokenizer.A_NONE),
@@ -239,6 +352,83 @@ def testCoreToken_Tokenize(mockGUI):
]
assert theToken.theMarkdown[-1] == "#### A Section\n\n"
# Note File
theToken.isNovel = False
theToken.isNote = True
theToken.theText = "#### Heading 4\n"
theToken.tokenizeText()
assert theToken.theTokens == [
(Tokenizer.T_HEAD4, 1, "Heading 4", None, Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
]
assert theToken.theMarkdown[-1] == "#### Heading 4\n\n"
# Title
# =====
# Story File
theToken.isNovel = True
theToken.isNote = False
theToken.theText = "#! Title\n"
theToken.tokenizeText()
assert theToken.theTokens == [
(Tokenizer.T_TITLE, 1, "Title", None, Tokenizer.A_CENTRE | Tokenizer.A_PBB),
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
]
assert theToken.theMarkdown[-1] == "#! Title\n\n"
# Note File
theToken.isNovel = False
theToken.isNote = True
theToken.theText = "#! Title\n"
theToken.tokenizeText()
assert theToken.theTokens == [
(Tokenizer.T_HEAD1, 1, "Title", None, Tokenizer.A_CENTRE),
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
]
assert theToken.theMarkdown[-1] == "#! Title\n\n"
# Unnumbered
# ==========
# Story File
theToken.isNovel = True
theToken.isNote = False
theToken.theText = "##! Prologue\n"
theToken.tokenizeText()
assert theToken.theTokens == [
(Tokenizer.T_UNNUM, 1, "Prologue", None, Tokenizer.A_PBB),
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
]
assert theToken.theMarkdown[-1] == "##! Prologue\n\n"
# Note File
theToken.isNovel = False
theToken.isNote = True
theToken.theText = "##! Prologue\n"
theToken.tokenizeText()
assert theToken.theTokens == [
(Tokenizer.T_HEAD2, 1, "Prologue", None, Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
]
assert theToken.theMarkdown[-1] == "##! Prologue\n\n"
# END Test testCoreToken_HeaderFormat
@pytest.mark.core
def testCoreToken_MetaFormat(mockGUI):
"""Test the tokenization of meta formats in the Tokenizer class.
"""
theProject = NWProject(mockGUI)
theToken = Tokenizer(theProject)
theToken.setKeepMarkdown(True)
# Comment
theToken.theText = "% A comment\n"
theToken.tokenizeText()
@@ -297,6 +487,72 @@ def testCoreToken_Tokenize(mockGUI):
]
assert theToken.theMarkdown[-1] == "@pov: Bod\n@plot: Main\n@location: Europe\n\n"
# END Test testCoreToken_MetaFormat
@pytest.mark.core
def testCoreToken_MarginFormat(mockGUI):
"""Test the tokenization of margin formats in the Tokenizer class.
"""
theProject = NWProject(mockGUI)
theToken = Tokenizer(theProject)
theToken.setKeepMarkdown(True)
# Alignment and Indentation
dblIndent = Tokenizer.A_IND_L | Tokenizer.A_IND_R
rIndAlign = Tokenizer.A_RIGHT | Tokenizer.A_IND_R
theToken.theText = (
"Some regular text\n\n"
"Some left-aligned text <<\n\n"
">> Some right-aligned text\n\n"
">> Some centered text <<\n\n"
"> Left-indented block\n\n"
"Right-indented block <\n\n"
"> Double-indented block <\n\n"
">> Right-indent, right-aligned <\n\n"
)
theToken.tokenizeText()
assert theToken.theTokens == [
(Tokenizer.T_TEXT, 1, "Some regular text", [], Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
(Tokenizer.T_TEXT, 3, "Some left-aligned text", [], Tokenizer.A_LEFT),
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
(Tokenizer.T_TEXT, 5, "Some right-aligned text", [], Tokenizer.A_RIGHT),
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
(Tokenizer.T_TEXT, 7, "Some centered text", [], Tokenizer.A_CENTRE),
(Tokenizer.T_EMPTY, 8, "", None, Tokenizer.A_NONE),
(Tokenizer.T_TEXT, 9, "Left-indented block", [], Tokenizer.A_IND_L),
(Tokenizer.T_EMPTY, 10, "", None, Tokenizer.A_NONE),
(Tokenizer.T_TEXT, 11, "Right-indented block", [], Tokenizer.A_IND_R),
(Tokenizer.T_EMPTY, 12, "", None, Tokenizer.A_NONE),
(Tokenizer.T_TEXT, 13, "Double-indented block", [], dblIndent),
(Tokenizer.T_EMPTY, 14, "", None, Tokenizer.A_NONE),
(Tokenizer.T_TEXT, 15, "Right-indent, right-aligned", [], rIndAlign),
(Tokenizer.T_EMPTY, 16, "", None, Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 16, "", None, Tokenizer.A_NONE),
]
assert theToken.theMarkdown[-1] == (
"Some regular text\n\n"
"Some left-aligned text\n\n"
"Some right-aligned text\n\n"
"Some centered text\n\n"
"Left-indented block\n\n"
"Right-indented block\n\n"
"Double-indented block\n\n"
"Right-indent, right-aligned\n\n\n"
)
# END Test testCoreToken_MarginFormat
@pytest.mark.core
def testCoreToken_TextFormat(mockGUI):
"""Test the tokenization of text formats in the Tokenizer class.
"""
theProject = NWProject(mockGUI)
theToken = Tokenizer(theProject)
theToken.setKeepMarkdown(True)
# Text
theToken.theText = "Some plain text\non two lines\n\n\n"
theToken.tokenizeText()
@@ -408,55 +664,214 @@ def testCoreToken_Tokenize(mockGUI):
"Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n\n"
)
# Alignment and Indentation
dblIndent = Tokenizer.A_IND_L | Tokenizer.A_IND_R
rIndAlign = Tokenizer.A_RIGHT | Tokenizer.A_IND_R
theToken.theText = (
"Some regular text\n\n"
"Some left-aligned text <<\n\n"
">> Some right-aligned text\n\n"
">> Some centered text <<\n\n"
"> Left-indented block\n\n"
"Right-indented block <\n\n"
"> Double-indented block <\n\n"
">> Right-indent, right-aligned <\n\n"
)
theToken.tokenizeText()
assert theToken.theTokens == [
(Tokenizer.T_TEXT, 1, "Some regular text", [], Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
(Tokenizer.T_TEXT, 3, "Some left-aligned text", [], Tokenizer.A_LEFT),
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
(Tokenizer.T_TEXT, 5, "Some right-aligned text", [], Tokenizer.A_RIGHT),
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
(Tokenizer.T_TEXT, 7, "Some centered text", [], Tokenizer.A_CENTRE),
(Tokenizer.T_EMPTY, 8, "", None, Tokenizer.A_NONE),
(Tokenizer.T_TEXT, 9, "Left-indented block", [], Tokenizer.A_IND_L),
(Tokenizer.T_EMPTY, 10, "", None, Tokenizer.A_NONE),
(Tokenizer.T_TEXT, 11, "Right-indented block", [], Tokenizer.A_IND_R),
(Tokenizer.T_EMPTY, 12, "", None, Tokenizer.A_NONE),
(Tokenizer.T_TEXT, 13, "Double-indented block", [], dblIndent),
(Tokenizer.T_EMPTY, 14, "", None, Tokenizer.A_NONE),
(Tokenizer.T_TEXT, 15, "Right-indent, right-aligned", [], rIndAlign),
(Tokenizer.T_EMPTY, 16, "", None, Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 16, "", None, Tokenizer.A_NONE),
]
assert theToken.theMarkdown[-1] == (
"Some regular text\n\n"
"Some left-aligned text\n\n"
"Some right-aligned text\n\n"
"Some centered text\n\n"
"Left-indented block\n\n"
"Right-indented block\n\n"
"Double-indented block\n\n"
"Right-indent, right-aligned\n\n\n"
)
# END Test testCoreToken_Tokenize
# END Test testCoreToken_TextFormat
@pytest.mark.core
def testCoreToken_Headers(mockGUI):
def testCoreToken_SpecialFormat(mockGUI):
"""Test the tokenization of special formats in the Tokenizer class.
"""
theProject = NWProject(mockGUI)
theToken = Tokenizer(theProject)
theToken.isNovel = True
# New Page
# ========
correctResp = [
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_CENTRE),
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
(Tokenizer.T_HEAD1, 5, "Title Two", None, Tokenizer.A_CENTRE | Tokenizer.A_PBB),
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
]
# Command wo/Space
theToken.isFirst = True
theToken.theText = (
"# Title One\n\n"
"[NEWPAGE]\n\n"
"# Title Two\n\n"
)
theToken.tokenizeText()
assert theToken.theTokens == correctResp
# Command w/Space
theToken.isFirst = True
theToken.theText = (
"# Title One\n\n"
"[NEW PAGE]\n\n"
"# Title Two\n\n"
)
theToken.tokenizeText()
assert theToken.theTokens == correctResp
# Trailing Spaces
theToken.isFirst = True
theToken.theText = (
"# Title One\n\n"
"[NEW PAGE] \t\n\n"
"# Title Two\n\n"
)
theToken.tokenizeText()
assert theToken.theTokens == correctResp
# Single Empty Paragraph
# ======================
theToken.theText = (
"# Title One\n\n"
"[VSPACE] \n\n"
"Some text to go here ...\n\n"
)
theToken.tokenizeText()
assert theToken.theTokens == [
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
(Tokenizer.T_SKIP, 3, "", None, Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
(Tokenizer.T_TEXT, 5, "Some text to go here ...", [], Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
]
# Multiple Empty Paragraphs
# =========================
# One Skip
theToken.theText = (
"# Title One\n\n"
"[VSPACE:1] \n\n"
"Some text to go here ...\n\n"
)
theToken.tokenizeText()
assert theToken.theTokens == [
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
(Tokenizer.T_SKIP, 3, "", None, Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
(Tokenizer.T_TEXT, 5, "Some text to go here ...", [], Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
]
# Three Skips
theToken.theText = (
"# Title One\n\n"
"[VSPACE:3] \n\n"
"Some text to go here ...\n\n"
)
theToken.tokenizeText()
assert theToken.theTokens == [
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
(Tokenizer.T_SKIP, 3, "", None, Tokenizer.A_NONE),
(Tokenizer.T_SKIP, 3, "", None, Tokenizer.A_NONE),
(Tokenizer.T_SKIP, 3, "", None, Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
(Tokenizer.T_TEXT, 5, "Some text to go here ...", [], Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
]
# Malformed Command, Case 1
theToken.theText = (
"# Title One\n\n"
"[VSPACE:3xa] \n\n"
"Some text to go here ...\n\n"
)
theToken.tokenizeText()
assert theToken.theTokens == [
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
(Tokenizer.T_TEXT, 5, "Some text to go here ...", [], Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
]
# Malformed Command, Case 2
theToken.theText = (
"# Title One\n\n"
"[VSPACE:3.5]\n\n"
"Some text to go here ...\n\n"
)
theToken.tokenizeText()
assert theToken.theTokens == [
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
(Tokenizer.T_TEXT, 5, "Some text to go here ...", [], Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
]
# Malformed Command, Case 3
theToken.theText = (
"# Title One\n\n"
"[VSPACE:-1]\n\n"
"Some text to go here ...\n\n"
)
theToken.tokenizeText()
assert theToken.theTokens == [
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
(Tokenizer.T_TEXT, 5, "Some text to go here ...", [], Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
]
# Empty Paragraph and Page Break
# ==============================
# Single Skip
theToken.theText = (
"# Title One\n\n"
"[NEW PAGE]\n\n"
"[VSPACE]\n\n"
"Some text to go here ...\n\n"
)
theToken.tokenizeText()
assert theToken.theTokens == [
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
(Tokenizer.T_SKIP, 5, "", None, Tokenizer.A_PBB),
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
(Tokenizer.T_TEXT, 7, "Some text to go here ...", [], 0),
(Tokenizer.T_EMPTY, 8, "", None, Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 8, "", None, Tokenizer.A_NONE),
]
# Multiple Skip
theToken.theText = (
"# Title One\n\n"
"[NEW PAGE]\n\n"
"[VSPACE:3]\n\n"
"Some text to go here ...\n\n"
)
theToken.tokenizeText()
assert theToken.theTokens == [
(Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
(Tokenizer.T_SKIP, 5, "", None, Tokenizer.A_PBB),
(Tokenizer.T_SKIP, 5, "", None, Tokenizer.A_NONE),
(Tokenizer.T_SKIP, 5, "", None, Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 6, "", None, Tokenizer.A_NONE),
(Tokenizer.T_TEXT, 7, "Some text to go here ...", [], 0),
(Tokenizer.T_EMPTY, 8, "", None, Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 8, "", None, Tokenizer.A_NONE),
]
# END Test testCoreToken_SpecialFormat
@pytest.mark.core
def testCoreToken_ProcessHeaders(mockGUI):
"""Test the header and page parser of the Tokenizer class.
"""
theProject = NWProject(mockGUI)
@@ -466,31 +881,45 @@ def testCoreToken_Headers(mockGUI):
# Nothing
theToken.theText = "Some text ...\n"
assert theToken.doHeaders() is True
assert theToken.doHeaders() is False
theToken.isNone = True
assert theToken.doHeaders() is False
theToken.isNone = False
assert theToken.doHeaders() is True
assert theToken.doHeaders() is False
theToken.isNote = True
assert theToken.doHeaders() is False
theToken.isNote = False
##
# Novel
# Story FIles
##
theToken.isNone = False
theToken.isNote = False
theToken.isNovel = True
# Titles
# ======
# H1: Title
theToken.theText = "# Novel Title\n"
# H1: Title, First Page
assert theToken.isFirst is True
theToken.theText = "# Part One\n"
theToken.setTitleFormat(r"T: %title%")
theToken.tokenizeText()
theToken.doHeaders()
assert theToken.theTokens == [
(Tokenizer.T_HEAD1, 1, "T: Novel Title", None, Tokenizer.A_NONE),
(Tokenizer.T_HEAD1, 1, "T: Part One", None, Tokenizer.A_CENTRE),
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
]
# H1: Title, Not First Page
assert theToken.isFirst is False
theToken.theText = "# Part One\n"
theToken.setTitleFormat(r"T: %title%")
theToken.tokenizeText()
theToken.doHeaders()
assert theToken.theTokens == [
(Tokenizer.T_HEAD1, 1, "T: Part One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
]
@@ -508,20 +937,8 @@ def testCoreToken_Headers(mockGUI):
]
# H2: Unnumbered Chapter
theToken.theText = "## Chapter One\n"
theToken.setUnNumberedFormat(r"U: %title%")
theToken.isUnNum = True
theToken.tokenizeText()
theToken.doHeaders()
assert theToken.theTokens == [
(Tokenizer.T_HEAD2, 1, "U: Chapter One", None, Tokenizer.A_PBB),
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
]
# H2: Unnumbered Chapter with Star
theToken.theText = "## *Prologue\n"
theToken.setUnNumberedFormat(r"U: %title%")
theToken.isUnNum = False
theToken.tokenizeText()
theToken.doHeaders()
assert theToken.theTokens == [
@@ -703,54 +1120,4 @@ def testCoreToken_Headers(mockGUI):
theToken.doHeaders()
assert theToken.firstScene is False
##
# Title or Partition
##
theToken.isNovel = False
# H1: Title
theToken.theText = "# Novel Title\n"
theToken.setTitleFormat(r"T: %title%")
theToken.isTitle = True
theToken.isPart = False
theToken.tokenizeText()
theToken.doHeaders()
assert theToken.theTokens == [
(Tokenizer.T_TITLE, 1, "Novel Title", None, Tokenizer.A_PBB_AUT | Tokenizer.A_CENTRE),
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_PBA | Tokenizer.A_CENTRE),
]
# H1: Partition
theToken.theText = "# Partition Title\n"
theToken.setTitleFormat(r"T: %title%")
theToken.isTitle = False
theToken.isPart = True
theToken.tokenizeText()
theToken.doHeaders()
assert theToken.theTokens == [
(Tokenizer.T_HEAD1, 1, "Partition Title", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE),
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_PBA | Tokenizer.A_CENTRE),
]
##
# Page
##
theToken.isNovel = False
theToken.isTitle = False
theToken.isPart = False
theToken.isPage = True
# Some Page Text
theToken.theText = "Page text\n\nMore text\n"
theToken.tokenizeText()
theToken.doHeaders()
assert theToken.theTokens == [
(Tokenizer.T_TEXT, 1, "Page text", [], Tokenizer.A_PBB),
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE),
(Tokenizer.T_TEXT, 3, "More text", [], Tokenizer.A_NONE),
(Tokenizer.T_EMPTY, 3, "", None, Tokenizer.A_NONE),
]
# END Test testCoreToken_Headers
# END Test testCoreToken_ProcessHeaders
+2 -2
View File
@@ -65,7 +65,7 @@ def testCoreToOdt_Convert(mockGUI):
theDoc.closeDocument()
assert xmlToText(theDoc._xText) == (
'<office:text>'
'<text:h text:style-name="Heading_1" text:outline-level="1">Title</text:h>'
'<text:h text:style-name="P1" text:outline-level="1">Title</text:h>'
'</office:text>'
)
@@ -77,7 +77,7 @@ def testCoreToOdt_Convert(mockGUI):
theDoc.closeDocument()
assert xmlToText(theDoc._xText) == (
'<office:text>'
'<text:h text:style-name="Heading_2" text:outline-level="2">Chapter Title</text:h>'
'<text:h text:style-name="P2" text:outline-level="2">Chapter Title</text:h>'
'</office:text>'
)
+41 -167
View File
@@ -33,7 +33,7 @@ from nw.constants import nwFiles
@pytest.fixture(scope="function")
def dummyItems(mockGUI):
def mockItems(mockGUI):
"""Create a list of mock items.
"""
theProject = NWProject(mockGUI)
@@ -54,7 +54,7 @@ def dummyItems(mockGUI):
itemC.itemName = "Chapter One"
itemC.itemType = nwItemType.FILE
itemC.itemClass = nwItemClass.NOVEL
itemC.itemLayout = nwItemLayout.CHAPTER
itemC.itemLayout = nwItemLayout.DOCUMENT
itemC.charCount = 300
itemC.wordCount = 50
itemC.paraCount = 2
@@ -63,7 +63,7 @@ def dummyItems(mockGUI):
itemD.itemName = "Scene One"
itemD.itemType = nwItemType.FILE
itemD.itemClass = nwItemClass.NOVEL
itemD.itemLayout = nwItemLayout.SCENE
itemD.itemLayout = nwItemLayout.DOCUMENT
itemD.charCount = 3000
itemD.wordCount = 500
itemD.paraCount = 20
@@ -110,7 +110,7 @@ def dummyItems(mockGUI):
@pytest.mark.core
def testCoreTree_BuildTree(mockGUI, dummyItems):
def testCoreTree_BuildTree(mockGUI, mockItems):
"""Test building a project tree from a list of items.
"""
theProject = NWProject(mockGUI)
@@ -128,7 +128,7 @@ def testCoreTree_BuildTree(mockGUI, dummyItems):
assert not theTree.isTrashRoot("a000000000003")
aHandles = []
for tHandle, pHandle, nwItem in dummyItems:
for tHandle, pHandle, nwItem in mockItems:
aHandles.append(tHandle)
assert theTree.append(tHandle, pHandle, nwItem)
@@ -138,7 +138,7 @@ def testCoreTree_BuildTree(mockGUI, dummyItems):
assert theTree
# Check the number of elements (calls __len__)
assert len(theTree) == len(dummyItems)
assert len(theTree) == len(mockItems)
# Check that we have the correct handles
assert theTree.handles() == aHandles
@@ -160,46 +160,46 @@ def testCoreTree_BuildTree(mockGUI, dummyItems):
itemT.isExpanded = False
assert not theTree.append("1234567890abc", None, itemT)
assert len(theTree) == len(dummyItems)
assert len(theTree) == len(mockItems)
# Generate handle automatically
itemT = NWItem(theProject)
itemT.itemName = "New File"
itemT.itemType = nwItemType.FILE
itemT.itemClass = nwItemClass.NOVEL
itemT.itemLayout = nwItemLayout.SCENE
itemT.itemLayout = nwItemLayout.DOCUMENT
assert theTree.append(None, None, itemT)
assert len(theTree) == len(dummyItems) + 1
assert len(theTree) == len(mockItems) + 1
theList = theTree.handles()
assert theList[-1] == "73475cb40a568"
# Try to add existing handle
assert not theTree.append("73475cb40a568", None, itemT)
assert len(theTree) == len(dummyItems) + 1
assert len(theTree) == len(mockItems) + 1
# Delete a non-existing item
del theTree["stuff"]
assert len(theTree) == len(dummyItems) + 1
assert len(theTree) == len(mockItems) + 1
# Delete the last item
del theTree["73475cb40a568"]
assert len(theTree) == len(dummyItems)
assert len(theTree) == len(mockItems)
assert "73475cb40a568" not in theTree
# Delete the Novel, Archive and Trash folders
del theTree["a000000000001"]
assert len(theTree) == len(dummyItems) - 1
assert len(theTree) == len(mockItems) - 1
assert "a000000000001" not in theTree
del theTree["a000000000002"]
assert len(theTree) == len(dummyItems) - 2
assert len(theTree) == len(mockItems) - 2
assert "a000000000002" not in theTree
assert theTree.archiveRoot() is None
del theTree["a000000000003"]
assert len(theTree) == len(dummyItems) - 3
assert len(theTree) == len(mockItems) - 3
assert "a000000000003" not in theTree
assert theTree.trashRoot() is None
@@ -207,16 +207,16 @@ def testCoreTree_BuildTree(mockGUI, dummyItems):
@pytest.mark.core
def testCoreTree_Methods(mockGUI, dummyItems):
"""Test bvarious class methods.
def testCoreTree_Methods(mockGUI, mockItems):
"""Test various class methods.
"""
theProject = NWProject(mockGUI)
theTree = NWTree(theProject)
for tHandle, pHandle, nwItem in dummyItems:
for tHandle, pHandle, nwItem in mockItems:
theTree.append(tHandle, pHandle, nwItem)
assert len(theTree) == len(dummyItems)
assert len(theTree) == len(mockItems)
# Root item lookup
theTree._treeRoots.append("stuff")
@@ -254,141 +254,15 @@ def testCoreTree_Methods(mockGUI, dummyItems):
]
# Change file layout
assert not theTree.setFileItemLayout("stuff", nwItemLayout.UNNUMBERED)
assert not theTree.setFileItemLayout("b000000000001", nwItemLayout.UNNUMBERED)
assert not theTree.setFileItemLayout("c000000000001", "stuff")
assert theTree.setFileItemLayout("c000000000001", nwItemLayout.UNNUMBERED)
assert theTree["c000000000001"].itemLayout == nwItemLayout.UNNUMBERED
assert theTree.setFileItemLayout("stuff", nwItemLayout.DOCUMENT) is False
assert theTree.setFileItemLayout("b000000000001", nwItemLayout.DOCUMENT) is False
assert theTree.setFileItemLayout("c000000000001", "stuff") is False
assert theTree.setFileItemLayout("c000000000001", nwItemLayout.NOTE) is True
assert theTree["c000000000001"].itemLayout == nwItemLayout.NOTE
# END Test testCoreTree_Methods
@pytest.mark.core
def testCoreTree_UpdateItemLayout(mockGUI, dummyItems):
"""Test building a project tree from a list of items.
"""
theProject = NWProject(mockGUI)
theTree = NWTree(theProject)
for tHandle, pHandle, nwItem in dummyItems:
theTree.append(tHandle, pHandle, nwItem)
assert len(theTree) == len(dummyItems)
# Check rejected items
assert not theTree.updateItemLayout("0000000000000", "H1") # Non-existent handle
assert not theTree.updateItemLayout("a000000000004", "H2") # Character file
assert not theTree.updateItemLayout("c000000000002", "H0") # Wrong header level
cHandle = "c000000000002"
# Check layouts we won't change
theTree[cHandle].setLayout(nwItemLayout.NO_LAYOUT)
assert not theTree.updateItemLayout("c000000000002", "H1")
theTree[cHandle].setLayout(nwItemLayout.TITLE)
assert not theTree.updateItemLayout("c000000000002", "H1")
theTree[cHandle].setLayout(nwItemLayout.PAGE)
assert not theTree.updateItemLayout("c000000000002", "H1")
theTree[cHandle].setLayout(nwItemLayout.NOTE)
assert not theTree.updateItemLayout("c000000000002", "H1")
# BOOK is also a layout we change to, but never from
theTree[cHandle].setLayout(nwItemLayout.BOOK)
assert not theTree.updateItemLayout("c000000000002", "H1")
# Test SCENE Changes
# ==================
# H1 -> BOOK
theTree[cHandle].setLayout(nwItemLayout.SCENE)
assert theTree.updateItemLayout("c000000000002", "H1")
assert theTree[cHandle].itemLayout == nwItemLayout.BOOK
# H2 -> CHAPTER
theTree[cHandle].setLayout(nwItemLayout.SCENE)
assert theTree.updateItemLayout("c000000000002", "H2")
assert theTree[cHandle].itemLayout == nwItemLayout.CHAPTER
# H3 -> No CHange
theTree[cHandle].setLayout(nwItemLayout.SCENE)
assert not theTree.updateItemLayout("c000000000002", "H3")
# H4 -> No CHange
theTree[cHandle].setLayout(nwItemLayout.SCENE)
assert not theTree.updateItemLayout("c000000000002", "H4")
# Test CHAPTER Changes
# ====================
# H1 -> BOOK
theTree[cHandle].setLayout(nwItemLayout.CHAPTER)
assert theTree.updateItemLayout("c000000000002", "H1")
assert theTree[cHandle].itemLayout == nwItemLayout.BOOK
# H2 -> No Change
theTree[cHandle].setLayout(nwItemLayout.CHAPTER)
assert not theTree.updateItemLayout("c000000000002", "H2")
# H3 -> SCENE
theTree[cHandle].setLayout(nwItemLayout.CHAPTER)
assert theTree.updateItemLayout("c000000000002", "H3")
assert theTree[cHandle].itemLayout == nwItemLayout.SCENE
# H4 -> SCENE
theTree[cHandle].setLayout(nwItemLayout.CHAPTER)
assert theTree.updateItemLayout("c000000000002", "H4")
assert theTree[cHandle].itemLayout == nwItemLayout.SCENE
# Test UNNUMBERED Changes
# =======================
# H1 -> BOOK
theTree[cHandle].setLayout(nwItemLayout.UNNUMBERED)
assert theTree.updateItemLayout("c000000000002", "H1")
assert theTree[cHandle].itemLayout == nwItemLayout.BOOK
# H2 -> No Change
theTree[cHandle].setLayout(nwItemLayout.UNNUMBERED)
assert not theTree.updateItemLayout("c000000000002", "H2")
# H3 -> SCENE
theTree[cHandle].setLayout(nwItemLayout.UNNUMBERED)
assert theTree.updateItemLayout("c000000000002", "H3")
assert theTree[cHandle].itemLayout == nwItemLayout.SCENE
# H4 -> SCENE
theTree[cHandle].setLayout(nwItemLayout.UNNUMBERED)
assert theTree.updateItemLayout("c000000000002", "H4")
assert theTree[cHandle].itemLayout == nwItemLayout.SCENE
# Test PARTITION Changes
# ======================
# H1 -> BOOK
theTree[cHandle].setLayout(nwItemLayout.PARTITION)
assert not theTree.updateItemLayout("c000000000002", "H1")
# H2 -> No Change
theTree[cHandle].setLayout(nwItemLayout.PARTITION)
assert theTree.updateItemLayout("c000000000002", "H2")
assert theTree[cHandle].itemLayout == nwItemLayout.CHAPTER
# H3 -> SCENE
theTree[cHandle].setLayout(nwItemLayout.PARTITION)
assert theTree.updateItemLayout("c000000000002", "H3")
assert theTree[cHandle].itemLayout == nwItemLayout.SCENE
# H4 -> SCENE
theTree[cHandle].setLayout(nwItemLayout.PARTITION)
assert theTree.updateItemLayout("c000000000002", "H4")
assert theTree[cHandle].itemLayout == nwItemLayout.SCENE
# END Test testCoreTree_UpdateItemLayout
@pytest.mark.core
def testCoreTree_MakeHandles(monkeypatch, mockGUI):
"""Test generating item handles.
@@ -433,16 +307,16 @@ def testCoreTree_MakeHandles(monkeypatch, mockGUI):
@pytest.mark.core
def testCoreTree_Stats(mockGUI, dummyItems):
def testCoreTree_Stats(mockGUI, mockItems):
"""Test project stats methods.
"""
theProject = NWProject(mockGUI)
theTree = NWTree(theProject)
for tHandle, pHandle, nwItem in dummyItems:
for tHandle, pHandle, nwItem in mockItems:
theTree.append(tHandle, pHandle, nwItem)
assert len(theTree) == len(dummyItems)
assert len(theTree) == len(mockItems)
theTree._treeOrder.append("stuff")
# Count Words
@@ -460,18 +334,18 @@ def testCoreTree_Stats(mockGUI, dummyItems):
@pytest.mark.core
def testCoreTree_Reorder(mockGUI, dummyItems):
def testCoreTree_Reorder(mockGUI, mockItems):
"""Test changing tree order.
"""
theProject = NWProject(mockGUI)
theTree = NWTree(theProject)
aHandle = []
for tHandle, pHandle, nwItem in dummyItems:
for tHandle, pHandle, nwItem in mockItems:
aHandle.append(tHandle)
theTree.append(tHandle, pHandle, nwItem)
assert len(theTree) == len(dummyItems)
assert len(theTree) == len(mockItems)
bHandle = aHandle.copy()
bHandle[2], bHandle[3] = bHandle[3], bHandle[2]
@@ -492,16 +366,16 @@ def testCoreTree_Reorder(mockGUI, dummyItems):
@pytest.mark.core
def testCoreTree_XMLPackUnpack(mockGUI, dummyItems):
def testCoreTree_XMLPackUnpack(mockGUI, mockItems):
"""Test packing and unpacking the tree to and from XML.
"""
theProject = NWProject(mockGUI)
theTree = NWTree(theProject)
for tHandle, pHandle, nwItem in dummyItems:
for tHandle, pHandle, nwItem in mockItems:
theTree.append(tHandle, pHandle, nwItem)
assert len(theTree) == len(dummyItems)
assert len(theTree) == len(mockItems)
nwXML = etree.Element("novelWriterXML")
theTree.packXML(nwXML)
@@ -516,11 +390,11 @@ def testCoreTree_XMLPackUnpack(mockGUI, dummyItems):
b"<expanded>True</expanded></item>"
b"<item handle=\"c000000000001\" order=\"0\" parent=\"b000000000001\">"
b"<name>Chapter One</name><type>FILE</type><class>NOVEL</class><status>None</status>"
b"<exported>True</exported><layout>CHAPTER</layout><charCount>300</charCount>"
b"<exported>True</exported><layout>DOCUMENT</layout><charCount>300</charCount>"
b"<wordCount>50</wordCount><paraCount>2</paraCount><cursorPos>0</cursorPos></item>"
b"<item handle=\"c000000000002\" order=\"0\" parent=\"b000000000001\">"
b"<name>Scene One</name><type>FILE</type><class>NOVEL</class><status>None</status>"
b"<exported>True</exported><layout>SCENE</layout><charCount>3000</charCount>"
b"<exported>True</exported><layout>DOCUMENT</layout><charCount>3000</charCount>"
b"<wordCount>500</wordCount><paraCount>20</paraCount><cursorPos>0</cursorPos></item>"
b"<item handle=\"a000000000002\" order=\"0\" parent=\"None\">"
b"<name>Outtakes</name><type>ROOT</type><class>ARCHIVE</class><status>None</status>"
@@ -542,22 +416,22 @@ def testCoreTree_XMLPackUnpack(mockGUI, dummyItems):
assert len(theTree) == 0
assert not theTree.unpackXML(nwXML)
assert theTree.unpackXML(nwXML[0])
assert len(theTree) == len(dummyItems)
assert len(theTree) == len(mockItems)
# END Test testCoreTree_XMLPackUnpack
@pytest.mark.core
def testCoreTree_ToCFile(monkeypatch, mockGUI, dummyItems, tmpDir):
def testCoreTree_ToCFile(monkeypatch, mockGUI, mockItems, tmpDir):
"""Test writing the ToC.txt file.
"""
theProject = NWProject(mockGUI)
theTree = NWTree(theProject)
for tHandle, pHandle, nwItem in dummyItems:
for tHandle, pHandle, nwItem in mockItems:
theTree.append(tHandle, pHandle, nwItem)
assert len(theTree) == len(dummyItems)
assert len(theTree) == len(mockItems)
theTree._treeOrder.append("stuff")
def dummyIsFile(fileName):
@@ -588,8 +462,8 @@ def testCoreTree_ToCFile(monkeypatch, mockGUI, dummyItems, tmpDir):
"\n"
"File Name Class Layout Document Label\n"
"-------------------------------------------------------------\n"
f"{pathA} NOVEL CHAPTER Chapter One\n"
f"{pathB} NOVEL SCENE Scene One\n"
f"{pathA} NOVEL DOCUMENT Chapter One\n"
f"{pathB} NOVEL DOCUMENT Scene One\n"
f"{pathC} CHARACTER NOTE Jane Doe\n"
)