Files
advanced-camera-card/tests/config/types.test.ts
T

492 lines
12 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import {
conditionalSchema,
customSchema,
dimensionsConfigSchema,
frigateCardCustomActionsBaseSchema,
frigateCardPTZSchema,
} from '../../src/config/types';
import { createConfig } from '../test-utils';
// @vitest-environment jsdom
describe('config defaults', () => {
it('should be as expected', () => {
expect(createConfig()).toEqual({
cameras: [],
cameras_global: {
dependencies: {
all_cameras: false,
cameras: [],
},
engine: 'auto',
frigate: {
client_id: 'frigate',
},
image: {
refresh_seconds: 1,
},
live_provider: 'auto',
motioneye: {
images: {
directory_pattern: '%Y-%m-%d',
file_pattern: '%H-%M-%S',
},
movies: {
directory_pattern: '%Y-%m-%d',
file_pattern: '%H-%M-%S',
},
},
triggers: {
events: ['events', 'clips', 'snapshots'],
entities: [],
motion: false,
occupancy: false,
},
},
debug: {
logging: false,
},
dimensions: {
aspect_ratio: [16, 9],
aspect_ratio_mode: 'dynamic',
max_height: '100vh',
min_height: '100px',
},
image: {
mode: 'url',
refresh_seconds: 1,
zoomable: true,
},
live: {
auto_mute: ['unselected', 'hidden', 'microphone'],
auto_pause: [],
auto_play: ['selected', 'visible'],
auto_unmute: ['microphone'],
controls: {
builtin: true,
next_previous: {
size: 48,
style: 'chevrons',
},
ptz: {
hide_home: false,
hide_pan_tilt: false,
hide_zoom: false,
mode: 'on',
orientation: 'horizontal',
position: 'bottom-right',
},
thumbnails: {
media_type: 'events',
events_media_type: 'all',
mode: 'right',
show_details: true,
show_download_control: true,
show_favorite_control: true,
show_timeline_control: true,
size: 100,
},
timeline: {
clustering_threshold: 3,
events_media_type: 'all',
mode: 'none',
pan_mode: 'pan',
show_recordings: true,
style: 'ribbon',
window_seconds: 3600,
},
},
draggable: true,
lazy_load: true,
lazy_unload: [],
microphone: {
always_connected: false,
disconnect_seconds: 90,
mute_after_microphone_mute_seconds: 60,
},
preload: false,
show_image_during_load: true,
transition_effect: 'slide',
zoomable: true,
},
media_gallery: {
controls: {
filter: {
mode: 'right',
},
thumbnails: {
mode: 'right',
show_details: false,
show_download_control: true,
show_favorite_control: true,
show_timeline_control: true,
size: 100,
},
},
},
media_viewer: {
auto_mute: ['unselected', 'hidden'],
auto_pause: ['unselected', 'hidden'],
auto_play: ['selected', 'visible'],
auto_unmute: [],
controls: {
builtin: true,
next_previous: {
size: 48,
style: 'thumbnails',
},
thumbnails: {
mode: 'right',
show_details: true,
show_download_control: true,
show_favorite_control: true,
show_timeline_control: true,
size: 100,
},
timeline: {
clustering_threshold: 3,
events_media_type: 'all',
mode: 'none',
pan_mode: 'pan',
show_recordings: true,
style: 'ribbon',
window_seconds: 3600,
},
},
draggable: true,
lazy_load: true,
snapshot_click_plays_clip: true,
transition_effect: 'slide',
zoomable: true,
},
menu: {
alignment: 'left',
button_size: 40,
buttons: {
camera_ui: {
enabled: true,
priority: 50,
},
cameras: {
enabled: true,
priority: 50,
},
clips: {
enabled: true,
priority: 50,
},
display_mode: {
enabled: true,
priority: 50,
},
download: {
enabled: true,
priority: 50,
},
expand: {
enabled: false,
priority: 50,
},
frigate: {
enabled: true,
priority: 50,
},
fullscreen: {
enabled: true,
priority: 50,
},
image: {
enabled: false,
priority: 50,
},
live: {
enabled: true,
priority: 50,
},
media_player: {
enabled: true,
priority: 50,
},
microphone: {
enabled: false,
priority: 50,
type: 'momentary',
},
mute: {
enabled: false,
priority: 50,
},
play: {
enabled: false,
priority: 50,
},
ptz: {
enabled: false,
priority: 50,
},
recordings: {
enabled: false,
priority: 50,
},
screenshot: {
enabled: false,
priority: 50,
},
snapshots: {
enabled: true,
priority: 50,
},
substreams: {
enabled: true,
priority: 50,
},
timeline: {
enabled: true,
priority: 50,
},
},
position: 'top',
style: 'hidden',
},
performance: {
features: {
animated_progress_indicator: true,
media_chunk_size: 50,
},
style: {
border_radius: true,
box_shadow: true,
},
},
timeline: {
clustering_threshold: 3,
controls: {
thumbnails: {
mode: 'right',
show_details: true,
show_download_control: true,
show_favorite_control: true,
show_timeline_control: true,
size: 100,
},
},
events_media_type: 'all',
pan_mode: 'pan',
show_recordings: true,
style: 'stack',
window_seconds: 3600,
},
type: 'frigate-hass-card',
view: {
camera_select: 'current',
dark_mode: 'off',
default: 'live',
triggers: {
show_trigger_status: false,
untrigger_seconds: 0,
actions: {
trigger: 'default',
untrigger: 'none',
interaction_mode: 'inactive',
},
filter_selected_camera: true,
},
interaction_seconds: 300,
reset_after_interaction: true,
update_cycle_camera: false,
update_force: false,
update_seconds: 0,
},
});
});
});
it('should transform dimensions.aspect_ratio', () => {
expect(
dimensionsConfigSchema.parse({
aspect_ratio: '16 / 9',
}),
).toEqual(expect.objectContaining({ aspect_ratio: [16, 9] }));
expect(
dimensionsConfigSchema.parse({
aspect_ratio: '16 : 9',
}),
).toEqual(expect.objectContaining({ aspect_ratio: [16, 9] }));
expect(
dimensionsConfigSchema.parse({
aspect_ratio: [16, 9],
}),
).toEqual(expect.objectContaining({ aspect_ratio: [16, 9] }));
});
it('should transform action', () => {
expect(
frigateCardCustomActionsBaseSchema.parse({
action: 'custom:frigate-card-action',
}),
).toEqual({
action: 'fire-dom-event',
});
});
describe('should convert webrtc card PTZ to Frigate card PTZ', () => {
it.each([
['left' as const],
['right' as const],
['up' as const],
['down' as const],
['zoom_in' as const],
['zoom_out' as const],
['home' as const],
])('%s', (action: string) => {
expect(
frigateCardPTZSchema.parse({
type: 'custom:frigate-card-ptz',
service: 'foo',
[`data_${action}`]: {
tap_action: {
action: 'none',
},
},
}),
).toEqual({
[`actions_${action}`]: {
tap_action: {
action: 'call-service',
service: 'foo',
data: {
tap_action: {
action: 'none',
},
},
},
},
service: 'foo',
hide_home: false,
hide_pan_tilt: false,
hide_zoom: false,
mode: 'on',
orientation: 'horizontal',
position: 'bottom-right',
});
});
});
describe('should lazy evaluate', () => {
it('conditional picture element', () => {
expect(
conditionalSchema.parse({
type: 'conditional',
conditions: [
{
condition: 'state',
entity: 'light.office_main_lights',
state: 'on',
state_not: 'off',
},
],
elements: [
{
type: 'icon',
icon: 'mdi:dog',
title: 'Woof',
style: {
left: '100px',
top: '400px',
},
},
],
}),
).toEqual({
conditions: [
{
condition: 'state',
entity: 'light.office_main_lights',
state: 'on',
state_not: 'off',
},
],
elements: [
{
icon: 'mdi:dog',
style: {
left: '100px',
top: '400px',
},
title: 'Woof',
type: 'icon',
},
],
type: 'conditional',
});
});
});
describe('should handle custom frigate elements', () => {
it('should add custom error on frigate entry', () => {
const result = customSchema.safeParse({
type: 'custom:frigate-card-foo',
});
expect(result.success).toBeFalsy();
if (!result.success) {
expect(result.error.errors[0]).toEqual({
code: 'custom',
message: 'Frigate-card custom elements must match specific schemas',
fatal: true,
path: ['type'],
});
}
});
it('should not add custom error on valid entry', () => {
const result = customSchema.safeParse({
type: 'custom:foo',
});
expect(result.success).toBeTruthy();
});
});
// https://github.com/dermotduffy/frigate-hass-card/issues/1280
it('should not require title controls to specify all options', () => {
expect(
createConfig({
cameras: [],
live: {
controls: {
title: {
mode: 'popup-top-left',
},
},
},
}),
).toBeTruthy();
});
it('should strip trailing slashes from go2rtc url', () => {
const config = createConfig({
cameras: [
{
go2rtc: {
url: 'https://my-custom-go2rtc//',
},
},
],
});
expect(config).toBeTruthy();
expect(config.cameras[0].go2rtc.url).toBe('https://my-custom-go2rtc');
});
it('media viewer should not support microphone based conditions', () => {
expect(() =>
createConfig({
cameras: [],
media_viewer: {
auto_unmute: 'microphone' as const,
},
}),
).toThrowError();
});