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"