test: Improve test coverage of various camera engine files (#2415)
This commit is contained in:
committed by
dermotduffy
parent
16ca8d79d2
commit
415a3e0886
@@ -0,0 +1,113 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import { BrowseMediaCameraManagerEngine } from '../../../src/camera-manager/browse-media/engine-browse-media';
|
||||
import {
|
||||
CameraManagerRequestCache,
|
||||
CameraQuery,
|
||||
QueryType,
|
||||
} from '../../../src/camera-manager/types';
|
||||
import { StateWatcher } from '../../../src/card-controller/hass/state-watcher';
|
||||
import { BROWSE_MEDIA_CACHE_SECONDS } from '../../../src/ha/browse-media/types';
|
||||
import { BrowseMediaWalker } from '../../../src/ha/browse-media/walker';
|
||||
import { ResolvedMediaCache } from '../../../src/ha/resolved-media';
|
||||
import { QuerySource } from '../../../src/query-source';
|
||||
import { ViewMedia } from '../../../src/view/item';
|
||||
import { CameraManagerReadOnlyConfigStore } from '../../../src/camera-manager/store';
|
||||
import { EntityRegistryManagerMock } from '../../ha/registry/entity/mock';
|
||||
import { createCameraConfig, createHASS } from '../../test-utils';
|
||||
|
||||
const createEngine = (): BrowseMediaCameraManagerEngine => {
|
||||
return new BrowseMediaCameraManagerEngine(
|
||||
new EntityRegistryManagerMock(),
|
||||
mock<StateWatcher>(),
|
||||
new BrowseMediaWalker(),
|
||||
new ResolvedMediaCache(),
|
||||
new CameraManagerRequestCache(),
|
||||
);
|
||||
};
|
||||
|
||||
describe('BrowseMediaCameraManagerEngine', () => {
|
||||
describe('generateDefaultEventQuery', () => {
|
||||
it('should generate event query', () => {
|
||||
const engine = createEngine();
|
||||
const cameraIDs = new Set(['camera1']);
|
||||
const result = engine.generateDefaultEventQuery(
|
||||
mock<CameraManagerReadOnlyConfigStore>(),
|
||||
cameraIDs,
|
||||
{},
|
||||
);
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
source: QuerySource.Camera,
|
||||
type: QueryType.Event,
|
||||
cameraIDs: cameraIDs,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should merge partial query fields', () => {
|
||||
const engine = createEngine();
|
||||
const cameraIDs = new Set(['camera1']);
|
||||
const result = engine.generateDefaultEventQuery(
|
||||
mock<CameraManagerReadOnlyConfigStore>(),
|
||||
cameraIDs,
|
||||
{ limit: 10 },
|
||||
);
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
source: QuerySource.Camera,
|
||||
type: QueryType.Event,
|
||||
cameraIDs: cameraIDs,
|
||||
limit: 10,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getMediaDownloadPath', () => {
|
||||
it('should call getMediaDownloadPath with content ID', async () => {
|
||||
const engine = createEngine();
|
||||
const hass = createHASS();
|
||||
const media = mock<ViewMedia>();
|
||||
media.getContentID.mockReturnValue('content-id');
|
||||
|
||||
await engine.getMediaDownloadPath(hass, createCameraConfig(), media);
|
||||
|
||||
expect(media.getContentID).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getQueryResultMaxAge', () => {
|
||||
it('should return cache seconds for event queries', () => {
|
||||
const engine = createEngine();
|
||||
const query = {
|
||||
type: QueryType.Event,
|
||||
} as CameraQuery;
|
||||
|
||||
expect(engine.getQueryResultMaxAge(query)).toBe(BROWSE_MEDIA_CACHE_SECONDS);
|
||||
});
|
||||
|
||||
it('should return null for non-event queries', () => {
|
||||
const engine = createEngine();
|
||||
const query = {
|
||||
type: QueryType.Recording,
|
||||
} as CameraQuery;
|
||||
|
||||
expect(engine.getQueryResultMaxAge(query)).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getMediaCapabilities', () => {
|
||||
it('should return correct capabilities', () => {
|
||||
const engine = createEngine();
|
||||
const media = mock<ViewMedia>();
|
||||
|
||||
expect(engine.getMediaCapabilities(media)).toEqual({
|
||||
canFavorite: false,
|
||||
canDownload: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,110 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import {
|
||||
FrigateEventViewMedia,
|
||||
FrigateRecordingViewMedia,
|
||||
FrigateReviewViewMedia,
|
||||
} from '../../../src/camera-manager/frigate/media';
|
||||
import { FrigateViewMediaClassifier } from '../../../src/camera-manager/frigate/media-classifier';
|
||||
import { ViewMedia, ViewMediaType } from '../../../src/view/item';
|
||||
import {
|
||||
createFrigateEvent,
|
||||
createFrigateRecording,
|
||||
createFrigateReview,
|
||||
} from '../../test-utils';
|
||||
|
||||
describe('FrigateViewMediaClassifier', () => {
|
||||
describe('isFrigateEvent', () => {
|
||||
it('should return true for event media', () => {
|
||||
const media = new FrigateEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
createFrigateEvent(),
|
||||
'content_id',
|
||||
'thumbnail',
|
||||
);
|
||||
expect(FrigateViewMediaClassifier.isFrigateEvent(media)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for non-event media', () => {
|
||||
const media = mock<ViewMedia>();
|
||||
expect(FrigateViewMediaClassifier.isFrigateEvent(media)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isFrigateRecording', () => {
|
||||
it('should return true for recording media', () => {
|
||||
const media = new FrigateRecordingViewMedia(
|
||||
ViewMediaType.Recording,
|
||||
'camera',
|
||||
createFrigateRecording(),
|
||||
'id',
|
||||
'content_id',
|
||||
'title',
|
||||
);
|
||||
expect(FrigateViewMediaClassifier.isFrigateRecording(media)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for non-recording media', () => {
|
||||
const media = mock<ViewMedia>();
|
||||
expect(FrigateViewMediaClassifier.isFrigateRecording(media)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isFrigateReview', () => {
|
||||
it('should return true for review media', () => {
|
||||
const media = new FrigateReviewViewMedia(
|
||||
'camera',
|
||||
createFrigateReview(),
|
||||
'content_id',
|
||||
'thumbnail',
|
||||
);
|
||||
expect(FrigateViewMediaClassifier.isFrigateReview(media)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for non-review media', () => {
|
||||
const media = mock<ViewMedia>();
|
||||
expect(FrigateViewMediaClassifier.isFrigateReview(media)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isFrigateMedia', () => {
|
||||
it('should return true for event media', () => {
|
||||
const media = new FrigateEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
createFrigateEvent(),
|
||||
'content_id',
|
||||
'thumbnail',
|
||||
);
|
||||
expect(FrigateViewMediaClassifier.isFrigateMedia(media)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true for review media', () => {
|
||||
const media = new FrigateReviewViewMedia(
|
||||
'camera',
|
||||
createFrigateReview(),
|
||||
'content_id',
|
||||
'thumbnail',
|
||||
);
|
||||
expect(FrigateViewMediaClassifier.isFrigateMedia(media)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true for recording media', () => {
|
||||
const media = new FrigateRecordingViewMedia(
|
||||
ViewMediaType.Recording,
|
||||
'camera',
|
||||
createFrigateRecording(),
|
||||
'id',
|
||||
'content_id',
|
||||
'title',
|
||||
);
|
||||
expect(FrigateViewMediaClassifier.isFrigateMedia(media)).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for non-frigate media', () => {
|
||||
const media = mock<ViewMedia>();
|
||||
expect(FrigateViewMediaClassifier.isFrigateMedia(media)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,59 +1,129 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
FrigateEventViewMedia,
|
||||
FrigateRecordingViewMedia,
|
||||
FrigateReviewViewMedia,
|
||||
FrigateViewMediaFactory,
|
||||
} from '../../../src/camera-manager/frigate/media';
|
||||
import { ViewMediaType } from '../../../src/view/item';
|
||||
import { createFrigateEvent, createFrigateReview } from '../../test-utils';
|
||||
|
||||
describe('FrigateReviewViewMedia', () => {
|
||||
it('should get description when scene is present', () => {
|
||||
const review = createFrigateReview({
|
||||
data: {
|
||||
objects: [],
|
||||
zones: [],
|
||||
metadata: {
|
||||
scene: 'A person walking',
|
||||
title: 'Title',
|
||||
},
|
||||
},
|
||||
});
|
||||
const media = new FrigateReviewViewMedia(
|
||||
'camera',
|
||||
review,
|
||||
'content_id',
|
||||
'thumbnail',
|
||||
);
|
||||
expect(media.getDescription()).toBe('A person walking');
|
||||
});
|
||||
|
||||
it('should get null description when scene is absent', () => {
|
||||
const review = createFrigateReview({
|
||||
data: {
|
||||
objects: [],
|
||||
zones: [],
|
||||
metadata: {
|
||||
title: 'Title',
|
||||
// scene is absent.
|
||||
},
|
||||
},
|
||||
});
|
||||
const media = new FrigateReviewViewMedia(
|
||||
'camera',
|
||||
review,
|
||||
'content_id',
|
||||
'thumbnail',
|
||||
);
|
||||
expect(media.getDescription()).toBeNull();
|
||||
});
|
||||
});
|
||||
import {
|
||||
createCameraConfig,
|
||||
createFrigateEvent,
|
||||
createFrigateRecording,
|
||||
createFrigateReview,
|
||||
} from '../../test-utils';
|
||||
|
||||
describe('FrigateEventViewMedia', () => {
|
||||
it('should get description when description is present', () => {
|
||||
it('should get start time', () => {
|
||||
const event = createFrigateEvent({ start_time: 1683395000 });
|
||||
const media = new FrigateEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
event,
|
||||
'content_id',
|
||||
'thumbnail',
|
||||
);
|
||||
expect(media.getStartTime()).toEqual(new Date(1683395000 * 1000));
|
||||
});
|
||||
|
||||
it('should get end time', () => {
|
||||
const event = createFrigateEvent({ end_time: 1683397124 });
|
||||
const media = new FrigateEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
event,
|
||||
'content_id',
|
||||
'thumbnail',
|
||||
);
|
||||
expect(media.getEndTime()).toEqual(new Date(1683397124 * 1000));
|
||||
});
|
||||
|
||||
it('should get null end time when null', () => {
|
||||
const event = createFrigateEvent({ end_time: null });
|
||||
const media = new FrigateEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
event,
|
||||
'content_id',
|
||||
'thumbnail',
|
||||
);
|
||||
expect(media.getEndTime()).toBeNull();
|
||||
});
|
||||
|
||||
it('should report in progress when no end time', () => {
|
||||
const event = createFrigateEvent({ end_time: null });
|
||||
const media = new FrigateEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
event,
|
||||
'content_id',
|
||||
'thumbnail',
|
||||
);
|
||||
expect(media.inProgress()).toBe(true);
|
||||
});
|
||||
|
||||
it('should report not in progress when end time exists', () => {
|
||||
const event = createFrigateEvent({ end_time: 1683397124 });
|
||||
const media = new FrigateEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
event,
|
||||
'content_id',
|
||||
'thumbnail',
|
||||
);
|
||||
expect(media.inProgress()).toBe(false);
|
||||
});
|
||||
|
||||
it('should get video content type as HLS', () => {
|
||||
const media = new FrigateEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
createFrigateEvent(),
|
||||
'content_id',
|
||||
'thumbnail',
|
||||
);
|
||||
expect(media.getVideoContentType()).toBe('hls');
|
||||
});
|
||||
|
||||
it('should get ID', () => {
|
||||
const event = createFrigateEvent({
|
||||
data: {
|
||||
description: 'A person walking',
|
||||
},
|
||||
id: 'test-event-id',
|
||||
});
|
||||
const media = new FrigateEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
event,
|
||||
'content_id',
|
||||
'thumbnail',
|
||||
);
|
||||
expect(media.getID()).toBe('test-event-id');
|
||||
});
|
||||
|
||||
it('should get content ID', () => {
|
||||
const media = new FrigateEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
createFrigateEvent(),
|
||||
'my-content-id',
|
||||
'thumbnail',
|
||||
);
|
||||
expect(media.getContentID()).toBe('my-content-id');
|
||||
});
|
||||
|
||||
it('should get title', () => {
|
||||
const media = new FrigateEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
createFrigateEvent(),
|
||||
'content_id',
|
||||
'thumbnail',
|
||||
);
|
||||
expect(media.getTitle()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should get description when present', () => {
|
||||
const event = createFrigateEvent({
|
||||
data: { description: 'A person walking' },
|
||||
});
|
||||
const media = new FrigateEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
@@ -65,7 +135,7 @@ describe('FrigateEventViewMedia', () => {
|
||||
expect(media.getDescription()).toBe('A person walking');
|
||||
});
|
||||
|
||||
it('should get null description when data is absent', () => {
|
||||
it('should get null description when absent', () => {
|
||||
const event = createFrigateEvent();
|
||||
const media = new FrigateEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
@@ -76,4 +146,664 @@ describe('FrigateEventViewMedia', () => {
|
||||
);
|
||||
expect(media.getDescription()).toBeNull();
|
||||
});
|
||||
|
||||
it('should get thumbnail', () => {
|
||||
const media = new FrigateEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
createFrigateEvent(),
|
||||
'content_id',
|
||||
'my-thumb',
|
||||
);
|
||||
expect(media.getThumbnail()).toBe('my-thumb');
|
||||
});
|
||||
|
||||
it('should get favorite status', () => {
|
||||
const event = createFrigateEvent({
|
||||
retain_indefinitely: true,
|
||||
});
|
||||
const media = new FrigateEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
event,
|
||||
'content_id',
|
||||
'thumbnail',
|
||||
);
|
||||
expect(media.isFavorite()).toBe(true);
|
||||
});
|
||||
|
||||
it('should get null favorite when undefined', () => {
|
||||
const event = createFrigateEvent();
|
||||
delete (event as Record<string, unknown>).retain_indefinitely;
|
||||
const media = new FrigateEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
event,
|
||||
'content_id',
|
||||
'thumbnail',
|
||||
);
|
||||
expect(media.isFavorite()).toBeNull();
|
||||
});
|
||||
|
||||
it('should set favorite', () => {
|
||||
const media = new FrigateEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
createFrigateEvent({ retain_indefinitely: false }),
|
||||
'content_id',
|
||||
'thumbnail',
|
||||
);
|
||||
media.setFavorite(true);
|
||||
expect(media.isFavorite()).toBe(true);
|
||||
});
|
||||
|
||||
it('should get what', () => {
|
||||
const event = createFrigateEvent({ label: 'cat' });
|
||||
const media = new FrigateEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
event,
|
||||
'content_id',
|
||||
'thumbnail',
|
||||
);
|
||||
expect(media.getWhat()).toEqual(['cat']);
|
||||
});
|
||||
|
||||
it('should get where when zones present', () => {
|
||||
const event = createFrigateEvent({
|
||||
zones: ['front_yard'],
|
||||
});
|
||||
const media = new FrigateEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
event,
|
||||
'content_id',
|
||||
'thumbnail',
|
||||
);
|
||||
expect(media.getWhere()).toEqual(['front_yard']);
|
||||
});
|
||||
|
||||
it('should get null where when no zones', () => {
|
||||
const event = createFrigateEvent({ zones: [] });
|
||||
const media = new FrigateEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
event,
|
||||
'content_id',
|
||||
'thumbnail',
|
||||
);
|
||||
expect(media.getWhere()).toBeNull();
|
||||
});
|
||||
|
||||
it('should get score', () => {
|
||||
const event = createFrigateEvent({
|
||||
top_score: 0.95,
|
||||
});
|
||||
const media = new FrigateEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
event,
|
||||
'content_id',
|
||||
'thumbnail',
|
||||
);
|
||||
expect(media.getScore()).toBe(0.95);
|
||||
});
|
||||
|
||||
it('should get tags from sub-labels', () => {
|
||||
const media = new FrigateEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
createFrigateEvent(),
|
||||
'content_id',
|
||||
'thumbnail',
|
||||
['Amazon', 'UPS'],
|
||||
);
|
||||
expect(media.getTags()).toEqual(['Amazon', 'UPS']);
|
||||
});
|
||||
|
||||
it('should get null tags when no sub-labels', () => {
|
||||
const media = new FrigateEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
createFrigateEvent(),
|
||||
'content_id',
|
||||
'thumbnail',
|
||||
);
|
||||
expect(media.getTags()).toBeNull();
|
||||
});
|
||||
|
||||
it('should be groupable with same type and criteria', () => {
|
||||
const media1 = new FrigateEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
createFrigateEvent({
|
||||
label: 'person',
|
||||
zones: ['yard'],
|
||||
}),
|
||||
'c1',
|
||||
't1',
|
||||
);
|
||||
const media2 = new FrigateEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
createFrigateEvent({
|
||||
label: 'person',
|
||||
zones: ['yard'],
|
||||
}),
|
||||
'c2',
|
||||
't2',
|
||||
);
|
||||
expect(media1.isGroupableWith(media2)).toBe(true);
|
||||
});
|
||||
|
||||
it('should not be groupable with different type', () => {
|
||||
const media1 = new FrigateEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
createFrigateEvent({ label: 'person' }),
|
||||
'c1',
|
||||
't1',
|
||||
);
|
||||
const media2 = new FrigateEventViewMedia(
|
||||
ViewMediaType.Snapshot,
|
||||
'camera',
|
||||
createFrigateEvent({ label: 'person' }),
|
||||
'c2',
|
||||
't2',
|
||||
);
|
||||
expect(media1.isGroupableWith(media2)).toBe(false);
|
||||
});
|
||||
|
||||
it('should not be groupable with different label', () => {
|
||||
const media1 = new FrigateEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
createFrigateEvent({ label: 'person' }),
|
||||
'c1',
|
||||
't1',
|
||||
);
|
||||
const media2 = new FrigateEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
createFrigateEvent({ label: 'car' }),
|
||||
'c2',
|
||||
't2',
|
||||
);
|
||||
expect(media1.isGroupableWith(media2)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('FrigateRecordingViewMedia', () => {
|
||||
it('should get ID', () => {
|
||||
const media = new FrigateRecordingViewMedia(
|
||||
ViewMediaType.Recording,
|
||||
'camera',
|
||||
createFrigateRecording(),
|
||||
'rec-id',
|
||||
'content_id',
|
||||
'title',
|
||||
);
|
||||
expect(media.getID()).toBe('rec-id');
|
||||
});
|
||||
|
||||
it('should get start time', () => {
|
||||
const startTime = new Date('2023-04-29T14:00:00');
|
||||
const media = new FrigateRecordingViewMedia(
|
||||
ViewMediaType.Recording,
|
||||
'camera',
|
||||
createFrigateRecording({ startTime }),
|
||||
'id',
|
||||
'content_id',
|
||||
'title',
|
||||
);
|
||||
expect(media.getStartTime()).toEqual(startTime);
|
||||
});
|
||||
|
||||
it('should get end time', () => {
|
||||
const endTime = new Date('2023-04-29T14:59:59');
|
||||
const media = new FrigateRecordingViewMedia(
|
||||
ViewMediaType.Recording,
|
||||
'camera',
|
||||
createFrigateRecording({ endTime }),
|
||||
'id',
|
||||
'content_id',
|
||||
'title',
|
||||
);
|
||||
expect(media.getEndTime()).toEqual(endTime);
|
||||
});
|
||||
|
||||
it('should report not in progress', () => {
|
||||
const media = new FrigateRecordingViewMedia(
|
||||
ViewMediaType.Recording,
|
||||
'camera',
|
||||
createFrigateRecording(),
|
||||
'id',
|
||||
'content_id',
|
||||
'title',
|
||||
);
|
||||
expect(media.inProgress()).toBe(false);
|
||||
});
|
||||
|
||||
it('should get video content type as HLS', () => {
|
||||
const media = new FrigateRecordingViewMedia(
|
||||
ViewMediaType.Recording,
|
||||
'camera',
|
||||
createFrigateRecording(),
|
||||
'id',
|
||||
'content_id',
|
||||
'title',
|
||||
);
|
||||
expect(media.getVideoContentType()).toBe('hls');
|
||||
});
|
||||
|
||||
it('should get content ID', () => {
|
||||
const media = new FrigateRecordingViewMedia(
|
||||
ViewMediaType.Recording,
|
||||
'camera',
|
||||
createFrigateRecording(),
|
||||
'id',
|
||||
'my-content-id',
|
||||
'title',
|
||||
);
|
||||
expect(media.getContentID()).toBe('my-content-id');
|
||||
});
|
||||
|
||||
it('should get title', () => {
|
||||
const media = new FrigateRecordingViewMedia(
|
||||
ViewMediaType.Recording,
|
||||
'camera',
|
||||
createFrigateRecording(),
|
||||
'id',
|
||||
'content_id',
|
||||
'My Recording',
|
||||
);
|
||||
expect(media.getTitle()).toBe('My Recording');
|
||||
});
|
||||
|
||||
it('should get event count', () => {
|
||||
const media = new FrigateRecordingViewMedia(
|
||||
ViewMediaType.Recording,
|
||||
'camera',
|
||||
createFrigateRecording({ events: 5 }),
|
||||
'id',
|
||||
'content_id',
|
||||
'title',
|
||||
);
|
||||
expect(media.getEventCount()).toBe(5);
|
||||
});
|
||||
});
|
||||
|
||||
describe('FrigateReviewViewMedia', () => {
|
||||
it('should get ID', () => {
|
||||
const review = createFrigateReview({ id: 'rev-1' });
|
||||
const media = new FrigateReviewViewMedia('camera', review, 'content_id', 'thumb');
|
||||
expect(media.getID()).toBe('rev-1');
|
||||
});
|
||||
|
||||
it('should get start time', () => {
|
||||
const review = createFrigateReview({
|
||||
start_time: 1683395000,
|
||||
});
|
||||
const media = new FrigateReviewViewMedia('camera', review, 'content_id', 'thumb');
|
||||
expect(media.getStartTime()).toEqual(new Date(1683395000 * 1000));
|
||||
});
|
||||
|
||||
it('should get end time', () => {
|
||||
const review = createFrigateReview({
|
||||
end_time: 1683397124,
|
||||
});
|
||||
const media = new FrigateReviewViewMedia('camera', review, 'content_id', 'thumb');
|
||||
expect(media.getEndTime()).toEqual(new Date(1683397124 * 1000));
|
||||
});
|
||||
|
||||
it('should get null end time when null', () => {
|
||||
const review = createFrigateReview({ end_time: null });
|
||||
const media = new FrigateReviewViewMedia('camera', review, 'content_id', 'thumb');
|
||||
expect(media.getEndTime()).toBeNull();
|
||||
});
|
||||
|
||||
it('should report in progress when no end time', () => {
|
||||
const review = createFrigateReview({ end_time: null });
|
||||
const media = new FrigateReviewViewMedia('camera', review, 'content_id', 'thumb');
|
||||
expect(media.inProgress()).toBe(true);
|
||||
});
|
||||
|
||||
it('should report not in progress when end time set', () => {
|
||||
const review = createFrigateReview({
|
||||
end_time: 1683397124,
|
||||
});
|
||||
const media = new FrigateReviewViewMedia('camera', review, 'content_id', 'thumb');
|
||||
expect(media.inProgress()).toBe(false);
|
||||
});
|
||||
|
||||
it('should get video content type as HLS', () => {
|
||||
const media = new FrigateReviewViewMedia(
|
||||
'camera',
|
||||
createFrigateReview(),
|
||||
'content_id',
|
||||
'thumb',
|
||||
);
|
||||
expect(media.getVideoContentType()).toBe('hls');
|
||||
});
|
||||
|
||||
it('should get content ID', () => {
|
||||
const media = new FrigateReviewViewMedia(
|
||||
'camera',
|
||||
createFrigateReview(),
|
||||
'my-content',
|
||||
'thumb',
|
||||
);
|
||||
expect(media.getContentID()).toBe('my-content');
|
||||
});
|
||||
|
||||
it('should get title from metadata', () => {
|
||||
const review = createFrigateReview({
|
||||
data: {
|
||||
objects: ['person'],
|
||||
zones: [],
|
||||
metadata: { title: 'Metadata Title' },
|
||||
},
|
||||
});
|
||||
const media = new FrigateReviewViewMedia('camera', review, 'content_id', 'thumb');
|
||||
expect(media.getTitle()).toBe('Metadata Title');
|
||||
});
|
||||
|
||||
it('should get title from review util when no metadata', () => {
|
||||
const review = createFrigateReview({
|
||||
data: { objects: ['person'], zones: [] },
|
||||
});
|
||||
const media = new FrigateReviewViewMedia('camera', review, 'content_id', 'thumb');
|
||||
expect(media.getTitle()).toBe('Person');
|
||||
});
|
||||
|
||||
it('should get description from scene', () => {
|
||||
const review = createFrigateReview({
|
||||
data: {
|
||||
objects: [],
|
||||
zones: [],
|
||||
metadata: {
|
||||
scene: 'A person walking',
|
||||
title: 'Title',
|
||||
},
|
||||
},
|
||||
});
|
||||
const media = new FrigateReviewViewMedia('camera', review, 'content_id', 'thumb');
|
||||
expect(media.getDescription()).toBe('A person walking');
|
||||
});
|
||||
|
||||
it('should get description from shortSummary', () => {
|
||||
const review = createFrigateReview({
|
||||
data: {
|
||||
objects: [],
|
||||
zones: [],
|
||||
metadata: {
|
||||
shortSummary: 'Short summary text',
|
||||
title: 'Title',
|
||||
},
|
||||
},
|
||||
});
|
||||
const media = new FrigateReviewViewMedia('camera', review, 'content_id', 'thumb');
|
||||
expect(media.getDescription()).toBe('Short summary text');
|
||||
});
|
||||
|
||||
it('should get null description when no metadata', () => {
|
||||
const review = createFrigateReview({
|
||||
data: { objects: [], zones: [] },
|
||||
});
|
||||
const media = new FrigateReviewViewMedia('camera', review, 'content_id', 'thumb');
|
||||
expect(media.getDescription()).toBeNull();
|
||||
});
|
||||
|
||||
it('should get thumbnail', () => {
|
||||
const media = new FrigateReviewViewMedia(
|
||||
'camera',
|
||||
createFrigateReview(),
|
||||
'content_id',
|
||||
'my-thumb',
|
||||
);
|
||||
expect(media.getThumbnail()).toBe('my-thumb');
|
||||
});
|
||||
|
||||
it('should get null thumbnail', () => {
|
||||
const media = new FrigateReviewViewMedia(
|
||||
'camera',
|
||||
createFrigateReview(),
|
||||
'content_id',
|
||||
null,
|
||||
);
|
||||
expect(media.getThumbnail()).toBeNull();
|
||||
});
|
||||
|
||||
it('should get severity for alert', () => {
|
||||
const review = createFrigateReview({
|
||||
severity: 'alert',
|
||||
});
|
||||
const media = new FrigateReviewViewMedia('camera', review, 'content_id', 'thumb');
|
||||
expect(media.getSeverity()).toBe('high');
|
||||
});
|
||||
|
||||
it('should get severity for detection', () => {
|
||||
const review = createFrigateReview({
|
||||
severity: 'detection',
|
||||
});
|
||||
const media = new FrigateReviewViewMedia('camera', review, 'content_id', 'thumb');
|
||||
expect(media.getSeverity()).toBe('medium');
|
||||
});
|
||||
|
||||
it('should get reviewed status', () => {
|
||||
const review = createFrigateReview({
|
||||
has_been_reviewed: true,
|
||||
});
|
||||
const media = new FrigateReviewViewMedia('camera', review, 'content_id', 'thumb');
|
||||
expect(media.isReviewed()).toBe(true);
|
||||
});
|
||||
|
||||
it('should set reviewed status', () => {
|
||||
const media = new FrigateReviewViewMedia(
|
||||
'camera',
|
||||
createFrigateReview({ has_been_reviewed: false }),
|
||||
'content_id',
|
||||
'thumb',
|
||||
);
|
||||
media.setReviewed(true);
|
||||
expect(media.isReviewed()).toBe(true);
|
||||
});
|
||||
|
||||
it('should get what from objects', () => {
|
||||
const review = createFrigateReview({
|
||||
data: { objects: ['person', 'car'], zones: [] },
|
||||
});
|
||||
const media = new FrigateReviewViewMedia('camera', review, 'content_id', 'thumb');
|
||||
expect(media.getWhat()).toEqual(['person', 'car']);
|
||||
});
|
||||
|
||||
it('should get null what when no objects', () => {
|
||||
const review = createFrigateReview({
|
||||
data: { zones: [] },
|
||||
});
|
||||
const media = new FrigateReviewViewMedia('camera', review, 'content_id', 'thumb');
|
||||
expect(media.getWhat()).toBeNull();
|
||||
});
|
||||
|
||||
it('should get where from zones', () => {
|
||||
const review = createFrigateReview({
|
||||
data: { objects: [], zones: ['front_yard'] },
|
||||
});
|
||||
const media = new FrigateReviewViewMedia('camera', review, 'content_id', 'thumb');
|
||||
expect(media.getWhere()).toEqual(['front_yard']);
|
||||
});
|
||||
|
||||
it('should get null where when no zones', () => {
|
||||
const review = createFrigateReview({
|
||||
data: { objects: [], zones: [] },
|
||||
});
|
||||
const media = new FrigateReviewViewMedia('camera', review, 'content_id', 'thumb');
|
||||
expect(media.getWhere()).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('FrigateViewMediaFactory', () => {
|
||||
describe('createEventViewMedia', () => {
|
||||
it('should create clip view media', () => {
|
||||
const config = createCameraConfig({
|
||||
frigate: {
|
||||
client_id: 'client',
|
||||
camera_name: 'front',
|
||||
},
|
||||
});
|
||||
const event = createFrigateEvent({
|
||||
has_clip: true,
|
||||
});
|
||||
const media = FrigateViewMediaFactory.createEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
config,
|
||||
event,
|
||||
);
|
||||
expect(media).toBeInstanceOf(FrigateEventViewMedia);
|
||||
expect(media?.getContentID()).toContain('clips');
|
||||
});
|
||||
|
||||
it('should create snapshot view media', () => {
|
||||
const config = createCameraConfig({
|
||||
frigate: {
|
||||
client_id: 'client',
|
||||
camera_name: 'front',
|
||||
},
|
||||
});
|
||||
const event = createFrigateEvent({
|
||||
has_snapshot: true,
|
||||
});
|
||||
const media = FrigateViewMediaFactory.createEventViewMedia(
|
||||
ViewMediaType.Snapshot,
|
||||
'camera',
|
||||
config,
|
||||
event,
|
||||
);
|
||||
expect(media).toBeInstanceOf(FrigateEventViewMedia);
|
||||
expect(media?.getContentID()).toContain('snapshots');
|
||||
});
|
||||
|
||||
it('should return null when clip missing', () => {
|
||||
const config = createCameraConfig({
|
||||
frigate: {
|
||||
client_id: 'client',
|
||||
camera_name: 'front',
|
||||
},
|
||||
});
|
||||
const event = createFrigateEvent({
|
||||
has_clip: false,
|
||||
});
|
||||
const media = FrigateViewMediaFactory.createEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
config,
|
||||
event,
|
||||
);
|
||||
expect(media).toBeNull();
|
||||
});
|
||||
|
||||
it('should return null when snapshot missing', () => {
|
||||
const config = createCameraConfig({
|
||||
frigate: {
|
||||
client_id: 'client',
|
||||
camera_name: 'front',
|
||||
},
|
||||
});
|
||||
const event = createFrigateEvent({
|
||||
has_snapshot: false,
|
||||
});
|
||||
const media = FrigateViewMediaFactory.createEventViewMedia(
|
||||
ViewMediaType.Snapshot,
|
||||
'camera',
|
||||
config,
|
||||
event,
|
||||
);
|
||||
expect(media).toBeNull();
|
||||
});
|
||||
|
||||
it('should return null when no camera_name', () => {
|
||||
const config = createCameraConfig();
|
||||
const media = FrigateViewMediaFactory.createEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
config,
|
||||
createFrigateEvent(),
|
||||
);
|
||||
expect(media).toBeNull();
|
||||
});
|
||||
|
||||
it('should pass sub-labels through', () => {
|
||||
const config = createCameraConfig({
|
||||
frigate: {
|
||||
client_id: 'client',
|
||||
camera_name: 'front',
|
||||
},
|
||||
});
|
||||
const media = FrigateViewMediaFactory.createEventViewMedia(
|
||||
ViewMediaType.Clip,
|
||||
'camera',
|
||||
config,
|
||||
createFrigateEvent(),
|
||||
['Amazon'],
|
||||
);
|
||||
expect(media?.getTags()).toEqual(['Amazon']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createRecordingViewMedia', () => {
|
||||
it('should create recording view media', () => {
|
||||
const config = createCameraConfig({
|
||||
frigate: {
|
||||
client_id: 'client',
|
||||
camera_name: 'front',
|
||||
},
|
||||
});
|
||||
const media = FrigateViewMediaFactory.createRecordingViewMedia(
|
||||
'camera',
|
||||
createFrigateRecording(),
|
||||
config,
|
||||
'Front Camera',
|
||||
);
|
||||
expect(media).toBeInstanceOf(FrigateRecordingViewMedia);
|
||||
});
|
||||
|
||||
it('should return null when no camera_name', () => {
|
||||
const config = createCameraConfig();
|
||||
const media = FrigateViewMediaFactory.createRecordingViewMedia(
|
||||
'camera',
|
||||
createFrigateRecording(),
|
||||
config,
|
||||
'Front Camera',
|
||||
);
|
||||
expect(media).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('createReviewViewMedia', () => {
|
||||
it('should create review view media', () => {
|
||||
const config = createCameraConfig({
|
||||
frigate: {
|
||||
client_id: 'client',
|
||||
camera_name: 'front',
|
||||
},
|
||||
});
|
||||
const media = FrigateViewMediaFactory.createReviewViewMedia(
|
||||
'camera',
|
||||
createFrigateReview(),
|
||||
config,
|
||||
);
|
||||
expect(media).toBeInstanceOf(FrigateReviewViewMedia);
|
||||
});
|
||||
|
||||
it('should return null when no camera_name', () => {
|
||||
const config = createCameraConfig();
|
||||
const media = FrigateViewMediaFactory.createReviewViewMedia(
|
||||
'camera',
|
||||
createFrigateReview(),
|
||||
config,
|
||||
);
|
||||
expect(media).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { recordingSummarySchema } from '../../../src/camera-manager/frigate/types';
|
||||
|
||||
describe('recordingSummarySchema', () => {
|
||||
it('should parse day string to date', () => {
|
||||
const result = recordingSummarySchema.parse([
|
||||
{
|
||||
day: '2023-05-06',
|
||||
events: 10,
|
||||
hours: [],
|
||||
},
|
||||
]);
|
||||
expect(result[0].day).toBeInstanceOf(Date);
|
||||
expect(result[0].day.getFullYear()).toBe(2023);
|
||||
});
|
||||
|
||||
it('should pass through non-string day values', () => {
|
||||
const date = new Date('2023-05-06');
|
||||
const result = recordingSummarySchema.parse([
|
||||
{
|
||||
day: date,
|
||||
events: 5,
|
||||
hours: [],
|
||||
},
|
||||
]);
|
||||
expect(result[0].day).toEqual(date);
|
||||
});
|
||||
|
||||
it('should preprocess hour from string to number', () => {
|
||||
const result = recordingSummarySchema.parse([
|
||||
{
|
||||
day: '2023-05-06',
|
||||
events: 3,
|
||||
hours: [{ hour: '15', duration: 3600, events: 3 }],
|
||||
},
|
||||
]);
|
||||
expect(result[0].hours[0].hour).toBe(15);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,117 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
import { CameraManagerEngine } from '../../../src/camera-manager/engine';
|
||||
import { MotionEyeCamera } from '../../../src/camera-manager/motioneye/camera';
|
||||
import { StateWatcher } from '../../../src/card-controller/hass/state-watcher';
|
||||
import { EntityRegistryManagerMock } from '../../ha/registry/entity/mock';
|
||||
import { createCameraConfig, createHASS, createRegistryEntity } from '../../test-utils';
|
||||
|
||||
const cameraEntity = createRegistryEntity({
|
||||
entity_id: 'camera.motioneye',
|
||||
platform: 'motioneye',
|
||||
});
|
||||
|
||||
describe('MotionEyeCamera', () => {
|
||||
describe('getProxyConfig', () => {
|
||||
it('should proxy media when proxy.media is auto', () => {
|
||||
const config = createCameraConfig({
|
||||
proxy: { media: 'auto' },
|
||||
});
|
||||
const camera = new MotionEyeCamera(config, mock<CameraManagerEngine>());
|
||||
expect(camera.getProxyConfig().media).toBe(true);
|
||||
});
|
||||
|
||||
it('should respect explicit false for proxy.media', () => {
|
||||
const config = createCameraConfig({
|
||||
proxy: { media: false },
|
||||
});
|
||||
const camera = new MotionEyeCamera(config, mock<CameraManagerEngine>());
|
||||
expect(camera.getProxyConfig().media).toBe(false);
|
||||
});
|
||||
|
||||
it('should respect explicit true for proxy.media', () => {
|
||||
const config = createCameraConfig({
|
||||
proxy: { media: true },
|
||||
});
|
||||
const camera = new MotionEyeCamera(config, mock<CameraManagerEngine>());
|
||||
expect(camera.getProxyConfig().media).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getEndpoints', () => {
|
||||
it('should return UI endpoint when motioneye url set', async () => {
|
||||
const config = createCameraConfig({
|
||||
camera_entity: 'camera.motioneye',
|
||||
motioneye: { url: 'http://motioneye.local' },
|
||||
});
|
||||
const camera = new MotionEyeCamera(config, mock<CameraManagerEngine>());
|
||||
await camera.initialize({
|
||||
hass: createHASS(),
|
||||
entityRegistryManager: new EntityRegistryManagerMock([cameraEntity]),
|
||||
stateWatcher: mock<StateWatcher>(),
|
||||
});
|
||||
|
||||
const endpoints = camera.getEndpoints();
|
||||
expect(endpoints?.ui).toEqual({
|
||||
endpoint: 'http://motioneye.local',
|
||||
});
|
||||
});
|
||||
|
||||
it('should return null UI endpoint when no url', async () => {
|
||||
const config = createCameraConfig({
|
||||
camera_entity: 'camera.motioneye',
|
||||
});
|
||||
const camera = new MotionEyeCamera(config, mock<CameraManagerEngine>());
|
||||
await camera.initialize({
|
||||
hass: createHASS(),
|
||||
entityRegistryManager: new EntityRegistryManagerMock([cameraEntity]),
|
||||
stateWatcher: mock<StateWatcher>(),
|
||||
});
|
||||
|
||||
const endpoints = camera.getEndpoints();
|
||||
expect(endpoints?.ui).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('capabilities', () => {
|
||||
it('should include clips and snapshots', async () => {
|
||||
const config = createCameraConfig({
|
||||
camera_entity: 'camera.motioneye',
|
||||
});
|
||||
const camera = new MotionEyeCamera(config, mock<CameraManagerEngine>());
|
||||
await camera.initialize({
|
||||
hass: createHASS(),
|
||||
entityRegistryManager: new EntityRegistryManagerMock([cameraEntity]),
|
||||
stateWatcher: mock<StateWatcher>(),
|
||||
});
|
||||
|
||||
const capabilities = camera.getCapabilities();
|
||||
expect(capabilities?.has('clips')).toBe(true);
|
||||
expect(capabilities?.has('snapshots')).toBe(true);
|
||||
});
|
||||
|
||||
it('should include PTZ when configured', async () => {
|
||||
const config = createCameraConfig({
|
||||
camera_entity: 'camera.motioneye',
|
||||
ptz: {
|
||||
actions_left: {
|
||||
action: 'perform-action',
|
||||
perform_action: 'homeassistant.toggle',
|
||||
target: {
|
||||
entity_id: 'switch.camera_move_left',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const camera = new MotionEyeCamera(config, mock<CameraManagerEngine>());
|
||||
await camera.initialize({
|
||||
hass: createHASS(),
|
||||
entityRegistryManager: new EntityRegistryManagerMock([cameraEntity]),
|
||||
stateWatcher: mock<StateWatcher>(),
|
||||
});
|
||||
|
||||
const capabilities = camera.getCapabilities();
|
||||
expect(capabilities?.getPTZCapabilities()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user