Merge pull request #1396 from dermotduffy/better-button-accuracy
Ensure timeline menu button shows up when it should
This commit is contained in:
@@ -3,7 +3,6 @@ import {
|
|||||||
ActionsConfig,
|
ActionsConfig,
|
||||||
ActionType,
|
ActionType,
|
||||||
FrigateCardCustomAction,
|
FrigateCardCustomAction,
|
||||||
FRIGATE_CARD_VIEW_DEFAULT,
|
|
||||||
} from '../config/types.js';
|
} from '../config/types.js';
|
||||||
import {
|
import {
|
||||||
convertActionToFrigateCardCustomAction,
|
convertActionToFrigateCardCustomAction,
|
||||||
@@ -177,14 +176,10 @@ export class ActionsManager {
|
|||||||
const viewOnCameraSelect = config?.view.camera_select ?? 'current';
|
const viewOnCameraSelect = config?.view.camera_select ?? 'current';
|
||||||
const targetViewName =
|
const targetViewName =
|
||||||
viewOnCameraSelect === 'current' ? view.view : viewOnCameraSelect;
|
viewOnCameraSelect === 'current' ? view.view : viewOnCameraSelect;
|
||||||
const verifiedViewName = this._api
|
|
||||||
.getViewManager()
|
|
||||||
.isViewSupportedByCamera(selectCameraID, targetViewName)
|
|
||||||
? targetViewName
|
|
||||||
: FRIGATE_CARD_VIEW_DEFAULT;
|
|
||||||
this._api.getViewManager().setViewByParameters({
|
this._api.getViewManager().setViewByParameters({
|
||||||
viewName: verifiedViewName,
|
viewName: targetViewName,
|
||||||
cameraID: selectCameraID,
|
cameraID: selectCameraID,
|
||||||
|
failSafe: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -190,7 +190,17 @@ export class ViewManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public isViewSupportedByCamera(cameraID: string, view: FrigateCardView): boolean {
|
public isViewSupportedByCamera(cameraID: string, view: FrigateCardView): boolean {
|
||||||
const capabilities = this._api.getCameraManager().getCameraCapabilities(cameraID);
|
const dependentCamerasCapabilities = this._api
|
||||||
|
.getCameraManager()
|
||||||
|
.getAggregateCameraCapabilities(
|
||||||
|
this._api.getCameraManager().getStore().getAllDependentCameras(cameraID),
|
||||||
|
);
|
||||||
|
const allCamerasCapabilities = this._api
|
||||||
|
.getCameraManager()
|
||||||
|
.getAggregateCameraCapabilities(
|
||||||
|
this._api.getCameraManager().getStore().getCameraIDs(),
|
||||||
|
);
|
||||||
|
|
||||||
switch (view) {
|
switch (view) {
|
||||||
case 'live':
|
case 'live':
|
||||||
case 'image':
|
case 'image':
|
||||||
@@ -198,20 +208,22 @@ export class ViewManager {
|
|||||||
return true;
|
return true;
|
||||||
case 'clip':
|
case 'clip':
|
||||||
case 'clips':
|
case 'clips':
|
||||||
return !!capabilities?.supportsClips;
|
return !!dependentCamerasCapabilities?.supportsClips;
|
||||||
case 'snapshot':
|
case 'snapshot':
|
||||||
case 'snapshots':
|
case 'snapshots':
|
||||||
return !!capabilities?.supportsSnapshots;
|
return !!dependentCamerasCapabilities?.supportsSnapshots;
|
||||||
case 'recording':
|
case 'recording':
|
||||||
case 'recordings':
|
case 'recordings':
|
||||||
return !!capabilities?.supportsRecordings;
|
return !!dependentCamerasCapabilities?.supportsRecordings;
|
||||||
case 'timeline':
|
case 'timeline':
|
||||||
return !!capabilities?.supportsTimeline;
|
// Show the timeline if any camera supports it, even cameras unrelated
|
||||||
|
// to the currently selected camera.
|
||||||
|
return !!allCamerasCapabilities?.supportsTimeline;
|
||||||
case 'media':
|
case 'media':
|
||||||
return (
|
return (
|
||||||
!!capabilities?.supportsClips ||
|
!!dependentCamerasCapabilities?.supportsClips ||
|
||||||
!!capabilities?.supportsSnapshots ||
|
!!dependentCamerasCapabilities?.supportsSnapshots ||
|
||||||
!!capabilities?.supportsRecordings
|
!!dependentCamerasCapabilities?.supportsRecordings
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -209,6 +209,7 @@ class FrigateCard extends LitElement {
|
|||||||
showCameraUIButton: this._controller.getCameraURLManager().hasCameraURL(),
|
showCameraUIButton: this._controller.getCameraURLManager().hasCameraURL(),
|
||||||
mediaPlayerController: this._controller.getMediaPlayerManager(),
|
mediaPlayerController: this._controller.getMediaPlayerManager(),
|
||||||
microphoneManager: this._controller.getMicrophoneManager(),
|
microphoneManager: this._controller.getMicrophoneManager(),
|
||||||
|
viewManager: this._controller.getViewManager(),
|
||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
.entityRegistryManager=${this._controller.getEntityRegistryManager()}
|
.entityRegistryManager=${this._controller.getEntityRegistryManager()}
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ import { StyleInfo } from 'lit/directives/style-map';
|
|||||||
import { CameraManager } from '../camera-manager/manager';
|
import { CameraManager } from '../camera-manager/manager';
|
||||||
import { MediaPlayerManager } from '../card-controller/media-player-manager';
|
import { MediaPlayerManager } from '../card-controller/media-player-manager';
|
||||||
import { MicrophoneManager } from '../card-controller/microphone-manager';
|
import { MicrophoneManager } from '../card-controller/microphone-manager';
|
||||||
|
import { ViewManager } from '../card-controller/view-manager';
|
||||||
import {
|
import {
|
||||||
|
FRIGATE_CARD_VIEWS_USER_SPECIFIED,
|
||||||
FrigateCardConfig,
|
FrigateCardConfig,
|
||||||
FrigateCardCustomAction,
|
FrigateCardCustomAction,
|
||||||
FRIGATE_CARD_VIEWS_USER_SPECIFIED,
|
|
||||||
MenuItem,
|
MenuItem,
|
||||||
} from '../config/types';
|
} from '../config/types';
|
||||||
import { FRIGATE_BUTTON_MENU_ICON } from '../const';
|
import { FRIGATE_BUTTON_MENU_ICON } from '../const';
|
||||||
@@ -31,6 +32,7 @@ export interface MenuButtonControllerOptions {
|
|||||||
inExpandedMode?: boolean;
|
inExpandedMode?: boolean;
|
||||||
microphoneManager?: MicrophoneManager | null;
|
microphoneManager?: MicrophoneManager | null;
|
||||||
mediaPlayerController?: MediaPlayerManager | null;
|
mediaPlayerController?: MediaPlayerManager | null;
|
||||||
|
viewManager?: ViewManager | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MenuButtonController {
|
export class MenuButtonController {
|
||||||
@@ -62,18 +64,19 @@ export class MenuButtonController {
|
|||||||
): MenuItem[] {
|
): MenuItem[] {
|
||||||
const visibleCameraIDs = cameraManager.getStore().getVisibleCameraIDs();
|
const visibleCameraIDs = cameraManager.getStore().getVisibleCameraIDs();
|
||||||
const selectedCameraID = view.camera;
|
const selectedCameraID = view.camera;
|
||||||
|
const substreamAwareCameraID =
|
||||||
|
view.context?.live?.overrides?.get(selectedCameraID) ?? selectedCameraID;
|
||||||
const selectedCameraConfig = cameraManager
|
const selectedCameraConfig = cameraManager
|
||||||
.getStore()
|
.getStore()
|
||||||
.getCameraConfig(selectedCameraID);
|
.getCameraConfig(selectedCameraID);
|
||||||
const allSelectedCameraIDs = cameraManager
|
const allSelectedCameraIDs = cameraManager
|
||||||
.getStore()
|
.getStore()
|
||||||
.getAllDependentCameras(selectedCameraID);
|
.getAllDependentCameras(selectedCameraID);
|
||||||
const selectedMedia = view.queryResults?.getSelectedResult();
|
|
||||||
|
|
||||||
const selectedCameraCapabilities =
|
const substreamAwareCameraCapabilities =
|
||||||
cameraManager.getCameraCapabilities(selectedCameraID);
|
cameraManager.getCameraCapabilities(substreamAwareCameraID);
|
||||||
const aggregateCapabilities =
|
|
||||||
cameraManager.getAggregateCameraCapabilities(allSelectedCameraIDs);
|
const selectedMedia = view.queryResults?.getSelectedResult();
|
||||||
const mediaCapabilities = selectedMedia
|
const mediaCapabilities = selectedMedia
|
||||||
? cameraManager?.getMediaCapabilities(selectedMedia)
|
? cameraManager?.getMediaCapabilities(selectedMedia)
|
||||||
: null;
|
: null;
|
||||||
@@ -125,7 +128,6 @@ export class MenuButtonController {
|
|||||||
|
|
||||||
if (selectedCameraID && allSelectedCameraIDs && view.is('live')) {
|
if (selectedCameraID && allSelectedCameraIDs && view.is('live')) {
|
||||||
const dependencies = [...allSelectedCameraIDs];
|
const dependencies = [...allSelectedCameraIDs];
|
||||||
const override = view.context?.live?.overrides?.get(selectedCameraID);
|
|
||||||
|
|
||||||
if (dependencies.length === 2) {
|
if (dependencies.length === 2) {
|
||||||
// If there are only two dependencies (the main camera, and 1 other)
|
// If there are only two dependencies (the main camera, and 1 other)
|
||||||
@@ -133,7 +135,9 @@ export class MenuButtonController {
|
|||||||
buttons.push({
|
buttons.push({
|
||||||
icon: 'mdi:video-input-component',
|
icon: 'mdi:video-input-component',
|
||||||
style:
|
style:
|
||||||
override && override !== selectedCameraID ? this._getEmphasizedStyle() : {},
|
substreamAwareCameraID !== selectedCameraID
|
||||||
|
? this._getEmphasizedStyle()
|
||||||
|
: {},
|
||||||
title: localize('config.menu.buttons.substreams'),
|
title: localize('config.menu.buttons.substreams'),
|
||||||
...config.menu.buttons.substreams,
|
...config.menu.buttons.substreams,
|
||||||
type: 'custom:frigate-card-menu-icon',
|
type: 'custom:frigate-card-menu-icon',
|
||||||
@@ -166,7 +170,9 @@ export class MenuButtonController {
|
|||||||
icon: 'mdi:video-input-component',
|
icon: 'mdi:video-input-component',
|
||||||
title: localize('config.menu.buttons.substreams'),
|
title: localize('config.menu.buttons.substreams'),
|
||||||
style:
|
style:
|
||||||
override && override !== selectedCameraID ? this._getEmphasizedStyle() : {},
|
substreamAwareCameraID !== selectedCameraID
|
||||||
|
? this._getEmphasizedStyle()
|
||||||
|
: {},
|
||||||
...config.menu.buttons.substreams,
|
...config.menu.buttons.substreams,
|
||||||
type: 'custom:frigate-card-menu-submenu',
|
type: 'custom:frigate-card-menu-submenu',
|
||||||
items: menuItems,
|
items: menuItems,
|
||||||
@@ -183,7 +189,7 @@ export class MenuButtonController {
|
|||||||
tap_action: createFrigateCardSimpleAction('live') as FrigateCardCustomAction,
|
tap_action: createFrigateCardSimpleAction('live') as FrigateCardCustomAction,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (aggregateCapabilities?.supportsClips) {
|
if (options?.viewManager?.isViewSupportedByCamera(selectedCameraID, 'clips')) {
|
||||||
buttons.push({
|
buttons.push({
|
||||||
icon: 'mdi:filmstrip',
|
icon: 'mdi:filmstrip',
|
||||||
...config.menu.buttons.clips,
|
...config.menu.buttons.clips,
|
||||||
@@ -195,7 +201,7 @@ export class MenuButtonController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aggregateCapabilities?.supportsSnapshots) {
|
if (options?.viewManager?.isViewSupportedByCamera(selectedCameraID, 'snapshots')) {
|
||||||
buttons.push({
|
buttons.push({
|
||||||
icon: 'mdi:camera',
|
icon: 'mdi:camera',
|
||||||
...config.menu.buttons.snapshots,
|
...config.menu.buttons.snapshots,
|
||||||
@@ -211,7 +217,7 @@ export class MenuButtonController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aggregateCapabilities?.supportsRecordings) {
|
if (options?.viewManager?.isViewSupportedByCamera(selectedCameraID, 'recordings')) {
|
||||||
buttons.push({
|
buttons.push({
|
||||||
icon: 'mdi:album',
|
icon: 'mdi:album',
|
||||||
...config.menu.buttons.recordings,
|
...config.menu.buttons.recordings,
|
||||||
@@ -236,9 +242,7 @@ export class MenuButtonController {
|
|||||||
tap_action: createFrigateCardSimpleAction('image') as FrigateCardCustomAction,
|
tap_action: createFrigateCardSimpleAction('image') as FrigateCardCustomAction,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Don't show the timeline button unless there's at least one non-birdseye
|
if (options?.viewManager?.isViewSupportedByCamera(selectedCameraID, 'timeline')) {
|
||||||
// camera with a Frigate camera name.
|
|
||||||
if (aggregateCapabilities?.supportsTimeline) {
|
|
||||||
buttons.push({
|
buttons.push({
|
||||||
icon: 'mdi:chart-gantt',
|
icon: 'mdi:chart-gantt',
|
||||||
...config.menu.buttons.timeline,
|
...config.menu.buttons.timeline,
|
||||||
@@ -419,7 +423,7 @@ export class MenuButtonController {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasUsablePTZ(selectedCameraCapabilities, config.live.controls.ptz)) {
|
if (hasUsablePTZ(substreamAwareCameraCapabilities, config.live.controls.ptz)) {
|
||||||
const isOn =
|
const isOn =
|
||||||
view.context?.live?.ptzVisible === false
|
view.context?.live?.ptzVisible === false
|
||||||
? false
|
? false
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||||
import { afterAll, describe, expect, it, vi } from 'vitest';
|
import { afterAll, describe, expect, it, vi } from 'vitest';
|
||||||
import { mock } from 'vitest-mock-extended';
|
import { mock } from 'vitest-mock-extended';
|
||||||
import {
|
import {
|
||||||
@@ -382,6 +383,7 @@ describe('ActionsManager.executeAction', () => {
|
|||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
viewName: 'live',
|
viewName: 'live',
|
||||||
cameraID: 'camera',
|
cameraID: 'camera',
|
||||||
|
failSafe: true,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -409,6 +411,7 @@ describe('ActionsManager.executeAction', () => {
|
|||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
viewName: 'timeline',
|
viewName: 'timeline',
|
||||||
cameraID: 'camera',
|
cameraID: 'camera',
|
||||||
|
failSafe: true,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -442,6 +445,7 @@ describe('ActionsManager.executeAction', () => {
|
|||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
viewName: 'clips',
|
viewName: 'clips',
|
||||||
cameraID: 'camera',
|
cameraID: 'camera',
|
||||||
|
failSafe: true,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -467,6 +471,7 @@ describe('ActionsManager.executeAction', () => {
|
|||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
viewName: 'live',
|
viewName: 'live',
|
||||||
cameraID: 'camera',
|
cameraID: 'camera',
|
||||||
|
failSafe: true,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -503,32 +508,6 @@ describe('ActionsManager.executeAction', () => {
|
|||||||
|
|
||||||
expect(api.getViewManager().setViewByParameters).not.toBeCalled();
|
expect(api.getViewManager().setViewByParameters).not.toBeCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('with an unsupported view', async () => {
|
|
||||||
const api = createCardAPI();
|
|
||||||
const manager = new ActionsManager(api);
|
|
||||||
|
|
||||||
vi.mocked(api.getViewManager().getView).mockReturnValue(
|
|
||||||
createView({
|
|
||||||
view: 'timeline',
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
await manager.executeFrigateAction(
|
|
||||||
createAction({
|
|
||||||
frigate_card_action: 'camera_select',
|
|
||||||
camera: 'camera',
|
|
||||||
})!,
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(api.getViewManager().setViewByParameters).toBeCalledWith(
|
|
||||||
expect.objectContaining({
|
|
||||||
// Should have fallen back to the default view.
|
|
||||||
viewName: 'live',
|
|
||||||
cameraID: 'camera',
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle live_substream_select action', async () => {
|
it('should handle live_substream_select action', async () => {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { EventMediaQueries } from '../../src/view/media-queries';
|
|||||||
import { MediaQueriesResults } from '../../src/view/media-queries-results';
|
import { MediaQueriesResults } from '../../src/view/media-queries-results';
|
||||||
import { View } from '../../src/view/view';
|
import { View } from '../../src/view/view';
|
||||||
import {
|
import {
|
||||||
|
createAggregateCameraCapabilities,
|
||||||
createCameraCapabilities,
|
createCameraCapabilities,
|
||||||
createCameraConfig,
|
createCameraConfig,
|
||||||
createCameraManager,
|
createCameraManager,
|
||||||
@@ -15,7 +16,7 @@ import {
|
|||||||
createHASS,
|
createHASS,
|
||||||
createStore,
|
createStore,
|
||||||
createView,
|
createView,
|
||||||
generateViewMediaArray
|
generateViewMediaArray,
|
||||||
} from '../test-utils';
|
} from '../test-utils';
|
||||||
|
|
||||||
vi.mock('../../src/utils/media-to-view');
|
vi.mock('../../src/utils/media-to-view');
|
||||||
@@ -218,8 +219,8 @@ describe('ViewManager.setViewByParameters', () => {
|
|||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
vi.mocked(api.getCameraManager().getCameraCapabilities).mockReturnValue(
|
vi.mocked(api.getCameraManager().getAggregateCameraCapabilities).mockReturnValue(
|
||||||
createCameraCapabilities({
|
createAggregateCameraCapabilities({
|
||||||
supportsClips: true,
|
supportsClips: true,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -247,8 +248,8 @@ describe('ViewManager.setViewByParameters', () => {
|
|||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
vi.mocked(api.getCameraManager().getCameraCapabilities).mockReturnValue(
|
vi.mocked(api.getCameraManager().getAggregateCameraCapabilities).mockReturnValue(
|
||||||
createCameraCapabilities({
|
createAggregateCameraCapabilities({
|
||||||
supportsClips: true,
|
supportsClips: true,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@@ -336,9 +337,11 @@ describe('ViewManager.setViewByParameters', () => {
|
|||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(createConfig());
|
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(createConfig());
|
||||||
vi.mocked(api.getCameraManager().getCameraCapabilities).mockReturnValue(createCameraCapabilities({
|
vi.mocked(api.getCameraManager().getAggregateCameraCapabilities).mockReturnValue(
|
||||||
|
createAggregateCameraCapabilities({
|
||||||
supportsSnapshots: false,
|
supportsSnapshots: false,
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const manager = new ViewManager(api);
|
const manager = new ViewManager(api);
|
||||||
manager.setViewByParameters({
|
manager.setViewByParameters({
|
||||||
@@ -360,9 +363,11 @@ describe('ViewManager.setViewByParameters', () => {
|
|||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(createConfig());
|
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(createConfig());
|
||||||
vi.mocked(api.getCameraManager().getCameraCapabilities).mockReturnValue(createCameraCapabilities({
|
vi.mocked(api.getCameraManager().getAggregateCameraCapabilities).mockReturnValue(
|
||||||
|
createAggregateCameraCapabilities({
|
||||||
supportsSnapshots: false,
|
supportsSnapshots: false,
|
||||||
}));
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
const manager = new ViewManager(api);
|
const manager = new ViewManager(api);
|
||||||
manager.setViewByParameters({
|
manager.setViewByParameters({
|
||||||
@@ -392,8 +397,8 @@ describe('ViewManager.setViewByParameters', () => {
|
|||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
vi.mocked(api.getCameraManager().getCameraCapabilities).mockReturnValue(
|
vi.mocked(api.getCameraManager().getAggregateCameraCapabilities).mockReturnValue(
|
||||||
createCameraCapabilities({
|
createAggregateCameraCapabilities({
|
||||||
supportsClips: true,
|
supportsClips: true,
|
||||||
supportsRecordings: true,
|
supportsRecordings: true,
|
||||||
supportsSnapshots: true,
|
supportsSnapshots: true,
|
||||||
@@ -443,8 +448,8 @@ describe('ViewManager.setViewByParameters', () => {
|
|||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
vi.mocked(api.getCameraManager().getCameraCapabilities).mockReturnValue(
|
vi.mocked(api.getCameraManager().getAggregateCameraCapabilities).mockReturnValue(
|
||||||
createCameraCapabilities({
|
createAggregateCameraCapabilities({
|
||||||
supportsClips: true,
|
supportsClips: true,
|
||||||
supportsRecordings: true,
|
supportsRecordings: true,
|
||||||
supportsSnapshots: true,
|
supportsSnapshots: true,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { CameraManager } from '../../src/camera-manager/manager';
|
|||||||
import { CameraManagerCameraMetadata } from '../../src/camera-manager/types';
|
import { CameraManagerCameraMetadata } from '../../src/camera-manager/types';
|
||||||
import { MediaPlayerManager } from '../../src/card-controller/media-player-manager';
|
import { MediaPlayerManager } from '../../src/card-controller/media-player-manager';
|
||||||
import { MicrophoneManager } from '../../src/card-controller/microphone-manager';
|
import { MicrophoneManager } from '../../src/card-controller/microphone-manager';
|
||||||
|
import { ViewManager } from '../../src/card-controller/view-manager';
|
||||||
import {
|
import {
|
||||||
MenuButtonController,
|
MenuButtonController,
|
||||||
MenuButtonControllerOptions,
|
MenuButtonControllerOptions,
|
||||||
@@ -17,7 +18,6 @@ import { ViewMedia } from '../../src/view/media';
|
|||||||
import { MediaQueriesResults } from '../../src/view/media-queries-results';
|
import { MediaQueriesResults } from '../../src/view/media-queries-results';
|
||||||
import { View } from '../../src/view/view';
|
import { View } from '../../src/view/view';
|
||||||
import {
|
import {
|
||||||
createAggregateCameraCapabilities,
|
|
||||||
createCameraCapabilities,
|
createCameraCapabilities,
|
||||||
createCameraConfig,
|
createCameraConfig,
|
||||||
createCameraManager,
|
createCameraManager,
|
||||||
@@ -41,6 +41,7 @@ const calculateButtons = (
|
|||||||
config?: FrigateCardConfig;
|
config?: FrigateCardConfig;
|
||||||
cameraManager?: CameraManager;
|
cameraManager?: CameraManager;
|
||||||
view?: View;
|
view?: View;
|
||||||
|
viewManager?: ViewManager;
|
||||||
},
|
},
|
||||||
): MenuItem[] => {
|
): MenuItem[] => {
|
||||||
let cameraManager: CameraManager | null = options?.cameraManager ?? null;
|
let cameraManager: CameraManager | null = options?.cameraManager ?? null;
|
||||||
@@ -71,8 +72,10 @@ describe('MenuButtonController', () => {
|
|||||||
controller = new MenuButtonController();
|
controller = new MenuButtonController();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have frigate menu button with hidden menu style', () => {
|
describe('should have frigate menu button', () => {
|
||||||
|
it('with hidden menu style', () => {
|
||||||
const buttons = calculateButtons(controller);
|
const buttons = calculateButtons(controller);
|
||||||
|
|
||||||
expect(buttons).toContainEqual({
|
expect(buttons).toContainEqual({
|
||||||
icon: 'frigate',
|
icon: 'frigate',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -84,10 +87,11 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have frigate menu button without hidden menu style', () => {
|
it('without hidden menu style', () => {
|
||||||
const buttons = calculateButtons(controller, {
|
const buttons = calculateButtons(controller, {
|
||||||
config: createConfig({ menu: { style: 'overlay' } }),
|
config: createConfig({ menu: { style: 'overlay' } }),
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(buttons).toContainEqual({
|
expect(buttons).toContainEqual({
|
||||||
icon: 'frigate',
|
icon: 'frigate',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -98,8 +102,10 @@ describe('MenuButtonController', () => {
|
|||||||
hold_action: createFrigateCardSimpleAction('diagnostics'),
|
hold_action: createFrigateCardSimpleAction('diagnostics'),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should have cameras menu', () => {
|
describe('should have cameras menu', () => {
|
||||||
|
it('with multiple cameras', () => {
|
||||||
const cameraManager = createCameraManager();
|
const cameraManager = createCameraManager();
|
||||||
vi.mocked(cameraManager.getStore).mockReturnValue(
|
vi.mocked(cameraManager.getStore).mockReturnValue(
|
||||||
createStore([{ cameraID: 'camera-1' }, { cameraID: 'camera-2' }]),
|
createStore([{ cameraID: 'camera-1' }, { cameraID: 'camera-2' }]),
|
||||||
@@ -108,8 +114,8 @@ describe('MenuButtonController', () => {
|
|||||||
title: 'title',
|
title: 'title',
|
||||||
icon: 'icon',
|
icon: 'icon',
|
||||||
});
|
});
|
||||||
|
|
||||||
const buttons = calculateButtons(controller, { cameraManager: cameraManager });
|
const buttons = calculateButtons(controller, { cameraManager: cameraManager });
|
||||||
|
|
||||||
expect(buttons).toContainEqual({
|
expect(buttons).toContainEqual({
|
||||||
icon: 'mdi:video-switch',
|
icon: 'mdi:video-switch',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -147,15 +153,15 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not have a cameras menu without a visible camera', () => {
|
it('without a visible camera', () => {
|
||||||
const cameraManager = createCameraManager();
|
const cameraManager = createCameraManager();
|
||||||
vi.mocked(cameraManager.getStore).mockReturnValue(
|
vi.mocked(cameraManager.getStore).mockReturnValue(
|
||||||
createStore([
|
createStore([
|
||||||
{ cameraID: 'camera-1', config: createCameraConfig({ hide: true }) },
|
{ cameraID: 'camera-1', config: createCameraConfig({ hide: true }) },
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const buttons = calculateButtons(controller, { cameraManager: cameraManager });
|
const buttons = calculateButtons(controller, { cameraManager: cameraManager });
|
||||||
|
|
||||||
expect(buttons).not.toEqual(
|
expect(buttons).not.toEqual(
|
||||||
expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
@@ -164,8 +170,10 @@ describe('MenuButtonController', () => {
|
|||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should have substream button with single dependency', () => {
|
describe('should have substream button', () => {
|
||||||
|
it('with single dependency', () => {
|
||||||
const cameraManager = createCameraManager();
|
const cameraManager = createCameraManager();
|
||||||
vi.mocked(cameraManager.getStore).mockReturnValue(
|
vi.mocked(cameraManager.getStore).mockReturnValue(
|
||||||
createStore([
|
createStore([
|
||||||
@@ -176,8 +184,8 @@ describe('MenuButtonController', () => {
|
|||||||
{ cameraID: 'camera-2' },
|
{ cameraID: 'camera-2' },
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const buttons = calculateButtons(controller, { cameraManager: cameraManager });
|
const buttons = calculateButtons(controller, { cameraManager: cameraManager });
|
||||||
|
|
||||||
expect(buttons).toContainEqual({
|
expect(buttons).toContainEqual({
|
||||||
icon: 'mdi:video-input-component',
|
icon: 'mdi:video-input-component',
|
||||||
style: {},
|
style: {},
|
||||||
@@ -192,7 +200,7 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have substream button selected with single dependency', () => {
|
it('with substream selected and single dependency', () => {
|
||||||
const cameraManager = createCameraManager();
|
const cameraManager = createCameraManager();
|
||||||
vi.mocked(cameraManager.getStore).mockReturnValue(
|
vi.mocked(cameraManager.getStore).mockReturnValue(
|
||||||
createStore([
|
createStore([
|
||||||
@@ -203,7 +211,6 @@ describe('MenuButtonController', () => {
|
|||||||
{ cameraID: 'camera-2' },
|
{ cameraID: 'camera-2' },
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const view = createView({
|
const view = createView({
|
||||||
camera: 'camera-1',
|
camera: 'camera-1',
|
||||||
context: {
|
context: {
|
||||||
@@ -212,11 +219,11 @@ describe('MenuButtonController', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const buttons = calculateButtons(controller, {
|
const buttons = calculateButtons(controller, {
|
||||||
cameraManager: cameraManager,
|
cameraManager: cameraManager,
|
||||||
view: view,
|
view: view,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(buttons).toContainEqual({
|
expect(buttons).toContainEqual({
|
||||||
icon: 'mdi:video-input-component',
|
icon: 'mdi:video-input-component',
|
||||||
style: { color: 'var(--primary-color, white)' },
|
style: { color: 'var(--primary-color, white)' },
|
||||||
@@ -231,7 +238,7 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have substream menu without substream on with multiple dependencies', () => {
|
it('with substream selected and multiple dependencies', () => {
|
||||||
const cameraManager = createCameraManager();
|
const cameraManager = createCameraManager();
|
||||||
vi.mocked(cameraManager.getStore).mockReturnValue(
|
vi.mocked(cameraManager.getStore).mockReturnValue(
|
||||||
createStore([
|
createStore([
|
||||||
@@ -271,6 +278,7 @@ describe('MenuButtonController', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const buttons = calculateButtons(controller, { cameraManager: cameraManager });
|
const buttons = calculateButtons(controller, { cameraManager: cameraManager });
|
||||||
|
|
||||||
expect(buttons).toContainEqual({
|
expect(buttons).toContainEqual({
|
||||||
icon: 'mdi:video-input-component',
|
icon: 'mdi:video-input-component',
|
||||||
title: 'Substream(s)',
|
title: 'Substream(s)',
|
||||||
@@ -322,7 +330,7 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have substream menu with substream on with multiple dependencies', () => {
|
it('with substream selected and with multiple dependencies', () => {
|
||||||
const cameraManager = createCameraManager();
|
const cameraManager = createCameraManager();
|
||||||
vi.mocked(cameraManager.getStore).mockReturnValue(
|
vi.mocked(cameraManager.getStore).mockReturnValue(
|
||||||
createStore([
|
createStore([
|
||||||
@@ -347,7 +355,6 @@ describe('MenuButtonController', () => {
|
|||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
const view = createView({
|
const view = createView({
|
||||||
camera: 'camera-1',
|
camera: 'camera-1',
|
||||||
context: {
|
context: {
|
||||||
@@ -356,7 +363,6 @@ describe('MenuButtonController', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const buttons = calculateButtons(controller, {
|
const buttons = calculateButtons(controller, {
|
||||||
cameraManager: cameraManager,
|
cameraManager: cameraManager,
|
||||||
view: view,
|
view: view,
|
||||||
@@ -414,9 +420,12 @@ describe('MenuButtonController', () => {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should have styled live menu button in live view', () => {
|
describe('should have live menu button', () => {
|
||||||
|
it('when in live view', () => {
|
||||||
const buttons = calculateButtons(controller);
|
const buttons = calculateButtons(controller);
|
||||||
|
|
||||||
expect(buttons).toContainEqual({
|
expect(buttons).toContainEqual({
|
||||||
icon: 'mdi:cctv',
|
icon: 'mdi:cctv',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -428,9 +437,10 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have unstyled live menu button in non-live views', () => {
|
it('when not in live view', () => {
|
||||||
const view = createView({ view: 'clips' });
|
const view = createView({ view: 'clips' });
|
||||||
const buttons = calculateButtons(controller, { view: view });
|
const buttons = calculateButtons(controller, { view: view });
|
||||||
|
|
||||||
expect(buttons).toContainEqual({
|
expect(buttons).toContainEqual({
|
||||||
icon: 'mdi:cctv',
|
icon: 'mdi:cctv',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -441,16 +451,17 @@ describe('MenuButtonController', () => {
|
|||||||
tap_action: { action: 'fire-dom-event', frigate_card_action: 'live' },
|
tap_action: { action: 'fire-dom-event', frigate_card_action: 'live' },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should have styled clips menu button in clips view', () => {
|
describe('should have clips menu button', () => {
|
||||||
const cameraManager = createCameraManager();
|
it('when in clips view', () => {
|
||||||
vi.mocked(cameraManager.getAggregateCameraCapabilities).mockReturnValue(
|
const viewManager = mock<ViewManager>();
|
||||||
createAggregateCameraCapabilities({ supportsClips: true }),
|
vi.mocked(viewManager.isViewSupportedByCamera).mockReturnValue(true);
|
||||||
);
|
|
||||||
const buttons = calculateButtons(controller, {
|
const buttons = calculateButtons(controller, {
|
||||||
cameraManager: cameraManager,
|
|
||||||
view: createView({ view: 'clips' }),
|
view: createView({ view: 'clips' }),
|
||||||
|
viewManager: viewManager,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(buttons).toContainEqual({
|
expect(buttons).toContainEqual({
|
||||||
icon: 'mdi:filmstrip',
|
icon: 'mdi:filmstrip',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -463,12 +474,13 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have unstyled clips menu button in non-clips view', () => {
|
it('when not in clips view', () => {
|
||||||
const cameraManager = createCameraManager();
|
const viewManager = mock<ViewManager>();
|
||||||
vi.mocked(cameraManager.getAggregateCameraCapabilities).mockReturnValue(
|
vi.mocked(viewManager.isViewSupportedByCamera).mockReturnValue(true);
|
||||||
createAggregateCameraCapabilities({ supportsClips: true }),
|
const buttons = calculateButtons(controller, {
|
||||||
);
|
viewManager: viewManager,
|
||||||
const buttons = calculateButtons(controller, { cameraManager: cameraManager });
|
});
|
||||||
|
|
||||||
expect(buttons).toContainEqual({
|
expect(buttons).toContainEqual({
|
||||||
icon: 'mdi:filmstrip',
|
icon: 'mdi:filmstrip',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -481,15 +493,28 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have styled snapshots menu button in snapshots view', () => {
|
it('when not supported', () => {
|
||||||
const cameraManager = createCameraManager();
|
const viewManager = mock<ViewManager>();
|
||||||
vi.mocked(cameraManager.getAggregateCameraCapabilities).mockReturnValue(
|
vi.mocked(viewManager.isViewSupportedByCamera).mockReturnValue(false);
|
||||||
createAggregateCameraCapabilities({ supportsSnapshots: true }),
|
const buttons = calculateButtons(controller, {
|
||||||
|
viewManager: viewManager,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(buttons).not.toEqual(
|
||||||
|
expect.arrayContaining([expect.objectContaining({ title: 'Clips gallery' })]),
|
||||||
);
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('should have snapshots menu button', () => {
|
||||||
|
it('when in snapshots view', () => {
|
||||||
|
const viewManager = mock<ViewManager>();
|
||||||
|
vi.mocked(viewManager.isViewSupportedByCamera).mockReturnValue(true);
|
||||||
const buttons = calculateButtons(controller, {
|
const buttons = calculateButtons(controller, {
|
||||||
cameraManager: cameraManager,
|
|
||||||
view: createView({ view: 'snapshots' }),
|
view: createView({ view: 'snapshots' }),
|
||||||
|
viewManager: viewManager,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(buttons).toContainEqual({
|
expect(buttons).toContainEqual({
|
||||||
icon: 'mdi:camera',
|
icon: 'mdi:camera',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -502,33 +527,49 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have unstyled snapshots menu button in non-snapshots view', () => {
|
it('when not in snapshots view', () => {
|
||||||
const cameraManager = createCameraManager();
|
const viewManager = mock<ViewManager>();
|
||||||
vi.mocked(cameraManager.getAggregateCameraCapabilities).mockReturnValue(
|
vi.mocked(viewManager.isViewSupportedByCamera).mockReturnValue(true);
|
||||||
createAggregateCameraCapabilities({ supportsSnapshots: true }),
|
|
||||||
);
|
|
||||||
const buttons = calculateButtons(controller, { cameraManager: cameraManager });
|
|
||||||
expect(buttons).toContainEqual({
|
|
||||||
icon: 'mdi:camera',
|
|
||||||
enabled: true,
|
|
||||||
priority: 50,
|
|
||||||
type: 'custom:frigate-card-menu-icon',
|
|
||||||
title: 'Snapshots gallery',
|
|
||||||
style: {},
|
|
||||||
tap_action: { action: 'fire-dom-event', frigate_card_action: 'snapshots' },
|
|
||||||
hold_action: { action: 'fire-dom-event', frigate_card_action: 'snapshot' },
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should have styled recordings menu button in recordings view', () => {
|
|
||||||
const cameraManager = createCameraManager();
|
|
||||||
vi.mocked(cameraManager.getAggregateCameraCapabilities).mockReturnValue(
|
|
||||||
createAggregateCameraCapabilities({ supportsRecordings: true }),
|
|
||||||
);
|
|
||||||
const buttons = calculateButtons(controller, {
|
const buttons = calculateButtons(controller, {
|
||||||
cameraManager: cameraManager,
|
viewManager: viewManager,
|
||||||
view: createView({ view: 'recordings' }),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
expect(buttons).toContainEqual({
|
||||||
|
icon: 'mdi:camera',
|
||||||
|
enabled: true,
|
||||||
|
priority: 50,
|
||||||
|
type: 'custom:frigate-card-menu-icon',
|
||||||
|
title: 'Snapshots gallery',
|
||||||
|
style: {},
|
||||||
|
tap_action: { action: 'fire-dom-event', frigate_card_action: 'snapshots' },
|
||||||
|
hold_action: { action: 'fire-dom-event', frigate_card_action: 'snapshot' },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('when not supported', () => {
|
||||||
|
const viewManager = mock<ViewManager>();
|
||||||
|
vi.mocked(viewManager.isViewSupportedByCamera).mockReturnValue(false);
|
||||||
|
const buttons = calculateButtons(controller, {
|
||||||
|
viewManager: viewManager,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(buttons).not.toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ title: 'Snapshots gallery' }),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('should have recordings menu button', () => {
|
||||||
|
it('when in recordings view', () => {
|
||||||
|
const viewManager = mock<ViewManager>();
|
||||||
|
vi.mocked(viewManager.isViewSupportedByCamera).mockReturnValue(true);
|
||||||
|
const buttons = calculateButtons(controller, {
|
||||||
|
view: createView({ view: 'recordings' }),
|
||||||
|
viewManager: viewManager,
|
||||||
|
});
|
||||||
|
|
||||||
expect(buttons).toContainEqual({
|
expect(buttons).toContainEqual({
|
||||||
icon: 'mdi:album',
|
icon: 'mdi:album',
|
||||||
enabled: false,
|
enabled: false,
|
||||||
@@ -541,12 +582,13 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have unstyled recordings menu button in non-recordings view', () => {
|
it('when not in recordings view', () => {
|
||||||
const cameraManager = createCameraManager();
|
const viewManager = mock<ViewManager>();
|
||||||
vi.mocked(cameraManager.getAggregateCameraCapabilities).mockReturnValue(
|
vi.mocked(viewManager.isViewSupportedByCamera).mockReturnValue(true);
|
||||||
createAggregateCameraCapabilities({ supportsRecordings: true }),
|
const buttons = calculateButtons(controller, {
|
||||||
);
|
viewManager: viewManager,
|
||||||
const buttons = calculateButtons(controller, { cameraManager: cameraManager });
|
});
|
||||||
|
|
||||||
expect(buttons).toContainEqual({
|
expect(buttons).toContainEqual({
|
||||||
icon: 'mdi:album',
|
icon: 'mdi:album',
|
||||||
enabled: false,
|
enabled: false,
|
||||||
@@ -559,10 +601,27 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have styled image menu button in image view', () => {
|
it('when not supported', () => {
|
||||||
|
const viewManager = mock<ViewManager>();
|
||||||
|
vi.mocked(viewManager.isViewSupportedByCamera).mockReturnValue(false);
|
||||||
|
const buttons = calculateButtons(controller, {
|
||||||
|
viewManager: viewManager,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(buttons).not.toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ title: 'Recordings gallery' }),
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('should have image menu button', () => {
|
||||||
|
it('when in image view', () => {
|
||||||
const buttons = calculateButtons(controller, {
|
const buttons = calculateButtons(controller, {
|
||||||
view: createView({ view: 'image' }),
|
view: createView({ view: 'image' }),
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(buttons).toContainEqual({
|
expect(buttons).toContainEqual({
|
||||||
icon: 'mdi:image',
|
icon: 'mdi:image',
|
||||||
enabled: false,
|
enabled: false,
|
||||||
@@ -574,8 +633,9 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have unstyled image menu button in non-image view', () => {
|
it('when not in image view', () => {
|
||||||
const buttons = calculateButtons(controller);
|
const buttons = calculateButtons(controller);
|
||||||
|
|
||||||
expect(buttons).toContainEqual({
|
expect(buttons).toContainEqual({
|
||||||
icon: 'mdi:image',
|
icon: 'mdi:image',
|
||||||
enabled: false,
|
enabled: false,
|
||||||
@@ -586,16 +646,17 @@ describe('MenuButtonController', () => {
|
|||||||
tap_action: { action: 'fire-dom-event', frigate_card_action: 'image' },
|
tap_action: { action: 'fire-dom-event', frigate_card_action: 'image' },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have styled timeline menu button in timeline view', () => {
|
|
||||||
const cameraManager = createCameraManager();
|
|
||||||
vi.mocked(cameraManager.getAggregateCameraCapabilities).mockReturnValue(
|
|
||||||
createAggregateCameraCapabilities({ supportsTimeline: true }),
|
|
||||||
);
|
|
||||||
const buttons = calculateButtons(controller, {
|
|
||||||
cameraManager: cameraManager,
|
|
||||||
view: createView({ view: 'timeline' }),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('should have timeline button', () => {
|
||||||
|
it('when in timeline view', () => {
|
||||||
|
const viewManager = mock<ViewManager>();
|
||||||
|
vi.mocked(viewManager.isViewSupportedByCamera).mockReturnValue(true);
|
||||||
|
const buttons = calculateButtons(controller, {
|
||||||
|
view: createView({ view: 'timeline' }),
|
||||||
|
viewManager: viewManager,
|
||||||
|
});
|
||||||
|
|
||||||
expect(buttons).toContainEqual({
|
expect(buttons).toContainEqual({
|
||||||
icon: 'mdi:chart-gantt',
|
icon: 'mdi:chart-gantt',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -607,14 +668,14 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have unstyled timeline menu button in non-timeline view', () => {
|
it('when not in timeline view', () => {
|
||||||
const cameraManager = createCameraManager();
|
const viewManager = mock<ViewManager>();
|
||||||
vi.mocked(cameraManager.getAggregateCameraCapabilities).mockReturnValue(
|
vi.mocked(viewManager.isViewSupportedByCamera).mockReturnValue(true);
|
||||||
createAggregateCameraCapabilities({ supportsTimeline: true }),
|
|
||||||
);
|
|
||||||
const buttons = calculateButtons(controller, {
|
const buttons = calculateButtons(controller, {
|
||||||
cameraManager: cameraManager,
|
view: createView({ view: 'live' }),
|
||||||
|
viewManager: viewManager,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(buttons).toContainEqual({
|
expect(buttons).toContainEqual({
|
||||||
icon: 'mdi:chart-gantt',
|
icon: 'mdi:chart-gantt',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -626,7 +687,22 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have download menu button', () => {
|
it('when not supported', () => {
|
||||||
|
const viewManager = mock<ViewManager>();
|
||||||
|
vi.mocked(viewManager.isViewSupportedByCamera).mockReturnValue(false);
|
||||||
|
const buttons = calculateButtons(controller, {
|
||||||
|
view: createView({ view: 'live' }),
|
||||||
|
viewManager: viewManager,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(buttons).not.toEqual(
|
||||||
|
expect.arrayContaining([expect.objectContaining({ title: 'Timeline view' })]),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('should have download button', () => {
|
||||||
|
it('when media available', () => {
|
||||||
vi.stubGlobal('navigator', { userAgent: 'foo' });
|
vi.stubGlobal('navigator', { userAgent: 'foo' });
|
||||||
|
|
||||||
const cameraManager = createCameraManager();
|
const cameraManager = createCameraManager();
|
||||||
@@ -643,6 +719,7 @@ describe('MenuButtonController', () => {
|
|||||||
cameraManager: cameraManager,
|
cameraManager: cameraManager,
|
||||||
view: view,
|
view: view,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(buttons).toContainEqual({
|
expect(buttons).toContainEqual({
|
||||||
icon: 'mdi:download',
|
icon: 'mdi:download',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -653,7 +730,7 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not have download menu button when being casted', () => {
|
it('not when being casted', () => {
|
||||||
vi.stubGlobal('navigator', {
|
vi.stubGlobal('navigator', {
|
||||||
userAgent:
|
userAgent:
|
||||||
'Mozilla/5.0 (Fuchsia) AppleWebKit/537.36 (KHTML, like Gecko) ' +
|
'Mozilla/5.0 (Fuchsia) AppleWebKit/537.36 (KHTML, like Gecko) ' +
|
||||||
@@ -674,15 +751,18 @@ describe('MenuButtonController', () => {
|
|||||||
cameraManager: cameraManager,
|
cameraManager: cameraManager,
|
||||||
view: view,
|
view: view,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(buttons).not.toEqual(
|
expect(buttons).not.toEqual(
|
||||||
expect.arrayContaining([expect.objectContaining({ title: 'Download' })]),
|
expect.arrayContaining([expect.objectContaining({ title: 'Download' })]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should have camera UI button', () => {
|
it('should have camera UI button', () => {
|
||||||
const buttons = calculateButtons(controller, {
|
const buttons = calculateButtons(controller, {
|
||||||
showCameraUIButton: true,
|
showCameraUIButton: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(buttons).toContainEqual({
|
expect(buttons).toContainEqual({
|
||||||
icon: 'mdi:web',
|
icon: 'mdi:web',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -693,7 +773,8 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have microphone button', () => {
|
describe('should have microphone button', () => {
|
||||||
|
it('with suitable loaded media', () => {
|
||||||
const microphoneManager = new MicrophoneManager(createCardAPI());
|
const microphoneManager = new MicrophoneManager(createCardAPI());
|
||||||
const buttons = calculateButtons(controller, {
|
const buttons = calculateButtons(controller, {
|
||||||
microphoneManager: microphoneManager,
|
microphoneManager: microphoneManager,
|
||||||
@@ -725,7 +806,7 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not have microphone button when media does not support it', () => {
|
it('without suitable loaded media', () => {
|
||||||
const microphoneManager = new MicrophoneManager(createCardAPI());
|
const microphoneManager = new MicrophoneManager(createCardAPI());
|
||||||
const buttons = calculateButtons(controller, {
|
const buttons = calculateButtons(controller, {
|
||||||
microphoneManager: microphoneManager,
|
microphoneManager: microphoneManager,
|
||||||
@@ -741,7 +822,7 @@ describe('MenuButtonController', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have microphone button when microphone forbidden', () => {
|
it('with forbidden microphone', () => {
|
||||||
const microphoneManager = new MicrophoneManager(createCardAPI());
|
const microphoneManager = new MicrophoneManager(createCardAPI());
|
||||||
mock<MicrophoneManager>(microphoneManager).isForbidden.mockReturnValue(true);
|
mock<MicrophoneManager>(microphoneManager).isForbidden.mockReturnValue(true);
|
||||||
const buttons = calculateButtons(controller, {
|
const buttons = calculateButtons(controller, {
|
||||||
@@ -763,7 +844,7 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have microphone button when microphone muted', () => {
|
it('with muted microphone', () => {
|
||||||
const microphoneManager = new MicrophoneManager(createCardAPI());
|
const microphoneManager = new MicrophoneManager(createCardAPI());
|
||||||
mock<MicrophoneManager>(microphoneManager).isMuted.mockReturnValue(true);
|
mock<MicrophoneManager>(microphoneManager).isMuted.mockReturnValue(true);
|
||||||
const buttons = calculateButtons(controller, {
|
const buttons = calculateButtons(controller, {
|
||||||
@@ -793,7 +874,7 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have microphone button when microphone muted with toggle type', () => {
|
it('with muted toggle type microphone', () => {
|
||||||
const microphoneManager = new MicrophoneManager(createCardAPI());
|
const microphoneManager = new MicrophoneManager(createCardAPI());
|
||||||
mock<MicrophoneManager>(microphoneManager).isMuted.mockReturnValue(true);
|
mock<MicrophoneManager>(microphoneManager).isMuted.mockReturnValue(true);
|
||||||
const buttons = calculateButtons(controller, {
|
const buttons = calculateButtons(controller, {
|
||||||
@@ -822,7 +903,7 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have microphone button when microphone unmuted with toggle type', () => {
|
it('with unmuted toggle type microphone', () => {
|
||||||
const microphoneManager = new MicrophoneManager(createCardAPI());
|
const microphoneManager = new MicrophoneManager(createCardAPI());
|
||||||
mock<MicrophoneManager>(microphoneManager).isMuted.mockReturnValue(false);
|
mock<MicrophoneManager>(microphoneManager).isMuted.mockReturnValue(false);
|
||||||
const buttons = calculateButtons(controller, {
|
const buttons = calculateButtons(controller, {
|
||||||
@@ -853,8 +934,10 @@ describe('MenuButtonController', () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should have fullscreen button', () => {
|
describe('should have fullscreen button', () => {
|
||||||
|
it('when not in fullscreen mode', () => {
|
||||||
// Need to write a readonly property.
|
// Need to write a readonly property.
|
||||||
vi.stubGlobal('navigator', { userAgent: 'foo' });
|
vi.stubGlobal('navigator', { userAgent: 'foo' });
|
||||||
const buttons = calculateButtons(controller, { inFullscreenMode: false });
|
const buttons = calculateButtons(controller, { inFullscreenMode: false });
|
||||||
@@ -870,7 +953,7 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have unfullscreen', () => {
|
it('when in fullscreen mode', () => {
|
||||||
vi.stubGlobal('navigator', { userAgent: 'foo' });
|
vi.stubGlobal('navigator', { userAgent: 'foo' });
|
||||||
const buttons = calculateButtons(controller, { inFullscreenMode: true });
|
const buttons = calculateButtons(controller, { inFullscreenMode: true });
|
||||||
|
|
||||||
@@ -884,8 +967,10 @@ describe('MenuButtonController', () => {
|
|||||||
style: { color: 'var(--primary-color, white)' },
|
style: { color: 'var(--primary-color, white)' },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should have expand button', () => {
|
describe('should have expand button', () => {
|
||||||
|
it('when not expanded', () => {
|
||||||
const buttons = calculateButtons(controller, { inExpandedMode: false });
|
const buttons = calculateButtons(controller, { inExpandedMode: false });
|
||||||
|
|
||||||
expect(buttons).toContainEqual({
|
expect(buttons).toContainEqual({
|
||||||
@@ -899,7 +984,7 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have unexpand button', () => {
|
it('when expanded', () => {
|
||||||
const buttons = calculateButtons(controller, { inExpandedMode: true });
|
const buttons = calculateButtons(controller, { inExpandedMode: true });
|
||||||
|
|
||||||
expect(buttons).toContainEqual({
|
expect(buttons).toContainEqual({
|
||||||
@@ -912,8 +997,10 @@ describe('MenuButtonController', () => {
|
|||||||
style: { color: 'var(--primary-color, white)' },
|
style: { color: 'var(--primary-color, white)' },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should have media players button', () => {
|
describe('should have media players button', () => {
|
||||||
|
it('with media players', () => {
|
||||||
const cameraManager = createCameraManager();
|
const cameraManager = createCameraManager();
|
||||||
vi.mocked(cameraManager.getStore).mockReturnValue(
|
vi.mocked(cameraManager.getStore).mockReturnValue(
|
||||||
createStore([
|
createStore([
|
||||||
@@ -970,7 +1057,7 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should disable media players button when entity not found', () => {
|
it('when entity not found', () => {
|
||||||
const cameraManager = createCameraManager();
|
const cameraManager = createCameraManager();
|
||||||
vi.mocked(cameraManager.getStore).mockReturnValue(
|
vi.mocked(cameraManager.getStore).mockReturnValue(
|
||||||
createStore([
|
createStore([
|
||||||
@@ -1011,6 +1098,7 @@ describe('MenuButtonController', () => {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should have pause button', () => {
|
it('should have pause button', () => {
|
||||||
const player = mock<FrigateCardMediaPlayer>();
|
const player = mock<FrigateCardMediaPlayer>();
|
||||||
@@ -1124,6 +1212,7 @@ describe('MenuButtonController', () => {
|
|||||||
vi.mocked(cameraManager.getStore).mockReturnValue(
|
vi.mocked(cameraManager.getStore).mockReturnValue(
|
||||||
createStore([{ cameraID: 'camera-1' }, { cameraID: 'camera-2' }]),
|
createStore([{ cameraID: 'camera-1' }, { cameraID: 'camera-2' }]),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
calculateButtons(controller, { cameraManager: cameraManager, view: view }),
|
calculateButtons(controller, { cameraManager: cameraManager, view: view }),
|
||||||
).toContainEqual({
|
).toContainEqual({
|
||||||
@@ -1154,6 +1243,7 @@ describe('MenuButtonController', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const buttons = calculateButtons(controller, { cameraManager: cameraManager });
|
const buttons = calculateButtons(controller, { cameraManager: cameraManager });
|
||||||
|
|
||||||
expect(buttons).not.toContainEqual({
|
expect(buttons).not.toContainEqual({
|
||||||
enabled: false,
|
enabled: false,
|
||||||
icon: 'mdi:pan',
|
icon: 'mdi:pan',
|
||||||
@@ -1176,8 +1266,8 @@ describe('MenuButtonController', () => {
|
|||||||
vi.mocked(cameraManager.getCameraCapabilities).mockReturnValue(
|
vi.mocked(cameraManager.getCameraCapabilities).mockReturnValue(
|
||||||
createCameraCapabilities({ ptz: {} }),
|
createCameraCapabilities({ ptz: {} }),
|
||||||
);
|
);
|
||||||
|
|
||||||
const buttons = calculateButtons(controller, { cameraManager: cameraManager });
|
const buttons = calculateButtons(controller, { cameraManager: cameraManager });
|
||||||
|
|
||||||
expect(buttons).toContainEqual({
|
expect(buttons).toContainEqual({
|
||||||
enabled: false,
|
enabled: false,
|
||||||
icon: 'mdi:pan',
|
icon: 'mdi:pan',
|
||||||
@@ -1204,11 +1294,11 @@ describe('MenuButtonController', () => {
|
|||||||
camera: 'camera-1',
|
camera: 'camera-1',
|
||||||
context: { live: { ptzVisible: false } },
|
context: { live: { ptzVisible: false } },
|
||||||
});
|
});
|
||||||
|
|
||||||
const buttons = calculateButtons(controller, {
|
const buttons = calculateButtons(controller, {
|
||||||
cameraManager: cameraManager,
|
cameraManager: cameraManager,
|
||||||
view: view,
|
view: view,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(buttons).toContainEqual({
|
expect(buttons).toContainEqual({
|
||||||
enabled: false,
|
enabled: false,
|
||||||
icon: 'mdi:pan',
|
icon: 'mdi:pan',
|
||||||
@@ -1223,14 +1313,65 @@ describe('MenuButtonController', () => {
|
|||||||
type: 'custom:frigate-card-menu-icon',
|
type: 'custom:frigate-card-menu-icon',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('when a substream is PTZ enabled', () => {
|
||||||
|
const cameraManager = createCameraManager();
|
||||||
|
vi.mocked(cameraManager.getStore).mockReturnValue(
|
||||||
|
createStore([
|
||||||
|
{
|
||||||
|
cameraID: 'camera-1',
|
||||||
|
config: createCameraConfig({ dependencies: { cameras: ['camera-2'] } }),
|
||||||
|
},
|
||||||
|
{ cameraID: 'camera-2' },
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
vi.mocked(cameraManager.getCameraCapabilities).mockImplementation(
|
||||||
|
(cameraID: string) => {
|
||||||
|
if (cameraID === 'camera-2') {
|
||||||
|
return createCameraCapabilities({ ptz: {} });
|
||||||
|
}
|
||||||
|
return createCameraCapabilities();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const view = createView({
|
||||||
|
camera: 'camera-1',
|
||||||
|
context: {
|
||||||
|
live: {
|
||||||
|
overrides: new Map([['camera-1', 'camera-2']]),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const buttons = calculateButtons(controller, {
|
||||||
|
cameraManager: cameraManager,
|
||||||
|
view: view,
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle dynamic buttons', () => {
|
expect(buttons).toContainEqual({
|
||||||
|
enabled: false,
|
||||||
|
icon: 'mdi:pan',
|
||||||
|
priority: 50,
|
||||||
|
style: {
|
||||||
|
color: 'var(--primary-color, white)',
|
||||||
|
},
|
||||||
|
tap_action: {
|
||||||
|
action: 'fire-dom-event',
|
||||||
|
frigate_card_action: 'show_ptz',
|
||||||
|
show_ptz: false,
|
||||||
|
},
|
||||||
|
title: 'Show PTZ controls',
|
||||||
|
type: 'custom:frigate-card-menu-icon',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('should handle dynamic buttons', () => {
|
||||||
|
it('successfully', () => {
|
||||||
const button: MenuItem = {
|
const button: MenuItem = {
|
||||||
...dynamicButton,
|
...dynamicButton,
|
||||||
style: {},
|
style: {},
|
||||||
};
|
};
|
||||||
controller.addDynamicMenuButton(button);
|
controller.addDynamicMenuButton(button);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
calculateButtons(controller).filter((menuButton) => isEqual(button, menuButton))
|
calculateButtons(controller).filter((menuButton) => isEqual(button, menuButton))
|
||||||
.length,
|
.length,
|
||||||
@@ -1247,7 +1388,7 @@ describe('MenuButtonController', () => {
|
|||||||
expect(calculateButtons(controller)).not.toContainEqual(button);
|
expect(calculateButtons(controller)).not.toContainEqual(button);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not set style for dynamic button with stock action', () => {
|
it('with stock HA action', () => {
|
||||||
const button: MenuItem = {
|
const button: MenuItem = {
|
||||||
...dynamicButton,
|
...dynamicButton,
|
||||||
tap_action: { action: 'navigate', navigation_path: 'foo' },
|
tap_action: { action: 'navigate', navigation_path: 'foo' },
|
||||||
@@ -1260,61 +1401,61 @@ describe('MenuButtonController', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not set style for dynamic button with non-Frigate fire-dom-event action', () => {
|
it('with non-Frigate fire-dom-event action', () => {
|
||||||
const button: MenuItem = {
|
const button: MenuItem = {
|
||||||
...dynamicButton,
|
...dynamicButton,
|
||||||
tap_action: { action: 'fire-dom-event' },
|
tap_action: { action: 'fire-dom-event' },
|
||||||
};
|
};
|
||||||
controller.addDynamicMenuButton(button);
|
controller.addDynamicMenuButton(button);
|
||||||
|
|
||||||
controller.addDynamicMenuButton(dynamicButton);
|
controller.addDynamicMenuButton(dynamicButton);
|
||||||
|
|
||||||
expect(calculateButtons(controller)).toContainEqual({
|
expect(calculateButtons(controller)).toContainEqual({
|
||||||
...button,
|
...button,
|
||||||
style: {},
|
style: {},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set style for dynamic button with Frigate view action', () => {
|
it('with Frigate view action', () => {
|
||||||
const button: MenuItem = {
|
const button: MenuItem = {
|
||||||
...dynamicButton,
|
...dynamicButton,
|
||||||
tap_action: { action: 'fire-dom-event', frigate_card_action: 'clips' },
|
tap_action: { action: 'fire-dom-event', frigate_card_action: 'clips' },
|
||||||
};
|
};
|
||||||
|
|
||||||
const view = createView({ view: 'clips' });
|
const view = createView({ view: 'clips' });
|
||||||
controller.addDynamicMenuButton(button);
|
controller.addDynamicMenuButton(button);
|
||||||
|
|
||||||
expect(calculateButtons(controller, { view: view })).toContainEqual({
|
expect(calculateButtons(controller, { view: view })).toContainEqual({
|
||||||
...button,
|
...button,
|
||||||
style: { color: 'var(--primary-color, white)' },
|
style: { color: 'var(--primary-color, white)' },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set style for dynamic button with Frigate default action', () => {
|
it('with Frigate default action', () => {
|
||||||
const button: MenuItem = {
|
const button: MenuItem = {
|
||||||
...dynamicButton,
|
...dynamicButton,
|
||||||
tap_action: { action: 'fire-dom-event', frigate_card_action: 'default' },
|
tap_action: { action: 'fire-dom-event', frigate_card_action: 'default' },
|
||||||
};
|
};
|
||||||
|
|
||||||
controller.addDynamicMenuButton(button);
|
controller.addDynamicMenuButton(button);
|
||||||
|
|
||||||
expect(calculateButtons(controller)).toContainEqual({
|
expect(calculateButtons(controller)).toContainEqual({
|
||||||
...button,
|
...button,
|
||||||
style: { color: 'var(--primary-color, white)' },
|
style: { color: 'var(--primary-color, white)' },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set style for dynamic button with fullscreen action', () => {
|
it('with fullscreen action', () => {
|
||||||
const button: MenuItem = {
|
const button: MenuItem = {
|
||||||
...dynamicButton,
|
...dynamicButton,
|
||||||
tap_action: { action: 'fire-dom-event', frigate_card_action: 'fullscreen' },
|
tap_action: { action: 'fire-dom-event', frigate_card_action: 'fullscreen' },
|
||||||
};
|
};
|
||||||
|
|
||||||
controller.addDynamicMenuButton(button);
|
controller.addDynamicMenuButton(button);
|
||||||
|
|
||||||
expect(calculateButtons(controller, { inFullscreenMode: true })).toContainEqual({
|
expect(calculateButtons(controller, { inFullscreenMode: true })).toContainEqual({
|
||||||
...button,
|
...button,
|
||||||
style: { color: 'var(--primary-color, white)' },
|
style: { color: 'var(--primary-color, white)' },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set style for dynamic button with camera_select action', () => {
|
it('with camera_select action', () => {
|
||||||
const button: MenuItem = {
|
const button: MenuItem = {
|
||||||
...dynamicButton,
|
...dynamicButton,
|
||||||
tap_action: {
|
tap_action: {
|
||||||
@@ -1323,16 +1464,16 @@ describe('MenuButtonController', () => {
|
|||||||
camera: 'foo',
|
camera: 'foo',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const view = createView({ camera: 'foo' });
|
const view = createView({ camera: 'foo' });
|
||||||
controller.addDynamicMenuButton(button);
|
controller.addDynamicMenuButton(button);
|
||||||
|
|
||||||
expect(calculateButtons(controller, { view: view })).toContainEqual({
|
expect(calculateButtons(controller, { view: view })).toContainEqual({
|
||||||
...button,
|
...button,
|
||||||
style: { color: 'var(--primary-color, white)' },
|
style: { color: 'var(--primary-color, white)' },
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set style for dynamic button with array of actions', () => {
|
it('with array of actions', () => {
|
||||||
const button: MenuItem = {
|
const button: MenuItem = {
|
||||||
...dynamicButton,
|
...dynamicButton,
|
||||||
tap_action: [
|
tap_action: [
|
||||||
@@ -1340,12 +1481,13 @@ describe('MenuButtonController', () => {
|
|||||||
{ action: 'fire-dom-event', frigate_card_action: 'clips' },
|
{ action: 'fire-dom-event', frigate_card_action: 'clips' },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const view = createView({ camera: 'clips' });
|
const view = createView({ camera: 'clips' });
|
||||||
controller.addDynamicMenuButton(button);
|
controller.addDynamicMenuButton(button);
|
||||||
|
|
||||||
expect(calculateButtons(controller, { view: view })).toContainEqual({
|
expect(calculateButtons(controller, { view: view })).toContainEqual({
|
||||||
...button,
|
...button,
|
||||||
style: {},
|
style: {},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user