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