Skip to content

Commit

Permalink
Add coverage to remaining missed lines
Browse files Browse the repository at this point in the history
  • Loading branch information
vkbo committed Nov 24, 2024
1 parent e30d2cb commit 4cadd15
Show file tree
Hide file tree
Showing 8 changed files with 199 additions and 44 deletions.
4 changes: 1 addition & 3 deletions novelwriter/extensions/configlayout.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ def addGroupLabel(self, label: str, identifier: int | None = None) -> None:
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,
Expand All @@ -204,8 +204,6 @@ def addRow(
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)
Expand Down
18 changes: 1 addition & 17 deletions novelwriter/gui/itemdetails.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,12 +225,7 @@ def updateTheme(self) -> None:

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

Expand Down Expand Up @@ -297,14 +292,3 @@ def onProjectItemChanged(self, tHandle: str, change: nwChange) -> None:
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
17 changes: 8 additions & 9 deletions novelwriter/gui/projtree.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ def setActiveHandle(self, tHandle: str | None) -> None:
@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)
Expand Down Expand Up @@ -404,17 +404,16 @@ def buildTemplatesMenu(self) -> None:
"""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

##
Expand Down
81 changes: 75 additions & 6 deletions tests/test_gui/test_gui_doceditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,23 @@

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
Expand Down Expand Up @@ -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."""
Expand Down
80 changes: 77 additions & 3 deletions tests/test_gui/test_gui_docviewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,21 @@

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
Expand Down Expand Up @@ -238,3 +243,72 @@ def mockExec(*a):
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
11 changes: 11 additions & 0 deletions tests/test_gui/test_gui_guimain.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()


Expand Down
18 changes: 17 additions & 1 deletion tests/test_gui/test_gui_projtree.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."
Expand Down Expand Up @@ -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
Expand Down
14 changes: 9 additions & 5 deletions tests/test_gui/test_gui_statusbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@


@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)
Expand Down Expand Up @@ -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()

0 comments on commit 4cadd15

Please sign in to comment.