2
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
3
* Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
5
* This program is free software: you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation, either version 2 or (at your option)
8
* version 3 of the License.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program. If not, see <http://www.gnu.org/licenses/>.
19
#include "TestGuiBrowser.h"
20
#include "gui/Application.h"
26
#include <QPlainTextEdit>
32
#include "browser/BrowserService.h"
33
#include "config-keepassx-tests.h"
34
#include "crypto/Crypto.h"
35
#include "gui/DatabaseTabWidget.h"
36
#include "gui/FileDialog.h"
37
#include "gui/MessageBox.h"
38
#include "gui/PasswordWidget.h"
39
#include "gui/entry/EditEntryWidget.h"
40
#include "gui/entry/EntryView.h"
42
int main(int argc, char* argv[])
44
#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
45
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
46
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
48
Application app(argc, argv);
49
app.setApplicationName("KeePassXC");
50
app.setApplicationVersion(KEEPASSXC_VERSION);
51
app.setQuitOnLastWindowClosed(false);
52
app.setAttribute(Qt::AA_Use96Dpi, true);
54
QTEST_DISABLE_KEYPAD_NAVIGATION
56
QTEST_SET_MAIN_SOURCE_PATH
57
return QTest::qExec(&tc, argc, argv);
60
void TestGuiBrowser::initTestCase()
62
QVERIFY(Crypto::init());
63
Config::createTempFileInstance();
64
// Disable autosave so we can test the modified file indicator
65
config()->set(Config::AutoSaveAfterEveryChange, false);
66
config()->set(Config::AutoSaveOnExit, false);
67
// Enable the tray icon so we can test hiding/restoring the windowQByteArray
68
config()->set(Config::GUI_ShowTrayIcon, true);
69
// Disable the update check first time alert
70
config()->set(Config::UpdateCheckMessageShown, true);
71
// Disable quick unlock
72
config()->set(Config::Security_QuickUnlock, false);
74
m_mainWindow.reset(new MainWindow());
75
m_tabWidget = m_mainWindow->findChild<DatabaseTabWidget*>("tabWidget");
79
// Every test starts with opening the temp database
80
void TestGuiBrowser::init()
82
m_dbFile.reset(new TemporaryFile());
83
m_dbFile->copyFromFile(QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabaseBrowser.kdbx"));
85
// make sure window is activated or focus tests may fail
86
m_mainWindow->activateWindow();
87
QApplication::processEvents();
89
fileDialog()->setNextFileName(m_dbFile->fileName());
90
triggerAction("actionDatabaseOpen");
92
auto* databaseOpenWidget = m_tabWidget->currentDatabaseWidget()->findChild<QWidget*>("databaseOpenWidget");
93
QVERIFY(databaseOpenWidget);
95
databaseOpenWidget->findChild<PasswordWidget*>("editPassword")->findChild<QLineEdit*>("passwordEdit");
96
QVERIFY(editPassword);
97
editPassword->setFocus();
99
QTest::keyClicks(editPassword, "a");
100
QTest::keyClick(editPassword, Qt::Key_Enter);
102
m_dbWidget = m_tabWidget->currentDatabaseWidget();
103
m_db = m_dbWidget->database();
106
// Every test ends with closing the temp database without saving
107
void TestGuiBrowser::cleanup()
109
// DO NOT save the database
111
MessageBox::setNextAnswer(MessageBox::No);
112
triggerAction("actionDatabaseClose");
113
QApplication::processEvents();
114
MessageBox::setNextAnswer(MessageBox::NoButton);
123
void TestGuiBrowser::cleanupTestCase()
128
void TestGuiBrowser::testEntrySettings()
130
// Enable the Browser Integration
131
config()->set(Config::Browser_Enabled, true);
133
auto* toolBar = m_mainWindow->findChild<QToolBar*>("toolBar");
134
auto* entryView = m_dbWidget->findChild<EntryView*>("entryView");
136
entryView->setFocus();
137
QVERIFY(entryView->hasFocus());
139
// Select the first entry in the database
140
QModelIndex entryItem = entryView->model()->index(0, 1);
141
Entry* entry = entryView->entryFromIndex(entryItem);
142
clickIndex(entryItem, entryView, Qt::LeftButton);
144
auto* entryEditAction = m_mainWindow->findChild<QAction*>("actionEntryEdit");
145
QWidget* entryEditWidget = toolBar->widgetForAction(entryEditAction);
146
QTest::mouseClick(entryEditWidget, Qt::LeftButton);
147
QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode);
148
auto* editEntryWidget = m_dbWidget->findChild<EditEntryWidget*>("editEntryWidget");
150
// Switch to Properties page and select all rows from the custom data table
151
editEntryWidget->setCurrentPage(5);
152
auto customDataTableView = editEntryWidget->findChild<QTableView*>("customDataTable");
153
QVERIFY(customDataTableView);
154
QTest::mouseClick(customDataTableView, Qt::LeftButton);
155
QTest::keyClick(customDataTableView, 'a', Qt::ControlModifier);
158
QCOMPARE(entry->customData()->size(), 2);
159
auto* removeButton = editEntryWidget->findChild<QPushButton*>("removeCustomDataButton");
160
QVERIFY(removeButton);
161
MessageBox::setNextAnswer(MessageBox::Delete);
162
QTest::mouseClick(removeButton, Qt::LeftButton);
165
auto* editEntryWidgetButtonBox = editEntryWidget->findChild<QDialogButtonBox*>("buttonBox");
166
QVERIFY(editEntryWidgetButtonBox);
167
auto* okButton = editEntryWidgetButtonBox->button(QDialogButtonBox::Ok);
169
QTRY_VERIFY(okButton->isEnabled());
170
QTest::mouseClick(okButton, Qt::LeftButton);
171
QApplication::processEvents();
173
QCOMPARE(entry->customData()->size(), 0);
176
void TestGuiBrowser::testAdditionalURLs()
178
auto* toolBar = m_mainWindow->findChild<QToolBar*>("toolBar");
179
auto* entryView = m_dbWidget->findChild<EntryView*>("entryView");
181
entryView->setFocus();
182
QVERIFY(entryView->hasFocus());
184
// Select the first entry in the database
185
QModelIndex entryItem = entryView->model()->index(0, 1);
186
clickIndex(entryItem, entryView, Qt::LeftButton);
188
auto* entryEditAction = m_mainWindow->findChild<QAction*>("actionEntryEdit");
189
QWidget* entryEditWidget = toolBar->widgetForAction(entryEditAction);
190
QTest::mouseClick(entryEditWidget, Qt::LeftButton);
191
QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::EditMode);
192
auto* editEntryWidget = m_dbWidget->findChild<EditEntryWidget*>("editEntryWidget");
194
// Switch to Browser Integration page and add three URL's
195
editEntryWidget->setCurrentPage(4);
196
auto* addURLButton = editEntryWidget->findChild<QPushButton*>("addURLButton");
197
QVERIFY(addURLButton);
199
auto* urlList = editEntryWidget->findChild<QListView*>("additionalURLsView");
202
QStringList testURLs = {"https://example1.com", "https://example2.com", "https://example3.com"};
204
for (const auto& url : testURLs) {
205
QTest::mouseClick(addURLButton, Qt::LeftButton);
206
QApplication::processEvents();
207
QTest::keyClicks(urlList->focusWidget(), url);
208
QTest::keyClick(urlList->focusWidget(), Qt::Key_Enter);
211
// Check the values from attributesEdit
212
editEntryWidget->setCurrentPage(1);
213
auto* attributesView = editEntryWidget->findChild<QListView*>("attributesView");
214
auto* attrTextEdit = editEntryWidget->findChild<QPlainTextEdit*>("attributesEdit");
216
// Go top of the list
217
attributesView->setFocus();
218
QTest::keyClick(attributesView->focusWidget(), Qt::Key_PageUp);
220
for (const auto& url : testURLs) {
221
QCOMPARE(attrTextEdit->toPlainText(), url);
222
QTest::keyClick(attributesView->focusWidget(), Qt::Key_Down);
226
void TestGuiBrowser::testGetDatabaseGroups()
228
auto result = browserService()->getDatabaseGroups();
229
QCOMPARE(result.length(), 1);
231
auto groups = result["groups"].toArray();
232
auto first = groups.at(0);
233
auto children = first.toObject()["children"].toArray();
234
QCOMPARE(first.toObject()["name"].toString(), QString("NewDatabase"));
235
QCOMPARE(children.size(), 6);
237
auto firstChild = children.at(0).toObject();
238
auto secondChild = children.at(1).toObject();
239
QCOMPARE(firstChild["name"].toString(), QString("General"));
240
QCOMPARE(secondChild["name"].toString(), QString("Windows"));
242
auto subGroups = firstChild["children"].toArray();
243
QCOMPARE(subGroups.count(), 1);
244
auto subGroupObj = subGroups.at(0).toObject();
245
QCOMPARE(subGroupObj["name"].toString(), QString("SubGroup"));
248
void TestGuiBrowser::triggerAction(const QString& name)
250
auto* action = m_mainWindow->findChild<QAction*>(name);
252
QVERIFY(action->isEnabled());
254
QApplication::processEvents();
257
void TestGuiBrowser::clickIndex(const QModelIndex& index,
258
QAbstractItemView* view,
259
Qt::MouseButton button,
260
Qt::KeyboardModifiers stateKey)
262
QTest::mouseClick(view->viewport(), button, stateKey, view->visualRect(index).center());