95 lines
3.0 KiB
TypeScript
95 lines
3.0 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import { renderMarkdown } from '@/lib/markdown'
|
|
import { applyMarkdownAction } from '@/lib/markdownEdit'
|
|
|
|
describe('renderMarkdown', () => {
|
|
it('renders bold text and a bulleted list', () => {
|
|
const html = renderMarkdown(
|
|
'Two days with **lime putty**.\n\n- Bring work clothes\n- Lunch',
|
|
)
|
|
|
|
expect(html).toContain('<strong>lime putty</strong>')
|
|
expect(html.match(/<li>/g)).toHaveLength(2)
|
|
})
|
|
|
|
it('shows raw HTML as text', () => {
|
|
const html = renderMarkdown('<script>alert(1)</script>')
|
|
|
|
expect(html).not.toContain('<script>')
|
|
expect(html).toContain('<script>')
|
|
})
|
|
|
|
it.each(['https://example.com', 'http://example.com', 'mailto:a@b.co'])(
|
|
'keeps the link %s active',
|
|
(url) => {
|
|
expect(renderMarkdown(`[x](${url})`)).toContain(`href="${url}"`)
|
|
},
|
|
)
|
|
|
|
it.each(['javascript:alert(1)', 'data:text/html,x', 'vbscript:x', '/relative', 'url'])(
|
|
'leaves the link %s inert',
|
|
(url) => {
|
|
expect(renderMarkdown(`[x](${url})`)).not.toContain('<a ')
|
|
},
|
|
)
|
|
|
|
it('opens links in a new tab without leaking the opener', () => {
|
|
const html = renderMarkdown('[x](https://example.com)')
|
|
|
|
expect(html).toContain('target="_blank"')
|
|
expect(html).toContain('rel="noopener noreferrer"')
|
|
})
|
|
|
|
it('does not load images', () => {
|
|
expect(renderMarkdown('')).not.toContain('<img')
|
|
})
|
|
})
|
|
|
|
describe('applyMarkdownAction', () => {
|
|
it.each([
|
|
['bold', '**lime**'],
|
|
['italic', '_lime_'],
|
|
['heading', '## lime'],
|
|
['link', '[lime](url)'],
|
|
['list', '- lime'],
|
|
] as const)('%s turns the selection into %s', (action, result) => {
|
|
expect(applyMarkdownAction(action, 'lime', 0, 4).value).toBe(result)
|
|
})
|
|
|
|
it('selects the url placeholder of a link', () => {
|
|
const edit = applyMarkdownAction('link', 'see lime now', 4, 8)
|
|
|
|
expect(edit.value.slice(edit.start, edit.end)).toBe('url')
|
|
})
|
|
|
|
it('prefixes every selected line of a list', () => {
|
|
expect(applyMarkdownAction('list', 'a\nb', 0, 3).value).toBe('- a\n- b')
|
|
})
|
|
|
|
it('puts the heading mark at the start of the line', () => {
|
|
expect(applyMarkdownAction('heading', 'one\ntwo lime', 8, 12).value).toBe(
|
|
'one\n## two lime',
|
|
)
|
|
})
|
|
|
|
it('moves the selection past the list marker of the first line', () => {
|
|
const edit = applyMarkdownAction('list', 'a\nb', 0, 3)
|
|
|
|
expect([edit.start, edit.end]).toEqual([2, 7])
|
|
})
|
|
|
|
it('prefixes only the current line when the cursor sits right after a line break', () => {
|
|
expect(applyMarkdownAction('list', 'one\ntwo', 4, 4).value).toBe('one\n- two')
|
|
expect(applyMarkdownAction('heading', 'one\ntwo', 4, 4).value).toBe('one\n## two')
|
|
})
|
|
|
|
it('prefixes the line the cursor ends, not the next one', () => {
|
|
expect(applyMarkdownAction('heading', 'one\ntwo', 3, 3).value).toBe('## one\ntwo')
|
|
})
|
|
|
|
it('prefixes the first line when it is empty and the caret sits at the start', () => {
|
|
expect(applyMarkdownAction('heading', '\nfoo', 0, 0).value).toBe('## \nfoo')
|
|
expect(applyMarkdownAction('list', '\nfoo', 0, 0).value).toBe('- \nfoo')
|
|
})
|
|
})
|