Initial commit
This commit is contained in:
commit
2683eae6b3
36 files changed
+7369
No files matched your search
@@ -0,0 +1,171 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import test from 'node:test'
|
||||
import * as cheerio from 'cheerio'
|
||||
import { buildExportHtml } from '../src/html-builder.js'
|
||||
import { normalizeBaseUrl } from '../src/utils.js'
|
||||
|
||||
const baseUrl = normalizeBaseUrl('https://wiki.example.com')
|
||||
|
||||
const pages = [
|
||||
{
|
||||
path: 'docs',
|
||||
locale: 'en',
|
||||
title: 'Docs',
|
||||
depth: 0,
|
||||
updatedAt: '2026-01-01T00:00:00.000Z',
|
||||
render: `
|
||||
<h1 id="welcome">Docs</h1>
|
||||
<p><a href="#local">Local</a></p>
|
||||
<p><a href="/en/docs/child#sub-heading">Child heading</a></p>
|
||||
<p><a href="/en/docs/child">Child</a></p>
|
||||
<p><a href="/en/docs-other">Outside subtree</a></p>
|
||||
<p><a href="/de/docs">German docs</a></p>
|
||||
<p><a href="https://example.com/page">External</a></p>
|
||||
<span id="local">Anchor target</span>
|
||||
`,
|
||||
},
|
||||
{
|
||||
path: 'docs/child',
|
||||
locale: 'en',
|
||||
title: 'Child',
|
||||
depth: 1,
|
||||
updatedAt: '2026-01-01T00:00:00.000Z',
|
||||
render: `
|
||||
<h2 id="sub-heading">Sub heading</h2>
|
||||
<h3>Auto heading</h3>
|
||||
<p aria-labelledby="sub-heading other-id"><a name="old-anchor">Back</a>
|
||||
<a href="/en/docs">Docs</a>
|
||||
<a href="/en/docs/child/grand#child-heading">Grand</a></p>
|
||||
<img src="/assets/pic.png" srcset="/assets/pic-2x.png 2x" loading="lazy" alt="Picture">
|
||||
<script>window.bad = true</script>
|
||||
<div onclick="alert(1)">Clickable</div>
|
||||
<iframe src="/embedded"></iframe>
|
||||
<form><input value="x"><p>Form content</p></form>
|
||||
`,
|
||||
},
|
||||
{
|
||||
path: 'docs/child/grand',
|
||||
locale: 'en',
|
||||
title: 'Grand',
|
||||
depth: 2,
|
||||
updatedAt: '2026-01-01T00:00:00.000Z',
|
||||
render: `<h2 id="child-heading">Grand child heading</h2>`,
|
||||
},
|
||||
]
|
||||
|
||||
async function build(overrides = {}) {
|
||||
return buildExportHtml({
|
||||
pages,
|
||||
baseUrl,
|
||||
locale: 'en',
|
||||
startPath: 'docs',
|
||||
startPage: { title: 'Docs' },
|
||||
localization: { namespacing: true, namespaces: ['en'] },
|
||||
includeToc: true,
|
||||
inlineImages: false,
|
||||
apiKey: 'test-key',
|
||||
...overrides,
|
||||
})
|
||||
}
|
||||
|
||||
test('rewrites internal links to PDF anchors and keeps external links online', async () => {
|
||||
const html = await build()
|
||||
const $ = cheerio.load(html)
|
||||
|
||||
assert.equal($('.wiki-content a[href="#wp1-sub-heading"]').length, 1)
|
||||
assert.equal($('.wiki-content a[href="#wiki-page-1"]').length, 1)
|
||||
assert.equal($('.wiki-content a[href="#wp2-child-heading"]').length, 1)
|
||||
assert.equal($('.wiki-content a[href="#wp0-local"]').length, 1)
|
||||
assert.ok(
|
||||
$('.wiki-content a[data-wiki-path="docs"]')
|
||||
.toArray()
|
||||
.map((element) => $(element).attr('href'))
|
||||
.includes('#wiki-page-0'),
|
||||
)
|
||||
|
||||
assert.equal($('a[href="https://wiki.example.com/en/docs-other"]').length, 1)
|
||||
assert.equal($('a[href="https://wiki.example.com/de/docs"]').length, 1)
|
||||
assert.equal($('a[href="https://example.com/page"]').length, 1)
|
||||
|
||||
assert.equal($('#wp1-sub-heading').length, 1)
|
||||
assert.equal($('#wp1-auto-heading').length, 1)
|
||||
assert.equal($('a[name="wp1-old-anchor"]').length, 1)
|
||||
assert.equal($('[aria-labelledby="wp1-sub-heading other-id"]').length, 1)
|
||||
|
||||
const childHrefs = $('.wiki-content a[data-wiki-path="docs/child"]')
|
||||
.toArray()
|
||||
.map((element) => $(element).attr('href'))
|
||||
assert.ok(childHrefs.includes('#wp1-sub-heading'))
|
||||
assert.ok(childHrefs.includes('#wiki-page-1'))
|
||||
assert.equal(
|
||||
$('.wiki-content a[data-wiki-path="docs/child/grand"]').attr('href'),
|
||||
'#wp2-child-heading',
|
||||
)
|
||||
assert.ok(
|
||||
$('.wiki-content a[data-wiki-path="docs"]')
|
||||
.toArray()
|
||||
.map((element) => $(element).attr('href'))
|
||||
.includes('#wiki-page-0'),
|
||||
)
|
||||
})
|
||||
|
||||
test('sanitizes scripts, event handlers, forms and iframes', async () => {
|
||||
const html = await build()
|
||||
const $ = cheerio.load(html)
|
||||
|
||||
assert.equal($('script').length, 0)
|
||||
assert.equal($('[onclick]').length, 0)
|
||||
assert.equal($('form').length, 0)
|
||||
assert.equal($('input').length, 0)
|
||||
assert.equal($('iframe').length, 0)
|
||||
assert.equal($('.pdf-embed a[href="https://wiki.example.com/embedded"]').length, 1)
|
||||
assert.match($('.wiki-content').text(), /Form content/)
|
||||
})
|
||||
|
||||
test('resolves images even when embedding is disabled', async () => {
|
||||
const html = await build()
|
||||
const $ = cheerio.load(html)
|
||||
|
||||
const image = $('img[alt="Picture"]')
|
||||
assert.equal(image.attr('src'), 'https://wiki.example.com/assets/pic.png')
|
||||
assert.equal(image.attr('srcset'), undefined)
|
||||
assert.equal(image.attr('loading'), undefined)
|
||||
})
|
||||
|
||||
test('supports wikis without URL locale namespacing', async () => {
|
||||
const withoutNamespacing = pages.map((page) => ({
|
||||
...page,
|
||||
render: page.render.replaceAll('/en/', '/'),
|
||||
}))
|
||||
|
||||
const html = await build({
|
||||
pages: withoutNamespacing,
|
||||
localization: { namespacing: false, namespaces: [] },
|
||||
})
|
||||
const $ = cheerio.load(html)
|
||||
|
||||
const childHrefs = $('.wiki-content a[data-wiki-path="docs/child"]')
|
||||
.toArray()
|
||||
.map((element) => $(element).attr('href'))
|
||||
assert.ok(childHrefs.includes('#wp1-sub-heading'))
|
||||
assert.ok(childHrefs.includes('#wiki-page-1'))
|
||||
assert.equal(
|
||||
$('.wiki-content a[data-wiki-path="docs/child/grand"]').attr('href'),
|
||||
'#wp2-child-heading',
|
||||
)
|
||||
assert.ok(
|
||||
$('.wiki-content a[data-wiki-path="docs"]')
|
||||
.toArray()
|
||||
.map((element) => $(element).attr('href'))
|
||||
.includes('#wiki-page-0'),
|
||||
)
|
||||
})
|
||||
|
||||
test('includes only the exported pages in the table of contents', async () => {
|
||||
const html = await build()
|
||||
const $ = cheerio.load(html)
|
||||
|
||||
const tocHrefs = $('.toc-entry a').toArray().map((element) => $(element).attr('href'))
|
||||
assert.deepEqual(tocHrefs, ['#wiki-page-0', '#wiki-page-1', '#wiki-page-2'])
|
||||
assert.equal($('.wiki-page').length, 3)
|
||||
})
|
||||
Reference in new issue
Block a user