Files
advanced-camera-card/tests/card-controller/status-bar-item-manager.test.ts
T

448 lines
14 KiB
TypeScript

import { describe, expect, it, vi } from 'vitest';
import { StatusBarItemManager } from '../../src/card-controller/status-bar-item-manager';
import type { StatusBarString } from '../../src/config/schema/actions/types';
import { QueryResults } from '../../src/view/query-results';
import { createCameraManager, createStore } from '../camera-manager/test-utils';
import { createCardAPI } from '../test-utils';
import { createView, TestViewMedia } from '../view/test-utils';
describe('StatusBarItemManager', () => {
const testItem: StatusBarString = {
type: 'custom:advanced-camera-card-status-bar-string' as const,
string: 'test',
};
it('should add', () => {
const manager = new StatusBarItemManager(createCardAPI());
manager.addDynamicStatusBarItem(testItem);
manager.addDynamicStatusBarItem(testItem);
expect(manager.calculateItems()).toEqual([testItem]);
});
it('should remove', () => {
const manager = new StatusBarItemManager(createCardAPI());
manager.addDynamicStatusBarItem(testItem);
manager.removeDynamicStatusBarItem({ ...testItem });
expect(manager.calculateItems()).not.toContain(testItem);
manager.removeDynamicStatusBarItem({ ...testItem, string: 'not-present' });
expect(manager.calculateItems()).not.toContain({
...testItem,
string: 'not-present',
});
});
it('should remove all', () => {
const manager = new StatusBarItemManager(createCardAPI());
manager.addDynamicStatusBarItem(testItem);
manager.removeAllDynamicStatusBarItems();
expect(manager.calculateItems()).not.toContain(testItem);
});
describe('should have standard status bar items', () => {
describe('should have title', () => {
describe('live', () => {
it('should show with metadata', () => {
const manager = new StatusBarItemManager(createCardAPI());
const store = createStore([
{
cameraID: 'camera-1',
},
]);
const cameraManager = createCameraManager(store);
vi.mocked(cameraManager.getCameraMetadata).mockReturnValue({
title: 'Camera Title',
icon: { icon: 'mdi:camera' },
});
expect(
manager.calculateItems({
cameraManager: cameraManager,
view: createView({ view: 'live', camera: 'camera-1' }),
}),
).toContainEqual({
type: 'custom:advanced-camera-card-status-bar-string' as const,
string: 'Camera Title',
expand: true,
sufficient: true,
});
});
it('should handle without metadata', () => {
const manager = new StatusBarItemManager(createCardAPI());
const cameraManager = createCameraManager();
expect(
manager.calculateItems({
cameraManager: cameraManager,
view: createView({ view: 'live', camera: 'MISSING-CAMERA' }),
}),
).not.toContainEqual(expect.objectContaining({ sufficient: true }));
});
});
describe('media', () => {
it('should show with a title', () => {
const manager = new StatusBarItemManager(createCardAPI());
const cameraManager = createCameraManager();
const media = [new TestViewMedia({ title: 'Media Title' })];
const queryResults = new QueryResults({
results: media,
});
const view = createView({
view: 'media',
queryResults: queryResults,
});
expect(
manager.calculateItems({
cameraManager: cameraManager,
view: view,
}),
).toContainEqual({
type: 'custom:advanced-camera-card-status-bar-string' as const,
string: 'Media Title',
expand: true,
sufficient: true,
});
});
it('should handle without a title', () => {
const manager = new StatusBarItemManager(createCardAPI());
const cameraManager = createCameraManager();
const media = [new TestViewMedia()];
const queryResults = new QueryResults({
results: media,
});
const view = createView({
view: 'media',
queryResults: queryResults,
});
expect(
manager.calculateItems({
cameraManager: cameraManager,
view: view,
}),
).not.toContainEqual(expect.objectContaining({ sufficient: true }));
});
});
});
describe('should have resolution', () => {
it.each([
['1080p landscape', '1080p', 1920, 1080],
['1080p portrait', '1080p', 1080, 1920],
['1080p approximate', '1080p', 1922, 1082],
['720p landscape', '720p', 1280, 720],
['720p portrait', '720p', 720, 1280],
['720p approximate', '720p', 1282, 722],
['VGA landscape', 'VGA', 640, 480],
['VGA portrait', 'VGA', 480, 640],
['VGA approximate', 'VGA', 642, 482],
['4K landscape', '4K', 3840, 2160],
['4K portrait', '4K', 2160, 3840],
['4K approximate', '4K', 3842, 2162],
['480p landscape', '480p', 720, 480],
['480p portrait', '480p', 480, 720],
['480p approximate', '480p', 722, 482],
['576p landscape', '576p', 720, 576],
['576p portrait', '576p', 576, 720],
['576p approximate', '576p', 722, 578],
['8K landscape', '8K', 7680, 4320],
['8K portrait', '8K', 4320, 7680],
['8K approximate', '8K', 7682, 4322],
['random', '123x456', 123, 456],
])(
'%s',
(_testName: string, expectedName: string, width: number, height: number) => {
const manager = new StatusBarItemManager(createCardAPI());
expect(
manager.calculateItems({
mediaLoadedInfo: { width, height },
}),
).toContainEqual({
type: 'custom:advanced-camera-card-status-bar-string' as const,
string: expectedName,
});
},
);
});
describe('should have technology', () => {
it('should show webrtc icon', () => {
const manager = new StatusBarItemManager(createCardAPI());
expect(
manager.calculateItems({
mediaLoadedInfo: { width: 640, height: 480, technology: ['webrtc'] },
}),
).toContainEqual({
type: 'custom:advanced-camera-card-status-bar-icon' as const,
icon: 'mdi:webrtc',
});
});
it('should show non-webrtc string', () => {
const manager = new StatusBarItemManager(createCardAPI());
expect(
manager.calculateItems({
mediaLoadedInfo: { width: 640, height: 480, technology: ['hls'] },
}),
).toContainEqual({
type: 'custom:advanced-camera-card-status-bar-string' as const,
string: 'HLS',
});
});
});
it('should have engine', () => {
const manager = new StatusBarItemManager(createCardAPI());
const store = createStore([
{
cameraID: 'camera-1',
},
]);
const cameraManager = createCameraManager(store);
vi.mocked(cameraManager.getCameraMetadata).mockReturnValue({
title: 'Camera Title',
icon: {
icon: 'mdi:camera',
},
engineIcon: 'ENGINE_ICON',
});
expect(
manager.calculateItems({
cameraManager: cameraManager,
view: createView({ view: 'live', camera: 'camera-1' }),
}),
).toContainEqual({
type: 'custom:advanced-camera-card-status-bar-icon' as const,
icon: 'ENGINE_ICON',
});
});
describe('issues', () => {
it('should show issue items', () => {
const manager = new StatusBarItemManager(createCardAPI());
const items = manager.calculateItems({
issues: [
{
key: 'config_upgrade',
issue: {
icon: 'mdi:update',
severity: 'medium',
notification: {
heading: {
text: 'Upgrade available',
icon: 'mdi:update',
severity: 'medium',
},
body: { text: 'Upgrade text' },
},
},
},
],
});
expect(items).toContainEqual(
expect.objectContaining({
type: 'custom:advanced-camera-card-status-bar-icon' as const,
icon: 'mdi:update',
severity: 'medium',
title: 'Upgrade available',
actions: expect.objectContaining({
tap_action: expect.objectContaining({
action: 'fire-dom-event',
advanced_camera_card_action: 'notification',
}),
}),
}),
);
});
it('should not show issue items when empty', () => {
const manager = new StatusBarItemManager(createCardAPI());
const items = manager.calculateItems({
issues: [],
});
expect(items).not.toContainEqual(
expect.objectContaining({
icon: 'mdi:update',
}),
);
});
it('should not show issue items by default', () => {
const manager = new StatusBarItemManager(createCardAPI());
const items = manager.calculateItems();
expect(items).not.toContainEqual(
expect.objectContaining({
icon: 'mdi:update',
}),
);
});
it('should filter out all issues when disabled', () => {
const manager = new StatusBarItemManager(createCardAPI());
const items = manager.calculateItems({
statusConfig: {
auto_hide: [],
position: 'bottom',
style: 'popup',
popup_seconds: 3,
height: 40,
items: {
engine: { enabled: true, priority: 50 },
issues: { enabled: false, priority: 50 },
resolution: { enabled: true, priority: 50 },
severity: { enabled: true, priority: 50 },
technology: { enabled: true, priority: 50 },
title: { enabled: true, priority: 50 },
},
},
issues: [
{
key: 'config_upgrade',
issue: {
icon: 'mdi:update',
severity: 'medium',
notification: {
heading: {
text: 'Upgrade available',
icon: 'mdi:update',
severity: 'medium',
},
body: { text: 'Upgrade text' },
},
},
},
],
});
expect(items).not.toContainEqual(
expect.objectContaining({
icon: 'mdi:update',
}),
);
});
it('should apply config overrides to issue items', () => {
const manager = new StatusBarItemManager(createCardAPI());
const items = manager.calculateItems({
statusConfig: {
auto_hide: [],
position: 'bottom',
style: 'popup',
popup_seconds: 3,
height: 40,
items: {
engine: { enabled: true, priority: 50 },
issues: { enabled: true, priority: 90 },
resolution: { enabled: true, priority: 50 },
severity: { enabled: true, priority: 50 },
technology: { enabled: true, priority: 50 },
title: { enabled: true, priority: 50 },
},
},
issues: [
{
key: 'config_upgrade',
issue: {
icon: 'mdi:update',
severity: 'medium',
notification: {
heading: {
text: 'Upgrade available',
icon: 'mdi:update',
severity: 'medium',
},
body: { text: 'Upgrade text' },
},
},
},
],
});
expect(items).toContainEqual(
expect.objectContaining({
icon: 'mdi:update',
priority: 90,
}),
);
});
});
describe('severity', () => {
it('should have severity in a viewer view', () => {
const manager = new StatusBarItemManager(createCardAPI());
const selectedResult = new TestViewMedia({
cameraID: 'camera-1',
});
vi.spyOn(selectedResult, 'getSeverity').mockReturnValue('high');
const queryResults = new QueryResults({
results: [selectedResult],
selectedIndex: 0,
});
expect(
manager.calculateItems({
view: createView({ view: 'media', queryResults: queryResults }),
}),
).toContainEqual({
type: 'custom:advanced-camera-card-status-bar-icon' as const,
icon: 'mdi:circle-medium',
severity: 'high',
});
});
it('should not have severity in live view', () => {
const manager = new StatusBarItemManager(createCardAPI());
const selectedResult = new TestViewMedia({
cameraID: 'camera-1',
});
vi.spyOn(selectedResult, 'getSeverity').mockReturnValue('high');
const queryResults = new QueryResults({
results: [selectedResult],
selectedIndex: 0,
});
expect(
manager.calculateItems({
view: createView({ view: 'live', queryResults: queryResults }),
}),
).not.toContainEqual(
expect.objectContaining({
icon: 'mdi:circle-medium',
}),
);
});
});
});
});