summaryrefslogtreecommitdiffstats
path: root/tests/auto/widgets
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-12-11 10:55:18 +0100
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-12-11 15:52:24 +0100
commit91696b2cb090e5b6147a30465f74d8d37db48615 (patch)
treee73d4adee4f9f2040e8986b8388737d3bf866a5d /tests/auto/widgets
parent1b79f9ccffe468107be5f6c5d3ad42cdcfdef585 (diff)
parentdad48eb41d1b4b4b32c73884a1c29dd6d9408c33 (diff)
Merge remote-tracking branch 'origin/5.15' into dev
Diffstat (limited to 'tests/auto/widgets')
-rw-r--r--tests/auto/widgets/accessibility/tst_accessibility.cpp3
-rw-r--r--tests/auto/widgets/loadsignals/tst_loadsignals.cpp40
-rw-r--r--tests/auto/widgets/qwebenginehistory/tst_qwebenginehistory.cpp3
-rw-r--r--tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp9
-rw-r--r--tests/auto/widgets/qwebengineview/BLACKLIST3
-rw-r--r--tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp225
-rw-r--r--tests/auto/widgets/touchinput/touchinput.pro2
-rw-r--r--tests/auto/widgets/touchinput/tst_touchinput.cpp341
-rw-r--r--tests/auto/widgets/util.h37
-rw-r--r--tests/auto/widgets/widgets.pro3
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
}