harbour-quill 0.2.12: note markdown con sync WebDAV
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
// 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("<b>html</b>") >= 0, "html escaped");
|
||||
assert(html.indexOf("<b>") < html.indexOf("<b>"), "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("", 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("", 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("", "", { 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");
|
||||
Reference in New Issue
Block a user