Some code flow improvements and main GUI test coverage

This commit is contained in:
Veronica Berglyd Olsen
2024-10-31 00:07:48 +01:00
parent a2b09fdfec
commit 0ba0674b9e
4 changed files with 134 additions and 53 deletions
+4 -8
View File
@@ -553,17 +553,13 @@ class GuiNovelTree(QTreeWidget):
if pHandle := self._actHandle: if pHandle := self._actHandle:
for key, item in self._treeMap.items(): for key, item in self._treeMap.items():
if key.startswith(pHandle): if key.startswith(pHandle):
item.setBackground(self.C_TITLE, brushOff) for i in range(self.columnCount()):
item.setBackground(self.C_WORDS, brushOff) item.setBackground(i, brushOff)
item.setBackground(self.C_EXTRA, brushOff)
item.setBackground(self.C_MORE, brushOff)
if tHandle: if tHandle:
for key, item in self._treeMap.items(): for key, item in self._treeMap.items():
if key.startswith(tHandle): if key.startswith(tHandle):
item.setBackground(self.C_TITLE, brushOn) for i in range(self.columnCount()):
item.setBackground(self.C_WORDS, brushOn) item.setBackground(i, brushOn)
item.setBackground(self.C_EXTRA, brushOn)
item.setBackground(self.C_MORE, brushOn)
if not didScroll: if not didScroll:
self.scrollToItem(item, QAbstractItemView.ScrollHint.PositionAtCenter) self.scrollToItem(item, QAbstractItemView.ScrollHint.PositionAtCenter)
didScroll = True didScroll = True
+31 -41
View File
@@ -844,35 +844,33 @@ class GuiMain(QMainWindow):
def closeMain(self) -> bool: def closeMain(self) -> bool:
"""Save everything, and close novelWriter.""" """Save everything, and close novelWriter."""
if SHARED.hasProject: if SHARED.hasProject and SHARED.question("%s<br>%s" % (
msgYes = SHARED.question("%s<br>%s" % ( self.tr("Do you want to exit novelWriter?"),
self.tr("Do you want to exit novelWriter?"), self.tr("Changes are saved automatically.")
self.tr("Changes are saved automatically.") )):
)) logger.info("Exiting novelWriter")
if not msgYes:
return False
logger.info("Exiting novelWriter") if not SHARED.focusMode:
CONFIG.setMainPanePos(self.splitMain.sizes())
CONFIG.setOutlinePanePos(self.outlineView.splitSizes())
if self.docViewerPanel.isVisible():
CONFIG.setViewPanePos(self.splitView.sizes())
if not SHARED.focusMode: CONFIG.showViewerPanel = self.docViewerPanel.isVisible()
CONFIG.setMainPanePos(self.splitMain.sizes()) wFull = Qt.WindowState.WindowFullScreen
CONFIG.setOutlinePanePos(self.outlineView.splitSizes()) if self.windowState() & wFull != wFull:
if self.docViewerPanel.isVisible(): # Ignore window size if in full screen mode
CONFIG.setViewPanePos(self.splitView.sizes()) CONFIG.setMainWinSize(self.width(), self.height())
CONFIG.showViewerPanel = self.docViewerPanel.isVisible() if SHARED.hasProject:
wFull = Qt.WindowState.WindowFullScreen self.closeProject(True)
if self.windowState() & wFull != wFull: CONFIG.saveConfig()
# Ignore window size if in full screen mode
CONFIG.setMainWinSize(self.width(), self.height())
if SHARED.hasProject: QApplication.quit()
self.closeProject(True)
CONFIG.saveConfig()
QApplication.quit() return True
return True return False
def closeViewerPanel(self, byUser: bool = True) -> bool: def closeViewerPanel(self, byUser: bool = True) -> bool:
"""Close the document view panel.""" """Close the document view panel."""
@@ -1101,8 +1099,16 @@ class GuiMain(QMainWindow):
@pyqtSlot(str, nwDocMode) @pyqtSlot(str, nwDocMode)
def _followTag(self, tag: str, mode: nwDocMode) -> None: def _followTag(self, tag: str, mode: nwDocMode) -> None:
"""Follow a tag after user interaction with a link.""" """Follow a tag after user interaction with a link."""
tHandle, sTitle = self._getTagSource(tag) tHandle, sTitle = SHARED.project.index.getTagSource(tag)
if tHandle is not None: if tHandle is None:
SHARED.error(self.tr(
"Could not find the reference for tag '{0}'. It either doesn't "
"exist, or the index is out of date. The index can be updated "
"from the Tools menu, or by pressing {1}."
).format(
tag, "F9"
))
else:
if mode == nwDocMode.EDIT: if mode == nwDocMode.EDIT:
self.openDocument(tHandle, sTitle=sTitle) self.openDocument(tHandle, sTitle=sTitle)
elif mode == nwDocMode.VIEW: elif mode == nwDocMode.VIEW:
@@ -1293,19 +1299,3 @@ class GuiMain(QMainWindow):
"""Set the window title and add the project's name.""" """Set the window title and add the project's name."""
self.setWindowTitle(" - ".join(filter(None, [projName, CONFIG.appName]))) self.setWindowTitle(" - ".join(filter(None, [projName, CONFIG.appName])))
return return
def _getTagSource(self, tag: str) -> tuple[str | None, str | None]:
"""Handle the index lookup of a tag and display an alert if the
tag cannot be found.
"""
tHandle, sTitle = SHARED.project.index.getTagSource(tag)
if tHandle is None:
SHARED.error(self.tr(
"Could not find the reference for tag '{0}'. It either doesn't "
"exist, or the index is out of date. The index can be updated "
"from the Tools menu, or by pressing {1}."
).format(
tag, "F9"
))
return None, None
return tHandle, sTitle
+4
View File
@@ -54,8 +54,10 @@ def resetConfigVars():
CONFIG.setBackupPath(_TMP_ROOT) CONFIG.setBackupPath(_TMP_ROOT)
CONFIG.setGuiFont(None) CONFIG.setGuiFont(None)
CONFIG.setTextFont(None) CONFIG.setTextFont(None)
CONFIG.backupOnClose = False
CONFIG._homePath = _TMP_ROOT CONFIG._homePath = _TMP_ROOT
CONFIG._dLocale = QLocale("en_GB") CONFIG._dLocale = QLocale("en_GB")
CONFIG.pdfDocs = _TMP_ROOT / "manual.pdf"
CONFIG.guiLocale = "en_GB" CONFIG.guiLocale = "en_GB"
return return
@@ -72,6 +74,7 @@ def sessionFixture():
shutil.rmtree(_TMP_ROOT) shutil.rmtree(_TMP_ROOT)
_TMP_ROOT.mkdir() _TMP_ROOT.mkdir()
_TMP_CONF.mkdir() _TMP_CONF.mkdir()
(_TMP_ROOT / "manual.pdf").touch()
return return
@@ -161,6 +164,7 @@ def nwGUI(qtbot, monkeypatch, functionFixture):
monkeypatch.setattr(QMessageBox, "result", lambda *a: QMessageBox.StandardButton.Yes) monkeypatch.setattr(QMessageBox, "result", lambda *a: QMessageBox.StandardButton.Yes)
nwGUI = main(["--testmode", f"--config={_TMP_CONF}", f"--data={_TMP_CONF}"]) nwGUI = main(["--testmode", f"--config={_TMP_CONF}", f"--data={_TMP_CONF}"])
assert nwGUI is not None
qtbot.addWidget(nwGUI) qtbot.addWidget(nwGUI)
resetConfigVars() resetConfigVars()
nwGUI.docEditor.initEditor() nwGUI.docEditor.initEditor()
+95 -4
View File
@@ -20,19 +20,22 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations from __future__ import annotations
import shutil
import sys import sys
from pathlib import Path
from shutil import copyfile from shutil import copyfile
import pytest import pytest
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPalette from PyQt5.QtGui import QPalette
from PyQt5.QtWidgets import QInputDialog, QMenu from PyQt5.QtWidgets import QInputDialog, QMenu, QMessageBox
from novelwriter import CONFIG, SHARED from novelwriter import CONFIG, SHARED
from novelwriter.constants import nwFiles
from novelwriter.dialogs.editlabel import GuiEditLabel from novelwriter.dialogs.editlabel import GuiEditLabel
from novelwriter.enum import nwDocAction, nwFocus, nwItemType, nwView from novelwriter.enum import nwDocAction, nwDocMode, nwFocus, nwItemType, nwView
from novelwriter.gui.doceditor import GuiDocEditor from novelwriter.gui.doceditor import GuiDocEditor
from novelwriter.gui.noveltree import GuiNovelView from novelwriter.gui.noveltree import GuiNovelView
from novelwriter.gui.outline import GuiOutlineView from novelwriter.gui.outline import GuiOutlineView
@@ -682,6 +685,12 @@ def testGuiMain_Viewing(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
def testGuiMain_Features(qtbot, monkeypatch, nwGUI, projPath, mockRnd): def testGuiMain_Features(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
"""Test various features of the main window.""" """Test various features of the main window."""
buildTestProject(nwGUI, projPath) buildTestProject(nwGUI, projPath)
cHandle = SHARED.project.newFile("Jane", C.hCharRoot)
newDoc = SHARED.project.storage.getDocument(cHandle)
newDoc.writeDocument("# Jane\n\n@tag: Jane\n\n")
nwGUI.projView.projTree.revealNewTreeItem(cHandle)
nwGUI.rebuildIndex(beQuiet=True)
assert SHARED.focusMode is False assert SHARED.focusMode is False
# Focus Mode # Focus Mode
@@ -721,6 +730,20 @@ def testGuiMain_Features(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
assert SHARED.focusMode is True assert SHARED.focusMode is True
nwGUI.closeDocument() nwGUI.closeDocument()
assert SHARED.focusMode is False assert SHARED.focusMode is False
nwGUI.openDocument(C.hSceneDoc)
# Pressing Escape turns off focus mode
nwGUI.toggleFocusMode()
assert SHARED.focusMode is True
qtbot.keyClick(nwGUI, Qt.Key.Key_Escape)
assert SHARED.focusMode is False
# If search is active, Escape is redirected to editor
nwGUI.toggleFocusMode()
assert SHARED.focusMode is True
nwGUI.docEditor.beginSearch()
qtbot.keyClick(nwGUI, Qt.Key.Key_Escape)
assert SHARED.focusMode is True
# Full Screen Mode # Full Screen Mode
# ================ # ================
@@ -738,8 +761,25 @@ def testGuiMain_Features(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
nwGUI.sideBar.mSettings.show() nwGUI.sideBar.mSettings.show()
nwGUI.sideBar.mSettings.hide() nwGUI.sideBar.mSettings.hide()
# Document Open Errors # Redirect Tag Open
# ==================== # =================
nwGUI.closeDocument()
nwGUI.closeDocViewer()
assert nwGUI.docEditor.docHandle is None
assert nwGUI.docViewer.docHandle is None
nwGUI._followTag("John", nwDocMode.EDIT) # Doesn't exist
assert nwGUI.docEditor.docHandle is None
assert nwGUI.docViewer.docHandle is None
nwGUI._followTag("Jane", nwDocMode.EDIT)
assert nwGUI.docEditor.docHandle == cHandle
assert nwGUI.docViewer.docHandle is None
nwGUI._followTag("Jane", nwDocMode.VIEW)
assert nwGUI.docEditor.docHandle == cHandle
assert nwGUI.docViewer.docHandle == cHandle
# Errors Handling
# ===============
# Cannot edit a folder # Cannot edit a folder
assert nwGUI.openDocument(C.hChapterDir) is False assert nwGUI.openDocument(C.hChapterDir) is False
@@ -752,6 +792,57 @@ def testGuiMain_Features(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
# qtbot.stop() # qtbot.stop()
@pytest.mark.gui
def testGuiMain_OpenClose(qtbot, monkeypatch, nwGUI, projPath, fncPath, mockRnd):
"""Test various features of the main window."""
buildTestProject(nwGUI, projPath)
nwGUI.openDocument(C.hSceneDoc)
nwGUI.viewDocument(C.hTitlePage)
# Handle broken index on project open
nwGUI.closeProject()
idxPath: Path = projPath / "meta" / nwFiles.INDEX_FILE
assert idxPath.read_text() != "{}"
idxPath.write_text("{}")
assert idxPath.read_text() == "{}"
nwGUI.openProject(projPath)
nwGUI.saveProject()
assert idxPath.read_text() != "{}"
assert nwGUI.docEditor.docHandle == C.hSceneDoc
assert nwGUI.docViewer.docHandle == C.hTitlePage
# Block closing
assert SHARED.hasProject is True
with monkeypatch.context() as mp:
mp.setattr(QMessageBox, "result", lambda *a: QMessageBox.StandardButton.No)
assert nwGUI.openProject(projPath) is False
assert SHARED.hasProject is True
# Don't open on lockfile question: No
lockPath: Path = projPath / nwFiles.PROJ_LOCK
lockBack: Path = projPath / f"{nwFiles.PROJ_LOCK}.bak"
shutil.copyfile(lockPath, lockBack)
nwGUI.closeProject()
shutil.copyfile(lockBack, lockPath)
with monkeypatch.context() as mp:
mp.setattr(QMessageBox, "result", lambda *a: QMessageBox.StandardButton.No)
assert nwGUI.openProject(projPath) is False
assert nwGUI.openProject(projPath) is True
# Backup on close
backDir = CONFIG.backupPath() / SHARED.project.data.name
assert not backDir.exists()
CONFIG.backupOnClose = True
assert nwGUI.openProject(projPath) is True
nwGUI.closeProject()
assert len(list(backDir.glob("*.zip"))) == 1
@pytest.mark.gui @pytest.mark.gui
def testGuiMain_FocusView(qtbot, monkeypatch, nwGUI, projPath, mockRnd): def testGuiMain_FocusView(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
"""Test switching focus and view of the main window.""" """Test switching focus and view of the main window."""