diff --git a/CMakeLists.txt b/CMakeLists.txt index 23069ad5..86d58408 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,89 +1,89 @@ project(zanshin) cmake_minimum_required(VERSION 3.2) find_package(ECM REQUIRED CONFIG) set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/ ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH}) include(GenerateExportHeader) include(KDEInstallDirs) include(KDECompilerSettings NO_POLICY_SCOPE) include(KDECMakeSettings) include(FeatureSummary) include(ECMInstallIcons) include(ECMMarkAsTest) include(ECMPoQmTools) -set(REQUIRED_QT_VERSION 5.10.0) +set(REQUIRED_QT_VERSION 5.12.0) find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED Core Gui Widgets Test) find_package(Boost REQUIRED) find_package(Threads REQUIRED) macro(assert_min_ver version) set(error_msg "${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_VERSION} not supported") if("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS "${version}") message(FATAL_ERROR "${msg}") endif() endmacro() if(APPLE) if((NOT "${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS "6.0.0.0") AND "${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS "6.0.0.6000058") # Apple Clang 6.0.0.6000057 is known to fail on some of our code using std::mem_fn # but have no issues with boost::mem_fn message("problematic Apple Clang version ${CMAKE_CXX_COMPILER_VERSION}, using boost::mem_fn") add_definitions(-DZANSHIN_USE_BOOST_MEM_FN) endif() endif() if(UNIX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++14") set(CMAKE_CXX_STANDARD 14) # Enable C++14, with cmake >= 3.1 set(CMAKE_CXX_EXTENSIONS OFF) # Don't enable gcc-specific extensions endif() kde_enable_exceptions() if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") endif() option(ZANSHIN_BUILD_COVERAGE "Build Zanshin with gcov support" OFF) if(ZANSHIN_BUILD_COVERAGE AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage") endif() option(ZANSHIN_BUILD_ASAN "Build Zanshin with asan support" OFF) if(ZANSHIN_BUILD_ASAN) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fno-optimize-sibling-calls") link_libraries("asan") endif() add_definitions(-DQT_USE_FAST_CONCATENATION -DQT_USE_FAST_OPERATOR_PLUS -DQT_NO_URL_CAST_FROM_STRING -DQT_STRICT_ITERATORS ) include_directories ( ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${Boost_INCLUDE_DIR} 3rdparty/kdepim/ 3rdparty/kdepim/libkdepim/ ) find_package(KF5 REQUIRED COMPONENTS AkonadiCalendar KontactInterface Runner WindowSystem I18n ) find_package(KF5Akonadi "5.1" CONFIG REQUIRED) add_subdirectory(3rdparty) add_subdirectory(src) if(BUILD_TESTING) add_subdirectory(tests) endif() diff --git a/src/presentation/taskfilterproxymodel.cpp b/src/presentation/taskfilterproxymodel.cpp index 6625d972..f33f794d 100644 --- a/src/presentation/taskfilterproxymodel.cpp +++ b/src/presentation/taskfilterproxymodel.cpp @@ -1,135 +1,136 @@ /* 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 "taskfilterproxymodel.h" #include #include "domain/task.h" #include "utils/datetime.h" #include "presentation/querytreemodelbase.h" using namespace Presentation; TaskFilterProxyModel::TaskFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent), m_sortType(TitleSort), m_showFuture(false) { setDynamicSortFilter(true); setSortCaseSensitivity(Qt::CaseInsensitive); setSortOrder(Qt::AscendingOrder); } TaskFilterProxyModel::SortType TaskFilterProxyModel::sortType() const { return m_sortType; } void TaskFilterProxyModel::setSortType(TaskFilterProxyModel::SortType type) { m_sortType = type; invalidate(); } void TaskFilterProxyModel::setSortOrder(Qt::SortOrder order) { sort(0, order); } bool TaskFilterProxyModel::showFutureTasks() const { return m_showFuture; } void TaskFilterProxyModel::setShowFutureTasks(bool show) { if (m_showFuture == show) return; m_showFuture = show; invalidate(); } static bool isFutureTask(const Domain::Task::Ptr &task) { if (!task) return false; if (!task->startDate().isValid()) return false; return task->startDate() > Utils::DateTime::currentDate(); } bool TaskFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); const auto task = index.data(QueryTreeModelBase::ObjectRole).value(); if (task) { - QRegExp regexp = filterRegExp(); - regexp.setCaseSensitivity(Qt::CaseInsensitive); + QRegularExpression regexp = filterRegularExpression(); + regexp.setPatternOptions(QRegularExpression::CaseInsensitiveOption); if (task->title().contains(regexp) || task->text().contains(regexp)) { return m_showFuture || !isFutureTask(task); } } for (int childRow = 0; childRow < sourceModel()->rowCount(index); childRow++) { if (filterAcceptsRow(childRow, index)) return true; } return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent); } static QDate validDate(const QDate &date = QDate()) { if (date.isValid()) return date; return QDate(80000, 12, 31); } bool TaskFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const { if (m_sortType != DateSort) return QSortFilterProxyModel::lessThan(left, right); const auto leftTask = left.data(QueryTreeModelBase::ObjectRole).value(); const auto rightTask = right.data(QueryTreeModelBase::ObjectRole).value(); // The addDays(1) is so that we sort non-tasks (e.g. notes) at the end const QDate leftDue = leftTask ? validDate(leftTask->dueDate()) : validDate().addDays(1); const QDate rightDue = rightTask ? validDate(rightTask->dueDate()) : validDate().addDays(1); const QDate leftStart = leftTask ? validDate(leftTask->startDate()) : validDate().addDays(1); const QDate rightStart = rightTask ? validDate(rightTask->startDate()) : validDate().addDays(1); return leftDue < rightDue || leftStart < rightStart; } diff --git a/src/widgets/filterwidget.cpp b/src/widgets/filterwidget.cpp index 23739609..7f2bbc69 100644 --- a/src/widgets/filterwidget.cpp +++ b/src/widgets/filterwidget.cpp @@ -1,96 +1,96 @@ /* 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 "filterwidget.h" #include #include #include #include #include #include "presentation/taskfilterproxymodel.h" #include "ui_filterwidget.h" using namespace Widgets; FilterWidget::FilterWidget(QWidget *parent) : QWidget(parent), ui(new Ui::FilterWidget), m_model(new Presentation::TaskFilterProxyModel(this)) { ui->setupUi(this); ui->extension->hide(); ui->sortTypeCombo->addItem(i18n("Sort by title"), Presentation::TaskFilterProxyModel::TitleSort); ui->sortTypeCombo->addItem(i18n("Sort by date"), Presentation::TaskFilterProxyModel::DateSort); setFocusProxy(ui->filterEdit); connect(ui->filterEdit, &QLineEdit::textChanged, this, &FilterWidget::onTextChanged); connect(ui->sortTypeCombo, static_cast(&QComboBox::currentIndexChanged), this, &FilterWidget::onSortTypeChanged); connect(ui->ascendingButton, &QToolButton::clicked, this, &FilterWidget::onAscendingClicked); connect(ui->descendingButton, &QToolButton::clicked, this, &FilterWidget::onDescendingClicked); } FilterWidget::~FilterWidget() { delete ui; } Presentation::TaskFilterProxyModel *FilterWidget::proxyModel() const { return m_model; } void FilterWidget::clear() { ui->filterEdit->clear(); } void FilterWidget::setShowFutureTasks(bool show) { m_model->setShowFutureTasks(show); } void FilterWidget::onTextChanged(const QString &text) { - m_model->setFilterFixedString(text); + m_model->setFilterRegularExpression(QRegularExpression::escape(text)); } void FilterWidget::onSortTypeChanged(int index) { const int data = ui->sortTypeCombo->itemData(index).toInt(); m_model->setSortType(Presentation::TaskFilterProxyModel::SortType(data)); } void FilterWidget::onAscendingClicked() { m_model->setSortOrder(Qt::AscendingOrder); } void FilterWidget::onDescendingClicked() { m_model->setSortOrder(Qt::DescendingOrder); } diff --git a/src/widgets/quickselectdialog.cpp b/src/widgets/quickselectdialog.cpp index 7949c38a..38d5a215 100644 --- a/src/widgets/quickselectdialog.cpp +++ b/src/widgets/quickselectdialog.cpp @@ -1,130 +1,130 @@ /* This file is part of Zanshin Copyright 2014 Kevin Ottens Copyright 2015 Franck Arrecot 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 "quickselectdialog.h" #include #include #include #include #include #include #include #include #include using namespace Widgets; QuickSelectDialog::QuickSelectDialog(QWidget *parent) : QDialog(parent), m_model(nullptr), m_filterProxyModel(new QSortFilterProxyModel(this)), m_label(new QLabel(this)), m_tree(new QTreeView(this)) { setWindowTitle(i18n("Quick Select Dialog")); m_label->setText(i18n("You can start typing to filter the list of available pages")); m_filterProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); m_filterProxyModel->setRecursiveFilteringEnabled(true); m_tree->setModel(m_filterProxyModel); m_tree->setObjectName(QStringLiteral("pagesView")); m_tree->header()->hide(); m_tree->expandAll(); m_tree->setFocus(); m_tree->setSelectionMode(QAbstractItemView::SingleSelection); m_tree->setSortingEnabled(false); m_tree->installEventFilter(this); auto buttonBox = new QDialogButtonBox(this); buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); auto mainLayout = new QVBoxLayout(this); mainLayout->addWidget(m_label); mainLayout->addWidget(m_tree); mainLayout->addWidget(buttonBox); connect(buttonBox, &QDialogButtonBox::accepted, this, &QuickSelectDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &QuickSelectDialog::reject); } int QuickSelectDialog::exec() { return QDialog::exec(); } QPersistentModelIndex QuickSelectDialog::selectedIndex() const { QModelIndex selected = m_tree->currentIndex(); return m_filterProxyModel->mapToSource(selected); } void QuickSelectDialog::applyFilterChanged(const QString &textFilter) { if (textFilter.isEmpty()) m_label->setText(i18n("You can start typing to filter the list of available pages")); else m_label->setText(i18n("Path: %1", textFilter)); - m_filterProxyModel->setFilterFixedString(textFilter); + m_filterProxyModel->setFilterRegularExpression(QRegularExpression::escape(textFilter)); m_tree->expandAll(); } bool QuickSelectDialog::eventFilter(QObject *, QEvent *ev) { if (ev->type() == QEvent::KeyPress) { auto event = static_cast(ev); - auto filter = m_filterProxyModel->filterRegExp().pattern(); + auto filter = m_filterProxyModel->filterRegularExpression().pattern(); switch (event->key()) { case Qt::Key_Backspace: filter.chop(1); break; case Qt::Key_Delete: filter = QString(); break; default: - if (event->text().contains(QRegExp("^(\\w| )+$"))) { + if (event->text().contains(QRegularExpression(QStringLiteral("^(\\w| )+$")))) { filter += event->text(); } break; } applyFilterChanged(filter); } return false; } void QuickSelectDialog::setModel(QAbstractItemModel *model) { if (model == m_model) return; m_model = model; m_filterProxyModel->setSourceModel(m_model); m_tree->expandAll(); } diff --git a/tests/units/presentation/taskfilterproxymodeltest.cpp b/tests/units/presentation/taskfilterproxymodeltest.cpp index fbf4e5ec..f6c37572 100644 --- a/tests/units/presentation/taskfilterproxymodeltest.cpp +++ b/tests/units/presentation/taskfilterproxymodeltest.cpp @@ -1,271 +1,271 @@ /* 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 #include "domain/task.h" #include "presentation/querytreemodelbase.h" #include "presentation/taskfilterproxymodel.h" #include "utils/datetime.h" Q_DECLARE_METATYPE(QList) class TaskFilterProxyModelTest : public QObject { Q_OBJECT private: QStandardItem *createTaskItem(const QString &title, const QString &text, const QDate &start = QDate(), const QDate &due = QDate()) const { auto task = Domain::Task::Ptr::create(); task->setTitle(title); task->setText(text); task->setStartDate(start); task->setDueDate(due); auto item = new QStandardItem; item->setData(task->title(), Qt::DisplayRole); item->setData(QVariant::fromValue(task), Presentation::QueryTreeModelBase::ObjectRole); return item; } private slots: void initTestCase() { qputenv("ZANSHIN_OVERRIDE_DATE", "2015-03-11"); } void shouldHaveDefaultState() { Presentation::TaskFilterProxyModel proxy; QVERIFY(!proxy.sourceModel()); QCOMPARE(proxy.sortColumn(), 0); QCOMPARE(proxy.sortOrder(), Qt::AscendingOrder); QCOMPARE(proxy.sortType(), Presentation::TaskFilterProxyModel::TitleSort); QCOMPARE(proxy.sortCaseSensitivity(), Qt::CaseInsensitive); QVERIFY(!proxy.showFutureTasks()); } void shouldFilterByTextAndTitle() { // GIVEN QStandardItemModel input; input.appendRow(createTaskItem(QStringLiteral("1. foo"), QStringLiteral("find me"))); input.appendRow(createTaskItem(QStringLiteral("2. Find Me"), QStringLiteral("bar"))); input.appendRow(createTaskItem(QStringLiteral("3. baz"), QStringLiteral("baz"))); Presentation::TaskFilterProxyModel output; output.setSourceModel(&input); // WHEN - output.setFilterFixedString(QStringLiteral("find me")); + output.setFilterRegularExpression(QStringLiteral("find me")); // THEN QCOMPARE(output.rowCount(), 2); QCOMPARE(output.index(0, 0).data().toString(), QStringLiteral("1. foo")); QCOMPARE(output.index(1, 0).data().toString(), QStringLiteral("2. Find Me")); } void shouldFilterByStartDate() { // GIVEN QStandardItemModel input; const auto today = Utils::DateTime::currentDate(); input.appendRow(createTaskItem(QStringLiteral("1. past"), QString(), today.addDays(-1))); input.appendRow(createTaskItem(QStringLiteral("2. present"), QString(), today)); input.appendRow(createTaskItem(QStringLiteral("3. future"), QString(), today.addDays(1))); input.appendRow(createTaskItem(QStringLiteral("4. whatever"), QString())); Presentation::TaskFilterProxyModel output; output.setSourceModel(&input); // WHEN output.setShowFutureTasks(true); // THEN QCOMPARE(output.rowCount(), 4); QCOMPARE(output.index(0, 0).data().toString(), QStringLiteral("1. past")); QCOMPARE(output.index(1, 0).data().toString(), QStringLiteral("2. present")); QCOMPARE(output.index(2, 0).data().toString(), QStringLiteral("3. future")); QCOMPARE(output.index(3, 0).data().toString(), QStringLiteral("4. whatever")); // WHEN output.setShowFutureTasks(false); // THEN QCOMPARE(output.rowCount(), 3); QCOMPARE(output.index(0, 0).data().toString(), QStringLiteral("1. past")); QCOMPARE(output.index(1, 0).data().toString(), QStringLiteral("2. present")); QCOMPARE(output.index(2, 0).data().toString(), QStringLiteral("4. whatever")); } void shouldKeepRowIfItHasAcceptableChildren() { // GIVEN QStandardItemModel input; input.appendRow(createTaskItem(QStringLiteral("1. foo"), QStringLiteral("find me"))); QStandardItem *item = createTaskItem(QStringLiteral("2. baz"), QStringLiteral("baz")); item->appendRow(createTaskItem(QStringLiteral("21. bar"), QStringLiteral("bar"))); item->appendRow(createTaskItem(QStringLiteral("22. find me"), QStringLiteral("foo"))); input.appendRow(item); input.appendRow(createTaskItem(QStringLiteral("3. baz"), QStringLiteral("baz"))); Presentation::TaskFilterProxyModel output; output.setSourceModel(&input); // WHEN - output.setFilterFixedString(QStringLiteral("find me")); + output.setFilterRegularExpression(QStringLiteral("find me")); // THEN QCOMPARE(output.rowCount(), 2); QCOMPARE(output.index(0, 0).data().toString(), QStringLiteral("1. foo")); QCOMPARE(output.index(1, 0).data().toString(), QStringLiteral("2. baz")); const QModelIndex parent = output.index(1, 0); QCOMPARE(output.rowCount(parent), 1); QCOMPARE(output.index(0, 0, parent).data().toString(), QStringLiteral("22. find me")); } void shouldSortFollowingType_data() { QTest::addColumn("sortType"); QTest::addColumn("sortOrder"); QTest::addColumn>("inputItems"); QTest::addColumn("expectedOutputTitles"); QList inputItems; QStringList expectedOutputTitles; inputItems.clear(); expectedOutputTitles.clear(); inputItems << createTaskItem(QStringLiteral("B"), QStringLiteral("foo")) << createTaskItem(QStringLiteral("C"), QStringLiteral("foo")); expectedOutputTitles << QStringLiteral("B") << QStringLiteral("C"); QTest::newRow("title ascending") << int(Presentation::TaskFilterProxyModel::TitleSort) << int(Qt::AscendingOrder) << inputItems << expectedOutputTitles; inputItems.clear(); expectedOutputTitles.clear(); inputItems << createTaskItem(QStringLiteral("B"), QStringLiteral("foo")) << createTaskItem(QStringLiteral("C"), QStringLiteral("foo")); expectedOutputTitles << QStringLiteral("C") << QStringLiteral("B"); QTest::newRow("title descending") << int(Presentation::TaskFilterProxyModel::TitleSort) << int(Qt::DescendingOrder) << inputItems << expectedOutputTitles; inputItems.clear(); expectedOutputTitles.clear(); inputItems << createTaskItem(QStringLiteral("B"), QStringLiteral("foo"), QDate(2014, 03, 10)) << createTaskItem(QStringLiteral("C"), QStringLiteral("foo"), QDate(2014, 03, 01)) << createTaskItem(QStringLiteral("D"), QStringLiteral("foo")); expectedOutputTitles << QStringLiteral("C") << QStringLiteral("B") << QStringLiteral("D"); QTest::newRow("start date ascending") << int(Presentation::TaskFilterProxyModel::DateSort) << int(Qt::AscendingOrder) << inputItems << expectedOutputTitles; inputItems.clear(); expectedOutputTitles.clear(); inputItems << createTaskItem(QStringLiteral("B"), QStringLiteral("foo"), QDate(2014, 03, 10)) << createTaskItem(QStringLiteral("C"), QStringLiteral("foo"), QDate(2014, 03, 01)) << createTaskItem(QStringLiteral("D"), QStringLiteral("foo")); expectedOutputTitles << QStringLiteral("D") << QStringLiteral("B") << QStringLiteral("C"); QTest::newRow("start date descending") << int(Presentation::TaskFilterProxyModel::DateSort) << int(Qt::DescendingOrder) << inputItems << expectedOutputTitles; inputItems.clear(); expectedOutputTitles.clear(); inputItems << createTaskItem(QStringLiteral("B"), QStringLiteral("foo"), QDate(), QDate(2014, 03, 10)) << createTaskItem(QStringLiteral("C"), QStringLiteral("foo"), QDate(), QDate(2014, 03, 01)) << createTaskItem(QStringLiteral("D"), QStringLiteral("foo")); expectedOutputTitles << QStringLiteral("C") << QStringLiteral("B") << QStringLiteral("D"); QTest::newRow("due date ascending") << int(Presentation::TaskFilterProxyModel::DateSort) << int(Qt::AscendingOrder) << inputItems << expectedOutputTitles; inputItems.clear(); expectedOutputTitles.clear(); inputItems << createTaskItem(QStringLiteral("B"), QStringLiteral("foo"), QDate(), QDate(2014, 03, 10)) << createTaskItem(QStringLiteral("C"), QStringLiteral("foo"), QDate(), QDate(2014, 03, 01)) << createTaskItem(QStringLiteral("D"), QStringLiteral("foo")); expectedOutputTitles << QStringLiteral("D") << QStringLiteral("B") << QStringLiteral("C"); QTest::newRow("due date descending") << int(Presentation::TaskFilterProxyModel::DateSort) << int(Qt::DescendingOrder) << inputItems << expectedOutputTitles; inputItems.clear(); expectedOutputTitles.clear(); inputItems << createTaskItem(QStringLiteral("A"), QStringLiteral("foo"), QDate(2014, 03, 01), QDate(2014, 03, 10)) << createTaskItem(QStringLiteral("B"), QStringLiteral("foo"), QDate(2014, 03, 10), QDate(2014, 03, 01)); expectedOutputTitles << QStringLiteral("B") << QStringLiteral("A"); QTest::newRow("due date over start date") << int(Presentation::TaskFilterProxyModel::DateSort) << int(Qt::AscendingOrder) << inputItems << expectedOutputTitles; inputItems.clear(); expectedOutputTitles.clear(); inputItems << createTaskItem(QStringLiteral("A"), QStringLiteral("foo"), QDate(), QDate(2014, 03, 10)) << createTaskItem(QStringLiteral("B"), QStringLiteral("foo"), QDate(2014, 03, 01), QDate()); expectedOutputTitles << QStringLiteral("B") << QStringLiteral("A"); QTest::newRow("due date over start date") << int(Presentation::TaskFilterProxyModel::DateSort) << int(Qt::AscendingOrder) << inputItems << expectedOutputTitles; } void shouldSortFollowingType() { // GIVEN QFETCH(int, sortType); QFETCH(int, sortOrder); QFETCH(QList, inputItems); QFETCH(QStringList, expectedOutputTitles); QStandardItemModel input; foreach (QStandardItem *item, inputItems) { input.appendRow(item); } // WHEN Presentation::TaskFilterProxyModel output; output.setSourceModel(&input); output.setSortType(Presentation::TaskFilterProxyModel::SortType(sortType)); output.setSortOrder(Qt::SortOrder(sortOrder)); QStringList outputTitles; outputTitles.reserve(output.rowCount()); for (int row = 0; row < output.rowCount(); row++) { outputTitles << output.index(row, 0).data().toString(); } // THEN QCOMPARE(outputTitles, expectedOutputTitles); } }; ZANSHIN_TEST_MAIN(TaskFilterProxyModelTest) #include "taskfilterproxymodeltest.moc" diff --git a/tests/units/widgets/filterwidgettest.cpp b/tests/units/widgets/filterwidgettest.cpp index 123b72f9..47441c53 100644 --- a/tests/units/widgets/filterwidgettest.cpp +++ b/tests/units/widgets/filterwidgettest.cpp @@ -1,217 +1,217 @@ /* 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 #include #include #include #include #include "widgets/filterwidget.h" #include "presentation/taskfilterproxymodel.h" class FilterWidgetTest : public QObject { Q_OBJECT private slots: void shouldHaveDefaultState() { Widgets::FilterWidget filter; QVERIFY(filter.proxyModel()); QVERIFY(!filter.proxyModel()->sourceModel()); - QCOMPARE(filter.proxyModel()->filterRegExp(), QRegExp()); + QCOMPARE(filter.proxyModel()->filterRegularExpression(), QRegularExpression()); QCOMPARE(filter.proxyModel()->sortOrder(), Qt::AscendingOrder); QCOMPARE(filter.proxyModel()->sortType(), Presentation::TaskFilterProxyModel::TitleSort); QLineEdit *filterEdit = filter.findChild(QStringLiteral("filterEdit")); QVERIFY(filterEdit); QVERIFY(filterEdit->isVisibleTo(&filter)); QVERIFY(filterEdit->text().isEmpty()); QCOMPARE(filterEdit->placeholderText(), i18n("Filter...")); auto extensionButton = filter.findChild(QStringLiteral("extensionButton")); QVERIFY(extensionButton); QVERIFY(extensionButton->isVisibleTo(&filter)); QVERIFY(!extensionButton->isChecked()); QVERIFY(extensionButton->autoRaise()); QComboBox *sortTypeCombo = filter.findChild(QStringLiteral("sortTypeCombo")); QVERIFY(sortTypeCombo); QVERIFY(!sortTypeCombo->isVisibleTo(&filter)); QCOMPARE(sortTypeCombo->currentIndex(), 0); auto ascendingButton = filter.findChild(QStringLiteral("ascendingButton")); QVERIFY(ascendingButton); QVERIFY(!ascendingButton->isVisibleTo(&filter)); QVERIFY(ascendingButton->isChecked()); QVERIFY(ascendingButton->autoRaise()); auto descendingButton = filter.findChild(QStringLiteral("descendingButton")); QVERIFY(descendingButton); QVERIFY(!descendingButton->isVisibleTo(&filter)); QVERIFY(!descendingButton->isChecked()); QVERIFY(descendingButton->autoRaise()); } void shouldChangeAppliedFilter() { // GIVEN Widgets::FilterWidget filter; QLineEdit *filterEdit = filter.findChild(QStringLiteral("filterEdit")); QVERIFY(filterEdit); // WHEN QTest::keyClicks(filterEdit, QStringLiteral("find me")); // THEN - QCOMPARE(filter.proxyModel()->filterRegExp().pattern(), QStringLiteral("find me")); + QCOMPARE(filter.proxyModel()->filterRegularExpression().pattern(), QStringLiteral("find\\ me")); } void shouldClearFilter() { // GIVEN Widgets::FilterWidget filter; QLineEdit *filterEdit = filter.findChild(QStringLiteral("filterEdit")); QVERIFY(filterEdit); filterEdit->setText("Foo"); // WHEN filter.clear(); // THEN QVERIFY(filterEdit->text().isEmpty()); - QVERIFY(filter.proxyModel()->filterRegExp().pattern().isEmpty()); + QVERIFY(filter.proxyModel()->filterRegularExpression().pattern().isEmpty()); } void shouldShowExtension() { // GIVEN Widgets::FilterWidget filter; QAbstractButton *extensionButton = filter.findChild(QStringLiteral("extensionButton")); QVERIFY(extensionButton); QComboBox *sortTypeCombo = filter.findChild(QStringLiteral("sortTypeCombo")); QVERIFY(sortTypeCombo); QAbstractButton *ascendingButton = filter.findChild(QStringLiteral("ascendingButton")); QVERIFY(ascendingButton); QAbstractButton *descendingButton = filter.findChild(QStringLiteral("descendingButton")); QVERIFY(descendingButton); // WHEN extensionButton->click(); // THEN QVERIFY(extensionButton->isChecked()); QVERIFY(sortTypeCombo->isVisibleTo(&filter)); QVERIFY(descendingButton->isVisibleTo(&filter)); QVERIFY(descendingButton->isVisibleTo(&filter)); // WHEN extensionButton->click(); // THEN QVERIFY(!extensionButton->isChecked()); QVERIFY(!sortTypeCombo->isVisibleTo(&filter)); QVERIFY(!descendingButton->isVisibleTo(&filter)); QVERIFY(!descendingButton->isVisibleTo(&filter)); } void shouldChangeSortType() { // GIVEN Widgets::FilterWidget filter; QComboBox *sortTypeCombo = filter.findChild(QStringLiteral("sortTypeCombo")); QVERIFY(sortTypeCombo); // WHEN sortTypeCombo->setCurrentIndex(1); // THEN QCOMPARE(filter.proxyModel()->sortType(), Presentation::TaskFilterProxyModel::DateSort); // WHEN sortTypeCombo->setCurrentIndex(0); // THEN QCOMPARE(filter.proxyModel()->sortType(), Presentation::TaskFilterProxyModel::TitleSort); } void shouldChangeSortOrder() { // GIVEN Widgets::FilterWidget filter; QAbstractButton *ascendingButton = filter.findChild(QStringLiteral("ascendingButton")); QVERIFY(ascendingButton); QAbstractButton *descendingButton = filter.findChild(QStringLiteral("descendingButton")); QVERIFY(descendingButton); // WHEN descendingButton->click(); // THEN QVERIFY(!ascendingButton->isChecked()); QVERIFY(descendingButton->isChecked()); QCOMPARE(filter.proxyModel()->sortOrder(), Qt::DescendingOrder); // WHEN ascendingButton->click(); // THEN QVERIFY(ascendingButton->isChecked()); QVERIFY(!descendingButton->isChecked()); QCOMPARE(filter.proxyModel()->sortOrder(), Qt::AscendingOrder); } void shouldShowFutureTasks() { // GIVEN Widgets::FilterWidget filter; filter.setShowFutureTasks(false); // THEN QVERIFY(!filter.proxyModel()->showFutureTasks()); // WHEN filter.setShowFutureTasks(true); // THEN QVERIFY(filter.proxyModel()->showFutureTasks()); } }; ZANSHIN_TEST_MAIN(FilterWidgetTest) #include "filterwidgettest.moc" diff --git a/tests/units/widgets/pageviewtest.cpp b/tests/units/widgets/pageviewtest.cpp index a126d74d..87ffcc83 100644 --- a/tests/units/widgets/pageviewtest.cpp +++ b/tests/units/widgets/pageviewtest.cpp @@ -1,899 +1,899 @@ /* 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "domain/task.h" #include "presentation/taskfilterproxymodel.h" #include "presentation/metatypes.h" #include "presentation/querytreemodelbase.h" #include "widgets/filterwidget.h" #include "widgets/itemdelegate.h" #include "widgets/pageview.h" #include "messageboxstub.h" class PageModelStub : public QObject { Q_OBJECT Q_PROPERTY(QAbstractItemModel* centralListModel READ centralListModel) public: void setProxyModel(QAbstractProxyModel *proxy) { proxyModel = proxy; proxyModel->setSourceModel(&itemModel); } QAbstractItemModel *centralListModel() { if (proxyModel) return proxyModel; return &itemModel; } QStandardItem *addStubItem(const QString &title, QStandardItem *parentItem = nullptr) { QStandardItem *item = new QStandardItem; item->setData(title, Qt::DisplayRole); if (!parentItem) itemModel.appendRow(item); else parentItem->appendRow(item); taskNames << title; return item; } Domain::Task::Ptr addTaskItem(const QString &title, QStandardItem *parentItem = nullptr) { auto item = addStubItem(title, parentItem); auto task = Domain::Task::Ptr::create(); task->setTitle(title); item->setData(QVariant::fromValue(task), Presentation::QueryTreeModelBase::ObjectRole); return task; } void addStubItems(const QStringList &list) { foreach (const QString &title, list) { addStubItem(title); } } public slots: void addItem(const QString &name, const QModelIndex &parentIndex) { taskNames << name; parentIndices << parentIndex; } void removeItem(const QModelIndex &index) { removedIndices << index; } void promoteItem(const QModelIndex &index) { promotedIndices << index; } public: QStringList taskNames; QList parentIndices; QList removedIndices; QList promotedIndices; QStandardItemModel itemModel; QAbstractProxyModel *proxyModel = nullptr; }; class RunningTaskModelStub : public Presentation::RunningTaskModelInterface { Q_OBJECT public: Domain::Task::Ptr runningTask() const override { return m_runningTask; } void setRunningTask(const Domain::Task::Ptr &task) override { m_runningTask = task; } void taskDeleted(const Domain::Task::Ptr &task) override { m_deletedTask = task; } void stopTask() override {} void doneTask() override {} private: Domain::Task::Ptr m_runningTask; Domain::Task::Ptr m_deletedTask; }; class PageViewTest : public QObject { Q_OBJECT private: KConfigGroup configGroup() { return KConfigGroup(KSharedConfig::openConfig(), "General"); } private slots: void shouldHaveDefaultState() { Widgets::PageView page; QCOMPARE(page.contentsMargins(), QMargins(0, 0, 0, 0)); QCOMPARE(page.layout()->contentsMargins(), QMargins(0, 0, 0, 3)); auto messageWidget = page.findChild(QStringLiteral("messageWidget")); QVERIFY(messageWidget); QVERIFY(!messageWidget->isVisibleTo(&page)); QVERIFY(!messageWidget->isCloseButtonVisible()); QVERIFY(messageWidget->wordWrap()); QVERIFY(messageWidget->text().isEmpty()); QVERIFY(messageWidget->icon().isNull()); QCOMPARE(messageWidget->messageType(), KMessageWidget::Error); QVERIFY(!messageWidget->isShowAnimationRunning()); QVERIFY(!messageWidget->isHideAnimationRunning()); auto centralView = page.findChild(QStringLiteral("centralView")); QVERIFY(centralView); QVERIFY(centralView->isVisibleTo(&page)); QVERIFY(!centralView->header()->isVisibleTo(&page)); QVERIFY(qobject_cast(centralView->itemDelegate())); QVERIFY(centralView->alternatingRowColors()); QCOMPARE(centralView->dragDropMode(), QTreeView::DragDrop); auto filter = page.findChild(QStringLiteral("filterWidget")); QVERIFY(filter); QVERIFY(!filter->isVisibleTo(&page)); QVERIFY(filter->proxyModel()); QCOMPARE(filter->proxyModel(), centralView->model()); QLineEdit *quickAddEdit = page.findChild(QStringLiteral("quickAddEdit")); QVERIFY(quickAddEdit); QVERIFY(quickAddEdit->isVisibleTo(&page)); QVERIFY(quickAddEdit->text().isEmpty()); QCOMPARE(quickAddEdit->placeholderText(), i18n("Type and press enter to add a task")); auto addAction = page.findChild(QStringLiteral("addItemAction")); QVERIFY(addAction); auto cancelAddAction = page.findChild(QStringLiteral("cancelAddItemAction")); QVERIFY(cancelAddAction); auto removeAction = page.findChild(QStringLiteral("removeItemAction")); QVERIFY(removeAction); auto promoteAction = page.findChild(QStringLiteral("promoteItemAction")); QVERIFY(promoteAction); auto filterAction = page.findChild(QStringLiteral("filterViewAction")); QVERIFY(filterAction); QVERIFY(filterAction->isCheckable()); QVERIFY(!filterAction->isChecked()); auto futureAction = page.findChild(QStringLiteral("futureViewAction")); QVERIFY(futureAction); QVERIFY(futureAction->isCheckable()); QVERIFY(futureAction->isChecked()); auto runTaskAction = page.findChild(QStringLiteral("runTaskAction")); QVERIFY(runTaskAction); QVERIFY(!runTaskAction->isEnabled()); auto actions = page.globalActions(); QCOMPARE(actions.value(QStringLiteral("page_view_add")), addAction); QCOMPARE(actions.value(QStringLiteral("page_view_remove")), removeAction); QCOMPARE(actions.value(QStringLiteral("page_view_promote")), promoteAction); QCOMPARE(actions.value(QStringLiteral("page_view_filter")), filterAction); QCOMPARE(actions.value(QStringLiteral("page_view_future")), futureAction); QCOMPARE(actions.value(QStringLiteral("page_run_task")), runTaskAction); } void shouldDisplayListFromPageModel() { // GIVEN QStandardItemModel model; QObject stubPageModel; stubPageModel.setProperty("centralListModel", QVariant::fromValue(static_cast(&model))); Widgets::PageView page; auto centralView = page.findChild(QStringLiteral("centralView")); QVERIFY(centralView); auto proxyModel = qobject_cast(centralView->model()); QVERIFY(proxyModel); QVERIFY(!proxyModel->sourceModel()); // WHEN page.setModel(&stubPageModel); // THEN QCOMPARE(page.model(), &stubPageModel); QVERIFY(page.isEnabled()); QCOMPARE(proxyModel->sourceModel(), &model); } void shouldNotCrashWithNullModel() { // GIVEN QStandardItemModel model; QObject stubPageModel; stubPageModel.setProperty("centralListModel", QVariant::fromValue(static_cast(&model))); Widgets::PageView page; page.setModel(&stubPageModel); auto centralView = page.findChild(QStringLiteral("centralView")); QVERIFY(centralView); auto proxyModel = qobject_cast(centralView->model()); QVERIFY(proxyModel); QCOMPARE(proxyModel->sourceModel(), &model); // WHEN page.setModel(nullptr); // THEN QVERIFY(!page.model()); QVERIFY(!page.isEnabled()); QVERIFY(!proxyModel->sourceModel()); } void shouldManageFocusThroughActions() { // GIVEN Widgets::PageView page; auto centralView = page.findChild(QStringLiteral("centralView")); auto quickAddEdit = page.findChild(QStringLiteral("quickAddEdit")); auto filter = page.findChild(QStringLiteral("filterWidget")); auto filterEdit = filter->findChild(); QVERIFY(filterEdit); page.show(); QVERIFY(QTest::qWaitForWindowExposed(&page)); centralView->setFocus(); QVERIFY(centralView->hasFocus()); QVERIFY(!quickAddEdit->hasFocus()); QVERIFY(!filter->isVisibleTo(&page)); QVERIFY(!filterEdit->hasFocus()); auto addAction = page.findChild(QStringLiteral("addItemAction")); auto cancelAddAction = page.findChild(QStringLiteral("cancelAddItemAction")); auto filterAction = page.findChild(QStringLiteral("filterViewAction")); // WHEN addAction->trigger(); // THEN QVERIFY(!centralView->hasFocus()); QVERIFY(quickAddEdit->hasFocus()); QVERIFY(!filter->isVisibleTo(&page)); QVERIFY(!filterEdit->hasFocus()); // WHEN cancelAddAction->trigger(); // THEN QVERIFY(centralView->hasFocus()); QVERIFY(!quickAddEdit->hasFocus()); QVERIFY(!filter->isVisibleTo(&page)); QVERIFY(!filterEdit->hasFocus()); // WHEN filterAction->trigger(); // THEN QVERIFY(!centralView->hasFocus()); QVERIFY(!quickAddEdit->hasFocus()); QVERIFY(filter->isVisibleTo(&page)); QVERIFY(filterEdit->hasFocus()); // WHEN cancelAddAction->trigger(); // THEN QVERIFY(centralView->hasFocus()); QVERIFY(!quickAddEdit->hasFocus()); QVERIFY(filter->isVisibleTo(&page)); QVERIFY(!filterEdit->hasFocus()); } void shouldManageFilterVisibilityThroughAction() { // GIVEN Widgets::PageView page; auto centralView = page.findChild(QStringLiteral("centralView")); auto filter = page.findChild(QStringLiteral("filterWidget")); auto filterEdit = filter->findChild(); QVERIFY(filterEdit); page.show(); QVERIFY(QTest::qWaitForWindowExposed(&page)); centralView->setFocus(); QVERIFY(centralView->hasFocus()); QVERIFY(!filter->isVisibleTo(&page)); QVERIFY(!filterEdit->hasFocus()); auto filterAction = page.findChild(QStringLiteral("filterViewAction")); // WHEN filterAction->trigger(); // THEN QVERIFY(!centralView->hasFocus()); QVERIFY(filter->isVisibleTo(&page)); QVERIFY(filterEdit->hasFocus()); // WHEN filterEdit->setText("Foo"); // THEN QCOMPARE(filterEdit->text(), QString("Foo")); // WHEN filterAction->trigger(); // THEN QVERIFY(centralView->hasFocus()); QVERIFY(!filter->isVisibleTo(&page)); QVERIFY(!filterEdit->hasFocus()); QVERIFY(filterEdit->text().isEmpty()); } void shouldManageFutureTasksVisibilityThroughAction() { // GIVEN Widgets::PageView page; auto filter = page.findChild(QStringLiteral("filterWidget")); auto filterProxy = filter->proxyModel(); QVERIFY(filterProxy); QVERIFY(filterProxy->showFutureTasks()); auto futureAction = page.findChild(QStringLiteral("futureViewAction")); // WHEN futureAction->trigger(); // THEN QVERIFY(!filterProxy->showFutureTasks()); // WHEN futureAction->trigger(); // THEN QVERIFY(filterProxy->showFutureTasks()); } void shouldStoreFutureTasksVisibilityDefaultState() { // GIVEN configGroup().deleteEntry("ShowFuture"); { Widgets::PageView page; auto futureAction = page.findChild(QStringLiteral("futureViewAction")); // THEN QVERIFY(futureAction->isChecked()); } // WHEN configGroup().writeEntry("ShowFuture", false); { Widgets::PageView page; auto futureAction = page.findChild(QStringLiteral("futureViewAction")); // THEN QVERIFY(!futureAction->isChecked()); } // WHEN configGroup().writeEntry("ShowFuture", true); { Widgets::PageView page; auto futureAction = page.findChild(QStringLiteral("futureViewAction")); // THEN QVERIFY(futureAction->isChecked()); } // WHEN configGroup().deleteEntry("ShowFuture"); { Widgets::PageView page; auto futureAction = page.findChild(QStringLiteral("futureViewAction")); // THEN QVERIFY(futureAction->isChecked()); } // WHEN Widgets::PageView page; auto futureAction = page.findChild(QStringLiteral("futureViewAction")); futureAction->trigger(); // THEN QVERIFY(configGroup().hasKey("ShowFuture")); QVERIFY(!configGroup().readEntry("ShowFuture", true)); // WHEN futureAction->trigger(); // THEN QVERIFY(configGroup().hasKey("ShowFuture")); QVERIFY(configGroup().readEntry("ShowFuture", false)); } void shouldCreateTasksWithNoParentWhenHittingReturnWithoutSelectedIndex() { // GIVEN PageModelStub stubPageModel; Widgets::PageView page; page.setModel(&stubPageModel); auto quickAddEdit = page.findChild(QStringLiteral("quickAddEdit")); // WHEN QTest::keyClick(quickAddEdit, Qt::Key_Return); // Does nothing (edit empty) QTest::keyClicks(quickAddEdit, QStringLiteral("Foo")); QTest::keyClick(quickAddEdit, Qt::Key_Return); QTest::keyClick(quickAddEdit, Qt::Key_Return); // Does nothing (edit empty) QTest::keyClicks(quickAddEdit, QStringLiteral("Bar")); QTest::keyClick(quickAddEdit, Qt::Key_Return); QTest::keyClick(quickAddEdit, Qt::Key_Return); // Does nothing (edit empty) // THEN QCOMPARE(stubPageModel.taskNames, QStringList() << QStringLiteral("Foo") << QStringLiteral("Bar")); QCOMPARE(stubPageModel.parentIndices.size(), 2); QCOMPARE(stubPageModel.parentIndices.first(), QPersistentModelIndex()); QCOMPARE(stubPageModel.parentIndices.last(), QPersistentModelIndex()); } void shouldCreateTasksWithNoParentWhenHittingReturnWithSeveralSelectedIndices() { // GIVEN PageModelStub stubPageModel; Q_ASSERT(stubPageModel.property("centralListModel").canConvert()); stubPageModel.addStubItems(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C")); QPersistentModelIndex index0 = stubPageModel.itemModel.index(0, 0); QPersistentModelIndex index1 = stubPageModel.itemModel.index(1, 0); Widgets::PageView page; page.setModel(&stubPageModel); auto centralView = page.findChild(QStringLiteral("centralView")); centralView->selectionModel()->select(index0, QItemSelectionModel::ClearAndSelect); centralView->selectionModel()->select(index1, QItemSelectionModel::Select); auto quickAddEdit = page.findChild(QStringLiteral("quickAddEdit")); // WHEN QTest::keyClicks(quickAddEdit, QStringLiteral("Foo")); QTest::keyClick(quickAddEdit, Qt::Key_Return); // THEN QCOMPARE(stubPageModel.taskNames, QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C") << QStringLiteral("Foo")); QCOMPARE(stubPageModel.parentIndices.size(), 1); QCOMPARE(stubPageModel.parentIndices.first(), QPersistentModelIndex()); } void shouldCreateTasksWithParentWhenHittingReturnWithOneSelectedIndex() { // GIVEN PageModelStub stubPageModel; Q_ASSERT(stubPageModel.property("centralListModel").canConvert()); stubPageModel.addStubItems(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C")); QPersistentModelIndex index = stubPageModel.itemModel.index(1, 0); Widgets::PageView page; page.setModel(&stubPageModel); auto centralView = page.findChild(QStringLiteral("centralView")); centralView->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect); auto quickAddEdit = page.findChild(QStringLiteral("quickAddEdit")); // WHEN QTest::keyClicks(quickAddEdit, QStringLiteral("Foo")); QTest::keyClick(quickAddEdit, Qt::Key_Return); // THEN QCOMPARE(stubPageModel.taskNames, QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C") << QStringLiteral("Foo")); QCOMPARE(stubPageModel.parentIndices.size(), 1); QCOMPARE(stubPageModel.parentIndices.first(), index); } void shouldDeleteItemWhenHittingTheDeleteKey() { // GIVEN PageModelStub stubPageModel; Q_ASSERT(stubPageModel.property("centralListModel").canConvert()); stubPageModel.addStubItems(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C")); QPersistentModelIndex index = stubPageModel.itemModel.index(1, 0); Widgets::PageView page; page.setModel(&stubPageModel); QTreeView *centralView = page.findChild(QStringLiteral("centralView")); centralView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); centralView->setFocus(); // Needed for shortcuts to work page.show(); QVERIFY(QTest::qWaitForWindowExposed(&page)); QTest::qWait(100); // WHEN QTest::keyPress(centralView, Qt::Key_Delete); // THEN QCOMPARE(stubPageModel.removedIndices.size(), 1); QCOMPARE(stubPageModel.removedIndices.first(), index); } void shouldNotTryToDeleteIfThereIsNoSelection() { // GIVEN PageModelStub stubPageModel; Q_ASSERT(stubPageModel.property("centralListModel").canConvert()); stubPageModel.addStubItems(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C")); Widgets::PageView page; page.setModel(&stubPageModel); QTreeView *centralView = page.findChild(QStringLiteral("centralView")); centralView->clearSelection(); page.findChild(QStringLiteral("quickAddEdit"))->setFocus(); // Needed for shortcuts to work page.show(); QVERIFY(QTest::qWaitForWindowExposed(&page)); QTest::qWait(100); // WHEN QTest::keyPress(centralView, Qt::Key_Delete); // THEN QVERIFY(stubPageModel.removedIndices.isEmpty()); } void shouldDisplayNotificationWhenHittingTheDeleteKeyOnAnItemWithChildren() { // GIVEN PageModelStub stubPageModel; Q_ASSERT(stubPageModel.property("centralListModel").canConvert()); stubPageModel.addStubItems(QStringList() << QStringLiteral("A") << QStringLiteral("B")); QStandardItem *parentIndex = stubPageModel.itemModel.item(1, 0); stubPageModel.addStubItem(QStringLiteral("C"), parentIndex); QPersistentModelIndex index = stubPageModel.itemModel.index(1, 0); Widgets::PageView page; page.setModel(&stubPageModel); auto msgbox = MessageBoxStub::Ptr::create(); page.setMessageBoxInterface(msgbox); QTreeView *centralView = page.findChild(QStringLiteral("centralView")); centralView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); QVERIFY(centralView->selectionModel()->currentIndex().isValid()); centralView->setFocus(); // Needed for shortcuts to work page.show(); QVERIFY(QTest::qWaitForWindowExposed(&page)); QTest::qWait(100); // WHEN QTest::keyPress(centralView, Qt::Key_Delete); // THEN QVERIFY(msgbox->called()); QCOMPARE(stubPageModel.removedIndices.size(), 1); QCOMPARE(stubPageModel.removedIndices.first(), index); } void shouldDisplayNotificationWhenHittingTheDeleteKeyOnAnItemWithHiddenChildren() { // GIVEN PageModelStub stubPageModel; Q_ASSERT(stubPageModel.property("centralListModel").canConvert()); stubPageModel.addStubItems(QStringList() << QStringLiteral("A") << QStringLiteral("B")); QStandardItem *parentIndex = stubPageModel.itemModel.item(1, 0); stubPageModel.addStubItem(QStringLiteral("C"), parentIndex); QSortFilterProxyModel proxyModel; stubPageModel.setProxyModel(&proxyModel); - proxyModel.setFilterFixedString("B"); + proxyModel.setFilterRegularExpression(QStringLiteral("B")); QPersistentModelIndex index = stubPageModel.centralListModel()->index(0, 0); QCOMPARE(index.data().toString(), QLatin1String("B")); Widgets::PageView page; page.setModel(&stubPageModel); auto msgbox = MessageBoxStub::Ptr::create(); page.setMessageBoxInterface(msgbox); QTreeView *centralView = page.findChild(QStringLiteral("centralView")); centralView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); QVERIFY(centralView->selectionModel()->currentIndex().isValid()); centralView->setFocus(); // Needed for shortcuts to work page.show(); QVERIFY(QTest::qWaitForWindowExposed(&page)); QTest::qWait(100); // WHEN QTest::keyPress(centralView, Qt::Key_Delete); // THEN QVERIFY(msgbox->called()); QCOMPARE(stubPageModel.removedIndices.size(), 1); QCOMPARE(stubPageModel.removedIndices.first(), index); } void shouldDeleteItemsWhenHittingTheDeleteKey() { // GIVEN PageModelStub stubPageModel; Q_ASSERT(stubPageModel.property("centralListModel").canConvert()); stubPageModel.addStubItems(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C")); QPersistentModelIndex index = stubPageModel.itemModel.index(1, 0); QPersistentModelIndex index2 = stubPageModel.itemModel.index(2, 0); Widgets::PageView page; page.setModel(&stubPageModel); auto msgbox = MessageBoxStub::Ptr::create(); page.setMessageBoxInterface(msgbox); QTreeView *centralView = page.findChild(QStringLiteral("centralView")); centralView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); centralView->selectionModel()->setCurrentIndex(index2, QItemSelectionModel::Select); centralView->setFocus(); // Needed for shortcuts to work page.show(); QVERIFY(QTest::qWaitForWindowExposed(&page)); QTest::qWait(100); // WHEN QTest::keyPress(centralView, Qt::Key_Delete); // THEN QVERIFY(msgbox->called()); QCOMPARE(stubPageModel.removedIndices.size(), 2); QCOMPARE(stubPageModel.removedIndices.first(), index); QCOMPARE(stubPageModel.removedIndices.at(1), index2); } void shouldPromoteItem() { // GIVEN PageModelStub stubPageModel; Q_ASSERT(stubPageModel.property("centralListModel").canConvert()); stubPageModel.addStubItems(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C")); QPersistentModelIndex index = stubPageModel.itemModel.index(1, 0); Widgets::PageView page; page.setModel(&stubPageModel); auto promoteAction = page.findChild(QStringLiteral("promoteItemAction")); QVERIFY(promoteAction); QTreeView *centralView = page.findChild(QStringLiteral("centralView")); centralView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); centralView->setFocus(); // WHEN promoteAction->trigger(); // THEN QCOMPARE(stubPageModel.promotedIndices.size(), 1); QCOMPARE(stubPageModel.promotedIndices.first(), index); } void shouldNotTryToPromoteItemIfThereIsNoSelection() { // GIVEN PageModelStub stubPageModel; Q_ASSERT(stubPageModel.property("centralListModel").canConvert()); stubPageModel.addStubItems(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C")); Widgets::PageView page; page.setModel(&stubPageModel); auto promoteAction = page.findChild(QStringLiteral("promoteItemAction")); QVERIFY(promoteAction); QTreeView *centralView = page.findChild(QStringLiteral("centralView")); centralView->selectionModel()->clear(); centralView->setFocus(); // WHEN promoteAction->trigger(); // THEN QVERIFY(stubPageModel.promotedIndices.isEmpty()); } void shouldClearCentralViewSelectionOnEscape() { // GIVEN PageModelStub stubPageModel; Q_ASSERT(stubPageModel.property("centralListModel").canConvert()); stubPageModel.addStubItems(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C")); QPersistentModelIndex index = stubPageModel.itemModel.index(1, 0); Widgets::PageView page; page.setModel(&stubPageModel); auto centralView = page.findChild(QStringLiteral("centralView")); centralView->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect); QVERIFY(!centralView->selectionModel()->selectedIndexes().isEmpty()); // WHEN QTest::keyClick(centralView, Qt::Key_Escape); // THEN QVERIFY(centralView->selectionModel()->selectedIndexes().isEmpty()); } void shouldReturnSelectedIndexes() { // GIVEN PageModelStub stubPageModel; Q_ASSERT(stubPageModel.property("centralListModel").canConvert()); stubPageModel.addStubItems(QStringList() << QStringLiteral("A") << QStringLiteral("B") << QStringLiteral("C")); auto index = stubPageModel.itemModel.index(1, 0); auto index2 = stubPageModel.itemModel.index(2, 0); Widgets::PageView page; page.setModel(&stubPageModel); auto centralView = page.findChild(QStringLiteral("centralView")); auto filterWidget = page.findChild(QStringLiteral("filterWidget")); auto displayedModel = filterWidget->proxyModel(); auto displayedIndex = displayedModel->index(1, 0); auto displayedIndex2 = displayedModel->index(2, 0); // WHEN centralView->selectionModel()->setCurrentIndex(displayedIndex, QItemSelectionModel::ClearAndSelect); centralView->selectionModel()->setCurrentIndex(displayedIndex2, QItemSelectionModel::Select); // THEN auto selectedIndexes = page.selectedIndexes(); QCOMPARE(selectedIndexes.size(), 2); QCOMPARE(selectedIndexes.at(0), index); QCOMPARE(selectedIndexes.at(1), index2); QCOMPARE(selectedIndexes.at(0).model(), index.model()); QCOMPARE(selectedIndexes.at(1).model(), index2.model()); } void shouldDisplayMessageOnError() { // GIVEN Widgets::PageView page; page.show(); QVERIFY(QTest::qWaitForWindowExposed(&page)); QTest::qWait(100); auto messageWidget = page.findChild(QStringLiteral("messageWidget")); QVERIFY(messageWidget); QVERIFY(!messageWidget->isVisibleTo(&page)); QCOMPARE(messageWidget->findChildren().size(), 1); auto closeButton = messageWidget->findChildren().first(); QVERIFY(closeButton); // WHEN page.displayErrorMessage(QStringLiteral("Foo Error")); // THEN QVERIFY(messageWidget->isVisibleTo(&page)); QVERIFY(messageWidget->isCloseButtonVisible()); QCOMPARE(messageWidget->text(), QStringLiteral("Foo Error")); QVERIFY(messageWidget->icon().isNull()); QCOMPARE(messageWidget->messageType(), KMessageWidget::Error); QVERIFY(messageWidget->isShowAnimationRunning()); QVERIFY(!messageWidget->isHideAnimationRunning()); // WHEN QTest::qWait(800); // THEN QVERIFY(!messageWidget->isShowAnimationRunning()); QVERIFY(!messageWidget->isHideAnimationRunning()); // WHEN closeButton->click(); // THEN QVERIFY(!messageWidget->isShowAnimationRunning()); QVERIFY(messageWidget->isHideAnimationRunning()); // WHEN QTest::qWait(800); // THEN QVERIFY(!messageWidget->isShowAnimationRunning()); QVERIFY(!messageWidget->isHideAnimationRunning()); } void shouldRunTask() { // GIVEN PageModelStub stubPageModel; Q_ASSERT(stubPageModel.property("centralListModel").canConvert()); auto task1 = stubPageModel.addTaskItem(QStringLiteral("Task1")); auto task2 = stubPageModel.addTaskItem(QStringLiteral("Task2")); Widgets::PageView page; page.setModel(&stubPageModel); RunningTaskModelStub stubRunningTaskModel; page.setRunningTaskModel(&stubRunningTaskModel); auto centralView = page.findChild(QStringLiteral("centralView")); QModelIndex index = stubPageModel.itemModel.index(0, 0); centralView->selectionModel()->setCurrentIndex(index, QItemSelectionModel::ClearAndSelect); QVERIFY(centralView->selectionModel()->currentIndex().isValid()); auto runTaskAction = page.findChild(QStringLiteral("runTaskAction")); QVERIFY(runTaskAction); QVERIFY(runTaskAction->isEnabled()); // WHEN starting the first task runTaskAction->trigger(); // THEN QCOMPARE(stubRunningTaskModel.property("runningTask").value(), task1); QCOMPARE(task1->startDate(), QDate::currentDate()); // WHEN starting the second task QModelIndex index2 = stubPageModel.itemModel.index(1, 0); centralView->selectionModel()->setCurrentIndex(index2, QItemSelectionModel::ClearAndSelect); runTaskAction->trigger(); // THEN QCOMPARE(stubRunningTaskModel.property("runningTask").value(), task2); QCOMPARE(task2->startDate(), QDate::currentDate()); } }; ZANSHIN_TEST_MAIN(PageViewTest) #include "pageviewtest.moc"