mirror of
https://github.com/zadam/trilium.git
synced 2025-10-19 22:58:52 +02:00
346 lines
14 KiB
TypeScript
346 lines
14 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
||
import markdownExportService from "./markdown.js";
|
||
import { trimIndentation } from "../../../spec/support/utils.js";
|
||
|
||
describe("Markdown export", () => {
|
||
|
||
it("exports correct language tag for known languages", () => {
|
||
const conversionTable = {
|
||
"language-text-x-nginx-conf": "nginx",
|
||
"language-text-x-diff": "diff",
|
||
"language-application-javascript-env-frontend": "javascript",
|
||
"language-application-javascript-env-backend": "javascript",
|
||
"language-text-x-asm-mips": "mips"
|
||
};
|
||
|
||
for (const [ input, output ] of Object.entries(conversionTable)) {
|
||
const html = trimIndentation`\
|
||
<p>A diff:</p>
|
||
<pre><code class="${input}">Hello
|
||
-world
|
||
+worldy
|
||
</code></pre>`;
|
||
const expected = trimIndentation`\
|
||
A diff:
|
||
|
||
\`\`\`${output}
|
||
Hello
|
||
-world
|
||
+worldy
|
||
|
||
\`\`\``;
|
||
|
||
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
||
}
|
||
});
|
||
|
||
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);
|
||
});
|
||
|
||
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);
|
||
});
|
||
|
||
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);
|
||
});
|
||
|
||
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);
|
||
});
|
||
|
||
it("rewrites image URL with spaces", () => {
|
||
const html = `<img src="Hello world .png"/>`;
|
||
const expected = ``;
|
||
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
||
});
|
||
|
||
it("supports keyboard shortcuts", () => {
|
||
const html = "<kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>Delete</kbd>";
|
||
expect(markdownExportService.toMarkdown(html)).toBe(html);
|
||
});
|
||
|
||
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}
|
||
> <figure class="table"><table><tbody><tr><td>1</td><td>2</td></tr><tr><td>3</td><td>4</td></tr></tbody></table></figure>
|
||
|
||
> [!CAUTION]
|
||
> This is a caution.
|
||
|
||
> [!WARNING]
|
||
> ## Title goes here
|
||
>${space}
|
||
> This is a warning.
|
||
|
||
After`;
|
||
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
||
});
|
||
|
||
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`\
|
||
<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"
|
||
#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></tbody></table>`;
|
||
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
||
});
|
||
|
||
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);
|
||
});
|
||
|
||
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", () => {
|
||
const html = /*html*/`<p><a class="reference-link" href="../../Canvas.html">Canvas</a></p>`;
|
||
const expected = `<a class="reference-link" href="../../Canvas.html">Canvas</a>`;
|
||
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
||
});
|
||
|
||
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 = [
|
||
/*html*/`<img src="Include Note_image.png" width="16" height="16">`,
|
||
/*html*/`<img src="Include Note_image.png" width="16">`,
|
||
/*html*/`<img src="Include Note_image.png" height="16">`,
|
||
/*html*/`<img class="image_resized" style="aspect-ratio:853/315;width:50%;" src="6_File_image.png" width="853" height="315">`
|
||
];
|
||
for (const expected of scenarios) {
|
||
const html = /*html*/`<p>${expected}</p>`;
|
||
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
||
}
|
||
});
|
||
|
||
it("preserves figures", () => {
|
||
const html = /*html*/trimIndentation`\
|
||
<figure class="image" style="width:53.44%;">
|
||
<img style="aspect-ratio:991/403;" src="Jump to Note_image.png" width="991"
|
||
height="403">
|
||
</figure>
|
||
`;
|
||
const expected = `<figure class="image" style="width:53.44%;"><img style="aspect-ratio:991/403;" src="Jump to Note_image.png" width="991" height="403"></figure>`;
|
||
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
||
});
|
||
|
||
it("converts inline math expressions into proper Markdown syntax", () => {
|
||
const html = /*html*/String.raw`<span class="math-tex">\(H(X, Y) = \sum_{i=1}^{M} \sum_{j=1}^{L} p(x_i, y_j) \log_2 \frac{1}{p(x_i, y_j)} = - \sum_{i=1}^{M} \sum_{j=1}^{L} p(x_i, y_j) \log_2 p(x_i, y_j) \frac{\text{bits}}{\text{symbol}}\)</span></span>`;
|
||
const expected = String.raw`$H(X, Y) = \sum_{i=1}^{M} \sum_{j=1}^{L} p(x_i, y_j) \log_2 \frac{1}{p(x_i, y_j)} = - \sum_{i=1}^{M} \sum_{j=1}^{L} p(x_i, y_j) \log_2 p(x_i, y_j) \frac{\text{bits}}{\text{symbol}}$`;
|
||
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
||
});
|
||
|
||
it("converts display math expressions into proper Markdown syntax", () => {
|
||
const html = /*html*/String.raw`<span class="math-tex">\[H(X, Y) = \sum_{i=1}^{M} \sum_{j=1}^{L} p(x_i, y_j) \log_2 \frac{1}{p(x_i, y_j)} = - \sum_{i=1}^{M} \sum_{j=1}^{L} p(x_i, y_j) \log_2 p(x_i, y_j) \frac{\text{bits}}{\text{symbol}}\]</span></span>`;
|
||
const expected = String.raw`$$H(X, Y) = \sum_{i=1}^{M} \sum_{j=1}^{L} p(x_i, y_j) \log_2 \frac{1}{p(x_i, y_j)} = - \sum_{i=1}^{M} \sum_{j=1}^{L} p(x_i, y_j) \log_2 p(x_i, y_j) \frac{\text{bits}}{\text{symbol}}$$`;
|
||
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
||
});
|
||
|
||
it("does not generate additional spacing when exporting lists with paragraph", () => {
|
||
const html = trimIndentation`\
|
||
<ul>
|
||
<li><a href="https://github.com/JYC333">@JYC333</a> made their first contribution
|
||
in <a href="https://github.com/TriliumNext/Notes/pull/294">#294</a>
|
||
|
||
</li>
|
||
<li>
|
||
<p><a href="https://github.com/TriliumNext/Notes/issues/375">Note Tooltip isn't removed when clicking on internal trilium link in read-only mode</a>
|
||
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p><a href="https://github.com/TriliumNext/Notes/issues/384">Calendar dropdown won't close if click/right-click other button that open notes from launcher bar</a>
|
||
|
||
</p>
|
||
</li>
|
||
</ul>
|
||
`;
|
||
const expected = trimIndentation`\
|
||
* [@JYC333](https://github.com/JYC333) made their first contribution in [#294](https://github.com/TriliumNext/Notes/pull/294)
|
||
* [Note Tooltip isn't removed when clicking on internal trilium link in read-only mode](https://github.com/TriliumNext/Notes/issues/375)
|
||
* [Calendar dropdown won't close if click/right-click other button that open notes from launcher bar](https://github.com/TriliumNext/Notes/issues/384)`;
|
||
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
||
});
|
||
|
||
it("preserves include note", () => {
|
||
const html = /*html*/`<section class="include-note" data-note-id="i4A5g9iOg9I0" data-box-size="full"> </section>`;
|
||
const expected = /*markdown*/`<section class="include-note" data-note-id="i4A5g9iOg9I0" data-box-size="full"> </section>`;
|
||
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
||
});
|
||
|
||
it("exports todo lists properly", () => {
|
||
const html = trimIndentation/*html*/`\
|
||
<ul class="todo-list">
|
||
<li>
|
||
<label class="todo-list__label">
|
||
<input type="checkbox" checked="checked" disabled="disabled"><span class="todo-list__label__description">Hello</span>
|
||
</label>
|
||
</li>
|
||
<li>
|
||
<label class="todo-list__label">
|
||
<input type="checkbox" disabled="disabled"><span class="todo-list__label__description">World</span>
|
||
</label>
|
||
</li>
|
||
</ul>
|
||
`;
|
||
const expected = trimIndentation`\
|
||
- [x] Hello
|
||
- [ ] World`;
|
||
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
||
});
|
||
|
||
});
|