mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-22 21:15:55 +03:00
Merge pull request #5408 from nextcloud/bugfix/errors-in-activity-list-sort
Ensure errors are displayed as first entries in tray window activity list, add testing for SortedActivityListModel
This commit is contained in:
commit
415acaff04
9 changed files with 889 additions and 498 deletions
|
@ -16,7 +16,7 @@
|
|||
#define ACTIVITYDATA_H
|
||||
|
||||
#include "syncfileitem.h"
|
||||
#include "folder.h"
|
||||
#include "syncresult.h"
|
||||
#include "account.h"
|
||||
|
||||
#include <QtCore>
|
||||
|
|
|
@ -438,18 +438,6 @@ void ActivityListModel::startFetchJob()
|
|||
job->start();
|
||||
}
|
||||
|
||||
void ActivityListModel::setFinalList(const ActivityList &finalList)
|
||||
{
|
||||
_finalList = finalList;
|
||||
|
||||
emit allConflictsChanged();
|
||||
}
|
||||
|
||||
const ActivityList &ActivityListModel::finalList() const
|
||||
{
|
||||
return _finalList;
|
||||
}
|
||||
|
||||
int ActivityListModel::currentItem() const
|
||||
{
|
||||
return _currentItem;
|
||||
|
|
|
@ -21,6 +21,10 @@
|
|||
|
||||
class QJsonDocument;
|
||||
|
||||
namespace ActivityListModelTestUtils {
|
||||
class TestingALM;
|
||||
}
|
||||
|
||||
namespace OCC {
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(lcActivity)
|
||||
|
@ -89,9 +93,7 @@ public:
|
|||
Q_ENUM(ErrorType)
|
||||
|
||||
explicit ActivityListModel(QObject *parent = nullptr);
|
||||
|
||||
explicit ActivityListModel(AccountState *accountState,
|
||||
QObject *parent = nullptr);
|
||||
explicit ActivityListModel(AccountState *accountState, QObject *parent = nullptr);
|
||||
|
||||
[[nodiscard]] QVariant data(const QModelIndex &index, int role) const override;
|
||||
[[nodiscard]] int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
|
@ -99,7 +101,6 @@ public:
|
|||
[[nodiscard]] QHash<int, QByteArray> roleNames() const override;
|
||||
|
||||
[[nodiscard]] bool canFetchMore(const QModelIndex &) const override;
|
||||
void fetchMore(const QModelIndex &) override;
|
||||
|
||||
ActivityList activityList() { return _finalList; }
|
||||
ActivityList errorsList() { return _notificationErrorsLists; }
|
||||
|
@ -120,6 +121,8 @@ public:
|
|||
[[nodiscard]] OCC::ActivityList allConflicts() const;
|
||||
|
||||
public slots:
|
||||
void fetchMore(const QModelIndex &) override;
|
||||
|
||||
void slotRefreshActivity();
|
||||
void slotRefreshActivityInitial();
|
||||
void slotRemoveAccount();
|
||||
|
@ -151,21 +154,22 @@ signals:
|
|||
protected:
|
||||
[[nodiscard]] bool currentlyFetching() const;
|
||||
|
||||
[[nodiscard]] const ActivityList &finalList() const; // added for unit tests
|
||||
|
||||
protected slots:
|
||||
void activitiesReceived(const QJsonDocument &json, int statusCode);
|
||||
void setAndRefreshCurrentlyFetching(bool value);
|
||||
void setDoneFetching(bool value);
|
||||
void setHideOldActivities(bool value);
|
||||
void setDisplayActions(bool value);
|
||||
void setFinalList(const OCC::ActivityList &finalList); // added for unit tests
|
||||
|
||||
virtual void startFetchJob();
|
||||
|
||||
private slots:
|
||||
void addEntriesToActivityList(const OCC::ActivityList &activityList);
|
||||
void accountStateHasChanged();
|
||||
void ingestActivities(const QJsonArray &activities);
|
||||
void appendMoreActivitiesAvailableEntry();
|
||||
void insertOrRemoveDummyFetchingActivity();
|
||||
void triggerCaseClashAction(OCC::Activity activity);
|
||||
|
||||
private:
|
||||
static QVariantList convertLinksToMenuEntries(const Activity &activity);
|
||||
|
@ -174,11 +178,6 @@ private:
|
|||
|
||||
[[nodiscard]] bool canFetchActivities() const;
|
||||
|
||||
void ingestActivities(const QJsonArray &activities);
|
||||
void appendMoreActivitiesAvailableEntry();
|
||||
void insertOrRemoveDummyFetchingActivity();
|
||||
void triggerCaseClashAction(Activity activity);
|
||||
|
||||
void displaySingleConflictDialog(const Activity &activity);
|
||||
void setHasSyncConflicts(bool conflictsFound);
|
||||
|
||||
|
@ -197,8 +196,8 @@ private:
|
|||
bool _displayActions = true;
|
||||
|
||||
int _currentItem = 0;
|
||||
int _maxActivities = 100;
|
||||
int _maxActivitiesDays = 30;
|
||||
static constexpr int _maxActivities = 100;
|
||||
static constexpr int _maxActivitiesDays = 30;
|
||||
bool _showMoreActivitiesAvailableEntry = false;
|
||||
|
||||
QPointer<ConflictDialog> _currentConflictDialog;
|
||||
|
@ -217,6 +216,8 @@ private:
|
|||
QElapsedTimer _durationSinceDisconnection;
|
||||
|
||||
static constexpr quint32 MaxActionButtons = 3;
|
||||
|
||||
friend class ActivityListModelTestUtils::TestingALM;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -44,30 +44,47 @@ bool SortedActivityListModel::lessThan(const QModelIndex &sourceLeft, const QMod
|
|||
return false;
|
||||
}
|
||||
|
||||
// Let's now check for errors as we want those near the top too
|
||||
// Sync result errors go first
|
||||
const auto leftSyncResultStatus = leftActivity._syncResultStatus;
|
||||
const auto rightSyncResultStatus = rightActivity._syncResultStatus;
|
||||
|
||||
const auto leftIsSyncResultError = leftSyncResultStatus == SyncResult::Error ||
|
||||
leftSyncResultStatus == SyncResult::SetupError ||
|
||||
leftSyncResultStatus == SyncResult::Problem;
|
||||
|
||||
const auto rightIsSyncResultError = rightSyncResultStatus == SyncResult::Error ||
|
||||
rightSyncResultStatus == SyncResult::SetupError ||
|
||||
rightSyncResultStatus == SyncResult::Problem;
|
||||
|
||||
if (leftIsSyncResultError != rightIsSyncResultError) {
|
||||
return leftIsSyncResultError;
|
||||
} // If they are both errors then we will order the errors according to enum order later
|
||||
|
||||
// Then sync file item status errors
|
||||
const auto leftSyncFileItemStatus = leftActivity._syncFileItemStatus;
|
||||
const auto rightSyncFileItemStatus = rightActivity._syncFileItemStatus;
|
||||
const bool leftIsErrorFileItemStatus = leftSyncFileItemStatus != SyncFileItem::NoStatus &&
|
||||
leftSyncFileItemStatus != SyncFileItem::Success;
|
||||
|
||||
const bool rightIsErrorFileItemStatus = rightSyncFileItemStatus != SyncFileItem::NoStatus &&
|
||||
rightSyncFileItemStatus != SyncFileItem::Success;
|
||||
|
||||
if (leftIsErrorFileItemStatus != rightIsErrorFileItemStatus) {
|
||||
return leftIsErrorFileItemStatus;
|
||||
}
|
||||
|
||||
// Let's go back to more broadly comparing by type
|
||||
if (const auto rightType = rightActivity._type; leftType != rightType) {
|
||||
return leftType < rightType;
|
||||
}
|
||||
|
||||
const auto leftSyncFileItemStatus = leftActivity._syncFileItemStatus;
|
||||
const auto rightSyncFileItemStatus = rightActivity._syncFileItemStatus;
|
||||
|
||||
// Then compare by status
|
||||
if (leftSyncFileItemStatus != rightSyncFileItemStatus) {
|
||||
// We want to shove errors towards the top.
|
||||
return (leftSyncFileItemStatus != SyncFileItem::NoStatus &&
|
||||
leftSyncFileItemStatus != SyncFileItem::Success) ||
|
||||
leftSyncFileItemStatus == SyncFileItem::FatalError ||
|
||||
leftSyncFileItemStatus < rightSyncFileItemStatus;
|
||||
if (leftSyncResultStatus != rightSyncResultStatus) {
|
||||
return leftSyncResultStatus < rightSyncResultStatus;
|
||||
}
|
||||
|
||||
const auto leftSyncResultStatus = leftActivity._syncResultStatus;
|
||||
const auto rightSyncResultStatus = rightActivity._syncResultStatus;
|
||||
|
||||
if (leftSyncResultStatus != rightSyncResultStatus) {
|
||||
// We only ever use SyncResult::Error in activities
|
||||
return (leftSyncResultStatus != SyncResult::Undefined &&
|
||||
leftSyncResultStatus != SyncResult::Success) ||
|
||||
leftSyncResultStatus == SyncResult::Error;
|
||||
if (leftSyncFileItemStatus != rightSyncFileItemStatus) {
|
||||
return leftSyncFileItemStatus < rightSyncFileItemStatus;
|
||||
}
|
||||
|
||||
// Finally sort by time, latest first
|
||||
|
|
|
@ -12,6 +12,7 @@ add_library(testutils
|
|||
testhelper.cpp
|
||||
sharetestutils.cpp
|
||||
endtoendtestutils.cpp
|
||||
activitylistmodeltestutils.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(testutils PUBLIC Nextcloud::sync Qt5::Test)
|
||||
|
@ -63,6 +64,7 @@ nextcloud_add_test(IconUtils)
|
|||
nextcloud_add_test(SetUserStatusDialog)
|
||||
nextcloud_add_test(UnifiedSearchListmodel)
|
||||
nextcloud_add_test(ActivityListModel)
|
||||
nextcloud_add_test(SortedActivityListModel)
|
||||
nextcloud_add_test(ActivityData)
|
||||
nextcloud_add_test(TalkReply)
|
||||
nextcloud_add_test(LockFile)
|
||||
|
|
511
test/activitylistmodeltestutils.cpp
Normal file
511
test/activitylistmodeltestutils.cpp
Normal file
|
@ -0,0 +1,511 @@
|
|||
/*
|
||||
* Copyright (C) by Claudio Cambra <claudio.cambra@nextcloud.com>
|
||||
*
|
||||
* 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) any later version.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "activitylistmodeltestutils.h"
|
||||
#include "syncenginetestutils.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
|
||||
namespace {
|
||||
static QByteArray fake404Response = R"(
|
||||
{"ocs":{"meta":{"status":"failure","statuscode":404,"message":"Invalid query, please check the syntax. API specifications are here: http:\/\/www.freedesktop.org\/wiki\/Specifications\/open-collaboration-services.\n"},"data":[]}}
|
||||
)";
|
||||
|
||||
static QByteArray fake400Response = R"(
|
||||
{"ocs":{"meta":{"status":"failure","statuscode":400,"message":"Parameter is incorrect.\n"},"data":[]}}
|
||||
)";
|
||||
|
||||
static QByteArray fake500Response = R"(
|
||||
{"ocs":{"meta":{"status":"failure","statuscode":500,"message":"Internal Server Error.\n"},"data":[]}}
|
||||
)";
|
||||
}
|
||||
|
||||
namespace ActivityListModelTestUtils
|
||||
{
|
||||
|
||||
QNetworkReply *almTestQnamOverride(FakeQNAM * const fakeQnam,
|
||||
const QNetworkAccessManager::Operation op,
|
||||
const QNetworkRequest &req,
|
||||
const QString &accountUrl,
|
||||
QObject * const parent,
|
||||
const int searchResultsReplyDelay,
|
||||
QIODevice * const device)
|
||||
{
|
||||
Q_UNUSED(device);
|
||||
QNetworkReply *reply = nullptr;
|
||||
|
||||
const auto urlQuery = QUrlQuery(req.url());
|
||||
const auto format = urlQuery.queryItemValue(QStringLiteral("format"));
|
||||
const auto since = urlQuery.queryItemValue(QStringLiteral("since")).toInt();
|
||||
const auto limit = urlQuery.queryItemValue(QStringLiteral("limit")).toInt();
|
||||
const auto path = req.url().path();
|
||||
|
||||
if (!req.url().toString().startsWith(accountUrl)) {
|
||||
reply = new FakeErrorReply(op, req, parent, 404, fake404Response);
|
||||
}
|
||||
if (format != QStringLiteral("json")) {
|
||||
reply = new FakeErrorReply(op, req, parent, 400, fake400Response);
|
||||
}
|
||||
|
||||
if (path.startsWith(QStringLiteral("/ocs/v2.php/apps/activity/api/v2/activity"))) {
|
||||
reply = new FakePayloadReply(op, req, FakeRemoteActivityStorage::instance()->activityJsonData(since, limit), searchResultsReplyDelay, fakeQnam);
|
||||
}
|
||||
|
||||
if (!reply) {
|
||||
return qobject_cast<QNetworkReply*>(new FakeErrorReply(op, req, parent, 404, QByteArrayLiteral("{error: \"Not found!\"}")));
|
||||
}
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
// Activity comparison is done by checking type, id, and accName
|
||||
// We need an activity with these details, at least
|
||||
|
||||
OCC::Activity exampleNotificationActivity(const QString &accountName, const int id)
|
||||
{
|
||||
OCC::Activity testNotificationActivity;
|
||||
|
||||
testNotificationActivity._accName = accountName;
|
||||
testNotificationActivity._id = id;
|
||||
testNotificationActivity._type = OCC::Activity::NotificationType;
|
||||
testNotificationActivity._dateTime = QDateTime::currentDateTime();
|
||||
testNotificationActivity._subject = QStringLiteral("Sample notification text");
|
||||
|
||||
return testNotificationActivity;
|
||||
}
|
||||
|
||||
OCC::Activity exampleSyncResultErrorActivity(const QString &accountName, const int id)
|
||||
{
|
||||
OCC::Activity testSyncResultErrorActivity;
|
||||
|
||||
testSyncResultErrorActivity._id = id;
|
||||
testSyncResultErrorActivity._type = OCC::Activity::SyncResultType;
|
||||
testSyncResultErrorActivity._syncResultStatus = OCC::SyncResult::Error;
|
||||
testSyncResultErrorActivity._dateTime = QDateTime::currentDateTime();
|
||||
testSyncResultErrorActivity._subject = QStringLiteral("Sample failed sync text");
|
||||
testSyncResultErrorActivity._message = QStringLiteral("/path/to/thingy");
|
||||
testSyncResultErrorActivity._link = QStringLiteral("/path/to/thingy");
|
||||
testSyncResultErrorActivity._accName = accountName;
|
||||
|
||||
return testSyncResultErrorActivity;
|
||||
}
|
||||
|
||||
OCC::Activity exampleSyncFileItemActivity(const QString &accountName, const QUrl &link, const int id)
|
||||
{
|
||||
OCC::Activity testSyncFileItemActivity;
|
||||
|
||||
testSyncFileItemActivity._id = id;
|
||||
testSyncFileItemActivity._type = OCC::Activity::SyncFileItemType; //client activity
|
||||
testSyncFileItemActivity._syncFileItemStatus = OCC::SyncFileItem::Success;
|
||||
testSyncFileItemActivity._dateTime = QDateTime::currentDateTime();
|
||||
testSyncFileItemActivity._message = QStringLiteral("Sample file successfully synced text");
|
||||
testSyncFileItemActivity._link = link;
|
||||
testSyncFileItemActivity._accName = accountName;
|
||||
testSyncFileItemActivity._file = QStringLiteral("xyz.pdf");
|
||||
|
||||
return testSyncFileItemActivity;
|
||||
}
|
||||
|
||||
OCC::Activity exampleFileIgnoredActivity(const QString &accountName, const QUrl &link, const int id)
|
||||
{
|
||||
OCC::Activity testFileIgnoredActivity;
|
||||
|
||||
testFileIgnoredActivity._id = id;
|
||||
testFileIgnoredActivity._type = OCC::Activity::SyncFileItemType;
|
||||
testFileIgnoredActivity._syncFileItemStatus = OCC::SyncFileItem::FileIgnored;
|
||||
testFileIgnoredActivity._dateTime = QDateTime::currentDateTime();
|
||||
testFileIgnoredActivity._subject = QStringLiteral("Sample ignored file sync text");
|
||||
testFileIgnoredActivity._link = link;
|
||||
testFileIgnoredActivity._accName = accountName;
|
||||
testFileIgnoredActivity._folder = QStringLiteral("thingy");
|
||||
testFileIgnoredActivity._file = QStringLiteral("test.txt");
|
||||
|
||||
return testFileIgnoredActivity;
|
||||
}
|
||||
|
||||
FakeRemoteActivityStorage *FakeRemoteActivityStorage::_instance = nullptr;
|
||||
|
||||
FakeRemoteActivityStorage* FakeRemoteActivityStorage::instance()
|
||||
{
|
||||
if (!_instance) {
|
||||
_instance = new FakeRemoteActivityStorage();
|
||||
_instance->init();
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
|
||||
void FakeRemoteActivityStorage::destroy()
|
||||
{
|
||||
if (_instance) {
|
||||
delete _instance;
|
||||
}
|
||||
|
||||
_instance = nullptr;
|
||||
}
|
||||
|
||||
void FakeRemoteActivityStorage::init()
|
||||
{
|
||||
if (!_activityData.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_metaSuccess = {{QStringLiteral("status"), QStringLiteral("ok")}, {QStringLiteral("statuscode"), 200},
|
||||
{QStringLiteral("message"), QStringLiteral("OK")}};
|
||||
|
||||
initActivityData();
|
||||
}
|
||||
|
||||
void FakeRemoteActivityStorage::initActivityData()
|
||||
{
|
||||
_activityData = {};
|
||||
|
||||
// Insert activity data
|
||||
for (quint32 i = 0; i <= _numItemsToInsert; i++) {
|
||||
QJsonObject activity;
|
||||
activity.insert(QStringLiteral("object_type"), "files");
|
||||
activity.insert(QStringLiteral("activity_id"), _startingId);
|
||||
activity.insert(QStringLiteral("type"), QStringLiteral("file"));
|
||||
activity.insert(QStringLiteral("subject"), QStringLiteral("You created %1.txt").arg(i));
|
||||
activity.insert(QStringLiteral("message"), QStringLiteral(""));
|
||||
activity.insert(QStringLiteral("object_name"), QStringLiteral("%1.txt").arg(i));
|
||||
activity.insert(QStringLiteral("datetime"), QDateTime::currentDateTime().toString(Qt::ISODate));
|
||||
activity.insert(QStringLiteral("icon"), QStringLiteral("http://example.de/apps/files/img/add-color.svg"));
|
||||
|
||||
_activityData.push_back(activity);
|
||||
|
||||
_startingId++;
|
||||
}
|
||||
|
||||
// Insert notification data
|
||||
for (quint32 i = 0; i < _numItemsToInsert; i++) {
|
||||
QJsonObject activity;
|
||||
activity.insert(QStringLiteral("activity_id"), _startingId);
|
||||
activity.insert(QStringLiteral("object_type"), "calendar");
|
||||
activity.insert(QStringLiteral("type"), QStringLiteral("calendar-event"));
|
||||
activity.insert(
|
||||
QStringLiteral("subject"), QStringLiteral("You created event %1 in calendar Events").arg(i));
|
||||
activity.insert(QStringLiteral("message"), QStringLiteral(""));
|
||||
activity.insert(QStringLiteral("object_name"), QStringLiteral(""));
|
||||
activity.insert(QStringLiteral("datetime"), QDateTime::currentDateTime().toString(Qt::ISODate));
|
||||
activity.insert(QStringLiteral("icon"), QStringLiteral("http://example.de/core/img/places/calendar.svg"));
|
||||
|
||||
_activityData.push_back(activity);
|
||||
|
||||
_startingId++;
|
||||
}
|
||||
|
||||
// Insert notification data
|
||||
for (quint32 i = 0; i < _numItemsToInsert; i++) {
|
||||
QJsonObject activity;
|
||||
activity.insert(QStringLiteral("activity_id"), _startingId);
|
||||
activity.insert(QStringLiteral("object_type"), "chat");
|
||||
activity.insert(QStringLiteral("type"), QStringLiteral("chat"));
|
||||
activity.insert(QStringLiteral("subject"), QStringLiteral("You have received %1's message").arg(i));
|
||||
activity.insert(QStringLiteral("message"), QStringLiteral(""));
|
||||
activity.insert(QStringLiteral("object_name"), QStringLiteral(""));
|
||||
activity.insert(QStringLiteral("datetime"), QDateTime::currentDateTime().toString(Qt::ISODate));
|
||||
activity.insert(QStringLiteral("icon"), QStringLiteral("http://example.de/core/img/places/talk.svg"));
|
||||
|
||||
QJsonArray actionsArray;
|
||||
|
||||
QJsonObject replyAction;
|
||||
replyAction.insert(QStringLiteral("label"), QStringLiteral("Reply"));
|
||||
replyAction.insert(QStringLiteral("link"), QStringLiteral(""));
|
||||
replyAction.insert(QStringLiteral("type"), QStringLiteral("REPLY"));
|
||||
replyAction.insert(QStringLiteral("primary"), true);
|
||||
actionsArray.push_back(replyAction);
|
||||
|
||||
QJsonObject primaryAction;
|
||||
primaryAction.insert(QStringLiteral("label"), QStringLiteral("View chat"));
|
||||
primaryAction.insert(QStringLiteral("link"), QStringLiteral("http://cloud.example.de/call/9p4vjdzd"));
|
||||
primaryAction.insert(QStringLiteral("type"), QStringLiteral("WEB"));
|
||||
primaryAction.insert(QStringLiteral("primary"), false);
|
||||
actionsArray.push_back(primaryAction);
|
||||
|
||||
QJsonObject additionalAction;
|
||||
additionalAction.insert(QStringLiteral("label"), QStringLiteral("Additional 1"));
|
||||
additionalAction.insert(QStringLiteral("link"), QStringLiteral("http://cloud.example.de/call/9p4vjdzd"));
|
||||
additionalAction.insert(QStringLiteral("type"), QStringLiteral("POST"));
|
||||
additionalAction.insert(QStringLiteral("primary"), false);
|
||||
actionsArray.push_back(additionalAction);
|
||||
additionalAction.insert(QStringLiteral("label"), QStringLiteral("Additional 2"));
|
||||
actionsArray.push_back(additionalAction);
|
||||
|
||||
activity.insert(QStringLiteral("actions"), actionsArray);
|
||||
|
||||
_activityData.push_back(activity);
|
||||
|
||||
_startingId++;
|
||||
}
|
||||
|
||||
// Insert notification data
|
||||
for (quint32 i = 0; i < _numItemsToInsert; i++) {
|
||||
QJsonObject activity;
|
||||
activity.insert(QStringLiteral("activity_id"), _startingId);
|
||||
activity.insert(QStringLiteral("object_type"), "room");
|
||||
activity.insert(QStringLiteral("type"), QStringLiteral("room"));
|
||||
activity.insert(QStringLiteral("subject"), QStringLiteral("You have been invited into room%1").arg(i));
|
||||
activity.insert(QStringLiteral("message"), QStringLiteral(""));
|
||||
activity.insert(QStringLiteral("object_name"), QStringLiteral(""));
|
||||
activity.insert(QStringLiteral("datetime"), QDateTime::currentDateTime().toString(Qt::ISODate));
|
||||
activity.insert(QStringLiteral("icon"), QStringLiteral("http://example.de/core/img/places/talk.svg"));
|
||||
|
||||
QJsonArray actionsArray;
|
||||
|
||||
QJsonObject replyAction;
|
||||
replyAction.insert(QStringLiteral("label"), QStringLiteral("Reply"));
|
||||
replyAction.insert(QStringLiteral("link"), QStringLiteral(""));
|
||||
replyAction.insert(QStringLiteral("type"), QStringLiteral("REPLY"));
|
||||
replyAction.insert(QStringLiteral("primary"), true);
|
||||
actionsArray.push_back(replyAction);
|
||||
|
||||
QJsonObject primaryAction;
|
||||
primaryAction.insert(QStringLiteral("label"), QStringLiteral("View chat"));
|
||||
primaryAction.insert(QStringLiteral("link"), QStringLiteral("http://cloud.example.de/call/9p4vjdzd"));
|
||||
primaryAction.insert(QStringLiteral("type"), QStringLiteral("WEB"));
|
||||
primaryAction.insert(QStringLiteral("primary"), false);
|
||||
actionsArray.push_back(primaryAction);
|
||||
|
||||
activity.insert(QStringLiteral("actions"), actionsArray);
|
||||
|
||||
_activityData.push_back(activity);
|
||||
|
||||
_startingId++;
|
||||
}
|
||||
|
||||
// Insert notification data
|
||||
for (quint32 i = 0; i < _numItemsToInsert; i++) {
|
||||
QJsonObject activity;
|
||||
activity.insert(QStringLiteral("activity_id"), _startingId);
|
||||
activity.insert(QStringLiteral("object_type"), "call");
|
||||
activity.insert(QStringLiteral("type"), QStringLiteral("call"));
|
||||
activity.insert(QStringLiteral("subject"), QStringLiteral("You have missed a %1's call").arg(i));
|
||||
activity.insert(QStringLiteral("message"), QStringLiteral(""));
|
||||
activity.insert(QStringLiteral("object_name"), QStringLiteral(""));
|
||||
activity.insert(QStringLiteral("datetime"), QDateTime::currentDateTime().toString(Qt::ISODate));
|
||||
activity.insert(QStringLiteral("icon"), QStringLiteral("http://example.de/core/img/places/talk.svg"));
|
||||
|
||||
QJsonArray actionsArray;
|
||||
|
||||
QJsonObject primaryAction;
|
||||
primaryAction.insert(QStringLiteral("label"), QStringLiteral("Call back"));
|
||||
primaryAction.insert(QStringLiteral("link"), QStringLiteral("http://cloud.example.de/call/9p4vjdzd"));
|
||||
primaryAction.insert(QStringLiteral("type"), QStringLiteral("WEB"));
|
||||
primaryAction.insert(QStringLiteral("primary"), true);
|
||||
actionsArray.push_back(primaryAction);
|
||||
|
||||
QJsonObject replyAction;
|
||||
replyAction.insert(QStringLiteral("label"), QStringLiteral("Reply"));
|
||||
replyAction.insert(QStringLiteral("link"), QStringLiteral(""));
|
||||
replyAction.insert(QStringLiteral("type"), QStringLiteral("REPLY"));
|
||||
replyAction.insert(QStringLiteral("primary"), false);
|
||||
actionsArray.push_back(replyAction);
|
||||
|
||||
activity.insert(QStringLiteral("actions"), actionsArray);
|
||||
|
||||
_activityData.push_back(activity);
|
||||
|
||||
_startingId++;
|
||||
}
|
||||
|
||||
// Insert notification data
|
||||
for (quint32 i = 0; i < _numItemsToInsert; i++) {
|
||||
QJsonObject activity;
|
||||
activity.insert(QStringLiteral("activity_id"), _startingId);
|
||||
activity.insert(QStringLiteral("object_type"), "2fa_id");
|
||||
activity.insert(QStringLiteral("subject"), QStringLiteral("Login attempt from 127.0.0.1"));
|
||||
activity.insert(QStringLiteral("message"), QStringLiteral("Please approve or deny the login attempt."));
|
||||
activity.insert(QStringLiteral("object_name"), QStringLiteral(""));
|
||||
activity.insert(QStringLiteral("datetime"), QDateTime::currentDateTime().toString(Qt::ISODate));
|
||||
activity.insert(QStringLiteral("icon"), QStringLiteral("http://example.de/core/img/places/password.svg"));
|
||||
|
||||
QJsonArray actionsArray;
|
||||
|
||||
QJsonObject primaryAction;
|
||||
primaryAction.insert(QStringLiteral("label"), QStringLiteral("Approve"));
|
||||
primaryAction.insert(QStringLiteral("link"), QStringLiteral("/ocs/v2.php/apps/twofactor_nextcloud_notification/api/v1/attempt/39"));
|
||||
primaryAction.insert(QStringLiteral("type"), QStringLiteral("POST"));
|
||||
primaryAction.insert(QStringLiteral("primary"), true);
|
||||
actionsArray.push_back(primaryAction);
|
||||
|
||||
QJsonObject secondaryAction;
|
||||
secondaryAction.insert(QStringLiteral("label"), QStringLiteral("Cancel"));
|
||||
secondaryAction.insert(QStringLiteral("link"),
|
||||
QString(QStringLiteral("/ocs/v2.php/apps/twofactor_nextcloud_notification/api/v1/attempt/39")));
|
||||
secondaryAction.insert(QStringLiteral("type"), QStringLiteral("DELETE"));
|
||||
secondaryAction.insert(QStringLiteral("primary"), false);
|
||||
actionsArray.push_back(secondaryAction);
|
||||
|
||||
activity.insert(QStringLiteral("actions"), actionsArray);
|
||||
|
||||
_activityData.push_back(activity);
|
||||
|
||||
_startingId++;
|
||||
}
|
||||
|
||||
// Insert notification data
|
||||
for (quint32 i = 0; i < _numItemsToInsert; i++) {
|
||||
QJsonObject activity;
|
||||
activity.insert(QStringLiteral("activity_id"), _startingId);
|
||||
activity.insert(QStringLiteral("object_type"), "create");
|
||||
activity.insert(QStringLiteral("subject"), QStringLiteral("Generate backup codes"));
|
||||
activity.insert(QStringLiteral("message"),
|
||||
QStringLiteral("You enabled two-factor authentication but did not generate backup codes yet. They are needed to restore access to your "
|
||||
"account in case you lose your second factor."));
|
||||
activity.insert(QStringLiteral("object_name"), QStringLiteral(""));
|
||||
activity.insert(QStringLiteral("datetime"), QDateTime::currentDateTime().toString(Qt::ISODate));
|
||||
activity.insert(QStringLiteral("icon"), QStringLiteral("http://example.de/core/img/places/password.svg"));
|
||||
|
||||
QJsonArray actionsArray;
|
||||
|
||||
QJsonObject secondaryAction;
|
||||
secondaryAction.insert(QStringLiteral("label"), QStringLiteral("Dismiss"));
|
||||
secondaryAction.insert(QStringLiteral("link"), QString(QStringLiteral("ocs/v2.php/apps/notifications/api/v2/notifications/19867")));
|
||||
secondaryAction.insert(QStringLiteral("type"), QStringLiteral("DELETE"));
|
||||
secondaryAction.insert(QStringLiteral("primary"), false);
|
||||
actionsArray.push_back(secondaryAction);
|
||||
|
||||
activity.insert(QStringLiteral("actions"), actionsArray);
|
||||
|
||||
_activityData.push_back(activity);
|
||||
|
||||
_startingId++;
|
||||
}
|
||||
|
||||
_startingId--;
|
||||
}
|
||||
|
||||
QByteArray FakeRemoteActivityStorage::activityJsonData(const int sinceId, const int limit)
|
||||
{
|
||||
QJsonArray data;
|
||||
|
||||
const auto itFound = std::find_if(
|
||||
std::cbegin(_activityData), std::cend(_activityData), [&sinceId](const QJsonValue ¤tActivityValue) {
|
||||
const auto currentActivityId =
|
||||
currentActivityValue.toObject().value(QStringLiteral("activity_id")).toInt();
|
||||
return currentActivityId == sinceId;
|
||||
});
|
||||
|
||||
const int startIndex = itFound != std::cend(_activityData)
|
||||
? static_cast<int>(std::distance(std::cbegin(_activityData), itFound))
|
||||
: -1;
|
||||
|
||||
if (startIndex > 0) {
|
||||
for (int dataIndex = startIndex, iteration = 0; dataIndex >= 0 && iteration < limit;
|
||||
--dataIndex, ++iteration) {
|
||||
if (_activityData[dataIndex].toObject().value(QStringLiteral("activity_id")).toInt()
|
||||
> sinceId - limit) {
|
||||
data.append(_activityData[dataIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject root;
|
||||
QJsonObject ocs;
|
||||
ocs.insert(QStringLiteral("data"), data);
|
||||
root.insert(QStringLiteral("ocs"), ocs);
|
||||
|
||||
return QJsonDocument(root).toJson();
|
||||
}
|
||||
|
||||
QJsonValue FakeRemoteActivityStorage::activityById(const int id) const
|
||||
{
|
||||
const auto itFound = std::find_if(
|
||||
std::cbegin(_activityData), std::cend(_activityData), [&id](const QJsonValue ¤tActivityValue) {
|
||||
const auto currentActivityId =
|
||||
currentActivityValue.toObject().value(QStringLiteral("activity_id")).toInt();
|
||||
return currentActivityId == id;
|
||||
});
|
||||
|
||||
if (itFound != std::cend(_activityData)) {
|
||||
return (*itFound);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
int FakeRemoteActivityStorage::startingIdLast() const
|
||||
{
|
||||
return _startingId;
|
||||
}
|
||||
|
||||
int FakeRemoteActivityStorage::numItemsToInsert() const {
|
||||
return _numItemsToInsert;
|
||||
}
|
||||
|
||||
int FakeRemoteActivityStorage::totalNumActivites() const {
|
||||
return _activityData.count();
|
||||
}
|
||||
|
||||
|
||||
void TestingALM::startFetchJobWithNumActivities(const int numActivities)
|
||||
{
|
||||
auto *job = new OCC::JsonApiJob(
|
||||
accountState()->account(), QLatin1String("ocs/v2.php/apps/activity/api/v2/activity"), this);
|
||||
QObject::connect(this, &TestingALM::activityJobStatusCode, this, &TestingALM::slotProcessReceivedActivities);
|
||||
QObject::connect(job, &OCC::JsonApiJob::jsonReceived, this, &TestingALM::activitiesReceived);
|
||||
|
||||
QUrlQuery params;
|
||||
params.addQueryItem(QLatin1String("since"), QString::number(currentItem()));
|
||||
params.addQueryItem(QLatin1String("limit"), QString::number(numActivities));
|
||||
job->addQueryParams(params);
|
||||
|
||||
setAndRefreshCurrentlyFetching(true);
|
||||
job->start();
|
||||
}
|
||||
|
||||
void TestingALM::startFetchJob()
|
||||
{
|
||||
startFetchJobWithNumActivities();
|
||||
}
|
||||
|
||||
void TestingALM::startMaxActivitiesFetchJob()
|
||||
{
|
||||
startFetchJobWithNumActivities(_maxActivities + 1);
|
||||
}
|
||||
|
||||
void TestingALM::slotProcessReceivedActivities()
|
||||
{
|
||||
auto finalListCopy = _finalList;
|
||||
for (int i = _numRowsPrev; i < rowCount(); ++i) {
|
||||
const auto modelIndex = index(i, 0);
|
||||
auto activity = finalListCopy.at(modelIndex.row());
|
||||
if (activity._links.isEmpty()) {
|
||||
const auto activityJsonObject = FakeRemoteActivityStorage::instance()->activityById(activity._id);
|
||||
|
||||
if (!activityJsonObject.isNull()) {
|
||||
// because "_links" are normally populated within the notificationhandler.cpp, which we don't run as part of this unit test, we have to fill them here
|
||||
// TODO: move the logic to populate "_links" to "activitylistmodel.cpp"
|
||||
const auto actions = activityJsonObject.toObject().value("actions").toArray();
|
||||
for (const auto &action : actions) {
|
||||
activity._links.append(OCC::ActivityLink::createFomJsonObject(action.toObject()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
finalListCopy[modelIndex.row()] = activity;
|
||||
}
|
||||
|
||||
_finalList = finalListCopy;
|
||||
emit allConflictsChanged();
|
||||
|
||||
setAndRefreshCurrentlyFetching(false);
|
||||
emit activitiesProcessed();
|
||||
}
|
||||
|
||||
}
|
105
test/activitylistmodeltestutils.h
Normal file
105
test/activitylistmodeltestutils.h
Normal file
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright (C) by Claudio Cambra <claudio.cambra@nextcloud.com>
|
||||
*
|
||||
* 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) any later version.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QVariantMap>
|
||||
|
||||
#include "gui/tray/activitylistmodel.h"
|
||||
|
||||
#include "libsync/account.h"
|
||||
#include "gui/accountstate.h"
|
||||
#include "gui/accountmanager.h"
|
||||
|
||||
#pragma once
|
||||
|
||||
class FakeQNAM;
|
||||
class QByteArray;
|
||||
class QJsonValue;
|
||||
|
||||
namespace ActivityListModelTestUtils
|
||||
{
|
||||
|
||||
[[nodiscard]] QNetworkReply *almTestQnamOverride(FakeQNAM * const fakeQnam,
|
||||
const QNetworkAccessManager::Operation op,
|
||||
const QNetworkRequest &req,
|
||||
const QString &accountUrl,
|
||||
QObject * const parent = nullptr,
|
||||
const int searchResultsReplyDelay = 0,
|
||||
QIODevice * const device = nullptr);
|
||||
|
||||
[[nodiscard]] OCC::Activity exampleNotificationActivity(const QString &accountName, const int id = 1);
|
||||
[[nodiscard]] OCC::Activity exampleSyncResultErrorActivity(const QString &accountName, const int id = 2);
|
||||
[[nodiscard]] OCC::Activity exampleSyncFileItemActivity(const QString &accountName, const QUrl &link, const int id = 3);
|
||||
[[nodiscard]] OCC::Activity exampleFileIgnoredActivity(const QString &accountName, const QUrl &link = {}, const int id = 4);
|
||||
|
||||
class FakeRemoteActivityStorage
|
||||
{
|
||||
FakeRemoteActivityStorage() = default;
|
||||
|
||||
public:
|
||||
static FakeRemoteActivityStorage *instance();
|
||||
|
||||
[[nodiscard]] QByteArray activityJsonData(const int sinceId, const int limit);
|
||||
[[nodiscard]] QJsonValue activityById(const int id) const;
|
||||
|
||||
[[nodiscard]] int startingIdLast() const;
|
||||
[[nodiscard]] int numItemsToInsert() const;
|
||||
[[nodiscard]] int totalNumActivites() const;
|
||||
|
||||
static void destroy();
|
||||
void init();
|
||||
void initActivityData();
|
||||
|
||||
private:
|
||||
QJsonArray _activityData;
|
||||
QVariantMap _metaSuccess;
|
||||
quint32 _numItemsToInsert = 30;
|
||||
int _startingId = 90000;
|
||||
|
||||
static FakeRemoteActivityStorage *_instance;
|
||||
};
|
||||
|
||||
class TestingALM : public OCC::ActivityListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TestingALM() = default;
|
||||
|
||||
[[nodiscard]] int maxActivities() const
|
||||
{
|
||||
return _maxActivities;
|
||||
};
|
||||
// Need to include the dummy "show more in activities app" activity
|
||||
[[nodiscard]] int maxPossibleActivities() const
|
||||
{
|
||||
return maxActivities() + 1;
|
||||
}
|
||||
|
||||
public slots:
|
||||
void startFetchJob() override;
|
||||
void startMaxActivitiesFetchJob();
|
||||
void slotProcessReceivedActivities();
|
||||
|
||||
signals:
|
||||
void activitiesProcessed();
|
||||
|
||||
private slots:
|
||||
void startFetchJobWithNumActivities(const int numActivities = 50);
|
||||
|
||||
private:
|
||||
int _numRowsPrev = 0;
|
||||
};
|
||||
|
||||
}
|
|
@ -12,11 +12,7 @@
|
|||
* for more details.
|
||||
*/
|
||||
|
||||
#include "gui/tray/activitylistmodel.h"
|
||||
|
||||
#include "account.h"
|
||||
#include "accountstate.h"
|
||||
#include "accountmanager.h"
|
||||
#include "activitylistmodeltestutils.h"
|
||||
#include "syncenginetestutils.h"
|
||||
#include "syncresult.h"
|
||||
|
||||
|
@ -25,395 +21,7 @@
|
|||
#include <QSignalSpy>
|
||||
#include <QTest>
|
||||
|
||||
namespace {
|
||||
constexpr auto startingId = 90000;
|
||||
}
|
||||
|
||||
static QByteArray fake404Response = R"(
|
||||
{"ocs":{"meta":{"status":"failure","statuscode":404,"message":"Invalid query, please check the syntax. API specifications are here: http:\/\/www.freedesktop.org\/wiki\/Specifications\/open-collaboration-services.\n"},"data":[]}}
|
||||
)";
|
||||
|
||||
static QByteArray fake400Response = R"(
|
||||
{"ocs":{"meta":{"status":"failure","statuscode":400,"message":"Parameter is incorrect.\n"},"data":[]}}
|
||||
)";
|
||||
|
||||
static QByteArray fake500Response = R"(
|
||||
{"ocs":{"meta":{"status":"failure","statuscode":500,"message":"Internal Server Error.\n"},"data":[]}}
|
||||
)";
|
||||
|
||||
class FakeRemoteActivityStorage
|
||||
{
|
||||
FakeRemoteActivityStorage() = default;
|
||||
|
||||
public:
|
||||
static FakeRemoteActivityStorage *instance()
|
||||
{
|
||||
if (!_instance) {
|
||||
_instance = new FakeRemoteActivityStorage();
|
||||
_instance->init();
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
|
||||
static void destroy()
|
||||
{
|
||||
if (_instance) {
|
||||
delete _instance;
|
||||
}
|
||||
|
||||
_instance = nullptr;
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
if (!_activityData.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_metaSuccess = {{QStringLiteral("status"), QStringLiteral("ok")}, {QStringLiteral("statuscode"), 200},
|
||||
{QStringLiteral("message"), QStringLiteral("OK")}};
|
||||
|
||||
initActivityData();
|
||||
}
|
||||
|
||||
void initActivityData()
|
||||
{
|
||||
// Insert activity data
|
||||
for (quint32 i = 0; i <= _numItemsToInsert; i++) {
|
||||
QJsonObject activity;
|
||||
activity.insert(QStringLiteral("object_type"), "files");
|
||||
activity.insert(QStringLiteral("activity_id"), _startingId);
|
||||
activity.insert(QStringLiteral("type"), QStringLiteral("file"));
|
||||
activity.insert(QStringLiteral("subject"), QStringLiteral("You created %1.txt").arg(i));
|
||||
activity.insert(QStringLiteral("message"), QStringLiteral(""));
|
||||
activity.insert(QStringLiteral("object_name"), QStringLiteral("%1.txt").arg(i));
|
||||
activity.insert(QStringLiteral("datetime"), QDateTime::currentDateTime().toString(Qt::ISODate));
|
||||
activity.insert(QStringLiteral("icon"), QStringLiteral("http://example.de/apps/files/img/add-color.svg"));
|
||||
|
||||
_activityData.push_back(activity);
|
||||
|
||||
_startingId++;
|
||||
}
|
||||
|
||||
// Insert notification data
|
||||
for (quint32 i = 0; i < _numItemsToInsert; i++) {
|
||||
QJsonObject activity;
|
||||
activity.insert(QStringLiteral("activity_id"), _startingId);
|
||||
activity.insert(QStringLiteral("object_type"), "calendar");
|
||||
activity.insert(QStringLiteral("type"), QStringLiteral("calendar-event"));
|
||||
activity.insert(
|
||||
QStringLiteral("subject"), QStringLiteral("You created event %1 in calendar Events").arg(i));
|
||||
activity.insert(QStringLiteral("message"), QStringLiteral(""));
|
||||
activity.insert(QStringLiteral("object_name"), QStringLiteral(""));
|
||||
activity.insert(QStringLiteral("datetime"), QDateTime::currentDateTime().toString(Qt::ISODate));
|
||||
activity.insert(QStringLiteral("icon"), QStringLiteral("http://example.de/core/img/places/calendar.svg"));
|
||||
|
||||
_activityData.push_back(activity);
|
||||
|
||||
_startingId++;
|
||||
}
|
||||
|
||||
// Insert notification data
|
||||
for (quint32 i = 0; i < _numItemsToInsert; i++) {
|
||||
QJsonObject activity;
|
||||
activity.insert(QStringLiteral("activity_id"), _startingId);
|
||||
activity.insert(QStringLiteral("object_type"), "chat");
|
||||
activity.insert(QStringLiteral("type"), QStringLiteral("chat"));
|
||||
activity.insert(QStringLiteral("subject"), QStringLiteral("You have received %1's message").arg(i));
|
||||
activity.insert(QStringLiteral("message"), QStringLiteral(""));
|
||||
activity.insert(QStringLiteral("object_name"), QStringLiteral(""));
|
||||
activity.insert(QStringLiteral("datetime"), QDateTime::currentDateTime().toString(Qt::ISODate));
|
||||
activity.insert(QStringLiteral("icon"), QStringLiteral("http://example.de/core/img/places/talk.svg"));
|
||||
|
||||
QJsonArray actionsArray;
|
||||
|
||||
QJsonObject replyAction;
|
||||
replyAction.insert(QStringLiteral("label"), QStringLiteral("Reply"));
|
||||
replyAction.insert(QStringLiteral("link"), QStringLiteral(""));
|
||||
replyAction.insert(QStringLiteral("type"), QStringLiteral("REPLY"));
|
||||
replyAction.insert(QStringLiteral("primary"), true);
|
||||
actionsArray.push_back(replyAction);
|
||||
|
||||
QJsonObject primaryAction;
|
||||
primaryAction.insert(QStringLiteral("label"), QStringLiteral("View chat"));
|
||||
primaryAction.insert(QStringLiteral("link"), QStringLiteral("http://cloud.example.de/call/9p4vjdzd"));
|
||||
primaryAction.insert(QStringLiteral("type"), QStringLiteral("WEB"));
|
||||
primaryAction.insert(QStringLiteral("primary"), false);
|
||||
actionsArray.push_back(primaryAction);
|
||||
|
||||
QJsonObject additionalAction;
|
||||
additionalAction.insert(QStringLiteral("label"), QStringLiteral("Additional 1"));
|
||||
additionalAction.insert(QStringLiteral("link"), QStringLiteral("http://cloud.example.de/call/9p4vjdzd"));
|
||||
additionalAction.insert(QStringLiteral("type"), QStringLiteral("POST"));
|
||||
additionalAction.insert(QStringLiteral("primary"), false);
|
||||
actionsArray.push_back(additionalAction);
|
||||
additionalAction.insert(QStringLiteral("label"), QStringLiteral("Additional 2"));
|
||||
actionsArray.push_back(additionalAction);
|
||||
|
||||
activity.insert(QStringLiteral("actions"), actionsArray);
|
||||
|
||||
_activityData.push_back(activity);
|
||||
|
||||
_startingId++;
|
||||
}
|
||||
|
||||
// Insert notification data
|
||||
for (quint32 i = 0; i < _numItemsToInsert; i++) {
|
||||
QJsonObject activity;
|
||||
activity.insert(QStringLiteral("activity_id"), _startingId);
|
||||
activity.insert(QStringLiteral("object_type"), "room");
|
||||
activity.insert(QStringLiteral("type"), QStringLiteral("room"));
|
||||
activity.insert(QStringLiteral("subject"), QStringLiteral("You have been invited into room%1").arg(i));
|
||||
activity.insert(QStringLiteral("message"), QStringLiteral(""));
|
||||
activity.insert(QStringLiteral("object_name"), QStringLiteral(""));
|
||||
activity.insert(QStringLiteral("datetime"), QDateTime::currentDateTime().toString(Qt::ISODate));
|
||||
activity.insert(QStringLiteral("icon"), QStringLiteral("http://example.de/core/img/places/talk.svg"));
|
||||
|
||||
QJsonArray actionsArray;
|
||||
|
||||
QJsonObject replyAction;
|
||||
replyAction.insert(QStringLiteral("label"), QStringLiteral("Reply"));
|
||||
replyAction.insert(QStringLiteral("link"), QStringLiteral(""));
|
||||
replyAction.insert(QStringLiteral("type"), QStringLiteral("REPLY"));
|
||||
replyAction.insert(QStringLiteral("primary"), true);
|
||||
actionsArray.push_back(replyAction);
|
||||
|
||||
QJsonObject primaryAction;
|
||||
primaryAction.insert(QStringLiteral("label"), QStringLiteral("View chat"));
|
||||
primaryAction.insert(QStringLiteral("link"), QStringLiteral("http://cloud.example.de/call/9p4vjdzd"));
|
||||
primaryAction.insert(QStringLiteral("type"), QStringLiteral("WEB"));
|
||||
primaryAction.insert(QStringLiteral("primary"), false);
|
||||
actionsArray.push_back(primaryAction);
|
||||
|
||||
activity.insert(QStringLiteral("actions"), actionsArray);
|
||||
|
||||
_activityData.push_back(activity);
|
||||
|
||||
_startingId++;
|
||||
}
|
||||
|
||||
// Insert notification data
|
||||
for (quint32 i = 0; i < _numItemsToInsert; i++) {
|
||||
QJsonObject activity;
|
||||
activity.insert(QStringLiteral("activity_id"), _startingId);
|
||||
activity.insert(QStringLiteral("object_type"), "call");
|
||||
activity.insert(QStringLiteral("type"), QStringLiteral("call"));
|
||||
activity.insert(QStringLiteral("subject"), QStringLiteral("You have missed a %1's call").arg(i));
|
||||
activity.insert(QStringLiteral("message"), QStringLiteral(""));
|
||||
activity.insert(QStringLiteral("object_name"), QStringLiteral(""));
|
||||
activity.insert(QStringLiteral("datetime"), QDateTime::currentDateTime().toString(Qt::ISODate));
|
||||
activity.insert(QStringLiteral("icon"), QStringLiteral("http://example.de/core/img/places/talk.svg"));
|
||||
|
||||
QJsonArray actionsArray;
|
||||
|
||||
QJsonObject primaryAction;
|
||||
primaryAction.insert(QStringLiteral("label"), QStringLiteral("Call back"));
|
||||
primaryAction.insert(QStringLiteral("link"), QStringLiteral("http://cloud.example.de/call/9p4vjdzd"));
|
||||
primaryAction.insert(QStringLiteral("type"), QStringLiteral("WEB"));
|
||||
primaryAction.insert(QStringLiteral("primary"), true);
|
||||
actionsArray.push_back(primaryAction);
|
||||
|
||||
QJsonObject replyAction;
|
||||
replyAction.insert(QStringLiteral("label"), QStringLiteral("Reply"));
|
||||
replyAction.insert(QStringLiteral("link"), QStringLiteral(""));
|
||||
replyAction.insert(QStringLiteral("type"), QStringLiteral("REPLY"));
|
||||
replyAction.insert(QStringLiteral("primary"), false);
|
||||
actionsArray.push_back(replyAction);
|
||||
|
||||
activity.insert(QStringLiteral("actions"), actionsArray);
|
||||
|
||||
_activityData.push_back(activity);
|
||||
|
||||
_startingId++;
|
||||
}
|
||||
|
||||
// Insert notification data
|
||||
for (quint32 i = 0; i < _numItemsToInsert; i++) {
|
||||
QJsonObject activity;
|
||||
activity.insert(QStringLiteral("activity_id"), _startingId);
|
||||
activity.insert(QStringLiteral("object_type"), "2fa_id");
|
||||
activity.insert(QStringLiteral("subject"), QStringLiteral("Login attempt from 127.0.0.1"));
|
||||
activity.insert(QStringLiteral("message"), QStringLiteral("Please approve or deny the login attempt."));
|
||||
activity.insert(QStringLiteral("object_name"), QStringLiteral(""));
|
||||
activity.insert(QStringLiteral("datetime"), QDateTime::currentDateTime().toString(Qt::ISODate));
|
||||
activity.insert(QStringLiteral("icon"), QStringLiteral("http://example.de/core/img/places/password.svg"));
|
||||
|
||||
QJsonArray actionsArray;
|
||||
|
||||
QJsonObject primaryAction;
|
||||
primaryAction.insert(QStringLiteral("label"), QStringLiteral("Approve"));
|
||||
primaryAction.insert(QStringLiteral("link"), QStringLiteral("/ocs/v2.php/apps/twofactor_nextcloud_notification/api/v1/attempt/39"));
|
||||
primaryAction.insert(QStringLiteral("type"), QStringLiteral("POST"));
|
||||
primaryAction.insert(QStringLiteral("primary"), true);
|
||||
actionsArray.push_back(primaryAction);
|
||||
|
||||
QJsonObject secondaryAction;
|
||||
secondaryAction.insert(QStringLiteral("label"), QStringLiteral("Cancel"));
|
||||
secondaryAction.insert(QStringLiteral("link"),
|
||||
QString(QStringLiteral("/ocs/v2.php/apps/twofactor_nextcloud_notification/api/v1/attempt/39")));
|
||||
secondaryAction.insert(QStringLiteral("type"), QStringLiteral("DELETE"));
|
||||
secondaryAction.insert(QStringLiteral("primary"), false);
|
||||
actionsArray.push_back(secondaryAction);
|
||||
|
||||
activity.insert(QStringLiteral("actions"), actionsArray);
|
||||
|
||||
_activityData.push_back(activity);
|
||||
|
||||
_startingId++;
|
||||
}
|
||||
|
||||
// Insert notification data
|
||||
for (quint32 i = 0; i < _numItemsToInsert; i++) {
|
||||
QJsonObject activity;
|
||||
activity.insert(QStringLiteral("activity_id"), _startingId);
|
||||
activity.insert(QStringLiteral("object_type"), "create");
|
||||
activity.insert(QStringLiteral("subject"), QStringLiteral("Generate backup codes"));
|
||||
activity.insert(QStringLiteral("message"), QStringLiteral("You enabled two-factor authentication but did not generate backup codes yet. They are needed to restore access to your account in case you lose your second factor."));
|
||||
activity.insert(QStringLiteral("object_name"), QStringLiteral(""));
|
||||
activity.insert(QStringLiteral("datetime"), QDateTime::currentDateTime().toString(Qt::ISODate));
|
||||
activity.insert(QStringLiteral("icon"), QStringLiteral("http://example.de/core/img/places/password.svg"));
|
||||
|
||||
QJsonArray actionsArray;
|
||||
|
||||
QJsonObject secondaryAction;
|
||||
secondaryAction.insert(QStringLiteral("label"), QStringLiteral("Dismiss"));
|
||||
secondaryAction.insert(QStringLiteral("link"),
|
||||
QString(QStringLiteral("ocs/v2.php/apps/notifications/api/v2/notifications/19867")));
|
||||
secondaryAction.insert(QStringLiteral("type"), QStringLiteral("DELETE"));
|
||||
secondaryAction.insert(QStringLiteral("primary"), false);
|
||||
actionsArray.push_back(secondaryAction);
|
||||
|
||||
activity.insert(QStringLiteral("actions"), actionsArray);
|
||||
|
||||
_activityData.push_back(activity);
|
||||
|
||||
_startingId++;
|
||||
}
|
||||
|
||||
_startingId--;
|
||||
}
|
||||
|
||||
const QByteArray activityJsonData(int sinceId, int limit)
|
||||
{
|
||||
QJsonArray data;
|
||||
|
||||
const auto itFound = std::find_if(
|
||||
std::cbegin(_activityData), std::cend(_activityData), [&sinceId](const QJsonValue ¤tActivityValue) {
|
||||
const auto currentActivityId =
|
||||
currentActivityValue.toObject().value(QStringLiteral("activity_id")).toInt();
|
||||
return currentActivityId == sinceId;
|
||||
});
|
||||
|
||||
const int startIndex = itFound != std::cend(_activityData)
|
||||
? static_cast<int>(std::distance(std::cbegin(_activityData), itFound))
|
||||
: -1;
|
||||
|
||||
if (startIndex > 0) {
|
||||
for (int dataIndex = startIndex, iteration = 0; dataIndex >= 0 && iteration < limit;
|
||||
--dataIndex, ++iteration) {
|
||||
if (_activityData[dataIndex].toObject().value(QStringLiteral("activity_id")).toInt()
|
||||
> sinceId - limit) {
|
||||
data.append(_activityData[dataIndex]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QJsonObject root;
|
||||
QJsonObject ocs;
|
||||
ocs.insert(QStringLiteral("data"), data);
|
||||
root.insert(QStringLiteral("ocs"), ocs);
|
||||
|
||||
return QJsonDocument(root).toJson();
|
||||
}
|
||||
|
||||
QJsonValue activityById(int id)
|
||||
{
|
||||
const auto itFound = std::find_if(
|
||||
std::cbegin(_activityData), std::cend(_activityData), [&id](const QJsonValue ¤tActivityValue) {
|
||||
const auto currentActivityId =
|
||||
currentActivityValue.toObject().value(QStringLiteral("activity_id")).toInt();
|
||||
return currentActivityId == id;
|
||||
});
|
||||
|
||||
if (itFound != std::cend(_activityData)) {
|
||||
return (*itFound);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] int startingIdLast() const { return _startingId; }
|
||||
|
||||
private:
|
||||
static FakeRemoteActivityStorage *_instance;
|
||||
QJsonArray _activityData;
|
||||
QVariantMap _metaSuccess;
|
||||
quint32 _numItemsToInsert = 30;
|
||||
int _startingId = startingId;
|
||||
};
|
||||
|
||||
FakeRemoteActivityStorage *FakeRemoteActivityStorage::_instance = nullptr;
|
||||
|
||||
class TestingALM : public OCC::ActivityListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TestingALM() = default;
|
||||
|
||||
void startFetchJob() override
|
||||
{
|
||||
auto *job = new OCC::JsonApiJob(
|
||||
accountState()->account(), QLatin1String("ocs/v2.php/apps/activity/api/v2/activity"), this);
|
||||
QObject::connect(this, &TestingALM::activityJobStatusCode, this, &TestingALM::slotProcessReceivedActivities);
|
||||
QObject::connect(job, &OCC::JsonApiJob::jsonReceived, this, &TestingALM::activitiesReceived);
|
||||
|
||||
QUrlQuery params;
|
||||
params.addQueryItem(QLatin1String("since"), QString::number(currentItem()));
|
||||
params.addQueryItem(QLatin1String("limit"), QString::number(50));
|
||||
job->addQueryParams(params);
|
||||
|
||||
setAndRefreshCurrentlyFetching(true);
|
||||
job->start();
|
||||
}
|
||||
|
||||
public slots:
|
||||
void slotProcessReceivedActivities()
|
||||
{
|
||||
if (rowCount() > _numRowsPrev) {
|
||||
auto finalListCopy = finalList();
|
||||
for (int i = _numRowsPrev; i < rowCount(); ++i) {
|
||||
const auto modelIndex = index(i, 0);
|
||||
auto activity = finalListCopy.at(modelIndex.row());
|
||||
if (activity._links.isEmpty()) {
|
||||
const auto activityJsonObject = FakeRemoteActivityStorage::instance()->activityById(activity._id);
|
||||
|
||||
if (!activityJsonObject.isNull()) {
|
||||
// because "_links" are normally populated within the notificationhandler.cpp, which we don't run as part of this unit test, we have to fill them here
|
||||
// TODO: move the logic to populate "_links" to "activitylistmodel.cpp"
|
||||
auto actions = activityJsonObject.toObject().value("actions").toArray();
|
||||
foreach (auto action, actions) {
|
||||
activity._links.append(OCC::ActivityLink::createFomJsonObject(action.toObject()));
|
||||
}
|
||||
|
||||
finalListCopy[modelIndex.row()] = activity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setFinalList(finalListCopy);
|
||||
}
|
||||
_numRowsPrev = rowCount();
|
||||
setAndRefreshCurrentlyFetching(false);
|
||||
emit activitiesProcessed();
|
||||
}
|
||||
signals:
|
||||
void activitiesProcessed();
|
||||
|
||||
private:
|
||||
int _numRowsPrev = 0;
|
||||
};
|
||||
using namespace ActivityListModelTestUtils;
|
||||
|
||||
class TestActivityListModel : public QObject
|
||||
{
|
||||
|
@ -489,70 +97,24 @@ private slots:
|
|||
accountState.reset(new OCC::AccountState(account));
|
||||
|
||||
fakeQnam->setOverride([this](QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *device) {
|
||||
Q_UNUSED(device);
|
||||
QNetworkReply *reply = nullptr;
|
||||
|
||||
const auto urlQuery = QUrlQuery(req.url());
|
||||
const auto format = urlQuery.queryItemValue(QStringLiteral("format"));
|
||||
const auto since = urlQuery.queryItemValue(QStringLiteral("since")).toInt();
|
||||
const auto limit = urlQuery.queryItemValue(QStringLiteral("limit")).toInt();
|
||||
const auto path = req.url().path();
|
||||
|
||||
if (!req.url().toString().startsWith(accountState->account()->url().toString())) {
|
||||
reply = new FakeErrorReply(op, req, this, 404, fake404Response);
|
||||
}
|
||||
if (format != QStringLiteral("json")) {
|
||||
reply = new FakeErrorReply(op, req, this, 400, fake400Response);
|
||||
}
|
||||
|
||||
if (path.startsWith(QStringLiteral("/ocs/v2.php/apps/activity/api/v2/activity"))) {
|
||||
reply = new FakePayloadReply(op, req, FakeRemoteActivityStorage::instance()->activityJsonData(since, limit), searchResultsReplyDelay, fakeQnam.data());
|
||||
}
|
||||
|
||||
if (!reply) {
|
||||
return qobject_cast<QNetworkReply*>(new FakeErrorReply(op, req, this, 404, QByteArrayLiteral("{error: \"Not found!\"}")));
|
||||
}
|
||||
|
||||
return reply;
|
||||
Q_UNUSED(device)
|
||||
return almTestQnamOverride(fakeQnam.data(),
|
||||
op,
|
||||
req,
|
||||
accountState->account()->url().toString(),
|
||||
this,
|
||||
searchResultsReplyDelay);
|
||||
});
|
||||
|
||||
OCC::AccountManager::instance()->addAccount(account);
|
||||
|
||||
// Activity comparison is done by checking type, id, and accName
|
||||
// We need an activity with these details, at least
|
||||
testNotificationActivity._accName = accountState->account()->displayName();
|
||||
testNotificationActivity._id = 1;
|
||||
testNotificationActivity._type = OCC::Activity::NotificationType;
|
||||
testNotificationActivity._dateTime = QDateTime::currentDateTime();
|
||||
testNotificationActivity._subject = QStringLiteral("Sample notification text");
|
||||
const auto accName = accountState->account()->displayName();
|
||||
const auto accUrl = accountState->account()->url();
|
||||
|
||||
testSyncResultErrorActivity._id = 2;
|
||||
testSyncResultErrorActivity._type = OCC::Activity::SyncResultType;
|
||||
testSyncResultErrorActivity._syncResultStatus = OCC::SyncResult::Error;
|
||||
testSyncResultErrorActivity._dateTime = QDateTime::currentDateTime();
|
||||
testSyncResultErrorActivity._subject = QStringLiteral("Sample failed sync text");
|
||||
testSyncResultErrorActivity._message = QStringLiteral("/path/to/thingy");
|
||||
testSyncResultErrorActivity._link = QStringLiteral("/path/to/thingy");
|
||||
testSyncResultErrorActivity._accName = accountState->account()->displayName();
|
||||
|
||||
testSyncFileItemActivity._id = 3;
|
||||
testSyncFileItemActivity._type = OCC::Activity::SyncFileItemType; //client activity
|
||||
testSyncFileItemActivity._syncFileItemStatus = OCC::SyncFileItem::Success;
|
||||
testSyncFileItemActivity._dateTime = QDateTime::currentDateTime();
|
||||
testSyncFileItemActivity._message = QStringLiteral("Sample file successfully synced text");
|
||||
testSyncFileItemActivity._link = accountState->account()->url();
|
||||
testSyncFileItemActivity._accName = accountState->account()->displayName();
|
||||
testSyncFileItemActivity._file = QStringLiteral("xyz.pdf");
|
||||
|
||||
testFileIgnoredActivity._id = 4;
|
||||
testFileIgnoredActivity._type = OCC::Activity::SyncFileItemType;
|
||||
testFileIgnoredActivity._syncFileItemStatus = OCC::SyncFileItem::FileIgnored;
|
||||
testFileIgnoredActivity._dateTime = QDateTime::currentDateTime();
|
||||
testFileIgnoredActivity._subject = QStringLiteral("Sample ignored file sync text");
|
||||
testFileIgnoredActivity._link = accountState->account()->url();
|
||||
testFileIgnoredActivity._accName = accountState->account()->displayName();
|
||||
testFileIgnoredActivity._folder = QStringLiteral("thingy");
|
||||
testFileIgnoredActivity._file = QStringLiteral("test.txt");
|
||||
testNotificationActivity = exampleNotificationActivity(accName);
|
||||
testSyncResultErrorActivity = exampleSyncResultErrorActivity(accName);
|
||||
testSyncFileItemActivity = exampleSyncFileItemActivity(accName, accUrl);
|
||||
testFileIgnoredActivity = exampleFileIgnoredActivity(accName, accUrl);
|
||||
};
|
||||
|
||||
// Test receiving activity from server
|
||||
|
|
205
test/testsortedactivitylistmodel.cpp
Normal file
205
test/testsortedactivitylistmodel.cpp
Normal file
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
* Copyright (C) by Claudio Cambra <claudio.cambra@nextcloud.com>
|
||||
*
|
||||
* 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) any later version.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "activitylistmodeltestutils.h"
|
||||
#include "syncenginetestutils.h"
|
||||
#include "syncresult.h"
|
||||
#include "tray/sortedactivitylistmodel.h"
|
||||
|
||||
#include <QAbstractItemModelTester>
|
||||
#include <QDesktopServices>
|
||||
#include <QSignalSpy>
|
||||
#include <QTest>
|
||||
|
||||
using namespace ActivityListModelTestUtils;
|
||||
|
||||
class TestSortedActivityListModel : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TestSortedActivityListModel() = default;
|
||||
~TestSortedActivityListModel() override
|
||||
{
|
||||
OCC::AccountManager::instance()->deleteAccount(accountState.data());
|
||||
}
|
||||
|
||||
QScopedPointer<FakeQNAM> fakeQnam;
|
||||
OCC::AccountPtr account;
|
||||
QScopedPointer<OCC::AccountState> accountState;
|
||||
|
||||
OCC::Activity testNotificationActivity;
|
||||
OCC::Activity testSyncResultErrorActivity;
|
||||
OCC::Activity testSyncFileItemActivity;
|
||||
OCC::Activity testFileIgnoredActivity;
|
||||
|
||||
QSharedPointer<OCC::SortedActivityListModel> testingSortedALM()
|
||||
{
|
||||
const auto model = new TestingALM;
|
||||
model->setAccountState(accountState.data());
|
||||
|
||||
QSharedPointer<OCC::SortedActivityListModel> sortedModel(new OCC::SortedActivityListModel);
|
||||
sortedModel->setSourceModel(model);
|
||||
QAbstractItemModelTester sortedModelTester(sortedModel.data());
|
||||
|
||||
return sortedModel;
|
||||
}
|
||||
|
||||
void addActivity(QSharedPointer<OCC::SortedActivityListModel> model,
|
||||
void(OCC::ActivityListModel::*addingMethod)(const OCC::Activity&),
|
||||
OCC::Activity &activity)
|
||||
{
|
||||
const auto originalRowCount = model->rowCount();
|
||||
const auto sourceModel = dynamic_cast<TestingALM*>(model->sourceModel());
|
||||
|
||||
(sourceModel->*addingMethod)(activity);
|
||||
QCOMPARE(model->rowCount(), originalRowCount + 1);
|
||||
}
|
||||
|
||||
void addActivity(QSharedPointer<OCC::SortedActivityListModel> model,
|
||||
void (OCC::ActivityListModel::*addingMethod)(const OCC::Activity &, OCC::ActivityListModel::ErrorType),
|
||||
OCC::Activity &activity,
|
||||
OCC::ActivityListModel::ErrorType type)
|
||||
{
|
||||
const auto originalRowCount = model->rowCount();
|
||||
const auto sourceModel = dynamic_cast<TestingALM *>(model->sourceModel());
|
||||
|
||||
(sourceModel->*addingMethod)(activity, type);
|
||||
QCOMPARE(model->rowCount(), originalRowCount + 1);
|
||||
|
||||
const auto index = model->index(0, 0);
|
||||
QVERIFY(index.isValid());
|
||||
}
|
||||
|
||||
private slots:
|
||||
void initTestCase()
|
||||
{
|
||||
fakeQnam.reset(new FakeQNAM({}));
|
||||
account = OCC::Account::create();
|
||||
account->setCredentials(new FakeCredentials{fakeQnam.data()});
|
||||
account->setUrl(QUrl(("http://example.de")));
|
||||
|
||||
accountState.reset(new OCC::AccountState(account));
|
||||
|
||||
fakeQnam->setOverride([this](QNetworkAccessManager::Operation op, const QNetworkRequest &req, QIODevice *device) {
|
||||
Q_UNUSED(device)
|
||||
return almTestQnamOverride(fakeQnam.data(),
|
||||
op,
|
||||
req,
|
||||
accountState->account()->url().toString(),
|
||||
this);
|
||||
});
|
||||
|
||||
OCC::AccountManager::instance()->addAccount(account);
|
||||
|
||||
const auto accName = accountState->account()->displayName();
|
||||
const auto accUrl = accountState->account()->url();
|
||||
|
||||
testNotificationActivity = exampleNotificationActivity(accName);
|
||||
testSyncResultErrorActivity = exampleSyncResultErrorActivity(accName);
|
||||
testSyncFileItemActivity = exampleSyncFileItemActivity(accName, accUrl);
|
||||
testFileIgnoredActivity = exampleFileIgnoredActivity(accName, accUrl);
|
||||
};
|
||||
|
||||
void testMatchingRowCounts()
|
||||
{
|
||||
const auto model = testingSortedALM();
|
||||
const auto sourceModel = dynamic_cast<TestingALM*>(model->sourceModel());
|
||||
QCOMPARE(sourceModel->rowCount(), 0);
|
||||
QCOMPARE(model->rowCount(), sourceModel->rowCount());
|
||||
|
||||
sourceModel->setCurrentItem(FakeRemoteActivityStorage::instance()->startingIdLast());
|
||||
sourceModel->startFetchJob();
|
||||
QSignalSpy activitiesJob(sourceModel, &TestingALM::activitiesProcessed);
|
||||
QVERIFY(activitiesJob.wait(3000));
|
||||
QCOMPARE(sourceModel->rowCount(), 50);
|
||||
QCOMPARE(model->rowCount(), sourceModel->rowCount());
|
||||
}
|
||||
|
||||
void testUpdate()
|
||||
{
|
||||
const auto model = testingSortedALM();
|
||||
const auto sourceModel = dynamic_cast<TestingALM*>(model->sourceModel());
|
||||
|
||||
sourceModel->setCurrentItem(FakeRemoteActivityStorage::instance()->startingIdLast());
|
||||
sourceModel->startFetchJob();
|
||||
QSignalSpy activitiesJob(sourceModel, &TestingALM::activitiesProcessed);
|
||||
QVERIFY(activitiesJob.wait(3000));
|
||||
QCOMPARE(sourceModel->rowCount(), 50);
|
||||
|
||||
addActivity(model, &TestingALM::addSyncFileItemToActivityList, testSyncFileItemActivity);
|
||||
addActivity(model, &TestingALM::addNotificationToActivityList, testNotificationActivity);
|
||||
addActivity(model, &TestingALM::addErrorToActivityList, testSyncResultErrorActivity, OCC::ActivityListModel::ErrorType::SyncError);
|
||||
addActivity(model, &TestingALM::addIgnoredFileToList, testFileIgnoredActivity);
|
||||
}
|
||||
|
||||
void testSort()
|
||||
{
|
||||
const auto model = testingSortedALM();
|
||||
const auto sourceModel = dynamic_cast<TestingALM*>(model->sourceModel());
|
||||
|
||||
sourceModel->setCurrentItem(FakeRemoteActivityStorage::instance()->startingIdLast());
|
||||
sourceModel->startMaxActivitiesFetchJob();
|
||||
QSignalSpy activitiesJob(sourceModel, &TestingALM::activitiesProcessed);
|
||||
QVERIFY(activitiesJob.wait(3000));
|
||||
QCOMPARE(sourceModel->rowCount(), sourceModel->maxPossibleActivities());
|
||||
|
||||
auto errorSyncFileItemActivity = exampleSyncFileItemActivity(accountState->account()->displayName(), {});
|
||||
errorSyncFileItemActivity._message = QStringLiteral("Something went wrong and eveything exploded!");
|
||||
errorSyncFileItemActivity._syncFileItemStatus = OCC::SyncFileItem::FatalError;
|
||||
|
||||
addActivity(model, &TestingALM::addSyncFileItemToActivityList, errorSyncFileItemActivity);
|
||||
addActivity(model, &TestingALM::addSyncFileItemToActivityList, testSyncFileItemActivity);
|
||||
addActivity(model, &TestingALM::addNotificationToActivityList, testNotificationActivity);
|
||||
addActivity(model, &TestingALM::addErrorToActivityList, testSyncResultErrorActivity, OCC::ActivityListModel::ErrorType::SyncError);
|
||||
addActivity(model, &TestingALM::addIgnoredFileToList, testFileIgnoredActivity);
|
||||
|
||||
const QVector<OCC::Activity::Type> activityDefaultTypeOrder {
|
||||
OCC::Activity::DummyFetchingActivityType,
|
||||
OCC::Activity::NotificationType,
|
||||
OCC::Activity::SyncResultType,
|
||||
OCC::Activity::SyncFileItemType,
|
||||
OCC::Activity::ActivityType,
|
||||
OCC::Activity::DummyMoreActivitiesAvailableType};
|
||||
auto currentTypeSection = 1;
|
||||
auto previousType = activityDefaultTypeOrder[currentTypeSection];
|
||||
|
||||
for (auto i = 0; i < model->rowCount(); ++i) {
|
||||
const auto index = model->index(i, 0);
|
||||
const auto activity = index.data(OCC::ActivityListModel::ActivityRole).value<OCC::Activity>();
|
||||
|
||||
qDebug() << i << activity._type << activity._subject << activity._message;
|
||||
if (i == 0) { // Error syncresult activity should be at top
|
||||
QCOMPARE(activity._type, OCC::Activity::SyncResultType);
|
||||
QCOMPARE(activity._syncResultStatus, OCC::SyncResult::Error);
|
||||
} else if (i == 1) { // Error syncfileitem activity should be next up
|
||||
QCOMPARE(activity._type, OCC::Activity::SyncFileItemType);
|
||||
QCOMPARE(activity._syncFileItemStatus, OCC::SyncFileItem::FatalError);
|
||||
} else if (i == 2) { // Ignored file syncfileitem activity should be next up
|
||||
QCOMPARE(activity._type, OCC::Activity::SyncFileItemType);
|
||||
QCOMPARE(activity._syncFileItemStatus, OCC::SyncFileItem::FileIgnored);
|
||||
} else { // Now normal type order
|
||||
while (i != 3 && activity._type != previousType) {
|
||||
++currentTypeSection;
|
||||
previousType = activityDefaultTypeOrder[currentTypeSection];
|
||||
}
|
||||
|
||||
QCOMPARE(activity._type, activityDefaultTypeOrder[currentTypeSection]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_MAIN(TestSortedActivityListModel)
|
||||
#include "testsortedactivitylistmodel.moc"
|
Loading…
Reference in a new issue