Events.test.jsx 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. import { h } from 'preact';
  2. import * as Api from '../../api';
  3. import * as Hooks from '../../hooks';
  4. import Events from '../Events';
  5. import { render, screen } from '@testing-library/preact';
  6. describe('Events Route', () => {
  7. let useEventsMock, useIntersectionMock;
  8. beforeEach(() => {
  9. useEventsMock = jest.spyOn(Api, 'useEvents').mockImplementation(() => ({
  10. data: null,
  11. status: 'loading',
  12. }));
  13. jest.spyOn(Api, 'useConfig').mockImplementation(() => ({
  14. data: {
  15. cameras: {
  16. front: { name: 'front', objects: { track: ['taco', 'cat', 'dog'] }, zones: [] },
  17. side: { name: 'side', objects: { track: ['taco', 'cat', 'dog'] }, zones: [] },
  18. },
  19. },
  20. }));
  21. jest.spyOn(Api, 'useApiHost').mockImplementation(() => 'http://localhost:5000');
  22. useIntersectionMock = jest.spyOn(Hooks, 'useIntersectionObserver').mockImplementation(() => [null, jest.fn()]);
  23. });
  24. test('shows an ActivityIndicator if not yet loaded', async () => {
  25. render(<Events limit={5} path="/events" />);
  26. expect(screen.queryByLabelText('Loading…')).toBeInTheDocument();
  27. });
  28. test('does not show ActivityIndicator after loaded', async () => {
  29. useEventsMock.mockReturnValue({ data: mockEvents, status: 'loaded' });
  30. render(<Events limit={5} path="/events" />);
  31. expect(screen.queryByLabelText('Loading…')).not.toBeInTheDocument();
  32. });
  33. test('loads more when the intersectionObserver fires', async () => {
  34. const setIntersectionNode = jest.fn();
  35. useIntersectionMock.mockReturnValue([null, setIntersectionNode]);
  36. useEventsMock.mockImplementation((searchString) => {
  37. if (searchString.includes('before=')) {
  38. const params = new URLSearchParams(searchString);
  39. const before = parseFloat(params.get('before'));
  40. const index = mockEvents.findIndex((el) => el.start_time === before + 0.0001);
  41. return { data: mockEvents.slice(index, index + 5), status: 'loaded' };
  42. }
  43. return { data: mockEvents.slice(0, 5), status: 'loaded' };
  44. });
  45. const { rerender } = render(<Events limit={5} path="/events" />);
  46. expect(setIntersectionNode).toHaveBeenCalled();
  47. expect(useEventsMock).toHaveBeenCalledWith('include_thumbnails=0&limit=5&');
  48. expect(screen.queryAllByTestId(/event-\d+/)).toHaveLength(5);
  49. useIntersectionMock.mockReturnValue([
  50. {
  51. isIntersecting: true,
  52. target: { dataset: { startTime: mockEvents[4].start_time } },
  53. },
  54. setIntersectionNode,
  55. ]);
  56. rerender(<Events limit={5} path="/events" />);
  57. expect(useEventsMock).toHaveBeenCalledWith(
  58. `include_thumbnails=0&limit=5&before=${mockEvents[4].start_time - 0.0001}`
  59. );
  60. expect(screen.queryAllByTestId(/event-\d+/)).toHaveLength(10);
  61. });
  62. });
  63. const mockEvents = new Array(12).fill(null).map((v, i) => ({
  64. end_time: 1613257337 + i,
  65. false_positive: false,
  66. has_clip: true,
  67. has_snapshot: true,
  68. id: i,
  69. label: 'person',
  70. start_time: 1613257326 + i,
  71. top_score: Math.random(),
  72. zones: ['front_patio'],
  73. thumbnail: '/9j/4aa...',
  74. }));