Files

159 lines
8.1 KiB
JavaScript

// Tests for Markdown.js and PropfindParser.js (pure ES5, runnable in node).
"use strict";
var assert = require("assert");
var fs = require("fs");
var path = require("path");
var comp = path.join(__dirname, "..", "qml", "components");
var Markdown = require(path.join(comp, "Markdown.js"));
var PropfindParser = require(path.join(comp, "PropfindParser.js"));
// ── Markdown ──────────────────────────────────────────────────────────────
var md = "# Titolo\n\nTesto con **grassetto**, *corsivo* e `codice`.\n\n" +
"- uno\n- due\n\n1. primo\n2. secondo\n\n> citazione\n\n" +
"[link](https://example.com)\n\n```\nvar x = 1;\n```\n\n---\n\n~~cancellato~~ e <b>html</b>";
var html = Markdown.render(md);
console.log("── Markdown output ──\n" + html + "\n────────────────────");
assert(html.indexOf("<h1>Titolo</h1>") >= 0, "heading");
assert(html.indexOf("<b>grassetto</b>") >= 0, "bold");
assert(html.indexOf("<i>corsivo</i>") >= 0, "italic");
assert(html.indexOf("<tt>codice</tt>") >= 0, "inline code");
assert(html.indexOf("<ul><li>uno</li><li>due</li></ul>") >= 0, "ul");
assert(html.indexOf("<ol><li>primo</li><li>secondo</li></ol>") >= 0, "ol");
assert(html.indexOf("<i>citazione</i>") >= 0, "quote");
assert(html.indexOf('<a href="https://example.com">link</a>') >= 0, "link");
assert(html.indexOf("<pre") >= 0 && html.indexOf("var x = 1;") >= 0, "code fence");
assert(html.indexOf("<hr/>") >= 0, "hr");
assert(html.indexOf("<s>cancellato</s>") >= 0, "strike");
assert(html.indexOf("&lt;b&gt;html&lt;/b&gt;") >= 0, "html escaped");
assert(html.indexOf("<b>") < html.indexOf("&lt;b&gt;"), "escape before markup");
// nested bold/italic: **a *b* c**
var h2 = Markdown.render("**a *b* c**");
assert(h2.indexOf("<b>a <i>b</i> c</b>") >= 0, "nested bold+italic: " + h2);
// italics must not swallow bold markers
var h3 = Markdown.render("*solo* e **doppio**");
assert(h3.indexOf("<i>solo</i>") >= 0 && h3.indexOf("<b>doppio</b>") >= 0, "mixed: " + h3);
// empty input
assert(Markdown.render("") === "", "empty");
assert(Markdown.render(null) === "", "null");
// UTF-8 BOM must not break the first heading
var h4 = Markdown.render("\uFEFF# Titolo con BOM\n\nTesto");
assert(h4.indexOf("<h1>Titolo con BOM</h1>") >= 0, "BOM stripped: " + h4);
assert(h4.indexOf("\uFEFF") < 0, "no BOM in output");
// heading without space is not a heading (standard markdown)
var h5 = Markdown.render("#NienteSpazio");
assert(h5.indexOf("<h1>") < 0, "no-space hash is not a heading");
// ── tables (GFM) ──
var tbl = Markdown.render("| A | B |\n|---|---|\n| 1 | 2 |\n| 3 | 4 |");
console.log("── table output ──\n" + tbl);
assert(tbl.indexOf("<table") >= 0, "table open");
assert(tbl.indexOf("<th><b>A</b></th>") >= 0, "th header: " + tbl);
assert(tbl.indexOf("<td>1</td>") >= 0, "td data");
assert(tbl.indexOf("<td>4</td>") >= 0, "last cell");
assert(tbl.indexOf("</table>") === tbl.lastIndexOf("</table>"), "one table only");
// alignment from delimiter
var tbl2 = Markdown.render("| A | B | C |\n|:--|--:|:-:|\n| x | y | z |");
assert(tbl2.indexOf('align="left"') >= 0, "left align");
assert(tbl2.indexOf('align="right"') >= 0, "right align");
assert(tbl2.indexOf('align="center"') >= 0, "center align");
// inline formatting inside cells
var tbl3 = Markdown.render("| **X** | `code` |\n|---|---|\n| [l](http://e.it) | *i* |");
assert(tbl3.indexOf("<b>X</b>") >= 0, "bold in cell");
assert(tbl3.indexOf("<tt>code</tt>") >= 0, "code in cell");
assert(tbl3.indexOf("<a href=\"http://e.it\">l</a>") >= 0, "link in cell");
// pipes without delimiter row stay a paragraph; bare dashes are still hr
var notTbl = Markdown.render("a | b\n\n---");
assert(notTbl.indexOf("<table") < 0, "no fake table");
assert(notTbl.indexOf("<hr/>") >= 0, "bare hr kept");
// single column table
var tbl4 = Markdown.render("| Solo |\n|---|\n| x |");
assert(tbl4.indexOf("<th><b>Solo</b></th>") >= 0, "single col header");
// ── links with baseUrl ──
var BASE = "https://host/remote.php/dav/files/u/Note";
var lk1 = Markdown.render("[rel](altra.md)", BASE);
assert(lk1.indexOf('href="' + BASE + '/altra.md"') >= 0, "relative link resolved: " + lk1);
var lk2 = Markdown.render("[web](https://example.com)", BASE);
assert(lk2.indexOf('href="https://example.com"') >= 0, "absolute link untouched");
var lk3 = Markdown.render("[a](#ancora) e [b](/abs)", BASE);
assert(lk3.indexOf('href="#ancora"') >= 0 && lk3.indexOf('href="/abs"') >= 0, "anchor/absolute path untouched");
var lk4 = Markdown.render("[rel](altra.md)");
assert(lk4.indexOf('href="altra.md"') >= 0, "no base: relative link left as-is");
// ── wiki links [[Nota]] ──
var wk1 = Markdown.render("Vedi [[altra nota]] e [[nota.md]]");
assert(wk1.indexOf('<a href="quill://altra%20nota.md">altra nota</a>') >= 0, "wiki link no ext: " + wk1);
assert(wk1.indexOf('<a href="quill://nota.md">nota</a>') >= 0, "wiki link with ext");
var wk2 = Markdown.render("[[Sub/pippo]]");
assert(wk2.indexOf('quill://Sub%2Fpippo.md') >= 0, "wiki link with path");
// ── images ──
var img1 = Markdown.render("![foto](img.png)", BASE, { imageMap: {}, imageLabel: "immagine" });
assert(img1.indexOf("<i>[immagine: foto]</i>") >= 0, "image placeholder with label: " + img1);
var m = {};
m[BASE + "/img.png"] = "/home/nemo/.cache/quill-images/ab12.png";
var img2 = Markdown.render("![foto](img.png)", BASE, { imageMap: m, imageLabel: "immagine" });
assert(img2.indexOf('<img src="file:///home/nemo/.cache/quill-images/ab12.png" width="350" height="-1" alt="foto"/>') >= 0,
"image inline from cache: " + img2);
var img3 = Markdown.render("![x](https://host/img.jpg)", "", { imageMap: {} });
assert(img3.indexOf("<i>[image: x]</i>") >= 0, "default label 'image'");
console.log("Markdown tests: OK");
// ── PropfindParser ────────────────────────────────────────────────────────
var xml = '<?xml version="1.0"?>' +
'<d:multistatus xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns" xmlns:nc="http://nextcloud.org/ns">' +
'<d:response>' +
'<d:href>/remote.php/dav/files/carlo/Note/</d:href>' +
'<d:propstat><d:prop><d:resourcetype><d:collection/></d:resourcetype></d:prop>' +
'<d:status>HTTP/1.1 200 OK</d:status></d:propstat>' +
'</d:response>' +
'<d:response>' +
'<d:href>/remote.php/dav/files/carlo/Note/Sub%20folder/</d:href>' +
'<d:propstat><d:prop><d:resourcetype><d:collection/></d:resourcetype></d:prop>' +
'<d:status>HTTP/1.1 200 OK</d:status></d:propstat>' +
'</d:response>' +
'<d:response>' +
'<d:href>/remote.php/dav/files/carlo/Note/Idea%20spaziale.md</d:href>' +
'<d:propstat><d:prop>' +
'<d:getlastmodified>Fri, 14 Aug 2026 09:30:00 GMT</d:getlastmodified>' +
'<d:getcontentlength>1234</d:getcontentlength>' +
'</d:prop><d:status>HTTP/1.1 200 OK</d:status></d:propstat>' +
'</d:response>' +
'<d:response>' +
'<d:href>/remote.php/dav/files/carlo/Note/readme.txt</d:href>' +
'<d:propstat><d:prop><d:getcontentlength>42</d:getcontentlength></d:prop>' +
'<d:status>HTTP/1.1 200 OK</d:status></d:propstat>' +
'</d:response>' +
'</d:multistatus>';
var baseUrl = "https://nextcloud.hackatoniclife.com/remote.php/dav/files/carlo/Note";
var entries = PropfindParser.parse(xml, baseUrl);
console.log("── Propfind entries ──");
entries.forEach(function (e) { console.log(JSON.stringify(e)); });
assert.strictEqual(entries.length, 3, "root excluded, 3 entries (1 dir + 2 files)");
assert.strictEqual(entries[0].name, "Sub folder", "dir first");
assert.strictEqual(entries[0].isDir, true, "isDir");
assert.strictEqual(entries[0].path, "Sub folder/", "dir path with slash");
assert.strictEqual(entries[1].name, "Idea spaziale.md", "decoded name");
assert.strictEqual(entries[1].path, "Idea spaziale.md", "decoded path");
assert.strictEqual(entries[1].mtime, "Fri, 14 Aug 2026 09:30:00 GMT", "mtime");
assert.strictEqual(entries[1].size, 1234, "size");
assert.strictEqual(entries[2].name, "readme.txt", "non-md kept (filtered by caller)");
console.log("PropfindParser tests: OK");