diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-05-14 10:49:00 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-05-15 17:26:19 +0200 |
commit | bc6df3888128e3a0e0d4e2f8a69970ac36d8abe7 (patch) | |
tree | 2cb49ad5fffa0f2011b3d98faa363f105135901e /tests/auto | |
parent | 10e66c6dd0b8a8dd17252d6408c13b689fac6995 (diff) | |
parent | 585da6f74012bd09e8a873080e368cff99c97cbf (diff) |
Merge remote-tracking branch 'origin/5.15' into dev
Conflicts:
src/pdf/quick/qquickpdfselection_p.h
Change-Id: I6eec37a01347c2d47cbfc1114326dfc6b58719ff
Diffstat (limited to 'tests/auto')
-rw-r--r-- | tests/auto/quick/qmltests/data/tst_audioMuted.qml | 63 | ||||
-rw-r--r-- | tests/auto/quick/qmltests/qmltests.pro | 1 | ||||
-rw-r--r-- | tests/auto/widgets/proxy/proxy_server.cpp | 18 | ||||
-rw-r--r-- | tests/auto/widgets/proxy/proxy_server.h | 7 | ||||
-rw-r--r-- | tests/auto/widgets/proxy/tst_proxy.cpp | 35 | ||||
-rw-r--r-- | tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp | 190 | ||||
-rw-r--r-- | tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp | 22 | ||||
-rw-r--r-- | tests/auto/widgets/util.h | 13 |
8 files changed, 327 insertions, 22 deletions
diff --git a/tests/auto/quick/qmltests/data/tst_audioMuted.qml b/tests/auto/quick/qmltests/data/tst_audioMuted.qml new file mode 100644 index 000000000..c626d07a0 --- /dev/null +++ b/tests/auto/quick/qmltests/data/tst_audioMuted.qml @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2020 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$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtWebEngine 1.4 + +TestWebEngineView { + id: view + width: 400 + height: 400 + + SignalSpy { + id: spy + target: view + signalName: "audioMutedChanged" + } + + TestCase { + id: test + name: "WebEngineViewAudioMuted" + + function test_audioMuted() { + compare(view.audioMuted, false); + view.audioMuted = true; + view.url = "about:blank"; + verify(view.waitForLoadSucceeded()); + compare(view.audioMuted, true); + compare(spy.count, 1); + compare(spy.signalArguments[0][0], true); + view.audioMuted = false; + compare(view.audioMuted, false); + compare(spy.count, 2); + compare(spy.signalArguments[1][0], false); + } + } +} + diff --git a/tests/auto/quick/qmltests/qmltests.pro b/tests/auto/quick/qmltests/qmltests.pro index 0b3ff7c7e..5c57f7ad9 100644 --- a/tests/auto/quick/qmltests/qmltests.pro +++ b/tests/auto/quick/qmltests/qmltests.pro @@ -31,6 +31,7 @@ OTHER_FILES += \ $$PWD/data/titleupdate.js \ $$PWD/data/tst_action.qml \ $$PWD/data/tst_activeFocusOnPress.qml \ + $$PWD/data/tst_audioMuted.qml \ $$PWD/data/tst_contextMenu.qml \ $$PWD/data/tst_desktopBehaviorLoadHtml.qml \ $$PWD/data/tst_download.qml \ diff --git a/tests/auto/widgets/proxy/proxy_server.cpp b/tests/auto/widgets/proxy/proxy_server.cpp index 55f014914..3bf915609 100644 --- a/tests/auto/widgets/proxy/proxy_server.cpp +++ b/tests/auto/widgets/proxy/proxy_server.cpp @@ -42,8 +42,16 @@ void ProxyServer::setCredentials(const QByteArray &user, const QByteArray passwo m_auth.append(QChar(':')); m_auth.append(password); m_auth = m_auth.toBase64(); + m_authenticate = true; } +void ProxyServer::setCookie(const QByteArray &cookie) +{ + m_cookie.append(QByteArrayLiteral("Cookie: ")); + m_cookie.append(cookie); +} + + bool ProxyServer::isListening() { return m_server.isListening(); @@ -75,7 +83,7 @@ void ProxyServer::handleReadReady() if (!m_data.endsWith("\r\n\r\n")) return; - if (!m_data.contains(QByteArrayLiteral("Proxy-Authorization: Basic"))) { + if (m_authenticate && !m_data.contains(QByteArrayLiteral("Proxy-Authorization: Basic"))) { socket->write("HTTP/1.1 407 Proxy Authentication Required\nProxy-Authenticate: " "Basic realm=\"Proxy requires authentication\"\r\n" "content-length: 0\r\n" @@ -83,8 +91,12 @@ void ProxyServer::handleReadReady() return; } - if (m_data.contains(m_auth)) { - emit success(); + if (m_authenticate && m_data.contains(m_auth)) { + emit authenticationSuccess(); + } + + if (m_data.contains(m_cookie)) { + emit cookieMatch(); } m_data.clear(); } diff --git a/tests/auto/widgets/proxy/proxy_server.h b/tests/auto/widgets/proxy/proxy_server.h index cb7c30600..7bc7b100b 100644 --- a/tests/auto/widgets/proxy/proxy_server.h +++ b/tests/auto/widgets/proxy/proxy_server.h @@ -39,6 +39,7 @@ class ProxyServer : public QObject public: explicit ProxyServer(QObject *parent = nullptr); void setCredentials(const QByteArray &user, const QByteArray password); + void setCookie(const QByteArray &cookie); bool isListening(); public slots: @@ -49,11 +50,15 @@ private slots: void handleReadReady(); signals: - void success(); + void authenticationSuccess(); + void cookieMatch(); + private: QByteArray m_data; QTcpServer m_server; QByteArray m_auth; + QByteArray m_cookie; + bool m_authenticate = false; }; #endif // PROXY_SERVER_H diff --git a/tests/auto/widgets/proxy/tst_proxy.cpp b/tests/auto/widgets/proxy/tst_proxy.cpp index 5f5dec016..c3e3c88a4 100644 --- a/tests/auto/widgets/proxy/tst_proxy.cpp +++ b/tests/auto/widgets/proxy/tst_proxy.cpp @@ -32,6 +32,17 @@ #include <QNetworkProxy> #include <QWebEnginePage> #include <QWebEngineView> +#include <QWebEngineUrlRequestInterceptor> + + +struct Interceptor : public QWebEngineUrlRequestInterceptor +{ + Interceptor(const QByteArray cookie):m_cookie(cookie){}; + void interceptRequest(QWebEngineUrlRequestInfo &info) override { + info.setHttpHeader(QByteArray("Cookie"), m_cookie); + }; + QByteArray m_cookie; +}; class tst_Proxy : public QObject { @@ -41,8 +52,10 @@ public: private slots: void proxyAuthentication(); + void forwardCookie(); }; + void tst_Proxy::proxyAuthentication() { QByteArray user(QByteArrayLiteral("test")); @@ -59,11 +72,31 @@ void tst_Proxy::proxyAuthentication() server.run(); QTRY_VERIFY2(server.isListening(), "Could not setup authentication server"); QWebEnginePage page; - QSignalSpy successSpy(&server, &ProxyServer::success); + QSignalSpy successSpy(&server, &ProxyServer::authenticationSuccess); page.load(QUrl("http://www.qt.io")); QTRY_VERIFY2(successSpy.count() > 0, "Could not get authentication token"); } +void tst_Proxy::forwardCookie() +{ + QNetworkProxy proxy; + proxy.setType(QNetworkProxy::HttpProxy); + proxy.setHostName("localhost"); + proxy.setPort(5555); + QNetworkProxy::setApplicationProxy(proxy); + ProxyServer server; + QByteArray cookie("foo=bar; sessionToken=123"); + server.setCookie(cookie); + server.run(); + QTRY_VERIFY2(server.isListening(), "Could not setup proxy server"); + Interceptor interceptor(cookie); + QWebEnginePage page; + page.setUrlRequestInterceptor(&interceptor); + QSignalSpy cookieSpy(&server, &ProxyServer::cookieMatch); + page.load(QUrl("http://www.qt.io")); + QTRY_VERIFY2(cookieSpy.count() > 0, "Could not get cookie"); +} + #include "tst_proxy.moc" QTEST_MAIN(tst_Proxy) diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp index 098656390..92ee791db 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp @@ -197,6 +197,8 @@ private Q_SLOTS: void dataURLFragment(); void devTools(); void openLinkInDifferentProfile(); + void openLinkInNewPage_data(); + void openLinkInNewPage(); void triggerActionWithoutMenu(); void dynamicFrame(); @@ -204,6 +206,7 @@ private Q_SLOTS: void notificationRequest(); void sendNotification(); void contentsSize(); + void notificationPermission(); void setLifecycleState(); void setVisible(); @@ -227,6 +230,7 @@ private Q_SLOTS: void renderProcessCrashed(); void renderProcessPid(); void backgroundColor(); + void audioMuted(); private: static QPoint elementCenter(QWebEnginePage *page, const QString &id); @@ -3378,6 +3382,162 @@ void tst_QWebEnginePage::openLinkInDifferentProfile() QVERIFY(spy2.takeFirst().value(0).toBool()); } +// What does createWindow do? +enum class OpenLinkInNewPageDecision { + // Returns nullptr, + ReturnNull, + // Returns this, + ReturnSelf, + // Returns page != this + ReturnOther, +}; + +// What causes createWindow to be called? +enum class OpenLinkInNewPageCause { + // User clicks on a link with target=_blank. + TargetBlank, + // User clicks with MiddleButton. + MiddleClick, +}; + +// What happens after createWindow? +enum class OpenLinkInNewPageEffect { + // The navigation request disappears into the ether. + Blocked, + // The navigation request becomes a navigation in the original page. + LoadInSelf, + // The navigation request becomes a navigation in a different page. + LoadInOther, +}; + +Q_DECLARE_METATYPE(OpenLinkInNewPageCause) +Q_DECLARE_METATYPE(OpenLinkInNewPageDecision) +Q_DECLARE_METATYPE(OpenLinkInNewPageEffect) + +void tst_QWebEnginePage::openLinkInNewPage_data() +{ + using Decision = OpenLinkInNewPageDecision; + using Cause = OpenLinkInNewPageCause; + using Effect = OpenLinkInNewPageEffect; + + QTest::addColumn<Decision>("decision"); + QTest::addColumn<Cause>("cause"); + QTest::addColumn<Effect>("effect"); + + // Note that the meaning of returning nullptr from createWindow is not + // consistent between the TargetBlank and MiddleClick scenarios. + // + // With TargetBlank, the open-in-new-page disposition comes from the HTML + // target attribute; something the user is probably not aware of. Returning + // nullptr is interpreted as a decision by the app to block an unwanted + // popup. + // + // With MiddleClick, the open-in-new-page disposition comes from the user's + // explicit intent. Returning nullptr is then interpreted as a failure by + // the app to fulfill this intent, which we try to compensate by ignoring + // the disposition and performing the navigation request normally. + + QTest::newRow("BlockPopup") << Decision::ReturnNull << Cause::TargetBlank << Effect::Blocked; + QTest::newRow("IgnoreIntent") << Decision::ReturnNull << Cause::MiddleClick << Effect::LoadInSelf; + QTest::newRow("OverridePopup") << Decision::ReturnSelf << Cause::TargetBlank << Effect::LoadInSelf; + QTest::newRow("OverrideIntent") << Decision::ReturnSelf << Cause::MiddleClick << Effect::LoadInSelf; + QTest::newRow("AcceptPopup") << Decision::ReturnOther << Cause::TargetBlank << Effect::LoadInOther; + QTest::newRow("AcceptIntent") << Decision::ReturnOther << Cause::MiddleClick << Effect::LoadInOther; +} + +void tst_QWebEnginePage::openLinkInNewPage() +{ + using Decision = OpenLinkInNewPageDecision; + using Cause = OpenLinkInNewPageCause; + using Effect = OpenLinkInNewPageEffect; + + class Page : public QWebEnginePage + { + public: + Page *targetPage = nullptr; + QSignalSpy spy{this, &QWebEnginePage::loadFinished}; + Page(QWebEngineProfile *profile) : QWebEnginePage(profile) {} + private: + QWebEnginePage *createWindow(WebWindowType) override { return targetPage; } + }; + + class View : public QWebEngineView + { + public: + View(Page *page) + { + resize(500, 500); + setPage(page); + } + }; + + QFETCH(Decision, decision); + QFETCH(Cause, cause); + QFETCH(Effect, effect); + + QWebEngineProfile profile; + Page page1(&profile); + Page page2(&profile); + View view1(&page1); + View view2(&page2); + + view1.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view1)); + + page1.setHtml("<html><body>" + "<a id='link' href='data:,hello' target='_blank'>link</a>" + "</body></html>"); + QTRY_COMPARE(page1.spy.count(), 1); + QVERIFY(page1.spy.takeFirst().value(0).toBool()); + + switch (decision) { + case Decision::ReturnNull: + page1.targetPage = nullptr; + break; + case Decision::ReturnSelf: + page1.targetPage = &page1; + break; + case Decision::ReturnOther: + page1.targetPage = &page2; + break; + } + + Qt::MouseButton button; + switch (cause) { + case Cause::TargetBlank: + button = Qt::LeftButton; + break; + case Cause::MiddleClick: + button = Qt::MiddleButton; + break; + } + QTest::mouseClick(view1.focusProxy(), button, {}, elementCenter(&page1, "link")); + + switch (effect) { + case Effect::Blocked: + // Nothing to test + break; + case Effect::LoadInSelf: + QTRY_COMPARE(page1.spy.count(), 1); + QVERIFY(page1.spy.takeFirst().value(0).toBool()); + QCOMPARE(page2.spy.count(), 0); + if (decision == Decision::ReturnSelf && cause == Cause::TargetBlank) + // History was discarded due to AddNewContents + QCOMPARE(page1.history()->count(), 1); + else + QCOMPARE(page1.history()->count(), 2); + QCOMPARE(page2.history()->count(), 0); + break; + case Effect::LoadInOther: + QTRY_COMPARE(page2.spy.count(), 1); + QVERIFY(page2.spy.takeFirst().value(0).toBool()); + QCOMPARE(page1.spy.count(), 0); + QCOMPARE(page1.history()->count(), 1); + QCOMPARE(page2.history()->count(), 1); + break; + } +} + void tst_QWebEnginePage::triggerActionWithoutMenu() { // Calling triggerAction should not crash even when for @@ -3455,6 +3615,18 @@ void tst_QWebEnginePage::notificationRequest() QCOMPARE(page.getPermission(), permission); } +void tst_QWebEnginePage::notificationPermission() +{ + QWebEngineProfile otr; + QWebEnginePage page(&otr, nullptr); + QSignalSpy spy(&page, &QWebEnginePage::loadFinished); + page.setHtml(QString("<html><body>Test</body></html>"), QUrl("https://www.example.com")); + QTRY_COMPARE(spy.count(), 1); + QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("Notification.permission")), QLatin1String("default")); + page.setFeaturePermission(QUrl("https://www.example.com"), QWebEnginePage::Notifications, QWebEnginePage::PermissionGrantedByUser); + QTRY_COMPARE(evaluateJavaScriptSync(&page, QStringLiteral("Notification.permission")), QLatin1String("granted")); +} + void tst_QWebEnginePage::sendNotification() { NotificationPage page(QWebEnginePage::PermissionGrantedByUser); @@ -4432,6 +4604,24 @@ void tst_QWebEnginePage::backgroundColor() QTRY_COMPARE(view.grab().toImage().pixelColor(center), Qt::green); } +void tst_QWebEnginePage::audioMuted() +{ + QWebEngineProfile profile; + QWebEnginePage page(&profile); + QSignalSpy spy(&page, &QWebEnginePage::audioMutedChanged); + + QCOMPARE(page.isAudioMuted(), false); + page.setAudioMuted(true); + loadSync(&page, QUrl("about:blank")); + QCOMPARE(page.isAudioMuted(), true); + QCOMPARE(spy.count(), 1); + QCOMPARE(spy[0][0], QVariant(true)); + page.setAudioMuted(false); + QCOMPARE(page.isAudioMuted(), false); + QCOMPARE(spy.count(), 2); + QCOMPARE(spy[1][0], QVariant(false)); +} + static QByteArrayList params = {QByteArrayLiteral("--use-fake-device-for-media-stream")}; W_QTEST_MAIN(tst_QWebEnginePage, params) diff --git a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp index 6350c8510..00d4bae5a 100644 --- a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp +++ b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp @@ -222,18 +222,6 @@ private: } }; -static bool loadSync(QWebEnginePage *page, const QUrl &url, bool ok = true) -{ - QSignalSpy spy(page, &QWebEnginePage::loadFinished); - page->load(url); - return (!spy.empty() || spy.wait(20000)) && (spy.front().value(0).toBool() == ok); -} - -static bool loadSync(QWebEngineView *view, const QUrl &url, bool ok = true) -{ - return loadSync(view->page(), url, ok); -} - void tst_QWebEngineProfile::clearDataFromCache() { TestServer server; @@ -258,7 +246,7 @@ void tst_QWebEngineProfile::clearDataFromCache() QTest::qWait(1000); QVERIFY(sizeBeforeClear > totalSize(cacheDir)); - QVERIFY(server.stop()); + (void)server.stop(); } void tst_QWebEngineProfile::disableCache() @@ -283,7 +271,7 @@ void tst_QWebEngineProfile::disableCache() QVERIFY(loadSync(&page, server.url("/hedgehog.html"))); QVERIFY(cacheDir.exists("Cache")); - QVERIFY(server.stop()); + (void)server.stop(); } class RedirectingUrlSchemeHandler : public QWebEngineUrlSchemeHandler @@ -876,7 +864,7 @@ void tst_QWebEngineProfile::changePersistentPath() QVERIFY(loadSync(&page, server.url("/hedgehog.html"))); QVERIFY(dataDir2.exists()); - QVERIFY(server.stop()); + (void)server.stop(); } void tst_QWebEngineProfile::changeHttpUserAgent() @@ -949,7 +937,7 @@ void tst_QWebEngineProfile::changeUseForGlobalCertificateVerification() page.reset(new QWebEnginePage(&profile)); QVERIFY(loadSync(page.get(), server.url("/hedgehog.html"))); // Don't check for error: there can be disconnects during GET hedgehog.png. - server.stop(); + (void)server.stop(); } void tst_QWebEngineProfile::changePersistentCookiesPolicy() @@ -973,7 +961,7 @@ void tst_QWebEngineProfile::changePersistentCookiesPolicy() QVERIFY(loadSync(&page, server.url("/hedgehog.html"))); QVERIFY(dataDir.exists("Cookies")); - QVERIFY(server.stop()); + (void)server.stop(); } class InitiatorSpy : public QWebEngineUrlSchemeHandler diff --git a/tests/auto/widgets/util.h b/tests/auto/widgets/util.h index ca03c5833..cb58f4243 100644 --- a/tests/auto/widgets/util.h +++ b/tests/auto/widgets/util.h @@ -36,6 +36,7 @@ #include <QSignalSpy> #include <QTimer> #include <qwebenginepage.h> +#include <qwebengineview.h> #if !defined(TESTS_SOURCE_DIR) #define TESTS_SOURCE_DIR "" @@ -160,6 +161,18 @@ static inline QUrl baseUrlSync(QWebEnginePage *page) return spy.waitForResult().toUrl(); } +static inline bool loadSync(QWebEnginePage *page, const QUrl &url, bool ok = true) +{ + QSignalSpy spy(page, &QWebEnginePage::loadFinished); + page->load(url); + return (!spy.empty() || spy.wait(20000)) && (spy.front().value(0).toBool() == ok); +} + +static inline bool loadSync(QWebEngineView *view, const QUrl &url, bool ok = true) +{ + return loadSync(view->page(), url, ok); +} + #define W_QSKIP(a, b) QSKIP(a) #define W_QTEST_MAIN(TestObject, params) \ |