Resolve HTML render bug (#951)

* Fix bug breaking HTML rendering with angle brackets in other blocks than normal text
* Restore cursor if document viewer fails to load
* Improve test coverage
This commit is contained in:
Veronica Berglyd Olsen
2022-01-04 20:52:36 +01:00
committed by GitHub
parent 59e10fbf0a
commit 9be2519406
4 changed files with 67 additions and 31 deletions
+23 -17
View File
@@ -154,25 +154,31 @@ class ToHtml(Tokenizer):
parStyle = None parStyle = None
tmpResult = [] tmpResult = []
for tType, tLine, tDirty, tFormat, tStyle in self._theTokens: for tType, tLine, tText, tFormat, tStyle in self._theTokens:
# Replace < and > and recompute formatting positions # Replace < and > with HTML entities
cText = [] if tFormat:
i = 0 # If we have formatting, we must recompute the locations
for c in tDirty: cText = []
if c == "<": i = 0
cText.append("&lt;") for c in tText:
tFormat = [[a + 3 if a > i else a, b, c] for a, b, c in tFormat] if c == "<":
i += 4 cText.append("&lt;")
elif c == ">": tFormat = [[a + 3 if a > i else a, b, c] for a, b, c in tFormat]
cText.append("&gt;") i += 4
tFormat = [[a + 3 if a > i else a, b, c] for a, b, c in tFormat] elif c == ">":
i += 4 cText.append("&gt;")
else: tFormat = [[a + 3 if a > i else a, b, c] for a, b, c in tFormat]
cText.append(c) i += 4
i += 1 else:
cText.append(c)
i += 1
tText = "".join(cText) tText = "".join(cText)
else:
# If we don't have formatting, we can do a plain replace
tText = tText.replace("<", "&lt;").replace(">", "&gt;")
# Styles # Styles
aStyle = [] aStyle = []
+1
View File
@@ -184,6 +184,7 @@ class GuiDocViewer(QTextBrowser):
logger.error("Failed to generate preview for document with handle '%s'", tHandle) logger.error("Failed to generate preview for document with handle '%s'", tHandle)
logException() logException()
self.setText(self.tr("An error occurred while generating the preview.")) self.setText(self.tr("An error occurred while generating the preview."))
qApp.restoreOverrideCursor()
return False return False
# Refresh the tab stops # Refresh the tab stops
+21
View File
@@ -417,6 +417,27 @@ def testCoreToHtml_SpecialCases(mockGUI):
"<p>Test &gt; text <em>&lt;<strong>bold</strong>&gt;</em> and more.</p>\n" "<p>Test &gt; text <em>&lt;<strong>bold</strong>&gt;</em> and more.</p>\n"
) )
# Test for bug #950
# =================
# See: https://github.com/vkbo/novelWriter/issues/950
theHtml.setComments(True)
theHtml._theText = "% Test > text _<**bold**>_ and more.\n"
theHtml.tokenizeText()
theHtml.doConvert()
assert theHtml.theResult == (
"<p class='comment'>"
"<strong>Comment:</strong> Test &gt; text _&lt;**bold**&gt;_ and more."
"</p>\n"
)
theHtml._theText = "## Heading <1>\n"
theHtml.tokenizeText()
theHtml.doConvert()
assert theHtml.theResult == (
"<h1 style='page-break-before: always;'>Heading &lt;1&gt;</h1>\n"
)
# END Test testCoreToHtml_SpecialCases # END Test testCoreToHtml_SpecialCases
+22 -14
View File
@@ -24,8 +24,10 @@ import pytest
from PyQt5.QtCore import Qt, QUrl from PyQt5.QtCore import Qt, QUrl
from PyQt5.QtGui import QTextCursor from PyQt5.QtGui import QTextCursor
from PyQt5.QtWidgets import qApp, QAction, QMessageBox from PyQt5.QtWidgets import qApp, QAction, QMessageBox
from mock import causeException
from novelwriter.enum import nwDocAction from novelwriter.enum import nwDocAction
from novelwriter.core import ToHtml
keyDelay = 2 keyDelay = 2
typeDelay = 1 typeDelay = 1
@@ -52,7 +54,7 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
assert nwGUI.theIndex._refIndex != {} assert nwGUI.theIndex._refIndex != {}
# Select a document in the project tree # Select a document in the project tree
assert nwGUI.treeView.setSelectedHandle("88243afbe5ed8") nwGUI.treeView.setSelectedHandle("88243afbe5ed8")
# Middle-click the selected item # Middle-click the selected item
theItem = nwGUI.treeView._getTreeItem("88243afbe5ed8") theItem = nwGUI.treeView._getTreeItem("88243afbe5ed8")
@@ -67,14 +69,14 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
assert nwGUI.docViewer.toPlainText() == origText assert nwGUI.docViewer.toPlainText() == origText
# Cursor line # Cursor line
assert not nwGUI.docViewer.setCursorLine("not a number") assert nwGUI.docViewer.setCursorLine("not a number") is False
assert nwGUI.docViewer.setCursorLine(3) assert nwGUI.docViewer.setCursorLine(3) is True
theCursor = nwGUI.docViewer.textCursor() theCursor = nwGUI.docViewer.textCursor()
assert theCursor.position() == 40 assert theCursor.position() == 40
# Cursor position # Cursor position
assert not nwGUI.docViewer.setCursorPosition("not a number") assert nwGUI.docViewer.setCursorPosition("not a number") is False
assert nwGUI.docViewer.setCursorPosition(100) assert nwGUI.docViewer.setCursorPosition(100) is True
# Select word # Select word
nwGUI.docViewer._makeSelection(QTextCursor.WordUnderCursor) nwGUI.docViewer._makeSelection(QTextCursor.WordUnderCursor)
@@ -83,17 +85,17 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
qClip.clear() qClip.clear()
# Cut # Cut
assert nwGUI.docViewer.docAction(nwDocAction.CUT) assert nwGUI.docViewer.docAction(nwDocAction.CUT) is True
assert qClip.text() == "laoreet" assert qClip.text() == "laoreet"
qClip.clear() qClip.clear()
# Copy # Copy
assert nwGUI.docViewer.docAction(nwDocAction.COPY) assert nwGUI.docViewer.docAction(nwDocAction.COPY) is True
assert qClip.text() == "laoreet" assert qClip.text() == "laoreet"
qClip.clear() qClip.clear()
# Select Paragraph # Select Paragraph
assert nwGUI.docViewer.docAction(nwDocAction.SEL_PARA) assert nwGUI.docViewer.docAction(nwDocAction.SEL_PARA) is True
theCursor = nwGUI.docViewer.textCursor() theCursor = nwGUI.docViewer.textCursor()
assert theCursor.selectedText() == ( assert theCursor.selectedText() == (
"Synopsis: Aenean ut placerat velit. Etiam laoreet ullamcorper risus, " "Synopsis: Aenean ut placerat velit. Etiam laoreet ullamcorper risus, "
@@ -103,26 +105,26 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
) )
# Select All # Select All
assert nwGUI.docViewer.docAction(nwDocAction.SEL_ALL) assert nwGUI.docViewer.docAction(nwDocAction.SEL_ALL) is True
theCursor = nwGUI.docViewer.textCursor() theCursor = nwGUI.docViewer.textCursor()
assert len(theCursor.selectedText()) == 3061 assert len(theCursor.selectedText()) == 3061
# Other actions # Other actions
assert not nwGUI.docViewer.docAction(nwDocAction.NO_ACTION) assert nwGUI.docViewer.docAction(nwDocAction.NO_ACTION) is False
# Close document # Close document
nwGUI.docViewer.docHeader._closeDocument() nwGUI.docViewer.docHeader._closeDocument()
assert nwGUI.docViewer.docHandle() is None assert nwGUI.docViewer.docHandle() is None
# Action on no document # Action on no document
assert not nwGUI.docViewer.docAction(nwDocAction.COPY) assert nwGUI.docViewer.docAction(nwDocAction.COPY) is False
# Open again via menu # Open again via menu
assert nwGUI.treeView.setSelectedHandle("88243afbe5ed8") assert nwGUI.treeView.setSelectedHandle("88243afbe5ed8")
nwGUI.mainMenu.aViewDoc.activate(QAction.Trigger) nwGUI.mainMenu.aViewDoc.activate(QAction.Trigger)
# Select "Bod" link # Select "Bod" link
assert nwGUI.docViewer.setCursorPosition(27) assert nwGUI.docViewer.setCursorPosition(27) is True
nwGUI.docViewer._makeSelection(QTextCursor.WordUnderCursor) nwGUI.docViewer._makeSelection(QTextCursor.WordUnderCursor)
theRect = nwGUI.docViewer.cursorRect() theRect = nwGUI.docViewer.cursorRect()
# qtbot.mouseClick(nwGUI.docViewer.viewport(), Qt.LeftButton, pos=theRect.center(), delay=100) # qtbot.mouseClick(nwGUI.docViewer.viewport(), Qt.LeftButton, pos=theRect.center(), delay=100)
@@ -168,17 +170,23 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, nwLipsum):
assert nwGUI.docViewer.stickyRef is viewState assert nwGUI.docViewer.stickyRef is viewState
# Document footer show/hide synopsis # Document footer show/hide synopsis
assert nwGUI.viewDocument("f96ec11c6a3da") assert nwGUI.viewDocument("f96ec11c6a3da") is True
assert len(nwGUI.docViewer.toPlainText()) == 4315 assert len(nwGUI.docViewer.toPlainText()) == 4315
nwGUI.docViewer.docFooter._doToggleSynopsis(False) nwGUI.docViewer.docFooter._doToggleSynopsis(False)
assert len(nwGUI.docViewer.toPlainText()) == 4099 assert len(nwGUI.docViewer.toPlainText()) == 4099
# Document footer show/hide comments # Document footer show/hide comments
assert nwGUI.viewDocument("846352075de7d") assert nwGUI.viewDocument("846352075de7d") is True
assert len(nwGUI.docViewer.toPlainText()) == 675 assert len(nwGUI.docViewer.toPlainText()) == 675
nwGUI.docViewer.docFooter._doToggleComments(False) nwGUI.docViewer.docFooter._doToggleComments(False)
assert len(nwGUI.docViewer.toPlainText()) == 635 assert len(nwGUI.docViewer.toPlainText()) == 635
# Crash the HTML rendering
with monkeypatch.context() as mp:
mp.setattr(ToHtml, "doConvert", causeException)
assert nwGUI.docViewer.loadText("846352075de7d") is False
assert nwGUI.docViewer.toPlainText() == "An error occurred while generating the preview."
# qtbot.stopForInteraction() # qtbot.stopForInteraction()
# END Test testGuiViewer_Main # END Test testGuiViewer_Main