import cheerio from 'cheerio' import { describe, expect, test } from 'vitest' import { renderContent } from '@/content-render/index' import { EOL } from 'os' // Use platform-specific line endings for realistic tests when templates have // been loaded from disk const nl = (str: string): string => str.replace(/\n/g, EOL) describe('renderContent', () => { test('takes a template and a context and returns a string (async)', async () => { const template = 'my favorite color is {{ color }}.' const context = { color: 'orange' } const output = await renderContent(template, context) expect(output, '
my favorite color is orange.
') }) test('preserves content within {% raw %} tags', async () => { const template = nl('For example: {% raw %}{% include cool_header.html %}{% endraw %}.') const expected = 'For example: {% include cool_header.html %}.
' const output = await renderContent(template) expect(output).toBe(expected) }) test('removes extra newlines to prevent lists from breaking', async () => { const template = nl(` 1. item one 1. item two 1. item three`) const html = await renderContent(template) const $ = cheerio.load(html, { xmlMode: true }) expect($('ol').length).toBe(1) expect($('ol > li').length).toBe(3) }) test('removes extra newlines from lists of links', async () => { const template = nl(`- item - item`) const html = await renderContent(template) const $ = cheerio.load(html, { xmlMode: true }) expect($('ul p').length).toBe(0) }) test('renders text only', async () => { const template = 'my favorite color is {{ color }}.' const context = { color: 'orange' } const output = await renderContent(template, context, { textOnly: true }) expect(output, 'my favorite color is orange.') }) test('renders empty templates', async () => { const template = '' const context = {} const output = await renderContent(template, context) expect(output).toBe('') }) test('does not render newlines around links in tables', async () => { const template = nl(` | Keyboard shortcut | Description |-----------|------------ |g c | Go to the **Code** tab |g i | Go to the **Issues** tab. For more information, see "[About issues](/articles/about-issues)." `) const html = await renderContent(template) const $ = cheerio.load(html, { xmlMode: true }) expect( $.html().includes('"About issues."'), ).toBeTruthy() }) test('does not render newlines around inline code in tables', async () => { const template = nl(` | Package manager | formats | | --- | --- | | Python | \`requirements.txt\`, \`pipfile.lock\` `) const html = await renderContent(template) const $ = cheerio.load(html, { xmlMode: true }) expect( $.html().includes('requirements.txt, pipfile.lock'),
).toBeTruthy()
})
test('does not render newlines around emphasis in code', async () => {
const template = nl(`
| Qualifier | Example
| ------------- | -------------
| user:USERNAME | [**user:defunkt ubuntu**](https://github.com/search?q=user%3Adefunkt+ubuntu&type=Issues) matches issues with the word "ubuntu" from repositories owned by @defunkt.
`)
const html = await renderContent(template)
const $ = cheerio.load(html, { xmlMode: true })
expect($.html().includes('user:USERNAME')).toBeTruthy()
})
test('renders code blocks with # comments', async () => {
const template = nl(`
1. This is a list item with code containing a comment:
\`\`\`shell
$ foo the bar
# some comment here
\`\`\`
1. This is another list item.
`)
const html = await renderContent(template)
const $ = cheerio.load(html, { xmlMode: true })
expect($('ol').length).toBe(1)
expect($.html().includes('')).toBeFalsy()
expect($.html().includes('')).toBeFalsy()
})
test('renders headings at the right level', async () => {
const template = nl(`
# This is a level 1
## This is a level 2
### This is a level 3
#### This is a level 4
##### This is a level 5
`)
const html = await renderContent(template)
const $ = cheerio.load(html, { xmlMode: true })
for (const level of [1, 2, 3, 4, 5]) {
expect(
$(`h${level}#this-is-a-level-${level} a[href="#this-is-a-level-${level}"]`).length,
).toBe(1)
}
})
test('does syntax highlighting', async () => {
let template = nl(`
\`\`\`js
const example = true
\`\`\`\`
`)
let html = await renderContent(template)
let $ = cheerio.load(html, { xmlMode: true })
expect($.html().includes('')).toBeTruthy()
expect($.html().includes('const')).toBeTruthy()
template = nl(`
\`\`\`erb
<% @articles.each do |article| %>
\`\`\`\`
`)
html = await renderContent(template)
$ = cheerio.load(html, { xmlMode: true })
expect($.html().includes('')).toBeTruthy()
expect($.html().includes('@articles')).toBeTruthy()
template = nl(`
\`\`\`http
POST / HTTP/2
\`\`\`\`
`)
html = await renderContent(template)
$ = cheerio.load(html, { xmlMode: true })
expect($.html().includes('')).toBeTruthy()
expect($.html().includes('POST')).toBeTruthy()
template = nl(`
\`\`\`groovy
plugins {
...
id 'maven-publish'
}
\`\`\`\`
`)
html = await renderContent(template)
$ = cheerio.load(html, { xmlMode: true })
expect($.html().includes('')).toBeTruthy()
expect(
$.html().includes(''maven-publish''),
).toBeTruthy()
template = nl(`
\`\`\`Dockerfile
FROM alpine:3.10
\`\`\`\`
`)
html = await renderContent(template)
$ = cheerio.load(html, { xmlMode: true })
expect($.html().includes('')).toBeTruthy()
expect($.html().includes('FROM')).toBeTruthy()
template = nl(`
\`\`\`Powershell
$resourceGroupName = "octocat-testgroup"
\`\`\`\`
`)
html = await renderContent(template)
$ = cheerio.load(html, { xmlMode: true })
expect($.html().includes('')).toBeTruthy()
expect(
$.html().includes('$resourceGroupName'),
).toBeTruthy()
})
test('does not autoguess code block language', async () => {
const template = nl(`
\`\`\`
var a = 1
\`\`\`
`)
const html = await renderContent(template)
const $ = cheerio.load(html, { xmlMode: true })
expect($.html().includes('var a = 1')).toBeTruthy()
})
test('renders a line break in a table', async () => {
const content = `| Webhook event payload | Activity types |
| --------------------- | -------------- |
| [\`issues\`](/webhooks/event-payloads/#issues) | - \`opened\`
- \`edited\`
- \`other\` |`
const file = await renderContent(content)
expect(file).toBe(
'Webhook event payload Activity types ' +
'issues- opened
- edited
- other
',
)
})
test('renders a copy button for code blocks with language specified', async () => {
const template = nl(`
\`\`\`javascript copy
var a = 1
\`\`\`
`)
const html = await renderContent(template)
const $ = cheerio.load(html)
const el = $('button.js-btn-copy')
expect(el.data('clipboard')).toBe(2967273189)
// Generates a murmurhash based ID that matches a
})
test('renders alerts with data-container attribute for analytics', async () => {
const template = nl(`
> [!NOTE]
> This is a note with a [link](https://example.com)
`)
const html = await renderContent(template, { alertTitles: { NOTE: 'Note' } })
const $ = cheerio.load(html)
const alertEl = $('.ghd-alert')
expect(alertEl.length).toBe(1)
expect(alertEl.attr('data-container')).toBe('alert')
expect(alertEl.hasClass('ghd-alert-accent')).toBe(true)
expect(alertEl.find('a[href="https://example.com"]').length).toBe(1)
})
})