diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 35b28559..d7dfe81d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,19 +1,21 @@ find_package(Qt5 REQUIRED NO_MODULE COMPONENTS QuickTest Network Quick) find_package(Sink CONFIG REQUIRED) find_package(KAsync CONFIG REQUIRED) +find_package(KF5CalendarCore CONFIG REQUIRED) add_executable(kubetestrunner kubetestrunner.cpp) target_link_libraries(kubetestrunner Qt5::QuickTest Qt5::Quick sink kubeframework ) install(FILES qmldir DESTINATION ${QML_INSTALL_DIR}/org/kube/test) add_library(testplugin SHARED testplugin.cpp teststore.cpp) target_link_libraries(testplugin kubeframework + KF5::CalendarCore ) install(TARGETS testplugin DESTINATION ${QML_INSTALL_DIR}/org/kube/test) diff --git a/tests/teststore.cpp b/tests/teststore.cpp index 6a5304ad..3a60676d 100644 --- a/tests/teststore.cpp +++ b/tests/teststore.cpp @@ -1,243 +1,306 @@ /* Copyright (c) 2018 Christian Mollekopf This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "teststore.h" #include #include #include + #include +#include +#include + #include +#include #include #include "framework/src/domain/mime/mailtemplates.h" using namespace Kube; static void iterateOverObjects(const QVariantList &list, std::function callback) { for (const auto &entry : list) { auto object = entry.toMap(); callback(object); } } static QStringList toStringList(const QVariantList &list) { QStringList s; for (const auto &e : list) { s << e.toString(); } return s; } static QByteArrayList toByteArrayList(const QVariantList &list) { QByteArrayList s; for (const auto &e : list) { s << e.toByteArray(); } return s; } static void createMail(const QVariantMap &object, const QByteArray &folder = {}) { using namespace Sink::ApplicationDomain; auto toAddresses = toStringList(object["to"].toList()); auto ccAddresses = toStringList(object["cc"].toList()); auto bccAddresses = toStringList(object["bcc"].toList()); QList attachments = {}; if (object.contains("attachments")) { auto attachmentSpecs = object["attachments"].toList(); for (int i = 0; i < attachmentSpecs.size(); ++i) { auto const &spec = attachmentSpecs.at(i).toMap(); attachments << Attachment{spec["name"].toString(), spec["name"].toString(), spec["mimeType"].toByteArray(), false, spec["data"].toByteArray()}; } } KMime::Types::Mailbox mb; mb.fromUnicodeString("identity@example.org"); auto msg = MailTemplates::createMessage({}, toAddresses, ccAddresses, bccAddresses, mb, object["subject"].toString(), object["body"].toString(), object["bodyIsHtml"].toBool(), attachments, {}, {}); if (object.contains("messageId")) { msg->messageID(true)->from7BitString(object["messageId"].toByteArray()); } if (object.contains("inReplyTo")) { msg->inReplyTo(true)->from7BitString(object["inReplyTo"].toByteArray()); } if (object.contains("date")) { msg->date(true)->setDateTime(QDateTime::fromString(object["date"].toString(), Qt::ISODate)); } msg->assemble(); auto mail = ApplicationDomainType::createEntity(object["resource"].toByteArray()); mail.setMimeMessage(msg->encodedContent(true)); mail.setUnread(object["unread"].toBool()); if (!folder.isEmpty()) { mail.setFolder(folder); } Sink::Store::create(mail).exec().waitForFinished(); } static void createFolder(const QVariantMap &object) { using namespace Sink::ApplicationDomain; auto folder = ApplicationDomainType::createEntity(object["resource"].toByteArray()); folder.setName(object["name"].toString()); folder.setSpecialPurpose(toByteArrayList(object["specialpurpose"].toList())); Sink::Store::create(folder).exec().waitForFinished(); iterateOverObjects(object.value("mails").toList(), [=](const QVariantMap &object) { createMail(object, folder.identifier()); }); } +static void createEvent(const QVariantMap &object, const QByteArray &calendarId = {}) +{ + using Sink::ApplicationDomain::ApplicationDomainType; + using Sink::ApplicationDomain::Event; + + auto sinkEvent = ApplicationDomainType::createEntity(object["resource"].toByteArray()); + + auto calcoreEvent = QSharedPointer::create(); + + QString uid; + if (object.contains("uid")) { + uid = object["uid"].toString(); + } else { + uid = QUuid::createUuid().toString(); + } + calcoreEvent->setUid(uid); + + auto summary = object["summary"].toString(); + calcoreEvent->setSummary(summary); + + if (object.contains("description")) { + auto description = object["description"].toString(); + calcoreEvent->setDescription(description); + } + + auto startTime = object["starts"].toDateTime(); + auto endTime = object["ends"].toDateTime(); + + calcoreEvent->setDtStart(startTime); + calcoreEvent->setDtEnd(endTime); + + auto ical = KCalCore::ICalFormat().toICalString(calcoreEvent); + sinkEvent.setIcal(ical.toUtf8()); + + sinkEvent.setCalendar(calendarId); + + Sink::Store::create(sinkEvent).exec().waitForFinished(); +} + +static void createCalendar(const QVariantMap &object) +{ + using Sink::ApplicationDomain::Calendar; + using Sink::ApplicationDomain::ApplicationDomainType; + + auto calendar = ApplicationDomainType::createEntity(object["resource"].toByteArray()); + calendar.setName(object["name"].toString()); + Sink::Store::create(calendar).exec().waitForFinished(); + + auto calendarId = calendar.identifier(); + iterateOverObjects(object.value("events").toList(), + [calendarId](const QVariantMap &object) { createEvent(object, calendarId); }); +} + void TestStore::setup(const QVariantMap &map) { using namespace Sink::ApplicationDomain; iterateOverObjects(map.value("accounts").toList(), [&] (const QVariantMap &object) { auto account = ApplicationDomainType::createEntity("", object["id"].toByteArray()); account.setName(object["name"].toString()); Sink::Store::create(account).exec().waitForFinished(); }); QByteArrayList resources; iterateOverObjects(map.value("resources").toList(), [&] (const QVariantMap &object) { resources << object["id"].toByteArray(); auto resource = [&] { using namespace Sink::ApplicationDomain; auto resource = ApplicationDomainType::createEntity("", object["id"].toByteArray()); if (object["type"] == "dummy") { resource.setResourceType("sink.dummy"); } else if (object["type"] == "mailtransport") { resource.setResourceType("sink.mailtransport"); resource.setProperty("testmode", true); + } else if (object["type"] == "caldav") { + resource.setResourceType("sink.caldav"); + resource.setProperty("testmode", true); } else { Q_ASSERT(false); } return resource; }(); resource.setAccount(object["account"].toByteArray()); Sink::Store::create(resource).exec().waitForFinished(); Sink::SecretStore::instance().insert(resource.identifier(), "secret"); }); iterateOverObjects(map.value("identities").toList(), [] (const QVariantMap &object) { auto identity = Sink::ApplicationDomain::Identity{}; identity.setAccount(object["account"].toByteArray()); identity.setAddress(object["address"].toString()); identity.setName(object["name"].toString()); Sink::Store::create(identity).exec().waitForFinished(); }); iterateOverObjects(map.value("folders").toList(), createFolder); iterateOverObjects(map.value("mails").toList(), [] (const QVariantMap &map) { createMail(map); }); + iterateOverObjects(map.value("calendars").toList(), createCalendar); + Sink::ResourceControl::flushMessageQueue(resources).exec().waitForFinished(); } QVariant TestStore::load(const QByteArray &type, const QVariantMap &filter) { using namespace Sink::ApplicationDomain; const auto list = loadList(type, filter); if (!list.isEmpty()) { if (list.size() > 1) { qWarning() << "While loading" << type << "with filter" << filter << "; got multiple elements, but returning the first one."; } return list.first(); } return {}; } template QVariantList toVariantList(const QList &list) { QVariantList result; std::transform(list.constBegin(), list.constEnd(), std::back_inserter(result), [] (const T &m) { return QVariant::fromValue(T::Ptr::create(m)); }); Q_ASSERT(list.size() == result.size()); return result; } QVariantList TestStore::loadList(const QByteArray &type, const QVariantMap &filter) { using namespace Sink::ApplicationDomain; Sink::Query query; if (filter.contains("resource")) { query.resourceFilter(filter.value("resource").toByteArray()); } for (QVariantMap::const_iterator it = filter.begin(); it != filter.end(); ++it) { if (it.key() == "messageId") { query.filter(it.value()); } else if (it.key() == "draft") { query.filter(it.value()); } else if (it.key() == "subject") { query.filter(it.value()); } } if (type == "mail") { return toVariantList(Sink::Store::read(query)); } if (type == "folder") { return toVariantList(Sink::Store::read(query)); } if (type == "resource") { return toVariantList(Sink::Store::read(query)); } if (type == "account") { return toVariantList(Sink::Store::read(query)); } Q_ASSERT(false); return {}; } QVariantMap TestStore::read(const QVariant &object) { using namespace Sink::ApplicationDomain; QVariantMap map; if (auto mail = object.value()) { map.insert("uid", mail->identifier()); map.insert("subject", mail->getSubject()); map.insert("draft", mail->getDraft()); return map; } Q_ASSERT(false); return {}; } diff --git a/views/calendar/main.qml b/views/calendar/main.qml index 3cce9adf..672e3e21 100644 --- a/views/calendar/main.qml +++ b/views/calendar/main.qml @@ -1,51 +1,76 @@ /* * Copyright (C) 2018 Christian Mollekopf, * * 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. * * 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. */ import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Window 2.0 import org.kube.framework 1.0 as Kube import org.kube.test 1.0 import "qml" ApplicationWindow { id: app height: Screen.desktopAvailableHeight * 0.8 width: Screen.desktopAvailableWidth * 0.8 Component.onCompleted: { var initialState = { accounts: [{ - id: "account1", - name: "Test Account" - }], + id: "account1", + name: "Test Account" + }], identities: [{ - account: "account1", - name: "Test Identity", - address: "identity@example.org" - }], - resources: [] + account: "account1", + name: "Test Identity", + address: "identity@example.org" + }], + resources: [{ + id: "caldavresource", + account: "account1", + type: "caldav", + }], + calendars: [{ + id: "calendar1", + resource: "caldavresource", + name: "Test Calendar", + events: [ + { + resource: "caldavresource", + summary: "Test Event1", + description: "This is test event #1", + starts: "2018-04-10T14:03:00", + ends: "2018-04-10T17:03:00", + }, + { + resource: "caldavresource", + summary: "Test Event2", + description: "This is test event #2", + starts: "2018-04-11T09:03:00", + ends: "2018-04-11T14:03:00", + }, + ], + }], } TestStore.setup(initialState) } View { anchors.fill: parent } }