diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 64fed3c19..3a1a931bb 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -30,6 +30,7 @@ Added - Support for the `colors -> webpage.bg` option with QtWebEngine - Support for the HTML5 fullscreen API (e.g. youtube videos) with QtWebEngine - Support for the `general -> print-element-backgrounds` option with QtWebEngine on Qt >= 5.8 +- Support for `:download --mhtml` with QtWebEngine Changed ~~~~~~~ diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py index 0e6d4b009..d21f5b19b 100644 --- a/qutebrowser/browser/browsertab.py +++ b/qutebrowser/browser/browsertab.py @@ -105,6 +105,10 @@ class AbstractAction: """Exit the fullscreen mode.""" raise NotImplementedError + def save_page(self): + """Save the current page.""" + raise NotImplementedError + class AbstractPrinting: diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index fc131151f..5c41a1ad2 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -44,12 +44,6 @@ from qutebrowser.commands import userscripts, cmdexc, cmdutils, runners from qutebrowser.config import config, configexc from qutebrowser.browser import (urlmarks, browsertab, inspector, navigate, webelem, downloads) -try: - from qutebrowser.browser.webkit import mhtml -except ImportError: - # Failing imports on QtWebEngine, only used in QtWebKit commands. - # FIXME:qtwebengine don't import this anymore at all - pass from qutebrowser.keyinput import modeman from qutebrowser.utils import (message, usertypes, log, qtutils, urlutils, objreg, utils, typing) @@ -1340,39 +1334,20 @@ class CommandDispatcher: urlutils.raise_cmdexc_if_invalid(url) download_manager.get(url, target=target) elif mhtml_: - self._download_mhtml(target) + tab = self._current_widget() + if tab.backend == usertypes.Backend.QtWebEngine: + webengine_download_manager = objreg.get( + 'webengine-download-manager') + try: + webengine_download_manager.get_mhtml(tab, target) + except browsertab.UnsupportedOperationError as e: + raise cmdexc.CommandError(e) + else: + download_manager.get_mhtml(tab, target) else: qnam = self._current_widget().networkaccessmanager() download_manager.get(self._current_url(), qnam=qnam, target=target) - def _download_mhtml(self, target=None): - """Download the current page as an MHTML file, including all assets. - - Args: - target: The download target for the file. - """ - tab = self._current_widget() - if tab.backend == usertypes.Backend.QtWebEngine: - raise cmdexc.CommandError("Download --mhtml is not implemented " - "with QtWebEngine yet") - if target is not None: - mhtml.start_download_checked(target, tab=tab) - return - - suggested_fn = self._current_title() + ".mht" - suggested_fn = utils.sanitize_filename(suggested_fn) - - filename = downloads.immediate_download_path() - if filename is not None: - target = downloads.FileDownloadTarget(filename) - mhtml.start_download_checked(target, tab=tab) - else: - question = downloads.get_filename_question( - suggested_filename=suggested_fn, url=tab.url(), parent=tab) - question.answered.connect(functools.partial( - mhtml.start_download_checked, tab=tab)) - message.global_bridge.ask(question, blocking=False) - @cmdutils.register(instance='command-dispatcher', scope='window') def view_source(self): """Show the source of the current page in a new tab.""" diff --git a/qutebrowser/browser/qtnetworkdownloads.py b/qutebrowser/browser/qtnetworkdownloads.py index eeda5d9be..f16d5ac1f 100644 --- a/qutebrowser/browser/qtnetworkdownloads.py +++ b/qutebrowser/browser/qtnetworkdownloads.py @@ -27,9 +27,9 @@ import collections from PyQt5.QtCore import pyqtSlot, pyqtSignal, QTimer from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply -from qutebrowser.utils import message, usertypes, log, urlutils +from qutebrowser.utils import message, usertypes, log, urlutils, utils from qutebrowser.browser import downloads -from qutebrowser.browser.webkit import http +from qutebrowser.browser.webkit import http, mhtml from qutebrowser.browser.webkit.network import networkmanager @@ -382,6 +382,27 @@ class DownloadManager(downloads.AbstractDownloadManager): req = QNetworkRequest(url) return self.get_request(req, **kwargs) + def get_mhtml(self, tab, target): + """Download the given tab as mhtml to the given DownloadTarget.""" + assert tab.backend == usertypes.Backend.QtWebKit + + if target is not None: + mhtml.start_download_checked(target, tab=tab) + return + + suggested_fn = utils.sanitize_filename(tab.title() + ".mhtml") + + filename = downloads.immediate_download_path() + if filename is not None: + target = downloads.FileDownloadTarget(filename) + mhtml.start_download_checked(target, tab=tab) + else: + question = downloads.get_filename_question( + suggested_filename=suggested_fn, url=tab.url(), parent=tab) + question.answered.connect(functools.partial( + mhtml.start_download_checked, tab=tab)) + message.global_bridge.ask(question, blocking=False) + def get_request(self, request, *, target=None, **kwargs): """Start a download with a QNetworkRequest. diff --git a/qutebrowser/browser/webengine/webenginedownloads.py b/qutebrowser/browser/webengine/webenginedownloads.py index 93632e3d3..072d363ba 100644 --- a/qutebrowser/browser/webengine/webenginedownloads.py +++ b/qutebrowser/browser/webengine/webenginedownloads.py @@ -47,6 +47,15 @@ class DownloadItem(downloads.AbstractDownloadItem): qt_item.downloadProgress.connect(self.stats.on_download_progress) qt_item.stateChanged.connect(self._on_state_changed) + def _is_page_download(self): + """Check if this item is a page (i.e. mhtml) download.""" + try: + return (self._qt_item.savePageFormat() != + QWebEngineDownloadItem.UnknownSaveFormat) + except AttributeError: + # Added in Qt 5.7 + return False + @pyqtSlot(QWebEngineDownloadItem.DownloadState) def _on_state_changed(self, state): state_name = debug.qenum_key(QWebEngineDownloadItem, state) @@ -59,6 +68,9 @@ class DownloadItem(downloads.AbstractDownloadItem): pass elif state == QWebEngineDownloadItem.DownloadCompleted: log.downloads.debug("Download {} finished".format(self.basename)) + if self._is_page_download(): + # Same logging as QtWebKit mhtml downloads. + log.downloads.debug("File successfully written.") self.successful = True self.done = True self.finished.emit() @@ -148,7 +160,15 @@ def _get_suggested_filename(path): class DownloadManager(downloads.AbstractDownloadManager): - """Manager for currently running downloads.""" + """Manager for currently running downloads. + + Attributes: + _mhtml_target: DownloadTarget for the next MHTML download. + """ + + def __init__(self, parent=None): + super().__init__(parent) + self._mhtml_target = None def install(self, profile): """Set up the download manager on a QWebEngineProfile.""" @@ -164,6 +184,11 @@ class DownloadManager(downloads.AbstractDownloadManager): self._init_item(download, auto_remove=False, suggested_filename=suggested_filename) + if self._mhtml_target is not None: + download.set_target(self._mhtml_target) + self._mhtml_target = None + return + filename = downloads.immediate_download_path() if filename is not None: # User doesn't want to be asked, so just use the download_dir @@ -180,3 +205,13 @@ class DownloadManager(downloads.AbstractDownloadManager): message.global_bridge.ask(question, blocking=True) # The filename is set via the question.answered signal, connected in # _init_filename_question. + + def get_mhtml(self, tab, target): + """Download the given tab as mhtml to the given target.""" + assert tab.backend == usertypes.Backend.QtWebEngine + # Raises browsertab.UnsupportedOperationError on older Qt versions + # but we let the caller handle that. + tab.action.check_save_page_supported() + assert self._mhtml_target is None, self._mhtml_target + self._mhtml_target = target + tab.action.save_page() diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 6a7dbae91..aaf406b85 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -90,6 +90,15 @@ class WebEngineAction(browsertab.AbstractAction): def exit_fullscreen(self): self._action(QWebEnginePage.ExitFullScreen) + def check_save_page_supported(self): + if not hasattr(QWebEnginePage, 'SavePage'): + raise browsertab.UnsupportedOperationError( + "Saving as mhtml is unsupported with Qt < 5.7") + + def save_page(self): + """Save the current page.""" + self._action(QWebEnginePage.SavePage) + class WebEnginePrinting(browsertab.AbstractPrinting): diff --git a/qutebrowser/browser/webkit/mhtml.py b/qutebrowser/browser/webkit/mhtml.py index 1d16a0669..bc9ea2695 100644 --- a/qutebrowser/browser/webkit/mhtml.py +++ b/qutebrowser/browser/webkit/mhtml.py @@ -470,7 +470,7 @@ class _Downloader: elif isinstance(self.target, downloads.OpenFileDownloadTarget): try: fobj = downloads.temp_download_manager.get_tmpfile( - self.tab.title() + '.mht') + self.tab.title() + '.mhtml') except OSError as exc: msg = "Download error: {}".format(exc) message.error(msg) @@ -544,9 +544,9 @@ def start_download_checked(target, tab): if not isinstance(target, downloads.FileDownloadTarget): _start_download(target, tab) return - # The default name is 'page title.mht' + # The default name is 'page title.mhtml' title = tab.title() - default_name = utils.sanitize_filename(title + '.mht') + default_name = utils.sanitize_filename(title + '.mhtml') # Remove characters which cannot be expressed in the file system encoding encoding = sys.getfilesystemencoding() diff --git a/qutebrowser/browser/webkit/webkittab.py b/qutebrowser/browser/webkit/webkittab.py index 964ef60cc..110808a55 100644 --- a/qutebrowser/browser/webkit/webkittab.py +++ b/qutebrowser/browser/webkit/webkittab.py @@ -59,6 +59,10 @@ class WebKitAction(browsertab.AbstractAction): def exit_fullscreen(self): raise browsertab.UnsupportedOperationError + def save_page(self): + """Save the current page.""" + raise browsertab.UnsupportedOperationError + class WebKitPrinting(browsertab.AbstractPrinting): diff --git a/tests/end2end/data/downloads/mhtml/complex/complex-webengine.mht b/tests/end2end/data/downloads/mhtml/complex/complex-webengine.mht new file mode 100644 index 000000000..735adcfe9 --- /dev/null +++ b/tests/end2end/data/downloads/mhtml/complex/complex-webengine.mht @@ -0,0 +1,711 @@ +From: +Subject: qutebrowser mhtml test +Date: today +MIME-Version: 1.0 +Content-Type: multipart/related; + type="text/html"; + boundary="---=_qute-UUID" + +-----=_qute-UUID +Content-Type: text/html +Content-ID: 42 +Content-Transfer-Encoding: quoted-printable +Content-Location: http://localhost:(port)/data/downloads/mhtml/complex/complex.html + + + =20 + qutebrowser mhtml test + =20 + + + =20 + + + =20 + + + =20 + + + =20 + + + =20 + + =20 + =20 + + =20 + + + + + =20 +

