Add tests for the welcome window
This commit is contained in:
@@ -160,7 +160,6 @@ def nwGUI(qtbot, monkeypatch, functionFixture):
|
||||
nwGUI = main(["--testmode", f"--config={_TMP_CONF}", f"--data={_TMP_CONF}"])
|
||||
qtbot.addWidget(nwGUI)
|
||||
resetConfigVars()
|
||||
SHARED._alert = None
|
||||
nwGUI.docEditor.initEditor()
|
||||
|
||||
nwGUI.show()
|
||||
|
||||
@@ -278,6 +278,19 @@ def testBaseConfig_SettersGetters(fncPath):
|
||||
|
||||
tstConf.setMainWinSize(1200, 650)
|
||||
|
||||
# Welcome Window Size
|
||||
tstConf.guiScale = 2.0
|
||||
tstConf.setWelcomeWinSize(70, 70)
|
||||
assert tstConf.welcomeWinSize == [70, 70]
|
||||
assert tstConf._welcomeSize == [35, 35]
|
||||
|
||||
tstConf.guiScale = 1.0
|
||||
tstConf.setWelcomeWinSize(70, 70)
|
||||
assert tstConf.welcomeWinSize == [70, 70]
|
||||
assert tstConf._welcomeSize == [70, 70]
|
||||
|
||||
tstConf.setWelcomeWinSize(800, 500)
|
||||
|
||||
# Preferences Size
|
||||
tstConf.guiScale = 2.0
|
||||
tstConf.setPreferencesWinSize(70, 70)
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
"""
|
||||
novelWriter – Welcome Window Tester
|
||||
===================================
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2023, Veronica Berglyd Olsen
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
from pytestqt.qtbot import QtBot
|
||||
|
||||
from PyQt5.QtCore import QPoint, Qt
|
||||
from PyQt5.QtWidgets import QAction, QFileDialog, QMenu
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
from novelwriter.constants import nwFiles
|
||||
from novelwriter.enum import nwItemClass
|
||||
from novelwriter.tools.welcome import GuiWelcome
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testToolWelcome_Main(qtbot: QtBot, monkeypatch, nwGUI, fncPath):
|
||||
"""Test the main Welcome window."""
|
||||
welcome = GuiWelcome(nwGUI)
|
||||
with qtbot.waitExposed(welcome):
|
||||
# This ensures the paint event is executed
|
||||
welcome.show()
|
||||
|
||||
# By default, the project list is shown
|
||||
assert welcome.mainStack.currentIndex() == 0
|
||||
|
||||
# Show the new project form
|
||||
welcome.newButton.click()
|
||||
assert welcome.mainStack.currentIndex() == 1
|
||||
|
||||
# Revert to project lits
|
||||
welcome.tabNew.cancelNewProject.emit()
|
||||
assert welcome.mainStack.currentIndex() == 0
|
||||
|
||||
# Open a project
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(SHARED, "getProjectPath", lambda *a, **k: fncPath)
|
||||
with qtbot.waitSignal(welcome.openProjectRequest) as signal:
|
||||
welcome.browseButton.click()
|
||||
assert signal.args and signal.args[0] == fncPath
|
||||
|
||||
# qtbot.stop()
|
||||
welcome.close()
|
||||
|
||||
# END Test testToolWelcome_Main
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testToolWelcome_Open(qtbot: QtBot, monkeypatch, nwGUI, fncPath):
|
||||
"""Test the open tab in the Welcome window."""
|
||||
monkeypatch.setattr(QMenu, "exec_", lambda *a: None)
|
||||
monkeypatch.setattr(QMenu, "deleteLater", lambda *a: None)
|
||||
|
||||
CONFIG.recentProjects.update("/stuff/project_one", "Project One", 12345, 1690000000)
|
||||
CONFIG.recentProjects.update("/stuff/project_two", "Project Two", 54321, 1700000000)
|
||||
dateOne = datetime.fromtimestamp(1700000000).strftime("%x")
|
||||
dateTwo = datetime.fromtimestamp(1690000000).strftime("%x")
|
||||
|
||||
welcome = GuiWelcome(nwGUI)
|
||||
with qtbot.waitExposed(welcome):
|
||||
welcome.show()
|
||||
|
||||
assert welcome.mainStack.currentIndex() == 0
|
||||
tabOpen = welcome.tabOpen
|
||||
listModel = tabOpen.listModel
|
||||
vPort = tabOpen.listWidget.viewport()
|
||||
|
||||
posOne = tabOpen.listWidget.rectForIndex(listModel.createIndex(0, 0)).center()
|
||||
posTwo = tabOpen.listWidget.rectForIndex(listModel.createIndex(1, 0)).center()
|
||||
|
||||
# Check items, which should be in opposite order
|
||||
assert listModel.data(listModel.createIndex(0, 0)) == (
|
||||
"Project Two", "/stuff/project_two", f"Last Opened: {dateOne}, Word Count: 54.3\u2009k"
|
||||
)
|
||||
assert listModel.data(listModel.createIndex(1, 0)) == (
|
||||
"Project One", "/stuff/project_one", f"Last Opened: {dateTwo}, Word Count: 12.3\u2009k"
|
||||
)
|
||||
|
||||
# Single click item
|
||||
assert tabOpen.selectedPath.text() == "Path: /stuff/project_two"
|
||||
qtbot.mouseClick(vPort, Qt.MouseButton.LeftButton, pos=posTwo, delay=10)
|
||||
assert tabOpen.selectedPath.text() == "Path: /stuff/project_one"
|
||||
|
||||
# Double Click item
|
||||
qtbot.mouseClick(vPort, Qt.MouseButton.LeftButton, pos=posTwo, delay=10)
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(welcome, "close", lambda *a: None)
|
||||
with qtbot.waitSignal(welcome.openProjectRequest, timeout=5000) as signal:
|
||||
qtbot.mouseDClick(vPort, Qt.MouseButton.LeftButton, pos=posTwo, delay=10)
|
||||
assert signal.args and signal.args[0] == Path("/stuff/project_one")
|
||||
|
||||
# Context Menu
|
||||
def getMenuForPos(pos: QPoint) -> QMenu | None:
|
||||
nonlocal tabOpen
|
||||
tabOpen._openContextMenu(pos)
|
||||
for obj in tabOpen.children():
|
||||
if isinstance(obj, QMenu) and obj.objectName() == "ContextMenu":
|
||||
return obj
|
||||
return None
|
||||
|
||||
qtbot.mouseClick(vPort, Qt.MouseButton.LeftButton, pos=posOne, delay=10)
|
||||
ctxMenu = getMenuForPos(posOne)
|
||||
assert isinstance(ctxMenu, QMenu)
|
||||
assert ctxMenu.actions()[0].text() == "Open Project"
|
||||
assert ctxMenu.actions()[1].text() == "Remove Project"
|
||||
|
||||
# Open item from context menu
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(welcome, "close", lambda *a: None)
|
||||
with qtbot.waitSignal(welcome.openProjectRequest, timeout=5000) as signal:
|
||||
ctxMenu.actions()[0].activate(QAction.ActionEvent.Trigger)
|
||||
assert signal.args and signal.args[0] == Path("/stuff/project_two")
|
||||
|
||||
ctxMenu.setObjectName("")
|
||||
ctxMenu.deleteLater()
|
||||
|
||||
# Delete item from context menu
|
||||
ctxMenu = getMenuForPos(posOne)
|
||||
assert isinstance(ctxMenu, QMenu)
|
||||
ctxMenu.actions()[1].activate(QAction.ActionEvent.Trigger)
|
||||
assert len(CONFIG.recentProjects.listEntries()) == 1
|
||||
|
||||
ctxMenu.setObjectName("")
|
||||
ctxMenu.deleteLater()
|
||||
|
||||
# Check removing entry error handling
|
||||
assert listModel.data(listModel.createIndex(1, 0)) == ("", "", "")
|
||||
assert listModel.removeEntry(listModel.createIndex(1, 0)) is False
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(listModel, "data", lambda *a: ("a", "b", "c"))
|
||||
assert listModel.removeEntry(listModel.createIndex(1, 0)) is False
|
||||
|
||||
# Delete last entry with keypress
|
||||
qtbot.mouseClick(vPort, Qt.MouseButton.LeftButton, pos=posOne, delay=10)
|
||||
qtbot.keyClick(tabOpen, Qt.Key.Key_Delete, delay=10)
|
||||
assert len(CONFIG.recentProjects.listEntries()) == 0
|
||||
|
||||
# qtbot.stop()
|
||||
welcome.close()
|
||||
|
||||
# END Test testToolWelcome_Open
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testToolWelcome_New(qtbot: QtBot, caplog, monkeypatch, nwGUI, fncPath):
|
||||
"""Test the new project tab in the Welcome window."""
|
||||
welcome = GuiWelcome(nwGUI)
|
||||
with qtbot.waitExposed(welcome):
|
||||
welcome.show()
|
||||
|
||||
welcome.newButton.click()
|
||||
assert welcome.mainStack.currentIndex() == 1
|
||||
tabNew = welcome.tabNew
|
||||
newForm = tabNew.projectForm
|
||||
|
||||
# Populate the path
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(QFileDialog, "getExistingDirectory", lambda *a, **k: fncPath)
|
||||
newForm.browsePath.click()
|
||||
assert newForm.projPath.text() == str(fncPath)
|
||||
|
||||
# Turning off root folders should disable notes switch
|
||||
newForm.addNotes.setChecked(True)
|
||||
newForm.addPlot.setChecked(False)
|
||||
newForm.addChar.setChecked(False)
|
||||
newForm.addWorld.setChecked(False)
|
||||
newForm._syncSwitches()
|
||||
assert newForm.addNotes.isChecked() is False
|
||||
|
||||
# Change fill info to sample
|
||||
newForm.fillSample.trigger()
|
||||
assert newForm._fillMode == newForm.FILL_SAMPLE
|
||||
assert newForm.projFill.text() == "Example Project"
|
||||
assert newForm.addNotes.isEnabled() is False
|
||||
assert newForm.addPlot.isEnabled() is False
|
||||
assert newForm.addChar.isEnabled() is False
|
||||
assert newForm.addWorld.isEnabled() is False
|
||||
assert newForm.numChapters.isEnabled() is False
|
||||
assert newForm.numScenes.isEnabled() is False
|
||||
|
||||
# Change fill info to template
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(SHARED, "getProjectPath", lambda *a, **k: fncPath)
|
||||
newForm.fillCopy.trigger()
|
||||
assert newForm._fillMode == newForm.FILL_COPY
|
||||
assert newForm.projFill.text() == f"Template: {fncPath}"
|
||||
assert newForm.addNotes.isEnabled() is False
|
||||
assert newForm.addPlot.isEnabled() is False
|
||||
assert newForm.addChar.isEnabled() is False
|
||||
assert newForm.addWorld.isEnabled() is False
|
||||
assert newForm.numChapters.isEnabled() is False
|
||||
assert newForm.numScenes.isEnabled() is False
|
||||
|
||||
# Change back to fill blank using the menu
|
||||
newForm.browseFill.click()
|
||||
assert newForm.fillMenu.isVisible() is True
|
||||
newForm.fillMenu.actions()[0].activate(QAction.ActionEvent.Trigger)
|
||||
newForm.fillMenu.close()
|
||||
assert newForm._fillMode == newForm.FILL_BLANK
|
||||
assert newForm.projFill.text() == "Fresh Project"
|
||||
assert newForm.addNotes.isEnabled() is True
|
||||
assert newForm.addPlot.isEnabled() is True
|
||||
assert newForm.addChar.isEnabled() is True
|
||||
assert newForm.addWorld.isEnabled() is True
|
||||
assert newForm.numChapters.isEnabled() is True
|
||||
assert newForm.numScenes.isEnabled() is True
|
||||
|
||||
# Creating a project without a name, pops an error
|
||||
caplog.clear()
|
||||
tabNew.createButton.click()
|
||||
assert "A project name is required." in caplog.text
|
||||
|
||||
# Set some more values, and extract data
|
||||
projPath = fncPath / "Test Project"
|
||||
newForm.projName.setText("Test Project")
|
||||
newForm.projAuthor.setText("Jane Smith")
|
||||
newForm.addPlot.setChecked(True)
|
||||
newForm.addChar.setChecked(True)
|
||||
newForm.addWorld.setChecked(True)
|
||||
newForm.addNotes.setChecked(True)
|
||||
newForm.numChapters.setValue(10)
|
||||
newForm.numScenes.setValue(6)
|
||||
assert newForm.getProjectData() == {
|
||||
"name": "Test Project",
|
||||
"author": "Jane Smith",
|
||||
"language": "en_GB",
|
||||
"path": str(projPath),
|
||||
"blank": True,
|
||||
"sample": False,
|
||||
"template": None,
|
||||
"chapters": 10,
|
||||
"scenes": 6,
|
||||
"roots": [nwItemClass.PLOT, nwItemClass.CHARACTER, nwItemClass.WORLD],
|
||||
"notes": True,
|
||||
}
|
||||
|
||||
# Create a project with these values
|
||||
with qtbot.waitSignal(welcome.openProjectRequest, timeout=5000) as signal:
|
||||
tabNew.createButton.click()
|
||||
assert signal.args and signal.args[0] == projPath
|
||||
assert (projPath / nwFiles.PROJ_FILE).exists()
|
||||
|
||||
# qtbot.stop()
|
||||
welcome.close()
|
||||
|
||||
# END Test testToolWelcome_New
|
||||
Reference in New Issue
Block a user