Complete coverage of new project function and remove test code from main app

This commit is contained in:
Veronica K. B. Olsen
2020-09-29 21:49:00 +02:00
parent 0c9849b906
commit 0b7cd71b92
6 changed files with 275 additions and 23 deletions
+1 -1
View File
@@ -938,7 +938,7 @@ class NWProject():
return False
if path.isdir(projPath):
if self.mainConf.showGUI and listdir(self.projPath):
if listdir(self.projPath):
self.theParent.makeAlert((
"New project folder is not empty. "
"Each project requires a dedicated project folder."
+2 -2
View File
@@ -246,7 +246,7 @@ class GuiMain(QMainWindow):
# Project Actions
##
def newProject(self, projData=None, forceNew=False):
def newProject(self, projData=None):
"""Create new project with a few default files and folders.
The variable forceNew is used for testing.
"""
@@ -268,7 +268,7 @@ class GuiMain(QMainWindow):
logger.error("No projData or projPath set")
return False
if path.isfile(path.join(projPath, self.theProject.projFile)) and not forceNew:
if path.isfile(path.join(projPath, self.theProject.projFile)):
self.makeAlert(
"A project already exists in that location. Please choose another folder.",
nwAlert.ERROR
+178
View File
@@ -0,0 +1,178 @@
<?xml version='1.0' encoding='utf-8'?>
<novelWriterXML appVersion="1.0b3" hexVersion="0x010000b3" fileVersion="1.2" timeStamp="2020-09-29 21:12:51">
<project>
<name>Test Custom</name>
<title>Test Novel</title>
<author>Jane Doe</author>
<author>John Doh</author>
<saveCount>1</saveCount>
<autoCount>1</autoCount>
<editTime>0</editTime>
</project>
<settings>
<doBackup>True</doBackup>
<spellCheck>False</spellCheck>
<spellLang>None</spellLang>
<autoOutline>True</autoOutline>
<lastEdited>None</lastEdited>
<lastViewed>None</lastViewed>
<lastWordCount>0</lastWordCount>
<novelWordCount>0</novelWordCount>
<notesWordCount>0</notesWordCount>
<autoReplace/>
<titleFormat>
<title>%title%</title>
<chapter>Chapter %ch%: %title%</chapter>
<unnumbered>%title%</unnumbered>
<scene>* * *</scene>
<section></section>
</titleFormat>
<status>
<entry blue="100" green="100" red="100">New</entry>
<entry blue="0" green="50" red="200">Note</entry>
<entry blue="0" green="150" red="200">Draft</entry>
<entry blue="0" green="200" red="50">Finished</entry>
</status>
<importance>
<entry blue="100" green="100" red="100">New</entry>
<entry blue="0" green="50" red="200">Minor</entry>
<entry blue="0" green="150" red="200">Major</entry>
<entry blue="0" green="200" red="50">Main</entry>
</importance>
</settings>
<content count="14">
<item handle="73475cb40a568" order="None" parent="None">
<name>Novel</name>
<type>ROOT</type>
<class>NOVEL</class>
<status>New</status>
<expanded>False</expanded>
</item>
<item handle="44cb730c42048" order="None" parent="None">
<name>Plot</name>
<type>ROOT</type>
<class>PLOT</class>
<status>New</status>
<expanded>False</expanded>
</item>
<item handle="71ee45a3c0db9" order="None" parent="None">
<name>Characters</name>
<type>ROOT</type>
<class>CHARACTER</class>
<status>New</status>
<expanded>False</expanded>
</item>
<item handle="811786ad1ae74" order="None" parent="None">
<name>Locations</name>
<type>ROOT</type>
<class>WORLD</class>
<status>New</status>
<expanded>False</expanded>
</item>
<item handle="25fc0e7096fc6" order="None" parent="None">
<name>Timeline</name>
<type>ROOT</type>
<class>TIMELINE</class>
<status>New</status>
<expanded>False</expanded>
</item>
<item handle="31489056e0916" order="None" parent="None">
<name>Objects</name>
<type>ROOT</type>
<class>OBJECT</class>
<status>New</status>
<expanded>False</expanded>
</item>
<item handle="98010bd9270f9" order="None" parent="None">
<name>Entity</name>
<type>ROOT</type>
<class>ENTITY</class>
<status>New</status>
<expanded>False</expanded>
</item>
<item handle="0e17daca5f3e1" order="None" parent="73475cb40a568">
<name>Title Page</name>
<type>FILE</type>
<class>NOVEL</class>
<status>New</status>
<exported>True</exported>
<layout>TITLE</layout>
<charCount>0</charCount>
<wordCount>0</wordCount>
<paraCount>0</paraCount>
<cursorPos>0</cursorPos>
</item>
<item handle="1a6562590ef19" order="None" parent="73475cb40a568">
<name>Scene 1</name>
<type>FILE</type>
<class>NOVEL</class>
<status>New</status>
<exported>True</exported>
<layout>SCENE</layout>
<charCount>0</charCount>
<wordCount>0</wordCount>
<paraCount>0</paraCount>
<cursorPos>0</cursorPos>
</item>
<item handle="031b4af5197ec" order="None" parent="73475cb40a568">
<name>Scene 2</name>
<type>FILE</type>
<class>NOVEL</class>
<status>New</status>
<exported>True</exported>
<layout>SCENE</layout>
<charCount>0</charCount>
<wordCount>0</wordCount>
<paraCount>0</paraCount>
<cursorPos>0</cursorPos>
</item>
<item handle="41cfc0d1f2d12" order="None" parent="73475cb40a568">
<name>Scene 3</name>
<type>FILE</type>
<class>NOVEL</class>
<status>New</status>
<exported>True</exported>
<layout>SCENE</layout>
<charCount>0</charCount>
<wordCount>0</wordCount>
<paraCount>0</paraCount>
<cursorPos>0</cursorPos>
</item>
<item handle="2858dcd1057d3" order="None" parent="73475cb40a568">
<name>Scene 4</name>
<type>FILE</type>
<class>NOVEL</class>
<status>New</status>
<exported>True</exported>
<layout>SCENE</layout>
<charCount>0</charCount>
<wordCount>0</wordCount>
<paraCount>0</paraCount>
<cursorPos>0</cursorPos>
</item>
<item handle="2fca346db6561" order="None" parent="73475cb40a568">
<name>Scene 5</name>
<type>FILE</type>
<class>NOVEL</class>
<status>New</status>
<exported>True</exported>
<layout>SCENE</layout>
<charCount>0</charCount>
<wordCount>0</wordCount>
<paraCount>0</paraCount>
<cursorPos>0</cursorPos>
</item>
<item handle="02d20bbd7e394" order="None" parent="73475cb40a568">
<name>Scene 6</name>
<type>FILE</type>
<class>NOVEL</class>
<status>New</status>
<exported>True</exported>
<layout>SCENE</layout>
<charCount>0</charCount>
<wordCount>0</wordCount>
<paraCount>0</paraCount>
<cursorPos>0</cursorPos>
</item>
</content>
</novelWriterXML>
+7 -11
View File
@@ -31,7 +31,7 @@ stepDelay = 20
@pytest.mark.gui
def testProjectSettings(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTempGUI, nwRef, nwTemp):
nwGUI = nw.main(["--testmode", "--config=%s" % nwFuncTemp, "--data=%s" % nwTemp])
nwGUI = nw.main(["--testmode", "--config=%s" % nwTemp, "--data=%s" % nwTemp])
qtbot.addWidget(nwGUI)
nwGUI.show()
qtbot.waitForWindowShown(nwGUI)
@@ -43,7 +43,7 @@ def testProjectSettings(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTempGUI, nwR
# Create new project
nwGUI.theProject.projTree.setSeed(42)
assert nwGUI.newProject({"projPath": nwFuncTemp}, True)
assert nwGUI.newProject({"projPath": nwFuncTemp})
nwGUI.mainConf.backupPath = nwFuncTemp
# Get the dialog object
@@ -141,7 +141,7 @@ def testProjectSettings(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTempGUI, nwR
@pytest.mark.gui
def testItemEditor(qtbot, yesToAll, nwFuncTemp, nwTempGUI, nwRef, nwTemp):
nwGUI = nw.main(["--testmode", "--config=%s" % nwFuncTemp, "--data=%s" % nwTemp])
nwGUI = nw.main(["--testmode", "--config=%s" % nwTemp, "--data=%s" % nwTemp])
qtbot.addWidget(nwGUI)
nwGUI.show()
qtbot.waitForWindowShown(nwGUI)
@@ -149,7 +149,7 @@ def testItemEditor(qtbot, yesToAll, nwFuncTemp, nwTempGUI, nwRef, nwTemp):
# Create new, save, open project
nwGUI.theProject.projTree.setSeed(42)
assert nwGUI.newProject({"projPath": nwFuncTemp}, True)
assert nwGUI.newProject({"projPath": nwFuncTemp})
assert nwGUI.openDocument("0e17daca5f3e1")
itemEdit = GuiItemEditor(nwGUI, nwGUI.theProject, "0e17daca5f3e1")
@@ -200,7 +200,7 @@ def testItemEditor(qtbot, yesToAll, nwFuncTemp, nwTempGUI, nwRef, nwTemp):
@pytest.mark.gui
def testWritingStatsExport(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTemp):
nwGUI = nw.main(["--testmode", "--config=%s" % nwFuncTemp, "--data=%s" % nwTemp])
nwGUI = nw.main(["--testmode", "--config=%s" % nwTemp, "--data=%s" % nwTemp])
qtbot.addWidget(nwGUI)
nwGUI.show()
qtbot.waitForWindowShown(nwGUI)
@@ -208,7 +208,7 @@ def testWritingStatsExport(qtbot, monkeypatch, yesToAll, nwFuncTemp, nwTemp):
# Create new, save, close project
nwGUI.theProject.projTree.setSeed(42)
assert nwGUI.newProject({"projPath": nwFuncTemp}, True)
assert nwGUI.newProject({"projPath": nwFuncTemp})
assert nwGUI.saveProject()
assert nwGUI.closeProject()
qtbot.wait(stepDelay)
@@ -703,7 +703,7 @@ def testNewProjectWizard(qtbot, monkeypatch, yesToAll, nwMinimal, nwTemp):
ProjWizardCustomPage, ProjWizardFinalPage
)
nwGUI = nw.main(["--testmode", "--config=%s" % nwMinimal, "--data=%s" % nwTemp])
nwGUI = nw.main(["--testmode", "--config=%s" % nwTemp, "--data=%s" % nwTemp])
qtbot.addWidget(nwGUI)
nwGUI.show()
qtbot.waitForWindowShown(nwGUI)
@@ -730,10 +730,6 @@ def testNewProjectWizard(qtbot, monkeypatch, yesToAll, nwMinimal, nwTemp):
monkeypatch.setattr(nwGUI, "showNewProjectDialog", lambda *args: {"projPath": nwMinimal})
assert not nwGUI.newProject()
# Force overwrite
monkeypatch.setattr(nwGUI, "showNewProjectDialog", lambda *args: {"projPath": nwMinimal})
assert nwGUI.newProject(forceNew=True)
nwGUI.closeMain()
nwGUI.close()
+4 -4
View File
@@ -91,7 +91,7 @@ def testLaunch(qtbot, nwFuncTemp, nwTemp):
@pytest.mark.gui
def testDocEditor(qtbot, yesToAll, nwFuncTemp, nwTempGUI, nwRef, nwTemp):
nwGUI = nw.main(["--testmode", "--config=%s" % nwFuncTemp, "--data=%s" % nwTemp])
nwGUI = nw.main(["--testmode", "--config=%s" % nwTemp, "--data=%s" % nwTemp])
qtbot.addWidget(nwGUI)
nwGUI.show()
qtbot.waitForWindowShown(nwGUI)
@@ -99,7 +99,7 @@ def testDocEditor(qtbot, yesToAll, nwFuncTemp, nwTempGUI, nwRef, nwTemp):
# Create new, save, close project
nwGUI.theProject.projTree.setSeed(42)
assert nwGUI.newProject({"projPath": nwFuncTemp}, True)
assert nwGUI.newProject({"projPath": nwFuncTemp})
assert nwGUI.saveProject()
assert nwGUI.closeProject()
@@ -1003,14 +1003,14 @@ def testContextMenu(qtbot, yesToAll, nwLipsum, nwTemp):
@pytest.mark.gui
def testInsertMenu(qtbot, monkeypatch, nwFuncTemp, nwTemp):
nwGUI = nw.main(["--testmode", "--config=%s" % nwFuncTemp, "--data=%s" % nwTemp])
nwGUI = nw.main(["--testmode", "--config=%s" % nwTemp, "--data=%s" % nwTemp])
qtbot.addWidget(nwGUI)
nwGUI.show()
qtbot.waitForWindowShown(nwGUI)
qtbot.wait(stepDelay)
nwGUI.theProject.projTree.setSeed(42)
assert nwGUI.newProject({"projPath": nwFuncTemp}, True)
assert nwGUI.newProject({"projPath": nwFuncTemp})
assert nwGUI.treeView._getTreeItem("0e17daca5f3e1") is not None
+83 -5
View File
@@ -16,6 +16,8 @@ from nw.constants import nwItemClass, nwItemType, nwItemLayout, nwFiles
@pytest.mark.project
def testProjectNewOpenSave(nwFuncTemp, nwTempProj, nwRef, nwTemp, nwDummy):
"""Test that a basic project can be created, and opened and saved.
"""
projFile = path.join(nwFuncTemp, "nwProject.nwx")
testFile = path.join(nwTempProj, "1_nwProject.nwx")
refFile = path.join(nwRef, "proj", "1_nwProject.nwx")
@@ -23,11 +25,18 @@ def testProjectNewOpenSave(nwFuncTemp, nwTempProj, nwRef, nwTemp, nwDummy):
theProject = NWProject(nwDummy)
theProject.projTree.setSeed(42)
# Setting no data should fail
assert not theProject.newProject({})
# Try again with a proper path
assert theProject.newProject({"projPath": nwFuncTemp})
assert theProject.setProjectPath(nwFuncTemp)
assert theProject.saveProject()
assert theProject.closeProject()
# Creating the project once more should fail
assert not theProject.newProject({"projPath": nwFuncTemp})
# Check the new project
copyfile(projFile, testFile)
assert cmpFiles(testFile, refFile, [2, 6, 7, 8])
@@ -53,6 +62,8 @@ def testProjectNewOpenSave(nwFuncTemp, nwTempProj, nwRef, nwTemp, nwDummy):
@pytest.mark.project
def testProjectNewRoot(nwFuncTemp, nwTempProj, nwRef, nwDummy):
"""Check that new root folders can be added to the project.
"""
projFile = path.join(nwFuncTemp, "nwProject.nwx")
testFile = path.join(nwTempProj, "2_nwProject.nwx")
refFile = path.join(nwRef, "proj", "2_nwProject.nwx")
@@ -85,6 +96,8 @@ def testProjectNewRoot(nwFuncTemp, nwTempProj, nwRef, nwDummy):
@pytest.mark.project
def testProjectNewFile(nwFuncTemp, nwTempProj, nwRef, nwDummy):
"""Check that new files can be added to the project.
"""
projFile = path.join(nwFuncTemp, "nwProject.nwx")
testFile = path.join(nwTempProj, "3_nwProject.nwx")
refFile = path.join(nwRef, "proj", "3_nwProject.nwx")
@@ -109,8 +122,10 @@ def testProjectNewFile(nwFuncTemp, nwTempProj, nwRef, nwDummy):
assert not theProject.projChanged
@pytest.mark.project
def testProjectNewCustom(nwFuncTemp, nwTempProj, nwRef, nwDummy):
def testProjectNewCustomA(nwFuncTemp, nwTempProj, nwRef, nwDummy):
"""Create a new project from a project wizard dictionary.
Custom type with chapters and scenes.
"""
projFile = path.join(nwFuncTemp, "nwProject.nwx")
testFile = path.join(nwTempProj, "4_nwProject.nwx")
refFile = path.join(nwRef, "proj", "4_nwProject.nwx")
@@ -145,8 +160,50 @@ def testProjectNewCustom(nwFuncTemp, nwTempProj, nwRef, nwDummy):
copyfile(projFile, testFile)
assert cmpFiles(testFile, refFile, [2, 6, 7, 8])
@pytest.mark.project
def testProjectNewCustomB(nwFuncTemp, nwTempProj, nwRef, nwDummy):
"""Create a new project from a project wizard dictionary.
Custom type without chapters, but with scenes.
"""
projFile = path.join(nwFuncTemp, "nwProject.nwx")
testFile = path.join(nwTempProj, "5_nwProject.nwx")
refFile = path.join(nwRef, "proj", "5_nwProject.nwx")
projData = {
"projName": "Test Custom",
"projTitle": "Test Novel",
"projAuthors": "Jane Doe\nJohn Doh\n",
"projPath": nwFuncTemp,
"popSample": False,
"popMinimal": False,
"popCustom": True,
"addRoots": [
nwItemClass.PLOT,
nwItemClass.CHARACTER,
nwItemClass.WORLD,
nwItemClass.TIMELINE,
nwItemClass.OBJECT,
nwItemClass.ENTITY,
],
"numChapters": 0,
"numScenes": 6,
"chFolders": True,
}
theProject = NWProject(nwDummy)
theProject.projTree.setSeed(42)
assert theProject.newProject(projData)
assert theProject.saveProject()
assert theProject.closeProject()
copyfile(projFile, testFile)
assert cmpFiles(testFile, refFile, [2, 6, 7, 8])
@pytest.mark.project
def testProjectNewSample(nwFuncTemp, nwRef, nwConf, nwDummy):
"""Check that we can create a new project can be created from the
provided sample project.
"""
projData = {
"projName": "Test Sample",
"projTitle": "Test Novel",
@@ -168,6 +225,8 @@ def testProjectNewSample(nwFuncTemp, nwRef, nwConf, nwDummy):
@pytest.mark.project
def testDocMeta(nwDummy, nwLipsum):
"""Check that the document meta data string is parsed correctly.
"""
theProject = NWProject(nwDummy)
theProject.projTree.setSeed(42)
assert theProject.openProject(nwLipsum)
@@ -243,6 +302,10 @@ def testSpellSimple(nwTemp, nwConf):
@pytest.mark.project
def testProjectOptions(nwDummy, nwLipsum):
"""Test the class that holds all the GUI state user options that are
tied to the current open project. Non-project related GUI options
are handled by the Config class.
"""
theProject = NWProject(nwDummy)
assert theProject.projMeta is None
@@ -301,7 +364,12 @@ def testProjectOptions(nwDummy, nwLipsum):
assert theOpts.getFloat("GuiWritingStats", "winWidth", False) is False
@pytest.mark.project
def testOrphanedFiles(nwDummy, nwLipsum):
def testProjectOrphanedFiles(nwDummy, nwLipsum):
"""Check that files in the content folder that are not tracked in
the project XML file are handled correctly by the orphaned files
function. It should also restore as much meta data as possible from
the meta line at the top of the document file.
"""
theProject = NWProject(nwDummy)
assert theProject.openProject(nwLipsum)
assert theProject.projTree["636b6aa9b697b"] is None
@@ -362,7 +430,12 @@ def testOrphanedFiles(nwDummy, nwLipsum):
assert theProject.closeProject()
@pytest.mark.project
def testOldProject(nwDummy, nwOldProj):
def testProjectOldFormat(nwDummy, nwOldProj):
"""Test that a project folder structure of version 1.0 can be
converted to the latest folder structure. Version 1.0 split the
documents into 'data_0' ... 'data_f' folders, which are now all
contained in a single 'content' folder.
"""
theProject = NWProject(nwDummy)
theProject.mainConf.showGUI = False
@@ -448,7 +521,12 @@ def testOldProject(nwDummy, nwOldProj):
assert path.isfile(path.join(nwOldProj, "ToC.txt"))
@pytest.mark.project
def testBackupProject(nwDummy, nwMinimal, nwTemp):
def testProjectBackup(nwDummy, nwMinimal, nwTemp):
"""Test the automated backup feature of the project class. The test
creates a backup of the Minimal test project, and then unzips the
backupd file and checks that the project XML file is identical to
the original file.
"""
theProject = NWProject(nwDummy)
assert theProject.openProject(nwMinimal)