| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362 |
- import type { Locator } from 'playwright-chromium'
- const getClassList = async (locator: Locator) => {
- const className = await locator.getAttribute('class')
- return className?.split(' ').filter(Boolean) ?? []
- }
- const trim = (str?: string | null) => str?.replace(/\u200B/g, '').trim()
- beforeEach(async () => {
- await goto('/markdown-extensions/')
- })
- describe('Links', () => {
- test('render internal link', async () => {
- const targetMap = Object.entries({
- home: '/',
- 'markdown-extensions': '/markdown-extensions/',
- heading: './#internal-links',
- 'omit extension': './foo.html',
- '.md extension': './foo.html',
- '.html extension': './foo.html'
- })
- const items = page.locator('#internal-links +ul a')
- const count = await items.count()
- expect(count).toBe(6)
- for (let i = 0; i < count; i++) {
- const [text, href] = targetMap[i]
- expect(await items.nth(i).textContent()).toBe(text)
- expect(await items.nth(i).getAttribute('href')).toBe(href)
- }
- })
- test('external link get target="_blank" and rel="noreferrer"', async () => {
- const link = page.locator('#external-links + p a')
- expect(await link.getAttribute('target')).toBe('_blank')
- expect(await link.getAttribute('rel')).toBe('noreferrer')
- })
- })
- describe('GitHub-Style Tables', () => {
- test('render table', async () => {
- const table = page.locator('#github-style-tables + table')
- expect(table).toBeTruthy()
- })
- })
- describe('Emoji', () => {
- test('render emoji', async () => {
- const emojis = ['🎉', '💯']
- const items = page.locator('#emoji + ul li')
- const count = await items.count()
- expect(count).toBe(2)
- for (let i = 0; i < count; i++) {
- expect(await items.nth(i).textContent()).toBe(emojis[i])
- }
- })
- })
- describe('Table of Contents', () => {
- test('render toc', async () => {
- const items = page.locator('#table-of-contents + nav ul li')
- expect(
- await items.evaluateAll((elements) =>
- elements.map((el) => el.childNodes[0].textContent)
- )
- ).toMatchInlineSnapshot(`
- [
- "Links",
- "Internal Links",
- "External Links",
- "GitHub-Style Tables",
- "Emoji",
- "Table of Contents",
- "Custom Containers",
- "Default Title",
- "Custom Title",
- "Line Highlighting in Code Blocks",
- "Single Line",
- "Multiple single lines, ranges",
- "Comment Highlight",
- "Line Numbers",
- "Import Code Snippets",
- "Basic Code Snippet",
- "Specify Region",
- "With Other Features",
- "Code Groups",
- "Basic Code Group",
- "With Other Features",
- "Markdown File Inclusion",
- "Region",
- "Markdown At File Inclusion",
- "Markdown Nested File Inclusion",
- "Region",
- "After Foo",
- "Sub sub",
- "Sub sub sub",
- "Markdown File Inclusion with Range",
- "Region",
- "Markdown File Inclusion with Range without Start",
- "Region",
- "Markdown File Inclusion with Range without End",
- "Region",
- "Markdown At File Region Snippet",
- "Region Snippet",
- "Markdown At File Range Region Snippet",
- "Range Region Line 2",
- "Markdown At File Range Region Snippet without start",
- "Range Region Line 1",
- "Markdown At File Range Region Snippet without end",
- "Range Region Line 3",
- "Markdown File Inclusion with Header",
- "header 1.1.1",
- "header 1.1.2",
- "Image Lazy Loading",
- ]
- `)
- })
- })
- describe('Custom Containers', () => {
- enum CustomBlocks {
- Info = 'INFO',
- Tip = 'TIP',
- Warning = 'WARNING',
- Danger = 'DANGER',
- Details = 'Details'
- }
- const classnameMap = {
- [CustomBlocks.Info]: 'info',
- [CustomBlocks.Tip]: 'tip',
- [CustomBlocks.Warning]: 'warning',
- [CustomBlocks.Danger]: 'danger',
- [CustomBlocks.Details]: 'details'
- }
- const getTitleText = (locator: Locator, type: CustomBlocks) => {
- if (type === CustomBlocks.Details) {
- return locator.locator('summary').textContent()
- } else {
- return locator.locator('.custom-block-title').textContent()
- }
- }
- test('default title', async () => {
- const blocks = page.locator('#default-title ~ .custom-block')
- for (const [index, type] of Object.values(CustomBlocks).entries()) {
- const block = blocks.nth(index)
- const classList = await getClassList(block)
- expect(classList).contain(classnameMap[type as CustomBlocks])
- expect(await getTitleText(block, type)).toBe(type)
- }
- })
- test('custom Title', async () => {
- const blocks = page.locator('#custom-title ~ .custom-block')
- expect(await getTitleText(blocks.nth(0), CustomBlocks.Danger)).toBe('STOP')
- expect(await getTitleText(blocks.nth(1), CustomBlocks.Details)).toBe(
- 'Click me to view the code'
- )
- })
- })
- describe('Line Highlighting in Code Blocks', () => {
- test('single line', async () => {
- const classList = await getClassList(
- page.locator('#single-line + div code > span').nth(3)
- )
- expect(classList).toContain('highlighted')
- })
- test('multiple single lines, ranges', async () => {
- const lines = page.locator(
- '#multiple-single-lines-ranges + div code > span'
- )
- for (const num of [1, 4, 6, 7, 8]) {
- expect(await getClassList(lines.nth(num - 1))).toContain('highlighted')
- }
- })
- test('comment highlight', async () => {
- const lines = page.locator('#comment-highlight + div code > span')
- expect(await getClassList(lines.nth(0))).toContain('has-focus')
- expect(await getClassList(lines.nth(1))).toContain('highlighted')
- expect(await getClassList(lines.nth(3))).toContain('diff')
- expect(await getClassList(lines.nth(3))).toContain('remove')
- expect(await getClassList(lines.nth(4))).toContain('diff')
- expect(await getClassList(lines.nth(4))).toContain('add')
- expect(await getClassList(lines.nth(5))).toContain('highlighted')
- expect(await getClassList(lines.nth(5))).toContain('error')
- expect(await getClassList(lines.nth(6))).toContain('highlighted')
- expect(await getClassList(lines.nth(6))).toContain('warning')
- })
- })
- describe('Line Numbers', () => {
- test('render line numbers', async () => {
- const div = page.locator('#line-numbers + div')
- expect(await getClassList(div)).toContain('line-numbers-mode')
- const lines = div.locator('.line-numbers-wrapper > span')
- expect(await lines.count()).toBe(2)
- })
- })
- describe('Import Code Snippets', () => {
- test('basic', async () => {
- const lines = page.locator('#basic-code-snippet + div code > span')
- expect(await lines.count()).toBe(11)
- })
- test('specify region', async () => {
- const lines = page.locator('#specify-region + div code > span')
- expect(await lines.count()).toBe(3)
- })
- test('with other features', async () => {
- const div = page.locator('#with-other-features + div')
- expect(await getClassList(div)).toContain('line-numbers-mode')
- const lines = div.locator('code > span')
- expect(await lines.count()).toBe(3)
- expect(await getClassList(lines.nth(0))).toContain('highlighted')
- })
- })
- describe('Code Groups', () => {
- test('basic', async () => {
- const div = page.locator('#basic-code-group + div')
- // tabs
- const labels = div.locator('.tabs > label')
- const labelNames = ['config.js', 'config.ts']
- const count = await labels.count()
- expect(count).toBe(2)
- for (let i = 0; i < count; i++) {
- const text = await labels.nth(i).textContent()
- expect(text).toBe(labelNames[i])
- }
- // blocks
- const blocks = div.locator('.blocks > div')
- expect(await getClassList(blocks.nth(0))).toContain('active')
- await labels.nth(1).click()
- expect(await getClassList(blocks.nth(1))).toContain('active')
- })
- test('with other features', async () => {
- const div = page.locator('#with-other-features-1 + div')
- // tabs
- const labels = div.locator('.tabs > label')
- const labelNames = ['foo.md', 'snippet with region']
- const count = await labels.count()
- expect(count).toBe(2)
- for (let i = 0; i < count; i++) {
- const text = await labels.nth(i).textContent()
- expect(text).toBe(labelNames[i])
- }
- // blocks
- const blocks = div.locator('.blocks > div')
- expect(await blocks.nth(0).locator('code > span').count()).toBe(11)
- expect(await getClassList(blocks.nth(1))).toContain('line-numbers-mode')
- expect(await getClassList(blocks.nth(1))).toContain('language-ts')
- expect(await blocks.nth(1).locator('code > span').count()).toBe(3)
- expect(
- await getClassList(blocks.nth(1).locator('code > span').nth(0))
- ).toContain('highlighted')
- })
- })
- describe('Markdown File Inclusion', () => {
- test('render markdown', async () => {
- const h1 = page.locator('#markdown-file-inclusion + h1')
- expect(await h1.getAttribute('id')).toBe('foo')
- })
- test('render markdown using @', async () => {
- const h1 = page.locator('#markdown-at-file-inclusion + h1')
- expect(await h1.getAttribute('id')).toBe('bar')
- })
- test('render markdown using nested inclusion', async () => {
- const h1 = page.locator('#markdown-nested-file-inclusion + h1')
- expect(await h1.getAttribute('id')).toBe('foo-1')
- })
- test('render markdown using nested inclusion inside sub folder', async () => {
- const h1 = page.locator('#after-foo + h1')
- expect(await h1.getAttribute('id')).toBe('inside-sub-folder')
- const h2 = page.locator('#after-foo + h1 + h2')
- expect(await h2.getAttribute('id')).toBe('sub-sub')
- const h3 = page.locator('#after-foo + h1 + h2 + h3')
- expect(await h3.getAttribute('id')).toBe('sub-sub-sub')
- })
- test('support selecting range', async () => {
- const h2 = page.locator('#markdown-file-inclusion-with-range + h2')
- expect(trim(await h2.textContent())).toBe('Region')
- const p = page.locator('#markdown-file-inclusion-with-range + h2 + p')
- expect(trim(await p.textContent())).toBe('This is a region')
- })
- test('support selecting range without specifying start', async () => {
- const p = page.locator(
- '#markdown-file-inclusion-with-range-without-start ~ p'
- )
- expect(trim(await p.nth(0).textContent())).toBe('This is before region')
- expect(trim(await p.nth(1).textContent())).toBe('This is a region')
- })
- test('support selecting range without specifying end', async () => {
- const p = page.locator(
- '#markdown-file-inclusion-with-range-without-end ~ p'
- )
- expect(trim(await p.nth(0).textContent())).toBe('This is a region')
- expect(trim(await p.nth(1).textContent())).toBe('This is after region')
- })
- test('support markdown region snippet', async () => {
- const h2 = page.locator('#markdown-at-file-region-snippet + h2')
- expect(await h2.getAttribute('id')).toBe('region-snippet')
- const line = page.locator('#markdown-at-file-range-region-snippet + h2')
- expect(await line.getAttribute('id')).toBe('range-region-line-2')
- const lineWithoutStart = page.locator(
- '#markdown-at-file-range-region-snippet-without-start + h2'
- )
- expect(await lineWithoutStart.getAttribute('id')).toBe(
- 'range-region-line-1'
- )
- const lineWithoutEnd = page.locator(
- '#markdown-at-file-range-region-snippet-without-end + h2'
- )
- expect(await lineWithoutEnd.getAttribute('id')).toBe('range-region-line-3')
- })
- test('ignore frontmatter if range is not specified', async () => {
- const p = page.locator('.vp-doc')
- expect(await p.textContent()).not.toContain('title')
- })
- })
- describe('Image Lazy Loading', () => {
- test('render loading="lazy" in the <img> tag', async () => {
- const img = page.locator('#image-lazy-loading + p img')
- expect(await img.getAttribute('loading')).toBe('lazy')
- })
- })
|