source-chooser.spec.ts 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. // @vitest-environment jsdom
  2. import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
  3. import type { PreviewFixtureManifest } from '../../src/fixture-manifest.ts'
  4. import { choosePreviewSource } from '../../src/client/source-chooser.ts'
  5. const MANIFEST_URL = new URL('https://preview.test/preview/fixtures.json')
  6. const MANIFEST: PreviewFixtureManifest = {
  7. version: 1,
  8. defaultFixture: 'example',
  9. fixtures: [{
  10. id: 'example',
  11. label: 'Example & <demo> "quoted" \'single\'',
  12. description: 'A deterministic example.',
  13. overlays: ['fixtures/base.tar.gz', 'fixtures/tail.tar.gz'],
  14. }],
  15. }
  16. function setLocation(search = ''): void {
  17. history.replaceState({}, '', `/preview.html${search}`)
  18. }
  19. function installManifest(manifest: PreviewFixtureManifest = MANIFEST): ReturnType<typeof vi.fn> {
  20. const fetch = vi.fn(async () => Response.json(manifest))
  21. vi.stubGlobal('fetch', fetch)
  22. return fetch
  23. }
  24. function submitChooser(): void {
  25. const form = document.querySelector<HTMLFormElement>('[data-preview-source-card]')
  26. if (form === null) throw new Error('test chooser form was not rendered')
  27. form.dispatchEvent(new Event('submit', { bubbles: true, cancelable: true }))
  28. }
  29. describe('Preview source chooser', () => {
  30. beforeEach(() => {
  31. document.head.replaceChildren()
  32. document.body.innerHTML = '<div id="root"></div>'
  33. setLocation()
  34. })
  35. afterEach(() => {
  36. vi.restoreAllMocks()
  37. vi.unstubAllGlobals()
  38. })
  39. it('bypasses the chooser and manifest for an explicit empty source', async () => {
  40. setLocation('?preview-fixture=none')
  41. const fetch = installManifest()
  42. await expect(choosePreviewSource(MANIFEST_URL)).resolves.toEqual([])
  43. expect(fetch).not.toHaveBeenCalled()
  44. expect(document.querySelector('[data-preview-source-chooser]')).toBeNull()
  45. })
  46. it('bypasses the chooser and resolves an explicit built-in fixture', async () => {
  47. document.body.replaceChildren()
  48. setLocation('?preview-fixture=example')
  49. const fetch = installManifest()
  50. await expect(choosePreviewSource(MANIFEST_URL)).resolves.toEqual([
  51. new URL('https://preview.test/preview/fixtures/base.tar.gz'),
  52. new URL('https://preview.test/preview/fixtures/tail.tar.gz'),
  53. ])
  54. expect(fetch).toHaveBeenCalledOnce()
  55. expect(document.querySelector('[data-preview-source-chooser]')).toBeNull()
  56. })
  57. it.each(['', 'missing', 'webfs'])(
  58. 'fails loud for the explicit unavailable source %j without opening the chooser',
  59. async (source) => {
  60. setLocation(`?preview-fixture=${source}`)
  61. installManifest()
  62. await expect(choosePreviewSource(MANIFEST_URL)).rejects.toThrow(/unknown or interactive source/)
  63. expect(document.querySelector('[data-preview-source-chooser]')).toBeNull()
  64. },
  65. )
  66. it('shows the chooser only when the query is absent and returns its default selection', async () => {
  67. installManifest()
  68. const root = document.getElementById('root')
  69. if (root === null) throw new Error('test root is missing')
  70. const bootPage = document.createElement('div')
  71. bootPage.dataset.dshBoot = ''
  72. root.append(bootPage)
  73. const selected = choosePreviewSource(MANIFEST_URL)
  74. await vi.waitFor(() => {
  75. expect(document.querySelector('[data-preview-source-chooser]')).not.toBeNull()
  76. })
  77. expect(document.querySelector<HTMLInputElement>('input[value="example"]')?.checked).toBe(true)
  78. expect(document.querySelector<HTMLInputElement>('input[value="webfs"]')?.disabled).toBe(true)
  79. expect(document.querySelector('[data-preview-source-card]')?.textContent)
  80. .toContain(MANIFEST.fixtures[0]?.label)
  81. expect(document.querySelector('[data-preview-source-card] script')).toBeNull()
  82. submitChooser()
  83. await expect(selected).resolves.toEqual([
  84. new URL('https://preview.test/preview/fixtures/base.tar.gz'),
  85. new URL('https://preview.test/preview/fixtures/tail.tar.gz'),
  86. ])
  87. expect(root.contains(bootPage)).toBe(true)
  88. expect(root.childElementCount).toBe(1)
  89. expect(document.querySelector('[data-preview-source-chooser]')).toBeNull()
  90. expect(document.querySelector('[data-preview-source-style]')).toBeNull()
  91. })
  92. it('selects the empty source when the manifest has no default', async () => {
  93. installManifest({ ...MANIFEST, defaultFixture: null })
  94. const selected = choosePreviewSource(MANIFEST_URL)
  95. await vi.waitFor(() => {
  96. expect(document.querySelector<HTMLInputElement>('input[value="none"]')?.checked).toBe(true)
  97. })
  98. submitChooser()
  99. await expect(selected).resolves.toEqual([])
  100. })
  101. it('reports manifest, mount, form, selection, and catalog failures', async () => {
  102. vi.stubGlobal('fetch', vi.fn(async () => new Response('missing', { status: 404 })))
  103. await expect(choosePreviewSource(MANIFEST_URL)).rejects.toThrow(/returned 404/)
  104. installManifest()
  105. document.body.replaceChildren()
  106. await expect(choosePreviewSource(MANIFEST_URL)).rejects.toThrow(/missing #root/)
  107. document.body.innerHTML = '<div id="root"></div>'
  108. const root = document.getElementById('root')
  109. if (root === null) throw new Error('test root is missing')
  110. const querySelector = vi.spyOn(HTMLElement.prototype, 'querySelector').mockReturnValueOnce(null)
  111. await expect(choosePreviewSource(MANIFEST_URL)).rejects.toThrow(/form was not rendered/)
  112. querySelector.mockRestore()
  113. document.head.replaceChildren()
  114. document.body.innerHTML = '<div id="root"></div>'
  115. const missingSelection = choosePreviewSource(MANIFEST_URL)
  116. await vi.waitFor(() => { expect(document.querySelector('form')).not.toBeNull() })
  117. document.querySelectorAll('input[name="preview-source"]').forEach((input) => {
  118. input.removeAttribute('name')
  119. })
  120. submitChooser()
  121. await expect(missingSelection).rejects.toThrow(/no source selected/)
  122. document.head.replaceChildren()
  123. document.body.innerHTML = '<div id="root"></div>'
  124. const unavailableSelection = choosePreviewSource(MANIFEST_URL)
  125. await vi.waitFor(() => { expect(document.querySelector('form')).not.toBeNull() })
  126. const selected = document.querySelector<HTMLInputElement>('input:checked')
  127. if (selected === null) throw new Error('test selection is missing')
  128. selected.value = 'missing'
  129. submitChooser()
  130. await expect(unavailableSelection).rejects.toThrow(/unavailable source/)
  131. })
  132. })