mirror of
				https://github.com/blakeblackshear/frigate.git
				synced 2025-10-27 10:52:11 +01:00 
			
		
		
		
	test(web): CameraImage (basic)
Testing Image and Canvas calls requires a lot of heavy dependencies, so this skips that part of the tests
This commit is contained in:
		
							parent
							
								
									a202c44a0f
								
							
						
					
					
						commit
						1aa9a7a093
					
				@ -2,32 +2,19 @@ import { h } from 'preact';
 | 
			
		||||
import ActivityIndicator from './ActivityIndicator';
 | 
			
		||||
import { useApiHost, useConfig } from '../api';
 | 
			
		||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'preact/hooks';
 | 
			
		||||
import { useResizeObserver } from '../hooks';
 | 
			
		||||
 | 
			
		||||
export default function CameraImage({ camera, onload, searchParams = '' }) {
 | 
			
		||||
  const { data: config } = useConfig();
 | 
			
		||||
  const apiHost = useApiHost();
 | 
			
		||||
  const [availableWidth, setAvailableWidth] = useState(0);
 | 
			
		||||
  const [hasLoaded, setHasLoaded] = useState(false);
 | 
			
		||||
  const containerRef = useRef(null);
 | 
			
		||||
  const canvasRef = useRef(null);
 | 
			
		||||
  const [{ width: availableWidth }] = useResizeObserver(containerRef);
 | 
			
		||||
 | 
			
		||||
  const { name, width, height } = config.cameras[camera];
 | 
			
		||||
  const aspectRatio = width / height;
 | 
			
		||||
 | 
			
		||||
  const resizeObserver = useMemo(() => {
 | 
			
		||||
    return new ResizeObserver((entries) => {
 | 
			
		||||
      window.requestAnimationFrame(() => {
 | 
			
		||||
        if (Array.isArray(entries) && entries.length) {
 | 
			
		||||
          setAvailableWidth(entries[0].contentRect.width);
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  }, []);
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    resizeObserver.observe(containerRef.current);
 | 
			
		||||
  }, [resizeObserver, containerRef]);
 | 
			
		||||
 | 
			
		||||
  const scaledHeight = useMemo(() => Math.min(Math.ceil(availableWidth / aspectRatio), height), [
 | 
			
		||||
    availableWidth,
 | 
			
		||||
    aspectRatio,
 | 
			
		||||
@ -57,7 +44,7 @@ export default function CameraImage({ camera, onload, searchParams = '' }) {
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div className="relative w-full" ref={containerRef}>
 | 
			
		||||
      <canvas height={scaledHeight} ref={canvasRef} width={scaledWidth} />
 | 
			
		||||
      <canvas data-testid="cameraimage-canvas" height={scaledHeight} ref={canvasRef} width={scaledWidth} />
 | 
			
		||||
      {!hasLoaded ? (
 | 
			
		||||
        <div className="absolute inset-0 flex justify-center" style={`height: ${scaledHeight}px`}>
 | 
			
		||||
          <ActivityIndicator />
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										36
									
								
								web/src/components/__tests__/CameraImage.test.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								web/src/components/__tests__/CameraImage.test.jsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,36 @@
 | 
			
		||||
import { h } from 'preact';
 | 
			
		||||
import * as Api from '../../api';
 | 
			
		||||
import * as Hooks from '../../hooks';
 | 
			
		||||
import CameraImage from '../CameraImage';
 | 
			
		||||
import { render, screen } from '@testing-library/preact';
 | 
			
		||||
 | 
			
		||||
jest.mock('../../api/baseUrl');
 | 
			
		||||
 | 
			
		||||
describe('CameraImage', () => {
 | 
			
		||||
  beforeEach(() => {
 | 
			
		||||
    jest.spyOn(Api, 'useConfig').mockImplementation(() => {
 | 
			
		||||
      return { data: { cameras: { front: { name: 'front', width: 1280, height: 720 } } } };
 | 
			
		||||
    });
 | 
			
		||||
    jest.spyOn(Api, 'useApiHost').mockReturnValue('http://base-url.local:5000');
 | 
			
		||||
    jest.spyOn(Hooks, 'useResizeObserver').mockImplementation(() => [{ width: 0 }]);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('renders an activity indicator while loading', async () => {
 | 
			
		||||
    render(<CameraImage camera="front" />);
 | 
			
		||||
    expect(screen.queryByLabelText('Loading…')).toBeInTheDocument();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  test('creates a scaled canvas using the available width & height, preserving camera aspect ratio', async () => {
 | 
			
		||||
    jest.spyOn(Hooks, 'useResizeObserver').mockReturnValueOnce([{ width: 720 }]);
 | 
			
		||||
 | 
			
		||||
    render(<CameraImage camera="front" />);
 | 
			
		||||
    expect(screen.queryByLabelText('Loading…')).toBeInTheDocument();
 | 
			
		||||
    expect(screen.queryByTestId('cameraimage-canvas')).toMatchInlineSnapshot(`
 | 
			
		||||
      <canvas
 | 
			
		||||
        data-testid="cameraimage-canvas"
 | 
			
		||||
        height="405"
 | 
			
		||||
        width="720"
 | 
			
		||||
      />
 | 
			
		||||
    `);
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										30
									
								
								web/src/hooks/index.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								web/src/hooks/index.jsx
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
			
		||||
import { useEffect, useMemo, useState } from 'preact/hooks';
 | 
			
		||||
 | 
			
		||||
export function useResizeObserver(...refs) {
 | 
			
		||||
  const [dimensions, setDimensions] = useState(
 | 
			
		||||
    new Array(refs.length).fill({ width: 0, height: 0, x: -Infinity, y: -Infinity })
 | 
			
		||||
  );
 | 
			
		||||
  const resizeObserver = useMemo(
 | 
			
		||||
    () =>
 | 
			
		||||
      new ResizeObserver((entries) => {
 | 
			
		||||
        window.requestAnimationFrame(() => {
 | 
			
		||||
          setDimensions(entries.map((entry) => entry.contentRect));
 | 
			
		||||
        });
 | 
			
		||||
      }),
 | 
			
		||||
    []
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    refs.forEach((ref) => {
 | 
			
		||||
      resizeObserver.observe(ref.current);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return () => {
 | 
			
		||||
      refs.forEach((ref) => {
 | 
			
		||||
        resizeObserver.unobserve(ref.current);
 | 
			
		||||
      });
 | 
			
		||||
    };
 | 
			
		||||
  }, [refs, resizeObserver]);
 | 
			
		||||
 | 
			
		||||
  return dimensions;
 | 
			
		||||
}
 | 
			
		||||
@ -3,14 +3,14 @@ import Card from '../components/Card.jsx';
 | 
			
		||||
import Button from '../components/Button.jsx';
 | 
			
		||||
import Heading from '../components/Heading.jsx';
 | 
			
		||||
import Switch from '../components/Switch.jsx';
 | 
			
		||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'preact/hooks';
 | 
			
		||||
import { useResizeObserver } from '../hooks';
 | 
			
		||||
import { useCallback, useMemo, useRef, useState } from 'preact/hooks';
 | 
			
		||||
import { useApiHost, useConfig } from '../api';
 | 
			
		||||
 | 
			
		||||
export default function CameraMasks({ camera, url }) {
 | 
			
		||||
  const { data: config } = useConfig();
 | 
			
		||||
  const apiHost = useApiHost();
 | 
			
		||||
  const imageRef = useRef(null);
 | 
			
		||||
  const [imageScale, setImageScale] = useState(1);
 | 
			
		||||
  const [snap, setSnap] = useState(true);
 | 
			
		||||
 | 
			
		||||
  const cameraConfig = config.cameras[camera];
 | 
			
		||||
@ -22,26 +22,8 @@ export default function CameraMasks({ camera, url }) {
 | 
			
		||||
    zones,
 | 
			
		||||
  } = cameraConfig;
 | 
			
		||||
 | 
			
		||||
  const resizeObserver = useMemo(
 | 
			
		||||
    () =>
 | 
			
		||||
      new ResizeObserver((entries) => {
 | 
			
		||||
        window.requestAnimationFrame(() => {
 | 
			
		||||
          if (Array.isArray(entries) && entries.length) {
 | 
			
		||||
            const scaledWidth = entries[0].contentRect.width;
 | 
			
		||||
            const scale = scaledWidth / width;
 | 
			
		||||
            setImageScale(scale);
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
      }),
 | 
			
		||||
    [width, setImageScale]
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    if (!imageRef.current) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    resizeObserver.observe(imageRef.current);
 | 
			
		||||
  }, [resizeObserver, imageRef]);
 | 
			
		||||
  const [{ width: scaledWidth }] = useResizeObserver(imageRef);
 | 
			
		||||
  const imageScale = scaledWidth / width;
 | 
			
		||||
 | 
			
		||||
  const [motionMaskPoints, setMotionMaskPoints] = useState(
 | 
			
		||||
    Array.isArray(motionMask)
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user