Compare commits

...

1 Commits

Author SHA1 Message Date
Joshua Goins
a190c45988 Proof-of-concept QML test
WIP, do not review yet
2025-12-21 12:00:11 -05:00
7 changed files with 183 additions and 1 deletions

View File

@@ -55,7 +55,7 @@ ecm_setup_version(${PROJECT_VERSION}
VERSION_HEADER ${CMAKE_CURRENT_BINARY_DIR}/neochat-version.h
)
find_package(Qt6 ${QT_MIN_VERSION} NO_MODULE COMPONENTS Core Quick Gui QuickControls2 Multimedia Svg TextToSpeech WebView)
find_package(Qt6 ${QT_MIN_VERSION} NO_MODULE COMPONENTS Core Quick Gui QuickControls2 Multimedia Svg TextToSpeech WebView QuickTest)
set_package_properties(Qt6 PROPERTIES
TYPE REQUIRED
PURPOSE "Basic application components"

View File

@@ -9,6 +9,21 @@ target_link_libraries(neochat_server PUBLIC Qt::HttpServer QuotientQt6)
add_definitions(-DDATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data" )
macro(add_qml_tests)
if (WIN32)
set(_extra_args -platform offscreen)
endif()
foreach(test ${ARGV})
add_test(NAME ${test}
COMMAND qmltest
${_extra_args}
-input ${test}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
endforeach()
endmacro()
ecm_add_test(
neochatroomtest.cpp
LINK_LIBRARIES neochat Qt::Test
@@ -104,3 +119,14 @@ ecm_add_test(
LINK_LIBRARIES neochat Qt::Test neochat_server
TEST_NAME roommanagertest
)
add_executable(qmltest qmltest.cpp)
target_link_libraries(qmltest
PRIVATE
Qt6::Qml
Qt6::QuickTest
neochat_server
neochat
neochatplugin)
add_qml_tests(tst_media.qml)

61
autotests/qmltest.cpp Normal file
View File

@@ -0,0 +1,61 @@
/*
* SPDX-FileCopyrightText: 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
*
* SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include <KLocalizedContext>
#include <KLocalizedString>
#include <QNetworkDiskCache>
#include <QQmlContext>
#include <QQmlEngine>
#include <QQmlNetworkAccessManagerFactory>
#include <QStandardPaths>
#include <Quotient/networkaccessmanager.h>
#include <quicktest.h>
#include "accountmanager.h"
#include "server.h"
using namespace Quotient;
using namespace Qt::StringLiterals;
class NetworkAccessManagerFactory : public QQmlNetworkAccessManagerFactory
{
QNetworkAccessManager *create(QObject *) override
{
auto nam = NetworkAccessManager::instance();
QObject::connect(nam, &QNetworkAccessManager::sslErrors, nam, [](auto reply, auto errors) {
reply->ignoreSslErrors(errors);
});
nam->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
return nam;
}
};
class Setup : public QObject
{
Q_OBJECT
public:
Setup() = default;
Server server;
public Q_SLOTS:
void qmlEngineAvailable(QQmlEngine *engine)
{
KLocalizedString::setApplicationDomain(QByteArrayLiteral("neochat"));
engine->setNetworkAccessManagerFactory(new NetworkAccessManagerFactory());
engine->rootContext()->setContextObject(new KLocalizedContext(engine));
server.start();
Q_UNUSED(new AccountManager(true));
}
};
QUICK_TEST_MAIN_WITH_SETUP(NeoChat, Setup)
#include "qmltest.moc"

View File

@@ -12,6 +12,8 @@
#include <QSslKey>
#include <QUuid>
#include <QBuffer>
#include <QImage>
#include <Quotient/networkaccessmanager.h>
using namespace Qt::Literals::StringLiterals;
@@ -116,6 +118,20 @@ void Server::start()
});
m_server.route(u"/_matrix/client/r0/sync"_s, QHttpServerRequest::Method::Get, this, &Server::sync);
m_server.route(u"/_matrix/client/v1/media/download/<arg>/<arg>"_s,
QHttpServerRequest::Method::Get,
[](const QString &serverName, const QString &mediaId, QHttpServerResponder &responder) {
qInfo() << serverName << mediaId;
QImage image(128, 128, QImage::Format::Format_RGB32);
image.fill(Qt::white);
QByteArray bytes;
QBuffer buffer(&bytes);
buffer.open(QIODevice::WriteOnly);
image.save(&buffer, "PNG");
responder.write(&buffer, "image/png", QHttpServerResponder::StatusCode::Ok);
});
QSslConfiguration config;
QFile key(QStringLiteral(DATA_DIR) + u"/localhost.key"_s);

73
autotests/tst_media.qml Normal file
View File

@@ -0,0 +1,73 @@
/*
* SPDX-FileCopyrightText: 2025 Joshua Goins <josh@redstrate.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
import QtQuick
import QtQuick.Controls as QQC2
import QtTest
import org.kde.kirigami as Kirigami
import org.kde.neochat.messagecontent
TestCase {
id: root
name: "ImageComponentTest"
width: 300
height: 300
// Base component to not re-initialize the same variables over and over
component BaseImageComponent: ImageComponent {
eventId: "dummyevent"
display: "dummytext"
fileTransferInfo: null
}
Component {
id: invalidMxcUrlComponent
BaseImageComponent {
componentAttributes: QtObject {
property bool isSticker: false
property bool animated: false
// Missing user_id, which is required in libQuotient
property string source: "mxc://localhost:1234/AAAAAAAAAAAAAAAAAAAAAAAA?room_id=!AjYwbldYDmSVfGrVHV:localhost&event_id=$vJfWLoXK02im0M3rlFWLosiHojrwWSknLb0JXveEE1o"
}
}
}
Component {
id: validMxcUrlComponent
BaseImageComponent {
componentAttributes: QtObject {
property bool isSticker: false
property bool animated: false
property string source: "mxc://localhost:1234/AAAAAAAAAAAAAAAAAAAAAAAA?user_id=@user:localhost:1234&room_id=!AjYwbldYDmSVfGrVHV:localhost&event_id=$vJfWLoXK02im0M3rlFWLosiHojrwWSknLb0JXveEE1o"
}
}
}
function test_invalid() {
wait(1000); // Wait for Quotient to grab the right capability
ignoreWarning("No connection specified, cannot convert mxc request");
const item = createTemporaryObject(invalidMxcUrlComponent, this);
verify(item);
compare(item._private.imageItem.status, Image.Loading);
// It should fail if we didn't specify the connection
tryCompare(item._private.imageItem, "status", Image.Error);
}
function test_basic() {
wait(1000); // Wait for Quotient to grab the right capability
const item = createTemporaryObject(validMxcUrlComponent, this);
verify(item);
compare(item._private.imageItem.status, Image.Loading); // initial load
tryCompare(item._private.imageItem, "status", Image.Error);
}
}

View File

@@ -43,6 +43,8 @@ Item {
*/
property var contentMaxHeight: undefined
readonly property alias _private: _private
implicitWidth: mediaSizeHelper.currentSize.width
implicitHeight: mediaSizeHelper.currentSize.height

View File

@@ -150,6 +150,10 @@ QSize MediaSizeHelper::currentSize() const
if (height > heightLimit()) {
return QSize(qRound(heightLimit() * aspectRatio()), qRound(heightLimit()));
}
// TODO: NO
if (qIsNaN(width) || qIsNaN(height)) {
return {};
}
return QSize(qRound(width), qRound(height));
} else {
qreal height = std::min(heightLimit(), resolvedMediaHeight());