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`\

A diff:

Hello
                -world
                +worldy
                
`; 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`\
Hello
            -world
            +worldy
            
`; const expected = trimIndentation`\ \`\`\` Hello -world +worldy \`\`\``; expect(markdownExportService.toMarkdown(html)).toBe(expected); }); it("supports code block with no language tag", () => { const html = trimIndentation`\
Hello
`; const expected = trimIndentation`\ \`\`\` Hello \`\`\``; expect(markdownExportService.toMarkdown(html)).toBe(expected); }); it("exports strikethrough text correctly", () => { const html = "helloHello world"; const expected = "~~hello~~Hello ~~world~~"; expect(markdownExportService.toMarkdown(html)).toBe(expected); }); it("exports headings properly", () => { const html = trimIndentation`\

Heading 1

Heading 2

Heading 3

Heading 4

Heading 5
Heading 6
`; 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 = ``; const expected = `![](Hello%20world%20%20.png)`; expect(markdownExportService.toMarkdown(html)).toBe(expected); }); it("supports keyboard shortcuts", () => { const html = "Ctrl + Alt + Delete"; expect(markdownExportService.toMarkdown(html)).toBe(html); }); it("exports admonitions properly", () => { const html = trimIndentation`\

Before

After

`; 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} >
12
34
> [!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`\
Row 1

Allows displaying the value of one or more attributes in the calendar like this: 

#weight="70"
                    #Mood="Good"
                    #calendar:displayedAttributes="weight,Mood"

It can also be used with relations, case in which it will display the title of the target note:

~assignee=@My assignee
                    #calendar:displayedAttributes="assignee"
`; const expected = trimIndentation`\
Row 1

Allows displaying the value of one or more attributes in the calendar like this: 

#weight="70"
                        #Mood="Good"
                        #calendar:displayedAttributes="weight,Mood"

It can also be used with relations, case in which it will display the title of the target note:

~assignee=@My assignee
                        #calendar:displayedAttributes="assignee"
`; expect(markdownExportService.toMarkdown(html)).toBe(expected); }); it("converts   to character", () => { const html = /*html*/`

Hello world.

`; const expected = `Hello\u00a0world.`; expect(markdownExportService.toMarkdown(html)).toBe(expected); }); it("preserves non-breaking space character", () => { const html = /*html*/`

Hello\u00adworld.

`; const expected = `Hello\u00adworld.`; expect(markdownExportService.toMarkdown(html)).toBe(expected); }); it("exports normal links verbatim", () => { const html = /*html*/`

Google

`; const expected = `[Google](https://www.google.com)`; expect(markdownExportService.toMarkdown(html)).toBe(expected); }); it("exports reference links verbatim", () => { const html = /*html*/`

Canvas

`; const expected = `Canvas`; expect(markdownExportService.toMarkdown(html)).toBe(expected); }); it("converts image if it has no custom properties", () => { const html = /*html*/`

`; const expected = `![](Include%20Note_image.png)`; expect(markdownExportService.toMarkdown(html)).toBe(expected); }); it("preserves image verbatim if it has a width or height attribute", () => { const scenarios = [ /*html*/``, /*html*/``, /*html*/``, /*html*/`` ]; for (const expected of scenarios) { const html = /*html*/`

${expected}

`; expect(markdownExportService.toMarkdown(html)).toBe(expected); } }); it("preserves figures", () => { const html = /*html*/trimIndentation`\
`; const expected = `
`; expect(markdownExportService.toMarkdown(html)).toBe(expected); }); it("converts inline math expressions into proper Markdown syntax", () => { const html = /*html*/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}}\)`; 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`\[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}}\]`; 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`\ `; 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*/`
 
`; const expected = /*markdown*/`
 
`; expect(markdownExportService.toMarkdown(html)).toBe(expected); }); it("exports todo lists properly", () => { const html = trimIndentation/*html*/`\ `; const expected = trimIndentation`\ - [x] Hello - [ ] World`; expect(markdownExportService.toMarkdown(html)).toBe(expected); }); });