mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-12-04 15:39:34 +01:00
Refactor attachment handling system with enhanced UI (#12085)
* Renamed NewEntryAttachmentsDialog to EditEntryAttachmentsDialog for clarity. * Introduced EditEntryAttachmentsDialog class to manage editing of existing attachments. * Added functionality to preview attachments while editing them. * Enhanced EntryAttachmentsModel with rowByKey method for better key management. * Add image attachment support with zoom functionality. * Add html and markdown detection. * Improve button layout on the attachment section when editing an entry
This commit is contained in:
@@ -18,7 +18,9 @@
|
||||
#include "TestTools.h"
|
||||
|
||||
#include "core/Clock.h"
|
||||
#include "core/Tools.h"
|
||||
|
||||
#include <QFileInfo>
|
||||
#include <QRegularExpression>
|
||||
#include <QTest>
|
||||
#include <QUuid>
|
||||
@@ -277,10 +279,8 @@ void TestTools::testMimeTypes()
|
||||
{
|
||||
const QStringList TextMimeTypes = {
|
||||
"text/plain", // Plain text
|
||||
"text/html", // HTML documents
|
||||
"text/css", // CSS stylesheets
|
||||
"text/javascript", // JavaScript files
|
||||
"text/markdown", // Markdown documents
|
||||
"text/xml", // XML documents
|
||||
"text/rtf", // Rich Text Format
|
||||
"text/vcard", // vCard files
|
||||
@@ -327,6 +327,9 @@ void TestTools::testMimeTypes()
|
||||
"application/x-shellscript", // Shell scripts
|
||||
};
|
||||
|
||||
QCOMPARE(Tools::toMimeType("text/html"), Tools::MimeType::Html);
|
||||
QCOMPARE(Tools::toMimeType("text/markdown"), Tools::MimeType::Markdown);
|
||||
|
||||
for (const auto& mime : TextMimeTypes) {
|
||||
QCOMPARE(Tools::toMimeType(mime), Tools::MimeType::PlainText);
|
||||
}
|
||||
@@ -339,3 +342,89 @@ void TestTools::testMimeTypes()
|
||||
QCOMPARE(Tools::toMimeType(mime), Tools::MimeType::Unknown);
|
||||
}
|
||||
}
|
||||
|
||||
void TestTools::testGetMimeType()
|
||||
{
|
||||
const QStringList Text = {"0x42", ""};
|
||||
|
||||
for (const auto& text : Text) {
|
||||
QCOMPARE(Tools::getMimeType(text.toUtf8()), Tools::MimeType::PlainText);
|
||||
}
|
||||
|
||||
const QByteArrayList ImageHeaders = {
|
||||
// JPEG: starts with 0xFF 0xD8 0xFF (Start of Image marker)
|
||||
QByteArray::fromHex("FFD8FF"),
|
||||
// PNG: starts with 0x89 0x50 0x4E 0x47 0D 0A 1A 0A (PNG signature)
|
||||
QByteArray::fromHex("89504E470D0A1A0A"),
|
||||
// GIF87a: original GIF format (1987 standard)
|
||||
QByteArray("GIF87a"),
|
||||
// GIF89a: extended GIF format (1989, supports animation, transparency, etc.)
|
||||
QByteArray("GIF89a"),
|
||||
};
|
||||
|
||||
for (const auto& image : ImageHeaders) {
|
||||
QCOMPARE(Tools::getMimeType(image), Tools::MimeType::Image);
|
||||
}
|
||||
|
||||
const QByteArrayList UnknownHeaders = {
|
||||
// MP3: typically starts with ID3 tag (ID3v2)
|
||||
QByteArray("ID3"),
|
||||
// MP4: usually starts with a 'ftyp' box (ISO base media file format)
|
||||
// Common major brands: isom, mp42, avc1, etc.
|
||||
QByteArray::fromHex("000000186674797069736F6D"), // size + 'ftyp' + 'isom'
|
||||
// PDF: starts with "%PDF-" followed by version (e.g., %PDF-1.7)
|
||||
QByteArray("%PDF-"),
|
||||
};
|
||||
|
||||
for (const auto& unknown : UnknownHeaders) {
|
||||
QCOMPARE(Tools::getMimeType(unknown), Tools::MimeType::Unknown);
|
||||
}
|
||||
}
|
||||
|
||||
void TestTools::testGetMimeTypeByFileInfo()
|
||||
{
|
||||
const QStringList Text = {"test.txt", "test.csv", "test.xml", "test.json"};
|
||||
|
||||
for (const auto& text : Text) {
|
||||
QCOMPARE(Tools::getMimeType(QFileInfo(text)), Tools::MimeType::PlainText);
|
||||
}
|
||||
|
||||
const QStringList Images = {"test.jpg", "test.png", "test.bmp", "test.svg"};
|
||||
|
||||
for (const auto& image : Images) {
|
||||
QCOMPARE(Tools::getMimeType(QFileInfo(image)), Tools::MimeType::Image);
|
||||
}
|
||||
|
||||
const QStringList Htmls = {"test.html", "test.htm"};
|
||||
|
||||
for (const auto& html : Htmls) {
|
||||
QCOMPARE(Tools::getMimeType(QFileInfo(html)), Tools::MimeType::Html);
|
||||
}
|
||||
|
||||
const QStringList Markdowns = {"test.md", "test.markdown"};
|
||||
|
||||
for (const auto& makdown : Markdowns) {
|
||||
QCOMPARE(Tools::getMimeType(QFileInfo(makdown)), Tools::MimeType::Markdown);
|
||||
}
|
||||
|
||||
const QStringList UnknownHeaders = {"test.doc", "test.pdf", "test.docx"};
|
||||
|
||||
for (const auto& unknown : UnknownHeaders) {
|
||||
QCOMPARE(Tools::getMimeType(unknown), Tools::MimeType::Unknown);
|
||||
}
|
||||
}
|
||||
|
||||
void TestTools::testIsTextMimeType()
|
||||
{
|
||||
const auto Text = {Tools::MimeType::PlainText, Tools::MimeType::Html, Tools::MimeType::Markdown};
|
||||
|
||||
for (const auto& text : Text) {
|
||||
QVERIFY(Tools::isTextMimeType(text));
|
||||
}
|
||||
|
||||
const auto NoText = {Tools::MimeType::Image, Tools::MimeType::Unknown};
|
||||
|
||||
for (const auto& noText : NoText) {
|
||||
QVERIFY(!Tools::isTextMimeType(noText));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#ifndef KEEPASSX_TESTTOOLS_H
|
||||
#define KEEPASSX_TESTTOOLS_H
|
||||
|
||||
#include "core/Tools.h"
|
||||
#include <QObject>
|
||||
|
||||
class TestTools : public QObject
|
||||
{
|
||||
@@ -38,6 +38,9 @@ private slots:
|
||||
void testConvertToRegex_data();
|
||||
void testArrayContainsValues();
|
||||
void testMimeTypes();
|
||||
void testGetMimeType();
|
||||
void testGetMimeTypeByFileInfo();
|
||||
void testIsTextMimeType();
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_TESTTOOLS_H
|
||||
|
||||
@@ -18,6 +18,10 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/..)
|
||||
add_unit_test(NAME testgui SOURCES TestGui.cpp ../util/TemporaryFile.cpp ../mock/MockRemoteProcess.cpp LIBS ${TEST_LIBRARIES})
|
||||
add_unit_test(NAME testguipixmaps SOURCES TestGuiPixmaps.cpp LIBS ${TEST_LIBRARIES})
|
||||
|
||||
file(GLOB_RECURSE ATTACHMENTS_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/attachments/*.cpp)
|
||||
add_unit_test(NAME testguiattachments SOURCES ${ATTACHMENTS_TEST_SOURCES} LIBS ${TEST_LIBRARIES})
|
||||
include_directories(testguiattachments PRIVATE ${PROJECT_SOURCE_DIR}/src/gui/entry)
|
||||
|
||||
if(WITH_XC_BROWSER)
|
||||
add_unit_test(NAME testguibrowser SOURCES TestGuiBrowser.cpp ../util/TemporaryFile.cpp LIBS ${TEST_LIBRARIES})
|
||||
endif()
|
||||
|
||||
94
tests/gui/attachments/TestAttachmentWidget.cpp
Normal file
94
tests/gui/attachments/TestAttachmentWidget.cpp
Normal file
@@ -0,0 +1,94 @@
|
||||
#include "TestAttachmentWidget.h"
|
||||
|
||||
#include <attachments/AttachmentWidget.h>
|
||||
|
||||
#include <attachments/AttachmentTypes.h>
|
||||
#include <attachments/ImageAttachmentsWidget.h>
|
||||
#include <attachments/TextAttachmentsWidget.h>
|
||||
|
||||
#include <QLabel>
|
||||
#include <QTest>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
void TestAttachmentsWidget::initTestCase()
|
||||
{
|
||||
m_attachmentWidget.reset(new AttachmentWidget());
|
||||
|
||||
QVERIFY(m_attachmentWidget);
|
||||
}
|
||||
|
||||
void TestAttachmentsWidget::testTextAttachment()
|
||||
{
|
||||
for (const auto& attachment : {attachments::Attachment{.name = "Test.txt", .data = "Test"},
|
||||
attachments::Attachment{.name = "Test.html", .data = "<h1> test </h1>"},
|
||||
attachments::Attachment{.name = "Test.md", .data = "**bold**"}}) {
|
||||
for (auto mode : {attachments::OpenMode::ReadWrite, attachments::OpenMode::ReadOnly}) {
|
||||
m_attachmentWidget->openAttachment(attachment, mode);
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
auto layout = m_attachmentWidget->findChild<QVBoxLayout*>("verticalLayout");
|
||||
QVERIFY(layout);
|
||||
|
||||
QCOMPARE(layout->count(), 1);
|
||||
|
||||
auto item = layout->itemAt(0);
|
||||
QVERIFY(item);
|
||||
|
||||
QVERIFY(qobject_cast<TextAttachmentsWidget*>(item->widget()));
|
||||
|
||||
auto actualAttachment = m_attachmentWidget->getAttachment();
|
||||
QCOMPARE(actualAttachment.name, attachment.name);
|
||||
QCOMPARE(actualAttachment.data, attachment.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TestAttachmentsWidget::testImageAttachment()
|
||||
{
|
||||
const auto Attachment = attachments::Attachment{.name = "Test.jpg", .data = QByteArray::fromHex("FFD8FF")};
|
||||
|
||||
m_attachmentWidget->openAttachment(Attachment, attachments::OpenMode::ReadWrite);
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
auto layout = m_attachmentWidget->findChild<QVBoxLayout*>("verticalLayout");
|
||||
QVERIFY(layout);
|
||||
|
||||
QCOMPARE(layout->count(), 1);
|
||||
|
||||
auto item = layout->itemAt(0);
|
||||
QVERIFY(item);
|
||||
|
||||
QVERIFY(qobject_cast<ImageAttachmentsWidget*>(item->widget()));
|
||||
|
||||
auto actualAttachment = m_attachmentWidget->getAttachment();
|
||||
QCOMPARE(actualAttachment.name, Attachment.name);
|
||||
QCOMPARE(actualAttachment.data, Attachment.data);
|
||||
}
|
||||
|
||||
void TestAttachmentsWidget::testUnknownAttachment()
|
||||
{
|
||||
const auto Attachment = attachments::Attachment{.name = "Test", .data = QByteArray{"ID3"}};
|
||||
|
||||
m_attachmentWidget->openAttachment(Attachment, attachments::OpenMode::ReadWrite);
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
auto layout = m_attachmentWidget->findChild<QVBoxLayout*>("verticalLayout");
|
||||
QVERIFY(layout);
|
||||
|
||||
QCOMPARE(layout->count(), 1);
|
||||
|
||||
auto item = layout->itemAt(0);
|
||||
QVERIFY(item);
|
||||
|
||||
auto label = qobject_cast<QLabel*>(item->widget());
|
||||
QVERIFY(label);
|
||||
|
||||
QVERIFY(!label->text().isEmpty());
|
||||
|
||||
auto actualAttachment = m_attachmentWidget->getAttachment();
|
||||
QCOMPARE(actualAttachment.name, Attachment.name);
|
||||
QCOMPARE(actualAttachment.data, Attachment.data);
|
||||
}
|
||||
38
tests/gui/attachments/TestAttachmentWidget.h
Normal file
38
tests/gui/attachments/TestAttachmentWidget.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2025 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <attachments/AttachmentWidget.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QScopedPointer>
|
||||
|
||||
class TestAttachmentsWidget : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
|
||||
void testTextAttachment();
|
||||
void testImageAttachment();
|
||||
void testUnknownAttachment();
|
||||
|
||||
private:
|
||||
QScopedPointer<AttachmentWidget> m_attachmentWidget;
|
||||
};
|
||||
46
tests/gui/attachments/TestAttachmentsGui.cpp
Normal file
46
tests/gui/attachments/TestAttachmentsGui.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
#include <QtTest>
|
||||
|
||||
#include "TestAttachmentWidget.h"
|
||||
#include "TestEditEntryAttachmentsDialog.h"
|
||||
#include "TestImageAttachmentsView.h"
|
||||
#include "TestImageAttachmentsWidget.h"
|
||||
#include "TestPreviewEntryAttachmentsDialog.h"
|
||||
#include "TestTextAttachmentsEditWidget.h"
|
||||
#include "TestTextAttachmentsPreviewWidget.h"
|
||||
#include "TestTextAttachmentsWidget.h"
|
||||
|
||||
#include <config-keepassx.h>
|
||||
#include <gui/Application.h>
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
|
||||
Application app(argc, argv);
|
||||
app.setApplicationName("KeePassXC");
|
||||
app.setApplicationVersion(KEEPASSXC_VERSION);
|
||||
app.setQuitOnLastWindowClosed(false);
|
||||
app.setAttribute(Qt::AA_Use96Dpi, true);
|
||||
app.applyTheme();
|
||||
|
||||
TestPreviewEntryAttachmentsDialog previewDialogTest{};
|
||||
TestEditEntryAttachmentsDialog editDialogTest{};
|
||||
TestTextAttachmentsWidget textAttachmentsWidget{};
|
||||
TestTextAttachmentsPreviewWidget textPreviewWidget{};
|
||||
TestTextAttachmentsEditWidget textEditWidget{};
|
||||
TestImageAttachmentsWidget imageWidget{};
|
||||
TestImageAttachmentsView imageView{};
|
||||
TestAttachmentsWidget attachmentWidget{};
|
||||
|
||||
int result = 0;
|
||||
result |= QTest::qExec(&previewDialogTest, argc, argv);
|
||||
result |= QTest::qExec(&editDialogTest, argc, argv);
|
||||
result |= QTest::qExec(&textAttachmentsWidget, argc, argv);
|
||||
result |= QTest::qExec(&textPreviewWidget, argc, argv);
|
||||
result |= QTest::qExec(&textEditWidget, argc, argv);
|
||||
result |= QTest::qExec(&imageWidget, argc, argv);
|
||||
result |= QTest::qExec(&imageView, argc, argv);
|
||||
result |= QTest::qExec(&attachmentWidget, argc, argv);
|
||||
|
||||
return result;
|
||||
}
|
||||
95
tests/gui/attachments/TestEditEntryAttachmentsDialog.cpp
Normal file
95
tests/gui/attachments/TestEditEntryAttachmentsDialog.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
#include "TestEditEntryAttachmentsDialog.h"
|
||||
|
||||
#include <attachments/AttachmentWidget.h>
|
||||
|
||||
#include <PreviewEntryAttachmentsDialog.h>
|
||||
|
||||
#include <QAbstractButton>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QSignalSpy>
|
||||
#include <QSizePolicy>
|
||||
#include <QTest>
|
||||
#include <QTestMouseEvent>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
void TestEditEntryAttachmentsDialog::initTestCase()
|
||||
{
|
||||
m_editDialog.reset(new EditEntryAttachmentsDialog());
|
||||
|
||||
QVERIFY(m_editDialog);
|
||||
}
|
||||
|
||||
void TestEditEntryAttachmentsDialog::testSetAttachment()
|
||||
{
|
||||
const attachments::Attachment Test{.name = "text.txt", .data = "Test"};
|
||||
m_editDialog->setAttachment(Test);
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
QVERIFY2(m_editDialog->windowTitle().contains(Test.name), "Expected file name in the title");
|
||||
|
||||
auto layout = m_editDialog->findChild<QVBoxLayout*>("verticalLayout");
|
||||
QVERIFY2(layout, "QVBoxLayout not found");
|
||||
QCOMPARE(layout->count(), 2);
|
||||
|
||||
auto widget = qobject_cast<AttachmentWidget*>(layout->itemAt(0)->widget());
|
||||
QVERIFY2(widget, "Expected AttachmentWidget");
|
||||
|
||||
auto sizePolicy = widget->sizePolicy();
|
||||
QCOMPARE(sizePolicy.horizontalPolicy(), QSizePolicy::Expanding);
|
||||
QCOMPARE(sizePolicy.verticalPolicy(), QSizePolicy::Expanding);
|
||||
|
||||
auto attachments = widget->getAttachment();
|
||||
|
||||
QCOMPARE(attachments.name, Test.name);
|
||||
QCOMPARE(attachments.data, Test.data);
|
||||
}
|
||||
|
||||
void TestEditEntryAttachmentsDialog::testSetAttachmentTwice()
|
||||
{
|
||||
const attachments::Attachment TestText{.name = "text.txt", .data = "Test"};
|
||||
m_editDialog->setAttachment(TestText);
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
const attachments::Attachment TestImage{
|
||||
.name = "test.jpg", .data = QByteArray::fromHex("FFD8FFE000104A46494600010101006000600000FFD9")};
|
||||
m_editDialog->setAttachment(TestImage);
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
QVERIFY2(m_editDialog->windowTitle().contains(TestImage.name), "Expected file name in the title");
|
||||
|
||||
auto layout = m_editDialog->findChild<QVBoxLayout*>("verticalLayout");
|
||||
QVERIFY2(layout, "QVBoxLayout not found");
|
||||
QCOMPARE(layout->count(), 2);
|
||||
|
||||
auto widget = qobject_cast<AttachmentWidget*>(layout->itemAt(0)->widget());
|
||||
QVERIFY2(widget, "Expected AttachmentWidget");
|
||||
|
||||
auto attachments = widget->getAttachment();
|
||||
|
||||
QCOMPARE(attachments.name, TestImage.name);
|
||||
QCOMPARE(attachments.data, TestImage.data);
|
||||
}
|
||||
|
||||
void TestEditEntryAttachmentsDialog::testBottonsBox()
|
||||
{
|
||||
const attachments::Attachment TestText{.name = "text.txt", .data = "Test"};
|
||||
m_editDialog->setAttachment(TestText);
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
QSignalSpy acceptButton(m_editDialog.data(), &PreviewEntryAttachmentsDialog::accepted);
|
||||
QSignalSpy closeButton(m_editDialog.data(), &PreviewEntryAttachmentsDialog::rejected);
|
||||
|
||||
auto buttonsBox = m_editDialog->findChild<QDialogButtonBox*>();
|
||||
QVERIFY2(buttonsBox, "ButtonsBox not found");
|
||||
|
||||
for (auto button : buttonsBox->buttons()) {
|
||||
QTest::mouseClick(button, Qt::LeftButton);
|
||||
}
|
||||
|
||||
QCOMPARE(acceptButton.count(), 1);
|
||||
QCOMPARE(closeButton.count(), 1);
|
||||
}
|
||||
39
tests/gui/attachments/TestEditEntryAttachmentsDialog.h
Normal file
39
tests/gui/attachments/TestEditEntryAttachmentsDialog.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2025 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "EditEntryAttachmentsDialog.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QScopedPointer>
|
||||
|
||||
class TestEditEntryAttachmentsDialog : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
|
||||
void testSetAttachment();
|
||||
void testSetAttachmentTwice();
|
||||
|
||||
void testBottonsBox();
|
||||
|
||||
private:
|
||||
QScopedPointer<EditEntryAttachmentsDialog> m_editDialog{};
|
||||
};
|
||||
76
tests/gui/attachments/TestImageAttachmentsView.cpp
Normal file
76
tests/gui/attachments/TestImageAttachmentsView.cpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#include "TestImageAttachmentsView.h"
|
||||
|
||||
#include <attachments/ImageAttachmentsView.h>
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QPixmap>
|
||||
#include <QSignalSpy>
|
||||
#include <QTest>
|
||||
#include <QWheelEvent>
|
||||
|
||||
void TestImageAttachmentsView::initTestCase()
|
||||
{
|
||||
m_view.reset(new ImageAttachmentsView());
|
||||
|
||||
// Generate the black rectange.
|
||||
QImage image(1000, 1000, QImage::Format_RGB32);
|
||||
image.fill(Qt::black);
|
||||
|
||||
auto scene = new QGraphicsScene();
|
||||
scene->addPixmap(QPixmap::fromImage(image));
|
||||
|
||||
m_view->setScene(scene);
|
||||
m_view->show();
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
}
|
||||
|
||||
void TestImageAttachmentsView::testEmitWheelEvent()
|
||||
{
|
||||
QSignalSpy ctrlWheelEvent{m_view.data(), &ImageAttachmentsView::ctrlWheelEvent};
|
||||
|
||||
QPoint center = m_view->rect().center();
|
||||
|
||||
m_view->setFocus();
|
||||
|
||||
QWheelEvent event(center, // local pos
|
||||
m_view->mapToGlobal(center), // global pos
|
||||
QPoint(0, 0),
|
||||
QPoint(0, 120),
|
||||
Qt::NoButton,
|
||||
Qt::ControlModifier,
|
||||
Qt::ScrollBegin,
|
||||
false);
|
||||
|
||||
QCoreApplication::sendEvent(m_view->viewport(), &event);
|
||||
|
||||
QCOMPARE(ctrlWheelEvent.count(), 1);
|
||||
}
|
||||
|
||||
void TestImageAttachmentsView::testEnableFit()
|
||||
{
|
||||
m_view->enableAutoFitInView();
|
||||
QVERIFY(m_view->isAutoFitInViewActivated());
|
||||
|
||||
const auto oldTransform = m_view->transform();
|
||||
|
||||
m_view->resize(m_view->size() + QSize(100, 100));
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
QVERIFY(m_view->transform() != oldTransform);
|
||||
}
|
||||
|
||||
void TestImageAttachmentsView::testDisableFit()
|
||||
{
|
||||
m_view->disableAutoFitInView();
|
||||
QVERIFY(!m_view->isAutoFitInViewActivated());
|
||||
|
||||
const auto expectedTransform = m_view->transform();
|
||||
|
||||
m_view->resize(m_view->size() + QSize(100, 100));
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
QCOMPARE(m_view->transform(), expectedTransform);
|
||||
}
|
||||
38
tests/gui/attachments/TestImageAttachmentsView.h
Normal file
38
tests/gui/attachments/TestImageAttachmentsView.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2025 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <attachments/ImageAttachmentsView.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QScopedPointer>
|
||||
|
||||
class TestImageAttachmentsView : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
|
||||
void testEmitWheelEvent();
|
||||
void testEnableFit();
|
||||
void testDisableFit();
|
||||
|
||||
private:
|
||||
QScopedPointer<ImageAttachmentsView> m_view{};
|
||||
};
|
||||
244
tests/gui/attachments/TestImageAttachmentsWidget.cpp
Normal file
244
tests/gui/attachments/TestImageAttachmentsWidget.cpp
Normal file
@@ -0,0 +1,244 @@
|
||||
#include "TestImageAttachmentsWidget.h"
|
||||
|
||||
#include <attachments/ImageAttachmentsView.h>
|
||||
#include <attachments/ImageAttachmentsWidget.h>
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QComboBox>
|
||||
#include <QCoreApplication>
|
||||
#include <QTest>
|
||||
#include <QTransform>
|
||||
|
||||
void TestImageAttachmentsWidget::initTestCase()
|
||||
{
|
||||
m_widget.reset(new ImageAttachmentsWidget());
|
||||
m_zoomCombobox = m_widget->findChild<QComboBox*>("zoomComboBox");
|
||||
QVERIFY(m_zoomCombobox);
|
||||
|
||||
m_imageAttachmentsView = m_widget->findChild<ImageAttachmentsView*>("imagesView");
|
||||
QVERIFY(m_imageAttachmentsView);
|
||||
|
||||
// Generate the black rectange.
|
||||
QImage image(1000, 1000, QImage::Format_RGB32);
|
||||
image.fill(Qt::black);
|
||||
|
||||
QByteArray imageBytes{};
|
||||
QBuffer buffer(&imageBytes);
|
||||
buffer.open(QIODevice::WriteOnly);
|
||||
|
||||
image.save(&buffer, "PNG");
|
||||
|
||||
m_widget->openAttachment({.name = "black.png", .data = std::move(imageBytes)}, attachments::OpenMode::ReadOnly);
|
||||
|
||||
m_widget->show();
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
}
|
||||
|
||||
void TestImageAttachmentsWidget::testFitInView()
|
||||
{
|
||||
QCOMPARE(m_zoomCombobox->currentText(), tr("Fit"));
|
||||
QVERIFY(m_imageAttachmentsView->isAutoFitInViewActivated());
|
||||
|
||||
auto zoomFactor = m_imageAttachmentsView->transform();
|
||||
|
||||
m_widget->setMinimumSize(m_widget->size() + QSize{100, 100});
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
QVERIFY(zoomFactor != m_imageAttachmentsView->transform());
|
||||
}
|
||||
|
||||
void TestImageAttachmentsWidget::testZoomCombobox()
|
||||
{
|
||||
for (const auto zoom : {0.25, 0.5, 1.0, 2.0}) {
|
||||
auto index = m_zoomCombobox->findData(zoom);
|
||||
QVERIFY(index != -1);
|
||||
|
||||
m_zoomCombobox->setCurrentIndex(index);
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
QCOMPARE(m_imageAttachmentsView->transform(), QTransform::fromScale(zoom, zoom));
|
||||
}
|
||||
}
|
||||
|
||||
void TestImageAttachmentsWidget::testEditZoomCombobox()
|
||||
{
|
||||
for (double i = 0.25; i < 5; i += 0.25) {
|
||||
m_zoomCombobox->setCurrentText(QString::number(i * 100));
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
QCOMPARE(m_imageAttachmentsView->transform(), QTransform::fromScale(i, i));
|
||||
}
|
||||
}
|
||||
|
||||
void TestImageAttachmentsWidget::testEditWithPercentZoomCombobox()
|
||||
{
|
||||
// Example 100 %
|
||||
for (double i = 0.25; i < 5; i += 0.25) {
|
||||
m_zoomCombobox->setCurrentText(QString("%1 %").arg(i * 100));
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
QCOMPARE(m_imageAttachmentsView->transform(), QTransform::fromScale(i, i));
|
||||
}
|
||||
|
||||
// Example 100%
|
||||
for (double i = 0.25; i < 5; i += 0.25) {
|
||||
m_zoomCombobox->setCurrentText(QString("%1%").arg(i * 100));
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
QCOMPARE(m_imageAttachmentsView->transform(), QTransform::fromScale(i, i));
|
||||
}
|
||||
}
|
||||
|
||||
void TestImageAttachmentsWidget::testInvalidValueZoomCombobox()
|
||||
{
|
||||
auto index = m_zoomCombobox->findData(1.0);
|
||||
QVERIFY(index != -1);
|
||||
|
||||
m_zoomCombobox->setCurrentIndex(index);
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
const QTransform expectedTransform = m_imageAttachmentsView->transform();
|
||||
|
||||
for (const auto& invalidValue : {"Help", "3,4", "", ".", "% 100"}) {
|
||||
m_zoomCombobox->setCurrentText(invalidValue);
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
QCOMPARE(m_imageAttachmentsView->transform(), expectedTransform);
|
||||
}
|
||||
}
|
||||
|
||||
void TestImageAttachmentsWidget::testZoomInByMouse()
|
||||
{
|
||||
QPoint center = m_imageAttachmentsView->rect().center();
|
||||
|
||||
// Set zoom: 100%
|
||||
auto index = m_zoomCombobox->findData(1.0);
|
||||
QVERIFY(index != -1);
|
||||
m_zoomCombobox->setCurrentIndex(index);
|
||||
|
||||
m_imageAttachmentsView->setFocus();
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
const auto transform = m_imageAttachmentsView->transform();
|
||||
|
||||
QWheelEvent event(center, // local pos
|
||||
m_imageAttachmentsView->mapToGlobal(center), // global pos
|
||||
QPoint(0, 0),
|
||||
QPoint(0, 120),
|
||||
Qt::NoButton,
|
||||
Qt::ControlModifier,
|
||||
Qt::ScrollBegin,
|
||||
false);
|
||||
|
||||
QCoreApplication::sendEvent(m_imageAttachmentsView->viewport(), &event);
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
QTransform t = m_imageAttachmentsView->transform();
|
||||
QVERIFY(t.m11() > transform.m11());
|
||||
QVERIFY(t.m22() > transform.m22());
|
||||
}
|
||||
|
||||
void TestImageAttachmentsWidget::testZoomOutByMouse()
|
||||
{
|
||||
QPoint center = m_imageAttachmentsView->rect().center();
|
||||
|
||||
// Set zoom: 100%
|
||||
auto index = m_zoomCombobox->findData(1.0);
|
||||
QVERIFY(index != -1);
|
||||
m_zoomCombobox->setCurrentIndex(index);
|
||||
|
||||
m_imageAttachmentsView->setFocus();
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
const auto transform = m_imageAttachmentsView->transform();
|
||||
|
||||
QWheelEvent event(center, // local pos
|
||||
center, // global pos
|
||||
QPoint(0, 0),
|
||||
QPoint(0, -120),
|
||||
Qt::NoButton,
|
||||
Qt::ControlModifier,
|
||||
Qt::ScrollBegin,
|
||||
true);
|
||||
|
||||
QCoreApplication::sendEvent(m_imageAttachmentsView->viewport(), &event);
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
QTransform t = m_imageAttachmentsView->transform();
|
||||
QVERIFY(t.m11() < transform.m11());
|
||||
QVERIFY(t.m22() < transform.m22());
|
||||
}
|
||||
|
||||
void TestImageAttachmentsWidget::testZoomLowerBound()
|
||||
{
|
||||
m_widget->setMinimumSize(100, 100);
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
auto minFactor = m_imageAttachmentsView->calculateFitInViewFactor();
|
||||
|
||||
// Set size less then minFactor
|
||||
m_zoomCombobox->setCurrentText(QString::number((minFactor * 100.0) / 2));
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
const auto expectTransform = m_imageAttachmentsView->transform();
|
||||
|
||||
QPoint center = m_imageAttachmentsView->rect().center();
|
||||
|
||||
QWheelEvent event(center, // local pos
|
||||
center, // global pos
|
||||
QPoint(0, 0),
|
||||
QPoint(0, -120),
|
||||
Qt::NoButton,
|
||||
Qt::ControlModifier,
|
||||
Qt::ScrollBegin,
|
||||
true);
|
||||
|
||||
QCoreApplication::sendEvent(m_imageAttachmentsView->viewport(), &event);
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
QCOMPARE(m_imageAttachmentsView->transform(), expectTransform);
|
||||
}
|
||||
|
||||
void TestImageAttachmentsWidget::testZoomUpperBound()
|
||||
{
|
||||
m_widget->setMinimumSize(100, 100);
|
||||
|
||||
// Set size less then minFactor
|
||||
m_zoomCombobox->setCurrentText(QString::number(500));
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
const auto expectTransform = m_imageAttachmentsView->transform();
|
||||
|
||||
QPoint center = m_imageAttachmentsView->rect().center();
|
||||
|
||||
QWheelEvent event(center, // local pos
|
||||
center, // global pos
|
||||
QPoint(0, 0),
|
||||
QPoint(0, 120),
|
||||
Qt::NoButton,
|
||||
Qt::ControlModifier,
|
||||
Qt::ScrollBegin,
|
||||
true);
|
||||
|
||||
QCoreApplication::sendEvent(m_imageAttachmentsView->viewport(), &event);
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
QCOMPARE(m_imageAttachmentsView->transform(), expectTransform);
|
||||
}
|
||||
49
tests/gui/attachments/TestImageAttachmentsWidget.h
Normal file
49
tests/gui/attachments/TestImageAttachmentsWidget.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2025 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <attachments/ImageAttachmentsWidget.h>
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QObject>
|
||||
#include <QScopedPointer>
|
||||
|
||||
class ImageAttachmentsView;
|
||||
|
||||
class TestImageAttachmentsWidget : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
|
||||
void testFitInView();
|
||||
void testZoomCombobox();
|
||||
void testEditZoomCombobox();
|
||||
void testEditWithPercentZoomCombobox();
|
||||
void testInvalidValueZoomCombobox();
|
||||
void testZoomInByMouse();
|
||||
void testZoomOutByMouse();
|
||||
void testZoomLowerBound();
|
||||
void testZoomUpperBound();
|
||||
|
||||
private:
|
||||
QScopedPointer<ImageAttachmentsWidget> m_widget{};
|
||||
QPointer<QComboBox> m_zoomCombobox{};
|
||||
QPointer<ImageAttachmentsView> m_imageAttachmentsView{};
|
||||
};
|
||||
97
tests/gui/attachments/TestPreviewEntryAttachmentsDialog.cpp
Normal file
97
tests/gui/attachments/TestPreviewEntryAttachmentsDialog.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
#include "TestPreviewEntryAttachmentsDialog.h"
|
||||
|
||||
#include <attachments/AttachmentWidget.h>
|
||||
|
||||
#include <PreviewEntryAttachmentsDialog.h>
|
||||
|
||||
#include <QAbstractButton>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QSignalSpy>
|
||||
#include <QSizePolicy>
|
||||
#include <QTest>
|
||||
#include <QTestMouseEvent>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
void TestPreviewEntryAttachmentsDialog::initTestCase()
|
||||
{
|
||||
m_previewDialog.reset(new PreviewEntryAttachmentsDialog());
|
||||
|
||||
QVERIFY(m_previewDialog);
|
||||
}
|
||||
|
||||
void TestPreviewEntryAttachmentsDialog::testSetAttachment()
|
||||
{
|
||||
const attachments::Attachment Test{.name = "text.txt", .data = "Test"};
|
||||
m_previewDialog->setAttachment(Test);
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
QVERIFY2(m_previewDialog->windowTitle().contains(Test.name), "Expected file name in the title");
|
||||
|
||||
auto layout = m_previewDialog->findChild<QVBoxLayout*>("verticalLayout");
|
||||
QVERIFY2(layout, "QVBoxLayout not found");
|
||||
QCOMPARE(layout->count(), 2);
|
||||
|
||||
auto widget = qobject_cast<AttachmentWidget*>(layout->itemAt(0)->widget());
|
||||
QVERIFY2(widget, "Expected AbstractAttachmentWidget");
|
||||
|
||||
auto sizePolicy = widget->sizePolicy();
|
||||
QCOMPARE(sizePolicy.horizontalPolicy(), QSizePolicy::Expanding);
|
||||
QCOMPARE(sizePolicy.verticalPolicy(), QSizePolicy::Expanding);
|
||||
|
||||
auto attachments = widget->getAttachment();
|
||||
|
||||
QCOMPARE(attachments.name, Test.name);
|
||||
QCOMPARE(attachments.data, Test.data);
|
||||
}
|
||||
|
||||
void TestPreviewEntryAttachmentsDialog::testSetAttachmentTwice()
|
||||
{
|
||||
const attachments::Attachment TestText{.name = "text.txt", .data = "Test"};
|
||||
m_previewDialog->setAttachment(TestText);
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
const attachments::Attachment TestImage{
|
||||
.name = "test.jpg", .data = QByteArray::fromHex("FFD8FFE000104A46494600010101006000600000FFD9")};
|
||||
m_previewDialog->setAttachment(TestImage);
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
QVERIFY2(m_previewDialog->windowTitle().contains(TestImage.name), "Expected file name in the title");
|
||||
|
||||
auto layout = m_previewDialog->findChild<QVBoxLayout*>("verticalLayout");
|
||||
QVERIFY2(layout, "QVBoxLayout not found");
|
||||
QCOMPARE(layout->count(), 2);
|
||||
|
||||
auto widget = qobject_cast<AttachmentWidget*>(layout->itemAt(0)->widget());
|
||||
QVERIFY2(widget, "Expected AbstractAttachmentWidget");
|
||||
|
||||
auto attachments = widget->getAttachment();
|
||||
|
||||
QCOMPARE(attachments.name, TestImage.name);
|
||||
QCOMPARE(attachments.data, TestImage.data);
|
||||
}
|
||||
|
||||
void TestPreviewEntryAttachmentsDialog::testBottonsBox()
|
||||
{
|
||||
const attachments::Attachment TestText{.name = "text.txt", .data = "Test"};
|
||||
m_previewDialog->setAttachment(TestText);
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
QSignalSpy saveButton(m_previewDialog.data(), &PreviewEntryAttachmentsDialog::saveAttachment);
|
||||
QSignalSpy openButton(m_previewDialog.data(), &PreviewEntryAttachmentsDialog::openAttachment);
|
||||
QSignalSpy closeButton(m_previewDialog.data(), &PreviewEntryAttachmentsDialog::rejected);
|
||||
|
||||
auto buttonsBox = m_previewDialog->findChild<QDialogButtonBox*>("dialogButtons");
|
||||
QVERIFY2(buttonsBox, "ButtonsBox not found");
|
||||
|
||||
for (auto button : buttonsBox->buttons()) {
|
||||
QTest::mouseClick(button, Qt::LeftButton);
|
||||
}
|
||||
|
||||
QCOMPARE(saveButton.count(), 1);
|
||||
QCOMPARE(openButton.count(), 1);
|
||||
QCOMPARE(closeButton.count(), 1);
|
||||
}
|
||||
40
tests/gui/attachments/TestPreviewEntryAttachmentsDialog.h
Normal file
40
tests/gui/attachments/TestPreviewEntryAttachmentsDialog.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2025 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "PreviewEntryAttachmentsDialog.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <qscopedpointer.h>
|
||||
|
||||
class TestPreviewEntryAttachmentsDialog : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
|
||||
void testSetAttachment();
|
||||
void testSetAttachmentTwice();
|
||||
|
||||
void testBottonsBox();
|
||||
|
||||
private:
|
||||
QScopedPointer<PreviewEntryAttachmentsDialog> m_previewDialog{};
|
||||
};
|
||||
49
tests/gui/attachments/TestTextAttachmentsEditWidget.cpp
Normal file
49
tests/gui/attachments/TestTextAttachmentsEditWidget.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include "TestTextAttachmentsEditWidget.h"
|
||||
|
||||
#include <attachments/TextAttachmentsEditWidget.h>
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QMouseEvent>
|
||||
#include <QPushButton>
|
||||
#include <QSignalSpy>
|
||||
#include <QTest>
|
||||
#include <QTestMouseEvent>
|
||||
#include <QTextEdit>
|
||||
|
||||
void TestTextAttachmentsEditWidget::initTestCase()
|
||||
{
|
||||
m_widget.reset(new TextAttachmentsEditWidget());
|
||||
}
|
||||
|
||||
void TestTextAttachmentsEditWidget::testEmitTextChanged()
|
||||
{
|
||||
QSignalSpy textChangedSignal(m_widget.data(), &TextAttachmentsEditWidget::textChanged);
|
||||
|
||||
m_widget->openAttachment({.name = "test.txt", .data = {}}, attachments::OpenMode::ReadWrite);
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
auto textEdit = m_widget->findChild<QTextEdit*>("attachmentsTextEdit");
|
||||
QVERIFY(textEdit);
|
||||
|
||||
const QByteArray NewText = "New test text";
|
||||
textEdit->setText(NewText);
|
||||
|
||||
QVERIFY(textChangedSignal.count() > 0);
|
||||
}
|
||||
|
||||
void TestTextAttachmentsEditWidget::testEmitPreviewButtonClicked()
|
||||
{
|
||||
QSignalSpy previwButtonClickedSignal(m_widget.data(), &TextAttachmentsEditWidget::previewButtonClicked);
|
||||
|
||||
m_widget->openAttachment({.name = "test.txt", .data = {}}, attachments::OpenMode::ReadWrite);
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
auto previewButton = m_widget->findChild<QPushButton*>("previewPushButton");
|
||||
QVERIFY(previewButton);
|
||||
|
||||
QTest::mouseClick(previewButton, Qt::LeftButton);
|
||||
|
||||
QCOMPARE(previwButtonClickedSignal.count(), 1);
|
||||
}
|
||||
37
tests/gui/attachments/TestTextAttachmentsEditWidget.h
Normal file
37
tests/gui/attachments/TestTextAttachmentsEditWidget.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (C) 2025 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <attachments/TextAttachmentsEditWidget.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QScopedPointer>
|
||||
|
||||
class TestTextAttachmentsEditWidget : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
|
||||
void testEmitTextChanged();
|
||||
void testEmitPreviewButtonClicked();
|
||||
|
||||
private:
|
||||
QScopedPointer<TextAttachmentsEditWidget> m_widget{};
|
||||
};
|
||||
40
tests/gui/attachments/TestTextAttachmentsPreviewWidget.cpp
Normal file
40
tests/gui/attachments/TestTextAttachmentsPreviewWidget.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include "TestTextAttachmentsPreviewWidget.h"
|
||||
|
||||
#include <attachments/TextAttachmentsPreviewWidget.h>
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QTest>
|
||||
|
||||
void TestTextAttachmentsPreviewWidget::initTestCase()
|
||||
{
|
||||
m_widget.reset(new TextAttachmentsPreviewWidget());
|
||||
}
|
||||
|
||||
void TestTextAttachmentsPreviewWidget::testDetectMimeByFile()
|
||||
{
|
||||
const auto combobox = m_widget->findChild<QComboBox*>("typeComboBox");
|
||||
QVERIFY(combobox);
|
||||
|
||||
const attachments::Attachment Text{.name = "test.txt", .data = {}};
|
||||
m_widget->openAttachment(Text, attachments::OpenMode::ReadOnly);
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
QCOMPARE(combobox->currentData().toInt(), TextAttachmentsPreviewWidget::PlainText);
|
||||
|
||||
const attachments::Attachment Html{.name = "test.html", .data = {}};
|
||||
m_widget->openAttachment(Html, attachments::OpenMode::ReadOnly);
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
QCOMPARE(combobox->currentData().toInt(), TextAttachmentsPreviewWidget::Html);
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
|
||||
const attachments::Attachment Markdown{.name = "test.md", .data = {}};
|
||||
m_widget->openAttachment(Markdown, attachments::OpenMode::ReadOnly);
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
QCOMPARE(combobox->currentData().toInt(), TextAttachmentsPreviewWidget::Markdown);
|
||||
#endif
|
||||
}
|
||||
36
tests/gui/attachments/TestTextAttachmentsPreviewWidget.h
Normal file
36
tests/gui/attachments/TestTextAttachmentsPreviewWidget.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2025 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <attachments/TextAttachmentsPreviewWidget.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QScopedPointer>
|
||||
|
||||
class TestTextAttachmentsPreviewWidget : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
|
||||
void testDetectMimeByFile();
|
||||
|
||||
private:
|
||||
QScopedPointer<TextAttachmentsPreviewWidget> m_widget{};
|
||||
};
|
||||
224
tests/gui/attachments/TestTextAttachmentsWidget.cpp
Normal file
224
tests/gui/attachments/TestTextAttachmentsWidget.cpp
Normal file
@@ -0,0 +1,224 @@
|
||||
#include "TestTextAttachmentsWidget.h"
|
||||
|
||||
#include <attachments/TextAttachmentsEditWidget.h>
|
||||
#include <attachments/TextAttachmentsPreviewWidget.h>
|
||||
#include <attachments/TextAttachmentsWidget.h>
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QSignalSpy>
|
||||
#include <QSplitter>
|
||||
#include <QTest>
|
||||
#include <QTestMouseEvent>
|
||||
#include <QTextEdit>
|
||||
#include <QTimer>
|
||||
|
||||
void TestTextAttachmentsWidget::initTestCase()
|
||||
{
|
||||
m_textWidget.reset(new TextAttachmentsWidget());
|
||||
}
|
||||
|
||||
void TestTextAttachmentsWidget::testInitTextWidget()
|
||||
{
|
||||
auto splitter = m_textWidget->findChild<QSplitter*>();
|
||||
QVERIFY2(splitter, "Splitter not found");
|
||||
|
||||
QCOMPARE(splitter->count(), 2);
|
||||
QVERIFY2(qobject_cast<TextAttachmentsEditWidget*>(splitter->widget(0)), "EditTextWidget not found");
|
||||
QVERIFY2(qobject_cast<TextAttachmentsPreviewWidget*>(splitter->widget(1)), "PreviewTextWidget not found");
|
||||
}
|
||||
|
||||
void TestTextAttachmentsWidget::testTextReadWriteWidget()
|
||||
{
|
||||
const attachments::Attachment Test{.name = "text.txt", .data = "Test"};
|
||||
m_textWidget->openAttachment(Test, attachments::OpenMode::ReadWrite);
|
||||
m_textWidget->show();
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
auto splitter = m_textWidget->findChild<QSplitter*>();
|
||||
QVERIFY2(splitter, "Splitter not found");
|
||||
auto sizes = splitter->sizes();
|
||||
QCOMPARE(sizes.size(), 2);
|
||||
QVERIFY2(sizes[0] > 0, "EditTextWidget width must be greater than zero");
|
||||
|
||||
QCOMPARE(sizes[1], 0);
|
||||
|
||||
auto widget = qobject_cast<TextAttachmentsEditWidget*>(splitter->widget(0));
|
||||
QVERIFY(widget);
|
||||
auto attachments = widget->getAttachment();
|
||||
|
||||
QCOMPARE(attachments.name, Test.name);
|
||||
QCOMPARE(attachments.data, Test.data);
|
||||
|
||||
auto previewWidget = qobject_cast<TextAttachmentsPreviewWidget*>(splitter->widget(1));
|
||||
QVERIFY(previewWidget);
|
||||
attachments = previewWidget->getAttachment();
|
||||
|
||||
QCOMPARE(attachments.name, Test.name);
|
||||
QCOMPARE(attachments.data, Test.data);
|
||||
}
|
||||
|
||||
void TestTextAttachmentsWidget::testTextReadWidget()
|
||||
{
|
||||
const attachments::Attachment Test{.name = "text.txt", .data = "Test"};
|
||||
m_textWidget->openAttachment(Test, attachments::OpenMode::ReadOnly);
|
||||
m_textWidget->show();
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
auto splitter = m_textWidget->findChild<QSplitter*>();
|
||||
QVERIFY2(splitter, "Splitter not found");
|
||||
auto sizes = splitter->sizes();
|
||||
QCOMPARE(sizes.size(), 2);
|
||||
QVERIFY2(sizes[1] > 0, "PreviewTextWidget width must be greater then zero");
|
||||
|
||||
QVERIFY(splitter->widget(0)->isHidden());
|
||||
|
||||
auto widget = qobject_cast<TextAttachmentsEditWidget*>(splitter->widget(0));
|
||||
QVERIFY(widget);
|
||||
auto attachments = widget->getAttachment();
|
||||
|
||||
QCOMPARE(attachments.name, Test.name);
|
||||
QCOMPARE(attachments.data, Test.data);
|
||||
|
||||
auto previewWidget = qobject_cast<TextAttachmentsPreviewWidget*>(splitter->widget(1));
|
||||
QVERIFY(previewWidget);
|
||||
attachments = previewWidget->getAttachment();
|
||||
|
||||
QCOMPARE(attachments.name, Test.name);
|
||||
QCOMPARE(attachments.data, Test.data);
|
||||
}
|
||||
|
||||
void TestTextAttachmentsWidget::testTextChanged()
|
||||
{
|
||||
const attachments::Attachment Test{.name = "text.txt", .data = "Test"};
|
||||
m_textWidget->openAttachment(Test, attachments::OpenMode::ReadWrite);
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
auto splitter = m_textWidget->findChild<QSplitter*>();
|
||||
QVERIFY2(splitter, "Splitter not found");
|
||||
QCOMPARE(splitter->sizes().size(), 2);
|
||||
|
||||
auto editWidget = qobject_cast<TextAttachmentsEditWidget*>(splitter->widget(0));
|
||||
QVERIFY2(editWidget, "Edit widget not found");
|
||||
|
||||
auto textEdit = editWidget->findChild<QTextEdit*>();
|
||||
QVERIFY(textEdit);
|
||||
|
||||
const QByteArray NewText = "New test text";
|
||||
textEdit->setText(NewText);
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
auto attachments = m_textWidget->getAttachment();
|
||||
|
||||
QCOMPARE(attachments.data, NewText);
|
||||
}
|
||||
|
||||
void TestTextAttachmentsWidget::testTextChangedInReadOnlyMode()
|
||||
{
|
||||
const attachments::Attachment Test{.name = "text.txt", .data = "Test"};
|
||||
m_textWidget->openAttachment(Test, attachments::OpenMode::ReadOnly);
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
auto splitter = m_textWidget->findChild<QSplitter*>();
|
||||
QVERIFY2(splitter, "Splitter not found");
|
||||
QCOMPARE(splitter->sizes().size(), 2);
|
||||
|
||||
auto editWidget = qobject_cast<TextAttachmentsEditWidget*>(splitter->widget(0));
|
||||
QVERIFY2(editWidget, "Edit widget not found");
|
||||
|
||||
auto textEdit = editWidget->findChild<QTextEdit*>();
|
||||
QVERIFY(textEdit);
|
||||
|
||||
const QByteArray NewText = "New test text";
|
||||
textEdit->setText(NewText);
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
auto attachments = m_textWidget->getAttachment();
|
||||
|
||||
QCOMPARE(attachments.data, Test.data);
|
||||
}
|
||||
|
||||
void TestTextAttachmentsWidget::testPreviewTextChanged()
|
||||
{
|
||||
const attachments::Attachment Test{.name = "text.txt", .data = "Test"};
|
||||
|
||||
auto previewTimer = m_textWidget->findChild<QTimer*>();
|
||||
QVERIFY2(previewTimer, "PreviewTimer not found!");
|
||||
|
||||
QSignalSpy timeout(previewTimer, &QTimer::timeout);
|
||||
|
||||
m_textWidget->openAttachment(Test, attachments::OpenMode::ReadWrite);
|
||||
|
||||
// Waiting for the first timeout
|
||||
while (timeout.count() < 1) {
|
||||
QCoreApplication::processEvents(QEventLoop::AllEvents, 50);
|
||||
}
|
||||
|
||||
auto splitter = m_textWidget->findChild<QSplitter*>();
|
||||
QVERIFY2(splitter, "Splitter not found");
|
||||
QCOMPARE(splitter->sizes().size(), 2);
|
||||
|
||||
splitter->setSizes({1, 1});
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
auto editWidget = qobject_cast<TextAttachmentsEditWidget*>(splitter->widget(0));
|
||||
QVERIFY2(editWidget, "Edit widget not found");
|
||||
|
||||
auto textEdit = editWidget->findChild<QTextEdit*>();
|
||||
QVERIFY(textEdit);
|
||||
|
||||
const QByteArray NewText = "New test text";
|
||||
textEdit->setText(NewText);
|
||||
|
||||
// Waiting for the second timeout
|
||||
while (timeout.count() < 2) {
|
||||
QCoreApplication::processEvents(QEventLoop::AllEvents, 50);
|
||||
}
|
||||
|
||||
auto previewWidget = qobject_cast<TextAttachmentsPreviewWidget*>(splitter->widget(1));
|
||||
auto attachments = previewWidget->getAttachment();
|
||||
|
||||
QCOMPARE(attachments.data, NewText);
|
||||
}
|
||||
|
||||
void TestTextAttachmentsWidget::testOpenPreviewButton()
|
||||
{
|
||||
const attachments::Attachment Test{.name = "text.txt", .data = "Test"};
|
||||
m_textWidget->openAttachment(Test, attachments::OpenMode::ReadWrite);
|
||||
m_textWidget->show();
|
||||
|
||||
QCoreApplication::processEvents();
|
||||
|
||||
auto splitter = m_textWidget->findChild<QSplitter*>();
|
||||
QVERIFY2(splitter, "Splitter not found");
|
||||
QCOMPARE(splitter->sizes().size(), 2);
|
||||
|
||||
auto editWidget = qobject_cast<TextAttachmentsEditWidget*>(splitter->widget(0));
|
||||
QVERIFY2(editWidget, "Edit widget not found");
|
||||
QVERIFY(editWidget->isVisible());
|
||||
|
||||
auto previewButton = editWidget->findChild<QPushButton*>("previewPushButton");
|
||||
|
||||
auto sizes = splitter->sizes();
|
||||
QVERIFY(sizes[0] > 0);
|
||||
QCOMPARE(sizes[1], 0);
|
||||
|
||||
QTest::mouseClick(previewButton, Qt::LeftButton);
|
||||
|
||||
sizes = splitter->sizes();
|
||||
QCOMPARE(sizes.size(), 2);
|
||||
QVERIFY(sizes[0] > 0);
|
||||
QVERIFY(sizes[1] > 0);
|
||||
|
||||
QTest::mouseClick(previewButton, Qt::LeftButton);
|
||||
sizes = splitter->sizes();
|
||||
QCOMPARE(sizes.size(), 2);
|
||||
QVERIFY(sizes[0] > 0);
|
||||
QCOMPARE(sizes[1], 0);
|
||||
}
|
||||
42
tests/gui/attachments/TestTextAttachmentsWidget.h
Normal file
42
tests/gui/attachments/TestTextAttachmentsWidget.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (C) 2025 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <attachments/TextAttachmentsWidget.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QScopedPointer>
|
||||
|
||||
class TestTextAttachmentsWidget : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
|
||||
void testInitTextWidget();
|
||||
void testTextReadWriteWidget();
|
||||
void testTextReadWidget();
|
||||
void testOpenPreviewButton();
|
||||
void testPreviewTextChanged();
|
||||
void testTextChanged();
|
||||
void testTextChangedInReadOnlyMode();
|
||||
|
||||
private:
|
||||
QScopedPointer<TextAttachmentsWidget> m_textWidget;
|
||||
};
|
||||
Reference in New Issue
Block a user