Verified Commit 84bd5c07 authored by Daniel Vrátil's avatar Daniel Vrátil 🤖
Browse files

Use Designer UI file for SubscriptionDialog

Includes tests.
parent ab1cfb6b
......@@ -4,3 +4,4 @@ endmacro()
add_akonadi_isolated_widget_test(tageditwidgettest.cpp)
add_akonadi_isolated_widget_test(tagwidgettest.cpp)
add_akonadi_isolated_widget_test(subscriptiondialogtest.cpp)
/*
* Copyright 2020 Daniel Vrátil <dvratil@kde.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
#include "qtest_akonadi.h"
#include <qnamespace.h>
#include <qtestmouse.h>
#include <shared/aktest.h>
#include <shared/akscopeguard.h>
#include "../libs/test_utils.h"
#include "subscriptiondialog.h"
#include "subscriptionmodel_p.h"
#include "subscriptionjob_p.h"
#include <QSignalSpy>
#include <QTest>
#include <QLineEdit>
#include <QPushButton>
#include <QTreeView>
#include <QDialogButtonBox>
#include <QDialog>
#include <QCheckBox>
#include <memory>
#include <deque>
using namespace Akonadi;
class SubscriptionDialogTest: public QObject
{
Q_OBJECT
struct TestSetup {
static constexpr int defaultCollectionCount = 7;
TestSetup()
{
widget = std::make_unique<SubscriptionDialog>(QStringList{
Collection::mimeType(),
QStringLiteral("application/octet-stream")
});
widget->setAttribute(Qt::WA_DeleteOnClose, false);
widget->show();
model = widget->findChild<SubscriptionModel*>();
QVERIFY(model);
QSignalSpy modelLoadedSpy(model, &SubscriptionModel::loaded);
buttonBox = widget->findChild<QDialogButtonBox*>();
QVERIFY(buttonBox);
QVERIFY(!buttonBox->button(QDialogButtonBox::Ok)->isEnabled());
searchLineEdit = widget->findChild<QLineEdit*>(QStringLiteral("searchLineEdit"));
QVERIFY(searchLineEdit);
subscribedOnlyChkBox = widget->findChild<QCheckBox*>(QStringLiteral("subscribedOnlyCheckBox"));
QVERIFY(subscribedOnlyChkBox);
collectionView = widget->findChild<QTreeView*>(QStringLiteral("collectionView"));
QVERIFY(collectionView);
subscribeButton = widget->findChild<QPushButton*>(QStringLiteral("subscribeButton"));
QVERIFY(subscribeButton);
unsubscribeButton = widget->findChild<QPushButton*>(QStringLiteral("unsubscribeButton"));
QVERIFY(unsubscribeButton);
QVERIFY(QTest::qWaitForWindowActive(widget.get()));
QVERIFY(modelLoadedSpy.wait());
QTest::qWait(100);
// Helps with testing :)
collectionView->expandAll();
// Post-setup conditions
QCOMPARE(countTotalRows(), defaultCollectionCount);
QVERIFY(buttonBox->button(QDialogButtonBox::Ok)->isEnabled());
valid = true;
}
~TestSetup()
{
}
int countTotalRows(const QModelIndex &parent = {}) const
{
const auto count = collectionView->model()->rowCount(parent);
int total = count;
for (int i = 0; i < count; ++i) {
total += countTotalRows(collectionView->model()->index(i, 0, parent));
}
return total;
}
static bool unsubscribeCollection(const Collection &col)
{
return modifySubscription({}, {col});
}
static bool subscribeCollection(const Collection &col)
{
return modifySubscription({col}, {});
}
static bool modifySubscription(const Collection::List &subscribe,
const Collection::List &unsubscribe)
{
auto job = new SubscriptionJob();
job->subscribe(subscribe);
job->unsubscribe(unsubscribe);
bool ok = false;
[job, &ok]() { AKVERIFYEXEC(job); ok = true; }();
AKVERIFY(ok);
return true;
}
bool selectCollection(const Collection &col)
{
AKVERIFY(col.isValid());
const QModelIndex colIdx = indexForCollection(col);
AKVERIFY(colIdx.isValid());
collectionView->scrollTo(colIdx);
QTest::mouseClick(collectionView->viewport(), Qt::LeftButton, {}, collectionView->visualRect(colIdx).center());
AKCOMPARE(collectionView->currentIndex(), colIdx);
return true;
}
bool isCollectionChecked(const Collection &col) const
{
AKVERIFY(col.isValid());
const auto colIdx = indexForCollection(col);
AKVERIFY(colIdx.isValid());
return collectionView->model()->data(colIdx, Qt::CheckStateRole).value<Qt::CheckState>() == Qt::Checked;
}
void acceptDialog()
{
auto button = buttonBox->button(QDialogButtonBox::Ok);
QTest::mouseClick(button, Qt::LeftButton);
}
QModelIndex indexForCollection(const Collection &col) const
{
auto model = collectionView->model();
std::deque<QModelIndex> idxQueue;
idxQueue.push_back(QModelIndex{});
while (!idxQueue.empty()) {
const auto idx = idxQueue.front();
idxQueue.pop_front();
if (model->data(idx, CollectionModel::CollectionIdRole).value<qint64>() == col.id()) {
return idx;
}
for (int i = 0; i < model->rowCount(idx); ++i) {
idxQueue.push_back(model->index(i, 0, idx));
}
}
return {};
}
std::unique_ptr<SubscriptionDialog> widget;
QDialogButtonBox *buttonBox = nullptr;
QLineEdit *searchLineEdit = nullptr;
QCheckBox *subscribedOnlyChkBox = nullptr;
QTreeView *collectionView = nullptr;
QPushButton *subscribeButton = nullptr;
QPushButton *unsubscribeButton = nullptr;
SubscriptionModel *model = nullptr;
bool valid = false;
};
private Q_SLOTS:
void initTestCase()
{
AkonadiTest::checkTestIsIsolated();
}
void testSearchFilter()
{
TestSetup test;
QVERIFY(test.valid);
QTest::keyClicks(test.searchLineEdit, QStringLiteral("foo"));
QCOMPARE(test.countTotalRows(), 2);
}
void testSubscribedOnlyCheckbox()
{
const auto col = Collection{collectionIdFromPath(QStringLiteral("res1/foo/bla"))};
const AkScopeGuard guard([col]() {
TestSetup::subscribeCollection(col);
});
QVERIFY(TestSetup::unsubscribeCollection(col));
TestSetup test;
QVERIFY(test.valid);
test.subscribedOnlyChkBox->setChecked(true);
QTRY_COMPARE(test.countTotalRows(), test.defaultCollectionCount - 1);
test.subscribedOnlyChkBox->setChecked(false);
QTRY_COMPARE(test.countTotalRows(), test.defaultCollectionCount);
}
void testSubscribeButton()
{
const auto col = Collection{collectionIdFromPath(QStringLiteral("res1/foo/bla"))};
const AkScopeGuard guard([col]() {
TestSetup::subscribeCollection(col);
});
QVERIFY(TestSetup::unsubscribeCollection(col));
TestSetup test;
QVERIFY(test.valid);
QVERIFY(test.selectCollection(col));
QTest::mouseClick(test.subscribeButton, Qt::LeftButton);
QVERIFY(test.isCollectionChecked(col));
auto monitor = getTestMonitor();
QSignalSpy monitorSpy(monitor.get(), &Monitor::collectionSubscribed);
test.acceptDialog();
QVERIFY(monitorSpy.wait());
}
void testUnsubscribeButton()
{
const auto col = Collection{collectionIdFromPath(QStringLiteral("res1/foo/bla"))};
TestSetup test;
QVERIFY(test.valid);
QVERIFY(test.selectCollection(col));
QTest::mouseClick(test.unsubscribeButton, Qt::LeftButton);
QVERIFY(!test.isCollectionChecked(col));
auto monitor = getTestMonitor();
QSignalSpy monitorSpy(monitor.get(), &Monitor::collectionUnsubscribed);
test.acceptDialog();
QVERIFY(monitorSpy.wait());
}
};
QTEST_AKONADIMAIN(SubscriptionDialogTest)
#include "subscriptiondialogtest.moc"
......@@ -53,6 +53,7 @@ set(akonadiwidgets_UI
erroroverlay.ui
manageaccountwidget.ui
selftestdialog.ui
subscriptiondialog.ui
tageditwidget.ui
tagmanagementdialog.ui
tagselectiondialog.ui
......
......@@ -18,6 +18,7 @@
*/
#include "subscriptiondialog.h"
#include "ui_subscriptiondialog.h"
#include "controlgui.h"
#include "recursivecollectionfilterproxymodel.h"
......@@ -67,21 +68,21 @@ public:
// TODO
qCWarning(AKONADIWIDGETS_LOG) << job->errorString();
}
q->deleteLater();
q->accept();
}
void modelLoaded()
{
filterRecursiveCollectionFilter->sort(0, Qt::AscendingOrder);
collectionView->setEnabled(true);
collectionView->expandAll();
mOkButton->setEnabled(true);
ui.collectionView->setEnabled(true);
ui.collectionView->expandAll();
ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
}
void slotSetPattern(const QString &text)
{
filterRecursiveCollectionFilter->setSearchPattern(text);
collectionView->expandAll();
ui.collectionView->expandAll();
}
void slotSetIncludeCheckedOnly(bool checked)
......@@ -108,31 +109,28 @@ public:
void slotSubscribe();
SubscriptionDialog *q = nullptr;
QTreeView *collectionView = nullptr;
QPushButton *subscribe = nullptr;
QPushButton *unSubscribe = nullptr;
Ui::SubscriptionDialog ui;
SubscriptionModel *model = nullptr;
RecursiveCollectionFilterProxyModel *filterRecursiveCollectionFilter = nullptr;
QPushButton *mOkButton = nullptr;
};
void SubscriptionDialog::Private::slotSubscribe()
{
const QModelIndexList list = collectionView->selectionModel()->selectedIndexes();
const QModelIndexList list = ui.collectionView->selectionModel()->selectedIndexes();
for (const QModelIndex &index : list) {
model->setData(index, Qt::Checked, Qt::CheckStateRole);
}
collectionView->setFocus();
ui.collectionView->setFocus();
}
void SubscriptionDialog::Private::slotUnSubscribe()
{
const QModelIndexList list = collectionView->selectionModel()->selectedIndexes();
const QModelIndexList list = ui.collectionView->selectionModel()->selectedIndexes();
for (const QModelIndex &index : list) {
model->setData(index, Qt::Unchecked, Qt::CheckStateRole);
}
collectionView->setFocus();
ui.collectionView->setFocus();
}
SubscriptionDialog::SubscriptionDialog(QWidget *parent)
......@@ -157,86 +155,40 @@ void SubscriptionDialog::showHiddenCollection(bool showHidden)
void SubscriptionDialog::init(const QStringList &mimetypes)
{
setAttribute(Qt::WA_DeleteOnClose);
setWindowTitle(i18nc("@title:window", "Local Subscriptions"));
QWidget *mainWidget = new QWidget(this);
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->addWidget(mainWidget);
d->model = new SubscriptionModel(this);
d->ui.setupUi(this);
d->filterRecursiveCollectionFilter
= new Akonadi::RecursiveCollectionFilterProxyModel(this);
d->model = new SubscriptionModel(this);
d->filterRecursiveCollectionFilter = new RecursiveCollectionFilterProxyModel(this);
d->filterRecursiveCollectionFilter->setSourceModel(d->model);
d->filterRecursiveCollectionFilter->setFilterCaseSensitivity(Qt::CaseInsensitive);
d->filterRecursiveCollectionFilter->setSortRole(Qt::DisplayRole);
d->filterRecursiveCollectionFilter->setSortCaseSensitivity(Qt::CaseSensitive);
d->filterRecursiveCollectionFilter->setSortLocaleAware(true);
if (!mimetypes.isEmpty()) {
d->filterRecursiveCollectionFilter->addContentMimeTypeInclusionFilters(mimetypes);
}
d->collectionView = new QTreeView(mainWidget);
mainLayout->addWidget(d->collectionView);
d->collectionView->setEditTriggers(QAbstractItemView::NoEditTriggers);
d->collectionView->header()->hide();
d->collectionView->setModel(d->filterRecursiveCollectionFilter);
d->collectionView->setSelectionMode(QAbstractItemView::ExtendedSelection);
QHBoxLayout *filterBarLayout = new QHBoxLayout;
filterBarLayout->addWidget(new QLabel(i18n("Search:")));
QLineEdit *lineEdit = new QLineEdit(mainWidget);
mainLayout->addWidget(lineEdit);
lineEdit->setClearButtonEnabled(true);
lineEdit->setFocus();
connect(lineEdit, &QLineEdit::textChanged, this, [this](const QString &str) { d->slotSetPattern(str); });
filterBarLayout->addWidget(lineEdit);
QCheckBox *checkBox = new QCheckBox(i18n("Subscribed only"), mainWidget);
mainLayout->addWidget(checkBox);
connect(checkBox, &QCheckBox::clicked, this, [this](bool state) { d->slotSetIncludeCheckedOnly(state); });
filterBarLayout->addWidget(checkBox);
d->ui.collectionView->setModel(d->filterRecursiveCollectionFilter);
d->ui.searchLineEdit->setFocus();
connect(d->ui.searchLineEdit, &QLineEdit::textChanged, this, [this](const QString &str) { d->slotSetPattern(str); });
connect(d->ui.subscribedOnlyCheckBox, &QCheckBox::toggled, this, [this](bool state) { d->slotSetIncludeCheckedOnly(state); });
connect(d->ui.subscribeButton, &QPushButton::clicked, this, [this]() { d->slotSubscribe(); });
connect(d->ui.unsubscribeButton, &QPushButton::clicked, this, [this]() { d->slotUnSubscribe(); });
QHBoxLayout *hboxLayout = new QHBoxLayout;
hboxLayout->addWidget(d->collectionView);
QVBoxLayout *subscribeButtonLayout = new QVBoxLayout;
d->subscribe = new QPushButton(i18n("Subscribe"));
subscribeButtonLayout->addWidget(d->subscribe);
connect(d->subscribe, &QPushButton::clicked, this, [this]() { d->slotSubscribe(); });
d->unSubscribe = new QPushButton(i18n("Unsubscribe"));
subscribeButtonLayout->addWidget(d->unSubscribe);
connect(d->unSubscribe, &QPushButton::clicked, this, [this]() { d->slotUnSubscribe(); });
subscribeButtonLayout->addItem(new QSpacerItem(5, 5, QSizePolicy::Minimum, QSizePolicy::Expanding));
hboxLayout->addLayout(subscribeButtonLayout);
mainLayout->addLayout(filterBarLayout);
mainLayout->addLayout(hboxLayout);
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, mainWidget);
d->mOkButton = buttonBox->button(QDialogButtonBox::Ok);
d->mOkButton->setDefault(true);
d->mOkButton->setShortcut(Qt::CTRL | Qt::Key_Return);
d->mOkButton->setEnabled(false);
mainLayout->addWidget(buttonBox);
auto okButton = d->ui.buttonBox->button(QDialogButtonBox::Ok);
okButton->setEnabled(false);
connect(d->model, &SubscriptionModel::loaded, this, [this]() { d->modelLoaded(); });
connect(d->mOkButton, &QPushButton::clicked, this, [this] () { d->done(); });
connect(buttonBox->button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &SubscriptionDialog::deleteLater);
ControlGui::widgetNeedsAkonadi(mainWidget);
connect(okButton, &QPushButton::clicked, this, [this] () { d->done(); });
ControlGui::widgetNeedsAkonadi(this);
d->readConfig();
}
SubscriptionDialog::~SubscriptionDialog()
{
d->writeConfig();
delete d;
}
#include "moc_subscriptiondialog.cpp"
......@@ -67,7 +67,7 @@ public:
private:
class Private;
Private *const d;
QScopedPointer<Private> const d;
void init(const QStringList &mimetypes);
};
......
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SubscriptionDialog</class>
<widget class="QDialog" name="SubscriptionDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>456</width>
<height>443</height>
</rect>
</property>
<property name="windowTitle">
<string>Local Subscriptions</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Search:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="searchLineEdit">
<property name="clearButtonEnabled">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="subscribedOnlyCheckBox">
<property name="text">
<string>&amp;Subscribed only</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QTreeView" name="collectionView">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::ExtendedSelection</enum>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="subscribeButton">
<property name="text">
<string>Su&amp;bscribe</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="unsubscribeButton">
<property name="text">
<string>&amp;Unsubscribe</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>SubscriptionDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>423</x>
<y>421</y>
</hint>
<hint type="destinationlabel">
<x>456</x>
<y>287</y>
</hint>
</hints>
</connection>
</connections>
</ui>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment