From 1c7a520edfb850cc34a29d96417c475a373ed355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCri=20Valdmann?= Date: Thu, 10 Aug 2017 12:24:27 +0200 Subject: Add test for downloading a link via user action Change-Id: Ide3294840ceb3d18da0c4da92d892ff467a9b739 Reviewed-by: Michal Klocek --- .../qwebenginedownloads/qwebenginedownloads.pro | 3 + .../tst_qwebenginedownloads.cpp | 418 +++++++++++++++++++++ tests/auto/widgets/widgets.pro | 4 +- 3 files changed, 424 insertions(+), 1 deletion(-) create mode 100644 tests/auto/widgets/qwebenginedownloads/qwebenginedownloads.pro create mode 100644 tests/auto/widgets/qwebenginedownloads/tst_qwebenginedownloads.cpp (limited to 'tests/auto/widgets') diff --git a/tests/auto/widgets/qwebenginedownloads/qwebenginedownloads.pro b/tests/auto/widgets/qwebenginedownloads/qwebenginedownloads.pro new file mode 100644 index 000000000..18a66c466 --- /dev/null +++ b/tests/auto/widgets/qwebenginedownloads/qwebenginedownloads.pro @@ -0,0 +1,3 @@ +include(../tests.pri) +include(../../shared/http.pri) +QT *= core-private diff --git a/tests/auto/widgets/qwebenginedownloads/tst_qwebenginedownloads.cpp b/tests/auto/widgets/qwebenginedownloads/tst_qwebenginedownloads.cpp new file mode 100644 index 000000000..a03681cb1 --- /dev/null +++ b/tests/auto/widgets/qwebenginedownloads/tst_qwebenginedownloads.cpp @@ -0,0 +1,418 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class tst_QWebEngineDownloads : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void downloadLink_data(); + void downloadLink(); +}; + +enum DownloadTestUserAction { + SaveLink, + Navigate, +}; + +enum DownloadTestFileAction { + FileIsDownloaded, + FileIsDisplayed, +}; + +Q_DECLARE_METATYPE(DownloadTestUserAction); +Q_DECLARE_METATYPE(DownloadTestFileAction); + +void tst_QWebEngineDownloads::downloadLink_data() +{ + QTest::addColumn("userAction"); + QTest::addColumn("anchorHasDownloadAttribute"); + QTest::addColumn("fileName"); + QTest::addColumn("fileContents"); + QTest::addColumn("fileMimeTypeDeclared"); + QTest::addColumn("fileMimeTypeDetected"); + QTest::addColumn("fileDisposition"); + QTest::addColumn("fileHasReferer"); + QTest::addColumn("fileAction"); + QTest::addColumn("downloadType"); + + // SaveLink should always trigger a download, even for empty files. + QTest::newRow("save link to empty file") + /* userAction */ << SaveLink + /* anchorHasDownloadAttribute */ << false + /* fileName */ << QByteArrayLiteral("foo.txt") + /* fileContents */ << QByteArrayLiteral("") + /* fileMimeTypeDeclared */ << QByteArrayLiteral("") + /* fileMimeTypeDetected */ << QByteArrayLiteral("") + /* fileDisposition */ << QByteArrayLiteral("") + /* fileHasReferer */ << false + /* fileAction */ << FileIsDownloaded + /* downloadType */ << QWebEngineDownloadItem::DownloadAttribute; + + // SaveLink should always trigger a download, also for text files. + QTest::newRow("save link to text file") + /* userAction */ << SaveLink + /* anchorHasDownloadAttribute */ << false + /* fileName */ << QByteArrayLiteral("foo.txt") + /* fileContents */ << QByteArrayLiteral("bar") + /* fileMimeTypeDeclared */ << QByteArrayLiteral("text/plain") + /* fileMimeTypeDetected */ << QByteArrayLiteral("text/plain") + /* fileDisposition */ << QByteArrayLiteral("") + /* fileHasReferer */ << false + /* fileAction */ << FileIsDownloaded + /* downloadType */ << QWebEngineDownloadItem::DownloadAttribute; + + // ... adding the "download" attribute should have no effect. + QTest::newRow("save link to text file (attribute)") + /* userAction */ << SaveLink + /* anchorHasDownloadAttribute */ << true + /* fileName */ << QByteArrayLiteral("foo.txt") + /* fileContents */ << QByteArrayLiteral("bar") + /* fileMimeTypeDeclared */ << QByteArrayLiteral("text/plain") + /* fileMimeTypeDetected */ << QByteArrayLiteral("text/plain") + /* fileDisposition */ << QByteArrayLiteral("") + /* fileHasReferer */ << false + /* fileAction */ << FileIsDownloaded + /* downloadType */ << QWebEngineDownloadItem::DownloadAttribute; + + // ... adding the "attachment" content disposition should also have no effect. + QTest::newRow("save link to text file (attachment)") + /* userAction */ << SaveLink + /* anchorHasDownloadAttribute */ << false + /* fileName */ << QByteArrayLiteral("foo.txt") + /* fileContents */ << QByteArrayLiteral("bar") + /* fileMimeTypeDeclared */ << QByteArrayLiteral("text/plain") + /* fileMimeTypeDetected */ << QByteArrayLiteral("text/plain") + /* fileDisposition */ << QByteArrayLiteral("attachment") + /* fileHasReferer */ << false + /* fileAction */ << FileIsDownloaded + /* downloadType */ << QWebEngineDownloadItem::UserRequested; + + // ... even adding both should have no effect. + QTest::newRow("save link to text file (attribute+attachment)") + /* userAction */ << SaveLink + /* anchorHasDownloadAttribute */ << true + /* fileName */ << QByteArrayLiteral("foo.txt") + /* fileContents */ << QByteArrayLiteral("bar") + /* fileMimeTypeDeclared */ << QByteArrayLiteral("text/plain") + /* fileMimeTypeDetected */ << QByteArrayLiteral("text/plain") + /* fileDisposition */ << QByteArrayLiteral("attachment") + /* fileHasReferer */ << false + /* fileAction */ << FileIsDownloaded + /* downloadType */ << QWebEngineDownloadItem::UserRequested; + + // Navigating to an empty file should show an empty page. + QTest::newRow("navigate to empty file") + /* userAction */ << Navigate + /* anchorHasDownloadAttribute */ << false + /* fileName */ << QByteArrayLiteral("foo.txt") + /* fileContents */ << QByteArrayLiteral("") + /* fileMimeTypeDeclared */ << QByteArrayLiteral("") + /* fileMimeTypeDetected */ << QByteArrayLiteral("") + /* fileDisposition */ << QByteArrayLiteral("") + /* fileHasReferer */ << true + /* fileAction */ << FileIsDisplayed + /* downloadType */ << /* unused */ QWebEngineDownloadItem::DownloadAttribute; + + // Navigating to a text file should show the text file. + QTest::newRow("navigate to text file") + /* userAction */ << Navigate + /* anchorHasDownloadAttribute */ << false + /* fileName */ << QByteArrayLiteral("foo.txt") + /* fileContents */ << QByteArrayLiteral("bar") + /* fileMimeTypeDeclared */ << QByteArrayLiteral("text/plain") + /* fileMimeTypeDetected */ << QByteArrayLiteral("text/plain") + /* fileDisposition */ << QByteArrayLiteral("") + /* fileHasReferer */ << true + /* fileAction */ << FileIsDisplayed + /* downloadType */ << /* unused */ QWebEngineDownloadItem::DownloadAttribute; + + // ... unless the link has the "download" attribute: then the file should be downloaded. + QTest::newRow("navigate to text file (attribute)") + /* userAction */ << Navigate + /* anchorHasDownloadAttribute */ << true + /* fileName */ << QByteArrayLiteral("foo.txt") + /* fileContents */ << QByteArrayLiteral("bar") + /* fileMimeTypeDeclared */ << QByteArrayLiteral("text/plain") + /* fileMimeTypeDetected */ << QByteArrayLiteral("text/plain") + /* fileDisposition */ << QByteArrayLiteral("") + /* fileHasReferer */ << false + /* fileAction */ << FileIsDownloaded + /* downloadType */ << QWebEngineDownloadItem::DownloadAttribute; + + // ... same with the content disposition header save for the download type. + QTest::newRow("navigate to text file (attachment)") + /* userAction */ << Navigate + /* anchorHasDownloadAttribute */ << false + /* fileName */ << QByteArrayLiteral("foo.txt") + /* fileContents */ << QByteArrayLiteral("bar") + /* fileMimeTypeDeclared */ << QByteArrayLiteral("text/plain") + /* fileMimeTypeDetected */ << QByteArrayLiteral("text/plain") + /* fileDisposition */ << QByteArrayLiteral("attachment") + /* fileHasReferer */ << true + /* fileAction */ << FileIsDownloaded + /* downloadType */ << QWebEngineDownloadItem::Attachment; + + // ... and both. + QTest::newRow("navigate to text file (attribute+attachment)") + /* userAction */ << Navigate + /* anchorHasDownloadAttribute */ << true + /* fileName */ << QByteArrayLiteral("foo.txt") + /* fileContents */ << QByteArrayLiteral("bar") + /* fileMimeTypeDeclared */ << QByteArrayLiteral("text/plain") + /* fileMimeTypeDetected */ << QByteArrayLiteral("text/plain") + /* fileDisposition */ << QByteArrayLiteral("attachment") + /* fileHasReferer */ << false + /* fileAction */ << FileIsDownloaded + /* downloadType */ << QWebEngineDownloadItem::Attachment; + + // The file's extension has no effect. + QTest::newRow("navigate to supposed zip file") + /* userAction */ << Navigate + /* anchorHasDownloadAttribute */ << false + /* fileName */ << QByteArrayLiteral("foo.zip") + /* fileContents */ << QByteArrayLiteral("bar") + /* fileMimeTypeDeclared */ << QByteArrayLiteral("") + /* fileMimeTypeDetected */ << QByteArrayLiteral("") + /* fileDisposition */ << QByteArrayLiteral("") + /* fileHasReferer */ << true + /* fileAction */ << FileIsDisplayed + /* downloadType */ << /* unused */ QWebEngineDownloadItem::DownloadAttribute; + + // ... the file's mime type however does. + QTest::newRow("navigate to supposed zip file (application/zip)") + /* userAction */ << Navigate + /* anchorHasDownloadAttribute */ << false + /* fileName */ << QByteArrayLiteral("foo.zip") + /* fileContents */ << QByteArrayLiteral("bar") + /* fileMimeTypeDeclared */ << QByteArrayLiteral("application/zip") + /* fileMimeTypeDetected */ << QByteArrayLiteral("application/zip") + /* fileDisposition */ << QByteArrayLiteral("") + /* fileHasReferer */ << true + /* fileAction */ << FileIsDownloaded + /* downloadType */ << QWebEngineDownloadItem::DownloadAttribute; + + // ... but we're not very picky about the exact type. + QTest::newRow("navigate to supposed zip file (application/octet-stream)") + /* userAction */ << Navigate + /* anchorHasDownloadAttribute */ << false + /* fileName */ << QByteArrayLiteral("foo.zip") + /* fileContents */ << QByteArrayLiteral("bar") + /* fileMimeTypeDeclared */ << QByteArrayLiteral("application/octet-stream") + /* fileMimeTypeDetected */ << QByteArrayLiteral("application/octet-stream") + /* fileDisposition */ << QByteArrayLiteral("") + /* fileHasReferer */ << true + /* fileAction */ << FileIsDownloaded + /* downloadType */ << QWebEngineDownloadItem::DownloadAttribute; + + // empty zip file (consisting only of "end of central directory record") + QByteArray zipFile = QByteArrayLiteral("PK\x05\x06") + QByteArray(18, 0); + + // The mime type is guessed automatically if not provided by the server. + QTest::newRow("navigate to actual zip file") + /* userAction */ << Navigate + /* anchorHasDownloadAttribute */ << false + /* fileName */ << QByteArrayLiteral("foo.zip") + /* fileContents */ << zipFile + /* fileMimeTypeDeclared */ << QByteArrayLiteral("") + /* fileMimeTypeDetected */ << QByteArrayLiteral("application/octet-stream") + /* fileDisposition */ << QByteArrayLiteral("") + /* fileHasReferer */ << true + /* fileAction */ << FileIsDownloaded + /* downloadType */ << QWebEngineDownloadItem::DownloadAttribute; + + // The mime type is not guessed automatically if provided by the server. + QTest::newRow("navigate to actual zip file (application/zip)") + /* userAction */ << Navigate + /* anchorHasDownloadAttribute */ << false + /* fileName */ << QByteArrayLiteral("foo.zip") + /* fileContents */ << zipFile + /* fileMimeTypeDeclared */ << QByteArrayLiteral("application/zip") + /* fileMimeTypeDetected */ << QByteArrayLiteral("application/zip") + /* fileDisposition */ << QByteArrayLiteral("") + /* fileHasReferer */ << true + /* fileAction */ << FileIsDownloaded + /* downloadType */ << QWebEngineDownloadItem::DownloadAttribute; +} + +void tst_QWebEngineDownloads::downloadLink() +{ + QFETCH(DownloadTestUserAction, userAction); + QFETCH(bool, anchorHasDownloadAttribute); + QFETCH(QByteArray, fileName); + QFETCH(QByteArray, fileContents); + QFETCH(QByteArray, fileMimeTypeDeclared); + QFETCH(QByteArray, fileMimeTypeDetected); + QFETCH(QByteArray, fileDisposition); + QFETCH(bool, fileHasReferer); + QFETCH(DownloadTestFileAction, fileAction); + QFETCH(QWebEngineDownloadItem::DownloadType, downloadType); + + HttpServer server; + QWebEngineProfile profile; + QWebEnginePage page(&profile); + QWebEngineView view; + view.setPage(&page); + + // 1. Load an HTML page with a link + // + // The only variation being whether the element has a "download" + // attribute or not. + view.load(server.url()); + view.show(); + auto indexRR = waitForRequest(&server); + QVERIFY(indexRR); + QCOMPARE(indexRR->requestMethod(), QByteArrayLiteral("GET")); + QCOMPARE(indexRR->requestPath(), QByteArrayLiteral("/")); + indexRR->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("text/html")); + QByteArray html; + html += QByteArrayLiteral("Link"); + indexRR->setResponseBody(html); + indexRR->sendResponse(); + bool loadOk = false; + QVERIFY(waitForSignal(&page, &QWebEnginePage::loadFinished, [&](bool ok) { loadOk = ok; })); + QVERIFY(loadOk); + + // 1.1. Ignore favicon request + auto favIconRR = waitForRequest(&server); + QVERIFY(favIconRR); + QCOMPARE(favIconRR->requestMethod(), QByteArrayLiteral("GET")); + QCOMPARE(favIconRR->requestPath(), QByteArrayLiteral("/favicon.ico")); + favIconRR->setResponseStatus(404); + favIconRR->sendResponse(); + + // 2. Simulate user action + // + // - Navigate: user left-clicks on link + // - SaveLink: user right-clicks on link and chooses "save link as" from menu + QWidget *renderWidget = view.focusWidget(); + QPoint linkPos(10, 10); + if (userAction == SaveLink) { + view.setContextMenuPolicy(Qt::CustomContextMenu); + auto event1 = new QContextMenuEvent(QContextMenuEvent::Mouse, linkPos); + auto event2 = new QMouseEvent(QEvent::MouseButtonPress, linkPos, Qt::RightButton, {}, {}); + auto event3 = new QMouseEvent(QEvent::MouseButtonRelease, linkPos, Qt::RightButton, {}, {}); + QCoreApplication::postEvent(renderWidget, event1); + QCoreApplication::postEvent(renderWidget, event2); + QCoreApplication::postEvent(renderWidget, event3); + QVERIFY(waitForSignal(&view, &QWidget::customContextMenuRequested)); + page.triggerAction(QWebEnginePage::DownloadLinkToDisk); + } else + QTest::mouseClick(renderWidget, Qt::LeftButton, {}, linkPos); + + // 3. Deliver requested file + // + // Request/response headers vary. + auto fileRR = waitForRequest(&server); + QVERIFY(fileRR); + QCOMPARE(fileRR->requestMethod(), QByteArrayLiteral("GET")); + QCOMPARE(fileRR->requestPath(), QByteArrayLiteral("/") + fileName); + if (fileHasReferer) + QCOMPARE(fileRR->requestHeader(QByteArrayLiteral("referer")), server.url().toEncoded()); + else + QCOMPARE(fileRR->requestHeader(QByteArrayLiteral("referer")), QByteArray()); + if (!fileDisposition.isEmpty()) + fileRR->setResponseHeader(QByteArrayLiteral("content-disposition"), fileDisposition); + if (!fileMimeTypeDeclared.isEmpty()) + fileRR->setResponseHeader(QByteArrayLiteral("content-type"), fileMimeTypeDeclared); + fileRR->setResponseBody(fileContents); + fileRR->sendResponse(); + + // 4a. File is displayed and not downloaded - end test + if (fileAction == FileIsDisplayed) { + QVERIFY(waitForSignal(&page, &QWebEnginePage::loadFinished, [&](bool ok) { loadOk = ok; })); + QVERIFY(loadOk); + return; + } + + // 4b. File is downloaded - check QWebEngineDownloadItem attributes + QTemporaryDir tmpDir; + QVERIFY(tmpDir.isValid()); + QByteArray slashFileName = QByteArrayLiteral("/") + fileName; + QString suggestedPath = + QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + slashFileName; + QString downloadPath = tmpDir.path() + slashFileName; + QUrl downloadUrl = server.url(slashFileName); + QWebEngineDownloadItem *downloadItem = nullptr; + QVERIFY(waitForSignal(&profile, &QWebEngineProfile::downloadRequested, + [&](QWebEngineDownloadItem *item) { + QCOMPARE(item->state(), QWebEngineDownloadItem::DownloadRequested); + QCOMPARE(item->isFinished(), false); + QCOMPARE(item->totalBytes(), -1); + QCOMPARE(item->receivedBytes(), 0); + QCOMPARE(item->interruptReason(), QWebEngineDownloadItem::NoReason); + QCOMPARE(item->type(), downloadType); + QCOMPARE(item->mimeType(), QString(fileMimeTypeDetected)); + QCOMPARE(item->path(), suggestedPath); + QCOMPARE(item->savePageFormat(), QWebEngineDownloadItem::UnknownSaveFormat); + QCOMPARE(item->url(), downloadUrl); + item->setPath(downloadPath); + item->accept(); + downloadItem = item; + })); + QVERIFY(downloadItem); + bool finishOk = false; + QVERIFY(waitForSignal(downloadItem, &QWebEngineDownloadItem::finished, [&]() { + auto item = downloadItem; + QCOMPARE(item->state(), QWebEngineDownloadItem::DownloadCompleted); + QCOMPARE(item->isFinished(), true); + QCOMPARE(item->totalBytes(), fileContents.size()); + QCOMPARE(item->receivedBytes(), fileContents.size()); + QCOMPARE(item->interruptReason(), QWebEngineDownloadItem::NoReason); + QCOMPARE(item->type(), downloadType); + QCOMPARE(item->mimeType(), QString(fileMimeTypeDetected)); + QCOMPARE(item->path(), downloadPath); + QCOMPARE(item->savePageFormat(), QWebEngineDownloadItem::UnknownSaveFormat); + QCOMPARE(item->url(), downloadUrl); + finishOk = true; + })); + QVERIFY(finishOk); + + // 5. Check actual file contents + QFile file(downloadPath); + QVERIFY(file.open(QIODevice::ReadOnly)); + QCOMPARE(file.readAll(), fileContents); +} + +QTEST_MAIN(tst_QWebEngineDownloads) +#include "tst_qwebenginedownloads.moc" diff --git a/tests/auto/widgets/widgets.pro b/tests/auto/widgets/widgets.pro index 90352310e..441eea0fa 100644 --- a/tests/auto/widgets/widgets.pro +++ b/tests/auto/widgets/widgets.pro @@ -3,6 +3,7 @@ TEMPLATE = subdirs SUBDIRS += \ qwebengineaccessibility \ qwebenginedefaultsurfaceformat \ + qwebenginedownloads \ qwebenginefaviconmanager \ qwebenginepage \ qwebenginehistory \ @@ -24,4 +25,5 @@ contains(WEBENGINE_CONFIG, use_spellchecker):!cross_compile { # QTBUG-60268 boot2qt: SUBDIRS -= qwebengineaccessibility qwebenginedefaultsurfaceformat \ qwebenginefaviconmanager qwebenginepage qwebenginehistory \ - qwebengineprofile qwebenginescript qwebengineview + qwebengineprofile qwebenginescript qwebengineview \ + qwebenginedownloads -- cgit v1.2.3