QtPass 1.6.0
Multi-platform GUI for pass, the standard unix password manager.
Loading...
Searching...
No Matches
tst_ui.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2018 Anne Jan Brouwer
2// SPDX-License-Identifier: GPL-3.0-or-later
3#include <QCoreApplication>
4#include <QDialog>
5#include <QLineEdit>
6#include <QPointer>
7#include <QSignalSpy>
8#include <QtTest>
9
16#include "../../../src/qtpass.h"
18
19class tst_ui : public QObject {
20 Q_OBJECT
21
22private Q_SLOTS:
23 void initTestCase();
24 void contentRemainsSame();
25 void emptyPassword();
26 void multilineRemainingData();
27 void cleanupTestCase();
28 void passwordDialogBasic();
29 void passwordDialogWithTemplate();
30 void qrCodePopupDeletesOnClose();
31 void qrCodePopupHasDeleteOnCloseAttribute();
32 void createQRCodePopupSetsDeleteOnClose();
33 void dialogWithoutDeleteOnCloseDoesNotAutoDelete();
34
35 // QPushButtonWithClipboard tests
36 void clipboardButtonDefaultText();
37 void clipboardButtonConstructorText();
38 void clipboardButtonGetSetText();
39 void clipboardButtonSetEmptyText();
40 void clipboardButtonSetAndGetRoundtrip();
41 void clipboardButtonClickEmitsSignal();
42 void clipboardButtonClickSignalCarriesText();
43 void clipboardButtonClickAfterSetTextCarriesNewText();
44
45 // QPushButtonAsQRCode tests
46 void qrCodeButtonDefaultText();
47 void qrCodeButtonConstructorText();
48 void qrCodeButtonGetSetText();
49 void qrCodeButtonSetEmptyText();
50 void qrCodeButtonSetAndGetRoundtrip();
51 void qrCodeButtonClickEmitsSignal();
52 void qrCodeButtonClickSignalCarriesText();
53 void qrCodeButtonClickAfterSetTextCarriesNewText();
54
55 // QPushButtonShowPassword tests
56 void showPasswordButtonInitialEchoMode();
57 void showPasswordButtonClickTogglesEchoMode();
58 void showPasswordButtonDoubleClickRestoresEchoMode();
59
60 // QProgressIndicator tests
61 void progressIndicatorDefaultNotAnimated();
62 void progressIndicatorDefaultDelay();
63 void progressIndicatorDefaultNotDisplayedWhenStopped();
64 void progressIndicatorDefaultColor();
65 void progressIndicatorStartAnimation();
66 void progressIndicatorStopAnimation();
67 void progressIndicatorStartStopCycle();
68 void progressIndicatorSetAnimationDelay();
69 void progressIndicatorSetDisplayedWhenStopped();
70 void progressIndicatorSetColor();
71 void progressIndicatorSizeHint();
72 void progressIndicatorHeightForWidth();
73 void progressIndicatorStopWhenNotRunningIsHarmless();
74 void progressIndicatorStartTwiceDoesNotDuplicate();
75
76 // DeselectableTreeView tests
77 void deselectableTreeViewConstruction();
78 void deselectableTreeViewHasEmptyClickedSignal();
79};
80
86void tst_ui::contentRemainsSame() {
87 QScopedPointer<PasswordDialog> d(
89 d->setTemplate("", false);
90 QString input = "pw\n";
91 d->setPass(input);
92 QCOMPARE(d->getPassword(), input);
93
94 d.reset(new PasswordDialog(PasswordConfiguration{}, nullptr));
95 input = "pw\nname: value\n";
96 d->setPass(input);
97 QCOMPARE(d->getPassword(), input);
98
99 d.reset(new PasswordDialog(PasswordConfiguration{}, nullptr));
100 d->setTemplate("name", false);
101 d->setPass(input);
102 QCOMPARE(d->getPassword(), input);
103
104 d.reset(new PasswordDialog(PasswordConfiguration{}, nullptr));
105 d->setTemplate("name", true);
106 d->setPass(input);
107 QCOMPARE(d->getPassword(), input);
108
109 d.reset(new PasswordDialog(PasswordConfiguration{}, nullptr));
110 d->setTemplate("", false);
111 d->templateAll(true);
112 d->setPass(input);
113 QCOMPARE(d->getPassword(), input);
114
115 d.reset(new PasswordDialog(PasswordConfiguration{}, nullptr));
116 d->setTemplate("", true);
117 d->templateAll(true);
118 d->setPass(input);
119 QCOMPARE(d->getPassword(), input);
120
121 d.reset(new PasswordDialog(PasswordConfiguration{}, nullptr));
122 d->setTemplate("name", true);
123 d->templateAll(true);
124 d->setPass(input);
125 QCOMPARE(d->getPassword(), input);
126}
127
128void tst_ui::initTestCase() {}
129
130void tst_ui::cleanupTestCase() {}
131
132void tst_ui::emptyPassword() {
133 QScopedPointer<PasswordDialog> d(
134 new PasswordDialog(PasswordConfiguration{}, nullptr));
135 d->setTemplate("", false);
136 d->setPass("");
137 QString result = d->getPassword();
138 QVERIFY(result.isEmpty() || result == "\n");
139}
140
141void tst_ui::multilineRemainingData() {
142 QScopedPointer<PasswordDialog> d(
143 new PasswordDialog(PasswordConfiguration{}, nullptr));
144 d->setTemplate("", false);
145 QString input = "secret\nline1\nline2\nline3\n";
146 d->setPass(input);
147 QString result = d->getPassword();
148 QStringList lines = result.split("\n");
149 QVERIFY(lines.length() >= 4);
150 lines.removeFirst();
151 QString remaining = lines.join("\n") + (result.endsWith("\n") ? "\n" : "");
152 QVERIFY(remaining.contains("line1"));
153 QVERIFY(remaining.contains("line2"));
154 QVERIFY(remaining.contains("line3"));
155}
156
157void tst_ui::passwordDialogBasic() {
158 PasswordConfiguration config;
159 config.length = 20;
160 QScopedPointer<PasswordDialog> d(new PasswordDialog(config, nullptr));
161 d->setTemplate("", false);
162 d->setPass("testpassword");
163 QString result = d->getPassword();
164 QVERIFY(result.contains("testpassword"));
165}
166
167void tst_ui::passwordDialogWithTemplate() {
168 PasswordConfiguration config;
169 QScopedPointer<PasswordDialog> d(new PasswordDialog(config, nullptr));
170 d->setTemplate("username", false);
171 d->setPass("mypassword\nusername: testuser");
172 QString result = d->getPassword();
173 QVERIFY(result.contains("mypassword"));
174}
175
182void tst_ui::qrCodePopupDeletesOnClose() {
183 QPointer<QDialog> popup(
184 new QDialog(nullptr, Qt::Popup | Qt::FramelessWindowHint));
185 popup->setAttribute(Qt::WA_DeleteOnClose);
186 QVERIFY(!popup.isNull());
187
188 popup->close();
189 QCoreApplication::processEvents();
190 QTRY_VERIFY(popup.isNull());
191}
192
198void tst_ui::qrCodePopupHasDeleteOnCloseAttribute() {
199 QDialog *popup = new QDialog(nullptr, Qt::Popup | Qt::FramelessWindowHint);
200 popup->setAttribute(Qt::WA_DeleteOnClose);
201 QVERIFY(popup->testAttribute(Qt::WA_DeleteOnClose));
202 delete popup;
203}
204
210void tst_ui::createQRCodePopupSetsDeleteOnClose() {
211 QPixmap image;
212 QDialog *popup = QtPass::createQRCodePopup(image);
213 QVERIFY(popup->testAttribute(Qt::WA_DeleteOnClose));
214 delete popup;
215}
216
223void tst_ui::dialogWithoutDeleteOnCloseDoesNotAutoDelete() {
224 QPointer<QDialog> popup(
225 new QDialog(nullptr, Qt::Popup | Qt::FramelessWindowHint));
226 // Intentionally NOT setting WA_DeleteOnClose.
227 QVERIFY(!popup->testAttribute(Qt::WA_DeleteOnClose));
228
229 popup->close();
230 QCoreApplication::processEvents();
231
232 QVERIFY2(
233 !popup.isNull(),
234 "QDialog without WA_DeleteOnClose must NOT be deleted after close()");
235 delete popup;
236}
237
238// ---- QPushButtonWithClipboard tests ----
239
240void tst_ui::clipboardButtonDefaultText() {
241 QPushButtonWithClipboard btn;
242 QCOMPARE(btn.getTextToCopy(), QString(""));
243}
244
245void tst_ui::clipboardButtonConstructorText() {
246 QPushButtonWithClipboard btn("hello");
247 QCOMPARE(btn.getTextToCopy(), QString("hello"));
248}
249
250void tst_ui::clipboardButtonGetSetText() {
251 QPushButtonWithClipboard btn("initial");
252 btn.setTextToCopy("updated");
253 QCOMPARE(btn.getTextToCopy(), QString("updated"));
254}
255
256void tst_ui::clipboardButtonSetEmptyText() {
257 QPushButtonWithClipboard btn("nonempty");
258 btn.setTextToCopy("");
259 QCOMPARE(btn.getTextToCopy(), QString(""));
260}
261
262void tst_ui::clipboardButtonSetAndGetRoundtrip() {
263 QPushButtonWithClipboard btn;
264 QString text = "password123!@#";
265 btn.setTextToCopy(text);
266 QCOMPARE(btn.getTextToCopy(), text);
267}
268
269void tst_ui::clipboardButtonClickEmitsSignal() {
270 QPushButtonWithClipboard btn("test");
271 QSignalSpy spy(&btn, SIGNAL(clicked(QString)));
272 btn.click();
273 QCOMPARE(spy.count(), 1);
274}
275
276void tst_ui::clipboardButtonClickSignalCarriesText() {
277 QPushButtonWithClipboard btn("mytext");
278 QSignalSpy spy(&btn, SIGNAL(clicked(QString)));
279 btn.click();
280 QCOMPARE(spy.count(), 1);
281 QList<QVariant> args = spy.takeFirst();
282 QCOMPARE(args.at(0).toString(), QString("mytext"));
283}
284
285void tst_ui::clipboardButtonClickAfterSetTextCarriesNewText() {
286 QPushButtonWithClipboard btn("original");
287 btn.setTextToCopy("changed");
288 QSignalSpy spy(&btn, SIGNAL(clicked(QString)));
289 btn.click();
290 QCOMPARE(spy.count(), 1);
291 QList<QVariant> args = spy.takeFirst();
292 QCOMPARE(args.at(0).toString(), QString("changed"));
293}
294
295// ---- QPushButtonAsQRCode tests ----
296
297void tst_ui::qrCodeButtonDefaultText() {
298 QPushButtonAsQRCode btn;
299 QCOMPARE(btn.getTextToCopy(), QString(""));
300}
301
302void tst_ui::qrCodeButtonConstructorText() {
303 QPushButtonAsQRCode btn("qrdata");
304 QCOMPARE(btn.getTextToCopy(), QString("qrdata"));
305}
306
307void tst_ui::qrCodeButtonGetSetText() {
308 QPushButtonAsQRCode btn("first");
309 btn.setTextToCopy("second");
310 QCOMPARE(btn.getTextToCopy(), QString("second"));
311}
312
313void tst_ui::qrCodeButtonSetEmptyText() {
314 QPushButtonAsQRCode btn("nonempty");
315 btn.setTextToCopy("");
316 QCOMPARE(btn.getTextToCopy(), QString(""));
317}
318
319void tst_ui::qrCodeButtonSetAndGetRoundtrip() {
320 QPushButtonAsQRCode btn;
321 QString text = "otpauth://totp/Example?secret=JBSWY3DPEHPK3PXP";
322 btn.setTextToCopy(text);
323 QCOMPARE(btn.getTextToCopy(), text);
324}
325
326void tst_ui::qrCodeButtonClickEmitsSignal() {
327 QPushButtonAsQRCode btn("somedata");
328 QSignalSpy spy(&btn, SIGNAL(clicked(QString)));
329 btn.click();
330 QCOMPARE(spy.count(), 1);
331}
332
333void tst_ui::qrCodeButtonClickSignalCarriesText() {
334 QPushButtonAsQRCode btn("payload");
335 QSignalSpy spy(&btn, SIGNAL(clicked(QString)));
336 btn.click();
337 QCOMPARE(spy.count(), 1);
338 QList<QVariant> args = spy.takeFirst();
339 QCOMPARE(args.at(0).toString(), QString("payload"));
340}
341
342void tst_ui::qrCodeButtonClickAfterSetTextCarriesNewText() {
343 QPushButtonAsQRCode btn("old");
344 btn.setTextToCopy("new");
345 QSignalSpy spy(&btn, SIGNAL(clicked(QString)));
346 btn.click();
347 QCOMPARE(spy.count(), 1);
348 QList<QVariant> args = spy.takeFirst();
349 QCOMPARE(args.at(0).toString(), QString("new"));
350}
351
352// ---- QPushButtonShowPassword tests ----
353
354void tst_ui::showPasswordButtonInitialEchoMode() {
355 QLineEdit line;
356 line.setEchoMode(QLineEdit::Password);
357 QPushButtonShowPassword btn(&line);
358 // Initial state: echo mode should remain Password until button is clicked
359 QCOMPARE(line.echoMode(), QLineEdit::Password);
360}
361
362void tst_ui::showPasswordButtonClickTogglesEchoMode() {
363 QLineEdit line;
364 line.setEchoMode(QLineEdit::Password);
365 QPushButtonShowPassword btn(&line);
366 // Click once: Password -> Normal
367 btn.click();
368 QCOMPARE(line.echoMode(), QLineEdit::Normal);
369}
370
371void tst_ui::showPasswordButtonDoubleClickRestoresEchoMode() {
372 QLineEdit line;
373 line.setEchoMode(QLineEdit::Password);
374 QPushButtonShowPassword btn(&line);
375 // Click once: Password -> Normal
376 btn.click();
377 QCOMPARE(line.echoMode(), QLineEdit::Normal);
378 // Click again: Normal -> Password
379 btn.click();
380 QCOMPARE(line.echoMode(), QLineEdit::Password);
381}
382
383// ---- QProgressIndicator tests ----
384
385void tst_ui::progressIndicatorDefaultNotAnimated() {
386 QProgressIndicator indicator;
387 QVERIFY(!indicator.isAnimated());
388}
389
390void tst_ui::progressIndicatorDefaultDelay() {
391 QProgressIndicator indicator;
392 QCOMPARE(indicator.animationDelay(), 40);
393}
394
395void tst_ui::progressIndicatorDefaultNotDisplayedWhenStopped() {
396 QProgressIndicator indicator;
397 QVERIFY(!indicator.isDisplayedWhenStopped());
398}
399
400void tst_ui::progressIndicatorDefaultColor() {
401 QProgressIndicator indicator;
402 QCOMPARE(indicator.color(), QColor(Qt::black));
403}
404
405void tst_ui::progressIndicatorStartAnimation() {
406 QProgressIndicator indicator;
407 QVERIFY(!indicator.isAnimated());
408 indicator.startAnimation();
409 QVERIFY(indicator.isAnimated());
410 indicator.stopAnimation();
411}
412
413void tst_ui::progressIndicatorStopAnimation() {
414 QProgressIndicator indicator;
415 indicator.startAnimation();
416 QVERIFY(indicator.isAnimated());
417 indicator.stopAnimation();
418 QVERIFY(!indicator.isAnimated());
419}
420
421void tst_ui::progressIndicatorStartStopCycle() {
422 QProgressIndicator indicator;
423 indicator.startAnimation();
424 QVERIFY(indicator.isAnimated());
425 indicator.stopAnimation();
426 QVERIFY(!indicator.isAnimated());
427 indicator.startAnimation();
428 QVERIFY(indicator.isAnimated());
429 indicator.stopAnimation();
430 QVERIFY(!indicator.isAnimated());
431}
432
433void tst_ui::progressIndicatorSetAnimationDelay() {
434 QProgressIndicator indicator;
435 indicator.setAnimationDelay(100);
436 QCOMPARE(indicator.animationDelay(), 100);
437}
438
439void tst_ui::progressIndicatorSetDisplayedWhenStopped() {
440 QProgressIndicator indicator;
441 QVERIFY(!indicator.isDisplayedWhenStopped());
442 indicator.setDisplayedWhenStopped(true);
443 QVERIFY(indicator.isDisplayedWhenStopped());
444 indicator.setDisplayedWhenStopped(false);
445 QVERIFY(!indicator.isDisplayedWhenStopped());
446}
447
448void tst_ui::progressIndicatorSetColor() {
449 QProgressIndicator indicator;
450 QColor red(Qt::red);
451 indicator.setColor(red);
452 QCOMPARE(indicator.color(), red);
453}
454
455void tst_ui::progressIndicatorSizeHint() {
456 QProgressIndicator indicator;
457 QSize hint = indicator.sizeHint();
458 QCOMPARE(hint, QSize(20, 20));
459}
460
461void tst_ui::progressIndicatorHeightForWidth() {
462 QProgressIndicator indicator;
463 QCOMPARE(indicator.heightForWidth(30), 30);
464 QCOMPARE(indicator.heightForWidth(50), 50);
465 QCOMPARE(indicator.heightForWidth(0), 0);
466}
467
468void tst_ui::progressIndicatorStopWhenNotRunningIsHarmless() {
469 QProgressIndicator indicator;
470 QVERIFY(!indicator.isAnimated());
471 // Stopping when already stopped should not crash or change state
472 indicator.stopAnimation();
473 QVERIFY(!indicator.isAnimated());
474}
475
476void tst_ui::progressIndicatorStartTwiceDoesNotDuplicate() {
477 QProgressIndicator indicator;
478 indicator.startAnimation();
479 QVERIFY(indicator.isAnimated());
480 // Starting again when already running should remain animated without crash
481 indicator.startAnimation();
482 QVERIFY(indicator.isAnimated());
483 indicator.stopAnimation();
484 QVERIFY(!indicator.isAnimated());
485}
486
487// ---- DeselectableTreeView tests ----
488
489void tst_ui::deselectableTreeViewConstruction() {
490 // Verify the view can be constructed and destroyed without issues
491 QScopedPointer<DeselectableTreeView> view(new DeselectableTreeView(nullptr));
492 QVERIFY(view != nullptr);
493}
494
495void tst_ui::deselectableTreeViewHasEmptyClickedSignal() {
496 // Verify emptyClicked signal is connectable via QSignalSpy
497 QScopedPointer<DeselectableTreeView> view(new DeselectableTreeView(nullptr));
498 QSignalSpy spy(view.data(), SIGNAL(emptyClicked()));
499 QVERIFY(spy.isValid());
500 // No click occurred yet, so count should be 0
501 QCOMPARE(spy.count(), 0);
502}
503
504QTEST_MAIN(tst_ui)
505#include "tst_ui.moc"
auto animationDelay() const -> int
Returns the delay between animation steps.
auto heightForWidth(int w) const -> int
QProgressIndicator::heightForWidth square ratio.
void stopAnimation()
Stops the spin animation.
auto isAnimated() const -> bool
Returns a Boolean value indicating whether the component is currently animated.
void startAnimation()
Starts the spin animation.
auto isDisplayedWhenStopped() const -> bool
Returns a Boolean value indicating whether the receiver shows itself even when it is not animating.
void setColor(const QColor &color)
Sets the color of the components to the given color.
void setDisplayedWhenStopped(bool state)
Sets whether the component hides itself when it is not animating.
virtual auto sizeHint() const -> QSize
QProgressIndicator::sizeHint default minimum size.
void setAnimationDelay(int delay)
Sets the delay between animation steps.
auto color() const -> const QColor &
Returns the color of the component.
void setTextToCopy(const QString &value)
QPushButtonAsQRCode::setTextToCopy sets text from associated text field.
auto getTextToCopy() const -> QString
QPushButtonAsQRCode::getTextToCopy returns the text of associated text field.
void setTextToCopy(const QString &value)
QPushButtonWithClipboard::setTextToCopy sets text from associated text field.
auto getTextToCopy() const -> QString
QPushButtonWithClipboard::getTextToCopy returns the text of associated text field.
static QDialog * createQRCodePopup(const QPixmap &image)
QtPass::createQRCodePopup creates a popup dialog with the given QR code image. This is extracted for ...
Definition qtpass.cpp:549
Holds the Password configuration settings.
int length
Length of the password.