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 payloadActivity 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)
  })
})