timelineZoom.spec.tsx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. import {fireEvent, render, screen} from 'sentry-test/reactTestingLibrary';
  2. import {useTimelineZoom} from './timelineZoom';
  3. interface TestProps {
  4. onSelect?: (startX: number, endX: number) => void;
  5. }
  6. function TestComponent({onSelect}: TestProps) {
  7. const {isActive, timelineSelector, selectionContainerRef} =
  8. useTimelineZoom<HTMLDivElement>({enabled: true, onSelect});
  9. return (
  10. <div data-test-id="body">
  11. {isActive && <div>Selection Active</div>}
  12. <div data-test-id="container" ref={selectionContainerRef}>
  13. {timelineSelector}
  14. </div>
  15. </div>
  16. );
  17. }
  18. beforeEach(() => {
  19. jest
  20. .spyOn(window, 'requestAnimationFrame')
  21. .mockImplementation((callback: FrameRequestCallback): number => {
  22. callback(0);
  23. return 0;
  24. });
  25. });
  26. afterEach(() => {
  27. jest.mocked(window.requestAnimationFrame).mockRestore();
  28. });
  29. function setupTestComponent() {
  30. const handleSelect = jest.fn();
  31. render(<TestComponent onSelect={handleSelect} />);
  32. const body = screen.getByTestId('body');
  33. const container = screen.getByTestId('container');
  34. container.getBoundingClientRect = jest.fn(() => ({
  35. x: 10,
  36. y: 10,
  37. width: 100,
  38. height: 100,
  39. left: 10,
  40. top: 10,
  41. right: 110,
  42. bottom: 110,
  43. toJSON: jest.fn(),
  44. }));
  45. return {handleSelect, body, container};
  46. }
  47. describe('TimelineZoom', function () {
  48. it('triggers onSelect', function () {
  49. const {handleSelect, body, container} = setupTestComponent();
  50. // Selector has not appeared
  51. expect(screen.queryByRole('presentation')).not.toBeInTheDocument();
  52. // Selection does not start when clicking outside the container
  53. fireEvent.mouseDown(body, {button: 0, clientX: 0, clientY: 0});
  54. expect(screen.queryByRole('presentation')).not.toBeInTheDocument();
  55. // Move cursor into the container, selection still not present
  56. fireEvent.mouseMove(body, {clientX: 20, clientY: 20});
  57. expect(screen.queryByRole('presentation')).not.toBeInTheDocument();
  58. // Left click starts selection
  59. fireEvent.mouseDown(body, {button: 0, clientX: 20, clientY: 20});
  60. const selection = screen.getByRole('presentation');
  61. expect(selection).toBeInTheDocument();
  62. expect(screen.getByText('Selection Active')).toBeInTheDocument();
  63. expect(container.style.getPropertyValue('--selectionStart')).toBe('10px');
  64. expect(container.style.getPropertyValue('--selectionWidth')).toBe('0px');
  65. // Body has disabled text selection
  66. expect(document.body).toHaveStyle({userSelect: 'none'});
  67. // Move right 15px
  68. fireEvent.mouseMove(body, {clientX: 35, clientY: 20});
  69. expect(container.style.getPropertyValue('--selectionWidth')).toBe('15px');
  70. // Move left 25px, at the edge of the container
  71. fireEvent.mouseMove(body, {clientX: 10, clientY: 20});
  72. expect(container.style.getPropertyValue('--selectionStart')).toBe('0px');
  73. expect(container.style.getPropertyValue('--selectionWidth')).toBe('10px');
  74. // Move left 5px more, selection does not move out of the container
  75. fireEvent.mouseMove(body, {clientX: 5, clientY: 20});
  76. expect(container.style.getPropertyValue('--selectionStart')).toBe('0px');
  77. expect(container.style.getPropertyValue('--selectionWidth')).toBe('10px');
  78. // Release to make selection
  79. fireEvent.mouseUp(body, {clientX: 5, clientY: 20});
  80. expect(handleSelect).toHaveBeenCalledWith(0, 10);
  81. });
  82. it('does not start selection with right click', function () {
  83. const {body} = setupTestComponent();
  84. // Move cursor into the container, selection still not present
  85. fireEvent.mouseMove(body, {clientX: 20, clientY: 20});
  86. // Right click does nothing
  87. fireEvent.mouseDown(body, {button: 1, clientX: 20, clientY: 20});
  88. expect(screen.queryByRole('presentation')).not.toBeInTheDocument();
  89. });
  90. it('does not select for very small regions', function () {
  91. const {handleSelect, body, container} = setupTestComponent();
  92. // Left click starts selection
  93. fireEvent.mouseMove(body, {clientX: 20, clientY: 20});
  94. fireEvent.mouseDown(body, {button: 0, clientX: 20, clientY: 20});
  95. fireEvent.mouseMove(body, {clientX: 22, clientY: 20});
  96. const selection = screen.getByRole('presentation');
  97. expect(selection).toBeInTheDocument();
  98. expect(screen.getByText('Selection Active')).toBeInTheDocument();
  99. expect(container.style.getPropertyValue('--selectionStart')).toBe('10px');
  100. expect(container.style.getPropertyValue('--selectionWidth')).toBe('2px');
  101. // Relase does not make selection for such a small range
  102. fireEvent.mouseUp(body, {clientX: 22, clientY: 20});
  103. expect(handleSelect).not.toHaveBeenCalledWith(0, 10);
  104. });
  105. });