Improve test coverage of project tree

This commit is contained in:
Veronica Berglyd Olsen
2023-11-22 18:48:10 +01:00
parent 62ea54e1ed
commit 445414d126
+116 -29
View File
@@ -23,18 +23,19 @@ from __future__ import annotations
import pytest
from pathlib import Path
from novelwriter.core.item import NWItem
from novelwriter.core.project import NWProject
from tools import C, buildTestProject
from mocked import causeOSError
from PyQt5.QtGui import QDragMoveEvent, QDropEvent
from PyQt5.QtCore import QMimeData, QPoint, QTimer, Qt
from PyQt5.QtGui import QDragEnterEvent, QDragMoveEvent, QDropEvent, QMouseEvent
from PyQt5.QtCore import QEvent, QMimeData, QPoint, QTimer, Qt
from PyQt5.QtWidgets import QMessageBox, QMenu, QTreeWidget, QTreeWidgetItem, QDialog
from novelwriter import CONFIG, SHARED
from novelwriter.enum import nwItemLayout, nwItemType, nwItemClass, nwWidget
from novelwriter.guimain import GuiMain
from novelwriter.core.item import NWItem
from novelwriter.gui.projtree import GuiProjectTree, GuiProjectView, _TreeContextMenu
from novelwriter.dialogs.docmerge import GuiDocMerge
from novelwriter.dialogs.docsplit import GuiDocSplit
@@ -51,81 +52,84 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, projPath, mockRn
project = SHARED.project
# Try to add item with no project
assert projView.projTree.newTreeItem(nwItemType.FILE) is False
assert projTree.newTreeItem(nwItemType.FILE) is False
# Create a project
buildTestProject(nwGUI, projPath)
# No itemType set
projView.projTree.clearSelection()
assert projView.projTree.newTreeItem(None) is False
projTree.clearSelection()
assert projTree.newTreeItem(None) is False
# Root Items
# ==========
# No class set
assert projView.projTree.newTreeItem(nwItemType.ROOT) is False
assert projTree.newTreeItem(nwItemType.ROOT) is False
# Create root item
assert projView.projTree.newTreeItem(nwItemType.ROOT, nwItemClass.WORLD) is True
assert projTree.newTreeItem(nwItemType.ROOT, nwItemClass.WORLD) is True
assert "0000000000010" in project.tree
# File/Folder Items
# =================
# No location selected for new item
projView.projTree.clearSelection()
projTree.clearSelection()
caplog.clear()
assert projView.projTree.newTreeItem(nwItemType.FILE) is False
assert projView.projTree.newTreeItem(nwItemType.FOLDER) is False
assert projTree.newTreeItem(nwItemType.FILE) is False
assert projTree.newTreeItem(nwItemType.FOLDER) is False
assert "Did not find anywhere" in caplog.text
# Create new folder as child of Novel folder
projView.setSelectedHandle(C.hNovelRoot)
assert projView.projTree.newTreeItem(nwItemType.FOLDER) is True
assert projTree.newTreeItem(nwItemType.FOLDER) is True
assert project.tree["0000000000011"].itemParent == C.hNovelRoot # type: ignore
assert project.tree["0000000000011"].itemRoot == C.hNovelRoot # type: ignore
assert project.tree["0000000000011"].itemClass == nwItemClass.NOVEL # type: ignore
# Add a new file in the new folder
projView.setSelectedHandle("0000000000011")
assert projView.projTree.newTreeItem(nwItemType.FILE) is True
assert projTree.newTreeItem(nwItemType.FILE) is True
assert project.tree["0000000000012"].itemParent == "0000000000011" # type: ignore
assert project.tree["0000000000012"].itemRoot == C.hNovelRoot # type: ignore
assert project.tree["0000000000012"].itemClass == nwItemClass.NOVEL # type: ignore
# Add a new chapter next to the other new file
projView.setSelectedHandle("0000000000012")
assert projView.projTree.newTreeItem(nwItemType.FILE, hLevel=2) is True
assert projTree.newTreeItem(nwItemType.FILE, hLevel=2) is True
assert project.tree["0000000000013"].itemParent == "0000000000011" # type: ignore
assert project.tree["0000000000013"].itemRoot == C.hNovelRoot # type: ignore
assert project.tree["0000000000013"].itemClass == nwItemClass.NOVEL # type: ignore
assert nwGUI.openDocument("0000000000013")
assert nwGUI.docEditor.getText() == "## New Chapter\n\n"
assert projTree._getItemWordCount("0000000000013") == 2
# Add a new scene next to the other new file
projView.setSelectedHandle("0000000000012")
assert projView.projTree.newTreeItem(nwItemType.FILE, hLevel=3) is True
assert projTree.newTreeItem(nwItemType.FILE, hLevel=3) is True
assert project.tree["0000000000014"].itemParent == "0000000000011" # type: ignore
assert project.tree["0000000000014"].itemRoot == C.hNovelRoot # type: ignore
assert project.tree["0000000000014"].itemClass == nwItemClass.NOVEL # type: ignore
assert nwGUI.openDocument("0000000000014")
assert nwGUI.docEditor.getText() == "### New Scene\n\n"
assert projTree._getItemWordCount("0000000000014") == 2
# Add a new file to the characters folder
projView.setSelectedHandle(C.hCharRoot)
assert projView.projTree.newTreeItem(nwItemType.FILE, hLevel=1, isNote=True) is True
assert projTree.newTreeItem(nwItemType.FILE, hLevel=1, isNote=True) is True
assert project.tree["0000000000015"].itemParent == C.hCharRoot # type: ignore
assert project.tree["0000000000015"].itemRoot == C.hCharRoot # type: ignore
assert project.tree["0000000000015"].itemClass == nwItemClass.CHARACTER # type: ignore
assert nwGUI.openDocument("0000000000015")
assert nwGUI.docEditor.getText() == "# New Note\n\n"
assert projTree._getItemWordCount("0000000000015") == 2
# Make sure the sibling folder bug trap works
projView.setSelectedHandle("0000000000013")
project.tree["0000000000013"].setParent(None) # This should not happen # type: ignore
caplog.clear()
assert projView.projTree.newTreeItem(nwItemType.FILE) is False
assert projTree.newTreeItem(nwItemType.FILE) is False
assert "Internal error" in caplog.text
project.tree["0000000000013"].setParent("0000000000011") # type: ignore
@@ -133,13 +137,17 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, projPath, mockRn
with monkeypatch.context() as mp:
mp.setattr(GuiEditLabel, "getLabel", lambda *a, **k: ("", False))
projView.setSelectedHandle("0000000000013")
assert projView.projTree.newTreeItem(nwItemType.FILE) is False
assert projTree.newTreeItem(nwItemType.FILE) is False
# Get the trash folder
projView.projTree._addTrashRoot()
with monkeypatch.context() as mp:
mp.setattr(NWProject, "trashFolder", lambda *a: None)
assert projTree._addTrashRoot() is None
assert isinstance(projTree._addTrashRoot(), QTreeWidgetItem)
trashHandle = project.trashFolder()
projView.setSelectedHandle(trashHandle)
assert projView.projTree.newTreeItem(nwItemType.FILE) is False
assert projTree.newTreeItem(nwItemType.FILE) is False
assert "Cannot add new files or folders to the Trash folder" in caplog.text
# Rename Item
@@ -158,14 +166,14 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, projPath, mockRn
# ============
# Also check error handling in reveal function
assert projView.projTree.revealNewTreeItem("abc") is False
assert projTree.revealNewTreeItem("abc") is False
# Add an item that cannot be displayed in the tree
nHandle = project.newFile("Test", None) # type: ignore
assert projView.projTree.revealNewTreeItem(nHandle) is False
assert projTree.revealNewTreeItem(nHandle) is False
# Adding an invalid item directly to the tree should also fail
assert projView.projTree._addTreeItem(None) is None
assert projTree._addTreeItem(None) is None
# Clean up
# qtbot.stop()
@@ -183,7 +191,7 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
projTree = nwGUI.projView.projTree
# Try to move item with no project
assert projView.projTree.moveTreeItem(1) is False
assert projTree.moveTreeItem(1) is False
# Create a project
buildTestProject(nwGUI, projPath)
@@ -245,7 +253,7 @@ def testGuiProjTree_MoveItems(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
"0000000000010", "0000000000011", "0000000000012",
]
# Move down again, and restore via undo
# Move down again
projView.setSelectedHandle("0000000000011")
assert projTree.moveTreeItem(1) is True
assert projTree.getTreeFromHandle(C.hChapterDir) == [
@@ -894,6 +902,45 @@ def testGuiProjTree_DragAndDrop(qtbot, monkeypatch, caplog, nwGUI: GuiMain, proj
projTree.saveTreeOrder()
assert SHARED.project.tree._order == treeOrder
# Make sure illegal drag events are cancelled
with monkeypatch.context() as mp:
mp.setattr(QTreeWidget, "dragEnterEvent", lambda *a: None)
mime = QMimeData()
mime.setText("foobar")
event = QDragEnterEvent(nPos, action, mime, mouse, modifier)
projTree.clearSelection()
projTree._getTreeItem(C.hNovelRoot).setSelected(True) # type: ignore
projTree._getTreeItem(C.hTitlePage).setSelected(True) # type: ignore
projTree._getTreeItem(C.hChapterDoc).setSelected(True) # type: ignore
assert projTree.selectedItems() == [ # Novel Root selection is cancelled automatically
projTree._getTreeItem(C.hTitlePage), projTree._getTreeItem(C.hChapterDoc)
]
projTree.dragEnterEvent(event)
assert mime.text() == ""
assert projTree._popAlert is not None
# Pop the alert
with monkeypatch.context() as mp:
mp.setattr(QTreeWidget, "startDrag", lambda *a: None)
projTree.startDrag(None) # type: ignore
assert projTree._popAlert is None
# Valid drag events are processed
with monkeypatch.context() as mp:
mp.setattr(QTreeWidget, "dragEnterEvent", lambda *a: None)
mime = QMimeData()
mime.setText("foobar")
event = QDragEnterEvent(nPos, action, mime, mouse, modifier)
projTree.clearSelection()
projTree._getTreeItem(C.hChapterDoc).setSelected(True) # type: ignore
projTree._getTreeItem(C.hSceneDoc).setSelected(True) # type: ignore
assert projTree.selectedItems() == [ # Novel Root selection is cancelled automatically
projTree._getTreeItem(C.hChapterDoc), projTree._getTreeItem(C.hSceneDoc)
]
projTree.dragEnterEvent(event)
assert mime.text() == "foobar"
assert projTree._popAlert is None
# qtbot.stop()
# END Test testGuiProjTree_DragAndDrop
@@ -1020,6 +1067,46 @@ def testGuiProjTree_Other(qtbot, monkeypatch, nwGUI: GuiMain, projPath, mockRnd)
projTree.moveToLevel(1)
assert projTree.getSelectedHandle() is None
# Mouse Button Clicks
# ===================
eType = QEvent.Type.MouseButtonPress
pos = projTree.visualItemRect(projTree._getTreeItem(C.hChapterDoc)).center()
button = Qt.MouseButton.MiddleButton
modifier = Qt.KeyboardModifier.NoModifier
# Trigger the viewer
event = QMouseEvent(eType, pos, button, button, modifier)
projTree.mousePressEvent(event)
assert nwGUI.docViewer.docHandle == C.hChapterDoc
# Trigger the left click clear
pos = QPoint(5000, 5000)
button = Qt.MouseButton.LeftButton
event = QMouseEvent(eType, pos, button, button, modifier)
projTree.setSelectedHandle(C.hChapterDoc)
projTree.mousePressEvent(event)
assert projTree.selectedItems() == []
# Rename Item
# ===========
with monkeypatch.context() as mp:
mp.setattr(GuiEditLabel, "getLabel", lambda *a, **k: ("FooBar", True))
projTree.clearSelection()
assert SHARED.project.tree[C.hChapterDoc].itemName == "New Chapter" # type: ignore
assert projView.renameTreeItem(C.hChapterDoc) is True
assert SHARED.project.tree[C.hChapterDoc].itemName == "FooBar" # type: ignore
projTree.setSelectedHandle(C.hSceneDoc)
assert SHARED.project.tree[C.hSceneDoc].itemName == "New Scene" # type: ignore
assert projView.renameTreeItem() is True
assert SHARED.project.tree[C.hSceneDoc].itemName == "FooBar" # type: ignore
# Check Crash Resistance
# ======================
projTree._postItemMove("dfghj") # This should exit cleanly
# qtbot.stop()
# END Test testGuiProjTree_Other
@@ -1216,14 +1303,14 @@ def testGuiProjTree_ContextMenu(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
# ============================
projView.setSelectedHandle(hNovelNote)
assert projView.projTree.newTreeItem(nwItemType.FOLDER) is True
assert projTree.newTreeItem(nwItemType.FOLDER) is True
projView.setSelectedHandle(hNewFolderOne)
assert projView.projTree.newTreeItem(nwItemType.FILE) is True
assert projTree.newTreeItem(nwItemType.FILE) is True
projView.setSelectedHandle(hNovelNote)
assert projView.projTree.newTreeItem(nwItemType.FOLDER) is True
assert projTree.newTreeItem(nwItemType.FOLDER) is True
projView.setSelectedHandle(hNewFolderTwo)
assert projView.projTree.newTreeItem(nwItemType.FILE, isNote=True) is True
assert projTree.newTreeItem(nwItemType.FILE, isNote=True) is True
nwItem = SHARED.project.tree[hNewFolderOne]
assert isinstance(nwItem, NWItem)