mirror of
https://github.com/bulwarkmail/native.git
synced 2026-11-05 08:06:58 +00:00
71 lines
3.3 KiB
TypeScript
71 lines
3.3 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { markdownToHtml, renderInline } from '../markdown-html';
|
|
|
|
const body = (md: string) => {
|
|
const html = markdownToHtml(md);
|
|
return html.slice(html.indexOf('<body>') + 6, html.indexOf('</body>'));
|
|
};
|
|
|
|
describe('markdownToHtml', () => {
|
|
it('renders headings, paragraphs and rules', () => {
|
|
expect(body('# Title\n\nSome text\nwrapped.\n\n---\n\n### Small ###'))
|
|
.toBe('<h1>Title</h1>\n<p>Some text\nwrapped.</p>\n<hr>\n<h3>Small</h3>');
|
|
expect(body('Big\n===\n\nSmaller\n---')).toBe('<h1>Big</h1>\n<h2>Smaller</h2>');
|
|
});
|
|
|
|
it('renders nested and ordered lists and task items', () => {
|
|
expect(body('- one\n- two\n - nested\n- three')).toBe(
|
|
'<ul><li>one</li><li>two\n<ul><li>nested</li></ul></li><li>three</li></ul>',
|
|
);
|
|
expect(body('3. c\n4. d')).toBe('<ol start="3"><li>c</li><li>d</li></ol>');
|
|
expect(body('- [x] done\n- [ ] open')).toBe('<ul><li class="task">\u2611 done</li><li class="task">\u2610 open</li></ul>');
|
|
});
|
|
|
|
it('keeps fenced and indented code verbatim and escaped', () => {
|
|
expect(body('```js\nconst a = "<b>";\n**not bold**\n```')).toBe(
|
|
'<pre><code class="language-js">const a = "<b>";\n**not bold**</code></pre>',
|
|
);
|
|
expect(body(' x < 1')).toBe('<pre><code>x < 1</code></pre>');
|
|
});
|
|
|
|
it('renders quotes and GFM tables with alignment', () => {
|
|
expect(body('> quoted\n> **text**')).toBe('<blockquote><p>quoted\n<strong>text</strong></p></blockquote>');
|
|
expect(body('| a | b |\n|:--|--:|\n| 1 | 2 |')).toBe(
|
|
'<table><thead><tr><th style="text-align:left">a</th><th style="text-align:right">b</th></tr></thead>'
|
|
+ '<tbody><tr><td style="text-align:left">1</td><td style="text-align:right">2</td></tr></tbody></table>',
|
|
);
|
|
});
|
|
|
|
it('escapes raw HTML instead of passing it through', () => {
|
|
expect(body('<script>alert(1)</script>')).toBe('<p><script>alert(1)</script></p>');
|
|
expect(body('<img src=x onerror=alert(1)>')).not.toContain('<img');
|
|
});
|
|
});
|
|
|
|
describe('renderInline', () => {
|
|
it('renders emphasis, code and strikethrough', () => {
|
|
expect(renderInline('**b** *i* _j_ `c*d*` ~~s~~')).toBe(
|
|
'<strong>b</strong> <em>i</em> <em>j</em> <code>c*d*</code> <del>s</del>',
|
|
);
|
|
expect(renderInline('snake_case_name stays')).toBe('snake_case_name stays');
|
|
expect(renderInline('\\*literal\\*')).toBe('*literal*');
|
|
});
|
|
|
|
it('links only to web and mail URLs', () => {
|
|
expect(renderInline('[site](https://example.org "T")')).toBe('<a href="https://example.org" title="T">site</a>');
|
|
expect(renderInline('[x](javascript:alert(1))')).toBe('x)');
|
|
expect(renderInline('[doc](./docs/a.md)')).toBe('doc');
|
|
expect(renderInline('see https://example.org/a.')).toBe('see <a href="https://example.org/a">https://example.org/a</a>.');
|
|
expect(renderInline('<mailto:a@b.c>')).toBe('<a href="mailto:a@b.c">mailto:a@b.c</a>');
|
|
});
|
|
|
|
it('keeps image URLs for the sanitiser and drops unsafe ones', () => {
|
|
expect(renderInline('')).toBe('<img src="https://example.org/l.png" alt="logo">');
|
|
expect(renderInline('')).toBe('x');
|
|
});
|
|
|
|
it('escapes attribute breakouts in link text and titles', () => {
|
|
expect(renderInline('[a"b](https://e.org "x\\" onmouseover=\\"y")')).not.toMatch(/onmouseover="/);
|
|
});
|
|
});
|