1 /*
2     This file is part of the KDE libraries
3 
4     SPDX-FileCopyrightText: 2007 David Faure <faure@kde.org>
5 
6     SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8 
9 #include <QClipboard>
10 #include <QSignalSpy>
11 #include <QTest>
12 #include <QToolButton>
13 #include <kcompletionbox.h>
14 #include <klineedit.h>
15 
16 class KLineEdit_UnitTest : public QObject
17 {
18     Q_OBJECT
19 
20 private Q_SLOTS:
21 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(5, 83)
testPassword()22     void testPassword()
23     {
24         KLineEdit w;
25         w.setPasswordMode(true);
26         QTest::keyClick(&w, Qt::Key_1);
27         QTest::keyClick(&w, Qt::Key_2);
28         QTest::keyClick(&w, Qt::Key_3);
29         QCOMPARE(w.text(), QString("123"));
30     }
31 #endif
32 
testReturnPressed()33     void testReturnPressed()
34     {
35         KLineEdit w;
36         w.setText(QStringLiteral("Hello world"));
37         QSignalSpy qReturnPressedSpy(&w, &QLineEdit::returnPressed);
38 
39 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(5, 81)
40         QSignalSpy kReturnPressedSpy(&w, qOverload<const QString &>(&KLineEdit::returnPressed));
41 #endif
42         QSignalSpy returnKeyPressedSpy(&w, &KLineEdit::returnKeyPressed);
43 
44         QTest::keyClick(&w, Qt::Key_Return);
45         QCOMPARE(qReturnPressedSpy.count(), 1);
46 
47 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(5, 81)
48         QCOMPARE(kReturnPressedSpy.count(), 1);
49         QCOMPARE(kReturnPressedSpy[0][0].toString(), QString("Hello world"));
50 #endif
51         QCOMPARE(returnKeyPressedSpy.count(), 1);
52         QCOMPARE(returnKeyPressedSpy.at(0).at(0).toString(), QStringLiteral("Hello world"));
53     }
54 
testTextEditedSignals()55     void testTextEditedSignals()
56     {
57         KLineEdit w;
58         QVERIFY(!w.isModified());
59 
60         // setText emits textChanged and userTextChanged, but not textEdited
61         QSignalSpy textChangedSpy(&w, &QLineEdit::textChanged);
62         QSignalSpy textEditedSpy(&w, &QLineEdit::textEdited);
63 #if KCOMPLETION_ENABLE_DEPRECATED_SINCE(4, 5)
64         QSignalSpy userTextChangedSpy(&w, &KLineEdit::userTextChanged);
65 #endif
66         w.setText(QStringLiteral("Hello worl"));
67 #if KCOMPLETION_ENABLE_DEPRECATED_SINCE(4, 5)
68         QCOMPARE(userTextChangedSpy.count(), 1);
69         QCOMPARE(userTextChangedSpy[0][0].toString(), w.text());
70 #endif
71         QCOMPARE(textChangedSpy.count(), 1);
72         QCOMPARE(textChangedSpy[0][0].toString(), w.text());
73         QCOMPARE(textEditedSpy.count(), 0);
74         QVERIFY(!w.isModified());
75 #if KCOMPLETION_ENABLE_DEPRECATED_SINCE(4, 5)
76         userTextChangedSpy.clear();
77 #endif
78         textChangedSpy.clear();
79         textEditedSpy.clear();
80 
81         // calling clear should emit textChanged and userTextChanged, but not textEdited
82         w.clear();
83         QCOMPARE(textChangedSpy.count(), 1);
84         QCOMPARE(textEditedSpy.count(), 0);
85 #if KCOMPLETION_ENABLE_DEPRECATED_SINCE(4, 5)
86         QCOMPARE(userTextChangedSpy.count(), 1);
87 #endif
88 
89         // if text box is already empty, calling clear() shouldn't emit
90         // any more signals
91         w.clear();
92         QCOMPARE(textChangedSpy.count(), 1);
93         QCOMPARE(textEditedSpy.count(), 0);
94 #if KCOMPLETION_ENABLE_DEPRECATED_SINCE(4, 5)
95         QCOMPARE(userTextChangedSpy.count(), 1);
96 #endif
97 
98         // set the text back for further tests below
99         w.setText(QStringLiteral("Hello worl"));
100 #if KCOMPLETION_ENABLE_DEPRECATED_SINCE(4, 5)
101         userTextChangedSpy.clear();
102 #endif
103         textChangedSpy.clear();
104         textEditedSpy.clear();
105 
106         // typing emits all three signals
107         QTest::keyClick(&w, Qt::Key_D);
108         QCOMPARE(w.text(), QString::fromLatin1("Hello world"));
109 #if KCOMPLETION_ENABLE_DEPRECATED_SINCE(4, 5)
110         QCOMPARE(userTextChangedSpy.count(), 1);
111         QCOMPARE(userTextChangedSpy[0][0].toString(), w.text());
112 #endif
113         QCOMPARE(textChangedSpy.count(), 1);
114         QCOMPARE(textChangedSpy[0][0].toString(), w.text());
115         QCOMPARE(textEditedSpy.count(), 1);
116         QCOMPARE(textEditedSpy[0][0].toString(), w.text());
117         QVERIFY(w.isModified());
118 
119         w.setText(QStringLiteral("K")); // prepare for next test
120 #if KCOMPLETION_ENABLE_DEPRECATED_SINCE(4, 5)
121         userTextChangedSpy.clear();
122 #endif
123         textChangedSpy.clear();
124         textEditedSpy.clear();
125         QVERIFY(!w.isModified());
126 
127         // the suggestion from auto completion emits textChanged but not userTextChanged nor textEdited
128         w.setCompletionMode(KCompletion::CompletionAuto);
129         KCompletion completion;
130         completion.setSoundsEnabled(false);
131         QStringList items;
132         items << QStringLiteral("KDE is cool") << QStringLiteral("KDE is really cool");
133         completion.setItems(items);
134         w.setCompletionObject(&completion);
135 
136         w.doCompletion(w.text());
137         QCOMPARE(w.text(), items.at(0));
138 
139 #if KCOMPLETION_ENABLE_DEPRECATED_SINCE(4, 5)
140         QCOMPARE(userTextChangedSpy.count(), 0);
141 #endif
142         QCOMPARE(textChangedSpy.count(), 1);
143         QCOMPARE(textChangedSpy[0][0].toString(), w.text());
144         QCOMPARE(textEditedSpy.count(), 0);
145         QVERIFY(!w.isModified());
146 #if KCOMPLETION_ENABLE_DEPRECATED_SINCE(4, 5)
147         userTextChangedSpy.clear();
148 #endif
149         textChangedSpy.clear();
150         textEditedSpy.clear();
151 
152         // accepting the completion suggestion now emits all three signals too
153         QTest::keyClick(&w, Qt::Key_Right);
154         QCOMPARE(w.text(), items.at(0));
155 
156 #if KCOMPLETION_ENABLE_DEPRECATED_SINCE(4, 5)
157         QCOMPARE(userTextChangedSpy.count(), 1);
158         QCOMPARE(userTextChangedSpy[0][0].toString(), w.text());
159 #endif
160         QCOMPARE(textChangedSpy.count(), 1);
161         QCOMPARE(textChangedSpy[0][0].toString(), w.text());
162         QCOMPARE(textEditedSpy.count(), 1);
163         QCOMPARE(textEditedSpy[0][0].toString(), w.text());
164         QVERIFY(w.isModified());
165 #if KCOMPLETION_ENABLE_DEPRECATED_SINCE(4, 5)
166         userTextChangedSpy.clear();
167 #endif
168         textChangedSpy.clear();
169         textEditedSpy.clear();
170 
171         // Now with popup completion
172         w.setCompletionMode(KCompletion::CompletionPopup);
173         w.setText(QStringLiteral("KDE"));
174         QVERIFY(!w.isModified());
175 #if KCOMPLETION_ENABLE_DEPRECATED_SINCE(4, 5)
176         userTextChangedSpy.clear();
177 #endif
178         textChangedSpy.clear();
179         textEditedSpy.clear();
180         w.doCompletion(w.text()); // popup appears
181         QCOMPARE(w.text(), QString::fromLatin1("KDE"));
182 #if KCOMPLETION_ENABLE_DEPRECATED_SINCE(4, 5)
183         QCOMPARE(textChangedSpy.count() + userTextChangedSpy.count() + textEditedSpy.count(), 0);
184 #else
185         QCOMPARE(textChangedSpy.count() + textEditedSpy.count(), 0);
186 #endif
187         w.completionBox()->down(); // select 1st item
188         QCOMPARE(w.text(), items.at(0));
189         QVERIFY(w.isModified());
190         w.completionBox()->down(); // select 2nd item
191         QCOMPARE(w.text(), items.at(1));
192 
193         // Selecting an item in the popup completion changes the lineedit text
194         // and emits textChanged and userTextChanged, but not textEdited.
195 #if KCOMPLETION_ENABLE_DEPRECATED_SINCE(4, 5)
196         QCOMPARE(userTextChangedSpy.count(), 2);
197 #endif
198         QCOMPARE(textChangedSpy.count(), 2);
199         QCOMPARE(textEditedSpy.count(), 0);
200 #if KCOMPLETION_ENABLE_DEPRECATED_SINCE(4, 5)
201         userTextChangedSpy.clear();
202 #endif
203         textChangedSpy.clear();
204         textEditedSpy.clear();
205 
206         QTest::keyClick(&w, Qt::Key_Enter); // activate
207         QVERIFY(!w.completionBox()->isVisible());
208         QCOMPARE(w.text(), items.at(1));
209         QVERIFY(w.isModified());
210         // Nothing else happens, the text was already set in the lineedit
211         QCOMPARE(textChangedSpy.count(), 0);
212         QCOMPARE(textEditedSpy.count(), 0);
213 #if KCOMPLETION_ENABLE_DEPRECATED_SINCE(4, 5)
214         QCOMPARE(userTextChangedSpy.count(), 0);
215 #endif
216 
217         // Now when using the mouse in the popup completion
218         w.setText(QStringLiteral("KDE"));
219         w.doCompletion(w.text()); // popup appears
220         QCOMPARE(w.text(), QString::fromLatin1("KDE"));
221         // Selecting an item in the popup completion changes the lineedit text and emits all 3 signals
222         const QRect rect = w.completionBox()->visualRect(w.completionBox()->model()->index(1, 0));
223 
224 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(5, 81)
225         QSignalSpy activatedSpy(w.completionBox(), qOverload<const QString &>(&KCompletionBox::activated));
226 #endif
227         QSignalSpy textActivatedSpy(w.completionBox(), &KCompletionBox::textActivated);
228 
229         QTest::mouseClick(w.completionBox()->viewport(), Qt::LeftButton, Qt::NoModifier, rect.center());
230 
231 #if KCOMPLETION_BUILD_DEPRECATED_SINCE(5, 81)
232         QCOMPARE(activatedSpy.count(), 1);
233 #endif
234         QCOMPARE(textActivatedSpy.count(), 1);
235 
236         QCOMPARE(w.text(), items.at(1));
237         QVERIFY(w.isModified());
238     }
239 
testCompletionBox()240     void testCompletionBox()
241     {
242         KLineEdit w;
243         w.setText(QStringLiteral("/"));
244         w.setCompletionMode(KCompletion::CompletionPopup);
245         KCompletion completion;
246         completion.setSoundsEnabled(false);
247         w.setCompletionObject(&completion);
248         QStringList items;
249         items << QStringLiteral("/home/") << QStringLiteral("/hold/") << QStringLiteral("/hole/");
250         completion.setItems(items);
251         QTest::keyClick(&w, 'h');
252         QCOMPARE(w.text(), QString::fromLatin1("/h"));
253         QCOMPARE(w.completionBox()->currentRow(), -1);
254         QCOMPARE(w.completionBox()->items(), items);
255         QTest::keyClick(&w, 'o');
256         QCOMPARE(w.text(), QString::fromLatin1("/ho"));
257         QCOMPARE(w.completionBox()->currentRow(), -1);
258         w.completionBox()->down(); // select 1st item
259         QCOMPARE(w.text(), items.at(0));
260         w.completionBox()->down(); // select 2nd item
261         QCOMPARE(w.text(), items.at(1));
262         w.completionBox()->up(); // select 1st item again
263         QCOMPARE(w.text(), items.at(0));
264         w.completionBox()->up(); // select last item
265         QCOMPARE(w.text(), items.at(2));
266         w.completionBox()->down(); // select 1st item again
267         QCOMPARE(w.text(), items.at(0));
268 
269         QStringList newItems;
270         newItems << QStringLiteral("/home/kde");
271         completion.setItems(newItems);
272         QTest::keyClick(&w, 'k');
273         QCOMPARE(w.text(), QString("/home/k"));
274         // QCOMPARE(w.completionBox()->currentRow(), -1); // #247552
275         w.completionBox()->down(); // select the item
276         QCOMPARE(w.completionBox()->items(), newItems);
277         QCOMPARE(w.text(), newItems.at(0));
278     }
279 
testPaste()280     void testPaste()
281     {
282         const QString origText = QApplication::clipboard()->text();
283         const QString pastedText = QStringLiteral("Test paste from klineedit_unittest");
284         QApplication::clipboard()->setText(pastedText);
285         KLineEdit w;
286         w.setText(QStringLiteral("Hello world"));
287         w.selectAll();
288         QTest::keyClick(&w, Qt::Key_V, Qt::ControlModifier);
289         QCOMPARE(w.text(), pastedText);
290         QApplication::clipboard()->setText(origText);
291     }
292 
testClearButtonClicked()293     void testClearButtonClicked()
294     {
295         KLineEdit w;
296         w.setText(QStringLiteral("Hello world"));
297         w.setClearButtonEnabled(true);
298         w.setClearButtonEnabled(false);
299         w.setClearButtonEnabled(true);
300         QSignalSpy spy(&w, &KLineEdit::clearButtonClicked);
301         QToolButton *tb = w.findChild<QToolButton *>();
302         QTest::mouseClick(tb, Qt::LeftButton, Qt::NoModifier);
303         QCOMPARE(w.text(), QString());
304         QCOMPARE(spy.count(), 1);
305     }
306 };
307 
308 QTEST_MAIN(KLineEdit_UnitTest)
309 
310 #include "klineedit_unittest.moc"
311