Files
Flora/tests/mdparser_test.cpp
T
grendervill b3fa778d88 MarkdownView: исправлен делегат и парсинг блоков
- Repeater delegate: убраны required property, заменены на property string blockType: model.type || "" и т.д. для html/code блоков (иначе QML Cannot create delegate когда у html нет codeId)
- parseBlocks тест в mdparser_test.cpp: проверка 3 блоков (html/code/html) и highlighted
- Убран console.log из MarkdownView::update
2026-09-01 21:24:37 +03:00

120 lines
5.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Тест MdParser (Фаза 6): рендер Markdown → HTML
// (заголовки, жирный/курсив/зачёркнутый, таблицы, ссылки, картинки,
// задачи с переключаемыми ссылками, подсветка кода, вики-ссылки).
#include <QCoreApplication>
#include <QTemporaryDir>
#include <QDir>
#include <QFile>
#include <cstdio>
#include "markdown/MdParser.h"
static int fail(const char *msg, const QString &detail = QString())
{
std::fprintf(stderr, "FAIL: %s | %s\n", msg, qPrintable(detail));
return 1;
}
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QTemporaryDir dir;
if (!dir.isValid())
return fail("temp dir");
const QString vault = dir.path();
QDir(vault).mkpath("sub");
{
QFile f(vault + "/sub/b.md");
f.open(QIODevice::WriteOnly);
f.write("b");
f.close();
QFile g(vault + "/sub/pic.png");
g.open(QIODevice::WriteOnly);
g.write("\x89PNG fake");
g.close();
}
MdParser p;
const QString fileDir = vault + "/sub";
auto probe = [](const QString &html, const char *sub) {
return html.contains(QString::fromUtf8(sub));
};
// Заголовок
QString h = p.render("# Заголовок\n", vault, fileDir);
if (!probe(h, "<h1>Заголовок</h1>"))
return fail("заголовок", h);
// Жирный / курсив / зачёркнутый
QString f = p.render("**ж** *к* ~~з~~\n", vault, fileDir);
if (!probe(f, "<b>ж</b>") || !probe(f, "<i>к</i>") || !probe(f, "<s>з</s>"))
return fail("инлайн-разметка", f);
// Таблица GFM
QString t = p.render("| A | B |\n|---|---|\n| 1 | 2 |\n", vault, fileDir);
if (!probe(t, "<table") || !probe(t, "<th") || !probe(t, "<td"))
return fail("таблица", t);
// Задачи (переключаемые ссылки + галочки)
QString sk = p.render("- [ ] задача\n- [x] готова\n", vault, fileDir);
if (!probe(sk, "flora://task/") || !probe(sk, "\u2610") || !probe(sk, "\u2611"))
return fail("задачи", sk);
// Относительная ссылка на существующий файл → flora://open
QString lk = p.render("[б](b.md)\n", vault, fileDir);
if (!probe(lk, "flora://open/") || !probe(lk, ">б</a>"))
return fail("ссылка", lk);
// Вики-ссылка [[b]]
QString wk = p.render("[[b]]\n", vault, fileDir);
if (!probe(wk, "flora://open/") || !probe(wk, ">b</a>"))
return fail("вики-ссылка", wk);
// Подсветка кода python (должны появиться цветные span'ы)
QString c = p.render("```python\nprint(\"hi\")\n```\n", vault, fileDir);
if (!probe(c, "language-Python") || !probe(c, "style=\"color:"))
return fail("подсветка кода", c);
// Простой код без языка — не должен падать
p.render("```\nпросто\n```\n", vault, fileDir);
// Язык «С++» из кириллицы должен мапиться на C++ (подсветка сработает)
QString cpp = p.render("```С++\nint x = 1;\n```\n", vault, fileDir);
if (!probe(cpp, "language-C++"))
return fail("алиас С++", cpp);
// Экранирование HTML — опасные теги должны экранироваться
QString e = p.render("<script>alert(1)</script>\n", vault, fileDir);
if (probe(e, "<script>"))
return fail("экранирование script", e);
// Безопасные теги должны рендериться как HTML
QString sup = p.render("x<sup>2</sup>=4\n", vault, fileDir);
if (!probe(sup, "<sup>2</sup>"))
return fail("sup не отрендерился", sup);
if (sup.trimmed().isEmpty())
return fail("sup пустой", sup);
QString span = p.render("<span style=\"background-color:#ffeb3b\">hi</span>\n", vault, fileDir);
if (!probe(span, "<span") || !probe(span, "background-color"))
return fail("span не отрендерился", span);
// Картинка → data: или placeholder (файл невалидный PNG — data не выйдет)
QString im = p.render("![а](pic.png)\n", vault, fileDir);
if (!probe(im, "<img") && !probe(im, "изображение"))
return fail("картинка", im);
// CodeBlock отдельный компонент — parseBlocks
auto blocks = p.parseBlocks("# Hello\n\n```cpp\nint x=1;\n```\n\nText", vault, fileDir);
if (blocks.size() != 3) return fail("parseBlocks size", QString::number(blocks.size()));
if (blocks[0].toMap().value("type").toString() != "html") return fail("block 0 type", blocks[0].toMap().value("type").toString());
if (blocks[1].toMap().value("type").toString() != "code") return fail("block 1 type", blocks[1].toMap().value("type").toString());
if (blocks[1].toMap().value("language").toString() != "C++" && blocks[1].toMap().value("language").toString() != "cpp") return fail("block 1 lang", blocks[1].toMap().value("language").toString());
if (!blocks[1].toMap().value("code").toString().contains("int x=1;")) return fail("block 1 code", blocks[1].toMap().value("code").toString());
if (blocks[1].toMap().value("highlighted").toString().isEmpty()) return fail("highlighted empty");
if (blocks[2].toMap().value("type").toString() != "html") return fail("block 2 type");
std::printf("MDPARSER TEST OK\n");
return 0;
}