Add coverage to remaining missed lines
This commit is contained in:
@@ -183,7 +183,7 @@ class NScrollableForm(QScrollArea):
|
||||
def addRow(
|
||||
self,
|
||||
label: str | None,
|
||||
widget: QWidget | list[QWidget | QPixmap | str | int],
|
||||
widget: QWidget | list[QWidget | QPixmap | int],
|
||||
helpText: str = "",
|
||||
unit: str | None = None,
|
||||
button: QWidget | None = None,
|
||||
@@ -204,8 +204,6 @@ class NScrollableForm(QScrollArea):
|
||||
icon = QLabel(self)
|
||||
icon.setPixmap(item)
|
||||
wBox.addWidget(icon)
|
||||
elif isinstance(item, str):
|
||||
wBox.addWidget(QLabel(item, self))
|
||||
elif isinstance(item, int):
|
||||
wBox.addSpacing(CONFIG.pxInt(item))
|
||||
qWidget = QWidget(self)
|
||||
|
||||
@@ -225,12 +225,7 @@ class GuiItemDetails(QWidget):
|
||||
|
||||
def updateViewBox(self, tHandle: str | None) -> None:
|
||||
"""Populate the details box from a given handle."""
|
||||
if tHandle is None:
|
||||
self.clearDetails()
|
||||
return
|
||||
|
||||
nwItem = SHARED.project.tree[tHandle]
|
||||
if nwItem is None:
|
||||
if not (tHandle and (nwItem := SHARED.project.tree[tHandle])):
|
||||
self.clearDetails()
|
||||
return
|
||||
|
||||
@@ -297,14 +292,3 @@ class GuiItemDetails(QWidget):
|
||||
elif change == nwChange.DELETE:
|
||||
self.updateViewBox(None)
|
||||
return
|
||||
|
||||
@pyqtSlot(str, int, int, int)
|
||||
def updateCounts(self, tHandle: str, cC: int, wC: int, pC: int) -> None:
|
||||
"""Update the counts if the handle is the same as the one we're
|
||||
already showing. Otherwise, do nothing.
|
||||
"""
|
||||
if tHandle == self._handle:
|
||||
self.cCountData.setText(f"{cC:n}")
|
||||
self.wCountData.setText(f"{wC:n}")
|
||||
self.pCountData.setText(f"{pC:n}")
|
||||
return
|
||||
|
||||
@@ -213,7 +213,7 @@ class GuiProjectView(QWidget):
|
||||
@pyqtSlot(str, Enum)
|
||||
def onProjectItemChanged(self, tHandle: str, change: nwChange) -> None:
|
||||
"""Refresh other content when project item changed."""
|
||||
self.projBar.processTemplateDocuments(tHandle, change)
|
||||
self.projBar.processTemplateDocuments(tHandle)
|
||||
return
|
||||
|
||||
@pyqtSlot(str)
|
||||
@@ -404,17 +404,16 @@ class GuiProjectToolBar(QWidget):
|
||||
"""Build the templates menu."""
|
||||
for tHandle, _ in SHARED.project.tree.iterRoots(nwItemClass.TEMPLATE):
|
||||
for dHandle in SHARED.project.tree.subTree(tHandle):
|
||||
self.processTemplateDocuments(dHandle, nwChange.CREATE)
|
||||
self.processTemplateDocuments(dHandle)
|
||||
return
|
||||
|
||||
def processTemplateDocuments(self, tHandle: str, change: nwChange) -> None:
|
||||
def processTemplateDocuments(self, tHandle: str) -> None:
|
||||
"""Process change in tree items to update menu content."""
|
||||
if change in (nwChange.CREATE, nwChange.UPDATE):
|
||||
if item := SHARED.project.tree[tHandle]:
|
||||
if item.isTemplateFile() and item.isActive:
|
||||
self.mTemplates.addUpdate(tHandle, item.itemName, item.getMainIcon())
|
||||
elif tHandle in self.mTemplates:
|
||||
self.mTemplates.remove(tHandle)
|
||||
if item := SHARED.project.tree[tHandle]:
|
||||
if item.isTemplateFile() and item.isActive:
|
||||
self.mTemplates.addUpdate(tHandle, item.itemName, item.getMainIcon())
|
||||
elif tHandle in self.mTemplates:
|
||||
self.mTemplates.remove(tHandle)
|
||||
return
|
||||
|
||||
##
|
||||
|
||||
@@ -24,22 +24,23 @@ from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from PyQt5.QtCore import QEvent, Qt, QThreadPool, QUrl
|
||||
from PyQt5.QtCore import QEvent, QMimeData, Qt, QThreadPool, QUrl
|
||||
from PyQt5.QtGui import (
|
||||
QClipboard, QDesktopServices, QFont, QMouseEvent, QTextBlock, QTextCursor,
|
||||
QTextOption
|
||||
QClipboard, QDesktopServices, QDragEnterEvent, QDragMoveEvent, QDropEvent,
|
||||
QFont, QMouseEvent, QTextBlock, QTextCursor, QTextOption
|
||||
)
|
||||
from PyQt5.QtWidgets import QAction, QApplication, QMenu
|
||||
from PyQt5.QtWidgets import QAction, QApplication, QMenu, QPlainTextEdit
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
from novelwriter.common import decodeMimeHandles
|
||||
from novelwriter.constants import nwKeyWords, nwUnicode
|
||||
from novelwriter.dialogs.editlabel import GuiEditLabel
|
||||
from novelwriter.enum import nwDocAction, nwDocInsert, nwItemClass, nwItemLayout, nwTrinary
|
||||
from novelwriter.gui.doceditor import GuiDocEditor
|
||||
from novelwriter.text.counting import standardCounter
|
||||
from novelwriter.types import (
|
||||
QtAlignJustify, QtAlignLeft, QtKeepAnchor, QtModCtrl, QtMouseLeft,
|
||||
QtMoveAnchor, QtMoveRight, QtScrollAlwaysOff, QtScrollAsNeeded
|
||||
QtAlignJustify, QtAlignLeft, QtKeepAnchor, QtModCtrl, QtModNone,
|
||||
QtMouseLeft, QtMoveAnchor, QtMoveRight, QtScrollAlwaysOff, QtScrollAsNeeded
|
||||
)
|
||||
|
||||
from tests.mocked import causeOSError
|
||||
@@ -209,6 +210,74 @@ def testGuiEditor_SaveText(qtbot, monkeypatch, caplog, nwGUI, projPath, ipsumTex
|
||||
# qtbot.stop()
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testGuiEditor_DragAndDrop(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
||||
"""Test drag and drop in the editor."""
|
||||
docEditor = nwGUI.docEditor
|
||||
|
||||
buildTestProject(nwGUI, projPath)
|
||||
assert nwGUI.openDocument(C.hTitlePage) is True
|
||||
assert docEditor.docHandle == C.hTitlePage
|
||||
|
||||
middle = docEditor.viewport().rect().center()
|
||||
action = Qt.DropAction.MoveAction
|
||||
mouse = Qt.MouseButton.NoButton
|
||||
|
||||
model = SHARED.project.tree.model
|
||||
docMime = model.mimeData([model.indexFromHandle(C.hSceneDoc)])
|
||||
noneMime = QMimeData()
|
||||
noneMime.setData("plain/text", b"")
|
||||
assert decodeMimeHandles(docMime) == [C.hSceneDoc]
|
||||
|
||||
# Drag Enter
|
||||
mockEnter = MagicMock()
|
||||
docEvent = QDragEnterEvent(middle, action, docMime, mouse, QtModNone)
|
||||
noneEvent = QDragEnterEvent(middle, action, noneMime, mouse, QtModNone)
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(QPlainTextEdit, "dragEnterEvent", mockEnter)
|
||||
|
||||
# Document Enter
|
||||
docEditor.dragEnterEvent(docEvent)
|
||||
assert docEvent.isAccepted() is True
|
||||
assert mockEnter.call_count == 0
|
||||
|
||||
# Regular Enter
|
||||
docEditor.dragEnterEvent(noneEvent)
|
||||
assert mockEnter.call_count == 1
|
||||
|
||||
# Drag Move
|
||||
mockMove = MagicMock()
|
||||
docEvent = QDragMoveEvent(middle, action, docMime, mouse, QtModNone)
|
||||
noneEvent = QDragMoveEvent(middle, action, noneMime, mouse, QtModNone)
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(QPlainTextEdit, "dragMoveEvent", mockMove)
|
||||
|
||||
# Document Move
|
||||
docEditor.dragMoveEvent(docEvent)
|
||||
assert docEvent.isAccepted() is True
|
||||
assert mockMove.call_count == 0
|
||||
|
||||
# Regular Move
|
||||
docEditor.dragMoveEvent(noneEvent)
|
||||
assert mockMove.call_count == 1
|
||||
|
||||
# Drop
|
||||
mockDrop = MagicMock()
|
||||
docEvent = QDropEvent(middle, action, docMime, mouse, QtModNone)
|
||||
noneEvent = QDropEvent(middle, action, noneMime, mouse, QtModNone)
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(QPlainTextEdit, "dropEvent", mockDrop)
|
||||
|
||||
# Document Drop
|
||||
docEditor.dropEvent(docEvent)
|
||||
assert mockDrop.call_count == 0
|
||||
assert docEditor.docHandle == C.hSceneDoc
|
||||
|
||||
# Regular Move
|
||||
docEditor.dropEvent(noneEvent)
|
||||
assert mockDrop.call_count == 1
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testGuiEditor_MetaData(qtbot, nwGUI, projPath, mockRnd):
|
||||
"""Test extracting various meta data and other values."""
|
||||
|
||||
@@ -24,16 +24,21 @@ from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from PyQt5.QtCore import QEvent, QPoint, Qt, QUrl
|
||||
from PyQt5.QtGui import QDesktopServices, QMouseEvent, QTextCursor
|
||||
from PyQt5.QtWidgets import QAction, QApplication, QMenu
|
||||
from PyQt5.QtCore import QEvent, QMimeData, QPoint, Qt, QUrl
|
||||
from PyQt5.QtGui import (
|
||||
QDesktopServices, QDragEnterEvent, QDragMoveEvent, QDropEvent, QMouseEvent,
|
||||
QTextCursor
|
||||
)
|
||||
from PyQt5.QtWidgets import QAction, QApplication, QMenu, QTextBrowser
|
||||
|
||||
from novelwriter import CONFIG, SHARED
|
||||
from novelwriter.common import decodeMimeHandles
|
||||
from novelwriter.enum import nwChange, nwDocAction
|
||||
from novelwriter.formats.toqdoc import ToQTextDocument
|
||||
from novelwriter.types import QtModNone, QtMouseLeft
|
||||
|
||||
from tests.mocked import causeException
|
||||
from tests.tools import C, buildTestProject
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
@@ -238,3 +243,72 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, prjLipsum):
|
||||
docViewer.updateTheme()
|
||||
|
||||
# qtbot.stop()
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testGuiViewer_DragAndDrop(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
||||
"""Test drag and drop in the viewer."""
|
||||
docViewer = nwGUI.docViewer
|
||||
|
||||
buildTestProject(nwGUI, projPath)
|
||||
assert nwGUI.openDocument(C.hTitlePage) is True
|
||||
assert nwGUI.viewDocument(C.hTitlePage) is True
|
||||
assert docViewer.docHandle == C.hTitlePage
|
||||
|
||||
middle = docViewer.viewport().rect().center()
|
||||
action = Qt.DropAction.MoveAction
|
||||
mouse = Qt.MouseButton.NoButton
|
||||
|
||||
model = SHARED.project.tree.model
|
||||
docMime = model.mimeData([model.indexFromHandle(C.hSceneDoc)])
|
||||
noneMime = QMimeData()
|
||||
noneMime.setData("plain/text", b"")
|
||||
assert decodeMimeHandles(docMime) == [C.hSceneDoc]
|
||||
|
||||
# Drag Enter
|
||||
mockEnter = MagicMock()
|
||||
docEvent = QDragEnterEvent(middle, action, docMime, mouse, QtModNone)
|
||||
noneEvent = QDragEnterEvent(middle, action, noneMime, mouse, QtModNone)
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(QTextBrowser, "dragEnterEvent", mockEnter)
|
||||
|
||||
# Document Enter
|
||||
docViewer.dragEnterEvent(docEvent)
|
||||
assert docEvent.isAccepted() is True
|
||||
assert mockEnter.call_count == 0
|
||||
|
||||
# Regular Enter
|
||||
docViewer.dragEnterEvent(noneEvent)
|
||||
assert mockEnter.call_count == 1
|
||||
|
||||
# Drag Move
|
||||
mockMove = MagicMock()
|
||||
docEvent = QDragMoveEvent(middle, action, docMime, mouse, QtModNone)
|
||||
noneEvent = QDragMoveEvent(middle, action, noneMime, mouse, QtModNone)
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(QTextBrowser, "dragMoveEvent", mockMove)
|
||||
|
||||
# Document Move
|
||||
docViewer.dragMoveEvent(docEvent)
|
||||
assert docEvent.isAccepted() is True
|
||||
assert mockMove.call_count == 0
|
||||
|
||||
# Regular Move
|
||||
docViewer.dragMoveEvent(noneEvent)
|
||||
assert mockMove.call_count == 1
|
||||
|
||||
# Drop
|
||||
mockDrop = MagicMock()
|
||||
docEvent = QDropEvent(middle, action, docMime, mouse, QtModNone)
|
||||
noneEvent = QDropEvent(middle, action, noneMime, mouse, QtModNone)
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(QTextBrowser, "dropEvent", mockDrop)
|
||||
|
||||
# Document Drop
|
||||
docViewer.dropEvent(docEvent)
|
||||
assert mockDrop.call_count == 0
|
||||
assert docViewer.docHandle == C.hSceneDoc
|
||||
|
||||
# Regular Move
|
||||
docViewer.dropEvent(noneEvent)
|
||||
assert mockDrop.call_count == 1
|
||||
|
||||
@@ -702,6 +702,17 @@ def testGuiMain_Viewing(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
||||
nwGUI.viewDocument(C.hSceneDoc)
|
||||
assert nwGUI.docViewer.toPlainText() == "New Scene\nWith some stuff in it!"
|
||||
|
||||
# Open with keypress
|
||||
nwGUI.closeDocViewer()
|
||||
assert nwGUI.docViewer.docHandle is None
|
||||
nwGUI.projView.setSelectedHandle(C.hSceneDoc)
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr(nwGUI.projView.projTree, "hasFocus", lambda *a: True)
|
||||
qtbot.keyClick(
|
||||
nwGUI.projView.projTree, Qt.Key.Key_Return, modifier=QtModShift, delay=KEY_DELAY
|
||||
)
|
||||
assert nwGUI.docViewer.docHandle == C.hSceneDoc
|
||||
|
||||
# qtbot.stop()
|
||||
|
||||
|
||||
|
||||
@@ -663,12 +663,22 @@ def testGuiProjTree_DeleteRequest(qtbot, caplog, monkeypatch, nwGUI, projPath, m
|
||||
"Scene 3", "Scene 4",
|
||||
]
|
||||
|
||||
# Opening documents before delete, will close them
|
||||
nwGUI.openDocument(hScenes[2])
|
||||
nwGUI.viewDocument(hScenes[3])
|
||||
|
||||
assert nwGUI.docEditor.docHandle == hScenes[2]
|
||||
assert nwGUI.docViewer.docHandle == hScenes[3]
|
||||
|
||||
# Trash can be completely emptied
|
||||
projTree.emptyTrash()
|
||||
assert [n.item.itemName for n in tree.model.root.allChildren()] == [
|
||||
"Novel", "Title Page", "Chapter Folder", "Plot", "Characters", "Trash",
|
||||
]
|
||||
|
||||
assert nwGUI.docEditor.docHandle is None
|
||||
assert nwGUI.docViewer.docHandle is None
|
||||
|
||||
# Emptying empty trash pops an alert
|
||||
projTree.emptyTrash()
|
||||
assert SHARED.lastAlert == "The Trash folder is already empty."
|
||||
@@ -1423,7 +1433,13 @@ def testGuiProjTree_Templates(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
||||
assert nwNewCharacter.itemName == "Note"
|
||||
assert project.storage.getDocument(hNewCharacter).readDocument() == "# Jane\n\n@tag: Jane\n\n"
|
||||
|
||||
# Remove the templates
|
||||
# Clearing the menu and rebuilding it should work
|
||||
projBar.mTemplates.clearMenu()
|
||||
assert len(projBar.mTemplates.actions()) == 0
|
||||
projBar.buildTemplatesMenu()
|
||||
assert len(projBar.mTemplates.actions()) == 2
|
||||
|
||||
# Remove the note template
|
||||
assert projBar.mTemplates.menuAction().isVisible() is True
|
||||
assert len(projBar.mTemplates.actions()) == 2
|
||||
assert trash.childCount() == 0
|
||||
|
||||
@@ -31,7 +31,7 @@ from tests.tools import C, buildTestProject
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testGuiStatusBar_Main(qtbot, nwGUI, projPath, mockRnd):
|
||||
def testGuiStatusBar_Main(qtbot, monkeypatch, nwGUI, projPath, mockRnd):
|
||||
"""Test the the various features of the status bar."""
|
||||
buildTestProject(nwGUI, projPath)
|
||||
cHandle = SHARED.project.newFile("A Note", C.hCharRoot)
|
||||
@@ -89,9 +89,13 @@ def testGuiStatusBar_Main(qtbot, nwGUI, projPath, mockRnd):
|
||||
nwGUI._lastTotalCount = 0
|
||||
nwGUI._updateStatusWordCount()
|
||||
assert nwGUI.mainStatus.statsText.text() == "Words: 9 (+9)"
|
||||
CONFIG.incNotesWCount = True
|
||||
nwGUI._lastTotalCount = 0
|
||||
nwGUI._updateStatusWordCount()
|
||||
assert nwGUI.mainStatus.statsText.text() == "Words: 11 (+11)"
|
||||
|
||||
# Update again, but through time tick
|
||||
with monkeypatch.context() as mp:
|
||||
mp.setattr("novelwriter.guimain.time", lambda *a: 50.0)
|
||||
CONFIG.incNotesWCount = True
|
||||
nwGUI._lastTotalCount = 0
|
||||
nwGUI._timeTick()
|
||||
assert nwGUI.mainStatus.statsText.text() == "Words: 11 (+11)"
|
||||
|
||||
# qtbot.stop()
|
||||
|
||||
Reference in New Issue
Block a user