From 656b7fdd2d972946fe980f4089f5e9d01160dc68 Mon Sep 17 00:00:00 2001 From: Peter Varga Date: Wed, 22 Mar 2017 16:29:46 +0100 Subject: Add auto tests for MultipleMouseClickHelper Qt WebEngine implements a custom handler for multiple mouse clicks. These tests are intended to test whether the mouse click events are properly forwarded to the Chromium's RenderWidgetHost. Custom mouse click test functions have been also added for the custom mouse click handler. Change-Id: Ifdc7d09f5e0f0f76c37e137e5743482bf3fb8abb Reviewed-by: Allan Sandfeld Jensen --- src/webengine/api/qquickwebenginetestsupport.cpp | 69 +++++++++++ src/webengine/api/qquickwebenginetestsupport_p.h | 19 +++ src/webengine/plugin/testsupport/plugin.cpp | 2 + src/webengine/webengine.pro | 2 + tests/auto/quick/qmltests/data/tst_mouseClick.qml | 131 +++++++++++++++++++++ tests/auto/quick/qmltests/qmltests.pro | 1 + .../widgets/qwebengineview/tst_qwebengineview.cpp | 95 +++++++++++++++ 7 files changed, 319 insertions(+) create mode 100644 tests/auto/quick/qmltests/data/tst_mouseClick.qml diff --git a/src/webengine/api/qquickwebenginetestsupport.cpp b/src/webengine/api/qquickwebenginetestsupport.cpp index 41f374766..b3290d3cc 100644 --- a/src/webengine/api/qquickwebenginetestsupport.cpp +++ b/src/webengine/api/qquickwebenginetestsupport.cpp @@ -40,9 +40,15 @@ #include "qquickwebenginetestsupport_p.h" #include "qquickwebengineloadrequest_p.h" +#include +#include QT_BEGIN_NAMESPACE +namespace QTest { + int Q_TESTLIB_EXPORT defaultMouseDelay(); +} + QQuickWebEngineErrorPage::QQuickWebEngineErrorPage() { } @@ -101,9 +107,67 @@ bool QQuickWebEngineTestInputContext::isInputPanelVisible() const } +QQuickWebEngineTestEvent::QQuickWebEngineTestEvent() +{ +} + +bool QQuickWebEngineTestEvent::mouseMultiClick(QObject *item, qreal x, qreal y, int clickCount) +{ + QTEST_ASSERT(item); + + QWindow *view = eventWindow(item); + if (!view) + return false; + + for (int i = 0; i < clickCount; ++i) { + mouseEvent(QMouseEvent::MouseButtonPress, view, item, QPointF(x, y)); + mouseEvent(QMouseEvent::MouseButtonRelease, view, item, QPointF(x, y)); + } + QTest::lastMouseTimestamp += QTest::mouseDoubleClickInterval; + + return true; +} + +QWindow *QQuickWebEngineTestEvent::eventWindow(QObject *item) +{ + QWindow *window = qobject_cast(item); + if (window) + return window; + + QQuickItem *quickItem = qobject_cast(item); + if (quickItem) + return quickItem->window(); + + QQuickItem *testParentItem = qobject_cast(parent()); + if (testParentItem) + return testParentItem->window(); + + return nullptr; +} + +void QQuickWebEngineTestEvent::mouseEvent(QEvent::Type type, QWindow *window, QObject *item, const QPointF &_pos) +{ + QTest::qWait(QTest::defaultMouseDelay()); + QTest::lastMouseTimestamp += QTest::defaultMouseDelay(); + + QPoint pos; + QQuickItem *sgitem = qobject_cast(item); + if (sgitem) + pos = sgitem->mapToScene(_pos).toPoint(); + + QMouseEvent me(type, pos, window->mapFromGlobal(pos), Qt::LeftButton, Qt::LeftButton, 0); + me.setTimestamp(++QTest::lastMouseTimestamp); + + QSpontaneKeyEvent::setSpontaneous(&me); + if (!qApp->notify(window, &me)) + QTest::qWarn("Mouse click event not accepted by receiving window"); +} + + QQuickWebEngineTestSupport::QQuickWebEngineTestSupport() : m_errorPage(new QQuickWebEngineErrorPage) , m_testInputContext(new QQuickWebEngineTestInputContext) + , m_testEvent(new QQuickWebEngineTestEvent) { } @@ -117,6 +181,11 @@ QQuickWebEngineTestInputContext *QQuickWebEngineTestSupport::testInputContext() return m_testInputContext.data(); } +QQuickWebEngineTestEvent * QQuickWebEngineTestSupport::testEvent() const +{ + return m_testEvent.data(); +} + QT_END_NAMESPACE #include "moc_qquickwebenginetestsupport_p.cpp" diff --git a/src/webengine/api/qquickwebenginetestsupport_p.h b/src/webengine/api/qquickwebenginetestsupport_p.h index a84d00307..b601fb47c 100644 --- a/src/webengine/api/qquickwebenginetestsupport_p.h +++ b/src/webengine/api/qquickwebenginetestsupport_p.h @@ -54,12 +54,14 @@ #include #include +#include #include #include QT_BEGIN_NAMESPACE class QQuickWebEngineLoadRequest; +class QWindow; class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineErrorPage : public QObject { Q_OBJECT @@ -92,15 +94,31 @@ private: bool m_visible; }; +class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineTestEvent : public QObject { + Q_OBJECT + +public: + QQuickWebEngineTestEvent(); + +public Q_SLOTS: + bool mouseMultiClick(QObject *item, qreal x, qreal y, int clickCount); + +private: + QWindow *eventWindow(QObject *item = 0); + void mouseEvent(QEvent::Type type, QWindow *window, QObject *item, const QPointF &_pos); +}; + class Q_WEBENGINE_PRIVATE_EXPORT QQuickWebEngineTestSupport : public QObject { Q_OBJECT Q_PROPERTY(QQuickWebEngineErrorPage *errorPage READ errorPage CONSTANT FINAL) Q_PROPERTY(QQuickWebEngineTestInputContext *testInputContext READ testInputContext CONSTANT FINAL) + Q_PROPERTY(QQuickWebEngineTestEvent *testEvent READ testEvent CONSTANT FINAL) public: QQuickWebEngineTestSupport(); QQuickWebEngineErrorPage *errorPage() const; QQuickWebEngineTestInputContext *testInputContext() const; + QQuickWebEngineTestEvent *testEvent() const; Q_SIGNALS: void windowCloseRejected(); @@ -109,6 +127,7 @@ Q_SIGNALS: private: QScopedPointer m_errorPage; QScopedPointer m_testInputContext; + QScopedPointer m_testEvent; }; QT_END_NAMESPACE diff --git a/src/webengine/plugin/testsupport/plugin.cpp b/src/webengine/plugin/testsupport/plugin.cpp index c7eda2405..d5c43a859 100644 --- a/src/webengine/plugin/testsupport/plugin.cpp +++ b/src/webengine/plugin/testsupport/plugin.cpp @@ -60,6 +60,8 @@ public: tr("Cannot create a separate instance of WebEngineErrorPage")); qmlRegisterUncreatableType(uri, 1, 0, "TestInputContext", tr("Cannot create a separate instance of WebEngineErrorPage")); + qmlRegisterUncreatableType(uri, 1, 0, "WebEngineTestEvent", + tr("Cannot create a separate instance of WebEngineTestEvent")); } }; diff --git a/src/webengine/webengine.pro b/src/webengine/webengine.pro index f8df714c0..f4bc65edb 100644 --- a/src/webengine/webengine.pro +++ b/src/webengine/webengine.pro @@ -56,6 +56,8 @@ HEADERS = \ ui_delegates_manager.h isQMLTestSupportApiEnabled() { + QT += testlib + SOURCES += api/qquickwebenginetestsupport.cpp HEADERS += api/qquickwebenginetestsupport_p.h diff --git a/tests/auto/quick/qmltests/data/tst_mouseClick.qml b/tests/auto/quick/qmltests/data/tst_mouseClick.qml new file mode 100644 index 000000000..527102f59 --- /dev/null +++ b/tests/auto/quick/qmltests/data/tst_mouseClick.qml @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtWebEngine 1.4 + +import QtWebEngine.testsupport 1.0 + +TestWebEngineView { + id: webEngineView + width: 200 + height: 200 + + testSupport: WebEngineTestSupport { + function mouseMultiClick(item, x, y, clickCount) { + if (!item) + qtest_fail("No item given to mouseMultiClick", 1); + + if (x === undefined) + x = item.width / 2; + if (y === undefined) + y = item.height / 2; + if (!testEvent.mouseMultiClick(item, x, y, clickCount)) + qtest_fail("window not shown", 2); + } + + function mouseDoubleClick(item, x, y) { + mouseMultiClick(item, x, y, 2); + } + + function mouseTripleClick(item, x, y) { + mouseMultiClick(item, x, y, 3); + } + } + + + TestCase { + name: "WebEngineViewMouseClick" + when: windowShown + + function getElementCenter(element) { + var center; + 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 } ); + tryVerify(function() { return center != undefined; }); + return center; + } + + function getTextSelection() { + var textSelection; + runJavaScript("window.getSelection().toString()", function(result) { textSelection = result }); + tryVerify(function() { return textSelection != undefined; }); + return textSelection; + } + + function test_singleClick() { + webEngineView.settings.focusOnNavigationEnabled = false; + webEngineView.loadHtml("" + + "
" + + ""); + verify(webEngineView.waitForLoadSucceeded()); + verify(!getActiveElementId()); + + var center = getElementCenter("input"); + mouseClick(webEngineView, center.x, center.y); + verifyElementHasFocus("input"); + compare(getTextSelection(), ""); + } + + function test_doubleClick() { + webEngineView.settings.focusOnNavigationEnabled = true; + webEngineView.loadHtml("" + + "
" + + ""); + verify(webEngineView.waitForLoadSucceeded()); + + var center = getElementCenter("input"); + webEngineView.testSupport.mouseDoubleClick(webEngineView, center.x, center.y); + verifyElementHasFocus("input"); + tryVerify(function() { return getTextSelection() == "Company" }); + + mouseClick(webEngineView, center.x, center.y); + tryVerify(function() { return getTextSelection() == "" }); + } + + function test_tripleClick() { + webEngineView.settings.focusOnNavigationEnabled = true; + webEngineView.loadHtml("" + + "
" + + ""); + verify(webEngineView.waitForLoadSucceeded()); + + var center = getElementCenter("input"); + webEngineView.testSupport.mouseTripleClick(webEngineView, center.x, center.y); + verifyElementHasFocus("input"); + tryVerify(function() { return getTextSelection() == "The Qt Company" }); + + mouseClick(webEngineView, center.x, center.y); + tryVerify(function() { return getTextSelection() == "" }); + } + } +} diff --git a/tests/auto/quick/qmltests/qmltests.pro b/tests/auto/quick/qmltests/qmltests.pro index 39b9d0151..ceb246dc0 100644 --- a/tests/auto/quick/qmltests/qmltests.pro +++ b/tests/auto/quick/qmltests/qmltests.pro @@ -61,6 +61,7 @@ OTHER_FILES += \ $$PWD/data/tst_loadProgress.qml \ $$PWD/data/tst_loadRecursionCrash.qml \ $$PWD/data/tst_loadUrl.qml \ + $$PWD/data/tst_mouseClick.qml \ $$PWD/data/tst_navigationHistory.qml \ $$PWD/data/tst_navigationRequested.qml \ $$PWD/data/tst_newViewRequest.qml \ diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp index 2ed461e57..5fe02ce5d 100644 --- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp +++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp @@ -92,6 +92,30 @@ static QRect elementGeometry(QWebEnginePage *page, const QString &id) return QRect(coords[0].toInt(), coords[1].toInt(), coords[2].toInt(), coords[3].toInt()); } +QT_BEGIN_NAMESPACE +namespace QTest { + int Q_TESTLIB_EXPORT defaultMouseDelay(); + + static void mouseEvent(QEvent::Type type, QWidget *widget, const QPoint &pos) + { + QTest::qWait(QTest::defaultMouseDelay()); + lastMouseTimestamp += QTest::defaultMouseDelay(); + QMouseEvent me(type, pos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); + me.setTimestamp(++lastMouseTimestamp); + QSpontaneKeyEvent::setSpontaneous(&me); + qApp->sendEvent(widget, &me); + } + + static void mouseMultiClick(QWidget *widget, const QPoint pos, int clickCount) + { + for (int i = 0; i < clickCount; ++i) { + mouseEvent(QMouseEvent::MouseButtonPress, widget, pos); + mouseEvent(QMouseEvent::MouseButtonRelease, widget, pos); + } + lastMouseTimestamp += mouseDoubleClickInterval; + } +} +QT_END_NAMESPACE class tst_QWebEngineView : public QObject { @@ -133,6 +157,7 @@ private Q_SLOTS: void inputMethodsTextFormat(); void keyboardEvents(); void keyboardFocusAfterPopup(); + void mouseClick(); void postData(); void inputFieldOverridesShortcuts(); @@ -1222,6 +1247,76 @@ void tst_QWebEngineView::keyboardFocusAfterPopup() QTRY_COMPARE(evaluateJavaScriptSync(webView->page(), "document.getElementById('input1').value").toString(), QStringLiteral("x")); } +void tst_QWebEngineView::mouseClick() +{ + QWebEngineView view; + view.show(); + view.resize(200, 200); + QTest::qWaitForWindowExposed(&view); + + QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool))); + QSignalSpy selectionChangedSpy(&view, SIGNAL(selectionChanged())); + QPoint textInputCenter; + + // Single Click + view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, false); + selectionChangedSpy.clear(); + + view.setHtml("" + "
" + ""); + QVERIFY(loadFinishedSpy.wait()); + + QVERIFY(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString().isEmpty()); + textInputCenter = elementCenter(view.page(), "input"); + QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, textInputCenter); + QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input")); + QCOMPARE(selectionChangedSpy.count(), 0); + QVERIFY(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString().isEmpty()); + + // Double click + view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true); + selectionChangedSpy.clear(); + + view.setHtml("" + "
" + ""); + QVERIFY(loadFinishedSpy.wait()); + + textInputCenter = elementCenter(view.page(), "input"); + QTest::mouseMultiClick(view.focusProxy(), textInputCenter, 2); + QVERIFY(selectionChangedSpy.wait()); + QCOMPARE(selectionChangedSpy.count(), 1); + QCOMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input")); + QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QStringLiteral("Company")); + + QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, textInputCenter); + QVERIFY(selectionChangedSpy.wait()); + QCOMPARE(selectionChangedSpy.count(), 2); + QVERIFY(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString().isEmpty()); + + // Triple click + view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true); + selectionChangedSpy.clear(); + + view.setHtml("" + "
" + ""); + QVERIFY(loadFinishedSpy.wait()); + + textInputCenter = elementCenter(view.page(), "input"); + QTest::mouseMultiClick(view.focusProxy(), textInputCenter, 3); + QVERIFY(selectionChangedSpy.wait()); + QTRY_COMPARE(selectionChangedSpy.count(), 2); + QCOMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input")); + QCOMPARE(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString(), QStringLiteral("The Qt Company")); + + QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, textInputCenter); + QVERIFY(selectionChangedSpy.wait()); + QCOMPARE(selectionChangedSpy.count(), 3); + QVERIFY(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString().isEmpty()); +} + void tst_QWebEngineView::postData() { QMap postData; -- cgit v1.2.3