From 70ba404cb876d3654a701cc72afcb2df45a21e03 Mon Sep 17 00:00:00 2001 From: CHOYSEN Date: Wed, 8 Mar 2023 12:38:48 +0800 Subject: [PATCH] test: e2e for markdown extensions (#2043) --- __tests__/e2e/.vitepress/config.ts | 13 + __tests__/e2e/markdown-extensions/foo.md | 7 + __tests__/e2e/markdown-extensions/index.md | 178 ++++++++++++++ .../markdown-extensions.test.ts | 232 ++++++++++++++++++ __tests__/e2e/multi-sidebar/index.test.ts | 3 +- 5 files changed, 432 insertions(+), 1 deletion(-) create mode 100644 __tests__/e2e/markdown-extensions/foo.md create mode 100644 __tests__/e2e/markdown-extensions/index.md create mode 100644 __tests__/e2e/markdown-extensions/markdown-extensions.test.ts diff --git a/__tests__/e2e/.vitepress/config.ts b/__tests__/e2e/.vitepress/config.ts index a31d3517..c81c3bf9 100644 --- a/__tests__/e2e/.vitepress/config.ts +++ b/__tests__/e2e/.vitepress/config.ts @@ -51,6 +51,19 @@ const sidebar: DefaultTheme.Config['sidebar'] = { link: '/dynamic-routes/bar' } ] + }, + { + text: 'Markdown Extensions', + items: [ + { + text: 'Test Page', + link: '/markdown-extensions/' + }, + { + text: 'Foo', + link: '/markdown-extensions/foo' + } + ] } ], '/multi-sidebar/': [ diff --git a/__tests__/e2e/markdown-extensions/foo.md b/__tests__/e2e/markdown-extensions/foo.md new file mode 100644 index 00000000..c601f247 --- /dev/null +++ b/__tests__/e2e/markdown-extensions/foo.md @@ -0,0 +1,7 @@ +# Foo + + +## Region + +this is region + \ No newline at end of file diff --git a/__tests__/e2e/markdown-extensions/index.md b/__tests__/e2e/markdown-extensions/index.md new file mode 100644 index 00000000..5bb4325a --- /dev/null +++ b/__tests__/e2e/markdown-extensions/index.md @@ -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 + + \ No newline at end of file diff --git a/__tests__/e2e/markdown-extensions/markdown-extensions.test.ts b/__tests__/e2e/markdown-extensions/markdown-extensions.test.ts new file mode 100644 index 00000000..24b38e01 --- /dev/null +++ b/__tests__/e2e/markdown-extensions/markdown-extensions.test.ts @@ -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') + }) +}) diff --git a/__tests__/e2e/multi-sidebar/index.test.ts b/__tests__/e2e/multi-sidebar/index.test.ts index b26b3d5b..9c25aa7c 100644 --- a/__tests__/e2e/multi-sidebar/index.test.ts +++ b/__tests__/e2e/multi-sidebar/index.test.ts @@ -14,7 +14,8 @@ describe('test multi sidebar sort root', () => { '& code', 'Static Data', 'Multi Sidebar Test', - 'Dynamic Routes' + 'Dynamic Routes', + 'Markdown Extensions' ]) }) })