File indexing completed on 2025-01-05 04:59: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_zanshin.h>
0008 
0009 #include <memory>
0010 
0011 #include <QMimeData>
0012 
0013 
0014 #include "domain/taskqueries.h"
0015 #include "domain/taskrepository.h"
0016 
0017 #include "presentation/inboxpagemodel.h"
0018 #include "presentation/errorhandler.h"
0019 #include "presentation/querytreemodelbase.h"
0020 
0021 #include "akonadi/akonadiserializerinterface.h"
0022 
0023 #include "utils/dependencymanager.h"
0024 #include "integration/dependencies.h"
0025 
0026 #include "testlib/fakejob.h"
0027 #include "testlib/akonadifakedata.h"
0028 #include "testlib/gencollection.h"
0029 #include "testlib/gentodo.h"
0030 #include "testlib/testhelpers.h"
0031 
0032 using namespace Testlib;
0033 
0034 class FakeErrorHandler : public Presentation::ErrorHandler
0035 {
0036 public:
0037     void doDisplayMessage(const QString &message) override
0038     {
0039         m_message = message;
0040     }
0041 
0042     QString m_message;
0043 };
0044 
0045 class InboxPageModelTest : public QObject
0046 {
0047     Q_OBJECT
0048 private slots:
0049     void cleanup()
0050     {
0051         // The first call to QueryTreeModelBase::data triggers fetchTaskExtraData which creates jobs.
0052         // Wait for these to finish so they don't mess up subsequent tests
0053         TestHelpers::waitForEmptyJobQueue();
0054     }
0055 
0056     void shouldListInboxInCentralListModel()
0057     {
0058         // GIVEN
0059 
0060         AkonadiFakeData data;
0061         auto deps = data.createDependencies();
0062         Integration::initializeDefaultDomainDependencies(*deps.get());
0063 
0064         // One top level collection
0065         data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent());
0066 
0067         // One root task
0068         data.createItem(GenTodo().withId(1).withParent(42).withUid("1").withTitle(QStringLiteral("rootTask")));
0069 
0070         // One task under the root task
0071         data.createItem(GenTodo().withId(2).withParent(42).withUid("2").withParentUid("1").done(true).withTitle(QStringLiteral("childTask")));
0072 
0073         auto serializer = deps->create<Akonadi::SerializerInterface>();
0074         Presentation::InboxPageModel inbox(deps->create<Domain::TaskQueries>(),
0075                                            deps->create<Domain::TaskRepository>());
0076 
0077         // WHEN
0078         QAbstractItemModel *model = inbox.centralListModel();
0079         TestHelpers::waitForEmptyJobQueue();
0080 
0081         // THEN
0082         const QModelIndex rootTaskIndex = model->index(0, 0);
0083         const QModelIndex childTaskIndex = model->index(0, 0, rootTaskIndex);
0084 
0085         QCOMPARE(model->rowCount(), 1);
0086         QCOMPARE(model->rowCount(rootTaskIndex), 1);
0087         QCOMPARE(model->rowCount(childTaskIndex), 0);
0088 
0089         auto rootTask = model->data(rootTaskIndex, Presentation::QueryTreeModelBase::ObjectRole).value<Domain::Task::Ptr>();
0090         auto childTask = model->data(childTaskIndex, Presentation::QueryTreeModelBase::ObjectRole).value<Domain::Task::Ptr>();
0091 
0092         const Qt::ItemFlags defaultFlags = Qt::ItemIsSelectable
0093                                          | Qt::ItemIsEnabled
0094                                          | Qt::ItemIsEditable
0095                                          | Qt::ItemIsDragEnabled;
0096         QCOMPARE(model->flags(rootTaskIndex), defaultFlags | Qt::ItemIsUserCheckable | Qt::ItemIsDropEnabled);
0097         QCOMPARE(model->flags(childTaskIndex), defaultFlags | Qt::ItemIsUserCheckable | Qt::ItemIsDropEnabled);
0098 
0099         QCOMPARE(model->data(rootTaskIndex).toString(), rootTask->title());
0100         QCOMPARE(model->data(childTaskIndex).toString(), childTask->title());
0101 
0102         QCOMPARE(model->data(rootTaskIndex, Qt::EditRole).toString(), rootTask->title());
0103         QCOMPARE(model->data(childTaskIndex, Qt::EditRole).toString(), childTask->title());
0104 
0105         QVERIFY(model->data(rootTaskIndex, Qt::CheckStateRole).isValid());
0106         QVERIFY(model->data(childTaskIndex, Qt::CheckStateRole).isValid());
0107 
0108         QCOMPARE(model->data(rootTaskIndex, Qt::CheckStateRole).toBool(), rootTask->isDone());
0109         QCOMPARE(model->data(childTaskIndex, Qt::CheckStateRole).toBool(), childTask->isDone());
0110 
0111         // WHEN
0112         QVERIFY(model->setData(rootTaskIndex, "newRootTask"));
0113         QVERIFY(model->setData(childTaskIndex, "newChildTask"));
0114 
0115         QVERIFY(model->setData(rootTaskIndex, Qt::Checked, Qt::CheckStateRole));
0116         QVERIFY(model->setData(childTaskIndex, Qt::Unchecked, Qt::CheckStateRole));
0117 
0118         // THEN
0119         QCOMPARE(rootTask->title(), QStringLiteral("newRootTask"));
0120         QCOMPARE(childTask->title(), QStringLiteral("newChildTask"));
0121 
0122         QCOMPARE(rootTask->isDone(), true);
0123         QCOMPARE(childTask->isDone(), false);
0124 
0125         // WHEN
0126         auto mimeData = std::unique_ptr<QMimeData>(model->mimeData(QModelIndexList() << childTaskIndex));
0127 
0128         // THEN
0129         QVERIFY(mimeData->hasFormat(QStringLiteral("application/x-zanshin-object")));
0130         QCOMPARE(mimeData->property("objects").value<Domain::Task::List>(),
0131                  Domain::Task::List() << childTask);
0132 
0133         // WHEN
0134         // - root (1)
0135         //   - child (2)
0136         //     - grandchild (48), will be dropped onto root
0137         data.createItem(GenTodo().withId(48).withParent(42).withUid("48").withParentUid("2").withTitle(QStringLiteral("childTask2")));
0138         QCOMPARE(model->rowCount(childTaskIndex), 1);
0139         auto grandChildTask = model->data(model->index(0, 0, childTaskIndex), Presentation::QueryTreeModelBase::ObjectRole).value<Domain::Task::Ptr>();
0140         QVERIFY(grandChildTask);
0141         mimeData.reset(new QMimeData);
0142         mimeData->setData(QStringLiteral("application/x-zanshin-object"), "object");
0143         mimeData->setProperty("objects", QVariant::fromValue(Domain::Task::List() << grandChildTask));
0144         QVERIFY(model->dropMimeData(mimeData.get(), Qt::MoveAction, -1, -1, rootTaskIndex));
0145         TestHelpers::waitForEmptyJobQueue();
0146 
0147         // THEN
0148         // root (1)
0149         // - child (2)
0150         // - second child (48)
0151         QCOMPARE(serializer->relatedUidFromItem(data.item(48)), QStringLiteral("1"));
0152         QCOMPARE(model->rowCount(), 1);
0153         QCOMPARE(model->rowCount(rootTaskIndex), 2);
0154         QCOMPARE(model->rowCount(childTaskIndex), 0);
0155 
0156         // WHEN
0157         // two more toplevel tasks
0158         data.createItem(GenTodo().withId(49).withParent(42).withUid("49").withTitle(QStringLiteral("childTask3")));
0159         auto task3 = model->data(model->index(1, 0), Presentation::QueryTreeModelBase::ObjectRole).value<Domain::Task::Ptr>();
0160         QVERIFY(task3);
0161         data.createItem(GenTodo().withId(50).withParent(42).withUid("50").withTitle(QStringLiteral("childTask4")));
0162         auto task4 = model->data(model->index(2, 0), Presentation::QueryTreeModelBase::ObjectRole).value<Domain::Task::Ptr>();
0163         QVERIFY(task4);
0164         mimeData.reset(new QMimeData);
0165         mimeData->setData(QStringLiteral("application/x-zanshin-object"), "object");
0166         mimeData->setProperty("objects", QVariant::fromValue(Domain::Task::List() << task3 << task4));
0167         QVERIFY(model->dropMimeData(mimeData.get(), Qt::MoveAction, -1, -1, rootTaskIndex));
0168         TestHelpers::waitForEmptyJobQueue();
0169 
0170         // THEN
0171         // root (1)
0172         // - child (2)
0173         // - second child (48)
0174         // - task3 (49)
0175         // - task4 (50)
0176 
0177         QCOMPARE(serializer->relatedUidFromItem(data.item(49)), QStringLiteral("1"));
0178         QCOMPARE(serializer->relatedUidFromItem(data.item(50)), QStringLiteral("1"));
0179         QCOMPARE(model->rowCount(), 1);
0180         QCOMPARE(model->rowCount(rootTaskIndex), 4);
0181     }
0182 
0183     void shouldAddTasksInInbox()
0184     {
0185         // GIVEN
0186         AkonadiFakeData data;
0187         auto deps = data.createDependencies();
0188         Integration::initializeDefaultDomainDependencies(*deps.get());
0189         data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent());
0190 
0191         Presentation::InboxPageModel pageModel(deps->create<Domain::TaskQueries>(),
0192                                                deps->create<Domain::TaskRepository>());
0193         QAbstractItemModel *model = pageModel.centralListModel();
0194 
0195         // WHEN
0196         auto title = QStringLiteral("New task");
0197         auto createdTask = pageModel.addItem(title);
0198         TestHelpers::waitForEmptyJobQueue();
0199 
0200         // THEN
0201         QVERIFY(createdTask);
0202         QCOMPARE(createdTask->title(), title);
0203         QCOMPARE(model->rowCount(), 1);
0204         auto taskInModel = model->data(model->index(0, 0), Presentation::QueryTreeModelBase::ObjectRole).value<Domain::Task::Ptr>();
0205         QVERIFY(taskInModel);
0206         QCOMPARE(taskInModel->title(), createdTask->title());
0207         QCOMPARE(taskInModel->startDate(), createdTask->startDate());
0208 
0209         QCOMPARE(data.items().count(), 1);
0210         TestHelpers::waitForEmptyJobQueue();
0211     }
0212 
0213     void shouldAddChildTask()
0214     {
0215         // GIVEN
0216         AkonadiFakeData data;
0217         auto deps = data.createDependencies();
0218         Integration::initializeDefaultDomainDependencies(*deps.get());
0219         data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent());
0220 
0221         // Two tasks
0222         data.createItem(GenTodo().withId(42).withParent(42).withUid("42").withTitle(QStringLiteral("task1")));
0223         data.createItem(GenTodo().withId(43).withParent(42).withUid("43").withTitle(QStringLiteral("task2")));
0224         QCOMPARE(data.items().count(), 2);
0225 
0226         Presentation::InboxPageModel pageModel(deps->create<Domain::TaskQueries>(),
0227                                                   deps->create<Domain::TaskRepository>());
0228         QAbstractItemModel *model = pageModel.centralListModel();
0229         TestHelpers::waitForEmptyJobQueue();
0230         QCOMPARE(model->rowCount(), 2);
0231 
0232         // WHEN
0233         const auto title = QStringLiteral("New task");
0234         const auto parentIndex = model->index(0, 0);
0235         const auto createdTask = pageModel.addItem(title, parentIndex);
0236         TestHelpers::waitForEmptyJobQueue();
0237 
0238         // THEN
0239         QVERIFY(createdTask);
0240         QCOMPARE(createdTask->title(), title);
0241 
0242         QCOMPARE(model->rowCount(parentIndex), 1);
0243         auto taskInModel = model->data(model->index(0, 0, parentIndex), Presentation::QueryTreeModelBase::ObjectRole).value<Domain::Task::Ptr>();
0244         QVERIFY(taskInModel);
0245         QCOMPARE(taskInModel->title(), createdTask->title());
0246         QCOMPARE(taskInModel->startDate(), createdTask->startDate());
0247 
0248         QCOMPARE(data.items().count(), 3);
0249         TestHelpers::waitForEmptyJobQueue();
0250     }
0251 
0252     void shouldGetAnErrorMessageWhenAddTaskFailed()
0253     {
0254         // GIVEN
0255         AkonadiFakeData data;
0256         auto deps = data.createDependencies();
0257         Integration::initializeDefaultDomainDependencies(*deps.get());
0258         data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent());
0259 
0260         Presentation::InboxPageModel pageModel(deps->create<Domain::TaskQueries>(),
0261                                                deps->create<Domain::TaskRepository>());
0262 
0263         FakeErrorHandler errorHandler;
0264         pageModel.setErrorHandler(&errorHandler);
0265 
0266         // WHEN
0267         data.storageBehavior().setCreateNextItemError(1, QStringLiteral("Foo"));
0268         pageModel.addItem(QStringLiteral("New task"));
0269         TestHelpers::waitForEmptyJobQueue();
0270 
0271         // THEN
0272         QCOMPARE(errorHandler.m_message, QStringLiteral("Cannot add task New task in Inbox: Foo"));
0273     }
0274 
0275     void shouldDeleteItems()
0276     {
0277         // GIVEN
0278         AkonadiFakeData data;
0279         auto deps = data.createDependencies();
0280         Integration::initializeDefaultDomainDependencies(*deps.get());
0281         data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent());
0282 
0283         // Two tasks
0284         data.createItem(GenTodo().withId(42).withParent(42).withUid("42").withTitle(QStringLiteral("task1")));
0285         data.createItem(GenTodo().withId(43).withParent(42).withUid("43").withTitle(QStringLiteral("task2")));
0286         QCOMPARE(data.items().count(), 2);
0287 
0288         Presentation::InboxPageModel pageModel(deps->create<Domain::TaskQueries>(),
0289                                                   deps->create<Domain::TaskRepository>());
0290         QAbstractItemModel *model = pageModel.centralListModel();
0291         TestHelpers::waitForEmptyJobQueue();
0292         QCOMPARE(model->rowCount(), 2);
0293 
0294         // WHEN
0295         const QModelIndex index = model->index(1, 0);
0296         QVERIFY(index.isValid());
0297         pageModel.removeItem(index);
0298         TestHelpers::waitForEmptyJobQueue();
0299 
0300         // THEN
0301         QCOMPARE(model->rowCount(), 1);
0302         QCOMPARE(data.items().count(), 1);
0303     }
0304 
0305     void shouldGetAnErrorMessageWhenDeleteItemsFailed()
0306     {
0307         // GIVEN
0308         AkonadiFakeData data;
0309         auto deps = data.createDependencies();
0310         Integration::initializeDefaultDomainDependencies(*deps.get());
0311         data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent());
0312 
0313         // Two tasks
0314         data.createItem(GenTodo().withId(42).withParent(42).withUid("42").withTitle(QStringLiteral("task1")));
0315         data.createItem(GenTodo().withId(43).withParent(42).withUid("43").withTitle(QStringLiteral("task2")));
0316         QCOMPARE(data.items().count(), 2);
0317 
0318         Presentation::InboxPageModel pageModel(deps->create<Domain::TaskQueries>(),
0319                                                   deps->create<Domain::TaskRepository>());
0320         QAbstractItemModel *model = pageModel.centralListModel();
0321         TestHelpers::waitForEmptyJobQueue();
0322         QCOMPARE(model->rowCount(), 2);
0323 
0324         FakeErrorHandler errorHandler;
0325         pageModel.setErrorHandler(&errorHandler);
0326 
0327         // WHEN
0328         data.storageBehavior().setDeleteNextItemError(1, QStringLiteral("Error deleting"));
0329         const QModelIndex index = model->index(1, 0);
0330         pageModel.removeItem(index);
0331         TestHelpers::waitForEmptyJobQueue();
0332 
0333         // THEN
0334         QCOMPARE(errorHandler.m_message, QStringLiteral("Cannot remove task task2 from Inbox: Error deleting"));
0335     }
0336 
0337     void shouldPromoteItem()
0338     {
0339         // GIVEN
0340 
0341         AkonadiFakeData data;
0342         auto deps = data.createDependencies();
0343         Integration::initializeDefaultDomainDependencies(*deps.get());
0344         data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent());
0345 
0346         // Two tasks
0347         data.createItem(GenTodo().withId(42).withParent(42).withUid("42").withTitle(QStringLiteral("task1")));
0348         data.createItem(GenTodo().withId(43).withParent(42).withUid("43").withTitle(QStringLiteral("task2")));
0349         QCOMPARE(data.items().count(), 2);
0350 
0351         auto serializer = deps->create<Akonadi::SerializerInterface>();
0352 
0353         QVERIFY(!serializer->isProjectItem(data.items().at(1)));
0354 
0355         Presentation::InboxPageModel pageModel(deps->create<Domain::TaskQueries>(),
0356                                                   deps->create<Domain::TaskRepository>());
0357         QAbstractItemModel *model = pageModel.centralListModel();
0358         TestHelpers::waitForEmptyJobQueue();
0359         QCOMPARE(model->rowCount(), 2);
0360 
0361         // WHEN
0362         const QModelIndex index = model->index(1, 0);
0363         pageModel.promoteItem(index);
0364         TestHelpers::waitForEmptyJobQueue();
0365 
0366         // THEN
0367         QCOMPARE(model->rowCount(), 1);
0368         QCOMPARE(data.items().count(), 2);
0369         QVERIFY(serializer->isProjectItem(data.items().at(1)));
0370     }
0371 
0372     void shouldGetAnErrorMessageWhenPromoteItemFailed()
0373     {
0374         // GIVEN
0375         AkonadiFakeData data;
0376         auto deps = data.createDependencies();
0377         Integration::initializeDefaultDomainDependencies(*deps.get());
0378         data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent());
0379 
0380         // Two tasks
0381         data.createItem(GenTodo().withId(42).withParent(42).withUid("42").withTitle(QStringLiteral("task1")));
0382         data.createItem(GenTodo().withId(43).withParent(42).withUid("43").withTitle(QStringLiteral("task2")));
0383         QCOMPARE(data.items().count(), 2);
0384 
0385         Presentation::InboxPageModel pageModel(deps->create<Domain::TaskQueries>(),
0386                                                deps->create<Domain::TaskRepository>());
0387         QAbstractItemModel *model = pageModel.centralListModel();
0388         TestHelpers::waitForEmptyJobQueue();
0389         QCOMPARE(model->rowCount(), 2);
0390 
0391         FakeErrorHandler errorHandler;
0392         pageModel.setErrorHandler(&errorHandler);
0393 
0394         // WHEN
0395         data.storageBehavior().setUpdateNextItemError(44, QStringLiteral("Foo"));
0396         const QModelIndex index = model->index(1, 0);
0397         pageModel.promoteItem(index);
0398         TestHelpers::waitForEmptyJobQueue();
0399 
0400         // THEN
0401         QCOMPARE(errorHandler.m_message, QStringLiteral("Cannot promote task task2 to be a project: Foo"));
0402     }
0403 
0404     void shouldGetAnErrorMessageWhenUpdateTaskFailed()
0405     {
0406         // GIVEN
0407         AkonadiFakeData data;
0408         auto deps = data.createDependencies();
0409         Integration::initializeDefaultDomainDependencies(*deps.get());
0410         data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent());
0411 
0412         // One task
0413         data.createItem(GenTodo().withId(42).withParent(42).withUid("42").withTitle(QStringLiteral("rootTask")));
0414 
0415         auto serializer = deps->create<Akonadi::SerializerInterface>();
0416         Presentation::InboxPageModel pageModel(deps->create<Domain::TaskQueries>(),
0417                                                deps->create<Domain::TaskRepository>());
0418         QAbstractItemModel *model = pageModel.centralListModel();
0419         TestHelpers::waitForEmptyJobQueue();
0420 
0421         FakeErrorHandler errorHandler;
0422         pageModel.setErrorHandler(&errorHandler);
0423 
0424         const QModelIndex index = model->index(0, 0);
0425         auto rootTask = model->data(index, Presentation::QueryTreeModelBase::ObjectRole).value<Domain::Task::Ptr>();
0426 
0427         // WHEN
0428         data.storageBehavior().setUpdateNextItemError(1, QStringLiteral("Update error"));
0429         QVERIFY(model->setData(index, "newRootTask"));
0430         TestHelpers::waitForEmptyJobQueue();
0431 
0432         // THEN
0433         QCOMPARE(errorHandler.m_message, QStringLiteral("Cannot modify task rootTask in Inbox: Update error"));
0434         QCOMPARE(rootTask->title(), QStringLiteral("newRootTask")); // Note that the task *did* get updated
0435         QCOMPARE(index.data().toString(), QStringLiteral("newRootTask")); // and therefore the model keeps showing the new value
0436         QCOMPARE(serializer->createTaskFromItem(data.item(42))->title(), QStringLiteral("rootTask")); // but the underlying data wasn't updated
0437     }
0438 
0439     void shouldGetAnErrorMessageWhenAssociateTaskFailed()
0440     {
0441         // GIVEN
0442         AkonadiFakeData data;
0443         auto deps = data.createDependencies();
0444         Integration::initializeDefaultDomainDependencies(*deps.get());
0445         data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent());
0446 
0447         // Three tasks
0448         data.createItem(GenTodo().withId(42).withParent(42).withUid("42").withTitle(QStringLiteral("task1")));
0449         data.createItem(GenTodo().withId(43).withParent(42).withUid("43").withTitle(QStringLiteral("task2")));
0450         data.createItem(GenTodo().withId(44).withParent(42).withUid("44").withTitle(QStringLiteral("task3")));
0451 
0452         Presentation::InboxPageModel pageModel(deps->create<Domain::TaskQueries>(),
0453                                                deps->create<Domain::TaskRepository>());
0454         QAbstractItemModel *model = pageModel.centralListModel();
0455         TestHelpers::waitForEmptyJobQueue();
0456         QCOMPARE(model->rowCount(), 3);
0457 
0458         FakeErrorHandler errorHandler;
0459         pageModel.setErrorHandler(&errorHandler);
0460         const QModelIndex rootTaskIndex = model->index(0, 0);
0461 
0462         auto task2 = model->data(model->index(1, 0), Presentation::QueryTreeModelBase::ObjectRole).value<Domain::Task::Ptr>();
0463         auto task3 = model->data(model->index(2, 0), Presentation::QueryTreeModelBase::ObjectRole).value<Domain::Task::Ptr>();
0464 
0465         // WHEN
0466         data.storageBehavior().setUpdateNextItemError(1, QStringLiteral("Update error"));
0467 
0468         auto mimeData = std::make_unique<QMimeData>();
0469         mimeData->setData(QStringLiteral("application/x-zanshin-object"), "object");
0470         mimeData->setProperty("objects", QVariant::fromValue(Domain::Task::List() << task2 << task3));
0471         QVERIFY(model->dropMimeData(mimeData.get(), Qt::MoveAction, -1, -1, rootTaskIndex));
0472         TestHelpers::waitForEmptyJobQueue();
0473 
0474         // THEN
0475         QCOMPARE(errorHandler.m_message, QStringLiteral("Cannot move task task2 as sub-task of task1: Update error"));
0476         QCOMPARE(model->rowCount(), 2); // One failed, one succeeded
0477     }
0478 
0479     void shouldDeparentWhenNoErrorHappens()
0480     {
0481         // GIVEN
0482         AkonadiFakeData data;
0483         auto deps = data.createDependencies();
0484         Integration::initializeDefaultDomainDependencies(*deps.get());
0485         data.createCollection(GenCollection().withId(42).withRootAsParent().withTaskContent());
0486 
0487         // One task, top level
0488         data.createItem(GenTodo().withId(42).withParent(42).withUid("42").withTitle(QStringLiteral("topLevelTask")));
0489 
0490         // Two tasks under the top level task
0491         data.createItem(GenTodo().withId(43).withParent(42).withUid("43").withParentUid("42").withTitle(QStringLiteral("childTask")).done());
0492         data.createItem(GenTodo().withId(44).withParent(42).withUid("44").withParentUid("42").withTitle(QStringLiteral("childTask2")));
0493 
0494         Presentation::InboxPageModel pageModel(deps->create<Domain::TaskQueries>(),
0495                                                deps->create<Domain::TaskRepository>());
0496         QAbstractItemModel *model = pageModel.centralListModel();
0497         TestHelpers::waitForEmptyJobQueue();
0498 
0499         const QModelIndex emptyPartModel = QModelIndex(); // model root, drop on the empty part is equivalent
0500         FakeErrorHandler errorHandler;
0501         pageModel.setErrorHandler(&errorHandler);
0502 
0503         const auto topLevelIndex = model->index(0, 0);
0504         QVERIFY(topLevelIndex.isValid());
0505         const auto childTask = model->data(model->index(0, 0, topLevelIndex), Presentation::QueryTreeModelBase::ObjectRole).value<Domain::Task::Ptr>();
0506         const auto childTask2 = model->data(model->index(1, 0, topLevelIndex), Presentation::QueryTreeModelBase::ObjectRole).value<Domain::Task::Ptr>();
0507 
0508         // WHEN
0509         data.storageBehavior().setUpdateNextItemError(1, QStringLiteral("Deparent error"));
0510 
0511         auto mimeData = std::make_unique<QMimeData>();
0512         mimeData->setData(QStringLiteral("application/x-zanshin-object"), "object");
0513         mimeData->setProperty("objects", QVariant::fromValue(Domain::Task::List() << childTask << childTask2)); // both will be DnD on the empty part
0514         QVERIFY(model->dropMimeData(mimeData.get(), Qt::MoveAction, -1, -1, emptyPartModel));
0515         TestHelpers::waitForEmptyJobQueue();
0516 
0517         // THEN
0518         QCOMPARE(errorHandler.m_message, QStringLiteral("Cannot deparent task childTask from its parent: Deparent error"));
0519         QCOMPARE(model->rowCount(), 2); // One failed, one succeeded
0520     }
0521 };
0522 
0523 ZANSHIN_TEST_MAIN(InboxPageModelTest)
0524 
0525 #include "inboxpagemodeltest.moc"