diff options
Diffstat (limited to 'packages/markdown/remark/test')
-rw-r--r-- | packages/markdown/remark/test/autolinking.test.js | 43 | ||||
-rw-r--r-- | packages/markdown/remark/test/browser.test.js | 20 | ||||
-rw-r--r-- | packages/markdown/remark/test/entities.test.js | 18 | ||||
-rw-r--r-- | packages/markdown/remark/test/frontmatter.test.js | 189 | ||||
-rw-r--r-- | packages/markdown/remark/test/plugins.test.js | 28 | ||||
-rw-r--r-- | packages/markdown/remark/test/remark-collect-images.test.js | 42 | ||||
-rw-r--r-- | packages/markdown/remark/test/shiki.test.js | 140 |
7 files changed, 480 insertions, 0 deletions
diff --git a/packages/markdown/remark/test/autolinking.test.js b/packages/markdown/remark/test/autolinking.test.js new file mode 100644 index 000000000..3fd5ad0fc --- /dev/null +++ b/packages/markdown/remark/test/autolinking.test.js @@ -0,0 +1,43 @@ +import assert from 'node:assert/strict'; +import { before, describe, it } from 'node:test'; +import { createMarkdownProcessor } from '../dist/index.js'; + +describe('autolinking', () => { + describe('plain md', () => { + let processor; + + before(async () => { + processor = await createMarkdownProcessor(); + }); + + it('autolinks URLs starting with a protocol in plain text', async () => { + const markdown = `See https://example.com for more.`; + const { code } = await processor.render(markdown); + + assert.equal( + code.replace(/\n/g, ''), + `<p>See <a href="https://example.com">https://example.com</a> for more.</p>`, + ); + }); + + it('autolinks URLs starting with "www." in plain text', async () => { + const markdown = `See www.example.com for more.`; + const { code } = await processor.render(markdown); + + assert.equal( + code.trim(), + `<p>See <a href="http://www.example.com">www.example.com</a> for more.</p>`, + ); + }); + + it('does not autolink URLs in code blocks', async () => { + const markdown = `See \`https://example.com\` or \`www.example.com\` for more.`; + const { code } = await processor.render(markdown); + + assert.equal( + code.trim(), + `<p>See <code>https://example.com</code> or <code>www.example.com</code> for more.</p>`, + ); + }); + }); +}); diff --git a/packages/markdown/remark/test/browser.test.js b/packages/markdown/remark/test/browser.test.js new file mode 100644 index 000000000..824f6fa0b --- /dev/null +++ b/packages/markdown/remark/test/browser.test.js @@ -0,0 +1,20 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import esbuild from 'esbuild'; + +describe('Bundle for browsers', async () => { + it('esbuild browser build should work', async () => { + try { + const result = await esbuild.build({ + platform: 'browser', + entryPoints: ['@astrojs/markdown-remark'], + bundle: true, + write: false, + }); + assert.ok(result.outputFiles.length > 0); + } catch (error) { + // Capture any esbuild errors and fail the test + assert.fail(error.message); + } + }); +}); diff --git a/packages/markdown/remark/test/entities.test.js b/packages/markdown/remark/test/entities.test.js new file mode 100644 index 000000000..3c244c15a --- /dev/null +++ b/packages/markdown/remark/test/entities.test.js @@ -0,0 +1,18 @@ +import assert from 'node:assert/strict'; +import { before, describe, it } from 'node:test'; +import { createMarkdownProcessor } from '../dist/index.js'; + +describe('entities', async () => { + let processor; + + before(async () => { + processor = await createMarkdownProcessor(); + }); + + it('should not unescape entities in regular Markdown', async () => { + const markdown = `<i>This should NOT be italic</i>`; + const { code } = await processor.render(markdown); + + assert.equal(code, `<p><i>This should NOT be italic</i></p>`); + }); +}); diff --git a/packages/markdown/remark/test/frontmatter.test.js b/packages/markdown/remark/test/frontmatter.test.js new file mode 100644 index 000000000..336245106 --- /dev/null +++ b/packages/markdown/remark/test/frontmatter.test.js @@ -0,0 +1,189 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { extractFrontmatter, parseFrontmatter } from '../dist/index.js'; + +const bom = '\uFEFF'; + +describe('extractFrontmatter', () => { + it('handles YAML', () => { + const yaml = `\nfoo: bar\n`; + assert.equal(extractFrontmatter(`---${yaml}---`), yaml); + assert.equal(extractFrontmatter(`${bom}---${yaml}---`), yaml); + assert.equal(extractFrontmatter(`\n---${yaml}---`), yaml); + assert.equal(extractFrontmatter(`\n \n---${yaml}---`), yaml); + assert.equal(extractFrontmatter(`---${yaml}---\ncontent`), yaml); + assert.equal(extractFrontmatter(`${bom}---${yaml}---\ncontent`), yaml); + assert.equal(extractFrontmatter(`\n\n---${yaml}---\n\ncontent`), yaml); + assert.equal(extractFrontmatter(`\n \n---${yaml}---\n\ncontent`), yaml); + assert.equal(extractFrontmatter(` ---${yaml}---`), undefined); + assert.equal(extractFrontmatter(`---${yaml} ---`), undefined); + assert.equal(extractFrontmatter(`text\n---${yaml}---\n\ncontent`), undefined); + }); + + it('handles TOML', () => { + const toml = `\nfoo = "bar"\n`; + assert.equal(extractFrontmatter(`+++${toml}+++`), toml); + assert.equal(extractFrontmatter(`${bom}+++${toml}+++`), toml); + assert.equal(extractFrontmatter(`\n+++${toml}+++`), toml); + assert.equal(extractFrontmatter(`\n \n+++${toml}+++`), toml); + assert.equal(extractFrontmatter(`+++${toml}+++\ncontent`), toml); + assert.equal(extractFrontmatter(`${bom}+++${toml}+++\ncontent`), toml); + assert.equal(extractFrontmatter(`\n\n+++${toml}+++\n\ncontent`), toml); + assert.equal(extractFrontmatter(`\n \n+++${toml}+++\n\ncontent`), toml); + assert.equal(extractFrontmatter(` +++${toml}+++`), undefined); + assert.equal(extractFrontmatter(`+++${toml} +++`), undefined); + assert.equal(extractFrontmatter(`text\n+++${toml}+++\n\ncontent`), undefined); + }); +}); + +describe('parseFrontmatter', () => { + it('works for YAML', () => { + const yaml = `\nfoo: bar\n`; + assert.deepEqual(parseFrontmatter(`---${yaml}---`), { + frontmatter: { foo: 'bar' }, + rawFrontmatter: yaml, + content: '', + }); + assert.deepEqual(parseFrontmatter(`${bom}---${yaml}---`), { + frontmatter: { foo: 'bar' }, + rawFrontmatter: yaml, + content: bom, + }); + assert.deepEqual(parseFrontmatter(`\n---${yaml}---`), { + frontmatter: { foo: 'bar' }, + rawFrontmatter: yaml, + content: '\n', + }); + assert.deepEqual(parseFrontmatter(`\n \n---${yaml}---`), { + frontmatter: { foo: 'bar' }, + rawFrontmatter: yaml, + content: '\n \n', + }); + assert.deepEqual(parseFrontmatter(`---${yaml}---\ncontent`), { + frontmatter: { foo: 'bar' }, + rawFrontmatter: yaml, + content: '\ncontent', + }); + assert.deepEqual(parseFrontmatter(`${bom}---${yaml}---\ncontent`), { + frontmatter: { foo: 'bar' }, + rawFrontmatter: yaml, + content: `${bom}\ncontent`, + }); + assert.deepEqual(parseFrontmatter(`\n\n---${yaml}---\n\ncontent`), { + frontmatter: { foo: 'bar' }, + rawFrontmatter: yaml, + content: '\n\n\n\ncontent', + }); + assert.deepEqual(parseFrontmatter(`\n \n---${yaml}---\n\ncontent`), { + frontmatter: { foo: 'bar' }, + rawFrontmatter: yaml, + content: '\n \n\n\ncontent', + }); + assert.deepEqual(parseFrontmatter(` ---${yaml}---`), { + frontmatter: {}, + rawFrontmatter: '', + content: ` ---${yaml}---`, + }); + assert.deepEqual(parseFrontmatter(`---${yaml} ---`), { + frontmatter: {}, + rawFrontmatter: '', + content: `---${yaml} ---`, + }); + assert.deepEqual(parseFrontmatter(`text\n---${yaml}---\n\ncontent`), { + frontmatter: {}, + rawFrontmatter: '', + content: `text\n---${yaml}---\n\ncontent`, + }); + }); + + it('works for TOML', () => { + const toml = `\nfoo = "bar"\n`; + assert.deepEqual(parseFrontmatter(`+++${toml}+++`), { + frontmatter: { foo: 'bar' }, + rawFrontmatter: toml, + content: '', + }); + assert.deepEqual(parseFrontmatter(`${bom}+++${toml}+++`), { + frontmatter: { foo: 'bar' }, + rawFrontmatter: toml, + content: bom, + }); + assert.deepEqual(parseFrontmatter(`\n+++${toml}+++`), { + frontmatter: { foo: 'bar' }, + rawFrontmatter: toml, + content: '\n', + }); + assert.deepEqual(parseFrontmatter(`\n \n+++${toml}+++`), { + frontmatter: { foo: 'bar' }, + rawFrontmatter: toml, + content: '\n \n', + }); + assert.deepEqual(parseFrontmatter(`+++${toml}+++\ncontent`), { + frontmatter: { foo: 'bar' }, + rawFrontmatter: toml, + content: '\ncontent', + }); + assert.deepEqual(parseFrontmatter(`${bom}+++${toml}+++\ncontent`), { + frontmatter: { foo: 'bar' }, + rawFrontmatter: toml, + content: `${bom}\ncontent`, + }); + assert.deepEqual(parseFrontmatter(`\n\n+++${toml}+++\n\ncontent`), { + frontmatter: { foo: 'bar' }, + rawFrontmatter: toml, + content: '\n\n\n\ncontent', + }); + assert.deepEqual(parseFrontmatter(`\n \n+++${toml}+++\n\ncontent`), { + frontmatter: { foo: 'bar' }, + rawFrontmatter: toml, + content: '\n \n\n\ncontent', + }); + assert.deepEqual(parseFrontmatter(` +++${toml}+++`), { + frontmatter: {}, + rawFrontmatter: '', + content: ` +++${toml}+++`, + }); + assert.deepEqual(parseFrontmatter(`+++${toml} +++`), { + frontmatter: {}, + rawFrontmatter: '', + content: `+++${toml} +++`, + }); + assert.deepEqual(parseFrontmatter(`text\n+++${toml}+++\n\ncontent`), { + frontmatter: {}, + rawFrontmatter: '', + content: `text\n+++${toml}+++\n\ncontent`, + }); + }); + + it('frontmatter style for YAML', () => { + const yaml = `\nfoo: bar\n`; + const parse1 = (style) => parseFrontmatter(`---${yaml}---`, { frontmatter: style }).content; + assert.deepEqual(parse1('preserve'), `---${yaml}---`); + assert.deepEqual(parse1('remove'), ''); + assert.deepEqual(parse1('empty-with-spaces'), ` \n \n `); + assert.deepEqual(parse1('empty-with-lines'), `\n\n`); + + const parse2 = (style) => + parseFrontmatter(`\n \n---${yaml}---\n\ncontent`, { frontmatter: style }).content; + assert.deepEqual(parse2('preserve'), `\n \n---${yaml}---\n\ncontent`); + assert.deepEqual(parse2('remove'), '\n \n\n\ncontent'); + assert.deepEqual(parse2('empty-with-spaces'), `\n \n \n \n \n\ncontent`); + assert.deepEqual(parse2('empty-with-lines'), `\n \n\n\n\n\ncontent`); + }); + + it('frontmatter style for TOML', () => { + const toml = `\nfoo = "bar"\n`; + const parse1 = (style) => parseFrontmatter(`+++${toml}+++`, { frontmatter: style }).content; + assert.deepEqual(parse1('preserve'), `+++${toml}+++`); + assert.deepEqual(parse1('remove'), ''); + assert.deepEqual(parse1('empty-with-spaces'), ` \n \n `); + assert.deepEqual(parse1('empty-with-lines'), `\n\n`); + + const parse2 = (style) => + parseFrontmatter(`\n \n+++${toml}+++\n\ncontent`, { frontmatter: style }).content; + assert.deepEqual(parse2('preserve'), `\n \n+++${toml}+++\n\ncontent`); + assert.deepEqual(parse2('remove'), '\n \n\n\ncontent'); + assert.deepEqual(parse2('empty-with-spaces'), `\n \n \n \n \n\ncontent`); + assert.deepEqual(parse2('empty-with-lines'), `\n \n\n\n\n\ncontent`); + }); +}); diff --git a/packages/markdown/remark/test/plugins.test.js b/packages/markdown/remark/test/plugins.test.js new file mode 100644 index 000000000..c52955f83 --- /dev/null +++ b/packages/markdown/remark/test/plugins.test.js @@ -0,0 +1,28 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { fileURLToPath } from 'node:url'; +import { createMarkdownProcessor } from '../dist/index.js'; + +describe('plugins', () => { + it('should be able to get file path when passing fileURL', async () => { + let context; + + const processor = await createMarkdownProcessor({ + remarkPlugins: [ + () => { + const transformer = (_tree, file) => { + context = file; + }; + return transformer; + }, + ], + }); + + await processor.render(`test`, { + fileURL: new URL('virtual.md', import.meta.url), + }); + + assert.ok(typeof context === 'object'); + assert.equal(context.path, fileURLToPath(new URL('virtual.md', import.meta.url))); + }); +}); diff --git a/packages/markdown/remark/test/remark-collect-images.test.js b/packages/markdown/remark/test/remark-collect-images.test.js new file mode 100644 index 000000000..669bee595 --- /dev/null +++ b/packages/markdown/remark/test/remark-collect-images.test.js @@ -0,0 +1,42 @@ +import assert from 'node:assert/strict'; +import { before, describe, it } from 'node:test'; +import { createMarkdownProcessor } from '../dist/index.js'; + +describe('collect images', async () => { + let processor; + + before(async () => { + processor = await createMarkdownProcessor(); + }); + + it('should collect inline image paths', async () => { + const markdown = `Hello `; + const fileURL = 'file.md'; + + const { + code, + metadata: { imagePaths }, + } = await processor.render(markdown, { fileURL }); + + assert.equal( + code, + '<p>Hello <img __ASTRO_IMAGE_="{"src":"./img.png","alt":"inline image url","index":0}"></p>', + ); + + assert.deepEqual(imagePaths, ['./img.png']); + }); + + it('should add image paths from definition', async () => { + const markdown = `Hello ![image ref][img-ref]\n\n[img-ref]: ./img.webp`; + const fileURL = 'file.md'; + + const { code, metadata } = await processor.render(markdown, { fileURL }); + + assert.equal( + code, + '<p>Hello <img __ASTRO_IMAGE_="{"src":"./img.webp","alt":"image ref","index":0}"></p>', + ); + + assert.deepEqual(metadata.imagePaths, ['./img.webp']); + }); +}); diff --git a/packages/markdown/remark/test/shiki.test.js b/packages/markdown/remark/test/shiki.test.js new file mode 100644 index 000000000..e230b2982 --- /dev/null +++ b/packages/markdown/remark/test/shiki.test.js @@ -0,0 +1,140 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; +import { createMarkdownProcessor, createShikiHighlighter } from '../dist/index.js'; + +describe('shiki syntax highlighting', () => { + it('does not add is:raw to the output', async () => { + const processor = await createMarkdownProcessor(); + const { code } = await processor.render('```\ntest\n```'); + + assert.ok(!code.includes('is:raw')); + }); + + it('supports light/dark themes', async () => { + const processor = await createMarkdownProcessor({ + shikiConfig: { + themes: { + light: 'github-light', + dark: 'github-dark', + }, + }, + }); + const { code } = await processor.render('```\ntest\n```'); + + // light theme is there: + assert.match(code, /background-color:/); + assert.match(code, /github-light/); + + // dark theme is there: + assert.match(code, /--shiki-dark-bg:/); + assert.match(code, /github-dark/); + }); + + it('createShikiHighlighter works', async () => { + const highlighter = await createShikiHighlighter(); + + const html = await highlighter.codeToHtml('const foo = "bar";', 'js'); + + assert.match(html, /astro-code github-dark/); + assert.match(html, /background-color:#24292e;color:#e1e4e8;/); + }); + + it('createShikiHighlighter works with codeToHast', async () => { + const highlighter = await createShikiHighlighter(); + + const hast = await highlighter.codeToHast('const foo = "bar";', 'js'); + + assert.match(hast.children[0].properties.class, /astro-code github-dark/); + assert.match(hast.children[0].properties.style, /background-color:#24292e;color:#e1e4e8;/); + }); + + it('diff +/- text has user-select: none', async () => { + const highlighter = await createShikiHighlighter(); + + const html = await highlighter.codeToHtml( + `\ +- const foo = "bar"; ++ const foo = "world";`, + 'diff', + ); + + assert.match(html, /user-select: none/); + assert.match(html, />-<\/span>/); + assert.match(html, />+<\/span>/); + }); + + it('renders attributes', async () => { + const highlighter = await createShikiHighlighter(); + + const html = await highlighter.codeToHtml(`foo`, 'js', { + attributes: { 'data-foo': 'bar', autofocus: true }, + }); + + assert.match(html, /data-foo="bar"/); + assert.match(html, /autofocus(?!=)/); + }); + + it('supports transformers that reads meta', async () => { + const highlighter = await createShikiHighlighter(); + + const html = await highlighter.codeToHtml(`foo`, 'js', { + meta: '{1,3-4}', + transformers: [ + { + pre(node) { + const meta = this.options.meta?.__raw; + if (meta) { + node.properties['data-test'] = meta; + } + }, + }, + ], + }); + + assert.match(html, /data-test="\{1,3-4\}"/); + }); + + it('supports the defaultColor setting', async () => { + const processor = await createMarkdownProcessor({ + shikiConfig: { + themes: { + light: 'github-light', + dark: 'github-dark', + }, + defaultColor: false, + }, + }); + const { code } = await processor.render('```\ntest\n```'); + + // Doesn't have `color` or `background-color` properties. + assert.doesNotMatch(code, /color:/); + }); + + it('the highlighter supports lang alias', async () => { + const highlighter = await createShikiHighlighter({ + langAlias: { + cjs: 'javascript', + }, + }); + + const html = await highlighter.codeToHtml(`let test = "some string"`, 'cjs', { + attributes: { 'data-foo': 'bar', autofocus: true }, + }); + + assert.match(html, /data-language="cjs"/); + }); + + it('the markdown processor support lang alias', async () => { + const processor = await createMarkdownProcessor({ + shikiConfig: { + langAlias: { + cjs: 'javascript', + }, + }, + }); + + const { code } = await processor.render('```cjs\nlet foo = "bar"\n```'); + + assert.match(code, /data-language="cjs"/); + }); +}); |