Welcome to the qutebrowser mhtml test page

+ =20 +
+ ...that the word qutebrowser is a word play on Qt, the + framework the browser is built with? +
+ =20 +

What is this page?

+ =20 +

This page is a test-case for the mhtml download feature of + qutebrowser. Under normal circumstances, you won't see this page, e= +xcept + if you're a qutebrowser developer or you're attending one = +of + The-Compiler's pytest demos.

+ =20 +
+ ...that this page was once a monstrosity with "this weird pixel= +ated + globe with the geocities-like background"? You can find the ol= +d + page in the old commits and indeed, it was quite atrocious. But hey= +, + every browser needs a globe... +
+ =20 +

This page references other assets and when the page is downloade= +d, + qutebrowser checks if each asset was downloaded. If some assets are + missing, the test fails and the poor developers have to search for = +the + error.

+ =20 +

Can I contribute to qutebrowser?

+ =20 +

Yes!

+ =20 +
+ ...that qutebrowser is free software? Free as in free beer= + and + free speech! Isn't that great? +
+ =20 +

...and how?

+ =20 +

See + here for more information.

+ =20 +

More useless trivia!

+ =20 +
+ ...that the font in the header is Comic Sans? +
+ =20 +
+ ...the IRC channel for qutebrowser is #qutebrowser on + irc.freenode.net +
+ =20 +
+ ...the area of a circle is =CF=80*r2? +
+ =20 +

To make this page a bit useful, I've included a chessboard, so y= +ou + can play chess. Just turn your screen 90 degrees, such that it form= +s a + flat, horizontal surface (you can skip this step if you're using a + tablet). Next, zoom the page until it fits your needs. Enjoy your r= +ound + of chess!

+ +
+ =20 + + +-----=_qute-UUID +Content-Type: image/png +Content-Transfer-Encoding: base64 +Content-Location: http://localhost:(port)/data/downloads/mhtml/complex/Background.png + +iVBORw0KGgoAAAANSUhEUgAAAAEAAABkCAIAAADITs03AAAACXBIWXMAAAsTAAALEwEAmpwYAAAA +B3RJTUUH4AICDwo6PeZczAAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUH +AAAAMElEQVQY0+3DUQ0AIAxDwaYSYK+W5t8H84ALvrjk1N2W9D+613IFUxgwwSlcRHPmAgsRBhHi +LLCEAAAAAElFTkSuQmCC +-----=_qute-UUID +Content-Type: text/css +Content-Transfer-Encoding: quoted-printable +Content-Location: http://localhost:(port)/data/downloads/mhtml/complex/external-in-extern.css + +@charset "utf-8"; + +h1, h2, h3, h4, h5, h6 { color: rgb(10, 57, 110); border-bottom: 1px dotted= + rgb(71, 71, 71); } +-----=_qute-UUID +Content-Type: image/png +Content-Transfer-Encoding: base64 +Content-Location: http://localhost:(port)/data/downloads/mhtml/complex/DYK.png + +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 +-----=_qute-UUID +Content-Type: text/css +Content-Transfer-Encoding: quoted-printable +Content-Location: http://localhost:(port)/data/downloads/mhtml/complex/base.css + +@charset "utf-8"; + +@import url("external-in-extern.css"); + +div.dyk { background-image: url("DYK.png"); background-repeat: no-repeat; m= +in-height: 128px; padding-left: 148px; margin-top: 10px; margin-bottom: 10p= +x; border: 2px solid rgb(71, 71, 71); border-radius: 64px; } +-----=_qute-UUID +Content-Type: image/png +Content-Transfer-Encoding: base64 +Content-Location: http://localhost:(port)/data/downloads/mhtml/complex/Banner.png + +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 +/a7WDh4p3SR1ryvQARUrQqSMGTMmwrXhnDlzQh3uCTd23Xu47rnLB3dVIHLfY3jnDnBG66SCvMKj +EctWJ81asKx4+YMrbBZ12HPPPcegQYMitnGUlpYyf/78BgcQjUEIwd/+9rfAZwqBhIbKBf65udtv +v71Z+UZHR7N06VKSk5MxpDz45fd7pkvdW3oy76spzF/6zeH7p17wT4fNet2sWbNYsWJFxKrX1157 +jddee63JPIKefQAj/2j5ncEy7DlU/NaZHVo+lJqaqg4bNixilW4QI0eODHrdcqmKWAlMHjFiBHa7 +PRSfN9wBfEJCAlOnTvXzjaa/Vbe+qqrodUk7Ly+vnubfFP70pz8Fu4zvvJqunqr28kvL0sTPQr0A +4vI41xu7diIBxpWaEUjN0BcYPi1MWwSpacjYdM648FKGXzKS5MR4PB4PXq8PTdPQdR2fT8Pr9eF2 ++38o/c7uxRWXX0JychJ6E472dUNit/i1zNQowYCWClYBbg00PeBMSR7vkHj2fV9/v2yopStYElqC +asPwaujSeJ0OSR6OheI57Y9nlvy7WBryRsAYN24czz///Ek5VQhqp0Dxi++vKQk9Q/IewL333hta +9fgLoF49enfKdDWkoaampobqM7JfTnVD99a6vdcDeVlZWXz99dd07NjxpAuWkpLCe++9F9wPKaXk +r40R/YmgRYsWfP3115x11ll+pc/jG//HJxblB+pwwu/reEdppetuYG9iYiLLly9vvgYVMIc++uij +AdO2fLLv9U9/G8z36scWHjSk/DRo5mwIEydODN77sdurLQc88fHxoXnjurjtttuCBFxeVF79St26 +KEJIgLvuuuuk3v2f//xnhg8f7u/shJh3KtvLqZCleZz04a5zeOscGqBVfTTbGzi0sMMIP06kTQiA +pNlHU62CPEtMlD2cx3UJLWItjGujYXcdZcuPP1FQUMjR4hLcbg/xcXHEx8eSlpZC507tychIp7a2 +ln/+ayG7du9tMEqH1+vl78/PZc4T88nNzScnpyM33noj2wo87C03KHdDYY1BmRsa3dQiFLSjB6j6 +4mUwfNRLZegosSnEDJ6KJakVvqoqj0/SunR66tFfp4lD/gF4HlBWrVrFddddV88bTFMYN25ccJHI +JiFE77B8s4HvgcQNGzZw2WWXRZgGG0OXLl0inp+amhra4N/Qxl/pNyUerKioIDExMTRtcOmllwZX +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 +-----=_qute-UUID +Content-Type: image/png +Content-Transfer-Encoding: base64 +Content-Location: http://localhost:(port)/data/downloads/mhtml/complex/Inline.png + +iVBORw0KGgoAAAANSUhEUgAAAKAAAACgCAYAAACLz2ctAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA +B3RJTUUH4AICEBUm88fqUAAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUH +AAABdUlEQVR42u3dwQmAQAxE0UTsv+W1hxAZhfcKkDl8dk+yfc45tai7Nz9Xy/Ps+9i+qyBIgAgQ +AYIAESAIEAGCABEgCBABggARIAgQAYIAESAIkP/qqlr9acA/EvY5AXEFgwARIAgQAYIAESAIEAGC +ABEgAgQBIkAQIAIEASJAeN3tHwn7kvucgLiCESAIEAGCABEgCBABggARIAgQAYIAESAIEAGCABEg +jHgnxL7oPicgrmAECAJEgCBABAgCRIAgQAQIAkSAIEAECAJEgCBABAgj3gmxL7rPCYgrGAGCABEg +CBABggARIAgQAYIAESAIEAGCABEgCBABwoh3QuyL7nMC4gpGgCBABAgCRIAgQAQIAkSAIEAECAJE +gCBABAgCRIAw4p0Q+6L7nIC4ghEgCBABggARIAgQAYIAESAIEAGCABEgCBABggARIIx4J8S+6D4n +IK5gBAgCRIAgQAQIAkSAIEAECAJEgCBABAgCRIAgQAQIIw997mYx/U+iXAAAAABJRU5ErkJggg== +-----=_qute-UUID diff --git a/tests/end2end/data/downloads/mhtml/complex/complex.html b/tests/end2end/data/downloads/mhtml/complex/complex.html index 1f34eb85c..4b34d3fed 100644 --- a/tests/end2end/data/downloads/mhtml/complex/complex.html +++ b/tests/end2end/data/downloads/mhtml/complex/complex.html @@ -22,7 +22,7 @@ diff --git a/tests/end2end/data/downloads/mhtml/complex/complex.mht b/tests/end2end/data/downloads/mhtml/complex/complex.mht index f367a8673..0cbcc4607 100644 --- a/tests/end2end/data/downloads/mhtml/complex/complex.mht +++ b/tests/end2end/data/downloads/mhtml/complex/complex.mht @@ -32,7 +32,7 @@ serif; =20 =20 diff --git a/tests/end2end/data/downloads/mhtml/complex/not-css.qss b/tests/end2end/data/downloads/mhtml/complex/not-css.qss new file mode 100644 index 000000000..e69de29bb diff --git a/tests/end2end/data/downloads/mhtml/simple/simple-webengine.mht b/tests/end2end/data/downloads/mhtml/simple/simple-webengine.mht new file mode 100644 index 000000000..d8bfdee70 --- /dev/null +++ b/tests/end2end/data/downloads/mhtml/simple/simple-webengine.mht @@ -0,0 +1,26 @@ +From: +Subject: Simple MHTML test +Date: today +MIME-Version: 1.0 +Content-Type: multipart/related; + type="text/html"; + boundary="---=_qute-UUID" + +-----=_qute-UUID +Content-Type: text/html +Content-ID: 42 +Content-Transfer-Encoding: quoted-printable +Content-Location: http://localhost:(port)/data/downloads/mhtml/simple/simple.html + + + =20 + Simple MHTML test + + + normal link to another page + =20 + + +-----=_qute-UUID diff --git a/tests/end2end/features/downloads.feature b/tests/end2end/features/downloads.feature index 7bfb66921..d052acfb6 100644 --- a/tests/end2end/features/downloads.feature +++ b/tests/end2end/features/downloads.feature @@ -197,44 +197,41 @@ Feature: Downloading things from a website. ## mhtml downloads - @qtwebengine_todo: :download --mhtml is not implemented yet Scenario: Downloading as mhtml is available - When I open html + When I open data/title.html And I run :download --mhtml And I wait for "File successfully written." in the log - Then no crash should happen + Then the downloaded file Test title.mhtml should exist - @qtwebengine_todo: :download --mhtml is not implemented yet + @qtwebengine_skip: QtWebEngine refuses to load this Scenario: Downloading as mhtml with non-ASCII headers When I open response-headers?Content-Type=text%2Fpl%C3%A4in - And I run :download --mhtml --dest mhtml-response-headers.mht + And I run :download --mhtml --dest mhtml-response-headers.mhtml And I wait for "File successfully written." in the log - Then no crash should happen + Then the downloaded file mhtml-response-headers.mhtml should exist - @qtwebengine_todo: :download --mhtml is not implemented yet Scenario: Overwriting existing mhtml file When I set storage -> prompt-download-directory to true - And I open html + And I open data/title.html And I run :download --mhtml - And I wait for "Asking question text='Please enter a location for http://localhost:*/html' title='Save file to:'>, *" in the log + And I wait for "Asking question text='Please enter a location for http://localhost:*/data/title.html' title='Save file to:'>, *" in the log And I run :prompt-accept And I wait for "File successfully written." in the log And I run :download --mhtml - And I wait for "Asking question text='Please enter a location for http://localhost:*/html' title='Save file to:'>, *" in the log + And I wait for "Asking question text='Please enter a location for http://localhost:*/data/title.html' title='Save file to:'>, *" in the log And I run :prompt-accept And I wait for "Asking question text='* already exists. Overwrite?' title='Overwrite existing file?'>, *" in the log And I run :prompt-accept yes And I wait for "File successfully written." in the log - Then no crash should happen + Then the downloaded file Test title.mhtml should exist - @qtwebengine_todo: :download --mhtml is not implemented yet Scenario: Opening a mhtml download directly When I set storage -> prompt-download-directory to true And I open html And I run :download --mhtml And I wait for the download prompt for "*" And I directly open the download - Then "Opening *.mht* with [*python*]" should be logged + Then "Opening *.mhtml* with [*python*]" should be logged ## :download-cancel diff --git a/tests/end2end/test_mhtml_e2e.py b/tests/end2end/test_mhtml_e2e.py index 91ed693f6..397e585cc 100644 --- a/tests/end2end/test_mhtml_e2e.py +++ b/tests/end2end/test_mhtml_e2e.py @@ -27,10 +27,6 @@ import collections import pytest -pytestmark = pytest.mark.qtwebengine_todo("mhtml downloads are not " - "implemented") - - def collect_tests(): basedir = os.path.dirname(__file__) datadir = os.path.join(basedir, 'data', 'downloads', 'mhtml') @@ -40,10 +36,15 @@ def collect_tests(): def normalize_line(line): line = line.rstrip('\n') - line = re.sub('boundary="---=_qute-[0-9a-f-]+"', + line = re.sub('boundary="-+(=_qute|MultipartBoundary)-[0-9a-zA-Z-]+"', 'boundary="---=_qute-UUID"', line) - line = re.sub('^-----=_qute-[0-9a-f-]+$', '-----=_qute-UUID', line) + line = re.sub('^-+(=_qute|MultipartBoundary)-[0-9a-zA-Z-]+$', + '-----=_qute-UUID', line) line = re.sub(r'localhost:\d{1,5}', 'localhost:(port)', line) + if line.startswith('Date: '): + line = 'Date: today' + if line.startswith('Content-ID: '): + line = 'Content-ID: 42' # Depending on Python's mimetypes module/the system's mime files, .js # files could be either identified as x-javascript or just javascript @@ -82,7 +83,7 @@ def download_dir(tmpdir): @pytest.mark.parametrize('test_name', collect_tests()) -def test_mhtml(test_name, download_dir, quteproc, httpbin): +def test_mhtml(request, test_name, download_dir, quteproc, httpbin): quteproc.set_setting('storage', 'download-directory', download_dir.location) quteproc.set_setting('storage', 'prompt-download-directory', 'false') @@ -104,13 +105,17 @@ def test_mhtml(test_name, download_dir, quteproc, httpbin): # Discard all requests that were necessary to display the page httpbin.clear_data() quteproc.send_cmd(':download --mhtml --dest "{}"'.format(download_dest)) - quteproc.wait_for(category='downloads', module='mhtml', - function='_finish_file', + quteproc.wait_for(category='downloads', message='File successfully written.') - expected_file = os.path.join(test_dir, '{}.mht'.format(test_name)) + suffix = '-webengine' if request.config.webengine else '' + filename = '{}{}.mht'.format(test_name, suffix) + expected_file = os.path.join(test_dir, filename) download_dir.compare_mhtml(expected_file) + if request.config.webengine: + return + with open(os.path.join(test_dir, 'requests'), encoding='utf-8') as f: expected_requests = [] for line in f: