diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/auto/quick/dialogs/testhandler.cpp | 2 | ||||
-rw-r--r-- | tests/auto/quick/dialogs/tst_dialogs.cpp | 79 | ||||
-rw-r--r-- | tests/auto/quick/qmltests/data/TestWebEngineView.qml | 6 | ||||
-rw-r--r-- | tests/auto/quick/qmltests/data/test2.html | 2 | ||||
-rw-r--r-- | tests/auto/quick/qmltests/data/tst_newViewRequest.qml | 25 | ||||
-rw-r--r-- | tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp | 9 | ||||
-rw-r--r-- | tests/auto/widgets/qwebengineview/BLACKLIST | 3 | ||||
-rw-r--r-- | tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp | 212 | ||||
-rw-r--r-- | tests/auto/widgets/touchinput/touchinput.pro | 2 | ||||
-rw-r--r-- | tests/auto/widgets/touchinput/tst_touchinput.cpp | 292 | ||||
-rw-r--r-- | tests/auto/widgets/util.h | 37 | ||||
-rw-r--r-- | tests/auto/widgets/widgets.pro | 3 |
12 files changed, 416 insertions, 256 deletions
diff --git a/tests/auto/quick/dialogs/testhandler.cpp b/tests/auto/quick/dialogs/testhandler.cpp index bdd63a547..78a944cc6 100644 --- a/tests/auto/quick/dialogs/testhandler.cpp +++ b/tests/auto/quick/dialogs/testhandler.cpp @@ -30,7 +30,7 @@ TestHandler::TestHandler(QObject *parent) : QObject(parent) { - setObjectName(QStringLiteral("TestListner")); + setObjectName(QStringLiteral("TestListener")); } QObject* TestHandler::request() const diff --git a/tests/auto/quick/dialogs/tst_dialogs.cpp b/tests/auto/quick/dialogs/tst_dialogs.cpp index 8e802a836..4df296bcd 100644 --- a/tests/auto/quick/dialogs/tst_dialogs.cpp +++ b/tests/auto/quick/dialogs/tst_dialogs.cpp @@ -29,22 +29,23 @@ #include "testhandler.h" #include "server.h" #include "util.h" + #include <QtWebEngine/private/qquickwebenginedialogrequests_p.h> #include <QtWebEngine/private/qquickwebenginecontextmenurequest_p.h> #include <QQuickWebEngineProfile> + +#include <QNetworkProxy> #include <QQmlApplicationEngine> #include <QQuickWindow> -#include <QTest> #include <QSignalSpy> -#include <QNetworkProxy> - +#include <QTest> -class tst_Dialogs : public QObject { +class tst_Dialogs : public QObject +{ Q_OBJECT public: tst_Dialogs(){} - private slots: void initTestCase(); void init(); @@ -57,11 +58,11 @@ private slots: void authenticationDialogRequested(); private: - void createDialog(const QLatin1String& dialog, bool &ok); + void createDialog(const QLatin1String &dialog, bool &ok); private: QScopedPointer<QQmlApplicationEngine> m_engine; - QQuickWindow *m_widnow; - TestHandler *m_listner; + QQuickWindow *m_window; + TestHandler *m_listener; }; void tst_Dialogs::initTestCase() @@ -70,10 +71,10 @@ void tst_Dialogs::initTestCase() qmlRegisterType<TestHandler>("io.qt.tester", 1, 0, "TestHandler"); m_engine.reset(new QQmlApplicationEngine()); m_engine->load(QUrl(QStringLiteral("qrc:/WebView.qml"))); - m_widnow = qobject_cast<QQuickWindow*>(m_engine->rootObjects().first()); - Q_ASSERT(m_widnow); - m_listner = m_widnow->findChild<TestHandler*>(QStringLiteral("TestListner")); - Q_ASSERT(m_listner); + m_window = qobject_cast<QQuickWindow*>(m_engine->rootObjects().first()); + Q_ASSERT(m_window); + m_listener = m_window->findChild<TestHandler*>(QStringLiteral("TestListener")); + Q_ASSERT(m_listener); QNetworkProxy proxy; proxy.setType(QNetworkProxy::HttpProxy); @@ -84,29 +85,29 @@ void tst_Dialogs::initTestCase() void tst_Dialogs::init() { - m_listner->setRequest(nullptr); - m_listner->setReady(false); + m_listener->setRequest(nullptr); + m_listener->setReady(false); } -void tst_Dialogs::createDialog(const QLatin1String& dialog, bool &ok) +void tst_Dialogs::createDialog(const QLatin1String &dialog, bool &ok) { QString trigger = QStringLiteral("document.getElementById('buttonOne').onclick = function() {document.getElementById('%1').click()}"); - QSignalSpy dialogSpy(m_listner, &TestHandler::requestChanged); - m_listner->runJavaScript(trigger.arg(dialog)); - QTRY_VERIFY(m_listner->ready()); - QTest::mouseClick(m_widnow, Qt::LeftButton); + QSignalSpy dialogSpy(m_listener, &TestHandler::requestChanged); + m_listener->runJavaScript(trigger.arg(dialog)); + QTRY_VERIFY(m_listener->ready()); + QTest::mouseClick(m_window, Qt::LeftButton); QTRY_COMPARE(dialogSpy.count(), 1); ok = true; } void tst_Dialogs::colorDialogRequested() { - m_listner->load(QUrl("qrc:/index.html")); - QTRY_VERIFY(m_listner->ready()); + m_listener->load(QUrl("qrc:/index.html")); + QTRY_VERIFY(m_listener->ready()); bool ok = false; createDialog(QLatin1String("colorpicker"), ok); if (ok) { - auto dialog = qobject_cast<QQuickWebEngineColorDialogRequest*>(m_listner->request()); + auto *dialog = qobject_cast<QQuickWebEngineColorDialogRequest*>(m_listener->request()); QVERIFY2(dialog, "Incorrect dialog requested"); dialog->dialogReject(); QVERIFY2(dialog->isAccepted(), "Dialog is not accepted"); @@ -116,23 +117,23 @@ void tst_Dialogs::colorDialogRequested() void tst_Dialogs::contextMenuRequested() { - m_listner->load(QUrl("qrc:/index.html")); - QTRY_COMPARE_WITH_TIMEOUT(m_listner->ready(), true, 20000); - QSignalSpy dialogSpy(m_listner, &TestHandler::requestChanged); - QTest::mouseClick(m_widnow, Qt::RightButton); + m_listener->load(QUrl("qrc:/index.html")); + QTRY_COMPARE_WITH_TIMEOUT(m_listener->ready(), true, 20000); + QSignalSpy dialogSpy(m_listener, &TestHandler::requestChanged); + QTest::mouseClick(m_window, Qt::RightButton); QTRY_COMPARE(dialogSpy.count(), 1); - auto dialog = qobject_cast<QQuickWebEngineContextMenuRequest*>(m_listner->request()); + auto dialog = qobject_cast<QQuickWebEngineContextMenuRequest*>(m_listener->request()); QVERIFY2(dialog, "Incorrect dialog requested"); } void tst_Dialogs::fileDialogRequested() { - m_listner->load(QUrl("qrc:/index.html")); - QTRY_VERIFY(m_listner->ready()); + m_listener->load(QUrl("qrc:/index.html")); + QTRY_VERIFY(m_listener->ready()); bool ok = false; createDialog(QLatin1String("filepicker"), ok); if (ok) { - auto dialog = qobject_cast<QQuickWebEngineFileDialogRequest*>(m_listner->request()); + auto dialog = qobject_cast<QQuickWebEngineFileDialogRequest*>(m_listener->request()); QVERIFY2(dialog, "Incorrect dialog requested"); dialog->dialogReject(); QVERIFY2(dialog->isAccepted(), "Dialog is not accepted"); @@ -173,11 +174,11 @@ void tst_Dialogs::authenticationDialogRequested() server.run(); QTRY_VERIFY2(server.isListening(), "Could not setup authentication server"); - QSignalSpy dialogSpy(m_listner, &TestHandler::requestChanged); - m_listner->load(url); + QSignalSpy dialogSpy(m_listener, &TestHandler::requestChanged); + m_listener->load(url); QTRY_COMPARE(dialogSpy.count(), 1); - auto dialog = qobject_cast<QQuickWebEngineAuthenticationDialogRequest*>(m_listner->request()); + auto *dialog = qobject_cast<QQuickWebEngineAuthenticationDialogRequest*>(m_listener->request()); QVERIFY2(dialog, "Incorrect dialog requested"); dialog->dialogReject(); QVERIFY2(dialog->isAccepted(), "Dialog is not accepted"); @@ -214,20 +215,20 @@ void tst_Dialogs::javaScriptDialogRequested() QFETCH(QString, message); QFETCH(QString, defaultText); - m_listner->load(QUrl("qrc:/index.html")); - QTRY_VERIFY(m_listner->ready()); + m_listener->load(QUrl("qrc:/index.html")); + QTRY_VERIFY(m_listener->ready()); - QSignalSpy dialogSpy(m_listner, &TestHandler::requestChanged); - m_listner->runJavaScript(script); + QSignalSpy dialogSpy(m_listener, &TestHandler::requestChanged); + m_listener->runJavaScript(script); QTRY_COMPARE(dialogSpy.count(), 1); - auto dialog = qobject_cast<QQuickWebEngineJavaScriptDialogRequest*>(m_listner->request()); + auto *dialog = qobject_cast<QQuickWebEngineJavaScriptDialogRequest*>(m_listener->request()); QVERIFY2(dialog, "Incorrect dialog requested"); dialog->dialogReject(); QVERIFY2(dialog->isAccepted(), "Dialog is not accepted"); QCOMPARE(dialog->type(), type); QCOMPARE(dialog->message(), message); QCOMPARE(dialog->defaultText(), defaultText); - QTRY_VERIFY(m_listner->ready()); // make sure javascript executes no longer + QTRY_VERIFY(m_listener->ready()); // make sure javascript executes no longer } static QByteArrayList params; diff --git a/tests/auto/quick/qmltests/data/TestWebEngineView.qml b/tests/auto/quick/qmltests/data/TestWebEngineView.qml index 6db076ae8..f2bc09e4b 100644 --- a/tests/auto/quick/qmltests/data/TestWebEngineView.qml +++ b/tests/auto/quick/qmltests/data/TestWebEngineView.qml @@ -85,12 +85,14 @@ WebEngineView { function getElementCenter(element) { var center; - runJavaScript("(function() {" + + testCase.tryVerify(function() { + runJavaScript("(function() {" + " var elem = document.getElementById('" + element + "');" + " var rect = elem.getBoundingClientRect();" + " return { 'x': (rect.left + rect.right) / 2, 'y': (rect.top + rect.bottom) / 2 };" + "})();", function(result) { center = result } ); - testCase.tryVerify(function() { return center !== undefined; }); + return center !== undefined; + }); return center; } diff --git a/tests/auto/quick/qmltests/data/test2.html b/tests/auto/quick/qmltests/data/test2.html index 629c2a063..7a02bf1f2 100644 --- a/tests/auto/quick/qmltests/data/test2.html +++ b/tests/auto/quick/qmltests/data/test2.html @@ -1,6 +1,6 @@ <html> <head><title>Test page with huge link area</title></head> <body> -<a title="A title" href="test1.html"><img width=200 height=200></a> +<a id="link" title="A title" href="test1.html"><img width=200 height=200></a> </body> </html> diff --git a/tests/auto/quick/qmltests/data/tst_newViewRequest.qml b/tests/auto/quick/qmltests/data/tst_newViewRequest.qml index 80389e9f8..08d63d956 100644 --- a/tests/auto/quick/qmltests/data/tst_newViewRequest.qml +++ b/tests/auto/quick/qmltests/data/tst_newViewRequest.qml @@ -38,6 +38,13 @@ TestWebEngineView { property var newViewRequest: null property var dialog: null property string viewType: "" + property var loadRequestArray: [] + + onLoadingChanged: { + loadRequestArray.push({ + "status": loadRequest.status, + }); + } SignalSpy { id: newViewRequestedSpy @@ -81,6 +88,7 @@ TestWebEngineView { newViewRequestedSpy.clear(); newViewRequest = null; viewType = ""; + loadRequestArray = []; } function cleanup() { @@ -163,6 +171,23 @@ TestWebEngineView { } newViewRequestedSpy.clear(); } + + loadRequestArray = []; + compare(loadRequestArray.length, 0); + webEngineView.url = Qt.resolvedUrl("test2.html"); + verify(webEngineView.waitForLoadSucceeded()); + var center = getElementCenter("link"); + mouseClick(webEngineView, center.x, center.y, Qt.LeftButton, Qt.ControlModifier); + tryCompare(newViewRequestedSpy, "count", 1); + compare(newViewRequest.requestedUrl, Qt.resolvedUrl("test1.html")); + compare(newViewRequest.destination, WebEngineView.NewViewInBackgroundTab); + verify(newViewRequest.userInitiated); + if (viewType === "" || viewType === "null") { + compare(loadRequestArray[0].status, WebEngineView.LoadStartedStatus); + compare(loadRequestArray[1].status, WebEngineView.LoadSucceededStatus); + compare(loadRequestArray.length, 2); + } + newViewRequestedSpy.clear(); } } } diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp index 040114258..55e888abf 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp @@ -3458,7 +3458,11 @@ void tst_QWebEnginePage::openLinkInNewPage_data() // the disposition and performing the navigation request normally. QTest::newRow("BlockPopup") << Decision::ReturnNull << Cause::TargetBlank << Effect::Blocked; +#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) + QTest::newRow("IgnoreIntent") << Decision::ReturnNull << Cause::MiddleClick << Effect::Blocked; +#else QTest::newRow("IgnoreIntent") << Decision::ReturnNull << Cause::MiddleClick << Effect::LoadInSelf; +#endif 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; @@ -3535,7 +3539,10 @@ void tst_QWebEnginePage::openLinkInNewPage() switch (effect) { case Effect::Blocked: - // Nothing to test + // Test nothing new loaded + QTest::qWait(500); + QCOMPARE(page1.spy.count(), 0); + QCOMPARE(page2.spy.count(), 0); break; case Effect::LoadInSelf: QTRY_COMPARE(page1.spy.count(), 1); diff --git a/tests/auto/widgets/qwebengineview/BLACKLIST b/tests/auto/widgets/qwebengineview/BLACKLIST index 266f08886..1aff12669 100644 --- a/tests/auto/widgets/qwebengineview/BLACKLIST +++ b/tests/auto/widgets/qwebengineview/BLACKLIST @@ -1,8 +1,5 @@ [microFocusCoordinates] osx -[textSelectionOutOfInputField] -* - [visibilityState3] windows diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp index cb73d7079..2257a6f4e 100644 --- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp +++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp @@ -25,7 +25,6 @@ #include <private/qinputmethod_p.h> #include <qpainter.h> #include <qpagelayout.h> -#include <qpa/qplatforminputcontext.h> #include <qwebengineview.h> #include <qwebenginepage.h> #include <qwebenginesettings.h> @@ -60,44 +59,6 @@ do { \ QCOMPARE((__expr), __expected); \ } while (0) -static QTouchDevice* s_touchDevice = nullptr; - -static QPoint elementCenter(QWebEnginePage *page, const QString &id) -{ - const QString jsCode( - "(function(){" - " var elem = document.getElementById('" + id + "');" - " var rect = elem.getBoundingClientRect();" - " return [(rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2];" - "})()"); - QVariantList rectList = evaluateJavaScriptSync(page, jsCode).toList(); - - if (rectList.count() != 2) { - qWarning("elementCenter failed."); - return QPoint(); - } - - return QPoint(rectList.at(0).toInt(), rectList.at(1).toInt()); -} - -static QRect elementGeometry(QWebEnginePage *page, const QString &id) -{ - const QString jsCode( - "(function() {" - " var elem = document.getElementById('" + id + "');" - " var rect = elem.getBoundingClientRect();" - " return [rect.left, rect.top, rect.right, rect.bottom];" - "})()"); - QVariantList coords = evaluateJavaScriptSync(page, jsCode).toList(); - - if (coords.count() != 4) { - qWarning("elementGeometry faield."); - return QRect(); - } - - return QRect(coords[0].toInt(), coords[1].toInt(), coords[2].toInt(), coords[3].toInt()); -} - QT_BEGIN_NAMESPACE namespace QTest { int Q_TESTLIB_EXPORT defaultMouseDelay(); @@ -167,9 +128,6 @@ private Q_SLOTS: void keyboardEvents(); void keyboardFocusAfterPopup(); void mouseClick(); - void touchTap(); - void touchTapAndHold(); - void touchTapAndHoldCancelled(); void postData(); void inputFieldOverridesShortcuts(); @@ -216,7 +174,6 @@ private Q_SLOTS: // It is only called once. void tst_QWebEngineView::initTestCase() { - s_touchDevice = QTest::createTouchDevice(); } // This will be called after the last test function is executed. @@ -1521,172 +1478,6 @@ void tst_QWebEngineView::mouseClick() QVERIFY(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString().isEmpty()); } -void tst_QWebEngineView::touchTap() -{ -#if defined(Q_OS_MACOS) - QSKIP("Synthetic touch events are not supported on macOS"); -#endif - - QWebEngineView view; - view.show(); - view.resize(200, 200); - QVERIFY(QTest::qWaitForWindowExposed(&view)); - - QSignalSpy loadFinishedSpy(&view, &QWebEngineView::loadFinished); - - view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, false); - view.setHtml("<html><body>" - "<p id='text' style='width: 150px;'>The Qt Company</p>" - "<div id='notext' style='width: 150px; height: 100px; background-color: #f00;'></div>" - "<form><input id='input' width='150px' type='text' value='The Qt Company2' /></form>" - "</body></html>"); - QVERIFY(loadFinishedSpy.wait()); - QVERIFY(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString().isEmpty()); - - auto singleTap = [](QWidget* target, const QPoint& tapCoords) -> void { - QTest::touchEvent(target, s_touchDevice).press(1, tapCoords, target); - QTest::touchEvent(target, s_touchDevice).stationary(1); - QTest::touchEvent(target, s_touchDevice).release(1, tapCoords, target); - }; - - // Single tap on text doesn't trigger a selection - singleTap(view.focusProxy(), elementCenter(view.page(), "text")); - QTRY_VERIFY(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString().isEmpty()); - QTRY_VERIFY(!view.hasSelection()); - - // Single tap inside the input field focuses it without selecting the text - singleTap(view.focusProxy(), elementCenter(view.page(), "input")); - QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input")); - QTRY_VERIFY(!view.hasSelection()); - - // Single tap on the div clears the input field focus - singleTap(view.focusProxy(), elementCenter(view.page(), "notext")); - QTRY_VERIFY(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString().isEmpty()); - - // Double tap on text still doesn't trigger a selection - singleTap(view.focusProxy(), elementCenter(view.page(), "text")); - singleTap(view.focusProxy(), elementCenter(view.page(), "text")); - QTRY_VERIFY(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString().isEmpty()); - QTRY_VERIFY(!view.hasSelection()); - - // Double tap inside the input field focuses it and selects the word under it - singleTap(view.focusProxy(), elementCenter(view.page(), "input")); - singleTap(view.focusProxy(), elementCenter(view.page(), "input")); - QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input")); - QTRY_COMPARE(view.selectedText(), QStringLiteral("Company2")); - - // Double tap outside the input field behaves like a single tap: clears its focus and selection - singleTap(view.focusProxy(), elementCenter(view.page(), "notext")); - singleTap(view.focusProxy(), elementCenter(view.page(), "notext")); - QTRY_VERIFY(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString().isEmpty()); - QTRY_VERIFY(!view.hasSelection()); -} - -void tst_QWebEngineView::touchTapAndHold() -{ -#if defined(Q_OS_MACOS) - QSKIP("Synthetic touch events are not supported on macOS"); -#endif - - QWebEngineView view; - view.show(); - view.resize(200, 200); - QVERIFY(QTest::qWaitForWindowExposed(&view)); - - QSignalSpy loadFinishedSpy(&view, &QWebEngineView::loadFinished); - - view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, false); - view.setHtml("<html><body>" - "<p id='text' style='width: 150px;'>The Qt Company</p>" - "<div id='notext' style='width: 150px; height: 100px; background-color: #f00;'></div>" - "<form><input id='input' width='150px' type='text' value='The Qt Company2' /></form>" - "</body></html>"); - QVERIFY(loadFinishedSpy.wait()); - QVERIFY(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString().isEmpty()); - - auto tapAndHold = [](QWidget* target, const QPoint& tapCoords) -> void { - QTest::touchEvent(target, s_touchDevice).press(1, tapCoords, target); - QTest::touchEvent(target, s_touchDevice).stationary(1); - QTest::qWait(1000); - QTest::touchEvent(target, s_touchDevice).release(1, tapCoords, target); - }; - - // Tap-and-hold on text selects the word under it - tapAndHold(view.focusProxy(), elementCenter(view.page(), "text")); - QTRY_VERIFY(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString().isEmpty()); - QTRY_COMPARE(view.selectedText(), QStringLiteral("Company")); - - // Tap-and-hold inside the input field focuses it and selects the word under it - tapAndHold(view.focusProxy(), elementCenter(view.page(), "input")); - QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input")); - QTRY_COMPARE(view.selectedText(), QStringLiteral("Company2")); - - // Only test the page context menu on Windows, as Linux doesn't handle context menus consistently - // and other non-desktop platforms like Android may not even support context menus at all -#if defined(Q_OS_WIN) - // Tap-and-hold clears the text selection and shows the page's context menu - QVERIFY(QApplication::activePopupWidget() == nullptr); - tapAndHold(view.focusProxy(), elementCenter(view.page(), "notext")); - QTRY_VERIFY(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString().isEmpty()); - QTRY_VERIFY(!view.hasSelection()); - QTRY_VERIFY(QApplication::activePopupWidget() != nullptr); - - QApplication::activePopupWidget()->close(); - QVERIFY(QApplication::activePopupWidget() == nullptr); -#endif -} - -void tst_QWebEngineView::touchTapAndHoldCancelled() -{ -#if defined(Q_OS_MACOS) - QSKIP("Synthetic touch events are not supported on macOS"); -#endif - - QWebEngineView view; - view.show(); - view.resize(200, 200); - QVERIFY(QTest::qWaitForWindowExposed(&view)); - - QSignalSpy loadFinishedSpy(&view, &QWebEngineView::loadFinished); - - view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, false); - view.setHtml("<html><body>" - "<p id='text' style='width: 150px;'>The Qt Company</p>" - "<div id='notext' style='width: 150px; height: 100px; background-color: #f00;'></div>" - "<form><input id='input' width='150px' type='text' value='The Qt Company2' /></form>" - "</body></html>"); - QVERIFY(loadFinishedSpy.wait()); - QVERIFY(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString().isEmpty()); - - auto cancelledTapAndHold = [](QWidget* target, const QPoint& tapCoords) -> void { - QTest::touchEvent(target, s_touchDevice).press(1, tapCoords, target); - QTest::touchEvent(target, s_touchDevice).stationary(1); - QTest::qWait(1000); - QWindowSystemInterface::handleTouchCancelEvent(target->windowHandle(), s_touchDevice); - }; - - // A cancelled tap-and-hold should cancel text selection, but currently doesn't - cancelledTapAndHold(view.focusProxy(), elementCenter(view.page(), "text")); - QEXPECT_FAIL("", "Incorrect Chromium selection behavior when cancelling tap-and-hold on text", Continue); - QTRY_VERIFY_WITH_TIMEOUT(!view.hasSelection(), 100); - - // A cancelled tap-and-hold should cancel input field focusing and selection, but currently doesn't - cancelledTapAndHold(view.focusProxy(), elementCenter(view.page(), "input")); - QEXPECT_FAIL("", "Incorrect Chromium selection behavior when cancelling tap-and-hold on input field", Continue); - QTRY_VERIFY_WITH_TIMEOUT(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString().isEmpty(), 100); - QEXPECT_FAIL("", "Incorrect Chromium focus behavior when cancelling tap-and-hold on input field", Continue); - QTRY_VERIFY_WITH_TIMEOUT(!view.hasSelection(), 100); - - // Only test the page context menu on Windows, as Linux doesn't handle context menus consistently - // and other non-desktop platforms like Android may not even support context menus at all -#if defined(Q_OS_WIN) - // A cancelled tap-and-hold cancels the context menu - QVERIFY(QApplication::activePopupWidget() == nullptr); - cancelledTapAndHold(view.focusProxy(), elementCenter(view.page(), "notext")); - QVERIFY(QApplication::activePopupWidget() == nullptr); -#endif -} - void tst_QWebEngineView::postData() { QMap<QString, QString> postData; @@ -2376,6 +2167,7 @@ void tst_QWebEngineView::textSelectionInInputField() void tst_QWebEngineView::textSelectionOutOfInputField() { QWebEngineView view; + view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true); view.resize(640, 480); view.show(); @@ -2385,6 +2177,7 @@ void tst_QWebEngineView::textSelectionOutOfInputField() " This is a text" "</body></html>"); QVERIFY(loadFinishedSpy.wait()); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QCOMPARE(selectionChangedSpy.count(), 0); QVERIFY(!view.hasSelection()); @@ -2433,6 +2226,7 @@ void tst_QWebEngineView::textSelectionOutOfInputField() " <input type='text' id='input1' value='QtWebEngine' size='50'/>" "</body></html>"); QVERIFY(loadFinishedSpy.wait()); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QCOMPARE(selectionChangedSpy.count(), 0); QVERIFY(!view.hasSelection()); diff --git a/tests/auto/widgets/touchinput/touchinput.pro b/tests/auto/widgets/touchinput/touchinput.pro new file mode 100644 index 000000000..d91c0074b --- /dev/null +++ b/tests/auto/widgets/touchinput/touchinput.pro @@ -0,0 +1,2 @@ +include(../tests.pri) +QT *= gui-private diff --git a/tests/auto/widgets/touchinput/tst_touchinput.cpp b/tests/auto/widgets/touchinput/tst_touchinput.cpp new file mode 100644 index 000000000..3eee6d824 --- /dev/null +++ b/tests/auto/widgets/touchinput/tst_touchinput.cpp @@ -0,0 +1,292 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#include "../util.h" + +#include <QtGui/qpa/qwindowsysteminterface.h> +#include <QSignalSpy> +#include <QTest> +#include <QTouchDevice> +#include <QWebEngineSettings> +#include <QWebEngineView> + +static QTouchDevice* s_touchDevice = nullptr; + +class TouchInputTest : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + void init(); + void cleanup(); + +private Q_SLOTS: + void touchTap(); + void touchTapAndHold(); + void touchTapAndHoldCancelled(); + void scrolling(); + void pinchZoom(); + +private: + QWebEngineView view; + QSignalSpy loadSpy { &view, &QWebEngineView::loadFinished }; + QPoint notextCenter, textCenter, inputCenter; + + QString activeElement() { return evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(); } + + void gestureScroll(bool down) { + auto target = view.focusProxy(); + QPoint p(target->width() / 2, target->height() / 4 * (down ? 3 : 1)); + + QTest::touchEvent(target, s_touchDevice).press(42, p, target); + + for (int i = 0; i < 3; ++i) { + down ? p -= QPoint(5, 15) : p += QPoint(5, 15); + QTest::qWait(100); // too fast and events are recognized as fling gesture + QTest::touchEvent(target, s_touchDevice).move(42, p, target); + } + + QTest::touchEvent(target, s_touchDevice).release(42, p, target); + } + + void gesturePinch(bool zoomIn) { + auto target = view.focusProxy(); + QPoint p(target->width() / 2, target->height() / 2); + auto t1 = p - QPoint(zoomIn ? 50 : 150, 10), t2 = p + QPoint(zoomIn ? 50 : 150, 10); + + QTest::touchEvent(target, s_touchDevice).press(42, t1, target); + QTest::touchEvent(target, s_touchDevice).stationary(42).press(24, t2, target); + + for (int i = 0; i < 3; ++i) { + if (zoomIn) { + t1 -= QPoint(25, 5); + t2 += QPoint(25, 5); + } else { + t1 += QPoint(35, 5); + t2 -= QPoint(35, 5); + } + QTest::qWait(100); // too fast and events are recognized as fling gesture + QTest::touchEvent(target, s_touchDevice).move(24, t1, target).move(42, t2, target); + } + + QTest::touchEvent(target, s_touchDevice).stationary(42).release(24, t2, target); + QTest::touchEvent(target, s_touchDevice).release(42, t1, target); + } + + int getScrollPosition(int *position = nullptr) { + int p = evaluateJavaScriptSync(view.page(), "window.scrollY").toInt(); + return position ? (*position = p) : p; + } + + double getScaleFactor(double *scale = nullptr) { + double s = evaluateJavaScriptSync(view.page(), "window.visualViewport.scale").toDouble(); + return scale ? (*scale = s) : s; + } +}; + +void TouchInputTest::initTestCase() +{ + s_touchDevice = QTest::createTouchDevice(); + + view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, false); + + view.show(); view.resize(480, 320); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + view.setHtml("<html><head><style>.rect { min-width: 240px; min-height: 120px; }</style></head><body>" + "<p id='text' style='width: 150px;'>The Qt Company</p>" + "<div id='notext' style='width: 150px; height: 100px; background-color: #f00;'></div>" + "<form><input id='input' width='150px' type='text' value='The Qt Company2' /></form>" + "<table style='width: 100%; padding: 15px; text-align: center;'>" + "<tr><td>BEFORE</td><td><div class='rect' style='background-color: #00f;'></div></td><td>AFTER</td></tr>" + "<tr><td>BEFORE</td><td><div class='rect' style='background-color: #0f0;'></div></td><td>AFTER</td></tr>" + "<tr><td>BEFORE</td><td><div class='rect' style='background-color: #f00;'></div></td><td>AFTER</td></tr></table>" + "</body></html>"); + QVERIFY(loadSpy.wait() && loadSpy.first().first().toBool()); + + notextCenter = elementCenter(view.page(), "notext"); + textCenter = elementCenter(view.page(), "text"); + inputCenter = elementCenter(view.page(), "input"); +} + +void TouchInputTest::init() +{ + QCOMPARE(activeElement(), QString()); +} + +void TouchInputTest::cleanup() +{ + evaluateJavaScriptSync(view.page(), "if (document.activeElement) document.activeElement.blur()"); + evaluateJavaScriptSync(view.page(), "window.scrollTo(0, 0)"); + QTRY_COMPARE(getScrollPosition(), 0); +} + +void TouchInputTest::touchTap() +{ + auto singleTap = [target = view.focusProxy()] (const QPoint& tapCoords) -> void { + QTest::touchEvent(target, s_touchDevice).press(1, tapCoords, target); + QTest::touchEvent(target, s_touchDevice).stationary(1); + QTest::touchEvent(target, s_touchDevice).release(1, tapCoords, target); + }; + + // Single tap on text doesn't trigger a selection + singleTap(textCenter); + QTRY_COMPARE(activeElement(), QString()); + QTRY_VERIFY(!view.hasSelection()); + + // Single tap inside the input field focuses it without selecting the text + singleTap(inputCenter); + QTRY_COMPARE(activeElement(), QStringLiteral("input")); + QTRY_VERIFY(!view.hasSelection()); + + // Single tap on the div clears the input field focus + singleTap(notextCenter); + QTRY_COMPARE(activeElement(), QString()); + + // Double tap on text still doesn't trigger a selection + singleTap(textCenter); + singleTap(textCenter); + QTRY_COMPARE(activeElement(), QString()); + QTRY_VERIFY(!view.hasSelection()); + + // Double tap inside the input field focuses it and selects the word under it + singleTap(inputCenter); + singleTap(inputCenter); + QTRY_COMPARE(activeElement(), QStringLiteral("input")); + QTRY_COMPARE(view.selectedText(), QStringLiteral("Company2")); + + // Double tap outside the input field behaves like a single tap: clears its focus and selection + singleTap(notextCenter); + singleTap(notextCenter); + QTRY_COMPARE(activeElement(), QString()); + QTRY_VERIFY(!view.hasSelection()); +} + +void TouchInputTest::touchTapAndHold() +{ + auto tapAndHold = [target = view.focusProxy()] (const QPoint& tapCoords) -> void { + QTest::touchEvent(target, s_touchDevice).press(1, tapCoords, target); + QTest::touchEvent(target, s_touchDevice).stationary(1); + QTest::qWait(1000); + QTest::touchEvent(target, s_touchDevice).release(1, tapCoords, target); + }; + + // Tap-and-hold on text selects the word under it + tapAndHold(textCenter); + QTRY_COMPARE(activeElement(), QString()); + QTRY_COMPARE(view.selectedText(), QStringLiteral("Company")); + + // Tap-and-hold inside the input field focuses it and selects the word under it + tapAndHold(inputCenter); + QTRY_COMPARE(activeElement(), QStringLiteral("input")); + QTRY_COMPARE(view.selectedText(), QStringLiteral("Company2")); + + // Only test the page context menu on Windows, as Linux doesn't handle context menus consistently + // and other non-desktop platforms like Android may not even support context menus at all +#if defined(Q_OS_WIN) + // Tap-and-hold clears the text selection and shows the page's context menu + QVERIFY(QApplication::activePopupWidget() == nullptr); + tapAndHold(notextCenter); + QTRY_COMPARE(activeElement(), QString()); + QTRY_VERIFY(!view.hasSelection()); + QTRY_VERIFY(QApplication::activePopupWidget() != nullptr); + + QApplication::activePopupWidget()->close(); + QVERIFY(QApplication::activePopupWidget() == nullptr); +#endif +} + +void TouchInputTest::touchTapAndHoldCancelled() +{ + auto cancelledTapAndHold = [target = view.focusProxy()] (const QPoint& tapCoords) -> void { + QTest::touchEvent(target, s_touchDevice).press(1, tapCoords, target); + QTest::touchEvent(target, s_touchDevice).stationary(1); + QTest::qWait(1000); + QWindowSystemInterface::handleTouchCancelEvent(target->windowHandle(), s_touchDevice); + }; + + // A cancelled tap-and-hold should cancel text selection, but currently doesn't + cancelledTapAndHold(textCenter); + QEXPECT_FAIL("", "Incorrect Chromium selection behavior when cancelling tap-and-hold on text", Continue); + QTRY_VERIFY_WITH_TIMEOUT(!view.hasSelection(), 100); + + // A cancelled tap-and-hold should cancel input field focusing and selection, but currently doesn't + cancelledTapAndHold(inputCenter); + QEXPECT_FAIL("", "Incorrect Chromium selection behavior when cancelling tap-and-hold on input field", Continue); + QTRY_VERIFY_WITH_TIMEOUT(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString().isEmpty(), 100); + QEXPECT_FAIL("", "Incorrect Chromium focus behavior when cancelling tap-and-hold on input field", Continue); + QTRY_VERIFY_WITH_TIMEOUT(!view.hasSelection(), 100); + + // Only test the page context menu on Windows, as Linux doesn't handle context menus consistently + // and other non-desktop platforms like Android may not even support context menus at all +#if defined(Q_OS_WIN) + // A cancelled tap-and-hold cancels the context menu + QVERIFY(QApplication::activePopupWidget() == nullptr); + cancelledTapAndHold(notextCenter); + QVERIFY(QApplication::activePopupWidget() == nullptr); +#endif +} + +void TouchInputTest::scrolling() +{ + int p = getScrollPosition(); + QCOMPARE(p, 0); + + // scroll a bit down... + for (int i = 0; i < 3; ++i) { + gestureScroll(/* down = */true); + int positionBefore = p; + QTRY_VERIFY2(getScrollPosition(&p) > positionBefore, qPrintable(QString("i: %1, position: %2 -> %3").arg(i).arg(positionBefore).arg(p))); + } + + // ... and then scroll page again but in opposite direction + for (int i = 0; i < 3; ++i) { + gestureScroll(/* down = */false); + int positionBefore = p; + QTRY_VERIFY2(getScrollPosition(&p) < positionBefore, qPrintable(QString("i: %1, position: %2 -> %3").arg(i).arg(positionBefore).arg(p))); + } + + QTRY_COMPARE(getScrollPosition(), 0); +} + +void TouchInputTest::pinchZoom() +{ + double scale = getScaleFactor(); + QCOMPARE(scale, 1.0); + + for (int i = 0; i < 3; ++i) { + gesturePinch(/* zoomIn = */true); + QTRY_VERIFY2(getScaleFactor(&scale) > 1.5, qPrintable(QString("i: %1, scale: %2").arg(i).arg(scale))); + gesturePinch(/* zoomIn = */false); + QTRY_COMPARE(getScaleFactor(&scale), 1.0); + } +} + +QTEST_MAIN(TouchInputTest) +#include "tst_touchinput.moc" diff --git a/tests/auto/widgets/util.h b/tests/auto/widgets/util.h index af0b9bf6f..461baf9ac 100644 --- a/tests/auto/widgets/util.h +++ b/tests/auto/widgets/util.h @@ -184,6 +184,43 @@ static inline bool loadSync(QWebEngineView *view, const QUrl &url, bool ok = tru return loadSync(view->page(), url, ok); } +static inline QPoint elementCenter(QWebEnginePage *page, const QString &id) +{ + const QString jsCode( + "(function(){" + " var elem = document.getElementById('" + id + "');" + " var rect = elem.getBoundingClientRect();" + " return [(rect.left + rect.right) / 2, (rect.top + rect.bottom) / 2];" + "})()"); + QVariantList rectList = evaluateJavaScriptSync(page, jsCode).toList(); + + if (rectList.count() != 2) { + qWarning("elementCenter failed."); + return QPoint(); + } + + return QPoint(rectList.at(0).toInt(), rectList.at(1).toInt()); +} + +static inline QRect elementGeometry(QWebEnginePage *page, const QString &id) +{ + const QString jsCode( + "(function() {" + " var elem = document.getElementById('" + id + "');" + " var rect = elem.getBoundingClientRect();" + " return [rect.left, rect.top, rect.right, rect.bottom];" + "})()"); + QVariantList coords = evaluateJavaScriptSync(page, jsCode).toList(); + + if (coords.count() != 4) { + qWarning("elementGeometry faield."); + return QRect(); + } + + return QRect(coords[0].toInt(), coords[1].toInt(), coords[2].toInt(), coords[3].toInt()); +} + + #define W_QSKIP(a, b) QSKIP(a) #define W_QTEST_MAIN(TestObject, params) \ diff --git a/tests/auto/widgets/widgets.pro b/tests/auto/widgets/widgets.pro index 6d65eecb5..2dc1eefcd 100644 --- a/tests/auto/widgets/widgets.pro +++ b/tests/auto/widgets/widgets.pro @@ -22,6 +22,9 @@ SUBDIRS += \ qwebenginesettings \ qwebengineview +# Synthetic touch events are not supported on macOS +!macos: SUBDIRS += touchinput + qtConfig(accessibility) { SUBDIRS += accessibility } |