1 /*
2     This file is part of the KDE libraries
3     SPDX-FileCopyrightText: 2017 David Faure <faure@kde.org>
4 
5     SPDX-License-Identifier: LGPL-2.1-or-later
6 */
7 #include <functional>
8 #include <kacceleratormanager.h>
9 
10 #include <QMenu>
11 #include <QPushButton>
12 #include <QTest>
13 
14 #define QSL QStringLiteral
15 
extractActionTexts(QMenu & menu,std::function<QString (const QAction *)> func)16 static QStringList extractActionTexts(QMenu &menu, std::function<QString(const QAction *)> func)
17 {
18     menu.aboutToShow(); // signals are now public in Qt5, how convenient :-)
19 
20     QStringList ret;
21     bool lastIsSeparator = false;
22     const auto menuActions = menu.actions();
23     for (const QAction *action : menuActions) {
24         if (action->isSeparator()) {
25             if (!lastIsSeparator) { // Qt gets rid of duplicate separators, so we should too
26                 ret.append(QStringLiteral("separator"));
27             }
28             lastIsSeparator = true;
29         } else {
30             lastIsSeparator = false;
31             if (action->menu()) {
32                 ret.append(QStringLiteral("submenu"));
33             } else {
34                 const QString text = func(action);
35                 ret.append(text);
36             }
37         }
38     }
39     return ret;
40 }
41 
42 class KAcceleratorManagerTest : public QObject
43 {
44     Q_OBJECT
45 
46 private Q_SLOTS:
initTestCase()47     void initTestCase()
48     {
49     }
50 
testActionTexts_data()51     void testActionTexts_data()
52     {
53         QTest::addColumn<QStringList>("initialTexts");
54         QTest::addColumn<QStringList>("expectedTexts");
55 
56         QTest::newRow("basic") << QStringList{QSL("Open"), QSL("Close"), QSL("Quit")} << QStringList{QSL("&Open"), QSL("&Close"), QSL("&Quit")};
57         QTest::newRow("same_first") << QStringList{QSL("Open"), QSL("Close"), QSL("Clone"), QSL("Quit")}
58                                     << QStringList{QSL("&Open"), QSL("&Close"), QSL("C&lone"), QSL("&Quit")};
59         QTest::newRow("cjk") << QStringList{QSL("Open (&O)"), QSL("Close (&C)"), QSL("Clone (&C)"), QSL("Quit (&Q)")}
60                              << QStringList{QSL("Open (&O)"), QSL("Close (&C)"), QSL("C&lone (C)"), QSL("Quit (&Q)")};
61     }
62 
testActionTexts()63     void testActionTexts()
64     {
65         // GIVEN
66         QFETCH(QStringList, initialTexts);
67         QFETCH(QStringList, expectedTexts);
68         QMenu menu;
69         for (const QString &text : std::as_const(initialTexts)) {
70             menu.addAction(text);
71         }
72         // WHEN
73         KAcceleratorManager::manage(&menu);
74         // THEN
75         const QStringList texts = extractActionTexts(menu, &QAction::text);
76         QCOMPARE(texts, expectedTexts);
77     }
78 
testExistingActionsShortcutsAreTakenIntoAccount()79     void testExistingActionsShortcutsAreTakenIntoAccount()
80     {
81         std::unique_ptr<QWidget> w(new QWidget());
82 
83         QPushButton *pb = new QPushButton(QSL("Open"), w.get());
84 
85         KAcceleratorManager::manage(w.get());
86         QCOMPARE(pb->text(), QSL("&Open"));
87 
88         delete pb;
89         pb = new QPushButton(QSL("Open"), w.get());
90 
91         QAction *a = new QAction();
92         a->setShortcut(QSL("Alt+O"));
93         w->addAction(a);
94 
95         KAcceleratorManager::manage(w.get());
96         QCOMPARE(pb->text(), QSL("O&pen"));
97     }
98 
testActionIconTexts_data()99     void testActionIconTexts_data()
100     {
101         QTest::addColumn<QStringList>("initialTexts");
102         QTest::addColumn<QStringList>("expectedTexts");
103 
104         QTest::newRow("basic") << QStringList{QSL("Open"), QSL("Close"), QSL("Quit")} << QStringList{QSL("Open"), QSL("Close"), QSL("Quit")};
105         QTest::newRow("same_first") << QStringList{QSL("Open"), QSL("Close"), QSL("Clone"), QSL("Quit")}
106                                     << QStringList{QSL("Open"), QSL("Close"), QSL("Clone"), QSL("Quit")};
107         QTest::newRow("cjk") << QStringList{QSL("Open (&O)"), QSL("Close (&C)"), QSL("Clone (&C)"), QSL("Quit (&Q)")}
108                              << QStringList{QSL("Open"), QSL("Close"), QSL("Clone"), QSL("Quit")};
109     }
110 
testActionIconTexts()111     void testActionIconTexts()
112     {
113         // GIVEN
114         QFETCH(QStringList, initialTexts);
115         QFETCH(QStringList, expectedTexts);
116         QMenu menu;
117         for (const QString &text : std::as_const(initialTexts)) {
118             menu.addAction(text);
119         }
120         // WHEN
121         KAcceleratorManager::manage(&menu);
122         // THEN
123         const QStringList iconTexts = extractActionTexts(menu, &QAction::iconText);
124         QCOMPARE(iconTexts, expectedTexts);
125     }
126 };
127 
128 QTEST_MAIN(KAcceleratorManagerTest)
129 
130 #include "kacceleratormanagertest.moc"
131