Add test coverage of editor context menu

This commit is contained in:
Veronica Berglyd Olsen
2023-11-23 20:25:18 +01:00
parent 3f842c2fbe
commit de11ea389a
2 changed files with 169 additions and 27 deletions
+24 -25
View File
@@ -1067,49 +1067,48 @@ class GuiDocEditor(QPlainTextEdit):
@pyqtSlot("QPoint")
def _openContextMenu(self, pos: QPoint) -> None:
"""Triggered by right click to open the context menu. Also
triggered by the Ctrl+. shortcut.
"""
"""Open the editor context menu at a given coordinate."""
uCursor = self.textCursor()
pCursor = self.cursorForPosition(pos)
pBlock = pCursor.block()
ctxMenu = QMenu(self)
ctxMenu.setObjectName("ContextMenu")
if pBlock.userState() == GuiDocHighlighter.BLOCK_TITLE:
aLabel = ctxMenu.addAction(self.tr("Set as Document Name"))
aLabel.triggered.connect(lambda: self._emitRenameItem(pBlock))
action = ctxMenu.addAction(self.tr("Set as Document Name"))
action.triggered.connect(lambda: self._emitRenameItem(pBlock))
# Follow
status = self._processTag(cursor=pCursor, follow=False)
if status == nwTrinary.POSITIVE:
aTag = ctxMenu.addAction(self.tr("Follow Tag"))
aTag.triggered.connect(lambda: self._processTag(cursor=pCursor, follow=True))
action = ctxMenu.addAction(self.tr("Follow Tag"))
action.triggered.connect(lambda: self._processTag(cursor=pCursor, follow=True))
ctxMenu.addSeparator()
elif status == nwTrinary.NEGATIVE:
aTag = ctxMenu.addAction(self.tr("Create Note for Tag"))
aTag.triggered.connect(lambda: self._processTag(cursor=pCursor, create=True))
action = ctxMenu.addAction(self.tr("Create Note for Tag"))
action.triggered.connect(lambda: self._processTag(cursor=pCursor, create=True))
ctxMenu.addSeparator()
# Cut, Copy and Paste
if uCursor.hasSelection():
aCut = ctxMenu.addAction(self.tr("Cut"))
aCut.triggered.connect(lambda: self.docAction(nwDocAction.CUT))
aCopy = ctxMenu.addAction(self.tr("Copy"))
aCopy.triggered.connect(lambda: self.docAction(nwDocAction.COPY))
action = ctxMenu.addAction(self.tr("Cut"))
action.triggered.connect(lambda: self.docAction(nwDocAction.CUT))
action = ctxMenu.addAction(self.tr("Copy"))
action.triggered.connect(lambda: self.docAction(nwDocAction.COPY))
aPaste = ctxMenu.addAction(self.tr("Paste"))
aPaste.triggered.connect(lambda: self.docAction(nwDocAction.PASTE))
action = ctxMenu.addAction(self.tr("Paste"))
action.triggered.connect(lambda: self.docAction(nwDocAction.PASTE))
ctxMenu.addSeparator()
# Selections
aSAll = ctxMenu.addAction(self.tr("Select All"))
aSAll.triggered.connect(lambda: self.docAction(nwDocAction.SEL_ALL))
aSWrd = ctxMenu.addAction(self.tr("Select Word"))
aSWrd.triggered.connect(
action = ctxMenu.addAction(self.tr("Select All"))
action.triggered.connect(lambda: self.docAction(nwDocAction.SEL_ALL))
action = ctxMenu.addAction(self.tr("Select Word"))
action.triggered.connect(
lambda: self._makePosSelection(QTextCursor.SelectionType.WordUnderCursor, pos)
)
aSPar = ctxMenu.addAction(self.tr("Select Paragraph"))
aSPar.triggered.connect(lambda: self._makePosSelection(
action = ctxMenu.addAction(self.tr("Select Paragraph"))
action.triggered.connect(lambda: self._makePosSelection(
QTextCursor.SelectionType.BlockUnderCursor, pos)
)
@@ -1128,16 +1127,16 @@ class GuiDocEditor(QPlainTextEdit):
ctxMenu.addSeparator()
ctxMenu.addAction(self.tr("Spelling Suggestion(s)"))
for option in suggest[:15]:
aFix = ctxMenu.addAction(f"{nwUnicode.U_ENDASH} {option}")
aFix.triggered.connect(
action = ctxMenu.addAction(f"{nwUnicode.U_ENDASH} {option}")
action.triggered.connect(
lambda _, option=option: self._correctWord(sCursor, option)
)
else:
ctxMenu.addAction("%s %s" % (nwUnicode.U_ENDASH, self.tr("No Suggestions")))
ctxMenu.addSeparator()
aAdd = ctxMenu.addAction(self.tr("Add Word to Dictionary"))
aAdd.triggered.connect(lambda: self._addWord(word, block))
action = ctxMenu.addAction(self.tr("Add Word to Dictionary"))
action.triggered.connect(lambda: self._addWord(word, block))
# Execute the context menu
ctxMenu.exec_(self.viewport().mapToGlobal(pos))
+145 -2
View File
@@ -20,13 +20,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import pytest
from novelwriter.dialogs.editlabel import GuiEditLabel
from tools import C, buildTestProject
from mocked import causeOSError
from PyQt5.QtGui import QTextBlock, QTextCursor, QTextOption
from PyQt5.QtGui import QClipboard, QTextBlock, QTextCursor, QTextOption
from PyQt5.QtCore import QThreadPool, Qt
from PyQt5.QtWidgets import QAction, qApp
from PyQt5.QtWidgets import QAction, QMenu, qApp
from novelwriter import CONFIG, SHARED
from novelwriter.enum import nwDocAction, nwDocInsert, nwItemLayout, nwTrinary, nwWidget
@@ -209,6 +210,148 @@ def testGuiEditor_MetaData(qtbot, nwGUI, projPath, mockRnd):
# END Test testGuiEditor_MetaData
@pytest.mark.gui
def testGuiEditor_ContextMenu(monkeypatch, qtbot, nwGUI, projPath, mockRnd):
"""Test the editor context menu."""
monkeypatch.setattr(QMenu, "exec_", lambda *a: None)
buildTestProject(nwGUI, projPath)
assert nwGUI.openDocument(C.hSceneDoc) is True
docEditor = nwGUI.docEditor
sceneItem = SHARED.project.tree[C.hSceneDoc]
assert sceneItem is not None
def getMenuForPos(pos: int, select: bool = False) -> QMenu | None:
nonlocal docEditor
cursor = docEditor.textCursor()
cursor.setPosition(pos)
if select:
cursor.select(QTextCursor.SelectionType.WordUnderCursor)
docEditor.setTextCursor(cursor)
docEditor._openContextMenu(docEditor.cursorRect().center())
for obj in docEditor.children():
if isinstance(obj, QMenu) and obj.objectName() == "ContextMenu":
return obj
return None
docText = (
"### A Scene\n\n"
"@pov: Jane\n"
"Some text ..."
)
docEditor.setPlainText(docText)
assert docEditor.getText() == docText
# Rename Item from Heading
ctxMenu = getMenuForPos(1)
assert ctxMenu is not None
actions = [x.text() for x in ctxMenu.actions() if x.text()]
assert actions == [
"Set as Document Name", "Paste",
"Select All", "Select Word", "Select Paragraph"
]
with monkeypatch.context() as mp:
mp.setattr(GuiEditLabel, "getLabel", lambda a, text: (text, True))
assert sceneItem.itemName == "New Scene"
ctxMenu.actions()[0].trigger()
assert sceneItem.itemName == "A Scene"
ctxMenu.setObjectName("")
ctxMenu.deleteLater()
# Create Character
ctxMenu = getMenuForPos(21)
assert ctxMenu is not None
actions = [x.text() for x in ctxMenu.actions() if x.text()]
assert actions == [
"Create Note for Tag", "Paste",
"Select All", "Select Word", "Select Paragraph"
]
ctxMenu.actions()[0].trigger()
janeItem = SHARED.project.tree["0000000000010"]
assert janeItem is not None
assert janeItem.itemName == "Jane"
ctxMenu.setObjectName("")
ctxMenu.deleteLater()
# Follow Character Tag
ctxMenu = getMenuForPos(21)
assert ctxMenu is not None
actions = [x.text() for x in ctxMenu.actions() if x.text()]
assert actions == [
"Follow Tag", "Paste",
"Select All", "Select Word", "Select Paragraph"
]
ctxMenu.actions()[0].trigger()
assert nwGUI.docViewer.docHandle == "0000000000010"
ctxMenu.setObjectName("")
ctxMenu.deleteLater()
# Select Word
ctxMenu = getMenuForPos(31)
assert ctxMenu is not None
actions = [x.text() for x in ctxMenu.actions() if x.text()]
assert actions == [
"Paste", "Select All", "Select Word", "Select Paragraph"
]
ctxMenu.actions()[3].trigger()
assert docEditor.textCursor().selectedText() == "text"
ctxMenu.setObjectName("")
ctxMenu.deleteLater()
# Select Paragraph
ctxMenu = getMenuForPos(31)
assert ctxMenu is not None
actions = [x.text() for x in ctxMenu.actions() if x.text()]
assert actions == [
"Paste", "Select All", "Select Word", "Select Paragraph"
]
ctxMenu.actions()[4].trigger()
assert docEditor.textCursor().selectedText() == "Some text ..."
ctxMenu.setObjectName("")
ctxMenu.deleteLater()
# Select All
ctxMenu = getMenuForPos(31)
assert ctxMenu is not None
actions = [x.text() for x in ctxMenu.actions() if x.text()]
assert actions == [
"Paste", "Select All", "Select Word", "Select Paragraph"
]
ctxMenu.actions()[2].trigger()
assert docEditor.textCursor().selectedText() == docEditor.document().toRawText()
ctxMenu.setObjectName("")
ctxMenu.deleteLater()
# Copy Text
ctxMenu = getMenuForPos(31, True)
assert ctxMenu is not None
assert docEditor.textCursor().selectedText() == "text"
actions = [x.text() for x in ctxMenu.actions() if x.text()]
assert actions == [
"Cut", "Copy", "Paste", "Select All", "Select Word", "Select Paragraph"
]
qApp.clipboard().clear()
ctxMenu.actions()[1].trigger()
assert qApp.clipboard().text(QClipboard.Mode.Clipboard) == "text"
# Cut Text
qApp.clipboard().clear()
ctxMenu.actions()[0].trigger()
assert qApp.clipboard().text(QClipboard.Mode.Clipboard) == "text"
assert "text" not in docEditor.getText()
# Paste Text
ctxMenu.actions()[2].trigger()
assert docEditor.getText() == docText
ctxMenu.setObjectName("")
ctxMenu.deleteLater()
# qtbot.stop()
# END Test testGuiEditor_ContextMenu
@pytest.mark.gui
def testGuiEditor_Actions(qtbot, nwGUI, projPath, ipsumText, mockRnd):
"""Test the document actions. This is not an extensive test of the