diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-12-11 10:55:18 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-12-11 15:52:24 +0100 |
commit | 91696b2cb090e5b6147a30465f74d8d37db48615 (patch) | |
tree | e73d4adee4f9f2040e8986b8388737d3bf866a5d /tests/auto/widgets | |
parent | 1b79f9ccffe468107be5f6c5d3ad42cdcfdef585 (diff) | |
parent | dad48eb41d1b4b4b32c73884a1c29dd6d9408c33 (diff) |
Merge remote-tracking branch 'origin/5.15' into dev
Change-Id: Id1a1f4ca84697ba18e2604090e3154295fe1858b
Diffstat (limited to 'tests/auto/widgets')
-rw-r--r-- | tests/auto/widgets/accessibility/tst_accessibility.cpp | 3 | ||||
-rw-r--r-- | tests/auto/widgets/loadsignals/tst_loadsignals.cpp | 40 | ||||
-rw-r--r-- | tests/auto/widgets/qwebenginehistory/tst_qwebenginehistory.cpp | 3 | ||||
-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 | 225 | ||||
-rw-r--r-- | tests/auto/widgets/touchinput/touchinput.pro | 2 | ||||
-rw-r--r-- | tests/auto/widgets/touchinput/tst_touchinput.cpp | 341 | ||||
-rw-r--r-- | tests/auto/widgets/util.h | 37 | ||||
-rw-r--r-- | tests/auto/widgets/widgets.pro | 3 |
10 files changed, 452 insertions, 214 deletions
diff --git a/tests/auto/widgets/accessibility/tst_accessibility.cpp b/tests/auto/widgets/accessibility/tst_accessibility.cpp index e73f7d89b..989ad0ee9 100644 --- a/tests/auto/widgets/accessibility/tst_accessibility.cpp +++ b/tests/auto/widgets/accessibility/tst_accessibility.cpp @@ -160,6 +160,9 @@ void tst_Accessibility::focusChild_data() void tst_Accessibility::focusChild() { +#if QT_VERSION < QT_VERSION_CHECK(5, 14, 1) + QSKIP("Requires newer base Qt"); +#endif auto traverseToWebDocumentAccessibleInterface = [](QAccessibleInterface *iface) -> QAccessibleInterface * { QFETCH(QList<QAccessible::Role>, ancestorRoles); for (int i = 0; i < ancestorRoles.size(); ++i) { diff --git a/tests/auto/widgets/loadsignals/tst_loadsignals.cpp b/tests/auto/widgets/loadsignals/tst_loadsignals.cpp index 143a4a4e5..c1e9013df 100644 --- a/tests/auto/widgets/loadsignals/tst_loadsignals.cpp +++ b/tests/auto/widgets/loadsignals/tst_loadsignals.cpp @@ -52,6 +52,7 @@ private Q_SLOTS: void secondLoadForError_WhenErrorPageEnabled(); void loadAfterInPageNavigation_qtbug66869(); void fileDownloadDoesNotTriggerLoadSignals_qtbug66661(); + void numberOfStartedAndFinishedSignalsIsSame(); private: QWebEngineProfile profile; @@ -243,5 +244,44 @@ void tst_LoadSignals::fileDownloadDoesNotTriggerLoadSignals_qtbug66661() QCOMPARE(loadFinishedSpy.size(), 1); } +void tst_LoadSignals::numberOfStartedAndFinishedSignalsIsSame() { + + HttpServer server; + server.setResourceDirs({ TESTS_SOURCE_DIR "/qwebengineprofile/resources" }); + connect(&server, &HttpServer::newRequest, [] (HttpReqRep *) { + QTest::qWait(250); // just add delay to trigger some progress for every sub resource + }); + QVERIFY(server.start()); + + view.load(server.url("/hedgehog.png")); + QTRY_COMPARE(loadFinishedSpy.size(), 1); + QVERIFY(loadFinishedSpy[0][0].toBool()); + + loadStartedSpy.clear(); + loadFinishedSpy.clear(); + loadProgressSpy.clear(); + + view.page()->setHtml("<html><body>" + "<img src=\"" + server.url("/hedgehog.png").toEncoded() + "\">" + "<form method='GET' name='hiddenform' action='qrc:///resources/page1.html' />" + "<script language='javascript'>document.forms[0].submit();</script>" + "</body></html>"); + + QTRY_COMPARE(loadStartedSpy.size(), 2); + QTRY_COMPARE(loadFinishedSpy.size(), 2); + + QTRY_VERIFY(!loadFinishedSpy[0][0].toBool()); + QTRY_VERIFY(loadFinishedSpy[1][0].toBool()); + + view.page()->setHtml("<html><body>" + "<form method='GET' name='hiddenform' action='qrc:///resources/page1.html' />" + "<script language='javascript'>document.forms[0].submit();</script>" + "</body></html>"); + QTRY_COMPARE(loadStartedSpy.size(), 4); + QTRY_COMPARE(loadFinishedSpy.size(), 4); + QVERIFY(loadFinishedSpy[2][0].toBool()); + QVERIFY(loadFinishedSpy[3][0].toBool()); +} + QTEST_MAIN(tst_LoadSignals) #include "tst_loadsignals.moc" diff --git a/tests/auto/widgets/qwebenginehistory/tst_qwebenginehistory.cpp b/tests/auto/widgets/qwebenginehistory/tst_qwebenginehistory.cpp index bdb486793..72a45379b 100644 --- a/tests/auto/widgets/qwebenginehistory/tst_qwebenginehistory.cpp +++ b/tests/auto/widgets/qwebenginehistory/tst_qwebenginehistory.cpp @@ -320,7 +320,8 @@ void tst_QWebEngineHistory::serialize_2() hist->forward(); QTRY_COMPARE(loadFinishedSpy->count(), 5); hist->forward(); - QTRY_COMPARE(loadFinishedSpy->count(), 6); + // In-page navigation, the last url was the page5.html + QTRY_COMPARE(loadFinishedSpy->count(), 5); QTRY_COMPARE(hist->currentItemIndex(), initialCurrentIndex); } diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp index a7e594d14..41ec977ab 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp @@ -3468,7 +3468,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; @@ -3545,7 +3549,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 87664cef9..1a0f77b78 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 QPointingDevice* 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. @@ -1203,6 +1160,7 @@ void tst_QWebEngineView::doNotBreakLayout() void tst_QWebEngineView::changeLocale() { +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) QStringList errorLines; QUrl url("http://non.existent/"); @@ -1238,6 +1196,7 @@ void tst_QWebEngineView::changeLocale() QTRY_VERIFY(!toPlainTextSync(viewDE.page()).isEmpty()); errorLines = toPlainTextSync(viewDE.page()).split(QRegularExpression("[\r\n]"), Qt::SkipEmptyParts); QCOMPARE(errorLines.first().toUtf8(), QByteArrayLiteral("Die Website ist nicht erreichbar")); +#endif } void tst_QWebEngineView::inputMethodsTextFormat_data() @@ -1283,6 +1242,7 @@ void tst_QWebEngineView::inputMethodsTextFormat() evaluateJavaScriptSync(view.page(), "document.getElementById('input1').focus()"); view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QFETCH(QString, string); QFETCH(int, start); @@ -1520,174 +1480,9 @@ 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->window(), s_touchDevice).press(0, tapCoords, target); - QTest::touchEvent(target->window(), s_touchDevice).stationary(0); - QTest::touchEvent(target->window(), s_touchDevice).release(0, 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(0, tapCoords, target); - QTest::touchEvent(target, s_touchDevice).stationary(0); - QTest::qWait(1000); - QTest::touchEvent(target, s_touchDevice).release(0, 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() { +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) QMap<QString, QString> postData; // use reserved characters to make the test harder to pass postData[QStringLiteral("Spä=m")] = QStringLiteral("ëgg:s"); @@ -1815,6 +1610,7 @@ void tst_QWebEngineView::postData() timeoutGuard.stop(); server.close(); +#endif } void tst_QWebEngineView::inputFieldOverridesShortcuts() @@ -2051,6 +1847,7 @@ void tst_QWebEngineView::inputContextQueryInput() " <input type='text' id='input1' value='' size='50'/>" "</body></html>"); QTRY_COMPARE(loadFinishedSpy.count(), 1); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QCOMPARE(testContext.infos.count(), 0); // Set focus on an input field. @@ -2202,6 +1999,7 @@ void tst_QWebEngineView::inputMethods() " <input type='text' id='input1' style='font-family: serif' value='' maxlength='20' size='50'/>" "</body></html>"); QTRY_COMPARE(loadFinishedSpy.size(), 1); + QVERIFY(QTest::qWaitForWindowExposed(&view)); QPoint textInputCenter = elementCenter(view.page(), "input1"); QTest::mouseClick(view.focusProxy(), Qt::LeftButton, {}, textInputCenter); @@ -2299,6 +2097,7 @@ void tst_QWebEngineView::textSelectionInInputField() " <input type='text' id='input1' value='QtWebEngine' size='50'/>" "</body></html>"); QVERIFY(loadFinishedSpy.wait()); + QVERIFY(QTest::qWaitForWindowExposed(&view)); // Tests for Selection when the Editor is NOT in Composition mode @@ -2372,6 +2171,7 @@ void tst_QWebEngineView::textSelectionInInputField() void tst_QWebEngineView::textSelectionOutOfInputField() { QWebEngineView view; + view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true); view.resize(640, 480); view.show(); @@ -2381,6 +2181,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()); @@ -2429,6 +2230,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()); @@ -2508,6 +2310,7 @@ void tst_QWebEngineView::emptyInputMethodEvent() " <input type='text' id='input1' value='QtWebEngine'/>" "</body></html>"); QVERIFY(loadFinishedSpy.wait()); + QVERIFY(QTest::qWaitForWindowExposed(&view)); evaluateJavaScriptSync(view.page(), "var inputEle = document.getElementById('input1'); inputEle.focus(); inputEle.select();"); QTRY_COMPARE(selectionChangedSpy.count(), 1); @@ -2556,6 +2359,7 @@ void tst_QWebEngineView::imeComposition() " <input type='text' id='input1' value='QtWebEngine inputMethod'/>" "</body></html>"); QVERIFY(loadFinishedSpy.wait()); + QVERIFY(QTest::qWaitForWindowExposed(&view)); evaluateJavaScriptSync(view.page(), "var inputEle = document.getElementById('input1'); inputEle.focus(); inputEle.select();"); QTRY_COMPARE(selectionChangedSpy.count(), 1); @@ -2773,6 +2577,7 @@ void tst_QWebEngineView::newlineInTextarea() " <textarea rows='5' cols='1' id='input1'></textarea>" "</body></html>"); QVERIFY(loadFinishedSpy.wait()); + QVERIFY(QTest::qWaitForWindowExposed(&view)); evaluateJavaScriptSync(view.page(), "var inputEle = document.getElementById('input1'); inputEle.focus(); inputEle.select();"); QTRY_VERIFY(evaluateJavaScriptSync(view.page(), "document.getElementById('input1').value").toString().isEmpty()); @@ -2897,6 +2702,7 @@ void tst_QWebEngineView::imeJSInputEvents() " <pre id='log'></pre>" "</body></html>"); QVERIFY(loadFinishedSpy.wait()); + QVERIFY(QTest::qWaitForWindowExposed(&view)); evaluateJavaScriptSync(view.page(), "document.getElementById('input').focus()"); QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input")); @@ -3019,6 +2825,7 @@ void tst_QWebEngineView::imeCompositionQueryEvent() " <input type='text' id='input1' />" "</body></html>"); QVERIFY(loadFinishedSpy.wait()); + QVERIFY(QTest::qWaitForWindowExposed(&view)); evaluateJavaScriptSync(view.page(), "document.getElementById('input1').focus()"); QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input1")); 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..6f22e8df8 --- /dev/null +++ b/tests/auto/widgets/touchinput/tst_touchinput.cpp @@ -0,0 +1,341 @@ +/**************************************************************************** +** +** 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 <QPointingDevice> +#include <QWebEngineSettings> +#include <QWebEngineView> + +static QPointingDevice* 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_data(); + void pinchZoom(); + void complexSequence(); + +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, bool tapOneByOne = false) { + 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); + + if (tapOneByOne) { + QTest::touchEvent(target, s_touchDevice).press(42, t1, target); + QTest::touchEvent(target, s_touchDevice).stationary(42).press(24, t2, target); + } else { + QTest::touchEvent(target, s_touchDevice).press(42, t1, target).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); + } + + if (tapOneByOne) { + QTest::touchEvent(target, s_touchDevice).stationary(42).release(24, t2, target); + QTest::touchEvent(target, s_touchDevice).release(42, t1, target); + } else { + QTest::touchEvent(target, s_touchDevice).release(42, t1, target).release(24, t2, 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_data() +{ + QTest::addColumn<bool>("tapOneByOne"); + QTest::addRow("sequential") << true; + QTest::addRow("simultaneous") << false; +} + +void TouchInputTest::pinchZoom() +{ + QFETCH(bool, tapOneByOne); + double scale = getScaleFactor(); + QCOMPARE(scale, 1.0); + + for (int i = 0; i < 3; ++i) { + gesturePinch(/* zoomIn = */true, tapOneByOne); + QTRY_VERIFY2(getScaleFactor(&scale) > 1.5, qPrintable(QString("i: %1, scale: %2").arg(i).arg(scale))); + gesturePinch(/* zoomIn = */false, tapOneByOne); + QTRY_COMPARE(getScaleFactor(&scale), 1.0); + } +} + +void TouchInputTest::complexSequence() +{ + auto t = view.focusProxy(); + QPoint pc(view.width() / 2, view.height() / 2), p1 = pc - QPoint(50, 25), p2 = pc + QPoint(50, 25); + + for (int i = 0; i < 4; ++i) { + QTest::touchEvent(t, s_touchDevice).press(42, p1, t); QTest::qWait(50); + QTest::touchEvent(t, s_touchDevice).stationary(42).press(24, p2, t); QTest::qWait(50); + QTest::touchEvent(t, s_touchDevice).release(42, p1, t).release(24, p2, t); + + // for additional variablity add zooming in on even steps and zooming out on odd steps + // MEMO scroll position will always be 0 while viewport scale factor > 1.0, so do zoom in after scroll + bool zoomIn = i % 2 == 0; + + if (!zoomIn) { + gesturePinch(false); + QTRY_COMPARE(getScaleFactor(), 1.0); + } + + int p = getScrollPosition(), positionBefore = p; + gestureScroll(true); + QTRY_VERIFY2_WITH_TIMEOUT(getScrollPosition(&p) > positionBefore, qPrintable(QString("i: %1, position: %2 -> %3").arg(i).arg(positionBefore).arg(p)), 1000); + + if (zoomIn) { + double s = getScaleFactor(), scaleBefore = s; + gesturePinch(true); + QTRY_VERIFY2(getScaleFactor(&s) > scaleBefore, qPrintable(QString("i: %1, scale: %2").arg(i).arg(s))); + } + } +} + +QTEST_MAIN(TouchInputTest) +#include "tst_touchinput.moc" diff --git a/tests/auto/widgets/util.h b/tests/auto/widgets/util.h index e030d1a2f..3be9a91b9 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 947587f5e..4ec9e5d63 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 } |