File indexing completed on 2024-12-01 12:40:33

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2021 Felix Ernst <fe.a.ernst@gmail.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0006 */
0007 
0008 #include "ktooltiphelper_unittest.h"
0009 
0010 #include <KToolTipHelper>
0011 
0012 #include <QTest>
0013 
0014 #include <QAction>
0015 #include <QApplication>
0016 #include <QFrame>
0017 #include <QHBoxLayout>
0018 #include <QLabel>
0019 #include <QMenu>
0020 #include <QStyle>
0021 #include <QToolButton>
0022 #include <QToolTip>
0023 #include <QWhatsThis>
0024 
0025 #include <memory>
0026 
0027 QString KToolTipHelper_UnitTest::shownToolTip(QWidget *widget)
0028 {
0029     QTest::mouseMove(m_frameWithoutToolTip);
0030     const bool resetWorked = QTest::qWaitFor(
0031         []() {
0032             return !QToolTip::isVisible();
0033         },
0034         3000);
0035     if (!resetWorked) {
0036         qWarning("The tooltip did not properly hide itself after moving to an area without tooltip.");
0037     }
0038 
0039     QTest::mouseMove(widget);
0040     if (!QTest::qWaitFor(&QToolTip::isVisible, widget->style()->styleHint(QStyle::SH_ToolTip_WakeUpDelay, nullptr, widget) + 1000)) {
0041         return QStringLiteral("");
0042     }
0043     return QToolTip::text();
0044 }
0045 
0046 void KToolTipHelper_UnitTest::initTestCase()
0047 {
0048     m_window.reset(new QMainWindow());
0049     m_centralWidget = new QWidget(m_window.get());
0050     m_centralWidget->setGeometry(0, 0, 100, 100);
0051     m_window->setCentralWidget(m_centralWidget);
0052 
0053     QHBoxLayout *layout = new QHBoxLayout(m_centralWidget);
0054 
0055     m_frame = new QFrame(m_centralWidget);
0056     m_frame->setFrameStyle(3);
0057     new QLabel(QStringLiteral("test area"), m_frame);
0058     layout->addWidget(m_frame);
0059 
0060     m_toolButton = new QToolButton(m_centralWidget);
0061     layout->addWidget(m_toolButton);
0062 
0063     m_frameWithoutToolTip = new QFrame(m_centralWidget);
0064     m_frameWithoutToolTip->setFrameStyle(2);
0065     new QLabel(QStringLiteral("no tooltip"), m_frameWithoutToolTip);
0066     layout->addWidget(m_frameWithoutToolTip);
0067 
0068     qApp->installEventFilter(KToolTipHelper::instance());
0069 
0070     m_window->show();
0071     m_window->ensurePolished();
0072 }
0073 
0074 void KToolTipHelper_UnitTest::testGeneralWidget()
0075 {
0076     // tests without whatsThis()
0077     QVERIFY(shownToolTip(m_frameWithoutToolTip).isEmpty());
0078     m_frame->setToolTip(QStringLiteral("frame's tooltip"));
0079     QCOMPARE(shownToolTip(m_frame), m_frame->toolTip());
0080 
0081     QHelpEvent *helpEvent = new QHelpEvent(QEvent::ToolTip, QPoint(1, 1), m_frame->mapToGlobal(QPoint(1, 1)));
0082     QVERIFY2(!KToolTipHelper::instance()->eventFilter(m_frame, helpEvent),
0083              "These most basic tooltips should not be filtered so applications can still rely"
0084              "on tooltip events in most cases.");
0085 
0086     // tests with whatsThis()
0087     m_frame->setToolTip(QStringLiteral(""));
0088     m_frame->setWhatsThis(QStringLiteral("frame's whatsThis"));
0089     QVERIFY2(shownToolTip(m_frame).isEmpty(), "No whatsThisHint should be shown when no toolTip was set.");
0090 
0091     m_frame->setToolTip(KToolTipHelper::whatsThisHintOnly());
0092     QVERIFY2(shownToolTip(m_frame) != KToolTipHelper::whatsThisHintOnly(),
0093              "The KToolTipHelper::whatsThisHintOnly()-string is no user-facing string"
0094              "and should therefore never actually be displayed.");
0095 
0096     // test when whatsThis() == toolTip()
0097     m_frame->setToolTip(QStringLiteral("frame's whatsThis"));
0098     const QString noWhatsThisToolTip = shownToolTip(m_frame);
0099     QVERIFY(noWhatsThisToolTip.contains(m_frame->toolTip()));
0100     QVERIFY2(noWhatsThisToolTip.length() == m_frame->toolTip().length(), "No whatsThisHint should be shown when the toolTip is a similar string.");
0101 
0102     m_frame->setToolTip(QStringLiteral("frame's tooltip"));
0103     const QString toolTip = shownToolTip(m_frame);
0104     QVERIFY(toolTip.contains(m_frame->toolTip()));
0105     QVERIFY2(toolTip.length() > m_frame->toolTip().length(), "The frame's toolTip is supposed to contain the whatsThisHint.");
0106 
0107     auto layout = new QVBoxLayout(m_frame);
0108     auto subFrame = std::unique_ptr<QFrame>(new QFrame(m_frame));
0109     new QLabel(QStringLiteral("subFrame"), subFrame.get());
0110     layout->addWidget(subFrame.get());
0111     QCOMPARE(shownToolTip(subFrame.get()), toolTip);
0112 
0113     subFrame->setToolTip(QStringLiteral("subFrame's tooltip"));
0114     QCOMPARE(shownToolTip(subFrame.get()), subFrame->toolTip());
0115 }
0116 
0117 void KToolTipHelper_UnitTest::testInvokingWhatsThis()
0118 {
0119     m_frame->setWhatsThis(QStringLiteral("frame's whatsThis"));
0120     m_frame->setToolTip(KToolTipHelper::whatsThisHintOnly());
0121     shownToolTip(m_frame);
0122     QTest::keyClick(m_frame, Qt::Key_Shift);
0123     QVERIFY2(QTest::qWaitFor(
0124                  []() {
0125                      return !QToolTip::isVisible();
0126                  },
0127                  4000),
0128              "whatsThis should be shown now.");
0129     QVERIFY2(shownToolTip(m_frame).isEmpty(),
0130              "A toolTip was shown which shouldn't be possible because a WhatsThis widget"
0131              "should be displayed at this moment.");
0132     QWhatsThis::hideText();
0133     QVERIFY2(!shownToolTip(m_frame).isEmpty(),
0134              "A toolTip was not shown although this should now be possible again "
0135              "because the WhatsThis widget was just hidden.");
0136 }
0137 
0138 void KToolTipHelper_UnitTest::testQToolButton()
0139 {
0140     QVERIFY(shownToolTip(m_toolButton).isEmpty());
0141 
0142     auto action = std::unique_ptr<QAction>(new QAction(QStringLiteral("action")));
0143     action->setToolTip(QStringLiteral("action's tooltip"));
0144     m_toolButton->setDefaultAction(action.get());
0145     QCOMPARE(shownToolTip(m_toolButton), action->toolTip());
0146 
0147     auto helpEvent = std::unique_ptr<QHelpEvent>(new QHelpEvent(QEvent::ToolTip, QPoint(1, 1), m_toolButton->mapToGlobal(QPoint(1, 1))));
0148     QVERIFY2(!KToolTipHelper::instance()->eventFilter(m_toolButton, helpEvent.get()),
0149              "These most basic tooltips should not be filtered so applications can still rely"
0150              "on tooltip events in most cases.");
0151 
0152     action->setShortcut(Qt::CTRL | Qt::Key_K);
0153     const QString toolTip(shownToolTip(m_toolButton));
0154     QVERIFY(toolTip.contains(action->toolTip()));
0155     // qDebug("%s > %s", qPrintable(toolTip), qPrintable(action->toolTip()));
0156     QVERIFY2(toolTip.length() > action->toolTip().length(), "The Keyboard shortcut should be visible.");
0157 
0158     action->setWhatsThis(QStringLiteral("action's whatsThis"));
0159     const QString toolTipWithWhatsThisHint(shownToolTip(m_toolButton));
0160     QVERIFY(toolTipWithWhatsThisHint.contains(toolTip));
0161     // qDebug("%s > %s", qPrintable(toolTipWithWhatsThisHint), qPrintable(toolTip));
0162     QVERIFY2(toolTipWithWhatsThisHint.length() > toolTip.length(), "The whatsThisHint should be visible.");
0163 
0164     action->setShortcut(QKeySequence());
0165     QVERIFY(shownToolTip(m_toolButton).length() < toolTipWithWhatsThisHint.length());
0166 
0167     action->setWhatsThis(QStringLiteral(""));
0168     QCOMPARE(shownToolTip(m_toolButton), action->toolTip());
0169 
0170     action->setToolTip(KToolTipHelper::whatsThisHintOnly());
0171     QVERIFY2(shownToolTip(m_toolButton).isEmpty(), "It should not show the whatsThisHint if there is no whatsThis text.");
0172 
0173     action->setWhatsThis(QStringLiteral("action's whatsThis"));
0174     QVERIFY2(!shownToolTip(m_toolButton).isEmpty(), "The whatsThisHint should be shown.");
0175 }
0176 
0177 void KToolTipHelper_UnitTest::testQMenu()
0178 {
0179     auto menu = std::unique_ptr<QMenu>(new QMenu(m_centralWidget));
0180     auto action = std::unique_ptr<QAction>(new QAction(QStringLiteral("action")));
0181     action->setDisabled(true); // The tooltip should also be shown for disabled actions.
0182     menu->addAction(action.get());
0183     menu->show();
0184     QVERIFY(shownToolTip(menu.get()).isEmpty());
0185 
0186     action->setToolTip(QStringLiteral("action's tooltip"));
0187     const QString toolTip(shownToolTip(menu.get()));
0188     QCOMPARE(toolTip, action->toolTip());
0189     action->setShortcut(Qt::CTRL | Qt::Key_K);
0190     QCOMPARE(shownToolTip(menu.get()), toolTip);
0191 
0192     action->setWhatsThis(QStringLiteral("action's whatsThis"));
0193     const QString toolTipWithWhatsThisHint(shownToolTip(menu.get()));
0194     QVERIFY2(toolTipWithWhatsThisHint.length() > toolTip.length(), "The tooltip is supposed to contain a whatsThisHint.");
0195 
0196     action->setToolTip(KToolTipHelper::whatsThisHintOnly());
0197     QVERIFY(shownToolTip(menu.get()).length() < toolTipWithWhatsThisHint.length());
0198 
0199     action->setWhatsThis(QStringLiteral(""));
0200     QVERIFY(shownToolTip(menu.get()).isEmpty());
0201 }
0202 
0203 void KToolTipHelper_UnitTest::cleanupTestCase()
0204 {
0205     qApp->removeEventFilter(KToolTipHelper::instance());
0206 }
0207 
0208 QTEST_MAIN(KToolTipHelper_UnitTest)
0209 
0210 #include "moc_ktooltiphelper_unittest.cpp"