diff options
author | 2023-05-17 09:13:10 -0400 | |
---|---|---|
committer | 2023-05-17 09:13:10 -0400 | |
commit | fb84622af04f795de8d17f24192de105f70fe910 (patch) | |
tree | 11a99efdb90c17207d3adc1095e88fa8daddd7e4 /packages/integrations/markdoc/test | |
parent | c91e837e961043e92253148f0f4291856653b993 (diff) | |
download | astro-fb84622af04f795de8d17f24192de105f70fe910.tar.gz astro-fb84622af04f795de8d17f24192de105f70fe910.tar.zst astro-fb84622af04f795de8d17f24192de105f70fe910.zip |
[Markdoc] `headings` and heading IDs (#7095)
* deps: markdown-remark
* wip: heading-ids function
* chore: add `@astrojs/markdoc` to external
* feat: `headings` support
* fix: allow `render` config on headings
* fix: nonexistent `userConfig`
* test: headings, toc, astro component render
* docs: README
* chore: changeset
* refactor: expose Markdoc helpers from runtime
* fix: bad named exports (commonjsssss)
* refactor: defaultNodes -> nodes
* deps: github-slugger
* fix: reset slugger cache on each render
* fix: bad astroNodes import
* docs: explain headingSlugger export
* docs: add back double stringify comment
* chore: bump to minor for internal exports change
Diffstat (limited to 'packages/integrations/markdoc/test')
12 files changed, 330 insertions, 0 deletions
diff --git a/packages/integrations/markdoc/test/fixtures/headings-custom/astro.config.mjs b/packages/integrations/markdoc/test/fixtures/headings-custom/astro.config.mjs new file mode 100644 index 000000000..29d846359 --- /dev/null +++ b/packages/integrations/markdoc/test/fixtures/headings-custom/astro.config.mjs @@ -0,0 +1,7 @@ +import { defineConfig } from 'astro/config'; +import markdoc from '@astrojs/markdoc'; + +// https://astro.build/config +export default defineConfig({ + integrations: [markdoc()], +}); diff --git a/packages/integrations/markdoc/test/fixtures/headings-custom/markdoc.config.mjs b/packages/integrations/markdoc/test/fixtures/headings-custom/markdoc.config.mjs new file mode 100644 index 000000000..32fcf61e2 --- /dev/null +++ b/packages/integrations/markdoc/test/fixtures/headings-custom/markdoc.config.mjs @@ -0,0 +1,11 @@ +import { defineMarkdocConfig, nodes } from '@astrojs/markdoc/config'; +import Heading from './src/components/Heading.astro'; + +export default defineMarkdocConfig({ + nodes: { + heading: { + ...nodes.heading, + render: Heading, + } + } +}); diff --git a/packages/integrations/markdoc/test/fixtures/headings-custom/package.json b/packages/integrations/markdoc/test/fixtures/headings-custom/package.json new file mode 100644 index 000000000..67a974912 --- /dev/null +++ b/packages/integrations/markdoc/test/fixtures/headings-custom/package.json @@ -0,0 +1,9 @@ +{ + "name": "@test/headings-custom", + "version": "0.0.0", + "private": true, + "dependencies": { + "@astrojs/markdoc": "workspace:*", + "astro": "workspace:*" + } +} diff --git a/packages/integrations/markdoc/test/fixtures/headings-custom/src/components/Heading.astro b/packages/integrations/markdoc/test/fixtures/headings-custom/src/components/Heading.astro new file mode 100644 index 000000000..ec6fa8305 --- /dev/null +++ b/packages/integrations/markdoc/test/fixtures/headings-custom/src/components/Heading.astro @@ -0,0 +1,14 @@ +--- +type Props = { + level: number; + id: string; +}; + +const { level, id }: Props = Astro.props; + +const Tag = `h${level}`; +--- + +<Tag data-custom-heading {id}> + <slot /> +</Tag> diff --git a/packages/integrations/markdoc/test/fixtures/headings-custom/src/content/docs/headings.mdoc b/packages/integrations/markdoc/test/fixtures/headings-custom/src/content/docs/headings.mdoc new file mode 100644 index 000000000..3eb66580a --- /dev/null +++ b/packages/integrations/markdoc/test/fixtures/headings-custom/src/content/docs/headings.mdoc @@ -0,0 +1,11 @@ +# Level 1 heading + +## Level **2 heading** + +### Level _3 heading_ + +#### Level [4 heading](/with-a-link) + +##### Level 5 heading with override {% #id-override %} + +###### Level 6 heading diff --git a/packages/integrations/markdoc/test/fixtures/headings-custom/src/pages/index.astro b/packages/integrations/markdoc/test/fixtures/headings-custom/src/pages/index.astro new file mode 100644 index 000000000..5880be0e3 --- /dev/null +++ b/packages/integrations/markdoc/test/fixtures/headings-custom/src/pages/index.astro @@ -0,0 +1,28 @@ +--- +import { getEntryBySlug } from "astro:content"; + +const post = await getEntryBySlug('docs', 'headings'); +const { Content, headings } = await post.render(); +--- + +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Content</title> +</head> +<body> + <nav data-toc> + <ul> + {headings.map(heading => ( + <li> + <a href={`#${heading.slug}`} data-depth={heading.depth}>{heading.text}</a> + </li> + ))} + </ul> + </nav> + <Content /> +</body> +</html> diff --git a/packages/integrations/markdoc/test/fixtures/headings/astro.config.mjs b/packages/integrations/markdoc/test/fixtures/headings/astro.config.mjs new file mode 100644 index 000000000..29d846359 --- /dev/null +++ b/packages/integrations/markdoc/test/fixtures/headings/astro.config.mjs @@ -0,0 +1,7 @@ +import { defineConfig } from 'astro/config'; +import markdoc from '@astrojs/markdoc'; + +// https://astro.build/config +export default defineConfig({ + integrations: [markdoc()], +}); diff --git a/packages/integrations/markdoc/test/fixtures/headings/markdoc.config.mjs b/packages/integrations/markdoc/test/fixtures/headings/markdoc.config.mjs new file mode 100644 index 000000000..a5863ec12 --- /dev/null +++ b/packages/integrations/markdoc/test/fixtures/headings/markdoc.config.mjs @@ -0,0 +1,3 @@ +import { defineMarkdocConfig } from '@astrojs/markdoc/config'; + +export default defineMarkdocConfig({}); diff --git a/packages/integrations/markdoc/test/fixtures/headings/package.json b/packages/integrations/markdoc/test/fixtures/headings/package.json new file mode 100644 index 000000000..1daaae400 --- /dev/null +++ b/packages/integrations/markdoc/test/fixtures/headings/package.json @@ -0,0 +1,9 @@ +{ + "name": "@test/headings", + "version": "0.0.0", + "private": true, + "dependencies": { + "@astrojs/markdoc": "workspace:*", + "astro": "workspace:*" + } +} diff --git a/packages/integrations/markdoc/test/fixtures/headings/src/content/docs/headings.mdoc b/packages/integrations/markdoc/test/fixtures/headings/src/content/docs/headings.mdoc new file mode 100644 index 000000000..3eb66580a --- /dev/null +++ b/packages/integrations/markdoc/test/fixtures/headings/src/content/docs/headings.mdoc @@ -0,0 +1,11 @@ +# Level 1 heading + +## Level **2 heading** + +### Level _3 heading_ + +#### Level [4 heading](/with-a-link) + +##### Level 5 heading with override {% #id-override %} + +###### Level 6 heading diff --git a/packages/integrations/markdoc/test/fixtures/headings/src/pages/index.astro b/packages/integrations/markdoc/test/fixtures/headings/src/pages/index.astro new file mode 100644 index 000000000..5880be0e3 --- /dev/null +++ b/packages/integrations/markdoc/test/fixtures/headings/src/pages/index.astro @@ -0,0 +1,28 @@ +--- +import { getEntryBySlug } from "astro:content"; + +const post = await getEntryBySlug('docs', 'headings'); +const { Content, headings } = await post.render(); +--- + +<!DOCTYPE html> +<html lang="en"> +<head> + <meta charset="UTF-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Content</title> +</head> +<body> + <nav data-toc> + <ul> + {headings.map(heading => ( + <li> + <a href={`#${heading.slug}`} data-depth={heading.depth}>{heading.text}</a> + </li> + ))} + </ul> + </nav> + <Content /> +</body> +</html> diff --git a/packages/integrations/markdoc/test/headings.test.js b/packages/integrations/markdoc/test/headings.test.js new file mode 100644 index 000000000..5db50065c --- /dev/null +++ b/packages/integrations/markdoc/test/headings.test.js @@ -0,0 +1,192 @@ +import { parseHTML } from 'linkedom'; +import { expect } from 'chai'; +import { loadFixture } from '../../../astro/test/test-utils.js'; + +async function getFixture(name) { + return await loadFixture({ + root: new URL(`./fixtures/${name}/`, import.meta.url), + }); +} + +describe('Markdoc - Headings', () => { + let fixture; + + before(async () => { + fixture = await getFixture('headings'); + }); + + describe('dev', () => { + let devServer; + + before(async () => { + devServer = await fixture.startDevServer(); + }); + + after(async () => { + await devServer.stop(); + }); + + it('applies IDs to headings', async () => { + const res = await fixture.fetch('/'); + const html = await res.text(); + const { document } = parseHTML(html); + + idTest(document); + }); + + it('generates a TOC with correct info', async () => { + const res = await fixture.fetch('/'); + const html = await res.text(); + const { document } = parseHTML(html); + + tocTest(document); + }); + }); + + describe('build', () => { + before(async () => { + await fixture.build(); + }); + + it('applies IDs to headings', async () => { + const html = await fixture.readFile('/index.html'); + const { document } = parseHTML(html); + + idTest(document); + }); + + it('generates a TOC with correct info', async () => { + const html = await fixture.readFile('/index.html'); + const { document } = parseHTML(html); + + tocTest(document); + }); + }); +}); + +describe('Markdoc - Headings with custom Astro renderer', () => { + let fixture; + + before(async () => { + fixture = await getFixture('headings-custom'); + }); + + describe('dev', () => { + let devServer; + + before(async () => { + devServer = await fixture.startDevServer(); + }); + + after(async () => { + await devServer.stop(); + }); + + it('applies IDs to headings', async () => { + const res = await fixture.fetch('/'); + const html = await res.text(); + const { document } = parseHTML(html); + + idTest(document); + }); + + it('generates a TOC with correct info', async () => { + const res = await fixture.fetch('/'); + const html = await res.text(); + const { document } = parseHTML(html); + + tocTest(document); + }); + + it('renders Astro component for each heading', async () => { + const res = await fixture.fetch('/'); + const html = await res.text(); + const { document } = parseHTML(html); + + astroComponentTest(document); + }); + }); + + describe('build', () => { + before(async () => { + await fixture.build(); + }); + + it('applies IDs to headings', async () => { + const html = await fixture.readFile('/index.html'); + const { document } = parseHTML(html); + + idTest(document); + }); + + it('generates a TOC with correct info', async () => { + const html = await fixture.readFile('/index.html'); + const { document } = parseHTML(html); + + tocTest(document); + }); + + it('renders Astro component for each heading', async () => { + const html = await fixture.readFile('/index.html'); + const { document } = parseHTML(html); + + astroComponentTest(document); + }); + }); +}); + +const depthToHeadingMap = { + 1: { + slug: 'level-1-heading', + text: 'Level 1 heading', + }, + 2: { + slug: 'level-2-heading', + text: 'Level 2 heading', + }, + 3: { + slug: 'level-3-heading', + text: 'Level 3 heading', + }, + 4: { + slug: 'level-4-heading', + text: 'Level 4 heading', + }, + 5: { + slug: 'id-override', + text: 'Level 5 heading with override', + }, + 6: { + slug: 'level-6-heading', + text: 'Level 6 heading', + }, +}; + +/** @param {Document} document */ +function idTest(document) { + for (const [depth, info] of Object.entries(depthToHeadingMap)) { + expect(document.querySelector(`h${depth}`)?.getAttribute('id')).to.equal(info.slug); + } +} + +/** @param {Document} document */ +function tocTest(document) { + const toc = document.querySelector('[data-toc] > ul'); + expect(toc.children).to.have.lengthOf(Object.keys(depthToHeadingMap).length); + + for (const [depth, info] of Object.entries(depthToHeadingMap)) { + const linkEl = toc.querySelector(`a[href="#${info.slug}"]`); + expect(linkEl).to.exist; + expect(linkEl.getAttribute('data-depth')).to.equal(depth); + expect(linkEl.textContent.trim()).to.equal(info.text); + } +} + +/** @param {Document} document */ +function astroComponentTest(document) { + const headings = document.querySelectorAll('h1, h2, h3, h4, h5, h6'); + + for (const heading of headings) { + expect(heading.hasAttribute('data-custom-heading')).to.be.true; + } +} |