Clean up config and improve project tree test coverage (#1607)
This commit is contained in:
+4
-24
@@ -181,7 +181,7 @@ class Config:
|
||||
# State
|
||||
self.showRefPanel = True # The reference panel for the viewer is visible
|
||||
self.showEditToolBar = False # The document editor toolbar visibility
|
||||
self.useShortcodes = False # Use shorcodes for basic formatting
|
||||
self.useShortcodes = False # Use shortcodes for basic formatting
|
||||
self.viewComments = True # Comments are shown in the viewer
|
||||
self.viewSynopsis = True # Synopsis is shown in the viewer
|
||||
|
||||
@@ -392,8 +392,7 @@ class Config:
|
||||
return self._appPath / "assets"
|
||||
|
||||
def lastPath(self) -> Path:
|
||||
"""Return the last path used by the user, but ensure it exists.
|
||||
"""
|
||||
"""Return the last path used by the user, if it exists."""
|
||||
if isinstance(self._lastPath, Path):
|
||||
if self._lastPath.is_dir():
|
||||
return self._lastPath
|
||||
@@ -401,9 +400,8 @@ class Config:
|
||||
|
||||
def backupPath(self) -> Path:
|
||||
"""Return the backup path."""
|
||||
if isinstance(self._backupPath, Path):
|
||||
if self._backupPath.is_dir():
|
||||
return self._backupPath
|
||||
if isinstance(self._backupPath, Path) and self._backupPath.is_dir():
|
||||
return self._backupPath
|
||||
return self._backPath
|
||||
|
||||
def errorText(self) -> str:
|
||||
@@ -614,24 +612,6 @@ class Config:
|
||||
self.searchNextFile = conf.rdBool(sec, "searchnextfile", self.searchNextFile)
|
||||
self.searchMatchCap = conf.rdBool(sec, "searchmatchcap", self.searchMatchCap)
|
||||
|
||||
# Deprecated Settings or Locations as of 2.0
|
||||
# ToDo: These will be loaded for a few minor releases until the users have converted them
|
||||
self.guiFont = conf.rdStr("Main", "guifont", self.guiFont)
|
||||
self.guiFontSize = conf.rdInt("Main", "guifontsize", self.guiFontSize)
|
||||
self.guiLocale = conf.rdStr("Main", "guilang", self.guiLocale)
|
||||
self._backupPath = conf.rdPath("Backup", "backuppath", self._backupPath)
|
||||
self.backupOnClose = conf.rdBool("Backup", "backuponclose", self.backupOnClose)
|
||||
self.askBeforeBackup = conf.rdBool("Backup", "askbeforebackup", self.askBeforeBackup)
|
||||
fmtSingleQuotes = conf.rdStrList(sec, "fmtsinglequote", [])
|
||||
fmtDoubleQuotes = conf.rdStrList(sec, "fmtdoublequote", [])
|
||||
|
||||
if isinstance(fmtSingleQuotes, list) and len(fmtSingleQuotes) == 2:
|
||||
self.fmtSQuoteOpen = fmtSingleQuotes[0]
|
||||
self.fmtSQuoteClose = fmtSingleQuotes[1]
|
||||
if isinstance(fmtDoubleQuotes, list) and len(fmtDoubleQuotes) == 2:
|
||||
self.fmtDQuoteOpen = fmtDoubleQuotes[0]
|
||||
self.fmtDQuoteClose = fmtDoubleQuotes[1]
|
||||
|
||||
# Check Values
|
||||
# ============
|
||||
|
||||
|
||||
@@ -511,7 +511,7 @@ class GuiProjectTree(QTreeWidget):
|
||||
self.setDragDropMode(QAbstractItemView.InternalMove)
|
||||
self.setDropIndicatorShown(True)
|
||||
|
||||
# Disable built-in autoscroll as it isn't working in some Qt
|
||||
# Disable built-in auto scroll as it isn't working in some Qt
|
||||
# releases (see #1561) and instead use our own implementation
|
||||
self.setAutoScroll(False)
|
||||
|
||||
@@ -533,7 +533,7 @@ class GuiProjectTree(QTreeWidget):
|
||||
self.itemDoubleClicked.connect(self._treeDoubleClick)
|
||||
self.itemSelectionChanged.connect(self._treeSelectionChange)
|
||||
|
||||
# Autoscroll
|
||||
# Auto Scroll
|
||||
self._scrollMargin = SHARED.theme.baseIconSize
|
||||
self._scrollDirection = 0
|
||||
self._scrollTimer = QTimer()
|
||||
@@ -1415,7 +1415,7 @@ class GuiProjectTree(QTreeWidget):
|
||||
return
|
||||
|
||||
def dragMoveEvent(self, event: QDragMoveEvent) -> None:
|
||||
"""Capture the drag move event to enable edge autoscroll."""
|
||||
"""Capture the drag move event to enable edge auto scroll."""
|
||||
y = event.pos().y()
|
||||
if y < self._scrollMargin:
|
||||
if not self._scrollTimer.isActive():
|
||||
@@ -1429,8 +1429,8 @@ class GuiProjectTree(QTreeWidget):
|
||||
return
|
||||
|
||||
def dropEvent(self, event: QDropEvent) -> None:
|
||||
"""Overload the drop item event to ensure relevant data has been
|
||||
updated.
|
||||
"""Overload the drop item event to ensure the drag and drop
|
||||
action is allowed, and update relevant data.
|
||||
"""
|
||||
sHandle = self.getSelectedHandle()
|
||||
sItem = self._getTreeItem(sHandle) if sHandle else None
|
||||
|
||||
@@ -45,7 +45,7 @@ def testBaseConfig_Constructor(monkeypatch):
|
||||
assert tstConf.osWindows is False
|
||||
assert tstConf.osUnknown is False
|
||||
|
||||
# macOS
|
||||
# MacOS
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("sys.platform", "darwin")
|
||||
tstConf = Config()
|
||||
@@ -225,6 +225,11 @@ def testBaseConfig_Methods(fncPath):
|
||||
tmpStuff.rmdir()
|
||||
assert tstConf.lastPath() == Path.home().absolute()
|
||||
|
||||
# Backup Path
|
||||
assert tstConf.backupPath() == tstConf._backPath
|
||||
tstConf.setBackupPath(fncPath)
|
||||
assert tstConf.backupPath() == fncPath
|
||||
|
||||
# Recent Projects
|
||||
assert isinstance(tstConf.recentProjects, RecentProjects)
|
||||
|
||||
@@ -233,8 +238,7 @@ def testBaseConfig_Methods(fncPath):
|
||||
|
||||
@pytest.mark.base
|
||||
def testBaseConfig_SettersGetters(fncPath):
|
||||
"""Set various sizes and positions
|
||||
"""
|
||||
"""Set various sizes and positions."""
|
||||
tstConf = Config()
|
||||
tstConf.initConfig(confPath=fncPath, dataPath=fncPath)
|
||||
|
||||
@@ -358,8 +362,7 @@ def testBaseConfig_SettersGetters(fncPath):
|
||||
|
||||
@pytest.mark.base
|
||||
def testBaseConfig_Internal(monkeypatch, fncPath):
|
||||
"""Check internal functions.
|
||||
"""
|
||||
"""Check internal functions."""
|
||||
tstConf = Config()
|
||||
tstConf.initConfig(confPath=fncPath, dataPath=fncPath)
|
||||
|
||||
@@ -381,8 +384,7 @@ def testBaseConfig_Internal(monkeypatch, fncPath):
|
||||
|
||||
@pytest.mark.base
|
||||
def testBaseConfig_RecentCache(monkeypatch, tstPaths):
|
||||
"""Test recent cache file.
|
||||
"""
|
||||
"""Test recent cache file."""
|
||||
cacheFile = tstPaths.cnfDir / nwFiles.RECENT_FILE
|
||||
recent = RecentProjects(CONFIG)
|
||||
|
||||
|
||||
@@ -27,8 +27,9 @@ from pathlib import Path
|
||||
from tools import C, buildTestProject
|
||||
from mocked import causeOSError
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import QMessageBox, QMenu, QTreeWidgetItem, QDialog
|
||||
from PyQt5.QtGui import QDragMoveEvent, QDropEvent
|
||||
from PyQt5.QtCore import 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
|
||||
@@ -46,7 +47,7 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, projPath, mockRn
|
||||
|
||||
projView = nwGUI.projView
|
||||
projTree = nwGUI.projView.projTree
|
||||
theProject = SHARED.project
|
||||
project = SHARED.project
|
||||
|
||||
# Try to add item with no project
|
||||
assert projView.projTree.newTreeItem(nwItemType.FILE) is False
|
||||
@@ -66,7 +67,7 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, projPath, mockRn
|
||||
|
||||
# Create root item
|
||||
assert projView.projTree.newTreeItem(nwItemType.ROOT, nwItemClass.WORLD) is True
|
||||
assert "0000000000010" in theProject.tree
|
||||
assert "0000000000010" in project.tree
|
||||
|
||||
# File/Folder Items
|
||||
# =================
|
||||
@@ -81,51 +82,51 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, projPath, mockRn
|
||||
# Create new folder as child of Novel folder
|
||||
projView.setSelectedHandle(C.hNovelRoot)
|
||||
assert projView.projTree.newTreeItem(nwItemType.FOLDER) is True
|
||||
assert theProject.tree["0000000000011"].itemParent == C.hNovelRoot
|
||||
assert theProject.tree["0000000000011"].itemRoot == C.hNovelRoot
|
||||
assert theProject.tree["0000000000011"].itemClass == nwItemClass.NOVEL
|
||||
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 theProject.tree["0000000000012"].itemParent == "0000000000011"
|
||||
assert theProject.tree["0000000000012"].itemRoot == C.hNovelRoot
|
||||
assert theProject.tree["0000000000012"].itemClass == nwItemClass.NOVEL
|
||||
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 theProject.tree["0000000000013"].itemParent == "0000000000011"
|
||||
assert theProject.tree["0000000000013"].itemRoot == C.hNovelRoot
|
||||
assert theProject.tree["0000000000013"].itemClass == nwItemClass.NOVEL
|
||||
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"
|
||||
|
||||
# Add a new scene next to the other new file
|
||||
projView.setSelectedHandle("0000000000012")
|
||||
assert projView.projTree.newTreeItem(nwItemType.FILE, hLevel=3) is True
|
||||
assert theProject.tree["0000000000014"].itemParent == "0000000000011"
|
||||
assert theProject.tree["0000000000014"].itemRoot == C.hNovelRoot
|
||||
assert theProject.tree["0000000000014"].itemClass == nwItemClass.NOVEL
|
||||
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"
|
||||
|
||||
# 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 theProject.tree["0000000000015"].itemParent == C.hCharRoot
|
||||
assert theProject.tree["0000000000015"].itemRoot == C.hCharRoot
|
||||
assert theProject.tree["0000000000015"].itemClass == nwItemClass.CHARACTER
|
||||
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"
|
||||
|
||||
# Make sure the sibling folder bug trap works
|
||||
projView.setSelectedHandle("0000000000013")
|
||||
theProject.tree["0000000000013"].setParent(None) # This should not happen
|
||||
project.tree["0000000000013"].setParent(None) # This should not happen # type: ignore
|
||||
caplog.clear()
|
||||
assert projView.projTree.newTreeItem(nwItemType.FILE) is False
|
||||
assert "Internal error" in caplog.text
|
||||
theProject.tree["0000000000013"].setParent("0000000000011")
|
||||
project.tree["0000000000013"].setParent("0000000000011") # type: ignore
|
||||
|
||||
# Cancel during creation
|
||||
with monkeypatch.context() as mp:
|
||||
@@ -135,7 +136,7 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, projPath, mockRn
|
||||
|
||||
# Get the trash folder
|
||||
projView.projTree._addTrashRoot()
|
||||
trashHandle = theProject.trashFolder()
|
||||
trashHandle = project.trashFolder()
|
||||
projView.setSelectedHandle(trashHandle)
|
||||
assert projView.projTree.newTreeItem(nwItemType.FILE) is False
|
||||
assert "Cannot add new files or folders to the Trash folder" in caplog.text
|
||||
@@ -147,7 +148,7 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, projPath, mockRn
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(GuiEditLabel, "getLabel", lambda *a, **k: ("Stuff", True))
|
||||
projTree.renameTreeItem(C.hPlotRoot) is True
|
||||
assert theProject.tree[C.hPlotRoot].itemName == "Stuff"
|
||||
assert project.tree[C.hPlotRoot].itemName == "Stuff" # type: ignore
|
||||
|
||||
# Rename invalid folder
|
||||
projTree.renameTreeItem("0000000000000") is False
|
||||
@@ -159,7 +160,7 @@ def testGuiProjTree_NewItems(qtbot, caplog, monkeypatch, nwGUI, projPath, mockRn
|
||||
assert projView.projTree.revealNewTreeItem("abc") is False
|
||||
|
||||
# Add an item that cannot be displayed in the tree
|
||||
nHandle = theProject.newFile("Test", None)
|
||||
nHandle = project.newFile("Test", None) # type: ignore
|
||||
assert projView.projTree.revealNewTreeItem(nHandle) is False
|
||||
|
||||
# Adding an invalid item directly to the tree should also fail
|
||||
@@ -550,7 +551,7 @@ def testGuiProjTree_ContextMenu(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
||||
|
||||
SHARED.project.newFile("SubNote", hNovelNote)
|
||||
projTree.revealNewTreeItem(hSubNote)
|
||||
assert SHARED.project.tree[hSubNote].itemParent == hNovelNote
|
||||
assert SHARED.project.tree[hSubNote].itemParent == hNovelNote # type: ignore
|
||||
|
||||
def itemPos(tHandle):
|
||||
return projTree.visualItemRect(projTree._getTreeItem(tHandle)).center()
|
||||
@@ -579,6 +580,7 @@ def testGuiProjTree_ContextMenu(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
||||
# =====================
|
||||
# Trigger the dedicated functions the menu entries connect to
|
||||
nwItem = SHARED.project.tree[hNovelNote]
|
||||
assert nwItem is not None
|
||||
|
||||
# Toggle active flag
|
||||
assert nwItem.isActive is True
|
||||
@@ -619,17 +621,17 @@ def testGuiProjTree_ContextMenu(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(QMessageBox, "result", lambda *a: QMessageBox.No)
|
||||
projTree._covertFolderToFile(hNewFolderOne, nwItemLayout.DOCUMENT)
|
||||
assert SHARED.project.tree[hNewFolderOne].isFolderType()
|
||||
assert SHARED.project.tree[hNewFolderOne].isFolderType() # type: ignore
|
||||
|
||||
# Convert the first folder to a document
|
||||
projTree._covertFolderToFile(hNewFolderOne, nwItemLayout.DOCUMENT)
|
||||
assert SHARED.project.tree[hNewFolderOne].isFileType()
|
||||
assert SHARED.project.tree[hNewFolderOne].isDocumentLayout()
|
||||
assert SHARED.project.tree[hNewFolderOne].isFileType() # type: ignore
|
||||
assert SHARED.project.tree[hNewFolderOne].isDocumentLayout() # type: ignore
|
||||
|
||||
# Convert the second folder to a note
|
||||
projTree._covertFolderToFile(hNewFolderTwo, nwItemLayout.NOTE)
|
||||
assert SHARED.project.tree[hNewFolderTwo].isFileType()
|
||||
assert SHARED.project.tree[hNewFolderTwo].isNoteLayout()
|
||||
assert SHARED.project.tree[hNewFolderTwo].isFileType() # type: ignore
|
||||
assert SHARED.project.tree[hNewFolderTwo].isNoteLayout() # type: ignore
|
||||
|
||||
# qtbot.stop()
|
||||
|
||||
@@ -646,19 +648,18 @@ def testGuiProjTree_MergeDocuments(qtbot, monkeypatch, nwGUI, projPath, mockRnd,
|
||||
monkeypatch.setattr(GuiDocMerge, "result", lambda *a: QDialog.Accepted)
|
||||
monkeypatch.setattr(GuiDocMerge, "getData", lambda *a: mergeData)
|
||||
|
||||
# Create a project
|
||||
buildTestProject(nwGUI, projPath)
|
||||
|
||||
theProject = SHARED.project
|
||||
project = SHARED.project
|
||||
projTree = nwGUI.projView.projTree
|
||||
|
||||
mergedDoc1 = "0000000000014"
|
||||
|
||||
# Create File to Merge
|
||||
hChapter1 = theProject.newFile("Chapter 1", C.hNovelRoot)
|
||||
hSceneOne11 = theProject.newFile("Scene 1.1", hChapter1)
|
||||
hSceneOne12 = theProject.newFile("Scene 1.2", hChapter1)
|
||||
hSceneOne13 = theProject.newFile("Scene 1.3", hChapter1)
|
||||
hChapter1 = project.newFile("Chapter 1", C.hNovelRoot)
|
||||
hSceneOne11 = project.newFile("Scene 1.1", hChapter1) # type: ignore
|
||||
hSceneOne12 = project.newFile("Scene 1.2", hChapter1) # type: ignore
|
||||
hSceneOne13 = project.newFile("Scene 1.3", hChapter1) # type: ignore
|
||||
|
||||
docText1 = "\n\n".join(ipsumText[0:2]) + "\n\n"
|
||||
docText2 = "\n\n".join(ipsumText[1:3]) + "\n\n"
|
||||
@@ -671,10 +672,10 @@ def testGuiProjTree_MergeDocuments(qtbot, monkeypatch, nwGUI, projPath, mockRnd,
|
||||
lenText4 = len(docText4)
|
||||
lenAll = lenText1 + lenText2 + lenText3 + lenText4
|
||||
|
||||
theProject.writeNewFile(hChapter1, 2, True, docText1)
|
||||
theProject.writeNewFile(hSceneOne11, 3, True, docText2)
|
||||
theProject.writeNewFile(hSceneOne12, 3, True, docText3)
|
||||
theProject.writeNewFile(hSceneOne13, 3, True, docText4)
|
||||
project.writeNewFile(hChapter1, 2, True, docText1) # type: ignore
|
||||
project.writeNewFile(hSceneOne11, 3, True, docText2) # type: ignore
|
||||
project.writeNewFile(hSceneOne12, 3, True, docText3) # type: ignore
|
||||
project.writeNewFile(hSceneOne13, 3, True, docText4) # type: ignore
|
||||
|
||||
projTree.revealNewTreeItem(hChapter1)
|
||||
projTree.revealNewTreeItem(hSceneOne11)
|
||||
@@ -705,7 +706,7 @@ def testGuiProjTree_MergeDocuments(qtbot, monkeypatch, nwGUI, projPath, mockRnd,
|
||||
|
||||
# The merge goes through
|
||||
assert projTree._mergeDocuments(hChapter1, True) is True
|
||||
assert len(theProject.storage.getDocument(mergedDoc1).readDocument()) > lenAll
|
||||
assert len(project.storage.getDocument(mergedDoc1).readDocument()) > lenAll # type: ignore
|
||||
|
||||
# Merge to Existing Doc
|
||||
# =====================
|
||||
@@ -724,13 +725,13 @@ def testGuiProjTree_MergeDocuments(qtbot, monkeypatch, nwGUI, projPath, mockRnd,
|
||||
|
||||
# Successful merge, and move to trash
|
||||
mergeData["moveToTrash"] = True
|
||||
assert len(theProject.storage.getDocument(hChapter1).readDocument()) < lenAll
|
||||
assert len(project.storage.getDocument(hChapter1).readDocument()) < lenAll # type: ignore
|
||||
assert projTree._mergeDocuments(hChapter1, False) is True
|
||||
assert len(theProject.storage.getDocument(hChapter1).readDocument()) > lenAll
|
||||
assert len(project.storage.getDocument(hChapter1).readDocument()) > lenAll # type: ignore
|
||||
|
||||
assert theProject.tree.isTrash(hSceneOne11)
|
||||
assert theProject.tree.isTrash(hSceneOne12)
|
||||
assert theProject.tree.isTrash(hSceneOne13)
|
||||
assert project.tree.isTrash(hSceneOne11) # type: ignore
|
||||
assert project.tree.isTrash(hSceneOne12) # type: ignore
|
||||
assert project.tree.isTrash(hSceneOne13) # type: ignore
|
||||
|
||||
# qtbot.stop()
|
||||
|
||||
@@ -770,7 +771,7 @@ def testGuiProjTree_SplitDocument(qtbot, monkeypatch, nwGUI, projPath, mockRnd,
|
||||
)
|
||||
|
||||
hSplitDoc = theProject.newFile("Split Doc", C.hNovelRoot)
|
||||
theProject.writeNewFile(hSplitDoc, 1, True, docText)
|
||||
theProject.writeNewFile(hSplitDoc, 1, True, docText) # type: ignore
|
||||
projTree.revealNewTreeItem(hSplitDoc, nHandle=C.hNovelRoot, wordCount=True)
|
||||
|
||||
docText = f"# Split Doc\n\n{docText}"
|
||||
@@ -835,7 +836,7 @@ def testGuiProjTree_SplitDocument(qtbot, monkeypatch, nwGUI, projPath, mockRnd,
|
||||
assert tHandle in theProject.tree
|
||||
assert (projPath / "content" / f"{tHandle}.nwd").is_file()
|
||||
|
||||
assert theProject.tree.isTrash(hSplitDoc) is True
|
||||
assert theProject.tree.isTrash(hSplitDoc) is True # type: ignore
|
||||
|
||||
# Cancelled by user
|
||||
with monkeypatch.context() as mp:
|
||||
@@ -880,7 +881,7 @@ def testGuiProjTree_Duplicate(qtbot, monkeypatch, nwGUI: GuiMain, projPath, mock
|
||||
assert projTree._duplicateFromHandle(C.hNovelRoot) is True
|
||||
assert len(SHARED.project.tree) == 21
|
||||
|
||||
# Check tree order that all items are next to eachother
|
||||
# Check tree order that all items are next to each other
|
||||
assert SHARED.project.tree._order == [
|
||||
C.hNovelRoot, C.hTitlePage, "0000000000010", C.hChapterDir, C.hChapterDoc, C.hSceneDoc,
|
||||
"0000000000011", "0000000000012", "0000000000013", "0000000000014", "0000000000015",
|
||||
@@ -904,14 +905,136 @@ def testGuiProjTree_Duplicate(qtbot, monkeypatch, nwGUI: GuiMain, projPath, mock
|
||||
# END Test testGuiProjTree_Duplicate
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testGuiProjTree_AutoScroll(qtbot, monkeypatch, nwGUI: GuiMain, projPath, mockRnd):
|
||||
"""Test the auto scroll feature."""
|
||||
buildTestProject(nwGUI, projPath)
|
||||
projTree: GuiProjectTree = nwGUI.projView.projTree
|
||||
|
||||
monkeypatch.setattr(GuiEditLabel, "getLabel", lambda parent, text: (text, True))
|
||||
monkeypatch.setattr(QTreeWidget, "dragMoveEvent", lambda *a, **k: None)
|
||||
monkeypatch.setattr(QTimer, "isActive", lambda *a: False)
|
||||
monkeypatch.setattr(QTimer, "start", lambda *a: None)
|
||||
|
||||
projTree.setSelectedHandle(C.hChapterDir, True)
|
||||
projTree._getTreeItem(C.hChapterDir).setExpanded(True) # type: ignore
|
||||
for i in range(100):
|
||||
projTree.newTreeItem(nwItemType.FILE, None, 3, False)
|
||||
|
||||
projTree.setSelectedHandle("0000000000015", True)
|
||||
nwGUI.resize(500, 500)
|
||||
|
||||
action = Qt.DropAction.MoveAction
|
||||
mime = QMimeData()
|
||||
mouse = Qt.MouseButton.LeftButton
|
||||
modifier = Qt.KeyboardModifier.NoModifier
|
||||
|
||||
# Scroll Down
|
||||
h = projTree.height()
|
||||
projTree.dragMoveEvent(QDragMoveEvent(QPoint(1, h-1), action, mime, mouse, modifier))
|
||||
assert projTree._scrollDirection == 1
|
||||
projTree._doAutoScroll()
|
||||
assert projTree._scrollDirection == 0
|
||||
projTree.dragMoveEvent(QDragMoveEvent(QPoint(1, h-2), action, mime, mouse, modifier))
|
||||
assert projTree._scrollDirection == 1
|
||||
projTree._doAutoScroll()
|
||||
assert projTree._scrollDirection == 0
|
||||
projTree.dragMoveEvent(QDragMoveEvent(QPoint(1, h-3), action, mime, mouse, modifier))
|
||||
assert projTree._scrollDirection == 1
|
||||
projTree._doAutoScroll()
|
||||
assert projTree._scrollDirection == 0
|
||||
projTree.dragMoveEvent(QDragMoveEvent(QPoint(1, h-4), action, mime, mouse, modifier))
|
||||
assert projTree._scrollDirection == 1
|
||||
projTree._doAutoScroll()
|
||||
assert projTree._scrollDirection == 0
|
||||
|
||||
# Scroll Up
|
||||
projTree.dragMoveEvent(QDragMoveEvent(QPoint(1, 1), action, mime, mouse, modifier))
|
||||
assert projTree._scrollDirection == -1
|
||||
projTree._doAutoScroll()
|
||||
assert projTree._scrollDirection == 0
|
||||
projTree.dragMoveEvent(QDragMoveEvent(QPoint(1, 2), action, mime, mouse, modifier))
|
||||
assert projTree._scrollDirection == -1
|
||||
projTree._doAutoScroll()
|
||||
assert projTree._scrollDirection == 0
|
||||
projTree.dragMoveEvent(QDragMoveEvent(QPoint(1, 3), action, mime, mouse, modifier))
|
||||
assert projTree._scrollDirection == -1
|
||||
projTree._doAutoScroll()
|
||||
assert projTree._scrollDirection == 0
|
||||
projTree.dragMoveEvent(QDragMoveEvent(QPoint(1, 4), action, mime, mouse, modifier))
|
||||
assert projTree._scrollDirection == -1
|
||||
projTree._doAutoScroll()
|
||||
assert projTree._scrollDirection == 0
|
||||
|
||||
# qtbot.stop()
|
||||
|
||||
# END Test testGuiProjTree_AutoScroll
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testGuiProjTree_DragAndDrop(qtbot, monkeypatch, caplog, nwGUI: GuiMain, projPath, mockRnd):
|
||||
"""Test the auto scroll feature."""
|
||||
buildTestProject(nwGUI, projPath)
|
||||
projTree: GuiProjectTree = nwGUI.projView.projTree
|
||||
|
||||
monkeypatch.setattr(QTreeWidget, "dropEvent", lambda *a, **k: None)
|
||||
|
||||
projTree.setSelectedHandle(C.hSceneDoc, True)
|
||||
projTree._getTreeItem(C.hChapterDir).setExpanded(True) # type: ignore
|
||||
|
||||
nPos = projTree.visualItemRect(projTree._getTreeItem(C.hNovelRoot)).bottomLeft()
|
||||
action = Qt.DropAction.MoveAction
|
||||
mime = QMimeData()
|
||||
mouse = Qt.MouseButton.LeftButton
|
||||
modifier = Qt.KeyboardModifier.NoModifier
|
||||
|
||||
# Move an item
|
||||
# The actual move is blocked, but the undo history should record
|
||||
# the event, although without an actual move implied
|
||||
assert projTree._lastMove == {}
|
||||
event = QDropEvent(nPos, action, mime, mouse, modifier)
|
||||
projTree.dropEvent(event)
|
||||
assert projTree._lastMove == {
|
||||
"item": projTree._getTreeItem(C.hSceneDoc),
|
||||
"parent": projTree._getTreeItem(C.hChapterDir),
|
||||
"index": 1,
|
||||
}
|
||||
|
||||
# Invalid location
|
||||
caplog.clear()
|
||||
event = QDropEvent(QPoint(1000, 1000), action, mime, mouse, modifier)
|
||||
projTree.dropEvent(event)
|
||||
assert event.isAccepted() is False
|
||||
assert "Invalid drop location" in caplog.text
|
||||
|
||||
# No item selected
|
||||
caplog.clear()
|
||||
event = QDropEvent(nPos, action, mime, mouse, modifier)
|
||||
projTree.clearSelection()
|
||||
projTree.dropEvent(event)
|
||||
assert event.isAccepted() is False
|
||||
assert "Invalid drag and drop event" in caplog.text
|
||||
|
||||
# Root item selected
|
||||
caplog.clear()
|
||||
event = QDropEvent(nPos, action, mime, mouse, modifier)
|
||||
projTree.clearSelection()
|
||||
projTree.setSelectedHandle(C.hNovelRoot, True)
|
||||
projTree.dropEvent(event)
|
||||
assert event.isAccepted() is False
|
||||
assert "Invalid drag and drop event" in caplog.text
|
||||
|
||||
# qtbot.stop()
|
||||
|
||||
# END Test testGuiProjTree_DragAndDrop
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testGuiProjTree_Other(qtbot, monkeypatch, nwGUI: GuiMain, projPath, mockRnd):
|
||||
"""Test various parts of the project tree class not covered by
|
||||
other tests.
|
||||
"""
|
||||
# Create a project
|
||||
buildTestProject(nwGUI, projPath)
|
||||
|
||||
projView: GuiProjectView = nwGUI.projView
|
||||
projTree: GuiProjectTree = nwGUI.projView.projTree
|
||||
|
||||
|
||||
Reference in New Issue
Block a user