From 325c8dc68d57dfefcb4641bed0255b2622be7c9a Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Sat, 12 Dec 2020 17:39:28 +0100 Subject: [PATCH] Split up the main gui test file --- ...=> guiEditor_Main_Final_031b4af5197ec.nwd} | 0 ...=> guiEditor_Main_Final_0e17daca5f3e1.nwd} | 0 ...=> guiEditor_Main_Final_1a6562590ef19.nwd} | 0 ...=> guiEditor_Main_Final_41cfc0d1f2d12.nwd} | 0 ...nwx => guiEditor_Main_Final_nwProject.nwx} | 0 ...x => guiEditor_Main_Initial_nwProject.nwx} | 0 tests/test_gui_doceditor.py | 506 ++++++ tests/test_gui_docviewer.py | 165 ++ tests/test_gui_main.py | 1526 ----------------- tests/test_gui_mainmenu.py | 533 ++++++ tests/test_gui_outline.py | 73 + tests/test_gui_projtree.py | 138 ++ tests/test_gui_theme.py | 170 ++ 13 files changed, 1585 insertions(+), 1526 deletions(-) rename tests/reference/{gui/1_031b4af5197ec.nwd => guiEditor_Main_Final_031b4af5197ec.nwd} (100%) rename tests/reference/{gui/1_0e17daca5f3e1.nwd => guiEditor_Main_Final_0e17daca5f3e1.nwd} (100%) rename tests/reference/{gui/1_1a6562590ef19.nwd => guiEditor_Main_Final_1a6562590ef19.nwd} (100%) rename tests/reference/{gui/1_41cfc0d1f2d12.nwd => guiEditor_Main_Final_41cfc0d1f2d12.nwd} (100%) rename tests/reference/{gui/1_nwProject.nwx => guiEditor_Main_Final_nwProject.nwx} (100%) rename tests/reference/{gui/0_nwProject.nwx => guiEditor_Main_Initial_nwProject.nwx} (100%) create mode 100644 tests/test_gui_doceditor.py create mode 100644 tests/test_gui_docviewer.py delete mode 100644 tests/test_gui_main.py create mode 100644 tests/test_gui_mainmenu.py create mode 100644 tests/test_gui_outline.py create mode 100644 tests/test_gui_projtree.py create mode 100644 tests/test_gui_theme.py diff --git a/tests/reference/gui/1_031b4af5197ec.nwd b/tests/reference/guiEditor_Main_Final_031b4af5197ec.nwd similarity index 100% rename from tests/reference/gui/1_031b4af5197ec.nwd rename to tests/reference/guiEditor_Main_Final_031b4af5197ec.nwd diff --git a/tests/reference/gui/1_0e17daca5f3e1.nwd b/tests/reference/guiEditor_Main_Final_0e17daca5f3e1.nwd similarity index 100% rename from tests/reference/gui/1_0e17daca5f3e1.nwd rename to tests/reference/guiEditor_Main_Final_0e17daca5f3e1.nwd diff --git a/tests/reference/gui/1_1a6562590ef19.nwd b/tests/reference/guiEditor_Main_Final_1a6562590ef19.nwd similarity index 100% rename from tests/reference/gui/1_1a6562590ef19.nwd rename to tests/reference/guiEditor_Main_Final_1a6562590ef19.nwd diff --git a/tests/reference/gui/1_41cfc0d1f2d12.nwd b/tests/reference/guiEditor_Main_Final_41cfc0d1f2d12.nwd similarity index 100% rename from tests/reference/gui/1_41cfc0d1f2d12.nwd rename to tests/reference/guiEditor_Main_Final_41cfc0d1f2d12.nwd diff --git a/tests/reference/gui/1_nwProject.nwx b/tests/reference/guiEditor_Main_Final_nwProject.nwx similarity index 100% rename from tests/reference/gui/1_nwProject.nwx rename to tests/reference/guiEditor_Main_Final_nwProject.nwx diff --git a/tests/reference/gui/0_nwProject.nwx b/tests/reference/guiEditor_Main_Initial_nwProject.nwx similarity index 100% rename from tests/reference/gui/0_nwProject.nwx rename to tests/reference/guiEditor_Main_Initial_nwProject.nwx diff --git a/tests/test_gui_doceditor.py b/tests/test_gui_doceditor.py new file mode 100644 index 00000000..5b09ca95 --- /dev/null +++ b/tests/test_gui_doceditor.py @@ -0,0 +1,506 @@ +# -*- coding: utf-8 -*- +"""novelWriter Main GUI Editor Class Tester +""" + +import pytest +import os + +from shutil import copyfile +from tools import cmpFiles + +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QTextCursor +from PyQt5.QtWidgets import QAction, QMessageBox + +from nw.constants import nwItemType, nwDocAction + +keyDelay = 2 +typeDelay = 1 +stepDelay = 20 + +@pytest.mark.gui +def testGuiEditor_Main(qtbot, monkeypatch, nwGUI, fncDir, fncProj, refDir, outDir): + """Test the document editor. + """ + # Block message box + monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes) + + # Create new, save, close project + nwGUI.theProject.projTree.setSeed(42) + assert nwGUI.newProject({"projPath": fncProj}) + assert nwGUI.saveProject() + assert nwGUI.closeProject() + + assert len(nwGUI.theProject.projTree) == 0 + assert len(nwGUI.theProject.projTree._treeOrder) == 0 + assert len(nwGUI.theProject.projTree._treeRoots) == 0 + assert nwGUI.theProject.projTree.trashRoot() is None + assert nwGUI.theProject.projPath is None + assert nwGUI.theProject.projMeta is None + assert nwGUI.theProject.projFile == "nwProject.nwx" + assert nwGUI.theProject.projName == "" + assert nwGUI.theProject.bookTitle == "" + assert len(nwGUI.theProject.bookAuthors) == 0 + assert not nwGUI.theProject.spellCheck + + # Check the files + projFile = os.path.join(fncProj, "nwProject.nwx") + testFile = os.path.join(outDir, "guiEditor_Main_Initial_nwProject.nwx") + compFile = os.path.join(refDir, "guiEditor_Main_Initial_nwProject.nwx") + copyfile(projFile, testFile) + assert cmpFiles(testFile, compFile, [2, 6, 7, 8]) + qtbot.wait(stepDelay) + + # qtbot.stopForInteraction() + + # Re-open project + assert nwGUI.openProject(fncProj) + qtbot.wait(stepDelay) + + # Check that we loaded the data + assert len(nwGUI.theProject.projTree) == 8 + assert len(nwGUI.theProject.projTree._treeOrder) == 8 + assert len(nwGUI.theProject.projTree._treeRoots) == 4 + assert nwGUI.theProject.projTree.trashRoot() is None + assert nwGUI.theProject.projPath == fncProj + assert nwGUI.theProject.projMeta == os.path.join(fncProj, "meta") + assert nwGUI.theProject.projFile == "nwProject.nwx" + assert nwGUI.theProject.projName == "New Project" + assert nwGUI.theProject.bookTitle == "" + assert len(nwGUI.theProject.bookAuthors) == 0 + assert not nwGUI.theProject.spellCheck + + # Check that tree items have been created + assert nwGUI.treeView._getTreeItem("73475cb40a568") is not None + assert nwGUI.treeView._getTreeItem("25fc0e7096fc6") is not None + assert nwGUI.treeView._getTreeItem("31489056e0916") is not None + assert nwGUI.treeView._getTreeItem("98010bd9270f9") is not None + assert nwGUI.treeView._getTreeItem("0e17daca5f3e1") is not None + assert nwGUI.treeView._getTreeItem("44cb730c42048") is not None + assert nwGUI.treeView._getTreeItem("71ee45a3c0db9") is not None + assert nwGUI.treeView._getTreeItem("811786ad1ae74") is not None + + nwGUI.mainMenu.aSpellCheck.setChecked(True) + assert nwGUI.mainMenu._toggleSpellCheck() + + # Change some settings + nwGUI.mainConf.hideHScroll = True + nwGUI.mainConf.hideVScroll = True + nwGUI.mainConf.scrollPastEnd = True + nwGUI.mainConf.autoScrollPos = 80 + nwGUI.mainConf.autoScroll = True + + # Add a Character File + nwGUI.setFocus(1) + nwGUI.treeView.clearSelection() + nwGUI.treeView._getTreeItem("71ee45a3c0db9").setSelected(True) + nwGUI.treeView.newTreeItem(nwItemType.FILE, None) + assert nwGUI.openSelectedItem() + + # Type something into the document + nwGUI.setFocus(2) + qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay) + for c in "# Jane Doe": + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + for c in "@tag: Jane": + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + for c in "This is a file about Jane.": + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + + # Add a Plot File + nwGUI.setFocus(1) + nwGUI.treeView.clearSelection() + nwGUI.treeView._getTreeItem("44cb730c42048").setSelected(True) + nwGUI.treeView.newTreeItem(nwItemType.FILE, None) + assert nwGUI.openSelectedItem() + + # Type something into the document + nwGUI.setFocus(2) + qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay) + for c in "# Main Plot": + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + for c in "@tag: MainPlot": + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + for c in "This is a file detailing the main plot.": + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + + # Add a World File + nwGUI.setFocus(1) + nwGUI.treeView.clearSelection() + nwGUI.treeView._getTreeItem("811786ad1ae74").setSelected(True) + nwGUI.treeView.newTreeItem(nwItemType.FILE, None) + assert nwGUI.openSelectedItem() + + # Add Some Text + nwGUI.docEditor.replaceText("Hello World!") + assert nwGUI.docEditor.getText() == "Hello World!" + nwGUI.docEditor.replaceText("") + + # Type something into the document + nwGUI.setFocus(2) + qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay) + for c in "# Main Location": + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + for c in "@tag: Home": + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + for c in "This is a file describing Jane's home.": + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + + # Trigger autosaves before making more changes + nwGUI._autoSaveDocument() + nwGUI._autoSaveProject() + + # Select the 'New Scene' file + nwGUI.setFocus(1) + nwGUI.treeView.clearSelection() + nwGUI.treeView._getTreeItem("73475cb40a568").setExpanded(True) + nwGUI.treeView._getTreeItem("31489056e0916").setExpanded(True) + nwGUI.treeView._getTreeItem("0e17daca5f3e1").setSelected(True) + assert nwGUI.openSelectedItem() + + # Type something into the document + nwGUI.setFocus(2) + qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay) + for c in "# Novel": + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + + for c in "## Chapter": + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + + for c in "@pov: Jane": + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + for c in "@plot: MainPlot": + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + + for c in "### Scene": + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + + for c in "% How about a comment?": + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + for c in "@pov: Jane": + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + for c in "@plot: MainPlot": + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + for c in "@location: Home": + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + + for c in "#### Some Section": + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + + for c in "@char: Jane": + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + + for c in "This is a paragraph of dummy text.": + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + + for c in ( + "This is another paragraph of much longer dummy text. " + "It is in fact very very dumb dummy text! " + ): + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + for c in "We can also try replacing \"quotes\", even single 'quotes' are replaced. ": + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + for c in "Isn't that nice? ": + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + for c in "We can hyphen-ate, make dashes -- and even longer dashes --- if we want. ": + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + for c in "Ellipsis? Not a problem either ... ": + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + for c in "How about three hyphens - -": + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Left, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Backspace, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Right, delay=keyDelay) + for c in "- for long dash? It works too.": + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + + for c in "\"Full line double quoted text.\"": + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + + for c in "'Full line single quoted text.'": + qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) + + qtbot.wait(stepDelay) + nwGUI.docEditor.wCounter.run() + qtbot.wait(stepDelay) + + # Save the document + assert nwGUI.docEditor.docChanged + assert nwGUI.saveDocument() + assert not nwGUI.docEditor.docChanged + qtbot.wait(stepDelay) + nwGUI.rebuildIndex() + qtbot.wait(stepDelay) + + # Open and view the edited document + nwGUI.setFocus(3) + assert nwGUI.openDocument("0e17daca5f3e1") + assert nwGUI.viewDocument("0e17daca5f3e1") + qtbot.wait(stepDelay) + assert nwGUI.saveProject() + assert nwGUI.closeDocViewer() + qtbot.wait(stepDelay) + + # Check a Quick Create and Delete + assert nwGUI.treeView.newTreeItem(nwItemType.FILE, None) + newHandle = nwGUI.treeView.getSelectedHandle() + assert nwGUI.theProject.projTree["2858dcd1057d3"] is not None + assert nwGUI.treeView.deleteItem() + assert nwGUI.treeView.setSelectedHandle(newHandle) + assert nwGUI.treeView.deleteItem() + assert nwGUI.theProject.projTree["2fca346db6561"] is not None # Trash + assert nwGUI.saveProject() + + # Check the files + projFile = os.path.join(fncProj, "nwProject.nwx") + testFile = os.path.join(outDir, "guiEditor_Main_Final_nwProject.nwx") + compFile = os.path.join(refDir, "guiEditor_Main_Final_nwProject.nwx") + copyfile(projFile, testFile) + assert cmpFiles(testFile, compFile, [2, 6, 7, 8]) + + projFile = os.path.join(fncProj, "content", "031b4af5197ec.nwd") + testFile = os.path.join(outDir, "guiEditor_Main_Final_031b4af5197ec.nwd") + compFile = os.path.join(refDir, "guiEditor_Main_Final_031b4af5197ec.nwd") + copyfile(projFile, testFile) + assert cmpFiles(testFile, compFile) + + projFile = os.path.join(fncProj, "content", "1a6562590ef19.nwd") + testFile = os.path.join(outDir, "guiEditor_Main_Final_1a6562590ef19.nwd") + compFile = os.path.join(refDir, "guiEditor_Main_Final_1a6562590ef19.nwd") + copyfile(projFile, testFile) + assert cmpFiles(testFile, compFile) + + projFile = os.path.join(fncProj, "content", "0e17daca5f3e1.nwd") + testFile = os.path.join(outDir, "guiEditor_Main_Final_0e17daca5f3e1.nwd") + compFile = os.path.join(refDir, "guiEditor_Main_Final_0e17daca5f3e1.nwd") + copyfile(projFile, testFile) + assert cmpFiles(testFile, compFile) + + projFile = os.path.join(fncProj, "content", "41cfc0d1f2d12.nwd") + testFile = os.path.join(outDir, "guiEditor_Main_Final_41cfc0d1f2d12.nwd") + compFile = os.path.join(refDir, "guiEditor_Main_Final_41cfc0d1f2d12.nwd") + copyfile(projFile, testFile) + assert cmpFiles(testFile, compFile) + + # qtbot.stopForInteraction() + +# END Test testGuiEditor_Main + +@pytest.mark.gui +def testGuiEditor_Search(qtbot, monkeypatch, nwGUI, nwLipsum): + """Test the document editor search functionality. + """ + # Block message box + monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes) + + nwGUI.theProject.projTree.setSeed(42) + assert nwGUI.openProject(nwLipsum) + assert nwGUI.openDocument("4c4f28287af27") + origText = nwGUI.docEditor.getText() + qtbot.wait(stepDelay) + + # Select the Word "est" + assert nwGUI.docEditor.setCursorPosition(618) + nwGUI.docEditor._makeSelection(QTextCursor.WordUnderCursor) + theCursor = nwGUI.docEditor.textCursor() + assert theCursor.selectedText() == "est" + + # Activate Search + nwGUI.mainMenu.aFind.activate(QAction.Trigger) + assert nwGUI.docEditor.docSearch.isVisible() + assert nwGUI.docEditor.docSearch.getSearchText() == "est" + + # Find Next by Enter + monkeypatch.setattr(nwGUI.docEditor.docSearch.searchBox, "hasFocus", lambda: True) + qtbot.keyClick(nwGUI.docEditor.docSearch.searchBox, Qt.Key_Return, delay=keyDelay) + assert abs(nwGUI.docEditor.getCursorPosition() - 1272) < 3 + + # Find Next by Button + qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay) + assert abs(nwGUI.docEditor.getCursorPosition() - 1486) < 3 + + # Activate Loop Search + nwGUI.docEditor.docSearch.toggleLoop.activate(QAction.Trigger) + assert nwGUI.docEditor.docSearch.toggleLoop.isChecked() + assert nwGUI.docEditor.docSearch.doLoop + + # Find Next by Menu Search > Find Next + nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) + assert abs(nwGUI.docEditor.getCursorPosition() - 620) < 3 + + # Close Search + nwGUI.docEditor.docSearch.cancelSearch.activate(QAction.Trigger) + assert not nwGUI.docEditor.docSearch.isVisible() + assert nwGUI.docEditor.setCursorPosition(15) + + # Toggle Search Again with Header Button + qtbot.mouseClick(nwGUI.docEditor.docHeader.searchButton, Qt.LeftButton, delay=keyDelay) + assert nwGUI.docEditor.docSearch.setSearchText("") + assert nwGUI.docEditor.docSearch.isVisible() + + # Enable RegEx Search + nwGUI.docEditor.docSearch.toggleRegEx.activate(QAction.Trigger) + assert nwGUI.docEditor.docSearch.toggleRegEx.isChecked() + assert nwGUI.docEditor.docSearch.isRegEx + + # Set Invalid RegEx + assert nwGUI.docEditor.docSearch.setSearchText(r"\bSus[") + qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay) + assert nwGUI.docEditor.getCursorPosition() < 3 # No result + + # Set Valid RegEx + assert nwGUI.docEditor.docSearch.setSearchText(r"\bSus") + qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay) + assert abs(nwGUI.docEditor.getCursorPosition() - 196) < 3 + + # Find Next and then Prev + nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) + assert abs(nwGUI.docEditor.getCursorPosition() - 297) < 3 + nwGUI.mainMenu.aFindPrev.activate(QAction.Trigger) + assert abs(nwGUI.docEditor.getCursorPosition() - 196) < 3 + + # Make RegEx Case Sensitive + nwGUI.docEditor.docSearch.toggleCase.activate(QAction.Trigger) + assert nwGUI.docEditor.docSearch.toggleCase.isChecked() + assert nwGUI.docEditor.docSearch.isCaseSense + + # Find Next (One Result) + nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) + assert abs(nwGUI.docEditor.getCursorPosition() - 599) < 3 + nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) + assert abs(nwGUI.docEditor.getCursorPosition() - 599) < 3 + + # Trigger Replace + nwGUI.mainMenu.aReplace.activate(QAction.Trigger) + assert nwGUI.docEditor.docSearch.setReplaceText("foo") + + # Disable RegEx Case Sensitive + nwGUI.docEditor.docSearch.toggleCase.activate(QAction.Trigger) + assert not nwGUI.docEditor.docSearch.toggleCase.isChecked() + assert not nwGUI.docEditor.docSearch.isCaseSense + + # Toggle Replace Preserve Case + nwGUI.docEditor.docSearch.toggleMatchCap.activate(QAction.Trigger) + assert nwGUI.docEditor.docSearch.toggleMatchCap.isChecked() + assert nwGUI.docEditor.docSearch.doMatchCap + + # Replace "Sus" with "Foo" via Menu + nwGUI.mainMenu.aReplaceNext.activate(QAction.Trigger) + assert nwGUI.docEditor.getText()[596:607] == "Foopendisse" + + # Find Next to Loop File + nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) + + # Replace "sus" with "foo" via Replace Button + qtbot.mouseClick(nwGUI.docEditor.docSearch.replaceButton, Qt.LeftButton, delay=keyDelay) + assert nwGUI.docEditor.getText()[193:201] == "foocipit" + + # Revert Last Two Replaces + assert nwGUI.docEditor.docAction(nwDocAction.UNDO) + assert nwGUI.docEditor.docAction(nwDocAction.UNDO) + assert nwGUI.docEditor.getText() == origText + + # Disable RegEx Search + nwGUI.docEditor.docSearch.toggleRegEx.activate(QAction.Trigger) + assert not nwGUI.docEditor.docSearch.toggleRegEx.isChecked() + assert not nwGUI.docEditor.docSearch.isRegEx + + # Close Search and Select "est" Again + nwGUI.docEditor.docSearch.cancelSearch.activate(QAction.Trigger) + assert nwGUI.docEditor.setCursorPosition(618) + nwGUI.docEditor._makeSelection(QTextCursor.WordUnderCursor) + theCursor = nwGUI.docEditor.textCursor() + assert theCursor.selectedText() == "est" + + # Activate Search Again + nwGUI.mainMenu.aFind.activate(QAction.Trigger) + assert nwGUI.docEditor.docSearch.isVisible() + assert nwGUI.docEditor.docSearch.getSearchText() == "est" + + # Enable Full Word Search + nwGUI.docEditor.docSearch.toggleWord.activate(QAction.Trigger) + assert nwGUI.docEditor.docSearch.toggleWord.isChecked() + assert nwGUI.docEditor.docSearch.isWholeWord + + # Only One Match + nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) + assert abs(nwGUI.docEditor.getCursorPosition() - 620) < 3 + nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) + assert abs(nwGUI.docEditor.getCursorPosition() - 620) < 3 + + # Enable Next Doc Search + nwGUI.docEditor.docSearch.toggleProject.activate(QAction.Trigger) + assert nwGUI.docEditor.docSearch.toggleProject.isChecked() + assert nwGUI.docEditor.docSearch.doNextFile + + # Next Match + nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) + assert nwGUI.docEditor.theHandle == "2426c6f0ca922" # Next document + nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) + assert abs(nwGUI.docEditor.getCursorPosition() - 620) < 3 + nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) + assert abs(nwGUI.docEditor.getCursorPosition() - 1127) < 3 + + # Toggle Replace + nwGUI.docEditor._beginReplace() + + # MonkeyPatch the focus cycle. We can't really test this very well, other than + # check that the tabs aren't captured when the main editor has focus + monkeypatch.setattr(nwGUI.docEditor, "hasFocus", lambda: True) + monkeypatch.setattr(nwGUI.docEditor.docSearch.searchBox, "hasFocus", lambda: False) + monkeypatch.setattr(nwGUI.docEditor.docSearch.replaceBox, "hasFocus", lambda: False) + assert not nwGUI.docEditor.focusNextPrevChild(True) + + monkeypatch.setattr(nwGUI.docEditor, "hasFocus", lambda: False) + monkeypatch.setattr(nwGUI.docEditor.docSearch.searchBox, "hasFocus", lambda: True) + monkeypatch.setattr(nwGUI.docEditor.docSearch.replaceBox, "hasFocus", lambda: False) + assert nwGUI.docEditor.focusNextPrevChild(True) + + monkeypatch.setattr(nwGUI.docEditor.docSearch.searchBox, "hasFocus", lambda: False) + monkeypatch.setattr(nwGUI.docEditor.docSearch.replaceBox, "hasFocus", lambda: True) + assert nwGUI.docEditor.focusNextPrevChild(True) + + # qtbot.stopForInteraction() + +# END Test testGuiEditor_Search diff --git a/tests/test_gui_docviewer.py b/tests/test_gui_docviewer.py new file mode 100644 index 00000000..3af74afa --- /dev/null +++ b/tests/test_gui_docviewer.py @@ -0,0 +1,165 @@ +# -*- coding: utf-8 -*- +"""novelWriter Main GUI Viewer Class Tester +""" + +import pytest + +from PyQt5.QtCore import Qt, QUrl +from PyQt5.QtGui import QTextCursor +from PyQt5.QtWidgets import qApp, QAction, QMessageBox + +from nw.constants import nwDocAction + +keyDelay = 2 +typeDelay = 1 +stepDelay = 20 + +@pytest.mark.gui +def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum): + """Test the document viewer. + """ + # Block message box + monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes) + + # Open project + nwGUI.theProject.projTree.setSeed(42) + assert nwGUI.openProject(nwLipsum) + + # Rebuild the index as it isn't automatically copied + assert nwGUI.theIndex.tagIndex == {} + assert nwGUI.theIndex.refIndex == {} + nwGUI.mainMenu.aRebuildIndex.activate(QAction.Trigger) + assert nwGUI.theIndex.tagIndex != {} + assert nwGUI.theIndex.refIndex != {} + + # Select a document in the project tree + assert nwGUI.treeView.setSelectedHandle("88243afbe5ed8") + + # Middle-click the selected item + theItem = nwGUI.treeView._getTreeItem("88243afbe5ed8") + theRect = nwGUI.treeView.visualItemRect(theItem) + qtbot.mouseClick(nwGUI.treeView.viewport(), Qt.MidButton, pos=theRect.center()) + assert nwGUI.docViewer.theHandle == "88243afbe5ed8" + + # Reload the text + origText = nwGUI.docViewer.toPlainText() + nwGUI.docViewer.setPlainText("Oops, all gone!") + nwGUI.docViewer.docHeader._refreshDocument() + assert nwGUI.docViewer.toPlainText() == origText + + # Cursor line + assert not nwGUI.docViewer.setCursorLine("not a number") + assert nwGUI.docViewer.setCursorLine(3) + theCursor = nwGUI.docViewer.textCursor() + assert theCursor.position() == 40 + + # Cursor position + assert not nwGUI.docViewer.setCursorPosition("not a number") + assert nwGUI.docViewer.setCursorPosition(100) + + # Select word + nwGUI.docViewer._makeSelection(QTextCursor.WordUnderCursor) + + qClip = qApp.clipboard() + qClip.clear() + + # Cut + assert nwGUI.docViewer.docAction(nwDocAction.CUT) + assert qClip.text() == "laoreet" + qClip.clear() + + # Copy + assert nwGUI.docViewer.docAction(nwDocAction.COPY) + assert qClip.text() == "laoreet" + qClip.clear() + + # Select Paragraph + assert nwGUI.docViewer.docAction(nwDocAction.SEL_PARA) + theCursor = nwGUI.docViewer.textCursor() + assert theCursor.selectedText() == ( + "Synopsis: Aenean ut placerat velit. Etiam laoreet ullamcorper risus, " + "eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et " + "mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. " + "Etiam finibus nisi vel mi molestie consectetur." + ) + + # Select All + assert nwGUI.docViewer.docAction(nwDocAction.SEL_ALL) + theCursor = nwGUI.docViewer.textCursor() + assert len(theCursor.selectedText()) == 3061 + + # Other actions + assert not nwGUI.docViewer.docAction(nwDocAction.NO_ACTION) + + # Close document + nwGUI.docViewer.docHeader._closeDocument() + assert nwGUI.docViewer.theHandle is None + + # Action on no document + assert not nwGUI.docViewer.docAction(nwDocAction.COPY) + + # Open again via menu + assert nwGUI.treeView.setSelectedHandle("88243afbe5ed8") + nwGUI.mainMenu.aViewDoc.activate(QAction.Trigger) + + # Select "Bod" link + assert nwGUI.docViewer.setCursorPosition(27) + nwGUI.docViewer._makeSelection(QTextCursor.WordUnderCursor) + theRect = nwGUI.docViewer.cursorRect() + # qtbot.mouseClick(nwGUI.docViewer.viewport(), Qt.LeftButton, pos=theRect.center(), delay=100) + nwGUI.docViewer._linkClicked(QUrl("#char=Bod")) + assert nwGUI.docViewer.theHandle == "4c4f28287af27" + + # Click mouse nav buttons + qtbot.mouseClick(nwGUI.docViewer.viewport(), Qt.BackButton, pos=theRect.center(), delay=100) + assert nwGUI.docViewer.theHandle == "88243afbe5ed8" + qtbot.mouseClick(nwGUI.docViewer.viewport(), Qt.ForwardButton, pos=theRect.center(), delay=100) + assert nwGUI.docViewer.theHandle == "4c4f28287af27" + + # Scroll bar default on empty document + nwGUI.docViewer.clear() + assert nwGUI.docViewer.getScrollPosition() == 0 + nwGUI.docViewer.reloadText() + + # Change document title + nwItem = nwGUI.theProject.projTree["4c4f28287af27"] + nwItem.setName("Test Title") + assert nwItem.itemName == "Test Title" + nwGUI.docViewer.updateDocInfo("4c4f28287af27") + assert nwGUI.docViewer.docHeader.theTitle.text() == "Characters › Test Title" + + # Ttile without full path + nwGUI.mainConf.showFullPath = False + nwGUI.docViewer.updateDocInfo("4c4f28287af27") + assert nwGUI.docViewer.docHeader.theTitle.text() == "Test Title" + nwGUI.mainConf.showFullPath = True + + # Document footer show/hide references + viewState = nwGUI.viewMeta.isVisible() + nwGUI.docViewer.docFooter._doShowHide() + assert nwGUI.viewMeta.isVisible() is not viewState + nwGUI.docViewer.docFooter._doShowHide() + assert nwGUI.viewMeta.isVisible() is viewState + + # Document footer sticky + viewState = nwGUI.docViewer.stickyRef + nwGUI.docViewer.docFooter._doToggleSticky(not viewState) + assert nwGUI.docViewer.stickyRef is not viewState + nwGUI.docViewer.docFooter._doToggleSticky(viewState) + assert nwGUI.docViewer.stickyRef is viewState + + # Document footer show/hide synopsis + assert nwGUI.viewDocument("f96ec11c6a3da") + assert len(nwGUI.docViewer.toPlainText()) == 4315 + nwGUI.docViewer.docFooter._doToggleSynopsis(False) + assert len(nwGUI.docViewer.toPlainText()) == 4099 + + # Document footer show/hide comments + assert nwGUI.viewDocument("846352075de7d") + assert len(nwGUI.docViewer.toPlainText()) == 675 + nwGUI.docViewer.docFooter._doToggleComments(False) + assert len(nwGUI.docViewer.toPlainText()) == 635 + + # qtbot.stopForInteraction() + +# END Test testGuiViewer_Main diff --git a/tests/test_gui_main.py b/tests/test_gui_main.py deleted file mode 100644 index b56c525b..00000000 --- a/tests/test_gui_main.py +++ /dev/null @@ -1,1526 +0,0 @@ -# -*- coding: utf-8 -*- -"""novelWriter Main GUI Class Tester -""" - -import nw -import pytest -import os - -from shutil import copyfile -from tools import cmpFiles - -from PyQt5.QtCore import Qt, QUrl, QPoint, QItemSelectionModel -from PyQt5.QtGui import QTextCursor, QColor, QPixmap, QIcon, QTextBlock -from PyQt5.QtWidgets import ( - qApp, QAction, QTreeWidgetItem, QStyle, QFileDialog, QMessageBox -) - -from nw.constants import ( - nwItemType, nwItemClass, nwUnicode, nwOutline, nwDocAction, nwDocInsert, - nwKeyWords -) - -keyDelay = 2 -typeDelay = 1 -stepDelay = 20 - -@pytest.mark.gui -def testDocEditor(qtbot, yesToAll, fncDir, nwTempGUI, refDir, tmpDir): - - nwGUI = nw.main(["--testmode", "--config=%s" % tmpDir, "--data=%s" % tmpDir]) - qtbot.addWidget(nwGUI) - nwGUI.show() - qtbot.waitForWindowShown(nwGUI) - qtbot.wait(stepDelay) - - # Create new, save, close project - nwGUI.theProject.projTree.setSeed(42) - assert nwGUI.newProject({"projPath": fncDir}) - assert nwGUI.saveProject() - assert nwGUI.closeProject() - - assert len(nwGUI.theProject.projTree) == 0 - assert len(nwGUI.theProject.projTree._treeOrder) == 0 - assert len(nwGUI.theProject.projTree._treeRoots) == 0 - assert nwGUI.theProject.projTree.trashRoot() is None - assert nwGUI.theProject.projPath is None - assert nwGUI.theProject.projMeta is None - assert nwGUI.theProject.projFile == "nwProject.nwx" - assert nwGUI.theProject.projName == "" - assert nwGUI.theProject.bookTitle == "" - assert len(nwGUI.theProject.bookAuthors) == 0 - assert not nwGUI.theProject.spellCheck - - # Check the files - projFile = os.path.join(fncDir, "nwProject.nwx") - testFile = os.path.join(nwTempGUI, "0_nwProject.nwx") - refFile = os.path.join(refDir, "gui", "0_nwProject.nwx") - copyfile(projFile, testFile) - assert cmpFiles(testFile, refFile, [2, 6, 7, 8]) - qtbot.wait(stepDelay) - - # qtbot.stopForInteraction() - - # Re-open project - assert nwGUI.openProject(fncDir) - qtbot.wait(stepDelay) - - # Check that we loaded the data - assert len(nwGUI.theProject.projTree) == 8 - assert len(nwGUI.theProject.projTree._treeOrder) == 8 - assert len(nwGUI.theProject.projTree._treeRoots) == 4 - assert nwGUI.theProject.projTree.trashRoot() is None - assert nwGUI.theProject.projPath == fncDir - assert nwGUI.theProject.projMeta == os.path.join(fncDir, "meta") - assert nwGUI.theProject.projFile == "nwProject.nwx" - assert nwGUI.theProject.projName == "New Project" - assert nwGUI.theProject.bookTitle == "" - assert len(nwGUI.theProject.bookAuthors) == 0 - assert not nwGUI.theProject.spellCheck - - # Check that tree items have been created - assert nwGUI.treeView._getTreeItem("73475cb40a568") is not None - assert nwGUI.treeView._getTreeItem("25fc0e7096fc6") is not None - assert nwGUI.treeView._getTreeItem("31489056e0916") is not None - assert nwGUI.treeView._getTreeItem("98010bd9270f9") is not None - assert nwGUI.treeView._getTreeItem("0e17daca5f3e1") is not None - assert nwGUI.treeView._getTreeItem("44cb730c42048") is not None - assert nwGUI.treeView._getTreeItem("71ee45a3c0db9") is not None - assert nwGUI.treeView._getTreeItem("811786ad1ae74") is not None - - nwGUI.mainMenu.aSpellCheck.setChecked(True) - assert nwGUI.mainMenu._toggleSpellCheck() - - # Change some settings - nwGUI.mainConf.hideHScroll = True - nwGUI.mainConf.hideVScroll = True - nwGUI.mainConf.scrollPastEnd = True - nwGUI.mainConf.autoScrollPos = 80 - nwGUI.mainConf.autoScroll = True - - # Add a Character File - nwGUI.setFocus(1) - nwGUI.treeView.clearSelection() - nwGUI.treeView._getTreeItem("71ee45a3c0db9").setSelected(True) - nwGUI.treeView.newTreeItem(nwItemType.FILE, None) - assert nwGUI.openSelectedItem() - - # Type something into the document - nwGUI.setFocus(2) - qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay) - for c in "# Jane Doe": - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - for c in "@tag: Jane": - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - for c in "This is a file about Jane.": - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - - # Add a Plot File - nwGUI.setFocus(1) - nwGUI.treeView.clearSelection() - nwGUI.treeView._getTreeItem("44cb730c42048").setSelected(True) - nwGUI.treeView.newTreeItem(nwItemType.FILE, None) - assert nwGUI.openSelectedItem() - - # Type something into the document - nwGUI.setFocus(2) - qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay) - for c in "# Main Plot": - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - for c in "@tag: MainPlot": - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - for c in "This is a file detailing the main plot.": - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - - # Add a World File - nwGUI.setFocus(1) - nwGUI.treeView.clearSelection() - nwGUI.treeView._getTreeItem("811786ad1ae74").setSelected(True) - nwGUI.treeView.newTreeItem(nwItemType.FILE, None) - assert nwGUI.openSelectedItem() - - # Add Some Text - nwGUI.docEditor.replaceText("Hello World!") - assert nwGUI.docEditor.getText() == "Hello World!" - nwGUI.docEditor.replaceText("") - - # Type something into the document - nwGUI.setFocus(2) - qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay) - for c in "# Main Location": - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - for c in "@tag: Home": - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - for c in "This is a file describing Jane's home.": - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - - # Trigger autosaves before making more changes - nwGUI._autoSaveDocument() - nwGUI._autoSaveProject() - - # Select the 'New Scene' file - nwGUI.setFocus(1) - nwGUI.treeView.clearSelection() - nwGUI.treeView._getTreeItem("73475cb40a568").setExpanded(True) - nwGUI.treeView._getTreeItem("31489056e0916").setExpanded(True) - nwGUI.treeView._getTreeItem("0e17daca5f3e1").setSelected(True) - assert nwGUI.openSelectedItem() - - # Type something into the document - nwGUI.setFocus(2) - qtbot.keyClick(nwGUI.docEditor, "a", modifier=Qt.ControlModifier, delay=keyDelay) - for c in "# Novel": - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - - for c in "## Chapter": - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - - for c in "@pov: Jane": - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - for c in "@plot: MainPlot": - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - - for c in "### Scene": - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - - for c in "% How about a comment?": - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - for c in "@pov: Jane": - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - for c in "@plot: MainPlot": - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - for c in "@location: Home": - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - - for c in "#### Some Section": - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - - for c in "@char: Jane": - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - - for c in "This is a paragraph of dummy text.": - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - - for c in ( - "This is another paragraph of much longer dummy text. " - "It is in fact very very dumb dummy text! " - ): - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - for c in "We can also try replacing \"quotes\", even single 'quotes' are replaced. ": - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - for c in "Isn't that nice? ": - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - for c in "We can hyphen-ate, make dashes -- and even longer dashes --- if we want. ": - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - for c in "Ellipsis? Not a problem either ... ": - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - for c in "How about three hyphens - -": - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Left, delay=keyDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Backspace, delay=keyDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Right, delay=keyDelay) - for c in "- for long dash? It works too.": - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - - for c in "\"Full line double quoted text.\"": - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - - for c in "'Full line single quoted text.'": - qtbot.keyClick(nwGUI.docEditor, c, delay=typeDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - qtbot.keyClick(nwGUI.docEditor, Qt.Key_Return, delay=keyDelay) - - qtbot.wait(stepDelay) - nwGUI.docEditor.wCounter.run() - qtbot.wait(stepDelay) - - # Save the document - assert nwGUI.docEditor.docChanged - assert nwGUI.saveDocument() - assert not nwGUI.docEditor.docChanged - qtbot.wait(stepDelay) - nwGUI.rebuildIndex() - qtbot.wait(stepDelay) - - # Open and view the edited document - nwGUI.setFocus(3) - assert nwGUI.openDocument("0e17daca5f3e1") - assert nwGUI.viewDocument("0e17daca5f3e1") - qtbot.wait(stepDelay) - assert nwGUI.saveProject() - assert nwGUI.closeDocViewer() - qtbot.wait(stepDelay) - - # Check a Quick Create and Delete - assert nwGUI.treeView.newTreeItem(nwItemType.FILE, None) - newHandle = nwGUI.treeView.getSelectedHandle() - assert nwGUI.theProject.projTree["2858dcd1057d3"] is not None - assert nwGUI.treeView.deleteItem() - assert nwGUI.treeView.setSelectedHandle(newHandle) - assert nwGUI.treeView.deleteItem() - assert nwGUI.theProject.projTree["2fca346db6561"] is not None # Trash - assert nwGUI.saveProject() - - # Check the files - projFile = os.path.join(fncDir, "nwProject.nwx") - testFile = os.path.join(nwTempGUI, "1_nwProject.nwx") - refFile = os.path.join(refDir, "gui", "1_nwProject.nwx") - copyfile(projFile, testFile) - assert cmpFiles(testFile, refFile, [2, 6, 7, 8]) - - projFile = os.path.join(fncDir, "content", "031b4af5197ec.nwd") - testFile = os.path.join(nwTempGUI, "1_031b4af5197ec.nwd") - refFile = os.path.join(refDir, "gui", "1_031b4af5197ec.nwd") - copyfile(projFile, testFile) - assert cmpFiles(testFile, refFile) - - projFile = os.path.join(fncDir, "content", "1a6562590ef19.nwd") - testFile = os.path.join(nwTempGUI, "1_1a6562590ef19.nwd") - refFile = os.path.join(refDir, "gui", "1_1a6562590ef19.nwd") - copyfile(projFile, testFile) - assert cmpFiles(testFile, refFile) - - projFile = os.path.join(fncDir, "content", "0e17daca5f3e1.nwd") - testFile = os.path.join(nwTempGUI, "1_0e17daca5f3e1.nwd") - refFile = os.path.join(refDir, "gui", "1_0e17daca5f3e1.nwd") - copyfile(projFile, testFile) - assert cmpFiles(testFile, refFile) - - projFile = os.path.join(fncDir, "content", "41cfc0d1f2d12.nwd") - testFile = os.path.join(nwTempGUI, "1_41cfc0d1f2d12.nwd") - refFile = os.path.join(refDir, "gui", "1_41cfc0d1f2d12.nwd") - copyfile(projFile, testFile) - assert cmpFiles(testFile, refFile) - - # qtbot.stopForInteraction() - nwGUI.closeMain() - nwGUI.close() - -@pytest.mark.gui -def testDocViewer(qtbot, yesToAll, nwLipsum, tmpDir): - - nwGUI = nw.main(["--testmode", "--config=%s" % nwLipsum, "--data=%s" % tmpDir]) - qtbot.addWidget(nwGUI) - nwGUI.show() - qtbot.waitForWindowShown(nwGUI) - qtbot.wait(stepDelay) - - # Open project - nwGUI.theProject.projTree.setSeed(42) - assert nwGUI.openProject(nwLipsum) - - # Rebuild the index as it isn't automatically copied - assert nwGUI.theIndex.tagIndex == {} - assert nwGUI.theIndex.refIndex == {} - nwGUI.mainMenu.aRebuildIndex.activate(QAction.Trigger) - assert nwGUI.theIndex.tagIndex != {} - assert nwGUI.theIndex.refIndex != {} - - # Select a document in the project tree - assert nwGUI.treeView.setSelectedHandle("88243afbe5ed8") - - # Middle-click the selected item - theItem = nwGUI.treeView._getTreeItem("88243afbe5ed8") - theRect = nwGUI.treeView.visualItemRect(theItem) - qtbot.mouseClick(nwGUI.treeView.viewport(), Qt.MidButton, pos=theRect.center()) - assert nwGUI.docViewer.theHandle == "88243afbe5ed8" - - # Reload the text - origText = nwGUI.docViewer.toPlainText() - nwGUI.docViewer.setPlainText("Oops, all gone!") - nwGUI.docViewer.docHeader._refreshDocument() - assert nwGUI.docViewer.toPlainText() == origText - - # Cursor line - assert not nwGUI.docViewer.setCursorLine("not a number") - assert nwGUI.docViewer.setCursorLine(3) - theCursor = nwGUI.docViewer.textCursor() - assert theCursor.position() == 40 - - # Cursor position - assert not nwGUI.docViewer.setCursorPosition("not a number") - assert nwGUI.docViewer.setCursorPosition(100) - - # Select word - nwGUI.docViewer._makeSelection(QTextCursor.WordUnderCursor) - - qClip = qApp.clipboard() - qClip.clear() - - # Cut - assert nwGUI.docViewer.docAction(nwDocAction.CUT) - assert qClip.text() == "laoreet" - qClip.clear() - - # Copy - assert nwGUI.docViewer.docAction(nwDocAction.COPY) - assert qClip.text() == "laoreet" - qClip.clear() - - # Select Paragraph - assert nwGUI.docViewer.docAction(nwDocAction.SEL_PARA) - theCursor = nwGUI.docViewer.textCursor() - assert theCursor.selectedText() == ( - "Synopsis: Aenean ut placerat velit. Etiam laoreet ullamcorper risus, " - "eget lobortis enim scelerisque non. Suspendisse id maximus nunc, et " - "mollis sapien. Curabitur vel semper sapien, non pulvinar dolor. " - "Etiam finibus nisi vel mi molestie consectetur." - ) - - # Select All - assert nwGUI.docViewer.docAction(nwDocAction.SEL_ALL) - theCursor = nwGUI.docViewer.textCursor() - assert len(theCursor.selectedText()) == 3061 - - # Other actions - assert not nwGUI.docViewer.docAction(nwDocAction.NO_ACTION) - - # Close document - nwGUI.docViewer.docHeader._closeDocument() - assert nwGUI.docViewer.theHandle is None - - # Action on no document - assert not nwGUI.docViewer.docAction(nwDocAction.COPY) - - # Open again via menu - assert nwGUI.treeView.setSelectedHandle("88243afbe5ed8") - nwGUI.mainMenu.aViewDoc.activate(QAction.Trigger) - - # Select "Bod" link - assert nwGUI.docViewer.setCursorPosition(27) - nwGUI.docViewer._makeSelection(QTextCursor.WordUnderCursor) - theRect = nwGUI.docViewer.cursorRect() - # qtbot.mouseClick(nwGUI.docViewer.viewport(), Qt.LeftButton, pos=theRect.center(), delay=100) - nwGUI.docViewer._linkClicked(QUrl("#char=Bod")) - assert nwGUI.docViewer.theHandle == "4c4f28287af27" - - # Click mouse nav buttons - qtbot.mouseClick(nwGUI.docViewer.viewport(), Qt.BackButton, pos=theRect.center(), delay=100) - assert nwGUI.docViewer.theHandle == "88243afbe5ed8" - qtbot.mouseClick(nwGUI.docViewer.viewport(), Qt.ForwardButton, pos=theRect.center(), delay=100) - assert nwGUI.docViewer.theHandle == "4c4f28287af27" - - # Scroll bar default on empty document - nwGUI.docViewer.clear() - assert nwGUI.docViewer.getScrollPosition() == 0 - nwGUI.docViewer.reloadText() - - # Change document title - nwItem = nwGUI.theProject.projTree["4c4f28287af27"] - nwItem.setName("Test Title") - assert nwItem.itemName == "Test Title" - nwGUI.docViewer.updateDocInfo("4c4f28287af27") - assert nwGUI.docViewer.docHeader.theTitle.text() == "Characters › Test Title" - - # Ttile without full path - nwGUI.mainConf.showFullPath = False - nwGUI.docViewer.updateDocInfo("4c4f28287af27") - assert nwGUI.docViewer.docHeader.theTitle.text() == "Test Title" - nwGUI.mainConf.showFullPath = True - - # Document footer show/hide references - viewState = nwGUI.viewMeta.isVisible() - nwGUI.docViewer.docFooter._doShowHide() - assert nwGUI.viewMeta.isVisible() is not viewState - nwGUI.docViewer.docFooter._doShowHide() - assert nwGUI.viewMeta.isVisible() is viewState - - # Document footer sticky - viewState = nwGUI.docViewer.stickyRef - nwGUI.docViewer.docFooter._doToggleSticky(not viewState) - assert nwGUI.docViewer.stickyRef is not viewState - nwGUI.docViewer.docFooter._doToggleSticky(viewState) - assert nwGUI.docViewer.stickyRef is viewState - - # Document footer show/hide synopsis - assert nwGUI.viewDocument("f96ec11c6a3da") - assert len(nwGUI.docViewer.toPlainText()) == 4315 - nwGUI.docViewer.docFooter._doToggleSynopsis(False) - assert len(nwGUI.docViewer.toPlainText()) == 4099 - - # Document footer show/hide comments - assert nwGUI.viewDocument("846352075de7d") - assert len(nwGUI.docViewer.toPlainText()) == 675 - nwGUI.docViewer.docFooter._doToggleComments(False) - assert len(nwGUI.docViewer.toPlainText()) == 635 - - # qtbot.stopForInteraction() - nwGUI.closeMain() - nwGUI.close() - -@pytest.mark.gui -def testProjectTree(qtbot, yesToAll, nwMinimal, tmpDir): - - nwGUI = nw.main(["--testmode", "--config=%s" % nwMinimal, "--data=%s" % tmpDir, nwMinimal]) - qtbot.addWidget(nwGUI) - nwGUI.show() - qtbot.waitForWindowShown(nwGUI) - qtbot.wait(stepDelay) - - nwGUI.theProject.projTree.setSeed(42) - nwTree = nwGUI.treeView - - # No location selected for new item - assert not nwTree.newTreeItem(nwItemType.FILE, None) - assert not nwTree.newTreeItem(nwItemType.FOLDER, None) - - # Select a location - chItem = nwTree._getTreeItem("a6d311a93600a") - nwTree.setCurrentItem(chItem, QItemSelectionModel.Current) - chItem.setExpanded(True) - - # Create new item with no class set - assert nwTree.newTreeItem(nwItemType.FILE, None) - assert nwTree.newTreeItem(nwItemType.FOLDER, None) - - # Add roots - assert not nwTree.newTreeItem(nwItemType.ROOT, None) # Defaults to NOVEL - assert not nwTree.newTreeItem(nwItemType.ROOT, nwItemClass.WORLD) # Duplicate - assert nwTree.newTreeItem(nwItemType.ROOT, nwItemClass.CUSTOM) # Valid - - # Check that we have the correct tree order - assert nwTree.getTreeFromHandle("a6d311a93600a") == [ - "a6d311a93600a", "f5ab3e30151e1", "8c659a11cd429", "73475cb40a568", "44cb730c42048" - ] - - # Move second item up twice (should give same result) - nwTree.setSelectedHandle("8c659a11cd429") - nwGUI.mainMenu.aMoveUp.activate(QAction.Trigger) - assert nwTree.getTreeFromHandle("a6d311a93600a") == [ - "a6d311a93600a", "8c659a11cd429", "f5ab3e30151e1", "73475cb40a568", "44cb730c42048" - ] - nwGUI.mainMenu.aMoveUp.activate(QAction.Trigger) - assert nwTree.getTreeFromHandle("a6d311a93600a") == [ - "a6d311a93600a", "8c659a11cd429", "f5ab3e30151e1", "73475cb40a568", "44cb730c42048" - ] - - # Move it back down four times (last to should be the same) - nwGUI.mainMenu.aMoveDown.activate(QAction.Trigger) - assert nwTree.getTreeFromHandle("a6d311a93600a") == [ - "a6d311a93600a", "f5ab3e30151e1", "8c659a11cd429", "73475cb40a568", "44cb730c42048" - ] - nwGUI.mainMenu.aMoveDown.activate(QAction.Trigger) - assert nwTree.getTreeFromHandle("a6d311a93600a") == [ - "a6d311a93600a", "f5ab3e30151e1", "73475cb40a568", "8c659a11cd429", "44cb730c42048" - ] - nwGUI.mainMenu.aMoveDown.activate(QAction.Trigger) - assert nwTree.getTreeFromHandle("a6d311a93600a") == [ - "a6d311a93600a", "f5ab3e30151e1", "73475cb40a568", "44cb730c42048", "8c659a11cd429" - ] - nwGUI.mainMenu.aMoveDown.activate(QAction.Trigger) - assert nwTree.getTreeFromHandle("a6d311a93600a") == [ - "a6d311a93600a", "f5ab3e30151e1", "73475cb40a568", "44cb730c42048", "8c659a11cd429" - ] - - # Move a root item (top level items are different) twice - nwTree.flushTreeOrder() - assert nwGUI.theProject.projTree._treeOrder.index("9d5247ab588e0") == 9 - nwTree.setSelectedHandle("9d5247ab588e0") - - nwGUI.mainMenu.aMoveDown.activate(QAction.Trigger) - nwTree.flushTreeOrder() - assert nwGUI.theProject.projTree._treeOrder.index("9d5247ab588e0") == 10 - - nwGUI.mainMenu.aMoveDown.activate(QAction.Trigger) - nwTree.flushTreeOrder() - assert nwGUI.theProject.projTree._treeOrder.index("9d5247ab588e0") == 10 - - # Add some content to the new file - nwGUI.openDocument("73475cb40a568") - nwGUI.docEditor.setText("# Hello World\n") - nwGUI.saveDocument() - assert os.path.isfile(os.path.join(nwMinimal, "content", "73475cb40a568.nwd")) - - # Delete the items we added earlier - nwTree.clearSelection() - assert not nwTree.emptyTrash() # No folder yet - assert not nwTree.deleteItem(None) - assert not nwTree.deleteItem("1111111111111") - assert nwTree.deleteItem("73475cb40a568") # New File - assert nwTree.deleteItem("44cb730c42048") # New Folder - assert nwTree.deleteItem("71ee45a3c0db9") # Custom Root - assert "73475cb40a568" in nwGUI.theProject.projTree._treeOrder - assert "44cb730c42048" not in nwGUI.theProject.projTree._treeOrder - assert "71ee45a3c0db9" not in nwGUI.theProject.projTree._treeOrder - - # The file is in trash, empty it - assert os.path.isfile(os.path.join(nwMinimal, "content", "73475cb40a568.nwd")) - assert nwTree.emptyTrash() - assert not nwTree.emptyTrash() # Already empty - assert not os.path.isfile(os.path.join(nwMinimal, "content", "73475cb40a568.nwd")) - assert "73475cb40a568" not in nwGUI.theProject.projTree._treeOrder - - # Close the project - nwGUI.closeProject() - - # Add an orphaned file - orphFile = os.path.join(nwMinimal, "content", "1234567890abc.nwd") - with open(orphFile, mode="w+", encoding="utf8") as outFile: - outFile.write("# Hello World\n") - - # Open the project again - nwGUI.openProject(nwMinimal) - - # Check that the orphaned file was found and added to the tree - assert nwTree.orphRoot is not None - nwTree.flushTreeOrder() - assert "1234567890abc" not in nwGUI.theProject.projTree._treeOrder - orItem = nwTree._getTreeItem("1234567890abc") - assert orItem.text(nwTree.C_NAME) == "Orphaned File 1" - - # qtbot.stopForInteraction() - nwGUI.closeMain() - nwGUI.close() - -@pytest.mark.gui -def testEditFormatMenu(qtbot, yesToAll, nwLipsum, tmpDir): - - nwGUI = nw.main(["--testmode", "--config=%s" % nwLipsum, "--data=%s" % tmpDir]) - qtbot.addWidget(nwGUI) - nwGUI.show() - qtbot.waitForWindowShown(nwGUI) - qtbot.wait(stepDelay) - - # Test Document Action with No Project - assert not nwGUI.docEditor.docAction(nwDocAction.COPY) - - nwGUI.theProject.projTree.setSeed(42) - assert nwGUI.openProject(nwLipsum) - qtbot.wait(stepDelay) - - # Split By Chapter - assert nwGUI.openDocument("4c4f28287af27") - assert nwGUI.docEditor.setCursorPosition(30) - - cleanText = nwGUI.docEditor.getText()[27:74] - - # Bold - nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger) - fmtStr = "**Pellentesque** nec erat ut nulla posuere commodo." - assert nwGUI.docEditor.getText()[27:78] == fmtStr - qtbot.wait(stepDelay) - nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[27:74] == cleanText - qtbot.wait(stepDelay) - - # Italic - nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger) - fmtStr = "_Pellentesque_ nec erat ut nulla posuere commodo." - assert nwGUI.docEditor.getText()[27:76] == fmtStr - qtbot.wait(stepDelay) - nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[27:74] == cleanText - qtbot.wait(stepDelay) - - # Strikethrough - nwGUI.mainMenu.aFmtStrike.activate(QAction.Trigger) - fmtStr = "~~Pellentesque~~ nec erat ut nulla posuere commodo." - assert nwGUI.docEditor.getText()[27:78] == fmtStr - qtbot.wait(stepDelay) - nwGUI.mainMenu.aFmtStrike.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[27:74] == cleanText - qtbot.wait(stepDelay) - - # Should get us back to plain - nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger) - qtbot.wait(stepDelay) - nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger) - qtbot.wait(stepDelay) - nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger) - qtbot.wait(stepDelay) - nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[27:74] == cleanText - qtbot.wait(stepDelay) - - # Double Quotes - nwGUI.mainMenu.aFmtDQuote.activate(QAction.Trigger) - fmtStr = "“Pellentesque” nec erat ut nulla posuere commodo." - assert nwGUI.docEditor.getText()[27:76] == fmtStr - qtbot.wait(stepDelay) - nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[27:74] == cleanText - qtbot.wait(stepDelay) - - # Single Quotes - nwGUI.mainMenu.aFmtSQuote.activate(QAction.Trigger) - fmtStr = "‘Pellentesque’ nec erat ut nulla posuere commodo." - assert nwGUI.docEditor.getText()[27:76] == fmtStr - qtbot.wait(stepDelay) - nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[27:74] == cleanText - qtbot.wait(stepDelay) - - # Block Formats - assert nwGUI.docEditor.setCursorPosition(30) - nwGUI.mainMenu.aFmtHead1.activate(QAction.Trigger) - fmtStr = "# Pellentesque nec erat ut nulla posuere commodo." - assert nwGUI.docEditor.getText()[27:76] == fmtStr - qtbot.wait(stepDelay) - nwGUI.mainMenu.aFmtHead2.activate(QAction.Trigger) - fmtStr = "## Pellentesque nec erat ut nulla posuere commodo." - assert nwGUI.docEditor.getText()[27:77] == fmtStr - qtbot.wait(stepDelay) - nwGUI.mainMenu.aFmtHead3.activate(QAction.Trigger) - fmtStr = "### Pellentesque nec erat ut nulla posuere commodo." - assert nwGUI.docEditor.getText()[27:78] == fmtStr - qtbot.wait(stepDelay) - nwGUI.mainMenu.aFmtHead4.activate(QAction.Trigger) - fmtStr = "#### Pellentesque nec erat ut nulla posuere commodo." - assert nwGUI.docEditor.getText()[27:79] == fmtStr - qtbot.wait(stepDelay) - nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[27:74] == cleanText - qtbot.wait(stepDelay) - nwGUI.mainMenu.aFmtComment.activate(QAction.Trigger) - fmtStr = "% Pellentesque nec erat ut nulla posuere commodo." - assert nwGUI.docEditor.getText()[27:76] == fmtStr - qtbot.wait(stepDelay) - nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[27:74] == cleanText - qtbot.wait(stepDelay) - - # Check comment with no space before text - assert nwGUI.docEditor.setCursorPosition(27) - assert nwGUI.docEditor.insertText("%") - fmtStr = "%Pellentesque nec erat ut nulla posuere commodo." - assert nwGUI.docEditor.getText()[27:75] == fmtStr - qtbot.wait(stepDelay) - - nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[27:74] == cleanText - qtbot.wait(stepDelay) - - # Undo/Redo - nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger) - fmtStr = "%Pellentesque nec erat ut nulla posuere commodo." - assert nwGUI.docEditor.getText()[27:75] == fmtStr - qtbot.wait(stepDelay) - nwGUI.mainMenu.aEditRedo.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[27:74] == cleanText - qtbot.wait(stepDelay) - - # Cut, Copy and Paste - assert nwGUI.docEditor.setCursorPosition(27) - nwGUI.docEditor._makeSelection(QTextCursor.WordUnderCursor) - - nwGUI.mainMenu.aEditCut.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[27:77] == ( - " nec erat ut nulla posuere commodo. Curabitur nisi" - ) - - nwGUI.mainMenu.aEditPaste.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[27:77] == ( - "Pellentesque nec erat ut nulla posuere commodo. Cu" - ) - - assert nwGUI.docEditor.setCursorPosition(27) - nwGUI.docEditor._makeSelection(QTextCursor.WordUnderCursor) - - nwGUI.mainMenu.aEditCopy.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[27:77] == ( - "Pellentesque nec erat ut nulla posuere commodo. Cu" - ) - - assert nwGUI.docEditor.setCursorPosition(27) - nwGUI.mainMenu.aEditPaste.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[27:77] == ( - "PellentesquePellentesque nec erat ut nulla posuere" - ) - nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger) - - # Select Paragraph/All - assert nwGUI.docEditor.setCursorPosition(30) - nwGUI.mainMenu.aSelectPar.activate(QAction.Trigger) - theCursor = nwGUI.docEditor.textCursor() - assert theCursor.selectedText() == ( - "Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta " - "imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit " - "placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. " - "Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. " - "Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, " - "rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin " - "nunc lacus, imperdiet nec posuere ac, interdum non lectus." - ) - - assert nwGUI.docEditor.setCursorPosition(30) - nwGUI.mainMenu.aSelectAll.activate(QAction.Trigger) - theCursor = nwGUI.docEditor.textCursor() - assert len(theCursor.selectedText()) == 1883 - - # Clear the Text - nwGUI.docEditor.clear() - assert nwGUI.docEditor.isEmpty() - - # Replace Quotes - nwGUI.docEditor.setText(( - "### New Text\n\n" - "Text with 'single' quotes and 'tricky stuff's'.\n\n" - "Also text with \"double\" quotes which are \"less tricky\".\n\n" - )) - - nwGUI.mainMenu.aSelectAll.activate(QAction.Trigger) - nwGUI.mainMenu.aFmtReplSng.activate(QAction.Trigger) - assert nwGUI.docEditor.getText() == ( - "### New Text\n\n" - "Text with ‘single’ quotes and ‘tricky stuff’s’.\n\n" - "Also text with \"double\" quotes which are \"less tricky\".\n\n" - ) - - nwGUI.mainMenu.aSelectAll.activate(QAction.Trigger) - nwGUI.mainMenu.aFmtReplDbl.activate(QAction.Trigger) - assert nwGUI.docEditor.getText() == ( - "### New Text\n\n" - "Text with ‘single’ quotes and ‘tricky stuff’s’.\n\n" - "Also text with “double” quotes which are “less tricky”.\n\n" - ) - - # Test Invalid Document Action - assert not nwGUI.docEditor.docAction(nwDocAction.NO_ACTION) - - # Test Invalid Formats - nwGUI.docEditor.setText(( - "### New Text\n\n" - "@tag: Bod\n\n" - "Text with 'single' quotes and 'tricky stuff's'.\n\n" - "Also text with \"double\" quotes which are \"less tricky\".\n\n" - )) - - # Cannot Format Tag - assert nwGUI.docEditor.setCursorPosition(17) - assert not nwGUI.docEditor._formatBlock(nwDocAction.BLOCK_TXT) - - # Cannot Format Empty Line - assert nwGUI.docEditor.setCursorPosition(13) - assert not nwGUI.docEditor._formatBlock(nwDocAction.BLOCK_TXT) - - # Invalid Action - assert nwGUI.docEditor.setCursorPosition(30) - assert not nwGUI.docEditor._formatBlock(nwDocAction.NO_ACTION) - - # Ensure No Changes - assert nwGUI.docEditor.getText() == ( - "### New Text\n\n" - "@tag: Bod\n\n" - "Text with 'single' quotes and 'tricky stuff's'.\n\n" - "Also text with \"double\" quotes which are \"less tricky\".\n\n" - ) - - # qtbot.stopForInteraction() - nwGUI.closeMain() - nwGUI.close() - -@pytest.mark.gui -def testContextMenu(qtbot, yesToAll, nwLipsum, tmpDir): - - nwGUI = nw.main(["--testmode", "--config=%s" % nwLipsum, "--data=%s" % tmpDir]) - qtbot.addWidget(nwGUI) - nwGUI.show() - qtbot.waitForWindowShown(nwGUI) - qtbot.wait(stepDelay) - - nwGUI.theProject.projTree.setSeed(42) - assert nwGUI.openProject(nwLipsum) - assert nwGUI.openDocument("4c4f28287af27") - qtbot.wait(stepDelay) - - # Editor Context Menu - theCursor = nwGUI.docEditor.textCursor() - theCursor.setPosition(100) - nwGUI.docEditor.setTextCursor(theCursor) - theRect = nwGUI.docEditor.cursorRect() - - nwGUI.docEditor._openContextMenu(theRect.bottomRight()) - qtbot.mouseClick(nwGUI.docEditor, Qt.LeftButton, pos=theRect.topLeft()) - - nwGUI.docEditor._makePosSelection(QTextCursor.WordUnderCursor, theRect.center()) - theCursor = nwGUI.docEditor.textCursor() - assert theCursor.selectedText() == "imperdiet" - - nwGUI.docEditor._makePosSelection(QTextCursor.BlockUnderCursor, theRect.center()) - theCursor = nwGUI.docEditor.textCursor() - assert theCursor.selectedText() == ( - "Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta " - "imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit " - "placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. " - "Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. " - "Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, " - "rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin " - "nunc lacus, imperdiet nec posuere ac, interdum non lectus." - ) - - # Viewer Context Menu - assert nwGUI.viewDocument("4c4f28287af27") - - theCursor = nwGUI.docViewer.textCursor() - theCursor.setPosition(100) - nwGUI.docViewer.setTextCursor(theCursor) - theRect = nwGUI.docViewer.cursorRect() - - nwGUI.docViewer._openContextMenu(theRect.bottomRight()) - qtbot.mouseClick(nwGUI.docViewer, Qt.LeftButton, pos=theRect.topLeft()) - - nwGUI.docViewer._makePosSelection(QTextCursor.WordUnderCursor, theRect.center()) - theCursor = nwGUI.docViewer.textCursor() - assert theCursor.selectedText() == "imperdiet" - - nwGUI.docEditor._makePosSelection(QTextCursor.BlockUnderCursor, theRect.center()) - theCursor = nwGUI.docEditor.textCursor() - assert theCursor.selectedText() == ( - "Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta " - "imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit " - "placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. " - "Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. " - "Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, " - "rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin " - "nunc lacus, imperdiet nec posuere ac, interdum non lectus." - ) - - # Navigation History - assert nwGUI.viewDocument("04468803b92e1") - assert nwGUI.docViewer.theHandle == "04468803b92e1" - assert nwGUI.docViewer.docHeader.backButton.isEnabled() - assert not nwGUI.docViewer.docHeader.forwardButton.isEnabled() - - qtbot.mouseClick(nwGUI.docViewer.docHeader.backButton, Qt.LeftButton) - assert nwGUI.docViewer.theHandle == "4c4f28287af27" - assert not nwGUI.docViewer.docHeader.backButton.isEnabled() - assert nwGUI.docViewer.docHeader.forwardButton.isEnabled() - - qtbot.mouseClick(nwGUI.docViewer.docHeader.forwardButton, Qt.LeftButton) - assert nwGUI.docViewer.theHandle == "04468803b92e1" - assert nwGUI.docViewer.docHeader.backButton.isEnabled() - assert not nwGUI.docViewer.docHeader.forwardButton.isEnabled() - - # qtbot.stopForInteraction() - nwGUI.closeMain() - nwGUI.close() - -@pytest.mark.gui -def testInsertMenu(qtbot, monkeypatch, fncDir, tmpDir): - nwGUI = nw.main(["--testmode", "--config=%s" % tmpDir, "--data=%s" % tmpDir]) - qtbot.addWidget(nwGUI) - nwGUI.show() - qtbot.waitForWindowShown(nwGUI) - qtbot.wait(stepDelay) - - nwGUI.theProject.projTree.setSeed(42) - assert nwGUI.newProject({"projPath": fncDir}) - - assert nwGUI.treeView._getTreeItem("0e17daca5f3e1") is not None - - nwGUI.setFocus(1) - nwGUI.treeView.clearSelection() - nwGUI.treeView._getTreeItem("0e17daca5f3e1").setSelected(True) - assert nwGUI.openSelectedItem() - nwGUI.docEditor.clear() - - # Test Faulty Inserts - assert nwGUI.docEditor.insertText("hello world") - assert nwGUI.docEditor.getText() == "hello world" - nwGUI.docEditor.clear() - - assert not nwGUI.docEditor.insertText(nwDocInsert.NO_INSERT) - assert nwGUI.docEditor.isEmpty() - - assert not nwGUI.docEditor.insertText(None) - assert nwGUI.docEditor.isEmpty() - - # qtbot.stopForInteraction() - - # Check Menu Entries - nwGUI.mainMenu.aInsENDash.activate(QAction.Trigger) - assert nwGUI.docEditor.getText() == nwUnicode.U_ENDASH - nwGUI.docEditor.clear() - - nwGUI.mainMenu.aInsEMDash.activate(QAction.Trigger) - assert nwGUI.docEditor.getText() == nwUnicode.U_EMDASH - nwGUI.docEditor.clear() - - nwGUI.mainMenu.aInsEllipsis.activate(QAction.Trigger) - assert nwGUI.docEditor.getText() == nwUnicode.U_HELLIP - nwGUI.docEditor.clear() - - nwGUI.mainMenu.aInsQuoteLS.activate(QAction.Trigger) - assert nwGUI.docEditor.getText() == nwGUI.mainConf.fmtSingleQuotes[0] - nwGUI.docEditor.clear() - - nwGUI.mainMenu.aInsQuoteRS.activate(QAction.Trigger) - assert nwGUI.docEditor.getText() == nwGUI.mainConf.fmtSingleQuotes[1] - nwGUI.docEditor.clear() - - nwGUI.mainMenu.aInsQuoteLD.activate(QAction.Trigger) - assert nwGUI.docEditor.getText() == nwGUI.mainConf.fmtDoubleQuotes[0] - nwGUI.docEditor.clear() - - nwGUI.mainMenu.aInsQuoteRD.activate(QAction.Trigger) - assert nwGUI.docEditor.getText() == nwGUI.mainConf.fmtDoubleQuotes[1] - nwGUI.docEditor.clear() - - nwGUI.mainMenu.aInsMSApos.activate(QAction.Trigger) - assert nwGUI.docEditor.getText() == nwUnicode.U_MAPOSS - nwGUI.docEditor.clear() - - nwGUI.mainMenu.aInsHardBreak.activate(QAction.Trigger) - assert nwGUI.docEditor.getText() == " \n" - nwGUI.docEditor.clear() - - nwGUI.mainMenu.aInsNBSpace.activate(QAction.Trigger) - if nwGUI.mainConf.verQtValue >= 50900: - assert nwGUI.docEditor.getText() == nwUnicode.U_NBSP - else: - assert nwGUI.docEditor.getText() == " " - nwGUI.docEditor.clear() - - nwGUI.mainMenu.aInsThinSpace.activate(QAction.Trigger) - assert nwGUI.docEditor.getText() == nwUnicode.U_THNSP - nwGUI.docEditor.clear() - - nwGUI.mainMenu.aInsThinNBSpace.activate(QAction.Trigger) - if nwGUI.mainConf.verQtValue >= 50900: - assert nwGUI.docEditor.getText() == nwUnicode.U_THNBSP - else: - assert nwGUI.docEditor.getText() == " " - nwGUI.docEditor.clear() - - ## - # Insert Keywords - ## - - nwGUI.docEditor.setText("Stuff") - nwGUI.mainMenu.mInsKWItems[nwKeyWords.TAG_KEY][0].activate(QAction.Trigger) - assert nwGUI.docEditor.getText() == "Stuff\n%s: " % nwKeyWords.TAG_KEY - - nwGUI.docEditor.setText("Stuff") - nwGUI.mainMenu.mInsKWItems[nwKeyWords.POV_KEY][0].activate(QAction.Trigger) - assert nwGUI.docEditor.getText() == "Stuff\n%s: " % nwKeyWords.POV_KEY - - nwGUI.docEditor.setText("Stuff") - nwGUI.mainMenu.mInsKWItems[nwKeyWords.CHAR_KEY][0].activate(QAction.Trigger) - assert nwGUI.docEditor.getText() == "Stuff\n%s: " % nwKeyWords.CHAR_KEY - - nwGUI.docEditor.setText("Stuff") - nwGUI.mainMenu.mInsKWItems[nwKeyWords.PLOT_KEY][0].activate(QAction.Trigger) - assert nwGUI.docEditor.getText() == "Stuff\n%s: " % nwKeyWords.PLOT_KEY - - nwGUI.docEditor.setText("Stuff") - nwGUI.mainMenu.mInsKWItems[nwKeyWords.TIME_KEY][0].activate(QAction.Trigger) - assert nwGUI.docEditor.getText() == "Stuff\n%s: " % nwKeyWords.TIME_KEY - - nwGUI.docEditor.setText("Stuff") - nwGUI.mainMenu.mInsKWItems[nwKeyWords.WORLD_KEY][0].activate(QAction.Trigger) - assert nwGUI.docEditor.getText() == "Stuff\n%s: " % nwKeyWords.WORLD_KEY - - nwGUI.docEditor.setText("Stuff") - nwGUI.mainMenu.mInsKWItems[nwKeyWords.OBJECT_KEY][0].activate(QAction.Trigger) - assert nwGUI.docEditor.getText() == "Stuff\n%s: " % nwKeyWords.OBJECT_KEY - - nwGUI.docEditor.setText("Stuff") - nwGUI.mainMenu.mInsKWItems[nwKeyWords.ENTITY_KEY][0].activate(QAction.Trigger) - assert nwGUI.docEditor.getText() == "Stuff\n%s: " % nwKeyWords.ENTITY_KEY - - nwGUI.docEditor.setText("Stuff") - nwGUI.mainMenu.mInsKWItems[nwKeyWords.CUSTOM_KEY][0].activate(QAction.Trigger) - assert nwGUI.docEditor.getText() == "Stuff\n%s: " % nwKeyWords.CUSTOM_KEY - - # Faulty Keyword Inserts - assert not nwGUI.docEditor.insertKeyWord("blabla") - monkeypatch.setattr(QTextBlock, "isValid", lambda *args, **kwards: False) - assert not nwGUI.docEditor.insertKeyWord(nwKeyWords.TAG_KEY) - monkeypatch.undo() - - nwGUI.docEditor.clear() - - ## - # Insert text from file - ## - - nwGUI.closeDocument() - - # First, with no path - monkeypatch.setattr(QFileDialog, "getOpenFileName", lambda *args, **kwards: ("", "")) - assert not nwGUI.importDocument() - - # Then with a path, but an invalid one - monkeypatch.setattr(QFileDialog, "getOpenFileName", lambda *args, **kwards: (" ", "")) - assert not nwGUI.importDocument() - - # Then a valid path, but bot a file that exists - theFile = os.path.join(tmpDir, "import.txt") - monkeypatch.setattr(QFileDialog, "getOpenFileName", lambda *args, **kwards: (theFile, "")) - assert not nwGUI.importDocument() - - # Create the file and try again, but with no target document open - with open(theFile, mode="w+", encoding="utf8") as outFile: - outFile.write("Foo") - assert not nwGUI.importDocument() - - # Open the document from before, and add some text to it - nwGUI.openDocument("0e17daca5f3e1") - nwGUI.docEditor.setText("Bar") - assert nwGUI.docEditor.getText() == "Bar" - - # The document isn't empty, so the message box should pop - monkeypatch.setattr(QMessageBox, "question", lambda *args, **kwargs: QMessageBox.No) - assert not nwGUI.importDocument() - assert nwGUI.docEditor.getText() == "Bar" - - # Finally, accept the replaced text, this time we use the menu entry to trigger it - monkeypatch.setattr(QMessageBox, "question", lambda *args, **kwargs: QMessageBox.Yes) - nwGUI.mainMenu.aImportFile.activate(QAction.Trigger) - assert nwGUI.docEditor.getText() == "Foo" - - ## - # Reveal file location - ## - - theMessage = "" - - def recordMsg(*args): - nonlocal theMessage - theMessage = args[3] - return None - - assert not theMessage - monkeypatch.setattr(QMessageBox, "information", recordMsg) - nwGUI.mainMenu.aFileDetails.activate(QAction.Trigger) - - theBits = theMessage.split("
") - assert len(theBits) == 3 - assert theBits[0] == "File details for the currently open file" - assert theBits[1] == "Handle: 0e17daca5f3e1" - assert theBits[2] == "Location: %s" % os.path.join(fncDir, "content", "0e17daca5f3e1.nwd") - - # qtbot.stopForInteraction() - nwGUI.closeMain() - nwGUI.close() - -@pytest.mark.gui -def testTextSearch(qtbot, monkeypatch, yesToAll, nwLipsum, tmpDir): - - nwGUI = nw.main(["--testmode", "--config=%s" % nwLipsum, "--data=%s" % tmpDir]) - qtbot.addWidget(nwGUI) - nwGUI.show() - qtbot.waitForWindowShown(nwGUI) - qtbot.wait(stepDelay) - - nwGUI.theProject.projTree.setSeed(42) - assert nwGUI.openProject(nwLipsum) - assert nwGUI.openDocument("4c4f28287af27") - origText = nwGUI.docEditor.getText() - qtbot.wait(stepDelay) - - # Select the Word "est" - assert nwGUI.docEditor.setCursorPosition(618) - nwGUI.docEditor._makeSelection(QTextCursor.WordUnderCursor) - theCursor = nwGUI.docEditor.textCursor() - assert theCursor.selectedText() == "est" - - # Activate Search - nwGUI.mainMenu.aFind.activate(QAction.Trigger) - assert nwGUI.docEditor.docSearch.isVisible() - assert nwGUI.docEditor.docSearch.getSearchText() == "est" - - # Find Next by Enter - monkeypatch.setattr(nwGUI.docEditor.docSearch.searchBox, "hasFocus", lambda: True) - qtbot.keyClick(nwGUI.docEditor.docSearch.searchBox, Qt.Key_Return, delay=keyDelay) - assert abs(nwGUI.docEditor.getCursorPosition() - 1272) < 3 - - # Find Next by Button - qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay) - assert abs(nwGUI.docEditor.getCursorPosition() - 1486) < 3 - - # Activate Loop Search - nwGUI.docEditor.docSearch.toggleLoop.activate(QAction.Trigger) - assert nwGUI.docEditor.docSearch.toggleLoop.isChecked() - assert nwGUI.docEditor.docSearch.doLoop - - # Find Next by Menu Search > Find Next - nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) - assert abs(nwGUI.docEditor.getCursorPosition() - 620) < 3 - - # Close Search - nwGUI.docEditor.docSearch.cancelSearch.activate(QAction.Trigger) - assert not nwGUI.docEditor.docSearch.isVisible() - assert nwGUI.docEditor.setCursorPosition(15) - - # Toggle Search Again with Header Button - qtbot.mouseClick(nwGUI.docEditor.docHeader.searchButton, Qt.LeftButton, delay=keyDelay) - assert nwGUI.docEditor.docSearch.setSearchText("") - assert nwGUI.docEditor.docSearch.isVisible() - - # Enable RegEx Search - nwGUI.docEditor.docSearch.toggleRegEx.activate(QAction.Trigger) - assert nwGUI.docEditor.docSearch.toggleRegEx.isChecked() - assert nwGUI.docEditor.docSearch.isRegEx - - # Set Invalid RegEx - assert nwGUI.docEditor.docSearch.setSearchText(r"\bSus[") - qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay) - assert nwGUI.docEditor.getCursorPosition() < 3 # No result - - # Set Valid RegEx - assert nwGUI.docEditor.docSearch.setSearchText(r"\bSus") - qtbot.mouseClick(nwGUI.docEditor.docSearch.searchButton, Qt.LeftButton, delay=keyDelay) - assert abs(nwGUI.docEditor.getCursorPosition() - 196) < 3 - - # Find Next and then Prev - nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) - assert abs(nwGUI.docEditor.getCursorPosition() - 297) < 3 - nwGUI.mainMenu.aFindPrev.activate(QAction.Trigger) - assert abs(nwGUI.docEditor.getCursorPosition() - 196) < 3 - - # Make RegEx Case Sensitive - nwGUI.docEditor.docSearch.toggleCase.activate(QAction.Trigger) - assert nwGUI.docEditor.docSearch.toggleCase.isChecked() - assert nwGUI.docEditor.docSearch.isCaseSense - - # Find Next (One Result) - nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) - assert abs(nwGUI.docEditor.getCursorPosition() - 599) < 3 - nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) - assert abs(nwGUI.docEditor.getCursorPosition() - 599) < 3 - - # Trigger Replace - nwGUI.mainMenu.aReplace.activate(QAction.Trigger) - assert nwGUI.docEditor.docSearch.setReplaceText("foo") - - # Disable RegEx Case Sensitive - nwGUI.docEditor.docSearch.toggleCase.activate(QAction.Trigger) - assert not nwGUI.docEditor.docSearch.toggleCase.isChecked() - assert not nwGUI.docEditor.docSearch.isCaseSense - - # Toggle Replace Preserve Case - nwGUI.docEditor.docSearch.toggleMatchCap.activate(QAction.Trigger) - assert nwGUI.docEditor.docSearch.toggleMatchCap.isChecked() - assert nwGUI.docEditor.docSearch.doMatchCap - - # Replace "Sus" with "Foo" via Menu - nwGUI.mainMenu.aReplaceNext.activate(QAction.Trigger) - assert nwGUI.docEditor.getText()[596:607] == "Foopendisse" - - # Find Next to Loop File - nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) - - # Replace "sus" with "foo" via Replace Button - qtbot.mouseClick(nwGUI.docEditor.docSearch.replaceButton, Qt.LeftButton, delay=keyDelay) - assert nwGUI.docEditor.getText()[193:201] == "foocipit" - - # Revert Last Two Replaces - assert nwGUI.docEditor.docAction(nwDocAction.UNDO) - assert nwGUI.docEditor.docAction(nwDocAction.UNDO) - assert nwGUI.docEditor.getText() == origText - - # Disable RegEx Search - nwGUI.docEditor.docSearch.toggleRegEx.activate(QAction.Trigger) - assert not nwGUI.docEditor.docSearch.toggleRegEx.isChecked() - assert not nwGUI.docEditor.docSearch.isRegEx - - # Close Search and Select "est" Again - nwGUI.docEditor.docSearch.cancelSearch.activate(QAction.Trigger) - assert nwGUI.docEditor.setCursorPosition(618) - nwGUI.docEditor._makeSelection(QTextCursor.WordUnderCursor) - theCursor = nwGUI.docEditor.textCursor() - assert theCursor.selectedText() == "est" - - # Activate Search Again - nwGUI.mainMenu.aFind.activate(QAction.Trigger) - assert nwGUI.docEditor.docSearch.isVisible() - assert nwGUI.docEditor.docSearch.getSearchText() == "est" - - # Enable Full Word Search - nwGUI.docEditor.docSearch.toggleWord.activate(QAction.Trigger) - assert nwGUI.docEditor.docSearch.toggleWord.isChecked() - assert nwGUI.docEditor.docSearch.isWholeWord - - # Only One Match - nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) - assert abs(nwGUI.docEditor.getCursorPosition() - 620) < 3 - nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) - assert abs(nwGUI.docEditor.getCursorPosition() - 620) < 3 - - # Enable Next Doc Search - nwGUI.docEditor.docSearch.toggleProject.activate(QAction.Trigger) - assert nwGUI.docEditor.docSearch.toggleProject.isChecked() - assert nwGUI.docEditor.docSearch.doNextFile - - # Next Match - nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) - assert nwGUI.docEditor.theHandle == "2426c6f0ca922" # Next document - nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) - assert abs(nwGUI.docEditor.getCursorPosition() - 620) < 3 - nwGUI.mainMenu.aFindNext.activate(QAction.Trigger) - assert abs(nwGUI.docEditor.getCursorPosition() - 1127) < 3 - - # Toggle Replace - nwGUI.docEditor._beginReplace() - - # MonkeyPatch the focus cycle. We can't really test this very well, other than - # check that the tabs aren't captured when the main editor has focus - monkeypatch.setattr(nwGUI.docEditor, "hasFocus", lambda: True) - monkeypatch.setattr(nwGUI.docEditor.docSearch.searchBox, "hasFocus", lambda: False) - monkeypatch.setattr(nwGUI.docEditor.docSearch.replaceBox, "hasFocus", lambda: False) - assert not nwGUI.docEditor.focusNextPrevChild(True) - - monkeypatch.setattr(nwGUI.docEditor, "hasFocus", lambda: False) - monkeypatch.setattr(nwGUI.docEditor.docSearch.searchBox, "hasFocus", lambda: True) - monkeypatch.setattr(nwGUI.docEditor.docSearch.replaceBox, "hasFocus", lambda: False) - assert nwGUI.docEditor.focusNextPrevChild(True) - - monkeypatch.setattr(nwGUI.docEditor.docSearch.searchBox, "hasFocus", lambda: False) - monkeypatch.setattr(nwGUI.docEditor.docSearch.replaceBox, "hasFocus", lambda: True) - assert nwGUI.docEditor.focusNextPrevChild(True) - - # qtbot.stopForInteraction() - nwGUI.closeMain() - nwGUI.close() - -@pytest.mark.gui -def testOutline(qtbot, yesToAll, nwLipsum, tmpDir): - - nwGUI = nw.main(["--testmode", "--config=%s" % nwLipsum, "--data=%s" % tmpDir]) - qtbot.addWidget(nwGUI) - nwGUI.show() - qtbot.waitForWindowShown(nwGUI) - qtbot.wait(stepDelay) - - assert nwGUI.openProject(nwLipsum) - nwGUI.mainConf.lastPath = nwLipsum - - nwGUI.rebuildIndex() - nwGUI.tabWidget.setCurrentIndex(nwGUI.idxTabProj) - - assert nwGUI.projView.topLevelItemCount() > 0 - - # Context Menu - nwGUI.projView._headerRightClick(QPoint(1, 1)) - nwGUI.projView.headerMenu.actionMap[nwOutline.CCOUNT].activate(QAction.Trigger) - nwGUI.projView.headerMenu.close() - qtbot.mouseClick(nwGUI.projView, Qt.LeftButton) - - nwGUI.projView._loadHeaderState() - assert not nwGUI.projView.colHidden[nwOutline.CCOUNT] - - # First Item - nwGUI.rebuildOutline() - selItem = nwGUI.projView.topLevelItem(0) - assert isinstance(selItem, QTreeWidgetItem) - - nwGUI.projView.setCurrentItem(selItem) - assert nwGUI.projMeta.titleLabel.text() == "Title" - assert nwGUI.projMeta.titleValue.text() == "Lorem Ipsum" - assert nwGUI.projMeta.fileValue.text() == "Lorem Ipsum" - assert nwGUI.projMeta.itemValue.text() == "Finished" - - assert nwGUI.projMeta.cCValue.text() == "230" - assert nwGUI.projMeta.wCValue.text() == "40" - assert nwGUI.projMeta.pCValue.text() == "3" - - # Scene One - actItem = nwGUI.projView.topLevelItem(1) - chpItem = actItem.child(0) - selItem = chpItem.child(0) - - nwGUI.projView.setCurrentItem(selItem) - assert nwGUI.projMeta.titleLabel.text() == "Scene" - assert nwGUI.projMeta.titleValue.text() == "Scene One" - assert nwGUI.projMeta.fileValue.text() == "Scene One" - assert nwGUI.projMeta.itemValue.text() == "Finished" - - # Click POV Link - assert nwGUI.projMeta.povKeyValue.text() == "Bod" - nwGUI.projMeta._tagClicked("#pov=Bod") - assert nwGUI.docViewer.theHandle == "4c4f28287af27" - - # qtbot.stopForInteraction() - nwGUI.closeMain() - nwGUI.close() - -@pytest.mark.gui -def testThemes(qtbot, yesToAll, nwMinimal, tmpDir): - - nwGUI = nw.main(["--testmode", "--config=%s" % nwMinimal, "--data=%s" % tmpDir, nwMinimal]) - qtbot.addWidget(nwGUI) - nwGUI.show() - qtbot.waitForWindowShown(nwGUI) - qtbot.wait(500) - - # Change Settings - assert nw.CONFIG.confPath == nwMinimal - nw.CONFIG.guiTheme = "default_dark" - nw.CONFIG.guiSyntax = "tomorrow_night_eighties" - nw.CONFIG.guiIcons = "typicons_colour_dark" - nw.CONFIG.guiDark = True - nw.CONFIG.guiFont = "Cantarell" - nw.CONFIG.guiFontSize = 11 - nw.CONFIG.confChanged = True - assert nw.CONFIG.saveConfig() - - nwGUI.closeMain() - nwGUI.close() - del nwGUI - - # Re-open - assert nw.CONFIG.confPath == nwMinimal - nwGUI = nw.main(["--testmode", "--config=%s" % nwMinimal, "--data=%s" % tmpDir, nwMinimal]) - assert nwGUI.mainConf.confPath == nwMinimal - qtbot.addWidget(nwGUI) - nwGUI.show() - qtbot.waitForWindowShown(nwGUI) - qtbot.wait(500) - - assert nw.CONFIG.guiTheme == "default_dark" - assert nw.CONFIG.guiSyntax == "tomorrow_night_eighties" - assert nw.CONFIG.guiIcons == "typicons_colour_dark" - assert nw.CONFIG.guiDark is True - assert nw.CONFIG.guiFont == "Cantarell" - assert nw.CONFIG.guiFontSize == 11 - - # Check GUI Colours - thePalette = nwGUI.palette() - assert thePalette.window().color() == QColor(54, 54, 54) - assert thePalette.windowText().color() == QColor(174, 174, 174) - assert thePalette.base().color() == QColor(62, 62, 62) - assert thePalette.alternateBase().color() == QColor(67, 67, 67) - assert thePalette.text().color() == QColor(174, 174, 174) - assert thePalette.toolTipBase().color() == QColor(255, 255, 192) - assert thePalette.toolTipText().color() == QColor(21, 21, 13) - assert thePalette.button().color() == QColor(62, 62, 62) - assert thePalette.buttonText().color() == QColor(174, 174, 174) - assert thePalette.brightText().color() == QColor(174, 174, 174) - assert thePalette.highlight().color() == QColor(44, 152, 247) - assert thePalette.highlightedText().color() == QColor(255, 255, 255) - assert thePalette.link().color() == QColor(44, 152, 247) - assert thePalette.linkVisited().color() == QColor(44, 152, 247) - - assert nwGUI.theTheme.treeWCount == [197, 200, 198] - assert nwGUI.theTheme.statNone == [150, 152, 150] - assert nwGUI.theTheme.statSaved == [39, 135, 78] - assert nwGUI.theTheme.statUnsaved == [138, 32, 32] - - # Check Syntax Colours - assert nwGUI.theTheme.colBack == [45, 45, 45] - assert nwGUI.theTheme.colText == [204, 204, 204] - assert nwGUI.theTheme.colLink == [102, 153, 204] - assert nwGUI.theTheme.colHead == [102, 153, 204] - assert nwGUI.theTheme.colHeadH == [102, 153, 204] - assert nwGUI.theTheme.colEmph == [249, 145, 57] - assert nwGUI.theTheme.colDialN == [242, 119, 122] - assert nwGUI.theTheme.colDialD == [153, 204, 153] - assert nwGUI.theTheme.colDialS == [255, 204, 102] - assert nwGUI.theTheme.colHidden == [153, 153, 153] - assert nwGUI.theTheme.colKey == [242, 119, 122] - assert nwGUI.theTheme.colVal == [204, 153, 204] - assert nwGUI.theTheme.colSpell == [242, 119, 122] - assert nwGUI.theTheme.colTagErr == [153, 204, 153] - assert nwGUI.theTheme.colRepTag == [102, 204, 204] - assert nwGUI.theTheme.colMod == [249, 145, 57] - - # Test Icon class - theIcons = nwGUI.theTheme.theIcons - nw.CONFIG.guiIcons = "invalid" - assert not theIcons.updateTheme() - nw.CONFIG.guiIcons = "typicons_colour_dark" - assert theIcons.updateTheme() - - # Ask for a non-existent key - anImg = theIcons.loadDecoration("nonsense", 20, 20) - assert isinstance(anImg, QPixmap) - assert anImg.isNull() - - # Add a non-existent file and request it - theIcons.DECO_MAP["nonsense"] = "nofile.jpg" - anImg = theIcons.loadDecoration("nonsense", 20, 20) - assert isinstance(anImg, QPixmap) - assert anImg.isNull() - - # Get a real image, with different size parameters - anImg = theIcons.loadDecoration("wiz-back", 20, None) - assert isinstance(anImg, QPixmap) - assert not anImg.isNull() - assert anImg.width() == 20 - assert anImg.height() >= 56 - - anImg = theIcons.loadDecoration("wiz-back", None, 70) - assert isinstance(anImg, QPixmap) - assert not anImg.isNull() - assert anImg.height() == 70 - assert anImg.width() >= 24 - - anImg = theIcons.loadDecoration("wiz-back", 30, 70) - assert isinstance(anImg, QPixmap) - assert not anImg.isNull() - assert anImg.height() == 70 - assert anImg.width() == 30 - - anImg = theIcons.loadDecoration("wiz-back", None, None) - assert isinstance(anImg, QPixmap) - assert not anImg.isNull() - assert anImg.height() >= 1500 - assert anImg.width() >= 500 - - # Load icons - anIcon = theIcons.getIcon("nonsense") - assert isinstance(anIcon, QIcon) - assert anIcon.isNull() - - anIcon = theIcons.getIcon("novelwriter") - assert isinstance(anIcon, QIcon) - assert not anIcon.isNull() - - # Add dummy icons and test alternative load paths - theIcons.ICON_MAP["testicon1"] = (QStyle.SP_DriveHDIcon, None) - anIcon = theIcons.getIcon("testicon1") - assert isinstance(anIcon, QIcon) - assert not anIcon.isNull() - - theIcons.ICON_MAP["testicon2"] = (None, "folder") - anIcon = theIcons.getIcon("testicon2") - assert isinstance(anIcon, QIcon) - - theIcons.ICON_MAP["testicon3"] = (None, None) - anIcon = theIcons.getIcon("testicon3") - assert isinstance(anIcon, QIcon) - assert anIcon.isNull() - - # qtbot.stopForInteraction() - nwGUI.closeMain() - nwGUI.close() diff --git a/tests/test_gui_mainmenu.py b/tests/test_gui_mainmenu.py new file mode 100644 index 00000000..33408505 --- /dev/null +++ b/tests/test_gui_mainmenu.py @@ -0,0 +1,533 @@ +# -*- coding: utf-8 -*- +"""novelWriter Main GUI Main Menu Class Tester +""" + +import pytest +import os + +from PyQt5.QtCore import Qt +from PyQt5.QtGui import QTextCursor, QTextBlock +from PyQt5.QtWidgets import QAction, QFileDialog, QMessageBox + +from nw.constants import nwUnicode, nwDocAction, nwDocInsert, nwKeyWords + +keyDelay = 2 +typeDelay = 1 +stepDelay = 20 + +@pytest.mark.gui +def testGuiMenu_EditFormat(qtbot, monkeypatch, nwGUI, nwLipsum): + """Test the main menu Edit and Format entries. + """ + # Block message box + monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes) + + # Test Document Action with No Project + assert not nwGUI.docEditor.docAction(nwDocAction.COPY) + + nwGUI.theProject.projTree.setSeed(42) + assert nwGUI.openProject(nwLipsum) + qtbot.wait(stepDelay) + + # Split By Chapter + assert nwGUI.openDocument("4c4f28287af27") + assert nwGUI.docEditor.setCursorPosition(30) + + cleanText = nwGUI.docEditor.getText()[27:74] + + # Bold + nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger) + fmtStr = "**Pellentesque** nec erat ut nulla posuere commodo." + assert nwGUI.docEditor.getText()[27:78] == fmtStr + qtbot.wait(stepDelay) + nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger) + assert nwGUI.docEditor.getText()[27:74] == cleanText + qtbot.wait(stepDelay) + + # Italic + nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger) + fmtStr = "_Pellentesque_ nec erat ut nulla posuere commodo." + assert nwGUI.docEditor.getText()[27:76] == fmtStr + qtbot.wait(stepDelay) + nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger) + assert nwGUI.docEditor.getText()[27:74] == cleanText + qtbot.wait(stepDelay) + + # Strikethrough + nwGUI.mainMenu.aFmtStrike.activate(QAction.Trigger) + fmtStr = "~~Pellentesque~~ nec erat ut nulla posuere commodo." + assert nwGUI.docEditor.getText()[27:78] == fmtStr + qtbot.wait(stepDelay) + nwGUI.mainMenu.aFmtStrike.activate(QAction.Trigger) + assert nwGUI.docEditor.getText()[27:74] == cleanText + qtbot.wait(stepDelay) + + # Should get us back to plain + nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger) + qtbot.wait(stepDelay) + nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger) + qtbot.wait(stepDelay) + nwGUI.mainMenu.aFmtEmph.activate(QAction.Trigger) + qtbot.wait(stepDelay) + nwGUI.mainMenu.aFmtStrong.activate(QAction.Trigger) + assert nwGUI.docEditor.getText()[27:74] == cleanText + qtbot.wait(stepDelay) + + # Double Quotes + nwGUI.mainMenu.aFmtDQuote.activate(QAction.Trigger) + fmtStr = "“Pellentesque” nec erat ut nulla posuere commodo." + assert nwGUI.docEditor.getText()[27:76] == fmtStr + qtbot.wait(stepDelay) + nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger) + assert nwGUI.docEditor.getText()[27:74] == cleanText + qtbot.wait(stepDelay) + + # Single Quotes + nwGUI.mainMenu.aFmtSQuote.activate(QAction.Trigger) + fmtStr = "‘Pellentesque’ nec erat ut nulla posuere commodo." + assert nwGUI.docEditor.getText()[27:76] == fmtStr + qtbot.wait(stepDelay) + nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger) + assert nwGUI.docEditor.getText()[27:74] == cleanText + qtbot.wait(stepDelay) + + # Block Formats + assert nwGUI.docEditor.setCursorPosition(30) + nwGUI.mainMenu.aFmtHead1.activate(QAction.Trigger) + fmtStr = "# Pellentesque nec erat ut nulla posuere commodo." + assert nwGUI.docEditor.getText()[27:76] == fmtStr + qtbot.wait(stepDelay) + nwGUI.mainMenu.aFmtHead2.activate(QAction.Trigger) + fmtStr = "## Pellentesque nec erat ut nulla posuere commodo." + assert nwGUI.docEditor.getText()[27:77] == fmtStr + qtbot.wait(stepDelay) + nwGUI.mainMenu.aFmtHead3.activate(QAction.Trigger) + fmtStr = "### Pellentesque nec erat ut nulla posuere commodo." + assert nwGUI.docEditor.getText()[27:78] == fmtStr + qtbot.wait(stepDelay) + nwGUI.mainMenu.aFmtHead4.activate(QAction.Trigger) + fmtStr = "#### Pellentesque nec erat ut nulla posuere commodo." + assert nwGUI.docEditor.getText()[27:79] == fmtStr + qtbot.wait(stepDelay) + nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger) + assert nwGUI.docEditor.getText()[27:74] == cleanText + qtbot.wait(stepDelay) + nwGUI.mainMenu.aFmtComment.activate(QAction.Trigger) + fmtStr = "% Pellentesque nec erat ut nulla posuere commodo." + assert nwGUI.docEditor.getText()[27:76] == fmtStr + qtbot.wait(stepDelay) + nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger) + assert nwGUI.docEditor.getText()[27:74] == cleanText + qtbot.wait(stepDelay) + + # Check comment with no space before text + assert nwGUI.docEditor.setCursorPosition(27) + assert nwGUI.docEditor.insertText("%") + fmtStr = "%Pellentesque nec erat ut nulla posuere commodo." + assert nwGUI.docEditor.getText()[27:75] == fmtStr + qtbot.wait(stepDelay) + + nwGUI.mainMenu.aFmtNoFormat.activate(QAction.Trigger) + assert nwGUI.docEditor.getText()[27:74] == cleanText + qtbot.wait(stepDelay) + + # Undo/Redo + nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger) + fmtStr = "%Pellentesque nec erat ut nulla posuere commodo." + assert nwGUI.docEditor.getText()[27:75] == fmtStr + qtbot.wait(stepDelay) + nwGUI.mainMenu.aEditRedo.activate(QAction.Trigger) + assert nwGUI.docEditor.getText()[27:74] == cleanText + qtbot.wait(stepDelay) + + # Cut, Copy and Paste + assert nwGUI.docEditor.setCursorPosition(27) + nwGUI.docEditor._makeSelection(QTextCursor.WordUnderCursor) + + nwGUI.mainMenu.aEditCut.activate(QAction.Trigger) + assert nwGUI.docEditor.getText()[27:77] == ( + " nec erat ut nulla posuere commodo. Curabitur nisi" + ) + + nwGUI.mainMenu.aEditPaste.activate(QAction.Trigger) + assert nwGUI.docEditor.getText()[27:77] == ( + "Pellentesque nec erat ut nulla posuere commodo. Cu" + ) + + assert nwGUI.docEditor.setCursorPosition(27) + nwGUI.docEditor._makeSelection(QTextCursor.WordUnderCursor) + + nwGUI.mainMenu.aEditCopy.activate(QAction.Trigger) + assert nwGUI.docEditor.getText()[27:77] == ( + "Pellentesque nec erat ut nulla posuere commodo. Cu" + ) + + assert nwGUI.docEditor.setCursorPosition(27) + nwGUI.mainMenu.aEditPaste.activate(QAction.Trigger) + assert nwGUI.docEditor.getText()[27:77] == ( + "PellentesquePellentesque nec erat ut nulla posuere" + ) + nwGUI.mainMenu.aEditUndo.activate(QAction.Trigger) + + # Select Paragraph/All + assert nwGUI.docEditor.setCursorPosition(30) + nwGUI.mainMenu.aSelectPar.activate(QAction.Trigger) + theCursor = nwGUI.docEditor.textCursor() + assert theCursor.selectedText() == ( + "Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta " + "imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit " + "placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. " + "Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. " + "Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, " + "rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin " + "nunc lacus, imperdiet nec posuere ac, interdum non lectus." + ) + + assert nwGUI.docEditor.setCursorPosition(30) + nwGUI.mainMenu.aSelectAll.activate(QAction.Trigger) + theCursor = nwGUI.docEditor.textCursor() + assert len(theCursor.selectedText()) == 1883 + + # Clear the Text + nwGUI.docEditor.clear() + assert nwGUI.docEditor.isEmpty() + + # Replace Quotes + nwGUI.docEditor.setText(( + "### New Text\n\n" + "Text with 'single' quotes and 'tricky stuff's'.\n\n" + "Also text with \"double\" quotes which are \"less tricky\".\n\n" + )) + + nwGUI.mainMenu.aSelectAll.activate(QAction.Trigger) + nwGUI.mainMenu.aFmtReplSng.activate(QAction.Trigger) + assert nwGUI.docEditor.getText() == ( + "### New Text\n\n" + "Text with ‘single’ quotes and ‘tricky stuff’s’.\n\n" + "Also text with \"double\" quotes which are \"less tricky\".\n\n" + ) + + nwGUI.mainMenu.aSelectAll.activate(QAction.Trigger) + nwGUI.mainMenu.aFmtReplDbl.activate(QAction.Trigger) + assert nwGUI.docEditor.getText() == ( + "### New Text\n\n" + "Text with ‘single’ quotes and ‘tricky stuff’s’.\n\n" + "Also text with “double” quotes which are “less tricky”.\n\n" + ) + + # Test Invalid Document Action + assert not nwGUI.docEditor.docAction(nwDocAction.NO_ACTION) + + # Test Invalid Formats + nwGUI.docEditor.setText(( + "### New Text\n\n" + "@tag: Bod\n\n" + "Text with 'single' quotes and 'tricky stuff's'.\n\n" + "Also text with \"double\" quotes which are \"less tricky\".\n\n" + )) + + # Cannot Format Tag + assert nwGUI.docEditor.setCursorPosition(17) + assert not nwGUI.docEditor._formatBlock(nwDocAction.BLOCK_TXT) + + # Cannot Format Empty Line + assert nwGUI.docEditor.setCursorPosition(13) + assert not nwGUI.docEditor._formatBlock(nwDocAction.BLOCK_TXT) + + # Invalid Action + assert nwGUI.docEditor.setCursorPosition(30) + assert not nwGUI.docEditor._formatBlock(nwDocAction.NO_ACTION) + + # Ensure No Changes + assert nwGUI.docEditor.getText() == ( + "### New Text\n\n" + "@tag: Bod\n\n" + "Text with 'single' quotes and 'tricky stuff's'.\n\n" + "Also text with \"double\" quotes which are \"less tricky\".\n\n" + ) + + # qtbot.stopForInteraction() + +# END Test testGuiMenu_EditFormat + +@pytest.mark.gui +def testGuiMenu_ContextMenus(qtbot, monkeypatch, nwGUI, nwLipsum): + """Test the context menus. + """ + # Block message box + monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes) + + nwGUI.theProject.projTree.setSeed(42) + assert nwGUI.openProject(nwLipsum) + assert nwGUI.openDocument("4c4f28287af27") + qtbot.wait(stepDelay) + + # Editor Context Menu + theCursor = nwGUI.docEditor.textCursor() + theCursor.setPosition(100) + nwGUI.docEditor.setTextCursor(theCursor) + theRect = nwGUI.docEditor.cursorRect() + + nwGUI.docEditor._openContextMenu(theRect.bottomRight()) + qtbot.mouseClick(nwGUI.docEditor, Qt.LeftButton, pos=theRect.topLeft()) + + nwGUI.docEditor._makePosSelection(QTextCursor.WordUnderCursor, theRect.center()) + theCursor = nwGUI.docEditor.textCursor() + assert theCursor.selectedText() == "imperdiet" + + nwGUI.docEditor._makePosSelection(QTextCursor.BlockUnderCursor, theRect.center()) + theCursor = nwGUI.docEditor.textCursor() + assert theCursor.selectedText() == ( + "Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta " + "imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit " + "placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. " + "Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. " + "Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, " + "rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin " + "nunc lacus, imperdiet nec posuere ac, interdum non lectus." + ) + + # Viewer Context Menu + assert nwGUI.viewDocument("4c4f28287af27") + + theCursor = nwGUI.docViewer.textCursor() + theCursor.setPosition(100) + nwGUI.docViewer.setTextCursor(theCursor) + theRect = nwGUI.docViewer.cursorRect() + + nwGUI.docViewer._openContextMenu(theRect.bottomRight()) + qtbot.mouseClick(nwGUI.docViewer, Qt.LeftButton, pos=theRect.topLeft()) + + nwGUI.docViewer._makePosSelection(QTextCursor.WordUnderCursor, theRect.center()) + theCursor = nwGUI.docViewer.textCursor() + assert theCursor.selectedText() == "imperdiet" + + nwGUI.docEditor._makePosSelection(QTextCursor.BlockUnderCursor, theRect.center()) + theCursor = nwGUI.docEditor.textCursor() + assert theCursor.selectedText() == ( + "Pellentesque nec erat ut nulla posuere commodo. Curabitur nisi augue, imperdiet et porta " + "imperdiet, efficitur id leo. Cras finibus arcu at nibh commodo congue. Proin suscipit " + "placerat condimentum. Aenean ante enim, cursus id lorem a, blandit venenatis nibh. " + "Maecenas suscipit porta elit, sit amet porta felis porttitor eu. Sed a dui nibh. " + "Phasellus sed faucibus dui. Pellentesque felis nulla, ultrices non efficitur quis, " + "rutrum id mi. Mauris tempus auctor nisl, in bibendum enim pellentesque sit amet. Proin " + "nunc lacus, imperdiet nec posuere ac, interdum non lectus." + ) + + # Navigation History + assert nwGUI.viewDocument("04468803b92e1") + assert nwGUI.docViewer.theHandle == "04468803b92e1" + assert nwGUI.docViewer.docHeader.backButton.isEnabled() + assert not nwGUI.docViewer.docHeader.forwardButton.isEnabled() + + qtbot.mouseClick(nwGUI.docViewer.docHeader.backButton, Qt.LeftButton) + assert nwGUI.docViewer.theHandle == "4c4f28287af27" + assert not nwGUI.docViewer.docHeader.backButton.isEnabled() + assert nwGUI.docViewer.docHeader.forwardButton.isEnabled() + + qtbot.mouseClick(nwGUI.docViewer.docHeader.forwardButton, Qt.LeftButton) + assert nwGUI.docViewer.theHandle == "04468803b92e1" + assert nwGUI.docViewer.docHeader.backButton.isEnabled() + assert not nwGUI.docViewer.docHeader.forwardButton.isEnabled() + + # qtbot.stopForInteraction() + +# END Test testGuiMenu_ContextMenus + +@pytest.mark.gui +def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncDir, fncProj): + """Test the Insert menu. + """ + # Block message box + monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes) + + nwGUI.theProject.projTree.setSeed(42) + assert nwGUI.newProject({"projPath": fncProj}) + + assert nwGUI.treeView._getTreeItem("0e17daca5f3e1") is not None + + nwGUI.setFocus(1) + nwGUI.treeView.clearSelection() + nwGUI.treeView._getTreeItem("0e17daca5f3e1").setSelected(True) + assert nwGUI.openSelectedItem() + nwGUI.docEditor.clear() + + # Test Faulty Inserts + assert nwGUI.docEditor.insertText("hello world") + assert nwGUI.docEditor.getText() == "hello world" + nwGUI.docEditor.clear() + + assert not nwGUI.docEditor.insertText(nwDocInsert.NO_INSERT) + assert nwGUI.docEditor.isEmpty() + + assert not nwGUI.docEditor.insertText(None) + assert nwGUI.docEditor.isEmpty() + + # qtbot.stopForInteraction() + + # Check Menu Entries + nwGUI.mainMenu.aInsENDash.activate(QAction.Trigger) + assert nwGUI.docEditor.getText() == nwUnicode.U_ENDASH + nwGUI.docEditor.clear() + + nwGUI.mainMenu.aInsEMDash.activate(QAction.Trigger) + assert nwGUI.docEditor.getText() == nwUnicode.U_EMDASH + nwGUI.docEditor.clear() + + nwGUI.mainMenu.aInsEllipsis.activate(QAction.Trigger) + assert nwGUI.docEditor.getText() == nwUnicode.U_HELLIP + nwGUI.docEditor.clear() + + nwGUI.mainMenu.aInsQuoteLS.activate(QAction.Trigger) + assert nwGUI.docEditor.getText() == nwGUI.mainConf.fmtSingleQuotes[0] + nwGUI.docEditor.clear() + + nwGUI.mainMenu.aInsQuoteRS.activate(QAction.Trigger) + assert nwGUI.docEditor.getText() == nwGUI.mainConf.fmtSingleQuotes[1] + nwGUI.docEditor.clear() + + nwGUI.mainMenu.aInsQuoteLD.activate(QAction.Trigger) + assert nwGUI.docEditor.getText() == nwGUI.mainConf.fmtDoubleQuotes[0] + nwGUI.docEditor.clear() + + nwGUI.mainMenu.aInsQuoteRD.activate(QAction.Trigger) + assert nwGUI.docEditor.getText() == nwGUI.mainConf.fmtDoubleQuotes[1] + nwGUI.docEditor.clear() + + nwGUI.mainMenu.aInsMSApos.activate(QAction.Trigger) + assert nwGUI.docEditor.getText() == nwUnicode.U_MAPOSS + nwGUI.docEditor.clear() + + nwGUI.mainMenu.aInsHardBreak.activate(QAction.Trigger) + assert nwGUI.docEditor.getText() == " \n" + nwGUI.docEditor.clear() + + nwGUI.mainMenu.aInsNBSpace.activate(QAction.Trigger) + if nwGUI.mainConf.verQtValue >= 50900: + assert nwGUI.docEditor.getText() == nwUnicode.U_NBSP + else: + assert nwGUI.docEditor.getText() == " " + nwGUI.docEditor.clear() + + nwGUI.mainMenu.aInsThinSpace.activate(QAction.Trigger) + assert nwGUI.docEditor.getText() == nwUnicode.U_THNSP + nwGUI.docEditor.clear() + + nwGUI.mainMenu.aInsThinNBSpace.activate(QAction.Trigger) + if nwGUI.mainConf.verQtValue >= 50900: + assert nwGUI.docEditor.getText() == nwUnicode.U_THNBSP + else: + assert nwGUI.docEditor.getText() == " " + nwGUI.docEditor.clear() + + ## + # Insert Keywords + ## + + nwGUI.docEditor.setText("Stuff") + nwGUI.mainMenu.mInsKWItems[nwKeyWords.TAG_KEY][0].activate(QAction.Trigger) + assert nwGUI.docEditor.getText() == "Stuff\n%s: " % nwKeyWords.TAG_KEY + + nwGUI.docEditor.setText("Stuff") + nwGUI.mainMenu.mInsKWItems[nwKeyWords.POV_KEY][0].activate(QAction.Trigger) + assert nwGUI.docEditor.getText() == "Stuff\n%s: " % nwKeyWords.POV_KEY + + nwGUI.docEditor.setText("Stuff") + nwGUI.mainMenu.mInsKWItems[nwKeyWords.CHAR_KEY][0].activate(QAction.Trigger) + assert nwGUI.docEditor.getText() == "Stuff\n%s: " % nwKeyWords.CHAR_KEY + + nwGUI.docEditor.setText("Stuff") + nwGUI.mainMenu.mInsKWItems[nwKeyWords.PLOT_KEY][0].activate(QAction.Trigger) + assert nwGUI.docEditor.getText() == "Stuff\n%s: " % nwKeyWords.PLOT_KEY + + nwGUI.docEditor.setText("Stuff") + nwGUI.mainMenu.mInsKWItems[nwKeyWords.TIME_KEY][0].activate(QAction.Trigger) + assert nwGUI.docEditor.getText() == "Stuff\n%s: " % nwKeyWords.TIME_KEY + + nwGUI.docEditor.setText("Stuff") + nwGUI.mainMenu.mInsKWItems[nwKeyWords.WORLD_KEY][0].activate(QAction.Trigger) + assert nwGUI.docEditor.getText() == "Stuff\n%s: " % nwKeyWords.WORLD_KEY + + nwGUI.docEditor.setText("Stuff") + nwGUI.mainMenu.mInsKWItems[nwKeyWords.OBJECT_KEY][0].activate(QAction.Trigger) + assert nwGUI.docEditor.getText() == "Stuff\n%s: " % nwKeyWords.OBJECT_KEY + + nwGUI.docEditor.setText("Stuff") + nwGUI.mainMenu.mInsKWItems[nwKeyWords.ENTITY_KEY][0].activate(QAction.Trigger) + assert nwGUI.docEditor.getText() == "Stuff\n%s: " % nwKeyWords.ENTITY_KEY + + nwGUI.docEditor.setText("Stuff") + nwGUI.mainMenu.mInsKWItems[nwKeyWords.CUSTOM_KEY][0].activate(QAction.Trigger) + assert nwGUI.docEditor.getText() == "Stuff\n%s: " % nwKeyWords.CUSTOM_KEY + + # Faulty Keyword Inserts + assert not nwGUI.docEditor.insertKeyWord("blabla") + monkeypatch.setattr(QTextBlock, "isValid", lambda *args, **kwards: False) + assert not nwGUI.docEditor.insertKeyWord(nwKeyWords.TAG_KEY) + monkeypatch.undo() + + nwGUI.docEditor.clear() + + ## + # Insert text from file + ## + + nwGUI.closeDocument() + + # First, with no path + monkeypatch.setattr(QFileDialog, "getOpenFileName", lambda *args, **kwards: ("", "")) + assert not nwGUI.importDocument() + + # Then with a path, but an invalid one + monkeypatch.setattr(QFileDialog, "getOpenFileName", lambda *args, **kwards: (" ", "")) + assert not nwGUI.importDocument() + + # Then a valid path, but bot a file that exists + theFile = os.path.join(fncDir, "import.txt") + monkeypatch.setattr(QFileDialog, "getOpenFileName", lambda *args, **kwards: (theFile, "")) + assert not nwGUI.importDocument() + + # Create the file and try again, but with no target document open + with open(theFile, mode="w+", encoding="utf8") as outFile: + outFile.write("Foo") + assert not nwGUI.importDocument() + + # Open the document from before, and add some text to it + nwGUI.openDocument("0e17daca5f3e1") + nwGUI.docEditor.setText("Bar") + assert nwGUI.docEditor.getText() == "Bar" + + # The document isn't empty, so the message box should pop + monkeypatch.setattr(QMessageBox, "question", lambda *args, **kwargs: QMessageBox.No) + assert not nwGUI.importDocument() + assert nwGUI.docEditor.getText() == "Bar" + + # Finally, accept the replaced text, this time we use the menu entry to trigger it + monkeypatch.setattr(QMessageBox, "question", lambda *args, **kwargs: QMessageBox.Yes) + nwGUI.mainMenu.aImportFile.activate(QAction.Trigger) + assert nwGUI.docEditor.getText() == "Foo" + + ## + # Reveal file location + ## + + theMessage = "" + + def recordMsg(*args): + nonlocal theMessage + theMessage = args[3] + return None + + assert not theMessage + monkeypatch.setattr(QMessageBox, "information", recordMsg) + nwGUI.mainMenu.aFileDetails.activate(QAction.Trigger) + + theBits = theMessage.split("
") + assert len(theBits) == 3 + assert theBits[0] == "File details for the currently open file" + assert theBits[1] == "Handle: 0e17daca5f3e1" + assert theBits[2] == "Location: %s" % os.path.join(fncProj, "content", "0e17daca5f3e1.nwd") + + # qtbot.stopForInteraction() + +# END Test testGuiMenu_Insert diff --git a/tests/test_gui_outline.py b/tests/test_gui_outline.py new file mode 100644 index 00000000..cf93c111 --- /dev/null +++ b/tests/test_gui_outline.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- +"""novelWriter Main GUI Outline Class Tester +""" + +import pytest + +from PyQt5.QtCore import Qt, QPoint +from PyQt5.QtWidgets import QAction, QTreeWidgetItem, QMessageBox + +from nw.constants import nwOutline + +keyDelay = 2 +typeDelay = 1 +stepDelay = 20 + +@pytest.mark.gui +def testGuiOutline_Main(qtbot, monkeypatch, nwGUI, nwLipsum): + """Test the outline view. + """ + # Block message box + monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes) + + assert nwGUI.openProject(nwLipsum) + nwGUI.mainConf.lastPath = nwLipsum + + nwGUI.rebuildIndex() + nwGUI.tabWidget.setCurrentIndex(nwGUI.idxTabProj) + + assert nwGUI.projView.topLevelItemCount() > 0 + + # Context Menu + nwGUI.projView._headerRightClick(QPoint(1, 1)) + nwGUI.projView.headerMenu.actionMap[nwOutline.CCOUNT].activate(QAction.Trigger) + nwGUI.projView.headerMenu.close() + qtbot.mouseClick(nwGUI.projView, Qt.LeftButton) + + nwGUI.projView._loadHeaderState() + assert not nwGUI.projView.colHidden[nwOutline.CCOUNT] + + # First Item + nwGUI.rebuildOutline() + selItem = nwGUI.projView.topLevelItem(0) + assert isinstance(selItem, QTreeWidgetItem) + + nwGUI.projView.setCurrentItem(selItem) + assert nwGUI.projMeta.titleLabel.text() == "Title" + assert nwGUI.projMeta.titleValue.text() == "Lorem Ipsum" + assert nwGUI.projMeta.fileValue.text() == "Lorem Ipsum" + assert nwGUI.projMeta.itemValue.text() == "Finished" + + assert nwGUI.projMeta.cCValue.text() == "230" + assert nwGUI.projMeta.wCValue.text() == "40" + assert nwGUI.projMeta.pCValue.text() == "3" + + # Scene One + actItem = nwGUI.projView.topLevelItem(1) + chpItem = actItem.child(0) + selItem = chpItem.child(0) + + nwGUI.projView.setCurrentItem(selItem) + assert nwGUI.projMeta.titleLabel.text() == "Scene" + assert nwGUI.projMeta.titleValue.text() == "Scene One" + assert nwGUI.projMeta.fileValue.text() == "Scene One" + assert nwGUI.projMeta.itemValue.text() == "Finished" + + # Click POV Link + assert nwGUI.projMeta.povKeyValue.text() == "Bod" + nwGUI.projMeta._tagClicked("#pov=Bod") + assert nwGUI.docViewer.theHandle == "4c4f28287af27" + + # qtbot.stopForInteraction() + +# END Test testGuiOutline_Main diff --git a/tests/test_gui_projtree.py b/tests/test_gui_projtree.py new file mode 100644 index 00000000..94726348 --- /dev/null +++ b/tests/test_gui_projtree.py @@ -0,0 +1,138 @@ +# -*- coding: utf-8 -*- +"""novelWriter Main GUI Project Tree Class Tester +""" + +import pytest +import os + +from PyQt5.QtCore import QItemSelectionModel +from PyQt5.QtWidgets import QAction, QMessageBox + +from nw.constants import nwItemType, nwItemClass + +keyDelay = 2 +typeDelay = 1 +stepDelay = 20 + +@pytest.mark.gui +def testGuiProjTree_Main(qtbot, monkeypatch, nwGUI, nwMinimal): + """Test the project tree. + """ + # Block message box + monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes) + + nwGUI.theProject.projTree.setSeed(42) + assert nwGUI.openProject(nwMinimal) + nwTree = nwGUI.treeView + + # No location selected for new item + assert not nwTree.newTreeItem(nwItemType.FILE, None) + assert not nwTree.newTreeItem(nwItemType.FOLDER, None) + + # Select a location + chItem = nwTree._getTreeItem("a6d311a93600a") + nwTree.setCurrentItem(chItem, QItemSelectionModel.Current) + chItem.setExpanded(True) + + # Create new item with no class set + assert nwTree.newTreeItem(nwItemType.FILE, None) + assert nwTree.newTreeItem(nwItemType.FOLDER, None) + + # Add roots + assert not nwTree.newTreeItem(nwItemType.ROOT, None) # Defaults to NOVEL + assert not nwTree.newTreeItem(nwItemType.ROOT, nwItemClass.WORLD) # Duplicate + assert nwTree.newTreeItem(nwItemType.ROOT, nwItemClass.CUSTOM) # Valid + + # Check that we have the correct tree order + assert nwTree.getTreeFromHandle("a6d311a93600a") == [ + "a6d311a93600a", "f5ab3e30151e1", "8c659a11cd429", "73475cb40a568", "44cb730c42048" + ] + + # Move second item up twice (should give same result) + nwTree.setSelectedHandle("8c659a11cd429") + nwGUI.mainMenu.aMoveUp.activate(QAction.Trigger) + assert nwTree.getTreeFromHandle("a6d311a93600a") == [ + "a6d311a93600a", "8c659a11cd429", "f5ab3e30151e1", "73475cb40a568", "44cb730c42048" + ] + nwGUI.mainMenu.aMoveUp.activate(QAction.Trigger) + assert nwTree.getTreeFromHandle("a6d311a93600a") == [ + "a6d311a93600a", "8c659a11cd429", "f5ab3e30151e1", "73475cb40a568", "44cb730c42048" + ] + + # Move it back down four times (last to should be the same) + nwGUI.mainMenu.aMoveDown.activate(QAction.Trigger) + assert nwTree.getTreeFromHandle("a6d311a93600a") == [ + "a6d311a93600a", "f5ab3e30151e1", "8c659a11cd429", "73475cb40a568", "44cb730c42048" + ] + nwGUI.mainMenu.aMoveDown.activate(QAction.Trigger) + assert nwTree.getTreeFromHandle("a6d311a93600a") == [ + "a6d311a93600a", "f5ab3e30151e1", "73475cb40a568", "8c659a11cd429", "44cb730c42048" + ] + nwGUI.mainMenu.aMoveDown.activate(QAction.Trigger) + assert nwTree.getTreeFromHandle("a6d311a93600a") == [ + "a6d311a93600a", "f5ab3e30151e1", "73475cb40a568", "44cb730c42048", "8c659a11cd429" + ] + nwGUI.mainMenu.aMoveDown.activate(QAction.Trigger) + assert nwTree.getTreeFromHandle("a6d311a93600a") == [ + "a6d311a93600a", "f5ab3e30151e1", "73475cb40a568", "44cb730c42048", "8c659a11cd429" + ] + + # Move a root item (top level items are different) twice + nwTree.flushTreeOrder() + assert nwGUI.theProject.projTree._treeOrder.index("9d5247ab588e0") == 9 + nwTree.setSelectedHandle("9d5247ab588e0") + + nwGUI.mainMenu.aMoveDown.activate(QAction.Trigger) + nwTree.flushTreeOrder() + assert nwGUI.theProject.projTree._treeOrder.index("9d5247ab588e0") == 10 + + nwGUI.mainMenu.aMoveDown.activate(QAction.Trigger) + nwTree.flushTreeOrder() + assert nwGUI.theProject.projTree._treeOrder.index("9d5247ab588e0") == 10 + + # Add some content to the new file + nwGUI.openDocument("73475cb40a568") + nwGUI.docEditor.setText("# Hello World\n") + nwGUI.saveDocument() + assert os.path.isfile(os.path.join(nwMinimal, "content", "73475cb40a568.nwd")) + + # Delete the items we added earlier + nwTree.clearSelection() + assert not nwTree.emptyTrash() # No folder yet + assert not nwTree.deleteItem(None) + assert not nwTree.deleteItem("1111111111111") + assert nwTree.deleteItem("73475cb40a568") # New File + assert nwTree.deleteItem("44cb730c42048") # New Folder + assert nwTree.deleteItem("71ee45a3c0db9") # Custom Root + assert "73475cb40a568" in nwGUI.theProject.projTree._treeOrder + assert "44cb730c42048" not in nwGUI.theProject.projTree._treeOrder + assert "71ee45a3c0db9" not in nwGUI.theProject.projTree._treeOrder + + # The file is in trash, empty it + assert os.path.isfile(os.path.join(nwMinimal, "content", "73475cb40a568.nwd")) + assert nwTree.emptyTrash() + assert not nwTree.emptyTrash() # Already empty + assert not os.path.isfile(os.path.join(nwMinimal, "content", "73475cb40a568.nwd")) + assert "73475cb40a568" not in nwGUI.theProject.projTree._treeOrder + + # Close the project + nwGUI.closeProject() + + # Add an orphaned file + orphFile = os.path.join(nwMinimal, "content", "1234567890abc.nwd") + with open(orphFile, mode="w+", encoding="utf8") as outFile: + outFile.write("# Hello World\n") + + # Open the project again + nwGUI.openProject(nwMinimal) + + # Check that the orphaned file was found and added to the tree + assert nwTree.orphRoot is not None + nwTree.flushTreeOrder() + assert "1234567890abc" not in nwGUI.theProject.projTree._treeOrder + orItem = nwTree._getTreeItem("1234567890abc") + assert orItem.text(nwTree.C_NAME) == "Orphaned File 1" + + # qtbot.stopForInteraction() + +# END Test testGuiProjTree_Main diff --git a/tests/test_gui_theme.py b/tests/test_gui_theme.py new file mode 100644 index 00000000..4214af59 --- /dev/null +++ b/tests/test_gui_theme.py @@ -0,0 +1,170 @@ +# -*- coding: utf-8 -*- +"""novelWriter Main GUI Class Tester +""" + +import nw +import pytest + +from PyQt5.QtGui import QColor, QPixmap, QIcon +from PyQt5.QtWidgets import QStyle, QMessageBox + +keyDelay = 2 +typeDelay = 1 +stepDelay = 20 + +@pytest.mark.gui +def testGuiTheme_Main(qtbot, monkeypatch, nwMinimal, tmpDir): + """Test the theme and icon classes. + """ + # Block message box + monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes) + + nwGUI = nw.main(["--testmode", "--config=%s" % nwMinimal, "--data=%s" % tmpDir, nwMinimal]) + qtbot.addWidget(nwGUI) + nwGUI.show() + qtbot.waitForWindowShown(nwGUI) + qtbot.wait(500) + + # Change Settings + assert nw.CONFIG.confPath == nwMinimal + nw.CONFIG.guiTheme = "default_dark" + nw.CONFIG.guiSyntax = "tomorrow_night_eighties" + nw.CONFIG.guiIcons = "typicons_colour_dark" + nw.CONFIG.guiDark = True + nw.CONFIG.guiFont = "Cantarell" + nw.CONFIG.guiFontSize = 11 + nw.CONFIG.confChanged = True + assert nw.CONFIG.saveConfig() + + nwGUI.closeMain() + nwGUI.close() + del nwGUI + + # Re-open + assert nw.CONFIG.confPath == nwMinimal + nwGUI = nw.main(["--testmode", "--config=%s" % nwMinimal, "--data=%s" % tmpDir, nwMinimal]) + assert nwGUI.mainConf.confPath == nwMinimal + qtbot.addWidget(nwGUI) + nwGUI.show() + qtbot.waitForWindowShown(nwGUI) + qtbot.wait(500) + + assert nw.CONFIG.guiTheme == "default_dark" + assert nw.CONFIG.guiSyntax == "tomorrow_night_eighties" + assert nw.CONFIG.guiIcons == "typicons_colour_dark" + assert nw.CONFIG.guiDark is True + assert nw.CONFIG.guiFont == "Cantarell" + assert nw.CONFIG.guiFontSize == 11 + + # Check GUI Colours + thePalette = nwGUI.palette() + assert thePalette.window().color() == QColor(54, 54, 54) + assert thePalette.windowText().color() == QColor(174, 174, 174) + assert thePalette.base().color() == QColor(62, 62, 62) + assert thePalette.alternateBase().color() == QColor(67, 67, 67) + assert thePalette.text().color() == QColor(174, 174, 174) + assert thePalette.toolTipBase().color() == QColor(255, 255, 192) + assert thePalette.toolTipText().color() == QColor(21, 21, 13) + assert thePalette.button().color() == QColor(62, 62, 62) + assert thePalette.buttonText().color() == QColor(174, 174, 174) + assert thePalette.brightText().color() == QColor(174, 174, 174) + assert thePalette.highlight().color() == QColor(44, 152, 247) + assert thePalette.highlightedText().color() == QColor(255, 255, 255) + assert thePalette.link().color() == QColor(44, 152, 247) + assert thePalette.linkVisited().color() == QColor(44, 152, 247) + + assert nwGUI.theTheme.treeWCount == [197, 200, 198] + assert nwGUI.theTheme.statNone == [150, 152, 150] + assert nwGUI.theTheme.statSaved == [39, 135, 78] + assert nwGUI.theTheme.statUnsaved == [138, 32, 32] + + # Check Syntax Colours + assert nwGUI.theTheme.colBack == [45, 45, 45] + assert nwGUI.theTheme.colText == [204, 204, 204] + assert nwGUI.theTheme.colLink == [102, 153, 204] + assert nwGUI.theTheme.colHead == [102, 153, 204] + assert nwGUI.theTheme.colHeadH == [102, 153, 204] + assert nwGUI.theTheme.colEmph == [249, 145, 57] + assert nwGUI.theTheme.colDialN == [242, 119, 122] + assert nwGUI.theTheme.colDialD == [153, 204, 153] + assert nwGUI.theTheme.colDialS == [255, 204, 102] + assert nwGUI.theTheme.colHidden == [153, 153, 153] + assert nwGUI.theTheme.colKey == [242, 119, 122] + assert nwGUI.theTheme.colVal == [204, 153, 204] + assert nwGUI.theTheme.colSpell == [242, 119, 122] + assert nwGUI.theTheme.colTagErr == [153, 204, 153] + assert nwGUI.theTheme.colRepTag == [102, 204, 204] + assert nwGUI.theTheme.colMod == [249, 145, 57] + + # Test Icon class + theIcons = nwGUI.theTheme.theIcons + nw.CONFIG.guiIcons = "invalid" + assert not theIcons.updateTheme() + nw.CONFIG.guiIcons = "typicons_colour_dark" + assert theIcons.updateTheme() + + # Ask for a non-existent key + anImg = theIcons.loadDecoration("nonsense", 20, 20) + assert isinstance(anImg, QPixmap) + assert anImg.isNull() + + # Add a non-existent file and request it + theIcons.DECO_MAP["nonsense"] = "nofile.jpg" + anImg = theIcons.loadDecoration("nonsense", 20, 20) + assert isinstance(anImg, QPixmap) + assert anImg.isNull() + + # Get a real image, with different size parameters + anImg = theIcons.loadDecoration("wiz-back", 20, None) + assert isinstance(anImg, QPixmap) + assert not anImg.isNull() + assert anImg.width() == 20 + assert anImg.height() >= 56 + + anImg = theIcons.loadDecoration("wiz-back", None, 70) + assert isinstance(anImg, QPixmap) + assert not anImg.isNull() + assert anImg.height() == 70 + assert anImg.width() >= 24 + + anImg = theIcons.loadDecoration("wiz-back", 30, 70) + assert isinstance(anImg, QPixmap) + assert not anImg.isNull() + assert anImg.height() == 70 + assert anImg.width() == 30 + + anImg = theIcons.loadDecoration("wiz-back", None, None) + assert isinstance(anImg, QPixmap) + assert not anImg.isNull() + assert anImg.height() >= 1500 + assert anImg.width() >= 500 + + # Load icons + anIcon = theIcons.getIcon("nonsense") + assert isinstance(anIcon, QIcon) + assert anIcon.isNull() + + anIcon = theIcons.getIcon("novelwriter") + assert isinstance(anIcon, QIcon) + assert not anIcon.isNull() + + # Add dummy icons and test alternative load paths + theIcons.ICON_MAP["testicon1"] = (QStyle.SP_DriveHDIcon, None) + anIcon = theIcons.getIcon("testicon1") + assert isinstance(anIcon, QIcon) + assert not anIcon.isNull() + + theIcons.ICON_MAP["testicon2"] = (None, "folder") + anIcon = theIcons.getIcon("testicon2") + assert isinstance(anIcon, QIcon) + + theIcons.ICON_MAP["testicon3"] = (None, None) + anIcon = theIcons.getIcon("testicon3") + assert isinstance(anIcon, QIcon) + assert anIcon.isNull() + + # qtbot.stopForInteraction() + nwGUI.closeMain() + nwGUI.close() + +# END Test testGuiTheme_Main