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"