# -*- 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()