Clean up project tests

This commit is contained in:
Veronica Berglyd Olsen
2023-12-01 17:45:25 +01:00
parent 3fe2ee3058
commit 8952bbab1d
+234 -239
View File
@@ -26,6 +26,7 @@ from shutil import copyfile
from zipfile import ZipFile from zipfile import ZipFile
from mocked import causeOSError from mocked import causeOSError
from novelwriter.core.item import NWItem
from tools import C, cmpFiles, buildTestProject, XML_IGNORE from tools import C, cmpFiles, buildTestProject, XML_IGNORE
from PyQt5.QtWidgets import QMessageBox from PyQt5.QtWidgets import QMessageBox
@@ -47,45 +48,45 @@ def testCoreProject_NewRoot(fncPath, tstPaths, mockGUI, mockRnd):
testFile = tstPaths.outDir / "coreProject_NewRoot_nwProject.nwx" testFile = tstPaths.outDir / "coreProject_NewRoot_nwProject.nwx"
compFile = tstPaths.refDir / "coreProject_NewRoot_nwProject.nwx" compFile = tstPaths.refDir / "coreProject_NewRoot_nwProject.nwx"
theProject = NWProject() project = NWProject()
mockRnd.reset() mockRnd.reset()
buildTestProject(theProject, fncPath) buildTestProject(project, fncPath)
assert theProject.newRoot(nwItemClass.NOVEL) == "0000000000010" assert project.newRoot(nwItemClass.NOVEL) == "0000000000010"
assert theProject.newRoot(nwItemClass.PLOT) == "0000000000011" assert project.newRoot(nwItemClass.PLOT) == "0000000000011"
assert theProject.newRoot(nwItemClass.CHARACTER) == "0000000000012" assert project.newRoot(nwItemClass.CHARACTER) == "0000000000012"
assert theProject.newRoot(nwItemClass.WORLD) == "0000000000013" assert project.newRoot(nwItemClass.WORLD) == "0000000000013"
assert theProject.newRoot(nwItemClass.TIMELINE) == "0000000000014" assert project.newRoot(nwItemClass.TIMELINE) == "0000000000014"
assert theProject.newRoot(nwItemClass.OBJECT) == "0000000000015" assert project.newRoot(nwItemClass.OBJECT) == "0000000000015"
assert theProject.newRoot(nwItemClass.CUSTOM) == "0000000000016" assert project.newRoot(nwItemClass.CUSTOM) == "0000000000016"
assert theProject.newRoot(nwItemClass.CUSTOM) == "0000000000017" assert project.newRoot(nwItemClass.CUSTOM) == "0000000000017"
assert theProject.projChanged is True assert project.projChanged is True
assert theProject.saveProject() is True assert project.saveProject() is True
theProject.closeProject() project.closeProject()
copyfile(projFile, testFile) copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE) assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
assert theProject.projChanged is False assert project.projChanged is False
# Delete the new items # Delete the new items
assert theProject.removeItem("0000000000010") is True assert project.removeItem("0000000000010") is True
assert theProject.removeItem("0000000000011") is True assert project.removeItem("0000000000011") is True
assert theProject.removeItem("0000000000012") is True assert project.removeItem("0000000000012") is True
assert theProject.removeItem("0000000000013") is True assert project.removeItem("0000000000013") is True
assert theProject.removeItem("0000000000014") is True assert project.removeItem("0000000000014") is True
assert theProject.removeItem("0000000000015") is True assert project.removeItem("0000000000015") is True
assert theProject.removeItem("0000000000016") is True assert project.removeItem("0000000000016") is True
assert theProject.removeItem("0000000000017") is True assert project.removeItem("0000000000017") is True
assert "0000000000010" not in theProject.tree assert "0000000000010" not in project.tree
assert "0000000000011" not in theProject.tree assert "0000000000011" not in project.tree
assert "0000000000012" not in theProject.tree assert "0000000000012" not in project.tree
assert "0000000000013" not in theProject.tree assert "0000000000013" not in project.tree
assert "0000000000014" not in theProject.tree assert "0000000000014" not in project.tree
assert "0000000000015" not in theProject.tree assert "0000000000015" not in project.tree
assert "0000000000016" not in theProject.tree assert "0000000000016" not in project.tree
assert "0000000000017" not in theProject.tree assert "0000000000017" not in project.tree
# END Test testCoreProject_NewRoot # END Test testCoreProject_NewRoot
@@ -97,68 +98,68 @@ def testCoreProject_NewFileFolder(monkeypatch, fncPath, tstPaths, mockGUI, mockR
testFile = tstPaths.outDir / "coreProject_NewFileFolder_nwProject.nwx" testFile = tstPaths.outDir / "coreProject_NewFileFolder_nwProject.nwx"
compFile = tstPaths.refDir / "coreProject_NewFileFolder_nwProject.nwx" compFile = tstPaths.refDir / "coreProject_NewFileFolder_nwProject.nwx"
theProject = NWProject() project = NWProject()
mockRnd.reset() mockRnd.reset()
buildTestProject(theProject, fncPath) buildTestProject(project, fncPath)
# Invalid call # Invalid call
assert theProject.newFolder("New Folder", "1234567890abc") is None assert project.newFolder("New Folder", "1234567890abc") is None
assert theProject.newFile("New File", "1234567890abc") is None assert project.newFile("New File", "1234567890abc") is None
# Add files properly # Add files properly
assert theProject.newFolder("Stuff", C.hNovelRoot) == "0000000000010" assert project.newFolder("Stuff", C.hNovelRoot) == "0000000000010"
assert theProject.newFile("Hello", "0000000000010") == "0000000000011" assert project.newFile("Hello", "0000000000010") == "0000000000011"
assert theProject.newFile("Jane", C.hCharRoot) == "0000000000012" assert project.newFile("Jane", C.hCharRoot) == "0000000000012"
assert "0000000000010" in theProject.tree assert "0000000000010" in project.tree
assert "0000000000011" in theProject.tree assert "0000000000011" in project.tree
assert "0000000000012" in theProject.tree assert "0000000000012" in project.tree
# Write to file, failed # Write to file, failed
assert theProject.writeNewFile("blabla", 1, True) is False # Not a handle assert project.writeNewFile("blabla", 1, True) is False # Not a handle
assert theProject.writeNewFile("0000000000010", 1, True) is False # Not a file assert project.writeNewFile("0000000000010", 1, True) is False # Not a file
assert theProject.writeNewFile(C.hTitlePage, 1, True) is False # Already has content assert project.writeNewFile(C.hTitlePage, 1, True) is False # Already has content
# Write to file, success # Write to file, success
assert theProject.writeNewFile("0000000000011", 2, True) is True assert project.writeNewFile("0000000000011", 2, True) is True
assert theProject.storage.getDocument("0000000000011").readDocument() == "## Hello\n\n" assert project.storage.getDocument("0000000000011").readDocument() == "## Hello\n\n"
# Write to file with additional text, success # Write to file with additional text, success
assert theProject.writeNewFile("0000000000012", 1, False, "Hi Jane\n\n") is True assert project.writeNewFile("0000000000012", 1, False, "Hi Jane\n\n") is True
assert theProject.storage.getDocument("0000000000012").readDocument() == ( assert project.storage.getDocument("0000000000012").readDocument() == (
"# Jane\n\nHi Jane\n\n" "# Jane\n\nHi Jane\n\n"
) )
# Save, close and check # Save, close and check
assert theProject.projChanged is True assert project.projChanged is True
assert theProject.saveProject() is True assert project.saveProject() is True
copyfile(projFile, testFile) copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE) assert cmpFiles(testFile, compFile, ignoreStart=XML_IGNORE)
assert theProject.projChanged is False assert project.projChanged is False
# Delete new file, but block access # Delete new file, but block access
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr("pathlib.Path.unlink", causeOSError) mp.setattr("pathlib.Path.unlink", causeOSError)
assert theProject.removeItem("0000000000011") is False assert project.removeItem("0000000000011") is False
assert "0000000000011" in theProject.tree assert "0000000000011" in project.tree
# Delete new files and folders # Delete new files and folders
assert (fncPath / "content" / "0000000000012.nwd").exists() assert (fncPath / "content" / "0000000000012.nwd").exists()
assert (fncPath / "content" / "0000000000011.nwd").exists() assert (fncPath / "content" / "0000000000011.nwd").exists()
assert theProject.removeItem("0000000000012") is True assert project.removeItem("0000000000012") is True
assert theProject.removeItem("0000000000011") is True assert project.removeItem("0000000000011") is True
assert theProject.removeItem("0000000000010") is True assert project.removeItem("0000000000010") is True
assert not (fncPath / "content" / "0000000000012.nwd").exists() assert not (fncPath / "content" / "0000000000012.nwd").exists()
assert not (fncPath / "content" / "0000000000011.nwd").exists() assert not (fncPath / "content" / "0000000000011.nwd").exists()
assert "0000000000010" not in theProject.tree assert "0000000000010" not in project.tree
assert "0000000000011" not in theProject.tree assert "0000000000011" not in project.tree
assert "0000000000012" not in theProject.tree assert "0000000000012" not in project.tree
theProject.closeProject() project.closeProject()
# END Test testCoreProject_NewFileFolder # END Test testCoreProject_NewFileFolder
@@ -166,93 +167,85 @@ def testCoreProject_NewFileFolder(monkeypatch, fncPath, tstPaths, mockGUI, mockR
@pytest.mark.core @pytest.mark.core
def testCoreProject_Open(monkeypatch, caplog, mockGUI, fncPath, mockRnd): def testCoreProject_Open(monkeypatch, caplog, mockGUI, fncPath, mockRnd):
"""Test opening a project.""" """Test opening a project."""
theProject = NWProject() project = NWProject()
mockRnd.reset() mockRnd.reset()
buildTestProject(theProject, fncPath) buildTestProject(project, fncPath)
# Initialising the storage class fails # Initialising the storage class fails
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr("novelwriter.core.storage.NWStorage.initProjectStorage", lambda *a, **k: False) mp.setattr("novelwriter.core.storage.NWStorage.initProjectStorage", lambda *a, **k: False)
assert theProject.openProject(fncPath) is False assert project.openProject(fncPath) is False
# Fail on lock file # Fail on lock file
theProject.storage._lockFilePath = fncPath / nwFiles.PROJ_LOCK project.storage._lockFilePath = fncPath / nwFiles.PROJ_LOCK
assert theProject.storage._writeLockFile() is True assert project.storage._writeLockFile() is True
assert theProject.openProject(fncPath) is False assert project.openProject(fncPath) is False
assert isinstance(theProject.lockStatus, list) assert isinstance(project.lockStatus, list)
# Fail to read lockfile (which still opens the project)
with monkeypatch.context() as mp:
mp.setattr("novelwriter.core.storage.NWStorage._readLockFile", lambda *a: ["ERROR"])
caplog.clear()
assert theProject.openProject(fncPath) is True
assert "Failed to check lock file" in caplog.text
theProject.closeProject()
# Force open with lockfile # Force open with lockfile
theProject.storage._lockFilePath = fncPath / nwFiles.PROJ_LOCK project.storage._lockFilePath = fncPath / nwFiles.PROJ_LOCK
assert theProject.storage._writeLockFile() is True assert project.storage._writeLockFile() is True
assert theProject.openProject(fncPath, clearLock=True) is True assert project.openProject(fncPath, clearLock=True) is True
theProject.closeProject() project.closeProject()
assert theProject.lockStatus is None assert project.lockStatus is None
# Fail getting xml reader # Fail getting xml reader
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr("novelwriter.core.storage.NWStorage.getXmlReader", lambda *a: None) mp.setattr("novelwriter.core.storage.NWStorage.getXmlReader", lambda *a: None)
assert theProject.openProject(fncPath) is False assert project.openProject(fncPath) is False
# Not a novelwriter XML file # Not a novelwriter XML file
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(ProjectXMLReader, "read", lambda *a: False) mp.setattr(ProjectXMLReader, "read", lambda *a: False)
mp.setattr(ProjectXMLReader, "state", property(lambda *a: XMLReadState.NOT_NWX_FILE)) mp.setattr(ProjectXMLReader, "state", property(lambda *a: XMLReadState.NOT_NWX_FILE))
assert theProject.openProject(fncPath) is False assert project.openProject(fncPath) is False
assert "Project file does not appear" in SHARED.lastAlert assert "Project file does not appear" in SHARED.lastAlert
# Unknown project file version # Unknown project file version
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(ProjectXMLReader, "read", lambda *a: False) mp.setattr(ProjectXMLReader, "read", lambda *a: False)
mp.setattr(ProjectXMLReader, "state", property(lambda *a: XMLReadState.UNKNOWN_VERSION)) mp.setattr(ProjectXMLReader, "state", property(lambda *a: XMLReadState.UNKNOWN_VERSION))
assert theProject.openProject(fncPath) is False assert project.openProject(fncPath) is False
assert "Unknown or unsupported novelWriter project file" in SHARED.lastAlert assert "Unknown or unsupported novelWriter project file" in SHARED.lastAlert
# Other parse error # Other parse error
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(ProjectXMLReader, "read", lambda *a: False) mp.setattr(ProjectXMLReader, "read", lambda *a: False)
mp.setattr(ProjectXMLReader, "state", property(lambda *a: XMLReadState.CANNOT_PARSE)) mp.setattr(ProjectXMLReader, "state", property(lambda *a: XMLReadState.CANNOT_PARSE))
assert theProject.openProject(fncPath) is False assert project.openProject(fncPath) is False
assert "Failed to parse project xml" in SHARED.lastAlert assert "Failed to parse project xml" in SHARED.lastAlert
# Won't convert legacy file # Won't convert legacy file
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(ProjectXMLReader, "state", property(lambda *a: XMLReadState.WAS_LEGACY)) mp.setattr(ProjectXMLReader, "state", property(lambda *a: XMLReadState.WAS_LEGACY))
mp.setattr(QMessageBox, "result", lambda *a: QMessageBox.No) mp.setattr(QMessageBox, "result", lambda *a: QMessageBox.No)
assert theProject.openProject(fncPath) is False assert project.openProject(fncPath) is False
assert "The file format of your project is about to be" in SHARED.lastAlert assert "The file format of your project is about to be" in SHARED.lastAlert
# Won't open project from newer version # Won't open project from newer version
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(ProjectXMLReader, "hexVersion", property(lambda *a: 0x99999999)) mp.setattr(ProjectXMLReader, "hexVersion", property(lambda *a: 0x99999999))
mp.setattr(QMessageBox, "result", lambda *a: QMessageBox.No) mp.setattr(QMessageBox, "result", lambda *a: QMessageBox.No)
assert theProject.openProject(fncPath) is False assert project.openProject(fncPath) is False
assert "This project was saved by a newer version" in SHARED.lastAlert assert "This project was saved by a newer version" in SHARED.lastAlert
# Fail checking items should still pass # Fail checking items should still pass
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr("novelwriter.core.tree.NWTree.updateItemData", lambda *a: False) mp.setattr("novelwriter.core.tree.NWTree.updateItemData", lambda *a: False)
assert theProject.openProject(fncPath) is True assert project.openProject(fncPath) is True
theProject.closeProject() project.closeProject()
# Trigger an index rebuild # Trigger an index rebuild
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(ProjectXMLReader, "state", property(lambda *a: XMLReadState.WAS_LEGACY)) mp.setattr(ProjectXMLReader, "state", property(lambda *a: XMLReadState.WAS_LEGACY))
mp.setattr("novelwriter.core.index.NWIndex.loadIndex", lambda *a: True) mp.setattr("novelwriter.core.index.NWIndex.loadIndex", lambda *a: True)
theProject.index._indexBroken = True project.index._indexBroken = True
assert theProject.openProject(fncPath) is True assert project.openProject(fncPath) is True
assert "The file format of your project is about to be" in SHARED.lastAlert assert "The file format of your project is about to be" in SHARED.lastAlert
assert theProject.index._indexBroken is False assert project.index._indexBroken is False
theProject.closeProject() project.closeProject()
# END Test testCoreProject_Open # END Test testCoreProject_Open
@@ -260,28 +253,28 @@ def testCoreProject_Open(monkeypatch, caplog, mockGUI, fncPath, mockRnd):
@pytest.mark.core @pytest.mark.core
def testCoreProject_Save(monkeypatch, mockGUI, mockRnd, fncPath): def testCoreProject_Save(monkeypatch, mockGUI, mockRnd, fncPath):
"""Test saving a project.""" """Test saving a project."""
theProject = NWProject() project = NWProject()
# Nothing to save # Nothing to save
assert theProject.saveProject() is False assert project.saveProject() is False
mockRnd.reset() mockRnd.reset()
buildTestProject(theProject, fncPath) buildTestProject(project, fncPath)
# Fail getting xml writer # Fail getting xml writer
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr("novelwriter.core.storage.NWStorage.getXmlWriter", lambda *a: None) mp.setattr("novelwriter.core.storage.NWStorage.getXmlWriter", lambda *a: None)
assert theProject.saveProject() is False assert project.saveProject() is False
# Fail writing # Fail writing
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr(ProjectXMLWriter, "write", lambda *a: False) mp.setattr(ProjectXMLWriter, "write", lambda *a: False)
assert theProject.saveProject() is False assert project.saveProject() is False
# Save with and without autosave # Save with and without autosave
assert theProject.saveProject(autoSave=False) is True assert project.saveProject(autoSave=False) is True
assert theProject.saveProject(autoSave=True) is True assert project.saveProject(autoSave=True) is True
theProject.closeProject() project.closeProject()
# END Test testCoreProject_Save # END Test testCoreProject_Save
@@ -289,13 +282,13 @@ def testCoreProject_Save(monkeypatch, mockGUI, mockRnd, fncPath):
@pytest.mark.core @pytest.mark.core
def testCoreProject_AccessItems(mockGUI, fncPath, mockRnd): def testCoreProject_AccessItems(mockGUI, fncPath, mockRnd):
"""Test helper functions for the project folder.""" """Test helper functions for the project folder."""
theProject = NWProject() project = NWProject()
buildTestProject(theProject, fncPath) buildTestProject(project, fncPath)
# Storage Objects # Storage Objects
assert isinstance(theProject.index, NWIndex) assert isinstance(project.index, NWIndex)
assert isinstance(theProject.tree, NWTree) assert isinstance(project.tree, NWTree)
assert isinstance(theProject.options, OptionState) assert isinstance(project.options, OptionState)
# Move Novel ROOT to after its files # Move Novel ROOT to after its files
oldOrder = [ oldOrder = [
@@ -318,20 +311,22 @@ def testCoreProject_AccessItems(mockGUI, fncPath, mockRnd):
C.hCharRoot, C.hCharRoot,
C.hWorldRoot, C.hWorldRoot,
] ]
assert theProject.tree.handles() == oldOrder assert project.tree.handles() == oldOrder
theProject.setTreeOrder(newOrder) project.setTreeOrder(newOrder)
assert theProject.tree.handles() == newOrder assert project.tree.handles() == newOrder
# Add a non-existing item # Add a non-existing item
theProject.tree._order.append(C.hInvalid) project.tree._order.append(C.hInvalid)
# Add an item with a non-existent parent # Add an item with a non-existent parent
nHandle = theProject.newFile("Test File", C.hChapterDir) nHandle = project.newFile("Test File", C.hChapterDir)
theProject.tree[nHandle].setParent("cba9876543210") nItem = project.tree[nHandle]
assert theProject.tree[nHandle].itemParent == "cba9876543210" assert isinstance(nItem, NWItem)
nItem.setParent("cba9876543210")
assert nItem.itemParent == "cba9876543210"
retOrder = [] retOrder = []
for tItem in theProject.iterProjectItems(): for tItem in project.iterProjectItems():
retOrder.append(tItem.itemHandle) retOrder.append(tItem.itemHandle)
assert retOrder == [ assert retOrder == [
@@ -345,7 +340,7 @@ def testCoreProject_AccessItems(mockGUI, fncPath, mockRnd):
C.hChapterDoc, C.hChapterDoc,
C.hSceneDoc, C.hSceneDoc,
] ]
assert theProject.tree[nHandle].itemParent is None assert nItem.itemParent is None
# END Test testCoreProject_AccessItems # END Test testCoreProject_AccessItems
@@ -353,9 +348,9 @@ def testCoreProject_AccessItems(mockGUI, fncPath, mockRnd):
@pytest.mark.core @pytest.mark.core
def testCoreProject_StatusImport(mockGUI, fncPath, mockRnd): def testCoreProject_StatusImport(mockGUI, fncPath, mockRnd):
"""Test the status and importance flag handling.""" """Test the status and importance flag handling."""
theProject = NWProject() project = NWProject()
mockRnd.reset() mockRnd.reset()
buildTestProject(theProject, fncPath) buildTestProject(project, fncPath)
statusKeys = [C.sNew, C.sNote, C.sDraft, C.sFinished] statusKeys = [C.sNew, C.sNote, C.sDraft, C.sFinished]
importKeys = [C.iNew, C.iMinor, C.iMajor, C.iMain] importKeys = [C.iNew, C.iMinor, C.iMajor, C.iMain]
@@ -363,15 +358,15 @@ def testCoreProject_StatusImport(mockGUI, fncPath, mockRnd):
# Change Status # Change Status
# ============= # =============
theProject.tree[C.hNovelRoot].setStatus(statusKeys[3]) project.tree[C.hNovelRoot].setStatus(statusKeys[3]) # type: ignore
theProject.tree[C.hPlotRoot].setStatus(statusKeys[2]) project.tree[C.hPlotRoot].setStatus(statusKeys[2]) # type: ignore
theProject.tree[C.hCharRoot].setStatus(statusKeys[1]) project.tree[C.hCharRoot].setStatus(statusKeys[1]) # type: ignore
theProject.tree[C.hWorldRoot].setStatus(statusKeys[3]) project.tree[C.hWorldRoot].setStatus(statusKeys[3]) # type: ignore
assert theProject.tree[C.hNovelRoot].itemStatus == statusKeys[3] assert project.tree[C.hNovelRoot].itemStatus == statusKeys[3] # type: ignore
assert theProject.tree[C.hPlotRoot].itemStatus == statusKeys[2] assert project.tree[C.hPlotRoot].itemStatus == statusKeys[2] # type: ignore
assert theProject.tree[C.hCharRoot].itemStatus == statusKeys[1] assert project.tree[C.hCharRoot].itemStatus == statusKeys[1] # type: ignore
assert theProject.tree[C.hWorldRoot].itemStatus == statusKeys[3] assert project.tree[C.hWorldRoot].itemStatus == statusKeys[3] # type: ignore
newList = [ newList = [
{"key": statusKeys[0], "name": "New", "cols": (1, 1, 1)}, {"key": statusKeys[0], "name": "New", "cols": (1, 1, 1)},
@@ -380,36 +375,36 @@ def testCoreProject_StatusImport(mockGUI, fncPath, mockRnd):
{"key": statusKeys[3], "name": "Edited", "cols": (4, 4, 4)}, # Renamed {"key": statusKeys[3], "name": "Edited", "cols": (4, 4, 4)}, # Renamed
{"key": None, "name": "Finished", "cols": (5, 5, 5)}, # New, reused name {"key": None, "name": "Finished", "cols": (5, 5, 5)}, # New, reused name
] ]
assert theProject.setStatusColours(None, None) is False assert project.setStatusColours(None, None) is False # type: ignore
assert theProject.setStatusColours([], []) is False assert project.setStatusColours([], []) is False
assert theProject.setStatusColours(newList, []) is True assert project.setStatusColours(newList, []) is True
assert theProject.data.itemStatus.name(statusKeys[0]) == "New" assert project.data.itemStatus.name(statusKeys[0]) == "New"
assert theProject.data.itemStatus.name(statusKeys[1]) == "Draft" assert project.data.itemStatus.name(statusKeys[1]) == "Draft"
assert theProject.data.itemStatus.name(statusKeys[2]) == "Note" assert project.data.itemStatus.name(statusKeys[2]) == "Note"
assert theProject.data.itemStatus.name(statusKeys[3]) == "Edited" assert project.data.itemStatus.name(statusKeys[3]) == "Edited"
assert theProject.data.itemStatus.cols(statusKeys[0]) == (1, 1, 1) assert project.data.itemStatus.cols(statusKeys[0]) == (1, 1, 1)
assert theProject.data.itemStatus.cols(statusKeys[1]) == (2, 2, 2) assert project.data.itemStatus.cols(statusKeys[1]) == (2, 2, 2)
assert theProject.data.itemStatus.cols(statusKeys[2]) == (3, 3, 3) assert project.data.itemStatus.cols(statusKeys[2]) == (3, 3, 3)
assert theProject.data.itemStatus.cols(statusKeys[3]) == (4, 4, 4) assert project.data.itemStatus.cols(statusKeys[3]) == (4, 4, 4)
# Check the new entry # Check the new entry
lastKey = theProject.data.itemStatus.check("s000010") lastKey = project.data.itemStatus.check("s000010")
assert lastKey == "s000010" assert lastKey == "s000010"
assert theProject.data.itemStatus.name(lastKey) == "Finished" assert project.data.itemStatus.name(lastKey) == "Finished"
assert theProject.data.itemStatus.cols(lastKey) == (5, 5, 5) assert project.data.itemStatus.cols(lastKey) == (5, 5, 5)
# Delete last entry # Delete last entry
assert theProject.setStatusColours([], [lastKey]) is True assert project.setStatusColours([], [lastKey]) is True
assert theProject.data.itemStatus.name(lastKey) == "New" assert project.data.itemStatus.name(lastKey) == "New"
# Change Importance # Change Importance
# ================= # =================
fHandle = theProject.newFile("Jane Doe", C.hCharRoot) fHandle = project.newFile("Jane Doe", C.hCharRoot)
theProject.tree[fHandle].setImport(importKeys[3]) project.tree[fHandle].setImport(importKeys[3]) # type: ignore
assert theProject.tree[fHandle].itemImport == importKeys[3] assert project.tree[fHandle].itemImport == importKeys[3] # type: ignore
newList = [ newList = [
{"key": importKeys[0], "name": "New", "cols": (1, 1, 1)}, {"key": importKeys[0], "name": "New", "cols": (1, 1, 1)},
{"key": importKeys[1], "name": "Minor", "cols": (2, 2, 2)}, {"key": importKeys[1], "name": "Minor", "cols": (2, 2, 2)},
@@ -417,44 +412,44 @@ def testCoreProject_StatusImport(mockGUI, fncPath, mockRnd):
{"key": importKeys[3], "name": "Min", "cols": (4, 4, 4)}, {"key": importKeys[3], "name": "Min", "cols": (4, 4, 4)},
{"key": None, "name": "Max", "cols": (5, 5, 5)}, {"key": None, "name": "Max", "cols": (5, 5, 5)},
] ]
assert theProject.setImportColours(None, None) is False assert project.setImportColours(None, None) is False # type: ignore
assert theProject.setImportColours([], []) is False assert project.setImportColours([], []) is False
assert theProject.setImportColours(newList, []) is True assert project.setImportColours(newList, []) is True
assert theProject.data.itemImport.name(importKeys[0]) == "New" assert project.data.itemImport.name(importKeys[0]) == "New"
assert theProject.data.itemImport.name(importKeys[1]) == "Minor" assert project.data.itemImport.name(importKeys[1]) == "Minor"
assert theProject.data.itemImport.name(importKeys[2]) == "Major" assert project.data.itemImport.name(importKeys[2]) == "Major"
assert theProject.data.itemImport.name(importKeys[3]) == "Min" assert project.data.itemImport.name(importKeys[3]) == "Min"
assert theProject.data.itemImport.cols(importKeys[0]) == (1, 1, 1) assert project.data.itemImport.cols(importKeys[0]) == (1, 1, 1)
assert theProject.data.itemImport.cols(importKeys[1]) == (2, 2, 2) assert project.data.itemImport.cols(importKeys[1]) == (2, 2, 2)
assert theProject.data.itemImport.cols(importKeys[2]) == (3, 3, 3) assert project.data.itemImport.cols(importKeys[2]) == (3, 3, 3)
assert theProject.data.itemImport.cols(importKeys[3]) == (4, 4, 4) assert project.data.itemImport.cols(importKeys[3]) == (4, 4, 4)
# Check the new entry # Check the new entry
lastKey = theProject.data.itemImport.check("i000012") lastKey = project.data.itemImport.check("i000012")
assert lastKey == "i000012" assert lastKey == "i000012"
assert theProject.data.itemImport.name(lastKey) == "Max" assert project.data.itemImport.name(lastKey) == "Max"
assert theProject.data.itemImport.cols(lastKey) == (5, 5, 5) assert project.data.itemImport.cols(lastKey) == (5, 5, 5)
# Delete last entry # Delete last entry
assert theProject.setImportColours([], [lastKey]) is True assert project.setImportColours([], [lastKey]) is True
assert theProject.data.itemImport.name(lastKey) == "New" assert project.data.itemImport.name(lastKey) == "New"
# Delete Status/Import # Delete Status/Import
# ==================== # ====================
theProject.data.itemStatus.resetCounts() project.data.itemStatus.resetCounts()
for key in list(theProject.data.itemStatus.keys()): for key in list(project.data.itemStatus.keys()):
assert theProject.data.itemStatus.remove(key) is True assert project.data.itemStatus.remove(key) is True
theProject.data.itemImport.resetCounts() project.data.itemImport.resetCounts()
for key in list(theProject.data.itemImport.keys()): for key in list(project.data.itemImport.keys()):
assert theProject.data.itemImport.remove(key) is True assert project.data.itemImport.remove(key) is True
assert len(theProject.data.itemStatus) == 0 assert len(project.data.itemStatus) == 0
assert len(theProject.data.itemImport) == 0 assert len(project.data.itemImport) == 0
assert theProject.saveProject() is True assert project.saveProject() is True
theProject.closeProject() project.closeProject()
# END Test testCoreProject_StatusImport # END Test testCoreProject_StatusImport
@@ -462,96 +457,96 @@ def testCoreProject_StatusImport(mockGUI, fncPath, mockRnd):
@pytest.mark.core @pytest.mark.core
def testCoreProject_Methods(monkeypatch, mockGUI, fncPath, mockRnd): def testCoreProject_Methods(monkeypatch, mockGUI, fncPath, mockRnd):
"""Test other project class methods and functions.""" """Test other project class methods and functions."""
theProject = NWProject() project = NWProject()
buildTestProject(theProject, fncPath) buildTestProject(project, fncPath)
# Project Name # Project Name
theProject.data.setName(" A Name ") project.data.setName(" A Name ")
assert theProject.data.name == "A Name" assert project.data.name == "A Name"
# Project Title # Project Title
theProject.data.setTitle(" A Title ") project.data.setTitle(" A Title ")
assert theProject.data.title == "A Title" assert project.data.title == "A Title"
# Project Author # Project Author
theProject.data.setAuthor(" Jane\tDoe ") project.data.setAuthor(" Jane\tDoe ")
assert theProject.data.author == "Jane Doe" assert project.data.author == "Jane Doe"
# Edit Time # Edit Time
theProject.data.setEditTime(1234) project.data.setEditTime(1234)
theProject._session._start = 1600000000 project._session._start = 1600000000
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr("novelwriter.core.project.time", lambda: 1600005600) mp.setattr("novelwriter.core.project.time", lambda: 1600005600)
assert theProject.currentEditTime == 6834 assert project.currentEditTime == 6834
# Trash folder # Trash folder
# Should create on first call, and just returned on later calls # Should create on first call, and just returned on later calls
hTrash = "0000000000010" hTrash = "0000000000010"
assert theProject.tree[hTrash] is None assert project.tree[hTrash] is None
assert theProject.trashFolder() == hTrash assert project.trashFolder() == hTrash
assert theProject.trashFolder() == hTrash assert project.trashFolder() == hTrash
# Spell check # Spell check
theProject.setProjectChanged(False) project.setProjectChanged(False)
theProject.data.setSpellCheck(True) project.data.setSpellCheck(True)
theProject.data.setSpellCheck(False) project.data.setSpellCheck(False)
assert theProject.projChanged is True assert project.projChanged is True
assert theProject.projOpened > 0 assert project.projOpened > 0
# Spell language # Spell language
theProject.setProjectChanged(False) project.setProjectChanged(False)
assert theProject.data.spellLang is None assert project.data.spellLang is None
theProject.data.setSpellLang(None) project.data.setSpellLang(None)
assert theProject.data.spellLang is None assert project.data.spellLang is None
theProject.data.setSpellLang("None") # Should be interpreted as None project.data.setSpellLang("None") # Should be interpreted as None
assert theProject.data.spellLang is None assert project.data.spellLang is None
theProject.data.setSpellLang("en_GB") project.data.setSpellLang("en_GB")
assert theProject.data.spellLang == "en_GB" assert project.data.spellLang == "en_GB"
assert theProject.projChanged is True assert project.projChanged is True
# Project Language # Project Language
theProject.setProjectChanged(False) project.setProjectChanged(False)
theProject.data.setLanguage("en") project.data.setLanguage("en")
theProject.setProjectLang(None) project.setProjectLang(None)
assert theProject.data.language is None assert project.data.language is None
theProject.setProjectLang("en_GB") project.setProjectLang("en_GB")
assert theProject.data.language == "en_GB" assert project.data.language == "en_GB"
# Language Lookup # Language Lookup
assert theProject.localLookup(1) == "One" assert project.localLookup(1) == "One"
assert theProject.localLookup(10) == "Ten" assert project.localLookup(10) == "Ten"
# Set invalid language # Set invalid language
theProject.data.setLanguage("foo") project.data.setLanguage("foo")
theProject._loadProjectLocalisation() project._loadProjectLocalisation()
assert theProject.localLookup(1) == "One" assert project.localLookup(1) == "One"
assert theProject.localLookup(10) == "Ten" assert project.localLookup(10) == "Ten"
# Block reading language data # Block reading language data
theProject.data.setLanguage("en") project.data.setLanguage("en")
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr("builtins.open", causeOSError) mp.setattr("builtins.open", causeOSError)
theProject._loadProjectLocalisation() project._loadProjectLocalisation()
assert theProject.localLookup(1) == "One" assert project.localLookup(1) == "One"
assert theProject.localLookup(10) == "Ten" assert project.localLookup(10) == "Ten"
# Last edited # Last edited
theProject.setProjectChanged(False) project.setProjectChanged(False)
theProject._data.setLastHandle("0123456789abc", "editor") project._data.setLastHandle("0123456789abc", "editor")
assert theProject._data.getLastHandle("editor") == "0123456789abc" assert project._data.getLastHandle("editor") == "0123456789abc"
assert theProject.projChanged assert project.projChanged
# Last viewed # Last viewed
theProject.setProjectChanged(False) project.setProjectChanged(False)
theProject._data.setLastHandle("0123456789abc", "viewer") project._data.setLastHandle("0123456789abc", "viewer")
assert theProject._data.getLastHandle("viewer") == "0123456789abc" assert project._data.getLastHandle("viewer") == "0123456789abc"
assert theProject.projChanged assert project.projChanged
# Autoreplace # Auto Replace
theProject.setProjectChanged(False) project.setProjectChanged(False)
theProject.data.setAutoReplace({"A": "B", "C": "D"}) project.data.setAutoReplace({"A": "B", "C": "D"})
assert theProject.data.autoReplace == {"A": "B", "C": "D"} assert project.data.autoReplace == {"A": "B", "C": "D"}
assert theProject.projChanged assert project.projChanged
# Change project tree order # Change project tree order
oldOrder = [ oldOrder = [
@@ -564,11 +559,11 @@ def testCoreProject_Methods(monkeypatch, mockGUI, fncPath, mockRnd):
"0000000000008", "0000000000009", "000000000000a", "0000000000008", "0000000000009", "000000000000a",
"000000000000e", "000000000000f", "000000000000e", "000000000000f",
] ]
assert theProject.tree.handles() == oldOrder assert project.tree.handles() == oldOrder
theProject.setTreeOrder(newOrder) project.setTreeOrder(newOrder)
assert theProject.tree.handles() == newOrder assert project.tree.handles() == newOrder
theProject.setTreeOrder(oldOrder) project.setTreeOrder(oldOrder)
assert theProject.tree.handles() == oldOrder assert project.tree.handles() == oldOrder
# END Test testCoreProject_Methods # END Test testCoreProject_Methods
@@ -580,38 +575,38 @@ def testCoreProject_Backup(monkeypatch, mockGUI, fncPath, tstPaths):
backup file and checks that the project XML file is identical to backup file and checks that the project XML file is identical to
the original file. the original file.
""" """
theProject = NWProject() project = NWProject()
# No Project # No Project
assert theProject.backupProject(doNotify=False) is False assert project.backupProject(doNotify=False) is False
buildTestProject(theProject, fncPath) buildTestProject(project, fncPath)
# Invalid Settings # Invalid Settings
# ================ # ================
# Missing project name # Missing project name
CONFIG._backupPath = tstPaths.tmpDir CONFIG._backupPath = tstPaths.tmpDir
theProject.data.setName("") project.data.setName("")
assert theProject.backupProject(doNotify=False) is False assert project.backupProject(doNotify=False) is False
# Valid Settings # Valid Settings
# ============== # ==============
CONFIG._backupPath = tstPaths.tmpDir CONFIG._backupPath = tstPaths.tmpDir
theProject.data.setName("Test Minimal") project.data.setName("Test Minimal")
# Can't make folder # Can't make folder
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr("pathlib.Path.mkdir", causeOSError) mp.setattr("pathlib.Path.mkdir", causeOSError)
assert theProject.backupProject(doNotify=False) is False assert project.backupProject(doNotify=False) is False
# Can't write archive # Can't write archive
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr("zipfile.ZipFile.write", causeOSError) mp.setattr("zipfile.ZipFile.write", causeOSError)
assert theProject.backupProject(doNotify=False) is False assert project.backupProject(doNotify=False) is False
# Test correct settings # Test correct settings
assert theProject.backupProject(doNotify=True) is True assert project.backupProject(doNotify=True) is True
theFiles = sorted((tstPaths.tmpDir / "Test Minimal").iterdir()) theFiles = sorted((tstPaths.tmpDir / "Test Minimal").iterdir())
assert len(theFiles) in (1, 2) # Sometimes 2 due to clock tick assert len(theFiles) in (1, 2) # Sometimes 2 due to clock tick