From 8d11edc26114e277285b4c88a6f2fa7924bd7e35 Mon Sep 17 00:00:00 2001 From: Kirill Burtsev Date: Fri, 16 Oct 2020 20:01:06 +0200 Subject: Move touch input tests to separate testcase Since it's very distinct area and not very specific to view. Aggregate test code in separate subfolder and simplify by removing duplications. Change-Id: Iaaa28612b074245dc548553099e7e17a70327a37 Reviewed-by: Allan Sandfeld Jensen --- .../widgets/qwebengineview/tst_qwebengineview.cpp | 209 --------------------- tests/auto/widgets/touchinput/touchinput.pro | 2 + tests/auto/widgets/touchinput/tst_touchinput.cpp | 200 ++++++++++++++++++++ tests/auto/widgets/util.h | 37 ++++ tests/auto/widgets/widgets.pro | 3 + 5 files changed, 242 insertions(+), 209 deletions(-) create mode 100644 tests/auto/widgets/touchinput/touchinput.pro create mode 100644 tests/auto/widgets/touchinput/tst_touchinput.cpp diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp index 04e4b3ada..0b7356034 100644 --- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp +++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -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. @@ -1520,172 +1477,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("" - "

The Qt Company

" - "
" - "
" - ""); - 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("" - "

The Qt Company

" - "
" - "
" - ""); - 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("" - "

The Qt Company

" - "
" - "
" - ""); - 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 postData; 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..fb5272dbe --- /dev/null +++ b/tests/auto/widgets/touchinput/tst_touchinput.cpp @@ -0,0 +1,200 @@ +/**************************************************************************** +** +** 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 +#include +#include +#include +#include +#include + +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(); + +private: + QWebEngineView view; + QSignalSpy loadSpy { &view, &QWebEngineView::loadFinished }; + QPoint notextCenter, textCenter, inputCenter; + + QString activeElement() { return evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(); } +}; + +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("" + "

The Qt Company

" + "
" + "
" + ""); + 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()"); +} + +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 +} + +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 } -- cgit v1.2.3