image-lightbox.client.spec.tsx 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
  1. // @vitest-environment jsdom
  2. import { afterEach, describe, expect, it, vi } from 'vitest'
  3. import { cleanup, fireEvent, render } from '@testing-library/react'
  4. import { ImageLightbox } from '../src/ImageLightbox.tsx'
  5. afterEach(cleanup)
  6. const labels = { dialog: '原图预览', close: '关闭原图预览' }
  7. describe('ImageLightbox', () => {
  8. it('focuses its close control, closes by button and Escape, and restores focus', () => {
  9. const opener = document.createElement('button')
  10. document.body.appendChild(opener)
  11. opener.focus()
  12. const onClose = vi.fn()
  13. const view = render(<ImageLightbox src="blob:original" alt="原图" labels={labels} onClose={onClose} />)
  14. const close = view.getByRole('button', { name: '关闭原图预览' })
  15. expect(document.activeElement).toBe(close)
  16. fireEvent.keyDown(window, { key: 'a' })
  17. expect(onClose).not.toHaveBeenCalled()
  18. fireEvent.keyDown(window, { key: 'Escape' })
  19. fireEvent.click(close)
  20. expect(onClose).toHaveBeenCalledTimes(2)
  21. view.unmount()
  22. expect(document.activeElement).toBe(opener)
  23. opener.remove()
  24. })
  25. it('tolerates a focus owner it cannot restore (no active element at mount)', () => {
  26. // jsdom always reports body as the fallback active element; stub the
  27. // element-less state a detached focus can leave.
  28. Object.defineProperty(document, 'activeElement', { configurable: true, get: () => null })
  29. try {
  30. const view = render(<ImageLightbox src="blob:original" alt="原图" labels={labels} onClose={vi.fn()} />)
  31. view.unmount()
  32. } finally {
  33. delete (document as { activeElement?: unknown }).activeElement
  34. }
  35. })
  36. it('closes on a mask press but not on a press over the image', () => {
  37. const onClose = vi.fn()
  38. const view = render(<ImageLightbox src="blob:original" alt="原图" labels={labels} onClose={onClose} />)
  39. fireEvent.mouseDown(view.getByRole('img'))
  40. expect(onClose).not.toHaveBeenCalled()
  41. const mask = document.querySelector('[aria-hidden="true"]') as HTMLElement
  42. fireEvent.mouseDown(mask)
  43. expect(onClose).toHaveBeenCalledTimes(1)
  44. })
  45. })