Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dcb7c52269 | |||
| c5704b78a4 |
+89
-3
@@ -65,6 +65,7 @@ set(SRC_FILES
|
|||||||
src/core/eventbus.cpp
|
src/core/eventbus.cpp
|
||||||
src/utils/notificationmanager.cpp
|
src/utils/notificationmanager.cpp
|
||||||
src/core/emailmanager.cpp
|
src/core/emailmanager.cpp
|
||||||
|
src/core/emailcomposerbridge.cpp
|
||||||
src/core/synchronizerprovider.cpp
|
src/core/synchronizerprovider.cpp
|
||||||
src/syncscheduler.cpp
|
src/syncscheduler.cpp
|
||||||
src/core/accountsetupdialoglauncher.cpp
|
src/core/accountsetupdialoglauncher.cpp
|
||||||
@@ -76,7 +77,7 @@ set(SRC_FILES
|
|||||||
add_executable(wino-mail-qt ${SRC_FILES})
|
add_executable(wino-mail-qt ${SRC_FILES})
|
||||||
|
|
||||||
# Link Qt
|
# Link Qt
|
||||||
target_link_libraries(wino-mail-qt PRIVATE Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Network Qt6::Sql Qt6::WebEngineWidgets Qt6::Qml Qt6::Quick Qt6::QuickControls2 Qt6::Multimedia)
|
target_link_libraries(wino-mail-qt PRIVATE Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Network Qt6::Sql Qt6::WebEngineWidgets Qt6::Qml Qt6::Quick Qt6::Multimedia)
|
||||||
|
|
||||||
# Link DTKWidget if found
|
# Link DTKWidget if found
|
||||||
if(USE_DTKWIDGET AND DTKWIDGET_FOUND)
|
if(USE_DTKWIDGET AND DTKWIDGET_FOUND)
|
||||||
@@ -84,8 +85,93 @@ if(USE_DTKWIDGET AND DTKWIDGET_FOUND)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
# If using external libs
|
# If using external libs
|
||||||
# target_link_libraries(wino-mail-qt PRIVATE ${LIBETPAN_LIBRARIES} ${GMIME_LIBRARIES})
|
target_link_libraries(wino-mail-qt PRIVATE ${GMIME_LIBRARIES})
|
||||||
# target_include_directories(wino-mail-qt PRIVATE ${LIBETPAN_INCLUDE_DIRS} ${GMIME_INCLUDE_DIRS})
|
target_include_directories(wino-mail-qt PRIVATE ${GMIME_INCLUDE_DIRS})
|
||||||
|
|
||||||
# Install (optional)
|
# Install (optional)
|
||||||
install(TARGETS wino-mail-qt DESTINATION bin)
|
install(TARGETS wino-mail-qt DESTINATION bin)
|
||||||
|
|
||||||
|
# Enable testing
|
||||||
|
enable_testing()
|
||||||
|
|
||||||
|
# Find Qt Test
|
||||||
|
find_package(Qt6 COMPONENTS Test REQUIRED)
|
||||||
|
|
||||||
|
# Unit tests for DAOs
|
||||||
|
add_executable(tests_dao_unit
|
||||||
|
tests/unit/test_accountdao.cpp
|
||||||
|
tests/unit/test_folderdao.cpp
|
||||||
|
tests/unit/test_mailitemdao.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(tests_dao_unit
|
||||||
|
PRIVATE Qt6::Core Qt6::Test
|
||||||
|
)
|
||||||
|
|
||||||
|
if(USE_DTKWIDGET AND DTKWIDGET_FOUND)
|
||||||
|
target_link_libraries(tests_dao_unit PRIVATE ${DTKWIDGET_LIBS})
|
||||||
|
endif()
|
||||||
|
if(GMIME_FOUND)
|
||||||
|
target_link_libraries(tests_dao_unit PRIVATE ${GMIME_LIBRARIES})
|
||||||
|
target_include_directories(tests_dao_unit PRIVATE ${GMIME_INCLUDE_DIRS})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_test(NAME dao_unit COMMAND tests_dao_unit)
|
||||||
|
|
||||||
|
# Unit tests for Translator
|
||||||
|
add_executable(tests_translator_unit
|
||||||
|
tests/unit/test_translator.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(tests_translator_unit
|
||||||
|
PRIVATE Qt6::Core Qt6::Test
|
||||||
|
)
|
||||||
|
|
||||||
|
if(USE_DTKWIDGET AND DTKWIDGET_FOUND)
|
||||||
|
target_link_libraries(tests_translator_unit PRIVATE ${DTKWIDGET_LIBS})
|
||||||
|
endif()
|
||||||
|
if(GMIME_FOUND)
|
||||||
|
target_link_libraries(tests_translator_unit PRIVATE ${GMIME_LIBRARIES})
|
||||||
|
target_include_directories(tests_translator_unit PRIVATE ${GMIME_INCLUDE_DIRS})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_test(NAME translator_unit COMMAND tests_translator_unit)
|
||||||
|
|
||||||
|
# Unit tests for EventBus
|
||||||
|
add_executable(tests_eventbus_unit
|
||||||
|
tests/unit/test_eventbus.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(tests_eventbus_unit
|
||||||
|
PRIVATE Qt6::Core Qt6::Test
|
||||||
|
)
|
||||||
|
|
||||||
|
if(USE_DTKWIDGET AND DTKWIDGET_FOUND)
|
||||||
|
target_link_libraries(tests_eventbus_unit PRIVATE ${DTKWIDGET_LIBS})
|
||||||
|
endif()
|
||||||
|
if(GMIME_FOUND)
|
||||||
|
target_link_libraries(tests_eventbus_unit PRIVATE ${GMIME_LIBRARIES})
|
||||||
|
target_include_directories(tests_eventbus_unit PRIVATE ${GMIME_INCLUDE_DIRS})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_test(NAME eventbus_unit COMMAND tests_eventbus_unit)
|
||||||
|
|
||||||
|
# Integration tests (require full application context)
|
||||||
|
add_executable(tests_sync_integration
|
||||||
|
tests/integration/test_syncscheduler.cpp
|
||||||
|
tests/integration/test_notificationmanager.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(tests_sync_integration
|
||||||
|
PRIVATE Qt6::Core Qt6::Gui Qt6::Widgets Qt6::Network Qt6::Sql Qt6::Test
|
||||||
|
)
|
||||||
|
|
||||||
|
if(USE_DTKWIDGET AND DTKWIDGET_FOUND)
|
||||||
|
target_link_libraries(tests_sync_integration PRIVATE ${DTKWIDGET_LIBS})
|
||||||
|
endif()
|
||||||
|
if(GMIME_FOUND)
|
||||||
|
target_link_libraries(tests_sync_integration PRIVATE ${GMIME_LIBRARIES})
|
||||||
|
target_include_directories(tests_sync_integration PRIVATE ${GMIME_INCLUDE_DIRS})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_test(NAME sync_integration COMMAND tests_sync_integration)
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
import QtQuick.Layouts 1.15
|
import QtQuick.Layouts 1.15
|
||||||
|
import QtWebEngine 1.15
|
||||||
|
import QtQml 2.15
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: composePage
|
id: composePage
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
|
// Expose a bridge to C++ for sending email and getting Quill content
|
||||||
|
property var emailBridge: emailComposerBridge
|
||||||
|
|
||||||
// Back button
|
// Back button
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors {
|
anchors {
|
||||||
@@ -69,17 +74,47 @@ Item {
|
|||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Body
|
// Body - WebEngineView loading editor.html with Quill
|
||||||
Text {
|
Text {
|
||||||
text: qsTr("Body:")
|
text: qsTr("Body:")
|
||||||
font.pointSize: 14
|
font.pointSize: 14
|
||||||
Layout.alignment: Qt.AlignLeft
|
Layout.alignment: Qt.AlignLeft
|
||||||
}
|
}
|
||||||
TextArea {
|
WebEngineView {
|
||||||
id: bodyArea
|
id: bodyWebView
|
||||||
placeholderText: qsTr("Write your message...")
|
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
url: "qrc:/resources/web/editor.html"
|
||||||
|
// Settings to enable webchannel
|
||||||
|
settings.javascriptEnabled: true
|
||||||
|
settings.localContentCanAccessRemoteUrls: true
|
||||||
|
settings.localContentCanAccessFileUrls: true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attachments area (simple list placeholder)
|
||||||
|
RowLayout {
|
||||||
|
id: attachmentsRow
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: 5
|
||||||
|
// Will be populated dynamically via C++ model or JS array
|
||||||
|
// For now, placeholder
|
||||||
|
Text {
|
||||||
|
text: qsTr("Attachments: ")
|
||||||
|
font.pointSize: 12
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
id: attachmentsText
|
||||||
|
text: qsTr("None")
|
||||||
|
font.pointSize: 12
|
||||||
|
color: "#666"
|
||||||
|
}
|
||||||
|
Button {
|
||||||
|
text: qsTr("Attach")
|
||||||
|
onClicked: {
|
||||||
|
// Call C++ to open file dialog and get selected files
|
||||||
|
emailBridge.openAttachmentDialog()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send button
|
// Send button
|
||||||
@@ -93,10 +128,11 @@ Item {
|
|||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
onClicked: {
|
onClicked: {
|
||||||
// TODO: Implement sending email
|
// Trigger sending via bridge
|
||||||
console.log("Sending email to:", toField.text, "subject:", subjectField.text)
|
emailBridge.sendComposedEmail(
|
||||||
// For now, just go back
|
toField.text,
|
||||||
StackView.view.pop()
|
subjectField.text
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Text {
|
Text {
|
||||||
|
|||||||
+106
-34
@@ -1,3 +1,20 @@
|
|||||||
|
#include <gmime/gmime.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
// Undefine macros that conflict with Qt's signals/slots
|
||||||
|
#ifdef public
|
||||||
|
#undef public
|
||||||
|
#endif
|
||||||
|
#ifdef signals
|
||||||
|
#undef signals
|
||||||
|
#endif
|
||||||
|
#ifdef slots
|
||||||
|
#undef slots
|
||||||
|
#endif
|
||||||
|
#ifdef emit
|
||||||
|
#undef emit
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "emailmanager.h"
|
#include "emailmanager.h"
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
@@ -5,6 +22,45 @@
|
|||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
|
||||||
|
/* Helper: recursively find a part with given subtype */
|
||||||
|
static GMimeObject *find_part_by_subtype(GMimeObject *obj, const char *subtype)
|
||||||
|
{
|
||||||
|
if (!obj)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
GMimeContentType *ctype = g_mime_object_get_content_type(obj);
|
||||||
|
if (ctype &&
|
||||||
|
g_ascii_strcasecmp(g_mime_content_type_get_media_subtype(ctype), subtype) == 0)
|
||||||
|
return obj;
|
||||||
|
|
||||||
|
if (GMIME_IS_MULTIPART(obj)) {
|
||||||
|
GMimeMultipart *multipart = GMIME_MULTIPART(obj);
|
||||||
|
guint count = g_mime_multipart_get_count(multipart);
|
||||||
|
for (guint i = 0; i < count; ++i) {
|
||||||
|
GMimeObject *part = g_mime_multipart_get_part(multipart, i);
|
||||||
|
GMimeObject *found = find_part_by_subtype(part, subtype);
|
||||||
|
if (found)
|
||||||
|
return found;
|
||||||
|
g_object_unref(part);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helper: extract content as QString */
|
||||||
|
static QString extract_content_as_string(GMimeObject *obj)
|
||||||
|
{
|
||||||
|
GMimeStream *stream = g_mime_stream_mem_new();
|
||||||
|
g_mime_object_write_to_stream(obj, nullptr, stream);
|
||||||
|
GMimeStreamMem *mem = GMIME_STREAM_MEM(stream);
|
||||||
|
GByteArray *array = g_mime_stream_mem_get_byte_array(mem);
|
||||||
|
const char *data = reinterpret_cast<const char*>(array->data);
|
||||||
|
gsize size = array->len;
|
||||||
|
QString result = QString::fromUtf8(data, size);
|
||||||
|
g_object_unref(stream);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
EmailManager::EmailManager(QObject *parent)
|
EmailManager::EmailManager(QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
{
|
{
|
||||||
@@ -12,70 +68,86 @@ EmailManager::EmailManager(QObject *parent)
|
|||||||
|
|
||||||
MailItem EmailManager::getMailItemById(qint64 id) const
|
MailItem EmailManager::getMailItemById(qint64 id) const
|
||||||
{
|
{
|
||||||
// Use the DAO to fetch the MailItem by id
|
|
||||||
auto optItem = MailItemDao::findById(id);
|
auto optItem = MailItemDao::findById(id);
|
||||||
if (optItem.has_value()) {
|
return optItem.has_value() ? optItem.value() : MailItem();
|
||||||
return optItem.value();
|
|
||||||
}
|
|
||||||
// Return an empty MailItem if not found
|
|
||||||
return MailItem();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EmailManager::getStorageDirectory() const
|
QString EmailManager::getStorageDirectory() const
|
||||||
{
|
{
|
||||||
// Define where .eml files are stored (e.g., in the application data directory)
|
|
||||||
QString storagePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
QString storagePath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||||||
QDir dir(storagePath);
|
QDir dir(storagePath);
|
||||||
if (!dir.exists()) {
|
if (!dir.exists())
|
||||||
dir.mkpath("."); // create if it doesn't exist
|
dir.mkpath(".");
|
||||||
}
|
|
||||||
// Assuming .eml files are stored directly in this directory
|
|
||||||
return storagePath;
|
return storagePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EmailManager::getEmailHtmlById(qint64 id) const
|
QString EmailManager::getEmailHtmlById(qint64 id) const
|
||||||
{
|
{
|
||||||
// Get the MailItem by id
|
|
||||||
MailItem item = getMailItemById(id);
|
MailItem item = getMailItemById(id);
|
||||||
if (item.id() == 0) {
|
if (item.id() == 0)
|
||||||
return "<html><body><h2>Error: Email not found</h2></body></html>";
|
return "<html><body><h2>Error: Email not found</h2></body></html>";
|
||||||
}
|
|
||||||
// Construct the file path: storage directory + fileId + .eml
|
|
||||||
QString storageDir = getStorageDirectory();
|
QString storageDir = getStorageDirectory();
|
||||||
QString fileName = item.fileId();
|
QString fileName = item.fileId();
|
||||||
if (fileName.isEmpty()) {
|
if (fileName.isEmpty())
|
||||||
return "<html><body><h2>Error: Email file ID is empty</h2></body></html>";
|
return "<html><body><h2>Error: Email file ID is empty</h2></body></html>";
|
||||||
}
|
|
||||||
QString filePath = storageDir + QDir::separator() + fileName + ".eml";
|
QString filePath = storageDir + QDir::separator() + fileName + ".eml";
|
||||||
// Convert the .eml file to HTML
|
|
||||||
return convertEmlToHtml(filePath);
|
return convertEmlToHtml(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString EmailManager::convertEmlToHtml(const QString& emlFilePath) const
|
QString EmailManager::convertEmlToHtml(const QString& emlFilePath) const
|
||||||
{
|
{
|
||||||
// TODO: Implement actual MIME to HTML conversion using gmime or similar
|
static bool initialized = false;
|
||||||
// For now, return a placeholder indicating the feature is not yet implemented
|
if (!initialized) {
|
||||||
QFile file(emlFilePath);
|
g_mime_init();
|
||||||
if (!file.exists()) {
|
initialized = true;
|
||||||
return "<html><body><h2>Error: Email file not found</h2></body></html>";
|
|
||||||
}
|
}
|
||||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
|
||||||
|
FILE *fp = fopen(emlFilePath.toLocal8Bit().constData(), "r");
|
||||||
|
if (!fp)
|
||||||
return "<html><body><h2>Error: Cannot open email file</h2></body></html>";
|
return "<html><body><h2>Error: Cannot open email file</h2></body></html>";
|
||||||
|
|
||||||
|
GMimeStream *istream = g_mime_stream_fs_new(fileno(fp));
|
||||||
|
GMimeParser *parser = g_mime_parser_new_with_stream(istream);
|
||||||
|
GMimeMessage *message = g_mime_parser_construct_message(parser, nullptr);
|
||||||
|
g_object_unref(parser);
|
||||||
|
g_object_unref(istream);
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
if (!message)
|
||||||
|
return "<html><body><h2>Error: Failed to parse email message</h2></body></html>";
|
||||||
|
|
||||||
|
/* Try to get HTML part */
|
||||||
|
GMimeObject *htmlPart = find_part_by_subtype(GMIME_OBJECT(message), "html");
|
||||||
|
if (htmlPart) {
|
||||||
|
QString html = extract_content_as_string(htmlPart);
|
||||||
|
g_object_unref(htmlPart);
|
||||||
|
g_object_unref(message);
|
||||||
|
return html;
|
||||||
}
|
}
|
||||||
QByteArray data = file.readAll();
|
|
||||||
file.close();
|
/* Fallback to plain text */
|
||||||
// Very basic conversion: just show raw content in a pre tag for now
|
GMimeObject *textPart = find_part_by_subtype(GMIME_OBJECT(message), "plain");
|
||||||
QString content = QString::fromUtf8(data);
|
if (textPart) {
|
||||||
content.replace("&", "&").replace("<", "<").replace(">", ">");
|
QString plain = extract_content_as_string(textPart);
|
||||||
return QString("<html><body style='font-family: monospace;'><h2>Email Content (raw)</h2><pre>%1</pre></body></html>")
|
g_object_unref(textPart);
|
||||||
.arg(content);
|
g_object_unref(message);
|
||||||
|
QString escaped = plain;
|
||||||
|
escaped.replace("&", "&").replace("<", "<").replace(">", ">");
|
||||||
|
return QString("<html><body style='font-family:monospace;'><pre>%1</pre></body></html>")
|
||||||
|
.arg(escaped);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No displayable part */
|
||||||
|
g_object_unref(message);
|
||||||
|
return "<html><body><h2>Error: No displayable content in email</h2></body></html>";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EmailManager::sendEmail(const QString& to, const QString& subject, const QString& body)
|
bool EmailManager::sendEmail(const QString& to, const QString& subject, const QString& body)
|
||||||
{
|
{
|
||||||
// TODO: Implement actual email sending
|
/* TODO: Implement real sending via RequestProcessor */
|
||||||
// For now, just show a message and return true
|
QMessageBox::information(nullptr, "Send Email",
|
||||||
QMessageBox::information(nullptr, "Send Email",
|
|
||||||
QString("To: %1\nSubject: %2\nBody: %3").arg(to, subject, body));
|
QString("To: %1\nSubject: %2\nBody: %3").arg(to, subject, body));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "core/translator.h"
|
#include "core/translator.h"
|
||||||
#include "db/dbchangeprocessor.h"
|
#include "db/dbchangeprocessor.h"
|
||||||
#include "core/emailmanager.h"
|
#include "core/emailmanager.h"
|
||||||
|
#include "core/emailcomposerbridge.h"
|
||||||
#include "syncscheduler.h"
|
#include "syncscheduler.h"
|
||||||
#include "utils/notificationmanager.h"
|
#include "utils/notificationmanager.h"
|
||||||
#include "core/accountsetupdialoglauncher.h"
|
#include "core/accountsetupdialoglauncher.h"
|
||||||
@@ -24,6 +25,9 @@ int main(int argc, char *argv[])
|
|||||||
// Create EmailManager to expose to QML
|
// Create EmailManager to expose to QML
|
||||||
EmailManager emailManager(&app);
|
EmailManager emailManager(&app);
|
||||||
|
|
||||||
|
// Create EmailComposerBridge to expose to QML
|
||||||
|
EmailComposerBridge emailComposerBridge(&app);
|
||||||
|
|
||||||
// Create and start the sync scheduler
|
// Create and start the sync scheduler
|
||||||
SyncScheduler syncScheduler(&app);
|
SyncScheduler syncScheduler(&app);
|
||||||
syncScheduler.start();
|
syncScheduler.start();
|
||||||
@@ -38,6 +42,7 @@ int main(int argc, char *argv[])
|
|||||||
QQmlApplicationEngine engine;
|
QQmlApplicationEngine engine;
|
||||||
engine.rootContext()->setContextProperty("translator", static_cast<QObject*>(&translator));
|
engine.rootContext()->setContextProperty("translator", static_cast<QObject*>(&translator));
|
||||||
engine.rootContext()->setContextProperty("emailManager", &emailManager);
|
engine.rootContext()->setContextProperty("emailManager", &emailManager);
|
||||||
|
engine.rootContext()->setContextProperty("emailComposerBridge", &emailComposerBridge);
|
||||||
engine.rootContext()->setContextProperty("syncScheduler", &syncScheduler);
|
engine.rootContext()->setContextProperty("syncScheduler", &syncScheduler);
|
||||||
engine.rootContext()->setContextProperty("notificationManager", ¬ificationManager);
|
engine.rootContext()->setContextProperty("notificationManager", ¬ificationManager);
|
||||||
engine.rootContext()->setContextProperty("accountSetupDialogLauncher", &accountSetupDialogLauncher);
|
engine.rootContext()->setContextProperty("accountSetupDialogLauncher", &accountSetupDialogLauncher);
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#include <QtTest/QtTest>
|
||||||
|
#include "../../src/syncscheduler.h"
|
||||||
|
|
||||||
|
class TestSyncScheduler : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private slots:
|
||||||
|
void testShouldRunSync();
|
||||||
|
};
|
||||||
|
|
||||||
|
void TestSyncScheduler::testShouldRunSync()
|
||||||
|
{
|
||||||
|
SyncScheduler scheduler;
|
||||||
|
// This test would need to mock QSettings and QDateTime
|
||||||
|
// For now, we just verify the object can be created
|
||||||
|
QVERIFY(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_MAIN(TestSyncScheduler)
|
||||||
|
#include "test_syncscheduler.moc"
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
#include <QtTest/QtTest>
|
||||||
|
#include "../../src/db/dao/accountdao.h"
|
||||||
|
#include "../../src/db/databasemanager.h"
|
||||||
|
#include "../../src/core/models/account.h"
|
||||||
|
|
||||||
|
class TestAccountDao : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private slots:
|
||||||
|
void initTestCase();
|
||||||
|
void cleanupTestCase();
|
||||||
|
void testInsertAndFind();
|
||||||
|
void testUpdate();
|
||||||
|
void testRemove();
|
||||||
|
};
|
||||||
|
|
||||||
|
void TestAccountDao::initTestCase()
|
||||||
|
{
|
||||||
|
DatabaseManager::instance().openDatabase(":memory:");
|
||||||
|
DatabaseManager::instance().createTables();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestAccountDao::cleanupTestCase()
|
||||||
|
{
|
||||||
|
DatabaseManager::instance().closeDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestAccountDao::testInsertAndFind()
|
||||||
|
{
|
||||||
|
Account account;
|
||||||
|
account.setEmail("test@example.com");
|
||||||
|
account.setProvider("IMAP");
|
||||||
|
account.setHost("imap.example.com");
|
||||||
|
account.setPort(993);
|
||||||
|
account.setUsername("testuser");
|
||||||
|
account.setPassword("testpass");
|
||||||
|
account.setUseSsl(true);
|
||||||
|
|
||||||
|
bool inserted = AccountDao::insert(account);
|
||||||
|
QVERIFY(inserted);
|
||||||
|
QVERIFY(account.id() > 0);
|
||||||
|
|
||||||
|
Account found = AccountDao::findById(account.id());
|
||||||
|
QVERIFY(found.isValid());
|
||||||
|
QCOMPARE(found.email(), QString("test@example.com"));
|
||||||
|
QCOMPARE(found.provider(), QString("IMAP"));
|
||||||
|
QCOMPARE(found.host(), QString("imap.example.com"));
|
||||||
|
QCOMPARE(found.port(), 993);
|
||||||
|
QCOMPARE(found.username(), QString("testuser"));
|
||||||
|
QCOMPARE(found.password(), QString("testpass"));
|
||||||
|
QVERIFY(found.useSsl());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestAccountDao::testUpdate()
|
||||||
|
{
|
||||||
|
Account account;
|
||||||
|
account.setEmail("original@example.com");
|
||||||
|
account.setProvider("IMAP");
|
||||||
|
account.setHost("imap.example.com");
|
||||||
|
account.setPort(993);
|
||||||
|
account.setUsername("user");
|
||||||
|
account.setPassword("pass");
|
||||||
|
account.setUseSsl(true);
|
||||||
|
|
||||||
|
bool inserted = AccountDao::insert(account);
|
||||||
|
QVERIFY(inserted);
|
||||||
|
qint64 id = account.id();
|
||||||
|
|
||||||
|
account.setEmail("updated@example.com");
|
||||||
|
account.setProvider("Gmail");
|
||||||
|
account.setUseSsl(false);
|
||||||
|
|
||||||
|
bool updated = AccountDao::update(account);
|
||||||
|
QVERIFY(updated);
|
||||||
|
|
||||||
|
Account found = AccountDao::findById(id);
|
||||||
|
QVERIFY(found.isValid());
|
||||||
|
QCOMPARE(found.email(), QString("updated@example.com"));
|
||||||
|
QCOMPARE(found.provider(), QString("Gmail"));
|
||||||
|
QVERIFY(!found.useSsl());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestAccountDao::testRemove()
|
||||||
|
{
|
||||||
|
Account account;
|
||||||
|
account.setEmail("todelete@example.com");
|
||||||
|
account.setProvider("IMAP");
|
||||||
|
account.setHost("imap.example.com");
|
||||||
|
account.setPort(993);
|
||||||
|
account.setUsername("user");
|
||||||
|
account.setPassword("pass");
|
||||||
|
account.setUseSsl(true);
|
||||||
|
|
||||||
|
bool inserted = AccountDao::insert(account);
|
||||||
|
QVERIFY(inserted);
|
||||||
|
qint64 id = account.id();
|
||||||
|
|
||||||
|
bool removed = AccountDao::remove(id);
|
||||||
|
QVERIFY(removed);
|
||||||
|
|
||||||
|
Account found = AccountDao::findById(id);
|
||||||
|
QVERIFY(!found.isValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_MAIN(TestAccountDao)
|
||||||
|
#include "test_accountdao.moc"
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
#include <QtTest/QtTest>
|
||||||
|
#include "../../src/core/eventbus.h"
|
||||||
|
#include "../../src/core/events.h"
|
||||||
|
|
||||||
|
class TestEventBus : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private slots:
|
||||||
|
void testPublishAndSubscribe();
|
||||||
|
};
|
||||||
|
|
||||||
|
void TestEventBus::testPublishAndSubscribe()
|
||||||
|
{
|
||||||
|
bool received = false;
|
||||||
|
int value = 0;
|
||||||
|
|
||||||
|
// Subscribe to an event of type int
|
||||||
|
auto subscription = EventBus::instance().subscribe<int>([&](int data) {
|
||||||
|
received = true;
|
||||||
|
value = data;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Publish an event
|
||||||
|
EventBus::instance().publish<int>(42);
|
||||||
|
|
||||||
|
// Check that we received the event
|
||||||
|
QVERIFY(received);
|
||||||
|
QCOMPARE(value, 42);
|
||||||
|
|
||||||
|
// Unsubscribe (optional, subscription goes out of scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_MAIN(TestEventBus)
|
||||||
|
#include "test_eventbus.moc"
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
#include <QtTest/QtTest>
|
||||||
|
#include "../../src/db/dao/folderdao.h"
|
||||||
|
#include "../../src/db/databasemanager.h"
|
||||||
|
#include "../../src/core/models/folder.h"
|
||||||
|
|
||||||
|
class TestFolderDao : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private slots:
|
||||||
|
void initTestCase();
|
||||||
|
void cleanupTestCase();
|
||||||
|
void testInsertAndFind();
|
||||||
|
void testUpdate();
|
||||||
|
void testRemove();
|
||||||
|
};
|
||||||
|
|
||||||
|
void TestFolderDao::initTestCase()
|
||||||
|
{
|
||||||
|
DatabaseManager::instance().openDatabase(":memory:");
|
||||||
|
DatabaseManager::instance().createTables();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestFolderDao::cleanupTestCase()
|
||||||
|
{
|
||||||
|
DatabaseManager::instance().closeDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestFolderDao::testInsertAndFind()
|
||||||
|
{
|
||||||
|
Folder folder;
|
||||||
|
folder.setAccountId(1);
|
||||||
|
folder.setName("Inbox");
|
||||||
|
folder.setPath("INBOX");
|
||||||
|
folder.setIsInbox(true);
|
||||||
|
|
||||||
|
bool inserted = FolderDao::insert(folder);
|
||||||
|
QVERIFY(inserted);
|
||||||
|
QVERIFY(folder.id() > 0);
|
||||||
|
|
||||||
|
Folder found = FolderDao::findById(folder.id());
|
||||||
|
QVERIFY(found.isValid());
|
||||||
|
QCOMPARE(found.accountId(), 1);
|
||||||
|
QCOMPARE(found.name(), QString("Inbox"));
|
||||||
|
QCOMPARE(found.path(), QString("INBOX"));
|
||||||
|
QVERIFY(found.isInbox());
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestFolderDao::testUpdate()
|
||||||
|
{
|
||||||
|
Folder folder;
|
||||||
|
folder.setAccountId(1);
|
||||||
|
folder.setName("Drafts");
|
||||||
|
folder.setPath("Drafts");
|
||||||
|
folder.setIsInbox(false);
|
||||||
|
|
||||||
|
bool inserted = FolderDao::insert(folder);
|
||||||
|
QVERIFY(inserted);
|
||||||
|
qint64 id = folder.id();
|
||||||
|
|
||||||
|
folder.setName("Updated Drafts");
|
||||||
|
folder.setPath("Updated/Drafts");
|
||||||
|
|
||||||
|
bool updated = FolderDao::update(folder);
|
||||||
|
QVERIFY(updated);
|
||||||
|
|
||||||
|
Folder found = FolderDao::findById(id);
|
||||||
|
QVERIFY(found.isValid());
|
||||||
|
QCOMPARE(found.name(), QString("Updated Drafts"));
|
||||||
|
QCOMPARE(found.path(), QString("Updated/Drafts"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestFolderDao::testRemove()
|
||||||
|
{
|
||||||
|
Folder folder;
|
||||||
|
folder.setAccountId(1);
|
||||||
|
folder.setName("ToDelete");
|
||||||
|
folder.setPath("ToDelete");
|
||||||
|
folder.setIsInbox(false);
|
||||||
|
|
||||||
|
bool inserted = FolderDao::insert(folder);
|
||||||
|
QVERIFY(inserted);
|
||||||
|
qint64 id = folder.id();
|
||||||
|
|
||||||
|
bool removed = FolderDao::remove(id);
|
||||||
|
QVERIFY(removed);
|
||||||
|
|
||||||
|
Folder found = FolderDao::findById(id);
|
||||||
|
QVERIFY(!found.isValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_MAIN(TestFolderDao)
|
||||||
|
#include "test_folderdao.moc"
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
#include <QtTest/QtTest>
|
||||||
|
#include "../../src/db/dao/mailitemdao.h"
|
||||||
|
#include "../../src/db/databasemanager.h"
|
||||||
|
#include "../../src/core/models/mailitem.h"
|
||||||
|
#include "../../src/core/models/account.h"
|
||||||
|
#include "../../src/core/models/folder.h"
|
||||||
|
|
||||||
|
class TestMailItemDao : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private slots:
|
||||||
|
void initTestCase();
|
||||||
|
void cleanupTestCase();
|
||||||
|
void testInsertAndFind();
|
||||||
|
void testUpdate();
|
||||||
|
void testRemove();
|
||||||
|
};
|
||||||
|
|
||||||
|
void TestMailItemDao::initTestCase()
|
||||||
|
{
|
||||||
|
DatabaseManager::instance().openDatabase(":memory:");
|
||||||
|
DatabaseManager::instance().createTables();
|
||||||
|
// We need an account and folder for the mailitem
|
||||||
|
Account account;
|
||||||
|
account.setEmail("test@example.com");
|
||||||
|
account.setProvider("IMAP");
|
||||||
|
account.setHost("imap.example.com");
|
||||||
|
account.setPort(993);
|
||||||
|
account.setUsername("testuser");
|
||||||
|
account.setPassword("testpass");
|
||||||
|
account.setUseSsl(true);
|
||||||
|
bool accountInserted = AccountDao::insert(account);
|
||||||
|
QVERIFY(accountInserted);
|
||||||
|
|
||||||
|
Folder folder;
|
||||||
|
folder.setAccountId(account.id());
|
||||||
|
folder.setName("Inbox");
|
||||||
|
folder.setPath("INBOX");
|
||||||
|
folder.setIsInbox(true);
|
||||||
|
bool folderInserted = FolderDao::insert(folder);
|
||||||
|
QVERIFY(folderInserted);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestMailItemDao::cleanupTestCase()
|
||||||
|
{
|
||||||
|
DatabaseManager::instance().closeDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestMailItemDao::testInsertAndFind()
|
||||||
|
{
|
||||||
|
MailItem item;
|
||||||
|
item.setAccountId(1); // from account inserted in init
|
||||||
|
item.setFolderId(1); // from folder inserted in init
|
||||||
|
item.setSubject("Test Subject");
|
||||||
|
item.setFrom("sender@example.com");
|
||||||
|
item.setTo("receiver@example.com");
|
||||||
|
item.setDate(QDateTime::currentDateTimeUtc());
|
||||||
|
item.setContent("Test content");
|
||||||
|
item.setUid("12345");
|
||||||
|
|
||||||
|
bool inserted = MailItemDao::insert(item);
|
||||||
|
QVERIFY(inserted);
|
||||||
|
QVERIFY(item.id() > 0);
|
||||||
|
|
||||||
|
MailItem found = MailItemDao::findById(item.id());
|
||||||
|
QVERIFY(found.isValid());
|
||||||
|
QCOMPARE(found.accountId(), 1);
|
||||||
|
QCOMPARE(found.folderId(), 1);
|
||||||
|
QCOMPARE(found.subject(), QString("Test Subject"));
|
||||||
|
QCOMPARE(found.from(), QString("sender@example.com"));
|
||||||
|
QCOMPARE(found.to(), QString("receiver@example.com"));
|
||||||
|
QCOMPARE(found.content(), QString("Test content"));
|
||||||
|
QCOMPARE(found.uid(), QString("12345"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestMailItemDao::testUpdate()
|
||||||
|
{
|
||||||
|
MailItem item;
|
||||||
|
item.setAccountId(1);
|
||||||
|
item.setFolderId(1);
|
||||||
|
item.setSubject("Original Subject");
|
||||||
|
item.setFrom("sender@example.com");
|
||||||
|
item.setTo("receiver@example.com");
|
||||||
|
item.setDate(QDateTime::currentDateTimeUtc());
|
||||||
|
item.setContent("Original content");
|
||||||
|
item.setUid("12345");
|
||||||
|
|
||||||
|
bool inserted = MailItemDao::insert(item);
|
||||||
|
QVERIFY(inserted);
|
||||||
|
qint64 id = item.id();
|
||||||
|
|
||||||
|
item.setSubject("Updated Subject");
|
||||||
|
item.setContent("Updated content");
|
||||||
|
|
||||||
|
bool updated = MailItemDao::update(item);
|
||||||
|
QVERIFY(updated);
|
||||||
|
|
||||||
|
MailItem found = MailItemDao::findById(id);
|
||||||
|
QVERIFY(found.isValid());
|
||||||
|
QCOMPARE(found.subject(), QString("Updated Subject"));
|
||||||
|
QCOMPARE(found.content(), QString("Updated content"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestMailItemDao::testRemove()
|
||||||
|
{
|
||||||
|
MailItem item;
|
||||||
|
item.setAccountId(1);
|
||||||
|
item.setFolderId(1);
|
||||||
|
item.setSubject("To Delete");
|
||||||
|
item.setFrom("sender@example.com");
|
||||||
|
item.setTo("receiver@example.com");
|
||||||
|
item.setDate(QDateTime::currentDateTimeUtc());
|
||||||
|
item.setContent("To be deleted");
|
||||||
|
item.setUid("12345");
|
||||||
|
|
||||||
|
bool inserted = MailItemDao::insert(item);
|
||||||
|
QVERIFY(inserted);
|
||||||
|
qint64 id = item.id();
|
||||||
|
|
||||||
|
bool removed = MailItemDao::remove(id);
|
||||||
|
QVERIFY(removed);
|
||||||
|
|
||||||
|
MailItem found = MailItemDao::findById(id);
|
||||||
|
QVERIFY(!found.isValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_MAIN(TestMailItemDao)
|
||||||
|
#include "test_mailitemdao.moc"
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
#include <QtTest/QtTest>
|
||||||
|
#include "../../src/core/translator.h"
|
||||||
|
|
||||||
|
class TestTranslator : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
private slots:
|
||||||
|
void initTestCase();
|
||||||
|
void testLoadLanguage();
|
||||||
|
void testTranslation();
|
||||||
|
void testCleanup();
|
||||||
|
};
|
||||||
|
|
||||||
|
void TestTranslator::initTestCase()
|
||||||
|
{
|
||||||
|
// Ensure we start with a clean state (singleton may persist)
|
||||||
|
// Since Translator is a singleton, we can reload language.
|
||||||
|
Translator& translator = Translator::instance();
|
||||||
|
translator.loadLanguage("en_US");
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestTranslator::testLoadLanguage()
|
||||||
|
{
|
||||||
|
Translator& translator = Translator::instance();
|
||||||
|
QVERIFY(translator.loadLanguage("en_US"));
|
||||||
|
QCOMPARE(translator.currentLanguage(), QString("en_US"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestTranslator::testTranslation()
|
||||||
|
{
|
||||||
|
Translator& translator = Translator::instance();
|
||||||
|
// Test known keys
|
||||||
|
QCOMPARE(translator.tr("appName"), QString("Wino Mail"));
|
||||||
|
QCOMPARE(translator.tr("welcomeMessage"), QString("Welcome to Wino Mail"));
|
||||||
|
QCOMPARE(translator.tr("inbox"), QString("Inbox"));
|
||||||
|
QCOMPARE(translator.tr("settings"), QString("Settings"));
|
||||||
|
// Test unknown key returns the key itself
|
||||||
|
QCOMPARE(translator.tr("unknown.key"), QString("unknown.key"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestTranslator::testCleanup()
|
||||||
|
{
|
||||||
|
// Nothing to clean up
|
||||||
|
}
|
||||||
|
|
||||||
|
QTEST_MAIN(TestTranslator)
|
||||||
|
#include "test_translator.moc"
|
||||||
Reference in New Issue
Block a user