File indexing completed on 2025-01-05 04:59:57

0001 /*
0002  * SPDX-FileCopyrightText: 2019 David Faure <faure@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_zanshin.h>
0008 
0009 #include <memory>
0010 
0011 #include <QMimeData>
0012 
0013 #include "utils/datetime.h"
0014 
0015 #include "domain/taskqueries.h"
0016 #include "domain/taskrepository.h"
0017 #include "presentation/alltaskspagemodel.h"
0018 #include "presentation/querytreemodelbase.h"
0019 
0020 #include "akonadi/akonadiserializerinterface.h"
0021 
0022 #include "utils/dependencymanager.h"
0023 #include "integration/dependencies.h"
0024 
0025 #include "testlib/fakejob.h"
0026 #include "testlib/akonadifakedata.h"
0027 #include "testlib/gencollection.h"
0028 #include "testlib/gentodo.h"
0029 #include "testlib/testhelpers.h"
0030 
0031 using namespace Testlib;
0032 
0033 class AllTasksPageModelTest : public QObject
0034 {
0035     Q_OBJECT
0036 private slots:
0037     void cleanup()
0038     {
0039         // The first call to QueryTreeModelBase::data triggers fetchTaskExtraData which creates jobs.
0040         // Wait for these to finish so they don't mess up subsequent tests
0041         TestHelpers::waitForEmptyJobQueue();
0042     }
0043 
0044     void shouldListAllTasksInCentralListModel()
0045     {
0046         // GIVEN
0047         const auto today = Utils::DateTime::currentDate();
0048 
0049         AkonadiFakeData data;
0050         auto deps = data.createDependencies();
0051         Integration::initializeDefaultDomainDependencies(*deps.get());
0052 
0053         // One top level collection
0054         data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent());
0055 
0056         // One project
0057         data.createItem(GenTodo().withId(47).withParent(42).withUid("47").withTitle(QStringLiteral("KDE")).asProject());
0058 
0059         // Three tasks
0060         data.createItem(GenTodo().withId(42).withParent(42).withUid("42").withTitle(QStringLiteral("task1")).withStartDate(today.addDays(-10)).withDueDate(today));
0061         data.createItem(GenTodo().withId(43).withParent(42).withUid("43").withParentUid("47").withTitle(QStringLiteral("task2")).withStartDate(today).withDueDate(today.addDays(10)));
0062         data.createItem(GenTodo().withId(44).withParent(42).withUid("44").withTitle(QStringLiteral("task3")).withStartDate(today.addYears(-4)).withDueDate(today.addYears(-3)));
0063 
0064         // Two tasks under the task1
0065         data.createItem(GenTodo().withId(45).withParent(42).withUid("45").withParentUid("42").withTitle(QStringLiteral("childTask11")));
0066         data.createItem(GenTodo().withId(46).withParent(42).withUid("46").withParentUid("42").withTitle(QStringLiteral("childTask12")).withStartDate(today).withDueDate(today));
0067 
0068         auto serializer = deps->create<Akonadi::SerializerInterface>();
0069         Presentation::AllTasksPageModel pageModel(deps->create<Domain::TaskQueries>(),
0070                                                   deps->create<Domain::TaskRepository>());
0071 
0072         // WHEN
0073         QAbstractItemModel *model = pageModel.centralListModel();
0074         TestHelpers::waitForEmptyJobQueue();
0075 
0076         // THEN
0077         QCOMPARE(model->rowCount(), 3);
0078 
0079         const QModelIndex task1Index = model->index(0, 0);
0080         const QModelIndex task2Index = model->index(1, 0);
0081         const QModelIndex task3Index = model->index(2, 0);
0082 
0083         const QModelIndex childTask11Index = model->index(0, 0, task1Index);
0084         const QModelIndex childTask12Index = model->index(1, 0, task1Index);
0085 
0086         QCOMPARE(model->rowCount(task1Index), 2);
0087         QCOMPARE(model->rowCount(task2Index), 0);
0088         QCOMPARE(model->rowCount(task3Index), 0);
0089 
0090         QVERIFY(childTask11Index.isValid());
0091         QVERIFY(childTask12Index.isValid());
0092         QCOMPARE(model->rowCount(childTask11Index), 0);
0093         QCOMPARE(model->rowCount(childTask12Index), 0);
0094 
0095         auto task1 = model->data(task1Index, Presentation::QueryTreeModelBase::ObjectRole).value<Domain::Task::Ptr>();
0096         auto task2 = model->data(task2Index, Presentation::QueryTreeModelBase::ObjectRole).value<Domain::Task::Ptr>();
0097         auto task3 = model->data(task3Index, Presentation::QueryTreeModelBase::ObjectRole).value<Domain::Task::Ptr>();
0098         auto childTask11 = model->data(childTask11Index, Presentation::QueryTreeModelBase::ObjectRole).value<Domain::Task::Ptr>();
0099         auto childTask12 = model->data(childTask12Index, Presentation::QueryTreeModelBase::ObjectRole).value<Domain::Task::Ptr>();
0100 
0101         TestHelpers::waitForEmptyJobQueue();
0102 
0103         const Qt::ItemFlags defaultFlags = Qt::ItemIsSelectable
0104                                          | Qt::ItemIsEnabled
0105                                          | Qt::ItemIsEditable
0106                                          | Qt::ItemIsDragEnabled;
0107         QCOMPARE(model->flags(task1Index), defaultFlags | Qt::ItemIsUserCheckable | Qt::ItemIsDropEnabled);
0108         QCOMPARE(model->flags(childTask11Index), defaultFlags | Qt::ItemIsUserCheckable | Qt::ItemIsDropEnabled);
0109         QCOMPARE(model->flags(childTask12Index), defaultFlags | Qt::ItemIsUserCheckable | Qt::ItemIsDropEnabled);
0110         QCOMPARE(model->flags(task2Index), defaultFlags | Qt::ItemIsUserCheckable | Qt::ItemIsDropEnabled);
0111         QCOMPARE(model->flags(task3Index), defaultFlags | Qt::ItemIsUserCheckable | Qt::ItemIsDropEnabled);
0112 
0113         QCOMPARE(model->data(task1Index).toString(), task1->title());
0114         QCOMPARE(model->data(childTask11Index).toString(), childTask11->title());
0115         QCOMPARE(model->data(childTask12Index).toString(), childTask12->title());
0116         QCOMPARE(model->data(task2Index).toString(), task2->title());
0117         QCOMPARE(model->data(task3Index).toString(), task3->title());
0118 
0119         QCOMPARE(model->data(task1Index, Qt::EditRole).toString(), task1->title());
0120         QCOMPARE(model->data(childTask11Index, Qt::EditRole).toString(), childTask11->title());
0121         QCOMPARE(model->data(childTask12Index, Qt::EditRole).toString(), childTask12->title());
0122         QCOMPARE(model->data(task2Index, Qt::EditRole).toString(), task2->title());
0123         QCOMPARE(model->data(task3Index, Qt::EditRole).toString(), task3->title());
0124 
0125         QVERIFY(model->data(task1Index, Qt::CheckStateRole).isValid());
0126         QVERIFY(model->data(childTask11Index, Qt::CheckStateRole).isValid());
0127         QVERIFY(model->data(childTask12Index, Qt::CheckStateRole).isValid());
0128         QVERIFY(model->data(task2Index, Qt::CheckStateRole).isValid());
0129         QVERIFY(model->data(task3Index, Qt::CheckStateRole).isValid());
0130 
0131         QCOMPARE(model->data(task1Index, Qt::CheckStateRole).toBool(), false);
0132         QCOMPARE(model->data(childTask11Index, Qt::CheckStateRole).toBool(), false);
0133         QCOMPARE(model->data(childTask12Index, Qt::CheckStateRole).toBool(), false);
0134         QCOMPARE(model->data(task2Index, Qt::CheckStateRole).toBool(), false);
0135         QCOMPARE(model->data(task3Index, Qt::CheckStateRole).toBool(), false);
0136 
0137         QVERIFY(model->data(task1Index, Presentation::QueryTreeModelBase::ProjectRole).isValid());
0138         QVERIFY(model->data(task2Index, Presentation::QueryTreeModelBase::ProjectRole).isValid());
0139         QCOMPARE(model->data(task1Index, Presentation::QueryTreeModelBase::ProjectRole).toString(), QString());
0140         QCOMPARE(model->data(task2Index, Presentation::QueryTreeModelBase::ProjectRole).toString(), QString("KDE"));
0141         QCOMPARE(model->data(task1Index, Presentation::QueryTreeModelBase::IsChildRole).toBool(), false);
0142         QCOMPARE(model->data(task2Index, Presentation::QueryTreeModelBase::IsChildRole).toBool(), false);
0143         QVERIFY(!model->data(childTask11Index, Presentation::QueryTreeModelBase::ProjectRole).isValid());
0144         QVERIFY(!model->data(childTask12Index, Presentation::QueryTreeModelBase::ProjectRole).isValid());
0145         QCOMPARE(model->data(childTask11Index, Presentation::QueryTreeModelBase::IsChildRole).toBool(), true);
0146         QCOMPARE(model->data(childTask12Index, Presentation::QueryTreeModelBase::IsChildRole).toBool(), true);
0147 
0148         // WHEN
0149         QVERIFY(model->setData(task1Index, "newTask1"));
0150         QVERIFY(model->setData(childTask11Index, "newChildTask11"));
0151         QVERIFY(model->setData(task2Index, "newTask2"));
0152         QVERIFY(model->setData(task3Index, "newTask3"));
0153         QVERIFY(model->setData(childTask12Index, "newChildTask12"));
0154 
0155         QVERIFY(model->setData(task1Index, Qt::Unchecked, Qt::CheckStateRole));
0156         QVERIFY(model->setData(childTask11Index, Qt::Unchecked, Qt::CheckStateRole));
0157         QVERIFY(model->setData(task2Index, Qt::Checked, Qt::CheckStateRole));
0158         QVERIFY(model->setData(task3Index, Qt::Unchecked, Qt::CheckStateRole));
0159         QVERIFY(model->setData(childTask12Index, Qt::Checked, Qt::CheckStateRole));
0160 
0161         // THEN
0162         QCOMPARE(task1->title(), QStringLiteral("newTask1"));
0163         QCOMPARE(childTask11->title(), QStringLiteral("newChildTask11"));
0164         QCOMPARE(childTask12->title(), QStringLiteral("newChildTask12"));
0165         QCOMPARE(task2->title(), QStringLiteral("newTask2"));
0166         QCOMPARE(task3->title(), QStringLiteral("newTask3"));
0167 
0168         QCOMPARE(task1->isDone(), false);
0169         QCOMPARE(childTask11->isDone(), false);
0170         QCOMPARE(childTask12->isDone(), true);
0171         QCOMPARE(task2->isDone(), true);
0172         QCOMPARE(task3->isDone(), false);
0173 
0174         // WHEN
0175         auto mimeData = std::unique_ptr<QMimeData>(model->mimeData(QModelIndexList() << childTask12Index));
0176 
0177         // THEN
0178         QVERIFY(mimeData->hasFormat(QStringLiteral("application/x-zanshin-object")));
0179         QCOMPARE(mimeData->property("objects").value<Domain::Task::List>(),
0180                  Domain::Task::List() << childTask12);
0181 
0182         // WHEN
0183         data.createItem(GenTodo().withId(48).withParent(42).withUid("48").withTitle(QStringLiteral("childTask2")));
0184         auto childTask2 = model->data(model->index(3, 0), Presentation::QueryTreeModelBase::ObjectRole).value<Domain::Task::Ptr>();
0185         mimeData.reset(new QMimeData);
0186         mimeData->setData(QStringLiteral("application/x-zanshin-object"), "object");
0187         mimeData->setProperty("objects", QVariant::fromValue(Domain::Task::List() << childTask2));
0188         model->dropMimeData(mimeData.get(), Qt::MoveAction, -1, -1, childTask11Index);
0189         TestHelpers::waitForEmptyJobQueue();
0190 
0191         // THEN
0192         QCOMPARE(serializer->relatedUidFromItem(data.item(48)), QStringLiteral("45"));
0193         QCOMPARE(model->rowCount(), 3);
0194         QCOMPARE(model->rowCount(childTask11Index), 1);
0195 
0196         // WHEN
0197         data.createItem(GenTodo().withId(49).withParent(42).withUid("49").withTitle(QStringLiteral("childTask3")));
0198         auto childTask3 = model->data(model->index(3, 0), Presentation::QueryTreeModelBase::ObjectRole).value<Domain::Task::Ptr>();
0199         data.createItem(GenTodo().withId(50).withParent(42).withUid("50").withTitle(QStringLiteral("childTask4")));
0200         auto childTask4 = model->data(model->index(4, 0), Presentation::QueryTreeModelBase::ObjectRole).value<Domain::Task::Ptr>();
0201         mimeData.reset(new QMimeData);
0202         mimeData->setData(QStringLiteral("application/x-zanshin-object"), "object");
0203         mimeData->setProperty("objects", QVariant::fromValue(Domain::Task::List() << childTask3 << childTask4));
0204         model->dropMimeData(mimeData.get(), Qt::MoveAction, -1, -1, childTask12Index);
0205         TestHelpers::waitForEmptyJobQueue();
0206 
0207         // THEN
0208         QCOMPARE(serializer->relatedUidFromItem(data.item(49)), QStringLiteral("46"));
0209         QCOMPARE(serializer->relatedUidFromItem(data.item(50)), QStringLiteral("46"));
0210         QCOMPARE(model->rowCount(), 3);
0211         QCOMPARE(model->rowCount(childTask12Index), 2);
0212 
0213         // WHEN
0214         QVERIFY(!childTask4->startDate().isValid());
0215         mimeData.reset(new QMimeData);
0216         mimeData->setData(QStringLiteral("application/x-zanshin-object"), "object");
0217         mimeData->setProperty("objects", QVariant::fromValue(Domain::Task::List() << childTask4));
0218         model->dropMimeData(mimeData.get(), Qt::MoveAction, -1, -1, QModelIndex());
0219         TestHelpers::waitForEmptyJobQueue();
0220 
0221         // THEN
0222         QCOMPARE(serializer->relatedUidFromItem(data.item(50)), QString());
0223         QCOMPARE(model->rowCount(), 4);
0224         QCOMPARE(model->rowCount(childTask12Index), 1);
0225         QCOMPARE(childTask4->startDate(), QDate()); // unlike the workday
0226     }
0227 
0228     void shouldAddTasksInAllTasksPage()
0229     {
0230         // GIVEN
0231         AkonadiFakeData data;
0232         auto deps = data.createDependencies();
0233         Integration::initializeDefaultDomainDependencies(*deps.get());
0234         data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent());
0235 
0236         Presentation::AllTasksPageModel pageModel(deps->create<Domain::TaskQueries>(),
0237                                                   deps->create<Domain::TaskRepository>());
0238         QAbstractItemModel *model = pageModel.centralListModel();
0239 
0240         // WHEN
0241         auto title = QStringLiteral("New task");
0242         auto createdTask = pageModel.addItem(title);
0243         TestHelpers::waitForEmptyJobQueue();
0244 
0245         // THEN
0246         QVERIFY(createdTask);
0247         QCOMPARE(createdTask->title(), title);
0248         QCOMPARE(createdTask->startDate(), QDate()); // that's one difference with the workday, which would set it to Utils::DateTime::currentDate()
0249 
0250         QCOMPARE(model->rowCount(), 1);
0251         auto taskInModel = model->data(model->index(0, 0), Presentation::QueryTreeModelBase::ObjectRole).value<Domain::Task::Ptr>();
0252         QVERIFY(taskInModel);
0253         QCOMPARE(taskInModel->title(), createdTask->title());
0254         QCOMPARE(taskInModel->startDate(), createdTask->startDate());
0255 
0256         QCOMPARE(data.items().count(), 1);
0257     }
0258 
0259     void shouldAddChildTask()
0260     {
0261         // GIVEN
0262         AkonadiFakeData data;
0263         auto deps = data.createDependencies();
0264         Integration::initializeDefaultDomainDependencies(*deps.get());
0265         data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent());
0266 
0267         // One project
0268         data.createItem(GenTodo().withId(47).withParent(42).withUid("47").withTitle(QStringLiteral("KDE")).asProject());
0269 
0270         // One task
0271         data.createItem(GenTodo().withId(42).withParent(42).withUid("42").withTitle(QStringLiteral("task1")));
0272         QCOMPARE(data.items().count(), 2);
0273 
0274         Presentation::AllTasksPageModel pageModel(deps->create<Domain::TaskQueries>(),
0275                                                   deps->create<Domain::TaskRepository>());
0276         QAbstractItemModel *model = pageModel.centralListModel();
0277         TestHelpers::waitForEmptyJobQueue();
0278         QCOMPARE(model->rowCount(), 1);
0279 
0280         // WHEN
0281         const auto title = QStringLiteral("New task");
0282         const auto parentIndex = model->index(0, 0);
0283         const auto createdTask = pageModel.addItem(title, parentIndex);
0284         TestHelpers::waitForEmptyJobQueue();
0285 
0286         // THEN
0287         QVERIFY(createdTask);
0288         QCOMPARE(createdTask->title(), title);
0289         QVERIFY(!createdTask->startDate().isValid());
0290 
0291         QCOMPARE(model->rowCount(parentIndex), 1);
0292         auto taskInModel = model->data(model->index(0, 0, parentIndex), Presentation::QueryTreeModelBase::ObjectRole).value<Domain::Task::Ptr>();
0293         QVERIFY(taskInModel);
0294         QCOMPARE(taskInModel->title(), createdTask->title());
0295         QCOMPARE(taskInModel->startDate(), createdTask->startDate());
0296 
0297         QCOMPARE(data.items().count(), 3);
0298     }
0299 
0300     void shouldDeleteItems()
0301     {
0302         // GIVEN
0303         AkonadiFakeData data;
0304         auto deps = data.createDependencies();
0305         Integration::initializeDefaultDomainDependencies(*deps.get());
0306         data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent());
0307 
0308         // Two tasks
0309         data.createItem(GenTodo().withId(42).withParent(42).withUid("42").withTitle(QStringLiteral("task1")));
0310         data.createItem(GenTodo().withId(43).withParent(42).withUid("43").withTitle(QStringLiteral("task2")));
0311         QCOMPARE(data.items().count(), 2);
0312 
0313         Presentation::AllTasksPageModel pageModel(deps->create<Domain::TaskQueries>(),
0314                                                   deps->create<Domain::TaskRepository>());
0315         QAbstractItemModel *model = pageModel.centralListModel();
0316         TestHelpers::waitForEmptyJobQueue();
0317         QCOMPARE(model->rowCount(), 2);
0318 
0319         // WHEN
0320         const QModelIndex index = model->index(1, 0);
0321         QVERIFY(index.isValid());
0322         pageModel.removeItem(index);
0323         TestHelpers::waitForEmptyJobQueue();
0324 
0325         // THEN
0326         QCOMPARE(model->rowCount(), 1);
0327         QCOMPARE(data.items().count(), 1);
0328     }
0329 
0330     void shouldPromoteItem()
0331     {
0332         // GIVEN
0333         AkonadiFakeData data;
0334         auto deps = data.createDependencies();
0335         Integration::initializeDefaultDomainDependencies(*deps.get());
0336         data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent());
0337 
0338         // Two tasks
0339         data.createItem(GenTodo().withId(42).withParent(42).withUid("42").withTitle(QStringLiteral("task1")));
0340         data.createItem(GenTodo().withId(43).withParent(42).withUid("43").withTitle(QStringLiteral("task2")));
0341         QCOMPARE(data.items().count(), 2);
0342 
0343         auto serializer = deps->create<Akonadi::SerializerInterface>();
0344 
0345         QVERIFY(!serializer->isProjectItem(data.items().at(1)));
0346 
0347         Presentation::AllTasksPageModel pageModel(deps->create<Domain::TaskQueries>(),
0348                                                   deps->create<Domain::TaskRepository>());
0349         QAbstractItemModel *model = pageModel.centralListModel();
0350         TestHelpers::waitForEmptyJobQueue();
0351         QCOMPARE(model->rowCount(), 2);
0352 
0353         // WHEN
0354         const QModelIndex index = model->index(1, 0);
0355         pageModel.promoteItem(index);
0356         TestHelpers::waitForEmptyJobQueue();
0357 
0358         // THEN
0359         QCOMPARE(model->rowCount(), 1);
0360         QCOMPARE(data.items().count(), 2);
0361         QVERIFY(serializer->isProjectItem(data.items().at(1)));
0362     }
0363 };
0364 
0365 ZANSHIN_TEST_MAIN(AllTasksPageModelTest)
0366 
0367 #include "alltaskspagemodeltest.moc"