diff --git a/src/presentation/availabletaskpagesmodel.cpp b/src/presentation/availabletaskpagesmodel.cpp index 4fd6590d..5da3bff7 100644 --- a/src/presentation/availabletaskpagesmodel.cpp +++ b/src/presentation/availabletaskpagesmodel.cpp @@ -1,343 +1,350 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "availabletaskpagesmodel.h" #include #include #include #include "domain/contextqueries.h" #include "domain/contextrepository.h" #include "domain/projectqueries.h" #include "domain/projectrepository.h" #include "domain/taskrepository.h" #include "presentation/availablepagessortfilterproxymodel.h" #include "presentation/contextpagemodel.h" #include "presentation/metatypes.h" #include "presentation/projectpagemodel.h" #include "presentation/querytreemodel.h" #include "presentation/taskinboxpagemodel.h" #include "presentation/workdaypagemodel.h" #include "utils/jobhandler.h" #include "utils/datetime.h" using namespace Presentation; -AvailableTaskPagesModel::AvailableTaskPagesModel(const Domain::ProjectQueries::Ptr &projectQueries, +AvailableTaskPagesModel::AvailableTaskPagesModel(const Domain::DataSourceQueries::Ptr &dataSourceQueries, + const Domain::ProjectQueries::Ptr &projectQueries, const Domain::ProjectRepository::Ptr &projectRepository, const Domain::ContextQueries::Ptr &contextQueries, const Domain::ContextRepository::Ptr &contextRepository, const Domain::TaskQueries::Ptr &taskQueries, const Domain::TaskRepository::Ptr &taskRepository, QObject *parent) : AvailablePagesModelInterface(parent), m_pageListModel(Q_NULLPTR), m_sortProxyModel(Q_NULLPTR), + m_dataSourceQueries(dataSourceQueries), m_projectQueries(projectQueries), m_projectRepository(projectRepository), m_contextQueries(contextQueries), m_contextRepository(contextRepository), m_taskQueries(taskQueries), m_taskRepository(taskRepository) { } QAbstractItemModel *AvailableTaskPagesModel::pageListModel() { if (!m_pageListModel) m_pageListModel = createPageListModel(); if (!m_sortProxyModel) { m_sortProxyModel = new AvailablePagesSortFilterProxyModel(this); m_sortProxyModel->setSourceModel(m_pageListModel); } return m_sortProxyModel; } bool AvailableTaskPagesModel::hasProjectPages() const { return true; } bool AvailableTaskPagesModel::hasContextPages() const { return true; } bool AvailableTaskPagesModel::hasTagPages() const { return false; } QObject *AvailableTaskPagesModel::createPageForIndex(const QModelIndex &index) { QObjectPtr object = index.data(QueryTreeModelBase::ObjectRole).value(); if (object == m_inboxObject) { auto inboxPageModel = new TaskInboxPageModel(m_taskQueries, m_taskRepository, this); inboxPageModel->setErrorHandler(errorHandler()); return inboxPageModel; } else if (object == m_workdayObject) { auto workdayPageModel = new WorkdayPageModel(m_taskQueries, m_taskRepository, this); workdayPageModel->setErrorHandler(errorHandler()); return workdayPageModel; } else if (auto project = object.objectCast()) { auto projectPageModel = new ProjectPageModel(project, m_projectQueries, m_projectRepository, m_taskQueries, m_taskRepository, this); projectPageModel->setErrorHandler(errorHandler()); return projectPageModel; } else if (auto context = object.objectCast()) { auto contextPageModel = new ContextPageModel(context, m_contextQueries, m_contextRepository, m_taskQueries, m_taskRepository, this); contextPageModel->setErrorHandler(errorHandler()); return contextPageModel; } return Q_NULLPTR; } void AvailableTaskPagesModel::addProject(const QString &name, const Domain::DataSource::Ptr &source) { auto project = Domain::Project::Ptr::create(); project->setName(name); const auto job = m_projectRepository->create(project, source); installHandler(job, i18n("Cannot add project %1 in dataSource %2", name, source->name())); } void AvailableTaskPagesModel::addContext(const QString &name) { auto context = Domain::Context::Ptr::create(); context->setName(name); const auto job = m_contextRepository->create(context); installHandler(job, i18n("Cannot add context %1", name)); } void AvailableTaskPagesModel::addTag(const QString &) { qFatal("Not supported"); } void AvailableTaskPagesModel::removeItem(const QModelIndex &index) { QObjectPtr object = index.data(QueryTreeModelBase::ObjectRole).value(); if (auto project = object.objectCast()) { const auto job = m_projectRepository->remove(project); installHandler(job, i18n("Cannot remove project %1", project->name())); } else if (auto context = object.objectCast()) { const auto job = m_contextRepository->remove(context); installHandler(job, i18n("Cannot remove context %1", context->name())); } else { Q_ASSERT(false); } } QAbstractItemModel *AvailableTaskPagesModel::createPageListModel() { m_inboxObject = QObjectPtr::create(); m_inboxObject->setProperty("name", i18n("Inbox")); m_workdayObject = QObjectPtr::create(); m_workdayObject->setProperty("name", i18n("Workday")); m_projectsObject = QObjectPtr::create(); m_projectsObject->setProperty("name", i18n("Projects")); m_contextsObject = QObjectPtr::create(); m_contextsObject->setProperty("name", i18n("Contexts")); m_rootsProvider = Domain::QueryResultProvider::Ptr::create(); m_rootsProvider->append(m_inboxObject); m_rootsProvider->append(m_workdayObject); m_rootsProvider->append(m_projectsObject); m_rootsProvider->append(m_contextsObject); auto query = [this](const QObjectPtr &object) -> Domain::QueryResultInterface::Ptr { if (!object) return Domain::QueryResult::create(m_rootsProvider); else if (object == m_projectsObject) - return Domain::QueryResult::copy(m_projectQueries->findAll()); + return Domain::QueryResult::copy(m_dataSourceQueries->findAllSelected()); else if (object == m_contextsObject) return Domain::QueryResult::copy(m_contextQueries->findAll()); + else if (const auto source = object.objectCast()) + return Domain::QueryResult::copy(m_dataSourceQueries->findProjects(source)); else return Domain::QueryResult::Ptr(); }; auto flags = [this](const QObjectPtr &object) { const Qt::ItemFlags defaultFlags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsDropEnabled; const Qt::ItemFlags immutableNodeFlags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDropEnabled; const Qt::ItemFlags structureNodeFlags = Qt::NoItemFlags; return object.objectCast() ? defaultFlags : object.objectCast() ? defaultFlags : object == m_inboxObject ? immutableNodeFlags : object == m_workdayObject ? immutableNodeFlags : structureNodeFlags; }; auto data = [this](const QObjectPtr &object, int role) -> QVariant { if (role != Qt::DisplayRole && role != Qt::EditRole && role != Qt::DecorationRole && role != QueryTreeModelBase::IconNameRole) { return QVariant(); } if (role == Qt::EditRole && (object == m_inboxObject || object == m_workdayObject || object == m_projectsObject - || object == m_contextsObject)) { + || object == m_contextsObject + || object.objectCast())) { return QVariant(); } if (role == Qt::DisplayRole || role == Qt::EditRole) { return object->property("name").toString(); } else if (role == Qt::DecorationRole || role == QueryTreeModelBase::IconNameRole) { const QString iconName = object == m_inboxObject ? QStringLiteral("mail-folder-inbox") : (object == m_workdayObject) ? QStringLiteral("go-jump-today") : (object == m_projectsObject) ? QStringLiteral("folder") : (object == m_contextsObject) ? QStringLiteral("folder") + : object.objectCast() ? QStringLiteral("folder") : object.objectCast() ? QStringLiteral("view-pim-notes") : QStringLiteral("view-pim-tasks"); if (role == Qt::DecorationRole) return QVariant::fromValue(QIcon::fromTheme(iconName)); else return iconName; } else { return QVariant(); } }; auto setData = [this](const QObjectPtr &object, const QVariant &value, int role) { if (role != Qt::EditRole) { return false; } if (object == m_inboxObject || object == m_workdayObject || object == m_projectsObject - || object == m_contextsObject) { + || object == m_contextsObject + || object.objectCast()) { return false; } if (auto project = object.objectCast()) { const auto currentName = project->name(); project->setName(value.toString()); const auto job = m_projectRepository->update(project); installHandler(job, i18n("Cannot modify project %1", currentName)); } else if (auto context = object.objectCast()) { const auto currentName = context->name(); context->setName(value.toString()); const auto job = m_contextRepository->update(context); installHandler(job, i18n("Cannot modify context %1", currentName)); } else { Q_ASSERT(false); } return true; }; auto drop = [this](const QMimeData *mimeData, Qt::DropAction, const QObjectPtr &object) { if (!mimeData->hasFormat(QStringLiteral("application/x-zanshin-object"))) return false; auto droppedArtifacts = mimeData->property("objects").value(); if (droppedArtifacts.isEmpty()) return false; if (auto project = object.objectCast()) { foreach (const auto &droppedArtifact, droppedArtifacts) { const auto job = m_projectRepository->associate(project, droppedArtifact); installHandler(job, i18n("Cannot add %1 to project %2", droppedArtifact->title(), project->name())); } return true; } else if (auto context = object.objectCast()) { if (std::any_of(droppedArtifacts.begin(), droppedArtifacts.end(), [](const Domain::Artifact::Ptr &droppedArtifact) { return !droppedArtifact.objectCast(); })) { return false; } foreach (const auto &droppedArtifact, droppedArtifacts) { auto task = droppedArtifact.staticCast(); const auto job = m_contextRepository->associate(context, task); installHandler(job, i18n("Cannot add %1 to context %2", task->title(), context->name())); } return true; } else if (object == m_inboxObject) { foreach (const auto &droppedArtifact, droppedArtifacts) { const auto job = m_projectRepository->dissociate(droppedArtifact); installHandler(job, i18n("Cannot move %1 to Inbox", droppedArtifact->title())); if (auto task = droppedArtifact.objectCast()) { Utils::JobHandler::install(job, [this, task] { const auto dissociateJob = m_taskRepository->dissociateAll(task); installHandler(dissociateJob, i18n("Cannot move task %1 to Inbox", task->title())); }); } } return true; } else if (object == m_workdayObject) { foreach (const auto &droppedArtifact, droppedArtifacts) { if (auto task = droppedArtifact.objectCast()) { task->setStartDate(Utils::DateTime::currentDateTime()); const auto job = m_taskRepository->update(task); installHandler(job, i18n("Cannot update task %1 to Workday", task->title())); } } return true; } return false; }; auto drag = [](const QObjectPtrList &) -> QMimeData* { return Q_NULLPTR; }; return new QueryTreeModel(query, flags, data, setData, drop, drag, this); } diff --git a/src/presentation/availabletaskpagesmodel.h b/src/presentation/availabletaskpagesmodel.h index dc1ed6e0..0e746234 100644 --- a/src/presentation/availabletaskpagesmodel.h +++ b/src/presentation/availabletaskpagesmodel.h @@ -1,93 +1,97 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef PRESENTATION_AVAILABLETASKPAGESMODEL_H #define PRESENTATION_AVAILABLETASKPAGESMODEL_H #include "presentation/availablepagesmodelinterface.h" #include "domain/contextqueries.h" #include "domain/contextrepository.h" +#include "domain/datasourcequeries.h" #include "domain/projectqueries.h" #include "domain/projectrepository.h" #include "domain/taskqueries.h" #include "domain/taskrepository.h" #include "presentation/metatypes.h" class QModelIndex; namespace Presentation { class AvailablePagesSortFilterProxyModel; class AvailableTaskPagesModel : public AvailablePagesModelInterface { Q_OBJECT public: - explicit AvailableTaskPagesModel(const Domain::ProjectQueries::Ptr &projectQueries, + explicit AvailableTaskPagesModel(const Domain::DataSourceQueries::Ptr &dataSourceQueries, + const Domain::ProjectQueries::Ptr &projectQueries, const Domain::ProjectRepository::Ptr &projectRepository, const Domain::ContextQueries::Ptr &contextQueries, const Domain::ContextRepository::Ptr &contextRepository, const Domain::TaskQueries::Ptr &taskQueries, const Domain::TaskRepository::Ptr &taskRepository, QObject *parent = Q_NULLPTR); QAbstractItemModel *pageListModel() Q_DECL_OVERRIDE; bool hasProjectPages() const Q_DECL_OVERRIDE; bool hasContextPages() const Q_DECL_OVERRIDE; bool hasTagPages() const Q_DECL_OVERRIDE; QObject *createPageForIndex(const QModelIndex &index) Q_DECL_OVERRIDE; void addProject(const QString &name, const Domain::DataSource::Ptr &source) Q_DECL_OVERRIDE; void addContext(const QString &name) Q_DECL_OVERRIDE; void addTag(const QString &name) Q_DECL_OVERRIDE; void removeItem(const QModelIndex &index) Q_DECL_OVERRIDE; private: QAbstractItemModel *createPageListModel(); QAbstractItemModel *m_pageListModel; Presentation::AvailablePagesSortFilterProxyModel *m_sortProxyModel; + Domain::DataSourceQueries::Ptr m_dataSourceQueries; + Domain::ProjectQueries::Ptr m_projectQueries; Domain::ProjectRepository::Ptr m_projectRepository; Domain::ContextQueries::Ptr m_contextQueries; Domain::ContextRepository::Ptr m_contextRepository; Domain::TaskQueries::Ptr m_taskQueries; Domain::TaskRepository::Ptr m_taskRepository; Domain::QueryResultProvider::Ptr m_rootsProvider; QObjectPtr m_inboxObject; QObjectPtr m_workdayObject; QObjectPtr m_projectsObject; QObjectPtr m_contextsObject; }; } #endif // PRESENTATION_AVAILABLETASKPAGESMODEL_H diff --git a/src/zanshin/app/dependencies.cpp b/src/zanshin/app/dependencies.cpp index f88e19f0..8e26793d 100644 --- a/src/zanshin/app/dependencies.cpp +++ b/src/zanshin/app/dependencies.cpp @@ -1,134 +1,135 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "dependencies.h" #include "akonadi/akonadicontextqueries.h" #include "akonadi/akonadicontextrepository.h" #include "akonadi/akonadidatasourcequeries.h" #include "akonadi/akonadidatasourcerepository.h" #include "akonadi/akonadiprojectqueries.h" #include "akonadi/akonadiprojectrepository.h" #include "akonadi/akonaditaskqueries.h" #include "akonadi/akonaditaskrepository.h" #include "akonadi/akonadicache.h" #include "akonadi/akonadicachingstorage.h" #include "akonadi/akonadimessaging.h" #include "akonadi/akonadimonitorimpl.h" #include "akonadi/akonadiserializer.h" #include "akonadi/akonadistorage.h" #include "presentation/artifacteditormodel.h" #include "presentation/availablesourcesmodel.h" #include "presentation/availabletaskpagesmodel.h" #include "presentation/runningtaskmodel.h" #include "utils/dependencymanager.h" void App::initializeDependencies() { auto &deps = Utils::DependencyManager::globalInstance(); deps.add(); deps.add(); deps.add(); deps.add(); deps.add([] (Utils::DependencyManager *deps) { return new Akonadi::CachingStorage(deps->create(), Akonadi::StorageInterface::Ptr(new Akonadi::Storage)); }); deps.add(); deps.add(); deps.add([] (Utils::DependencyManager *deps) { return new Akonadi::DataSourceQueries(Akonadi::StorageInterface::Tasks, deps->create(), deps->create(), deps->create()); }); deps.add(); deps.add(); deps.add(); deps.add(); deps.add(); deps.add([] (Utils::DependencyManager *deps) { auto model = new Presentation::ArtifactEditorModel; auto repository = deps->create(); model->setSaveFunction([repository] (const Domain::Artifact::Ptr &artifact) { auto task = artifact.objectCast(); Q_ASSERT(task); return repository->update(task); }); model->setDelegateFunction([repository] (const Domain::Task::Ptr &task, const Domain::Task::Delegate &delegate) { return repository->delegate(task, delegate); }); return model; }); deps.add(); deps.add(); deps.add(); } diff --git a/tests/features/cuke-steps.cpp b/tests/features/cuke-steps.cpp index 83fd52ab..c9dfaf9e 100644 --- a/tests/features/cuke-steps.cpp +++ b/tests/features/cuke-steps.cpp @@ -1,903 +1,905 @@ /* This file is part of Zanshin Copyright 2014-2015 Kevin Ottens This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include #include #include +#include + #include "presentation/applicationmodel.h" #include "presentation/errorhandler.h" #include "presentation/querytreemodelbase.h" +#include "akonadi/akonadiapplicationselectedattribute.h" #include "akonadi/akonadicache.h" #include "akonadi/akonadicachingstorage.h" #include "akonadi/akonadimonitorimpl.h" #include "akonadi/akonadimessaginginterface.h" +#include "akonadi/akonaditimestampattribute.h" #include "utils/dependencymanager.h" #include "utils/jobhandler.h" #include "testlib/akonadifakedata.h" #include "testlib/akonadifakedataxmlloader.h" #include "testlib/monitorspy.h" #include "testlib/testsafety.h" static int argc = 1; static char *argv0 = "cuke-steps"; static QApplication app(argc, &argv0); namespace CukeSteps { void initializeAppDependencies(); } namespace cucumber { namespace internal { template<> inline QString fromString(const std::string& s) { return QString::fromUtf8(s.data()); } } } using namespace cucumber; class FakeErrorHandler : public Presentation::ErrorHandler { public: void doDisplayMessage(const QString &) { } }; class ZanshinContext : public QObject { Q_OBJECT public: explicit ZanshinContext(QObject *parent = Q_NULLPTR) : QObject(parent), app(), presentation(Q_NULLPTR), editor(Q_NULLPTR), proxyModel(new QSortFilterProxyModel(this)), m_model(Q_NULLPTR), m_sourceModel(Q_NULLPTR), monitorSpy(Q_NULLPTR) { qputenv("ZANSHIN_OVERRIDE_DATETIME", "2015-03-10"); static bool initializedDependencies = false; if (!initializedDependencies) { CukeSteps::initializeAppDependencies(); MonitorSpy::setExpirationDelay(200); initializedDependencies = true; } + Akonadi::AttributeFactory::registerAttribute(); + Akonadi::AttributeFactory::registerAttribute(); + const auto xmlFile = QString::fromLocal8Bit(qgetenv("ZANSHIN_USER_XMLDATA")); if (xmlFile.isEmpty()) { qDebug() << "FATAL ERROR! ZANSHIN_USER_XMLDATA WAS NOT PROVIDED\n\n"; exit(1); } auto searchCollection = Akonadi::Collection(1); searchCollection.setParentCollection(Akonadi::Collection::root()); searchCollection.setName(QStringLiteral("Search")); m_data.createCollection(searchCollection); auto loader = Testlib::AkonadiFakeDataXmlLoader(&m_data); loader.load(xmlFile); // Swap regular dependencies for the fake data ones auto &deps = Utils::DependencyManager::globalInstance(); deps.add( [this] (Utils::DependencyManager *) { return m_data.createMonitor(); } ); deps.add( [this] (Utils::DependencyManager *deps) { return new Akonadi::CachingStorage(deps->create(), Akonadi::StorageInterface::Ptr(m_data.createStorage())); } ); deps.add( [this] (Utils::DependencyManager *) -> Akonadi::MessagingInterface* { return Q_NULLPTR; } ); using namespace Presentation; proxyModel->setDynamicSortFilter(true); auto appModel = ApplicationModel::Ptr::create(); appModel->setErrorHandler(&m_errorHandler); app = appModel; auto monitor = Utils::DependencyManager::globalInstance().create(); monitorSpy = new MonitorSpy(monitor.data(), this); } ~ZanshinContext() { } // Note that setModel might invalidate the 'index' member variable, due to proxyModel->setSourceModel. void setModel(QAbstractItemModel *model) { if (m_sourceModel == model) return; m_sourceModel = model; if (!qobject_cast(model)) { proxyModel->setObjectName(QStringLiteral("m_proxyModel_in_ZanshinContext")); proxyModel->setSourceModel(model); proxyModel->setSortRole(Qt::DisplayRole); proxyModel->sort(0); m_model = proxyModel; } else { m_model = model; } } QAbstractItemModel *sourceModel() { return m_sourceModel; } QAbstractItemModel *model() { return m_model; } Domain::Artifact::Ptr currentArtifact() const { return index.data(Presentation::QueryTreeModelBase::ObjectRole) .value(); } void waitForEmptyJobQueue() { while (Utils::JobHandler::jobCount() != 0) { QTest::qWait(20); } } void waitForStableState() { waitForEmptyJobQueue(); monitorSpy->waitForStableState(); } QObjectPtr app; QList indices; QPersistentModelIndex index; QObject *presentation; QObject *editor; QList dragIndices; private: Testlib::AkonadiFakeData m_data; QSortFilterProxyModel *proxyModel; QAbstractItemModel *m_model; QAbstractItemModel *m_sourceModel; MonitorSpy *monitorSpy; FakeErrorHandler m_errorHandler; }; namespace Zanshin { QString indexString(const QModelIndex &index, int role = Qt::DisplayRole) { if (role != Qt::DisplayRole) return index.data(role).toString(); QString data = index.data(role).toString(); if (index.parent().isValid()) return indexString(index.parent(), role) + " / " + data; else return data; } QModelIndex findIndex(QAbstractItemModel *model, const QString &string, int role = Qt::DisplayRole, const QModelIndex &root = QModelIndex()) { for (int row = 0; row < model->rowCount(root); row++) { const QModelIndex index = model->index(row, 0, root); if (indexString(index, role) == string) return index; if (model->rowCount(index) > 0) { const QModelIndex found = findIndex(model, string, role, index); if (found.isValid()) return found; } } return QModelIndex(); } static void collectIndicesImpl(ZanshinContext *context, const QModelIndex &root = QModelIndex()) { QAbstractItemModel *model = context->model(); for (int row = 0; row < model->rowCount(root); row++) { const QModelIndex index = model->index(row, 0, root); context->indices << index; if (model->rowCount(index) > 0) collectIndicesImpl(context, index); } } static void collectIndices(ZanshinContext *context) { context->indices.clear(); collectIndicesImpl(context); } void dumpIndices(const QList &indices) { qDebug() << "Dumping list of size:" << indices.size(); for (int row = 0; row < indices.size(); row++) { qDebug() << row << indexString(indices.at(row)); } } inline bool verify(bool statement, const char *str, const char *file, int line) { if (statement) return true; qDebug() << "Statement" << str << "returned FALSE"; qDebug() << "Loc:" << file << line; return false; } template inline bool compare(T const &t1, T const &t2, const char *actual, const char *expected, const char *file, int line) { if (t1 == t2) return true; qDebug() << "Compared values are not the same"; qDebug() << "Actual (" << actual << ") :" << QTest::toString(t1); qDebug() << "Expected (" << expected << ") :" << QTest::toString(t2); qDebug() << "Loc:" << file << line; return false; } } // namespace Zanshin #define COMPARE(actual, expected) \ do {\ if (!Zanshin::compare(actual, expected, #actual, #expected, __FILE__, __LINE__))\ BOOST_REQUIRE(false);\ } while (0) // Note: you should make sure that context->indices is filled in before calling this, // e.g. calling Zanshin::collectIndices(context.get()) if not already done. #define COMPARE_OR_DUMP(actual, expected) \ do {\ if (!Zanshin::compare(actual, expected, #actual, #expected, __FILE__, __LINE__)) {\ Zanshin::dumpIndices(context->indices); \ BOOST_REQUIRE(false);\ }\ } while (0) #define VERIFY(statement) \ do {\ if (!Zanshin::verify((statement), #statement, __FILE__, __LINE__))\ BOOST_REQUIRE(false);\ } while (0) // Note: you should make sure that context->indices is filled in before calling this, // e.g. calling Zanshin::collectIndices(context.get()) if not already done. #define VERIFY_OR_DUMP(statement) \ do {\ if (!Zanshin::verify((statement), #statement, __FILE__, __LINE__)) {\ Zanshin::dumpIndices(context->indices); \ BOOST_REQUIRE(false);\ }\ } while (0) #define VERIFY_OR_DO(statement, whatToDo) \ do {\ if (!Zanshin::verify((statement), #statement, __FILE__, __LINE__)) {\ whatToDo; \ BOOST_REQUIRE(false);\ }\ } while (0) GIVEN("^I display the available data sources$") { ScenarioScope context; auto availableSources = context->app->property("availableSources").value(); VERIFY(availableSources); auto sourceListModel = availableSources->property("sourceListModel").value(); VERIFY(sourceListModel); context->presentation = availableSources; context->setModel(sourceListModel); } GIVEN("^I display the available pages$") { ScenarioScope context; context->presentation = context->app->property("availablePages").value(); context->setModel(context->presentation->property("pageListModel").value()); } GIVEN("^I display the \"(.*)\" page$") { REGEX_PARAM(QString, pageName); ScenarioScope context; auto availablePages = context->app->property("availablePages").value(); VERIFY(availablePages); auto pageListModel = availablePages->property("pageListModel").value(); VERIFY(pageListModel); context->waitForEmptyJobQueue(); QModelIndex pageIndex = Zanshin::findIndex(pageListModel, pageName); VERIFY(pageIndex.isValid()); QObject *page = Q_NULLPTR; QMetaObject::invokeMethod(availablePages, "createPageForIndex", Q_RETURN_ARG(QObject*, page), Q_ARG(QModelIndex, pageIndex)); VERIFY(page); VERIFY(context->app->setProperty("currentPage", QVariant::fromValue(page))); context->presentation = context->app->property("currentPage").value(); } GIVEN("^there is an item named \"(.+)\" in the central list$") { REGEX_PARAM(QString, itemName); ScenarioScope context; auto model = context->presentation->property("centralListModel").value(); context->setModel(model); context->waitForEmptyJobQueue(); Zanshin::collectIndices(context.get()); context->index = Zanshin::findIndex(context->model(), itemName); VERIFY_OR_DUMP(context->index.isValid()); } GIVEN("^there is an item named \"(.+)\" in the available data sources$") { REGEX_PARAM(QString, itemName); ScenarioScope context; auto availableSources = context->app->property("availableSources").value(); VERIFY(availableSources); auto model = availableSources->property("sourceListModel").value(); VERIFY(model); context->waitForEmptyJobQueue(); context->setModel(model); Zanshin::collectIndices(context.get()); context->index = Zanshin::findIndex(context->model(), itemName); VERIFY_OR_DUMP(context->index.isValid()); } GIVEN("^the central list contains items named:") { TABLE_PARAM(tableParam); ScenarioScope context; context->dragIndices.clear(); auto model = context->presentation->property("centralListModel").value(); context->waitForEmptyJobQueue(); context->setModel(model); for (const auto &row : tableParam.hashes()) { for (const auto &it : row) { const QString itemName = QString::fromUtf8(it.second.data()); QModelIndex index = Zanshin::findIndex(context->model(), itemName); VERIFY_OR_DO(index.isValid(), Zanshin::dumpIndices(context->dragIndices)); context->dragIndices << index; } } } WHEN("^I look at the central list$") { ScenarioScope context; auto model = context->presentation->property("centralListModel").value(); context->setModel(model); context->waitForStableState(); } WHEN("^I check the item$") { ScenarioScope context; VERIFY(context->model()->setData(context->index, Qt::Checked, Qt::CheckStateRole)); context->waitForStableState(); } WHEN("^I uncheck the item$") { ScenarioScope context; VERIFY(context->model()->setData(context->index, Qt::Unchecked, Qt::CheckStateRole)); context->waitForStableState(); } WHEN("^I remove the item$") { ScenarioScope context; VERIFY(QMetaObject::invokeMethod(context->presentation, "removeItem", Q_ARG(QModelIndex, context->index))); context->waitForStableState(); } WHEN("^I promote the item$") { ScenarioScope context; VERIFY(QMetaObject::invokeMethod(context->presentation, "promoteItem", Q_ARG(QModelIndex, context->index))); context->waitForStableState(); } WHEN("^I add a project named \"(.*)\" in the source named \"(.*)\"$") { REGEX_PARAM(QString, projectName); REGEX_PARAM(QString, sourceName); ScenarioScope context; auto availableSources = context->app->property("availableSources").value(); VERIFY(availableSources); auto sourceList = availableSources->property("sourceListModel").value(); VERIFY(sourceList); context->waitForStableState(); QModelIndex index = Zanshin::findIndex(sourceList, sourceName); VERIFY(index.isValid()); auto source = index.data(Presentation::QueryTreeModelBase::ObjectRole) .value(); VERIFY(source); VERIFY(QMetaObject::invokeMethod(context->presentation, "addProject", Q_ARG(QString, projectName), Q_ARG(Domain::DataSource::Ptr, source))); context->waitForStableState(); } -WHEN("^I rename a \"(.*)\" named \"(.*)\" to \"(.*)\"$") { - REGEX_PARAM(QString, objectType); +WHEN("^I rename the page named \"(.*)\" under \"(.*)\" to \"(.*)\"$") { REGEX_PARAM(QString, oldName); + REGEX_PARAM(QString, path); REGEX_PARAM(QString, newName); - const QString pageNodeName = (objectType == QStringLiteral("project")) ? QStringLiteral("Projects / ") - : (objectType == QStringLiteral("context")) ? QStringLiteral("Contexts / ") - : QString(); + const QString pageNodeName = path + " / "; VERIFY(!pageNodeName.isEmpty()); ScenarioScope context; auto availablePages = context->app->property("availablePages").value(); VERIFY(availablePages); auto pageListModel = availablePages->property("pageListModel").value(); VERIFY(pageListModel); context->waitForStableState(); QModelIndex pageIndex = Zanshin::findIndex(pageListModel, pageNodeName + oldName); VERIFY(pageIndex.isValid()); pageListModel->setData(pageIndex, newName); context->waitForStableState(); } -WHEN("^I remove a \"(.*)\" named \"(.*)\"$") { - REGEX_PARAM(QString, objectType); - REGEX_PARAM(QString, objectName); +WHEN("^I remove the page named \"(.*)\" under \"(.*)\"$") { + REGEX_PARAM(QString, name); + REGEX_PARAM(QString, path); - const QString pageNodeName = (objectType == QStringLiteral("project")) ? QStringLiteral("Projects / ") - : (objectType == QStringLiteral("context")) ? QStringLiteral("Contexts / ") - : (objectType == QStringLiteral("tag")) ? QStringLiteral("Tags / ") - : QString(); + const QString pageNodeName = path + " / "; VERIFY(!pageNodeName.isEmpty()); ScenarioScope context; auto availablePages = context->app->property("availablePages").value(); VERIFY(availablePages); auto pageListModel = availablePages->property("pageListModel").value(); VERIFY(pageListModel); context->waitForStableState(); - QModelIndex pageIndex = Zanshin::findIndex(pageListModel, pageNodeName + objectName); + QModelIndex pageIndex = Zanshin::findIndex(pageListModel, pageNodeName + name); VERIFY(pageIndex.isValid()); VERIFY(QMetaObject::invokeMethod(availablePages, "removeItem", Q_ARG(QModelIndex, pageIndex))); context->waitForStableState(); } WHEN("^I add a \"(.*)\" named \"(.+)\"$") { REGEX_PARAM(QString, objectType); REGEX_PARAM(QString, objectName); QByteArray actionName = (objectType == QStringLiteral("context")) ? "addContext" : (objectType == QStringLiteral("note")) ? "addItem" : (objectType == QStringLiteral("task")) ? "addItem" : (objectType == QStringLiteral("tag")) ? "addTag" : QByteArray(); VERIFY(!actionName.isEmpty()); ScenarioScope context; context->waitForStableState(); VERIFY(QMetaObject::invokeMethod(context->presentation, actionName.data(), Q_ARG(QString, objectName))); context->waitForStableState(); } WHEN("^I add a child named \"(.+)\" under the task named \"(.+)\"$") { REGEX_PARAM(QString, childName); REGEX_PARAM(QString, parentName); ScenarioScope context; context->waitForStableState(); auto parentIndex = QModelIndex(); for (int row = 0; row < context->indices.size(); row++) { auto index = context->indices.at(row); if (Zanshin::indexString(index) == parentName) { parentIndex = index; break; } } VERIFY_OR_DUMP(parentIndex.isValid()); VERIFY(QMetaObject::invokeMethod(context->presentation, "addItem", Q_ARG(QString, childName), Q_ARG(QModelIndex, parentIndex))); context->waitForStableState(); } WHEN("^I list the items$") { ScenarioScope context; context->waitForStableState(); Zanshin::collectIndices(context.get()); context->waitForStableState(); } WHEN("^I open the item in the editor$") { ScenarioScope context; auto artifact = context->currentArtifact(); VERIFY(artifact); context->editor = context->app->property("editor").value(); VERIFY(context->editor); VERIFY(context->editor->setProperty("artifact", QVariant::fromValue(artifact))); } WHEN("^I mark it done in the editor$") { ScenarioScope context; VERIFY(context->editor->setProperty("done", true)); } WHEN("^I change the editor (.*) to \"(.*)\"$") { REGEX_PARAM(QString, field); REGEX_PARAM(QString, string); const QVariant value = (field == QStringLiteral("text")) ? string : (field == QStringLiteral("title")) ? string : (field == QStringLiteral("start date")) ? QDateTime::fromString(string, Qt::ISODate) : (field == QStringLiteral("due date")) ? QDateTime::fromString(string, Qt::ISODate) : QVariant(); const QByteArray property = (field == QStringLiteral("text")) ? field.toUtf8() : (field == QStringLiteral("title")) ? field.toUtf8() : (field == QStringLiteral("start date")) ? "startDate" : (field == QStringLiteral("due date")) ? "dueDate" : QByteArray(); VERIFY(value.isValid()); VERIFY(!property.isEmpty()); ScenarioScope context; VERIFY(context->editor->setProperty("editingInProgress", true)); VERIFY(context->editor->setProperty(property, value)); } WHEN("^I rename the item to \"(.+)\"$") { REGEX_PARAM(QString, title); ScenarioScope context; VERIFY(context->editor->setProperty("editingInProgress", false)); VERIFY(context->model()->setData(context->index, title, Qt::EditRole)); context->waitForStableState(); } WHEN("^I open the item in the editor again$") { ScenarioScope context; auto artifact = context->currentArtifact(); VERIFY(artifact); VERIFY(context->editor->setProperty("artifact", QVariant::fromValue(Domain::Artifact::Ptr()))); VERIFY(context->editor->setProperty("artifact", QVariant::fromValue(artifact))); context->waitForStableState(); } WHEN("^I drop the item on \"(.*)\" in the central list") { REGEX_PARAM(QString, itemName); ScenarioScope context; VERIFY(context->index.isValid()); const QMimeData *data = context->model()->mimeData(QModelIndexList() << context->index); QAbstractItemModel *destModel = context->model(); QModelIndex dropIndex = Zanshin::findIndex(destModel, itemName); VERIFY(dropIndex.isValid()); VERIFY(destModel->dropMimeData(data, Qt::MoveAction, -1, -1, dropIndex)); context->waitForStableState(); } WHEN("^I drop the item on the blank area of the central list") { ScenarioScope context; VERIFY(context->index.isValid()); const QMimeData *data = context->model()->mimeData(QModelIndexList() << context->index); QAbstractItemModel *destModel = context->model(); VERIFY(destModel->dropMimeData(data, Qt::MoveAction, -1, -1, QModelIndex())); context->waitForStableState(); } WHEN("^I drop items on \"(.*)\" in the central list") { REGEX_PARAM(QString, itemName); ScenarioScope context; VERIFY(!context->dragIndices.isEmpty()); QModelIndexList indexes; std::transform(context->dragIndices.constBegin(), context->dragIndices.constEnd(), std::back_inserter(indexes), [] (const QPersistentModelIndex &index) { VERIFY(index.isValid()); return index; }); const QMimeData *data = context->model()->mimeData(indexes); QAbstractItemModel *destModel = context->model(); QModelIndex dropIndex = Zanshin::findIndex(destModel, itemName); VERIFY(dropIndex.isValid()); VERIFY(destModel->dropMimeData(data, Qt::MoveAction, -1, -1, dropIndex)); context->waitForStableState(); } WHEN("^I drop the item on \"(.*)\" in the page list") { REGEX_PARAM(QString, itemName); ScenarioScope context; VERIFY(context->index.isValid()); const QMimeData *data = context->model()->mimeData(QModelIndexList() << context->index); auto availablePages = context->app->property("availablePages").value(); VERIFY(availablePages); auto destModel = availablePages->property("pageListModel").value(); VERIFY(destModel); context->waitForStableState(); QModelIndex dropIndex = Zanshin::findIndex(destModel, itemName); VERIFY(dropIndex.isValid()); VERIFY(destModel->dropMimeData(data, Qt::MoveAction, -1, -1, dropIndex)); context->waitForStableState(); } WHEN("^I drop items on \"(.*)\" in the page list") { REGEX_PARAM(QString, itemName); ScenarioScope context; VERIFY(!context->dragIndices.isEmpty()); QModelIndexList indexes; std::transform(context->dragIndices.constBegin(), context->dragIndices.constEnd(), std::back_inserter(indexes), [] (const QPersistentModelIndex &index) { VERIFY(index.isValid()); return index; }); const QMimeData *data = context->model()->mimeData(indexes); auto availablePages = context->app->property("availablePages").value(); VERIFY(availablePages); auto destModel = availablePages->property("pageListModel").value(); VERIFY(destModel); context->waitForStableState(); QModelIndex dropIndex = Zanshin::findIndex(destModel, itemName); VERIFY(dropIndex.isValid()); VERIFY(destModel->dropMimeData(data, Qt::MoveAction, -1, -1, dropIndex)); context->waitForStableState(); } WHEN("^the setting key (\\S+) changes to (\\d+)$") { REGEX_PARAM(QString, keyName); REGEX_PARAM(qint64, id); ScenarioScope context; KConfigGroup config(KSharedConfig::openConfig(), "General"); config.writeEntry(keyName, id); } WHEN("^the user changes the default data source to \"(.*)\"$") { REGEX_PARAM(QString, sourceName); ScenarioScope context; context->waitForStableState(); auto sourceIndex = Zanshin::findIndex(context->model(), sourceName); auto availableSources = context->app->property("availableSources").value(); VERIFY(availableSources); VERIFY(QMetaObject::invokeMethod(availableSources, "setDefaultItem", Q_ARG(QModelIndex, sourceIndex))); context->waitForStableState(); } THEN("^the list is") { TABLE_PARAM(tableParam); ScenarioScope context; auto roleNames = context->model()->roleNames(); QSet usedRoles; QStandardItemModel inputModel; for (const auto &row : tableParam.hashes()) { QStandardItem *item = new QStandardItem; for (const auto &it : row) { const QByteArray roleName = it.first.data(); const QString value = QString::fromUtf8(it.second.data()); const int role = roleNames.key(roleName, -1); VERIFY_OR_DUMP(role != -1); item->setData(value, role); usedRoles.insert(role); } inputModel.appendRow(item); } QSortFilterProxyModel proxy; QAbstractItemModel *referenceModel; if (!qobject_cast(context->sourceModel())) { referenceModel = &proxy; proxy.setSourceModel(&inputModel); proxy.setSortRole(Qt::DisplayRole); proxy.sort(0); proxy.setObjectName(QStringLiteral("the_list_is_proxy")); } else { referenceModel = &inputModel; } for (int row = 0; row < context->indices.size(); row++) { QModelIndex expectedIndex = referenceModel->index(row, 0); QModelIndex resultIndex = context->indices.at(row); foreach (const auto &role, usedRoles) { COMPARE_OR_DUMP(Zanshin::indexString(resultIndex, role), Zanshin::indexString(expectedIndex, role)); } } COMPARE_OR_DUMP(context->indices.size(), referenceModel->rowCount()); } THEN("^the list contains \"(.+)\"$") { REGEX_PARAM(QString, itemName); ScenarioScope context; for (int row = 0; row < context->indices.size(); row++) { if (Zanshin::indexString(context->indices.at(row)) == itemName) return; } VERIFY_OR_DUMP(false); } THEN("^the list does not contain \"(.+)\"$") { REGEX_PARAM(QString, itemName); ScenarioScope context; for (int row = 0; row < context->indices.size(); row++) { VERIFY_OR_DUMP(Zanshin::indexString(context->indices.at(row)) != itemName); } } THEN("^the task corresponding to the item is done$") { ScenarioScope context; auto artifact = context->currentArtifact(); VERIFY(artifact); auto task = artifact.dynamicCast(); VERIFY(task); VERIFY(task->isDone()); } THEN("^the editor shows the task as done$") { ScenarioScope context; VERIFY(context->editor->property("done").toBool()); } THEN("^the editor shows \"(.*)\" as (.*)$") { REGEX_PARAM(QString, string); REGEX_PARAM(QString, field); const QVariant value = (field == QStringLiteral("text")) ? string : (field == QStringLiteral("title")) ? string : (field == QStringLiteral("delegate")) ? string : (field == QStringLiteral("start date")) ? QDateTime::fromString(string, Qt::ISODate) : (field == QStringLiteral("due date")) ? QDateTime::fromString(string, Qt::ISODate) : QVariant(); const QByteArray property = (field == QStringLiteral("text")) ? field.toUtf8() : (field == QStringLiteral("title")) ? field.toUtf8() : (field == QStringLiteral("delegate")) ? "delegateText" : (field == QStringLiteral("start date")) ? "startDate" : (field == QStringLiteral("due date")) ? "dueDate" : QByteArray(); VERIFY(value.isValid()); VERIFY(!property.isEmpty()); ScenarioScope context; COMPARE(context->editor->property(property), value); } THEN("^the default data source is \"(.*)\"$") { REGEX_PARAM(QString, expectedName); ScenarioScope context; context->waitForStableState(); auto expectedIndex = Zanshin::findIndex(context->model(), expectedName); VERIFY(expectedIndex.isValid()); auto defaultRole = context->model()->roleNames().key("default", -1); VERIFY(expectedIndex.data(defaultRole).toBool()); } THEN("^the setting key (\\S+) is (\\d+)$") { REGEX_PARAM(QString, keyName); REGEX_PARAM(qint64, expectedId); KConfigGroup config(KSharedConfig::openConfig(), "General"); const qint64 id = config.readEntry(keyName, -1); COMPARE(id, expectedId); } #include "cuke-steps.moc" diff --git a/tests/features/renku/features/tags/tag-remove.feature b/tests/features/renku/features/tags/tag-remove.feature index af24a594..a553d0da 100644 --- a/tests/features/renku/features/tags/tag-remove.feature +++ b/tests/features/renku/features/tags/tag-remove.feature @@ -1,14 +1,14 @@ Feature: Tag removal As someone using notes I can remove a tag In order to maintain their semantic Scenario: Removed tag disappear from the list Given I display the available pages - When I remove a "tag" named "Physics" + When I remove the page named "Physics" under "Tags" And I list the items Then the list is: | display | icon | | Inbox | mail-folder-inbox | | Tags | folder | | Tags / Philosophy | view-pim-tasks | diff --git a/tests/features/zanshin/features/contexts/context-add.feature b/tests/features/zanshin/features/contexts/context-add.feature index d35430e8..594e218b 100644 --- a/tests/features/zanshin/features/contexts/context-add.feature +++ b/tests/features/zanshin/features/contexts/context-add.feature @@ -1,23 +1,25 @@ Feature: Context creation As someone using tasks I can create a context In order to give them some semantic Scenario: New contexts appear in the list Given I display the available pages When I add a "context" named "Internet" And I list the items Then the list is: - | display | icon | - | Inbox | mail-folder-inbox | - | Workday | go-jump-today | - | Projects | folder | - | Projects / Backlog | view-pim-tasks | - | Projects / Prepare talk about TDD | view-pim-tasks | - | Projects / Read List | view-pim-tasks | - | Contexts | folder | - | Contexts / Errands | view-pim-notes | - | Contexts / Internet | view-pim-notes | - | Contexts / Online | view-pim-notes | + | display | icon | + | Inbox | mail-folder-inbox | + | Workday | go-jump-today | + | Projects | folder | + | Projects / Calendar1 | folder | + | Projects / Calendar1 / Prepare talk about TDD | view-pim-tasks | + | Projects / Calendar1 / Read List | view-pim-tasks | + | Projects / Calendar2 | folder | + | Projects / Calendar2 / Backlog | view-pim-tasks | + | Contexts | folder | + | Contexts / Errands | view-pim-notes | + | Contexts / Internet | view-pim-notes | + | Contexts / Online | view-pim-notes | diff --git a/tests/features/zanshin/features/contexts/context-drag-and-drop.feature b/tests/features/zanshin/features/contexts/context-drag-and-drop.feature index b9f5e66b..845eed44 100644 --- a/tests/features/zanshin/features/contexts/context-drag-and-drop.feature +++ b/tests/features/zanshin/features/contexts/context-drag-and-drop.feature @@ -1,29 +1,29 @@ Feature: Context task association As someone collecting tasks I can associate tasks to a context In order to describe the tasks resources Scenario: Dropping a task on a context from the inbox Given I display the "Inbox" page And there is an item named "Buy rutabagas" in the central list When I drop the item on "Contexts / Errands" in the page list And I display the "Contexts / Errands" page And I look at the central list And I list the items Then the list is: | display | | Buy kiwis | | Buy rutabagas | Scenario: Dropping a task on a context from the project central list - Given I display the "Projects / Prepare talk about TDD" page + Given I display the "Projects / Calendar1 / Prepare talk about TDD" page And there is an item named "Create examples and exercices" in the central list When I drop the item on "Contexts / Online" in the page list And I display the "Contexts / Online" page And I look at the central list And I list the items Then the list is: | display | | Create examples and exercices | | Create examples and exercices / Train for the FizzBuzz kata | | Create examples and exercices / Train for the Gilded Rose kata | diff --git a/tests/features/zanshin/features/contexts/context-edit.feature b/tests/features/zanshin/features/contexts/context-edit.feature index af2f336a..ff49c6cc 100644 --- a/tests/features/zanshin/features/contexts/context-edit.feature +++ b/tests/features/zanshin/features/contexts/context-edit.feature @@ -1,20 +1,22 @@ Feature: Context rename As someone collecting tasks I can rename a context In order to refine my tasks organization Scenario: Renamed context appear in the list Given I display the available pages - When I rename a "context" named "Errands" to "Chores" + When I rename the page named "Errands" under "Contexts" to "Chores" And I list the items Then the list is: - | display | icon | - | Inbox | mail-folder-inbox | - | Workday | go-jump-today | - | Projects | folder | - | Projects / Backlog | view-pim-tasks | - | Projects / Prepare talk about TDD | view-pim-tasks | - | Projects / Read List | view-pim-tasks | - | Contexts | folder | - | Contexts / Chores | view-pim-notes | - | Contexts / Online | view-pim-notes | + | display | icon | + | Inbox | mail-folder-inbox | + | Workday | go-jump-today | + | Projects | folder | + | Projects / Calendar1 | folder | + | Projects / Calendar1 / Prepare talk about TDD | view-pim-tasks | + | Projects / Calendar1 / Read List | view-pim-tasks | + | Projects / Calendar2 | folder | + | Projects / Calendar2 / Backlog | view-pim-tasks | + | Contexts | folder | + | Contexts / Chores | view-pim-notes | + | Contexts / Online | view-pim-notes | diff --git a/tests/features/zanshin/features/contexts/context-remove.feature b/tests/features/zanshin/features/contexts/context-remove.feature index 8576fdfa..22a729a2 100644 --- a/tests/features/zanshin/features/contexts/context-remove.feature +++ b/tests/features/zanshin/features/contexts/context-remove.feature @@ -1,21 +1,22 @@ Feature: Context removal As someone using tasks I can remove a context In order to maintain their semantic -@wip Scenario: Removed context disappear from the list Given I display the available pages - When I remove a "context" named "Online" + When I remove the page named "Online" under "Contexts" And I list the items Then the list is: - | display | icon | - | Contexts | folder | - | Contexts / Chores | view-pim-notes | - | Contexts / Internet | view-pim-notes | - | Inbox | mail-folder-inbox | - | Projects | folder | - | Projects / Backlog | view-pim-tasks | - | Projects / Prepare talk about TDD | view-pim-tasks | - | Projects / Read List | view-pim-tasks | + | display | icon | + | Inbox | mail-folder-inbox | + | Workday | go-jump-today | + | Projects | folder | + | Projects / Calendar1 | folder | + | Projects / Calendar1 / Prepare talk about TDD | view-pim-tasks | + | Projects / Calendar1 / Read List | view-pim-tasks | + | Projects / Calendar2 | folder | + | Projects / Calendar2 / Backlog | view-pim-tasks | + | Contexts | folder | + | Contexts / Errands | view-pim-notes | diff --git a/tests/features/zanshin/features/contexts/context-task-remove.feature b/tests/features/zanshin/features/contexts/context-task-remove.feature index a096436a..64284deb 100644 --- a/tests/features/zanshin/features/contexts/context-task-remove.feature +++ b/tests/features/zanshin/features/contexts/context-task-remove.feature @@ -1,29 +1,29 @@ Feature: Context task dissociation As someone collecting tasks I can delete a task related to a context In order to keep my context meaningful Scenario: Removing a task from a context keeps it in the project page it's linked to - Given I display the "Projects / Prepare talk about TDD" page + Given I display the "Projects / Calendar1 / Prepare talk about TDD" page And there is an item named "Create examples and exercices" in the central list And I drop the item on "Contexts / Online" in the page list And I display the "Contexts / Online" page And there is an item named "Create examples and exercices" in the central list When I remove the item And I look at the central list Then the list does not contain "Create examples and exercices" - And I display the "Projects / Prepare talk about TDD" page + And I display the "Projects / Calendar1 / Prepare talk about TDD" page Then there is an item named "Create examples and exercices" in the central list Scenario: Removing a task linked only to a context moves it back to the inbox Given I display the "Inbox" page And I look at the central list Then the list is: | display | And I display the "Contexts / Errands" page And there is an item named "Buy kiwis" in the central list When I remove the item And I look at the central list Then the list does not contain "Buy kiwis" And I display the "Inbox" page Then there is an item named "Buy kiwis" in the central list diff --git a/tests/features/zanshin/features/datasource/datasource-selection.feature b/tests/features/zanshin/features/datasource/datasource-selection.feature index 99e50109..6e249859 100644 --- a/tests/features/zanshin/features/datasource/datasource-selection.feature +++ b/tests/features/zanshin/features/datasource/datasource-selection.feature @@ -1,65 +1,68 @@ Feature: Data sources selection As an advanced user I can select or deselect sources In order to see more or less content Scenario: Unchecking impacts the inbox Given I display the "Inbox" page And there is an item named "TestData / Calendar1" in the available data sources When I uncheck the item And I look at the central list And I list the items Then the list is: | display | | Buy apples | | Buy pears | | Errands | Scenario: Checking impacts the inbox Given I display the "Inbox" page And there is an item named "TestData / Calendar1" in the available data sources When I check the item And I look at the central list And I list the items Then the list is: | display | | "Capital in the Twenty-First Century" by Thomas Piketty | | "The Pragmatic Programmer" by Hunt and Thomas | | Buy cheese | | Buy kiwis | | Buy apples | | Buy pears | | Errands | | Buy rutabagas | Scenario: Unchecking impacts project list Given there is an item named "TestData / Calendar1" in the available data sources When I uncheck the item And I display the available pages And I list the items Then the list is: - | display | - | Inbox | - | Workday | - | Projects | - | Projects / Backlog | - | Contexts | - | Contexts / Errands | - | Contexts / Online | + | display | + | Inbox | + | Workday | + | Projects | + | Projects / Calendar2 | + | Projects / Calendar2 / Backlog | + | Contexts | + | Contexts / Errands | + | Contexts / Online | Scenario: Checking impacts project list Given there is an item named "TestData / Calendar1" in the available data sources When I check the item And I display the available pages And I list the items Then the list is: - | display | - | Inbox | - | Workday | - | Projects | - | Projects / Backlog | - | Projects / Prepare talk about TDD | - | Projects / Read List | - | Contexts | - | Contexts / Errands | - | Contexts / Online | + | display | + | Inbox | + | Workday | + | Projects | + | Projects / Calendar1 | + | Projects / Calendar1 / Prepare talk about TDD | + | Projects / Calendar1 / Read List | + | Projects / Calendar2 | + | Projects / Calendar2 / Backlog | + | Contexts | + | Contexts / Errands | + | Contexts / Online | diff --git a/tests/features/zanshin/features/editing/adding-task.feature b/tests/features/zanshin/features/editing/adding-task.feature index 5a71cc06..45cd813c 100644 --- a/tests/features/zanshin/features/editing/adding-task.feature +++ b/tests/features/zanshin/features/editing/adding-task.feature @@ -1,30 +1,30 @@ Feature: Adding tasks As a task junkie I can create task by giving a title In order to collect ideas while reflecting on my life Scenario Outline: Adding a task in a page Given I display the "" page And I look at the central list When I add a "task" named "" And I list the items Then the list contains "<title>" Examples: - | page | title | - | Inbox | Buy a book | - | Projects / Backlog | Setup a release party | + | page | title | + | Inbox | Buy a book | + | Projects / Calendar2 / Backlog | Setup a release party | Scenario Outline: Adding a task as a child of another task in a page Given I display the "<page>" page And I add a "task" named "<parent>" And I look at the central list And I list the items When I add a child named "<title>" under the task named "<parent>" And I list the items Then the list contains "<parent> / <title>" Examples: - | page | parent | title | - | Inbox | Buy a book | Make sure it is a good book | - | Projects / Backlog | Setup a release party | Make sure there was a release | + | page | parent | title | + | Inbox | Buy a book | Make sure it is a good book | + | Projects / Calendar2 / Backlog | Setup a release party | Make sure there was a release | diff --git a/tests/features/zanshin/features/editing/removing-task.feature b/tests/features/zanshin/features/editing/removing-task.feature index e450dc0d..368c9a2f 100644 --- a/tests/features/zanshin/features/editing/removing-task.feature +++ b/tests/features/zanshin/features/editing/removing-task.feature @@ -1,17 +1,17 @@ Feature: Removing tasks As a task junkie I can delete a task so it is removed In order to clean up the old junk I accumulated Scenario Outline: Removing a simple task from a page Given I display the "<page>" page And there is an item named "<title>" in the central list When I remove the item And I list the items Then the list does not contain "<title>" Examples: - | page | title | - | Inbox | Buy cheese | - | Projects / Read List | "Domain Driven Design" by Eric Evans | + | page | title | + | Inbox | Buy cheese | + | Projects / Calendar1 / Read List | "Domain Driven Design" by Eric Evans | diff --git a/tests/features/zanshin/features/inbox/inbox-drag-and-drop.feature b/tests/features/zanshin/features/inbox/inbox-drag-and-drop.feature index 67b8a529..42198fcc 100644 --- a/tests/features/zanshin/features/inbox/inbox-drag-and-drop.feature +++ b/tests/features/zanshin/features/inbox/inbox-drag-and-drop.feature @@ -1,129 +1,129 @@ Feature: Inbox task association As someone collecting tasks I can associate a task to another one In order to deal with complex tasks requiring several steps Scenario: Dropping a task on another one makes it a child Given I display the "Inbox" page And there is an item named "Buy apples" in the central list When I drop the item on "Errands" in the central list And I list the items Then the list is: | display | | Errands | | Errands / Buy apples | | "Capital in the Twenty-First Century" by Thomas Piketty | | "The Pragmatic Programmer" by Hunt and Thomas | | Buy cheese | | Buy kiwis | | Buy pears | | Buy rutabagas | Scenario: Dropping a child task on the inbox makes it top-level Given I display the "Inbox" page And there is an item named "Buy apples" in the central list And I drop the item on "Errands" in the central list And there is an item named "Errands / Buy apples" in the central list When I drop the item on "Inbox" in the page list And I list the items Then the list is: | display | | Errands | | Buy apples | | "Capital in the Twenty-First Century" by Thomas Piketty | | "The Pragmatic Programmer" by Hunt and Thomas | | Buy cheese | | Buy kiwis | | Buy pears | | Buy rutabagas | Scenario: Dropping two tasks on another one makes them children Given I display the "Inbox" page And the central list contains items named: | display | | Buy apples | | Buy pears | When I drop items on "Errands" in the central list And I list the items Then the list is: | display | | Errands | | Errands / Buy apples | | Errands / Buy pears | | "Capital in the Twenty-First Century" by Thomas Piketty | | "The Pragmatic Programmer" by Hunt and Thomas | | Buy cheese | | Buy kiwis | | Buy rutabagas | Scenario: Dropping two child tasks on the inbox makes them top-level Given I display the "Inbox" page And the central list contains items named: | display | | Buy apples | | Buy pears | And I drop items on "Errands" in the central list And the central list contains items named: | display | | Errands / Buy apples | | Errands / Buy pears | When I drop items on "Inbox" in the page list And I list the items Then the list is: | display | | Errands | | Buy apples | | "Capital in the Twenty-First Century" by Thomas Piketty | | "The Pragmatic Programmer" by Hunt and Thomas | | Buy cheese | | Buy kiwis | | Buy pears | | Buy rutabagas | Scenario: Dropping a task on the inbox removes it from it's associated project - Given I display the "Projects / Prepare talk about TDD" page + Given I display the "Projects / Calendar1 / Prepare talk about TDD" page And there is an item named "Create Sozi SVG" in the central list When I drop the item on "Inbox" in the page list And I display the "Inbox" page And I look at the central list And I list the items Then the list is: | display | | Errands | | Buy apples | | "Capital in the Twenty-First Century" by Thomas Piketty | | "The Pragmatic Programmer" by Hunt and Thomas | | Buy cheese | | Buy kiwis | | Buy pears | | Buy rutabagas | | Create Sozi SVG | Scenario: Deparenting a task by dropping on the central list's blank area Given I display the "Inbox" page And I look at the central list And there is an item named "Buy apples" in the central list And I drop the item on "Errands" in the central list And I look at the central list And there is an item named "Errands / Buy apples" in the central list When I drop the item on the blank area of the central list And I list the items Then the list is: | display | | Errands | | Buy apples | | "Capital in the Twenty-First Century" by Thomas Piketty | | "The Pragmatic Programmer" by Hunt and Thomas | | Buy cheese | | Buy kiwis | | Buy pears | | Buy rutabagas | Scenario: Dropping a task on the inbox removes it from all it's contexts Given I display the "Contexts / Errands" page And there is an item named "Buy kiwis" in the central list When I drop the item on "Inbox" in the page list And I display the "Contexts / Errands" page And I look at the central list Then the list does not contain "Buy kiwis" diff --git a/tests/features/zanshin/features/pages/pages-display.feature b/tests/features/zanshin/features/pages/pages-display.feature index 45c37414..d36bd0d8 100644 --- a/tests/features/zanshin/features/pages/pages-display.feature +++ b/tests/features/zanshin/features/pages/pages-display.feature @@ -1,20 +1,22 @@ Feature: Available pages content As someone collecting tasks I can see all the pages available to me In order to display them and add tasks to them Scenario: Inbox, projects, contexts and tags appear in the list Given I display the available pages When I list the items Then the list is: - | display | icon | - | Inbox | mail-folder-inbox | - | Workday | go-jump-today | - | Projects | folder | - | Projects / Backlog | view-pim-tasks | - | Projects / Prepare talk about TDD | view-pim-tasks | - | Projects / Read List | view-pim-tasks | - | Contexts | folder | - | Contexts / Errands | view-pim-notes | - | Contexts / Online | view-pim-notes | + | display | icon | + | Inbox | mail-folder-inbox | + | Workday | go-jump-today | + | Projects | folder | + | Projects / Calendar1 | folder | + | Projects / Calendar1 / Prepare talk about TDD | view-pim-tasks | + | Projects / Calendar1 / Read List | view-pim-tasks | + | Projects / Calendar2 | folder | + | Projects / Calendar2 / Backlog | view-pim-tasks | + | Contexts | folder | + | Contexts / Errands | view-pim-notes | + | Contexts / Online | view-pim-notes | diff --git a/tests/features/zanshin/features/projects/project-add.feature b/tests/features/zanshin/features/projects/project-add.feature index bd2d50fd..f3951db2 100644 --- a/tests/features/zanshin/features/projects/project-add.feature +++ b/tests/features/zanshin/features/projects/project-add.feature @@ -1,21 +1,23 @@ Feature: Project creation As someone collecting tasks I can create a project In order to organize my tasks Scenario: New projects appear in the list Given I display the available pages When I add a project named "Birthday" in the source named "TestData / Calendar1" And I list the items Then the list is: - | display | icon | - | Inbox | mail-folder-inbox | - | Workday | go-jump-today | - | Projects | folder | - | Projects / Backlog | view-pim-tasks | - | Projects / Birthday | view-pim-tasks | - | Projects / Prepare talk about TDD | view-pim-tasks | - | Projects / Read List | view-pim-tasks | - | Contexts | folder | - | Contexts / Errands | view-pim-notes | - | Contexts / Online | view-pim-notes | + | display | icon | + | Inbox | mail-folder-inbox | + | Workday | go-jump-today | + | Projects | folder | + | Projects / Calendar1 | folder | + | Projects / Calendar1 / Birthday | view-pim-tasks | + | Projects / Calendar1 / Prepare talk about TDD | view-pim-tasks | + | Projects / Calendar1 / Read List | view-pim-tasks | + | Projects / Calendar2 | folder | + | Projects / Calendar2 / Backlog | view-pim-tasks | + | Contexts | folder | + | Contexts / Errands | view-pim-notes | + | Contexts / Online | view-pim-notes | diff --git a/tests/features/zanshin/features/projects/project-display.feature b/tests/features/zanshin/features/projects/project-display.feature index c87efb83..04212ded 100644 --- a/tests/features/zanshin/features/projects/project-display.feature +++ b/tests/features/zanshin/features/projects/project-display.feature @@ -1,13 +1,13 @@ Feature: Project content As someone collecting tasks I can display a project In order to see the artifacts associated to it Scenario: Project tasks appear in the corresponding page - Given I display the "Projects / Read List" page + Given I display the "Projects / Calendar1 / Read List" page And I look at the central list When I list the items Then the list is: | display | | "Clean Code" by Robert C Martin | | "Domain Driven Design" by Eric Evans | diff --git a/tests/features/zanshin/features/projects/project-drag-and-drop.feature b/tests/features/zanshin/features/projects/project-drag-and-drop.feature index 77fbbb9b..db2f08b0 100644 --- a/tests/features/zanshin/features/projects/project-drag-and-drop.feature +++ b/tests/features/zanshin/features/projects/project-drag-and-drop.feature @@ -1,28 +1,28 @@ Feature: Project task association As someone collecting tasks I can associate a task to a project In order to organize my work Scenario: Dropping a task on a project Given I display the "Inbox" page And there is an item named ""The Pragmatic Programmer" by Hunt and Thomas" in the central list - When I drop the item on "Projects / Read List" in the page list - And I display the "Projects / Read List" page + When I drop the item on "Projects / Calendar1 / Read List" in the page list + And I display the "Projects / Calendar1 / Read List" page And I look at the central list And I list the items Then the list is: | display | | "Clean Code" by Robert C Martin | | "Domain Driven Design" by Eric Evans | | "The Pragmatic Programmer" by Hunt and Thomas | Scenario: Dropping a task on a project from context central list Given I display the "Contexts / Errands" page And there is an item named "Buy kiwis" in the central list - When I drop the item on "Projects / Backlog" in the page list - And I display the "Projects / Backlog" page + When I drop the item on "Projects / Calendar2 / Backlog" in the page list + And I display the "Projects / Calendar2 / Backlog" page And I look at the central list And I list the items Then the list is: | display | | Buy kiwis | diff --git a/tests/features/zanshin/features/projects/project-edit.feature b/tests/features/zanshin/features/projects/project-edit.feature index 1d936c8e..4bd5b420 100644 --- a/tests/features/zanshin/features/projects/project-edit.feature +++ b/tests/features/zanshin/features/projects/project-edit.feature @@ -1,20 +1,22 @@ Feature: Project rename As someone collecting tasks I can rename a project In order to refine my tasks organization Scenario: Renamed projects appear in the list Given I display the available pages - When I rename a "project" named "Backlog" to "Party" + When I rename the page named "Backlog" under "Projects / Calendar2" to "Party" And I list the items Then the list is: - | display | icon | - | Inbox | mail-folder-inbox | - | Workday | go-jump-today | - | Projects | folder | - | Projects / Party | view-pim-tasks | - | Projects / Prepare talk about TDD | view-pim-tasks | - | Projects / Read List | view-pim-tasks | - | Contexts | folder | - | Contexts / Errands | view-pim-notes | - | Contexts / Online | view-pim-notes | + | display | icon | + | Inbox | mail-folder-inbox | + | Workday | go-jump-today | + | Projects | folder | + | Projects / Calendar1 | folder | + | Projects / Calendar1 / Prepare talk about TDD | view-pim-tasks | + | Projects / Calendar1 / Read List | view-pim-tasks | + | Projects / Calendar2 | folder | + | Projects / Calendar2 / Party | view-pim-tasks | + | Contexts | folder | + | Contexts / Errands | view-pim-notes | + | Contexts / Online | view-pim-notes | diff --git a/tests/features/zanshin/features/projects/project-remove.feature b/tests/features/zanshin/features/projects/project-remove.feature index bbbebd59..08e93b30 100644 --- a/tests/features/zanshin/features/projects/project-remove.feature +++ b/tests/features/zanshin/features/projects/project-remove.feature @@ -1,19 +1,21 @@ Feature: Project destruction As someone collecting tasks I can delete a project In order to clean my tasks Scenario: Removing a simple project from the list Given I display the available pages - When I remove a "project" named "Prepare talk about TDD" + When I remove the page named "Prepare talk about TDD" under "Projects / Calendar1" And I list the items Then the list is: - | display | icon | - | Inbox | mail-folder-inbox | - | Workday | go-jump-today | - | Projects | folder | - | Projects / Backlog | view-pim-tasks | - | Projects / Read List | view-pim-tasks | - | Contexts | folder | - | Contexts / Errands | view-pim-notes | - | Contexts / Online | view-pim-notes | + | display | icon | + | Inbox | mail-folder-inbox | + | Workday | go-jump-today | + | Projects | folder | + | Projects / Calendar1 | folder | + | Projects / Calendar1 / Read List | view-pim-tasks | + | Projects / Calendar2 | folder | + | Projects / Calendar2 / Backlog | view-pim-tasks | + | Contexts | folder | + | Contexts / Errands | view-pim-notes | + | Contexts / Online | view-pim-notes | diff --git a/tests/features/zanshin/features/projects/project-task-add.feature b/tests/features/zanshin/features/projects/project-task-add.feature index 1dd4fe62..77d9cdb0 100644 --- a/tests/features/zanshin/features/projects/project-task-add.feature +++ b/tests/features/zanshin/features/projects/project-task-add.feature @@ -1,15 +1,15 @@ Feature: Task creation from a project As someone collecting tasks I can add a task directly inside a project In order to organize my tasks Scenario: Task added from a project appear in its list - Given I display the "Projects / Backlog" page + Given I display the "Projects / Calendar2 / Backlog" page When I add a "task" named "Buy a cake" And I add a "task" named "Buy a present" And I look at the central list When I list the items Then the list is: | display | | Buy a cake | | Buy a present | diff --git a/tests/features/zanshin/features/projects/project-task-promote.feature b/tests/features/zanshin/features/projects/project-task-promote.feature index 3e31d50e..3181fb59 100644 --- a/tests/features/zanshin/features/projects/project-task-promote.feature +++ b/tests/features/zanshin/features/projects/project-task-promote.feature @@ -1,26 +1,28 @@ Feature: Task promotion As someone collecting tasks I can promote a task into a project In order to organize my tasks Scenario: Task promoted into a project appears in the list - Given I display the "Projects / Backlog" page + Given I display the "Projects / Calendar2 / Backlog" page And I add a "task" named "Design a present" And I look at the central list And there is an item named "Design a present" in the central list When I promote the item And I display the available pages And I list the items Then the list is: - | display | icon | - | Inbox | mail-folder-inbox | - | Workday | go-jump-today | - | Projects | folder | - | Projects / Backlog | view-pim-tasks | - | Projects / Design a present | view-pim-tasks | - | Projects / Prepare talk about TDD | view-pim-tasks | - | Projects / Read List | view-pim-tasks | - | Contexts | folder | - | Contexts / Errands | view-pim-notes | - | Contexts / Online | view-pim-notes | + | display | icon | + | Inbox | mail-folder-inbox | + | Workday | go-jump-today | + | Projects | folder | + | Projects / Calendar1 | folder | + | Projects / Calendar1 / Prepare talk about TDD | view-pim-tasks | + | Projects / Calendar1 / Read List | view-pim-tasks | + | Projects / Calendar2 | folder | + | Projects / Calendar2 / Backlog | view-pim-tasks | + | Projects / Calendar2 / Design a present | view-pim-tasks | + | Contexts | folder | + | Contexts / Errands | view-pim-notes | + | Contexts / Online | view-pim-notes | diff --git a/tests/units/presentation/availabletaskpagesmodeltest.cpp b/tests/units/presentation/availabletaskpagesmodeltest.cpp index 02186747..6cc41e06 100644 --- a/tests/units/presentation/availabletaskpagesmodeltest.cpp +++ b/tests/units/presentation/availabletaskpagesmodeltest.cpp @@ -1,1139 +1,1283 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens <ervin@kde.org> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include <testlib/qtest_zanshin.h> #include <memory> #include <QMimeData> #include <KLocalizedString> #include "utils/mockobject.h" #include "utils/datetime.h" #include "domain/note.h" #include "presentation/availabletaskpagesmodel.h" #include "presentation/contextpagemodel.h" #include "presentation/errorhandler.h" #include "presentation/projectpagemodel.h" #include "presentation/querytreemodelbase.h" #include "presentation/taskinboxpagemodel.h" #include "presentation/workdaypagemodel.h" #include "testlib/fakejob.h" using namespace mockitopp; using namespace mockitopp::matcher; class FakeErrorHandler : public Presentation::ErrorHandler { public: void doDisplayMessage(const QString &message) { m_message = message; } QString m_message; }; class AvailableTaskPagesModelTest : public QObject { Q_OBJECT private slots: void shouldDeclareOnlyProjectAndContextPages() { // GIVEN - Presentation::AvailableTaskPagesModel pages({}, {}, {}, {}, {}, {}); + Presentation::AvailableTaskPagesModel pages({}, {}, {}, {}, {}, {}, {}); // THEN QVERIFY(pages.hasProjectPages()); QVERIFY(pages.hasContextPages()); QVERIFY(!pages.hasTagPages()); } void shouldListAvailablePages() { // GIVEN - // Two projects - auto project1 = Domain::Project::Ptr::create(); - project1->setName(QStringLiteral("Project 1")); - auto project2 = Domain::Project::Ptr::create(); - project2->setName(QStringLiteral("Project 2")); - auto projectProvider = Domain::QueryResultProvider<Domain::Project::Ptr>::Ptr::create(); - auto projectResult = Domain::QueryResult<Domain::Project::Ptr>::create(projectProvider); - projectProvider->append(project2); - projectProvider->append(project1); // note: reversed order, to test sorting + // Two selected data sources + auto source1 = Domain::DataSource::Ptr::create(); + source1->setName("source1"); + auto source2 = Domain::DataSource::Ptr::create(); + source2->setName("source2"); + auto sourceProvider = Domain::QueryResultProvider<Domain::DataSource::Ptr>::Ptr::create(); + auto sourceResult = Domain::QueryResult<Domain::DataSource::Ptr>::create(sourceProvider); + sourceProvider->append(source1); + sourceProvider->append(source2); + + // Two projects under source 1 + auto project11 = Domain::Project::Ptr::create(); + project11->setName(QStringLiteral("Project 11")); + auto project12 = Domain::Project::Ptr::create(); + project12->setName(QStringLiteral("Project 12")); + auto project1Provider = Domain::QueryResultProvider<Domain::Project::Ptr>::Ptr::create(); + auto project1Result = Domain::QueryResult<Domain::Project::Ptr>::create(project1Provider); + project1Provider->append(project12); + project1Provider->append(project11); // note: reversed order, to test sorting + + // Two projects under source 2 + auto project21 = Domain::Project::Ptr::create(); + project21->setName(QStringLiteral("Project 21")); + auto project22 = Domain::Project::Ptr::create(); + project22->setName(QStringLiteral("Project 22")); + auto project2Provider = Domain::QueryResultProvider<Domain::Project::Ptr>::Ptr::create(); + auto project2Result = Domain::QueryResult<Domain::Project::Ptr>::create(project2Provider); + project2Provider->append(project22); + project2Provider->append(project21); // note: reversed order, to test sorting // Two contexts auto context1 = Domain::Context::Ptr::create(); context1->setName(QStringLiteral("context 1")); auto context2 = Domain::Context::Ptr::create(); context2->setName(QStringLiteral("context 2")); auto contextProvider = Domain::QueryResultProvider<Domain::Context::Ptr>::Ptr::create(); auto contextResult = Domain::QueryResult<Domain::Context::Ptr>::create(contextProvider); contextProvider->append(context1); contextProvider->append(context2); // Two artifacts (used for dropping later on) Domain::Artifact::Ptr taskToDrop(new Domain::Task); Domain::Artifact::Ptr noteToDrop(new Domain::Note); - Utils::MockObject<Domain::ProjectQueries> projectQueriesMock; - projectQueriesMock(&Domain::ProjectQueries::findAll).when().thenReturn(projectResult); + Utils::MockObject<Domain::DataSourceQueries> dataSourceQueriesMock; + dataSourceQueriesMock(&Domain::DataSourceQueries::findAllSelected).when().thenReturn(sourceResult); + dataSourceQueriesMock(&Domain::DataSourceQueries::findProjects).when(source1).thenReturn(project1Result); + dataSourceQueriesMock(&Domain::DataSourceQueries::findProjects).when(source2).thenReturn(project2Result); Utils::MockObject<Domain::ProjectRepository> projectRepositoryMock; Utils::MockObject<Domain::TaskRepository> taskRepositoryMock; Utils::MockObject<Domain::ContextQueries> contextQueriesMock; contextQueriesMock(&Domain::ContextQueries::findAll).when().thenReturn(contextResult); Utils::MockObject<Domain::ContextRepository> contextRepositoryMock; - Presentation::AvailableTaskPagesModel pages(projectQueriesMock.getInstance(), + Presentation::AvailableTaskPagesModel pages(dataSourceQueriesMock.getInstance(), + Domain::ProjectQueries::Ptr(), projectRepositoryMock.getInstance(), contextQueriesMock.getInstance(), contextRepositoryMock.getInstance(), Domain::TaskQueries::Ptr(), taskRepositoryMock.getInstance()); // WHEN QAbstractItemModel *model = pages.pageListModel(); // THEN const QModelIndex inboxIndex = model->index(0, 0); const QModelIndex workdayIndex = model->index(1, 0); const QModelIndex projectsIndex = model->index(2, 0); - const QModelIndex project1Index = model->index(0, 0, projectsIndex); - const QModelIndex project2Index = model->index(1, 0, projectsIndex); + const QModelIndex source1Index = model->index(0, 0, projectsIndex); + const QModelIndex project11Index = model->index(0, 0, source1Index); + const QModelIndex project12Index = model->index(1, 0, source1Index); + const QModelIndex source2Index = model->index(1, 0, projectsIndex); + const QModelIndex project21Index = model->index(0, 0, source2Index); + const QModelIndex project22Index = model->index(1, 0, source2Index); const QModelIndex contextsIndex = model->index(3, 0); const QModelIndex context1Index = model->index(0, 0, contextsIndex); const QModelIndex context2Index = model->index(1, 0, contextsIndex); QCOMPARE(model->rowCount(), 4); QCOMPARE(model->rowCount(inboxIndex), 0); QCOMPARE(model->rowCount(workdayIndex), 0); QCOMPARE(model->rowCount(projectsIndex), 2); - QCOMPARE(model->rowCount(project1Index), 0); - QCOMPARE(model->rowCount(project2Index), 0); + QCOMPARE(model->rowCount(source1Index), 2); + QCOMPARE(model->rowCount(project11Index), 0); + QCOMPARE(model->rowCount(project12Index), 0); + QCOMPARE(model->rowCount(source2Index), 2); + QCOMPARE(model->rowCount(project21Index), 0); + QCOMPARE(model->rowCount(project22Index), 0); QCOMPARE(model->rowCount(contextsIndex), 2); QCOMPARE(model->rowCount(context1Index), 0); QCOMPARE(model->rowCount(context2Index), 0); const Qt::ItemFlags defaultFlags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable; QCOMPARE(model->flags(inboxIndex), (defaultFlags & ~(Qt::ItemIsEditable)) | Qt::ItemIsDropEnabled); QCOMPARE(model->flags(workdayIndex), (defaultFlags & ~(Qt::ItemIsEditable)) | Qt::ItemIsDropEnabled); QCOMPARE(model->flags(projectsIndex), Qt::NoItemFlags); - QCOMPARE(model->flags(project1Index), defaultFlags | Qt::ItemIsDropEnabled); - QCOMPARE(model->flags(project2Index), defaultFlags | Qt::ItemIsDropEnabled); + QCOMPARE(model->flags(source1Index), Qt::NoItemFlags); + QCOMPARE(model->flags(project11Index), defaultFlags | Qt::ItemIsDropEnabled); + QCOMPARE(model->flags(project12Index), defaultFlags | Qt::ItemIsDropEnabled); + QCOMPARE(model->flags(source2Index), Qt::NoItemFlags); + QCOMPARE(model->flags(project21Index), defaultFlags | Qt::ItemIsDropEnabled); + QCOMPARE(model->flags(project22Index), defaultFlags | Qt::ItemIsDropEnabled); QCOMPARE(model->flags(contextsIndex), Qt::NoItemFlags); QCOMPARE(model->flags(context1Index), defaultFlags | Qt::ItemIsDropEnabled); QCOMPARE(model->flags(context2Index), defaultFlags | Qt::ItemIsDropEnabled); QCOMPARE(model->data(inboxIndex).toString(), i18n("Inbox")); QCOMPARE(model->data(workdayIndex).toString(), i18n("Workday")); QCOMPARE(model->data(projectsIndex).toString(), i18n("Projects")); - QCOMPARE(model->data(project1Index).toString(), project1->name()); - QCOMPARE(model->data(project2Index).toString(), project2->name()); + QCOMPARE(model->data(source1Index).toString(), source1->name()); + QCOMPARE(model->data(project11Index).toString(), project11->name()); + QCOMPARE(model->data(project12Index).toString(), project12->name()); + QCOMPARE(model->data(source2Index).toString(), source2->name()); + QCOMPARE(model->data(project21Index).toString(), project21->name()); + QCOMPARE(model->data(project22Index).toString(), project22->name()); QCOMPARE(model->data(contextsIndex).toString(), i18n("Contexts")); QCOMPARE(model->data(context1Index).toString(), context1->name()); QCOMPARE(model->data(context2Index).toString(), context2->name()); QVERIFY(!model->data(inboxIndex, Qt::EditRole).isValid()); QVERIFY(!model->data(workdayIndex, Qt::EditRole).isValid()); QVERIFY(!model->data(projectsIndex, Qt::EditRole).isValid()); - QCOMPARE(model->data(project1Index, Qt::EditRole).toString(), project1->name()); - QCOMPARE(model->data(project2Index, Qt::EditRole).toString(), project2->name()); + QVERIFY(!model->data(source1Index, Qt::EditRole).isValid()); + QCOMPARE(model->data(project11Index, Qt::EditRole).toString(), project11->name()); + QCOMPARE(model->data(project12Index, Qt::EditRole).toString(), project12->name()); + QVERIFY(!model->data(source2Index, Qt::EditRole).isValid()); + QCOMPARE(model->data(project21Index, Qt::EditRole).toString(), project21->name()); + QCOMPARE(model->data(project22Index, Qt::EditRole).toString(), project22->name()); QVERIFY(!model->data(contextsIndex, Qt::EditRole).isValid()); QCOMPARE(model->data(context1Index, Qt::EditRole).toString(), context1->name()); QCOMPARE(model->data(context2Index, Qt::EditRole).toString(), context2->name()); QCOMPARE(model->data(inboxIndex, Presentation::QueryTreeModelBase::IconNameRole).toString(), QStringLiteral("mail-folder-inbox")); QCOMPARE(model->data(workdayIndex, Presentation::QueryTreeModelBase::IconNameRole).toString(), QStringLiteral("go-jump-today")); QCOMPARE(model->data(projectsIndex, Presentation::QueryTreeModelBase::IconNameRole).toString(), QStringLiteral("folder")); - QCOMPARE(model->data(project1Index, Presentation::QueryTreeModelBase::IconNameRole).toString(), QStringLiteral("view-pim-tasks")); - QCOMPARE(model->data(project2Index, Presentation::QueryTreeModelBase::IconNameRole).toString(), QStringLiteral("view-pim-tasks")); + QCOMPARE(model->data(source1Index, Presentation::QueryTreeModelBase::IconNameRole).toString(), QStringLiteral("folder")); + QCOMPARE(model->data(project11Index, Presentation::QueryTreeModelBase::IconNameRole).toString(), QStringLiteral("view-pim-tasks")); + QCOMPARE(model->data(project12Index, Presentation::QueryTreeModelBase::IconNameRole).toString(), QStringLiteral("view-pim-tasks")); + QCOMPARE(model->data(source2Index, Presentation::QueryTreeModelBase::IconNameRole).toString(), QStringLiteral("folder")); + QCOMPARE(model->data(project21Index, Presentation::QueryTreeModelBase::IconNameRole).toString(), QStringLiteral("view-pim-tasks")); + QCOMPARE(model->data(project22Index, Presentation::QueryTreeModelBase::IconNameRole).toString(), QStringLiteral("view-pim-tasks")); QCOMPARE(model->data(contextsIndex, Presentation::QueryTreeModelBase::IconNameRole).toString(), QStringLiteral("folder")); QCOMPARE(model->data(context1Index, Presentation::QueryTreeModelBase::IconNameRole).toString(), QStringLiteral("view-pim-notes")); QCOMPARE(model->data(context2Index, Presentation::QueryTreeModelBase::IconNameRole).toString(), QStringLiteral("view-pim-notes")); QVERIFY(!model->data(inboxIndex, Qt::CheckStateRole).isValid()); QVERIFY(!model->data(workdayIndex, Qt::CheckStateRole).isValid()); QVERIFY(!model->data(projectsIndex, Qt::CheckStateRole).isValid()); - QVERIFY(!model->data(project1Index, Qt::CheckStateRole).isValid()); - QVERIFY(!model->data(project2Index, Qt::CheckStateRole).isValid()); + QVERIFY(!model->data(source1Index, Qt::CheckStateRole).isValid()); + QVERIFY(!model->data(project11Index, Qt::CheckStateRole).isValid()); + QVERIFY(!model->data(project12Index, Qt::CheckStateRole).isValid()); + QVERIFY(!model->data(source2Index, Qt::CheckStateRole).isValid()); + QVERIFY(!model->data(project21Index, Qt::CheckStateRole).isValid()); + QVERIFY(!model->data(project22Index, Qt::CheckStateRole).isValid()); QVERIFY(!model->data(contextsIndex, Qt::CheckStateRole).isValid()); QVERIFY(!model->data(context1Index, Qt::CheckStateRole).isValid()); QVERIFY(!model->data(context2Index, Qt::CheckStateRole).isValid()); // WHEN - projectRepositoryMock(&Domain::ProjectRepository::update).when(project1).thenReturn(new FakeJob(this)); - projectRepositoryMock(&Domain::ProjectRepository::update).when(project2).thenReturn(new FakeJob(this)); + projectRepositoryMock(&Domain::ProjectRepository::update).when(project11).thenReturn(new FakeJob(this)); + projectRepositoryMock(&Domain::ProjectRepository::update).when(project12).thenReturn(new FakeJob(this)); + projectRepositoryMock(&Domain::ProjectRepository::update).when(project21).thenReturn(new FakeJob(this)); + projectRepositoryMock(&Domain::ProjectRepository::update).when(project22).thenReturn(new FakeJob(this)); contextRepositoryMock(&Domain::ContextRepository::update).when(context1).thenReturn(new FakeJob(this)); contextRepositoryMock(&Domain::ContextRepository::update).when(context2).thenReturn(new FakeJob(this)); QVERIFY(!model->setData(inboxIndex, "Foo")); QVERIFY(!model->setData(projectsIndex, "Foo")); - QVERIFY(model->setData(project1Index, "New Project 1")); - QVERIFY(model->setData(project2Index, "New Project 2")); + QVERIFY(!model->setData(source1Index, "Foo")); + QVERIFY(model->setData(project11Index, "New Project 11")); + QVERIFY(model->setData(project12Index, "New Project 12")); + QVERIFY(!model->setData(source2Index, "Foo")); + QVERIFY(model->setData(project21Index, "New Project 21")); + QVERIFY(model->setData(project22Index, "New Project 22")); QVERIFY(!model->setData(contextsIndex, "Foo")); QVERIFY(model->setData(context1Index, "New Context 1")); QVERIFY(model->setData(context2Index, "New Context 2")); // THEN - QVERIFY(projectRepositoryMock(&Domain::ProjectRepository::update).when(project1).exactly(1)); - QVERIFY(projectRepositoryMock(&Domain::ProjectRepository::update).when(project2).exactly(1)); + QVERIFY(projectRepositoryMock(&Domain::ProjectRepository::update).when(project11).exactly(1)); + QVERIFY(projectRepositoryMock(&Domain::ProjectRepository::update).when(project12).exactly(1)); + QVERIFY(projectRepositoryMock(&Domain::ProjectRepository::update).when(project21).exactly(1)); + QVERIFY(projectRepositoryMock(&Domain::ProjectRepository::update).when(project22).exactly(1)); QVERIFY(contextRepositoryMock(&Domain::ContextRepository::update).when(context1).exactly(1)); QVERIFY(contextRepositoryMock(&Domain::ContextRepository::update).when(context2).exactly(1)); - QCOMPARE(project1->name(), QStringLiteral("New Project 1")); - QCOMPARE(project2->name(), QStringLiteral("New Project 2")); + QCOMPARE(project11->name(), QStringLiteral("New Project 11")); + QCOMPARE(project12->name(), QStringLiteral("New Project 12")); + QCOMPARE(project21->name(), QStringLiteral("New Project 21")); + QCOMPARE(project22->name(), QStringLiteral("New Project 22")); QCOMPARE(context1->name(), QStringLiteral("New Context 1")); QCOMPARE(context2->name(), QStringLiteral("New Context 2")); // WHEN - projectRepositoryMock(&Domain::ProjectRepository::associate).when(project1, taskToDrop).thenReturn(new FakeJob(this)); + projectRepositoryMock(&Domain::ProjectRepository::associate).when(project11, taskToDrop).thenReturn(new FakeJob(this)); auto data = std::make_unique<QMimeData>(); data->setData(QStringLiteral("application/x-zanshin-object"), "object"); data->setProperty("objects", QVariant::fromValue(Domain::Artifact::List() << taskToDrop)); - model->dropMimeData(data.get(), Qt::MoveAction, -1, -1, project1Index); + model->dropMimeData(data.get(), Qt::MoveAction, -1, -1, project11Index); // THEN - QVERIFY(projectRepositoryMock(&Domain::ProjectRepository::associate).when(project1, taskToDrop).exactly(1)); + QVERIFY(projectRepositoryMock(&Domain::ProjectRepository::associate).when(project11, taskToDrop).exactly(1)); // WHEN a task is dropped on a context contextRepositoryMock(&Domain::ContextRepository::associate).when(context1, taskToDrop.objectCast<Domain::Task>()).thenReturn(new FakeJob(this)); data.reset(new QMimeData); data->setData(QStringLiteral("application/x-zanshin-object"), "object"); data->setProperty("objects", QVariant::fromValue(Domain::Artifact::List() << taskToDrop)); model->dropMimeData(data.get(), Qt::MoveAction, -1, -1, context1Index); // THEN QVERIFY(contextRepositoryMock(&Domain::ContextRepository::associate).when(context1, taskToDrop.objectCast<Domain::Task>()).exactly(1)); // WHEN projectRepositoryMock(&Domain::ProjectRepository::dissociate).when(taskToDrop).thenReturn(new FakeJob(this)); taskRepositoryMock(&Domain::TaskRepository::dissociateAll).when(taskToDrop.objectCast<Domain::Task>()).thenReturn(new FakeJob(this)); data.reset(new QMimeData); data->setData(QStringLiteral("application/x-zanshin-object"), "object"); data->setProperty("objects", QVariant::fromValue(Domain::Artifact::List() << taskToDrop)); model->dropMimeData(data.get(), Qt::MoveAction, -1, -1, inboxIndex); QTest::qWait(150); // THEN QVERIFY(projectRepositoryMock(&Domain::ProjectRepository::dissociate).when(taskToDrop).exactly(1)); QVERIFY(taskRepositoryMock(&Domain::TaskRepository::dissociateAll).when(taskToDrop.objectCast<Domain::Task>()).exactly(1)); // WHEN - projectRepositoryMock(&Domain::ProjectRepository::associate).when(project2, noteToDrop).thenReturn(new FakeJob(this)); + projectRepositoryMock(&Domain::ProjectRepository::associate).when(project12, noteToDrop).thenReturn(new FakeJob(this)); data.reset(new QMimeData); data->setData(QStringLiteral("application/x-zanshin-object"), "object"); data->setProperty("objects", QVariant::fromValue(Domain::Artifact::List() << noteToDrop)); - model->dropMimeData(data.get(), Qt::MoveAction, -1, -1, project2Index); + model->dropMimeData(data.get(), Qt::MoveAction, -1, -1, project12Index); // THEN - QVERIFY(projectRepositoryMock(&Domain::ProjectRepository::associate).when(project2, noteToDrop).exactly(1)); + QVERIFY(projectRepositoryMock(&Domain::ProjectRepository::associate).when(project12, noteToDrop).exactly(1)); // WHEN Domain::Artifact::Ptr taskToDrop2(new Domain::Task); Domain::Artifact::Ptr noteToDrop2(new Domain::Note); - projectRepositoryMock(&Domain::ProjectRepository::associate).when(project1, taskToDrop2).thenReturn(new FakeJob(this)); - projectRepositoryMock(&Domain::ProjectRepository::associate).when(project1, noteToDrop2).thenReturn(new FakeJob(this)); + projectRepositoryMock(&Domain::ProjectRepository::associate).when(project11, taskToDrop2).thenReturn(new FakeJob(this)); + projectRepositoryMock(&Domain::ProjectRepository::associate).when(project11, noteToDrop2).thenReturn(new FakeJob(this)); data.reset(new QMimeData); data->setData(QStringLiteral("application/x-zanshin-object"), "object"); data->setProperty("objects", QVariant::fromValue(Domain::Artifact::List() << taskToDrop2 << noteToDrop2)); - model->dropMimeData(data.get(), Qt::MoveAction, -1, -1, project1Index); + model->dropMimeData(data.get(), Qt::MoveAction, -1, -1, project11Index); // THEN - QVERIFY(projectRepositoryMock(&Domain::ProjectRepository::associate).when(project1, taskToDrop2).exactly(1)); - QVERIFY(projectRepositoryMock(&Domain::ProjectRepository::associate).when(project1, noteToDrop2).exactly(1)); + QVERIFY(projectRepositoryMock(&Domain::ProjectRepository::associate).when(project11, taskToDrop2).exactly(1)); + QVERIFY(projectRepositoryMock(&Domain::ProjectRepository::associate).when(project11, noteToDrop2).exactly(1)); // WHEN a task and a note are dropped on a context data.reset(new QMimeData); data->setData(QStringLiteral("application/x-zanshin-object"), "object"); data->setProperty("objects", QVariant::fromValue(Domain::Artifact::List() << taskToDrop2 << noteToDrop2)); model->dropMimeData(data.get(), Qt::MoveAction, -1, -1, context1Index); // THEN QVERIFY(contextRepositoryMock(&Domain::ContextRepository::associate).when(context1, taskToDrop2.objectCast<Domain::Task>()).exactly(0)); // WHEN two tasks are dropped on a context Domain::Task::Ptr taskToDrop3(new Domain::Task); Domain::Task::Ptr taskToDrop4(new Domain::Task); contextRepositoryMock(&Domain::ContextRepository::associate).when(context1, taskToDrop3).thenReturn(new FakeJob(this)); contextRepositoryMock(&Domain::ContextRepository::associate).when(context1, taskToDrop4).thenReturn(new FakeJob(this)); data.reset(new QMimeData); data->setData(QStringLiteral("application/x-zanshin-object"), "object"); data->setProperty("objects", QVariant::fromValue(Domain::Artifact::List() << taskToDrop3 << taskToDrop4)); model->dropMimeData(data.get(), Qt::MoveAction, -1, -1, context1Index); // THEN QVERIFY(contextRepositoryMock(&Domain::ContextRepository::associate).when(context1, taskToDrop3).exactly(1)); QVERIFY(contextRepositoryMock(&Domain::ContextRepository::associate).when(context1, taskToDrop4).exactly(1)); // WHEN a task is drop on the workday Domain::Task::Ptr taskToDrop5(new Domain::Task); taskRepositoryMock(&Domain::TaskRepository::update).when(taskToDrop5).thenReturn(new FakeJob(this)); data.reset(new QMimeData); data->setData(QStringLiteral("application/x-zanshin-object"), "object"); data->setProperty("objects", QVariant::fromValue(Domain::Artifact::List() << taskToDrop5)); model->dropMimeData(data.get(), Qt::MoveAction, -1, -1, workdayIndex); // THEN QCOMPARE(taskToDrop5->startDate().date(), Utils::DateTime::currentDateTime().date()); // WHEN two task are drop on the workday Domain::Task::Ptr taskToDrop6(new Domain::Task); Domain::Task::Ptr taskToDrop7(new Domain::Task); taskRepositoryMock(&Domain::TaskRepository::update).when(taskToDrop6).thenReturn(new FakeJob(this)); taskRepositoryMock(&Domain::TaskRepository::update).when(taskToDrop7).thenReturn(new FakeJob(this)); data.reset(new QMimeData); data->setData(QStringLiteral("application/x-zanshin-object"), "object"); data->setProperty("objects", QVariant::fromValue(Domain::Artifact::List() << taskToDrop6 << taskToDrop7)); model->dropMimeData(data.get(), Qt::MoveAction, -1, -1, workdayIndex); // THEN QCOMPARE(taskToDrop6->startDate().date(), Utils::DateTime::currentDateTime().date()); QCOMPARE(taskToDrop7->startDate().date(), Utils::DateTime::currentDateTime().date()); } void shouldCreateInboxPage() { // GIVEN - // Empty project provider - auto projectProvider = Domain::QueryResultProvider<Domain::Project::Ptr>::Ptr::create(); - auto projectResult = Domain::QueryResult<Domain::Project::Ptr>::create(projectProvider); + // Empty sources provider + auto sourceProvider = Domain::QueryResultProvider<Domain::DataSource::Ptr>::Ptr::create(); + auto sourceResult = Domain::QueryResult<Domain::DataSource::Ptr>::create(sourceProvider); // Empty context provider auto contextProvider = Domain::QueryResultProvider<Domain::Context::Ptr>::Ptr::create(); auto contextResult = Domain::QueryResult<Domain::Context::Ptr>::create(contextProvider); // context mocking Utils::MockObject<Domain::ContextQueries> contextQueriesMock; contextQueriesMock(&Domain::ContextQueries::findAll).when().thenReturn(contextResult); Utils::MockObject<Domain::ContextRepository> contextRepositoryMock; - // projects mocking - Utils::MockObject<Domain::ProjectQueries> projectQueriesMock; - projectQueriesMock(&Domain::ProjectQueries::findAll).when().thenReturn(projectResult); + // sources mocking + Utils::MockObject<Domain::DataSourceQueries> dataSourceQueriesMock; + dataSourceQueriesMock(&Domain::DataSourceQueries::findAllSelected).when().thenReturn(sourceResult); Utils::MockObject<Domain::ProjectRepository> projectRepositoryMock; - Presentation::AvailableTaskPagesModel pages(projectQueriesMock.getInstance(), + Presentation::AvailableTaskPagesModel pages(dataSourceQueriesMock.getInstance(), + Domain::ProjectQueries::Ptr(), projectRepositoryMock.getInstance(), contextQueriesMock.getInstance(), contextRepositoryMock.getInstance(), Domain::TaskQueries::Ptr(), Domain::TaskRepository::Ptr()); // WHEN QAbstractItemModel *model = pages.pageListModel(); // THEN const QModelIndex inboxIndex = model->index(0, 0); QObject *inboxPage = pages.createPageForIndex(inboxIndex); QVERIFY(qobject_cast<Presentation::TaskInboxPageModel*>(inboxPage)); } void shouldCreateWorkdayPage() { // GIVEN - // Empty project provider - auto projectProvider = Domain::QueryResultProvider<Domain::Project::Ptr>::Ptr::create(); - auto projectResult = Domain::QueryResult<Domain::Project::Ptr>::create(projectProvider); + // Empty sources provider + auto sourceProvider = Domain::QueryResultProvider<Domain::DataSource::Ptr>::Ptr::create(); + auto sourceResult = Domain::QueryResult<Domain::DataSource::Ptr>::create(sourceProvider); // Empty context provider auto contextProvider = Domain::QueryResultProvider<Domain::Context::Ptr>::Ptr::create(); auto contextResult = Domain::QueryResult<Domain::Context::Ptr>::create(contextProvider); // context mocking Utils::MockObject<Domain::ContextQueries> contextQueriesMock; contextQueriesMock(&Domain::ContextQueries::findAll).when().thenReturn(contextResult); Utils::MockObject<Domain::ContextRepository> contextRepositoryMock; - // projects mocking - Utils::MockObject<Domain::ProjectQueries> projectQueriesMock; - projectQueriesMock(&Domain::ProjectQueries::findAll).when().thenReturn(projectResult); + // sources mocking + Utils::MockObject<Domain::DataSourceQueries> dataSourceQueriesMock; + dataSourceQueriesMock(&Domain::DataSourceQueries::findAllSelected).when().thenReturn(sourceResult); Utils::MockObject<Domain::ProjectRepository> projectRepositoryMock; - Presentation::AvailableTaskPagesModel pages(projectQueriesMock.getInstance(), + Presentation::AvailableTaskPagesModel pages(dataSourceQueriesMock.getInstance(), + Domain::ProjectQueries::Ptr(), projectRepositoryMock.getInstance(), contextQueriesMock.getInstance(), contextRepositoryMock.getInstance(), Domain::TaskQueries::Ptr(), Domain::TaskRepository::Ptr()); // WHEN QAbstractItemModel *model = pages.pageListModel(); // THEN const QModelIndex workdayIndex = model->index(1, 0); QObject *workdayPage = pages.createPageForIndex(workdayIndex); QVERIFY(qobject_cast<Presentation::WorkdayPageModel*>(workdayPage)); } void shouldCreateProjectsPage() { // GIVEN + // One selected data source + auto source = Domain::DataSource::Ptr::create(); + source->setName("source"); + auto sourceProvider = Domain::QueryResultProvider<Domain::DataSource::Ptr>::Ptr::create(); + auto sourceResult = Domain::QueryResult<Domain::DataSource::Ptr>::create(sourceProvider); + sourceProvider->append(source); + // Two projects auto project1 = Domain::Project::Ptr::create(); - project1->setName(QStringLiteral("Project 1")); + project1->setName(QStringLiteral("Project 11")); auto project2 = Domain::Project::Ptr::create(); - project2->setName(QStringLiteral("Project 2")); + project2->setName(QStringLiteral("Project 12")); auto projectProvider = Domain::QueryResultProvider<Domain::Project::Ptr>::Ptr::create(); auto projectResult = Domain::QueryResult<Domain::Project::Ptr>::create(projectProvider); projectProvider->append(project1); projectProvider->append(project2); // No contexts auto contextProvider = Domain::QueryResultProvider<Domain::Context::Ptr>::Ptr::create(); auto contextResult = Domain::QueryResult<Domain::Context::Ptr>::create(contextProvider); - // projects mocking - Utils::MockObject<Domain::ProjectQueries> projectQueriesMock; - projectQueriesMock(&Domain::ProjectQueries::findAll).when().thenReturn(projectResult); + // data source mocking + Utils::MockObject<Domain::DataSourceQueries> dataSourceQueriesMock; + dataSourceQueriesMock(&Domain::DataSourceQueries::findAllSelected).when().thenReturn(sourceResult); + dataSourceQueriesMock(&Domain::DataSourceQueries::findProjects).when(source).thenReturn(projectResult); + // projects mocking Utils::MockObject<Domain::ProjectRepository> projectRepositoryMock; // contexts mocking Utils::MockObject<Domain::ContextQueries> contextQueriesMock; contextQueriesMock(&Domain::ContextQueries::findAll).when().thenReturn(contextResult); - Presentation::AvailableTaskPagesModel pages(projectQueriesMock.getInstance(), + Presentation::AvailableTaskPagesModel pages(dataSourceQueriesMock.getInstance(), + Domain::ProjectQueries::Ptr(), projectRepositoryMock.getInstance(), contextQueriesMock.getInstance(), Domain::ContextRepository::Ptr(), Domain::TaskQueries::Ptr(), Domain::TaskRepository::Ptr()); // WHEN QAbstractItemModel *model = pages.pageListModel(); // THEN const QModelIndex projectsIndex = model->index(2, 0); - const QModelIndex project1Index = model->index(0, 0, projectsIndex); - const QModelIndex project2Index = model->index(1, 0, projectsIndex); + const QModelIndex sourceIndex = model->index(0, 0, projectsIndex); + const QModelIndex project1Index = model->index(0, 0, sourceIndex); + const QModelIndex project2Index = model->index(1, 0, sourceIndex); QObject *projectsPage = pages.createPageForIndex(projectsIndex); + QObject *sourcesPage = pages.createPageForIndex(sourceIndex); QObject *project1Page = pages.createPageForIndex(project1Index); QObject *project2Page = pages.createPageForIndex(project2Index); QVERIFY(!projectsPage); + QVERIFY(!sourcesPage); QVERIFY(qobject_cast<Presentation::ProjectPageModel*>(project1Page)); QCOMPARE(qobject_cast<Presentation::ProjectPageModel*>(project1Page)->project(), project1); QVERIFY(qobject_cast<Presentation::ProjectPageModel*>(project2Page)); QCOMPARE(qobject_cast<Presentation::ProjectPageModel*>(project2Page)->project(), project2); } void shouldCreateContextsPage() { // GIVEN // Two contexts auto context1 = Domain::Context::Ptr::create(); context1->setName(QStringLiteral("context 1")); auto context2 = Domain::Context::Ptr::create(); context2->setName(QStringLiteral("context 2")); auto contextProvider = Domain::QueryResultProvider<Domain::Context::Ptr>::Ptr::create(); auto contextResult = Domain::QueryResult<Domain::Context::Ptr>::create(contextProvider); contextProvider->append(context1); contextProvider->append(context2); - // Empty Project provider - auto projectProvider = Domain::QueryResultProvider<Domain::Project::Ptr>::Ptr::create(); - auto projectResult = Domain::QueryResult<Domain::Project::Ptr>::create(projectProvider); + // Empty sources provider + auto sourceProvider = Domain::QueryResultProvider<Domain::DataSource::Ptr>::Ptr::create(); + auto sourceResult = Domain::QueryResult<Domain::DataSource::Ptr>::create(sourceProvider); // contexts mocking Utils::MockObject<Domain::ContextQueries> contextQueriesMock; contextQueriesMock(&Domain::ContextQueries::findAll).when().thenReturn(contextResult); Utils::MockObject<Domain::ContextRepository> contextRepositoryMock; - // projects mocking - Utils::MockObject<Domain::ProjectQueries> projectQueriesMock; - projectQueriesMock(&Domain::ProjectQueries::findAll).when().thenReturn(projectResult); + // sources mocking + Utils::MockObject<Domain::DataSourceQueries> dataSourceQueriesMock; + dataSourceQueriesMock(&Domain::DataSourceQueries::findAllSelected).when().thenReturn(sourceResult); + // projects mocking Utils::MockObject<Domain::ProjectRepository> projectRepositoryMock; - Presentation::AvailableTaskPagesModel pages(projectQueriesMock.getInstance(), + Presentation::AvailableTaskPagesModel pages(dataSourceQueriesMock.getInstance(), + Domain::ProjectQueries::Ptr(), projectRepositoryMock.getInstance(), contextQueriesMock.getInstance(), contextRepositoryMock.getInstance(), Domain::TaskQueries::Ptr(), Domain::TaskRepository::Ptr()); // WHEN QAbstractItemModel *model = pages.pageListModel(); // THEN const QModelIndex contextsIndex = model->index(3, 0); const QModelIndex context1Index = model->index(0, 0, contextsIndex); const QModelIndex context2Index = model->index(1, 0, contextsIndex); QObject *contextsPage = pages.createPageForIndex(contextsIndex); QObject *context1Page = pages.createPageForIndex(context1Index); QObject *context2Page = pages.createPageForIndex(context2Index); QVERIFY(!contextsPage); QVERIFY(qobject_cast<Presentation::ContextPageModel*>(context1Page)); QCOMPARE(qobject_cast<Presentation::ContextPageModel*>(context1Page)->context(), context1); QVERIFY(qobject_cast<Presentation::ContextPageModel*>(context2Page)); QCOMPARE(qobject_cast<Presentation::ContextPageModel*>(context2Page)->context(), context2); } void shouldAddProjects() { // GIVEN auto source = Domain::DataSource::Ptr::create(); Utils::MockObject<Domain::ProjectRepository> projectRepositoryMock; projectRepositoryMock(&Domain::ProjectRepository::create).when(any<Domain::Project::Ptr>(), any<Domain::DataSource::Ptr>()) .thenReturn(new FakeJob(this)); - Presentation::AvailableTaskPagesModel pages(Domain::ProjectQueries::Ptr(), + Presentation::AvailableTaskPagesModel pages(Domain::DataSourceQueries::Ptr(), + Domain::ProjectQueries::Ptr(), projectRepositoryMock.getInstance(), Domain::ContextQueries::Ptr(), Domain::ContextRepository::Ptr(), Domain::TaskQueries::Ptr(), Domain::TaskRepository::Ptr()); // WHEN pages.addProject(QStringLiteral("Foo"), source); // THEN QVERIFY(projectRepositoryMock(&Domain::ProjectRepository::create).when(any<Domain::Project::Ptr>(), any<Domain::DataSource::Ptr>()) .exactly(1)); } void shouldGetAnErrorMessageWhenAddProjectFailed() { // GIVEN auto source = Domain::DataSource::Ptr::create(); source->setName(QStringLiteral("Source1")); Utils::MockObject<Domain::ProjectRepository> projectRepositoryMock; auto job = new FakeJob(this); job->setExpectedError(KJob::KilledJobError, QStringLiteral("Foo")); projectRepositoryMock(&Domain::ProjectRepository::create).when(any<Domain::Project::Ptr>(), any<Domain::DataSource::Ptr>()) .thenReturn(job); - Presentation::AvailableTaskPagesModel pages(Domain::ProjectQueries::Ptr(), + Presentation::AvailableTaskPagesModel pages(Domain::DataSourceQueries::Ptr(), + Domain::ProjectQueries::Ptr(), projectRepositoryMock.getInstance(), Domain::ContextQueries::Ptr(), Domain::ContextRepository::Ptr(), Domain::TaskQueries::Ptr(), Domain::TaskRepository::Ptr()); FakeErrorHandler errorHandler; pages.setErrorHandler(&errorHandler); // WHEN pages.addProject(QStringLiteral("Foo"), source); // THEN QTest::qWait(150); QCOMPARE(errorHandler.m_message, QStringLiteral("Cannot add project Foo in dataSource Source1: Foo")); } void shouldAddContexts() { // GIVEN Utils::MockObject<Domain::ContextRepository> contextRepositoryMock; contextRepositoryMock(&Domain::ContextRepository::create).when(any<Domain::Context::Ptr>()) .thenReturn(new FakeJob(this)); - Presentation::AvailableTaskPagesModel pages(Domain::ProjectQueries::Ptr(), + Presentation::AvailableTaskPagesModel pages(Domain::DataSourceQueries::Ptr(), + Domain::ProjectQueries::Ptr(), Domain::ProjectRepository::Ptr(), Domain::ContextQueries::Ptr(), contextRepositoryMock.getInstance(), Domain::TaskQueries::Ptr(), Domain::TaskRepository::Ptr()); // WHEN pages.addContext(QStringLiteral("Foo")); // THEN QVERIFY(contextRepositoryMock(&Domain::ContextRepository::create).when(any<Domain::Context::Ptr>()) .exactly(1)); } void shouldGetAnErrorMessageWhenAddContextFailed() { // GIVEN Utils::MockObject<Domain::ContextRepository> contextRepositoryMock; auto job = new FakeJob(this); job->setExpectedError(KJob::KilledJobError, QStringLiteral("Foo")); contextRepositoryMock(&Domain::ContextRepository::create).when(any<Domain::Context::Ptr>()) .thenReturn(job); - Presentation::AvailableTaskPagesModel pages(Domain::ProjectQueries::Ptr(), + Presentation::AvailableTaskPagesModel pages(Domain::DataSourceQueries::Ptr(), + Domain::ProjectQueries::Ptr(), Domain::ProjectRepository::Ptr(), Domain::ContextQueries::Ptr(), contextRepositoryMock.getInstance(), Domain::TaskQueries::Ptr(), Domain::TaskRepository::Ptr()); FakeErrorHandler errorHandler; pages.setErrorHandler(&errorHandler); // WHEN pages.addContext(QStringLiteral("Foo")); // THEN QTest::qWait(150); QCOMPARE(errorHandler.m_message, QStringLiteral("Cannot add context Foo: Foo")); } void shouldRemoveProject() { // GIVEN + // One selected data source + auto source = Domain::DataSource::Ptr::create(); + source->setName("source"); + auto sourceProvider = Domain::QueryResultProvider<Domain::DataSource::Ptr>::Ptr::create(); + auto sourceResult = Domain::QueryResult<Domain::DataSource::Ptr>::create(sourceProvider); + sourceProvider->append(source); + // Two projects auto project1 = Domain::Project::Ptr::create(); project1->setName(QStringLiteral("Project 1")); auto project2 = Domain::Project::Ptr::create(); project2->setName(QStringLiteral("Project 2")); auto projectProvider = Domain::QueryResultProvider<Domain::Project::Ptr>::Ptr::create(); auto projectResult = Domain::QueryResult<Domain::Project::Ptr>::create(projectProvider); projectProvider->append(project1); projectProvider->append(project2); // No contexts auto contextProvider = Domain::QueryResultProvider<Domain::Context::Ptr>::Ptr::create(); auto contextResult = Domain::QueryResult<Domain::Context::Ptr>::create(contextProvider); - Utils::MockObject<Domain::ProjectQueries> projectQueriesMock; - projectQueriesMock(&Domain::ProjectQueries::findAll).when().thenReturn(projectResult); + // data source mocking + Utils::MockObject<Domain::DataSourceQueries> dataSourceQueriesMock; + dataSourceQueriesMock(&Domain::DataSourceQueries::findAllSelected).when().thenReturn(sourceResult); + dataSourceQueriesMock(&Domain::DataSourceQueries::findProjects).when(source).thenReturn(projectResult); + + // projects mocking + Utils::MockObject<Domain::ProjectRepository> projectRepositoryMock; + // contexts mocking Utils::MockObject<Domain::ContextQueries> contextQueriesMock; contextQueriesMock(&Domain::ContextQueries::findAll).when().thenReturn(contextResult); - Utils::MockObject<Domain::ProjectRepository> projectRepositoryMock; - - Presentation::AvailableTaskPagesModel pages(projectQueriesMock.getInstance(), + Presentation::AvailableTaskPagesModel pages(dataSourceQueriesMock.getInstance(), + Domain::ProjectQueries::Ptr(), projectRepositoryMock.getInstance(), contextQueriesMock.getInstance(), Domain::ContextRepository::Ptr(), Domain::TaskQueries::Ptr(), Domain::TaskRepository::Ptr()); QAbstractItemModel *model = pages.pageListModel(); const QModelIndex projectsIndex = model->index(2, 0); - const QModelIndex project1Index = model->index(0, 0, projectsIndex); + const QModelIndex sourceIndex = model->index(0, 0, projectsIndex); + const QModelIndex project1Index = model->index(0, 0, sourceIndex); projectRepositoryMock(&Domain::ProjectRepository::remove).when(project1).thenReturn(new FakeJob(this)); // WHEN pages.removeItem(project1Index); // THEN QVERIFY(projectRepositoryMock(&Domain::ProjectRepository::remove).when(project1).exactly(1)); } void shouldGetAnErrorMessageWhenRemoveProjectFailed() { // GIVEN + // One selected data source + auto source = Domain::DataSource::Ptr::create(); + source->setName("source"); + auto sourceProvider = Domain::QueryResultProvider<Domain::DataSource::Ptr>::Ptr::create(); + auto sourceResult = Domain::QueryResult<Domain::DataSource::Ptr>::create(sourceProvider); + sourceProvider->append(source); + // Two projects auto project1 = Domain::Project::Ptr::create(); project1->setName(QStringLiteral("Project 1")); auto project2 = Domain::Project::Ptr::create(); project2->setName(QStringLiteral("Project 2")); auto projectProvider = Domain::QueryResultProvider<Domain::Project::Ptr>::Ptr::create(); auto projectResult = Domain::QueryResult<Domain::Project::Ptr>::create(projectProvider); projectProvider->append(project1); projectProvider->append(project2); // No contexts auto contextProvider = Domain::QueryResultProvider<Domain::Context::Ptr>::Ptr::create(); auto contextResult = Domain::QueryResult<Domain::Context::Ptr>::create(contextProvider); - Utils::MockObject<Domain::ProjectQueries> projectQueriesMock; - projectQueriesMock(&Domain::ProjectQueries::findAll).when().thenReturn(projectResult); + // data source mocking + Utils::MockObject<Domain::DataSourceQueries> dataSourceQueriesMock; + dataSourceQueriesMock(&Domain::DataSourceQueries::findAllSelected).when().thenReturn(sourceResult); + dataSourceQueriesMock(&Domain::DataSourceQueries::findProjects).when(source).thenReturn(projectResult); + // projects mocking + Utils::MockObject<Domain::ProjectRepository> projectRepositoryMock; + + // contexts mocking Utils::MockObject<Domain::ContextQueries> contextQueriesMock; contextQueriesMock(&Domain::ContextQueries::findAll).when().thenReturn(contextResult); - Utils::MockObject<Domain::ProjectRepository> projectRepositoryMock; - - Presentation::AvailableTaskPagesModel pages(projectQueriesMock.getInstance(), + Presentation::AvailableTaskPagesModel pages(dataSourceQueriesMock.getInstance(), + Domain::ProjectQueries::Ptr(), projectRepositoryMock.getInstance(), contextQueriesMock.getInstance(), Domain::ContextRepository::Ptr(), Domain::TaskQueries::Ptr(), Domain::TaskRepository::Ptr()); FakeErrorHandler errorHandler; pages.setErrorHandler(&errorHandler); QAbstractItemModel *model = pages.pageListModel(); const QModelIndex projectsIndex = model->index(2, 0); - const QModelIndex project1Index = model->index(0, 0, projectsIndex); + const QModelIndex sourceIndex = model->index(0, 0, projectsIndex); + const QModelIndex project1Index = model->index(0, 0, sourceIndex); auto job = new FakeJob(this); job->setExpectedError(KJob::KilledJobError, QStringLiteral("Foo")); projectRepositoryMock(&Domain::ProjectRepository::remove).when(project1).thenReturn(job); // WHEN pages.removeItem(project1Index); // THEN QTest::qWait(150); QCOMPARE(errorHandler.m_message, QStringLiteral("Cannot remove project Project 1: Foo")); } void shouldRemoveContext() { // GIVEN // Two contexts auto context1 = Domain::Context::Ptr::create(); context1->setName(QStringLiteral("context 1")); auto contextProvider = Domain::QueryResultProvider<Domain::Context::Ptr>::Ptr::create(); auto contextResult = Domain::QueryResult<Domain::Context::Ptr>::create(contextProvider); contextProvider->append(context1); - // empty projects - auto projectProvider = Domain::QueryResultProvider<Domain::Project::Ptr>::Ptr::create(); - auto projectResult = Domain::QueryResult<Domain::Project::Ptr>::create(projectProvider); + + // Empty sources provider + auto sourceProvider = Domain::QueryResultProvider<Domain::DataSource::Ptr>::Ptr::create(); + auto sourceResult = Domain::QueryResult<Domain::DataSource::Ptr>::create(sourceProvider); // contexts mocking Utils::MockObject<Domain::ContextQueries> contextQueriesMock; contextQueriesMock(&Domain::ContextQueries::findAll).when().thenReturn(contextResult); Utils::MockObject<Domain::ContextRepository> contextRepositoryMock; - // projects mocking - Utils::MockObject<Domain::ProjectQueries> projectQueriesMock; - projectQueriesMock(&Domain::ProjectQueries::findAll).when().thenReturn(projectResult); + // sources mocking + Utils::MockObject<Domain::DataSourceQueries> dataSourceQueriesMock; + dataSourceQueriesMock(&Domain::DataSourceQueries::findAllSelected).when().thenReturn(sourceResult); - Presentation::AvailableTaskPagesModel pages(projectQueriesMock.getInstance(), + Presentation::AvailableTaskPagesModel pages(dataSourceQueriesMock.getInstance(), + Domain::ProjectQueries::Ptr(), Domain::ProjectRepository::Ptr(), contextQueriesMock.getInstance(), contextRepositoryMock.getInstance(), Domain::TaskQueries::Ptr(), Domain::TaskRepository::Ptr()); QAbstractItemModel *model = pages.pageListModel(); const QModelIndex contextsIndex = model->index(3, 0); const QModelIndex context1Index = model->index(0, 0, contextsIndex); contextRepositoryMock(&Domain::ContextRepository::remove).when(context1).thenReturn(new FakeJob(this)); // WHEN pages.removeItem(context1Index); // THEN QVERIFY(contextRepositoryMock(&Domain::ContextRepository::remove).when(context1).exactly(1)); } void shouldGetAnErrorMessageWhenRemoveContextFailed() { // GIVEN // Two contexts auto context1 = Domain::Context::Ptr::create(); context1->setName(QStringLiteral("context 1")); auto contextProvider = Domain::QueryResultProvider<Domain::Context::Ptr>::Ptr::create(); auto contextResult = Domain::QueryResult<Domain::Context::Ptr>::create(contextProvider); contextProvider->append(context1); - // empty projects - auto projectProvider = Domain::QueryResultProvider<Domain::Project::Ptr>::Ptr::create(); - auto projectResult = Domain::QueryResult<Domain::Project::Ptr>::create(projectProvider); + + // Empty sources provider + auto sourceProvider = Domain::QueryResultProvider<Domain::DataSource::Ptr>::Ptr::create(); + auto sourceResult = Domain::QueryResult<Domain::DataSource::Ptr>::create(sourceProvider); // contexts mocking Utils::MockObject<Domain::ContextQueries> contextQueriesMock; contextQueriesMock(&Domain::ContextQueries::findAll).when().thenReturn(contextResult); Utils::MockObject<Domain::ContextRepository> contextRepositoryMock; - // projects mocking - Utils::MockObject<Domain::ProjectQueries> projectQueriesMock; - projectQueriesMock(&Domain::ProjectQueries::findAll).when().thenReturn(projectResult); + // sources mocking + Utils::MockObject<Domain::DataSourceQueries> dataSourceQueriesMock; + dataSourceQueriesMock(&Domain::DataSourceQueries::findAllSelected).when().thenReturn(sourceResult); - Presentation::AvailableTaskPagesModel pages(projectQueriesMock.getInstance(), + Presentation::AvailableTaskPagesModel pages(dataSourceQueriesMock.getInstance(), + Domain::ProjectQueries::Ptr(), Domain::ProjectRepository::Ptr(), contextQueriesMock.getInstance(), contextRepositoryMock.getInstance(), Domain::TaskQueries::Ptr(), Domain::TaskRepository::Ptr()); FakeErrorHandler errorHandler; pages.setErrorHandler(&errorHandler); QAbstractItemModel *model = pages.pageListModel(); const QModelIndex contextsIndex = model->index(3, 0); const QModelIndex context1Index = model->index(0, 0, contextsIndex); auto job = new FakeJob(this); job->setExpectedError(KJob::KilledJobError, QStringLiteral("Foo")); contextRepositoryMock(&Domain::ContextRepository::remove).when(context1).thenReturn(job); // WHEN pages.removeItem(context1Index); // THEN QTest::qWait(150); QCOMPARE(errorHandler.m_message, QStringLiteral("Cannot remove context context 1: Foo")); } void shouldGetAnErrorMessageWhenUpdateProjectFailed() { // GIVEN - // Two projects + // One selected data source + auto source = Domain::DataSource::Ptr::create(); + source->setName("source1"); + auto sourceProvider = Domain::QueryResultProvider<Domain::DataSource::Ptr>::Ptr::create(); + auto sourceResult = Domain::QueryResult<Domain::DataSource::Ptr>::create(sourceProvider); + sourceProvider->append(source); + + // Two projects under the source auto project1 = Domain::Project::Ptr::create(); project1->setName(QStringLiteral("Project 1")); auto project2 = Domain::Project::Ptr::create(); project2->setName(QStringLiteral("Project 2")); auto projectProvider = Domain::QueryResultProvider<Domain::Project::Ptr>::Ptr::create(); auto projectResult = Domain::QueryResult<Domain::Project::Ptr>::create(projectProvider); projectProvider->append(project1); projectProvider->append(project2); // Two contexts auto context1 = Domain::Context::Ptr::create(); context1->setName(QStringLiteral("context 1")); auto context2 = Domain::Context::Ptr::create(); context2->setName(QStringLiteral("context 2")); auto contextProvider = Domain::QueryResultProvider<Domain::Context::Ptr>::Ptr::create(); auto contextResult = Domain::QueryResult<Domain::Context::Ptr>::create(contextProvider); contextProvider->append(context1); contextProvider->append(context2); - // Two artifacts (used for dropping later on) - Domain::Artifact::Ptr taskToDrop(new Domain::Task); - Domain::Artifact::Ptr noteToDrop(new Domain::Note); - - Utils::MockObject<Domain::ProjectQueries> projectQueriesMock; - projectQueriesMock(&Domain::ProjectQueries::findAll).when().thenReturn(projectResult); + Utils::MockObject<Domain::DataSourceQueries> dataSourceQueriesMock; + dataSourceQueriesMock(&Domain::DataSourceQueries::findAllSelected).when().thenReturn(sourceResult); + dataSourceQueriesMock(&Domain::DataSourceQueries::findProjects).when(source).thenReturn(projectResult); Utils::MockObject<Domain::ProjectRepository> projectRepositoryMock; - Utils::MockObject<Domain::TaskRepository> taskRepositoryMock; Utils::MockObject<Domain::ContextQueries> contextQueriesMock; contextQueriesMock(&Domain::ContextQueries::findAll).when().thenReturn(contextResult); Utils::MockObject<Domain::ContextRepository> contextRepositoryMock; - Presentation::AvailableTaskPagesModel pages(projectQueriesMock.getInstance(), + Presentation::AvailableTaskPagesModel pages(dataSourceQueriesMock.getInstance(), + Domain::ProjectQueries::Ptr(), projectRepositoryMock.getInstance(), contextQueriesMock.getInstance(), contextRepositoryMock.getInstance(), Domain::TaskQueries::Ptr(), - taskRepositoryMock.getInstance()); + Domain::TaskRepository::Ptr()); FakeErrorHandler errorHandler; pages.setErrorHandler(&errorHandler); QAbstractItemModel *model = pages.pageListModel(); const QModelIndex projectsIndex = model->index(2, 0); - const QModelIndex project1Index = model->index(0, 0, projectsIndex); + const QModelIndex sourceIndex = model->index(0, 0, projectsIndex); + const QModelIndex project1Index = model->index(0, 0, sourceIndex); // WHEN auto job = new FakeJob(this); job->setExpectedError(KJob::KilledJobError, QStringLiteral("Foo")); projectRepositoryMock(&Domain::ProjectRepository::update).when(project1).thenReturn(job); QVERIFY(model->setData(project1Index, "New Project 1")); // THEN QTest::qWait(150); QCOMPARE(errorHandler.m_message, QStringLiteral("Cannot modify project Project 1: Foo")); } void shouldGetAnErrorMessageWhenUpdateContextFailed() { // GIVEN - // Two projects + // One selected data source + auto source = Domain::DataSource::Ptr::create(); + source->setName("source1"); + auto sourceProvider = Domain::QueryResultProvider<Domain::DataSource::Ptr>::Ptr::create(); + auto sourceResult = Domain::QueryResult<Domain::DataSource::Ptr>::create(sourceProvider); + sourceProvider->append(source); + + // Two projects under the source auto project1 = Domain::Project::Ptr::create(); project1->setName(QStringLiteral("Project 1")); auto project2 = Domain::Project::Ptr::create(); project2->setName(QStringLiteral("Project 2")); auto projectProvider = Domain::QueryResultProvider<Domain::Project::Ptr>::Ptr::create(); auto projectResult = Domain::QueryResult<Domain::Project::Ptr>::create(projectProvider); projectProvider->append(project1); projectProvider->append(project2); // Two contexts auto context1 = Domain::Context::Ptr::create(); context1->setName(QStringLiteral("context 1")); auto context2 = Domain::Context::Ptr::create(); context2->setName(QStringLiteral("context 2")); auto contextProvider = Domain::QueryResultProvider<Domain::Context::Ptr>::Ptr::create(); auto contextResult = Domain::QueryResult<Domain::Context::Ptr>::create(contextProvider); contextProvider->append(context1); contextProvider->append(context2); - // Two artifacts (used for dropping later on) - Domain::Artifact::Ptr taskToDrop(new Domain::Task); - Domain::Artifact::Ptr noteToDrop(new Domain::Note); - - Utils::MockObject<Domain::ProjectQueries> projectQueriesMock; - projectQueriesMock(&Domain::ProjectQueries::findAll).when().thenReturn(projectResult); + Utils::MockObject<Domain::DataSourceQueries> dataSourceQueriesMock; + dataSourceQueriesMock(&Domain::DataSourceQueries::findAllSelected).when().thenReturn(sourceResult); + dataSourceQueriesMock(&Domain::DataSourceQueries::findProjects).when(source).thenReturn(projectResult); Utils::MockObject<Domain::ProjectRepository> projectRepositoryMock; - Utils::MockObject<Domain::TaskRepository> taskRepositoryMock; Utils::MockObject<Domain::ContextQueries> contextQueriesMock; contextQueriesMock(&Domain::ContextQueries::findAll).when().thenReturn(contextResult); Utils::MockObject<Domain::ContextRepository> contextRepositoryMock; - Presentation::AvailableTaskPagesModel pages(projectQueriesMock.getInstance(), + Presentation::AvailableTaskPagesModel pages(dataSourceQueriesMock.getInstance(), + Domain::ProjectQueries::Ptr(), projectRepositoryMock.getInstance(), contextQueriesMock.getInstance(), contextRepositoryMock.getInstance(), Domain::TaskQueries::Ptr(), - taskRepositoryMock.getInstance()); + Domain::TaskRepository::Ptr()); FakeErrorHandler errorHandler; pages.setErrorHandler(&errorHandler); QAbstractItemModel *model = pages.pageListModel(); const QModelIndex contextsIndex = model->index(3, 0); const QModelIndex context1Index = model->index(0, 0, contextsIndex); // WHEN auto job = new FakeJob(this); job->setExpectedError(KJob::KilledJobError, QStringLiteral("Foo")); contextRepositoryMock(&Domain::ContextRepository::update).when(context1).thenReturn(job); QVERIFY(model->setData(context1Index, "New Context 1")); // THEN QTest::qWait(150); QCOMPARE(errorHandler.m_message, QStringLiteral("Cannot modify context context 1: Foo")); } void shouldGetAnErrorMessageWhenAssociateProjectFailed() { // GIVEN - // Two projects + // One selected data source + auto source = Domain::DataSource::Ptr::create(); + source->setName("source1"); + auto sourceProvider = Domain::QueryResultProvider<Domain::DataSource::Ptr>::Ptr::create(); + auto sourceResult = Domain::QueryResult<Domain::DataSource::Ptr>::create(sourceProvider); + sourceProvider->append(source); + + // Two projects under the source auto project1 = Domain::Project::Ptr::create(); project1->setName(QStringLiteral("Project 1")); auto project2 = Domain::Project::Ptr::create(); project2->setName(QStringLiteral("Project 2")); auto projectProvider = Domain::QueryResultProvider<Domain::Project::Ptr>::Ptr::create(); auto projectResult = Domain::QueryResult<Domain::Project::Ptr>::create(projectProvider); projectProvider->append(project1); projectProvider->append(project2); // Two contexts auto context1 = Domain::Context::Ptr::create(); context1->setName(QStringLiteral("context 1")); auto context2 = Domain::Context::Ptr::create(); context2->setName(QStringLiteral("context 2")); auto contextProvider = Domain::QueryResultProvider<Domain::Context::Ptr>::Ptr::create(); auto contextResult = Domain::QueryResult<Domain::Context::Ptr>::create(contextProvider); contextProvider->append(context1); contextProvider->append(context2); - // Two artifacts (used for dropping later on) + // One task (used for dropping later on) Domain::Artifact::Ptr taskToDrop(new Domain::Task); taskToDrop->setTitle(QStringLiteral("taskDropped")); - Domain::Artifact::Ptr noteToDrop(new Domain::Note); - Utils::MockObject<Domain::ProjectQueries> projectQueriesMock; - projectQueriesMock(&Domain::ProjectQueries::findAll).when().thenReturn(projectResult); + Utils::MockObject<Domain::DataSourceQueries> dataSourceQueriesMock; + dataSourceQueriesMock(&Domain::DataSourceQueries::findAllSelected).when().thenReturn(sourceResult); + dataSourceQueriesMock(&Domain::DataSourceQueries::findProjects).when(source).thenReturn(projectResult); Utils::MockObject<Domain::ProjectRepository> projectRepositoryMock; - Utils::MockObject<Domain::TaskRepository> taskRepositoryMock; Utils::MockObject<Domain::ContextQueries> contextQueriesMock; contextQueriesMock(&Domain::ContextQueries::findAll).when().thenReturn(contextResult); Utils::MockObject<Domain::ContextRepository> contextRepositoryMock; - Presentation::AvailableTaskPagesModel pages(projectQueriesMock.getInstance(), + Utils::MockObject<Domain::TaskRepository> taskRepositoryMock; + + Presentation::AvailableTaskPagesModel pages(dataSourceQueriesMock.getInstance(), + Domain::ProjectQueries::Ptr(), projectRepositoryMock.getInstance(), contextQueriesMock.getInstance(), contextRepositoryMock.getInstance(), Domain::TaskQueries::Ptr(), taskRepositoryMock.getInstance()); FakeErrorHandler errorHandler; pages.setErrorHandler(&errorHandler); QAbstractItemModel *model = pages.pageListModel(); const QModelIndex projectsIndex = model->index(2, 0); - const QModelIndex project1Index = model->index(0, 0, projectsIndex); + const QModelIndex sourceIndex = model->index(0, 0, projectsIndex); + const QModelIndex project1Index = model->index(0, 0, sourceIndex); // WHEN auto job = new FakeJob(this); job->setExpectedError(KJob::KilledJobError, QStringLiteral("Foo")); projectRepositoryMock(&Domain::ProjectRepository::associate).when(project1, taskToDrop).thenReturn(job); auto data = std::make_unique<QMimeData>(); data->setData(QStringLiteral("application/x-zanshin-object"), "object"); data->setProperty("objects", QVariant::fromValue(Domain::Artifact::List() << taskToDrop)); model->dropMimeData(data.get(), Qt::MoveAction, -1, -1, project1Index); // THEN QTest::qWait(150); QCOMPARE(errorHandler.m_message, QStringLiteral("Cannot add taskDropped to project Project 1: Foo")); } void shouldGetAnErrorMessageWhenAssociateContextFailed() { // GIVEN - // Two projects + // One selected data source + auto source = Domain::DataSource::Ptr::create(); + source->setName("source1"); + auto sourceProvider = Domain::QueryResultProvider<Domain::DataSource::Ptr>::Ptr::create(); + auto sourceResult = Domain::QueryResult<Domain::DataSource::Ptr>::create(sourceProvider); + sourceProvider->append(source); + + // Two projects under the source auto project1 = Domain::Project::Ptr::create(); project1->setName(QStringLiteral("Project 1")); auto project2 = Domain::Project::Ptr::create(); project2->setName(QStringLiteral("Project 2")); auto projectProvider = Domain::QueryResultProvider<Domain::Project::Ptr>::Ptr::create(); auto projectResult = Domain::QueryResult<Domain::Project::Ptr>::create(projectProvider); projectProvider->append(project1); projectProvider->append(project2); // Two contexts auto context1 = Domain::Context::Ptr::create(); context1->setName(QStringLiteral("context 1")); auto context2 = Domain::Context::Ptr::create(); context2->setName(QStringLiteral("context 2")); auto contextProvider = Domain::QueryResultProvider<Domain::Context::Ptr>::Ptr::create(); auto contextResult = Domain::QueryResult<Domain::Context::Ptr>::create(contextProvider); contextProvider->append(context1); contextProvider->append(context2); - // Two artifacts (used for dropping later on) + // One task (used for dropping later on) Domain::Artifact::Ptr taskToDrop(new Domain::Task); taskToDrop->setTitle(QStringLiteral("taskDropped")); - Utils::MockObject<Domain::ProjectQueries> projectQueriesMock; - projectQueriesMock(&Domain::ProjectQueries::findAll).when().thenReturn(projectResult); + Utils::MockObject<Domain::DataSourceQueries> dataSourceQueriesMock; + dataSourceQueriesMock(&Domain::DataSourceQueries::findAllSelected).when().thenReturn(sourceResult); + dataSourceQueriesMock(&Domain::DataSourceQueries::findProjects).when(source).thenReturn(projectResult); Utils::MockObject<Domain::ProjectRepository> projectRepositoryMock; - Utils::MockObject<Domain::TaskRepository> taskRepositoryMock; Utils::MockObject<Domain::ContextQueries> contextQueriesMock; contextQueriesMock(&Domain::ContextQueries::findAll).when().thenReturn(contextResult); Utils::MockObject<Domain::ContextRepository> contextRepositoryMock; - Presentation::AvailableTaskPagesModel pages(projectQueriesMock.getInstance(), + Utils::MockObject<Domain::TaskRepository> taskRepositoryMock; + + Presentation::AvailableTaskPagesModel pages(dataSourceQueriesMock.getInstance(), + Domain::ProjectQueries::Ptr(), projectRepositoryMock.getInstance(), contextQueriesMock.getInstance(), contextRepositoryMock.getInstance(), Domain::TaskQueries::Ptr(), taskRepositoryMock.getInstance()); FakeErrorHandler errorHandler; pages.setErrorHandler(&errorHandler); QAbstractItemModel *model = pages.pageListModel(); const QModelIndex contextsIndex = model->index(3, 0); const QModelIndex context1Index = model->index(0, 0, contextsIndex); // WHEN auto job = new FakeJob(this); job->setExpectedError(KJob::KilledJobError, QStringLiteral("Foo")); contextRepositoryMock(&Domain::ContextRepository::associate).when(context1, taskToDrop.objectCast<Domain::Task>()).thenReturn(job); auto data = std::make_unique<QMimeData>(); data->setData(QStringLiteral("application/x-zanshin-object"), "object"); data->setProperty("objects", QVariant::fromValue(Domain::Artifact::List() << taskToDrop)); model->dropMimeData(data.get(), Qt::MoveAction, -1, -1, context1Index); // THEN QTest::qWait(150); QCOMPARE(errorHandler.m_message, QStringLiteral("Cannot add taskDropped to context context 1: Foo")); } void shouldGetAnErrorMessageWhenDissociateFailed() { // GIVEN - // No project - auto projectProvider = Domain::QueryResultProvider<Domain::Project::Ptr>::Ptr::create(); - auto projectResult = Domain::QueryResult<Domain::Project::Ptr>::create(projectProvider); + // Empty source provider + auto sourceProvider = Domain::QueryResultProvider<Domain::DataSource::Ptr>::Ptr::create(); + auto sourceResult = Domain::QueryResult<Domain::DataSource::Ptr>::create(sourceProvider); - // No context + // Empty context provider auto contextProvider = Domain::QueryResultProvider<Domain::Context::Ptr>::Ptr::create(); auto contextResult = Domain::QueryResult<Domain::Context::Ptr>::create(contextProvider); - // Two artifacts (used for dropping later on) + // One task (used for dropping later on) Domain::Artifact::Ptr taskToDrop(new Domain::Task); taskToDrop->setTitle(QStringLiteral("taskDropped")); - Utils::MockObject<Domain::ProjectQueries> projectQueriesMock; - projectQueriesMock(&Domain::ProjectQueries::findAll).when().thenReturn(projectResult); - - Utils::MockObject<Domain::ProjectRepository> projectRepositoryMock; - Utils::MockObject<Domain::TaskRepository> taskRepositoryMock; - + // context mocking Utils::MockObject<Domain::ContextQueries> contextQueriesMock; contextQueriesMock(&Domain::ContextQueries::findAll).when().thenReturn(contextResult); Utils::MockObject<Domain::ContextRepository> contextRepositoryMock; - Presentation::AvailableTaskPagesModel pages(projectQueriesMock.getInstance(), + // sources mocking + Utils::MockObject<Domain::DataSourceQueries> dataSourceQueriesMock; + dataSourceQueriesMock(&Domain::DataSourceQueries::findAllSelected).when().thenReturn(sourceResult); + + Utils::MockObject<Domain::ProjectRepository> projectRepositoryMock; + Utils::MockObject<Domain::TaskRepository> taskRepositoryMock; + + Presentation::AvailableTaskPagesModel pages(dataSourceQueriesMock.getInstance(), + Domain::ProjectQueries::Ptr(), projectRepositoryMock.getInstance(), contextQueriesMock.getInstance(), contextRepositoryMock.getInstance(), Domain::TaskQueries::Ptr(), taskRepositoryMock.getInstance()); FakeErrorHandler errorHandler; pages.setErrorHandler(&errorHandler); QAbstractItemModel *model = pages.pageListModel(); const QModelIndex inboxIndex = model->index(0, 0); // WHEN auto job = new FakeJob(this); job->setExpectedError(KJob::KilledJobError, QStringLiteral("Foo")); projectRepositoryMock(&Domain::ProjectRepository::dissociate).when(taskToDrop).thenReturn(job); taskRepositoryMock(&Domain::TaskRepository::dissociateAll).when(taskToDrop.objectCast<Domain::Task>()).thenReturn(new FakeJob(this)); auto data = std::make_unique<QMimeData>(); data->setData(QStringLiteral("application/x-zanshin-object"), "object"); data->setProperty("objects", QVariant::fromValue(Domain::Artifact::List() << taskToDrop)); model->dropMimeData(data.get(), Qt::MoveAction, -1, -1, inboxIndex); // THEN QTest::qWait(150); QCOMPARE(errorHandler.m_message, QStringLiteral("Cannot move taskDropped to Inbox: Foo")); } }; ZANSHIN_TEST_MAIN(AvailableTaskPagesModelTest) #include "availabletaskpagesmodeltest.moc"