mirror of https://github.com/vuejs/vitepress
test: e2e for markdown extensions (#2043)
parent
bc30c57db0
commit
70ba404cb8
@ -0,0 +1,7 @@
|
||||
# Foo
|
||||
|
||||
<!-- #region snippet -->
|
||||
## Region
|
||||
|
||||
this is region
|
||||
<!-- #endregion snippet -->
|
@ -0,0 +1,178 @@
|
||||
# Markdown Extensions
|
||||
|
||||
## Links
|
||||
|
||||
### Internal Links
|
||||
|
||||
- [home](/)
|
||||
- [markdown-extensions](/markdown-extensions/)
|
||||
- [heading](./#internal-links)
|
||||
- [omit extension](./foo)
|
||||
- [.md extension](./foo.md)
|
||||
- [.html extension](./foo.html)
|
||||
|
||||
### External Links
|
||||
|
||||
[VitePress on GitHub](https://github.com/vuejs/vitepress)
|
||||
|
||||
## GitHub-Style Tables
|
||||
|
||||
| Tables | Are | Cool |
|
||||
| ------------- | :-----------: | -----: |
|
||||
| col 3 is | right-aligned | \$1600 |
|
||||
| col 2 is | centered | \$12 |
|
||||
| zebra stripes | are neat | \$1 |
|
||||
|
||||
## Emoji
|
||||
|
||||
- :tada:
|
||||
- :100:
|
||||
|
||||
## Table of Contents
|
||||
|
||||
[[toc]]
|
||||
|
||||
## Custom Containers
|
||||
|
||||
### Default Title
|
||||
|
||||
::: info
|
||||
This is an info box.
|
||||
:::
|
||||
|
||||
::: tip
|
||||
This is a tip.
|
||||
:::
|
||||
|
||||
::: warning
|
||||
This is a warning.
|
||||
:::
|
||||
|
||||
::: danger
|
||||
This is a dangerous warning.
|
||||
:::
|
||||
|
||||
::: details
|
||||
This is a details block.
|
||||
:::
|
||||
|
||||
### Custom Title
|
||||
|
||||
::: danger STOP
|
||||
Danger zone, do not proceed
|
||||
:::
|
||||
|
||||
::: details Click me to view the code
|
||||
```js
|
||||
console.log('Hello, VitePress!')
|
||||
```
|
||||
:::
|
||||
|
||||
## Line Highlighting in Code Blocks
|
||||
|
||||
### Single Line
|
||||
|
||||
```js{4}
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
msg: 'Highlighted!'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Multiple single lines, ranges
|
||||
|
||||
```js{1,4,6-8}
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
msg: `Highlighted!
|
||||
This line isn't highlighted,
|
||||
but this and the next 2 are.`,
|
||||
motd: 'VitePress is awesome',
|
||||
lorem: 'ipsum',
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Comment Highlight
|
||||
|
||||
```js
|
||||
export default { // [!code focus]
|
||||
data() { // [!code hl]
|
||||
return {
|
||||
msg: 'Removed' // [!code --]
|
||||
msg: 'Added' // [!code ++]
|
||||
msg: 'Error', // [!code error]
|
||||
msg: 'Warning' // [!code warning]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Line Numbers
|
||||
|
||||
```ts:line-numbers
|
||||
const line1 = 'This is line 1'
|
||||
const line2 = 'This is line 2'
|
||||
```
|
||||
|
||||
## Import Code Snippets
|
||||
|
||||
### Basic Code Snippet
|
||||
|
||||
<<< @/markdown-extensions/foo.md
|
||||
|
||||
### Specify Region
|
||||
|
||||
<<< @/markdown-extensions/foo.md#snippet
|
||||
|
||||
### With Other Features
|
||||
|
||||
<<< @/markdown-extensions/foo.md#snippet{1 ts:line-numbers} [snippet with region]
|
||||
|
||||
## Code Groups
|
||||
|
||||
### Basic Code Group
|
||||
|
||||
::: code-group
|
||||
|
||||
```js [config.js]
|
||||
/**
|
||||
* @type {import('vitepress').UserConfig}
|
||||
*/
|
||||
const config = {
|
||||
// ...
|
||||
}
|
||||
|
||||
export default config
|
||||
```
|
||||
|
||||
```ts [config.ts]
|
||||
import type { UserConfig } from 'vitepress'
|
||||
|
||||
const config: UserConfig = {
|
||||
// ...
|
||||
}
|
||||
|
||||
export default config
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### With Other Features
|
||||
|
||||
::: code-group
|
||||
|
||||
<<< @/markdown-extensions/foo.md
|
||||
|
||||
<<< @/markdown-extensions/foo.md#snippet{1 ts:line-numbers} [snippet with region]
|
||||
|
||||
:::
|
||||
|
||||
## Markdown File Inclusion
|
||||
|
||||
<!--@include: ./foo.md-->
|
@ -0,0 +1,232 @@
|
||||
import type { Locator } from 'playwright-chromium'
|
||||
|
||||
const getClassList = async (locator: Locator) => {
|
||||
const className = await locator.getAttribute('class')
|
||||
return className?.split(' ').filter(Boolean) ?? []
|
||||
}
|
||||
|
||||
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')
|
||||
const count = await items.count()
|
||||
expect(count).toBe(23)
|
||||
})
|
||||
})
|
||||
|
||||
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(7)
|
||||
})
|
||||
|
||||
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(7)
|
||||
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')
|
||||
})
|
||||
})
|
Loading…
Reference in new issue