diff options
Diffstat (limited to 'tests/auto/widgets/loadsignals/tst_loadsignals.cpp')
-rw-r--r-- | tests/auto/widgets/loadsignals/tst_loadsignals.cpp | 523 |
1 files changed, 406 insertions, 117 deletions
diff --git a/tests/auto/widgets/loadsignals/tst_loadsignals.cpp b/tests/auto/widgets/loadsignals/tst_loadsignals.cpp index 32bf53931..6140b3766 100644 --- a/tests/auto/widgets/loadsignals/tst_loadsignals.cpp +++ b/tests/auto/widgets/loadsignals/tst_loadsignals.cpp @@ -1,41 +1,63 @@ -/**************************************************************************** -** -** Copyright (C) 2018 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$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include <QtTest/QtTest> #include "httpserver.h" -#include "../util.h" +#include <util.h> #include "qdebug.h" +#include "qwebengineloadinginfo.h" #include "qwebenginepage.h" #include "qwebengineprofile.h" #include "qwebenginesettings.h" #include "qwebengineview.h" +enum { + LoadStarted = QWebEngineLoadingInfo::LoadStartedStatus, + LoadStopped = QWebEngineLoadingInfo::LoadStoppedStatus, + LoadSucceeded = QWebEngineLoadingInfo::LoadSucceededStatus, + LoadFailed = QWebEngineLoadingInfo::LoadFailedStatus, +}; +static const QList<int> SignalsOrderOnce({ LoadStarted, LoadSucceeded}); +static const QList<int> SignalsOrderTwice({ LoadStarted, LoadSucceeded, LoadStarted, LoadSucceeded }); +static const QList<int> SignalsOrderOnceFailure({ LoadStarted, LoadFailed }); +static const QList<int> SignalsOrderTwiceWithFailure({ LoadStarted, LoadSucceeded, LoadStarted, LoadFailed }); + +class TestPage : public QWebEnginePage +{ +public: + QSet<QUrl> blacklist; + int navigationRequestCount = 0; + QList<int> signalsOrder; + QList<int> loadProgress; + QList<QWebEngineLoadingInfo> loadingInfos; + + explicit TestPage(QObject *parent = nullptr) : TestPage(nullptr, parent) { } + TestPage(QWebEngineProfile *profile, QObject *parent = nullptr) : QWebEnginePage(profile, parent) { + connect(this, &QWebEnginePage::loadStarted, [this] () { signalsOrder.append(LoadStarted); }); + connect(this, &QWebEnginePage::loadProgress, [this] (int p) { loadProgress.append(p); }); + connect(this, &QWebEnginePage::loadFinished, [this] (bool r) { signalsOrder.append(r ? LoadSucceeded : LoadFailed); }); + connect(this, &QWebEnginePage::loadingChanged, [this] (const QWebEngineLoadingInfo &i) { loadingInfos.append(i); }); + } + ~TestPage() { Q_ASSERT(signalsOrder.size() == loadingInfos.size()); } + + void reset() + { + blacklist.clear(); + navigationRequestCount = 0; + signalsOrder.clear(); + loadProgress.clear(); + loadingInfos.clear(); + } + +protected: + bool acceptNavigationRequest(const QUrl &url, NavigationType, bool) override + { + ++navigationRequestCount; + return !blacklist.contains(url); + } +}; + class tst_LoadSignals : public QObject { Q_OBJECT @@ -48,32 +70,59 @@ private Q_SLOTS: void monotonicity(); void loadStartedAndFinishedCount_data(); void loadStartedAndFinishedCount(); - void secondLoadForError_WhenErrorPageEnabled_data(); - void secondLoadForError_WhenErrorPageEnabled(); + void loadStartedAndFinishedCountClick_data(); + void loadStartedAndFinishedCountClick(); + void rejectNavigationRequest_data(); + void rejectNavigationRequest(); void loadAfterInPageNavigation_qtbug66869(); - void fileDownloadDoesNotTriggerLoadSignals_qtbug66661(); + void fileDownload(); + void numberOfStartedAndFinishedSignalsIsSame_data(); + void numberOfStartedAndFinishedSignalsIsSame(); + void loadFinishedAfterNotFoundError_data(); + void loadFinishedAfterNotFoundError(); + void errorPageTriggered_data(); + void errorPageTriggered(); private: + void clickLink(QPoint linkPos); + QWebEngineProfile profile; - QWebEnginePage page{&profile}; + TestPage page{&profile}; QWebEngineView view; QSignalSpy loadStartedSpy{&page, &QWebEnginePage::loadStarted}; - QSignalSpy loadProgressSpy{&page, &QWebEnginePage::loadProgress}; QSignalSpy loadFinishedSpy{&page, &QWebEnginePage::loadFinished}; + void resetSpies() { + loadStartedSpy.clear(); + loadFinishedSpy.clear(); + } }; void tst_LoadSignals::initTestCase() { view.setPage(&page); - view.resize(1024,768); + view.resize(640, 480); view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); } void tst_LoadSignals::init() { - loadStartedSpy.clear(); - loadProgressSpy.clear(); - loadFinishedSpy.clear(); + // Reset content + if (!view.url().isEmpty()) { + loadFinishedSpy.clear(); + view.load(QUrl("about:blank")); + QTRY_COMPARE(loadFinishedSpy.size(), 1); + } + resetSpies(); + page.reset(); +} + +void tst_LoadSignals::clickLink(QPoint linkPos) +{ + // Simulate left-clicking on link. + QTRY_VERIFY(view.focusProxy()); + QWidget *renderWidget = view.focusProxy(); + QTest::mouseClick(renderWidget, Qt::LeftButton, {}, linkPos); } /** @@ -82,31 +131,137 @@ void tst_LoadSignals::init() void tst_LoadSignals::loadStartedAndFinishedCount_data() { QTest::addColumn<QUrl>("url"); - QTest::addColumn<int>("expectedLoadCount"); - QTest::newRow("Normal") << QUrl("qrc:///resources/page1.html") << 1; - QTest::newRow("WithAnchor") << QUrl("qrc:///resources/page2.html#anchor") << 1; - - // In this case, we get an unexpected additional loadStarted, but no corresponding - // loadFinished, so expectedLoadCount=2 would also not work. See also QTBUG-65223 - QTest::newRow("WithAnchorClickedFromJS") << QUrl("qrc:///resources/page3.html") << 1; + QTest::addColumn<QList<int>>("expectedSignals"); + QTest::newRow("Simple") << QUrl("qrc:///resources/page1.html") << SignalsOrderOnce; + QTest::newRow("SimpleWithAnchor") << QUrl("qrc:///resources/page2.html#anchor") << SignalsOrderOnce; + QTest::newRow("SamePageImmediate") << QUrl("qrc:///resources/page5.html") << SignalsOrderOnce; + QTest::newRow("SamePageDeferred") << QUrl("qrc:///resources/page3.html") << SignalsOrderOnce; + QTest::newRow("OtherPageImmediate") << QUrl("qrc:///resources/page6.html") << SignalsOrderOnce; + QTest::newRow("OtherPageDeferred") << QUrl("qrc:///resources/page7.html") << SignalsOrderTwice; + QTest::newRow("SamePageImmediateJS") << QUrl("qrc:///resources/page8.html") << SignalsOrderOnce; } void tst_LoadSignals::loadStartedAndFinishedCount() { QFETCH(QUrl, url); - QFETCH(int, expectedLoadCount); + QFETCH(QList<int>, expectedSignals); view.load(url); + + int expectedLoadCount = expectedSignals.size() / 2; + QTRY_COMPARE(loadStartedSpy.size(), expectedLoadCount); QTRY_COMPARE(loadFinishedSpy.size(), expectedLoadCount); + + // verify no more signals is emitted by waiting for another loadStarted or loadFinished + QTRY_LOOP_IMPL(loadStartedSpy.size() != expectedLoadCount || loadFinishedSpy.size() != expectedLoadCount, 1000, 100); + + // No further signals should have occurred within this time and expected number of signals is preserved + QCOMPARE(loadStartedSpy.size(), expectedLoadCount); + QCOMPARE(loadFinishedSpy.size(), expectedLoadCount); + QCOMPARE(page.signalsOrder, expectedSignals); + QCOMPARE(page.signalsOrder.size(), page.loadingInfos.size()); + for (int i = 0; i < page.signalsOrder.size(); ++i) + QCOMPARE(page.signalsOrder[i], page.loadingInfos[i].status()); +} + +/** + * Load a URL, then simulate a click to load a different URL. + */ +void tst_LoadSignals::loadStartedAndFinishedCountClick_data() +{ + QTest::addColumn<QUrl>("url"); + QTest::addColumn<int>("numberOfSignals"); + QTest::newRow("SamePage") << QUrl("qrc:///resources/page2.html") << 0; // in-page navigation to anchor shouldn't emit anything + QTest::newRow("OtherPage") << QUrl("qrc:///resources/page1.html") << 1; +} + +void tst_LoadSignals::loadStartedAndFinishedCountClick() +{ + QFETCH(QUrl, url); + QFETCH(int, numberOfSignals); + + view.load(url); + QTRY_COMPARE(loadStartedSpy.size(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 1); QVERIFY(loadFinishedSpy[0][0].toBool()); + resetSpies(); + + clickLink(QPoint(10, 10)); + if (numberOfSignals > 0) { + QTRY_COMPARE(loadStartedSpy.size(), numberOfSignals); + QTRY_COMPARE(loadFinishedSpy.size(), numberOfSignals); + QVERIFY(loadFinishedSpy[0][0].toBool()); + } + + // verify no more signals is emitted by waiting for another loadStarted or loadFinished + QTRY_LOOP_IMPL(loadStartedSpy.size() != numberOfSignals || loadFinishedSpy.size() != numberOfSignals, 1000, 100); + + // No further loadStarted should have occurred within this time + QCOMPARE(loadStartedSpy.size(), numberOfSignals); + QCOMPARE(loadFinishedSpy.size(), numberOfSignals); + QCOMPARE(page.signalsOrder, numberOfSignals > 0 ? SignalsOrderTwice : SignalsOrderOnce); +} + +void tst_LoadSignals::rejectNavigationRequest_data() +{ + QTest::addColumn<QUrl>("initialUrl"); + QTest::addColumn<QUrl>("rejectedUrl"); + QTest::addColumn<int>("expectedNavigations"); + QTest::addColumn<QList<int>>("expectedSignals"); + QTest::addColumn<int>("errorCode"); + QTest::addColumn<QWebEngineLoadingInfo::ErrorDomain>("errorDomain"); + QTest::newRow("Simple") + << QUrl("qrc:///resources/page1.html") + << QUrl("qrc:///resources/page1.html") + << 1 << SignalsOrderOnceFailure << -3 << QWebEngineLoadingInfo::InternalErrorDomain; + QTest::newRow("SamePageImmediate") + << QUrl("qrc:///resources/page5.html") + << QUrl("qrc:///resources/page5.html#anchor") + << 1 << SignalsOrderOnce << 200 << QWebEngineLoadingInfo::HttpStatusCodeDomain; + QTest::newRow("SamePageDeferred") + << QUrl("qrc:///resources/page3.html") + << QUrl("qrc:///resources/page3.html#anchor") + << 1 << SignalsOrderOnce << 200 << QWebEngineLoadingInfo::HttpStatusCodeDomain; + QTest::newRow("OtherPageImmediate") + << QUrl("qrc:///resources/page6.html") + << QUrl("qrc:///resources/page2.html#anchor") + << 2 << SignalsOrderOnceFailure << -3 << QWebEngineLoadingInfo::InternalErrorDomain; + QTest::newRow("OtherPageDeferred") + << QUrl("qrc:///resources/page7.html") + << QUrl("qrc:///resources/page2.html#anchor") + << 2 << SignalsOrderTwiceWithFailure << -3 << QWebEngineLoadingInfo::InternalErrorDomain; +} - // Wait for 10 seconds (abort waiting if another loadStarted or loadFinished occurs) - QTRY_LOOP_IMPL((loadStartedSpy.size() != expectedLoadCount) - || (loadFinishedSpy.size() != expectedLoadCount), 10000, 100); +/** + * Returning false from acceptNavigationRequest means that the load + * fails, not that the load never starts. + * + * See QTBUG-75185. + */ +void tst_LoadSignals::rejectNavigationRequest() +{ + QFETCH(QUrl, initialUrl); + QFETCH(QUrl, rejectedUrl); + QFETCH(int, expectedNavigations); + QFETCH(QList<int>, expectedSignals); + QFETCH(int, errorCode); + QFETCH(QWebEngineLoadingInfo::ErrorDomain, errorDomain); + + page.blacklist.insert(rejectedUrl); + page.load(initialUrl); + QTRY_COMPARE(page.navigationRequestCount, expectedNavigations); + int expectedLoadCount = expectedSignals.size() / 2; + QTRY_COMPARE(loadFinishedSpy.size(), expectedLoadCount); + QCOMPARE(page.signalsOrder, expectedSignals); + + // verify no more signals is emitted by waiting for another loadStarted or loadFinished + QTRY_LOOP_IMPL(loadStartedSpy.size() != expectedLoadCount || loadFinishedSpy.size() != expectedLoadCount, 1000, 100); // No further loadStarted should have occurred within this time QCOMPARE(loadStartedSpy.size(), expectedLoadCount); QCOMPARE(loadFinishedSpy.size(), expectedLoadCount); + QCOMPARE(page.loadingInfos.last().errorCode(), errorCode); + QCOMPARE(page.loadingInfos.last().errorDomain(), errorDomain); } /** @@ -115,63 +270,29 @@ void tst_LoadSignals::loadStartedAndFinishedCount() void tst_LoadSignals::monotonicity() { HttpServer server; - server.setResourceDirs({ TESTS_SHARED_DATA_DIR }); + server.setResourceDirs({ server.sharedDataDir() }); connect(&server, &HttpServer::newRequest, [] (HttpReqRep *) { QTest::qWait(250); // just add delay to trigger some progress for every sub resource }); QVERIFY(server.start()); view.load(server.url("/loadprogress/main.html")); - QTRY_COMPARE(loadFinishedSpy.size(), 1); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 10000); QVERIFY(loadFinishedSpy[0][0].toBool()); + QVERIFY(page.loadProgress.size() >= 3); // first loadProgress should have 0% progress - QCOMPARE(loadProgressSpy.takeFirst()[0].toInt(), 0); + QCOMPARE(page.loadProgress.first(), 0); // every loadProgress should have more progress than the one before - int progress = 0; - for (const auto &item : loadProgressSpy) { - QVERIFY(progress < item[0].toInt()); - progress = item[0].toInt(); + int progress = -1; + for (int p : page.loadProgress) { + QVERIFY(progress < p); + progress = p; } // last loadProgress should have 100% progress - QCOMPARE(loadProgressSpy.last()[0].toInt(), 100); -} - -/** - * Test that we get a second loadStarted and loadFinished signal - * for error-pages (unless error-pages are disabled) - */ -void tst_LoadSignals::secondLoadForError_WhenErrorPageEnabled_data() -{ - QTest::addColumn<bool>("enabled"); - // in this case, we get no second loadStarted and loadFinished, although we had - // agreed on making the navigation to an error page an individual load - QTest::newRow("ErrorPageEnabled") << true; - QTest::newRow("ErrorPageDisabled") << false; -} - -void tst_LoadSignals::secondLoadForError_WhenErrorPageEnabled() -{ - QFETCH(bool, enabled); - view.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, enabled); - int expectedLoadCount = (enabled ? 2 : 1); - - // RFC 2606 guarantees that this will never become a valid domain - view.load(QUrl("http://nonexistent.invalid")); - QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), expectedLoadCount, 10000); - QVERIFY(!loadFinishedSpy[0][0].toBool()); - if (enabled) - QVERIFY(loadFinishedSpy[1][0].toBool()); - - // Wait for 10 seconds (abort waiting if another loadStarted or loadFinished occurs) - QTRY_LOOP_IMPL((loadStartedSpy.size() != expectedLoadCount) - || (loadFinishedSpy.size() != expectedLoadCount), 10000, 100); - - // No further loadStarted should have occurred within this time - QCOMPARE(loadStartedSpy.size(), expectedLoadCount); - QCOMPARE(loadFinishedSpy.size(), expectedLoadCount); + QCOMPARE(page.loadProgress.last(), 100); } /** @@ -185,27 +306,17 @@ void tst_LoadSignals::loadAfterInPageNavigation_qtbug66869() QVERIFY(loadFinishedSpy[0][0].toBool()); // page3 does an in-page navigation after 500ms - QTest::qWait(2000); - loadFinishedSpy.clear(); - loadProgressSpy.clear(); - loadStartedSpy.clear(); + QTRY_COMPARE(view.url(), QUrl("qrc:///resources/page3.html#anchor")); // second load view.load(QUrl("qrc:///resources/page1.html")); - QTRY_COMPARE(loadFinishedSpy.size(), 1); + QTRY_COMPARE(loadFinishedSpy.size(), 2); QVERIFY(loadFinishedSpy[0][0].toBool()); // loadStarted and loadFinished should have been signalled - QCOMPARE(loadStartedSpy.size(), 1); - - // reminder that we still need to solve the core issue - QFAIL("https://codereview.qt-project.org/#/c/222112/ only hides the symptom, the core issue still needs to be solved"); + QCOMPARE(loadStartedSpy.size(), 2); } -/** - * Test that file-downloads don't trigger loadStarted or loadFinished signals. - * See QTBUG-66661 - */ -void tst_LoadSignals::fileDownloadDoesNotTriggerLoadSignals_qtbug66661() +void tst_LoadSignals::fileDownload() { view.load(QUrl("qrc:///resources/page4.html")); QTRY_COMPARE(loadFinishedSpy.size(), 1); @@ -214,12 +325,12 @@ void tst_LoadSignals::fileDownloadDoesNotTriggerLoadSignals_qtbug66661() // allow the download QTemporaryDir tempDir; QVERIFY(tempDir.isValid()); - QWebEngineDownloadItem::DownloadState downloadState = QWebEngineDownloadItem::DownloadRequested; + QWebEngineDownloadRequest::DownloadState downloadState = QWebEngineDownloadRequest::DownloadRequested; ScopedConnection sc1 = connect(&profile, &QWebEngineProfile::downloadRequested, - [&downloadState, &tempDir](QWebEngineDownloadItem *item) { - connect(item, &QWebEngineDownloadItem::stateChanged, - [&downloadState](QWebEngineDownloadItem::DownloadState newState) { + [&downloadState, &tempDir](QWebEngineDownloadRequest *item) { + connect(item, &QWebEngineDownloadRequest::stateChanged, + [&downloadState](QWebEngineDownloadRequest::DownloadState newState) { downloadState = newState; }); item->setDownloadDirectory(tempDir.path()); @@ -227,20 +338,198 @@ void tst_LoadSignals::fileDownloadDoesNotTriggerLoadSignals_qtbug66661() }); // trigger the download link that becomes focused on page4 - QTest::qWait(1000); QTest::sendKeyEvent(QTest::Press, view.focusProxy(), Qt::Key_Return, QString("\r"), Qt::NoModifier); QTest::sendKeyEvent(QTest::Release, view.focusProxy(), Qt::Key_Return, QString("\r"), Qt::NoModifier); - // Wait for 10 seconds (abort waiting if another loadStarted or loadFinished occurs) - QTRY_LOOP_IMPL((loadStartedSpy.size() != 1) - || (loadFinishedSpy.size() != 1), 10000, 100); - // Download must have occurred - QTRY_COMPARE(downloadState, QWebEngineDownloadItem::DownloadCompleted); + QTRY_COMPARE(downloadState, QWebEngineDownloadRequest::DownloadCompleted); + QTRY_COMPARE(loadFinishedSpy.size() + loadStartedSpy.size(), 4); - // No further loadStarted should have occurred within this time - QCOMPARE(loadStartedSpy.size(), 1); + // verify no more signals is emitted by waiting for another loadStarted or loadFinished + QTRY_LOOP_IMPL(loadStartedSpy.size() != 2 || loadFinishedSpy.size() != 2, 1000, 100); + + QCOMPARE(page.signalsOrder, SignalsOrderTwiceWithFailure); + QCOMPARE(page.loadingInfos[3].errorCode(), -3); + QCOMPARE(page.loadingInfos[3].errorDomain(), QWebEngineLoadingInfo::InternalErrorDomain); +} + +void tst_LoadSignals::numberOfStartedAndFinishedSignalsIsSame_data() +{ + QTest::addColumn<bool>("imageFromServer"); + QTest::addColumn<QString>("imageResourceUrl"); + // triggers these calls in delegate internally: + // just two ordered triples DidStartNavigation/DidFinishNavigation/DidFinishLoad + QTest::newRow("no_image_resource") << false << ""; + // out of order: DidStartNavigation/DidFinishNavigation/DidStartNavigation/DidFailLoad/DidFinishNavigation/DidFinishLoad + QTest::newRow("with_invalid_image") << false << "https://non.existent.locahost/image.png"; + // out of order: DidStartNavigation/DidFinishNavigation/DidStartNavigation/DidFinishLoad/DidFinishNavigation/DidFinishLoad + QTest::newRow("with_server_image") << true << ""; +} + +void tst_LoadSignals::numberOfStartedAndFinishedSignalsIsSame() +{ + QFETCH(bool, imageFromServer); + QFETCH(QString, imageResourceUrl); + + HttpServer server; + server.setResourceDirs({ server.sharedDataDir() }); + QVERIFY(server.start()); + + QUrl serverImage = server.url("/hedgehog.png"); + QString imageUrl(!imageFromServer && imageResourceUrl.isEmpty() + ? "" : (imageFromServer ? serverImage.toEncoded() : imageResourceUrl)); + + auto html = "<html><head><link rel='icon' href='data:,'></head><body>" + "%1" "<form method='GET' name='hiddenform' action='qrc:///resources/page1.html' />" + "<script language='javascript'>document.forms[0].submit();</script>" + "</body></html>"; + view.page()->setHtml(QString(html).arg(imageUrl.isEmpty() ? "" : "<img src='" + imageUrl + "'>")); + QTRY_COMPARE(loadFinishedSpy.size(), 1); + + resetSpies(); + QTRY_LOOP_IMPL(loadStartedSpy.size() || loadFinishedSpy.size(), 1000, 100); + QCOMPARE(page.signalsOrder, SignalsOrderOnce); + QCOMPARE(page.loadingInfos[1].errorCode(), 200); + QCOMPARE(page.loadingInfos[1].errorDomain(), QWebEngineLoadingInfo::HttpStatusCodeDomain); +} + +void tst_LoadSignals::loadFinishedAfterNotFoundError_data() +{ + QTest::addColumn<bool>("rfcInvalid"); + QTest::addColumn<bool>("withServer"); + QTest::addColumn<int>("errorCode"); + QTest::addColumn<int>("errorDomain"); + QTest::addRow("rfc_invalid") << true << false << -105 << int(QWebEngineLoadingInfo::ConnectionErrorDomain); + QTest::addRow("non_existent") << false << false << -105 << int(QWebEngineLoadingInfo::ConnectionErrorDomain); + QTest::addRow("server_404") << false << true << 404 << int(QWebEngineLoadingInfo::HttpStatusCodeDomain); +} + +void tst_LoadSignals::loadFinishedAfterNotFoundError() +{ + QFETCH(bool, rfcInvalid); + QFETCH(bool, withServer); + QFETCH(int, errorCode); + QFETCH(int, errorDomain); + + QScopedPointer<HttpServer> server; + if (withServer) { + server.reset(new HttpServer); + QVERIFY(server->start()); + } + view.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); + auto url = server + ? server->url("/not-found-page.html") + : QUrl(rfcInvalid ? "http://some.invalid" : "http://non.existent/url"); + view.load(url); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 1, 20000); + QVERIFY(!loadFinishedSpy.at(0).at(0).toBool()); + QCOMPARE(toPlainTextSync(view.page()), QString()); QCOMPARE(loadFinishedSpy.size(), 1); + QCOMPARE(loadStartedSpy.size(), 1); + QVERIFY(std::is_sorted(page.loadProgress.begin(), page.loadProgress.end())); + page.loadProgress.clear(); + + { auto &&loadStart = page.loadingInfos[0], &&loadFinish = page.loadingInfos[1]; + QCOMPARE(loadStart.status(), QWebEngineLoadingInfo::LoadStartedStatus); + QCOMPARE(loadStart.isErrorPage(), false); + QCOMPARE(loadStart.errorCode(), 0); + QCOMPARE(loadStart.errorDomain(), QWebEngineLoadingInfo::NoErrorDomain); + QCOMPARE(loadStart.errorString(), QString()); + QCOMPARE(loadFinish.status(), QWebEngineLoadingInfo::LoadFailedStatus); + QCOMPARE(loadFinish.isErrorPage(), false); + QCOMPARE(loadFinish.errorCode(), errorCode); + QCOMPARE(loadFinish.errorDomain(), errorDomain); + QVERIFY(!loadFinish.errorString().isEmpty()); + } + + view.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, true); + url = server + ? server->url("/another-missing-one.html") + : QUrl(rfcInvalid ? "http://some.other.invalid" : "http://another.non.existent/url"); + view.load(url); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 2, 20000); + QVERIFY(!loadFinishedSpy.at(1).at(0).toBool()); + QCOMPARE(loadStartedSpy.size(), 2); + + QEXPECT_FAIL("", "No more loads (like separate load for error pages) are expected", Continue); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.size(), 3, 1000); + QCOMPARE(loadStartedSpy.size(), 2); + QVERIFY(std::is_sorted(page.loadProgress.begin(), page.loadProgress.end())); + + { auto &&loadStart = page.loadingInfos[2], &&loadFinish = page.loadingInfos[3]; + QCOMPARE(loadStart.status(), QWebEngineLoadingInfo::LoadStartedStatus); + QCOMPARE(loadStart.isErrorPage(), false); + QCOMPARE(loadStart.errorCode(), 0); + QCOMPARE(loadStart.errorDomain(), QWebEngineLoadingInfo::NoErrorDomain); + QCOMPARE(loadStart.errorString(), QString()); + QCOMPARE(loadFinish.status(), QWebEngineLoadingInfo::LoadFailedStatus); + QCOMPARE(loadFinish.isErrorPage(), true); + QCOMPARE(loadFinish.errorCode(), errorCode); + QCOMPARE(loadFinish.errorDomain(), errorDomain); + QVERIFY(!loadFinish.errorString().isEmpty()); + } +} + +void tst_LoadSignals::errorPageTriggered_data() +{ + QTest::addColumn<QString>("urlPath"); + QTest::addColumn<bool>("loadSucceed"); + QTest::addColumn<bool>("triggersErrorPage"); + QTest::addColumn<int>("errorCode"); + QTest::addColumn<QWebEngineLoadingInfo::ErrorDomain>("errorDomain"); + QTest::newRow("/content/200") << QStringLiteral("/content/200") << true << false << 200 << QWebEngineLoadingInfo::HttpStatusCodeDomain; + QTest::newRow("/empty/200") << QStringLiteral("/content/200") << true << false << 200 << QWebEngineLoadingInfo::HttpStatusCodeDomain; + QTest::newRow("/content/404") << QStringLiteral("/content/404") << false << false << 404 << QWebEngineLoadingInfo::HttpStatusCodeDomain; + QTest::newRow("/empty/404") << QStringLiteral("/empty/404") << false << true << 404 << QWebEngineLoadingInfo::HttpStatusCodeDomain; +} + +void tst_LoadSignals::errorPageTriggered() +{ + HttpServer server; + connect(&server, &HttpServer::newRequest, [] (HttpReqRep *rr) { + QList<QByteArray> parts = rr->requestPath().split('/'); + if (parts.size() != 3) { + // For example, /favicon.ico + rr->sendResponse(404); + return; + } + bool isDocumentEmpty = (parts[1] == "empty"); + int httpStatusCode = parts[2].toInt(); + + rr->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("text/html")); + if (!isDocumentEmpty) { + rr->setResponseBody(QByteArrayLiteral("<html></html>")); + } + rr->sendResponse(httpStatusCode); + }); + QVERIFY(server.start()); + + QFETCH(QString, urlPath); + QFETCH(bool, loadSucceed); + QFETCH(bool, triggersErrorPage); + QFETCH(int, errorCode); + QFETCH(QWebEngineLoadingInfo::ErrorDomain, errorDomain); + + view.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, true); + view.load(server.url(urlPath)); + QTRY_COMPARE(loadFinishedSpy.size(), 1); + QCOMPARE(loadFinishedSpy[0][0].toBool(), loadSucceed); + if (triggersErrorPage) + QVERIFY(toPlainTextSync(view.page()).contains("HTTP ERROR 404")); + else + QVERIFY(toPlainTextSync(view.page()).isEmpty()); + QCOMPARE(page.loadingInfos[1].errorCode(), errorCode); + QCOMPARE(page.loadingInfos[1].errorDomain(), errorDomain); + loadFinishedSpy.clear(); + + view.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); + view.load(server.url(urlPath)); + QTRY_COMPARE(loadFinishedSpy.size(), 1); + QCOMPARE(loadFinishedSpy[0][0].toBool(), loadSucceed); + QVERIFY(toPlainTextSync(view.page()).isEmpty()); + QCOMPARE(page.loadingInfos[3].errorCode(), errorCode); + QCOMPARE(page.loadingInfos[3].errorDomain(), errorDomain); + loadFinishedSpy.clear(); } QTEST_MAIN(tst_LoadSignals) |