diff --git a/src/app/htmlparser.h b/src/app/htmlparser.h index 0b35385e19a3c8519f3e1478fdbefd4213067ae8..76f636e0e875a8759dce1b0dd8c1919b631a1a2f 100644 --- a/src/app/htmlparser.h +++ b/src/app/htmlparser.h @@ -49,7 +49,7 @@ public: bool parseHtmlString(const QString& html) { - return tidyParseString(doc_, html.toLocal8Bit().data()) >= 0; + return tidyParseString(doc_, html.toUtf8().data()) >= 0; } using TagNodeList = QMap<TidyTagId, QList<TidyNode>>; @@ -86,11 +86,14 @@ public: // Extract the text value from a node. QString getNodeText(TidyNode node) { - TidyBuffer nodeValue = {}; + TidyBuffer nodeValue = {0}; if (!node || tidyNodeGetText(doc_, node, &nodeValue) != yes) { return QString(); } - QString result = QString::fromUtf8((char*) nodeValue.bp, nodeValue.size); + QString result; + if (nodeValue.bp && nodeValue.size > 0) { + result = QString::fromUtf8(reinterpret_cast<char*>(nodeValue.bp), nodeValue.size); + } tidyBufFree(&nodeValue); return result; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d50908cfafa38450f72e40d1c049793e23b7d935..bc68fcf0363310c77e4224cde866c913de1f7636 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -8,6 +8,7 @@ set(QT_TESTING_MODULES QuickTest Test Widgets + HttpServer ) find_package(Qt${QT_VERSION_MAJOR} CONFIG REQUIRED ${QT_TESTING_MODULES}) foreach(MODULE ${QT_TESTING_MODULES}) @@ -84,6 +85,7 @@ set(UNIT_TESTS_SOURCE_FILES ${CMAKE_SOURCE_DIR}/tests/unittests/account_unittest.cpp ${CMAKE_SOURCE_DIR}/tests/unittests/contact_unittest.cpp ${CMAKE_SOURCE_DIR}/tests/unittests/messageparser_unittest.cpp + ${CMAKE_SOURCE_DIR}/tests/unittests/previewengine_unittest.cpp ${CMAKE_SOURCE_DIR}/tests/unittests/globaltestenvironment.h ${COMMON_TESTS_SOURCES}) diff --git a/tests/unittests/previewengine_unittest.cpp b/tests/unittests/previewengine_unittest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..70808dfd285d8feed76fdafdd7a0d119e8632748 --- /dev/null +++ b/tests/unittests/previewengine_unittest.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2024 Savoir-faire Linux Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "globaltestenvironment.h" + +#include <QtHttpServer/QHttpServer> + +class PreviewEngineFixture : public ::testing::Test +{ +public: + // Prepare unit test context. Called at + // prior each unit test execution + void SetUp() override { + server = new QHttpServer(); + // Setup a server that can return an HTML body. + server->listen(QHostAddress::LocalHost, 8000); + } + + // Close unit test context. Called + // after each unit test ending + void TearDown() override { + delete server; + } + + // An instance of QHttpServer used to create a server. + QHttpServer* server; +}; + +/*! + * WHEN We parse a link + * THEN The infoReady signal should be emitted once with the correct info + */ +TEST_F(PreviewEngineFixture, ParsingALinkEmitsInfoReadySignal) +{ + auto link = QString("http://localhost:8000/test"); + server->route("/test", [] () { + return QString("<meta property=\"og:title\" content=\"Test title\">"); + }); + + QSignalSpy infoReadySpy(globalEnv.previewEngine.data(), &PreviewEngine::infoReady); + + Q_EMIT globalEnv.previewEngine->parseLink("msgId_01", link); + + // Wait for the infoReady signal which should be emitted once with the correct ID. + infoReadySpy.wait(); + EXPECT_EQ(infoReadySpy.count(), 1) << "infoReady signal should be emitted once"; + + QList<QVariant> infoReadyArguments = infoReadySpy.takeFirst(); + EXPECT_TRUE(infoReadyArguments.at(0).typeId() == qMetaTypeId<QString>()); + EXPECT_EQ(infoReadyArguments.at(0).toString(), "msgId_01"); +} + +/*! + * WHEN We parse a link that has a description containing characters encoded using UTF-8 + * THEN The description should be parsed and match the original string + */ +TEST_F(PreviewEngineFixture, UTF8CharactersAreParsedCorrectly) +{ + auto link = QString("http://localhost:8000/test"); + server->route("/test", [] () { + return QString("<meta property=\"og:description\" content=\"Test de caractères Utf-8"); + }); + + QSignalSpy infoReadySpy(globalEnv.previewEngine.data(), &PreviewEngine::infoReady); + + Q_EMIT globalEnv.previewEngine->parseLink("msgId_01", link); + + // Wait for the infoReady signal which should be emitted once. + infoReadySpy.wait(); + EXPECT_EQ(infoReadySpy.count(), 1) << "infoReady signal should be emitted once"; + + QList<QVariant> infoReadyArguments = infoReadySpy.takeFirst(); + EXPECT_TRUE(infoReadyArguments.at(1).typeId() == qMetaTypeId<QVariantMap>()); + + // Check that the description is parsed correctly. + QVariantMap info = infoReadyArguments.at(1).toMap(); + EXPECT_TRUE(info.contains("description")); + EXPECT_EQ(info["description"].toString(), "Test de caractères Utf-8"); +}