File indexing completed on 2025-01-19 04:56:59
0001 /* 0002 * SPDX-FileCopyrightText: 2014 Kevin Ottens <ervin@kde.org> 0003 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0004 */ 0005 0006 0007 #include <testlib/qtest_gui_zanshin.h> 0008 0009 #include <QAbstractItemModel> 0010 #include <QAction> 0011 #include <QDebug> 0012 #include <QHeaderView> 0013 #include <QStandardItemModel> 0014 #include <QStringListModel> 0015 #include <QToolBar> 0016 #include <QTreeView> 0017 0018 #include "domain/project.h" 0019 #include "domain/context.h" 0020 0021 #include "presentation/metatypes.h" 0022 #include "presentation/querytreemodelbase.h" 0023 0024 #include "widgets/availablepagesview.h" 0025 #include "widgets/nameanddatasourcedialog.h" 0026 #include "widgets/quickselectdialog.h" 0027 0028 #include "messageboxstub.h" 0029 0030 class NameAndDataSourceDialogStub : public Widgets::NameAndDataSourceDialogInterface 0031 { 0032 public: 0033 typedef QSharedPointer<NameAndDataSourceDialogStub> Ptr; 0034 0035 explicit NameAndDataSourceDialogStub() 0036 : parent(nullptr), 0037 execCount(0), 0038 sourceModel(nullptr), 0039 source(Domain::DataSource::Ptr::create()) 0040 { 0041 } 0042 0043 int exec() override 0044 { 0045 execCount++; 0046 return QDialog::Accepted; 0047 } 0048 0049 void setDataSourcesModel(QAbstractItemModel *model) override 0050 { 0051 sourceModel = model; 0052 } 0053 0054 QString name() const override 0055 { 0056 return QStringLiteral("name"); 0057 } 0058 0059 Domain::DataSource::Ptr dataSource() const override 0060 { 0061 return source; 0062 } 0063 0064 void setWindowTitle(const QString &title) override 0065 { 0066 windowTitle = title; 0067 } 0068 0069 QWidget *parent; 0070 int execCount; 0071 QAbstractItemModel *sourceModel; 0072 Domain::DataSource::Ptr source; 0073 QString windowTitle; 0074 }; 0075 0076 class QuickSelectDialogStub : public Widgets::QuickSelectDialogInterface 0077 { 0078 public: 0079 typedef QSharedPointer<QuickSelectDialogStub> Ptr; 0080 0081 explicit QuickSelectDialogStub() 0082 : parent(nullptr), 0083 execCount(0), 0084 itemModel(nullptr) 0085 { 0086 } 0087 0088 int exec() override 0089 { 0090 execCount++; 0091 return QDialog::Accepted; 0092 } 0093 0094 void setModel(QAbstractItemModel *model) override 0095 { 0096 itemModel = model; 0097 } 0098 0099 QPersistentModelIndex selectedIndex() const override 0100 { 0101 return index; 0102 } 0103 0104 QWidget *parent; 0105 int execCount; 0106 QAbstractItemModel *itemModel; 0107 QPersistentModelIndex index; 0108 }; 0109 0110 class AvailablePagesModelStub : public QObject 0111 { 0112 Q_OBJECT 0113 public: 0114 explicit AvailablePagesModelStub(QObject *parent = nullptr) 0115 : QObject(parent) 0116 { 0117 } 0118 public slots: 0119 void addProject(const QString &name, const Domain::DataSource::Ptr &source) 0120 { 0121 projectNames << name; 0122 sources << source; 0123 } 0124 0125 void addContext(const QString &name, const Domain::DataSource::Ptr &source) 0126 { 0127 contextNames << name; 0128 sources << source; 0129 } 0130 0131 void removeItem(const QModelIndex &index) 0132 { 0133 projectRemoved = index.data().toString(); 0134 } 0135 0136 public Q_SLOTS: 0137 QObject *createPageForIndex(const QModelIndex &) { return nullptr; } 0138 0139 public: 0140 QStringList projectNames; 0141 QStringList contextNames; 0142 QList<Domain::DataSource::Ptr> sources; 0143 QString projectRemoved; 0144 }; 0145 0146 class AvailablePagesViewTest : public QObject 0147 { 0148 Q_OBJECT 0149 private slots: 0150 void shouldHaveDefaultState() 0151 { 0152 Widgets::AvailablePagesView available; 0153 0154 QVERIFY(!available.model()); 0155 QVERIFY(!available.projectSourcesModel()); 0156 QVERIFY(available.defaultProjectSource().isNull()); 0157 0158 auto pagesView = available.findChild<QTreeView*>(QStringLiteral("pagesView")); 0159 QVERIFY(pagesView); 0160 QVERIFY(pagesView->isVisibleTo(&available)); 0161 QVERIFY(!pagesView->header()->isVisibleTo(&available)); 0162 QCOMPARE(pagesView->dragDropMode(), QTreeView::DropOnly); 0163 0164 auto actionBar = available.findChild<QToolBar*>(QStringLiteral("actionBar")); 0165 QVERIFY(actionBar); 0166 QVERIFY(actionBar->isVisibleTo(&available)); 0167 0168 auto addProjectAction = available.findChild<QAction*>(QStringLiteral("addProjectAction")); 0169 QVERIFY(addProjectAction); 0170 auto addContextAction = available.findChild<QAction*>(QStringLiteral("addContextAction")); 0171 QVERIFY(addContextAction); 0172 auto removeAction = available.findChild<QAction*>(QStringLiteral("removeAction")); 0173 QVERIFY(removeAction); 0174 auto goPreviousAction = available.findChild<QAction*>(QStringLiteral("goPreviousAction")); 0175 QVERIFY(goPreviousAction); 0176 auto goNextAction = available.findChild<QAction*>(QStringLiteral("goNextAction")); 0177 QVERIFY(goNextAction); 0178 auto goToAction = available.findChild<QAction*>(QStringLiteral("goToAction")); 0179 QVERIFY(goToAction); 0180 0181 auto projectDialogFactory = available.projectDialogFactory(); 0182 QVERIFY(projectDialogFactory(&available).dynamicCast<Widgets::NameAndDataSourceDialog>()); 0183 0184 auto quickSelectDialogFactory = available.quickSelectDialogFactory(); 0185 QVERIFY(quickSelectDialogFactory(&available).dynamicCast<Widgets::QuickSelectDialog>()); 0186 0187 auto actions = available.globalActions(); 0188 QCOMPARE(actions.value(QStringLiteral("pages_project_add")), addProjectAction); 0189 QCOMPARE(actions.value(QStringLiteral("pages_context_add")), addContextAction); 0190 QCOMPARE(actions.value(QStringLiteral("pages_remove")), removeAction); 0191 QCOMPARE(actions.value(QStringLiteral("pages_go_previous")), goPreviousAction); 0192 QCOMPARE(actions.value(QStringLiteral("pages_go_next")), goNextAction); 0193 QCOMPARE(actions.value(QStringLiteral("pages_go_to")), goToAction); 0194 } 0195 0196 void shouldDisplayListFromPageModel() 0197 { 0198 // GIVEN 0199 QStringListModel model(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C") ); 0200 0201 AvailablePagesModelStub stubPagesModel; 0202 stubPagesModel.setProperty("pageListModel", QVariant::fromValue(static_cast<QAbstractItemModel*>(&model))); 0203 0204 Widgets::AvailablePagesView available; 0205 auto pagesView = available.findChild<QTreeView*>(QStringLiteral("pagesView")); 0206 QVERIFY(pagesView); 0207 QVERIFY(!pagesView->model()); 0208 0209 // WHEN 0210 available.setModel(&stubPagesModel); 0211 QTest::qWait(10); 0212 0213 // THEN 0214 QCOMPARE(pagesView->model(), &model); 0215 QCOMPARE(pagesView->selectionModel()->currentIndex(), model.index(0, 0)); 0216 } 0217 0218 void shouldNotCrashWithNullModel() 0219 { 0220 // GIVEN 0221 QStringListModel model(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C") ); 0222 0223 AvailablePagesModelStub stubPagesModel; 0224 stubPagesModel.setProperty("pageListModel", QVariant::fromValue(static_cast<QAbstractItemModel*>(&model))); 0225 0226 Widgets::AvailablePagesView available; 0227 available.setModel(&stubPagesModel); 0228 QTest::qWait(10); 0229 0230 auto pagesView = available.findChild<QTreeView*>(QStringLiteral("pagesView")); 0231 QVERIFY(pagesView); 0232 QCOMPARE(pagesView->model(), &model); 0233 0234 // WHEN 0235 available.setModel(nullptr); 0236 QTest::qWait(10); 0237 0238 // THEN 0239 QVERIFY(!available.isEnabled()); 0240 QVERIFY(!pagesView->model()); 0241 } 0242 0243 void shouldAddNewProjects() 0244 { 0245 // GIVEN 0246 AvailablePagesModelStub model; 0247 QStringListModel sourceModel; 0248 auto dialogStub = NameAndDataSourceDialogStub::Ptr::create(); 0249 0250 auto source = Domain::DataSource::Ptr::create(); 0251 0252 Widgets::AvailablePagesView available; 0253 available.setModel(&model); 0254 available.setProjectSourcesModel(&sourceModel); 0255 available.setDefaultProjectSource(source); 0256 available.setProjectDialogFactory([dialogStub] (QWidget *parent) { 0257 dialogStub->parent = parent; 0258 return dialogStub; 0259 }); 0260 0261 auto addProjectAction = available.findChild<QAction*>(QStringLiteral("addProjectAction")); 0262 0263 // WHEN 0264 addProjectAction->trigger(); 0265 0266 // THEN 0267 QCOMPARE(dialogStub->execCount, 1); 0268 QCOMPARE(dialogStub->parent, &available); 0269 QCOMPARE(dialogStub->sourceModel, &sourceModel); 0270 QCOMPARE(model.projectNames.size(), 1); 0271 QCOMPARE(model.projectNames.first(), dialogStub->name()); 0272 QCOMPARE(model.sources.size(), 1); 0273 QCOMPARE(model.sources.first(), dialogStub->dataSource()); 0274 QCOMPARE(available.defaultProjectSource(), dialogStub->dataSource()); 0275 } 0276 0277 void shouldAddNewContexts() 0278 { 0279 // GIVEN 0280 AvailablePagesModelStub model; 0281 QStringListModel sourceModel; 0282 auto dialogStub = NameAndDataSourceDialogStub::Ptr::create(); 0283 0284 auto source = Domain::DataSource::Ptr::create(); 0285 0286 Widgets::AvailablePagesView available; 0287 available.setModel(&model); 0288 available.setProjectSourcesModel(&sourceModel); 0289 available.setDefaultProjectSource(source); 0290 available.setProjectDialogFactory([dialogStub] (QWidget *parent) { 0291 dialogStub->parent = parent; 0292 return dialogStub; 0293 }); 0294 0295 auto addContextAction = available.findChild<QAction*>(QStringLiteral("addContextAction")); 0296 0297 // WHEN 0298 addContextAction->trigger(); 0299 0300 // THEN 0301 QCOMPARE(dialogStub->execCount, 1); 0302 QCOMPARE(dialogStub->parent, &available); 0303 QCOMPARE(dialogStub->sourceModel, &sourceModel); 0304 QCOMPARE(dialogStub->windowTitle, QStringLiteral("Add a context")); 0305 QCOMPARE(model.contextNames.size(), 1); 0306 QCOMPARE(model.contextNames.first(), QStringLiteral("name")); 0307 QCOMPARE(model.sources.size(), 1); 0308 QCOMPARE(model.sources.first(), dialogStub->dataSource()); 0309 QCOMPARE(available.defaultProjectSource(), dialogStub->dataSource()); 0310 } 0311 0312 void shouldRemoveAPage_data() 0313 { 0314 QTest::addColumn<QObjectPtr>("object"); 0315 QTest::addColumn<bool>("actionEnabled"); 0316 0317 auto project1 = Domain::Project::Ptr::create(); 0318 project1->setName(QStringLiteral("Project 1")); 0319 QTest::newRow("project") << QObjectPtr(project1) << true; 0320 0321 auto context1 = Domain::Context::Ptr::create(); 0322 context1->setName(QStringLiteral("Context 1")); 0323 QTest::newRow("context") << QObjectPtr(context1) << true; 0324 0325 QTest::newRow("non removable") << QObjectPtr::create() << false; 0326 } 0327 0328 void shouldRemoveAPage() 0329 { 0330 QFETCH(QObjectPtr, object); 0331 QFETCH(bool, actionEnabled); 0332 0333 // GIVEN 0334 QStringList list; 0335 list << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C"); 0336 QStandardItemModel model; 0337 for (int row = 0; row < list.count(); ++row) { 0338 model.setItem(row, new QStandardItem(list.at(row))); 0339 } 0340 QVERIFY(model.setData(model.index(0, 0), QVariant::fromValue(object), Presentation::QueryTreeModelBase::ObjectRole)); 0341 0342 AvailablePagesModelStub stubPagesModel; 0343 stubPagesModel.setProperty("pageListModel", QVariant::fromValue(static_cast<QAbstractItemModel*>(&model))); 0344 0345 Widgets::AvailablePagesView available; 0346 auto pagesView = available.findChild<QTreeView*>(QStringLiteral("pagesView")); 0347 QVERIFY(pagesView); 0348 QVERIFY(!pagesView->model()); 0349 0350 available.setModel(&stubPagesModel); 0351 QTest::qWait(10); 0352 0353 auto removeAction = available.findChild<QAction*>(QStringLiteral("removeAction")); 0354 0355 auto msgbox = MessageBoxStub::Ptr::create(); 0356 available.setMessageBoxInterface(msgbox); 0357 0358 // WHEN 0359 if (actionEnabled) 0360 removeAction->trigger(); 0361 0362 // THEN 0363 QCOMPARE(removeAction->isEnabled(), actionEnabled); 0364 if (actionEnabled) { 0365 QCOMPARE(stubPagesModel.projectRemoved, list.first()); 0366 } 0367 } 0368 0369 void shouldGoToPreviousSelectablePage() 0370 { 0371 // GIVEN 0372 QStandardItemModel model; 0373 model.appendRow(new QStandardItem(QStringLiteral("Inbox"))); 0374 auto projects = new QStandardItem(QStringLiteral("Projects")); 0375 projects->setFlags(Qt::NoItemFlags); 0376 model.appendRow(projects); 0377 projects->appendRow(new QStandardItem(QStringLiteral("Project 1"))); 0378 projects->appendRow(new QStandardItem(QStringLiteral("Project 2"))); 0379 0380 AvailablePagesModelStub stubPagesModel; 0381 stubPagesModel.setProperty("pageListModel", QVariant::fromValue(static_cast<QAbstractItemModel*>(&model))); 0382 0383 Widgets::AvailablePagesView available; 0384 auto pagesView = available.findChild<QTreeView*>(QStringLiteral("pagesView")); 0385 QVERIFY(pagesView); 0386 QVERIFY(!pagesView->model()); 0387 0388 available.setModel(&stubPagesModel); 0389 QTest::qWait(10); 0390 0391 auto goPreviousAction = available.findChild<QAction*>(QStringLiteral("goPreviousAction")); 0392 pagesView->setCurrentIndex(model.index(1, 0, model.indexFromItem(projects))); 0393 0394 // WHEN 0395 goPreviousAction->trigger(); 0396 0397 // THEN 0398 QCOMPARE(pagesView->currentIndex(), 0399 model.index(0, 0, model.indexFromItem(projects))); 0400 0401 // WHEN 0402 goPreviousAction->trigger(); 0403 0404 // THEN 0405 QCOMPARE(pagesView->currentIndex(), 0406 model.index(0, 0)); 0407 0408 // WHEN 0409 goPreviousAction->trigger(); 0410 0411 // THEN 0412 QCOMPARE(pagesView->currentIndex(), 0413 model.index(0, 0)); 0414 } 0415 0416 void shouldGoToNextSelectablePage() 0417 { 0418 // GIVEN 0419 QStandardItemModel model; 0420 model.appendRow(new QStandardItem(QStringLiteral("Inbox"))); 0421 auto projects = new QStandardItem(QStringLiteral("Projects")); 0422 projects->setFlags(Qt::NoItemFlags); 0423 model.appendRow(projects); 0424 projects->appendRow(new QStandardItem(QStringLiteral("Project 1"))); 0425 projects->appendRow(new QStandardItem(QStringLiteral("Project 2"))); 0426 0427 AvailablePagesModelStub stubPagesModel; 0428 stubPagesModel.setProperty("pageListModel", QVariant::fromValue(static_cast<QAbstractItemModel*>(&model))); 0429 0430 Widgets::AvailablePagesView available; 0431 auto pagesView = available.findChild<QTreeView*>(QStringLiteral("pagesView")); 0432 QVERIFY(pagesView); 0433 QVERIFY(!pagesView->model()); 0434 0435 available.setModel(&stubPagesModel); 0436 QTest::qWait(10); 0437 0438 auto goNextAction = available.findChild<QAction*>(QStringLiteral("goNextAction")); 0439 pagesView->setCurrentIndex(model.index(0, 0)); 0440 0441 // WHEN 0442 goNextAction->trigger(); 0443 0444 // THEN 0445 QCOMPARE(pagesView->currentIndex(), 0446 model.index(0, 0, model.indexFromItem(projects))); 0447 0448 // WHEN 0449 goNextAction->trigger(); 0450 0451 // THEN 0452 QCOMPARE(pagesView->currentIndex(), 0453 model.index(1, 0, model.indexFromItem(projects))); 0454 0455 // WHEN 0456 goNextAction->trigger(); 0457 0458 // THEN 0459 QCOMPARE(pagesView->currentIndex(), 0460 model.index(1, 0, model.indexFromItem(projects))); 0461 } 0462 0463 void shouldGoToUserSelectedIndex() 0464 { 0465 // GIVEN 0466 QStandardItemModel model; 0467 model.appendRow(new QStandardItem(QStringLiteral("Inbox"))); 0468 auto projects = new QStandardItem(QStringLiteral("Projects")); 0469 projects->setFlags(Qt::NoItemFlags); 0470 model.appendRow(projects); 0471 projects->appendRow(new QStandardItem(QStringLiteral("Project 1"))); 0472 projects->appendRow(new QStandardItem(QStringLiteral("Project 2"))); 0473 0474 AvailablePagesModelStub stubPagesModel; 0475 stubPagesModel.setProperty("pageListModel", QVariant::fromValue(static_cast<QAbstractItemModel*>(&model))); 0476 auto dialogStub = QuickSelectDialogStub::Ptr::create(); 0477 // Project 2 will be selected 0478 dialogStub->index = model.index(1, 0, model.index(1, 0)); 0479 0480 Widgets::AvailablePagesView available; 0481 available.setModel(&stubPagesModel); 0482 available.setQuickSelectDialogFactory([dialogStub] (QWidget *parent) { 0483 dialogStub->parent = parent; 0484 return dialogStub; 0485 }); 0486 0487 auto pagesView = available.findChild<QTreeView*>(QStringLiteral("pagesView")); 0488 QVERIFY(pagesView); 0489 QCOMPARE(pagesView->model(), &model); 0490 0491 auto goToAction = available.findChild<QAction*>(QStringLiteral("goToAction")); 0492 0493 // WHEN 0494 goToAction->trigger(); 0495 0496 // THEN 0497 QCOMPARE(dialogStub->execCount, 1); 0498 QCOMPARE(dialogStub->parent, &available); 0499 QCOMPARE(dialogStub->itemModel, &model); 0500 QCOMPARE(QPersistentModelIndex(pagesView->currentIndex()), dialogStub->index); 0501 } 0502 }; 0503 0504 ZANSHIN_TEST_MAIN(AvailablePagesViewTest) 0505 0506 #include "availablepagesviewtest.moc"