/**************************************************************************** ** ** 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 #include #include #include #include #include class WebView : public QWebEngineView { Q_OBJECT public: void activateMenu(QWidget *widget, const QPoint &position) { QTest::mouseMove(widget, position); QTest::mousePress(widget, Qt::RightButton, 0, position); QContextMenuEvent evcont(QContextMenuEvent::Mouse, position, mapToGlobal(position)); event(&evcont); QTest::mouseRelease(widget, Qt::RightButton, 0, position); } 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_Spellchecking : public QObject { Q_OBJECT private Q_SLOTS: void init(); void cleanup(); void settings(); void spellcheck(); void spellcheck_data(); private: void load(); WebView *m_view; }; void tst_Spellchecking::init() { m_view = new WebView(); } void tst_Spellchecking::load() { m_view->page()->load(QUrl("qrc:///resources/index.html")); m_view->show(); QSignalSpy spyFinished(m_view->page(), &QWebEnginePage::loadFinished); QVERIFY(spyFinished.wait()); } void tst_Spellchecking::cleanup() { delete m_view; } void tst_Spellchecking::settings() { // Default profile has spellchecking disabled QVERIFY(!QWebEngineProfile::defaultProfile()->isSpellCheckEnabled()); QVERIFY(QWebEngineProfile::defaultProfile()->spellCheckLanguages().isEmpty()); // New named profiles have spellchecking disabled auto profile1 = std::make_unique(QStringLiteral("Profile1")); QVERIFY(!profile1->isSpellCheckEnabled()); QVERIFY(profile1->spellCheckLanguages().isEmpty()); auto profile2 = std::make_unique(QStringLiteral("Profile2")); QVERIFY(!profile2->isSpellCheckEnabled()); QVERIFY(profile2->spellCheckLanguages().isEmpty()); // New otr profiles have spellchecking disabled auto profile3 = std::make_unique(); QVERIFY(!profile2->isSpellCheckEnabled()); QVERIFY(profile2->spellCheckLanguages().isEmpty()); // Settings can be changed profile1->setSpellCheckEnabled(true); QVERIFY(profile1->isSpellCheckEnabled()); profile1->setSpellCheckLanguages({"en-US"}); QVERIFY(profile1->spellCheckLanguages() == QStringList({"en-US"})); profile1->setSpellCheckLanguages({"en-US","de-DE"}); QVERIFY(profile1->spellCheckLanguages() == QStringList({"en-US","de-DE"})); // Settings are per profile QVERIFY(!profile2->isSpellCheckEnabled()); QVERIFY(profile2->spellCheckLanguages().isEmpty()); QVERIFY(!profile3->isSpellCheckEnabled()); QVERIFY(profile3->spellCheckLanguages().isEmpty()); // Settings are not persisted // TODO(juvaldma): Write from dtor currently usually happens *after* the // read from the ctor, so this test would pass even if settings were // persisted. It would start to fail on the second run though. profile1.reset(); profile1 = std::make_unique(QStringLiteral("Profile1")); QVERIFY(!profile1->isSpellCheckEnabled()); QVERIFY(profile1->spellCheckLanguages().isEmpty()); } void tst_Spellchecking::spellcheck() { QFETCH(QStringList, languages); QFETCH(QStringList, suggestions); m_view->settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true); QWebEngineProfile *profile = QWebEngineProfile::defaultProfile(); QVERIFY(profile); profile->setSpellCheckLanguages(languages); profile->setSpellCheckEnabled(true); load(); QCOMPARE(profile->spellCheckLanguages(), languages); // make textarea editable evaluateJavaScriptSync(m_view->page(), "makeEditable();"); // calcuate position of misspelled word QVariantList list = evaluateJavaScriptSync(m_view->page(), "findWordPosition('I lowe Qt ....','lowe');").toList(); QRect rect(list[0].value(),list[1].value(),list[2].value(),list[3].value()); //type text, spellchecker needs time QTest::mouseMove(m_view->focusWidget(), QPoint(20,20)); QTest::mousePress(m_view->focusWidget(), Qt::LeftButton, 0, QPoint(20,20)); QTest::mouseRelease(m_view->focusWidget(), Qt::LeftButton, 0, QPoint(20,20)); QString text("I lowe 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); bool gotMisspelledWord = false; // clumsy QTRY_VERIFY still execs expr after first success QString detail; // check that spellchecker has done text processing and filled misspelled word QTRY_VERIFY2([&] () { detail.clear(); if (gotMisspelledWord) return true; // open menu on misspelled word m_view->activateMenu(m_view->focusWidget(), rect.center()); QSignalSpy spyMenuReady(m_view, &WebView::menuReady); if (!spyMenuReady.wait()) { detail = "menu was not shown"; return false; } if (!m_view->data().isValid()) { detail = "invalid data"; return false; } if (!m_view->data().isContentEditable()) { detail = "content is not editable"; return false; } if (m_view->data().misspelledWord().isEmpty()) { detail = "no misspelled word"; return false; }; gotMisspelledWord = true; return true; } (), qPrintable(QString("Context menu: %1").arg(detail))); // check misspelled word QCOMPARE(m_view->data().misspelledWord(), QStringLiteral("lowe")); // check suggestions QCOMPARE(m_view->data().spellCheckerSuggestions(), suggestions); // check replace word m_view->page()->replaceMisspelledWord("love"); text = "I love Qt ...."; QTRY_VERIFY(evaluateJavaScriptSync(m_view->page(), "text();").toString() == text); } void tst_Spellchecking::spellcheck_data() { QTest::addColumn("languages"); QTest::addColumn("suggestions"); QTest::newRow("en-US") << QStringList({"en-US"}) << QStringList({"low", "love"}); QTest::newRow("en-US,de-DE") << QStringList({"en-US", "de-DE"}) << QStringList({"löwe", "low", "love"}); } QTEST_MAIN(tst_Spellchecking) #include "tst_spellchecking.moc"