Test Coverage of the Editor (#787)
* Drop the _qDocument class variables * Split up the current doceditor test file * Add tests for editor init, load and save * Add tests for editor meta functions and actions * Add tests for editor insert functions * Add tests for editor text manipulation and block formatting * Fix code style * Add text coverage for alignment and indent
This commit is contained in:
committed by
GitHub
parent
1d16cc2a03
commit
36be7b0b33
+349
-331
@@ -111,9 +111,9 @@ class GuiDocEditor(QTextEdit):
|
||||
self._typPadChar = " "
|
||||
|
||||
# Core Elements and Signals
|
||||
self._qDocument = self.document()
|
||||
self._qDocument.contentsChange.connect(self._docChange)
|
||||
self._qDocument.documentLayout().documentSizeChanged.connect(self._docSizeChanged)
|
||||
qDoc = self.document()
|
||||
qDoc.contentsChange.connect(self._docChange)
|
||||
qDoc.documentLayout().documentSizeChanged.connect(self._docSizeChanged)
|
||||
|
||||
# Document Title
|
||||
self.docHeader = GuiDocEditHeader(self)
|
||||
@@ -121,7 +121,7 @@ class GuiDocEditor(QTextEdit):
|
||||
self.docSearch = GuiDocEditSearch(self)
|
||||
|
||||
# Syntax
|
||||
self.hLight = GuiDocHighlighter(self._qDocument, self.theParent)
|
||||
self.hLight = GuiDocHighlighter(qDoc, self.theParent)
|
||||
|
||||
# Context Menu
|
||||
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||
@@ -221,9 +221,10 @@ class GuiDocEditor(QTextEdit):
|
||||
|
||||
# Set font
|
||||
theFont = QFont()
|
||||
qDoc = self.document()
|
||||
if self.mainConf.textFont is None:
|
||||
# If none is defined, set the default back to config
|
||||
self.mainConf.textFont = self._qDocument.defaultFont().family()
|
||||
self.mainConf.textFont = qDoc.defaultFont().family()
|
||||
|
||||
theFont.setFamily(self.mainConf.textFont)
|
||||
theFont.setPointSize(self.mainConf.textSize)
|
||||
@@ -246,7 +247,7 @@ class GuiDocEditor(QTextEdit):
|
||||
|
||||
# Set default text margins
|
||||
cM = self.mainConf.getTextMargin()
|
||||
self._qDocument.setDocumentMargin(0)
|
||||
qDoc.setDocumentMargin(0)
|
||||
self.setViewportMargins(cM, cM, cM, cM)
|
||||
|
||||
# Also set the document text options for the document text flow
|
||||
@@ -259,7 +260,7 @@ class GuiDocEditor(QTextEdit):
|
||||
if self.mainConf.showLineEndings:
|
||||
theOpt.setFlags(theOpt.flags() | QTextOption.ShowLineAndParagraphSeparators)
|
||||
|
||||
self._qDocument.setDefaultTextOption(theOpt)
|
||||
qDoc.setDefaultTextOption(theOpt)
|
||||
|
||||
# Scroll bars
|
||||
if self.mainConf.hideVScroll:
|
||||
@@ -275,7 +276,7 @@ class GuiDocEditor(QTextEdit):
|
||||
# Refresh the tab stops
|
||||
if self.mainConf.verQtValue >= 51000:
|
||||
self.setTabStopDistance(self.mainConf.getTabWidth())
|
||||
else:
|
||||
else: # pragma: no cover
|
||||
self.setTabStopWidth(self.mainConf.getTabWidth())
|
||||
|
||||
# Initialise the syntax highlighter
|
||||
@@ -370,7 +371,6 @@ class GuiDocEditor(QTextEdit):
|
||||
self.setCursorLine(tLine)
|
||||
|
||||
self.docFooter.updateLineCount()
|
||||
self.lengthLast = self._qDocument.characterCount()
|
||||
self._docHeaders = self.theIndex.getHandleHeaders(self._docHandle)
|
||||
|
||||
qApp.processEvents()
|
||||
@@ -378,7 +378,7 @@ class GuiDocEditor(QTextEdit):
|
||||
qApp.restoreOverrideCursor()
|
||||
|
||||
# This is a hack to fix invisble cursor on an empty document
|
||||
if self._qDocument.characterCount() <= 1:
|
||||
if self.document().characterCount() <= 1:
|
||||
self.setPlainText("\n")
|
||||
self.setPlainText("")
|
||||
self.setCursorPosition(0)
|
||||
@@ -391,7 +391,7 @@ class GuiDocEditor(QTextEdit):
|
||||
|
||||
return True
|
||||
|
||||
def updateTagHighLighting(self, forceBigDoc=False):
|
||||
def updateTagHighLighting(self):
|
||||
"""Rerun the syntax highlighter on all meta data lines.
|
||||
"""
|
||||
self.hLight.rehighlightByType(GuiDocHighlighter.BLOCK_META)
|
||||
@@ -400,7 +400,7 @@ class GuiDocEditor(QTextEdit):
|
||||
def redrawText(self):
|
||||
"""Redraw the text by marking the document content as "dirty".
|
||||
"""
|
||||
self._qDocument.markContentsDirty(0, self._qDocument.characterCount())
|
||||
self.document().markContentsDirty(0, self.document().characterCount())
|
||||
self.updateDocMargins()
|
||||
return
|
||||
|
||||
@@ -532,9 +532,9 @@ class GuiDocEditor(QTextEdit):
|
||||
|
||||
tmpDocChanged = self._docChanged
|
||||
if self.mainConf.scrollPastEnd:
|
||||
docFrame = self._qDocument.rootFrame().frameFormat()
|
||||
docFrame = self.document().rootFrame().frameFormat()
|
||||
docFrame.setBottomMargin(max(0, 0.9*(wH - uM - lM - 4*tB)))
|
||||
self._qDocument.rootFrame().setFrameFormat(docFrame)
|
||||
self.document().rootFrame().setFrameFormat(docFrame)
|
||||
|
||||
# This is needed as the setFrameFormat function itself will
|
||||
# trigger the contetsChanged signal which sets _docChanged, so we
|
||||
@@ -576,7 +576,7 @@ class GuiDocEditor(QTextEdit):
|
||||
def isEmpty(self):
|
||||
"""Wrapper function to check if the current document is empty.
|
||||
"""
|
||||
return self._qDocument.isEmpty()
|
||||
return self.document().isEmpty()
|
||||
|
||||
def currentDictionary(self):
|
||||
"""Return the current dictionary object.
|
||||
@@ -596,7 +596,7 @@ class GuiDocEditor(QTextEdit):
|
||||
See: https://doc.qt.io/qt-5/qtextdocument.html#toPlainText
|
||||
"""
|
||||
if self.mainConf.verQtValue >= 50900:
|
||||
theText = self._qDocument.toRawText()
|
||||
theText = self.document().toRawText()
|
||||
theText = theText.replace(nwUnicode.U_LSEP, "\n") # Line separators
|
||||
theText = theText.replace(nwUnicode.U_PSEP, "\n") # Paragraph separators
|
||||
else:
|
||||
@@ -628,7 +628,7 @@ class GuiDocEditor(QTextEdit):
|
||||
if not isinstance(thePosition, int):
|
||||
return False
|
||||
|
||||
nChars = self._qDocument.characterCount()
|
||||
nChars = self.document().characterCount()
|
||||
if nChars > 1:
|
||||
theCursor = self.textCursor()
|
||||
theCursor.setPosition(min(max(thePosition, 0), nChars-1))
|
||||
@@ -652,7 +652,7 @@ class GuiDocEditor(QTextEdit):
|
||||
return False
|
||||
|
||||
if theLine >= 0:
|
||||
theBlock = self._qDocument.findBlockByLineNumber(theLine)
|
||||
theBlock = self.document().findBlockByLineNumber(theLine)
|
||||
if theBlock:
|
||||
self.setCursorPosition(theBlock.position())
|
||||
self.docFooter.updateLineCount()
|
||||
@@ -710,7 +710,8 @@ class GuiDocEditor(QTextEdit):
|
||||
def spellCheckDocument(self):
|
||||
"""Rerun the highlighter to update spell checking status of the
|
||||
currently loaded text. The fastest way to do this, at least as
|
||||
of Qt 5.13, is to clear the text and put it back.
|
||||
of Qt 5.13, is to clear the text and put it back. This clears
|
||||
the undo stack, so we only do it for big documents.
|
||||
"""
|
||||
logger.verbose("Running spell checker")
|
||||
if self._spellCheck:
|
||||
@@ -739,11 +740,16 @@ class GuiDocEditor(QTextEdit):
|
||||
passed to it without having to consider the internal logic of
|
||||
this class when calling these actions from other classes.
|
||||
"""
|
||||
logger.verbose("Requesting action: '%s'", theAction.name)
|
||||
if self._docHandle is None:
|
||||
logger.error("No document open")
|
||||
return False
|
||||
|
||||
if not isinstance(theAction, nwDocAction):
|
||||
logger.error("Not a document action")
|
||||
return False
|
||||
|
||||
logger.verbose("Requesting action: %s" % theAction.name)
|
||||
|
||||
self._allowAutoReplace(False)
|
||||
if theAction == nwDocAction.UNDO:
|
||||
self.undo()
|
||||
@@ -1012,7 +1018,7 @@ class GuiDocEditor(QTextEdit):
|
||||
self._lastEdit = time()
|
||||
self._lastFind = None
|
||||
|
||||
if self._qDocument.characterCount() > nwConst.MAX_DOCSIZE:
|
||||
if self.document().characterCount() > nwConst.MAX_DOCSIZE:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
"The document has grown too big and you cannot add more text to it. "
|
||||
"The maximum size of a single novelWriter document is {0} MB."
|
||||
@@ -1029,7 +1035,7 @@ class GuiDocEditor(QTextEdit):
|
||||
self.wcTimer.start()
|
||||
|
||||
if self._doReplace and chrAdd == 1:
|
||||
self._docAutoReplace(self._qDocument.findBlock(thePos))
|
||||
self._docAutoReplace(self.document().findBlock(thePos))
|
||||
|
||||
return
|
||||
|
||||
@@ -1198,7 +1204,7 @@ class GuiDocEditor(QTextEdit):
|
||||
# Must not be emitted if docHandle is None!
|
||||
self.docCountsChanged.emit(self._docHandle, cCount, wCount, pCount)
|
||||
|
||||
self._checkDocSize(self._qDocument.characterCount())
|
||||
self._checkDocSize(self.document().characterCount())
|
||||
self.docFooter.updateCounts()
|
||||
|
||||
return
|
||||
@@ -1211,7 +1217,7 @@ class GuiDocEditor(QTextEdit):
|
||||
moved to has been drawn before the move is made.
|
||||
"""
|
||||
if self._queuePos is not None:
|
||||
thePos = self._qDocument.documentLayout().hitTest(
|
||||
thePos = self.document().documentLayout().hitTest(
|
||||
QPointF(theSize.width(), theSize.height()), Qt.FuzzyHit
|
||||
)
|
||||
if self._queuePos <= thePos:
|
||||
@@ -1362,226 +1368,74 @@ class GuiDocEditor(QTextEdit):
|
||||
return
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
# Internal Functions : Text Manipulation
|
||||
##
|
||||
|
||||
def _followTag(self, theCursor=None, loadTag=True):
|
||||
"""Activated by Ctrl+Enter. Checks that we're in a block
|
||||
starting with '@'. We then find the word under the cursor and
|
||||
check that it is after the ':'. If all this is fine, we have a
|
||||
tag and can tell the document viewer to try and find and load
|
||||
the file where the tag is defined.
|
||||
def _toggleFormat(self, fLen, fChar):
|
||||
"""Toggle the formatting of a specific type for a piece of text.
|
||||
If more than one block is selected, the formatting is applied to
|
||||
the first block.
|
||||
"""
|
||||
if theCursor is None:
|
||||
theCursor = self.textCursor()
|
||||
|
||||
theBlock = theCursor.block()
|
||||
theText = theBlock.text()
|
||||
|
||||
if len(theText) == 0:
|
||||
theCursor = self._autoSelect()
|
||||
if not theCursor.hasSelection():
|
||||
logger.warning("No selection made, nothing to do")
|
||||
return False
|
||||
|
||||
if theText.startswith("@"):
|
||||
posS = theCursor.selectionStart()
|
||||
posE = theCursor.selectionEnd()
|
||||
|
||||
theCursor.select(QTextCursor.WordUnderCursor)
|
||||
theWord = theCursor.selectedText()
|
||||
cPos = theText.find(":")
|
||||
wPos = theCursor.selectionStart() - theBlock.position()
|
||||
if wPos <= cPos:
|
||||
return False
|
||||
blockS = self.document().findBlock(posS)
|
||||
blockE = self.document().findBlock(posE)
|
||||
|
||||
if loadTag:
|
||||
logger.verbose("Attempting to follow tag '%s'", theWord)
|
||||
self.theParent.docViewer.loadFromTag(theWord)
|
||||
if blockS != blockE:
|
||||
posE = blockS.position() + blockS.length() - 1
|
||||
theCursor.clearSelection()
|
||||
theCursor.setPosition(posS, QTextCursor.MoveAnchor)
|
||||
theCursor.setPosition(posE, QTextCursor.KeepAnchor)
|
||||
self.setTextCursor(theCursor)
|
||||
|
||||
numB = 0
|
||||
for n in range(fLen):
|
||||
if self.document().characterAt(posS-n-1) == fChar:
|
||||
numB += 1
|
||||
else:
|
||||
logger.verbose("Potential tag '%s'", theWord)
|
||||
break
|
||||
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _openSpellContext(self):
|
||||
"""Opens the spell check context menu at the current point of
|
||||
the cursor.
|
||||
"""
|
||||
self._openContextMenu(self.cursorRect().center())
|
||||
return
|
||||
|
||||
def _docAutoReplace(self, theBlock):
|
||||
"""Auto-replace text elements based on main configuration.
|
||||
"""
|
||||
if not theBlock.isValid():
|
||||
return
|
||||
|
||||
theText = theBlock.text()
|
||||
theCursor = self.textCursor()
|
||||
thePos = theCursor.positionInBlock()
|
||||
theLen = len(theText)
|
||||
|
||||
if theLen < 1 or thePos-1 > theLen:
|
||||
return
|
||||
|
||||
theOne = theText[thePos-1:thePos]
|
||||
theTwo = theText[thePos-2:thePos]
|
||||
theThree = theText[thePos-3:thePos]
|
||||
|
||||
if not theOne: # Makes Neo sad
|
||||
return
|
||||
|
||||
nDelete = 0
|
||||
tInsert = theOne
|
||||
|
||||
if self.mainConf.doReplaceDQuote and theTwo == ' "':
|
||||
nDelete = 1
|
||||
tInsert = self._typDQOpen
|
||||
|
||||
elif self.mainConf.doReplaceDQuote and theOne == '"':
|
||||
nDelete = 1
|
||||
if thePos == 1:
|
||||
tInsert = self._typDQOpen
|
||||
numA = 0
|
||||
for n in range(fLen):
|
||||
if self.document().characterAt(posE+n) == fChar:
|
||||
numA += 1
|
||||
else:
|
||||
tInsert = self._typDQClose
|
||||
|
||||
elif self.mainConf.doReplaceSQuote and theTwo == " '":
|
||||
nDelete = 1
|
||||
tInsert = self._typSQOpen
|
||||
|
||||
elif self.mainConf.doReplaceSQuote and theOne == "'":
|
||||
nDelete = 1
|
||||
if thePos == 1:
|
||||
tInsert = self._typSQOpen
|
||||
else:
|
||||
tInsert = self._typSQClose
|
||||
|
||||
elif self.mainConf.doReplaceDash and theThree == "---":
|
||||
nDelete = 3
|
||||
tInsert = nwUnicode.U_EMDASH
|
||||
|
||||
elif self.mainConf.doReplaceDash and theTwo == "--":
|
||||
nDelete = 2
|
||||
tInsert = nwUnicode.U_ENDASH
|
||||
|
||||
elif self.mainConf.doReplaceDash and theTwo == nwUnicode.U_ENDASH + "-":
|
||||
nDelete = 2
|
||||
tInsert = nwUnicode.U_EMDASH
|
||||
|
||||
elif self.mainConf.doReplaceDots and theThree == "...":
|
||||
nDelete = 3
|
||||
tInsert = nwUnicode.U_HELLIP
|
||||
|
||||
tCheck = tInsert
|
||||
if tCheck in self.mainConf.fmtPadBefore:
|
||||
nDelete = max(nDelete, 1)
|
||||
tInsert = self._typPadChar + tInsert
|
||||
|
||||
if tCheck in self.mainConf.fmtPadAfter:
|
||||
nDelete = max(nDelete, 1)
|
||||
tInsert = tInsert + self._typPadChar
|
||||
|
||||
if nDelete > 0:
|
||||
theCursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, nDelete)
|
||||
theCursor.insertText(tInsert)
|
||||
|
||||
return
|
||||
|
||||
def _updateHeaders(self, checkPos=False, checkLevel=False):
|
||||
"""Update the headers record and return True if anything
|
||||
changed, if a check flag was provided.
|
||||
"""
|
||||
if self._docHandle is None:
|
||||
return False
|
||||
|
||||
newHeaders = self.theIndex.getHandleHeaders(self._docHandle)
|
||||
if checkPos:
|
||||
newPos = [x[0] for x in newHeaders]
|
||||
oldPos = [x[0] for x in self._docHeaders]
|
||||
if checkLevel:
|
||||
newLev = [x[1] for x in newHeaders]
|
||||
oldLev = [x[1] for x in self._docHeaders]
|
||||
|
||||
self._docHeaders = newHeaders
|
||||
|
||||
if checkPos:
|
||||
return newPos != oldPos
|
||||
if checkLevel:
|
||||
return newLev != oldLev
|
||||
|
||||
return False
|
||||
|
||||
def _replaceQuotes(self, sQuote, oQuote, cQuote):
|
||||
"""Replace all straight quotes in the selected text.
|
||||
"""
|
||||
theCursor = self.textCursor()
|
||||
if theCursor.hasSelection():
|
||||
posS = theCursor.selectionStart()
|
||||
posE = theCursor.selectionEnd()
|
||||
closeCheck = (
|
||||
" ", "\n", nwUnicode.U_LSEP, nwUnicode.U_PSEP
|
||||
)
|
||||
|
||||
self._allowAutoReplace(False)
|
||||
for posC in range(posS, posE+1):
|
||||
theCursor.setPosition(posC)
|
||||
theCursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, 2)
|
||||
selText = theCursor.selectedText()
|
||||
|
||||
nS = len(selText)
|
||||
if nS == 2:
|
||||
pC = selText[0]
|
||||
cC = selText[1]
|
||||
elif nS == 1:
|
||||
pC = " "
|
||||
cC = selText[0]
|
||||
else:
|
||||
continue
|
||||
|
||||
if cC != sQuote:
|
||||
continue
|
||||
|
||||
theCursor.clearSelection()
|
||||
theCursor.setPosition(posC)
|
||||
if pC in closeCheck:
|
||||
theCursor.beginEditBlock()
|
||||
theCursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, 1)
|
||||
theCursor.insertText(oQuote)
|
||||
theCursor.endEditBlock()
|
||||
else:
|
||||
theCursor.beginEditBlock()
|
||||
theCursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, 1)
|
||||
theCursor.insertText(cQuote)
|
||||
theCursor.endEditBlock()
|
||||
|
||||
self._allowAutoReplace(True)
|
||||
break
|
||||
|
||||
if fLen == min(numA, numB):
|
||||
self._clearSurrounding(theCursor, fLen)
|
||||
else:
|
||||
self.theParent.makeAlert(self.tr(
|
||||
"Please select some text before calling replace quotes."
|
||||
), nwAlert.ERROR)
|
||||
self._wrapSelection(fChar*fLen)
|
||||
|
||||
return
|
||||
return True
|
||||
|
||||
def _checkDocSize(self, theSize):
|
||||
"""Check if document size crosses the big document limit set in
|
||||
config. If so, we will set the big document flag to True.
|
||||
def _clearSurrounding(self, theCursor, nChars):
|
||||
"""Clears n characters before and after the cursor.
|
||||
"""
|
||||
bigLim = self.mainConf.bigDocLimit*1000
|
||||
newState = theSize > bigLim
|
||||
if not theCursor.hasSelection():
|
||||
logger.warning("No selection made, nothing to do")
|
||||
return False
|
||||
|
||||
if newState != self._bigDoc:
|
||||
if newState:
|
||||
logger.info(
|
||||
f"The document size is {theSize:n} > {bigLim:n}, "
|
||||
f"big doc mode has been enabled"
|
||||
)
|
||||
else:
|
||||
logger.info(
|
||||
f"The document size is {theSize:n} <= {bigLim:n}, "
|
||||
f"big doc mode has been disabled"
|
||||
)
|
||||
posS = theCursor.selectionStart()
|
||||
posE = theCursor.selectionEnd()
|
||||
theCursor.clearSelection()
|
||||
theCursor.beginEditBlock()
|
||||
theCursor.setPosition(posS)
|
||||
for i in range(nChars):
|
||||
theCursor.deletePreviousChar()
|
||||
theCursor.setPosition(posE)
|
||||
for i in range(nChars):
|
||||
theCursor.deletePreviousChar()
|
||||
theCursor.endEditBlock()
|
||||
theCursor.clearSelection()
|
||||
|
||||
self._bigDoc = newState
|
||||
|
||||
return
|
||||
return True
|
||||
|
||||
def _wrapSelection(self, tBefore, tAfter=None):
|
||||
"""Wraps the selected text in whatever is in tBefore and tAfter.
|
||||
@@ -1593,120 +1447,84 @@ class GuiDocEditor(QTextEdit):
|
||||
tAfter = tBefore
|
||||
|
||||
theCursor = self._autoSelect()
|
||||
if theCursor.hasSelection():
|
||||
posS = theCursor.selectionStart()
|
||||
posE = theCursor.selectionEnd()
|
||||
|
||||
blockS = self._qDocument.findBlock(posS)
|
||||
blockE = self._qDocument.findBlock(posE)
|
||||
if blockS != blockE:
|
||||
posE = blockS.position() + blockS.length() - 1
|
||||
|
||||
theCursor.clearSelection()
|
||||
theCursor.beginEditBlock()
|
||||
theCursor.setPosition(posE)
|
||||
theCursor.insertText(tAfter)
|
||||
theCursor.setPosition(posS)
|
||||
theCursor.insertText(tBefore)
|
||||
theCursor.endEditBlock()
|
||||
|
||||
theCursor.setPosition(posE + len(tBefore), QTextCursor.MoveAnchor)
|
||||
theCursor.setPosition(posS + len(tBefore), QTextCursor.KeepAnchor)
|
||||
self.setTextCursor(theCursor)
|
||||
|
||||
else:
|
||||
if not theCursor.hasSelection():
|
||||
logger.warning("No selection made, nothing to do")
|
||||
return
|
||||
return False
|
||||
|
||||
def _clearSurrounding(self, theCursor, nChars):
|
||||
"""Clears n characters before and after the cursor.
|
||||
"""
|
||||
if theCursor.hasSelection():
|
||||
posS = theCursor.selectionStart()
|
||||
posE = theCursor.selectionEnd()
|
||||
theCursor.clearSelection()
|
||||
theCursor.beginEditBlock()
|
||||
theCursor.setPosition(posS)
|
||||
for i in range(nChars):
|
||||
theCursor.deletePreviousChar()
|
||||
theCursor.setPosition(posE)
|
||||
for i in range(nChars):
|
||||
theCursor.deletePreviousChar()
|
||||
theCursor.endEditBlock()
|
||||
theCursor.clearSelection()
|
||||
else:
|
||||
logger.warning("No selection made, nothing to do")
|
||||
return
|
||||
posS = theCursor.selectionStart()
|
||||
posE = theCursor.selectionEnd()
|
||||
|
||||
def _autoSelect(self):
|
||||
"""Returns a cursor which may or may not have a selection based
|
||||
on user settings and document action.
|
||||
qDoc = self.document()
|
||||
blockS = qDoc.findBlock(posS)
|
||||
blockE = qDoc.findBlock(posE)
|
||||
if blockS != blockE:
|
||||
posE = blockS.position() + blockS.length() - 1
|
||||
|
||||
theCursor.clearSelection()
|
||||
theCursor.beginEditBlock()
|
||||
theCursor.setPosition(posE)
|
||||
theCursor.insertText(tAfter)
|
||||
theCursor.setPosition(posS)
|
||||
theCursor.insertText(tBefore)
|
||||
theCursor.endEditBlock()
|
||||
|
||||
theCursor.setPosition(posE + len(tBefore), QTextCursor.MoveAnchor)
|
||||
theCursor.setPosition(posS + len(tBefore), QTextCursor.KeepAnchor)
|
||||
self.setTextCursor(theCursor)
|
||||
|
||||
return True
|
||||
|
||||
def _replaceQuotes(self, sQuote, oQuote, cQuote):
|
||||
"""Replace all straight quotes in the selected text.
|
||||
"""
|
||||
theCursor = self.textCursor()
|
||||
if self.mainConf.autoSelect and not theCursor.hasSelection():
|
||||
theCursor.select(QTextCursor.WordUnderCursor)
|
||||
posS = theCursor.selectionStart()
|
||||
posE = theCursor.selectionEnd()
|
||||
if not theCursor.hasSelection():
|
||||
self.theParent.makeAlert(self.tr(
|
||||
"Please select some text before calling replace quotes."
|
||||
), nwAlert.ERROR)
|
||||
return False
|
||||
|
||||
# Underscore counts as a part of the word, so check that the
|
||||
# selection isn't wrapped in italics markers.
|
||||
reSelect = False
|
||||
if self._qDocument.characterAt(posS) == "_":
|
||||
posS += 1
|
||||
reSelect = True
|
||||
if self._qDocument.characterAt(posE) == "_":
|
||||
posE -= 1
|
||||
reSelect = True
|
||||
if reSelect:
|
||||
theCursor.clearSelection()
|
||||
theCursor.setPosition(posS, QTextCursor.MoveAnchor)
|
||||
theCursor.setPosition(posE-1, QTextCursor.KeepAnchor)
|
||||
posS = theCursor.selectionStart()
|
||||
posE = theCursor.selectionEnd()
|
||||
closeCheck = (
|
||||
" ", "\n", nwUnicode.U_LSEP, nwUnicode.U_PSEP
|
||||
)
|
||||
|
||||
self.setTextCursor(theCursor)
|
||||
self._allowAutoReplace(False)
|
||||
for posC in range(posS, posE+1):
|
||||
theCursor.setPosition(posC)
|
||||
theCursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, 2)
|
||||
selText = theCursor.selectedText()
|
||||
|
||||
return theCursor
|
||||
nS = len(selText)
|
||||
if nS == 2:
|
||||
pC = selText[0]
|
||||
cC = selText[1]
|
||||
elif nS == 1:
|
||||
pC = " "
|
||||
cC = selText[0]
|
||||
else: # pragma: no cover
|
||||
continue
|
||||
|
||||
def _toggleFormat(self, fLen, fChar):
|
||||
"""Toggle the formatting of a specific type for a piece of text.
|
||||
If more than one block is selected, the formatting is applied to
|
||||
the first block.
|
||||
"""
|
||||
theCursor = self._autoSelect()
|
||||
if theCursor.hasSelection():
|
||||
posS = theCursor.selectionStart()
|
||||
posE = theCursor.selectionEnd()
|
||||
if cC != sQuote:
|
||||
continue
|
||||
|
||||
blockS = self._qDocument.findBlock(posS)
|
||||
blockE = self._qDocument.findBlock(posE)
|
||||
|
||||
if blockS != blockE:
|
||||
posE = blockS.position() + blockS.length() - 1
|
||||
theCursor.clearSelection()
|
||||
theCursor.setPosition(posS, QTextCursor.MoveAnchor)
|
||||
theCursor.setPosition(posE, QTextCursor.KeepAnchor)
|
||||
self.setTextCursor(theCursor)
|
||||
|
||||
numB = 0
|
||||
for n in range(fLen):
|
||||
if self._qDocument.characterAt(posS-n-1) == fChar:
|
||||
numB += 1
|
||||
else:
|
||||
break
|
||||
|
||||
numA = 0
|
||||
for n in range(fLen):
|
||||
if self._qDocument.characterAt(posE+n) == fChar:
|
||||
numA += 1
|
||||
else:
|
||||
break
|
||||
|
||||
cLevel = min(numB, numA)
|
||||
if cLevel == fLen:
|
||||
self._clearSurrounding(theCursor, fLen)
|
||||
theCursor.clearSelection()
|
||||
theCursor.setPosition(posC)
|
||||
if pC in closeCheck:
|
||||
theCursor.beginEditBlock()
|
||||
theCursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, 1)
|
||||
theCursor.insertText(oQuote)
|
||||
theCursor.endEditBlock()
|
||||
else:
|
||||
self._wrapSelection(fChar*fLen)
|
||||
theCursor.beginEditBlock()
|
||||
theCursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, 1)
|
||||
theCursor.insertText(cQuote)
|
||||
theCursor.endEditBlock()
|
||||
|
||||
return
|
||||
self._allowAutoReplace(True)
|
||||
|
||||
return True
|
||||
|
||||
def _formatBlock(self, docAction):
|
||||
"""Changes the block format of the block under the cursor.
|
||||
@@ -1874,6 +1692,206 @@ class GuiDocEditor(QTextEdit):
|
||||
|
||||
return True
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _followTag(self, theCursor=None, loadTag=True):
|
||||
"""Activated by Ctrl+Enter. Checks that we're in a block
|
||||
starting with '@'. We then find the word under the cursor and
|
||||
check that it is after the ':'. If all this is fine, we have a
|
||||
tag and can tell the document viewer to try and find and load
|
||||
the file where the tag is defined.
|
||||
"""
|
||||
if theCursor is None:
|
||||
theCursor = self.textCursor()
|
||||
|
||||
theBlock = theCursor.block()
|
||||
theText = theBlock.text()
|
||||
|
||||
if len(theText) == 0:
|
||||
return False
|
||||
|
||||
if theText.startswith("@"):
|
||||
|
||||
theCursor.select(QTextCursor.WordUnderCursor)
|
||||
theWord = theCursor.selectedText()
|
||||
cPos = theText.find(":")
|
||||
wPos = theCursor.selectionStart() - theBlock.position()
|
||||
if wPos <= cPos:
|
||||
return False
|
||||
|
||||
if loadTag:
|
||||
logger.verbose("Attempting to follow tag '%s'" % theWord)
|
||||
self.theParent.docViewer.loadFromTag(theWord)
|
||||
else:
|
||||
logger.verbose("Potential tag '%s'" % theWord)
|
||||
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _openSpellContext(self):
|
||||
"""Opens the spell check context menu at the current point of
|
||||
the cursor.
|
||||
"""
|
||||
self._openContextMenu(self.cursorRect().center())
|
||||
return
|
||||
|
||||
def _docAutoReplace(self, theBlock):
|
||||
"""Auto-replace text elements based on main configuration.
|
||||
"""
|
||||
if not theBlock.isValid():
|
||||
return
|
||||
|
||||
theText = theBlock.text()
|
||||
theCursor = self.textCursor()
|
||||
thePos = theCursor.positionInBlock()
|
||||
theLen = len(theText)
|
||||
|
||||
if theLen < 1 or thePos-1 > theLen:
|
||||
return
|
||||
|
||||
theOne = theText[thePos-1:thePos]
|
||||
theTwo = theText[thePos-2:thePos]
|
||||
theThree = theText[thePos-3:thePos]
|
||||
|
||||
if not theOne:
|
||||
# Sorry, Neo and Zathras
|
||||
return
|
||||
|
||||
nDelete = 0
|
||||
tInsert = theOne
|
||||
|
||||
if self.mainConf.doReplaceDQuote and theTwo == ' "':
|
||||
nDelete = 1
|
||||
tInsert = self._typDQOpen
|
||||
|
||||
elif self.mainConf.doReplaceDQuote and theOne == '"':
|
||||
nDelete = 1
|
||||
if thePos == 1:
|
||||
tInsert = self._typDQOpen
|
||||
else:
|
||||
tInsert = self._typDQClose
|
||||
|
||||
elif self.mainConf.doReplaceSQuote and theTwo == " '":
|
||||
nDelete = 1
|
||||
tInsert = self._typSQOpen
|
||||
|
||||
elif self.mainConf.doReplaceSQuote and theOne == "'":
|
||||
nDelete = 1
|
||||
if thePos == 1:
|
||||
tInsert = self._typSQOpen
|
||||
else:
|
||||
tInsert = self._typSQClose
|
||||
|
||||
elif self.mainConf.doReplaceDash and theThree == "---":
|
||||
nDelete = 3
|
||||
tInsert = nwUnicode.U_EMDASH
|
||||
|
||||
elif self.mainConf.doReplaceDash and theTwo == "--":
|
||||
nDelete = 2
|
||||
tInsert = nwUnicode.U_ENDASH
|
||||
|
||||
elif self.mainConf.doReplaceDash and theTwo == nwUnicode.U_ENDASH + "-":
|
||||
nDelete = 2
|
||||
tInsert = nwUnicode.U_EMDASH
|
||||
|
||||
elif self.mainConf.doReplaceDots and theThree == "...":
|
||||
nDelete = 3
|
||||
tInsert = nwUnicode.U_HELLIP
|
||||
|
||||
tCheck = tInsert
|
||||
if tCheck in self.mainConf.fmtPadBefore:
|
||||
nDelete = max(nDelete, 1)
|
||||
tInsert = self._typPadChar + tInsert
|
||||
|
||||
if tCheck in self.mainConf.fmtPadAfter:
|
||||
nDelete = max(nDelete, 1)
|
||||
tInsert = tInsert + self._typPadChar
|
||||
|
||||
if nDelete > 0:
|
||||
theCursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, nDelete)
|
||||
theCursor.insertText(tInsert)
|
||||
|
||||
return
|
||||
|
||||
def _updateHeaders(self, checkPos=False, checkLevel=False):
|
||||
"""Update the headers record and return True if anything
|
||||
changed, if a check flag was provided.
|
||||
"""
|
||||
if self._docHandle is None:
|
||||
return False
|
||||
|
||||
newHeaders = self.theIndex.getHandleHeaders(self._docHandle)
|
||||
if checkPos:
|
||||
newPos = [x[0] for x in newHeaders]
|
||||
oldPos = [x[0] for x in self._docHeaders]
|
||||
if checkLevel:
|
||||
newLev = [x[1] for x in newHeaders]
|
||||
oldLev = [x[1] for x in self._docHeaders]
|
||||
|
||||
self._docHeaders = newHeaders
|
||||
|
||||
if checkPos:
|
||||
return newPos != oldPos
|
||||
if checkLevel:
|
||||
return newLev != oldLev
|
||||
|
||||
return False
|
||||
|
||||
def _checkDocSize(self, theSize):
|
||||
"""Check if document size crosses the big document limit set in
|
||||
config. If so, we will set the big document flag to True.
|
||||
"""
|
||||
bigLim = round(self.mainConf.bigDocLimit*1000)
|
||||
newState = theSize > bigLim
|
||||
|
||||
if newState != self._bigDoc:
|
||||
if newState:
|
||||
logger.info(
|
||||
f"The document size is {theSize:n} > {bigLim:n}, "
|
||||
f"big doc mode has been enabled"
|
||||
)
|
||||
else:
|
||||
logger.info(
|
||||
f"The document size is {theSize:n} <= {bigLim:n}, "
|
||||
f"big doc mode has been disabled"
|
||||
)
|
||||
|
||||
self._bigDoc = newState
|
||||
|
||||
return
|
||||
|
||||
def _autoSelect(self):
|
||||
"""Returns a cursor which may or may not have a selection based
|
||||
on user settings and document action.
|
||||
"""
|
||||
theCursor = self.textCursor()
|
||||
if self.mainConf.autoSelect and not theCursor.hasSelection():
|
||||
theCursor.select(QTextCursor.WordUnderCursor)
|
||||
posS = theCursor.selectionStart()
|
||||
posE = theCursor.selectionEnd()
|
||||
|
||||
# Underscore counts as a part of the word, so check that the
|
||||
# selection isn't wrapped in italics markers.
|
||||
reSelect = False
|
||||
qDoc = self.document()
|
||||
if qDoc.characterAt(posS) == "_":
|
||||
posS += 1
|
||||
reSelect = True
|
||||
if qDoc.characterAt(posE) == "_":
|
||||
posE -= 1
|
||||
reSelect = True
|
||||
if reSelect:
|
||||
theCursor.clearSelection()
|
||||
theCursor.setPosition(posS, QTextCursor.MoveAnchor)
|
||||
theCursor.setPosition(posE-1, QTextCursor.KeepAnchor)
|
||||
|
||||
self.setTextCursor(theCursor)
|
||||
|
||||
return theCursor
|
||||
|
||||
def _makeSelection(self, selMode):
|
||||
"""Wrapper function to select text based on a selection mode.
|
||||
"""
|
||||
@@ -2753,7 +2771,7 @@ class GuiDocEditFooter(QWidget):
|
||||
else:
|
||||
theCursor = self.docEditor.textCursor()
|
||||
iLine = theCursor.blockNumber() + 1
|
||||
iDist = 100*iLine/self.docEditor._qDocument.blockCount()
|
||||
iDist = 100*iLine/self.docEditor.document().blockCount()
|
||||
|
||||
self.linesText.setText(
|
||||
self.tr("Line: {0} ({1})").format(f"{iLine:n}", f"{iDist:.0f} %")
|
||||
@@ -2775,7 +2793,7 @@ class GuiDocEditFooter(QWidget):
|
||||
self.tr("Words: {0} ({1})").format(f"{wCount:n}", f"{wDiff:+n}")
|
||||
)
|
||||
|
||||
byteSize = self.docEditor._qDocument.characterCount()
|
||||
byteSize = self.docEditor.document().characterCount()
|
||||
self.wordsText.setToolTip(
|
||||
self.tr("Document size is {0} bytes").format(f"{byteSize:n}")
|
||||
)
|
||||
|
||||
@@ -261,11 +261,11 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
||||
"""Loop through all blocks and rehighlight those of a given
|
||||
content type.
|
||||
"""
|
||||
qDocument = self.document()
|
||||
nBlocks = qDocument.blockCount()
|
||||
qDoc = self.document()
|
||||
nBlocks = qDoc.blockCount()
|
||||
bfTime = time()
|
||||
for i in range(nBlocks):
|
||||
theBlock = qDocument.findBlockByNumber(i)
|
||||
theBlock = qDoc.findBlockByNumber(i)
|
||||
if theBlock.userState() & theType > 0:
|
||||
self.rehighlightBlock(theBlock)
|
||||
afTime = time()
|
||||
|
||||
+6
-7
@@ -61,7 +61,6 @@ class GuiDocViewer(QTextBrowser):
|
||||
|
||||
# Internal Variables
|
||||
self._docHandle = None
|
||||
self._qDocument = self.document()
|
||||
|
||||
# Settings
|
||||
self.setMinimumWidth(self.mainConf.pxInt(300))
|
||||
@@ -106,7 +105,7 @@ class GuiDocViewer(QTextBrowser):
|
||||
theFont = QFont()
|
||||
if self.mainConf.textFont is None:
|
||||
# If none is defined, set the default back to config
|
||||
self.mainConf.textFont = self._qDocument.defaultFont().family()
|
||||
self.mainConf.textFont = self.document().defaultFont().family()
|
||||
theFont.setFamily(self.mainConf.textFont)
|
||||
theFont.setPointSize(self.mainConf.textSize)
|
||||
self.setFont(theFont)
|
||||
@@ -127,11 +126,11 @@ class GuiDocViewer(QTextBrowser):
|
||||
self.docFooter.matchColours()
|
||||
|
||||
# Set default text margins
|
||||
self._qDocument.setDocumentMargin(0)
|
||||
self.document().setDocumentMargin(0)
|
||||
theOpt = QTextOption()
|
||||
if self.mainConf.doJustify:
|
||||
theOpt.setAlignment(Qt.AlignJustify)
|
||||
self._qDocument.setDefaultTextOption(theOpt)
|
||||
self.document().setDefaultTextOption(theOpt)
|
||||
|
||||
# Scroll bars
|
||||
if self.mainConf.hideVScroll:
|
||||
@@ -234,7 +233,7 @@ class GuiDocViewer(QTextBrowser):
|
||||
def redrawText(self):
|
||||
"""Redraw the text by marking the document content as "dirty".
|
||||
"""
|
||||
self._qDocument.markContentsDirty(0, self._qDocument.characterCount())
|
||||
self.document().markContentsDirty(0, self.document().characterCount())
|
||||
self.updateDocMargins()
|
||||
return
|
||||
|
||||
@@ -373,7 +372,7 @@ class GuiDocViewer(QTextBrowser):
|
||||
if not isinstance(theLine, int):
|
||||
return False
|
||||
if theLine >= 0:
|
||||
theBlock = self._qDocument.findBlockByLineNumber(theLine)
|
||||
theBlock = self.document().findBlockByLineNumber(theLine)
|
||||
if theBlock:
|
||||
self.setCursorPosition(theBlock.position())
|
||||
logger.verbose("Cursor moved to line %d", theLine)
|
||||
@@ -563,7 +562,7 @@ class GuiDocViewer(QTextBrowser):
|
||||
mColG=self.theTheme.colMod[1],
|
||||
mColB=self.theTheme.colMod[2],
|
||||
)
|
||||
self._qDocument.setDefaultStyleSheet(styleSheet)
|
||||
self.document().setDefaultStyleSheet(styleSheet)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
+9
-10
@@ -980,7 +980,7 @@ class GuiBuildNovel(QDialog):
|
||||
thePrinter.setFontEmbeddingEnabled(True)
|
||||
thePrinter.setColorMode(QPrinter.Color)
|
||||
thePrinter.setOutputFileName(savePath)
|
||||
self.docView.qDocument.print(thePrinter)
|
||||
self.docView.document().print(thePrinter)
|
||||
wSuccess = True
|
||||
|
||||
except Exception as e:
|
||||
@@ -1018,7 +1018,7 @@ class GuiBuildNovel(QDialog):
|
||||
"""
|
||||
qApp.setOverrideCursor(QCursor(Qt.WaitCursor))
|
||||
thePrinter.setOrientation(QPrinter.Portrait)
|
||||
self.docView.qDocument.print(thePrinter)
|
||||
self.docView.document().print(thePrinter)
|
||||
qApp.restoreOverrideCursor()
|
||||
return
|
||||
|
||||
@@ -1195,8 +1195,7 @@ class GuiBuildNovelDocView(QTextBrowser):
|
||||
self.setMinimumWidth(40*self.theParent.theTheme.textNWidth)
|
||||
self.setOpenExternalLinks(False)
|
||||
|
||||
self.qDocument = self.document()
|
||||
self.qDocument.setDocumentMargin(self.mainConf.getTextMargin())
|
||||
self.document().setDocumentMargin(self.mainConf.getTextMargin())
|
||||
self.setPlaceholderText(self.tr(
|
||||
"This area will show the content of the document to be "
|
||||
"exported or printed. Press the \"Build Preview\" button "
|
||||
@@ -1206,7 +1205,7 @@ class GuiBuildNovelDocView(QTextBrowser):
|
||||
theFont = QFont()
|
||||
if self.mainConf.textFont is None:
|
||||
# If none is defined, set the default back to config
|
||||
self.mainConf.textFont = self.qDocument.defaultFont().family()
|
||||
self.mainConf.textFont = self.document().defaultFont().family()
|
||||
theFont.setFamily(self.mainConf.textFont)
|
||||
theFont.setPointSize(self.mainConf.textSize)
|
||||
self.setFont(theFont)
|
||||
@@ -1256,12 +1255,12 @@ class GuiBuildNovelDocView(QTextBrowser):
|
||||
def setJustify(self, doJustify):
|
||||
"""Set the justify text option.
|
||||
"""
|
||||
theOpt = self.qDocument.defaultTextOption()
|
||||
theOpt = self.document().defaultTextOption()
|
||||
if doJustify:
|
||||
theOpt.setAlignment(Qt.AlignJustify)
|
||||
else:
|
||||
theOpt.setAlignment(Qt.AlignAbsolute)
|
||||
self.qDocument.setDefaultTextOption(theOpt)
|
||||
self.document().setDefaultTextOption(theOpt)
|
||||
return
|
||||
|
||||
def setTextFont(self, textFont, textSize):
|
||||
@@ -1298,7 +1297,7 @@ class GuiBuildNovelDocView(QTextBrowser):
|
||||
|
||||
# Since we change the content while it may still be rendering, we mark
|
||||
# the document dirty again to make sure it's re-rendered properly.
|
||||
self.qDocument.markContentsDirty(0, self.qDocument.characterCount())
|
||||
self.document().markContentsDirty(0, self.document().characterCount())
|
||||
qApp.restoreOverrideCursor()
|
||||
|
||||
return
|
||||
@@ -1312,14 +1311,14 @@ class GuiBuildNovelDocView(QTextBrowser):
|
||||
theStyles.append("a {color: rgb(66, 113, 174);}")
|
||||
theStyles.append(".tags {color: rgb(245, 135, 31); font-weight: bold;}")
|
||||
|
||||
self.qDocument.setDefaultStyleSheet("\n".join(theStyles))
|
||||
self.document().setDefaultStyleSheet("\n".join(theStyles))
|
||||
|
||||
return
|
||||
|
||||
def clearStyleSheet(self):
|
||||
"""Clears the document stylesheet.
|
||||
"""
|
||||
self.qDocument.setDefaultStyleSheet("")
|
||||
self.document().setDefaultStyleSheet("")
|
||||
return
|
||||
|
||||
##
|
||||
|
||||
@@ -240,3 +240,62 @@ def nwOldProj(tmpDir):
|
||||
shutil.rmtree(dstDir)
|
||||
|
||||
return
|
||||
|
||||
|
||||
##
|
||||
# Useful Fixtures
|
||||
##
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def ipsumText():
|
||||
"""Return five paragraphs of Lorem Ipsum text.
|
||||
"""
|
||||
thatIpsum = [(
|
||||
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc maximus justo non dictum co"
|
||||
"mmodo. Curabitur lacinia tempor orci vel luctus. Phasellus porta metus eu massa luctus, e"
|
||||
"get euismod risus rhoncus. Vestibulum sed arcu nisi. Maecenas pretium facilisis velit, ve"
|
||||
"l semper lacus aliquam sit amet. Vestibulum vulputate neque ligula, rhoncus blandit turpi"
|
||||
"s consequat id. Mauris sagittis vehicula imperdiet. Duis sed nunc pretium, ornare purus v"
|
||||
"el, sodales augue. Maecenas a suscipit risus. Quisque volutpat justo eleifend est ullamco"
|
||||
"rper fermentum. Donec ullamcorper et tortor a laoreet. Nam id risus nisi. Vivamus non imp"
|
||||
"erdiet erat, sit amet imperdiet felis. Mauris vitae neque et est aliquam scelerisque non "
|
||||
"non ipsum."
|
||||
), (
|
||||
"Nullam laoreet lorem nec malesuada vehicula. Vivamus tempus sodales lectus sed viverra. A"
|
||||
"enean lacinia sollicitudin quam, quis tempus eros suscipit id. Duis sed rutrum nisi, ut p"
|
||||
"ulvinar magna. Nam et cursus tortor. Phasellus ac odio tellus. Nullam in iaculis ipsum. V"
|
||||
"ivamus ante sem, ultricies sed varius quis, tristique nec tellus. Nullam eu urna vitae la"
|
||||
"cus hendrerit gravida. Quisque pulvinar erat ex, id efficitur velit sodales vitae. Proin "
|
||||
"vestibulum, sapien eget mattis euismod, tortor quam viverra risus, at congue mauris torto"
|
||||
"r eu nunc. Mauris pellentesque elit leo, quis eleifend sem placerat a. Vivamus iaculis du"
|
||||
"i eget tellus volutpat, ac varius nisi facilisis."
|
||||
), (
|
||||
"Nullam a nisl magna. Praesent commodo nec diam aliquet vestibulum. In sapien velit, sodal"
|
||||
"es feugiat porta ut, rhoncus a elit. Quisque egestas nisi eu eros laoreet, quis facilisis"
|
||||
" est pretium. Nullam bibendum sed tellus nec lobortis. Duis elit massa, volutpat a lacini"
|
||||
"a a, ullamcorper in dui. Suspendisse ac laoreet dui. Curabitur elementum, tortor elementu"
|
||||
"m ultricies laoreet, nunc massa vulputate augue, vitae tincidunt nunc enim eget nisl."
|
||||
), (
|
||||
"Pellentesque nibh urna, volutpat et feugiat porta, rutrum sed lectus. Aliquam eget risus "
|
||||
"id orci tincidunt condimentum et sit amet purus. Curabitur tincidunt odio vel ante feugia"
|
||||
"t feugiat. Proin nunc lorem, molestie a sapien et, varius elementum nunc. Donec non ferme"
|
||||
"ntum nisl. In et massa placerat, faucibus felis eu, congue nisi. Proin sed tortor non lor"
|
||||
"em mattis cursus. Vestibulum magna neque, bibendum vel nibh et, tincidunt rhoncus nisi. D"
|
||||
"uis pulvinar mi a quam rutrum maximus. Nunc sollicitudin, urna in cursus facilisis, augue"
|
||||
" neque imperdiet metus, ac finibus lorem ante id nulla. Sed maximus eleifend justo id feu"
|
||||
"giat. Cras eget diam vel est blandit tempor nec a leo. Mauris risus est, fringilla in ali"
|
||||
"quam a, sagittis vel enim. Nullam sodales id erat placerat lobortis."
|
||||
), (
|
||||
"Integer ac gravida quam. Quisque eleifend nisl nec pretium tincidunt. Quisque sollicitudi"
|
||||
"n nisi in hendrerit scelerisque. Sed ornare nisl lacus, sit amet consectetur lectus egest"
|
||||
"as et. Vivamus nec arcu lorem. Donec rhoncus, purus a porta accumsan, nunc lectus iaculis"
|
||||
" libero, et fringilla tellus augue et velit. Integer varius felis scelerisque, vulputate "
|
||||
"tellus eu, laoreet justo. Suspendisse sit amet sem vehicula, auctor odio sed, aliquet eni"
|
||||
"m. In ac tortor sed tortor fringilla elementum. Nulla non odio at magna vulputate sceleri"
|
||||
"sque. Nam elementum diam eu rutrum scelerisque. Sed fermentum, felis quis vulputate ferme"
|
||||
"ntum, libero metus sollicitudin est, in faucibus purus nulla non dolor. Ut vitae felis po"
|
||||
"rta, feugiat nunc et, bibendum neque. Nullam nec lorem nec metus ullamcorper malesuada ut"
|
||||
" a nisl. Etiam eget tristique dui. Nulla sed mi finibus, venenatis tellus non, maximus en"
|
||||
"im."
|
||||
)]
|
||||
return thatIpsum
|
||||
|
||||
+1112
-306
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,354 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
novelWriter – Main GUI Editor Class Tester
|
||||
==========================================
|
||||
|
||||
This file is a part of novelWriter
|
||||
Copyright 2018–2021, Veronica Berglyd Olsen
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import os
|
||||
|
||||
from shutil import copyfile
|
||||
from tools import cmpFiles
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import QMessageBox, QDialog
|
||||
|
||||
from nw.dialogs.itemeditor import GuiItemEditor
|
||||
from nw.gui.doceditor import GuiDocEditor
|
||||
from nw.gui.projtree import GuiProjectTree
|
||||
from nw.enum import nwItemType, nwWidget
|
||||
|
||||
keyDelay = 2
|
||||
typeDelay = 1
|
||||
stepDelay = 20
|
||||
|
||||
|
||||
@pytest.mark.gui
|
||||
def testGuiMain_Editing(qtbot, monkeypatch, nwGUI, fncProj, refDir, outDir):
|
||||
"""Test the document editor.
|
||||
"""
|
||||
# Block message box
|
||||
monkeypatch.setattr(QMessageBox, "question", lambda *args: QMessageBox.Yes)
|
||||
monkeypatch.setattr(QMessageBox, "information", lambda *args: QMessageBox.Yes)
|
||||
monkeypatch.setattr(GuiItemEditor, "exec_", lambda *args: None)
|
||||
monkeypatch.setattr(GuiItemEditor, "result", lambda *args: QDialog.Accepted)
|
||||
monkeypatch.setattr(GuiProjectTree, "hasFocus", lambda *args: True)
|
||||
monkeypatch.setattr(GuiDocEditor, "hasFocus", lambda *args: True)
|
||||
|
||||
# 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.switchFocus(nwWidget.TREE)
|
||||
nwGUI.treeView.clearSelection()
|
||||
nwGUI.treeView._getTreeItem("71ee45a3c0db9").setSelected(True)
|
||||
nwGUI.treeView.newTreeItem(nwItemType.FILE, None)
|
||||
assert nwGUI.openSelectedItem()
|
||||
|
||||
# Type something into the document
|
||||
nwGUI.switchFocus(nwWidget.EDITOR)
|
||||
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.switchFocus(nwWidget.TREE)
|
||||
nwGUI.treeView.clearSelection()
|
||||
nwGUI.treeView._getTreeItem("44cb730c42048").setSelected(True)
|
||||
nwGUI.treeView.newTreeItem(nwItemType.FILE, None)
|
||||
assert nwGUI.openSelectedItem()
|
||||
|
||||
# Type something into the document
|
||||
nwGUI.switchFocus(nwWidget.EDITOR)
|
||||
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.switchFocus(nwWidget.TREE)
|
||||
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.switchFocus(nwWidget.EDITOR)
|
||||
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.switchFocus(nwWidget.TREE)
|
||||
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.switchFocus(nwWidget.EDITOR)
|
||||
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 nonsense 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 nonsense text. "
|
||||
"It is in fact 1 very very NONSENSICAL nonsense 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.switchFocus(nwWidget.VIEWER)
|
||||
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 testGuiMain_Editing
|
||||
Reference in New Issue
Block a user