From 0b7cd71b9252dea4a652c7e063b1d324b8464caa Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Tue, 29 Sep 2020 21:49:00 +0200 Subject: [PATCH] Complete coverage of new project function and remove test code from main app --- nw/core/project.py | 2 +- nw/guimain.py | 4 +- tests/reference/proj/5_nwProject.nwx | 178 +++++++++++++++++++++++++++ tests/test_dialogs.py | 18 ++- tests/test_gui.py | 8 +- tests/test_project.py | 88 ++++++++++++- 6 files changed, 275 insertions(+), 23 deletions(-) create mode 100644 tests/reference/proj/5_nwProject.nwx diff --git a/nw/core/project.py b/nw/core/project.py index f914bfb5..72ac278d 100644 --- a/nw/core/project.py +++ b/nw/core/project.py @@ -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." diff --git a/nw/guimain.py b/nw/guimain.py index 859145df..44b99d56 100644 --- a/nw/guimain.py +++ b/nw/guimain.py @@ -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 diff --git a/tests/reference/proj/5_nwProject.nwx b/tests/reference/proj/5_nwProject.nwx new file mode 100644 index 00000000..9935736e --- /dev/null +++ b/tests/reference/proj/5_nwProject.nwx @@ -0,0 +1,178 @@ + + + + Test Custom + Test Novel + Jane Doe + John Doh + 1 + 1 + 0 + + + True + False + None + True + None + None + 0 + 0 + 0 + + + %title% + Chapter %ch%: %title% + %title% + * * * +
+
+ + New + Note + Draft + Finished + + + New + Minor + Major + Main + +
+ + + Novel + ROOT + NOVEL + New + False + + + Plot + ROOT + PLOT + New + False + + + Characters + ROOT + CHARACTER + New + False + + + Locations + ROOT + WORLD + New + False + + + Timeline + ROOT + TIMELINE + New + False + + + Objects + ROOT + OBJECT + New + False + + + Entity + ROOT + ENTITY + New + False + + + Title Page + FILE + NOVEL + New + True + TITLE + 0 + 0 + 0 + 0 + + + Scene 1 + FILE + NOVEL + New + True + SCENE + 0 + 0 + 0 + 0 + + + Scene 2 + FILE + NOVEL + New + True + SCENE + 0 + 0 + 0 + 0 + + + Scene 3 + FILE + NOVEL + New + True + SCENE + 0 + 0 + 0 + 0 + + + Scene 4 + FILE + NOVEL + New + True + SCENE + 0 + 0 + 0 + 0 + + + Scene 5 + FILE + NOVEL + New + True + SCENE + 0 + 0 + 0 + 0 + + + Scene 6 + FILE + NOVEL + New + True + SCENE + 0 + 0 + 0 + 0 + + +
diff --git a/tests/test_dialogs.py b/tests/test_dialogs.py index 40970d2f..3de991dc 100644 --- a/tests/test_dialogs.py +++ b/tests/test_dialogs.py @@ -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() diff --git a/tests/test_gui.py b/tests/test_gui.py index 85c09982..c3d02811 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -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 diff --git a/tests/test_project.py b/tests/test_project.py index 8656ad89..fc980d96 100644 --- a/tests/test_project.py +++ b/tests/test_project.py @@ -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)