Skip to content

Commit a5ee03b

Browse files
committed
Add missing layer 4 e2e markdown plugin tests
1 parent b249fc8 commit a5ee03b

7 files changed

Lines changed: 369 additions & 11 deletions

File tree

src/lib/config/markdown.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@ Object.defineProperty(rehypeAutolinkHeadings, 'name', { value: 'rehypeAutolinkHe
1919

2020
/** Remark plugins */
2121
import remarkBreaks from 'remark-breaks'
22+
Object.defineProperty(remarkBreaks, 'name', { value: 'remarkBreaks' })
23+
2224
import remarkEmoji from 'remark-emoji'
25+
Object.defineProperty(remarkEmoji, 'name', { value: 'remarkEmoji' })
26+
2327
import remarkLinkifyRegex from 'remark-linkify-regex'
28+
2429
import type { Options as RemarkTocOptions } from 'remark-toc'
2530
import remarkToc from 'remark-toc'
26-
27-
// Override function names for better debugging
28-
Object.defineProperty(remarkBreaks, 'name', { value: 'remarkBreaks' })
29-
Object.defineProperty(remarkEmoji, 'name', { value: 'remarkEmoji' })
30-
Object.defineProperty(remarkLinkifyRegex, 'name', { value: 'remarkLinkifyRegex' })
3131
Object.defineProperty(remarkToc, 'name', { value: 'remarkToc' })
3232

3333
// Create a named instance of remarkLinkifyRegex for URL auto-linking
@@ -36,18 +36,19 @@ Object.defineProperty(remarkLinkifyRegexUrls, 'name', { value: 'remarkLinkifyReg
3636

3737
// Use our TypeScript implementations (modern Remark API)
3838
import remarkAbbreviations from '../markdown/plugins/remark-abbr/index'
39+
Object.defineProperty(remarkAbbreviations, 'name', { value: 'remarkAbbreviations' })
40+
3941
import remarkAttributes from '../markdown/plugins/remark-attr/index'
42+
Object.defineProperty(remarkAttributes, 'name', { value: 'remarkAttributes' })
43+
4044
import remarkAttribution from '../markdown/plugins/remark-attribution/index'
45+
Object.defineProperty(remarkAttribution, 'name', { value: 'remarkAttribution' })
46+
4147
import remarkReplacements from '../markdown/plugins/remark-replacements/index'
48+
Object.defineProperty(remarkReplacements, 'name', { value: 'remarkReplacements' })
4249

4350
/** Add custom CSS classes to Markdown-generated elements in this file */
4451
import { rehypeTailwindClasses } from '../markdown/plugins/rehype-tailwind'
45-
46-
// Override function names for better debugging
47-
Object.defineProperty(remarkAbbreviations, 'name', { value: 'remarkAbbreviations' })
48-
Object.defineProperty(remarkAttributes, 'name', { value: 'remarkAttributes' })
49-
Object.defineProperty(remarkAttribution, 'name', { value: 'remarkAttribution' })
50-
Object.defineProperty(remarkReplacements, 'name', { value: 'remarkReplacements' })
5152
Object.defineProperty(rehypeTailwindClasses, 'name', { value: 'rehypeTailwindClasses' })
5253

5354
/** Configuration for remark-attr plugin */
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/**
2+
* Layer 4: E2E Tests - rehypeAutolinkHeadings
3+
*
4+
* Tests for the rehypeAutolinkHeadings plugin which adds anchor links
5+
* to heading elements for easy section linking.
6+
*/
7+
8+
import { describe, it, expect, beforeAll } from 'vitest'
9+
import { renderMarkdown } from '../../../helpers/markdownLoader'
10+
11+
let html: string
12+
13+
beforeAll(async () => {
14+
const markdown = `
15+
# Main Title
16+
17+
## Section One
18+
19+
Content here.
20+
21+
## Section Two
22+
23+
More content.
24+
25+
### Subsection
26+
27+
Details.
28+
`
29+
html = await renderMarkdown(markdown)
30+
})
31+
32+
describe('Layer 4: E2E - rehypeAutolinkHeadings', () => {
33+
it('should add IDs to headings', () => {
34+
// Headings should have id attributes
35+
expect(html).toMatch(/<h[1-6][^>]*id=["'][^"']+["']/)
36+
})
37+
38+
it('should add anchor links to headings', () => {
39+
// Plugin should wrap or add links within headings
40+
expect(html).toMatch(/<h[1-6][^>]*>.*<a/)
41+
})
42+
43+
it('should generate slugified IDs from heading text', () => {
44+
// IDs should be based on heading text
45+
expect(html).toMatch(/id=["']main-title["']|id=["']section-one["']|id=["']section-two["']/)
46+
})
47+
48+
it('should handle multiple heading levels', () => {
49+
const h1Count = (html.match(/<h1/g) || []).length
50+
const h2Count = (html.match(/<h2/g) || []).length
51+
const h3Count = (html.match(/<h3/g) || []).length
52+
53+
expect(h1Count).toBeGreaterThanOrEqual(1)
54+
expect(h2Count).toBeGreaterThanOrEqual(2)
55+
expect(h3Count).toBeGreaterThanOrEqual(1)
56+
})
57+
58+
it('should preserve heading text content', () => {
59+
expect(html).toContain('Main Title')
60+
expect(html).toContain('Section One')
61+
expect(html).toContain('Section Two')
62+
expect(html).toContain('Subsection')
63+
})
64+
65+
it('should add accessible attributes to anchor links', () => {
66+
// Anchor links should have aria-hidden or similar for accessibility
67+
expect(html).toMatch(/aria-hidden=["']true["']/)
68+
})
69+
})
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/**
2+
* Layer 4: E2E Tests - rehypeTailwindClasses
3+
*
4+
* Tests for the rehypeTailwindClasses plugin which adds Tailwind CSS
5+
* utility classes to HTML elements for consistent styling.
6+
*/
7+
8+
import { describe, it, expect, beforeAll } from "vitest";
9+
import { renderMarkdown } from "../../../helpers/markdownLoader";
10+
11+
let html: string
12+
13+
beforeAll(async () => {
14+
const markdown = `
15+
# Heading
16+
17+
This is a paragraph with some text.
18+
19+
## Lists
20+
21+
- List item one
22+
- List item two
23+
- List item three
24+
25+
## Code
26+
27+
Here is some \`inline code\`.
28+
29+
\`\`\`typescript
30+
const example = "code block"
31+
\`\`\`
32+
33+
## Links
34+
35+
Check out [this link](https://example.com).
36+
`
37+
html = await renderMarkdown(markdown)
38+
})
39+
40+
describe('Layer 4: E2E - rehypeTailwindClasses', () => {
41+
it('should add Tailwind classes to headings', () => {
42+
// h2-h6 headings should have Tailwind typography classes
43+
// h1 may not have classes but h2 and below should
44+
expect(html).toMatch(/<h[2-6][^>]*class=["'][^"']*/)
45+
})
46+
47+
it('should add Tailwind classes to paragraphs', () => {
48+
// Paragraphs should have spacing and text classes
49+
expect(html).toMatch(/<p[^>]*class=["'][^"']*mb-/)
50+
})
51+
52+
it('should add Tailwind classes to lists', () => {
53+
// Lists should have list styling classes
54+
expect(html).toMatch(/<ul[^>]*class=["'][^"']*list-/)
55+
})
56+
57+
it('should add Tailwind classes to list items', () => {
58+
// List items should have margin classes
59+
expect(html).toMatch(/<li[^>]*class=["'][^"']*mb-/)
60+
})
61+
62+
it('should add Tailwind classes to code elements', () => {
63+
// Code should have background and padding classes
64+
expect(html).toMatch(/<code[^>]*class=["'][^"']*bg-/)
65+
})
66+
67+
it('should add Tailwind classes to links', () => {
68+
// Links should have color and hover classes
69+
expect(html).toMatch(/<a[^>]*class=["'][^"']*/)
70+
})
71+
72+
it('should preserve content structure', () => {
73+
expect(html).toContain('This is a paragraph')
74+
expect(html).toContain('List item one')
75+
expect(html).toContain('inline code')
76+
expect(html).toContain('this link')
77+
})
78+
79+
it('should maintain semantic HTML structure', () => {
80+
// Elements should still be semantically correct
81+
expect(html).toContain('<h1')
82+
expect(html).toContain('<h2')
83+
expect(html).toContain('<p')
84+
expect(html).toContain('<ul')
85+
expect(html).toContain('<li')
86+
expect(html).toContain('<code')
87+
expect(html).toContain('<a')
88+
})
89+
})
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/**
2+
* Layer 4: E2E Tests - remarkBreaks
3+
*
4+
* Tests for the remarkBreaks plugin which adds support for hard line breaks
5+
* without requiring double spaces or backslashes.
6+
*/
7+
8+
import { describe, it, expect, beforeAll } from 'vitest'
9+
import { renderMarkdown } from '../../../helpers/markdownLoader'
10+
11+
let html: string
12+
13+
beforeAll(async () => {
14+
const markdown = `Line one
15+
Line two
16+
Line three
17+
18+
New paragraph`
19+
html = await renderMarkdown(markdown)
20+
})
21+
22+
describe('Layer 4: E2E - remarkBreaks', () => {
23+
it('should convert single newlines to hard line breaks', () => {
24+
// The plugin should convert single newlines to <br> tags
25+
expect(html).toContain('<br>')
26+
})
27+
28+
it('should preserve paragraph breaks for double newlines', () => {
29+
// Double newlines should create separate paragraphs
30+
const paragraphCount = (html.match(/<p/g) || []).length
31+
expect(paragraphCount).toBeGreaterThanOrEqual(2)
32+
})
33+
34+
it('should maintain text content', () => {
35+
expect(html).toContain('Line one')
36+
expect(html).toContain('Line two')
37+
expect(html).toContain('Line three')
38+
expect(html).toContain('New paragraph')
39+
})
40+
})
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* Layer 4: E2E Tests - remarkLinkifyRegex
3+
*
4+
* Tests for the remarkLinkifyRegex plugin which automatically converts
5+
* URLs in text to clickable links.
6+
*/
7+
8+
import { describe, it, expect, beforeAll } from "vitest";
9+
import { renderMarkdown } from "../../../helpers/markdownLoader";
10+
11+
let html: string
12+
13+
beforeAll(async () => {
14+
const markdown = `
15+
Visit https://example.com for more info.
16+
Check out http://test.org as well.
17+
Email me at user@example.com
18+
`
19+
html = await renderMarkdown(markdown)
20+
})
21+
22+
describe('Layer 4: E2E - remarkLinkifyRegex', () => {
23+
it('should convert URLs to links', () => {
24+
expect(html).toContain('<a')
25+
expect(html).toContain('https://example.com')
26+
expect(html).toContain('http://test.org')
27+
})
28+
29+
it('should handle multiple URLs in the same content', () => {
30+
const linkCount = (html.match(/<a/g) || []).length
31+
expect(linkCount).toBeGreaterThanOrEqual(2)
32+
})
33+
34+
it('should maintain text content around URLs', () => {
35+
expect(html).toContain('Visit')
36+
expect(html).toContain('for more info')
37+
expect(html).toContain('Check out')
38+
})
39+
})
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/**
2+
* Layer 4: E2E Tests - remarkReplacements
3+
*
4+
* Tests for the remarkReplacements plugin which handles typographic
5+
* replacements like arrows, fractions, and multiplication signs.
6+
* Note: Smart quotes, em dashes, and copyright symbols are handled by Astro's smartypants.
7+
*/
8+
9+
import { describe, it, expect, beforeAll } from 'vitest'
10+
import { renderMarkdown } from '../../../helpers/markdownLoader'
11+
12+
let html: string
13+
14+
beforeAll(async () => {
15+
const markdown = `
16+
Arrow right --> and left <--
17+
Double arrow <==>
18+
Plus-minus +- symbol
19+
Fractions: 1/2, 1/4, 3/4
20+
Multiplication: 2 x 4 equals 8
21+
`
22+
html = await renderMarkdown(markdown)
23+
})
24+
25+
describe('Layer 4: E2E - remarkReplacements', () => {
26+
it('should convert arrow symbols', () => {
27+
// Plugin should convert --> to → and <-- to ←
28+
expect(html).toMatch(/|&rarr;/)
29+
expect(html).toMatch(/|&larr;/)
30+
})
31+
32+
it('should convert double arrows', () => {
33+
// Should convert <==> to ⇔
34+
expect(html).toMatch(/|&hArr;/)
35+
})
36+
37+
it('should convert plus-minus', () => {
38+
expect(html).toMatch(/±|&plusmn;/)
39+
})
40+
41+
it('should convert fractions', () => {
42+
expect(html).toMatch(/½|&frac12;/)
43+
expect(html).toMatch(/¼|&frac14;/)
44+
expect(html).toMatch(/¾|&frac34;/)
45+
})
46+
47+
it('should convert multiplication sign', () => {
48+
expect(html).toMatch(/×|&times;/)
49+
})
50+
51+
it('should maintain text content', () => {
52+
expect(html).toContain('equals')
53+
expect(html).toContain('symbol')
54+
})
55+
})

0 commit comments

Comments
 (0)