|
|
|
|
@ -6,6 +6,7 @@
|
|
|
|
|
#include <QtTest> |
|
|
|
|
#include <QStandardItemModel> |
|
|
|
|
#include <QClipboard> |
|
|
|
|
#include <QPainter> |
|
|
|
|
extern "C" { |
|
|
|
|
#include "chat/invite_link.h" |
|
|
|
|
} |
|
|
|
|
@ -13,6 +14,75 @@ extern "C" {
|
|
|
|
|
class InviteLinksTest : public QObject { |
|
|
|
|
Q_OBJECT |
|
|
|
|
private slots: |
|
|
|
|
void reactionFooter_data() { |
|
|
|
|
QTest::addColumn<QString>("type"); QTest::addColumn<bool>("outgoing"); QTest::addColumn<int>("width"); |
|
|
|
|
for (const auto& type : {"text/plain", "audio/opus", "image/png", "video/mp4", "application/octet-stream", "poll"}) |
|
|
|
|
for (bool outgoing : {false, true}) for (int width : {280, 600}) { |
|
|
|
|
const QByteArray name = QString("%1-%2-%3").arg(type).arg(outgoing).arg(width).toUtf8(); |
|
|
|
|
QTest::newRow(name.constData()) << QString(type) << outgoing << width; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void reactionFooter() { |
|
|
|
|
QFETCH(QString, type); QFETCH(bool, outgoing); QFETCH(int, width); |
|
|
|
|
QStandardItemModel model; auto* message = new QStandardItem; |
|
|
|
|
message->setData(type == "text/plain" || type == "poll" ? QString("Example message") : QString::fromUtf8("😀"), MsgTextRole); |
|
|
|
|
message->setData(type, MsgContentTypeRole); message->setData(outgoing, MsgIsOutgoingRole); |
|
|
|
|
message->setData("1001", MsgChannelIdRole); message->setData(QString(64, 'a'), MsgMessageKeyRole); |
|
|
|
|
message->setData("Alice", MsgAuthorRole); message->setData("12:34", MsgTimeRole); |
|
|
|
|
message->setData("00:05", MsgVoiceDurationRole); message->setData("example.dat", MsgFileDisplayNameRole); |
|
|
|
|
message->setData(1, MsgIdRole); message->setData(1, MsgFileDownloadStateRole); |
|
|
|
|
message->setData(QVariantList{QVariantMap{{"id", 7}, {"emoji", QString::fromUtf8("👍")}, {"count", 2}, {"mine", true}}}, MsgReactionsRole); |
|
|
|
|
model.appendRow(message); |
|
|
|
|
ChatView view; MessageDelegate delegate; view.setModel(&model); view.setItemDelegate(&delegate); |
|
|
|
|
view.setSelectionMode(QAbstractItemView::NoSelection); view.resize(width, 480); view.show(); QTest::qWait(10); |
|
|
|
|
int downloads = 0; delegate.setDownloadCallback([&](const QModelIndex&) { downloads++; return true; }); |
|
|
|
|
const auto index = model.index(0, 0); QStyleOptionViewItem option; option.initFrom(&view); option.widget = &view; |
|
|
|
|
option.rect = view.visualRect(index); option.rect.setHeight(delegate.sizeHint(option, index).height()); |
|
|
|
|
QSignalSpy requested(&delegate, &MessageDelegate::reactionRequested); |
|
|
|
|
QMap<int, QPoint> points; QRect heartHits; |
|
|
|
|
/* Ищем области по фактическим событиям, не повторяя вычисления layout. */ |
|
|
|
|
for (int y = 1; y < option.rect.height(); y += 4) for (int x = 1; x < view.viewport()->width(); x += 4) { |
|
|
|
|
const QPoint point(x, y); |
|
|
|
|
QMouseEvent press(QEvent::MouseButtonPress, point, view.viewport()->mapToGlobal(point), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); |
|
|
|
|
QMouseEvent release(QEvent::MouseButtonRelease, point, view.viewport()->mapToGlobal(point), Qt::LeftButton, Qt::NoButton, Qt::NoModifier); |
|
|
|
|
requested.clear(); delegate.editorEvent(&press, &model, option, index); delegate.editorEvent(&release, &model, option, index); |
|
|
|
|
if (!requested.isEmpty()) { |
|
|
|
|
QCOMPARE(requested.count(), 1); const int emoji = requested.first().at(1).toInt(); |
|
|
|
|
if (!points.contains(emoji)) points[emoji] = point; |
|
|
|
|
if (emoji == -1) heartHits = heartHits.isValid() ? heartHits.united(QRect(point, QSize(1, 1))) : QRect(point, QSize(1, 1)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
QVERIFY(points.contains(-1) && points.contains(7)); downloads = 0; requested.clear(); |
|
|
|
|
QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, points[-1]); |
|
|
|
|
QCOMPARE(requested.count(), 1); QCOMPARE(requested.last().at(1).toInt(), -1); |
|
|
|
|
QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, points[7]); |
|
|
|
|
QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, points[7]); |
|
|
|
|
QCOMPARE(requested.count(), 3); QCOMPARE(requested.last().at(1).toInt(), 7); QCOMPARE(downloads, 0); |
|
|
|
|
QTest::mouseClick(view.viewport(), Qt::RightButton, Qt::NoModifier, points[-1]); QCOMPARE(requested.count(), 3); |
|
|
|
|
QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::NoModifier, points[-1]); |
|
|
|
|
QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::NoModifier, points[-1] + QPoint(20, 0)); |
|
|
|
|
QCOMPARE(requested.count(), 3); QCOMPARE(downloads, 0); |
|
|
|
|
|
|
|
|
|
const QColor background(70, 80, 90); QImage image(option.rect.size(), QImage::Format_ARGB32); image.fill(background); |
|
|
|
|
QPainter painter(&image); delegate.paint(&painter, option, index); painter.end(); |
|
|
|
|
QCOMPARE(image.pixelColor(heartHits.center()), background); /* внутри сердечко прозрачно */ |
|
|
|
|
bool whiteContour = false; |
|
|
|
|
for (int y = heartHits.top(); y <= heartHits.bottom(); y++) for (int x = heartHits.left(); x <= heartHits.right(); x++) |
|
|
|
|
if (image.pixelColor(x, y) == QColor(Qt::white)) whiteContour = true; |
|
|
|
|
QVERIFY(whiteContour); |
|
|
|
|
if (type == "text/plain" && !outgoing && width == 600) QVERIFY(image.save("/tmp/utun2-reaction-heart-preview.png")); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void largeEmojiHasNoReactionFooter() { |
|
|
|
|
QStandardItemModel model; auto* message = new QStandardItem; |
|
|
|
|
message->setData(QString::fromUtf8("😀"), MsgTextRole); message->setData("1001", MsgChannelIdRole); model.appendRow(message); |
|
|
|
|
ChatView view; MessageDelegate delegate; view.setModel(&model); view.setItemDelegate(&delegate); view.resize(600, 240); |
|
|
|
|
QStyleOptionViewItem option; option.initFrom(&view); option.widget = &view; |
|
|
|
|
const auto index = model.index(0, 0); const auto size = delegate.sizeHint(option, index); |
|
|
|
|
message->setData(QString(64, 'a'), MsgMessageKeyRole); QCOMPARE(delegate.sizeHint(option, index), size); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void messageLinkBoundaries() { |
|
|
|
|
QFile fixture(QFINDTESTDATA("message_links_cases.tsv")); |
|
|
|
|
QVERIFY(fixture.open(QIODevice::ReadOnly)); |
|
|
|
|
|