From 4d9b94cfe68d1ac1a6e240f1d52f93cbafd5a127 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Sun, 5 Jun 2022 22:31:10 +0200
Subject: [PATCH] Improve test coverage of Outline
---
novelwriter/gui/outline.py | 2 +-
novelwriter/guimain.py | 2 +-
tests/test_gui/test_gui_outline.py | 198 ++++++++++++++++++++++++++---
3 files changed, 181 insertions(+), 21 deletions(-)
diff --git a/novelwriter/gui/outline.py b/novelwriter/gui/outline.py
index 61b35410..34cb35da 100644
--- a/novelwriter/gui/outline.py
+++ b/novelwriter/gui/outline.py
@@ -461,7 +461,7 @@ class GuiOutlineView(QTreeWidget):
document editor.
"""
tHandle, tLine = self.getSelectedHandle()
- self.theParent.openDocument(tHandle, tLine=tLine-1, doScroll=True)
+ self.theParent.openDocument(tHandle, tLine=tLine - 1, doScroll=True)
return
@pyqtSlot()
diff --git a/novelwriter/guimain.py b/novelwriter/guimain.py
index c2051c6e..6a056dca 100644
--- a/novelwriter/guimain.py
+++ b/novelwriter/guimain.py
@@ -1453,7 +1453,7 @@ class GuiMain(QMainWindow):
return
##
- # Slots
+ # Private Slots
##
@pyqtSlot(str, Enum)
diff --git a/tests/test_gui/test_gui_outline.py b/tests/test_gui/test_gui_outline.py
index f089928e..49a86b16 100644
--- a/tests/test_gui/test_gui_outline.py
+++ b/tests/test_gui/test_gui_outline.py
@@ -19,17 +19,144 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
"""
+import os
+import time
import pytest
-from PyQt5.QtWidgets import QTreeWidgetItem, QMessageBox
+from tools import buildTestProject, writeFile
-keyDelay = 2
-typeDelay = 1
-stepDelay = 20
+from PyQt5.QtCore import Qt
+from PyQt5.QtWidgets import QWidget, QMessageBox, QAction
+
+from novelwriter.enum import nwItemClass, nwOutline, nwView
@pytest.mark.gui
-def testGuiOutline_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
+def testGuiOutline_Main(qtbot, monkeypatch, nwGUI, fncDir):
+ """Test the outline view.
+ """
+ # Block message box
+ monkeypatch.setattr(QMessageBox, "question", lambda *a: QMessageBox.Yes)
+ monkeypatch.setattr(QMessageBox, "information", lambda *a: QMessageBox.Yes)
+
+ # Create a project
+ prjDir = os.path.join(fncDir, "project")
+ buildTestProject(nwGUI, prjDir)
+
+ nwGUI.rebuildIndex()
+ nwGUI._changeView(nwView.OUTLINE)
+
+ outlineMain = nwGUI.projView
+ outlineView = outlineMain.outlineView
+ outlineData = outlineMain.outlineData
+ outlineMenu = outlineMain.outlineBar.mColumns
+
+ # Toggle scrollbars
+ nwGUI.mainConf.hideVScroll = True
+ nwGUI.mainConf.hideHScroll = True
+ nwGUI.projView.initOutline()
+ assert outlineView.verticalScrollBarPolicy() == Qt.ScrollBarAlwaysOff
+ assert outlineView.horizontalScrollBarPolicy() == Qt.ScrollBarAlwaysOff
+ assert outlineData.verticalScrollBarPolicy() == Qt.ScrollBarAlwaysOff
+ assert outlineData.horizontalScrollBarPolicy() == Qt.ScrollBarAlwaysOff
+
+ nwGUI.mainConf.hideVScroll = False
+ nwGUI.mainConf.hideHScroll = False
+ nwGUI.projView.initOutline()
+ assert outlineView.verticalScrollBarPolicy() == Qt.ScrollBarAsNeeded
+ assert outlineView.horizontalScrollBarPolicy() == Qt.ScrollBarAsNeeded
+ assert outlineData.verticalScrollBarPolicy() == Qt.ScrollBarAsNeeded
+ assert outlineData.horizontalScrollBarPolicy() == Qt.ScrollBarAsNeeded
+
+ # Check focus
+ with monkeypatch.context() as mp:
+ mp.setattr(QWidget, "hasFocus", lambda *a: True)
+ assert outlineMain.treeFocus() is True
+
+ outlineMain.setTreeFocus() # Can't check. just ensures that it doesn't error
+
+ # Option State
+ # ============
+ pOptions = nwGUI.theProject.options
+ colNames = [h.name for h in nwOutline]
+ colItems = [h for h in nwOutline]
+ colWidth = {h: outlineView.DEF_WIDTH[h] for h in nwOutline}
+ colHidden = {h: outlineView.DEF_HIDDEN[h] for h in nwOutline}
+
+ assert outlineView.topLevelItemCount() > 0
+
+ # Save header state not allowed
+ outlineView._lastBuild = 0
+ outlineView._saveHeaderState()
+ assert pOptions.getValue("GuiOutline", "headerOrder", []) == []
+
+ # Allow saving header state
+ outlineView._lastBuild = time.time()
+ outlineView._saveHeaderState()
+ assert pOptions.getValue("GuiOutline", "headerOrder", []) == colNames
+ assert outlineView._treeOrder == colItems
+ assert outlineView._colWidth == colWidth
+ assert outlineView._colHidden == colHidden
+
+ # Get default values
+ optItems = pOptions.getValue("GuiOutline", "headerOrder", [])
+ optWidth = pOptions.getValue("GuiOutline", "columnWidth", {})
+ optHidden = pOptions.getValue("GuiOutline", "columnHidden", {})
+
+ # Add invalid column name
+ pOptions.setValue("GuiOutline", "headerOrder", optItems + ["blabla"])
+ outlineView._loadHeaderState()
+ assert outlineView._treeOrder == colItems
+ assert outlineView._colHidden == colHidden
+
+ # Add duplicate column name
+ pOptions.setValue("GuiOutline", "headerOrder", optItems + [optItems[-1]])
+ outlineView._loadHeaderState()
+ assert outlineView._treeOrder == colItems
+ assert outlineView._colHidden == colHidden
+
+ # Invalid column width data
+ pOptions.setValue("GuiOutline", "headerOrder", optItems)
+ pOptions.setValue("GuiOutline", "columnWidth", {"blabla": None})
+ outlineView._loadHeaderState()
+ assert outlineView._treeOrder == colItems
+ assert outlineView._colHidden == colHidden
+
+ # Invalid column width data
+ pOptions.setValue("GuiOutline", "headerOrder", optItems)
+ pOptions.setValue("GuiOutline", "columnWidth", optWidth)
+ pOptions.setValue("GuiOutline", "columnHidden", {"bloabla": None})
+ outlineView._loadHeaderState()
+ assert outlineView._treeOrder == colItems
+ assert outlineView._colHidden == colHidden
+
+ # Valid settings
+ pOptions.setValue("GuiOutline", "headerOrder", optItems)
+ pOptions.setValue("GuiOutline", "columnWidth", optWidth)
+ pOptions.setValue("GuiOutline", "columnHidden", optHidden)
+ outlineView._loadHeaderState()
+ assert outlineView._treeOrder == colItems
+ assert outlineView._colHidden == colHidden
+
+ # Header Menu
+ # ===========
+
+ # Trigger the menu entry for all hidden columns
+ for hItem in nwOutline:
+ if outlineView.DEF_HIDDEN[hItem]:
+ outlineMenu.actionMap[hItem].activate(QAction.Trigger)
+
+ # Now no columns should be hidden
+ outlineView._saveHeaderState()
+ assert not any(pOptions.getValue("GuiOutline", "columnHidden", None).values())
+
+ # qtbot.stop()
+
+# END Test testGuiOutline_Main
+
+
+@pytest.mark.gui
+def testGuiOutline_Content(qtbot, monkeypatch, nwGUI, nwLipsum):
"""Test the outline view.
"""
# Block message box
@@ -40,26 +167,59 @@ def testGuiOutline_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
nwGUI.mainConf.lastPath = nwLipsum
nwGUI.rebuildIndex()
- nwGUI.mainStack.setCurrentIndex(nwGUI.idxOutlineView)
+ nwGUI._changeView(nwView.OUTLINE)
- outlineView = nwGUI.projView.outlineView
- outlineData = nwGUI.projView.outlineData
+ outlineMain = nwGUI.projView
+ outlineBar = outlineMain.outlineBar
+ outlineView = outlineMain.outlineView
+ outlineData = outlineMain.outlineData
- assert outlineView.topLevelItemCount() > 0
+ lipHandle = "b3643d0f92e32"
- # Context Menu
- # outlineView._headerRightClick(QPoint(1, 1))
- # outlineView.headerMenu.actionMap[nwOutline.CCOUNT].activate(QAction.Trigger)
- # outlineView.headerMenu.close()
- # qtbot.mouseClick(outlineView, Qt.LeftButton)
+ # Check defaults in dropdown list
+ assert outlineBar.novelValue.itemData(0) == lipHandle
+ assert outlineBar.novelValue.itemData(1) is None # Separator
+ assert outlineBar.novelValue.itemData(2) == "" # All novels
- # outlineView._loadHeaderState()
- # assert not outlineView._colHidden[nwOutline.CCOUNT]
+ # Add a second novel folder
+ newHandle = nwGUI.theProject.newRoot(nwItemClass.NOVEL)
+ nwGUI.treeView.revealNewTreeItem(newHandle)
+
+ # Check new values in dropdown list
+ assert outlineBar.novelValue.itemData(0) == lipHandle
+ assert outlineBar.novelValue.itemData(1) == newHandle
+ assert outlineBar.novelValue.itemData(2) is None # Separator
+ assert outlineBar.novelValue.itemData(3) == "" # All novels
+
+ # Add a bunch of files in a header order that hits all tree combos
+ docList = [
+ ("Section 1", 4), ("Scene 1", 3), ("Chapter 1", 2), ("Part 1", 1),
+ ("Section 2", 4), ("Scene 2", 3), ("Chapter 2", 2),
+ ("Section 3", 4), ("Scene 3", 3),
+ ("Section 4", 4),
+ ]
+ for dTitle, hLevel in docList:
+ aHandle = nwGUI.theProject.newFile(dTitle, newHandle)
+ hHash = "#"*hLevel
+ writeFile(os.path.join(nwLipsum, "content", f"{aHandle}.nwd"), f"{hHash} {dTitle}\n\n")
+ nwGUI.treeView.revealNewTreeItem(aHandle)
+
+ nwGUI.rebuildIndex()
+
+ # Build the second novel
+ outlineBar.novelValue.setCurrentIndex(1)
+ outlineBar._refreshRequested()
+
+ # Go back to Lipsum
+ outlineBar.novelValue.setCurrentIndex(0)
+ outlineBar._refreshRequested()
+
+ # Check Details
+ # =============
# First Item
outlineView.refreshTree()
selItem = outlineView.topLevelItem(0)
- assert isinstance(selItem, QTreeWidgetItem)
outlineView.setCurrentItem(selItem)
assert outlineData.titleLabel.text() == "Title"
@@ -110,6 +270,6 @@ def testGuiOutline_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
outlineView._treeDoubleClick(selItem, 0)
assert nwGUI.docEditor.docHandle() == "88243afbe5ed8"
- # qtbot.stopForInteraction()
+ # qtbot.stop()
-# END Test testGuiOutline_Main
+# END Test testGuiOutline_Content