diff options
Diffstat (limited to 'tests/auto/widgets')
38 files changed, 1613 insertions, 366 deletions
diff --git a/tests/auto/widgets/positionplugin/plugin.cpp b/tests/auto/widgets/positionplugin/plugin.cpp deleted file mode 100644 index 74d30469d..000000000 --- a/tests/auto/widgets/positionplugin/plugin.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL21$ -** 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 http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 or version 3 as published by the Free -** Software Foundation and appearing in the file LICENSE.LGPLv21 and -** LICENSE.LGPLv3 included in the packaging of this file. Please review the -** following information to ensure the GNU Lesser General Public License -** requirements will be met: https://www.gnu.org/licenses/lgpl.html and -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** As a special exception, The Qt Company gives you certain additional -** rights. These rights are described in The Qt Company LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtPositioning/qgeopositioninfosource.h> -#include <QtPositioning/qgeopositioninfosourcefactory.h> -#include <QObject> -#include <QtPlugin> - -class DummySource : public QGeoPositionInfoSource -{ - Q_OBJECT - -public: - DummySource(QObject *parent=0); - - void startUpdates() {} - void stopUpdates() {} - void requestUpdate(int) {} - - QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const; - PositioningMethods supportedPositioningMethods() const; - - int minimumUpdateInterval() const; - Error error() const; -}; - -DummySource::DummySource(QObject *parent) : - QGeoPositionInfoSource(parent) -{ -} - -QGeoPositionInfoSource::Error DummySource::error() const -{ - return QGeoPositionInfoSource::NoError; -} - -int DummySource::minimumUpdateInterval() const -{ - return 1000; -} - -QGeoPositionInfo DummySource::lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const -{ - Q_UNUSED(fromSatellitePositioningMethodsOnly); - return QGeoPositionInfo(QGeoCoordinate(54.186824, 12.087262), QDateTime::currentDateTime()); -} - -QGeoPositionInfoSource::PositioningMethods DummySource::supportedPositioningMethods() const -{ - return QGeoPositionInfoSource::AllPositioningMethods; -} - - -class QGeoPositionInfoSourceFactoryTest : public QObject, public QGeoPositionInfoSourceFactory -{ - Q_OBJECT - Q_PLUGIN_METADATA(IID "org.qt-project.qt.position.sourcefactory/5.0" - FILE "plugin.json") - Q_INTERFACES(QGeoPositionInfoSourceFactory) - -public: - QGeoPositionInfoSource *positionInfoSource(QObject *parent); - QGeoSatelliteInfoSource *satelliteInfoSource(QObject *parent); - QGeoAreaMonitorSource *areaMonitor(QObject *parent); -}; - -QGeoPositionInfoSource *QGeoPositionInfoSourceFactoryTest::positionInfoSource(QObject *parent) -{ - return new DummySource(parent); -} - -QGeoSatelliteInfoSource *QGeoPositionInfoSourceFactoryTest::satelliteInfoSource(QObject *) -{ - return 0; -} - -QGeoAreaMonitorSource *QGeoPositionInfoSourceFactoryTest::areaMonitor(QObject* ) -{ - return 0; -} - -#include "plugin.moc" diff --git a/tests/auto/widgets/positionplugin/plugin.json b/tests/auto/widgets/positionplugin/plugin.json deleted file mode 100644 index 68acaded3..000000000 --- a/tests/auto/widgets/positionplugin/plugin.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Keys": ["test.source"], - "Provider": "test.source", - "Position": true, - "Satellite": false, - "Monitor": false, - "Priority": 0, - "Testable": true -} diff --git a/tests/auto/widgets/positionplugin/positionplugin.pro b/tests/auto/widgets/positionplugin/positionplugin.pro deleted file mode 100644 index 6f2e736c6..000000000 --- a/tests/auto/widgets/positionplugin/positionplugin.pro +++ /dev/null @@ -1,13 +0,0 @@ -TARGET = qtwebengine_positioning_testplugin - -QT += positioning - -SOURCES += plugin.cpp - -OTHER_FILES += \ - plugin.json - -PLUGIN_TYPE = position -PLUGIN_CLASS_NAME = TestPositionPlugin -PLUGIN_EXTENDS = - -load(qt_plugin) diff --git a/tests/auto/widgets/qwebengineaccessibility/tst_qwebengineaccessibility.cpp b/tests/auto/widgets/qwebengineaccessibility/tst_qwebengineaccessibility.cpp index 016c4f98c..63ca25396 100644 --- a/tests/auto/widgets/qwebengineaccessibility/tst_qwebengineaccessibility.cpp +++ b/tests/auto/widgets/qwebengineaccessibility/tst_qwebengineaccessibility.cpp @@ -112,9 +112,9 @@ void tst_QWebEngineView::hierarchy() QCOMPARE(text->parent(), grouping); QCOMPARE(grouping->indexOfChild(text), 0); QCOMPARE(text->childCount(), 0); - QCOMPARE(text->text(QAccessible::Name), QString()); + QCOMPARE(text->text(QAccessible::Name), QStringLiteral("Hello world")); QCOMPARE(text->text(QAccessible::Description), QString()); - QCOMPARE(text->text(QAccessible::Value), QStringLiteral("Hello world")); + QCOMPARE(text->text(QAccessible::Value), QString()); QAccessibleInterface *input = grouping->child(1); QCOMPARE(input->role(), QAccessible::EditableText); @@ -182,9 +182,9 @@ void tst_QWebEngineView::text() QAccessibleInterface *grouping2 = document->child(1); QAccessibleInterface *label1 = grouping2->child(0); QCOMPARE(label1->role(), QAccessible::StaticText); - QCOMPARE(label1->text(QAccessible::Name), QString()); + QCOMPARE(label1->text(QAccessible::Name), QStringLiteral("Enter your name here:")); QCOMPARE(label1->text(QAccessible::Description), QString()); - QCOMPARE(label1->text(QAccessible::Value), QStringLiteral("Enter your name here:")); + QCOMPARE(label1->text(QAccessible::Value), QString()); QAccessibleInterface *grouping3 = document->child(2); QAccessibleInterface *input2 = grouping3->child(0); QCOMPARE(input2->role(), QAccessible::EditableText); @@ -194,9 +194,9 @@ void tst_QWebEngineView::text() QAccessibleInterface *grouping4 = document->child(3); QAccessibleInterface *label2 = grouping4->child(0); QCOMPARE(label2->role(), QAccessible::StaticText); - QCOMPARE(label2->text(QAccessible::Name), QString()); + QCOMPARE(label2->text(QAccessible::Name), QStringLiteral("Provide both first and last name.")); QCOMPARE(label2->text(QAccessible::Description), QString()); - QCOMPARE(label2->text(QAccessible::Value), QStringLiteral("Provide both first and last name.")); + QCOMPARE(label2->text(QAccessible::Value), QString()); // Good day! [edit] QAccessibleInterface *grouping5 = document->child(4); diff --git a/tests/auto/widgets/qwebenginedefaultsurfaceformat/tst_qwebenginedefaultsurfaceformat.cpp b/tests/auto/widgets/qwebenginedefaultsurfaceformat/tst_qwebenginedefaultsurfaceformat.cpp index e42a8a75e..3757a7842 100644 --- a/tests/auto/widgets/qwebenginedefaultsurfaceformat/tst_qwebenginedefaultsurfaceformat.cpp +++ b/tests/auto/widgets/qwebenginedefaultsurfaceformat/tst_qwebenginedefaultsurfaceformat.cpp @@ -51,6 +51,9 @@ private Q_SLOTS: void tst_QWebEngineDefaultSurfaceFormat::customDefaultSurfaceFormat() { +#if defined(Q_OS_WIN) + QSKIP("Crashes on Windows"); +#endif // Setting a new default QSurfaceFormat with a core OpenGL profile before // app instantiation should succeed, without abort() being called. int argc = 1; diff --git a/tests/auto/widgets/qwebenginefaviconmanager/qwebenginefaviconmanager.pro b/tests/auto/widgets/qwebenginefaviconmanager/qwebenginefaviconmanager.pro new file mode 100644 index 000000000..e99c7f493 --- /dev/null +++ b/tests/auto/widgets/qwebenginefaviconmanager/qwebenginefaviconmanager.pro @@ -0,0 +1 @@ +include(../tests.pri) diff --git a/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-misc.html b/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-misc.html new file mode 100644 index 000000000..9e788bdf4 --- /dev/null +++ b/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-misc.html @@ -0,0 +1,11 @@ +<html> + <head> + <title>Favicon Test</title> + <link rel="shortcut icon" href="icons/qt32.ico" /> + <link rel="apple-touch-icon" href="icons/qt144.png" /> + <link rel="shortcut icon" href="icons/unavailable.ico" /> + </head> + <body> + <h1>Favicon Test</h1> + </body> +</html> diff --git a/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-multi.html b/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-multi.html new file mode 100644 index 000000000..cc5f3fd66 --- /dev/null +++ b/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-multi.html @@ -0,0 +1,9 @@ +<html> + <head> + <title>Multi-sized Favicon Test</title> + <link rel="shortcut icon" sizes="16x16 32x23 64x64" href="icons/qtmulti.ico" /> + </head> + <body> + <h1>Multi-sized Favicon Test</h1> + </body> +</html> diff --git a/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-shortcut.html b/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-shortcut.html new file mode 100644 index 000000000..786cdb816 --- /dev/null +++ b/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-shortcut.html @@ -0,0 +1,10 @@ +<html> + <head> + <title>Favicon Test</title> + <link rel="shortcut icon" href="icons/qt32.ico" /> + <link rel="shortcut icon" href="icons/qt144.png" /> + </head> + <body> + <h1>Favicon Test</h1> + </body> +</html> diff --git a/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-single.html b/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-single.html new file mode 100644 index 000000000..eb4675c75 --- /dev/null +++ b/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-single.html @@ -0,0 +1,9 @@ +<html> + <head> + <title>Favicon Test</title> + <link rel="shortcut icon" href="icons/qt32.ico" /> + </head> + <body> + <h1>Favicon Test</h1> + </body> +</html> diff --git a/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-touch.html b/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-touch.html new file mode 100644 index 000000000..271783434 --- /dev/null +++ b/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-touch.html @@ -0,0 +1,10 @@ +<html> + <head> + <title>Favicon Test</title> + <link rel="apple-touch-icon" href="icons/qt32.ico" /> + <link rel="apple-touch-icon" href="icons/qt144.png" /> + </head> + <body> + <h1>Favicon Test</h1> + </body> +</html> diff --git a/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-unavailable.html b/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-unavailable.html new file mode 100644 index 000000000..c45664294 --- /dev/null +++ b/tests/auto/widgets/qwebenginefaviconmanager/resources/favicon-unavailable.html @@ -0,0 +1,9 @@ +<html> + <head> + <title>Favicon Test</title> + <link rel="shortcut icon" href="icons/unavailable.ico" /> + </head> + <body> + <h1>Favicon Test</h1> + </body> +</html> diff --git a/tests/auto/widgets/qwebenginefaviconmanager/resources/icons/qt144.png b/tests/auto/widgets/qwebenginefaviconmanager/resources/icons/qt144.png Binary files differnew file mode 100644 index 000000000..050b1e066 --- /dev/null +++ b/tests/auto/widgets/qwebenginefaviconmanager/resources/icons/qt144.png diff --git a/tests/auto/widgets/qwebenginefaviconmanager/resources/icons/qt32.ico b/tests/auto/widgets/qwebenginefaviconmanager/resources/icons/qt32.ico Binary files differnew file mode 100644 index 000000000..2f6fcb5bc --- /dev/null +++ b/tests/auto/widgets/qwebenginefaviconmanager/resources/icons/qt32.ico diff --git a/tests/auto/widgets/qwebenginefaviconmanager/resources/icons/qtmulti.ico b/tests/auto/widgets/qwebenginefaviconmanager/resources/icons/qtmulti.ico Binary files differnew file mode 100644 index 000000000..81e5a22e8 --- /dev/null +++ b/tests/auto/widgets/qwebenginefaviconmanager/resources/icons/qtmulti.ico diff --git a/tests/auto/widgets/qwebenginefaviconmanager/resources/test1.html b/tests/auto/widgets/qwebenginefaviconmanager/resources/test1.html new file mode 100644 index 000000000..b323f966e --- /dev/null +++ b/tests/auto/widgets/qwebenginefaviconmanager/resources/test1.html @@ -0,0 +1 @@ +<html><body><p>Some text 1</p></body></html> diff --git a/tests/auto/widgets/qwebenginefaviconmanager/tst_qwebenginefaviconmanager.cpp b/tests/auto/widgets/qwebenginefaviconmanager/tst_qwebenginefaviconmanager.cpp new file mode 100644 index 000000000..38311cad2 --- /dev/null +++ b/tests/auto/widgets/qwebenginefaviconmanager/tst_qwebenginefaviconmanager.cpp @@ -0,0 +1,478 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 <QtTest/QtTest> +#include "../util.h" + +#include <qwebenginepage.h> +#include <qwebenginesettings.h> +#include <qwebengineview.h> + + +class tst_QWebEngineFaviconManager : public QObject { + Q_OBJECT + +public Q_SLOTS: + void init(); + void initTestCase(); + void cleanupTestCase(); + void cleanup(); + +private Q_SLOTS: + void faviconLoad(); + void faviconLoadFromResources(); + void faviconLoadEncodedUrl(); + void noFavicon(); + void aboutBlank(); + void unavailableFavicon(); + void errorPageEnabled(); + void errorPageDisabled(); + void bestFavicon(); + void touchIcon(); + void multiIcon(); + void candidateIcon(); + void downloadIconsDisabled_data(); + void downloadIconsDisabled(); + void downloadTouchIconsEnabled_data(); + void downloadTouchIconsEnabled(); + +private: + QWebEngineView *m_view; + QWebEnginePage *m_page; +}; + + +void tst_QWebEngineFaviconManager::init() +{ + m_view = new QWebEngineView(); + m_page = m_view->page(); +} + + +void tst_QWebEngineFaviconManager::initTestCase() +{ +} + +void tst_QWebEngineFaviconManager::cleanupTestCase() +{ +} + + +void tst_QWebEngineFaviconManager::cleanup() +{ + delete m_view; +} + +void tst_QWebEngineFaviconManager::faviconLoad() +{ + if (!QDir(TESTS_SOURCE_DIR).exists()) + W_QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll); + + QSignalSpy loadFinishedSpy(m_page, SIGNAL(loadFinished(bool))); + QSignalSpy iconUrlChangedSpy(m_page, SIGNAL(iconUrlChanged(QUrl))); + QSignalSpy iconChangedSpy(m_page, SIGNAL(iconChanged(QIcon))); + + QUrl url = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginefaviconmanager/resources/favicon-single.html")); + m_page->load(url); + + QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(iconUrlChangedSpy.count(), 1); + QTRY_COMPARE(iconChangedSpy.count(), 1); + + QUrl iconUrl = iconUrlChangedSpy.at(0).at(0).toString(); + QCOMPARE(iconUrl, m_page->iconUrl()); + QCOMPARE(iconUrl, QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginefaviconmanager/resources/icons/qt32.ico"))); + + const QIcon &icon = m_page->icon(); + QVERIFY(!icon.isNull()); + + QCOMPARE(icon.availableSizes().count(), 1); + QSize iconSize = icon.availableSizes().first(); + QCOMPARE(iconSize, QSize(32, 32)); +} + +void tst_QWebEngineFaviconManager::faviconLoadFromResources() +{ + QSignalSpy loadFinishedSpy(m_page, SIGNAL(loadFinished(bool))); + QSignalSpy iconUrlChangedSpy(m_page, SIGNAL(iconUrlChanged(QUrl))); + QSignalSpy iconChangedSpy(m_page, SIGNAL(iconChanged(QIcon))); + + QUrl url("qrc:/resources/favicon-single.html"); + m_page->load(url); + + QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(iconUrlChangedSpy.count(), 1); + QTRY_COMPARE(iconChangedSpy.count(), 1); + + QUrl iconUrl = iconUrlChangedSpy.at(0).at(0).toString(); + QCOMPARE(iconUrl, m_page->iconUrl()); + QCOMPARE(iconUrl, QUrl("qrc:/resources/icons/qt32.ico")); + + const QIcon &icon = m_page->icon(); + QVERIFY(!icon.isNull()); + + QCOMPARE(icon.availableSizes().count(), 1); + QSize iconSize = icon.availableSizes().first(); + QCOMPARE(iconSize, QSize(32, 32)); +} + +void tst_QWebEngineFaviconManager::faviconLoadEncodedUrl() +{ + if (!QDir(TESTS_SOURCE_DIR).exists()) + W_QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll); + + QSignalSpy loadFinishedSpy(m_page, SIGNAL(loadFinished(bool))); + QSignalSpy iconUrlChangedSpy(m_page, SIGNAL(iconUrlChanged(QUrl))); + QSignalSpy iconChangedSpy(m_page, SIGNAL(iconChanged(QIcon))); + + QString urlString = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginefaviconmanager/resources/favicon-single.html")).toString(); + QUrl url(urlString + QLatin1String("?favicon=load should work with#whitespace!")); + m_page->load(url); + + QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(iconUrlChangedSpy.count(), 1); + QTRY_COMPARE(iconChangedSpy.count(), 1); + + QUrl iconUrl = iconUrlChangedSpy.at(0).at(0).toString(); + QCOMPARE(m_page->iconUrl(), iconUrl); + QCOMPARE(iconUrl, QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginefaviconmanager/resources/icons/qt32.ico"))); + + const QIcon &icon = m_page->icon(); + QVERIFY(!icon.isNull()); + + QCOMPARE(icon.availableSizes().count(), 1); + QSize iconSize = icon.availableSizes().first(); + QCOMPARE(iconSize, QSize(32, 32)); +} + +void tst_QWebEngineFaviconManager::noFavicon() +{ + if (!QDir(TESTS_SOURCE_DIR).exists()) + W_QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll); + + QSignalSpy loadFinishedSpy(m_page, SIGNAL(loadFinished(bool))); + QSignalSpy iconUrlChangedSpy(m_page, SIGNAL(iconUrlChanged(QUrl))); + QSignalSpy iconChangedSpy(m_page, SIGNAL(iconChanged(QIcon))); + + QUrl url = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginefaviconmanager/resources/test1.html")); + m_page->load(url); + + QTRY_COMPARE(loadFinishedSpy.count(), 1); + QCOMPARE(iconUrlChangedSpy.count(), 0); + QCOMPARE(iconChangedSpy.count(), 0); + + QVERIFY(m_page->iconUrl().isEmpty()); + QVERIFY(m_page->icon().isNull()); +} + +void tst_QWebEngineFaviconManager::aboutBlank() +{ + QSignalSpy loadFinishedSpy(m_page, SIGNAL(loadFinished(bool))); + QSignalSpy iconUrlChangedSpy(m_page, SIGNAL(iconUrlChanged(QUrl))); + QSignalSpy iconChangedSpy(m_page, SIGNAL(iconChanged(QIcon))); + + QUrl url("about:blank"); + m_page->load(url); + + QTRY_COMPARE(loadFinishedSpy.count(), 1); + QCOMPARE(iconUrlChangedSpy.count(), 0); + QCOMPARE(iconChangedSpy.count(), 0); + + QVERIFY(m_page->iconUrl().isEmpty()); + QVERIFY(m_page->icon().isNull()); +} + +void tst_QWebEngineFaviconManager::unavailableFavicon() +{ + if (!QDir(TESTS_SOURCE_DIR).exists()) + W_QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll); + + QSignalSpy loadFinishedSpy(m_page, SIGNAL(loadFinished(bool))); + QSignalSpy iconUrlChangedSpy(m_page, SIGNAL(iconUrlChanged(QUrl))); + QSignalSpy iconChangedSpy(m_page, SIGNAL(iconChanged(QIcon))); + + QUrl url = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginefaviconmanager/resources/favicon-unavailable.html")); + m_page->load(url); + + QTRY_COMPARE(loadFinishedSpy.count(), 1); + QCOMPARE(iconUrlChangedSpy.count(), 0); + QCOMPARE(iconChangedSpy.count(), 0); + + QVERIFY(m_page->iconUrl().isEmpty()); + QVERIFY(m_page->icon().isNull()); +} + +void tst_QWebEngineFaviconManager::errorPageEnabled() +{ + m_page->settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, true); + + QSignalSpy loadFinishedSpy(m_page, SIGNAL(loadFinished(bool))); + QSignalSpy iconUrlChangedSpy(m_page, SIGNAL(iconUrlChanged(QUrl))); + QSignalSpy iconChangedSpy(m_page, SIGNAL(iconChanged(QIcon))); + + QUrl url("invalid://url"); + m_page->load(url); + + QTRY_COMPARE(loadFinishedSpy.count(), 1); + QCOMPARE(iconUrlChangedSpy.count(), 0); + QCOMPARE(iconChangedSpy.count(), 0); + + QVERIFY(m_page->iconUrl().isEmpty()); + QVERIFY(m_page->icon().isNull()); +} + +void tst_QWebEngineFaviconManager::errorPageDisabled() +{ + m_page->settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); + + QSignalSpy loadFinishedSpy(m_page, SIGNAL(loadFinished(bool))); + QSignalSpy iconUrlChangedSpy(m_page, SIGNAL(iconUrlChanged(QUrl))); + QSignalSpy iconChangedSpy(m_page, SIGNAL(iconChanged(QIcon))); + + QUrl url("invalid://url"); + m_page->load(url); + + QTRY_COMPARE(loadFinishedSpy.count(), 1); + QCOMPARE(iconUrlChangedSpy.count(), 0); + QCOMPARE(iconChangedSpy.count(), 0); + + QVERIFY(m_page->iconUrl().isEmpty()); + QVERIFY(m_page->icon().isNull()); +} + +void tst_QWebEngineFaviconManager::bestFavicon() +{ + if (!QDir(TESTS_SOURCE_DIR).exists()) + W_QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll); + + QSignalSpy loadFinishedSpy(m_page, SIGNAL(loadFinished(bool))); + QSignalSpy iconUrlChangedSpy(m_page, SIGNAL(iconUrlChanged(QUrl))); + QSignalSpy iconChangedSpy(m_page, SIGNAL(iconChanged(QIcon))); + + QUrl url, iconUrl; + QIcon icon; + QSize iconSize; + + url = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginefaviconmanager/resources/favicon-misc.html")); + m_page->load(url); + + QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(iconUrlChangedSpy.count(), 1); + QTRY_COMPARE(iconChangedSpy.count(), 1); + + iconUrl = iconUrlChangedSpy.at(0).at(0).toString(); + QCOMPARE(iconUrl, m_page->iconUrl()); + // Touch icon is ignored + QCOMPARE(iconUrl, QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginefaviconmanager/resources/icons/qt32.ico"))); + + icon = m_page->icon(); + QVERIFY(!icon.isNull()); + + QCOMPARE(icon.availableSizes().count(), 1); + iconSize = icon.availableSizes().first(); + QCOMPARE(iconSize, QSize(32, 32)); + + loadFinishedSpy.clear(); + iconUrlChangedSpy.clear(); + iconChangedSpy.clear(); + + url = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginefaviconmanager/resources/favicon-shortcut.html")); + m_page->load(url); + + QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_VERIFY(iconUrlChangedSpy.count() >= 1); + QTRY_VERIFY(iconChangedSpy.count() >= 1); + + iconUrl = iconUrlChangedSpy.last().at(0).toString(); + + // If the icon URL is empty we have to wait for + // the second iconChanged signal that propagates the expected URL + if (iconUrl.isEmpty()) { + QTRY_COMPARE(iconUrlChangedSpy.count(), 2); + QTRY_COMPARE(iconChangedSpy.count(), 2); + iconUrl = iconUrlChangedSpy.last().at(0).toString(); + } + + QCOMPARE(iconUrl, m_page->iconUrl()); + QCOMPARE(iconUrl, QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginefaviconmanager/resources/icons/qt144.png"))); + + icon = m_page->icon(); + QVERIFY(!icon.isNull()); + + QVERIFY(icon.availableSizes().count() >= 1); + QVERIFY(icon.availableSizes().contains(QSize(144, 144))); +} + +void tst_QWebEngineFaviconManager::touchIcon() +{ + if (!QDir(TESTS_SOURCE_DIR).exists()) + W_QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll); + + QSignalSpy loadFinishedSpy(m_page, SIGNAL(loadFinished(bool))); + QSignalSpy iconUrlChangedSpy(m_page, SIGNAL(iconUrlChanged(QUrl))); + QSignalSpy iconChangedSpy(m_page, SIGNAL(iconChanged(QIcon))); + + QUrl url = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginefaviconmanager/resources/favicon-touch.html")); + m_page->load(url); + + QTRY_COMPARE(loadFinishedSpy.count(), 1); + QCOMPARE(iconUrlChangedSpy.count(), 0); + QCOMPARE(iconChangedSpy.count(), 0); + + QVERIFY(m_page->iconUrl().isEmpty()); + QVERIFY(m_page->icon().isNull()); +} + +void tst_QWebEngineFaviconManager::multiIcon() +{ + if (!QDir(TESTS_SOURCE_DIR).exists()) + W_QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll); + + QSignalSpy loadFinishedSpy(m_page, SIGNAL(loadFinished(bool))); + QSignalSpy iconUrlChangedSpy(m_page, SIGNAL(iconUrlChanged(QUrl))); + QSignalSpy iconChangedSpy(m_page, SIGNAL(iconChanged(QIcon))); + + QUrl url = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginefaviconmanager/resources/favicon-multi.html")); + m_page->load(url); + + QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(iconUrlChangedSpy.count(), 1); + QTRY_COMPARE(iconChangedSpy.count(), 1); + + QUrl iconUrl = iconUrlChangedSpy.at(0).at(0).toString(); + QCOMPARE(m_page->iconUrl(), iconUrl); + QCOMPARE(iconUrl, QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginefaviconmanager/resources/icons/qtmulti.ico"))); + + const QIcon &icon = m_page->icon(); + QVERIFY(!icon.isNull()); + QCOMPARE(icon.availableSizes().count(), 3); + QVERIFY(icon.availableSizes().contains(QSize(16, 16))); + QVERIFY(icon.availableSizes().contains(QSize(32, 32))); + QVERIFY(icon.availableSizes().contains(QSize(64, 64))); +} + +void tst_QWebEngineFaviconManager::candidateIcon() +{ + if (!QDir(TESTS_SOURCE_DIR).exists()) + W_QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll); + + QSignalSpy loadFinishedSpy(m_page, SIGNAL(loadFinished(bool))); + QSignalSpy iconUrlChangedSpy(m_page, SIGNAL(iconUrlChanged(QUrl))); + QSignalSpy iconChangedSpy(m_page, SIGNAL(iconChanged(QIcon))); + + QUrl url = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginefaviconmanager/resources/favicon-shortcut.html")); + m_page->load(url); + + QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(iconUrlChangedSpy.count(), 1); + QTRY_COMPARE(iconChangedSpy.count(), 1); + + QUrl iconUrl = iconUrlChangedSpy.at(0).at(0).toString(); + QCOMPARE(m_page->iconUrl(), iconUrl); + QCOMPARE(iconUrl, QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginefaviconmanager/resources/icons/qt144.png"))); + + const QIcon &icon = m_page->icon(); + QVERIFY(!icon.isNull()); + QCOMPARE(icon.availableSizes().count(), 2); + QVERIFY(icon.availableSizes().contains(QSize(32, 32))); + QVERIFY(icon.availableSizes().contains(QSize(144, 144))); +} + +void tst_QWebEngineFaviconManager::downloadIconsDisabled_data() +{ + QTest::addColumn<QUrl>("url"); + QTest::newRow("misc") << QUrl("qrc:/resources/favicon-misc.html"); + QTest::newRow("shortcut") << QUrl("qrc:/resources/favicon-shortcut.html"); + QTest::newRow("single") << QUrl("qrc:/resources/favicon-single.html"); + QTest::newRow("touch") << QUrl("qrc:/resources/favicon-touch.html"); + QTest::newRow("unavailable") << QUrl("qrc:/resources/favicon-unavailable.html"); +} + +void tst_QWebEngineFaviconManager::downloadIconsDisabled() +{ + QFETCH(QUrl, url); + + m_page->settings()->setAttribute(QWebEngineSettings::AutoLoadIconsForPage, false); + + QSignalSpy loadFinishedSpy(m_page, SIGNAL(loadFinished(bool))); + QSignalSpy iconUrlChangedSpy(m_page, SIGNAL(iconUrlChanged(QUrl))); + QSignalSpy iconChangedSpy(m_page, SIGNAL(iconChanged(QIcon))); + + m_page->load(url); + + QTRY_COMPARE(loadFinishedSpy.count(), 1); + QCOMPARE(iconUrlChangedSpy.count(), 0); + QCOMPARE(iconChangedSpy.count(), 0); + + QVERIFY(m_page->iconUrl().isEmpty()); + QVERIFY(m_page->icon().isNull()); +} + +void tst_QWebEngineFaviconManager::downloadTouchIconsEnabled_data() +{ + QTest::addColumn<QUrl>("url"); + QTest::addColumn<QUrl>("expectedIconUrl"); + QTest::addColumn<QSize>("expectedIconSize"); + QTest::newRow("misc") << QUrl("qrc:/resources/favicon-misc.html") << QUrl("qrc:/resources/icons/qt144.png") << QSize(144, 144); + QTest::newRow("shortcut") << QUrl("qrc:/resources/favicon-shortcut.html") << QUrl("qrc:/resources/icons/qt144.png") << QSize(144, 144); + QTest::newRow("single") << QUrl("qrc:/resources/favicon-single.html") << QUrl("qrc:/resources/icons/qt32.ico") << QSize(32, 32); + QTest::newRow("touch") << QUrl("qrc:/resources/favicon-touch.html") << QUrl("qrc:/resources/icons/qt144.png") << QSize(144, 144); +} + +void tst_QWebEngineFaviconManager::downloadTouchIconsEnabled() +{ + QFETCH(QUrl, url); + QFETCH(QUrl, expectedIconUrl); + QFETCH(QSize, expectedIconSize); + + m_page->settings()->setAttribute(QWebEngineSettings::TouchIconsEnabled, true); + + QSignalSpy loadFinishedSpy(m_page, SIGNAL(loadFinished(bool))); + QSignalSpy iconUrlChangedSpy(m_page, SIGNAL(iconUrlChanged(QUrl))); + QSignalSpy iconChangedSpy(m_page, SIGNAL(iconChanged(QIcon))); + + m_page->load(url); + + QTRY_COMPARE(loadFinishedSpy.count(), 1); + QTRY_COMPARE(iconUrlChangedSpy.count(), 1); + QTRY_COMPARE(iconChangedSpy.count(), 1); + + const QUrl &iconUrl = iconUrlChangedSpy.at(0).at(0).toString(); + QCOMPARE(m_page->iconUrl(), iconUrl); + QCOMPARE(iconUrl, expectedIconUrl); + + const QIcon &icon = m_page->icon(); + QVERIFY(!icon.isNull()); + + QVERIFY(icon.availableSizes().count() >= 1); + QVERIFY(icon.availableSizes().contains(expectedIconSize)); +} + +QTEST_MAIN(tst_QWebEngineFaviconManager) + +#include "tst_qwebenginefaviconmanager.moc" diff --git a/tests/auto/widgets/qwebenginefaviconmanager/tst_qwebenginefaviconmanager.qrc b/tests/auto/widgets/qwebenginefaviconmanager/tst_qwebenginefaviconmanager.qrc new file mode 100644 index 000000000..a352f8a83 --- /dev/null +++ b/tests/auto/widgets/qwebenginefaviconmanager/tst_qwebenginefaviconmanager.qrc @@ -0,0 +1,14 @@ +<!DOCTYPE RCC><RCC version="1.0"> +<qresource> + <file>resources/favicon-misc.html</file> + <file>resources/favicon-multi.html</file> + <file>resources/favicon-shortcut.html</file> + <file>resources/favicon-single.html</file> + <file>resources/favicon-touch.html</file> + <file>resources/favicon-unavailable.html</file> + <file>resources/icons/qt144.png</file> + <file>resources/icons/qt32.ico</file> + <file>resources/icons/qtmulti.ico</file> + <file>resources/test1.html</file> +</qresource> +</RCC> diff --git a/tests/auto/widgets/qwebengineinspector/tst_qwebengineinspector.cpp b/tests/auto/widgets/qwebengineinspector/tst_qwebengineinspector.cpp index 8d7e41f0f..000214b9a 100644 --- a/tests/auto/widgets/qwebengineinspector/tst_qwebengineinspector.cpp +++ b/tests/auto/widgets/qwebengineinspector/tst_qwebengineinspector.cpp @@ -1,21 +1,30 @@ -/* - Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ +/**************************************************************************** +** +** Copyright (C) 2016 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 <QtTest/QtTest> diff --git a/tests/auto/widgets/qwebenginepage/BLACKLIST b/tests/auto/widgets/qwebenginepage/BLACKLIST index 045783c94..2b9453b0e 100644 --- a/tests/auto/widgets/qwebenginepage/BLACKLIST +++ b/tests/auto/widgets/qwebenginepage/BLACKLIST @@ -1,8 +1,8 @@ [comboBoxPopupPositionAfterMove] linux -[geolocationRequestJS] -* - [macCopyUnicodeToClipboard] osx + +[getUserMediaRequest] +* diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp index ede50e63f..518713b51 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp @@ -1,5 +1,5 @@ /* - Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2016 The Qt Company Ltd. Copyright (C) 2009 Girish Ramakrishnan <girish@forwardbias.in> Copyright (C) 2010 Holger Hans Peter Freyther @@ -20,6 +20,7 @@ */ #include "../util.h" +#include <QByteArray> #include <QClipboard> #include <QDir> #include <QGraphicsWidget> @@ -216,7 +217,6 @@ private Q_SLOTS: void hitTestContent(); void baseUrl_data(); void baseUrl(); - void renderHints(); void scrollPosition(); void scrollToAnchor(); void scrollbarsOff(); @@ -241,6 +241,8 @@ private Q_SLOTS: void setZoomFactor(); void mouseButtonTranslation(); + void printToPdf(); + private: static QPoint elementCenter(QWebEnginePage *page, const QString &id); @@ -421,9 +423,14 @@ void tst_QWebEnginePage::geolocationRequestJS() W_QSKIP("Geolocation is not supported.", SkipSingle); } - evaluateJavaScriptSync(newPage, "var errorCode = 0; function error(err) { errorCode = err.code; } function success(pos) { } navigator.geolocation.getCurrentPosition(success, error)"); + evaluateJavaScriptSync(newPage, "var errorCode = 0; var done = false; function error(err) { errorCode = err.code; done = true; } function success(pos) { done = true; } navigator.geolocation.getCurrentPosition(success, error)"); + + QTRY_VERIFY(evaluateJavaScriptSync(newPage, "done").toBool()); + int result = evaluateJavaScriptSync(newPage, "errorCode").toInt(); + if (result == 2) + QEXPECT_FAIL("", "No location service available.", Continue); + QCOMPARE(result, errorCode); - QTRY_COMPARE(evaluateJavaScriptSync(newPage, "errorCode").toInt(), errorCode); delete view; } @@ -2886,6 +2893,9 @@ void tst_QWebEnginePage::testJSPrompt() { JSPromptPage page; bool res; + QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); + page.setHtml(QStringLiteral("<html><body></body></html>")); + QTRY_COMPARE(loadSpy.count(), 1); // OK + QString() res = evaluateJavaScriptSync(&page, @@ -3398,9 +3408,9 @@ void tst_QWebEnginePage::loadSignalsOrder() QFETCH(QUrl, url); QWebEnginePage page; SpyForLoadSignalsOrder loadSpy(&page); - waitForSignal(&loadSpy, SIGNAL(started())); + waitForSignal(&loadSpy, SIGNAL(started()), 500); page.load(url); - QTRY_VERIFY(loadSpy.isFinished()); + QTRY_VERIFY_WITH_TIMEOUT(loadSpy.isFinished(), 500); } void tst_QWebEnginePage::undoActionHaveCustomText() @@ -4209,167 +4219,30 @@ void tst_QWebEnginePage::baseUrl() QCOMPARE(baseUrlSync(m_page), baseUrl); } -class DummyPaintEngine: public QPaintEngine { -public: - - DummyPaintEngine() - : QPaintEngine(QPaintEngine::AllFeatures) - , renderHints(0) - { - } - - bool begin(QPaintDevice*) - { - setActive(true); - return true; - } - - bool end() - { - setActive(false); - return false; - } - - void updateState(const QPaintEngineState& state) - { - renderHints = state.renderHints(); - } - - void drawPath(const QPainterPath&) { } - void drawPixmap(const QRectF&, const QPixmap&, const QRectF&) { } - - QPaintEngine::Type type() const - { - return static_cast<QPaintEngine::Type>(QPaintEngine::User + 2); - } - - QPainter::RenderHints renderHints; -}; - -class DummyPaintDevice: public QPaintDevice { -public: - DummyPaintDevice() - : QPaintDevice() - , m_engine(new DummyPaintEngine) - { - } - - ~DummyPaintDevice() - { - delete m_engine; - } - - QPaintEngine* paintEngine() const - { - return m_engine; - } - - QPainter::RenderHints renderHints() const - { - return m_engine->renderHints; - } - -protected: - int metric(PaintDeviceMetric metric) const; - -private: - DummyPaintEngine* m_engine; - friend class DummyPaintEngine; -}; - - -int DummyPaintDevice::metric(PaintDeviceMetric metric) const -{ - switch (metric) { - case PdmWidth: - return 400; - break; - - case PdmHeight: - return 200; - break; - - case PdmNumColors: - return INT_MAX; - break; - - case PdmDepth: - return 32; - break; - - default: - break; - } - return 0; -} - -void tst_QWebEnginePage::renderHints() -{ -#if !defined(QWEBENGINEPAGE_RENDER) - QSKIP("QWEBENGINEPAGE_RENDER"); -#else - QString html("<html><body><p>Hello, world!</p></body></html>"); - - QWebEnginePage page; - page.setHtml(html); - page.setViewportSize(page.contentsSize()); - - // We will call frame->render and trap the paint engine state changes - // to ensure that GraphicsContext does not clobber the render hints. - DummyPaintDevice buffer; - QPainter painter(&buffer); - - painter.setRenderHint(QPainter::TextAntialiasing, false); - page.render(&painter); - QVERIFY(!(buffer.renderHints() & QPainter::TextAntialiasing)); - QVERIFY(!(buffer.renderHints() & QPainter::SmoothPixmapTransform)); - QVERIFY(!(buffer.renderHints() & QPainter::HighQualityAntialiasing)); - - painter.setRenderHint(QPainter::TextAntialiasing, true); - page.render(&painter); - QVERIFY(buffer.renderHints() & QPainter::TextAntialiasing); - QVERIFY(!(buffer.renderHints() & QPainter::SmoothPixmapTransform)); - QVERIFY(!(buffer.renderHints() & QPainter::HighQualityAntialiasing)); - - painter.setRenderHint(QPainter::SmoothPixmapTransform, true); - page.render(&painter); - QVERIFY(buffer.renderHints() & QPainter::TextAntialiasing); - QVERIFY(buffer.renderHints() & QPainter::SmoothPixmapTransform); - QVERIFY(!(buffer.renderHints() & QPainter::HighQualityAntialiasing)); - - painter.setRenderHint(QPainter::HighQualityAntialiasing, true); - page.render(&painter); - QVERIFY(buffer.renderHints() & QPainter::TextAntialiasing); - QVERIFY(buffer.renderHints() & QPainter::SmoothPixmapTransform); - QVERIFY(buffer.renderHints() & QPainter::HighQualityAntialiasing); -#endif -} - void tst_QWebEnginePage::scrollPosition() { -#if !defined(QWEBENGINEPAGE_EVALUATEJAVASCRIPT) - QSKIP("QWEBENGINEPAGE_EVALUATEJAVASCRIPT"); -#else // enlarged image in a small viewport, to provoke the scrollbars to appear QString html("<html><body><img src='qrc:/image.png' height=500 width=500/></body></html>"); - QWebEnginePage page; - page.setViewportSize(QSize(200, 200)); + QWebEngineView view; + view.setFixedSize(200,200); + view.show(); - page.setHtml(html); - page.setScrollBarPolicy(Qt::Vertical, Qt::ScrollBarAlwaysOff); - page.setScrollBarPolicy(Qt::Horizontal, Qt::ScrollBarAlwaysOff); + QTest::qWaitForWindowExposed(&view); + + QSignalSpy loadSpy(view.page(), SIGNAL(loadFinished(bool))); + view.setHtml(html); + QTRY_COMPARE(loadSpy.count(), 1); // try to set the scroll offset programmatically - page.setScrollPosition(QPoint(23, 29)); - QCOMPARE(page.scrollPosition().x(), 23); - QCOMPARE(page.scrollPosition().y(), 29); + view.page()->runJavaScript("window.scrollTo(23, 29);"); + QTRY_COMPARE(view.page()->scrollPosition().x(), qreal(23)); + QCOMPARE(view.page()->scrollPosition().y(), qreal(29)); - int x = page.evaluateJavaScript("window.scrollX").toInt(); - int y = page.evaluateJavaScript("window.scrollY").toInt(); + int x = evaluateJavaScriptSync(view.page(), "window.scrollX").toInt(); + int y = evaluateJavaScriptSync(view.page(), "window.scrollY").toInt(); QCOMPARE(x, 23); QCOMPARE(y, 29); -#endif } void tst_QWebEnginePage::scrollToAnchor() @@ -5053,6 +4926,37 @@ void tst_QWebEnginePage::setZoomFactor() delete page; } +void tst_QWebEnginePage::printToPdf() +{ + QTemporaryDir tempDir(QDir::tempPath() + "/tst_qwebengineview-XXXXXX"); + QVERIFY(tempDir.isValid()); + QWebEnginePage page; + QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); + page.load(QUrl("qrc:///resources/basic_printing_page.html")); + QTRY_VERIFY(spy.count() == 1); + + QPageLayout layout(QPageSize(QPageSize::A4), QPageLayout::Portrait, QMarginsF(0.0, 0.0, 0.0, 0.0)); + QString path = tempDir.path() + "/print_1_success.pdf"; + page.printToPdf(path, layout); + QTRY_VERIFY(QFile::exists(path)); + +#if !defined(Q_OS_WIN) + path = tempDir.path() + "/print_//2_failed.pdf"; +#else + path = tempDir.path() + "/print_|2_failed.pdf"; +#endif + page.printToPdf(path, QPageLayout()); + QTRY_VERIFY(!QFile::exists(path)); + + CallbackSpy<QByteArray> successfulSpy; + page.printToPdf(successfulSpy.ref(), layout); + QVERIFY(successfulSpy.waitForResult().length() > 0); + + CallbackSpy<QByteArray> failedInvalidLayoutSpy; + page.printToPdf(failedInvalidLayoutSpy.ref(), QPageLayout()); + QCOMPARE(failedInvalidLayoutSpy.waitForResult().length(), 0); +} + void tst_QWebEnginePage::mouseButtonTranslation() { QWebEngineView *view = new QWebEngineView; diff --git a/tests/auto/widgets/qwebengineprofile/BLACKLIST b/tests/auto/widgets/qwebengineprofile/BLACKLIST new file mode 100644 index 000000000..fc1c957dd --- /dev/null +++ b/tests/auto/widgets/qwebengineprofile/BLACKLIST @@ -0,0 +1,5 @@ +[clearDataFromCache] +* +[disableCache] +* + diff --git a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp index 23a56a9cb..579a0f776 100644 --- a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp +++ b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp @@ -1,34 +1,26 @@ /**************************************************************************** ** -** Copyright (C) 2015 The Qt Company Ltd. -** Contact: http://www.qt.io/licensing/ +** Copyright (C) 2016 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:LGPL$ +** $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 http://www.qt.io/terms-conditions. For further -** information use the contact form at http://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPLv3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl.html. +** 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 2.0 or later as published by the Free -** Software Foundation and appearing in the file LICENSE.GPL included in -** the packaging of this file. Please review the following information to -** ensure the GNU General Public License version 2.0 requirements will be -** met: http://www.gnu.org/licenses/gpl-2.0.html. +** 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$ ** @@ -40,6 +32,7 @@ #include <QtWebEngineCore/qwebengineurlrequestjob.h> #include <QtWebEngineCore/qwebengineurlschemehandler.h> #include <QtWebEngineWidgets/qwebengineprofile.h> +#include <QtWebEngineWidgets/qwebenginepage.h> #include <QtWebEngineWidgets/qwebenginesettings.h> #include <QtWebEngineWidgets/qwebengineview.h> #include <QtWebEngineWidgets/qwebenginedownloaditem.h> @@ -51,8 +44,11 @@ class tst_QWebEngineProfile : public QObject private Q_SLOTS: void defaultProfile(); void profileConstructors(); + void clearDataFromCache(); + void disableCache(); void urlSchemeHandlers(); void urlSchemeHandlerFailRequest(); + void urlSchemeHandlerFailOnRead(); void customUserAgent(); void httpAcceptLanguage(); void downloadItem(); @@ -80,7 +76,77 @@ void tst_QWebEngineProfile::profileConstructors() QCOMPARE(diskProfile.httpCacheType(), QWebEngineProfile::DiskHttpCache); QCOMPARE(otrProfile.persistentCookiesPolicy(), QWebEngineProfile::NoPersistentCookies); QCOMPARE(diskProfile.persistentCookiesPolicy(), QWebEngineProfile::AllowPersistentCookies); +} + +void tst_QWebEngineProfile::clearDataFromCache() +{ + QWebEnginePage page; + + QDir cacheDir("./tst_QWebEngineProfile_cacheDir"); + cacheDir.makeAbsolute(); + if (cacheDir.exists()) + cacheDir.removeRecursively(); + cacheDir.mkpath(cacheDir.path()); + + QWebEngineProfile *profile = page.profile(); + profile->setCachePath(cacheDir.path()); + profile->setHttpCacheType(QWebEngineProfile::DiskHttpCache); + + QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); + page.load(QUrl("http://qt-project.org")); + if (!loadFinishedSpy.wait(10000) || !loadFinishedSpy.at(0).at(0).toBool()) + QSKIP("Couldn't load page from network, skipping test."); + + cacheDir.refresh(); + QVERIFY(cacheDir.entryList().contains("Cache")); + cacheDir.cd("./Cache"); + int filesBeforeClear = cacheDir.entryList().count(); + + QFileSystemWatcher fileSystemWatcher; + fileSystemWatcher.addPath(cacheDir.path()); + QSignalSpy directoryChangedSpy(&fileSystemWatcher, SIGNAL(directoryChanged(const QString &))); + + // It deletes most of the files, but not all of them. + profile->clearHttpCache(); + QTest::qWait(1000); + QTRY_VERIFY(directoryChangedSpy.count() > 0); + + cacheDir.refresh(); + QVERIFY(filesBeforeClear > cacheDir.entryList().count()); + cacheDir.removeRecursively(); +} + +void tst_QWebEngineProfile::disableCache() +{ + QWebEnginePage page; + QDir cacheDir("./tst_QWebEngineProfile_cacheDir"); + if (cacheDir.exists()) + cacheDir.removeRecursively(); + cacheDir.mkpath(cacheDir.path()); + + QWebEngineProfile *profile = page.profile(); + profile->setCachePath(cacheDir.path()); + QVERIFY(!cacheDir.entryList().contains("Cache")); + + profile->setHttpCacheType(QWebEngineProfile::NoCache); + QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool))); + page.load(QUrl("http://qt-project.org")); + if (!loadFinishedSpy.wait(10000) || !loadFinishedSpy.at(0).at(0).toBool()) + QSKIP("Couldn't load page from network, skipping test."); + + cacheDir.refresh(); + QVERIFY(!cacheDir.entryList().contains("Cache")); + + profile->setHttpCacheType(QWebEngineProfile::DiskHttpCache); + page.load(QUrl("http://qt-project.org")); + if (!loadFinishedSpy.wait(10000) || !loadFinishedSpy.at(1).at(0).toBool()) + QSKIP("Couldn't load page from network, skipping test."); + + cacheDir.refresh(); + QVERIFY(cacheDir.entryList().contains("Cache")); + + cacheDir.removeRecursively(); } class RedirectingUrlSchemeHandler : public QWebEngineUrlSchemeHandler @@ -174,10 +240,47 @@ class FailingUrlSchemeHandler : public QWebEngineUrlSchemeHandler public: void requestStarted(QWebEngineUrlRequestJob *job) override { - job->fail(QWebEngineUrlRequestJob::RequestFailed); + job->fail(QWebEngineUrlRequestJob::UrlInvalid); + } +}; + +class FailingIODevice : public QIODevice +{ +public: + FailingIODevice(QWebEngineUrlRequestJob *job) : m_job(job) + { + } + + qint64 readData(char *, qint64) Q_DECL_OVERRIDE + { + m_job->fail(QWebEngineUrlRequestJob::RequestFailed); + return -1; + } + qint64 writeData(const char *, qint64) Q_DECL_OVERRIDE + { + m_job->fail(QWebEngineUrlRequestJob::RequestFailed); + return -1; + } + void close() Q_DECL_OVERRIDE + { + QIODevice::close(); + deleteLater(); + } + +private: + QWebEngineUrlRequestJob *m_job; +}; + +class FailOnReadUrlSchemeHandler : public QWebEngineUrlSchemeHandler +{ +public: + void requestStarted(QWebEngineUrlRequestJob *job) override + { + job->reply(QByteArrayLiteral("text/plain"), new FailingIODevice(job)); } }; + void tst_QWebEngineProfile::urlSchemeHandlerFailRequest() { FailingUrlSchemeHandler handler; @@ -192,6 +295,20 @@ void tst_QWebEngineProfile::urlSchemeHandlerFailRequest() QCOMPARE(toPlainTextSync(view.page()), QString()); } +void tst_QWebEngineProfile::urlSchemeHandlerFailOnRead() +{ + FailOnReadUrlSchemeHandler handler; + QWebEngineProfile profile; + profile.installUrlSchemeHandler("foo", &handler); + QWebEngineView view; + QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool))); + view.setPage(new QWebEnginePage(&profile, &view)); + view.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); + view.load(QUrl(QStringLiteral("foo://bar"))); + QVERIFY(loadFinishedSpy.wait()); + QCOMPARE(toPlainTextSync(view.page()), QString()); +} + void tst_QWebEngineProfile::customUserAgent() { QString defaultUserAgent = QWebEngineProfile::defaultProfile()->httpUserAgent(); diff --git a/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp b/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp index 53762f54c..d5ecd8841 100644 --- a/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp +++ b/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp @@ -24,6 +24,7 @@ #include <qwebenginescriptcollection.h> #include <qwebengineview.h> #include "../util.h" +#include <QWebChannel> class tst_QWebEngineScript: public QObject { Q_OBJECT @@ -34,7 +35,9 @@ private Q_SLOTS: void injectionPoint_data(); void scriptWorld(); void scriptModifications(); - + void webChannel_data(); + void webChannel(); + void noTransportWithoutWebChannel(); }; void tst_QWebEngineScript::domEditing() @@ -115,12 +118,14 @@ void tst_QWebEngineScript::scriptWorld() page.load(QUrl("about:blank")); waitForSignal(&page, SIGNAL(loadFinished(bool))); QCOMPARE(evaluateJavaScriptSync(&page, "typeof(userScriptTest) != \"undefined\" && userScriptTest == 1;"), QVariant::fromValue(true)); + QCOMPARE(evaluateJavaScriptSyncInWorld(&page, "typeof(userScriptTest) == \"undefined\"", QWebEngineScript::ApplicationWorld), QVariant::fromValue(true)); script.setWorldId(QWebEngineScript::ApplicationWorld); page.scripts().clear(); page.scripts().insert(script); page.load(QUrl("about:blank")); waitForSignal(&page, SIGNAL(loadFinished(bool))); QCOMPARE(evaluateJavaScriptSync(&page, "typeof(userScriptTest) == \"undefined\""), QVariant::fromValue(true)); + QCOMPARE(evaluateJavaScriptSyncInWorld(&page, "typeof(userScriptTest) != \"undefined\" && userScriptTest == 1;", QWebEngineScript::ApplicationWorld), QVariant::fromValue(true)); } void tst_QWebEngineScript::scriptModifications() @@ -148,6 +153,92 @@ void tst_QWebEngineScript::scriptModifications() QVERIFY(page.scripts().count() == 0); } +class TestObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) +public: + TestObject(QObject *parent = 0) : QObject(parent) { } + + void setText(const QString &text) + { + if (text == m_text) + return; + m_text = text; + emit textChanged(text); + } + + QString text() const { return m_text; } + +signals: + void textChanged(const QString &text); + +private: + QString m_text; +}; + + +void tst_QWebEngineScript::webChannel_data() +{ + QTest::addColumn<int>("worldId"); + QTest::addColumn<bool>("reloadFirst"); + QTest::newRow("MainWorld") << static_cast<int>(QWebEngineScript::MainWorld) << false; + QTest::newRow("ApplicationWorld") << static_cast<int>(QWebEngineScript::ApplicationWorld) << false; + QTest::newRow("MainWorldWithReload") << static_cast<int>(QWebEngineScript::MainWorld) << true; + QTest::newRow("ApplicationWorldWithReload") << static_cast<int>(QWebEngineScript::ApplicationWorld) << true; +} + +void tst_QWebEngineScript::webChannel() +{ + QFETCH(int, worldId); + QFETCH(bool, reloadFirst); + QWebEnginePage page; + TestObject testObject; + QScopedPointer<QWebChannel> channel(new QWebChannel(this)); + channel->registerObject(QStringLiteral("object"), &testObject); + page.setWebChannel(channel.data(), worldId); + + QFile qwebchanneljs(":/qwebchannel.js"); + QVERIFY(qwebchanneljs.exists()); + qwebchanneljs.open(QFile::ReadOnly); + QByteArray scriptSrc = qwebchanneljs.readAll(); + qwebchanneljs.close(); + QWebEngineScript script; + script.setInjectionPoint(QWebEngineScript::DocumentCreation); + script.setWorldId(worldId); + script.setSourceCode(QString::fromLatin1(scriptSrc)); + page.scripts().insert(script); + page.setHtml(QStringLiteral("<html><body></body></html>")); + waitForSignal(&page, SIGNAL(loadFinished(bool))); + if (reloadFirst) { + // Check that the transport is also reinstalled on navigation + page.triggerAction(QWebEnginePage::Reload); + waitForSignal(&page, SIGNAL(loadFinished(bool))); + } + page.runJavaScript(QLatin1String( + "new QWebChannel(qt.webChannelTransport," + " function(channel) {" + " channel.objects.object.text = 'test';" + " }" + ");"), worldId); + waitForSignal(&testObject, SIGNAL(textChanged(QString))); + QCOMPARE(testObject.text(), QStringLiteral("test")); + + if (worldId != QWebEngineScript::MainWorld) + QCOMPARE(evaluateJavaScriptSync(&page, "qt.webChannelTransport"), QVariant(QVariant::Invalid)); +} + +void tst_QWebEngineScript::noTransportWithoutWebChannel() +{ + QWebEnginePage page; + page.setHtml(QStringLiteral("<html><body></body></html>")); + + QCOMPARE(evaluateJavaScriptSync(&page, "qt.webChannelTransport"), QVariant(QVariant::Invalid)); + page.triggerAction(QWebEnginePage::Reload); + waitForSignal(&page, SIGNAL(loadFinished(bool))); + QCOMPARE(evaluateJavaScriptSync(&page, "qt.webChannelTransport"), QVariant(QVariant::Invalid)); +} + QTEST_MAIN(tst_QWebEngineScript) #include "tst_qwebenginescript.moc" diff --git a/tests/auto/widgets/qwebenginespellcheck/dict/en-US.aff b/tests/auto/widgets/qwebenginespellcheck/dict/en-US.aff new file mode 100644 index 000000000..ff8185771 --- /dev/null +++ b/tests/auto/widgets/qwebenginespellcheck/dict/en-US.aff @@ -0,0 +1,5 @@ +SET UTF-8 +TRY esianrtolcdugmphbyfvkwzqESIANRTOLCDUGMPHBYFVKWZQ + +PFX Q Y 1 +PFX Q 0 q . diff --git a/tests/auto/widgets/qwebenginespellcheck/dict/en-US.dic b/tests/auto/widgets/qwebenginespellcheck/dict/en-US.dic new file mode 100644 index 000000000..3d4ecdfa4 --- /dev/null +++ b/tests/auto/widgets/qwebenginespellcheck/dict/en-US.dic @@ -0,0 +1,11 @@ +10 +he/Q +I/Q +it/Q +love/Q +loves/Q +qt/Q +she/Q +they/Q +we/Q +you/Q diff --git a/tests/auto/widgets/qwebenginespellcheck/qwebenginespellcheck.pro b/tests/auto/widgets/qwebenginespellcheck/qwebenginespellcheck.pro new file mode 100644 index 000000000..437aad937 --- /dev/null +++ b/tests/auto/widgets/qwebenginespellcheck/qwebenginespellcheck.pro @@ -0,0 +1,22 @@ +include(../tests.pri) + +DISTFILES += \ + dict/en-US.dic \ + dict/en-US.aff + +qtPrepareTool(CONVERT_TOOL, qwebengine_convert_dict) + +debug_and_release { + CONFIG(debug, debug|release): DICTIONARIES_DIR = debug/qtwebengine_dictionaries + else: DICTIONARIES_DIR = release/qtwebengine_dictionaries +} else { + DICTIONARIES_DIR = qtwebengine_dictionaries +} + +dict.files = $$PWD/dict/en-US.dic +dictoolbuild.input = dict.files +dictoolbuild.output = $${DICTIONARIES_DIR}/${QMAKE_FILE_BASE}.bdic +dictoolbuild.commands = $${CONVERT_TOOL} ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT} +dictoolbuild.name = Build ${QMAKE_FILE_IN_BASE} +dictoolbuild.CONFIG = no_link target_predeps +QMAKE_EXTRA_COMPILERS += dictoolbuild diff --git a/tests/auto/widgets/qwebenginespellcheck/resources/index.html b/tests/auto/widgets/qwebenginespellcheck/resources/index.html new file mode 100644 index 000000000..520979244 --- /dev/null +++ b/tests/auto/widgets/qwebenginespellcheck/resources/index.html @@ -0,0 +1,36 @@ +<html> + <head> + <script type="text/javascript"> + function makeEditable() + { + document.getElementsByClassName('textarea')[0].contentEditable = true; + } + + function text() + { + return document.getElementsByClassName('textarea')[0].innerHTML; + } + + function findWordPosition(text,word) + { + var divElement = document.getElementsByClassName('textarea')[0]; + divElement.innerHTML = text; + var regex = new RegExp(word,'g'); + divElement.innerHTML = divElement.innerHTML.replace(regex, '<span id="word">' + word + '</span>'); + var spanElement = document.getElementById('word'); + var rect = spanElement.getBoundingClientRect(); + var array = new Array(); + array.push(rect.left); + array.push(rect.top); + array.push(rect.right - rect.left); + array.push(rect.bottom - rect.top); + divElement.innerHTML = ""; + return array; + } + </script> + </head> + <body> + <div class="textarea" style="width:300px; height:200px; border: 1px solid #ccc"></div> + </body> +</html> + diff --git a/tests/auto/widgets/qwebenginespellcheck/tst_qwebenginespellcheck.cpp b/tests/auto/widgets/qwebenginespellcheck/tst_qwebenginespellcheck.cpp new file mode 100644 index 000000000..2dfe3305d --- /dev/null +++ b/tests/auto/widgets/qwebenginespellcheck/tst_qwebenginespellcheck.cpp @@ -0,0 +1,178 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 <QtTest/QtTest> +#include <QtWebEngineWidgets/qwebenginecontextmenudata.h> +#include <QtWebEngineWidgets/qwebengineprofile.h> +#include <QtWebEngineWidgets/qwebenginepage.h> +#include <QtWebEngineWidgets/qwebengineview.h> + +class WebView : public QWebEngineView +{ + Q_OBJECT +public: + void activateMenu(const QPoint &position) + { + QTest::mouseMove(focusWidget(), position); + QTest::mousePress(focusWidget(), Qt::RightButton, 0, position); + QContextMenuEvent evcont(QContextMenuEvent::Mouse, position, mapToGlobal(position)); + event(&evcont); + } + + const QWebEngineContextMenuData& data() + { + return m_data; + } + +signals: + void menuReady(); + +protected: + void contextMenuEvent(QContextMenuEvent *) + { + m_data = page()->contextMenuData(); + emit menuReady(); + } +private: + QWebEngineContextMenuData m_data; +}; + +class tst_QWebEngineSpellcheck : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void init(); + void cleanup(); + void initTestCase(); + void spellCheckLanguage(); + void spellCheckEnabled(); + void spellcheck(); + +private: + void load(); + WebView *m_view; +}; + +void tst_QWebEngineSpellcheck::initTestCase() +{ + QWebEngineProfile *profile = QWebEngineProfile::defaultProfile(); + QVERIFY(profile); + QVERIFY(!profile->isSpellCheckEnabled()); + QVERIFY(profile->spellCheckLanguage().isEmpty()); +} + +void tst_QWebEngineSpellcheck::init() +{ + QWebEngineProfile *profile = QWebEngineProfile::defaultProfile(); + profile->setSpellCheckEnabled(false); + profile->setSpellCheckLanguage(QString::null); + m_view = new WebView(); +} + +void tst_QWebEngineSpellcheck::load() +{ + m_view->page()->load(QUrl("qrc:///resources/index.html")); + m_view->show(); + waitForSignal(m_view->page(), SIGNAL(loadFinished(bool))); +} + +void tst_QWebEngineSpellcheck::cleanup() +{ + delete m_view; +} + +void tst_QWebEngineSpellcheck::spellCheckLanguage() +{ + QWebEngineProfile *profile = QWebEngineProfile::defaultProfile(); + QVERIFY(profile); + profile->setSpellCheckLanguage("en-US"); + QVERIFY(profile->spellCheckLanguage() == "en-US"); +} + +void tst_QWebEngineSpellcheck::spellCheckEnabled() +{ + QWebEngineProfile *profile = QWebEngineProfile::defaultProfile(); + QVERIFY(profile); + profile->setSpellCheckEnabled(true); + QVERIFY(profile->isSpellCheckEnabled()); +} + +void tst_QWebEngineSpellcheck::spellcheck() +{ + QWebEngineProfile *profile = QWebEngineProfile::defaultProfile(); + QVERIFY(profile); + profile->setSpellCheckLanguage("en-US"); + profile->setSpellCheckEnabled(true); + load(); + + // make textarea editable + evaluateJavaScriptSync(m_view->page(), "makeEditable();"); + + // calcuate position of misspelled word + QVariantList list = evaluateJavaScriptSync(m_view->page(), "findWordPosition('I lovee Qt ....','lovee');").toList(); + QRect rect(list[0].value<int>(),list[1].value<int>(),list[2].value<int>(),list[3].value<int>()); + + //type text, spellchecker needs time + QTest::mouseMove(m_view->focusWidget(), QPoint(20,20)); + QTest::mousePress(m_view->focusWidget(), Qt::LeftButton, 0, QPoint(20,20)); + QString text("I lovee Qt ...."); + for (int i = 0; i < text.length(); i++) { + QTest::keyClicks(m_view->focusWidget(), text.at(i)); + QTest::qWait(60); + } + + // make sure text is there + QString result = evaluateJavaScriptSync(m_view->page(), "text();").toString(); + QVERIFY(result == text); + + // open menu on misspelled word + m_view->activateMenu(rect.center()); + waitForSignal(m_view, SIGNAL(menuReady())); + + // check if menu is valid + QVERIFY(m_view->data().isValid()); + QVERIFY(m_view->data().isContentEditable()); + + // check misspelled word + QVERIFY(m_view->data().misspelledWord() == "lovee"); + + // check suggestions + QStringList expected {"love", "loves"}; + QVERIFY(m_view->data().spellCheckerSuggestions() == expected); + + // check replace word + m_view->page()->replaceMisspelledWord("love"); + text = "I love Qt ...."; + result = evaluateJavaScriptSync(m_view->page(), "text();").toString(); + QVERIFY(result == text); +} + +QTEST_MAIN(tst_QWebEngineSpellcheck) +#include "tst_qwebenginespellcheck.moc" diff --git a/tests/auto/widgets/qwebenginespellcheck/tst_qwebenginespellcheck.qrc b/tests/auto/widgets/qwebenginespellcheck/tst_qwebenginespellcheck.qrc new file mode 100644 index 000000000..505b932c7 --- /dev/null +++ b/tests/auto/widgets/qwebenginespellcheck/tst_qwebenginespellcheck.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file>resources/index.html</file> + </qresource> +</RCC> diff --git a/tests/auto/widgets/qwebengineview/resources/basic_printing_page.html b/tests/auto/widgets/qwebengineview/resources/basic_printing_page.html new file mode 100644 index 000000000..0c6ff379f --- /dev/null +++ b/tests/auto/widgets/qwebengineview/resources/basic_printing_page.html @@ -0,0 +1,8 @@ +<html> +<head> +<title> Basic Printing Page </title> +</head> +<body> +<h1>Hello Paper World</h1> +</body> +</html> diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp index e66bf18cd..afb53ba20 100644 --- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp +++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp @@ -1,5 +1,5 @@ /* - Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2016 The Qt Company Ltd. Copyright (C) 2009 Torch Mobile Inc. Copyright (C) 2009 Girish Ramakrishnan <girish@forwardbias.in> @@ -23,12 +23,14 @@ #include "../util.h" #include <qpainter.h> +#include <qpagelayout.h> #include <qwebengineview.h> #include <qwebenginepage.h> #include <qwebenginesettings.h> #include <qnetworkrequest.h> #include <qdiriterator.h> #include <qstackedlayout.h> +#include <qtemporarydir.h> #define VERIFY_INPUTMETHOD_HINTS(actual, expect) \ QVERIFY(actual == expect); diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.qrc b/tests/auto/widgets/qwebengineview/tst_qwebengineview.qrc index 6685a8086..b32b533c2 100644 --- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.qrc +++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.qrc @@ -1,8 +1,9 @@ -<!DOCTYPE RCC><RCC version="1.0"> -<qresource> - <file>resources/index.html</file> - <file>resources/frame_a.html</file> - <file>resources/input_types.html</file> - <file>resources/scrolltest_page.html</file> -</qresource> +<RCC> + <qresource prefix="/"> + <file>resources/index.html</file> + <file>resources/frame_a.html</file> + <file>resources/input_types.html</file> + <file>resources/scrolltest_page.html</file> + <file>resources/basic_printing_page.html</file> + </qresource> </RCC> diff --git a/tests/auto/widgets/resources/qwebchannel.js b/tests/auto/widgets/resources/qwebchannel.js new file mode 100644 index 000000000..1da8f5496 --- /dev/null +++ b/tests/auto/widgets/resources/qwebchannel.js @@ -0,0 +1,408 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com> +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebChannel 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$ +** +****************************************************************************/ + +"use strict"; + +var QWebChannelMessageTypes = { + signal: 1, + propertyUpdate: 2, + init: 3, + idle: 4, + debug: 5, + invokeMethod: 6, + connectToSignal: 7, + disconnectFromSignal: 8, + setProperty: 9, + response: 10, +}; + +var QWebChannel = function(transport, initCallback) +{ + if (typeof transport !== "object" || typeof transport.send !== "function") { + console.error("The QWebChannel expects a transport object with a send function and onmessage callback property." + + " Given is: transport: " + typeof(transport) + ", transport.send: " + typeof(transport.send)); + return; + } + + var channel = this; + this.transport = transport; + + this.send = function(data) + { + if (typeof(data) !== "string") { + data = JSON.stringify(data); + } + channel.transport.send(data); + } + + this.transport.onmessage = function(message) + { + var data = message.data; + if (typeof data === "string") { + data = JSON.parse(data); + } + switch (data.type) { + case QWebChannelMessageTypes.signal: + channel.handleSignal(data); + break; + case QWebChannelMessageTypes.response: + channel.handleResponse(data); + break; + case QWebChannelMessageTypes.propertyUpdate: + channel.handlePropertyUpdate(data); + break; + default: + console.error("invalid message received:", message.data); + break; + } + } + + this.execCallbacks = {}; + this.execId = 0; + this.exec = function(data, callback) + { + if (!callback) { + // if no callback is given, send directly + channel.send(data); + return; + } + if (channel.execId === Number.MAX_VALUE) { + // wrap + channel.execId = Number.MIN_VALUE; + } + if (data.hasOwnProperty("id")) { + console.error("Cannot exec message with property id: " + JSON.stringify(data)); + return; + } + data.id = channel.execId++; + channel.execCallbacks[data.id] = callback; + channel.send(data); + }; + + this.objects = {}; + + this.handleSignal = function(message) + { + var object = channel.objects[message.object]; + if (object) { + object.signalEmitted(message.signal, message.args); + } else { + console.warn("Unhandled signal: " + message.object + "::" + message.signal); + } + } + + this.handleResponse = function(message) + { + if (!message.hasOwnProperty("id")) { + console.error("Invalid response message received: ", JSON.stringify(message)); + return; + } + channel.execCallbacks[message.id](message.data); + delete channel.execCallbacks[message.id]; + } + + this.handlePropertyUpdate = function(message) + { + for (var i in message.data) { + var data = message.data[i]; + var object = channel.objects[data.object]; + if (object) { + object.propertyUpdate(data.signals, data.properties); + } else { + console.warn("Unhandled property update: " + data.object + "::" + data.signal); + } + } + channel.exec({type: QWebChannelMessageTypes.idle}); + } + + this.debug = function(message) + { + channel.send({type: QWebChannelMessageTypes.debug, data: message}); + }; + + channel.exec({type: QWebChannelMessageTypes.init}, function(data) { + for (var objectName in data) { + var object = new QObject(objectName, data[objectName], channel); + } + // now unwrap properties, which might reference other registered objects + for (var objectName in channel.objects) { + channel.objects[objectName].unwrapProperties(); + } + if (initCallback) { + initCallback(channel); + } + channel.exec({type: QWebChannelMessageTypes.idle}); + }); +}; + +function QObject(name, data, webChannel) +{ + this.__id__ = name; + webChannel.objects[name] = this; + + // List of callbacks that get invoked upon signal emission + this.__objectSignals__ = {}; + + // Cache of all properties, updated when a notify signal is emitted + this.__propertyCache__ = {}; + + var object = this; + + // ---------------------------------------------------------------------- + + this.unwrapQObject = function(response) + { + if (response instanceof Array) { + // support list of objects + var ret = new Array(response.length); + for (var i = 0; i < response.length; ++i) { + ret[i] = object.unwrapQObject(response[i]); + } + return ret; + } + if (!response + || !response["__QObject*__"] + || response.id === undefined) { + return response; + } + + var objectId = response.id; + if (webChannel.objects[objectId]) + return webChannel.objects[objectId]; + + if (!response.data) { + console.error("Cannot unwrap unknown QObject " + objectId + " without data."); + return; + } + + var qObject = new QObject( objectId, response.data, webChannel ); + qObject.destroyed.connect(function() { + if (webChannel.objects[objectId] === qObject) { + delete webChannel.objects[objectId]; + // reset the now deleted QObject to an empty {} object + // just assigning {} though would not have the desired effect, but the + // below also ensures all external references will see the empty map + // NOTE: this detour is necessary to workaround QTBUG-40021 + var propertyNames = []; + for (var propertyName in qObject) { + propertyNames.push(propertyName); + } + for (var idx in propertyNames) { + delete qObject[propertyNames[idx]]; + } + } + }); + // here we are already initialized, and thus must directly unwrap the properties + qObject.unwrapProperties(); + return qObject; + } + + this.unwrapProperties = function() + { + for (var propertyIdx in object.__propertyCache__) { + object.__propertyCache__[propertyIdx] = object.unwrapQObject(object.__propertyCache__[propertyIdx]); + } + } + + function addSignal(signalData, isPropertyNotifySignal) + { + var signalName = signalData[0]; + var signalIndex = signalData[1]; + object[signalName] = { + connect: function(callback) { + if (typeof(callback) !== "function") { + console.error("Bad callback given to connect to signal " + signalName); + return; + } + + object.__objectSignals__[signalIndex] = object.__objectSignals__[signalIndex] || []; + object.__objectSignals__[signalIndex].push(callback); + + if (!isPropertyNotifySignal && signalName !== "destroyed") { + // only required for "pure" signals, handled separately for properties in propertyUpdate + // also note that we always get notified about the destroyed signal + webChannel.exec({ + type: QWebChannelMessageTypes.connectToSignal, + object: object.__id__, + signal: signalIndex + }); + } + }, + disconnect: function(callback) { + if (typeof(callback) !== "function") { + console.error("Bad callback given to disconnect from signal " + signalName); + return; + } + object.__objectSignals__[signalIndex] = object.__objectSignals__[signalIndex] || []; + var idx = object.__objectSignals__[signalIndex].indexOf(callback); + if (idx === -1) { + console.error("Cannot find connection of signal " + signalName + " to " + callback.name); + return; + } + object.__objectSignals__[signalIndex].splice(idx, 1); + if (!isPropertyNotifySignal && object.__objectSignals__[signalIndex].length === 0) { + // only required for "pure" signals, handled separately for properties in propertyUpdate + webChannel.exec({ + type: QWebChannelMessageTypes.disconnectFromSignal, + object: object.__id__, + signal: signalIndex + }); + } + } + }; + } + + /** + * Invokes all callbacks for the given signalname. Also works for property notify callbacks. + */ + function invokeSignalCallbacks(signalName, signalArgs) + { + var connections = object.__objectSignals__[signalName]; + if (connections) { + connections.forEach(function(callback) { + callback.apply(callback, signalArgs); + }); + } + } + + this.propertyUpdate = function(signals, propertyMap) + { + // update property cache + for (var propertyIndex in propertyMap) { + var propertyValue = propertyMap[propertyIndex]; + object.__propertyCache__[propertyIndex] = propertyValue; + } + + for (var signalName in signals) { + // Invoke all callbacks, as signalEmitted() does not. This ensures the + // property cache is updated before the callbacks are invoked. + invokeSignalCallbacks(signalName, signals[signalName]); + } + } + + this.signalEmitted = function(signalName, signalArgs) + { + invokeSignalCallbacks(signalName, signalArgs); + } + + function addMethod(methodData) + { + var methodName = methodData[0]; + var methodIdx = methodData[1]; + object[methodName] = function() { + var args = []; + var callback; + for (var i = 0; i < arguments.length; ++i) { + if (typeof arguments[i] === "function") + callback = arguments[i]; + else + args.push(arguments[i]); + } + + webChannel.exec({ + "type": QWebChannelMessageTypes.invokeMethod, + "object": object.__id__, + "method": methodIdx, + "args": args + }, function(response) { + if (response !== undefined) { + var result = object.unwrapQObject(response); + if (callback) { + (callback)(result); + } + } + }); + }; + } + + function bindGetterSetter(propertyInfo) + { + var propertyIndex = propertyInfo[0]; + var propertyName = propertyInfo[1]; + var notifySignalData = propertyInfo[2]; + // initialize property cache with current value + // NOTE: if this is an object, it is not directly unwrapped as it might + // reference other QObject that we do not know yet + object.__propertyCache__[propertyIndex] = propertyInfo[3]; + + if (notifySignalData) { + if (notifySignalData[0] === 1) { + // signal name is optimized away, reconstruct the actual name + notifySignalData[0] = propertyName + "Changed"; + } + addSignal(notifySignalData, true); + } + + Object.defineProperty(object, propertyName, { + configurable: true, + get: function () { + var propertyValue = object.__propertyCache__[propertyIndex]; + if (propertyValue === undefined) { + // This shouldn't happen + console.warn("Undefined value in property cache for property \"" + propertyName + "\" in object " + object.__id__); + } + + return propertyValue; + }, + set: function(value) { + if (value === undefined) { + console.warn("Property setter for " + propertyName + " called with undefined value!"); + return; + } + object.__propertyCache__[propertyIndex] = value; + webChannel.exec({ + "type": QWebChannelMessageTypes.setProperty, + "object": object.__id__, + "property": propertyIndex, + "value": value + }); + } + }); + + } + + // ---------------------------------------------------------------------- + + data.methods.forEach(addMethod); + + data.properties.forEach(bindGetterSetter); + + data.signals.forEach(function(signal) { addSignal(signal, false); }); + + for (var name in data.enums) { + object[name] = data.enums[name]; + } +} + +//required for use with nodejs +if (typeof module === 'object') { + module.exports = { + QWebChannel: QWebChannel + }; +} diff --git a/tests/auto/widgets/resources/tests.qrc b/tests/auto/widgets/resources/tests.qrc new file mode 100644 index 000000000..5e9df2873 --- /dev/null +++ b/tests/auto/widgets/resources/tests.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file>qwebchannel.js</file> + </qresource> +</RCC> diff --git a/tests/auto/widgets/tests.pri b/tests/auto/widgets/tests.pri index 8a62ce2a6..ca19a9496 100644 --- a/tests/auto/widgets/tests.pri +++ b/tests/auto/widgets/tests.pri @@ -9,6 +9,7 @@ TARGET = tst_$$TARGET SOURCES += $${TARGET}.cpp INCLUDEPATH += $$PWD +RESOURCES += ../resources/tests.qrc exists($$_PRO_FILE_PWD_/$${TARGET}.qrc): RESOURCES += $${TARGET}.qrc QT += testlib network webenginewidgets widgets diff --git a/tests/auto/widgets/util.h b/tests/auto/widgets/util.h index 2b485fc0f..770579f1f 100644 --- a/tests/auto/widgets/util.h +++ b/tests/auto/widgets/util.h @@ -1,21 +1,31 @@ -/* - Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ +/**************************************************************************** +** +** Copyright (C) 2016 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$ +** +****************************************************************************/ + // Functions and macros that really need to be in QTestLib #if 0 @@ -156,6 +166,13 @@ static inline QVariant evaluateJavaScriptSync(QWebEnginePage *page, const QStrin return spy.waitForResult(); } +static inline QVariant evaluateJavaScriptSyncInWorld(QWebEnginePage *page, const QString &script, int worldId) +{ + CallbackSpy<QVariant> spy; + page->runJavaScript(script, worldId, spy.ref()); + return spy.waitForResult(); +} + static inline QUrl baseUrlSync(QWebEnginePage *page) { CallbackSpy<QVariant> spy; diff --git a/tests/auto/widgets/widgets.pro b/tests/auto/widgets/widgets.pro index 3220b1379..2f5416701 100644 --- a/tests/auto/widgets/widgets.pro +++ b/tests/auto/widgets/widgets.pro @@ -3,6 +3,7 @@ TEMPLATE = subdirs SUBDIRS += \ qwebengineaccessibility \ qwebenginedefaultsurfaceformat \ + qwebenginefaviconmanager \ qwebenginepage \ qwebenginehistory \ qwebenginehistoryinterface \ @@ -12,7 +13,7 @@ SUBDIRS += \ qwebenginesettings \ qwebengineview -qtHaveModule(positioning) { - SUBDIRS += positionplugin - qwebenginepage.depends = positionplugin +# QTBUG-53135, osx does not use hunspell +!contains(WEBENGINE_CONFIG, no_spellcheck):!osx:!cross_compile { + SUBDIRS += qwebenginespellcheck } |