Files
advanced-camera-card/tests/utils/media-info.test.ts
T
Dermot Duffy fc32727860 feat: Add support for Frigate reviews / detections [initial PR] (#2315)
- Add support for Frigate reviews / detections.
 - Add support for GenAI metadata.
- Significant internal refactor to more flexible "UnifiedQuery" to allow
mixing cameras with simple metadata and review metadata (e.g. a timeline
view of a Frigate camera with reviews, and a Reolink camera with simple
metadata).
 - Add support for folder media as camera media.

There are a few more PRs to commit prior to this going live, but
commiting this for now due to the scale of the change.

BREAKING CHANGE: `media_type` and `events_type` are retired under
`live`, `viewer` and `timeline` configuration sections, instead media
type is associated (once) with the camera under `media`.
2026-01-19 13:32:50 -08:00

203 lines
5.7 KiB
TypeScript

import { describe, expect, it, vi } from 'vitest';
import { mock } from 'vitest-mock-extended';
import { MediaLoadedCapabilities, MediaPlayer } from '../../src/types';
import {
createMediaLoadedInfo,
dispatchExistingMediaLoadedInfoAsEvent,
dispatchMediaLoadedEvent,
dispatchMediaPauseEvent,
dispatchMediaPlayEvent,
dispatchMediaUnloadedEvent,
dispatchMediaVolumeChangeEvent,
isValidMediaLoadedInfo,
} from '../../src/utils/media-info';
import { createMediaLoadedInfo as createTestMediaLoadedInfo } from '../test-utils.js';
const options = {
player: mock<MediaPlayer>(),
capabilities: mock<MediaLoadedCapabilities>(),
};
// @vitest-environment jsdom
describe('createMediaLoadedInfo', () => {
it('should create from image', () => {
const img = document.createElement('img');
// Need to write readonly properties.
Object.defineProperty(img, 'naturalWidth', { value: 10 });
Object.defineProperty(img, 'naturalHeight', { value: 20 });
expect(createMediaLoadedInfo(img, options)).toEqual({
width: 10,
height: 20,
...options,
});
});
it('should create from video', () => {
const video = document.createElement('video');
// Need to write readonly properties.
Object.defineProperty(video, 'videoWidth', { value: 30 });
Object.defineProperty(video, 'videoHeight', { value: 40 });
expect(createMediaLoadedInfo(video, options)).toEqual({
width: 30,
height: 40,
...options,
});
});
it('should create from canvas', () => {
const canvas = document.createElement('canvas');
canvas.width = 50;
canvas.height = 60;
expect(createMediaLoadedInfo(canvas, options)).toEqual({
width: 50,
height: 60,
...options,
});
});
it('should not create from unknown', () => {
const div = document.createElement('div');
expect(createMediaLoadedInfo(div, options)).toBeNull();
});
it('should create from event', () => {
const img = document.createElement('img');
// Need to write readonly properties.
Object.defineProperty(img, 'naturalWidth', { value: 70 });
Object.defineProperty(img, 'naturalHeight', { value: 80 });
const event = new Event('foo');
Object.defineProperty(event, 'composedPath', { value: () => [img] });
expect(createMediaLoadedInfo(event, options)).toEqual({
width: 70,
height: 80,
...options,
});
});
});
// @vitest-environment jsdom
describe('dispatchMediaLoadedEvent', () => {
const options = {
player: mock<MediaPlayer>(),
capabilities: mock<MediaLoadedCapabilities>(),
};
it('should dispatch', () => {
const handler = vi.fn();
const div = document.createElement('div');
div.addEventListener('advanced-camera-card:media:loaded', handler);
// Need to write readonly properties.
const img = document.createElement('img');
Object.defineProperty(img, 'naturalWidth', { value: 10 });
Object.defineProperty(img, 'naturalHeight', { value: 20 });
dispatchMediaLoadedEvent(div, img, options);
expect(handler).toBeCalledWith(
expect.objectContaining({
detail: {
width: 10,
height: 20,
...options,
},
}),
);
});
it('should not dispatch', () => {
const handler = vi.fn();
const div = document.createElement('div');
div.addEventListener('advanced-camera-card:media:loaded', handler);
dispatchMediaLoadedEvent(div, div, options);
expect(handler).not.toBeCalled();
});
});
// @vitest-environment jsdom
describe('dispatchExistingMediaLoadedInfoAsEvent', () => {
it('should dispatch', () => {
const handler = vi.fn();
const div = document.createElement('div');
div.addEventListener('advanced-camera-card:media:loaded', handler);
const info = createTestMediaLoadedInfo();
dispatchExistingMediaLoadedInfoAsEvent(div, info);
expect(handler).toBeCalledWith(
expect.objectContaining({
detail: info,
}),
);
});
});
// @vitest-environment jsdom
describe('dispatchMediaUnloadedEvent', () => {
it('should dispatch', () => {
const handler = vi.fn();
const div = document.createElement('div');
div.addEventListener('advanced-camera-card:media:unloaded', handler);
dispatchMediaUnloadedEvent(div);
expect(handler).toBeCalled();
});
});
// @vitest-environment jsdom
describe('dispatchMediaVolumeChangeEvent', () => {
it('should dispatch', () => {
const handler = vi.fn();
const div = document.createElement('div');
div.addEventListener('advanced-camera-card:media:volumechange', handler);
dispatchMediaVolumeChangeEvent(div);
expect(handler).toBeCalled();
});
});
// @vitest-environment jsdom
describe('dispatchMediaPlayEvent', () => {
it('should dispatch', () => {
const handler = vi.fn();
const div = document.createElement('div');
div.addEventListener('advanced-camera-card:media:play', handler);
dispatchMediaPlayEvent(div);
expect(handler).toBeCalled();
});
});
// @vitest-environment jsdom
describe('dispatchMediaPauseEvent', () => {
it('should dispatch', () => {
const handler = vi.fn();
const div = document.createElement('div');
div.addEventListener('advanced-camera-card:media:pause', handler);
dispatchMediaPauseEvent(div);
expect(handler).toBeCalled();
});
});
// @vitest-environment jsdom
describe('isValidMediaLoadedInfo', () => {
it('should be valid with correct dimensions', () => {
expect(
isValidMediaLoadedInfo(createTestMediaLoadedInfo({ width: 100, height: 100 })),
).toBeTruthy();
});
it('should be invalid with unlikely dimensions', () => {
expect(
isValidMediaLoadedInfo(createTestMediaLoadedInfo({ width: 0, height: 0 })),
).toBeFalsy();
});
});