2025-01-13 00:32:58 +01:00
|
|
|
import { describe, it, expect } from "vitest";
|
2025-03-14 16:25:10 +02:00
|
|
|
import markdownExportService from "./markdown.js";
|
2025-01-10 22:04:04 +02:00
|
|
|
import { trimIndentation } from "../../../spec/support/utils.js";
|
2024-12-17 23:35:08 +02:00
|
|
|
|
|
|
|
describe("Markdown export", () => {
|
|
|
|
|
2025-03-11 21:05:55 +02:00
|
|
|
it("exports correct language tag for known languages", () => {
|
|
|
|
const conversionTable = {
|
|
|
|
"language-text-x-nginx-conf": "nginx",
|
2025-03-11 21:11:23 +02:00
|
|
|
"language-text-x-diff": "diff",
|
2025-03-11 21:05:55 +02:00
|
|
|
"language-application-javascript-env-frontend": "javascript",
|
2025-03-12 10:54:44 +02:00
|
|
|
"language-application-javascript-env-backend": "javascript",
|
|
|
|
"language-text-x-asm-mips": "mips"
|
2025-03-11 21:05:55 +02:00
|
|
|
};
|
2024-12-17 23:35:08 +02:00
|
|
|
|
2025-03-11 21:11:23 +02:00
|
|
|
for (const [ input, output ] of Object.entries(conversionTable)) {
|
2025-03-11 21:05:55 +02:00
|
|
|
const html = trimIndentation`\
|
|
|
|
<p>A diff:</p>
|
2025-03-11 21:11:23 +02:00
|
|
|
<pre><code class="${input}">Hello
|
2025-03-11 21:05:55 +02:00
|
|
|
-world
|
|
|
|
+worldy
|
|
|
|
</code></pre>`;
|
|
|
|
const expected = trimIndentation`\
|
|
|
|
A diff:
|
2024-12-17 23:35:08 +02:00
|
|
|
|
2025-03-11 21:11:23 +02:00
|
|
|
\`\`\`${output}
|
2025-03-11 21:05:55 +02:00
|
|
|
Hello
|
|
|
|
-world
|
|
|
|
+worldy
|
2024-12-17 23:40:39 +02:00
|
|
|
|
2025-03-11 21:05:55 +02:00
|
|
|
\`\`\``;
|
2025-01-11 14:11:18 +02:00
|
|
|
|
2025-03-11 21:05:55 +02:00
|
|
|
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
|
|
|
}
|
2025-01-11 14:11:18 +02:00
|
|
|
});
|
|
|
|
|
2024-12-17 23:40:39 +02:00
|
|
|
it("removes auto tag for code blocks", () => {
|
|
|
|
const html = trimIndentation`\
|
|
|
|
<pre><code class="language-text-x-trilium-auto">Hello
|
|
|
|
-world
|
|
|
|
+worldy
|
|
|
|
</code></pre>`;
|
|
|
|
const expected = trimIndentation`\
|
|
|
|
\`\`\`
|
|
|
|
Hello
|
|
|
|
-world
|
|
|
|
+worldy
|
|
|
|
|
|
|
|
\`\`\``;
|
|
|
|
|
|
|
|
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
2024-12-17 23:42:31 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
it("supports code block with no language tag", () => {
|
|
|
|
const html = trimIndentation`\
|
|
|
|
<pre><code>Hello</code></pre>`;
|
|
|
|
const expected = trimIndentation`\
|
|
|
|
\`\`\`
|
|
|
|
Hello
|
|
|
|
\`\`\``;
|
|
|
|
|
|
|
|
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
|
|
|
});
|
2025-02-08 22:59:28 +02:00
|
|
|
|
|
|
|
it("exports strikethrough text correctly", () => {
|
|
|
|
const html = "<s>hello</s>Hello <s>world</s>";
|
|
|
|
const expected = "~~hello~~Hello ~~world~~";
|
|
|
|
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
|
|
|
});
|
2025-02-22 12:45:21 +02:00
|
|
|
|
|
|
|
it("exports headings properly", () => {
|
|
|
|
const html = trimIndentation`\
|
|
|
|
<h1>Heading 1</h1>
|
|
|
|
<h2>Heading 2</h2>
|
|
|
|
<h3>Heading 3</h3>
|
|
|
|
<h4>Heading 4</h4>
|
|
|
|
<h5>Heading 5</h5>
|
|
|
|
<h6>Heading 6</h6>
|
|
|
|
`;
|
|
|
|
const expected = trimIndentation`\
|
|
|
|
# Heading 1
|
|
|
|
|
|
|
|
## Heading 2
|
|
|
|
|
|
|
|
### Heading 3
|
|
|
|
|
|
|
|
#### Heading 4
|
|
|
|
|
|
|
|
##### Heading 5
|
|
|
|
|
|
|
|
###### Heading 6`;
|
|
|
|
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
|
|
|
});
|
2025-03-11 17:12:48 +02:00
|
|
|
|
|
|
|
it("rewrites image URL with spaces", () => {
|
|
|
|
const html = `<img src="Hello world .png"/>`;
|
|
|
|
const expected = ``;
|
|
|
|
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
|
|
|
});
|
2025-03-14 17:59:50 +02:00
|
|
|
|
|
|
|
it("supports keyboard shortcuts", () => {
|
|
|
|
const html = "<kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>Delete</kbd>";
|
|
|
|
expect(markdownExportService.toMarkdown(html)).toBe(html);
|
|
|
|
});
|
|
|
|
|
2025-03-15 11:25:42 +02:00
|
|
|
it("exports admonitions properly", () => {
|
|
|
|
const html = trimIndentation`\
|
|
|
|
<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>
|
|
|
|
<figure class="table">
|
|
|
|
<table>
|
|
|
|
<tbody>
|
|
|
|
<tr>
|
|
|
|
<td>
|
|
|
|
1
|
|
|
|
</td>
|
|
|
|
<td>
|
|
|
|
2
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
<tr>
|
|
|
|
<td>
|
|
|
|
3
|
|
|
|
</td>
|
|
|
|
<td>
|
|
|
|
4
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
</tbody>
|
|
|
|
</table>
|
|
|
|
</figure>
|
|
|
|
</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>
|
|
|
|
`;
|
|
|
|
|
|
|
|
const space = " "; // editor config trimming space.
|
|
|
|
const expected = trimIndentation`\
|
|
|
|
Before
|
|
|
|
|
|
|
|
> [!NOTE]
|
|
|
|
> This is a note.
|
|
|
|
|
|
|
|
> [!TIP]
|
|
|
|
> This is a tip.
|
|
|
|
|
|
|
|
> [!IMPORTANT]
|
|
|
|
> This is a very important information.
|
|
|
|
>${space}
|
2025-04-05 17:51:03 +03:00
|
|
|
> <figure class="table"><table><tbody><tr><td>1</td><td>2</td></tr><tr><td>3</td><td>4</td></tr></tbody></table></figure>
|
2025-03-15 11:25:42 +02:00
|
|
|
|
|
|
|
> [!CAUTION]
|
|
|
|
> This is a caution.
|
|
|
|
|
|
|
|
> [!WARNING]
|
|
|
|
> ## Title goes here
|
|
|
|
>${space}
|
|
|
|
> This is a warning.
|
|
|
|
|
|
|
|
After`;
|
|
|
|
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
|
|
|
});
|
|
|
|
|
2025-03-19 18:42:40 +02:00
|
|
|
it("exports code in tables properly", () => {
|
|
|
|
const html = trimIndentation`\
|
|
|
|
<table>
|
|
|
|
<tr>
|
|
|
|
<td>
|
|
|
|
Row 1
|
|
|
|
</td>
|
|
|
|
<td>
|
|
|
|
<p>Allows displaying the value of one or more attributes in the calendar
|
|
|
|
like this: </p>
|
|
|
|
<p>
|
|
|
|
<img src="13_Calendar View_image.png" alt="">
|
|
|
|
</p>
|
|
|
|
|
|
|
|
<pre><code class="language-text-x-trilium-auto">#weight="70"
|
|
|
|
#Mood="Good"
|
|
|
|
#calendar:displayedAttributes="weight,Mood"</code></pre>
|
|
|
|
<p>It can also be used with relations, case in which it will display the
|
|
|
|
title of the target note:</p><pre><code class="language-text-x-trilium-auto">~assignee=@My assignee
|
|
|
|
#calendar:displayedAttributes="assignee"</code></pre>
|
|
|
|
</td>
|
|
|
|
</tr>
|
|
|
|
</table>
|
|
|
|
`;
|
|
|
|
|
|
|
|
const expected = trimIndentation`\
|
2025-03-19 18:54:39 +02:00
|
|
|
<table><tbody><tr><td>Row 1</td><td><p>Allows displaying the value of one or more attributes in the calendar like this: </p><p><img src="13_Calendar View_image.png" alt=""></p><pre><code class="language-text-x-trilium-auto">#weight="70"
|
2025-03-19 18:42:40 +02:00
|
|
|
#Mood="Good"
|
|
|
|
#calendar:displayedAttributes="weight,Mood"</code></pre><p>It can also be used with relations, case in which it will display the title of the target note:</p><pre><code class="language-text-x-trilium-auto">~assignee=@My assignee
|
2025-03-19 18:54:39 +02:00
|
|
|
#calendar:displayedAttributes="assignee"</code></pre></td></tr></tbody></table>`;
|
2025-03-19 18:42:40 +02:00
|
|
|
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
|
|
|
});
|
|
|
|
|
2025-04-03 19:29:51 +03:00
|
|
|
it("converts to character", () => {
|
|
|
|
const html = /*html*/`<p>Hello world.</p>`;
|
|
|
|
const expected = `Hello\u00a0world.`;
|
|
|
|
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("preserves non-breaking space character", () => {
|
|
|
|
const html = /*html*/`<p>Hello\u00adworld.</p>`;
|
|
|
|
const expected = `Hello\u00adworld.`;
|
|
|
|
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
|
|
|
});
|
|
|
|
|
2025-04-05 02:31:52 +03:00
|
|
|
it("exports normal links verbatim", () => {
|
|
|
|
const html = /*html*/`<p><a href="https://www.google.com">Google</a></p>`;
|
|
|
|
const expected = `[Google](https://www.google.com)`;
|
|
|
|
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("exports reference links verbatim", () => {
|
2025-04-05 01:56:31 +03:00
|
|
|
const html = /*html*/`<p><a class="reference-link" href="../../Canvas.html">Canvas</a></p>`;
|
2025-04-05 02:31:52 +03:00
|
|
|
const expected = `<a class="reference-link" href="../../Canvas.html">Canvas</a>`;
|
2025-04-05 01:56:31 +03:00
|
|
|
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
|
|
|
});
|
|
|
|
|
2025-04-05 03:01:06 +03:00
|
|
|
it("converts image if it has no custom properties", () => {
|
|
|
|
const html = /*html*/`<p><img src="Include Note_image.png"></p>`;
|
|
|
|
const expected = ``;
|
|
|
|
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("preserves image verbatim if it has a width or height attribute", () => {
|
|
|
|
const scenarios = [
|
|
|
|
`<img src="Include Note_image.png" width="16" height="16">`,
|
|
|
|
`<img src="Include Note_image.png" width="16">`,
|
|
|
|
`<img src="Include Note_image.png" height="16">`
|
|
|
|
];
|
|
|
|
for (const expected of scenarios) {
|
|
|
|
const html = /*html*/`<p>${expected}</p>`;
|
|
|
|
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2025-04-05 03:23:31 +03:00
|
|
|
it("preserves figures", () => {
|
|
|
|
const html = /*html*/trimIndentation`\
|
|
|
|
<figure class="image">
|
|
|
|
<img style="aspect-ratio:991/403;" src="Jump to Note_image.png" width="991"
|
|
|
|
height="403">
|
|
|
|
</figure>
|
|
|
|
`;
|
|
|
|
const expected = `<figure class="image"><img style="aspect-ratio:991/403;" src="Jump to Note_image.png" width="991" height="403"></figure>`;
|
|
|
|
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
|
|
|
});
|
|
|
|
|
2025-04-05 09:32:08 +03:00
|
|
|
it("converts inline math expressions into proper Markdown syntax", () => {
|
2025-04-05 11:04:40 +03:00
|
|
|
const html = /*html*/`<p>The equation is <span class="math-tex">\\(e=mc^{2}\\)</span>.</p>`;
|
2025-04-05 09:28:18 +03:00
|
|
|
const expected = `The equation is\u00a0$e=mc^{2}$.`;
|
|
|
|
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
|
|
|
});
|
|
|
|
|
2025-04-05 09:32:08 +03:00
|
|
|
it("converts display math expressions into proper Markdown syntax", () => {
|
2025-04-05 11:04:40 +03:00
|
|
|
const html = /*html*/`<span class="math-tex">\\[\sqrt{x^{2}+1}\\]</span>`;
|
2025-04-05 09:32:08 +03:00
|
|
|
const expected = `$$\sqrt{x^{2}+1}$$`;
|
|
|
|
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
|
|
|
});
|
|
|
|
|
2024-12-22 15:45:54 +02:00
|
|
|
});
|