Reorganise remainin GUI tests, but no improved coverage

This commit is contained in:
Veronica K. B. Olsen
2020-12-12 18:58:05 +01:00
parent 325c8dc68d
commit 585287b487
7 changed files with 575 additions and 549 deletions
+14 -1
View File
@@ -52,7 +52,9 @@ pytest-3 -v -m core
Available markers are:
* '`core`' for unit tests covering the classes in the `nw/core` folder
* `base` for unit tests covering the non-gui classes of the `bw` folder..
* `core` for unit tests covering the classes in the `nw/core` folder.
* `gui` for unit and integrations tests covering the classes in the `nw/gui` folder.
## Tests
@@ -78,6 +80,17 @@ The commands for the respective test categories are listed below.
| Unit | Tokenizer class | nw/core/tokenizer.py | `-m core` | `-k testCoreToken` |
| Integration | About Dialogs | nw/gui/about.py | `-m gui` | `-k testGuiAbout` |
| Integration | Build Novel Project Tool | nw/gui/build.py | `-m gui` | `-k testGuiBuild` |
| Integration | Document Editor Widget | nw/gui/doceditor.py | `-m gui` | `-k testGuiEditor` |
| Integration | Document Viewer Widget | nw/gui/docviewer.py | `-m gui` | `-k testGuiViewer` |
| Integration | Item Editor Dialog | nw/gui/itemeditor.py | `-m gui` | `-k testGuiItemEditor` |
| Integration | Menu Widgets | nw/gui/mainmenu.py | `-m gui` | `-k testGuiMenu` |
| Integration | Merge Tool | nw/gui/docmerge.py | `-m gui` | `-k testGuiMergeSplit` |
| Integration | Outline Widget | nw/gui/outline.py | `-m gui` | `-k testGuiOutline` |
| Integration | Preferences Dialog | nw/gui/preferences.py | `-m gui` | `-k testGuiPreferences` |
| Integration | Project Load Dialog | nw/gui/projload.py | `-m gui` | `-k testGuiProjLoad` |
| Integration | Project Settings Dialog | nw/gui/projsettings.py | `-m gui` | `-k testGuiProjSettings` |
| Integration | Project Tree Widget | nw/gui/projtree.py | `-m gui` | `-k testGuiProjTree` |
| Integration | Theme/Icon Classes | nw/gui/theme.py | `-m gui` | `-k testGuiTheme` |
| Integration | Split Tool | nw/gui/docsplit.py | `-m gui` | `-k testGuiMergeSplit` |
| Integration | Writing Stats Dialog | nw/gui/writingstats.py | `-m gui` | `-k testGuiWritingStats` |
| Integration | Various Dialogs | N/A | `-m gui` | `-k testGuiDialogs` |
-41
View File
@@ -9,8 +9,6 @@ import os
from dummy import DummyMain
from PyQt5.QtWidgets import QMessageBox
sys.path.insert(1, os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)))
import nw # noqa: E402
@@ -184,42 +182,3 @@ def nwOldProj(tmpDir):
if os.path.isdir(oldProjDir):
shutil.rmtree(oldProjDir)
return
##
# Monkey Patch Dialogs
##
@pytest.fixture(scope="function")
def yesToAll(monkeypatch):
"""Make the message boxes/questions always say yes.
"""
monkeypatch.setattr(
QMessageBox, "question", lambda *args, **kwargs: QMessageBox.Yes
)
monkeypatch.setattr(
QMessageBox, "information", lambda *args, **kwargs: QMessageBox.Yes
)
monkeypatch.setattr(
QMessageBox, "warning", lambda *args, **kwargs: QMessageBox.Yes
)
monkeypatch.setattr(
QMessageBox, "critical", lambda *args, **kwargs: QMessageBox.Yes
)
yield
monkeypatch.undo()
return
# =============================================================================================== #
##
# Temporary Test Folders
##
@pytest.fixture(scope="session")
def nwTempGUI(tmpDir):
"""A temporary folder for GUI tests.
"""
guiDir = os.path.join(tmpDir, "gui")
if not os.path.isdir(guiDir):
os.mkdir(guiDir)
return guiDir
@@ -60,7 +60,7 @@ backuponclose = True
askbeforebackup = True
[State]
showrefpanel = False
showrefpanel = True
viewcomments = True
viewsynopsis = True
searchcase = False
+14 -506
View File
@@ -2,511 +2,23 @@
"""novelWriter Dialog Class Tester
"""
import nw
import pytest
import os
import sys
from shutil import copyfile
from tools import cmpFiles, getGuiItem
from PyQt5.QtCore import QItemSelectionModel
from PyQt5.QtWidgets import QListWidgetItem, QDialog, QFileDialog, QMessageBox
from PyQt5.QtCore import Qt, QItemSelectionModel
from PyQt5.QtWidgets import (
QDialogButtonBox, QTreeWidgetItem, QListWidgetItem, QDialog, QAction,
QFileDialog, QFontDialog
)
from nw.gui import GuiProjectWizard, GuiProjectLoad, GuiPreferences
from nw.gui.custom import QuotesDialog
from nw.constants import nwItemClass
keyDelay = 2
typeDelay = 1
stepDelay = 20
@pytest.mark.gui
def testNewProjectWizard(qtbot, monkeypatch, yesToAll, nwMinimal, tmpDir):
if sys.platform.startswith("darwin"):
# Disable for macOS because the test segfaults on QWizard.show()
return
from PyQt5.QtWidgets import QWizard
from nw.gui.projwizard import (
ProjWizardIntroPage, ProjWizardFolderPage, ProjWizardPopulatePage,
ProjWizardCustomPage, ProjWizardFinalPage
)
nwGUI = nw.main(["--testmode", "--config=%s" % tmpDir, "--data=%s" % tmpDir])
qtbot.addWidget(nwGUI)
nwGUI.show()
qtbot.waitForWindowShown(nwGUI)
qtbot.wait(stepDelay)
##
# Test New Project Function
##
# New with a project open should cause an error
assert nwGUI.openProject(nwMinimal)
assert not nwGUI.newProject()
# Close project, but call with invalid path
assert nwGUI.closeProject()
monkeypatch.setattr(nwGUI, "showNewProjectDialog", lambda *args: None)
assert not nwGUI.newProject()
# Now, with an empty dictionary
monkeypatch.setattr(nwGUI, "showNewProjectDialog", lambda *args: {})
assert not nwGUI.newProject()
# Now, with a non-empty folder
monkeypatch.setattr(nwGUI, "showNewProjectDialog", lambda *args: {"projPath": nwMinimal})
assert not nwGUI.newProject()
monkeypatch.undo()
##
# Test the Wizard
##
monkeypatch.setattr(GuiProjectWizard, "exec_", lambda *args: None)
nwGUI.mainConf.lastPath = " "
nwGUI.closeProject()
nwGUI.showNewProjectDialog()
qtbot.waitUntil(lambda: getGuiItem("GuiProjectWizard") is not None, timeout=1000)
nwWiz = getGuiItem("GuiProjectWizard")
assert isinstance(nwWiz, GuiProjectWizard)
nwWiz.show()
qtbot.wait(stepDelay)
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()
nwWiz.reject()
nwWiz.close()
# qtbot.stopForInteraction()
nwGUI.closeMain()
nwGUI.close()
@pytest.mark.gui
def testLoadProject(qtbot, monkeypatch, yesToAll, nwMinimal, tmpDir):
nwGUI = nw.main(["--testmode", "--config=%s" % nwMinimal, "--data=%s" % tmpDir])
qtbot.addWidget(nwGUI)
nwGUI.show()
qtbot.waitForWindowShown(nwGUI)
qtbot.wait(stepDelay)
assert nwGUI.openProject(nwMinimal)
assert nwGUI.closeProject()
qtbot.wait(stepDelay)
monkeypatch.setattr(GuiProjectLoad, "exec_", lambda *args: None)
monkeypatch.setattr(GuiProjectLoad, "result", lambda *args: QDialog.Accepted)
nwGUI.mainMenu.aOpenProject.activate(QAction.Trigger)
qtbot.waitUntil(lambda: getGuiItem("GuiProjectLoad") is not None, timeout=1000)
nwLoad = getGuiItem("GuiProjectLoad")
assert isinstance(nwLoad, GuiProjectLoad)
nwLoad.show()
qtbot.wait(stepDelay)
recentCount = nwLoad.listBox.topLevelItemCount()
assert recentCount > 0
qtbot.wait(stepDelay)
selItem = nwLoad.listBox.topLevelItem(0)
selPath = selItem.data(nwLoad.C_NAME, Qt.UserRole)
assert isinstance(selItem, QTreeWidgetItem)
qtbot.wait(stepDelay)
nwLoad.selPath.setText("")
nwLoad.listBox.setCurrentItem(selItem)
nwLoad._doSelectRecent()
assert nwLoad.selPath.text() == selPath
qtbot.wait(stepDelay)
qtbot.mouseClick(nwLoad.buttonBox.button(QDialogButtonBox.Open), Qt.LeftButton)
assert nwLoad.openPath == selPath
assert nwLoad.openState == nwLoad.OPEN_STATE
# Just create a new project load from scratch for the rest of the test
del nwLoad
qtbot.wait(stepDelay)
nwGUI.mainMenu.aOpenProject.activate(QAction.Trigger)
qtbot.waitUntil(lambda: getGuiItem("GuiProjectLoad") is not None, timeout=1000)
qtbot.wait(stepDelay)
nwLoad = getGuiItem("GuiProjectLoad")
assert isinstance(nwLoad, GuiProjectLoad)
nwLoad.show()
qtbot.wait(stepDelay)
qtbot.mouseClick(nwLoad.buttonBox.button(QDialogButtonBox.Cancel), Qt.LeftButton)
assert nwLoad.openPath is None
assert nwLoad.openState == nwLoad.NONE_STATE
qtbot.wait(stepDelay)
nwLoad.show()
qtbot.mouseClick(nwLoad.newButton, Qt.LeftButton)
assert nwLoad.openPath is None
assert nwLoad.openState == nwLoad.NEW_STATE
qtbot.wait(stepDelay)
nwLoad.show()
nwLoad._keyPressDelete()
assert nwLoad.listBox.topLevelItemCount() == recentCount - 1
getFile = os.path.join(nwMinimal, "nwProject.nwx")
monkeypatch.setattr(QFileDialog, "getOpenFileName", lambda *args, **kwargs: (getFile, None))
qtbot.mouseClick(nwLoad.browseButton, Qt.LeftButton)
assert nwLoad.openPath == nwMinimal
assert nwLoad.openState == nwLoad.OPEN_STATE
# qtbot.stopForInteraction()
nwLoad.close()
nwGUI.closeMain()
nwGUI.close()
@pytest.mark.gui
def testPreferences(qtbot, monkeypatch, yesToAll, nwMinimal, tmpDir, refDir, tmpConf):
nwGUI = nw.main(["--testmode", "--config=%s" % nwMinimal, "--data=%s" % tmpDir])
qtbot.addWidget(nwGUI)
nwGUI.show()
qtbot.waitForWindowShown(nwGUI)
qtbot.wait(stepDelay)
assert nwGUI.openProject(nwMinimal)
monkeypatch.setattr(GuiPreferences, "exec_", lambda *args: None)
monkeypatch.setattr(GuiPreferences, "result", lambda *args: QDialog.Accepted)
nwGUI.mainMenu.aPreferences.activate(QAction.Trigger)
qtbot.waitUntil(lambda: getGuiItem("GuiPreferences") is not None, timeout=1000)
nwPrefs = getGuiItem("GuiPreferences")
assert isinstance(nwPrefs, GuiPreferences)
nwPrefs.show()
# Override Config
tmpConf.confPath = nwMinimal
tmpConf.showGUI = False
nwGUI.mainConf = tmpConf
nwPrefs.mainConf = tmpConf
nwPrefs.tabGeneral.mainConf = tmpConf
nwPrefs.tabProjects.mainConf = tmpConf
nwPrefs.tabLayout.mainConf = tmpConf
nwPrefs.tabEditing.mainConf = tmpConf
nwPrefs.tabAutoRep.mainConf = tmpConf
# General Settings
qtbot.wait(keyDelay)
tabGeneral = nwPrefs.tabGeneral
nwPrefs._tabBox.setCurrentWidget(tabGeneral)
qtbot.wait(keyDelay)
assert not tabGeneral.preferDarkIcons.isChecked()
qtbot.mouseClick(tabGeneral.preferDarkIcons, Qt.LeftButton)
assert tabGeneral.preferDarkIcons.isChecked()
qtbot.wait(keyDelay)
assert tabGeneral.showFullPath.isChecked()
qtbot.mouseClick(tabGeneral.showFullPath, Qt.LeftButton)
assert not tabGeneral.showFullPath.isChecked()
qtbot.wait(keyDelay)
assert not tabGeneral.hideVScroll.isChecked()
qtbot.mouseClick(tabGeneral.hideVScroll, Qt.LeftButton)
assert tabGeneral.hideVScroll.isChecked()
qtbot.wait(keyDelay)
assert not tabGeneral.hideHScroll.isChecked()
qtbot.mouseClick(tabGeneral.hideHScroll, Qt.LeftButton)
assert tabGeneral.hideHScroll.isChecked()
# Check font button
monkeypatch.setattr(QFontDialog, "getFont", lambda font, obj: (font, True))
qtbot.mouseClick(tabGeneral.fontButton, Qt.LeftButton)
qtbot.wait(keyDelay)
tabGeneral.guiFontSize.setValue(12)
# Projects Settings
qtbot.wait(keyDelay)
tabProjects = nwPrefs.tabProjects
nwPrefs._tabBox.setCurrentWidget(tabProjects)
tabProjects.backupPath = "no/where"
qtbot.wait(keyDelay)
assert not tabProjects.backupOnClose.isChecked()
qtbot.mouseClick(tabProjects.backupOnClose, Qt.LeftButton)
assert tabProjects.backupOnClose.isChecked()
# Check Browse button
monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *args, **kwargs: "")
assert not tabProjects._backupFolder()
monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *args, **kwargs: "some/dir")
qtbot.mouseClick(tabProjects.backupGetPath, Qt.LeftButton)
qtbot.wait(keyDelay)
tabProjects.autoSaveDoc.setValue(20)
tabProjects.autoSaveProj.setValue(40)
# Text Layout Settings
qtbot.wait(keyDelay)
tabLayout = nwPrefs.tabLayout
nwPrefs._tabBox.setCurrentWidget(tabLayout)
qtbot.wait(keyDelay)
qtbot.mouseClick(tabLayout.fontButton, Qt.LeftButton)
qtbot.wait(keyDelay)
tabLayout.textStyleSize.setValue(13)
tabLayout.textFlowMax.setValue(700)
tabLayout.focusDocWidth.setValue(900)
tabLayout.textMargin.setValue(45)
tabLayout.tabWidth.setValue(45)
qtbot.wait(keyDelay)
assert not tabLayout.textFlowFixed.isChecked()
qtbot.mouseClick(tabLayout.textFlowFixed, Qt.LeftButton)
assert tabLayout.textFlowFixed.isChecked()
qtbot.wait(keyDelay)
assert not tabLayout.hideFocusFooter.isChecked()
qtbot.mouseClick(tabLayout.hideFocusFooter, Qt.LeftButton)
assert tabLayout.hideFocusFooter.isChecked()
qtbot.wait(keyDelay)
assert tabLayout.textJustify.isChecked()
qtbot.mouseClick(tabLayout.textJustify, Qt.LeftButton)
assert not tabLayout.textJustify.isChecked()
qtbot.wait(keyDelay)
assert tabLayout.scrollPastEnd.isChecked()
qtbot.mouseClick(tabLayout.scrollPastEnd, Qt.LeftButton)
assert not tabLayout.scrollPastEnd.isChecked()
qtbot.wait(keyDelay)
assert not tabLayout.autoScroll.isChecked()
qtbot.mouseClick(tabLayout.autoScroll, Qt.LeftButton)
assert tabLayout.autoScroll.isChecked()
# Editor Settings
qtbot.wait(keyDelay)
tabEditing = nwPrefs.tabEditing
nwPrefs._tabBox.setCurrentWidget(tabEditing)
qtbot.wait(keyDelay)
assert tabEditing.highlightQuotes.isChecked()
qtbot.mouseClick(tabEditing.highlightQuotes, Qt.LeftButton)
assert not tabEditing.highlightQuotes.isChecked()
qtbot.wait(keyDelay)
assert tabEditing.highlightEmph.isChecked()
qtbot.mouseClick(tabEditing.highlightEmph, Qt.LeftButton)
assert not tabEditing.highlightEmph.isChecked()
qtbot.wait(keyDelay)
assert not tabEditing.showTabsNSpaces.isChecked()
qtbot.mouseClick(tabEditing.showTabsNSpaces, Qt.LeftButton)
assert tabEditing.showTabsNSpaces.isChecked()
qtbot.wait(keyDelay)
assert not tabEditing.showLineEndings.isChecked()
qtbot.mouseClick(tabEditing.showLineEndings, Qt.LeftButton)
assert tabEditing.showLineEndings.isChecked()
qtbot.wait(keyDelay)
tabEditing.bigDocLimit.setValue(500)
# Auto-Replace Settings
qtbot.wait(keyDelay)
tabAutoRep = nwPrefs.tabAutoRep
nwPrefs._tabBox.setCurrentWidget(tabAutoRep)
qtbot.wait(keyDelay)
assert tabAutoRep.autoSelect.isChecked()
qtbot.mouseClick(tabAutoRep.autoSelect, Qt.LeftButton)
assert not tabAutoRep.autoSelect.isChecked()
qtbot.wait(keyDelay)
assert tabAutoRep.autoReplaceMain.isChecked()
qtbot.mouseClick(tabAutoRep.autoReplaceMain, Qt.LeftButton)
assert not tabAutoRep.autoReplaceMain.isChecked()
qtbot.wait(keyDelay)
assert not tabAutoRep.autoReplaceSQ.isEnabled()
assert not tabAutoRep.autoReplaceDQ.isEnabled()
assert not tabAutoRep.autoReplaceDash.isEnabled()
assert not tabAutoRep.autoReplaceDots.isEnabled()
monkeypatch.setattr(QuotesDialog, "selectedQuote", "'")
monkeypatch.setattr(QuotesDialog, "exec_", lambda *args: QDialog.Accepted)
qtbot.mouseClick(tabAutoRep.btnDoubleStyleC, Qt.LeftButton)
# Save and Check Config
qtbot.mouseClick(nwPrefs.buttonBox.button(QDialogButtonBox.Ok), Qt.LeftButton)
assert tmpConf.confChanged
tmpConf.lastPath = ""
assert nwGUI.mainConf.saveConfig()
# qtbot.stopForInteraction()
nwGUI.closeMain()
refConf = os.path.join(refDir, "novelwriter_prefs.conf")
projConf = os.path.join(nwGUI.mainConf.confPath, "novelwriter.conf")
testConf = os.path.join(tmpDir, "novelwriter_prefs.conf")
copyfile(projConf, testConf)
ignoreLines = [
2, # Timestamp
9, # Release Notes
12, 13, 14, 15, 16, 17, 18, # Window sizes
7, 28, # Fonts (depends on system default)
]
assert cmpFiles(testConf, refConf, ignoreLines)
@pytest.mark.gui
def testQuotesDialog(qtbot, yesToAll, nwMinimal, tmpDir):
nwGUI = nw.main(["--testmode", "--config=%s" % nwMinimal, "--data=%s" % tmpDir, nwMinimal])
qtbot.addWidget(nwGUI)
nwGUI.show()
qtbot.waitForWindowShown(nwGUI)
qtbot.wait(stepDelay)
def testGuiDialogs_Quotes(qtbot, monkeypatch, nwGUI, nwMinimal):
"""Test the quote symbols dialog.
"""
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes)
nwQuot = QuotesDialog(nwGUI)
nwQuot.show()
@@ -527,20 +39,16 @@ def testQuotesDialog(qtbot, yesToAll, nwMinimal, tmpDir):
# qtbot.stopForInteraction()
nwQuot._doReject()
nwQuot.close()
nwGUI.closeMain()
nwGUI.close()
# END Test testDialogs_Quotes
@pytest.mark.gui
def testDialogsOpenClose(qtbot, monkeypatch, yesToAll, nwMinimal, tmpDir):
nwGUI = nw.main(["--testmode", "--config=%s" % nwMinimal, "--data=%s" % tmpDir, nwMinimal])
qtbot.addWidget(nwGUI)
nwGUI.show()
qtbot.waitForWindowShown(nwGUI)
qtbot.wait(stepDelay)
def testGuiDialogs_Other(qtbot, monkeypatch, nwGUI, nwMinimal, tmpDir):
"""Various other dialog tests.
"""
monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *args, **kwargs: tmpDir)
assert nwGUI.selectProjectPath() == tmpDir
# qtbot.stopForInteraction()
nwGUI.closeMain()
nwGUI.close()
# END Test testGuiDialogs_Other
+235
View File
@@ -0,0 +1,235 @@
# -*- coding: utf-8 -*-
"""novelWriter Dialog Class Tester
"""
import nw
import pytest
import os
from shutil import copyfile
from tools import cmpFiles, getGuiItem
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
QDialogButtonBox, QDialog, QAction, QFileDialog, QFontDialog, QMessageBox
)
from nw.gui import GuiPreferences
from nw.config import Config
from nw.gui.custom import QuotesDialog
keyDelay = 2
typeDelay = 1
stepDelay = 20
@pytest.mark.gui
def testGuiPreferences_Main(qtbot, monkeypatch, fncDir, outDir, refDir):
"""Test the load project wizard.
"""
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes)
monkeypatch.setattr(QMessageBox, "information", lambda *args: QMessageBox.Yes)
# Must create a clean config and GUI object as the test-wide
# nw.CONFIG object is created on import an can be tainted by other tests
confFile = os.path.join(fncDir, "novelwriter.conf")
if os.path.isfile(confFile):
os.unlink(confFile)
theConf = Config()
theConf.initConfig(fncDir, fncDir)
theConf.setLastPath("")
origConf = nw.CONFIG
nw.CONFIG = theConf
nwGUI = nw.main(["--testmode", "--config=%s" % fncDir, "--data=%s" % fncDir])
qtbot.addWidget(nwGUI)
nwGUI.show()
qtbot.waitForWindowShown(nwGUI)
qtbot.wait(20)
theConf = nwGUI.mainConf
assert theConf.confPath == fncDir
monkeypatch.setattr(GuiPreferences, "exec_", lambda *args: None)
monkeypatch.setattr(GuiPreferences, "result", lambda *args: QDialog.Accepted)
nwGUI.mainMenu.aPreferences.activate(QAction.Trigger)
qtbot.waitUntil(lambda: getGuiItem("GuiPreferences") is not None, timeout=1000)
nwPrefs = getGuiItem("GuiPreferences")
assert isinstance(nwPrefs, GuiPreferences)
nwPrefs.show()
assert nwPrefs.mainConf.confPath == fncDir
# qtbot.stopForInteraction()
# General Settings
qtbot.wait(keyDelay)
tabGeneral = nwPrefs.tabGeneral
nwPrefs._tabBox.setCurrentWidget(tabGeneral)
qtbot.wait(keyDelay)
assert not tabGeneral.preferDarkIcons.isChecked()
qtbot.mouseClick(tabGeneral.preferDarkIcons, Qt.LeftButton)
assert tabGeneral.preferDarkIcons.isChecked()
qtbot.wait(keyDelay)
assert tabGeneral.showFullPath.isChecked()
qtbot.mouseClick(tabGeneral.showFullPath, Qt.LeftButton)
assert not tabGeneral.showFullPath.isChecked()
qtbot.wait(keyDelay)
assert not tabGeneral.hideVScroll.isChecked()
qtbot.mouseClick(tabGeneral.hideVScroll, Qt.LeftButton)
assert tabGeneral.hideVScroll.isChecked()
qtbot.wait(keyDelay)
assert not tabGeneral.hideHScroll.isChecked()
qtbot.mouseClick(tabGeneral.hideHScroll, Qt.LeftButton)
assert tabGeneral.hideHScroll.isChecked()
# Check font button
monkeypatch.setattr(QFontDialog, "getFont", lambda font, obj: (font, True))
qtbot.mouseClick(tabGeneral.fontButton, Qt.LeftButton)
qtbot.wait(keyDelay)
tabGeneral.guiFontSize.setValue(12)
# Projects Settings
qtbot.wait(keyDelay)
tabProjects = nwPrefs.tabProjects
nwPrefs._tabBox.setCurrentWidget(tabProjects)
tabProjects.backupPath = "no/where"
qtbot.wait(keyDelay)
assert not tabProjects.backupOnClose.isChecked()
qtbot.mouseClick(tabProjects.backupOnClose, Qt.LeftButton)
assert tabProjects.backupOnClose.isChecked()
# Check Browse button
monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *args, **kwargs: "")
assert not tabProjects._backupFolder()
monkeypatch.setattr(QFileDialog, "getExistingDirectory", lambda *args, **kwargs: "some/dir")
qtbot.mouseClick(tabProjects.backupGetPath, Qt.LeftButton)
qtbot.wait(keyDelay)
tabProjects.autoSaveDoc.setValue(20)
tabProjects.autoSaveProj.setValue(40)
# Text Layout Settings
qtbot.wait(keyDelay)
tabLayout = nwPrefs.tabLayout
nwPrefs._tabBox.setCurrentWidget(tabLayout)
qtbot.wait(keyDelay)
qtbot.mouseClick(tabLayout.fontButton, Qt.LeftButton)
qtbot.wait(keyDelay)
tabLayout.textStyleSize.setValue(13)
tabLayout.textFlowMax.setValue(700)
tabLayout.focusDocWidth.setValue(900)
tabLayout.textMargin.setValue(45)
tabLayout.tabWidth.setValue(45)
qtbot.wait(keyDelay)
assert not tabLayout.textFlowFixed.isChecked()
qtbot.mouseClick(tabLayout.textFlowFixed, Qt.LeftButton)
assert tabLayout.textFlowFixed.isChecked()
qtbot.wait(keyDelay)
assert not tabLayout.hideFocusFooter.isChecked()
qtbot.mouseClick(tabLayout.hideFocusFooter, Qt.LeftButton)
assert tabLayout.hideFocusFooter.isChecked()
qtbot.wait(keyDelay)
assert tabLayout.textJustify.isChecked()
qtbot.mouseClick(tabLayout.textJustify, Qt.LeftButton)
assert not tabLayout.textJustify.isChecked()
qtbot.wait(keyDelay)
assert tabLayout.scrollPastEnd.isChecked()
qtbot.mouseClick(tabLayout.scrollPastEnd, Qt.LeftButton)
assert not tabLayout.scrollPastEnd.isChecked()
qtbot.wait(keyDelay)
assert not tabLayout.autoScroll.isChecked()
qtbot.mouseClick(tabLayout.autoScroll, Qt.LeftButton)
assert tabLayout.autoScroll.isChecked()
# Editor Settings
qtbot.wait(keyDelay)
tabEditing = nwPrefs.tabEditing
nwPrefs._tabBox.setCurrentWidget(tabEditing)
qtbot.wait(keyDelay)
assert tabEditing.highlightQuotes.isChecked()
qtbot.mouseClick(tabEditing.highlightQuotes, Qt.LeftButton)
assert not tabEditing.highlightQuotes.isChecked()
qtbot.wait(keyDelay)
assert tabEditing.highlightEmph.isChecked()
qtbot.mouseClick(tabEditing.highlightEmph, Qt.LeftButton)
assert not tabEditing.highlightEmph.isChecked()
qtbot.wait(keyDelay)
assert not tabEditing.showTabsNSpaces.isChecked()
qtbot.mouseClick(tabEditing.showTabsNSpaces, Qt.LeftButton)
assert tabEditing.showTabsNSpaces.isChecked()
qtbot.wait(keyDelay)
assert not tabEditing.showLineEndings.isChecked()
qtbot.mouseClick(tabEditing.showLineEndings, Qt.LeftButton)
assert tabEditing.showLineEndings.isChecked()
qtbot.wait(keyDelay)
tabEditing.bigDocLimit.setValue(500)
# Auto-Replace Settings
qtbot.wait(keyDelay)
tabAutoRep = nwPrefs.tabAutoRep
nwPrefs._tabBox.setCurrentWidget(tabAutoRep)
qtbot.wait(keyDelay)
assert tabAutoRep.autoSelect.isChecked()
qtbot.mouseClick(tabAutoRep.autoSelect, Qt.LeftButton)
assert not tabAutoRep.autoSelect.isChecked()
qtbot.wait(keyDelay)
assert tabAutoRep.autoReplaceMain.isChecked()
qtbot.mouseClick(tabAutoRep.autoReplaceMain, Qt.LeftButton)
assert not tabAutoRep.autoReplaceMain.isChecked()
qtbot.wait(keyDelay)
assert not tabAutoRep.autoReplaceSQ.isEnabled()
assert not tabAutoRep.autoReplaceDQ.isEnabled()
assert not tabAutoRep.autoReplaceDash.isEnabled()
assert not tabAutoRep.autoReplaceDots.isEnabled()
monkeypatch.setattr(QuotesDialog, "selectedQuote", "'")
monkeypatch.setattr(QuotesDialog, "exec_", lambda *args: QDialog.Accepted)
qtbot.mouseClick(tabAutoRep.btnDoubleStyleC, Qt.LeftButton)
# Save and Check Config
qtbot.mouseClick(nwPrefs.buttonBox.button(QDialogButtonBox.Ok), Qt.LeftButton)
assert theConf.confChanged
theConf.lastPath = ""
assert nwGUI.mainConf.saveConfig()
projFile = os.path.join(fncDir, "novelwriter.conf")
testFile = os.path.join(outDir, "guiPreferences_novelwriter.conf")
compFile = os.path.join(refDir, "guiPreferences_novelwriter.conf")
copyfile(projFile, testFile)
ignoreLines = [
2, # Timestamp
9, # Release Notes
12, 13, 14, 15, 16, 17, 18, # Window sizes
7, 28, # Fonts (depends on system default)
]
assert cmpFiles(testFile, compFile, ignoreLines)
# Clean up
nw.CONFIG = origConf
nwGUI.closeMain()
# qtbot.stopForInteraction()
# END Test testGuiPreferences_Main
+99
View File
@@ -0,0 +1,99 @@
# -*- coding: utf-8 -*-
"""novelWriter Dialog Class Tester
"""
import pytest
import os
from tools import getGuiItem
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
QDialogButtonBox, QTreeWidgetItem, QDialog, QAction, QFileDialog,
QMessageBox
)
from nw.gui import GuiProjectLoad
keyDelay = 2
typeDelay = 1
stepDelay = 20
@pytest.mark.gui
def testGuiLoadProject_Main(qtbot, monkeypatch, nwGUI, nwMinimal):
"""Test the load project wizard.
"""
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes)
assert nwGUI.openProject(nwMinimal)
assert nwGUI.closeProject()
qtbot.wait(stepDelay)
monkeypatch.setattr(GuiProjectLoad, "exec_", lambda *args: None)
monkeypatch.setattr(GuiProjectLoad, "result", lambda *args: QDialog.Accepted)
nwGUI.mainMenu.aOpenProject.activate(QAction.Trigger)
qtbot.waitUntil(lambda: getGuiItem("GuiProjectLoad") is not None, timeout=1000)
nwLoad = getGuiItem("GuiProjectLoad")
assert isinstance(nwLoad, GuiProjectLoad)
nwLoad.show()
qtbot.wait(stepDelay)
recentCount = nwLoad.listBox.topLevelItemCount()
assert recentCount > 0
qtbot.wait(stepDelay)
selItem = nwLoad.listBox.topLevelItem(0)
selPath = selItem.data(nwLoad.C_NAME, Qt.UserRole)
assert isinstance(selItem, QTreeWidgetItem)
qtbot.wait(stepDelay)
nwLoad.selPath.setText("")
nwLoad.listBox.setCurrentItem(selItem)
nwLoad._doSelectRecent()
assert nwLoad.selPath.text() == selPath
qtbot.wait(stepDelay)
qtbot.mouseClick(nwLoad.buttonBox.button(QDialogButtonBox.Open), Qt.LeftButton)
assert nwLoad.openPath == selPath
assert nwLoad.openState == nwLoad.OPEN_STATE
# Just create a new project load from scratch for the rest of the test
del nwLoad
qtbot.wait(stepDelay)
nwGUI.mainMenu.aOpenProject.activate(QAction.Trigger)
qtbot.waitUntil(lambda: getGuiItem("GuiProjectLoad") is not None, timeout=1000)
qtbot.wait(stepDelay)
nwLoad = getGuiItem("GuiProjectLoad")
assert isinstance(nwLoad, GuiProjectLoad)
nwLoad.show()
qtbot.wait(stepDelay)
qtbot.mouseClick(nwLoad.buttonBox.button(QDialogButtonBox.Cancel), Qt.LeftButton)
assert nwLoad.openPath is None
assert nwLoad.openState == nwLoad.NONE_STATE
qtbot.wait(stepDelay)
nwLoad.show()
qtbot.mouseClick(nwLoad.newButton, Qt.LeftButton)
assert nwLoad.openPath is None
assert nwLoad.openState == nwLoad.NEW_STATE
qtbot.wait(stepDelay)
nwLoad.show()
nwLoad._keyPressDelete()
assert nwLoad.listBox.topLevelItemCount() == recentCount - 1
getFile = os.path.join(nwMinimal, "nwProject.nwx")
monkeypatch.setattr(QFileDialog, "getOpenFileName", lambda *args, **kwargs: (getFile, None))
qtbot.mouseClick(nwLoad.browseButton, Qt.LeftButton)
assert nwLoad.openPath == nwMinimal
assert nwLoad.openState == nwLoad.OPEN_STATE
nwLoad.close()
# qtbot.stopForInteraction()
# END Test testGuiLoadProject_Main
+212
View File
@@ -0,0 +1,212 @@
# -*- coding: utf-8 -*-
"""novelWriter Project Wizard Class Tester
"""
import pytest
import os
import sys
from tools import getGuiItem
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QFileDialog, QWizard, QMessageBox
from nw.gui import GuiProjectWizard
from nw.constants import nwItemClass
from nw.gui.projwizard import (
ProjWizardIntroPage, ProjWizardFolderPage, ProjWizardPopulatePage,
ProjWizardCustomPage, ProjWizardFinalPage
)
keyDelay = 2
typeDelay = 1
stepDelay = 20
@pytest.mark.gui
def testGuiProjectWizard_Main(qtbot, monkeypatch, nwGUI, nwMinimal):
"""Test the new project wizard.
"""
# Block message box
monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes)
if sys.platform.startswith("darwin"):
# Disable for macOS because the test segfaults on QWizard.show()
return
##
# Test New Project Function
##
# New with a project open should cause an error
assert nwGUI.openProject(nwMinimal)
assert not nwGUI.newProject()
# Close project, but call with invalid path
assert nwGUI.closeProject()
monkeypatch.setattr(nwGUI, "showNewProjectDialog", lambda *args: None)
assert not nwGUI.newProject()
# Now, with an empty dictionary
monkeypatch.setattr(nwGUI, "showNewProjectDialog", lambda *args: {})
assert not nwGUI.newProject()
# Now, with a non-empty folder
monkeypatch.setattr(nwGUI, "showNewProjectDialog", lambda *args: {"projPath": nwMinimal})
assert not nwGUI.newProject()
monkeypatch.undo()
##
# Test the Wizard
##
monkeypatch.setattr(GuiProjectWizard, "exec_", lambda *args: None)
nwGUI.mainConf.lastPath = " "
nwGUI.closeProject()
nwGUI.showNewProjectDialog()
qtbot.waitUntil(lambda: getGuiItem("GuiProjectWizard") is not None, timeout=1000)
nwWiz = getGuiItem("GuiProjectWizard")
assert isinstance(nwWiz, GuiProjectWizard)
nwWiz.show()
qtbot.wait(stepDelay)
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()
nwWiz.reject()
nwWiz.close()
# qtbot.stopForInteraction()
# END Test testGuiProjectWizard_Main