diff --git a/src/presentation/availablenotepagesmodel.cpp b/src/presentation/availablenotepagesmodel.cpp index 4c2a244c..6a7e8b73 100644 --- a/src/presentation/availablenotepagesmodel.cpp +++ b/src/presentation/availablenotepagesmodel.cpp @@ -1,241 +1,256 @@ /* This file is part of Zanshin Copyright 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 "availablenotepagesmodel.h" #include #include #include "presentation/availablepagessortfilterproxymodel.h" #include "presentation/noteinboxpagemodel.h" #include "presentation/querytreemodel.h" #include "presentation/tagpagemodel.h" #include "utils/jobhandler.h" using namespace Presentation; AvailableNotePagesModel::AvailableNotePagesModel(const Domain::NoteQueries::Ptr ¬eQueries, const Domain::NoteRepository::Ptr ¬eRepository, const Domain::TagQueries::Ptr &tagQueries, const Domain::TagRepository::Ptr &tagRepository, QObject *parent) : AvailablePagesModelInterface(parent), m_pageListModel(Q_NULLPTR), m_sortProxyModel(Q_NULLPTR), m_noteQueries(noteQueries), m_noteRepository(noteRepository), m_tagQueries(tagQueries), m_tagRepository(tagRepository) { } QAbstractItemModel *AvailableNotePagesModel::pageListModel() { if (!m_pageListModel) m_pageListModel = createPageListModel(); if (!m_sortProxyModel) m_sortProxyModel = new AvailablePagesSortFilterProxyModel(this); m_sortProxyModel->setSourceModel(m_pageListModel); return m_sortProxyModel; } +bool AvailableNotePagesModel::hasProjectPages() const +{ + return false; +} + +bool AvailableNotePagesModel::hasContextPages() const +{ + return false; +} + +bool AvailableNotePagesModel::hasTagPages() const +{ + return true; +} + QObject *AvailableNotePagesModel::createPageForIndex(const QModelIndex &index) { QObjectPtr object = index.data(QueryTreeModelBase::ObjectRole).value(); if (object == m_inboxObject) { auto inboxPageModel = new NoteInboxPageModel(m_noteQueries, m_noteRepository, this); inboxPageModel->setErrorHandler(errorHandler()); return inboxPageModel; } else if (auto tag = object.objectCast()) { auto tagPageModel = new TagPageModel(tag, m_tagQueries, m_tagRepository, m_noteRepository, this); tagPageModel->setErrorHandler(errorHandler()); return tagPageModel; } return Q_NULLPTR; } void AvailableNotePagesModel::addProject(const QString &, const Domain::DataSource::Ptr &) { qFatal("Not supported"); } void AvailableNotePagesModel::addContext(const QString &) { qFatal("Not supported"); } void AvailableNotePagesModel::addTag(const QString &name) { auto tag = Domain::Tag::Ptr::create(); tag->setName(name); const auto job = m_tagRepository->create(tag); installHandler(job, tr("Cannot add tag %1").arg(name)); } void AvailableNotePagesModel::removeItem(const QModelIndex &index) { QObjectPtr object = index.data(QueryTreeModelBase::ObjectRole).value(); if (auto tag = object.objectCast()) { const auto job = m_tagRepository->remove(tag); installHandler(job, tr("Cannot remove tag %1").arg(tag->name())); } else { Q_ASSERT(false); } } QAbstractItemModel *AvailableNotePagesModel::createPageListModel() { m_inboxObject = QObjectPtr::create(); m_inboxObject->setProperty("name", tr("Inbox")); m_tagsObject = QObjectPtr::create(); m_tagsObject->setProperty("name", tr("Tags")); m_rootsProvider = Domain::QueryResultProvider::Ptr::create(); m_rootsProvider->append(m_inboxObject); m_rootsProvider->append(m_tagsObject); auto query = [this](const QObjectPtr &object) -> Domain::QueryResultInterface::Ptr { if (!object) return Domain::QueryResult::create(m_rootsProvider); else if (object == m_tagsObject) return Domain::QueryResult::copy(m_tagQueries->findAll()); 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 == m_inboxObject ? 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_tagsObject)) { 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) ? "mail-folder-inbox" : (object == m_tagsObject) ? "folder" : "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 &, int role) { if (role != Qt::EditRole) { return false; } if (object == m_inboxObject || object == m_tagsObject) { return false; } if (object.objectCast()) { return false; // Tag renaming is NOT allowed } else { Q_ASSERT(false); } return true; }; auto drop = [this](const QMimeData *mimeData, Qt::DropAction, const QObjectPtr &object) { if (!mimeData->hasFormat("application/x-zanshin-object")) return false; auto droppedArtifacts = mimeData->property("objects").value(); if (droppedArtifacts.isEmpty()) return false; if (std::any_of(droppedArtifacts.begin(), droppedArtifacts.end(), [](const Domain::Artifact::Ptr &droppedArtifact) { return !droppedArtifact.objectCast(); })) { return false; } if (auto tag = object.objectCast()) { foreach (const auto &droppedArtifact, droppedArtifacts) { auto note = droppedArtifact.staticCast(); const auto job = m_tagRepository->associate(tag, note); installHandler(job, tr("Cannot tag %1 with %2").arg(note->title()).arg(tag->name())); } return true; } else if (object == m_inboxObject) { foreach (const auto &droppedArtifact, droppedArtifacts) { auto note = droppedArtifact.staticCast(); const auto job = m_tagRepository->dissociateAll(note); installHandler(job, tr("Cannot move %1 to Inbox").arg(note->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/availablenotepagesmodel.h b/src/presentation/availablenotepagesmodel.h index 5c0a4e74..ec54ac4f 100644 --- a/src/presentation/availablenotepagesmodel.h +++ b/src/presentation/availablenotepagesmodel.h @@ -1,79 +1,83 @@ /* This file is part of Zanshin Copyright 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. */ #ifndef PRESENTATION_AVAILABLENOTEPAGESMODEL_H #define PRESENTATION_AVAILABLENOTEPAGESMODEL_H #include "presentation/availablepagesmodelinterface.h" #include "domain/notequeries.h" #include "domain/noterepository.h" #include "domain/tagqueries.h" #include "domain/tagrepository.h" #include "presentation/metatypes.h" namespace Presentation { class AvailablePagesSortFilterProxyModel; class AvailableNotePagesModel : public AvailablePagesModelInterface { Q_OBJECT public: explicit AvailableNotePagesModel(const Domain::NoteQueries::Ptr ¬eQueries, const Domain::NoteRepository::Ptr ¬eRepository, const Domain::TagQueries::Ptr &tagQueries, const Domain::TagRepository::Ptr &tagRepository, 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::NoteQueries::Ptr m_noteQueries; Domain::NoteRepository::Ptr m_noteRepository; Domain::TagQueries::Ptr m_tagQueries; Domain::TagRepository::Ptr m_tagRepository; Domain::QueryResultProvider::Ptr m_rootsProvider; QObjectPtr m_inboxObject; QObjectPtr m_tagsObject; }; } #endif // PRESENTATION_AVAILABLENOTEPAGESMODEL_H diff --git a/src/presentation/availablepagesmodelinterface.h b/src/presentation/availablepagesmodelinterface.h index c829aef2..a14d1ef9 100644 --- a/src/presentation/availablepagesmodelinterface.h +++ b/src/presentation/availablepagesmodelinterface.h @@ -1,59 +1,66 @@ /* This file is part of Zanshin Copyright 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. */ #ifndef PRESENTATION_AVAILABLEPAGESMODELINTERFACE_H #define PRESENTATION_AVAILABLEPAGESMODELINTERFACE_H #include #include "domain/datasource.h" #include "presentation/errorhandlingmodelbase.h" class QAbstractItemModel; class QModelIndex; namespace Presentation { class AvailablePagesModelInterface : public QObject, public ErrorHandlingModelBase { Q_OBJECT Q_PROPERTY(QAbstractItemModel* pageListModel READ pageListModel) + Q_PROPERTY(bool hasProjectPages READ hasProjectPages) + Q_PROPERTY(bool hasContextPages READ hasContextPages) + Q_PROPERTY(bool hasTagPages READ hasTagPages) public: explicit AvailablePagesModelInterface(QObject *parent = Q_NULLPTR); virtual QAbstractItemModel *pageListModel() = 0; + virtual bool hasProjectPages() const = 0; + virtual bool hasContextPages() const = 0; + virtual bool hasTagPages() const = 0; + Q_SCRIPTABLE virtual QObject *createPageForIndex(const QModelIndex &index) = 0; public slots: virtual void addProject(const QString &name, const Domain::DataSource::Ptr &source) = 0; virtual void addContext(const QString &name) = 0; virtual void addTag(const QString &name) = 0; virtual void removeItem(const QModelIndex &index) = 0; }; } #endif // PRESENTATION_AVAILABLEPAGESMODELINTERFACE_H diff --git a/src/presentation/availabletaskpagesmodel.cpp b/src/presentation/availabletaskpagesmodel.cpp index 6436f385..d8d054db 100644 --- a/src/presentation/availabletaskpagesmodel.cpp +++ b/src/presentation/availabletaskpagesmodel.cpp @@ -1,323 +1,338 @@ /* 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 "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, 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_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_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_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, tr("Cannot add project %1 in dataSource %2").arg(name).arg(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, tr("Cannot add context %1").arg(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, tr("Cannot remove project %1").arg(project->name())); } else if (auto context = object.objectCast()) { const auto job = m_contextRepository->remove(context); installHandler(job, tr("Cannot remove context %1").arg(context->name())); } else { Q_ASSERT(false); } } QAbstractItemModel *AvailableTaskPagesModel::createPageListModel() { m_inboxObject = QObjectPtr::create(); m_inboxObject->setProperty("name", tr("Inbox")); m_workdayObject = QObjectPtr::create(); m_workdayObject->setProperty("name", tr("Workday")); m_projectsObject = QObjectPtr::create(); m_projectsObject->setProperty("name", tr("Projects")); m_contextsObject = QObjectPtr::create(); m_contextsObject->setProperty("name", tr("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()); else if (object == m_contextsObject) return Domain::QueryResult::copy(m_contextQueries->findAll()); 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)) { 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 ? "mail-folder-inbox" : (object == m_workdayObject) ? "go-jump-today" : (object == m_projectsObject) ? "folder" : (object == m_contextsObject) ? "folder" : object.objectCast() ? "view-pim-notes" : "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) { 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, tr("Cannot modify project %1").arg(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, tr("Cannot modify context %1").arg(currentName)); } else { Q_ASSERT(false); } return true; }; auto drop = [this](const QMimeData *mimeData, Qt::DropAction, const QObjectPtr &object) { if (!mimeData->hasFormat("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, tr("Cannot add %1 to project %2").arg(droppedArtifact->title()).arg(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, tr("Cannot add %1 to context %2").arg(task->title()).arg(context->name())); } return true; } else if (object == m_inboxObject) { foreach (const auto &droppedArtifact, droppedArtifacts) { const auto job = m_projectRepository->dissociate(droppedArtifact); installHandler(job, tr("Cannot move %1 to Inbox").arg(droppedArtifact->title())); if (auto task = droppedArtifact.objectCast()) { Utils::JobHandler::install(job, [this, task] { const auto dissociateJob = m_taskRepository->dissociateAll(task); installHandler(dissociateJob, tr("Cannot move task %1 to Inbox").arg(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, tr("Cannot update task %1 to Workday").arg(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 d8ec625b..dc1ed6e0 100644 --- a/src/presentation/availabletaskpagesmodel.h +++ b/src/presentation/availabletaskpagesmodel.h @@ -1,89 +1,93 @@ /* 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/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, 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::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/tests/units/presentation/applicationmodeltest.cpp b/tests/units/presentation/applicationmodeltest.cpp index 7ced3bb2..9517fb46 100644 --- a/tests/units/presentation/applicationmodeltest.cpp +++ b/tests/units/presentation/applicationmodeltest.cpp @@ -1,175 +1,179 @@ /* 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 #include "utils/dependencymanager.h" #include "utils/mockobject.h" #include "presentation/applicationmodel.h" #include "presentation/artifacteditormodel.h" #include "presentation/availablepagesmodelinterface.h" #include "presentation/availablesourcesmodel.h" #include "presentation/errorhandler.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 FakeAvailablePagesModel : public Presentation::AvailablePagesModelInterface { Q_OBJECT public: explicit FakeAvailablePagesModel(QObject *parent = Q_NULLPTR) : Presentation::AvailablePagesModelInterface(parent) {} QAbstractItemModel *pageListModel() Q_DECL_OVERRIDE { return Q_NULLPTR; } + bool hasProjectPages() const Q_DECL_OVERRIDE { return false; } + bool hasContextPages() const Q_DECL_OVERRIDE { return false; } + bool hasTagPages() const Q_DECL_OVERRIDE { return false; } + QObject *createPageForIndex(const QModelIndex &) Q_DECL_OVERRIDE { return Q_NULLPTR; } void addProject(const QString &, const Domain::DataSource::Ptr &) Q_DECL_OVERRIDE {} void addContext(const QString &) Q_DECL_OVERRIDE {} void addTag(const QString &) Q_DECL_OVERRIDE {} void removeItem(const QModelIndex &) Q_DECL_OVERRIDE {} }; class ApplicationModelTest : public QObject { Q_OBJECT public: explicit ApplicationModelTest(QObject *parent = Q_NULLPTR) : QObject(parent) { Utils::DependencyManager::globalInstance().add(); Utils::DependencyManager::globalInstance().add( [] (Utils::DependencyManager *) { return new Presentation::ArtifactEditorModel; }); Utils::DependencyManager::globalInstance().add( [] (Utils::DependencyManager *) { return new Presentation::AvailableSourcesModel(Domain::DataSourceQueries::Ptr(), Domain::DataSourceRepository::Ptr()); }); } private slots: void shouldProvideAvailableSourcesModel() { // GIVEN Presentation::ApplicationModel app; // WHEN QObject *available = app.availableSources(); // THEN QVERIFY(qobject_cast(available)); } void shouldProvideAvailablePagesModel() { // GIVEN Presentation::ApplicationModel app; // WHEN QObject *available = app.availablePages(); // THEN QVERIFY(qobject_cast(available)); } void shouldProvideCurrentPage() { // GIVEN Presentation::ApplicationModel app; QVERIFY(!app.currentPage()); QSignalSpy spy(&app, SIGNAL(currentPageChanged(QObject*))); // WHEN auto page = new QObject(this); app.setCurrentPage(page); // THEN QCOMPARE(app.currentPage(), page); QCOMPARE(spy.count(), 1); QCOMPARE(spy.takeFirst().takeFirst().value(), page); } void shouldProvideArtifactEditorModel() { // GIVEN Presentation::ApplicationModel app; // WHEN QObject *page = app.editor(); // THEN QVERIFY(qobject_cast(page)); } void shouldSetErrorHandlerToAllModels() { // GIVEN // An ErrorHandler FakeErrorHandler errorHandler; Presentation::ApplicationModel app; // WHEN app.setErrorHandler(&errorHandler); // THEN auto availableSource = static_cast(app.availableSources()); auto availablePages = static_cast(app.availablePages()); auto editor = static_cast(app.editor()); QCOMPARE(availableSource->errorHandler(), &errorHandler); QCOMPARE(availablePages->errorHandler(), &errorHandler); QCOMPARE(editor->errorHandler(), &errorHandler); // WHEN FakeErrorHandler errorHandler2; app.setErrorHandler(&errorHandler2); // THEN QCOMPARE(availableSource->errorHandler(), &errorHandler2); QCOMPARE(availablePages->errorHandler(), &errorHandler2); QCOMPARE(editor->errorHandler(), &errorHandler2); } }; QTEST_MAIN(ApplicationModelTest) #include "applicationmodeltest.moc" diff --git a/tests/units/presentation/availablenotepagesmodeltest.cpp b/tests/units/presentation/availablenotepagesmodeltest.cpp index 41a04020..7f96fc69 100644 --- a/tests/units/presentation/availablenotepagesmodeltest.cpp +++ b/tests/units/presentation/availablenotepagesmodeltest.cpp @@ -1,453 +1,464 @@ /* This file is part of Zanshin Copyright 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 "utils/mockobject.h" #include "presentation/availablenotepagesmodel.h" #include "presentation/errorhandler.h" #include "presentation/noteinboxpagemodel.h" #include "presentation/querytreemodelbase.h" #include "presentation/tagpagemodel.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 AvailableNotePagesModelTest : public QObject { Q_OBJECT private slots: + void shouldDeclareOnlyProjectAndContextPages() + { + // GIVEN + Presentation::AvailableNotePagesModel pages({}, {}, {}, {}); + + // THEN + QVERIFY(!pages.hasProjectPages()); + QVERIFY(!pages.hasContextPages()); + QVERIFY(pages.hasTagPages()); + } + void shouldListAvailablePages() { // GIVEN // Two tags auto tag1 = Domain::Tag::Ptr::create(); tag1->setName("Tag 1"); auto tag2 = Domain::Tag::Ptr::create(); tag2->setName("Tag 2"); auto tagProvider = Domain::QueryResultProvider::Ptr::create(); auto tagResult = Domain::QueryResult::create(tagProvider); tagProvider->append(tag1); tagProvider->append(tag2); // One note (used for dropping later on) auto noteToDrop = Domain::Note::Ptr::create(); Utils::MockObject tagQueriesMock; tagQueriesMock(&Domain::TagQueries::findAll).when().thenReturn(tagResult); Utils::MockObject tagRepositoryMock; Presentation::AvailableNotePagesModel pages(Domain::NoteQueries::Ptr(), Domain::NoteRepository::Ptr(), tagQueriesMock.getInstance(), tagRepositoryMock.getInstance()); // WHEN QAbstractItemModel *model = pages.pageListModel(); // THEN const QModelIndex inboxIndex = model->index(0, 0); const QModelIndex tagsIndex = model->index(1, 0); const QModelIndex tag1Index = model->index(0, 0, tagsIndex); const QModelIndex tag2Index = model->index(1, 0, tagsIndex); QCOMPARE(model->rowCount(), 2); QCOMPARE(model->rowCount(inboxIndex), 0); QCOMPARE(model->rowCount(tagsIndex), 2); QCOMPARE(model->rowCount(tag1Index), 0); const Qt::ItemFlags defaultFlags = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable; QCOMPARE(model->flags(inboxIndex), (defaultFlags & ~(Qt::ItemIsEditable)) | Qt::ItemIsDropEnabled); QCOMPARE(model->flags(tagsIndex), Qt::NoItemFlags); QCOMPARE(model->flags(tag1Index), defaultFlags | Qt::ItemIsDropEnabled); QCOMPARE(model->flags(tag2Index), defaultFlags | Qt::ItemIsDropEnabled); QCOMPARE(model->data(inboxIndex).toString(), tr("Inbox")); QCOMPARE(model->data(tagsIndex).toString(), tr("Tags")); QCOMPARE(model->data(tag1Index).toString(), tag1->name()); QCOMPARE(model->data(tag2Index).toString(), tag2->name()); QVERIFY(!model->data(inboxIndex, Qt::EditRole).isValid()); QVERIFY(!model->data(tagsIndex, Qt::EditRole).isValid()); QCOMPARE(model->data(tag1Index, Qt::EditRole).toString(), tag1->name()); QCOMPARE(model->data(tag2Index, Qt::EditRole).toString(), tag2->name()); QCOMPARE(model->data(inboxIndex, Presentation::QueryTreeModelBase::IconNameRole).toString(), QString("mail-folder-inbox")); QCOMPARE(model->data(tagsIndex, Presentation::QueryTreeModelBase::IconNameRole).toString(), QString("folder")); QCOMPARE(model->data(tag1Index, Presentation::QueryTreeModelBase::IconNameRole).toString(), QString("view-pim-tasks")); QCOMPARE(model->data(tag2Index, Presentation::QueryTreeModelBase::IconNameRole).toString(), QString("view-pim-tasks")); QVERIFY(!model->data(inboxIndex, Qt::CheckStateRole).isValid()); QVERIFY(!model->data(tagsIndex, Qt::CheckStateRole).isValid()); QVERIFY(!model->data(tag1Index, Qt::CheckStateRole).isValid()); QVERIFY(!model->data(tag2Index, Qt::CheckStateRole).isValid()); QVERIFY(!model->setData(inboxIndex, "foo", Qt::EditRole)); QVERIFY(!model->setData(tagsIndex, "foo", Qt::EditRole)); QVERIFY(!model->setData(tag1Index, "foo", Qt::EditRole)); QVERIFY(!model->setData(tag2Index, "foo", Qt::EditRole)); // WHEN tagRepositoryMock(&Domain::TagRepository::associate).when(tag1, noteToDrop).thenReturn(new FakeJob(this)); QMimeData *data = new QMimeData; data->setData("application/x-zanshin-object", "object"); data->setProperty("objects", QVariant::fromValue(Domain::Artifact::List() << noteToDrop)); model->dropMimeData(data, Qt::MoveAction, -1, -1, tag1Index); // THEN QVERIFY(tagRepositoryMock(&Domain::TagRepository::associate).when(tag1, noteToDrop).exactly(1)); // WHEN tagRepositoryMock(&Domain::TagRepository::dissociateAll).when(noteToDrop).thenReturn(new FakeJob(this)); data = new QMimeData; data->setData("application/x-zanshin-object", "object"); data->setProperty("objects", QVariant::fromValue(Domain::Artifact::List() << noteToDrop)); model->dropMimeData(data, Qt::MoveAction, -1, -1, inboxIndex); QTest::qWait(150); // THEN QVERIFY(tagRepositoryMock(&Domain::TagRepository::dissociateAll).when(noteToDrop).exactly(1)); } void shouldCreateInboxPage() { // GIVEN // Empty tag provider auto tagProvider = Domain::QueryResultProvider::Ptr::create(); auto tagResult = Domain::QueryResult::create(tagProvider); Utils::MockObject tagQueriesMock; tagQueriesMock(&Domain::TagQueries::findAll).when().thenReturn(tagResult); Presentation::AvailableNotePagesModel pages(Domain::NoteQueries::Ptr(), Domain::NoteRepository::Ptr(), tagQueriesMock.getInstance(), Domain::TagRepository::Ptr()); // WHEN QAbstractItemModel *model = pages.pageListModel(); // THEN const QModelIndex inboxIndex = model->index(0, 0); QObject *inboxPage = pages.createPageForIndex(inboxIndex); QVERIFY(qobject_cast(inboxPage)); } void shouldCreateTagsPage() { // GIVEN // Two tags auto tag1 = Domain::Tag::Ptr::create(); tag1->setName("tag 1"); auto tag2 = Domain::Tag::Ptr::create(); tag2->setName("tag 2"); auto tagProvider = Domain::QueryResultProvider::Ptr::create(); auto tagResult = Domain::QueryResult::create(tagProvider); tagProvider->append(tag1); tagProvider->append(tag2); // tags mocking Utils::MockObject tagQueriesMock; tagQueriesMock(&Domain::TagQueries::findAll).when().thenReturn(tagResult); Presentation::AvailableNotePagesModel pages(Domain::NoteQueries::Ptr(), Domain::NoteRepository::Ptr(), tagQueriesMock.getInstance(), Domain::TagRepository::Ptr()); // WHEN QAbstractItemModel *model = pages.pageListModel(); // THEN const QModelIndex tagsIndex = model->index(1, 0); const QModelIndex tag1Index = model->index(0, 0, tagsIndex); const QModelIndex tag2Index = model->index(1, 0, tagsIndex); QObject *tagsPage = pages.createPageForIndex(tagsIndex); QObject *tag1Page = pages.createPageForIndex(tag1Index); QObject *tag2Page = pages.createPageForIndex(tag2Index); QVERIFY(!tagsPage); QVERIFY(qobject_cast(tag1Page)); QCOMPARE(qobject_cast(tag1Page)->tag(), tag1); QVERIFY(qobject_cast(tag2Page)); QCOMPARE(qobject_cast(tag2Page)->tag(), tag2); } void shouldAddTags() { // GIVEN Utils::MockObject tagRepositoryMock; tagRepositoryMock(&Domain::TagRepository::create).when(any()) .thenReturn(new FakeJob(this)); Presentation::AvailableNotePagesModel pages(Domain::NoteQueries::Ptr(), Domain::NoteRepository::Ptr(), Domain::TagQueries::Ptr(), tagRepositoryMock.getInstance()); // WHEN pages.addTag("Foo"); // THEN QVERIFY(tagRepositoryMock(&Domain::TagRepository::create).when(any()) .exactly(1)); } void shouldGetAnErrorMessageWhenAddTagFailed() { // GIVEN Utils::MockObject tagRepositoryMock; auto job = new FakeJob(this); job->setExpectedError(KJob::KilledJobError, "Foo"); tagRepositoryMock(&Domain::TagRepository::create).when(any()) .thenReturn(job); Presentation::AvailableNotePagesModel pages(Domain::NoteQueries::Ptr(), Domain::NoteRepository::Ptr(), Domain::TagQueries::Ptr(), tagRepositoryMock.getInstance()); FakeErrorHandler errorHandler; pages.setErrorHandler(&errorHandler); // WHEN pages.addTag("Foo"); // THEN QTest::qWait(150); QCOMPARE(errorHandler.m_message, QString("Cannot add tag Foo: Foo")); } void shouldRemoveTag() { // GIVEN // Two tags auto tag1 = Domain::Tag::Ptr::create(); tag1->setName("tag 1"); auto tag2 = Domain::Tag::Ptr::create(); tag2->setName("tag 2"); auto tagProvider = Domain::QueryResultProvider::Ptr::create(); auto tagResult = Domain::QueryResult::create(tagProvider); tagProvider->append(tag1); tagProvider->append(tag2); // tags mocking Utils::MockObject tagQueriesMock; tagQueriesMock(&Domain::TagQueries::findAll).when().thenReturn(tagResult); Utils::MockObject tagRepositoryMock; Presentation::AvailableNotePagesModel pages(Domain::NoteQueries::Ptr(), Domain::NoteRepository::Ptr(), tagQueriesMock.getInstance(), tagRepositoryMock.getInstance()); FakeErrorHandler errorHandler; pages.setErrorHandler(&errorHandler); QAbstractItemModel *model = pages.pageListModel(); const QModelIndex tagsIndex = model->index(1, 0); const QModelIndex tag1Index = model->index(0, 0, tagsIndex); auto job = new FakeJob(this); tagRepositoryMock(&Domain::TagRepository::remove).when(tag1).thenReturn(job); // WHEN pages.removeItem(tag1Index); // THEN QTest::qWait(150); QVERIFY(errorHandler.m_message.isEmpty()); QVERIFY(tagRepositoryMock(&Domain::TagRepository::remove).when(tag1).exactly(1)); } void shouldGetAnErrorMessageWhenRemoveTagFailed() { // GIVEN // Two tags auto tag1 = Domain::Tag::Ptr::create(); tag1->setName("tag 1"); auto tag2 = Domain::Tag::Ptr::create(); tag2->setName("tag 2"); auto tagProvider = Domain::QueryResultProvider::Ptr::create(); auto tagResult = Domain::QueryResult::create(tagProvider); tagProvider->append(tag1); tagProvider->append(tag2); // tags mocking Utils::MockObject tagQueriesMock; tagQueriesMock(&Domain::TagQueries::findAll).when().thenReturn(tagResult); Utils::MockObject tagRepositoryMock; Presentation::AvailableNotePagesModel pages(Domain::NoteQueries::Ptr(), Domain::NoteRepository::Ptr(), tagQueriesMock.getInstance(), tagRepositoryMock.getInstance()); FakeErrorHandler errorHandler; pages.setErrorHandler(&errorHandler); QAbstractItemModel *model = pages.pageListModel(); const QModelIndex tagsIndex = model->index(1, 0); const QModelIndex tag1Index = model->index(0, 0, tagsIndex); auto job = new FakeJob(this); job->setExpectedError(KJob::KilledJobError, "Foo"); tagRepositoryMock(&Domain::TagRepository::remove).when(tag1).thenReturn(job); // WHEN pages.removeItem(tag1Index); // THEN QTest::qWait(150); QCOMPARE(errorHandler.m_message, QString("Cannot remove tag tag 1: Foo")); } void shouldGetAnErrorMessageWhenAssociateTagFailed() { // GIVEN // Two tags auto tag1 = Domain::Tag::Ptr::create(); tag1->setName("Tag 1"); auto tag2 = Domain::Tag::Ptr::create(); tag2->setName("Tag 2"); auto tagProvider = Domain::QueryResultProvider::Ptr::create(); auto tagResult = Domain::QueryResult::create(tagProvider); tagProvider->append(tag1); tagProvider->append(tag2); // One note (used for dropping later on) auto noteToDrop = Domain::Note::Ptr::create(); noteToDrop->setTitle("noteDropped"); // tags mocking Utils::MockObject tagQueriesMock; tagQueriesMock(&Domain::TagQueries::findAll).when().thenReturn(tagResult); Utils::MockObject tagRepositoryMock; Presentation::AvailableNotePagesModel pages(Domain::NoteQueries::Ptr(), Domain::NoteRepository::Ptr(), tagQueriesMock.getInstance(), tagRepositoryMock.getInstance()); FakeErrorHandler errorHandler; pages.setErrorHandler(&errorHandler); QAbstractItemModel *model = pages.pageListModel(); const QModelIndex tagsIndex = model->index(1, 0); const QModelIndex tag1Index = model->index(0, 0, tagsIndex); // WHEN auto job = new FakeJob(this); job->setExpectedError(KJob::KilledJobError, "Foo"); tagRepositoryMock(&Domain::TagRepository::associate).when(tag1, noteToDrop).thenReturn(job); auto data = new QMimeData; data->setData("application/x-zanshin-object", "object"); data->setProperty("objects", QVariant::fromValue(Domain::Artifact::List() << noteToDrop)); model->dropMimeData(data, Qt::MoveAction, -1, -1, tag1Index); // THEN QTest::qWait(150); QCOMPARE(errorHandler.m_message, QString("Cannot tag noteDropped with Tag 1: Foo")); } void shouldGetAnErrorMessageWhenDissociateTaskFailed() { // GIVEN // Two tags auto tag1 = Domain::Tag::Ptr::create(); tag1->setName("tag 1"); auto tag2 = Domain::Tag::Ptr::create(); tag2->setName("tag 2"); auto tagProvider = Domain::QueryResultProvider::Ptr::create(); auto tagResult = Domain::QueryResult::create(tagProvider); tagProvider->append(tag1); tagProvider->append(tag2); // One note (used for dropping later on) auto noteToDrop = Domain::Note::Ptr::create(); noteToDrop->setTitle("noteDropped"); // tags mocking Utils::MockObject tagQueriesMock; tagQueriesMock(&Domain::TagQueries::findAll).when().thenReturn(tagResult); Utils::MockObject tagRepositoryMock; Presentation::AvailableNotePagesModel pages(Domain::NoteQueries::Ptr(), Domain::NoteRepository::Ptr(), tagQueriesMock.getInstance(), tagRepositoryMock.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, "Foo"); tagRepositoryMock(&Domain::TagRepository::dissociateAll).when(noteToDrop).thenReturn(job); auto data = new QMimeData; data->setData("application/x-zanshin-object", "object"); data->setProperty("objects", QVariant::fromValue(Domain::Artifact::List() << noteToDrop)); model->dropMimeData(data, Qt::MoveAction, -1, -1, inboxIndex); // THEN QTest::qWait(150); QCOMPARE(errorHandler.m_message, QString("Cannot move noteDropped to Inbox: Foo")); } }; QTEST_MAIN(AvailableNotePagesModelTest) #include "availablenotepagesmodeltest.moc" diff --git a/tests/units/presentation/availabletaskpagesmodeltest.cpp b/tests/units/presentation/availabletaskpagesmodeltest.cpp index f092979c..95a1ecbc 100644 --- a/tests/units/presentation/availabletaskpagesmodeltest.cpp +++ b/tests/units/presentation/availabletaskpagesmodeltest.cpp @@ -1,1122 +1,1133 @@ /* 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 #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({}, {}, {}, {}, {}, {}); + + // THEN + QVERIFY(pages.hasProjectPages()); + QVERIFY(pages.hasContextPages()); + QVERIFY(!pages.hasTagPages()); + } + void shouldListAvailablePages() { // GIVEN // Two projects auto project1 = Domain::Project::Ptr::create(); project1->setName("Project 1"); auto project2 = Domain::Project::Ptr::create(); project2->setName("Project 2"); auto projectProvider = Domain::QueryResultProvider::Ptr::create(); auto projectResult = Domain::QueryResult::create(projectProvider); projectProvider->append(project2); projectProvider->append(project1); // note: reversed order, to test sorting // Two contexts auto context1 = Domain::Context::Ptr::create(); context1->setName("context 1"); auto context2 = Domain::Context::Ptr::create(); context2->setName("context 2"); auto contextProvider = Domain::QueryResultProvider::Ptr::create(); auto contextResult = Domain::QueryResult::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 projectQueriesMock; projectQueriesMock(&Domain::ProjectQueries::findAll).when().thenReturn(projectResult); Utils::MockObject projectRepositoryMock; Utils::MockObject taskRepositoryMock; Utils::MockObject contextQueriesMock; contextQueriesMock(&Domain::ContextQueries::findAll).when().thenReturn(contextResult); Utils::MockObject contextRepositoryMock; Presentation::AvailableTaskPagesModel pages(projectQueriesMock.getInstance(), 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 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(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(contextsIndex), Qt::NoItemFlags); QCOMPARE(model->flags(context1Index), defaultFlags | Qt::ItemIsDropEnabled); QCOMPARE(model->flags(context2Index), defaultFlags | Qt::ItemIsDropEnabled); QCOMPARE(model->data(inboxIndex).toString(), tr("Inbox")); QCOMPARE(model->data(workdayIndex).toString(), tr("Workday")); QCOMPARE(model->data(projectsIndex).toString(), tr("Projects")); QCOMPARE(model->data(project1Index).toString(), project1->name()); QCOMPARE(model->data(project2Index).toString(), project2->name()); QCOMPARE(model->data(contextsIndex).toString(), tr("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(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(), QString("mail-folder-inbox")); QCOMPARE(model->data(workdayIndex, Presentation::QueryTreeModelBase::IconNameRole).toString(), QString("go-jump-today")); QCOMPARE(model->data(projectsIndex, Presentation::QueryTreeModelBase::IconNameRole).toString(), QString("folder")); QCOMPARE(model->data(project1Index, Presentation::QueryTreeModelBase::IconNameRole).toString(), QString("view-pim-tasks")); QCOMPARE(model->data(project2Index, Presentation::QueryTreeModelBase::IconNameRole).toString(), QString("view-pim-tasks")); QCOMPARE(model->data(contextsIndex, Presentation::QueryTreeModelBase::IconNameRole).toString(), QString("folder")); QCOMPARE(model->data(context1Index, Presentation::QueryTreeModelBase::IconNameRole).toString(), QString("view-pim-notes")); QCOMPARE(model->data(context2Index, Presentation::QueryTreeModelBase::IconNameRole).toString(), QString("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(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)); 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(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(contextRepositoryMock(&Domain::ContextRepository::update).when(context1).exactly(1)); QVERIFY(contextRepositoryMock(&Domain::ContextRepository::update).when(context2).exactly(1)); QCOMPARE(project1->name(), QString("New Project 1")); QCOMPARE(project2->name(), QString("New Project 2")); QCOMPARE(context1->name(), QString("New Context 1")); QCOMPARE(context2->name(), QString("New Context 2")); // WHEN projectRepositoryMock(&Domain::ProjectRepository::associate).when(project1, taskToDrop).thenReturn(new FakeJob(this)); QMimeData *data = new QMimeData; data->setData("application/x-zanshin-object", "object"); data->setProperty("objects", QVariant::fromValue(Domain::Artifact::List() << taskToDrop)); model->dropMimeData(data, Qt::MoveAction, -1, -1, project1Index); // THEN QVERIFY(projectRepositoryMock(&Domain::ProjectRepository::associate).when(project1, taskToDrop).exactly(1)); // WHEN a task is dropped on a context contextRepositoryMock(&Domain::ContextRepository::associate).when(context1, taskToDrop.objectCast()).thenReturn(new FakeJob(this)); data = new QMimeData; data->setData("application/x-zanshin-object", "object"); data->setProperty("objects", QVariant::fromValue(Domain::Artifact::List() << taskToDrop)); model->dropMimeData(data, Qt::MoveAction, -1, -1, context1Index); // THEN QVERIFY(contextRepositoryMock(&Domain::ContextRepository::associate).when(context1, taskToDrop.objectCast()).exactly(1)); // WHEN projectRepositoryMock(&Domain::ProjectRepository::dissociate).when(taskToDrop).thenReturn(new FakeJob(this)); taskRepositoryMock(&Domain::TaskRepository::dissociateAll).when(taskToDrop.objectCast()).thenReturn(new FakeJob(this)); data = new QMimeData; data->setData("application/x-zanshin-object", "object"); data->setProperty("objects", QVariant::fromValue(Domain::Artifact::List() << taskToDrop)); model->dropMimeData(data, 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()).exactly(1)); // WHEN projectRepositoryMock(&Domain::ProjectRepository::associate).when(project2, noteToDrop).thenReturn(new FakeJob(this)); data = new QMimeData; data->setData("application/x-zanshin-object", "object"); data->setProperty("objects", QVariant::fromValue(Domain::Artifact::List() << noteToDrop)); model->dropMimeData(data, Qt::MoveAction, -1, -1, project2Index); // THEN QVERIFY(projectRepositoryMock(&Domain::ProjectRepository::associate).when(project2, 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)); data = new QMimeData; data->setData("application/x-zanshin-object", "object"); data->setProperty("objects", QVariant::fromValue(Domain::Artifact::List() << taskToDrop2 << noteToDrop2)); model->dropMimeData(data, Qt::MoveAction, -1, -1, project1Index); // THEN QVERIFY(projectRepositoryMock(&Domain::ProjectRepository::associate).when(project1, taskToDrop2).exactly(1)); QVERIFY(projectRepositoryMock(&Domain::ProjectRepository::associate).when(project1, noteToDrop2).exactly(1)); // WHEN a task and a note are dropped on a context data = new QMimeData; data->setData("application/x-zanshin-object", "object"); data->setProperty("objects", QVariant::fromValue(Domain::Artifact::List() << taskToDrop2 << noteToDrop2)); model->dropMimeData(data, Qt::MoveAction, -1, -1, context1Index); // THEN QVERIFY(contextRepositoryMock(&Domain::ContextRepository::associate).when(context1, taskToDrop2.objectCast()).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 = new QMimeData; data->setData("application/x-zanshin-object", "object"); data->setProperty("objects", QVariant::fromValue(Domain::Artifact::List() << taskToDrop3 << taskToDrop4)); model->dropMimeData(data, 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 = new QMimeData; data->setData("application/x-zanshin-object", "object"); data->setProperty("objects", QVariant::fromValue(Domain::Artifact::List() << taskToDrop5)); model->dropMimeData(data, 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 = new QMimeData; data->setData("application/x-zanshin-object", "object"); data->setProperty("objects", QVariant::fromValue(Domain::Artifact::List() << taskToDrop6 << taskToDrop7)); model->dropMimeData(data, 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::Ptr::create(); auto projectResult = Domain::QueryResult::create(projectProvider); // Empty context provider auto contextProvider = Domain::QueryResultProvider::Ptr::create(); auto contextResult = Domain::QueryResult::create(contextProvider); // context mocking Utils::MockObject contextQueriesMock; contextQueriesMock(&Domain::ContextQueries::findAll).when().thenReturn(contextResult); Utils::MockObject contextRepositoryMock; // projects mocking Utils::MockObject projectQueriesMock; projectQueriesMock(&Domain::ProjectQueries::findAll).when().thenReturn(projectResult); Utils::MockObject projectRepositoryMock; Presentation::AvailableTaskPagesModel pages(projectQueriesMock.getInstance(), 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(inboxPage)); } void shouldCreateWorkdayPage() { // GIVEN // Empty project provider auto projectProvider = Domain::QueryResultProvider::Ptr::create(); auto projectResult = Domain::QueryResult::create(projectProvider); // Empty context provider auto contextProvider = Domain::QueryResultProvider::Ptr::create(); auto contextResult = Domain::QueryResult::create(contextProvider); // context mocking Utils::MockObject contextQueriesMock; contextQueriesMock(&Domain::ContextQueries::findAll).when().thenReturn(contextResult); Utils::MockObject contextRepositoryMock; // projects mocking Utils::MockObject projectQueriesMock; projectQueriesMock(&Domain::ProjectQueries::findAll).when().thenReturn(projectResult); Utils::MockObject projectRepositoryMock; Presentation::AvailableTaskPagesModel pages(projectQueriesMock.getInstance(), 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(workdayPage)); } void shouldCreateProjectsPage() { // GIVEN // Two projects auto project1 = Domain::Project::Ptr::create(); project1->setName("Project 1"); auto project2 = Domain::Project::Ptr::create(); project2->setName("Project 2"); auto projectProvider = Domain::QueryResultProvider::Ptr::create(); auto projectResult = Domain::QueryResult::create(projectProvider); projectProvider->append(project1); projectProvider->append(project2); // No contexts auto contextProvider = Domain::QueryResultProvider::Ptr::create(); auto contextResult = Domain::QueryResult::create(contextProvider); // projects mocking Utils::MockObject projectQueriesMock; projectQueriesMock(&Domain::ProjectQueries::findAll).when().thenReturn(projectResult); Utils::MockObject projectRepositoryMock; // contexts mocking Utils::MockObject contextQueriesMock; contextQueriesMock(&Domain::ContextQueries::findAll).when().thenReturn(contextResult); Presentation::AvailableTaskPagesModel pages(projectQueriesMock.getInstance(), 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); QObject *projectsPage = pages.createPageForIndex(projectsIndex); QObject *project1Page = pages.createPageForIndex(project1Index); QObject *project2Page = pages.createPageForIndex(project2Index); QVERIFY(!projectsPage); QVERIFY(qobject_cast(project1Page)); QCOMPARE(qobject_cast(project1Page)->project(), project1); QVERIFY(qobject_cast(project2Page)); QCOMPARE(qobject_cast(project2Page)->project(), project2); } void shouldCreateContextsPage() { // GIVEN // Two contexts auto context1 = Domain::Context::Ptr::create(); context1->setName("context 1"); auto context2 = Domain::Context::Ptr::create(); context2->setName("context 2"); auto contextProvider = Domain::QueryResultProvider::Ptr::create(); auto contextResult = Domain::QueryResult::create(contextProvider); contextProvider->append(context1); contextProvider->append(context2); // Empty Project provider auto projectProvider = Domain::QueryResultProvider::Ptr::create(); auto projectResult = Domain::QueryResult::create(projectProvider); // contexts mocking Utils::MockObject contextQueriesMock; contextQueriesMock(&Domain::ContextQueries::findAll).when().thenReturn(contextResult); Utils::MockObject contextRepositoryMock; // projects mocking Utils::MockObject projectQueriesMock; projectQueriesMock(&Domain::ProjectQueries::findAll).when().thenReturn(projectResult); Utils::MockObject projectRepositoryMock; Presentation::AvailableTaskPagesModel pages(projectQueriesMock.getInstance(), 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(context1Page)); QCOMPARE(qobject_cast(context1Page)->context(), context1); QVERIFY(qobject_cast(context2Page)); QCOMPARE(qobject_cast(context2Page)->context(), context2); } void shouldAddProjects() { // GIVEN auto source = Domain::DataSource::Ptr::create(); Utils::MockObject projectRepositoryMock; projectRepositoryMock(&Domain::ProjectRepository::create).when(any(), any()) .thenReturn(new FakeJob(this)); Presentation::AvailableTaskPagesModel pages(Domain::ProjectQueries::Ptr(), projectRepositoryMock.getInstance(), Domain::ContextQueries::Ptr(), Domain::ContextRepository::Ptr(), Domain::TaskQueries::Ptr(), Domain::TaskRepository::Ptr()); // WHEN pages.addProject("Foo", source); // THEN QVERIFY(projectRepositoryMock(&Domain::ProjectRepository::create).when(any(), any()) .exactly(1)); } void shouldGetAnErrorMessageWhenAddProjectFailed() { // GIVEN auto source = Domain::DataSource::Ptr::create(); source->setName("Source1"); Utils::MockObject projectRepositoryMock; auto job = new FakeJob(this); job->setExpectedError(KJob::KilledJobError, "Foo"); projectRepositoryMock(&Domain::ProjectRepository::create).when(any(), any()) .thenReturn(job); Presentation::AvailableTaskPagesModel pages(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("Foo", source); // THEN QTest::qWait(150); QCOMPARE(errorHandler.m_message, QString("Cannot add project Foo in dataSource Source1: Foo")); } void shouldAddContexts() { // GIVEN Utils::MockObject contextRepositoryMock; contextRepositoryMock(&Domain::ContextRepository::create).when(any()) .thenReturn(new FakeJob(this)); Presentation::AvailableTaskPagesModel pages(Domain::ProjectQueries::Ptr(), Domain::ProjectRepository::Ptr(), Domain::ContextQueries::Ptr(), contextRepositoryMock.getInstance(), Domain::TaskQueries::Ptr(), Domain::TaskRepository::Ptr()); // WHEN pages.addContext("Foo"); // THEN QVERIFY(contextRepositoryMock(&Domain::ContextRepository::create).when(any()) .exactly(1)); } void shouldGetAnErrorMessageWhenAddContextFailed() { // GIVEN Utils::MockObject contextRepositoryMock; auto job = new FakeJob(this); job->setExpectedError(KJob::KilledJobError, "Foo"); contextRepositoryMock(&Domain::ContextRepository::create).when(any()) .thenReturn(job); Presentation::AvailableTaskPagesModel pages(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("Foo"); // THEN QTest::qWait(150); QCOMPARE(errorHandler.m_message, QString("Cannot add context Foo: Foo")); } void shouldRemoveProject() { // GIVEN // Two projects auto project1 = Domain::Project::Ptr::create(); project1->setName("Project 1"); auto project2 = Domain::Project::Ptr::create(); project2->setName("Project 2"); auto projectProvider = Domain::QueryResultProvider::Ptr::create(); auto projectResult = Domain::QueryResult::create(projectProvider); projectProvider->append(project1); projectProvider->append(project2); // No contexts auto contextProvider = Domain::QueryResultProvider::Ptr::create(); auto contextResult = Domain::QueryResult::create(contextProvider); Utils::MockObject projectQueriesMock; projectQueriesMock(&Domain::ProjectQueries::findAll).when().thenReturn(projectResult); Utils::MockObject contextQueriesMock; contextQueriesMock(&Domain::ContextQueries::findAll).when().thenReturn(contextResult); Utils::MockObject projectRepositoryMock; Presentation::AvailableTaskPagesModel pages(projectQueriesMock.getInstance(), 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); 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 // Two projects auto project1 = Domain::Project::Ptr::create(); project1->setName("Project 1"); auto project2 = Domain::Project::Ptr::create(); project2->setName("Project 2"); auto projectProvider = Domain::QueryResultProvider::Ptr::create(); auto projectResult = Domain::QueryResult::create(projectProvider); projectProvider->append(project1); projectProvider->append(project2); // No contexts auto contextProvider = Domain::QueryResultProvider::Ptr::create(); auto contextResult = Domain::QueryResult::create(contextProvider); Utils::MockObject projectQueriesMock; projectQueriesMock(&Domain::ProjectQueries::findAll).when().thenReturn(projectResult); Utils::MockObject contextQueriesMock; contextQueriesMock(&Domain::ContextQueries::findAll).when().thenReturn(contextResult); Utils::MockObject projectRepositoryMock; Presentation::AvailableTaskPagesModel pages(projectQueriesMock.getInstance(), 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); auto job = new FakeJob(this); job->setExpectedError(KJob::KilledJobError, "Foo"); projectRepositoryMock(&Domain::ProjectRepository::remove).when(project1).thenReturn(job); // WHEN pages.removeItem(project1Index); // THEN QTest::qWait(150); QCOMPARE(errorHandler.m_message, QString("Cannot remove project Project 1: Foo")); } void shouldRemoveContext() { // GIVEN // Two contexts auto context1 = Domain::Context::Ptr::create(); context1->setName("context 1"); auto contextProvider = Domain::QueryResultProvider::Ptr::create(); auto contextResult = Domain::QueryResult::create(contextProvider); contextProvider->append(context1); // empty projects auto projectProvider = Domain::QueryResultProvider::Ptr::create(); auto projectResult = Domain::QueryResult::create(projectProvider); // contexts mocking Utils::MockObject contextQueriesMock; contextQueriesMock(&Domain::ContextQueries::findAll).when().thenReturn(contextResult); Utils::MockObject contextRepositoryMock; // projects mocking Utils::MockObject projectQueriesMock; projectQueriesMock(&Domain::ProjectQueries::findAll).when().thenReturn(projectResult); Presentation::AvailableTaskPagesModel pages(projectQueriesMock.getInstance(), 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("context 1"); auto contextProvider = Domain::QueryResultProvider::Ptr::create(); auto contextResult = Domain::QueryResult::create(contextProvider); contextProvider->append(context1); // empty projects auto projectProvider = Domain::QueryResultProvider::Ptr::create(); auto projectResult = Domain::QueryResult::create(projectProvider); // contexts mocking Utils::MockObject contextQueriesMock; contextQueriesMock(&Domain::ContextQueries::findAll).when().thenReturn(contextResult); Utils::MockObject contextRepositoryMock; // projects mocking Utils::MockObject projectQueriesMock; projectQueriesMock(&Domain::ProjectQueries::findAll).when().thenReturn(projectResult); Presentation::AvailableTaskPagesModel pages(projectQueriesMock.getInstance(), 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, "Foo"); contextRepositoryMock(&Domain::ContextRepository::remove).when(context1).thenReturn(job); // WHEN pages.removeItem(context1Index); // THEN QTest::qWait(150); QCOMPARE(errorHandler.m_message, QString("Cannot remove context context 1: Foo")); } void shouldGetAnErrorMessageWhenUpdateProjectFailed() { // GIVEN // Two projects auto project1 = Domain::Project::Ptr::create(); project1->setName("Project 1"); auto project2 = Domain::Project::Ptr::create(); project2->setName("Project 2"); auto projectProvider = Domain::QueryResultProvider::Ptr::create(); auto projectResult = Domain::QueryResult::create(projectProvider); projectProvider->append(project1); projectProvider->append(project2); // Two contexts auto context1 = Domain::Context::Ptr::create(); context1->setName("context 1"); auto context2 = Domain::Context::Ptr::create(); context2->setName("context 2"); auto contextProvider = Domain::QueryResultProvider::Ptr::create(); auto contextResult = Domain::QueryResult::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 projectQueriesMock; projectQueriesMock(&Domain::ProjectQueries::findAll).when().thenReturn(projectResult); Utils::MockObject projectRepositoryMock; Utils::MockObject taskRepositoryMock; Utils::MockObject contextQueriesMock; contextQueriesMock(&Domain::ContextQueries::findAll).when().thenReturn(contextResult); Utils::MockObject contextRepositoryMock; Presentation::AvailableTaskPagesModel pages(projectQueriesMock.getInstance(), 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); // WHEN auto job = new FakeJob(this); job->setExpectedError(KJob::KilledJobError, "Foo"); projectRepositoryMock(&Domain::ProjectRepository::update).when(project1).thenReturn(job); QVERIFY(model->setData(project1Index, "New Project 1")); // THEN QTest::qWait(150); QCOMPARE(errorHandler.m_message, QString("Cannot modify project Project 1: Foo")); } void shouldGetAnErrorMessageWhenUpdateContextFailed() { // GIVEN // Two projects auto project1 = Domain::Project::Ptr::create(); project1->setName("Project 1"); auto project2 = Domain::Project::Ptr::create(); project2->setName("Project 2"); auto projectProvider = Domain::QueryResultProvider::Ptr::create(); auto projectResult = Domain::QueryResult::create(projectProvider); projectProvider->append(project1); projectProvider->append(project2); // Two contexts auto context1 = Domain::Context::Ptr::create(); context1->setName("context 1"); auto context2 = Domain::Context::Ptr::create(); context2->setName("context 2"); auto contextProvider = Domain::QueryResultProvider::Ptr::create(); auto contextResult = Domain::QueryResult::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 projectQueriesMock; projectQueriesMock(&Domain::ProjectQueries::findAll).when().thenReturn(projectResult); Utils::MockObject projectRepositoryMock; Utils::MockObject taskRepositoryMock; Utils::MockObject contextQueriesMock; contextQueriesMock(&Domain::ContextQueries::findAll).when().thenReturn(contextResult); Utils::MockObject contextRepositoryMock; Presentation::AvailableTaskPagesModel pages(projectQueriesMock.getInstance(), 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, "Foo"); contextRepositoryMock(&Domain::ContextRepository::update).when(context1).thenReturn(job); QVERIFY(model->setData(context1Index, "New Context 1")); // THEN QTest::qWait(150); QCOMPARE(errorHandler.m_message, QString("Cannot modify context context 1: Foo")); } void shouldGetAnErrorMessageWhenAssociateProjectFailed() { // GIVEN // Two projects auto project1 = Domain::Project::Ptr::create(); project1->setName("Project 1"); auto project2 = Domain::Project::Ptr::create(); project2->setName("Project 2"); auto projectProvider = Domain::QueryResultProvider::Ptr::create(); auto projectResult = Domain::QueryResult::create(projectProvider); projectProvider->append(project1); projectProvider->append(project2); // Two contexts auto context1 = Domain::Context::Ptr::create(); context1->setName("context 1"); auto context2 = Domain::Context::Ptr::create(); context2->setName("context 2"); auto contextProvider = Domain::QueryResultProvider::Ptr::create(); auto contextResult = Domain::QueryResult::create(contextProvider); contextProvider->append(context1); contextProvider->append(context2); // Two artifacts (used for dropping later on) Domain::Artifact::Ptr taskToDrop(new Domain::Task); taskToDrop->setTitle("taskDropped"); Domain::Artifact::Ptr noteToDrop(new Domain::Note); Utils::MockObject projectQueriesMock; projectQueriesMock(&Domain::ProjectQueries::findAll).when().thenReturn(projectResult); Utils::MockObject projectRepositoryMock; Utils::MockObject taskRepositoryMock; Utils::MockObject contextQueriesMock; contextQueriesMock(&Domain::ContextQueries::findAll).when().thenReturn(contextResult); Utils::MockObject contextRepositoryMock; Presentation::AvailableTaskPagesModel pages(projectQueriesMock.getInstance(), 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); // WHEN auto job = new FakeJob(this); job->setExpectedError(KJob::KilledJobError, "Foo"); projectRepositoryMock(&Domain::ProjectRepository::associate).when(project1, taskToDrop).thenReturn(job); QMimeData *data = new QMimeData; data->setData("application/x-zanshin-object", "object"); data->setProperty("objects", QVariant::fromValue(Domain::Artifact::List() << taskToDrop)); model->dropMimeData(data, Qt::MoveAction, -1, -1, project1Index); // THEN QTest::qWait(150); QCOMPARE(errorHandler.m_message, QString("Cannot add taskDropped to project Project 1: Foo")); } void shouldGetAnErrorMessageWhenAssociateContextFailed() { // GIVEN // Two projects auto project1 = Domain::Project::Ptr::create(); project1->setName("Project 1"); auto project2 = Domain::Project::Ptr::create(); project2->setName("Project 2"); auto projectProvider = Domain::QueryResultProvider::Ptr::create(); auto projectResult = Domain::QueryResult::create(projectProvider); projectProvider->append(project1); projectProvider->append(project2); // Two contexts auto context1 = Domain::Context::Ptr::create(); context1->setName("context 1"); auto context2 = Domain::Context::Ptr::create(); context2->setName("context 2"); auto contextProvider = Domain::QueryResultProvider::Ptr::create(); auto contextResult = Domain::QueryResult::create(contextProvider); contextProvider->append(context1); contextProvider->append(context2); // Two artifacts (used for dropping later on) Domain::Artifact::Ptr taskToDrop(new Domain::Task); taskToDrop->setTitle("taskDropped"); Utils::MockObject projectQueriesMock; projectQueriesMock(&Domain::ProjectQueries::findAll).when().thenReturn(projectResult); Utils::MockObject projectRepositoryMock; Utils::MockObject taskRepositoryMock; Utils::MockObject contextQueriesMock; contextQueriesMock(&Domain::ContextQueries::findAll).when().thenReturn(contextResult); Utils::MockObject contextRepositoryMock; Presentation::AvailableTaskPagesModel pages(projectQueriesMock.getInstance(), 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, "Foo"); contextRepositoryMock(&Domain::ContextRepository::associate).when(context1, taskToDrop.objectCast()).thenReturn(job); auto data = new QMimeData; data->setData("application/x-zanshin-object", "object"); data->setProperty("objects", QVariant::fromValue(Domain::Artifact::List() << taskToDrop)); model->dropMimeData(data, Qt::MoveAction, -1, -1, context1Index); // THEN QTest::qWait(150); QCOMPARE(errorHandler.m_message, QString("Cannot add taskDropped to context context 1: Foo")); } void shouldGetAnErrorMessageWhenDissociateFailed() { // GIVEN // No project auto projectProvider = Domain::QueryResultProvider::Ptr::create(); auto projectResult = Domain::QueryResult::create(projectProvider); // No context auto contextProvider = Domain::QueryResultProvider::Ptr::create(); auto contextResult = Domain::QueryResult::create(contextProvider); // Two artifacts (used for dropping later on) Domain::Artifact::Ptr taskToDrop(new Domain::Task); taskToDrop->setTitle("taskDropped"); Utils::MockObject projectQueriesMock; projectQueriesMock(&Domain::ProjectQueries::findAll).when().thenReturn(projectResult); Utils::MockObject projectRepositoryMock; Utils::MockObject taskRepositoryMock; Utils::MockObject contextQueriesMock; contextQueriesMock(&Domain::ContextQueries::findAll).when().thenReturn(contextResult); Utils::MockObject contextRepositoryMock; Presentation::AvailableTaskPagesModel pages(projectQueriesMock.getInstance(), 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, "Foo"); projectRepositoryMock(&Domain::ProjectRepository::dissociate).when(taskToDrop).thenReturn(job); taskRepositoryMock(&Domain::TaskRepository::dissociateAll).when(taskToDrop.objectCast()).thenReturn(new FakeJob(this)); auto data = new QMimeData; data->setData("application/x-zanshin-object", "object"); data->setProperty("objects", QVariant::fromValue(Domain::Artifact::List() << taskToDrop)); model->dropMimeData(data, Qt::MoveAction, -1, -1, inboxIndex); // THEN QTest::qWait(150); QCOMPARE(errorHandler.m_message, QString("Cannot move taskDropped to Inbox: Foo")); } }; QTEST_MAIN(AvailableTaskPagesModelTest) #include "availabletaskpagesmodeltest.moc"