code-groups.spec.ts 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. /** Native code-group markup remains isolated when local search copies a section. */
  2. import assert from 'node:assert/strict'
  3. import { readFileSync } from 'node:fs'
  4. import { JSDOM } from 'jsdom'
  5. import { createMarkdownRenderer } from 'vitepress'
  6. import { expect, it } from 'vitest'
  7. import { codeGroupFallbackHead, isolateCodeGroupRadios } from '../.vitepress/code-groups.ts'
  8. const source = '::: code-group\n\n```sh [Linux/macOS]\n. .venv/bin/activate\n```\n\n```powershell [Windows PowerShell]\n.venv\\Scripts\\Activate.ps1\n```\n\n:::\n'
  9. it('keeps the page selection when a search excerpt selects the same named radio', async () => {
  10. const md = await createMarkdownRenderer(import.meta.dirname, { config: isolateCodeGroupRadios })
  11. const html = md.render(source)
  12. const dom = new JSDOM(`<main>${html}</main><aside></aside>`)
  13. try {
  14. const { document } = dom.window
  15. const pageRadios = document.querySelectorAll<HTMLInputElement>('main input')
  16. const excerpt = document.querySelector('aside')
  17. assert(pageRadios[0] && pageRadios[1] && excerpt)
  18. pageRadios[1].checked = true
  19. // Search remounts the compiled page, reusing its generated radio names and IDs.
  20. excerpt.innerHTML = html
  21. expect(pageRadios[1].checked).toBe(true)
  22. const excerptRadio = excerpt.querySelector<HTMLInputElement>('input')
  23. assert(excerptRadio)
  24. excerptRadio.checked = true
  25. expect(pageRadios[0].checked).toBe(false)
  26. expect(pageRadios[1].checked).toBe(true)
  27. expect(excerptRadio.checked).toBe(true)
  28. expect(pageRadios[0].name).toBe(excerptRadio.name)
  29. expect(pageRadios[0].form).not.toBeNull()
  30. expect(pageRadios[0].form).not.toBe(excerptRadio.form)
  31. expect(document.querySelector('main .tabs')?.hasAttribute('@submit.prevent')).toBe(true)
  32. expect([...document.querySelectorAll('main code')].map(code => code.textContent)).toEqual([
  33. '. .venv/bin/activate', '.venv\\Scripts\\Activate.ps1',
  34. ])
  35. expect(document.querySelectorAll('main .blocks > .active')).toHaveLength(1)
  36. } finally {
  37. dom.window.close()
  38. }
  39. })
  40. it('exposes both commands without client handlers in MPA output', async () => {
  41. const md = await createMarkdownRenderer(import.meta.dirname, { config: isolateCodeGroupRadios })
  42. const nativeStyles = readFileSync(new URL('../node_modules/vitepress/dist/client/theme-default/styles/components/vp-code-group.css', import.meta.url), 'utf8')
  43. const fallback = codeGroupFallbackHead(true)
  44. const dom = new JSDOM(`<style>${nativeStyles}</style>${md.render(source)}`)
  45. try {
  46. const { document } = dom.window
  47. const blocks = [...document.querySelectorAll<HTMLElement>('.blocks > div')]
  48. expect(blocks.map(block => dom.window.getComputedStyle(block).display)).toEqual(['block', 'none'])
  49. for (const [tag, , content] of fallback) {
  50. const element = document.createElement(tag)
  51. element.textContent = content ?? ''
  52. document.head.append(element)
  53. }
  54. expect(blocks.map(block => dom.window.getComputedStyle(block).display)).toEqual(['block', 'block'])
  55. const controls = [...document.querySelectorAll<HTMLElement>('.tabs, button.copy')]
  56. expect(controls).toHaveLength(3)
  57. expect(controls.every(control => dom.window.getComputedStyle(control).display === 'none')).toBe(true)
  58. expect(blocks.map(block => block.querySelector('code')?.textContent)).toEqual([
  59. '. .venv/bin/activate', '.venv\\Scripts\\Activate.ps1',
  60. ])
  61. expect(codeGroupFallbackHead(false)).toEqual([])
  62. } finally {
  63. dom.window.close()
  64. }
  65. })