Move test files into subfolders with similar structure as source code
This commit is contained in:
@@ -0,0 +1,163 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
novelWriter – NWDoc Class Tester
|
||||
================================
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, 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 dummy import causeOSError
|
||||
|
||||
from nw.core import NWProject, NWDoc
|
||||
from nw.core.item import NWItem
|
||||
from nw.constants import nwItemClass, nwItemLayout
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreDocument_LoadSave(monkeypatch, dummyGUI, nwMinimal):
|
||||
"""Test loading and saving a document with the NWDoc class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
assert theProject.openProject(nwMinimal)
|
||||
assert theProject.projPath == nwMinimal
|
||||
|
||||
theDoc = NWDoc(theProject, dummyGUI)
|
||||
sHandle = "8c659a11cd429"
|
||||
|
||||
# Not a valid handle
|
||||
assert theDoc.openDocument("dummy") is None
|
||||
|
||||
# Non-existent handle
|
||||
assert theDoc.openDocument("0000000000000") is None
|
||||
|
||||
# Cause open() to fail while loading
|
||||
def dummyOpen(*args, **kwargs):
|
||||
raise OSError
|
||||
|
||||
monkeypatch.setattr("builtins.open", dummyOpen)
|
||||
assert theDoc.openDocument(sHandle) is None
|
||||
monkeypatch.undo()
|
||||
|
||||
# Load the text
|
||||
assert theDoc.openDocument(sHandle) == "### New Scene\n\n"
|
||||
|
||||
# Try to open a new (non-existent) file
|
||||
nHandle = theProject.projTree.findRoot(nwItemClass.NOVEL)
|
||||
assert nHandle is not None
|
||||
xHandle = theProject.newFile("New File", nwItemClass.NOVEL, nHandle)
|
||||
assert theDoc.openDocument(xHandle) == ""
|
||||
|
||||
# Check cached item
|
||||
assert isinstance(theDoc._theItem, NWItem)
|
||||
assert theDoc.openDocument(xHandle, isOrphan=True) == ""
|
||||
assert theDoc._theItem is None
|
||||
|
||||
# Set handle and save again
|
||||
theText = "### Test File\n\nText ...\n\n"
|
||||
assert theDoc.openDocument(xHandle) == ""
|
||||
assert theDoc.saveDocument(theText)
|
||||
|
||||
# Save again to ensure temp file and previous file is handled
|
||||
assert theDoc.saveDocument(theText)
|
||||
|
||||
# Check file content
|
||||
docPath = os.path.join(nwMinimal, "content", xHandle+".nwd")
|
||||
with open(docPath, mode="r", encoding="utf8") as inFile:
|
||||
assert inFile.read() == (
|
||||
"%%~name: New File\n"
|
||||
f"%%~path: a508bb932959c/{xHandle}\n"
|
||||
"%%~kind: NOVEL/SCENE\n"
|
||||
"### Test File\n\n"
|
||||
"Text ...\n\n"
|
||||
)
|
||||
|
||||
# Force no meta data
|
||||
theDoc._theItem = None
|
||||
assert theDoc.saveDocument(theText)
|
||||
|
||||
with open(docPath, mode="r", encoding="utf8") as inFile:
|
||||
assert inFile.read() == theText
|
||||
|
||||
# Cause open() to fail while saving
|
||||
monkeypatch.setattr("builtins.open", causeOSError)
|
||||
assert not theDoc.saveDocument(theText)
|
||||
monkeypatch.undo()
|
||||
|
||||
# Saving with no handle
|
||||
theDoc.clearDocument()
|
||||
assert not theDoc.saveDocument(theText)
|
||||
|
||||
# Delete the last document
|
||||
assert not theDoc.deleteDocument("dummy")
|
||||
assert os.path.isfile(docPath)
|
||||
|
||||
# Cause the delete to fail
|
||||
monkeypatch.setattr("os.unlink", causeOSError)
|
||||
assert not theDoc.deleteDocument(xHandle)
|
||||
monkeypatch.undo()
|
||||
|
||||
# Make the delete pass
|
||||
assert theDoc.deleteDocument(xHandle)
|
||||
assert not os.path.isfile(docPath)
|
||||
|
||||
# END Test testCoreDocument_Load
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreDocument_Methods(monkeypatch, dummyGUI, nwMinimal):
|
||||
"""Test other methods of the NWDoc class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
assert theProject.openProject(nwMinimal)
|
||||
assert theProject.projPath == nwMinimal
|
||||
|
||||
theDoc = NWDoc(theProject, dummyGUI)
|
||||
sHandle = "8c659a11cd429"
|
||||
docPath = os.path.join(nwMinimal, "content", sHandle+".nwd")
|
||||
|
||||
assert theDoc.openDocument(sHandle) == "### New Scene\n\n"
|
||||
|
||||
# Check location
|
||||
assert theDoc.getFileLocation() == docPath
|
||||
|
||||
# Check the item
|
||||
assert theDoc.getCurrentItem() is not None
|
||||
assert theDoc.getCurrentItem().itemHandle == sHandle
|
||||
|
||||
# Check the meta
|
||||
theName, theParent, theClass, theLayout = theDoc.getMeta()
|
||||
assert theName == "New Scene"
|
||||
assert theParent == "a6d311a93600a"
|
||||
assert theClass == nwItemClass.NOVEL
|
||||
assert theLayout == nwItemLayout.SCENE
|
||||
|
||||
# Add meta data garbage
|
||||
assert theDoc.saveDocument("%%~ stuff\n### Test File\n\nText ...\n\n")
|
||||
with open(docPath, mode="r", encoding="utf8") as inFile:
|
||||
assert inFile.read() == (
|
||||
"%%~name: New Scene\n"
|
||||
f"%%~path: a6d311a93600a/{sHandle}\n"
|
||||
"%%~kind: NOVEL/SCENE\n"
|
||||
"%%~ stuff\n"
|
||||
"### Test File\n\n"
|
||||
"Text ...\n\n"
|
||||
)
|
||||
|
||||
assert theDoc.openDocument(sHandle) == "### Test File\n\nText ...\n\n"
|
||||
|
||||
# END Test testCoreDocument_Methods
|
||||
@@ -0,0 +1,678 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
novelWriter – Project Class Tester
|
||||
==================================
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, 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 pytest
|
||||
import os
|
||||
import json
|
||||
|
||||
from shutil import copyfile
|
||||
|
||||
from tools import cmpFiles
|
||||
|
||||
from nw.core.project import NWProject
|
||||
from nw.core.index import NWIndex
|
||||
from nw.constants import nwItemClass, nwItemLayout
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreIndex_LoadSave(monkeypatch, nwLipsum, dummyGUI, outDir, refDir):
|
||||
"""Test core functionality of scaning, saving, loading and checking
|
||||
the index cache file.
|
||||
"""
|
||||
projFile = os.path.join(nwLipsum, "meta", "tagsIndex.json")
|
||||
testFile = os.path.join(outDir, "coreIndex_LoadSave_tagsIndex.json")
|
||||
compFile = os.path.join(refDir, "coreIndex_LoadSave_tagsIndex.json")
|
||||
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject.projTree.setSeed(42)
|
||||
assert theProject.openProject(nwLipsum)
|
||||
|
||||
monkeypatch.setattr("nw.core.index.time", lambda: 123.4)
|
||||
|
||||
theIndex = NWIndex(theProject, dummyGUI)
|
||||
notIndexable = {
|
||||
"b3643d0f92e32": False, # Novel ROOT
|
||||
"45e6b01ca35c1": False, # Chapter One FOLDER
|
||||
"6bd935d2490cd": False, # Chapter Two FOLDER
|
||||
"67a8707f2f249": False, # Character ROOT
|
||||
"6c6afb1247750": False, # Plot ROOT
|
||||
"60bdf227455cc": False, # World ROOT
|
||||
}
|
||||
for tItem in theProject.projTree:
|
||||
assert theIndex.reIndexHandle(tItem.itemHandle) is notIndexable.get(tItem.itemHandle, True)
|
||||
|
||||
assert not theIndex.reIndexHandle(None)
|
||||
|
||||
# Dummy exception function
|
||||
def doPanic(*arg, **kwargs):
|
||||
raise Exception
|
||||
|
||||
# Make the save fail
|
||||
monkeypatch.setattr(json, "dump", doPanic)
|
||||
assert not theIndex.saveIndex()
|
||||
|
||||
# Make the save pass
|
||||
monkeypatch.undo()
|
||||
assert theIndex.saveIndex()
|
||||
|
||||
# Take a copy of the index
|
||||
tagIndex = str(theIndex._tagIndex)
|
||||
refIndex = str(theIndex._refIndex)
|
||||
novelIndex = str(theIndex._novelIndex)
|
||||
noteIndex = str(theIndex._noteIndex)
|
||||
textCounts = str(theIndex._textCounts)
|
||||
|
||||
# Delete a handle
|
||||
assert theIndex._tagIndex.get("Bod", None) is not None
|
||||
assert theIndex._refIndex.get("4c4f28287af27", None) is not None
|
||||
assert theIndex._noteIndex.get("4c4f28287af27", None) is not None
|
||||
assert theIndex._textCounts.get("4c4f28287af27", None) is not None
|
||||
theIndex.deleteHandle("4c4f28287af27")
|
||||
assert theIndex._tagIndex.get("Bod", None) is None
|
||||
assert theIndex._refIndex.get("4c4f28287af27", None) is None
|
||||
assert theIndex._noteIndex.get("4c4f28287af27", None) is None
|
||||
assert theIndex._textCounts.get("4c4f28287af27", None) is None
|
||||
|
||||
# Clear the index
|
||||
theIndex.clearIndex()
|
||||
assert not theIndex._tagIndex
|
||||
assert not theIndex._refIndex
|
||||
assert not theIndex._novelIndex
|
||||
assert not theIndex._noteIndex
|
||||
assert not theIndex._textCounts
|
||||
|
||||
# Make the load fail
|
||||
monkeypatch.setattr(json, "load", doPanic)
|
||||
assert not theIndex.loadIndex()
|
||||
|
||||
# Make the load pass
|
||||
monkeypatch.undo()
|
||||
assert theIndex.loadIndex()
|
||||
|
||||
assert str(theIndex._tagIndex) == tagIndex
|
||||
assert str(theIndex._refIndex) == refIndex
|
||||
assert str(theIndex._novelIndex) == novelIndex
|
||||
assert str(theIndex._noteIndex) == noteIndex
|
||||
assert str(theIndex._textCounts) == textCounts
|
||||
|
||||
# Break the index and check that we notice
|
||||
assert not theIndex.indexBroken
|
||||
theIndex._tagIndex["Bod"].append("Stuff") # No longer len() == 4
|
||||
theIndex.checkIndex()
|
||||
assert theIndex.indexBroken
|
||||
|
||||
assert theIndex.loadIndex()
|
||||
assert not theIndex.indexBroken
|
||||
theIndex._refIndex["fb609cd8319dc"]["T000001"]["tags"].append("Stuff") # No longer len() == 3
|
||||
theIndex.checkIndex()
|
||||
assert theIndex.indexBroken
|
||||
|
||||
assert theIndex.loadIndex()
|
||||
assert not theIndex.indexBroken
|
||||
theIndex._novelIndex["7a992350f3eb6"]["T000001"]["Stuff"] = "" # No longer len(keys()) == 8
|
||||
theIndex.checkIndex()
|
||||
assert theIndex.indexBroken
|
||||
|
||||
assert theIndex.loadIndex()
|
||||
assert not theIndex.indexBroken
|
||||
theIndex._noteIndex["4c4f28287af27"]["T000001"]["Stuff"] = "" # No longer len(keys()) == 8
|
||||
theIndex.checkIndex()
|
||||
assert theIndex.indexBroken
|
||||
|
||||
assert theIndex.loadIndex()
|
||||
assert not theIndex.indexBroken
|
||||
theIndex._textCounts["7a992350f3eb6"].append("Stuff") # No longer len() == 3
|
||||
theIndex.checkIndex()
|
||||
assert theIndex.indexBroken
|
||||
|
||||
# Make the try/except trigger as well
|
||||
assert theIndex.loadIndex()
|
||||
assert not theIndex.indexBroken
|
||||
theIndex._refIndex["fb609cd8319dc"]["T000001"] = {"tagssss": []} # Wrong key name
|
||||
theIndex.checkIndex()
|
||||
assert theIndex.indexBroken
|
||||
|
||||
# Finalise
|
||||
assert theProject.closeProject()
|
||||
|
||||
copyfile(projFile, testFile)
|
||||
assert cmpFiles(testFile, compFile)
|
||||
|
||||
# END Test testCoreIndex_LoadSave
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreIndex_ScanThis(nwMinimal, dummyGUI):
|
||||
"""Test the tag scanner function scanThis.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject.projTree.setSeed(42)
|
||||
assert theProject.openProject(nwMinimal)
|
||||
|
||||
theIndex = NWIndex(theProject, dummyGUI)
|
||||
|
||||
isValid, theBits, thePos = theIndex.scanThis("tag: this, and this")
|
||||
assert not isValid
|
||||
|
||||
isValid, theBits, thePos = theIndex.scanThis("@")
|
||||
assert not isValid
|
||||
|
||||
isValid, theBits, thePos = theIndex.scanThis("@:")
|
||||
assert not isValid
|
||||
|
||||
isValid, theBits, thePos = theIndex.scanThis(" @a: b")
|
||||
assert not isValid
|
||||
|
||||
isValid, theBits, thePos = theIndex.scanThis("@a:")
|
||||
assert isValid
|
||||
assert theBits == ["@a"]
|
||||
assert thePos == [0]
|
||||
|
||||
isValid, theBits, thePos = theIndex.scanThis("@a:b")
|
||||
assert isValid
|
||||
assert theBits == ["@a", "b"]
|
||||
assert thePos == [0, 3]
|
||||
|
||||
isValid, theBits, thePos = theIndex.scanThis("@a:b,c,d")
|
||||
assert isValid
|
||||
assert theBits == ["@a", "b", "c", "d"]
|
||||
assert thePos == [0, 3, 5, 7]
|
||||
|
||||
isValid, theBits, thePos = theIndex.scanThis("@a : b , c , d")
|
||||
assert isValid
|
||||
assert theBits == ["@a", "b", "c", "d"]
|
||||
assert thePos == [0, 5, 9, 13]
|
||||
|
||||
isValid, theBits, thePos = theIndex.scanThis("@tag: this, and this")
|
||||
assert isValid
|
||||
assert theBits == ["@tag", "this", "and this"]
|
||||
assert thePos == [0, 6, 12]
|
||||
|
||||
assert theProject.closeProject()
|
||||
|
||||
# END Test testCoreIndex_ScanThis
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreIndex_CheckThese(nwMinimal, dummyGUI):
|
||||
"""Test the tag checker function checkThese.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject.projTree.setSeed(42)
|
||||
assert theProject.openProject(nwMinimal)
|
||||
|
||||
theIndex = NWIndex(theProject, dummyGUI)
|
||||
nHandle = theProject.newFile("Hello", nwItemClass.NOVEL, "a508bb932959c")
|
||||
cHandle = theProject.newFile("Jane", nwItemClass.CHARACTER, "afb3043c7b2b3")
|
||||
nItem = theProject.projTree[nHandle]
|
||||
cItem = theProject.projTree[cHandle]
|
||||
|
||||
assert not theIndex.novelChangedSince(0)
|
||||
assert not theIndex.notesChangedSince(0)
|
||||
assert not theIndex.indexChangedSince(0)
|
||||
|
||||
assert theIndex.scanText(cHandle, (
|
||||
"# Jane Smith\n"
|
||||
"@tag: Jane"
|
||||
))
|
||||
assert theIndex.scanText(nHandle, (
|
||||
"# Hello World!\n"
|
||||
"@pov: Jane"
|
||||
))
|
||||
assert theIndex._tagIndex == {"Jane": [2, cHandle, "CHARACTER", "T000001"]}
|
||||
assert theIndex.getNovelData(nHandle, "T000001")["title"] == "Hello World!"
|
||||
|
||||
assert theIndex.novelChangedSince(0)
|
||||
assert theIndex.notesChangedSince(0)
|
||||
assert theIndex.indexChangedSince(0)
|
||||
|
||||
assert theIndex.checkThese([], cItem) == []
|
||||
assert theIndex.checkThese(["@tag", "Jane"], cItem) == [True, True]
|
||||
assert theIndex.checkThese(["@tag", "John"], cItem) == [True, True]
|
||||
assert theIndex.checkThese(["@tag", "Jane"], nItem) == [True, False]
|
||||
assert theIndex.checkThese(["@tag", "John"], nItem) == [True, True]
|
||||
assert theIndex.checkThese(["@pov", "John"], nItem) == [True, False]
|
||||
assert theIndex.checkThese(["@pov", "Jane"], nItem) == [True, True]
|
||||
assert theIndex.checkThese(["@ pov", "Jane"], nItem) == [False, False]
|
||||
assert theIndex.checkThese(["@what", "Jane"], nItem) == [False, False]
|
||||
|
||||
assert theProject.closeProject()
|
||||
|
||||
# END Test testCoreIndex_CheckThese
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreIndex_ScanText(nwMinimal, dummyGUI):
|
||||
"""Check the index text scanner.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject.projTree.setSeed(42)
|
||||
assert theProject.openProject(nwMinimal)
|
||||
|
||||
theIndex = NWIndex(theProject, dummyGUI)
|
||||
|
||||
# Some items for fail to scan tests
|
||||
dHandle = theProject.newFolder("Folder", nwItemClass.NOVEL, "a508bb932959c")
|
||||
xHandle = theProject.newFile("No Layout", nwItemClass.NOVEL, "a508bb932959c")
|
||||
xItem = theProject.projTree[xHandle]
|
||||
xItem.setLayout(nwItemLayout.NO_LAYOUT)
|
||||
|
||||
# Check invalid data
|
||||
assert not theIndex.scanText(None, "Hello World!")
|
||||
assert not theIndex.scanText(dHandle, "Hello World!")
|
||||
assert not theIndex.scanText(xHandle, "Hello World!")
|
||||
|
||||
xItem.setLayout(nwItemLayout.SCENE)
|
||||
xItem.setParent(None)
|
||||
assert not theIndex.scanText(xHandle, "Hello World!")
|
||||
|
||||
# Create the trash folder
|
||||
tHandle = theProject.trashFolder()
|
||||
assert theProject.projTree[tHandle] is not None
|
||||
xItem.setParent(tHandle)
|
||||
assert not theIndex.scanText(xHandle, "Hello World!")
|
||||
|
||||
# Create the archive root
|
||||
aHandle = theProject.newRoot("Outtakes", nwItemClass.ARCHIVE)
|
||||
assert theProject.projTree[aHandle] is not None
|
||||
xItem.setParent(aHandle)
|
||||
assert not theIndex.scanText(xHandle, "Hello World!")
|
||||
|
||||
# Make some usable items
|
||||
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")
|
||||
|
||||
# Index correct text
|
||||
assert theIndex.scanText(cHandle, (
|
||||
"# Jane Smith\n"
|
||||
"@tag: Jane\n"
|
||||
))
|
||||
assert theIndex.scanText(nHandle, (
|
||||
"# Hello World!\n"
|
||||
"@pov: Jane\n"
|
||||
"@char: Jane\n\n"
|
||||
"% this is a comment\n\n"
|
||||
"This is a story about Jane Smith.\n\n"
|
||||
"Well, not really.\n"
|
||||
))
|
||||
assert str(theIndex._tagIndex) == "{'Jane': [2, '%s', 'CHARACTER', 'T000001']}" % cHandle
|
||||
assert theIndex.getNovelData(nHandle, "T000001")["title"] == "Hello World!"
|
||||
|
||||
# Check that title sections are indexed properly
|
||||
assert theIndex.scanText(nHandle, (
|
||||
"# Title One\n\n"
|
||||
"% synopsis: Synopsis One.\n\n"
|
||||
"Paragraph One.\n\n"
|
||||
"## Title Two\n\n"
|
||||
"% synopsis: Synopsis Two.\n\n"
|
||||
"Paragraph Two.\n\n"
|
||||
"### Title Three\n\n"
|
||||
"% synopsis: Synopsis Three.\n\n"
|
||||
"Paragraph Three.\n\n"
|
||||
"#### Title Four\n\n"
|
||||
"% synopsis: Synopsis Four.\n\n"
|
||||
"Paragraph Four.\n\n"
|
||||
"##### Title Five\n\n" # Not interpreted as a title, the hashes are counted as a word
|
||||
"Paragraph Five.\n\n"
|
||||
))
|
||||
assert theIndex._refIndex[nHandle].get("T000000", None) is not None # Always there
|
||||
assert theIndex._refIndex[nHandle].get("T000001", None) is not None # Heading 1
|
||||
assert theIndex._refIndex[nHandle].get("T000002", None) is None
|
||||
assert theIndex._refIndex[nHandle].get("T000003", None) is None
|
||||
assert theIndex._refIndex[nHandle].get("T000004", None) is None
|
||||
assert theIndex._refIndex[nHandle].get("T000005", None) is None
|
||||
assert theIndex._refIndex[nHandle].get("T000006", None) is None
|
||||
assert theIndex._refIndex[nHandle].get("T000007", None) is not None # Heading 2
|
||||
assert theIndex._refIndex[nHandle].get("T000008", None) is None
|
||||
assert theIndex._refIndex[nHandle].get("T000009", None) is None
|
||||
assert theIndex._refIndex[nHandle].get("T000010", None) is None
|
||||
assert theIndex._refIndex[nHandle].get("T000011", None) is None
|
||||
assert theIndex._refIndex[nHandle].get("T000012", None) is None
|
||||
assert theIndex._refIndex[nHandle].get("T000013", None) is not None # Heading 3
|
||||
assert theIndex._refIndex[nHandle].get("T000014", None) is None
|
||||
assert theIndex._refIndex[nHandle].get("T000015", None) is None
|
||||
assert theIndex._refIndex[nHandle].get("T000016", None) is None
|
||||
assert theIndex._refIndex[nHandle].get("T000017", None) is None
|
||||
assert theIndex._refIndex[nHandle].get("T000018", None) is None
|
||||
assert theIndex._refIndex[nHandle].get("T000019", None) is not None # Heading 4
|
||||
assert theIndex._refIndex[nHandle].get("T000020", None) is None
|
||||
assert theIndex._refIndex[nHandle].get("T000021", None) is None
|
||||
assert theIndex._refIndex[nHandle].get("T000022", None) is None
|
||||
assert theIndex._refIndex[nHandle].get("T000023", None) is None
|
||||
assert theIndex._refIndex[nHandle].get("T000024", None) is None
|
||||
assert theIndex._refIndex[nHandle].get("T000025", None) is None
|
||||
assert theIndex._refIndex[nHandle].get("T000026", None) is None
|
||||
|
||||
assert theIndex._novelIndex[nHandle]["T000001"]["level"] == "H1"
|
||||
assert theIndex._novelIndex[nHandle]["T000007"]["level"] == "H2"
|
||||
assert theIndex._novelIndex[nHandle]["T000013"]["level"] == "H3"
|
||||
assert theIndex._novelIndex[nHandle]["T000019"]["level"] == "H4"
|
||||
|
||||
assert theIndex._novelIndex[nHandle]["T000001"]["title"] == "Title One"
|
||||
assert theIndex._novelIndex[nHandle]["T000007"]["title"] == "Title Two"
|
||||
assert theIndex._novelIndex[nHandle]["T000013"]["title"] == "Title Three"
|
||||
assert theIndex._novelIndex[nHandle]["T000019"]["title"] == "Title Four"
|
||||
|
||||
assert theIndex._novelIndex[nHandle]["T000001"]["layout"] == "SCENE"
|
||||
assert theIndex._novelIndex[nHandle]["T000007"]["layout"] == "SCENE"
|
||||
assert theIndex._novelIndex[nHandle]["T000013"]["layout"] == "SCENE"
|
||||
assert theIndex._novelIndex[nHandle]["T000019"]["layout"] == "SCENE"
|
||||
|
||||
assert theIndex._novelIndex[nHandle]["T000001"]["synopsis"] == "Synopsis One."
|
||||
assert theIndex._novelIndex[nHandle]["T000007"]["synopsis"] == "Synopsis Two."
|
||||
assert theIndex._novelIndex[nHandle]["T000013"]["synopsis"] == "Synopsis Three."
|
||||
assert theIndex._novelIndex[nHandle]["T000019"]["synopsis"] == "Synopsis Four."
|
||||
|
||||
assert theIndex._novelIndex[nHandle]["T000001"]["cCount"] == 23
|
||||
assert theIndex._novelIndex[nHandle]["T000007"]["cCount"] == 23
|
||||
assert theIndex._novelIndex[nHandle]["T000013"]["cCount"] == 27
|
||||
assert theIndex._novelIndex[nHandle]["T000019"]["cCount"] == 56
|
||||
|
||||
assert theIndex._novelIndex[nHandle]["T000001"]["wCount"] == 4
|
||||
assert theIndex._novelIndex[nHandle]["T000007"]["wCount"] == 4
|
||||
assert theIndex._novelIndex[nHandle]["T000013"]["wCount"] == 4
|
||||
assert theIndex._novelIndex[nHandle]["T000019"]["wCount"] == 9
|
||||
|
||||
assert theIndex._novelIndex[nHandle]["T000001"]["pCount"] == 1
|
||||
assert theIndex._novelIndex[nHandle]["T000007"]["pCount"] == 1
|
||||
assert theIndex._novelIndex[nHandle]["T000013"]["pCount"] == 1
|
||||
assert theIndex._novelIndex[nHandle]["T000019"]["pCount"] == 3
|
||||
|
||||
assert theIndex.scanText(cHandle, (
|
||||
"# Title One\n\n"
|
||||
"@tag: One\n\n"
|
||||
"% synopsis: Synopsis One.\n\n"
|
||||
"Paragraph One.\n\n"
|
||||
))
|
||||
assert theIndex._refIndex[cHandle].get("T000000", None) is not None
|
||||
assert theIndex._refIndex[cHandle].get("T000001", None) is not None
|
||||
assert theIndex._refIndex[cHandle].get("T000002", None) is None
|
||||
assert theIndex._refIndex[cHandle].get("T000003", None) is None
|
||||
assert theIndex._refIndex[cHandle].get("T000004", None) is None
|
||||
assert theIndex._refIndex[cHandle].get("T000005", None) is None
|
||||
assert theIndex._refIndex[cHandle].get("T000006", None) is None
|
||||
assert theIndex._refIndex[cHandle].get("T000007", None) is None
|
||||
|
||||
assert theIndex._noteIndex[cHandle]["T000001"]["level"] == "H1"
|
||||
assert theIndex._noteIndex[cHandle]["T000001"]["title"] == "Title One"
|
||||
assert theIndex._noteIndex[cHandle]["T000001"]["layout"] == "NOTE"
|
||||
assert theIndex._noteIndex[cHandle]["T000001"]["synopsis"] == "Synopsis One."
|
||||
assert theIndex._noteIndex[cHandle]["T000001"]["cCount"] == 23
|
||||
assert theIndex._noteIndex[cHandle]["T000001"]["wCount"] == 4
|
||||
assert theIndex._noteIndex[cHandle]["T000001"]["pCount"] == 1
|
||||
|
||||
assert theIndex.scanText(sHandle, (
|
||||
"# Title One\n\n"
|
||||
"@pov: One\n\n" # Valid
|
||||
"@char: Two\n\n" # Invalid tag
|
||||
"@:\n\n" # Invalid line
|
||||
"% synopsis: Synopsis One.\n\n"
|
||||
"Paragraph One.\n\n"
|
||||
))
|
||||
assert theIndex._refIndex[sHandle]["T000001"]["tags"] == (
|
||||
[[3, "@pov", "One"], [5, "@char", "Two"]]
|
||||
)
|
||||
|
||||
# Page wo/Title
|
||||
theProject.projTree[pHandle].itemLayout = nwItemLayout.PAGE
|
||||
assert theIndex.scanText(pHandle, (
|
||||
"This is a page with some text on it.\n\n"
|
||||
))
|
||||
assert theIndex._novelIndex[pHandle]["T000000"]["level"] == "H0"
|
||||
assert theIndex._novelIndex[pHandle]["T000000"]["title"] == "Untitled Page"
|
||||
assert theIndex._novelIndex[pHandle]["T000000"]["layout"] == "PAGE"
|
||||
assert theIndex._novelIndex[pHandle]["T000000"]["synopsis"] == ""
|
||||
assert theIndex._novelIndex[pHandle]["T000000"]["cCount"] == 36
|
||||
assert theIndex._novelIndex[pHandle]["T000000"]["wCount"] == 9
|
||||
assert theIndex._novelIndex[pHandle]["T000000"]["pCount"] == 1
|
||||
assert pHandle not in theIndex._noteIndex
|
||||
|
||||
theProject.projTree[pHandle].itemLayout = nwItemLayout.NOTE
|
||||
assert theIndex.scanText(pHandle, (
|
||||
"This is a page with some text on it.\n\n"
|
||||
))
|
||||
assert theIndex._noteIndex[pHandle]["T000000"]["level"] == "H0"
|
||||
assert theIndex._noteIndex[pHandle]["T000000"]["title"] == "Untitled Page"
|
||||
assert theIndex._noteIndex[pHandle]["T000000"]["layout"] == "NOTE"
|
||||
assert theIndex._noteIndex[pHandle]["T000000"]["synopsis"] == ""
|
||||
assert theIndex._noteIndex[pHandle]["T000000"]["cCount"] == 36
|
||||
assert theIndex._noteIndex[pHandle]["T000000"]["wCount"] == 9
|
||||
assert theIndex._noteIndex[pHandle]["T000000"]["pCount"] == 1
|
||||
assert pHandle not in theIndex._novelIndex
|
||||
|
||||
assert theProject.closeProject()
|
||||
|
||||
# END Test testCoreIndex_ScanText
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreIndex_ExtractData(nwMinimal, dummyGUI):
|
||||
"""Check the index data extraction functions.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject.projTree.setSeed(42)
|
||||
assert theProject.openProject(nwMinimal)
|
||||
|
||||
theIndex = NWIndex(theProject, dummyGUI)
|
||||
nHandle = theProject.newFile("Hello", nwItemClass.NOVEL, "a508bb932959c")
|
||||
cHandle = theProject.newFile("Jane", nwItemClass.CHARACTER, "afb3043c7b2b3")
|
||||
|
||||
assert theIndex.getNovelData("", "") is None
|
||||
assert theIndex.getNovelData("a508bb932959c", "") is None
|
||||
|
||||
assert theIndex.scanText(cHandle, (
|
||||
"# Jane Smith\n"
|
||||
"@tag: Jane\n"
|
||||
))
|
||||
assert theIndex.scanText(nHandle, (
|
||||
"# Hello World!\n"
|
||||
"@pov: Jane\n"
|
||||
"@char: Jane\n\n"
|
||||
"% this is a comment\n\n"
|
||||
"This is a story about Jane Smith.\n\n"
|
||||
"Well, not really.\n"
|
||||
))
|
||||
|
||||
# The novel structure should contain the pointer to the novel file header
|
||||
theKeys = []
|
||||
for aKey, _, _, _ in theIndex.novelStructure():
|
||||
theKeys.append(aKey)
|
||||
|
||||
assert theKeys == ["%s:T000001" % nHandle]
|
||||
|
||||
# Check that excluded files can be skipped
|
||||
theProject.projTree[nHandle].setExported(False)
|
||||
|
||||
theKeys = []
|
||||
for aKey, _, _, _ in theIndex.novelStructure(skipExcluded=False):
|
||||
theKeys.append(aKey)
|
||||
|
||||
assert theKeys == ["%s:T000001" % nHandle]
|
||||
|
||||
theKeys = []
|
||||
for aKey, _, _, _ in theIndex.novelStructure(skipExcluded=True):
|
||||
theKeys.append(aKey)
|
||||
|
||||
assert theKeys == []
|
||||
|
||||
theKeys = []
|
||||
for aKey, _, _, _ in theIndex.novelStructure():
|
||||
theKeys.append(aKey)
|
||||
|
||||
assert theKeys == []
|
||||
|
||||
# The novel file should have the correct counts
|
||||
cC, wC, pC = theIndex.getCounts(nHandle)
|
||||
assert cC == 62 # Characters in text and title only
|
||||
assert wC == 12 # Words in text and title only
|
||||
assert pC == 2 # Paragraphs in text only
|
||||
|
||||
##
|
||||
# getReferences
|
||||
##
|
||||
|
||||
# Look up an ivalid handle
|
||||
theRefs = theIndex.getReferences("Not a handle")
|
||||
assert theRefs["@pov"] == []
|
||||
assert theRefs["@char"] == []
|
||||
|
||||
# The novel file should now refer to Jane as @pov and @char
|
||||
theRefs = theIndex.getReferences(nHandle)
|
||||
assert theRefs["@pov"] == ["Jane"]
|
||||
assert theRefs["@char"] == ["Jane"]
|
||||
|
||||
##
|
||||
# getBackReferenceList
|
||||
##
|
||||
|
||||
# None handle should return an empty dict
|
||||
assert theIndex.getBackReferenceList(None) == {}
|
||||
|
||||
# The character file should have a record of the reference from the novel file
|
||||
theRefs = theIndex.getBackReferenceList(cHandle)
|
||||
assert theRefs == {nHandle: "T000001"}
|
||||
|
||||
##
|
||||
# getTagSource
|
||||
##
|
||||
|
||||
assert theIndex.getTagSource("Jane") == (cHandle, 2, "T000001")
|
||||
assert theIndex.getTagSource("John") == (None, 0, "T000000")
|
||||
|
||||
##
|
||||
# getCounts for whole text and sections
|
||||
##
|
||||
|
||||
# Get section counts for a novel file
|
||||
assert theIndex.scanText(nHandle, (
|
||||
"# Hello World!\n"
|
||||
"@pov: Jane\n"
|
||||
"@char: Jane\n\n"
|
||||
"% this is a comment\n\n"
|
||||
"This is a story about Jane Smith.\n\n"
|
||||
"Well, not really.\n\n"
|
||||
"# Hello World!\n"
|
||||
"@pov: Jane\n"
|
||||
"@char: Jane\n\n"
|
||||
"% this is a comment\n\n"
|
||||
"This is a story about Jane Smith.\n\n"
|
||||
"Well, not really.\n"
|
||||
))
|
||||
# Whole document
|
||||
cC, wC, pC = theIndex.getCounts(nHandle)
|
||||
assert cC == 124
|
||||
assert wC == 24
|
||||
assert pC == 4
|
||||
|
||||
# First part
|
||||
cC, wC, pC = theIndex.getCounts(nHandle, "T000001")
|
||||
assert cC == 62
|
||||
assert wC == 12
|
||||
assert pC == 2
|
||||
|
||||
# First part
|
||||
cC, wC, pC = theIndex.getCounts(nHandle, "T000011")
|
||||
assert cC == 62
|
||||
assert wC == 12
|
||||
assert pC == 2
|
||||
|
||||
# Get section counts for a note file
|
||||
assert theIndex.scanText(cHandle, (
|
||||
"# Hello World!\n"
|
||||
"@pov: Jane\n"
|
||||
"@char: Jane\n\n"
|
||||
"% this is a comment\n\n"
|
||||
"This is a story about Jane Smith.\n\n"
|
||||
"Well, not really.\n\n"
|
||||
"# Hello World!\n"
|
||||
"@pov: Jane\n"
|
||||
"@char: Jane\n\n"
|
||||
"% this is a comment\n\n"
|
||||
"This is a story about Jane Smith.\n\n"
|
||||
"Well, not really.\n"
|
||||
))
|
||||
# Whole document
|
||||
cC, wC, pC = theIndex.getCounts(cHandle)
|
||||
assert cC == 124
|
||||
assert wC == 24
|
||||
assert pC == 4
|
||||
|
||||
# First part
|
||||
cC, wC, pC = theIndex.getCounts(cHandle, "T000001")
|
||||
assert cC == 62
|
||||
assert wC == 12
|
||||
assert pC == 2
|
||||
|
||||
# First part
|
||||
cC, wC, pC = theIndex.getCounts(cHandle, "T000011")
|
||||
assert cC == 62
|
||||
assert wC == 12
|
||||
assert pC == 2
|
||||
|
||||
##
|
||||
# Novel Stats
|
||||
##
|
||||
|
||||
hHandle = theProject.newFile("Chapter", nwItemClass.NOVEL, "a508bb932959c")
|
||||
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
|
||||
|
||||
assert theIndex.scanText(hHandle, "## Chapter One\n\n")
|
||||
assert theIndex.scanText(sHandle, "### Scene One\n\n")
|
||||
assert theIndex.scanText(tHandle, "### Scene Two\n\n")
|
||||
|
||||
assert theIndex._listNovelHandles(False) == [nHandle, hHandle, sHandle, tHandle]
|
||||
assert theIndex._listNovelHandles(True) == [hHandle, sHandle, tHandle]
|
||||
|
||||
# Add a fake handle to the tree and check that it's ignored
|
||||
theProject.projTree._treeOrder.append("0000000000000")
|
||||
assert theIndex._listNovelHandles(False) == [nHandle, hHandle, sHandle, tHandle]
|
||||
theProject.projTree._treeOrder.remove("0000000000000")
|
||||
|
||||
# Extract stats
|
||||
assert theIndex.getNovelWordCount(False) == 30
|
||||
assert theIndex.getNovelWordCount(True) == 6
|
||||
assert theIndex.getNovelTitleCounts(False) == [0, 2, 1, 2, 0]
|
||||
assert theIndex.getNovelTitleCounts(True) == [0, 0, 1, 2, 0]
|
||||
|
||||
# Table of Contents
|
||||
assert theIndex.getTableOfContents(0, True) == []
|
||||
assert theIndex.getTableOfContents(1, True) == []
|
||||
assert theIndex.getTableOfContents(2, True) == [
|
||||
("%s:T000001" % hHandle, "H2", "Chapter One", 6),
|
||||
]
|
||||
assert theIndex.getTableOfContents(3, True) == [
|
||||
("%s:T000001" % hHandle, "H2", "Chapter One", 2),
|
||||
("%s:T000001" % sHandle, "H3", "Scene One", 2),
|
||||
("%s:T000001" % tHandle, "H3", "Scene Two", 2),
|
||||
]
|
||||
|
||||
assert theIndex.getTableOfContents(0, False) == []
|
||||
assert theIndex.getTableOfContents(1, False) == [
|
||||
("%s:T000001" % nHandle, "H1", "Hello World!", 12),
|
||||
("%s:T000011" % nHandle, "H1", "Hello World!", 18),
|
||||
]
|
||||
|
||||
assert theProject.closeProject()
|
||||
|
||||
# END Test testCoreIndex_ExtractData
|
||||
@@ -0,0 +1,401 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
novelWriter – NWItem Class Tester
|
||||
=================================
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, 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 pytest
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from nw.core import NWProject
|
||||
from nw.core.item import NWItem
|
||||
from nw.constants import nwItemClass, nwItemType, nwItemLayout
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreItem_Setters(dummyGUI):
|
||||
"""Test all the simple setters for the NWItem class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theItem = NWItem(theProject)
|
||||
|
||||
# Name
|
||||
theItem.setName("A Name")
|
||||
assert theItem.itemName == "A Name"
|
||||
theItem.setName("\t A Name ")
|
||||
assert theItem.itemName == "A Name"
|
||||
theItem.setName(123)
|
||||
assert theItem.itemName == ""
|
||||
|
||||
# Handle
|
||||
theItem.setHandle(123)
|
||||
assert theItem.itemHandle is None
|
||||
theItem.setHandle("0123456789abcdef")
|
||||
assert theItem.itemHandle is None
|
||||
theItem.setHandle("0123456789abg")
|
||||
assert theItem.itemHandle is None
|
||||
theItem.setHandle("0123456789abc")
|
||||
assert theItem.itemHandle == "0123456789abc"
|
||||
|
||||
# Parent
|
||||
theItem.setParent(None)
|
||||
assert theItem.itemParent is None
|
||||
theItem.setParent(123)
|
||||
assert theItem.itemParent is None
|
||||
theItem.setParent("0123456789abcdef")
|
||||
assert theItem.itemParent is None
|
||||
theItem.setParent("0123456789abg")
|
||||
assert theItem.itemParent is None
|
||||
theItem.setParent("0123456789abc")
|
||||
assert theItem.itemParent == "0123456789abc"
|
||||
|
||||
# Order
|
||||
theItem.setOrder(None)
|
||||
assert theItem.itemOrder == 0
|
||||
theItem.setOrder("1")
|
||||
assert theItem.itemOrder == 1
|
||||
theItem.setOrder(1)
|
||||
assert theItem.itemOrder == 1
|
||||
|
||||
# Status
|
||||
theItem.setStatus("Nonsense")
|
||||
assert theItem.itemStatus == "New"
|
||||
theItem.setStatus("New")
|
||||
assert theItem.itemStatus == "New"
|
||||
theItem.setStatus("Minor")
|
||||
assert theItem.itemStatus == "Minor"
|
||||
theItem.setStatus("Major")
|
||||
assert theItem.itemStatus == "Major"
|
||||
theItem.setStatus("Main")
|
||||
assert theItem.itemStatus == "Main"
|
||||
|
||||
# Importance
|
||||
theItem.itemClass = nwItemClass.NOVEL
|
||||
theItem.setStatus("Nonsense")
|
||||
assert theItem.itemStatus == "New"
|
||||
theItem.setStatus("New")
|
||||
assert theItem.itemStatus == "New"
|
||||
theItem.setStatus("Note")
|
||||
assert theItem.itemStatus == "Note"
|
||||
theItem.setStatus("Draft")
|
||||
assert theItem.itemStatus == "Draft"
|
||||
theItem.setStatus("Finished")
|
||||
assert theItem.itemStatus == "Finished"
|
||||
|
||||
# Expanded
|
||||
theItem.setExpanded(8)
|
||||
assert not theItem.isExpanded
|
||||
theItem.setExpanded(None)
|
||||
assert not theItem.isExpanded
|
||||
theItem.setExpanded("None")
|
||||
assert not theItem.isExpanded
|
||||
theItem.setExpanded("What?")
|
||||
assert not theItem.isExpanded
|
||||
theItem.setExpanded("True")
|
||||
assert theItem.isExpanded
|
||||
theItem.setExpanded(True)
|
||||
assert theItem.isExpanded
|
||||
|
||||
# Exported
|
||||
theItem.setExported(8)
|
||||
assert not theItem.isExported
|
||||
theItem.setExported(None)
|
||||
assert not theItem.isExported
|
||||
theItem.setExported("None")
|
||||
assert not theItem.isExported
|
||||
theItem.setExported("What?")
|
||||
assert not theItem.isExported
|
||||
theItem.setExported("True")
|
||||
assert theItem.isExported
|
||||
theItem.setExported(True)
|
||||
assert theItem.isExported
|
||||
|
||||
# CharCount
|
||||
theItem.setCharCount(None)
|
||||
assert theItem.charCount == 0
|
||||
theItem.setCharCount("1")
|
||||
assert theItem.charCount == 1
|
||||
theItem.setCharCount(1)
|
||||
assert theItem.charCount == 1
|
||||
|
||||
# WordCount
|
||||
theItem.setWordCount(None)
|
||||
assert theItem.wordCount == 0
|
||||
theItem.setWordCount("1")
|
||||
assert theItem.wordCount == 1
|
||||
theItem.setWordCount(1)
|
||||
assert theItem.wordCount == 1
|
||||
|
||||
# ParaCount
|
||||
theItem.setParaCount(None)
|
||||
assert theItem.paraCount == 0
|
||||
theItem.setParaCount("1")
|
||||
assert theItem.paraCount == 1
|
||||
theItem.setParaCount(1)
|
||||
assert theItem.paraCount == 1
|
||||
|
||||
# CursorPos
|
||||
theItem.setCursorPos(None)
|
||||
assert theItem.cursorPos == 0
|
||||
theItem.setCursorPos("1")
|
||||
assert theItem.cursorPos == 1
|
||||
theItem.setCursorPos(1)
|
||||
assert theItem.cursorPos == 1
|
||||
|
||||
# Initial Count
|
||||
theItem.setWordCount(234)
|
||||
theItem.saveInitialCount()
|
||||
assert theItem.initCount == 234
|
||||
|
||||
# END Test testCoreItem_Setters
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreItem_TypeSetter(dummyGUI):
|
||||
"""Test the setter for all the nwItemType values for the NWItem
|
||||
class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theItem = NWItem(theProject)
|
||||
|
||||
# Type
|
||||
theItem.setType(None)
|
||||
assert theItem.itemType == nwItemType.NO_TYPE
|
||||
theItem.setType("NONSENSE")
|
||||
assert theItem.itemType == nwItemType.NO_TYPE
|
||||
theItem.setType("NO_TYPE")
|
||||
assert theItem.itemType == nwItemType.NO_TYPE
|
||||
theItem.setType("ROOT")
|
||||
assert theItem.itemType == nwItemType.ROOT
|
||||
theItem.setType("FOLDER")
|
||||
assert theItem.itemType == nwItemType.FOLDER
|
||||
theItem.setType("FILE")
|
||||
assert theItem.itemType == nwItemType.FILE
|
||||
theItem.setType("TRASH")
|
||||
assert theItem.itemType == nwItemType.TRASH
|
||||
theItem.setType(nwItemType.ROOT)
|
||||
assert theItem.itemType == nwItemType.ROOT
|
||||
|
||||
# END Test testCoreItem_TypeSetter
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreItem_ClassSetter(dummyGUI):
|
||||
"""Test the setter for all the nwItemClass values for the NWItem
|
||||
class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theItem = NWItem(theProject)
|
||||
|
||||
# Class
|
||||
theItem.setClass(None)
|
||||
assert theItem.itemClass == nwItemClass.NO_CLASS
|
||||
theItem.setClass("NONSENSE")
|
||||
assert theItem.itemClass == nwItemClass.NO_CLASS
|
||||
theItem.setClass("NO_CLASS")
|
||||
assert theItem.itemClass == nwItemClass.NO_CLASS
|
||||
theItem.setClass("NOVEL")
|
||||
assert theItem.itemClass == nwItemClass.NOVEL
|
||||
theItem.setClass("PLOT")
|
||||
assert theItem.itemClass == nwItemClass.PLOT
|
||||
theItem.setClass("CHARACTER")
|
||||
assert theItem.itemClass == nwItemClass.CHARACTER
|
||||
theItem.setClass("WORLD")
|
||||
assert theItem.itemClass == nwItemClass.WORLD
|
||||
theItem.setClass("TIMELINE")
|
||||
assert theItem.itemClass == nwItemClass.TIMELINE
|
||||
theItem.setClass("OBJECT")
|
||||
assert theItem.itemClass == nwItemClass.OBJECT
|
||||
theItem.setClass("ENTITY")
|
||||
assert theItem.itemClass == nwItemClass.ENTITY
|
||||
theItem.setClass("CUSTOM")
|
||||
assert theItem.itemClass == nwItemClass.CUSTOM
|
||||
theItem.setClass("ARCHIVE")
|
||||
assert theItem.itemClass == nwItemClass.ARCHIVE
|
||||
theItem.setClass("TRASH")
|
||||
assert theItem.itemClass == nwItemClass.TRASH
|
||||
theItem.setClass(nwItemClass.NOVEL)
|
||||
assert theItem.itemClass == nwItemClass.NOVEL
|
||||
|
||||
# END Test testCoreItem_ClassSetter
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreItem_LayoutSetter(dummyGUI):
|
||||
"""Test the setter for all the nwItemLayout values for the NWItem
|
||||
class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theItem = NWItem(theProject)
|
||||
|
||||
# Layout
|
||||
theItem.setLayout(None)
|
||||
assert theItem.itemLayout == nwItemLayout.NO_LAYOUT
|
||||
theItem.setLayout("NONSENSE")
|
||||
assert theItem.itemLayout == nwItemLayout.NO_LAYOUT
|
||||
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("NOTE")
|
||||
assert theItem.itemLayout == nwItemLayout.NOTE
|
||||
theItem.setLayout(nwItemLayout.NOTE)
|
||||
assert theItem.itemLayout == nwItemLayout.NOTE
|
||||
|
||||
# END Test testCoreItem_LayoutSetter
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreItem_XMLPackUnpack(dummyGUI):
|
||||
"""Test packing and unpacking XML objects for the NWItem class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
nwXML = etree.Element("novelWriterXML")
|
||||
|
||||
# File
|
||||
# ====
|
||||
|
||||
theItem = NWItem(theProject)
|
||||
theItem.setHandle("0123456789abc")
|
||||
theItem.setParent("0123456789abc")
|
||||
theItem.setOrder(1)
|
||||
theItem.setName("A Name")
|
||||
theItem.setClass("NOVEL")
|
||||
theItem.setType("FILE")
|
||||
theItem.setStatus("Main")
|
||||
theItem.setLayout("NOTE")
|
||||
theItem.setExported(False)
|
||||
theItem.setParaCount(3)
|
||||
theItem.setWordCount(5)
|
||||
theItem.setCharCount(7)
|
||||
theItem.setCursorPos(11)
|
||||
|
||||
# Pack
|
||||
xContent = etree.SubElement(nwXML, "content")
|
||||
theItem.packXML(xContent)
|
||||
assert etree.tostring(xContent, pretty_print=False, encoding="utf-8") == (
|
||||
b"<content>"
|
||||
b"<item handle=\"0123456789abc\" order=\"1\" parent=\"0123456789abc\">"
|
||||
b"<name>A Name</name><type>FILE</type><class>NOVEL</class><status>New</status>"
|
||||
b"<exported>False</exported><layout>NOTE</layout><charCount>7</charCount>"
|
||||
b"<wordCount>5</wordCount><paraCount>3</paraCount><cursorPos>11</cursorPos></item>"
|
||||
b"</content>"
|
||||
)
|
||||
|
||||
# Unpack
|
||||
theItem = NWItem(theProject)
|
||||
assert theItem.unpackXML(xContent[0])
|
||||
assert theItem.itemHandle == "0123456789abc"
|
||||
assert theItem.itemParent == "0123456789abc"
|
||||
assert theItem.itemOrder == 1
|
||||
assert theItem.isExported is False
|
||||
assert theItem.paraCount == 3
|
||||
assert theItem.wordCount == 5
|
||||
assert theItem.charCount == 7
|
||||
assert theItem.cursorPos == 11
|
||||
assert theItem.itemClass == nwItemClass.NOVEL
|
||||
assert theItem.itemType == nwItemType.FILE
|
||||
assert theItem.itemLayout == nwItemLayout.NOTE
|
||||
|
||||
# Folder
|
||||
# ======
|
||||
|
||||
theItem = NWItem(theProject)
|
||||
theItem.setHandle("0123456789abc")
|
||||
theItem.setParent("0123456789abc")
|
||||
theItem.setOrder(1)
|
||||
theItem.setName("A Name")
|
||||
theItem.setClass("NOVEL")
|
||||
theItem.setType("FOLDER")
|
||||
theItem.setStatus("Main")
|
||||
theItem.setLayout("NOTE")
|
||||
theItem.setExpanded(True)
|
||||
theItem.setExported(False)
|
||||
theItem.setParaCount(3)
|
||||
theItem.setWordCount(5)
|
||||
theItem.setCharCount(7)
|
||||
theItem.setCursorPos(11)
|
||||
|
||||
# Pack
|
||||
xContent = etree.SubElement(nwXML, "content")
|
||||
theItem.packXML(xContent)
|
||||
assert etree.tostring(xContent, pretty_print=False, encoding="utf-8") == (
|
||||
b"<content>"
|
||||
b"<item handle=\"0123456789abc\" order=\"1\" parent=\"0123456789abc\">"
|
||||
b"<name>A Name</name><type>FOLDER</type><class>NOVEL</class><status>New</status>"
|
||||
b"<expanded>True</expanded></item>"
|
||||
b"</content>"
|
||||
)
|
||||
|
||||
# Unpack
|
||||
theItem = NWItem(theProject)
|
||||
assert theItem.unpackXML(xContent[0])
|
||||
assert theItem.itemHandle == "0123456789abc"
|
||||
assert theItem.itemParent == "0123456789abc"
|
||||
assert theItem.itemOrder == 1
|
||||
assert theItem.isExpanded is True
|
||||
assert theItem.isExported is True
|
||||
assert theItem.paraCount == 0
|
||||
assert theItem.wordCount == 0
|
||||
assert theItem.charCount == 0
|
||||
assert theItem.cursorPos == 0
|
||||
assert theItem.itemClass == nwItemClass.NOVEL
|
||||
assert theItem.itemType == nwItemType.FOLDER
|
||||
assert theItem.itemLayout == nwItemLayout.NO_LAYOUT
|
||||
|
||||
# Errors
|
||||
|
||||
## Not an Item
|
||||
xDummy = etree.SubElement(nwXML, "stuff")
|
||||
assert not theItem.unpackXML(xDummy)
|
||||
|
||||
## Item without Handle
|
||||
xDummy = etree.SubElement(nwXML, "item", attrib={"stuff": "nah"})
|
||||
assert not theItem.unpackXML(xDummy)
|
||||
|
||||
## Item with Invalid SubElement
|
||||
xDummy = etree.SubElement(nwXML, "item", attrib={"handle": "0123456789abc"})
|
||||
xParam = etree.SubElement(xDummy, "invalid")
|
||||
xParam.text = "stuff"
|
||||
assert not theItem.unpackXML(xDummy)
|
||||
|
||||
# Pack Valid Item
|
||||
xDummy = etree.SubElement(nwXML, "group")
|
||||
theItem._subPack(xDummy, "subGroup", {"one": "two"}, "value", False)
|
||||
assert etree.tostring(xDummy, pretty_print=False, encoding="utf-8") == (
|
||||
b"<group><subGroup one=\"two\">value</subGroup></group>"
|
||||
)
|
||||
|
||||
# Pack Not Allowed None
|
||||
xDummy = etree.SubElement(nwXML, "group")
|
||||
assert theItem._subPack(xDummy, "subGroup", {}, None, False) is None
|
||||
assert theItem._subPack(xDummy, "subGroup", {}, "None", False) is None
|
||||
assert etree.tostring(xDummy, pretty_print=False, encoding="utf-8") == (
|
||||
b"<group/>"
|
||||
)
|
||||
|
||||
# END Test testCoreItem_XMLPackUnpack
|
||||
@@ -0,0 +1,151 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
novelWriter – OptionState Class Tester
|
||||
======================================
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, 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 json
|
||||
import pytest
|
||||
|
||||
from dummy import causeOSError
|
||||
|
||||
from nw.core import NWProject
|
||||
from nw.core.options import OptionState
|
||||
from nw.constants import nwFiles
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreOptions_LoadSave(monkeypatch, dummyGUI, tmpDir):
|
||||
"""Test loading and saving from the OptionState class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theOpts = OptionState(theProject)
|
||||
|
||||
# Write a test file
|
||||
optFile = os.path.join(tmpDir, nwFiles.OPTS_FILE)
|
||||
with open(optFile, mode="w+", encoding="utf8") as outFile:
|
||||
json.dump({
|
||||
"GuiBuildNovel": {
|
||||
"winWidth": 1000,
|
||||
"winHeight": 700,
|
||||
"addNovel": True,
|
||||
"addNotes": False,
|
||||
"textFont": "Cantarell",
|
||||
"dummyItem": None,
|
||||
},
|
||||
"DummyGroup": {
|
||||
"dummyItem": None,
|
||||
},
|
||||
}, outFile)
|
||||
|
||||
# Load and save with no path set
|
||||
theProject.projMeta = None
|
||||
assert not theOpts.loadSettings()
|
||||
assert not theOpts.saveSettings()
|
||||
|
||||
# Set path
|
||||
theProject.projMeta = tmpDir
|
||||
assert theProject.projMeta == tmpDir
|
||||
|
||||
# Cause open() to fail
|
||||
monkeypatch.setattr("builtins.open", causeOSError)
|
||||
assert not theOpts.loadSettings()
|
||||
assert not theOpts.saveSettings()
|
||||
monkeypatch.undo()
|
||||
|
||||
# Load proper
|
||||
assert theOpts.loadSettings()
|
||||
|
||||
# Check that unwanted items have been removed
|
||||
assert theOpts.theState == {
|
||||
"GuiBuildNovel": {
|
||||
"winWidth": 1000,
|
||||
"winHeight": 700,
|
||||
"addNovel": True,
|
||||
"addNotes": False,
|
||||
"textFont": "Cantarell",
|
||||
},
|
||||
}
|
||||
|
||||
# Save proper
|
||||
assert theOpts.saveSettings()
|
||||
|
||||
# Load again to check we get the values back
|
||||
assert theOpts.loadSettings()
|
||||
assert theOpts.theState == {
|
||||
"GuiBuildNovel": {
|
||||
"winWidth": 1000,
|
||||
"winHeight": 700,
|
||||
"addNovel": True,
|
||||
"addNotes": False,
|
||||
"textFont": "Cantarell",
|
||||
},
|
||||
}
|
||||
|
||||
# END Test testCoreOptions_LoadSave
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreOptions_SetGet(monkeypatch, dummyGUI, tmpDir):
|
||||
"""Test setting and getting values from the OptionState class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theOpts = OptionState(theProject)
|
||||
|
||||
# Set invalid values
|
||||
assert not theOpts.setValue("DummyGroup", "dummyItem", None)
|
||||
assert not theOpts.setValue("GuiBuildNovel", "dummyItem", None)
|
||||
|
||||
# Set valid value
|
||||
assert theOpts.setValue("GuiBuildNovel", "winWidth", 100)
|
||||
|
||||
# Set some values of different types
|
||||
assert theOpts.setValue("GuiBuildNovel", "winWidth", 100)
|
||||
assert theOpts.setValue("GuiBuildNovel", "winHeight", 12.34)
|
||||
assert theOpts.setValue("GuiBuildNovel", "addNovel", True)
|
||||
assert theOpts.setValue("GuiBuildNovel", "textFont", "Cantarell")
|
||||
|
||||
# Generic get, doesn't check type
|
||||
assert theOpts.getValue("GuiBuildNovel", "winWidth", None) == 100
|
||||
assert theOpts.getValue("GuiBuildNovel", "winHeight", None) == 12.34
|
||||
assert theOpts.getValue("GuiBuildNovel", "addNovel", None) is True
|
||||
assert theOpts.getValue("GuiBuildNovel", "textFont", None) == "Cantarell"
|
||||
assert theOpts.getValue("GuiBuildNovel", "dummyItem", None) is None
|
||||
|
||||
# Get type-specific
|
||||
assert theOpts.getString("GuiBuildNovel", "winWidth", None) == "100"
|
||||
assert theOpts.getString("GuiBuildNovel", "dummyItem", None) is None
|
||||
assert theOpts.getInt("GuiBuildNovel", "winWidth", None) == 100
|
||||
assert theOpts.getInt("GuiBuildNovel", "textFont", None) is None
|
||||
assert theOpts.getInt("GuiBuildNovel", "dummyItem", None) is None
|
||||
assert theOpts.getFloat("GuiBuildNovel", "winWidth", None) == 100.0
|
||||
assert theOpts.getFloat("GuiBuildNovel", "textFont", None) is None
|
||||
assert theOpts.getFloat("GuiBuildNovel", "dummyItem", None) is None
|
||||
assert theOpts.getBool("GuiBuildNovel", "addNovel", None) is True
|
||||
assert theOpts.getBool("GuiBuildNovel", "dummyItem", None) is None
|
||||
|
||||
# Check integer validators
|
||||
assert theOpts.validIntRange(5, 0, 9, 3) == 5
|
||||
assert theOpts.validIntRange(5, 0, 4, 3) == 3
|
||||
assert theOpts.validIntRange(5, 0, 5, 3) == 5
|
||||
assert theOpts.validIntRange(0, 0, 5, 3) == 0
|
||||
|
||||
assert theOpts.validIntTuple(0, (0, 1, 2), 3) == 0
|
||||
assert theOpts.validIntTuple(5, (0, 1, 2), 3) == 3
|
||||
|
||||
# END Test testCoreOptions_SetGet
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,188 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
novelWriter – Spell Check Class Tester
|
||||
======================================
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, 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 sys
|
||||
import pytest
|
||||
|
||||
from dummy import causeOSError
|
||||
from tools import readFile, writeFile
|
||||
|
||||
from nw.core.spellcheck import NWSpellCheck, NWSpellEnchant, NWSpellSimple
|
||||
from nw.constants import nwConst
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreSpell_Super(monkeypatch, tmpDir, tmpConf):
|
||||
"""Test the spell checker super class
|
||||
"""
|
||||
wList = os.path.join(tmpDir, "wordlist.txt")
|
||||
writeFile(wList, "a_word\nb_word\nc_word\n")
|
||||
|
||||
spChk = NWSpellCheck()
|
||||
spChk.mainConf = tmpConf
|
||||
|
||||
# Check that dummy functions return results that reflects that spell
|
||||
# checking is effectively disabled
|
||||
assert spChk.setLanguage("", "") is None
|
||||
assert spChk.checkWord("")
|
||||
assert spChk.suggestWords("") == []
|
||||
assert spChk.listDictionaries() == []
|
||||
assert spChk.describeDict() == ("", "")
|
||||
|
||||
# Check language info
|
||||
assert NWSpellCheck.expandLanguage("en") == "English"
|
||||
assert NWSpellCheck.expandLanguage("en_GB") == "English (GB)"
|
||||
|
||||
# Add a word to the user's dictionary
|
||||
assert spChk._readProjectDictionary("dummy") is False
|
||||
monkeypatch.setattr("builtins.open", causeOSError)
|
||||
assert spChk._readProjectDictionary(wList) is False
|
||||
monkeypatch.undo()
|
||||
assert spChk._readProjectDictionary(wList) is True
|
||||
assert spChk.projectDict == wList
|
||||
|
||||
# Cannot write to file
|
||||
monkeypatch.setattr("builtins.open", causeOSError)
|
||||
assert spChk.addWord("d_word") is False
|
||||
monkeypatch.undo()
|
||||
assert readFile(wList) == "a_word\nb_word\nc_word\n"
|
||||
|
||||
# First time, OK
|
||||
assert spChk.addWord("d_word") is True
|
||||
assert readFile(wList) == "a_word\nb_word\nc_word\nd_word\n"
|
||||
|
||||
# But not added twice
|
||||
assert spChk.addWord("d_word") is False
|
||||
assert readFile(wList) == "a_word\nb_word\nc_word\nd_word\n"
|
||||
|
||||
# END Test testCoreSpell_Super
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreSpell_Enchant(monkeypatch, tmpDir, tmpConf):
|
||||
"""Test the pyenchant spell checker
|
||||
"""
|
||||
wList = os.path.join(tmpDir, "wordlist.txt")
|
||||
writeFile(wList, "a_word\nb_word\nc_word\n")
|
||||
|
||||
# Block the enchant package (and trigger the dummy class)
|
||||
monkeypatch.setitem(sys.modules, "enchant", None)
|
||||
spChk = NWSpellEnchant()
|
||||
|
||||
spChk.setLanguage("en", wList)
|
||||
assert spChk.setLanguage("", "") is None
|
||||
assert spChk.checkWord("")
|
||||
assert spChk.suggestWords("") == []
|
||||
assert spChk.listDictionaries() == []
|
||||
assert spChk.describeDict() == ("", "")
|
||||
|
||||
monkeypatch.undo()
|
||||
|
||||
# Load the proper enchant package
|
||||
spChk = NWSpellEnchant()
|
||||
spChk.mainConf = tmpConf
|
||||
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")
|
||||
|
||||
spChk.addWord("d_word")
|
||||
assert spChk.checkWord("d_word")
|
||||
|
||||
wSuggest = spChk.suggestWords("wrod")
|
||||
assert len(wSuggest) > 0
|
||||
assert "word" in wSuggest
|
||||
|
||||
dList = spChk.listDictionaries()
|
||||
assert len(dList) > 0
|
||||
|
||||
aTag, aName = spChk.describeDict()
|
||||
assert aTag == "en"
|
||||
assert aName != ""
|
||||
|
||||
# END Test testCoreSpell_Enchant
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreSpell_Simple(monkeypatch, tmpDir, tmpConf):
|
||||
"""Test the fallback simple spell checker
|
||||
"""
|
||||
wList = os.path.join(tmpDir, "wordlist.txt")
|
||||
wDict = os.path.join(tmpDir, "en.dict")
|
||||
writeFile(wList, "a_word\nb_word\nc_word\n")
|
||||
writeFile(wDict, "# Comment\ne_word\nf_word\ng_word\n")
|
||||
|
||||
spChk = NWSpellSimple()
|
||||
spChk.mainConf = tmpConf
|
||||
spChk.mainConf.dictPath = tmpDir
|
||||
|
||||
# Load dictionary, but fail
|
||||
monkeypatch.setattr("builtins.open", causeOSError)
|
||||
spChk.setLanguage("en", wList)
|
||||
assert spChk.spellLanguage is None
|
||||
assert spChk.WORDS == spChk.projDict
|
||||
monkeypatch.undo()
|
||||
|
||||
# Load dictionary properly
|
||||
spChk.setLanguage("en", wList)
|
||||
assert spChk.projDict == ["a_word", "b_word", "c_word"]
|
||||
assert spChk.WORDS == ["e_word", "f_word", "g_word", "a_word", "b_word", "c_word"]
|
||||
|
||||
# Check words
|
||||
assert spChk.checkWord("a_word")
|
||||
assert spChk.checkWord("b_word")
|
||||
assert spChk.checkWord("c_word")
|
||||
assert not spChk.checkWord("d_word")
|
||||
assert spChk.checkWord("e_word")
|
||||
assert spChk.checkWord("f_word")
|
||||
assert spChk.checkWord("g_word")
|
||||
|
||||
# Add word
|
||||
spChk.addWord("d_word")
|
||||
assert spChk.checkWord("d_word")
|
||||
|
||||
# Check spelling
|
||||
assert spChk.suggestWords(" \t") == []
|
||||
|
||||
wSuggest = spChk.suggestWords("d_wrod")
|
||||
assert len(wSuggest) > 0
|
||||
assert "d_word" in wSuggest
|
||||
|
||||
# Break the matching
|
||||
monkeypatch.setattr("difflib.get_close_matches", lambda *args, **kwargs: [""])
|
||||
assert spChk.suggestWords("word") == []
|
||||
monkeypatch.undo()
|
||||
|
||||
# Capitalisation
|
||||
wSuggest = spChk.suggestWords("D_wrod")
|
||||
assert len(wSuggest) > 0
|
||||
assert "D_word" in wSuggest
|
||||
|
||||
# List dictionaries
|
||||
assert spChk.listDictionaries() == [("en", "English [%s]" % nwConst.SP_INTERNAL)]
|
||||
|
||||
# Description
|
||||
aTag, aName = spChk.describeDict()
|
||||
assert aTag == "en"
|
||||
assert aName == nwConst.SP_INTERNAL
|
||||
|
||||
# END Test testCoreSpell_Simple
|
||||
@@ -0,0 +1,144 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
novelWriter – Status Class Tester
|
||||
=================================
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, 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 pytest
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from nw.core.status import NWStatus
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreStatus_Entries():
|
||||
"""Test all the simple setters for the NWItem class.
|
||||
"""
|
||||
theStatus = NWStatus()
|
||||
|
||||
# Add entries
|
||||
theStatus.addEntry("New", (100, 100, 100))
|
||||
theStatus.addEntry("Minor", (200, 50, 0))
|
||||
theStatus.addEntry("Major", (200, 150, 0))
|
||||
theStatus.addEntry("Main", (50, 200, 0))
|
||||
|
||||
assert theStatus._theLabels == ["New", "Minor", "Major", "Main"]
|
||||
assert theStatus._theColours == [(100, 100, 100), (200, 50, 0), (200, 150, 0), (50, 200, 0)]
|
||||
assert theStatus._theCounts == [0, 0, 0, 0]
|
||||
assert theStatus._theMap["New"] == 0
|
||||
assert theStatus._theMap["Minor"] == 1
|
||||
assert theStatus._theMap["Major"] == 2
|
||||
assert theStatus._theMap["Main"] == 3
|
||||
assert theStatus._theLength == 4
|
||||
|
||||
# Lookups
|
||||
assert theStatus.lookupEntry(None) is None
|
||||
assert theStatus.lookupEntry("dummy") is None
|
||||
assert theStatus.lookupEntry("Main") == 3
|
||||
|
||||
# Checks
|
||||
assert theStatus.checkEntry(123) == "New"
|
||||
assert theStatus.checkEntry("Stuff") == "New"
|
||||
assert theStatus.checkEntry("New ") == "New"
|
||||
assert theStatus.checkEntry(" Main ") == "Main"
|
||||
|
||||
# Set new list
|
||||
newList = [
|
||||
("New", 1, 1, 1, "New"),
|
||||
("Minor", 2, 2, 2, "Minor"),
|
||||
("Major", 3, 3, 3, "Major"),
|
||||
("Min", 4, 4, 4, "Main"),
|
||||
("Max", 5, 5, 5, None),
|
||||
]
|
||||
assert theStatus.setNewEntries(None) == {}
|
||||
assert theStatus.setNewEntries(newList) == {"Main": "Min"}
|
||||
|
||||
assert theStatus._theLabels == ["New", "Minor", "Major", "Min", "Max"]
|
||||
assert theStatus._theColours == [(1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, 5)]
|
||||
assert theStatus._theCounts == [0, 0, 0, 0, 0]
|
||||
assert theStatus._theMap["New"] == 0
|
||||
assert theStatus._theMap["Minor"] == 1
|
||||
assert theStatus._theMap["Major"] == 2
|
||||
assert theStatus._theMap["Min"] == 3
|
||||
assert theStatus._theMap["Max"] == 4
|
||||
assert theStatus._theLength == 5
|
||||
|
||||
# Add counts
|
||||
countTo = [3, 5, 7, 9, 11]
|
||||
for i, n in enumerate(countTo):
|
||||
for _ in range(n):
|
||||
theStatus.countEntry(theStatus._theLabels[i])
|
||||
assert theStatus._theCounts == countTo
|
||||
|
||||
# Iterate
|
||||
for i, (sA, sB, sC) in enumerate(theStatus):
|
||||
assert sA == theStatus._theLabels[i]
|
||||
assert sB == theStatus._theColours[i]
|
||||
assert sC == theStatus._theCounts[i]
|
||||
|
||||
assert theStatus[9] == (None, None, None)
|
||||
|
||||
# Clear counts
|
||||
theStatus.resetCounts()
|
||||
assert theStatus._theCounts == [0, 0, 0, 0, 0]
|
||||
|
||||
# END Test testCoreStatus_Entries
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreStatus_XMLPackUnpack():
|
||||
"""Test all the simple setters for the NWItem class.
|
||||
"""
|
||||
theStatus = NWStatus()
|
||||
theStatus.addEntry("New", (100, 100, 100))
|
||||
theStatus.addEntry("Minor", (200, 50, 0))
|
||||
theStatus.addEntry("Major", (200, 150, 0))
|
||||
theStatus.addEntry("Main", (50, 200, 0))
|
||||
|
||||
countTo = [3, 5, 7, 9]
|
||||
for i, n in enumerate(countTo):
|
||||
for _ in range(n):
|
||||
theStatus.countEntry(theStatus._theLabels[i])
|
||||
|
||||
nwXML = etree.Element("novelWriterXML")
|
||||
|
||||
# Pack
|
||||
xStatus = etree.SubElement(nwXML, "status")
|
||||
theStatus.packXML(xStatus)
|
||||
assert etree.tostring(xStatus, pretty_print=False, encoding="utf-8") == (
|
||||
b"<status>"
|
||||
b"<entry blue=\"100\" green=\"100\" red=\"100\">New</entry>"
|
||||
b"<entry blue=\"0\" green=\"50\" red=\"200\">Minor</entry>"
|
||||
b"<entry blue=\"0\" green=\"150\" red=\"200\">Major</entry>"
|
||||
b"<entry blue=\"0\" green=\"200\" red=\"50\">Main</entry>"
|
||||
b"</status>"
|
||||
)
|
||||
|
||||
# Unpack
|
||||
theStatus = NWStatus()
|
||||
assert theStatus.unpackXML(xStatus)
|
||||
assert theStatus._theLabels == ["New", "Minor", "Major", "Main"]
|
||||
assert theStatus._theColours == [(100, 100, 100), (200, 50, 0), (200, 150, 0), (50, 200, 0)]
|
||||
assert theStatus._theCounts == [0, 0, 0, 0]
|
||||
assert theStatus._theMap["New"] == 0
|
||||
assert theStatus._theMap["Minor"] == 1
|
||||
assert theStatus._theMap["Major"] == 2
|
||||
assert theStatus._theMap["Main"] == 3
|
||||
assert theStatus._theLength == 4
|
||||
|
||||
# END Test testCoreStatus_XMLPackUnpack
|
||||
@@ -0,0 +1,391 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
novelWriter – ToHtml Class Tester
|
||||
=================================
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, 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 pytest
|
||||
|
||||
from nw.core import NWProject, NWIndex, ToHtml
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreToHtml_Format(dummyGUI):
|
||||
"""Test all the formatters for the ToHtml class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
dummyGUI.theIndex = NWIndex(theProject, dummyGUI)
|
||||
theHtml = ToHtml(theProject, dummyGUI)
|
||||
|
||||
# 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") == (
|
||||
"<div><span class='tags'>Tag:</span> <a name='tag_Jane'>Jane</a></div>"
|
||||
)
|
||||
assert theHtml._formatKeywords("char: Bod, Jane") == (
|
||||
"<div>"
|
||||
"<span class='tags'>Characters:</span> "
|
||||
"<a href='#tag_Bod'>Bod</a>, "
|
||||
"<a href='#tag_Jane'>Jane</a>"
|
||||
"</div>"
|
||||
)
|
||||
|
||||
# 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") == (
|
||||
"<div><span class='tags'>Tag:</span> <a name='tag_Jane'>Jane</a></div>"
|
||||
)
|
||||
assert theHtml._formatKeywords("char: Bod, Jane") == (
|
||||
"<div>"
|
||||
"<span class='tags'>Characters:</span> "
|
||||
"<a href='#char=Bod'>Bod</a>, "
|
||||
"<a href='#char=Jane'>Jane</a>"
|
||||
"</div>"
|
||||
)
|
||||
|
||||
# END Test testCoreToHtml_Format
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreToHtml_Convert(dummyGUI):
|
||||
"""Test the converter of the ToHtml class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
dummyGUI.theIndex = NWIndex(theProject, dummyGUI)
|
||||
theHtml = ToHtml(theProject, dummyGUI)
|
||||
|
||||
# Export Mode
|
||||
# ===========
|
||||
|
||||
theHtml.isNovel = True
|
||||
|
||||
# Header 1
|
||||
theHtml.theText = "# Title\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == "<h1 class='title'>Title</h1>\n"
|
||||
|
||||
# Header 2
|
||||
theHtml.theText = "## Chapter Title\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == "<h1>Chapter Title</h1>\n"
|
||||
|
||||
# Header 3
|
||||
theHtml.theText = "### Scene Title\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == "<h2>Scene Title</h2>\n"
|
||||
|
||||
# Header 4
|
||||
theHtml.theText = "#### Section Title\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == "<h3>Section Title</h3>\n"
|
||||
|
||||
theHtml.isNovel = False
|
||||
theHtml.setLinkHeaders(True)
|
||||
|
||||
# Header 1
|
||||
theHtml.theText = "# Heading One\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == "<h1><a name='T000001'></a>Heading One</h1>\n"
|
||||
|
||||
# Header 2
|
||||
theHtml.theText = "## Heading Two\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == "<h2><a name='T000001'></a>Heading Two</h2>\n"
|
||||
|
||||
# Header 3
|
||||
theHtml.theText = "### Heading Three\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == "<h3><a name='T000001'></a>Heading Three</h3>\n"
|
||||
|
||||
# Header 4
|
||||
theHtml.theText = "#### Heading Four\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == "<h4><a name='T000001'></a>Heading Four</h4>\n"
|
||||
|
||||
# Text
|
||||
theHtml.theText = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
"<p>Some <strong>nested bold and <em>italic</em> and "
|
||||
"<del>strikethrough</del> text</strong> here</p>\n"
|
||||
)
|
||||
|
||||
# Text w/Hard Break
|
||||
theHtml.theText = "Line one \nLine two \nLine three\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
"<p class='break'>Line one<br/>Line two<br/>Line three</p>\n"
|
||||
)
|
||||
|
||||
# Synopsis
|
||||
theHtml.theText = "%synopsis: The synopsis ...\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == ""
|
||||
|
||||
theHtml.setSynopsis(True)
|
||||
theHtml.theText = "%synopsis: The synopsis ...\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
"<p class='synopsis'><strong>Synopsis: </strong>The synopsis ...</p>\n"
|
||||
)
|
||||
|
||||
# Comment
|
||||
theHtml.theText = "% A comment ...\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == ""
|
||||
|
||||
theHtml.setComments(True)
|
||||
theHtml.theText = "% A comment ...\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
"<p class='comment'><strong>Comment: </strong>A comment ...</p>\n"
|
||||
)
|
||||
|
||||
# Keywords
|
||||
theHtml.theText = "@char: Bod, Jane\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == ""
|
||||
|
||||
theHtml.setKeywords(True)
|
||||
theHtml.theText = "@char: Bod, Jane\n"
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
"<div><span class='tags'>Characters:</span> "
|
||||
"<a href='#tag_Bod'>Bod</a>, <a href='#tag_Jane'>Jane</a></div>"
|
||||
)
|
||||
|
||||
# Direct Tests
|
||||
# ============
|
||||
|
||||
theHtml.isNovel = True
|
||||
|
||||
# Title
|
||||
theHtml.theTokens = [
|
||||
(theHtml.T_TITLE, 1, "A Title", None, theHtml.A_PBB_NO | 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: never;'>"
|
||||
"<a name='T000001'></a>A Title</h1>\n"
|
||||
)
|
||||
|
||||
# Separator
|
||||
theHtml.theTokens = [
|
||||
(theHtml.T_SEP, 1, "* * *", None, theHtml.A_CENTRE),
|
||||
(theHtml.T_EMPTY, 1, "", None, theHtml.A_NONE),
|
||||
]
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == "<p class='sep'>* * *</p>\n"
|
||||
|
||||
# Skip
|
||||
theHtml.theTokens = [
|
||||
(theHtml.T_SKIP, 1, "", None, theHtml.A_NONE),
|
||||
(theHtml.T_EMPTY, 1, "", None, theHtml.A_NONE),
|
||||
]
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == "<p class='skip'> </p>\n"
|
||||
|
||||
# Styles
|
||||
# ======
|
||||
|
||||
theHtml.setLinkHeaders(False)
|
||||
|
||||
# Align Left
|
||||
theHtml.setStyles(False)
|
||||
theHtml.theTokens = [
|
||||
(theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_LEFT),
|
||||
]
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
"<h1 class='title'>A Title</h1>\n"
|
||||
)
|
||||
|
||||
theHtml.setStyles(True)
|
||||
|
||||
# Align Left
|
||||
theHtml.theTokens = [
|
||||
(theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_LEFT),
|
||||
]
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
"<h1 class='title' style='text-align: left;'>A Title</h1>\n"
|
||||
)
|
||||
|
||||
# Align Right
|
||||
theHtml.theTokens = [
|
||||
(theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_RIGHT),
|
||||
]
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
"<h1 class='title' style='text-align: right;'>A Title</h1>\n"
|
||||
)
|
||||
|
||||
# Align Centre
|
||||
theHtml.theTokens = [
|
||||
(theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_CENTRE),
|
||||
]
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
"<h1 class='title' style='text-align: center;'>A Title</h1>\n"
|
||||
)
|
||||
|
||||
# Align Justify
|
||||
theHtml.theTokens = [
|
||||
(theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_JUSTIFY),
|
||||
]
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
"<h1 class='title' style='text-align: justify;'>A Title</h1>\n"
|
||||
)
|
||||
|
||||
# Page Break Always
|
||||
theHtml.theTokens = [
|
||||
(theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_PBB | theHtml.A_PBA),
|
||||
]
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
"<h1 class='title' "
|
||||
"style='page-break-before: always; page-break-after: always;'>A Title</h1>\n"
|
||||
)
|
||||
|
||||
# Page Break Avoid
|
||||
theHtml.theTokens = [
|
||||
(theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_PBB_AV | theHtml.A_PBA_AV),
|
||||
]
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
"<h1 class='title' "
|
||||
"style='page-break-before: avoid; page-break-after: avoid;'>A Title</h1>\n"
|
||||
)
|
||||
|
||||
# Page Break ANever
|
||||
theHtml.theTokens = [
|
||||
(theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_PBB_NO | theHtml.A_PBA_NO),
|
||||
]
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
"<h1 class='title' "
|
||||
"style='page-break-before: never; page-break-after: never;'>A Title</h1>\n"
|
||||
)
|
||||
|
||||
# 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_Convert
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreToHtml_Methods(dummyGUI):
|
||||
"""Test all the other methods of the ToHtml class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theHtml = ToHtml(theProject, dummyGUI)
|
||||
|
||||
# Auto-Replace
|
||||
docText = "Text with <brackets> & short–dash, long—dash …\n"
|
||||
theHtml.theText = docText
|
||||
theHtml.doAutoReplace()
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theResult == (
|
||||
"<p>Text with <brackets> & short–dash, long—dash …</p>\n"
|
||||
)
|
||||
|
||||
# Revert on MD
|
||||
assert theHtml.theMarkdown == (
|
||||
"Text with <brackets> & short–dash, long—dash …\n\n"
|
||||
)
|
||||
theHtml.doPostProcessing()
|
||||
assert theHtml.theMarkdown == docText + "\n"
|
||||
|
||||
# With Preview, No Revert
|
||||
theHtml.setPreview(True, True)
|
||||
theHtml.theText = docText
|
||||
theHtml.doAutoReplace()
|
||||
theHtml.tokenizeText()
|
||||
theHtml.doConvert()
|
||||
assert theHtml.theMarkdown == (
|
||||
"Text with <brackets> & short–dash, long—dash …\n\n"
|
||||
)
|
||||
theHtml.doPostProcessing()
|
||||
assert theHtml.theMarkdown == (
|
||||
"Text with <brackets> & short–dash, long—dash …\n\n"
|
||||
)
|
||||
|
||||
# CSS
|
||||
# ===
|
||||
|
||||
assert len(theHtml.getStyleSheet()) > 1
|
||||
assert "p {text-align: left;}" in theHtml.getStyleSheet()
|
||||
assert "p {text-align: justify;}" not in theHtml.getStyleSheet()
|
||||
|
||||
theHtml.setJustify(True)
|
||||
assert "p {text-align: left;}" not in theHtml.getStyleSheet()
|
||||
assert "p {text-align: justify;}" in theHtml.getStyleSheet()
|
||||
|
||||
theHtml.setStyles(False)
|
||||
assert theHtml.getStyleSheet() == []
|
||||
|
||||
# END Test testCoreToHtml_Methods
|
||||
@@ -0,0 +1,662 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
novelWriter – Tokenizer Class Tester
|
||||
====================================
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, 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 pytest
|
||||
|
||||
from nw.core import NWProject, NWDoc
|
||||
from nw.core.tokenizer import Tokenizer
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreToken_Setters(dummyGUI):
|
||||
"""Test all the setters for the Tokenizer class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theToken = Tokenizer(theProject, dummyGUI)
|
||||
|
||||
# Verify defaults
|
||||
assert theToken.fmtTitle == "%title%"
|
||||
assert theToken.fmtChapter == "%title%"
|
||||
assert theToken.fmtUnNum == "%title%"
|
||||
assert theToken.fmtScene == "%title%"
|
||||
assert theToken.fmtSection == "%title%"
|
||||
assert theToken.hideScene is False
|
||||
assert theToken.hideSection is False
|
||||
assert theToken.linkHeaders is False
|
||||
assert theToken.doBodyText is True
|
||||
assert theToken.doSynopsis is False
|
||||
assert theToken.doComments is False
|
||||
assert theToken.doKeywords is False
|
||||
assert theToken.doJustify is False
|
||||
|
||||
# Set new values
|
||||
theToken.setTitleFormat("T: %title%")
|
||||
theToken.setChapterFormat("C: %title%")
|
||||
theToken.setUnNumberedFormat("U: %title%")
|
||||
theToken.setSceneFormat("S: %title%", True)
|
||||
theToken.setSectionFormat("X: %title%", True)
|
||||
theToken.setLinkHeaders(True)
|
||||
theToken.setBodyText(False)
|
||||
theToken.setSynopsis(True)
|
||||
theToken.setComments(True)
|
||||
theToken.setKeywords(True)
|
||||
theToken.setJustify(True)
|
||||
|
||||
# Check new values
|
||||
assert theToken.fmtTitle == "T: %title%"
|
||||
assert theToken.fmtChapter == "C: %title%"
|
||||
assert theToken.fmtUnNum == "U: %title%"
|
||||
assert theToken.fmtScene == "S: %title%"
|
||||
assert theToken.fmtSection == "X: %title%"
|
||||
assert theToken.hideScene is True
|
||||
assert theToken.hideSection is True
|
||||
assert theToken.linkHeaders is True
|
||||
assert theToken.doBodyText is False
|
||||
assert theToken.doSynopsis is True
|
||||
assert theToken.doComments is True
|
||||
assert theToken.doKeywords is True
|
||||
assert theToken.doJustify is True
|
||||
|
||||
# END Test testCoreToken_Setters
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreToken_TextOps(monkeypatch, nwMinimal, dummyGUI):
|
||||
"""Test handling files and text in the Tokenizer class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theProject.projTree.setSeed(42)
|
||||
theToken = Tokenizer(theProject, dummyGUI)
|
||||
|
||||
assert theProject.openProject(nwMinimal)
|
||||
sHandle = "8c659a11cd429"
|
||||
|
||||
# Set some content to work with
|
||||
|
||||
docText = (
|
||||
"### Scene Six\n\n"
|
||||
"This is text with _italic text_, some **bold text**, some ~~deleted text~~, "
|
||||
"and some **_mixed text_** and **some _nested_ text**.\n\n"
|
||||
"#### Replace\n\n"
|
||||
"Also, replace <A> and <B>.\n\n"
|
||||
)
|
||||
docTextR = docText.replace("<A>", "this").replace("<B>", "that")
|
||||
|
||||
nDoc = NWDoc(theProject, dummyGUI)
|
||||
nDoc.openDocument(sHandle)
|
||||
nDoc.saveDocument(docText)
|
||||
nDoc.clearDocument()
|
||||
|
||||
theProject.setAutoReplace({"A": "this", "B": "that"})
|
||||
|
||||
assert theProject.saveProject()
|
||||
|
||||
# Root heading
|
||||
assert theToken.addRootHeading("dummy") is False
|
||||
assert theToken.addRootHeading(sHandle) is False
|
||||
assert theToken.addRootHeading("7695ce551d265") is True
|
||||
assert theToken.theMarkdown == "# Notes: Plot\n\n"
|
||||
|
||||
# Set text
|
||||
assert theToken.setText("dummy") is False
|
||||
assert theToken.setText(sHandle) is True
|
||||
assert theToken.theText == docText
|
||||
|
||||
monkeypatch.setattr("nw.constants.nwConst.MAX_DOCSIZE", 100)
|
||||
assert theToken.setText(sHandle, docText) is True
|
||||
assert theToken.theText == (
|
||||
"# ERROR\n\n"
|
||||
"Document 'New Scene' is too big (0.00 MB). Skipping.\n\n"
|
||||
)
|
||||
monkeypatch.undo()
|
||||
|
||||
assert theToken.setText(sHandle, docText) is True
|
||||
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
|
||||
|
||||
# Auto replace
|
||||
theToken.doAutoReplace()
|
||||
assert theToken.theText == docTextR
|
||||
|
||||
# Access
|
||||
assert theToken.getResult() is None
|
||||
assert theToken.getResultSize() == 0
|
||||
theToken.theResult = ""
|
||||
assert theToken.getResultSize() == 0
|
||||
|
||||
# Post Processing
|
||||
theToken.theResult = r"This is text with escapes: \** \~~ \__"
|
||||
theToken.doPostProcessing()
|
||||
assert theToken.theResult == "This is text with escapes: ** ~~ __"
|
||||
|
||||
# END Test testCoreToken_TextOps
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreToken_Tokenize(dummyGUI):
|
||||
"""Test the tokenization of the Tokenizer class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theToken = Tokenizer(theProject, dummyGUI)
|
||||
|
||||
# Header 1
|
||||
theToken.theText = "# Novel Title\n"
|
||||
theToken.tokenizeText()
|
||||
assert theToken.theTokens == [
|
||||
(Tokenizer.T_HEAD1, 1, "Novel Title", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
assert theToken.theMarkdown == "# Novel Title\n\n"
|
||||
|
||||
# Header 2
|
||||
theToken.theText = "## Chapter One\n"
|
||||
theToken.tokenizeText()
|
||||
assert theToken.theTokens == [
|
||||
(Tokenizer.T_HEAD2, 1, "Chapter One", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
assert theToken.theMarkdown == "## Chapter One\n\n"
|
||||
|
||||
# Header 3
|
||||
theToken.theText = "### Scene One\n"
|
||||
theToken.tokenizeText()
|
||||
assert theToken.theTokens == [
|
||||
(Tokenizer.T_HEAD3, 1, "Scene One", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
assert theToken.theMarkdown == "### Scene One\n\n"
|
||||
|
||||
# Header 4
|
||||
theToken.theText = "#### A Section\n"
|
||||
theToken.tokenizeText()
|
||||
assert theToken.theTokens == [
|
||||
(Tokenizer.T_HEAD4, 1, "A Section", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
assert theToken.theMarkdown == "#### A Section\n\n"
|
||||
|
||||
# Comment
|
||||
theToken.theText = "% A comment\n"
|
||||
theToken.tokenizeText()
|
||||
assert theToken.theTokens == [
|
||||
(Tokenizer.T_COMMENT, 1, "A comment", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
assert theToken.theMarkdown == "\n"
|
||||
|
||||
theToken.setComments(True)
|
||||
theToken.tokenizeText()
|
||||
assert theToken.theMarkdown == "% A comment\n\n"
|
||||
|
||||
# Symopsis
|
||||
theToken.theText = "%synopsis: The synopsis\n"
|
||||
theToken.tokenizeText()
|
||||
assert theToken.theTokens == [
|
||||
(Tokenizer.T_SYNOPSIS, 1, "The synopsis", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
theToken.theText = "% synopsis: The synopsis\n"
|
||||
theToken.tokenizeText()
|
||||
assert theToken.theTokens == [
|
||||
(Tokenizer.T_SYNOPSIS, 1, "The synopsis", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
assert theToken.theMarkdown == "\n"
|
||||
|
||||
theToken.setSynopsis(True)
|
||||
theToken.tokenizeText()
|
||||
assert theToken.theMarkdown == "% synopsis: The synopsis\n\n"
|
||||
|
||||
# Keyword
|
||||
theToken.theText = "@char: Bod\n"
|
||||
theToken.tokenizeText()
|
||||
assert theToken.theTokens == [
|
||||
(Tokenizer.T_KEYWORD, 1, "char: Bod", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
assert theToken.theMarkdown == "\n"
|
||||
|
||||
theToken.setKeywords(True)
|
||||
theToken.tokenizeText()
|
||||
assert theToken.theMarkdown == "@char: Bod\n\n"
|
||||
|
||||
# Text
|
||||
theToken.theText = "Some plain text\non two lines\n\n\n"
|
||||
theToken.tokenizeText()
|
||||
assert theToken.theTokens == [
|
||||
(Tokenizer.T_TEXT, 1, "Some plain text", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_TEXT, 2, "on two lines", [], Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 3, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
assert theToken.theMarkdown == "Some plain text\non two lines\n\n\n\n"
|
||||
|
||||
theToken.setBodyText(False)
|
||||
theToken.tokenizeText()
|
||||
assert theToken.theTokens == [
|
||||
(Tokenizer.T_EMPTY, 3, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
assert theToken.theMarkdown == "\n\n\n"
|
||||
theToken.setBodyText(True)
|
||||
|
||||
# Text Emphasis
|
||||
theToken.theText = "Some **bolded text** on this lines\n"
|
||||
theToken.tokenizeText()
|
||||
assert theToken.theTokens == [
|
||||
(
|
||||
Tokenizer.T_TEXT, 1,
|
||||
"Some **bolded text** on this lines",
|
||||
[
|
||||
[5, 2, Tokenizer.FMT_B_B],
|
||||
[18, 2, Tokenizer.FMT_B_E],
|
||||
],
|
||||
Tokenizer.A_NONE
|
||||
),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
assert theToken.theMarkdown == "Some **bolded text** on this lines\n\n"
|
||||
|
||||
theToken.theText = "Some _italic text_ on this lines\n"
|
||||
theToken.tokenizeText()
|
||||
assert theToken.theTokens == [
|
||||
(
|
||||
Tokenizer.T_TEXT, 1,
|
||||
"Some _italic text_ on this lines",
|
||||
[
|
||||
[5, 1, Tokenizer.FMT_I_B],
|
||||
[17, 1, Tokenizer.FMT_I_E],
|
||||
],
|
||||
Tokenizer.A_NONE
|
||||
),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
assert theToken.theMarkdown == "Some _italic text_ on this lines\n\n"
|
||||
|
||||
theToken.theText = "Some **_bold italic text_** on this lines\n"
|
||||
theToken.tokenizeText()
|
||||
assert theToken.theTokens == [
|
||||
(
|
||||
Tokenizer.T_TEXT, 1,
|
||||
"Some **_bold italic text_** on this lines",
|
||||
[
|
||||
[5, 2, Tokenizer.FMT_B_B],
|
||||
[7, 1, Tokenizer.FMT_I_B],
|
||||
[24, 1, Tokenizer.FMT_I_E],
|
||||
[25, 2, Tokenizer.FMT_B_E],
|
||||
],
|
||||
Tokenizer.A_NONE
|
||||
),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
assert theToken.theMarkdown == "Some **_bold italic text_** on this lines\n\n"
|
||||
|
||||
theToken.theText = "Some ~~strikethrough text~~ on this lines\n"
|
||||
theToken.tokenizeText()
|
||||
assert theToken.theTokens == [
|
||||
(
|
||||
Tokenizer.T_TEXT, 1,
|
||||
"Some ~~strikethrough text~~ on this lines",
|
||||
[
|
||||
[5, 2, Tokenizer.FMT_D_B],
|
||||
[25, 2, Tokenizer.FMT_D_E],
|
||||
],
|
||||
Tokenizer.A_NONE
|
||||
),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
assert theToken.theMarkdown == "Some ~~strikethrough text~~ on this lines\n\n"
|
||||
|
||||
theToken.theText = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n"
|
||||
theToken.tokenizeText()
|
||||
assert theToken.theTokens == [
|
||||
(
|
||||
Tokenizer.T_TEXT, 1,
|
||||
"Some **nested bold and _italic_ and ~~strikethrough~~ text** here",
|
||||
[
|
||||
[5, 2, Tokenizer.FMT_B_B],
|
||||
[23, 1, Tokenizer.FMT_I_B],
|
||||
[30, 1, Tokenizer.FMT_I_E],
|
||||
[36, 2, Tokenizer.FMT_D_B],
|
||||
[51, 2, Tokenizer.FMT_D_E],
|
||||
[58, 2, Tokenizer.FMT_B_E],
|
||||
],
|
||||
Tokenizer.A_NONE
|
||||
),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
assert theToken.theMarkdown == (
|
||||
"Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n\n"
|
||||
)
|
||||
|
||||
# Check the markdown function as well
|
||||
assert theToken.getFilteredMarkdown() == (
|
||||
"Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n\n"
|
||||
)
|
||||
|
||||
# END Test testCoreToken_Tokenize
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreToken_Headers(dummyGUI):
|
||||
"""Test the header and page parser of the Tokenizer class.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theToken = Tokenizer(theProject, dummyGUI)
|
||||
|
||||
# Nothing
|
||||
theToken.theText = "Some text ...\n"
|
||||
assert theToken.doHeaders() is True
|
||||
theToken.isNone = True
|
||||
assert theToken.doHeaders() is False
|
||||
theToken.isNone = False
|
||||
assert theToken.doHeaders() is True
|
||||
theToken.isNote = True
|
||||
assert theToken.doHeaders() is False
|
||||
theToken.isNote = False
|
||||
|
||||
##
|
||||
# Novel
|
||||
##
|
||||
|
||||
theToken.isNovel = True
|
||||
|
||||
# Titles
|
||||
# ======
|
||||
|
||||
# H1: Title
|
||||
theToken.theText = "# Novel Title\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_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# Chapters
|
||||
# ========
|
||||
|
||||
# H2: Chapter
|
||||
theToken.theText = "## Chapter One\n"
|
||||
theToken.setChapterFormat(r"C: %title%")
|
||||
theToken.tokenizeText()
|
||||
theToken.doHeaders()
|
||||
assert theToken.theTokens == [
|
||||
(Tokenizer.T_HEAD2, 1, "C: Chapter One", None, Tokenizer.A_PBB),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# 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 == [
|
||||
(Tokenizer.T_HEAD2, 1, "U: Prologue", None, Tokenizer.A_PBB),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# H2: Chapter Word Number
|
||||
theToken.theText = "## Chapter\n"
|
||||
theToken.setChapterFormat(r"Chapter %chw%")
|
||||
theToken.numChapter = 0
|
||||
theToken.tokenizeText()
|
||||
theToken.doHeaders()
|
||||
assert theToken.theTokens == [
|
||||
(Tokenizer.T_HEAD2, 1, "Chapter One", None, Tokenizer.A_PBB),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# H2: Chapter Roman Number Upper Case
|
||||
theToken.theText = "## Chapter\n"
|
||||
theToken.setChapterFormat(r"Chapter %chI%")
|
||||
theToken.tokenizeText()
|
||||
theToken.doHeaders()
|
||||
assert theToken.theTokens == [
|
||||
(Tokenizer.T_HEAD2, 1, "Chapter II", None, Tokenizer.A_PBB),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# H2: Chapter Roman Number Lower Case
|
||||
theToken.theText = "## Chapter\n"
|
||||
theToken.setChapterFormat(r"Chapter %chi%")
|
||||
theToken.tokenizeText()
|
||||
theToken.doHeaders()
|
||||
assert theToken.theTokens == [
|
||||
(Tokenizer.T_HEAD2, 1, "Chapter iii", None, Tokenizer.A_PBB),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# Scenes
|
||||
# ======
|
||||
|
||||
# H3: Scene w/Title
|
||||
theToken.theText = "### Scene One\n"
|
||||
theToken.setSceneFormat(r"S: %title%", False)
|
||||
theToken.tokenizeText()
|
||||
theToken.doHeaders()
|
||||
assert theToken.theTokens == [
|
||||
(Tokenizer.T_HEAD3, 1, "S: Scene One", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# H3: Scene Hidden wo/Format
|
||||
theToken.theText = "### Scene One\n"
|
||||
theToken.setSceneFormat(r"", True)
|
||||
theToken.tokenizeText()
|
||||
theToken.doHeaders()
|
||||
assert theToken.theTokens == [
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# H3: Scene wo/Format, first
|
||||
theToken.theText = "### Scene One\n"
|
||||
theToken.setSceneFormat(r"", False)
|
||||
theToken.firstScene = True
|
||||
theToken.tokenizeText()
|
||||
theToken.doHeaders()
|
||||
assert theToken.theTokens == [
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# H3: Scene wo/Format, not first
|
||||
theToken.theText = "### Scene One\n"
|
||||
theToken.setSceneFormat(r"", False)
|
||||
theToken.firstScene = False
|
||||
theToken.tokenizeText()
|
||||
theToken.doHeaders()
|
||||
assert theToken.theTokens == [
|
||||
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# H3: Scene Separator, first
|
||||
theToken.theText = "### Scene One\n"
|
||||
theToken.setSceneFormat(r"* * *", False)
|
||||
theToken.firstScene = True
|
||||
theToken.tokenizeText()
|
||||
theToken.doHeaders()
|
||||
assert theToken.theTokens == [
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# H3: Scene Separator, not first
|
||||
theToken.theText = "### Scene One\n"
|
||||
theToken.setSceneFormat(r"* * *", False)
|
||||
theToken.firstScene = False
|
||||
theToken.tokenizeText()
|
||||
theToken.doHeaders()
|
||||
assert theToken.theTokens == [
|
||||
(Tokenizer.T_SEP, 1, "* * *", None, Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# H3: Scene w/Absolute Number
|
||||
theToken.theText = "### A Scene\n"
|
||||
theToken.setSceneFormat(r"Scene %sca%", False)
|
||||
theToken.numAbsScene = 0
|
||||
theToken.numChScene = 0
|
||||
theToken.tokenizeText()
|
||||
theToken.doHeaders()
|
||||
assert theToken.theTokens == [
|
||||
(Tokenizer.T_HEAD3, 1, "Scene 1", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# H3: Scene w/Chapter Number
|
||||
theToken.theText = "### A Scene\n"
|
||||
theToken.setSceneFormat(r"Scene %ch%.%sc%", False)
|
||||
theToken.numAbsScene = 0
|
||||
theToken.numChScene = 1
|
||||
theToken.tokenizeText()
|
||||
theToken.doHeaders()
|
||||
assert theToken.theTokens == [
|
||||
(Tokenizer.T_HEAD3, 1, "Scene 3.2", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# Sections
|
||||
# ========
|
||||
|
||||
# H4: Section Hidden wo/Format
|
||||
theToken.theText = "#### A Section\n"
|
||||
theToken.setSectionFormat(r"", True)
|
||||
theToken.tokenizeText()
|
||||
theToken.doHeaders()
|
||||
assert theToken.theTokens == [
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# H4: Section Visible wo/Format
|
||||
theToken.theText = "#### A Section\n"
|
||||
theToken.setSectionFormat(r"", False)
|
||||
theToken.tokenizeText()
|
||||
theToken.doHeaders()
|
||||
assert theToken.theTokens == [
|
||||
(Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# H4: Section w/Format
|
||||
theToken.theText = "#### A Section\n"
|
||||
theToken.setSectionFormat(r"X: %title%", False)
|
||||
theToken.tokenizeText()
|
||||
theToken.doHeaders()
|
||||
assert theToken.theTokens == [
|
||||
(Tokenizer.T_HEAD4, 1, "X: A Section", None, Tokenizer.A_NONE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# H4: Section Separator
|
||||
theToken.theText = "#### A Section\n"
|
||||
theToken.setSectionFormat(r"* * *", False)
|
||||
theToken.tokenizeText()
|
||||
theToken.doHeaders()
|
||||
assert theToken.theTokens == [
|
||||
(Tokenizer.T_SEP, 1, "* * *", None, Tokenizer.A_CENTRE),
|
||||
(Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE),
|
||||
]
|
||||
|
||||
# Check the first scene detector
|
||||
assert theToken.firstScene is False
|
||||
theToken.firstScene = True
|
||||
assert theToken.firstScene is True
|
||||
theToken.theText = "Some text ...\n"
|
||||
theToken.tokenizeText()
|
||||
theToken.doHeaders()
|
||||
assert theToken.firstScene is False
|
||||
|
||||
##
|
||||
# Title or Partition
|
||||
##
|
||||
|
||||
theToken.isNovel = False
|
||||
|
||||
# H1: Title
|
||||
theToken.theText = "# Novel Title\n"
|
||||
theToken.tokenizeText()
|
||||
theToken.isTitle = True
|
||||
theToken.isPart = False
|
||||
theToken.doHeaders()
|
||||
assert theToken.theTokens == [
|
||||
(Tokenizer.T_TITLE, 1, "Novel Title", None, Tokenizer.A_PBB_NO | 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.tokenizeText()
|
||||
theToken.isTitle = False
|
||||
theToken.isPart = True
|
||||
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.A_LEFT),
|
||||
(Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_LEFT),
|
||||
(Tokenizer.T_TEXT, 3, "More text", [], Tokenizer.A_LEFT),
|
||||
(Tokenizer.T_EMPTY, 3, "", None, Tokenizer.A_LEFT),
|
||||
]
|
||||
|
||||
# END Test testCoreToken_Headers
|
||||
@@ -0,0 +1,127 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
novelWriter – Tools Tester
|
||||
==========================
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, 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 pytest
|
||||
|
||||
from nw.core.tools import countWords, numberToRoman, numberToWord
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreTools_CountWords():
|
||||
"""Test the word counter and the exclusion filers.
|
||||
"""
|
||||
testText = (
|
||||
"# Heading One\n"
|
||||
"## Heading Two\n"
|
||||
"### Heading Three\n"
|
||||
"#### Heading Four\n"
|
||||
"\n"
|
||||
"@tag: value\n"
|
||||
"\n"
|
||||
"% A comment that should n ot be counted.\n"
|
||||
"\n"
|
||||
"The first paragraph.\n"
|
||||
"\n"
|
||||
"The second paragraph.\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"The third paragraph.\n"
|
||||
"\n"
|
||||
"Dashes\u2013and even longer\u2014dashes."
|
||||
)
|
||||
cC, wC, pC = countWords(testText)
|
||||
|
||||
assert cC == 138
|
||||
assert wC == 22
|
||||
assert pC == 4
|
||||
|
||||
# END Test testCoreTools_CountWords
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreTools_RomanNumbers():
|
||||
"""Test conversion of integers to Roman numbers.
|
||||
"""
|
||||
assert numberToRoman(None, False) == "NAN"
|
||||
assert numberToRoman(0, False) == "OOR"
|
||||
assert numberToRoman(1, False) == "I"
|
||||
assert numberToRoman(2, False) == "II"
|
||||
assert numberToRoman(3, False) == "III"
|
||||
assert numberToRoman(4, False) == "IV"
|
||||
assert numberToRoman(5, False) == "V"
|
||||
assert numberToRoman(6, False) == "VI"
|
||||
assert numberToRoman(7, False) == "VII"
|
||||
assert numberToRoman(8, False) == "VIII"
|
||||
assert numberToRoman(9, False) == "IX"
|
||||
assert numberToRoman(10, False) == "X"
|
||||
assert numberToRoman(14, False) == "XIV"
|
||||
assert numberToRoman(42, False) == "XLII"
|
||||
assert numberToRoman(99, False) == "XCIX"
|
||||
assert numberToRoman(142, False) == "CXLII"
|
||||
assert numberToRoman(542, False) == "DXLII"
|
||||
assert numberToRoman(999, False) == "CMXCIX"
|
||||
assert numberToRoman(2010, False) == "MMX"
|
||||
assert numberToRoman(999, True) == "cmxcix"
|
||||
|
||||
# END Test testCoreTools_RomanNumbers
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreTools_NumberWords():
|
||||
"""Test the conversion of integer to English words.
|
||||
"""
|
||||
assert numberToWord(0, "en") == "Zero"
|
||||
assert numberToWord(1, "en") == "One"
|
||||
assert numberToWord(2, "en") == "Two"
|
||||
assert numberToWord(3, "en") == "Three"
|
||||
assert numberToWord(4, "en") == "Four"
|
||||
assert numberToWord(5, "en") == "Five"
|
||||
assert numberToWord(6, "en") == "Six"
|
||||
assert numberToWord(7, "en") == "Seven"
|
||||
assert numberToWord(8, "en") == "Eight"
|
||||
assert numberToWord(9, "en") == "Nine"
|
||||
assert numberToWord(10, "en") == "Ten"
|
||||
assert numberToWord(11, "en") == "Eleven"
|
||||
assert numberToWord(12, "en") == "Twelve"
|
||||
assert numberToWord(13, "en") == "Thirteen"
|
||||
assert numberToWord(14, "en") == "Fourteen"
|
||||
assert numberToWord(15, "en") == "Fifteen"
|
||||
assert numberToWord(16, "en") == "Sixteen"
|
||||
assert numberToWord(17, "en") == "Seventeen"
|
||||
assert numberToWord(18, "en") == "Eighteen"
|
||||
assert numberToWord(19, "en") == "Nineteen"
|
||||
assert numberToWord(20, "en") == "Twenty"
|
||||
assert numberToWord(21, "en") == "Twenty-One"
|
||||
assert numberToWord(29, "en") == "Twenty-Nine"
|
||||
assert numberToWord(42, "en") == "Forty-Two"
|
||||
assert numberToWord(114, "en") == "One Hundred Fourteen"
|
||||
assert numberToWord(142, "en") == "One Hundred Forty-Two"
|
||||
assert numberToWord(999, "en") == "Nine Hundred Ninety-Nine"
|
||||
|
||||
# Check a few with a nonsense language setting
|
||||
assert numberToWord(1, "foo") == "One"
|
||||
assert numberToWord(2, "foo") == "Two"
|
||||
assert numberToWord(3, "foo") == "Three"
|
||||
|
||||
# Test out of range values
|
||||
assert numberToWord(12345, "en") == "[Out of Range]"
|
||||
assert numberToWord(-2345, "en") == "[Negative]"
|
||||
assert numberToWord("234", "en") == "[NaN]"
|
||||
|
||||
# END Test testCoreTools_NumberWords
|
||||
@@ -0,0 +1,451 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
novelWriter – NWTree Class Tester
|
||||
=================================
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, 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 lxml import etree
|
||||
|
||||
from nw.core.project import NWProject, NWItem, NWTree
|
||||
from nw.constants import nwItemClass, nwItemType, nwItemLayout, nwFiles
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def dummyItems(dummyGUI):
|
||||
"""Create a list of dummy items.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
|
||||
itemA = NWItem(theProject)
|
||||
itemA.itemName = "Novel"
|
||||
itemA.itemType = nwItemType.ROOT
|
||||
itemA.itemClass = nwItemClass.NOVEL
|
||||
itemA.isExpanded = True
|
||||
|
||||
itemB = NWItem(theProject)
|
||||
itemB.itemName = "Act One"
|
||||
itemB.itemType = nwItemType.FOLDER
|
||||
itemB.itemClass = nwItemClass.NOVEL
|
||||
itemB.isExpanded = True
|
||||
|
||||
itemC = NWItem(theProject)
|
||||
itemC.itemName = "Chapter One"
|
||||
itemC.itemType = nwItemType.FILE
|
||||
itemC.itemClass = nwItemClass.NOVEL
|
||||
itemC.itemLayout = nwItemLayout.CHAPTER
|
||||
itemC.charCount = 300
|
||||
itemC.wordCount = 50
|
||||
itemC.paraCount = 2
|
||||
|
||||
itemD = NWItem(theProject)
|
||||
itemD.itemName = "Scene One"
|
||||
itemD.itemType = nwItemType.FILE
|
||||
itemD.itemClass = nwItemClass.NOVEL
|
||||
itemD.itemLayout = nwItemLayout.SCENE
|
||||
itemD.charCount = 3000
|
||||
itemD.wordCount = 500
|
||||
itemD.paraCount = 20
|
||||
|
||||
itemE = NWItem(theProject)
|
||||
itemE.itemName = "Outtakes"
|
||||
itemE.itemType = nwItemType.ROOT
|
||||
itemE.itemClass = nwItemClass.ARCHIVE
|
||||
itemE.isExpanded = False
|
||||
|
||||
itemF = NWItem(theProject)
|
||||
itemF.itemName = "Trash"
|
||||
itemF.itemType = nwItemType.TRASH
|
||||
itemF.itemClass = nwItemClass.TRASH
|
||||
itemF.isExpanded = False
|
||||
|
||||
itemG = NWItem(theProject)
|
||||
itemG.itemName = "Characters"
|
||||
itemG.itemType = nwItemType.ROOT
|
||||
itemG.itemClass = nwItemClass.CHARACTER
|
||||
itemG.isExpanded = True
|
||||
|
||||
itemH = NWItem(theProject)
|
||||
itemH.itemName = "Jane Doe"
|
||||
itemH.itemType = nwItemType.FILE
|
||||
itemH.itemClass = nwItemClass.CHARACTER
|
||||
itemH.itemLayout = nwItemLayout.NOTE
|
||||
itemH.charCount = 2000
|
||||
itemH.wordCount = 400
|
||||
itemH.paraCount = 16
|
||||
|
||||
theItems = [
|
||||
("a000000000001", None, itemA),
|
||||
("b000000000001", "a000000000001", itemB),
|
||||
("c000000000001", "b000000000001", itemC),
|
||||
("c000000000002", "b000000000001", itemD),
|
||||
("a000000000002", None, itemE),
|
||||
("a000000000003", None, itemF),
|
||||
("a000000000004", None, itemG),
|
||||
("b000000000002", "a000000000002", itemH),
|
||||
]
|
||||
|
||||
return theItems
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreTree_BuildTree(dummyGUI, dummyItems):
|
||||
"""Test building a project tree from a list of items.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theTree = NWTree(theProject)
|
||||
|
||||
theTree.setSeed(42)
|
||||
assert theTree._handleSeed == 42
|
||||
|
||||
# Check that tree is empty (calls NWTree.__bool__)
|
||||
assert not theTree
|
||||
|
||||
# Check for archive and trash folders
|
||||
assert theTree.trashRoot() is None
|
||||
assert theTree.archiveRoot() is None
|
||||
assert not theTree.isTrashRoot("a000000000003")
|
||||
|
||||
aHandles = []
|
||||
for tHandle, pHande, nwItem in dummyItems:
|
||||
aHandles.append(tHandle)
|
||||
assert theTree.append(tHandle, pHande, nwItem)
|
||||
|
||||
assert theTree._treeChanged
|
||||
|
||||
# Check that tree is not empty (calls __bool__)
|
||||
assert theTree
|
||||
|
||||
# Check the number of elements (calls __len__)
|
||||
assert len(theTree) == len(dummyItems)
|
||||
|
||||
# Check that we have the correct handles
|
||||
assert theTree.handles() == aHandles
|
||||
|
||||
# Check by iterator (calls __iter__, __next__ and __getitem__)
|
||||
for theItem, theHandle in zip(theTree, aHandles):
|
||||
assert theItem.itemHandle == theHandle
|
||||
|
||||
# Check that we have the correct archive and trash folders
|
||||
assert theTree.trashRoot() == "a000000000003"
|
||||
assert theTree.archiveRoot() == "a000000000002"
|
||||
assert theTree.isTrashRoot("a000000000003")
|
||||
|
||||
# Try to add another trash folder
|
||||
itemT = NWItem(theProject)
|
||||
itemT.itemName = "Trash"
|
||||
itemT.itemType = nwItemType.TRASH
|
||||
itemT.itemClass = nwItemClass.TRASH
|
||||
itemT.isExpanded = False
|
||||
|
||||
assert not theTree.append("1234567890abc", None, itemT)
|
||||
assert len(theTree) == len(dummyItems)
|
||||
|
||||
# Generate handle automatically
|
||||
itemT = NWItem(theProject)
|
||||
itemT.itemName = "New File"
|
||||
itemT.itemType = nwItemType.FILE
|
||||
itemT.itemClass = nwItemClass.NOVEL
|
||||
itemT.itemLayout = nwItemLayout.SCENE
|
||||
|
||||
assert theTree.append(None, None, itemT)
|
||||
assert len(theTree) == len(dummyItems) + 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
|
||||
|
||||
# Delete a non-existing item
|
||||
del theTree["dummy"]
|
||||
assert len(theTree) == len(dummyItems) + 1
|
||||
|
||||
# Delete the last item
|
||||
del theTree["73475cb40a568"]
|
||||
assert len(theTree) == len(dummyItems)
|
||||
assert "73475cb40a568" not in theTree
|
||||
|
||||
# Delete the Novel, Archive and Trash folders
|
||||
del theTree["a000000000001"]
|
||||
assert len(theTree) == len(dummyItems) - 1
|
||||
assert "a000000000001" not in theTree
|
||||
|
||||
del theTree["a000000000002"]
|
||||
assert len(theTree) == len(dummyItems) - 2
|
||||
assert "a000000000002" not in theTree
|
||||
assert theTree.archiveRoot() is None
|
||||
|
||||
del theTree["a000000000003"]
|
||||
assert len(theTree) == len(dummyItems) - 3
|
||||
assert "a000000000003" not in theTree
|
||||
assert theTree.trashRoot() is None
|
||||
|
||||
# END Test testCoreTree_BuildTree
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreTree_Methods(dummyGUI, dummyItems):
|
||||
"""Test building a project tree from a list of items.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theTree = NWTree(theProject)
|
||||
|
||||
for tHandle, pHande, nwItem in dummyItems:
|
||||
theTree.append(tHandle, pHande, nwItem)
|
||||
|
||||
assert len(theTree) == len(dummyItems)
|
||||
|
||||
# Root item lookup
|
||||
theTree._treeRoots.append("dummy")
|
||||
assert theTree.findRoot(nwItemClass.WORLD) is None
|
||||
assert theTree.findRoot(nwItemClass.NOVEL) == "a000000000001"
|
||||
assert theTree.findRoot(nwItemClass.CHARACTER) == "a000000000004"
|
||||
|
||||
# Check for root uniqueness
|
||||
assert theTree.checkRootUnique(nwItemClass.CUSTOM)
|
||||
assert theTree.checkRootUnique(nwItemClass.WORLD)
|
||||
assert not theTree.checkRootUnique(nwItemClass.NOVEL)
|
||||
assert not theTree.checkRootUnique(nwItemClass.CHARACTER)
|
||||
|
||||
# Find root item of child item
|
||||
assert theTree.getRootItem("b000000000001").itemHandle == "a000000000001"
|
||||
assert theTree.getRootItem("c000000000001").itemHandle == "a000000000001"
|
||||
assert theTree.getRootItem("c000000000002").itemHandle == "a000000000001"
|
||||
assert theTree.getRootItem("dummy") is None
|
||||
|
||||
# Get item path
|
||||
assert theTree.getItemPath("dummy") == []
|
||||
assert theTree.getItemPath("c000000000001") == [
|
||||
"c000000000001", "b000000000001", "a000000000001"
|
||||
]
|
||||
|
||||
# Break the folder parent handle
|
||||
theTree["b000000000001"].itemParent = "dummy"
|
||||
assert theTree.getItemPath("c000000000001") == [
|
||||
"c000000000001", "b000000000001"
|
||||
]
|
||||
|
||||
theTree["b000000000001"].itemParent = "a000000000001"
|
||||
assert theTree.getItemPath("c000000000001") == [
|
||||
"c000000000001", "b000000000001", "a000000000001"
|
||||
]
|
||||
|
||||
# Change file layout
|
||||
assert not theTree.setFileItemLayout("dummy", 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
|
||||
|
||||
# END Test testCoreTree_Methods
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreTree_MakeHandles(monkeypatch, dummyGUI):
|
||||
"""Test generating item handles.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theTree = NWTree(theProject)
|
||||
|
||||
theTree.setSeed(42)
|
||||
|
||||
tHandle = theTree._makeHandle()
|
||||
assert tHandle == "73475cb40a568"
|
||||
|
||||
# Add the next in line to the project to foprce duplicate
|
||||
theTree._projTree["44cb730c42048"] = None
|
||||
tHandle = theTree._makeHandle()
|
||||
assert tHandle == "71ee45a3c0db9"
|
||||
|
||||
# Fix the time() function and force a handle collission
|
||||
theTree.setSeed(None)
|
||||
monkeypatch.setattr("nw.core.tree.time", lambda: 123.4)
|
||||
|
||||
tHandle = theTree._makeHandle()
|
||||
theTree._projTree[tHandle] = None
|
||||
assert tHandle == "5f466d7afa48b"
|
||||
|
||||
tHandle = theTree._makeHandle()
|
||||
theTree._projTree[tHandle] = None
|
||||
assert tHandle == "a79acf4c634a7"
|
||||
|
||||
monkeypatch.undo()
|
||||
|
||||
# END Test testCoreTree_MakeHandles
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreTree_Stats(dummyGUI, dummyItems):
|
||||
"""Test project stats methods.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theTree = NWTree(theProject)
|
||||
|
||||
for tHandle, pHande, nwItem in dummyItems:
|
||||
theTree.append(tHandle, pHande, nwItem)
|
||||
|
||||
assert len(theTree) == len(dummyItems)
|
||||
theTree._treeOrder.append("dummy")
|
||||
|
||||
# Count Words
|
||||
novelWords, noteWords = theTree.sumWords()
|
||||
assert novelWords == 550
|
||||
assert noteWords == 400
|
||||
|
||||
# Count types
|
||||
nRoot, nFolder, nFile = theTree.countTypes()
|
||||
assert nRoot == 3
|
||||
assert nFolder == 1
|
||||
assert nFile == 3
|
||||
|
||||
# END Test testCoreTree_Stats
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreTree_Reorder(dummyGUI, dummyItems):
|
||||
"""Test changing tree order.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theTree = NWTree(theProject)
|
||||
|
||||
aHandle = []
|
||||
for tHandle, pHande, nwItem in dummyItems:
|
||||
aHandle.append(tHandle)
|
||||
theTree.append(tHandle, pHande, nwItem)
|
||||
|
||||
assert len(theTree) == len(dummyItems)
|
||||
|
||||
bHandle = aHandle.copy()
|
||||
bHandle[2], bHandle[3] = bHandle[3], bHandle[2]
|
||||
assert aHandle != bHandle
|
||||
|
||||
assert theTree.handles() == aHandle
|
||||
theTree.setOrder(bHandle)
|
||||
assert theTree.handles() == bHandle
|
||||
|
||||
theTree.setOrder(bHandle + ["dummy"])
|
||||
assert theTree.handles() == bHandle
|
||||
|
||||
theTree._treeOrder.append("dummy")
|
||||
theTree.setOrder(bHandle)
|
||||
assert theTree.handles() == bHandle
|
||||
|
||||
# END Test testCoreTree_Reorder
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreTree_XMLPackUnpack(dummyGUI, dummyItems):
|
||||
"""Test changing tree order.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theTree = NWTree(theProject)
|
||||
|
||||
for tHandle, pHande, nwItem in dummyItems:
|
||||
theTree.append(tHandle, pHande, nwItem)
|
||||
|
||||
assert len(theTree) == len(dummyItems)
|
||||
|
||||
nwXML = etree.Element("novelWriterXML")
|
||||
theTree.packXML(nwXML)
|
||||
assert etree.tostring(nwXML, pretty_print=False, encoding="utf-8") == (
|
||||
b"<novelWriterXML>"
|
||||
b"<content count=\"8\">"
|
||||
b"<item handle=\"a000000000001\" order=\"0\" parent=\"None\">"
|
||||
b"<name>Novel</name><type>ROOT</type><class>NOVEL</class><status>None</status>"
|
||||
b"<expanded>True</expanded></item>"
|
||||
b"<item handle=\"b000000000001\" order=\"0\" parent=\"a000000000001\">"
|
||||
b"<name>Act One</name><type>FOLDER</type><class>NOVEL</class><status>None</status>"
|
||||
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"<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"<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>"
|
||||
b"<expanded>False</expanded></item>"
|
||||
b"<item handle=\"a000000000003\" order=\"0\" parent=\"None\">"
|
||||
b"<name>Trash</name><type>TRASH</type><class>TRASH</class><status>None</status>"
|
||||
b"<expanded>False</expanded></item>"
|
||||
b"<item handle=\"a000000000004\" order=\"0\" parent=\"None\">"
|
||||
b"<name>Characters</name><type>ROOT</type><class>CHARACTER</class><status>None</status>"
|
||||
b"<expanded>True</expanded></item>"
|
||||
b"<item handle=\"b000000000002\" order=\"0\" parent=\"a000000000002\">"
|
||||
b"<name>Jane Doe</name><type>FILE</type><class>CHARACTER</class><status>None</status>"
|
||||
b"<exported>True</exported><layout>NOTE</layout><charCount>2000</charCount>"
|
||||
b"<wordCount>400</wordCount><paraCount>16</paraCount><cursorPos>0</cursorPos></item>"
|
||||
b"</content></novelWriterXML>"
|
||||
)
|
||||
|
||||
theTree.clear()
|
||||
assert len(theTree) == 0
|
||||
assert not theTree.unpackXML(nwXML)
|
||||
assert theTree.unpackXML(nwXML[0])
|
||||
assert len(theTree) == len(dummyItems)
|
||||
|
||||
# END Test testCoreTree_XMLPackUnpack
|
||||
|
||||
@pytest.mark.core
|
||||
def testCoreTree_ToCFile(monkeypatch, dummyGUI, dummyItems, tmpDir):
|
||||
"""Test writing the ToC.txt file.
|
||||
"""
|
||||
theProject = NWProject(dummyGUI)
|
||||
theTree = NWTree(theProject)
|
||||
|
||||
for tHandle, pHande, nwItem in dummyItems:
|
||||
theTree.append(tHandle, pHande, nwItem)
|
||||
|
||||
assert len(theTree) == len(dummyItems)
|
||||
theTree._treeOrder.append("dummy")
|
||||
|
||||
def dummyIsFile(fileName):
|
||||
"""Return True for items that are files in novelWriter and
|
||||
should thus also be files in the project folder structure.
|
||||
"""
|
||||
dItem = theTree[fileName[8:21]]
|
||||
assert dItem is not None
|
||||
return dItem.itemType == nwItemType.FILE
|
||||
|
||||
monkeypatch.setattr("os.path.isfile", dummyIsFile)
|
||||
|
||||
theProject.projContent = "content"
|
||||
theProject.projPath = None
|
||||
assert not theTree.writeToCFile()
|
||||
|
||||
theProject.projPath = tmpDir
|
||||
assert theTree.writeToCFile()
|
||||
|
||||
pathA = os.path.join("content", "c000000000001.nwd")
|
||||
pathB = os.path.join("content", "c000000000002.nwd")
|
||||
pathC = os.path.join("content", "b000000000002.nwd")
|
||||
|
||||
with open(os.path.join(tmpDir, nwFiles.TOC_TXT), mode="r", encoding="utf8") as inFile:
|
||||
assert inFile.read() == (
|
||||
"\n"
|
||||
"Table of Contents\n"
|
||||
"=================\n"
|
||||
"\n"
|
||||
"File Name Class Layout Document Label\n"
|
||||
"-------------------------------------------------------------\n"
|
||||
f"{pathA} NOVEL CHAPTER Chapter One\n"
|
||||
f"{pathB} NOVEL SCENE Scene One\n"
|
||||
f"{pathC} CHARACTER NOTE Jane Doe\n"
|
||||
)
|
||||
|
||||
# END Test testCoreTree_ToCFile
|
||||
Reference in New Issue
Block a user