2025-01-13 00:32:58 +01:00
import { describe , it , expect } from "vitest" ;
2025-01-10 22:03:08 +02:00
import { trimIndentation } from "../../../spec/support/utils.js" ;
import markdownService from "./markdown.js" ;
describe ( "markdown" , ( ) = > {
2025-03-11 21:11:23 +02:00
it ( "rewrites language of known language tags" , ( ) = > {
const conversionTable = {
"nginx" : "language-text-x-nginx-conf" ,
"diff" : "language-text-x-diff" ,
"javascript" : "language-application-javascript-env-backend" ,
2025-03-12 10:54:44 +02:00
"css" : "language-text-css" ,
"mips" : "language-text-x-asm-mips"
2025-03-11 21:11:23 +02:00
} ;
for ( const [ input , output ] of Object . entries ( conversionTable ) ) {
const result = markdownService . renderToHtml ( trimIndentation ` \
\ ` \` \` ${ input }
Hi
\ ` \` \`
` , "title");
expect ( result ) . toBe ( trimIndentation ` \
< pre > < code class = "${output}" > Hi < / code > < / pre > ` );
}
} ) ;
it ( "rewrites language of unknown language tags" , ( ) = > {
const result = markdownService . renderToHtml ( trimIndentation ` \
\ ` \` \` unknownlanguage
Hi
\ ` \` \`
` , "title");
expect ( result ) . toBe ( trimIndentation ` \
< pre > < code class = "language-text-x-trilium-auto" > Hi < / code > < / pre > ` );
} ) ;
2025-01-10 22:03:08 +02:00
it ( "converts h1 heading" , ( ) = > {
const result = markdownService . renderToHtml ( trimIndentation ` \
# Hello
# # world
# another one
Hello , world
` , "title");
2025-03-15 17:51:10 +02:00
expect ( result ) . toBe ( ` <h2>Hello</h2><h2>world</h2><h2>another one</h2><p>Hello, world</p> ` ) ;
2025-01-10 22:03:08 +02:00
} ) ;
2025-01-11 15:21:32 +02:00
2025-03-11 17:51:35 +02:00
it ( "parses duplicate title with escape correctly" , ( ) = > {
2025-04-12 12:46:00 +03:00
const titles = [
"What's new" ,
"Node.js, Electron and `better-sqlite3`"
] ;
for ( const title of titles ) {
const result = markdownService . renderToHtml ( trimIndentation ` \
# $ { title }
Hi there
` , title)
expect ( result ) . toBe ( ` <p>Hi there</p> ` ) ;
}
2025-03-11 17:51:35 +02:00
} ) ;
2025-03-14 19:50:26 +02:00
it ( "trims unnecessary whitespace" , ( ) = > {
const input = ` \
# # Heading 1
Title
\ ` \` \`
code block 1
second line 2
\ ` \` \`
2025-03-15 21:20:44 +02:00
* Hello
* world
1 . Hello
2 . World
2025-03-14 19:50:26 +02:00
` ;
const expected = ` \
< h2 > Heading 1 < / h2 > < p > Title < / p > < pre > < code class = "language-text-x-trilium-auto" > code block 1
2025-03-15 21:20:44 +02:00
second line 2 < / code > < / pre > < ul > < li > Hello < / li > < li > world < / li > < / ul > < ol > < li > Hello < / li > < li > World < / li > < / ol > ` ;
2025-03-14 19:50:26 +02:00
expect ( markdownService . renderToHtml ( input , "Troubleshooting" ) ) . toBe ( expected ) ;
} ) ;
2025-03-15 11:58:11 +02:00
it ( "imports admonitions properly" , ( ) = > {
const space = " " ; // editor config trimming space.
const input = trimIndentation ` \
Before
> [ ! NOTE ]
> This is a note .
> [ ! TIP ]
> This is a tip .
> [ ! IMPORTANT ]
> This is a very important information .
> [ ! CAUTION ]
> This is a caution .
> [ ! WARNING ]
> # # Title goes here
> $ { space }
> This is a warning .
After ` ;
const expected = ` <p>Before</p><aside class="admonition note"><p>This is a note.</p></aside><aside class="admonition tip"><p>This is a tip.</p></aside><aside class="admonition important"><p>This is a very important information.</p></aside><aside class="admonition caution"><p>This is a caution.</p></aside><aside class="admonition warning"><h2>Title goes here</h2><p>This is a warning.</p></aside><p>After</p> ` ;
2025-03-15 22:39:33 +02:00
expect ( markdownService . renderToHtml ( input , "Title" ) ) . toStrictEqual ( expected ) ;
2025-03-15 11:58:11 +02:00
} ) ;
2025-03-16 13:58:31 +02:00
it ( "imports images with same outcome as if inserted from CKEditor" , ( ) = > {
const input = "" ;
const expected = ` <p><img src="api/attachments/YbkR3wt2zMcA/image/image"></p> ` ;
expect ( markdownService . renderToHtml ( input , "Title" ) ) . toStrictEqual ( expected ) ;
} ) ;
2025-03-29 13:47:02 +02:00
it ( "maintains code blocks with XML/HTML" , ( ) = > {
const input = trimIndentation ` \
Before
\ ` \` \`
< application
. . .
android :testOnly = "false" >
. . .
< / application >
\ ` \` \`
After ` ;
const expected = trimIndentation ` \
< p > Before < / p > < pre > < code class = "language-text-x-trilium-auto" > & lt ; application
. . .
android :testOnly = "false" & gt ;
. . .
& lt ; / a p p l i c a t i o n & g t ; < / c o d e > < / p r e > < p > A f t e r < / p > ` ;
expect ( markdownService . renderToHtml ( input , "Title" ) ) . toStrictEqual ( expected ) ;
} ) ;
2025-04-02 23:30:35 +03:00
it ( "does not escape unneeded characters" , ( ) = > {
const input = ` It's important to note that these examples are not natively supported by Trilium out of the box; instead, they demonstrate what you can build within Trilium. ` ;
const expected = ` <p>It's important to note that these examples are not natively supported by Trilium out of the box; instead, they demonstrate what you can build within Trilium.</p> ` ;
expect ( markdownService . renderToHtml ( input , "Title" ) ) . toStrictEqual ( expected ) ;
} ) ;
2025-04-03 19:29:51 +03:00
it ( "preserves " , ( ) = > {
const input = ` Hello world. ` ;
const expected = /*html*/ ` <p>Hello world.</p> ` ;
expect ( markdownService . renderToHtml ( input , "Title" ) ) . toStrictEqual ( expected ) ;
} ) ;
it ( "converts non-breaking space character to " , ( ) = > {
const input = ` Hello \ u00a0world. ` ;
const expected = /*html*/ ` <p>Hello world.</p> ` ;
expect ( markdownService . renderToHtml ( input , "Title" ) ) . toStrictEqual ( expected ) ;
} ) ;
2025-04-05 01:56:31 +03:00
it ( "supports normal links" , ( ) = > {
const input = ` [Google](https://www.google.com) ` ;
const expected = /*html*/ ` <p><a href="https://www.google.com">Google</a></p> ` ;
expect ( markdownService . renderToHtml ( input , "Title" ) ) . toStrictEqual ( expected ) ;
} ) ;
2025-04-05 02:31:52 +03:00
it ( "does not touch relative links" , ( ) = > {
2025-04-05 01:56:31 +03:00
const input = ` [Canvas](../../Canvas.html) ` ;
2025-04-05 02:31:52 +03:00
const expected = /*html*/ ` <p><a href="../../Canvas.html">Canvas</a></p> ` ;
expect ( markdownService . renderToHtml ( input , "Title" ) ) . toStrictEqual ( expected ) ;
} ) ;
it ( "imports back to reference links" , ( ) = > {
const input = ` <a class="reference-link" href="../../Canvas.html">Canvas</a> ` ;
2025-04-05 01:56:31 +03:00
const expected = /*html*/ ` <p><a class="reference-link" href="../../Canvas.html">Canvas</a></p> ` ;
expect ( markdownService . renderToHtml ( input , "Title" ) ) . toStrictEqual ( expected ) ;
2025-04-05 11:37:26 +03:00
} ) ;
2025-04-05 20:59:53 +03:00
it ( "preserves figures and images with sizes" , ( ) = > {
2025-04-05 20:36:20 +03:00
const scenarios = [
/*html*/ ` <figure class="image image-style-align-center image_resized" style="width:53.44%;"><img style="aspect-ratio:991/403;" src="Jump to Note_image.png" width="991" height="403"></figure> ` ,
2025-04-05 20:59:53 +03:00
/*html*/ ` <figure class="image image-style-align-center image_resized" style="width:53.44%;"><img style="aspect-ratio:991/403;" src="Jump to Note_image.png" width="991" height="403"></figure> ` ,
/*html*/ ` <img class="image_resized" style="aspect-ratio:853/315;width:50%;" src="6_File_image.png" width="853" height="315"> `
2025-04-05 20:36:20 +03:00
] ;
for ( const scenario of scenarios ) {
expect ( markdownService . renderToHtml ( scenario , "Title" ) ) . toStrictEqual ( scenario ) ;
}
2025-04-05 01:56:31 +03:00
} ) ;
2025-04-05 09:57:44 +03:00
it ( "converts inline math expressions into Mathtex format" , ( ) = > {
const input = ` The equation is \ u00a0 $ e=mc^{2} $ . ` ;
2025-04-05 09:59:10 +03:00
const expected = /*html*/ ` <p>The equation is <span class="math-tex"> \\ (e=mc^{2} \\ )</span>.</p> ` ;
2025-04-05 09:57:44 +03:00
expect ( markdownService . renderToHtml ( input , "Title" ) ) . toStrictEqual ( expected ) ;
} ) ;
it ( "converts display math expressions into Mathtex format" , ( ) = > {
const input = ` $ $ \ sqrt{x^{2}+1} $ $ ` ;
const expected = /*html*/ ` <p><span class="math-tex"> \\ [ \ sqrt{x^{2}+1} \\ ]</span></p> ` ;
expect ( markdownService . renderToHtml ( input , "Title" ) ) . toStrictEqual ( expected ) ;
} ) ;
2025-04-05 10:46:33 +03:00
it ( "preserves escaped math expressions" , ( ) = > {
const scenarios = [
"\\$\\$\sqrt{x^{2}+1}\\$\\$" ,
"The equation is \\$e=mc^{2}\\$."
] ;
for ( const scenario of scenarios ) {
expect ( markdownService . renderToHtml ( scenario , "Title" ) ) . toStrictEqual ( ` <p> ${ scenario } </p> ` ) ;
}
} ) ;
2025-04-05 21:13:12 +03:00
it ( "preserves table with column widths" , ( ) = > {
const html = /*html*/ ` <figure class="table" style="width:100%;"><table class="ck-table-resized"><colgroup><col style="width:2.77%;"><col style="width:33.42%;"><col style="width:63.81%;"></colgroup><thead><tr><th> </th><th> </th><th> </th></tr></thead><tbody><tr><td>1</td><td><img class="image_resized" style="aspect-ratio:562/454;width:100%;" src="1_Geo Map_image.png" width="562" height="454"></td><td>Go to any location on openstreetmap.org and right click to bring up the context menu. Select the “Show address” item.</td></tr><tr><td>2</td><td><img class="image_resized" style="aspect-ratio:696/480;width:100%;" src="Geo Map_image.png" width="696" height="480"></td><td>The address will be visible in the top-left of the screen, in the place of the search bar. <br><br>Select the coordinates and copy them into the clipboard.</td></tr><tr><td>3</td><td><img class="image_resized" style="aspect-ratio:640/276;width:100%;" src="5_Geo Map_image.png" width="640" height="276"></td><td>Simply paste the value inside the text box into the <code>#geolocation</code> attribute of a child note of the map and then it should be displayed on the map.</td></tr></tbody></table></figure> ` ;
expect ( markdownService . renderToHtml ( html , "Title" ) ) . toStrictEqual ( html ) ;
} ) ;
2025-04-05 22:37:02 +03:00
it ( "generates strike-through text" , ( ) = > {
const input = ` ~~Hello~~ world. ` ;
const expected = /*html*/ ` <p><del>Hello</del> world.</p> ` ;
expect ( markdownService . renderToHtml ( input , "Title" ) ) . toStrictEqual ( expected ) ;
} ) ;
2025-04-11 19:25:22 +03:00
it ( "does not generate additional spacing when importing lists" , ( ) = > {
const input = trimIndentation ` \
# # # 🐞 Bugfixes
* [ v0 . 90.4 docker does not read USER \ _UID and USER \ _GID from environment ] ( https : //github.com/TriliumNext/Notes/issues/331)
* [ Invalid CSRF token on Android phone ] ( https : //github.com/TriliumNext/Notes/issues/318)
* [ Excess spacing in lists ] ( https : //github.com/TriliumNext/Notes/issues/341)`;
const expected = [
/*html*/ ` <h3>🐞 Bugfixes</h3> ` ,
/*html*/ ` <ul> ` ,
/*html*/ ` <li><a href="https://github.com/TriliumNext/Notes/issues/331">v0.90.4 docker does not read USER_UID and USER_GID from environment</a></li> ` ,
/*html*/ ` <li><a href="https://github.com/TriliumNext/Notes/issues/318">Invalid CSRF token on Android phone</a></li> ` ,
/*html*/ ` <li><a href="https://github.com/TriliumNext/Notes/issues/341">Excess spacing in lists</a></li> ` ,
/*html*/ ` </ul> `
] . join ( "" ) ;
expect ( markdownService . renderToHtml ( input , "Title" ) ) . toStrictEqual ( expected ) ;
} ) ;
2025-01-10 22:03:08 +02:00
} ) ;