okular/autotests/markdowntest.cpp
bob sayshilol 0bc8c76c3c Fix strikethrough rendering in markdown converter
Previously the `<del>` tag that Discount emits would be removed by Qt
since it's not in the subset of HTML that it understands. It does know
about `<s>` though, so replace `<del>` with `<s>` to render strikethroughs
correctly.

Note that I couldn't get Okular to build locally (PEBCAK issue) so I split out this file to debug it. I've tested against http://daringfireball.net/projects/downloads/MarkdownTest_1.0.zip and ff866f73d4/tests/data and the only difference is that trailing whitespace is now trimmed from the end of a line:
```diff
8c8
< <p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Dashes:</p>
---
> <p style=" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Dashes: </p>
```
A quick search suggests this isn't a problem though https://stackoverflow.com/a/27026403:
> Within block tags (i.e. `<p>`, `<h1>`, `<div>`, ...) spaces as well as line breaks at the beginning or end of the tags should always be ignored (i.e. `<p>test</p>` should look the same as `<p> test </p>`).

I also tried https://doc.qt.io/qt-5/qtextdocument.html#setMarkdown which solves the strikethrough problem, though there are many visible differences with that approach. Most notably, everything is squashed together and code blocks lose their formatting.
2024-04-22 17:00:02 +00:00

227 lines
7.8 KiB
C++

