File indexing completed on 2024-05-12 12:54:38

0001 // SPDX-FileCopyrightText: 2023 James Graham <james.h.graham@protonmail.com>
0002 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0003 
0004 #include <QObject>
0005 #include <QTest>
0006 
0007 #include "texthandler.h"
0008 
0009 #include <Quotient/quotient_common.h>
0010 #include <Quotient/syncdata.h>
0011 #include <qnamespace.h>
0012 
0013 using namespace Quotient;
0014 
0015 class TestRoom : public NeoChatRoom
0016 {
0017 public:
0018     using NeoChatRoom::NeoChatRoom;
0019 
0020     void update(SyncRoomData &&data, bool fromCache = false)
0021     {
0022         Room::updateData(std::move(data), fromCache);
0023     }
0024 };
0025 
0026 class TextHandlerTest : public QObject
0027 {
0028     Q_OBJECT
0029 
0030 private:
0031     Connection *connection = nullptr;
0032     TestRoom *room = nullptr;
0033 
0034 private Q_SLOTS:
0035     void initTestCase();
0036 
0037     void allowedAttributes();
0038     void stripDisallowedTags();
0039     void stripDisallowedAttributes();
0040     void emptyCodeTags();
0041 
0042     void sendSimpleStringCase();
0043     void sendSingleParaMarkup();
0044     void sendMultipleSectionMarkup();
0045     void sendBadLinks();
0046     void sendEscapeCode();
0047     void sendCodeClass();
0048 
0049     void receiveStripReply();
0050     void receivePlainTextIn();
0051 
0052     void receiveRichInPlainOut_data();
0053     void receiveRichInPlainOut();
0054     void receivePlainStripHtml();
0055     void receivePlainStripMarkup();
0056     void receiveStripNewlines();
0057 
0058     void receiveRichUserPill();
0059     void receiveRichStrikethrough();
0060     void receiveRichtextIn();
0061     void receiveRichMxcUrl();
0062     void receiveRichPlainUrl();
0063     void receiveRichEmote();
0064     void receiveRichEdited_data();
0065     void receiveRichEdited();
0066     void receiveLineSeparator();
0067 
0068     void linkPreviewsMatch_data();
0069     void linkPreviewsMatch();
0070     void linkPreviewsReject_data();
0071     void linkPreviewsReject();
0072 };
0073 
0074 void TextHandlerTest::initTestCase()
0075 {
0076     connection = Connection::makeMockConnection(QStringLiteral("@bob:kde.org"));
0077     room = new TestRoom(connection, QStringLiteral("#myroom:kde.org"), JoinState::Join);
0078 
0079     const auto json = QJsonDocument::fromJson(R"EVENT({
0080   "account_data": {
0081     "events": [
0082       {
0083         "content": {
0084           "tags": {
0085             "u.work": {
0086               "order": 0.9
0087             }
0088           }
0089         },
0090         "type": "m.tag"
0091       },
0092       {
0093         "content": {
0094           "custom_config_key": "custom_config_value"
0095         },
0096         "type": "org.example.custom.room.config"
0097       }
0098     ]
0099   },
0100   "ephemeral": {
0101     "events": [
0102       {
0103         "content": {
0104           "user_ids": [
0105             "@alice:matrix.org",
0106             "@bob:example.com"
0107           ]
0108         },
0109         "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
0110         "type": "m.typing"
0111       }
0112     ]
0113   },
0114   "state": {
0115     "events": [
0116       {
0117         "content": {
0118           "avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
0119           "displayname": "Alice Margatroid",
0120           "membership": "join",
0121           "reason": "Looking for support"
0122         },
0123         "event_id": "$143273582443PhrSn:example.org",
0124         "origin_server_ts": 1432735824653,
0125         "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
0126         "sender": "@example:example.org",
0127         "state_key": "@alice:example.org",
0128         "type": "m.room.member",
0129         "unsigned": {
0130           "age": 1234
0131         }
0132       }
0133     ]
0134   },
0135   "summary": {
0136     "m.heroes": [
0137       "@alice:example.com",
0138       "@bob:example.com"
0139     ],
0140     "m.invited_member_count": 0,
0141     "m.joined_member_count": 2
0142   },
0143   "timeline": {
0144     "events": [
0145       {
0146         "content": {
0147           "body": "This is an **example** text message",
0148           "format": "org.matrix.custom.html",
0149           "formatted_body": "<b>This is an example text message</b>",
0150           "msgtype": "m.text"
0151         },
0152         "event_id": "$143273582443PhrSn:example.org",
0153         "origin_server_ts": 1432735824654,
0154         "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
0155         "sender": "@example:example.org",
0156         "type": "m.room.message",
0157         "unsigned": {
0158           "age": 1232
0159         }
0160       },
0161       {
0162         "content": {
0163           "body": "/me This is an emote.",
0164           "format": "org.matrix.custom.html",
0165           "formatted_body": "This is an emote.",
0166           "msgtype": "m.emote"
0167         },
0168         "event_id": "$153273582443PhrSn:example.org",
0169         "origin_server_ts": 1532735824654,
0170         "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
0171         "sender": "@example:example.org",
0172         "type": "m.room.message",
0173         "unsigned": {
0174           "age": 1231
0175         }
0176       },
0177       {
0178         "content": {
0179             "body": "tested",
0180             "msgtype": "m.text"
0181         },
0182         "event_id": "$zrCiBxBnqqTn0Z5FY78qSZAszno_w8nJJXzfBULG-3E",
0183         "origin_server_ts": 1680948575928,
0184         "room_id": "!jEsUZKDJdhlrceRyVU:example.org",
0185         "sender": "@example:example.org",
0186         "type": "m.room.message",
0187         "unsigned": {
0188             "age": 1747776,
0189             "m.relations": {
0190                 "m.replace": {
0191                     "event_id": "$UX0PlpyI7vYO32iHMuuYEP7ECMh4sX3XLGiB2SwM4mQ",
0192                     "origin_server_ts": 1680948580992,
0193                     "sender": "@example:example.org"
0194                 }
0195             }
0196         }
0197       }
0198     ],
0199     "limited": true,
0200     "prev_batch": "t34-23535_0_0"
0201   }
0202 })EVENT");
0203     SyncRoomData roomData(QStringLiteral("@bob:kde.org"), JoinState::Join, json.object());
0204     room->update(std::move(roomData));
0205 }
0206 
0207 void TextHandlerTest::allowedAttributes()
0208 {
0209     const QString testInputString1 = QStringLiteral("<p><span data-mx-spoiler><font color=#FFFFFF>Test</font><span></p>");
0210     const QString testOutputString1 = QStringLiteral("<p><span data-mx-spoiler><font color=#FFFFFF>Test</font><span></p>");
0211     // Handle urls where the href has either single (') or double (") quotes.
0212     const QString testInputString2 = QStringLiteral("<p><a href=\"https://kde.org\">link</a><a href='https://kde.org'>link</a></p>");
0213     const QString testOutputString2 = QStringLiteral("<p><a href=\"https://kde.org\">link</a><a href='https://kde.org'>link</a></p>");
0214 
0215     TextHandler testTextHandler;
0216     testTextHandler.setData(testInputString1);
0217 
0218     QCOMPARE(testTextHandler.handleSendText(), testOutputString1);
0219     QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString1);
0220 
0221     testTextHandler.setData(testInputString2);
0222     QCOMPARE(testTextHandler.handleSendText(), testOutputString2);
0223     QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString2);
0224 }
0225 
0226 void TextHandlerTest::stripDisallowedTags()
0227 {
0228     const QString testInputString = QStringLiteral("<p>Allowed</p> <span>Allowed</span> <body>Disallowed</body>");
0229     const QString testOutputString = QStringLiteral("<p>Allowed</p> <span>Allowed</span> Disallowed");
0230 
0231     TextHandler testTextHandler;
0232     testTextHandler.setData(testInputString);
0233 
0234     QCOMPARE(testTextHandler.handleSendText(), testOutputString);
0235     QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
0236 }
0237 
0238 void TextHandlerTest::stripDisallowedAttributes()
0239 {
0240     const QString testInputString = QStringLiteral("<p style=\"font-size:50px;\" color=#FFFFFF>Test</p>");
0241     const QString testOutputString = QStringLiteral("<p>Test</p>");
0242 
0243     TextHandler testTextHandler;
0244     testTextHandler.setData(testInputString);
0245 
0246     QCOMPARE(testTextHandler.handleSendText(), testOutputString);
0247     QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
0248 }
0249 
0250 /**
0251  * Make sure that empty code tags are handled.
0252  * (this was a bug during development hence the test)
0253  */
0254 void TextHandlerTest::emptyCodeTags()
0255 {
0256     const QString testInputString = QStringLiteral("<pre><code></code></pre>");
0257     const QString testOutputString = QStringLiteral("<pre><code></code></pre>");
0258 
0259     TextHandler testTextHandler;
0260     testTextHandler.setData(testInputString);
0261 
0262     QCOMPARE(testTextHandler.handleSendText(), testOutputString);
0263     QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
0264 }
0265 
0266 void TextHandlerTest::sendSimpleStringCase()
0267 {
0268     const QString testInputString = QStringLiteral("This data should just be put in a paragraph.");
0269     const QString testOutputString = QStringLiteral("<p>This data should just be put in a paragraph.</p>");
0270 
0271     TextHandler testTextHandler;
0272     testTextHandler.setData(testInputString);
0273 
0274     QCOMPARE(testTextHandler.handleSendText(), testOutputString);
0275 }
0276 
0277 void TextHandlerTest::sendSingleParaMarkup()
0278 {
0279     const QString testInputString = QStringLiteral(
0280         "Text para with **bold**, *italic*, [link](https://kde.org), ![image](mxc://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e), `inline code`.");
0281     const QString testOutputString = QStringLiteral(
0282         "<p>Text para with <strong>bold</strong>, <em>italic</em>, <a href=\"https://kde.org\">link</a>, <img "
0283         "src=\"mxc://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e\" alt=\"image\">, <code>inline code</code>.</p>");
0284 
0285     TextHandler testTextHandler;
0286     testTextHandler.setData(testInputString);
0287 
0288     QCOMPARE(testTextHandler.handleSendText(), testOutputString);
0289 }
0290 
0291 void TextHandlerTest::sendMultipleSectionMarkup()
0292 {
0293     const QString testInputString =
0294         QStringLiteral("Text para\n> blockquote\n* List 1\n* List 2\n1. one\n2. two\n# Heading 1\n## Heading 2\nhorizontal rule\n\n---\n```\ncodeblock\n```");
0295     const QString testOutputString = QStringLiteral(
0296         "<p>Text para</p>\n<blockquote>\n<p>blockquote</p>\n</blockquote>\n<ul>\n<li>List 1</li>\n<li>List "
0297         "2</li>\n</ul>\n<ol>\n<li>one</li>\n<li>two</li>\n</ol>\n<h1>Heading 1</h1>\n<h2>Heading 2</h2>\n<p>horizontal "
0298         "rule</p>\n<hr>\n<pre><code>codeblock\n</code></pre>");
0299 
0300     TextHandler testTextHandler;
0301     testTextHandler.setData(testInputString);
0302 
0303     QCOMPARE(testTextHandler.handleSendText(), testOutputString);
0304 }
0305 
0306 void TextHandlerTest::sendBadLinks()
0307 {
0308     const QString testInputString = QStringLiteral("[link](kde.org), ![image](https://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e)");
0309     const QString testOutputString = QStringLiteral("<p><a>link</a>, <img alt=\"image\"></p>");
0310 
0311     TextHandler testTextHandler;
0312     testTextHandler.setData(testInputString);
0313 
0314     QCOMPARE(testTextHandler.handleSendText(), testOutputString);
0315 }
0316 
0317 /**
0318  * All text between code tags is treated as plain so it should get escaped.
0319  */
0320 void TextHandlerTest::sendEscapeCode()
0321 {
0322     const QString testInputString = QStringLiteral("```\n<p>Test <span style=\"font-size:50px;\">some</span> code</p>\n```");
0323     const QString testOutputString =
0324         QStringLiteral("<pre><code>&lt;p&gt;Test &lt;span style=&quot;font-size:50px;&quot;&gt;some&lt;/span&gt; code&lt;/p&gt;\n</code></pre>");
0325 
0326     TextHandler testTextHandler;
0327     testTextHandler.setData(testInputString);
0328 
0329     QCOMPARE(testTextHandler.handleSendText(), testOutputString);
0330 }
0331 
0332 void TextHandlerTest::sendCodeClass()
0333 {
0334     const QString testInputString = QStringLiteral("```html\nsome code\n```\n<pre><code class=\"code-underline\">some more code</code></pre>");
0335     const QString testOutputString = QStringLiteral("<pre><code class=\"language-html\">some code\n</code></pre>\n<pre><code>some more code</code></pre>");
0336 
0337     TextHandler testTextHandler;
0338     testTextHandler.setData(testInputString);
0339 
0340     QCOMPARE(testTextHandler.handleSendText(), testOutputString);
0341 }
0342 
0343 void TextHandlerTest::receiveStripReply()
0344 {
0345     const QString testInputString = QStringLiteral(
0346         "<mx-reply><blockquote><a href=\"https://matrix.to/#/!somewhere:example.org/$event:example.org\">In reply to</a><a "
0347         "href=\"https://matrix.to/#/@alice:example.org\">@alice:example.org</a><br />Message replied to.</blockquote></mx-reply>Reply message.");
0348     const QString testOutputString = QStringLiteral("Reply message.");
0349 
0350     TextHandler testTextHandler;
0351     testTextHandler.setData(testInputString);
0352 
0353     QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
0354     QCOMPARE(testTextHandler.handleRecievePlainText(), testOutputString);
0355 }
0356 
0357 void TextHandlerTest::receiveRichInPlainOut_data()
0358 {
0359     QTest::addColumn<QString>("testInputString");
0360     QTest::addColumn<QString>("testOutputString");
0361 
0362     QTest::newRow("ampersand") << QStringLiteral("a &amp; b") << QStringLiteral("a & b");
0363     QTest::newRow("quote") << QStringLiteral("&quot;a and b&quot;") << QStringLiteral("\"a and b\"");
0364 }
0365 
0366 void TextHandlerTest::receiveRichInPlainOut()
0367 {
0368     QFETCH(QString, testInputString);
0369     QFETCH(QString, testOutputString);
0370 
0371     TextHandler testTextHandler;
0372     testTextHandler.setData(testInputString);
0373 
0374     QCOMPARE(testTextHandler.handleRecievePlainText(Qt::RichText), testOutputString);
0375 }
0376 
0377 void TextHandlerTest::receivePlainTextIn()
0378 {
0379     const QString testInputString = QStringLiteral("<plain text in tag bracket>\nTest link https://kde.org.");
0380     const QString testOutputStringRich = QStringLiteral("&lt;plain text in tag bracket&gt;<br>Test link <a href=\"https://kde.org\">https://kde.org</a>.");
0381     QString testOutputStringPlain = QStringLiteral("<plain text in tag bracket>\nTest link https://kde.org.");
0382 
0383     // Make sure quotes are maintained in a plain string.
0384     const QString testInputString2 = QStringLiteral("last line is \"Time to switch to a new topic.\"");
0385     const QString testOutputString2 = QStringLiteral("last line is \"Time to switch to a new topic.\"");
0386 
0387     TextHandler testTextHandler;
0388     testTextHandler.setData(testInputString);
0389 
0390     QCOMPARE(testTextHandler.handleRecieveRichText(Qt::PlainText), testOutputStringRich);
0391     QCOMPARE(testTextHandler.handleRecievePlainText(), testOutputStringPlain);
0392 
0393     testTextHandler.setData(testInputString2);
0394     QCOMPARE(testTextHandler.handleRecieveRichText(Qt::PlainText), testOutputString2);
0395     QCOMPARE(testTextHandler.handleRecievePlainText(), testOutputString2);
0396 }
0397 
0398 void TextHandlerTest::receiveStripNewlines()
0399 {
0400     const QString testInputStringPlain = QStringLiteral("Test\nmany\nnew\nlines.");
0401     const QString testInputStringRich = QStringLiteral("Test<br>many<br />new<br>lines.");
0402     const QString testOutputString = QStringLiteral("Test many new lines.");
0403 
0404     const QString testInputStringPlain2 = QStringLiteral("* List\n* Items");
0405     const QString testOutputString2 = QStringLiteral("List Items");
0406 
0407     TextHandler testTextHandler;
0408     testTextHandler.setData(testInputStringPlain);
0409 
0410     QCOMPARE(testTextHandler.handleRecievePlainText(Qt::PlainText, true), testOutputString);
0411     QCOMPARE(testTextHandler.handleRecieveRichText(Qt::PlainText, nullptr, nullptr, true), testOutputString);
0412 
0413     testTextHandler.setData(testInputStringRich);
0414     QCOMPARE(testTextHandler.handleRecievePlainText(Qt::RichText, true), testOutputString);
0415     QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText, nullptr, nullptr, true), testOutputString);
0416 
0417     testTextHandler.setData(testInputStringPlain2);
0418     QCOMPARE(testTextHandler.handleRecievePlainText(Qt::RichText, true), testOutputString2);
0419 }
0420 
0421 /**
0422  * For a plain text output of a received string all html is stripped except for
0423  * code which is unescaped if it's html.
0424  */
0425 void TextHandlerTest::receivePlainStripHtml()
0426 {
0427     const QString testInputString = QStringLiteral("<p>Test</p> <pre><code>Some code <strong>with tags</strong></code></pre>");
0428     const QString testOutputString = QStringLiteral("Test Some code <strong>with tags</strong>");
0429 
0430     TextHandler testTextHandler;
0431     testTextHandler.setData(testInputString);
0432 
0433     QCOMPARE(testTextHandler.handleRecievePlainText(Qt::RichText), testOutputString);
0434 }
0435 
0436 void TextHandlerTest::receivePlainStripMarkup()
0437 {
0438     const QString testInputString = QStringLiteral("**bold** `<p>inline code</p>` *italic*");
0439     const QString testOutputString = QStringLiteral("bold <p>inline code</p> italic");
0440 
0441     TextHandler testTextHandler;
0442     testTextHandler.setData(testInputString);
0443 
0444     QCOMPARE(testTextHandler.handleRecievePlainText(), testOutputString);
0445 }
0446 
0447 void TextHandlerTest::receiveRichUserPill()
0448 {
0449     const QString testInputString = QStringLiteral("<p><a href=\"https://matrix.to/#/@alice:example.org\">@alice:example.org</a></p>");
0450     const QString testOutputString = QStringLiteral("<p><b><a href=\"https://matrix.to/#/@alice:example.org\">@alice:example.org</a></b></p>");
0451 
0452     TextHandler testTextHandler;
0453     testTextHandler.setData(testInputString);
0454 
0455     QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
0456 }
0457 
0458 void TextHandlerTest::receiveRichStrikethrough()
0459 {
0460     const QString testInputString = QStringLiteral("<p><del>Test</del></p>");
0461     const QString testOutputString = QStringLiteral("<p><s>Test</s></p>");
0462 
0463     TextHandler testTextHandler;
0464     testTextHandler.setData(testInputString);
0465 
0466     QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
0467 }
0468 
0469 void TextHandlerTest::receiveRichtextIn()
0470 {
0471     const QString testInputString = QStringLiteral("<p>Test</p> <pre><code>Some code <strong>with tags</strong></code></pre>");
0472     const QString testOutputString = QStringLiteral("<p>Test</p> <pre><code>Some code &lt;strong&gt;with tags&lt;/strong&gt;</code></pre>");
0473 
0474     TextHandler testTextHandler;
0475     testTextHandler.setData(testInputString);
0476 
0477     QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString);
0478 }
0479 
0480 void TextHandlerTest::receiveRichMxcUrl()
0481 {
0482     const QString testInputString = QStringLiteral(
0483         "<img src=\"mxc://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e\" alt=\"image\"><img src=\"mxc://kde.org/34c3464b3a1bd7f55af2d559e07d2c773c430e73\" "
0484         "alt=\"image\">");
0485     const QString testOutputString = QStringLiteral(
0486         "<img "
0487         "src=\"mxc://kde.org/aebd3ffd40503e1ef0525bf8f0d60282fec6183e?user_id=@bob:kde.org&room_id=%23myroom:kde.org&event_id=$143273582443PhrSn:example.org\" "
0488         "alt=\"image\"><img "
0489         "src=\"mxc://kde.org/34c3464b3a1bd7f55af2d559e07d2c773c430e73?user_id=@bob:kde.org&room_id=%23myroom:kde.org&event_id=$143273582443PhrSn:example.org\" "
0490         "alt=\"image\">");
0491 
0492     TextHandler testTextHandler;
0493     testTextHandler.setData(testInputString);
0494 
0495     QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText, room, room->messageEvents().at(0).get()), testOutputString);
0496 }
0497 
0498 /**
0499  * For when your rich input string has a plain text url left in.
0500  *
0501  * This test is to show that a url that is already rich will be left alone but a
0502  * plain one will be linkified.
0503  */
0504 void TextHandlerTest::receiveRichPlainUrl()
0505 {
0506     // This is an actual link that caused trouble which is why it's so long. Keeping
0507     // so we can confirm consistent behaviour for complex urls.
0508     const QString testInputStringLink1 = QStringLiteral(
0509         "https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im "
0510         "<a "
0511         "href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/"
0512         "$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im\">Link already rich</a>");
0513     const QString testOutputStringLink1 = QStringLiteral(
0514         "<a "
0515         "href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/"
0516         "$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im\">https://matrix.to/#/"
0517         "!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im</a> <a "
0518         "href=\"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/"
0519         "$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&amp;via=matrix.org&amp;via=fedora.im\">Link already rich</a>");
0520 
0521     // Another real case. The linkification wasn't handling it when a single link
0522     // contains what looks like and email. It was been broken into 3 but needs to
0523     // be just single link.
0524     const QString testInputStringLink2 = QStringLiteral("https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/");
0525     const QString testOutputStringLink2 = QStringLiteral(
0526         "<a "
0527         "href=\"https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/\">https://lore.kernel.org/lkml/"
0528         "CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/</a>");
0529 
0530     QString testInputStringEmail = QStringLiteral(R"(email@example.com <a href="mailto:email@example.com">Link already rich</a>)");
0531     QString testOutputStringEmail =
0532         QStringLiteral(R"(<a href="mailto:email@example.com">email@example.com</a> <a href="mailto:email@example.com">Link already rich</a>)");
0533 
0534     QString testInputStringMxId = QStringLiteral("@user:kde.org <a href=\"https://matrix.to/#/@user:kde.org\">Link already rich</a>");
0535     QString testOutputStringMxId = QStringLiteral(
0536         "<b><a href=\"https://matrix.to/#/@user:kde.org\">@user:kde.org</a></b> <b><a href=\"https://matrix.to/#/@user:kde.org\">Link already rich</a></b>");
0537 
0538     TextHandler testTextHandler;
0539     testTextHandler.setData(testInputStringLink1);
0540 
0541     QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringLink1);
0542 
0543     testTextHandler.setData(testInputStringLink2);
0544     QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringLink2);
0545 
0546     testTextHandler.setData(testInputStringEmail);
0547     QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringEmail);
0548 
0549     testTextHandler.setData(testInputStringMxId);
0550     QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText), testOutputStringMxId);
0551 }
0552 
0553 // Test that user pill is add to an emote message.
0554 // N.B. The second message in the test timeline is marked as an emote.
0555 void TextHandlerTest::receiveRichEmote()
0556 {
0557     auto event = room->messageEvents().at(1).get();
0558     auto author = static_cast<NeoChatUser *>(room->user(event->senderId()));
0559     const QString testInputString = QStringLiteral("This is an emote.");
0560     const QString testOutputString = QStringLiteral("* <a href=\"https://matrix.to/#/@example:example.org\" style=\"color:") + author->color().name()
0561         + QStringLiteral("\">@example:example.org</a> This is an emote.");
0562 
0563     TextHandler testTextHandler;
0564     testTextHandler.setData(testInputString);
0565 
0566     QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText, room, event), testOutputString);
0567 }
0568 
0569 void TextHandlerTest::receiveRichEdited_data()
0570 {
0571     QTest::addColumn<QString>("testInputString");
0572     QTest::addColumn<QString>("testOutputString");
0573 
0574     QTest::newRow("basic") << QStringLiteral("Edited") << QStringLiteral("Edited <span style=\"color:#000000\">(edited)</span>");
0575     QTest::newRow("multiple paragraphs") << QStringLiteral("<p>Edited</p>\n<p>Edited</p>")
0576                                          << QStringLiteral("<p>Edited</p>\n<p>Edited <span style=\"color:#000000\">(edited)</span></p>");
0577     QTest::newRow("blockquote") << QStringLiteral("<blockquote>Edited</blockquote>")
0578                                 << QStringLiteral("<blockquote>Edited</blockquote><p> <span style=\"color:#000000\">(edited)</span></p>");
0579 }
0580 
0581 void TextHandlerTest::receiveRichEdited()
0582 {
0583     QFETCH(QString, testInputString);
0584     QFETCH(QString, testOutputString);
0585 
0586     TextHandler testTextHandler;
0587     testTextHandler.setData(testInputString);
0588 
0589     QCOMPARE(testTextHandler.handleRecieveRichText(Qt::RichText, room, room->messageEvents().at(2).get()), testOutputString);
0590 }
0591 
0592 void TextHandlerTest::receiveLineSeparator()
0593 {
0594     auto text = QStringLiteral("foo\u2028bar");
0595     TextHandler textHandler;
0596     textHandler.setData(text);
0597     QCOMPARE(textHandler.handleRecievePlainText(Qt::PlainText, true), QStringLiteral("foo bar"));
0598 }
0599 
0600 void TextHandlerTest::linkPreviewsMatch_data()
0601 {
0602     QTest::addColumn<QString>("testInputString");
0603     QTest::addColumn<QList<QUrl>>("testOutputLinks");
0604 
0605     QTest::newRow("plainHttps") << QStringLiteral("https://kde.org") << QList<QUrl>({QUrl("https://kde.org")});
0606     QTest::newRow("richHttps") << QStringLiteral("<a href=\"https://kde.org\">Rich Link</a>") << QList<QUrl>({QUrl("https://kde.org")});
0607     QTest::newRow("plainWww") << QStringLiteral("www.example.org") << QList<QUrl>({QUrl("www.example.org")});
0608     QTest::newRow("multipleHttps") << QStringLiteral("https://kde.org www.example.org")
0609                                    << QList<QUrl>({
0610                                           QUrl("https://kde.org"),
0611                                           QUrl("www.example.org"),
0612                                       });
0613 }
0614 
0615 void TextHandlerTest::linkPreviewsMatch()
0616 {
0617     QFETCH(QString, testInputString);
0618     QFETCH(QList<QUrl>, testOutputLinks);
0619 
0620     TextHandler testTextHandler;
0621     testTextHandler.setData(testInputString);
0622 
0623     QCOMPARE(testTextHandler.getLinkPreviews(), testOutputLinks);
0624 }
0625 
0626 void TextHandlerTest::linkPreviewsReject_data()
0627 {
0628     QTest::addColumn<QString>("testInputString");
0629     QTest::addColumn<QList<QUrl>>("testOutputLinks");
0630 
0631     QTest::newRow("mxc") << QStringLiteral("mxc://example.org/SEsfnsuifSDFSSEF") << QList<QUrl>();
0632     QTest::newRow("matrixTo") << QStringLiteral("https://matrix.to/#/@alice:example.org") << QList<QUrl>();
0633     QTest::newRow("noSpace") << QStringLiteral("testhttps://kde.org") << QList<QUrl>();
0634 }
0635 
0636 void TextHandlerTest::linkPreviewsReject()
0637 {
0638     QFETCH(QString, testInputString);
0639     QFETCH(QList<QUrl>, testOutputLinks);
0640 
0641     TextHandler testTextHandler;
0642     testTextHandler.setData(testInputString);
0643 
0644     QCOMPARE(testTextHandler.getLinkPreviews(), testOutputLinks);
0645 }
0646 
0647 QTEST_GUILESS_MAIN(TextHandlerTest)
0648 #include "texthandlertest.moc"