From 92b41ec300af19c9aab5a7c29597dce8e3cd5fd5 Mon Sep 17 00:00:00 2001 From: evgeny Date: Sun, 4 Oct 2026 15:38:10 +0200 Subject: [PATCH] Test wrapped message links and per-member invitation button hit targets --- tools/chatgui/CMakeLists.txt | 9 +++ tools/chatgui/tests/test_invite_links.cpp | 76 +++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 tools/chatgui/tests/test_invite_links.cpp diff --git a/tools/chatgui/CMakeLists.txt b/tools/chatgui/CMakeLists.txt index c4beeb9c..3a9b0b4d 100644 --- a/tools/chatgui/CMakeLists.txt +++ b/tools/chatgui/CMakeLists.txt @@ -232,6 +232,15 @@ target_link_libraries(test_emoji_playback PRIVATE ${QT_LIBS} ${QT_TEST_LIB} rlot add_test(NAME test_emoji_playback COMMAND test_emoji_playback) set_tests_properties(test_emoji_playback PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen" TIMEOUT 30) +add_executable(test_invite_links tests/test_invite_links.cpp src/messagedelegate.cpp src/chatview.cpp + src/accountdelegate.cpp src/flagpainter.cpp src/animtimer.cpp src/lottieicon.cpp src/emoji.cpp + src/voiceplayback.cpp src/sound_manager.cpp src/voice_audio_io.cpp src/audio_device.cpp + transport/miniaudio_impl.c transport/audio_diagnostics.cpp) +target_include_directories(test_invite_links PRIVATE ${UTUN_INCLUDE_DIRS}) +target_link_libraries(test_invite_links PRIVATE ${QT_LIBS} ${QT_TEST_LIB} rlottie::rlottie ZLIB::ZLIB utun_voice utun pthread ${CMAKE_DL_LIBS}) +add_test(NAME test_invite_links COMMAND test_invite_links) +set_tests_properties(test_invite_links PROPERTIES ENVIRONMENT "QT_QPA_PLATFORM=offscreen" TIMEOUT 30) + add_executable(test_config_updater tests/test_config_updater.cpp transport/config_updater.cpp diff --git a/tools/chatgui/tests/test_invite_links.cpp b/tools/chatgui/tests/test_invite_links.cpp new file mode 100644 index 00000000..89231357 --- /dev/null +++ b/tools/chatgui/tests/test_invite_links.cpp @@ -0,0 +1,76 @@ +/* Проверка реальных координат перенесённых ссылок и кнопки приглашения в строке участника. */ +#include "../src/messagedelegate.h" +#include "../src/chatview.h" +#include "../src/accountdelegate.h" +#include +#include +extern "C" { +#include "chat/invite_link.h" +} + +class InviteLinksTest : public QObject { + Q_OBJECT +private slots: + void wrappedLinksPreserveTheirCompleteText() { + struct InviteData invite = {}; + invite.channelId = 42; invite.join_key = 123; invite.pubkey[0] = 1; invite.addrCount = 8; + for (auto& address : invite.addrs) { address.family = 6; address.proto = INVITE_PROTO_TCP; address.port = 443; } + char buffer[1024]; + QVERIFY(invite_link_encode(&invite, nullptr, buffer, sizeof(buffer)) > 0); + const QString first = QString::fromUtf8(buffer); + invite.join_key++; + QVERIFY(invite_link_encode(&invite, nullptr, buffer, sizeof(buffer)) > 0); + const QString second = QString::fromUtf8(buffer); + const QString text = QStringLiteral("Приглашение\n(%1),\nВторая группа: %2.").arg(first, second); + QStandardItemModel model; + auto* message = new QStandardItem; + message->setData(text, MsgTextRole); message->setData(QStringLiteral("text/plain"), MsgContentTypeRole); + message->setData(true, MsgIsOutgoingRole); + model.appendRow(message); + ChatView view; MessageDelegate delegate; + view.setModel(&model); view.setItemDelegate(&delegate); view.resize(280, 900); + view.setInviteHitTest([&](const QModelIndex& index, const QPoint& point) { + QStyleOptionViewItem option; option.initFrom(&view); option.widget = &view; option.rect = view.visualRect(index); + return delegate.inviteLinkAt(option, index, point); + }); + view.show(); QTest::qWait(30); + const QModelIndex index = model.index(0, 0); + QStyleOptionViewItem option; option.initFrom(&view); option.widget = &view; option.rect = view.visualRect(index); + QPoint firstPoint, secondPoint; + QSet firstRows; + for (int y = 10; y < option.rect.height(); y += 8) { + for (int x = 10; x < view.viewport()->width(); x += 16) { + const QString link = delegate.inviteLinkAt(option, index, QPoint(x, y)); + if (link == first) { firstPoint = QPoint(x, y); firstRows.insert(y); } + else if (link == second) secondPoint = QPoint(x, y); + else QVERIFY(link.isEmpty()); + } + } + QVERIFY(firstRows.size() > 4); QVERIFY(!firstPoint.isNull()); QVERIFY(!secondPoint.isNull()); + QVERIFY(delegate.inviteLinkAt(option, index, QPoint(0, 0)).isEmpty()); + QSignalSpy clicked(&view, &ChatView::inviteLinkActivated); + QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, firstPoint); + QCOMPARE(clicked.count(), 1); QCOMPARE(clicked.last().first().toString(), first); + QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, secondPoint); + QCOMPARE(clicked.count(), 2); QCOMPARE(clicked.last().first().toString(), second); + } + + void memberPlusTargetsOtherMembersOnly() { + QStandardItemModel model; + auto* other = new QStandardItem("other"); other->setData(false, Qt::UserRole + 2); model.appendRow(other); + auto* self = new QStandardItem("self"); self->setData(true, Qt::UserRole + 2); model.appendRow(self); + QListView view; AccountDelegate delegate; + view.setModel(&model); view.setItemDelegate(&delegate); view.resize(240, 140); view.show(); + QTest::qWait(30); + QSignalSpy invites(&delegate, &AccountDelegate::inviteRequested); + const QRect row = view.visualRect(model.index(0, 0)); + QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, QPoint(row.right() - 17, row.center().y())); + QCOMPARE(invites.count(), 1); QCOMPARE(qvariant_cast(invites.first().first()).row(), 0); + const QRect own = view.visualRect(model.index(1, 0)); + QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, QPoint(own.right() - 17, own.center().y())); + QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, QPoint(60, row.center().y())); + QCOMPARE(invites.count(), 1); + } +}; +QTEST_MAIN(InviteLinksTest) +#include "test_invite_links.moc"