/*
SPDX-FileCopyrightText: 2020 Markus Brenneis <support.gulp21+kde@gmail.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <QTest>
#include "../settings_core.h"
#include "core/document.h"
#include "generators/markdown/converter.h"
#include <QMimeDatabase>
#include <QMimeType>
#include <QTextDocument>
#include <memory>
class MarkdownTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void initTestCase();
void testFancyPantsEnabled();
void testFancyPantsDisabled();
void testImageSizes();
void testSpecialCharsInImageFileName();
void testStrikeThrough();
void testHtmlTagFixup();
private:
void findImages(QTextFrame *parent, QVector<QTextImageFormat> &images);
void findImages(const QTextBlock &parent, QVector<QTextImageFormat> &images);
};
void MarkdownTest::initTestCase()
{
Okular::SettingsCore::instance(QStringLiteral("markdowntest"));
}
void MarkdownTest::testFancyPantsEnabled()
{
Markdown::Converter converter;
converter.setFancyPantsEnabled(true);
std::unique_ptr<QTextDocument> document(converter.convert(QStringLiteral(KDESRCDIR "data/imageSizes.md")));
QTextFrame::iterator secondFrame = ++(document->rootFrame()->begin());
QVERIFY(secondFrame.currentBlock().text().startsWith(QStringLiteral("©")));
}
void MarkdownTest::testFancyPantsDisabled()
{
Markdown::Converter converter;
converter.setFancyPantsEnabled(false);
std::unique_ptr<QTextDocument> document(converter.convert(QStringLiteral(KDESRCDIR "data/imageSizes.md")));
QTextFrame::iterator secondFrame = ++(document->rootFrame()->begin());
QVERIFY(secondFrame.currentBlock().text().startsWith(QStringLiteral("(c)")));
}
void MarkdownTest::testImageSizes()
{
Markdown::Converter converter;
std::unique_ptr<QTextDocument> document(converter.convert(QStringLiteral(KDESRCDIR "data/imageSizes.md")));
QTextFrame *parent = document->rootFrame();
QVector<QTextImageFormat> images;
findImages(parent, images);
QCOMPARE(images.size(), 17);
qreal expectedSizes[][2] = {// width, height
// small image
{412, 349},
{100, 84.70873786407767},
{118.0515759312321, 100},
{100, 100},
{890, 753.9077669902913},
{890, 890},
// wide image
{890, 178},
{100, 20},
{500, 100},
{100, 100},
{890, 178},
{890, 890},
// tall image
{300, 1500},
{100, 500},
{20, 100},
{100, 100},
{890, 890}};
for (int i = 0; i < images.size(); i++) {
QCOMPARE(images[i].width(), expectedSizes[i][0]);
QCOMPARE(images[i].height(), expectedSizes[i][1]);
}
}
void MarkdownTest::findImages(QTextFrame *parent, QVector<QTextImageFormat> &images)
{
for (QTextFrame::iterator it = parent->begin(); !it.atEnd(); ++it) {
QTextFrame *textFrame = it.currentFrame();
const QTextBlock textBlock = it.currentBlock();
if (textFrame) {
findImages(textFrame, images);
} else if (textBlock.isValid()) {
findImages(textBlock, images);
}
}
}
void MarkdownTest::findImages(const QTextBlock &parent, QVector<QTextImageFormat> &images)
{
for (QTextBlock::iterator it = parent.begin(); !it.atEnd(); ++it) {
const QTextFragment textFragment = it.fragment();
if (textFragment.isValid()) {
const QTextCharFormat textCharFormat = textFragment.charFormat();
if (textCharFormat.isImageFormat()) {
images.append(textCharFormat.toImageFormat());
}
}
}
}
void MarkdownTest::testSpecialCharsInImageFileName()
{
Markdown::Converter converter;
std::unique_ptr<QTextDocument> document(converter.convert(QStringLiteral(KDESRCDIR "data/imageUrlsWithSpecialChars.md")));
QTextFrame *parent = document->rootFrame();
QVector<QTextImageFormat> images;
findImages(parent, images);
QCOMPARE(images.size(), 1);
QVERIFY(images[0].name().endsWith(QStringLiteral("kartöffelchen.jpg")));
QVERIFY(!images[0].name().contains(QStringLiteral("kart%C3%B6ffelchen.jpg")));
}
void MarkdownTest::testStrikeThrough()
{
Markdown::Converter converter;
converter.setFancyPantsEnabled(true);
std::unique_ptr<QTextDocument> document(converter.convert(QStringLiteral(KDESRCDIR "data/strikethrough.md")));
const QTextFrame *rootFrame = document->rootFrame();
auto frameIter = rootFrame->begin();
// Header line.
QCOMPARE_NE(frameIter, rootFrame->end());
QCOMPARE(frameIter.currentBlock().text(), QStringLiteral("Test for strikethrough tag workaround"));
// Ordinary line.
{
++frameIter;
QCOMPARE_NE(frameIter, rootFrame->end());
auto block = frameIter.currentBlock();
QCOMPARE(block.text(), QStringLiteral("Line without strikethrough"));
// Single format for the entire line.
auto formats = block.textFormats();
QCOMPARE(formats.size(), 1);
QVERIFY(!formats[0].format.fontStrikeOut());
}
// Part of the line has a strikethrough.
{
++frameIter;
QCOMPARE_NE(frameIter, rootFrame->end());
auto block = frameIter.currentBlock();
QCOMPARE(block.text(), QStringLiteral("Line with strikethrough"));
// The "with" should be the only thing striked out.
auto formats = block.textFormats();
QCOMPARE(formats.size(), 3);
QCOMPARE(block.text().sliced(formats[0].start, formats[0].length), QStringLiteral("Line "));
QVERIFY(!formats[0].format.fontStrikeOut());
QCOMPARE(block.text().sliced(formats[1].start, formats[1].length), QStringLiteral("with"));
QVERIFY(formats[1].format.fontStrikeOut());
QCOMPARE(block.text().sliced(formats[2].start, formats[2].length), QStringLiteral(" strikethrough"));
QVERIFY(!formats[2].format.fontStrikeOut());
}
// Code block shouldn't have leading spaces, or be modified by our fixup.
{
++frameIter;
QCOMPARE_NE(frameIter, rootFrame->end());
auto block = frameIter.currentBlock();
QCOMPARE(block.text(), QStringLiteral("~~Strikethrough~~ should be <del>ignored</del> in a <s>code block</s>"));
}
}
void MarkdownTest::testHtmlTagFixup()
{
const QString wrapperTag = QStringLiteral("ignored_by_qt");
const QString wrapperTagBegin = QStringLiteral("<ignored_by_qt>");
const QString wrapperTagEnd = QStringLiteral("</ignored_by_qt>");
// These should passthrough unchanged.
const QString testCases[] = {
QStringLiteral("basic test"),
QStringLiteral("<p>test with tag</p>"),
QStringLiteral("line with <em>combined <strong>tags</strong></em>"),
};
for (const QString &inputHtml : testCases) {
const QString outputHtml = Markdown::detail::fixupHtmlTags(QString(inputHtml));
QCOMPARE(outputHtml, wrapperTagBegin + inputHtml + wrapperTagEnd);
}
// <del> should become <s>.
{
const QString outputHtml = Markdown::detail::fixupHtmlTags(QStringLiteral("<del>test</del>"));
QCOMPARE(outputHtml, wrapperTagBegin + QStringLiteral("<s>test</s>") + wrapperTagEnd);
}
// Check that the wrapper is ignored by Qt.
{
QTextDocument dom;
dom.setHtml(wrapperTagBegin + QStringLiteral("basic test") + wrapperTagEnd);
QVERIFY(!dom.toHtml().contains(wrapperTag));
}
}
QTEST_MAIN(MarkdownTest)
#include "markdowntest.moc"