diff --git a/tests/test_tools/test_tools_projwizard.py b/tests/test_tools/test_tools_projwizard.py index 53f1cc79..f82e45a6 100644 --- a/tests/test_tools/test_tools_projwizard.py +++ b/tests/test_tools/test_tools_projwizard.py @@ -21,12 +21,11 @@ along with this program. If not, see . import pytest import os -import sys from tools import getGuiItem from PyQt5.QtCore import Qt -from PyQt5.QtWidgets import QFileDialog, QWizard, QMessageBox +from PyQt5.QtWidgets import QFileDialog, QWizard, QMessageBox, QDialog from novelwriter.enum import nwItemClass from novelwriter.tools.projwizard import ( @@ -40,9 +39,9 @@ stepDelay = 20 @pytest.mark.gui -@pytest.mark.skipif(sys.platform.startswith("darwin"), reason="Not running on Darwin") -def testToolProjectWizard_Main(qtbot, monkeypatch, nwGUI, nwMinimal): - """Test the new project wizard. +# @pytest.mark.skipif(sys.platform.startswith("darwin"), reason="Not running on Darwin") +def testToolProjectWizard_Handling(qtbot, monkeypatch, nwGUI, nwMinimal): + """Test the launch of the project wizard. Disabled for macOS because the test segfaults on QWizard.show() """ # Block message box @@ -55,173 +54,212 @@ def testToolProjectWizard_Main(qtbot, monkeypatch, nwGUI, nwMinimal): # New with a project open should cause an error assert nwGUI.openProject(nwMinimal) - assert not nwGUI.newProject() + with monkeypatch.context() as mp: + mp.setattr(nwGUI, "closeProject", lambda *a: False) + assert nwGUI.newProject() is False # Close project, but call with invalid path assert nwGUI.closeProject() with monkeypatch.context() as mp: mp.setattr(nwGUI, "showNewProjectDialog", lambda *a: None) - assert not nwGUI.newProject() + assert nwGUI.newProject() is False # Now, with an empty dictionary mp.setattr(nwGUI, "showNewProjectDialog", lambda *a: {}) - assert not nwGUI.newProject() + assert nwGUI.newProject() is False # Now, with a non-empty folder mp.setattr(nwGUI, "showNewProjectDialog", lambda *a: {"projPath": nwMinimal}) - assert not nwGUI.newProject() + assert nwGUI.newProject() is False ## - # Test the Wizard + # Test the Wizard Launching ## - monkeypatch.setattr(GuiProjectWizard, "exec_", lambda *a: None) nwGUI.mainConf.lastPath = " " + monkeypatch.setattr(GuiProjectWizard, "exec_", lambda *a: None) - nwGUI.closeProject() - nwGUI.showNewProjectDialog() + result = nwGUI.showNewProjectDialog() qtbot.waitUntil(lambda: getGuiItem("GuiProjectWizard") is not None, timeout=1000) nwWiz = getGuiItem("GuiProjectWizard") assert isinstance(nwWiz, GuiProjectWizard) nwWiz.show() + qtbot.wait(stepDelay) + qtbot.mouseClick(nwWiz.button(QWizard.CancelButton), Qt.LeftButton) + assert result is None - for wStep in range(4): - # This does not actually create the project, it just generates the - # dictionary that defines it. - - # Intro Page - introPage = nwWiz.currentPage() - assert isinstance(introPage, ProjWizardIntroPage) - assert not nwWiz.button(QWizard.NextButton).isEnabled() - - qtbot.wait(stepDelay) - for c in ("Test Minimal %d" % wStep): - qtbot.keyClick(introPage.projName, c, delay=typeDelay) - - qtbot.wait(stepDelay) - for c in "Minimal Novel": - qtbot.keyClick(introPage.projTitle, c, delay=typeDelay) - - qtbot.wait(stepDelay) - for c in "Jane Doe": - qtbot.keyClick(introPage.projAuthors, c, delay=typeDelay) - - # Setting projName should activate the button - assert nwWiz.button(QWizard.NextButton).isEnabled() - - qtbot.wait(stepDelay) - qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton) - - # Folder Page - storagePage = nwWiz.currentPage() - assert isinstance(storagePage, ProjWizardFolderPage) - assert not nwWiz.button(QWizard.NextButton).isEnabled() - - if wStep == 0: - # Check invalid path first, the first time we reach here - monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *a, **kw: "") - qtbot.wait(stepDelay) - qtbot.mouseClick(storagePage.browseButton, Qt.LeftButton, delay=100) - assert storagePage.projPath.text() == "" - - # Then, we always return nwMinimal as path - monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *a, **kw: nwMinimal) - - qtbot.wait(stepDelay) - qtbot.mouseClick(storagePage.browseButton, Qt.LeftButton, delay=100) - projPath = os.path.join(nwMinimal, "Test Minimal %d" % wStep) - assert storagePage.projPath.text() == projPath - - # Setting projPath should activate the button - assert nwWiz.button(QWizard.NextButton).isEnabled() - - qtbot.wait(stepDelay) - qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton) - - # Populate Page - popPage = nwWiz.currentPage() - assert isinstance(popPage, ProjWizardPopulatePage) - assert nwWiz.button(QWizard.NextButton).isEnabled() - - qtbot.wait(stepDelay) - if wStep == 0: - popPage.popMinimal.setChecked(True) - elif wStep == 1: - popPage.popCustom.setChecked(True) - elif wStep == 2: - popPage.popCustom.setChecked(True) - elif wStep == 3: - popPage.popSample.setChecked(True) - - qtbot.wait(stepDelay) - qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton) - - # Custom Page - if wStep == 1 or wStep == 2: - customPage = nwWiz.currentPage() - assert isinstance(customPage, ProjWizardCustomPage) - assert nwWiz.button(QWizard.NextButton).isEnabled() - - customPage.addPlot.setChecked(True) - customPage.addChar.setChecked(True) - customPage.addWorld.setChecked(True) - customPage.addTime.setChecked(True) - customPage.addObject.setChecked(True) - customPage.addEntity.setChecked(True) - - if wStep == 2: - customPage.numChapters.setValue(0) - customPage.numScenes.setValue(10) - customPage.chFolders.setChecked(False) - - qtbot.wait(stepDelay) - qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton) - - # Final Page - finalPage = nwWiz.currentPage() - assert isinstance(finalPage, ProjWizardFinalPage) - assert nwWiz.button(QWizard.FinishButton).isEnabled() # But we don't click it - - # Check Data - projData = nwGUI._assembleProjectWizardData(nwWiz) - assert projData["projName"] == "Test Minimal %d" % wStep - assert projData["projTitle"] == "Minimal Novel" - assert projData["projAuthors"] == "Jane Doe" - assert projData["projPath"] == projPath - assert projData["popMinimal"] == (wStep == 0) - assert projData["popCustom"] == (wStep == 1 or wStep == 2) - assert projData["popSample"] == (wStep == 3) - if wStep == 1 or wStep == 2: - assert projData["addRoots"] == [ - nwItemClass.PLOT, - nwItemClass.CHARACTER, - nwItemClass.WORLD, - nwItemClass.TIMELINE, - nwItemClass.OBJECT, - nwItemClass.ENTITY, - ] - if wStep == 1: - assert projData["numChapters"] == 5 - assert projData["numScenes"] == 5 - assert projData["chFolders"] - else: - assert projData["numChapters"] == 0 - assert projData["numScenes"] == 10 - assert not projData["chFolders"] - else: - assert projData["addRoots"] == [] - assert projData["numChapters"] == 0 - assert projData["numScenes"] == 0 - assert not projData["chFolders"] - - # Restart the wizard for next iteration - nwWiz.restart() + with monkeypatch.context() as mp: + mp.setattr(GuiProjectWizard, "result", lambda *a: QDialog.Accepted) + result = nwGUI.showNewProjectDialog() + nwWiz.button(QWizard.CancelButton).click() + assert isinstance(result, dict) nwWiz.reject() nwWiz.close() # qtbot.stopForInteraction() -# END Test testToolProjectWizard_Main +# END Test testToolProjectWizard_Handling + + +@pytest.mark.gui +@pytest.mark.parametrize("prjType", ["minimal", "custom1", "custom2", "sample"]) +def testToolProjectWizard_Run(qtbot, monkeypatch, nwGUI, fncDir, prjType): + """Test the new project wizard with a set of selection scenarios. + """ + monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes) + monkeypatch.setattr(QMessageBox, "critical", lambda *a: QMessageBox.Yes) + monkeypatch.setattr(GuiProjectWizard, "exec_", lambda *a: None) + + nwGUI.mainConf.lastPath = " " + nwWiz = GuiProjectWizard(nwGUI) + nwWiz.show() + qtbot.wait(stepDelay) + + # Intro Page + # ========== + + introPage = nwWiz.currentPage() + assert isinstance(introPage, ProjWizardIntroPage) + assert not nwWiz.button(QWizard.NextButton).isEnabled() + + introPage.projName.setText("Test Wizard") + introPage.projTitle.setText("My Novel") + introPage.projAuthors.setPlainText("Jane Doe") + + # Setting projName should activate the button + assert nwWiz.button(QWizard.NextButton).isEnabled() + + qtbot.wait(stepDelay) + qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton) + + # Folder Page + # =========== + + storagePage = nwWiz.currentPage() + assert isinstance(storagePage, ProjWizardFolderPage) + assert not nwWiz.button(QWizard.NextButton).isEnabled() + assert storagePage.errLabel.text() == "" + + # Set an invalid path + storagePage.projPath.setText(os.path.join(fncDir, "not", "a", "path")) + assert not nwWiz.button(QWizard.NextButton).isEnabled() + assert storagePage.errLabel.text().startswith("Error") + + # Set an existing path + storagePage.projPath.setText(fncDir) + assert not nwWiz.button(QWizard.NextButton).isEnabled() + assert storagePage.errLabel.text().startswith("Error") + + # Return a non-result from browse + with monkeypatch.context() as mp: + mp.setattr(QFileDialog, "getExistingDirectory", lambda *a, **k: "") + qtbot.mouseClick(storagePage.browseButton, Qt.LeftButton, delay=100) + assert storagePage.errLabel.text() == "" + + # Let the browse feature handle it + projPath = os.path.join(fncDir, "Test Wizard") + with monkeypatch.context() as mp: + mp.setattr(QFileDialog, "getExistingDirectory", lambda *a, **k: fncDir) + qtbot.mouseClick(storagePage.browseButton, Qt.LeftButton, delay=100) + + assert storagePage.projPath.text() == projPath + assert storagePage.errLabel.text() == "" + + # Setting projPath should activate the button + assert nwWiz.button(QWizard.NextButton).isEnabled() + + qtbot.wait(stepDelay) + qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton) + + # Populate Page + # ============= + + popPage = nwWiz.currentPage() + assert isinstance(popPage, ProjWizardPopulatePage) + assert nwWiz.button(QWizard.NextButton).isEnabled() + + qtbot.wait(stepDelay) + if prjType.startswith("minimal"): + popPage.popMinimal.setChecked(True) + elif prjType.startswith("custom"): + popPage.popCustom.setChecked(True) + elif prjType.startswith("sample"): + popPage.popSample.setChecked(True) + + qtbot.wait(stepDelay) + qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton) + + # Custom Page + # =========== + if prjType.startswith("custom"): + + customPage = nwWiz.currentPage() + assert isinstance(customPage, ProjWizardCustomPage) + assert nwWiz.button(QWizard.NextButton).isEnabled() + + customPage.addPlot.setChecked(True) + customPage.addChar.setChecked(True) + customPage.addWorld.setChecked(True) + customPage.addTime.setChecked(True) + customPage.addObject.setChecked(True) + customPage.addEntity.setChecked(True) + + if prjType == "custom2": + customPage.numChapters.setValue(0) + customPage.numScenes.setValue(10) + customPage.chFolders.setChecked(False) + + qtbot.wait(stepDelay) + qtbot.mouseClick(nwWiz.button(QWizard.NextButton), Qt.LeftButton) + + # Final Page + # ========== + + finalPage = nwWiz.currentPage() + assert isinstance(finalPage, ProjWizardFinalPage) + assert nwWiz.button(QWizard.FinishButton).isEnabled() # But we don't click it + + # Check Data + # ========== + + projData = nwGUI._assembleProjectWizardData(nwWiz) + assert projData["projName"] == "Test Wizard" + assert projData["projTitle"] == "My Novel" + assert projData["projAuthors"] == "Jane Doe" + assert projData["projPath"] == projPath + assert projData["popMinimal"] == prjType.startswith("minimal") + assert projData["popCustom"] == prjType.startswith("custom") + assert projData["popSample"] == prjType.startswith("sample") + if prjType.startswith("custom"): + assert projData["addRoots"] == [ + nwItemClass.PLOT, + nwItemClass.CHARACTER, + nwItemClass.WORLD, + nwItemClass.TIMELINE, + nwItemClass.OBJECT, + nwItemClass.ENTITY, + ] + if prjType == "custom1": + assert projData["numChapters"] == 5 + assert projData["numScenes"] == 5 + assert projData["chFolders"] + else: + assert projData["numChapters"] == 0 + assert projData["numScenes"] == 10 + assert not projData["chFolders"] + else: + assert projData["addRoots"] == [] + assert projData["numChapters"] == 0 + assert projData["numScenes"] == 0 + assert not projData["chFolders"] + + # Cleanup + nwWiz.reject() + nwWiz.close() + + # qtbot.stopForInteraction() + +# END Test testToolProjectWizard_Run