aboutsummaryrefslogtreecommitdiff
path: root/packages/markdown/remark/test
diff options
context:
space:
mode:
Diffstat (limited to 'packages/markdown/remark/test')
-rw-r--r--packages/markdown/remark/test/autolinking.test.js43
-rw-r--r--packages/markdown/remark/test/browser.test.js20
-rw-r--r--packages/markdown/remark/test/entities.test.js18
-rw-r--r--packages/markdown/remark/test/frontmatter.test.js189
-rw-r--r--packages/markdown/remark/test/plugins.test.js28
-rw-r--r--packages/markdown/remark/test/remark-collect-images.test.js42
-rw-r--r--packages/markdown/remark/test/shiki.test.js140
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 = `&lt;i&gt;This should NOT be italic&lt;/i&gt;`;
+ const { code } = await processor.render(markdown);
+
+ assert.equal(code, `<p>&#x3C;i>This should NOT be italic&#x3C;/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 ![inline image url](./img.png)`;
+ const fileURL = 'file.md';
+
+ const {
+ code,
+ metadata: { imagePaths },
+ } = await processor.render(markdown, { fileURL });
+
+ assert.equal(
+ code,
+ '<p>Hello <img __ASTRO_IMAGE_="{&#x22;src&#x22;:&#x22;./img.png&#x22;,&#x22;alt&#x22;:&#x22;inline image url&#x22;,&#x22;index&#x22;: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_="{&#x22;src&#x22;:&#x22;./img.webp&#x22;,&#x22;alt&#x22;:&#x22;image ref&#x22;,&#x22;index&#x22;: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"/);
+ });
+});