From f2fc19b168765ae7d509b0b847b214e49bb952b5 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Sat, 26 Oct 2024 23:28:34 +0200
Subject: [PATCH] Add test coverage of new features
---
novelwriter/gui/doceditor.py | 2 +-
tests/test_formats/test_fmt_todocx.py | 42 +++++++++++++++++++
tests/test_gui/test_gui_doceditor.py | 49 +++++++++++++++++++++--
tests/test_gui/test_gui_docviewer.py | 12 +++++-
tests/test_tools/test_tools_manuscript.py | 20 ++++++++-
5 files changed, 117 insertions(+), 8 deletions(-)
diff --git a/novelwriter/gui/doceditor.py b/novelwriter/gui/doceditor.py
index 0ede0960..2d98567d 100644
--- a/novelwriter/gui/doceditor.py
+++ b/novelwriter/gui/doceditor.py
@@ -985,7 +985,7 @@ class GuiDocEditor(QPlainTextEdit):
pressed, check if we're clicking on a tag, and trigger the
follow tag function.
"""
- if QApplication.keyboardModifiers() == QtModCtrl:
+ if event.modifiers() & QtModCtrl == QtModCtrl:
cursor = self.cursorForPosition(event.pos())
mData, mType = self._qDocument.metaDataAtPos(cursor.position())
if mData and mType == "url":
diff --git a/tests/test_formats/test_fmt_todocx.py b/tests/test_formats/test_fmt_todocx.py
index 26567dde..e4ecc793 100644
--- a/tests/test_formats/test_fmt_todocx.py
+++ b/tests/test_formats/test_fmt_todocx.py
@@ -291,6 +291,48 @@ def testFmtToDocX_ParagraphStyles(mockGUI):
assert doc._pars == []
+@pytest.mark.core
+def testFmtToDocX_Links(mockGUI):
+ """Test formatting of links."""
+ project = NWProject()
+ doc = ToDocX(project)
+ doc.initDocument()
+
+ # Register 2 links
+ rd1 = doc._appendExternalRel("http://example.com")
+ rd2 = doc._appendExternalRel("https://example.com")
+ assert rd1 == "rId1"
+ assert rd2 == "rId2"
+
+ # Link 1
+ xTest = ET.Element(_wTag("body"))
+ doc._text = "Foo http://example.com bar"
+ doc.tokenizeText()
+ doc.doConvert()
+ doc._pars[-1].toXml(xTest)
+ assert xmlToText(xTest) == (
+ ''
+ 'Foo '
+ ''
+ 'http://example.com'
+ ' bar'
+ )
+
+ # Link 2
+ xTest = ET.Element(_wTag("body"))
+ doc._text = "Foo https://example.com bar"
+ doc.tokenizeText()
+ doc.doConvert()
+ doc._pars[-1].toXml(xTest)
+ assert xmlToText(xTest) == (
+ ''
+ 'Foo '
+ ''
+ 'https://example.com'
+ ' bar'
+ )
+
+
@pytest.mark.core
def testFmtToDocX_ParagraphFormatting(mockGUI):
"""Test formatting of paragraphs."""
diff --git a/tests/test_gui/test_gui_doceditor.py b/tests/test_gui/test_gui_doceditor.py
index 7fef876f..2e0cac3f 100644
--- a/tests/test_gui/test_gui_doceditor.py
+++ b/tests/test_gui/test_gui_doceditor.py
@@ -20,10 +20,15 @@ along with this program. If not, see .
"""
from __future__ import annotations
+from unittest.mock import MagicMock
+
import pytest
-from PyQt5.QtCore import QEvent, Qt, QThreadPool
-from PyQt5.QtGui import QClipboard, QFont, QMouseEvent, QTextBlock, QTextCursor, QTextOption
+from PyQt5.QtCore import QEvent, Qt, QThreadPool, QUrl
+from PyQt5.QtGui import (
+ QClipboard, QDesktopServices, QFont, QMouseEvent, QTextBlock, QTextCursor,
+ QTextOption
+)
from PyQt5.QtWidgets import QAction, QApplication, QMenu
from novelwriter import CONFIG, SHARED
@@ -267,8 +272,9 @@ def testGuiEditor_ContextMenu(monkeypatch, qtbot, nwGUI, projPath, mockRnd):
docText = (
"### A Scene\n\n"
- "@pov: Jane\n"
- "Some text ..."
+ "@pov: Jane\n\n"
+ "Some text ...\n\n"
+ "... and a link to http://example.com\n\n"
)
docEditor.setPlainText(docText)
assert docEditor.getText() == docText
@@ -289,6 +295,17 @@ def testGuiEditor_ContextMenu(monkeypatch, qtbot, nwGUI, projPath, mockRnd):
ctxMenu.setObjectName("")
ctxMenu.deleteLater()
+ # Open Link
+ ctxMenu = getMenuForPos(docEditor, 63)
+ assert ctxMenu is not None
+ actions = [x.text() for x in ctxMenu.actions() if x.text()]
+ assert actions == [
+ "Open URL", "Paste",
+ "Select All", "Select Word", "Select Paragraph"
+ ]
+ ctxMenu.setObjectName("")
+ ctxMenu.deleteLater()
+
# Create Character
ctxMenu = getMenuForPos(docEditor, 21)
assert ctxMenu is not None
@@ -1656,6 +1673,30 @@ def testGuiEditor_Tags(qtbot, nwGUI, projPath, ipsumText, mockRnd):
# qtbot.stop()
+@pytest.mark.gui
+def testGuiEditor_Links(qtbot, monkeypatch, nwGUI, projPath, ipsumText, mockRnd):
+ """Test the document editor links functionality."""
+ buildTestProject(nwGUI, projPath)
+ nwGUI.openDocument(C.hSceneDoc)
+ docEditor = nwGUI.docEditor
+ docEditor.replaceText("### Scene\n\nFoo http://www.example.com bar.\n\n")
+
+ docEditor.setCursorPosition(20)
+ position = docEditor.cursorRect().center()
+ event = QMouseEvent(
+ QEvent.Type.MouseButtonPress, position, QtMouseLeft, QtMouseLeft, QtModCtrl
+ )
+
+ with monkeypatch.context() as mp:
+ openUrl = MagicMock()
+ mp.setattr(QDesktopServices, "openUrl", openUrl)
+ docEditor.mouseReleaseEvent(event)
+ assert openUrl.called is True
+ assert openUrl.call_args[0][0] == QUrl("http://www.example.com")
+
+ # qtbot.stop()
+
+
@pytest.mark.gui
def testGuiEditor_Completer(qtbot, nwGUI, projPath, mockRnd):
"""Test the document editor meta completer functionality."""
diff --git a/tests/test_gui/test_gui_docviewer.py b/tests/test_gui/test_gui_docviewer.py
index 34a559f3..14082922 100644
--- a/tests/test_gui/test_gui_docviewer.py
+++ b/tests/test_gui/test_gui_docviewer.py
@@ -20,10 +20,12 @@ along with this program. If not, see .
"""
from __future__ import annotations
+from unittest.mock import MagicMock
+
import pytest
from PyQt5.QtCore import QEvent, QPoint, Qt, QUrl
-from PyQt5.QtGui import QMouseEvent, QTextCursor
+from PyQt5.QtGui import QDesktopServices, QMouseEvent, QTextCursor
from PyQt5.QtWidgets import QAction, QApplication, QMenu
from novelwriter import CONFIG, SHARED
@@ -165,6 +167,14 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, prjLipsum):
docViewer._linkClicked(QUrl("#somewhere_else"))
assert signal.args[0].url() == "#somewhere_else"
+ # Web links should trigger the browser
+ with monkeypatch.context() as mp:
+ openUrl = MagicMock()
+ mp.setattr(QDesktopServices, "openUrl", openUrl)
+ docViewer._linkClicked(QUrl("http://www.example.com"))
+ assert openUrl.called is True
+ assert openUrl.call_args[0][0] == QUrl("http://www.example.com")
+
# Click mouse nav buttons
qtbot.mouseClick(docViewer.viewport(), Qt.BackButton, pos=rect.center(), delay=100)
assert docViewer.docHandle == "88243afbe5ed8"
diff --git a/tests/test_tools/test_tools_manuscript.py b/tests/test_tools/test_tools_manuscript.py
index 08f74ce7..67f64c2a 100644
--- a/tests/test_tools/test_tools_manuscript.py
+++ b/tests/test_tools/test_tools_manuscript.py
@@ -22,9 +22,12 @@ from __future__ import annotations
import sys
+from unittest.mock import MagicMock
+
import pytest
-from PyQt5.QtCore import pyqtSlot
+from PyQt5.QtCore import QUrl, pyqtSlot
+from PyQt5.QtGui import QDesktopServices
from PyQt5.QtPrintSupport import QPrintPreviewDialog
from PyQt5.QtWidgets import QAction, QListWidgetItem
@@ -225,13 +228,26 @@ def testToolManuscript_Features(monkeypatch, qtbot, nwGUI, projPath, mockRnd):
assert (item := listView.topLevelItem(3)) and item.data(0, keyRole) == "000000000000c:T0006"
assert (item := listView.topLevelItem(4)) and item.data(0, keyRole) == "000000000000e:T0001"
- # Click Outline
+ # Click outline
item = listView.topLevelItem(4)
assert item is not None
with qtbot.waitSignal(manus.buildOutline.outlineEntryClicked) as signal:
manus.buildOutline._onItemClick(item)
assert signal.args == ["000000000000e:T0001"]
+ # Preview Navigation
+ assert manus.docPreview.source() == QUrl("#000000000000e:T0001")
+ manus.docPreview.navigateTo("000000000000c:T0002")
+ assert manus.docPreview.source() == QUrl("#000000000000c:T0002")
+ manus.docPreview._linkClicked(QUrl("#000000000000c:T0003"))
+ assert manus.docPreview.source() == QUrl("#000000000000c:T0003")
+ with monkeypatch.context() as mp:
+ openUrl = MagicMock()
+ mp.setattr(QDesktopServices, "openUrl", openUrl)
+ manus.docPreview._linkClicked(QUrl("http://www.example.com"))
+ assert openUrl.called is True
+ assert openUrl.call_args[0][0] == QUrl("http://www.example.com")
+
# Check Preview Stats
assert manus.docStats.mainStack.currentWidget() == manus.docStats.minWidget
assert manus.docStats.minWordCount.text() == "25"