PreviewModalShell.test.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. /**
  2. * Tests for PreviewModalShell (#2976) — the backdrop, panel size and header
  3. * every file preview shares.
  4. */
  5. import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
  6. import { act, fireEvent, render, screen } from '@testing-library/react';
  7. import userEvent from '@testing-library/user-event';
  8. import { PreviewModalShell } from '../../components/PreviewModalShell';
  9. import { usePreviewFullscreen } from '../../hooks/usePreviewFullscreen';
  10. const mockOnClose = vi.fn();
  11. function Harness({ closeOnBackdropClick }: { closeOnBackdropClick?: boolean }) {
  12. const fullscreen = usePreviewFullscreen();
  13. return (
  14. <PreviewModalShell
  15. title="drawing.pdf"
  16. fullscreen={fullscreen}
  17. onClose={mockOnClose}
  18. closeOnBackdropClick={closeOnBackdropClick}
  19. actions={<button type="button">Zoom in</button>}
  20. >
  21. <div data-testid="preview-body" onDoubleClick={fullscreen.toggleFullscreen}>
  22. body
  23. </div>
  24. </PreviewModalShell>
  25. );
  26. }
  27. function panelOf(): HTMLElement {
  28. return screen.getByText('drawing.pdf').closest('.flex-col') as HTMLElement;
  29. }
  30. describe('PreviewModalShell', () => {
  31. const requestFullscreen = vi.fn();
  32. const exitFullscreen = vi.fn();
  33. beforeEach(() => {
  34. vi.clearAllMocks();
  35. requestFullscreen.mockResolvedValue(undefined);
  36. exitFullscreen.mockResolvedValue(undefined);
  37. Object.defineProperty(document, 'fullscreenEnabled', { configurable: true, value: true });
  38. Object.defineProperty(document, 'fullscreenElement', { configurable: true, value: null, writable: true });
  39. Object.defineProperty(document, 'exitFullscreen', { configurable: true, value: exitFullscreen });
  40. Object.defineProperty(HTMLElement.prototype, 'requestFullscreen', { configurable: true, value: requestFullscreen });
  41. });
  42. afterEach(() => {
  43. delete (document as { fullscreenEnabled?: boolean }).fullscreenEnabled;
  44. delete (document as { fullscreenElement?: Element | null }).fullscreenElement;
  45. delete (document as { exitFullscreen?: () => Promise<void> }).exitFullscreen;
  46. delete (HTMLElement.prototype as { requestFullscreen?: () => Promise<void> }).requestFullscreen;
  47. });
  48. // The browser flips fullscreenElement and fires fullscreenchange; the mocks
  49. // do neither, so the test plays the browser's part.
  50. function browserEntersFullscreen(panel: Element | null) {
  51. (document as { fullscreenElement: Element | null }).fullscreenElement = panel;
  52. act(() => {
  53. document.dispatchEvent(new Event('fullscreenchange'));
  54. });
  55. }
  56. it('gives the panel the size every preview shares', () => {
  57. render(<Harness />);
  58. const panel = panelOf();
  59. expect(panel.className).toContain('w-[min(1800px,96vw)]');
  60. expect(panel.className).toContain('h-[94vh]');
  61. expect(panel.className).toContain('rounded-lg');
  62. });
  63. it('renders the title, the preview actions and the close button', async () => {
  64. const user = userEvent.setup();
  65. render(<Harness />);
  66. expect(screen.getByText('drawing.pdf')).toBeInTheDocument();
  67. expect(screen.getByRole('button', { name: 'Zoom in' })).toBeInTheDocument();
  68. await user.click(screen.getByRole('button', { name: 'Close' }));
  69. expect(mockOnClose).toHaveBeenCalledTimes(1);
  70. });
  71. it('goes fullscreen on a double-click in the body and fills the screen', () => {
  72. render(<Harness />);
  73. const panel = panelOf();
  74. fireEvent.doubleClick(screen.getByTestId('preview-body'));
  75. expect(requestFullscreen).toHaveBeenCalledTimes(1);
  76. expect(requestFullscreen.mock.instances[0]).toBe(panel);
  77. browserEntersFullscreen(panel);
  78. expect(panel.className).toContain('max-w-none');
  79. expect(panel.className).not.toContain('w-[min(1800px,96vw)]');
  80. expect(screen.getByRole('button', { name: 'Exit fullscreen' })).toBeInTheDocument();
  81. });
  82. it('toggles fullscreen from the header button', async () => {
  83. const user = userEvent.setup();
  84. render(<Harness />);
  85. await user.click(screen.getByRole('button', { name: 'Fullscreen' }));
  86. expect(requestFullscreen).toHaveBeenCalledTimes(1);
  87. browserEntersFullscreen(panelOf());
  88. await user.click(screen.getByRole('button', { name: 'Exit fullscreen' }));
  89. expect(exitFullscreen).toHaveBeenCalledTimes(1);
  90. });
  91. it('closes on Escape, but leaves it to the browser while fullscreen', () => {
  92. render(<Harness />);
  93. browserEntersFullscreen(panelOf());
  94. fireEvent.keyDown(window, { key: 'Escape' });
  95. expect(mockOnClose).not.toHaveBeenCalled();
  96. browserEntersFullscreen(null);
  97. fireEvent.keyDown(window, { key: 'Escape' });
  98. expect(mockOnClose).toHaveBeenCalledTimes(1);
  99. });
  100. it('closes on a backdrop click only where the preview asks for it', async () => {
  101. const user = userEvent.setup();
  102. const { unmount } = render(<Harness />);
  103. const backdrop = panelOf().parentElement as HTMLElement;
  104. await user.click(backdrop);
  105. expect(mockOnClose).not.toHaveBeenCalled();
  106. unmount();
  107. render(<Harness closeOnBackdropClick />);
  108. await user.click(panelOf().parentElement as HTMLElement);
  109. expect(mockOnClose).toHaveBeenCalledTimes(1);
  110. // A click inside the panel is not a backdrop click.
  111. await user.click(screen.getByTestId('preview-body'));
  112. expect(mockOnClose).toHaveBeenCalledTimes(1);
  113. });
  114. });