From 45257bf37f5281a7d3c898b301dad0df9b72d7f7 Mon Sep 17 00:00:00 2001 From: evgeny Date: Mon, 26 Oct 2026 22:51:10 +0100 Subject: [PATCH] Exercise desktop editing through the real menu composer core and quoted replies --- tools/chatgui/CMakeLists.txt | 8 ++ tools/chatgui/tests/test_group_edits.cpp | 98 ++++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 tools/chatgui/tests/test_group_edits.cpp diff --git a/tools/chatgui/CMakeLists.txt b/tools/chatgui/CMakeLists.txt index 8a25dd67..5ddc6faf 100644 --- a/tools/chatgui/CMakeLists.txt +++ b/tools/chatgui/CMakeLists.txt @@ -509,6 +509,14 @@ target_link_libraries(test_dm_deletion PRIVATE ${DM_DELETION_LIBRARIES} ${QT_TES add_test(NAME test_dm_deletion COMMAND test_dm_deletion) set_tests_properties(test_dm_deletion PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen" TIMEOUT 60) +add_executable(test_group_edits tests/test_group_edits.cpp ${DM_DELETION_SOURCES}) +add_dependencies(test_group_edits chatgui_translations) +target_include_directories(test_group_edits PRIVATE $) +target_compile_definitions(test_group_edits PRIVATE $) +target_link_libraries(test_group_edits PRIVATE ${DM_DELETION_LIBRARIES} ${QT_TEST_LIB}) +add_test(NAME test_group_edits COMMAND test_group_edits) +set_tests_properties(test_group_edits PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen" TIMEOUT 60) + # Ручной запуск с DISPLAY: проверяет применение desktop hints оконным менеджером, не меняя настройки рабочих столов. if(TARGET X11::X11) add_executable(test_message_notification_x11 tests/test_message_notification_x11.cpp) diff --git a/tools/chatgui/tests/test_group_edits.cpp b/tools/chatgui/tests/test_group_edits.cpp new file mode 100644 index 00000000..5dd7fad8 --- /dev/null +++ b/tools/chatgui/tests/test_group_edits.cpp @@ -0,0 +1,98 @@ +/* Настоящие MainWindow/uasync/SQLite: контекстное меню, composer и обновление существующей строки/цитаты. */ +#include "../src/mainwindow.h" +#include "../src/messagelist.h" +#include "../src/messagedelegate.h" +#include "../src/chatview.h" +#include "../src/inputbar.h" +#include "../db/db_manager.h" +#include "../transport/utun_node.h" +#include "../transport/gui_bridge.h" +#include +#include +#include +#include +#include +#include +#include +extern "C" { +#include "utun_instance.h" +#include "chat/chat_core.h" +#include "debug_config.h" +} + +static int newMessages; +static int inCore(std::function operation) { + auto* task = new std::packaged_task([operation] { return operation(gui_bridge_get_inst()); }); + auto result = task->get_future(); + gui_bridge_post_uasync_fn([](void* arg) { + std::unique_ptr> work(static_cast*>(arg)); (*work)(); + }, task); + if (result.wait_for(std::chrono::seconds(5)) != std::future_status::ready) qFatal("Edit test: core task timed out"); + return result.get(); +} + +class GroupEditTest : public QObject { + Q_OBJECT +private slots: + void editThroughMenuAndCore() { + debug_config_init(); gui_bridge_init(); QTemporaryDir directory; QVERIFY(directory.isValid()); + const auto configPath = directory.filePath("node.cfg"); QFile config(configPath); QVERIFY(config.open(QIODevice::WriteOnly)); + const QByteArray settings = "[global]\ntun_enabled=no\n[server:local]\naddr=127.0.0.1:0\ntype=private\n" + "[chatserver]\ngroup_autoconnect=0\nstorage_autoload=0\nlocal_invites_enabled=0\nlocal_broadcasts_enabled=0\n" + "[ntp]\nenabled=no\n"; + QCOMPARE(config.write(settings), settings.size()); config.close(); DbManager db; + MainWindow window(nullptr, &db, configPath, directory.filePath("db"), directory.filePath("edit.log"), "warn", "chat_sync=debug"); + window.show(); QVERIFY2(window.utunNode()->isRunning(), qPrintable(window.utunNode()->startupError())); + QTRY_VERIFY_WITH_TIMEOUT(db.isOpen(), 3000); + QCOMPARE(inCore([](UTUN_INSTANCE* inst) { chat_core_create_channel_auto(inst, "Editable"); return 0; }), 0); + QTRY_COMPARE(db.getChannels().size(), 1); const auto channel = db.getChannels().first().channelId; + QByteArray id; + QCOMPARE(inCore([&](UTUN_INSTANCE* inst) { + chat_msg_submit req{}; req.inst = inst; strcpy(req.content_type, "text/plain"); + snprintf(req.channel_id, sizeof(req.channel_id), "%s", qPrintable(channel)); + req.data = (uint8_t*)"Original"; req.data_len = 8; + if (chat_core_submit_message(inst, &req)) return -1; + id = QByteArray((const char*)req.message_id, 32); memcpy(req.reply_to, req.message_id, 32); + req.data = (uint8_t*)"Reply"; req.data_len = 5; return chat_core_submit_message(inst, &req); + }), 0); + auto* history = window.findChild(); QVERIFY(history); history->loadChannel(channel); + auto* view = history->findChild(); auto* input = history->findChild(); QVERIFY(view && input); + auto* editor = input->findChild(); QVERIFY(editor); + QTRY_COMPARE(view->model()->rowCount(), 2); + const QModelIndex original = view->model()->index(0, 0), reply = view->model()->index(1, 0); + QCOMPARE(original.data(MsgTextRole).toString(), QString("Original")); + editor->setPlainText("Unsent draft"); + auto openEditor = [&] { + QTimer::singleShot(0, [] { + auto* menu = qobject_cast(QApplication::activePopupWidget()); + if (!menu) qFatal("Edit menu missing"); + for (auto* action : menu->actions()) if (action->text() == "Edit message") { action->trigger(); menu->close(); return; } + qFatal("Edit action missing"); + }); + view->scrollTo(original); QCoreApplication::processEvents(); + const auto pos = view->visualRect(original).center(); + QMetaObject::invokeMethod(view, "customContextMenuRequested", Qt::DirectConnection, Q_ARG(QPoint, pos)); + }; + openEditor(); QCOMPARE(editor->toPlainText(), QString("Original")); + gui_bridge_set_msg_received_cb([](const char*, int, uint64_t, int) { ++newMessages; }); newMessages = 0; + editor->setPlainText(QString::fromUtf8("Изменено 👍")); QTest::keyClick(editor, Qt::Key_Return); + QTRY_COMPARE_WITH_TIMEOUT(original.data(MsgVersionRole).toULongLong(), quint64(2), 3000); + QCOMPARE(view->model()->rowCount(), 2); QCOMPARE(original.data(MsgTextRole).toString(), QString::fromUtf8("Изменено 👍")); + QVERIFY(original.data(MsgTimeRole).toString().contains("edited ")); + QCOMPARE(reply.data(MsgQuoteTextRole).toString(), QString::fromUtf8("Изменено 👍")); + QTRY_COMPARE(editor->toPlainText(), QString("Unsent draft")); QCOMPARE(newMessages, 0); + openEditor(); editor->setPlainText("Stale editor draft"); + QCOMPARE(inCore([&](UTUN_INSTANCE* inst) { + return chat_core_edit_message(inst, qPrintable(channel), (const uint8_t*)id.constData(), 2, "Remote edit", 11); + }), 0); + QTRY_COMPARE(original.data(MsgVersionRole).toULongLong(), quint64(3)); + QCOMPARE(editor->toPlainText(), QString("Stale editor draft")); + QSignalSpy failures(input, &InputBar::attachmentRejected); QTest::keyClick(editor, Qt::Key_Return); + QTRY_COMPARE(failures.size(), 1); QCOMPARE(editor->toPlainText(), QString("Stale editor draft")); + QCOMPARE(original.data(MsgTextRole).toString(), QString("Remote edit")); QCOMPARE(newMessages, 0); + QTest::keyClick(editor, Qt::Key_Escape); QCOMPARE(editor->toPlainText(), QString("Unsent draft")); + gui_bridge_set_msg_received_cb(nullptr); + } +}; +QTEST_MAIN(GroupEditTest) +#include "test_group_edits.moc"