diff options
Diffstat (limited to 'tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp')
-rw-r--r-- | tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp | 268 |
1 files changed, 255 insertions, 13 deletions
diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp index 827ac2757..2862be1dd 100644 --- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp +++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp @@ -60,6 +60,8 @@ do { \ QCOMPARE((__expr), __expected); \ } while (0) +static QTouchDevice* s_touchDevice = nullptr; + static QPoint elementCenter(QWebEnginePage *page, const QString &id) { const QString jsCode( @@ -165,6 +167,9 @@ private Q_SLOTS: void keyboardEvents(); void keyboardFocusAfterPopup(); void mouseClick(); + void touchTap(); + void touchTapAndHold(); + void touchTapAndHoldCancelled(); void postData(); void inputFieldOverridesShortcuts(); @@ -192,6 +197,9 @@ private Q_SLOTS: void webUIURLs_data(); void webUIURLs(); void visibilityState(); + void visibilityState2(); + void visibilityState3(); + void jsKeyboardEvent_data(); void jsKeyboardEvent(); void deletePage(); void closeOpenerTab(); @@ -208,6 +216,7 @@ 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. @@ -1511,6 +1520,172 @@ void tst_QWebEngineView::mouseClick() QVERIFY(view.focusProxy()->inputMethodQuery(Qt::ImCurrentSelection).toString().isEmpty()); } +void tst_QWebEngineView::touchTap() +{ +#if defined(Q_OS_MACOS) + QSKIP("Synthetic touch events are not supported on macOS"); +#endif + + QWebEngineView view; + view.show(); + view.resize(200, 200); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + QSignalSpy loadFinishedSpy(&view, &QWebEngineView::loadFinished); + + view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, false); + view.setHtml("<html><body>" + "<p id='text' style='width: 150px;'>The Qt Company</p>" + "<div id='notext' style='width: 150px; height: 100px; background-color: #f00;'></div>" + "<form><input id='input' width='150px' type='text' value='The Qt Company2' /></form>" + "</body></html>"); + QVERIFY(loadFinishedSpy.wait()); + QVERIFY(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString().isEmpty()); + + auto singleTap = [](QWidget* target, const QPoint& tapCoords) -> void { + QTest::touchEvent(target, s_touchDevice).press(1, tapCoords, target); + QTest::touchEvent(target, s_touchDevice).stationary(1); + QTest::touchEvent(target, s_touchDevice).release(1, tapCoords, target); + }; + + // Single tap on text doesn't trigger a selection + singleTap(view.focusProxy(), elementCenter(view.page(), "text")); + QTRY_VERIFY(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString().isEmpty()); + QTRY_VERIFY(!view.hasSelection()); + + // Single tap inside the input field focuses it without selecting the text + singleTap(view.focusProxy(), elementCenter(view.page(), "input")); + QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input")); + QTRY_VERIFY(!view.hasSelection()); + + // Single tap on the div clears the input field focus + singleTap(view.focusProxy(), elementCenter(view.page(), "notext")); + QTRY_VERIFY(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString().isEmpty()); + + // Double tap on text still doesn't trigger a selection + singleTap(view.focusProxy(), elementCenter(view.page(), "text")); + singleTap(view.focusProxy(), elementCenter(view.page(), "text")); + QTRY_VERIFY(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString().isEmpty()); + QTRY_VERIFY(!view.hasSelection()); + + // Double tap inside the input field focuses it and selects the word under it + singleTap(view.focusProxy(), elementCenter(view.page(), "input")); + singleTap(view.focusProxy(), elementCenter(view.page(), "input")); + QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input")); + QTRY_COMPARE(view.selectedText(), QStringLiteral("Company2")); + + // Double tap outside the input field behaves like a single tap: clears its focus and selection + singleTap(view.focusProxy(), elementCenter(view.page(), "notext")); + singleTap(view.focusProxy(), elementCenter(view.page(), "notext")); + QTRY_VERIFY(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString().isEmpty()); + QTRY_VERIFY(!view.hasSelection()); +} + +void tst_QWebEngineView::touchTapAndHold() +{ +#if defined(Q_OS_MACOS) + QSKIP("Synthetic touch events are not supported on macOS"); +#endif + + QWebEngineView view; + view.show(); + view.resize(200, 200); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + QSignalSpy loadFinishedSpy(&view, &QWebEngineView::loadFinished); + + view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, false); + view.setHtml("<html><body>" + "<p id='text' style='width: 150px;'>The Qt Company</p>" + "<div id='notext' style='width: 150px; height: 100px; background-color: #f00;'></div>" + "<form><input id='input' width='150px' type='text' value='The Qt Company2' /></form>" + "</body></html>"); + QVERIFY(loadFinishedSpy.wait()); + QVERIFY(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString().isEmpty()); + + auto tapAndHold = [](QWidget* target, const QPoint& tapCoords) -> void { + QTest::touchEvent(target, s_touchDevice).press(1, tapCoords, target); + QTest::touchEvent(target, s_touchDevice).stationary(1); + QTest::qWait(1000); + QTest::touchEvent(target, s_touchDevice).release(1, tapCoords, target); + }; + + // Tap-and-hold on text selects the word under it + tapAndHold(view.focusProxy(), elementCenter(view.page(), "text")); + QTRY_VERIFY(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString().isEmpty()); + QTRY_COMPARE(view.selectedText(), QStringLiteral("Company")); + + // Tap-and-hold inside the input field focuses it and selects the word under it + tapAndHold(view.focusProxy(), elementCenter(view.page(), "input")); + QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input")); + QTRY_COMPARE(view.selectedText(), QStringLiteral("Company2")); + + // Only test the page context menu on Windows, as Linux doesn't handle context menus consistently + // and other non-desktop platforms like Android may not even support context menus at all +#if defined(Q_OS_WIN) + // Tap-and-hold clears the text selection and shows the page's context menu + QVERIFY(QApplication::activePopupWidget() == nullptr); + tapAndHold(view.focusProxy(), elementCenter(view.page(), "notext")); + QTRY_VERIFY(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString().isEmpty()); + QTRY_VERIFY(!view.hasSelection()); + QTRY_VERIFY(QApplication::activePopupWidget() != nullptr); + + QApplication::activePopupWidget()->close(); + QVERIFY(QApplication::activePopupWidget() == nullptr); +#endif +} + +void tst_QWebEngineView::touchTapAndHoldCancelled() +{ +#if defined(Q_OS_MACOS) + QSKIP("Synthetic touch events are not supported on macOS"); +#endif + + QWebEngineView view; + view.show(); + view.resize(200, 200); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + QSignalSpy loadFinishedSpy(&view, &QWebEngineView::loadFinished); + + view.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, false); + view.setHtml("<html><body>" + "<p id='text' style='width: 150px;'>The Qt Company</p>" + "<div id='notext' style='width: 150px; height: 100px; background-color: #f00;'></div>" + "<form><input id='input' width='150px' type='text' value='The Qt Company2' /></form>" + "</body></html>"); + QVERIFY(loadFinishedSpy.wait()); + QVERIFY(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString().isEmpty()); + + auto cancelledTapAndHold = [](QWidget* target, const QPoint& tapCoords) -> void { + QTest::touchEvent(target, s_touchDevice).press(1, tapCoords, target); + QTest::touchEvent(target, s_touchDevice).stationary(1); + QTest::qWait(1000); + QWindowSystemInterface::handleTouchCancelEvent(target->windowHandle(), s_touchDevice); + }; + + // A cancelled tap-and-hold should cancel text selection, but currently doesn't + cancelledTapAndHold(view.focusProxy(), elementCenter(view.page(), "text")); + QEXPECT_FAIL("", "Incorrect Chromium selection behavior when cancelling tap-and-hold on text", Continue); + QTRY_VERIFY_WITH_TIMEOUT(!view.hasSelection(), 100); + + // A cancelled tap-and-hold should cancel input field focusing and selection, but currently doesn't + cancelledTapAndHold(view.focusProxy(), elementCenter(view.page(), "input")); + QEXPECT_FAIL("", "Incorrect Chromium selection behavior when cancelling tap-and-hold on input field", Continue); + QTRY_VERIFY_WITH_TIMEOUT(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString().isEmpty(), 100); + QEXPECT_FAIL("", "Incorrect Chromium focus behavior when cancelling tap-and-hold on input field", Continue); + QTRY_VERIFY_WITH_TIMEOUT(!view.hasSelection(), 100); + + // Only test the page context menu on Windows, as Linux doesn't handle context menus consistently + // and other non-desktop platforms like Android may not even support context menus at all +#if defined(Q_OS_WIN) + // A cancelled tap-and-hold cancels the context menu + QVERIFY(QApplication::activePopupWidget() == nullptr); + cancelledTapAndHold(view.focusProxy(), elementCenter(view.page(), "notext")); + QVERIFY(QApplication::activePopupWidget() == nullptr); +#endif +} + void tst_QWebEngineView::postData() { QMap<QString, QString> postData; @@ -1529,7 +1704,7 @@ void tst_QWebEngineView::postData() eventloop.quit(); }); - connect(socket, &QIODevice::readyRead, this, [this, socket, &server, &postData](){ + connect(socket, &QIODevice::readyRead, this, [socket, &server, &postData](){ QByteArray rawData = socket->readAll(); QStringList lines = QString::fromLocal8Bit(rawData).split("\r\n"); @@ -1655,6 +1830,7 @@ void tst_QWebEngineView::inputFieldOverridesShortcuts() view.setHtml(QString("<html><body>" "<button id=\"btn1\" type=\"button\">push it real good</button>" "<input id=\"input1\" type=\"text\" value=\"x\">" + "<input id=\"pass1\" type=\"password\" value=\"x\">" "</body></html>")); QVERIFY(loadFinishedSpy.wait()); @@ -1666,6 +1842,11 @@ void tst_QWebEngineView::inputFieldOverridesShortcuts() "document.getElementById('input1').value").toString(); }; + auto passwordFieldValue = [&view] () -> QString { + return evaluateJavaScriptSync(view.page(), + "document.getElementById('pass1').value").toString(); + }; + // The input form is not focused. The action is triggered on pressing Shift+Delete. action->setShortcut(Qt::SHIFT + Qt::Key_Delete); QTest::keyClick(view.windowHandle(), Qt::Key_Delete, Qt::ShiftModifier); @@ -1689,8 +1870,20 @@ void tst_QWebEngineView::inputFieldOverridesShortcuts() QTRY_COMPARE(inputFieldValue(), QString("yxx")); QVERIFY(!actionTriggered); + // The password input form is focused. The action is not triggered, and the form's text changed. + evaluateJavaScriptSync(view.page(), "document.getElementById('pass1').focus();"); + QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("pass1")); + actionTriggered = false; + QTest::keyClick(view.windowHandle(), Qt::Key_Y); + QTRY_COMPARE(passwordFieldValue(), QString("yx")); + QTest::keyClick(view.windowHandle(), Qt::Key_X); + QTRY_COMPARE(passwordFieldValue(), QString("yxx")); + QVERIFY(!actionTriggered); + // The input form is focused. Make sure we don't override all short cuts. // A Ctrl-1 action is no default Qt key binding and should be triggerable. + evaluateJavaScriptSync(view.page(), "document.getElementById('input1').focus();"); + QTRY_COMPARE(evaluateJavaScriptSync(view.page(), "document.activeElement.id").toString(), QStringLiteral("input1")); action->setShortcut(Qt::CTRL + Qt::Key_1); QTest::keyClick(view.windowHandle(), Qt::Key_1, Qt::ControlModifier); QTRY_VERIFY(actionTriggered); @@ -3123,7 +3316,7 @@ void tst_QWebEngineView::webUIURLs() view.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool))); view.load(url); - QVERIFY(loadFinishedSpy.wait()); + QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000); QCOMPARE(loadFinishedSpy.takeFirst().at(0).toBool(), supported); } @@ -3140,6 +3333,60 @@ void tst_QWebEngineView::visibilityState() QCOMPARE(evaluateJavaScriptSync(view.page(), "document.visibilityState").toString(), QStringLiteral("visible")); } +void tst_QWebEngineView::visibilityState2() +{ + QWebEngineView view; + QSignalSpy spy(&view, &QWebEngineView::loadFinished); + view.show(); + view.load(QStringLiteral("about:blank")); + view.hide(); + QVERIFY(spy.count() || spy.wait()); + QVERIFY(spy.takeFirst().takeFirst().toBool()); + QCOMPARE(evaluateJavaScriptSync(view.page(), "document.visibilityState").toString(), QStringLiteral("hidden")); +} + +void tst_QWebEngineView::visibilityState3() +{ + QWebEnginePage page1; + QWebEnginePage page2; + QSignalSpy spy1(&page1, &QWebEnginePage::loadFinished); + QSignalSpy spy2(&page2, &QWebEnginePage::loadFinished); + page1.load(QStringLiteral("about:blank")); + page2.load(QStringLiteral("about:blank")); + QVERIFY(spy1.count() || spy1.wait()); + QVERIFY(spy2.count() || spy2.wait()); + QWebEngineView view; + view.setPage(&page1); + view.show(); + QCOMPARE(evaluateJavaScriptSync(&page1, "document.visibilityState").toString(), QStringLiteral("visible")); + QCOMPARE(evaluateJavaScriptSync(&page2, "document.visibilityState").toString(), QStringLiteral("hidden")); + view.setPage(&page2); + QCOMPARE(evaluateJavaScriptSync(&page1, "document.visibilityState").toString(), QStringLiteral("hidden")); + QCOMPARE(evaluateJavaScriptSync(&page2, "document.visibilityState").toString(), QStringLiteral("visible")); +} + +void tst_QWebEngineView::jsKeyboardEvent_data() +{ + QTest::addColumn<char>("key"); + QTest::addColumn<Qt::KeyboardModifiers>("modifiers"); + QTest::addColumn<QString>("expected"); + +#if defined(Q_OS_MACOS) + // See Qt::AA_MacDontSwapCtrlAndMeta + Qt::KeyboardModifiers controlModifier = Qt::MetaModifier; +#else + Qt::KeyboardModifiers controlModifier = Qt::ControlModifier; +#endif + + QTest::newRow("Ctrl+Shift+A") << 'A' << (controlModifier | Qt::ShiftModifier) << QStringLiteral( + "16,ShiftLeft,Shift,false,true,false;" + "17,ControlLeft,Control,true,true,false;" + "65,KeyA,A,true,true,false;"); + QTest::newRow("Ctrl+z") << 'z' << controlModifier << QStringLiteral( + "17,ControlLeft,Control,true,false,false;" + "90,KeyZ,z,true,false,false;"); +} + void tst_QWebEngineView::jsKeyboardEvent() { QWebEngineView view; @@ -3149,18 +3396,13 @@ void tst_QWebEngineView::jsKeyboardEvent() "addEventListener('keydown', (ev) => {" " log += [ev.keyCode, ev.code, ev.key, ev.ctrlKey, ev.shiftKey, ev.altKey].join(',') + ';';" "});"); + + QFETCH(char, key); + QFETCH(Qt::KeyboardModifiers, modifiers); + QFETCH(QString, expected); + // Note that this only tests the fallback code path where native scan codes are not used. -#if defined(Q_OS_MACOS) - // See Qt::AA_MacDontSwapCtrlAndMeta - QTest::keyClick(view.focusProxy(), 'A', Qt::MetaModifier | Qt::ShiftModifier); -#else - QTest::keyClick(view.focusProxy(), 'A', Qt::ControlModifier | Qt::ShiftModifier); -#endif - QString expected = QStringLiteral( - "16,ShiftLeft,Shift,false,true,false;" - "17,ControlLeft,Control,true,true,false;" - "65,KeyA,A,true,true,false;" - ); + QTest::keyClick(view.focusProxy(), key, modifiers); QTRY_VERIFY(evaluateJavaScriptSync(view.page(), "log") != QVariant(QString())); QCOMPARE(evaluateJavaScriptSync(view.page(), "log"), expected); } |