ThumbnailHoverPreview.test.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. /**
  2. * The larger thumbnail shown while the pointer rests on a queue thumbnail
  3. * (#3313). Desktop only, and portalled so a batch group's overflow:hidden
  4. * cannot clip it.
  5. */
  6. import { describe, it, expect, afterEach } from 'vitest';
  7. import { fireEvent, render, screen } from '@testing-library/react';
  8. import { ThumbnailHoverPreview } from '../../components/ThumbnailHoverPreview';
  9. const realMatchMedia = window.matchMedia;
  10. function pointer(fine: boolean) {
  11. window.matchMedia = ((query: string) => ({ ...realMatchMedia(query), matches: fine })) as typeof window.matchMedia;
  12. }
  13. function renderThumb(src: string | null, rect: Partial<DOMRect> = {}) {
  14. render(
  15. <div style={{ overflow: 'hidden' }} data-testid="clipping-parent">
  16. <ThumbnailHoverPreview src={src} className="thumb">
  17. <img src={src ?? ''} alt="thumb" />
  18. </ThumbnailHoverPreview>
  19. </div>,
  20. );
  21. const anchor = screen.getByAltText('thumb').parentElement as HTMLElement;
  22. anchor.getBoundingClientRect = () =>
  23. ({ left: 100, right: 156, top: 200, bottom: 256, width: 56, height: 56, ...rect }) as DOMRect;
  24. return anchor;
  25. }
  26. describe('ThumbnailHoverPreview', () => {
  27. afterEach(() => {
  28. window.matchMedia = realMatchMedia;
  29. });
  30. it('shows the image enlarged while hovered, outside any clipping parent', () => {
  31. pointer(true);
  32. const anchor = renderThumb('/thumb.png');
  33. fireEvent.mouseEnter(anchor);
  34. const preview = screen.getByTestId('thumbnail-hover-preview');
  35. expect(preview).toHaveAttribute('src', '/thumb.png');
  36. expect(preview).toHaveStyle({ width: '256px', height: '256px', left: '164px', top: '200px' });
  37. expect(screen.getByTestId('clipping-parent')).not.toContainElement(preview);
  38. fireEvent.mouseLeave(anchor);
  39. expect(screen.queryByTestId('thumbnail-hover-preview')).not.toBeInTheDocument();
  40. });
  41. it('opens to the left when there is no room on the right', () => {
  42. pointer(true);
  43. const anchor = renderThumb('/thumb.png', { left: window.innerWidth - 60, right: window.innerWidth - 4 });
  44. fireEvent.mouseEnter(anchor);
  45. expect(screen.getByTestId('thumbnail-hover-preview')).toHaveStyle({ left: `${window.innerWidth - 60 - 8 - 256}px` });
  46. });
  47. it('stays inside the viewport at the bottom edge', () => {
  48. pointer(true);
  49. const anchor = renderThumb('/thumb.png', { top: window.innerHeight - 30 });
  50. fireEvent.mouseEnter(anchor);
  51. expect(screen.getByTestId('thumbnail-hover-preview')).toHaveStyle({ top: `${window.innerHeight - 256 - 8}px` });
  52. });
  53. it('closes when the page scrolls', () => {
  54. pointer(true);
  55. const anchor = renderThumb('/thumb.png');
  56. fireEvent.mouseEnter(anchor);
  57. fireEvent.scroll(window);
  58. expect(screen.queryByTestId('thumbnail-hover-preview')).not.toBeInTheDocument();
  59. });
  60. it('shows nothing on a touch screen', () => {
  61. pointer(false);
  62. const anchor = renderThumb('/thumb.png');
  63. fireEvent.mouseEnter(anchor);
  64. expect(screen.queryByTestId('thumbnail-hover-preview')).not.toBeInTheDocument();
  65. });
  66. it('shows nothing without an image', () => {
  67. pointer(true);
  68. const anchor = renderThumb(null);
  69. fireEvent.mouseEnter(anchor);
  70. expect(screen.queryByTestId('thumbnail-hover-preview')).not.toBeInTheDocument();
  71. });
  72. });