Files
advanced-camera-card/tests/card-controller/actions/actions/ptz.test.ts
T

508 lines
13 KiB
TypeScript

import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest';
import { PTZAction } from '../../../../src/card-controller/actions/actions/ptz';
import {
createCameraConfig,
createCameraManager,
createCardAPI,
createStore,
createView,
} from '../../../test-utils';
import { Capabilities } from '../../../../src/camera-manager/capabilities';
describe('should handle ptz action', () => {
it('should execute simple action', async () => {
const api = createCardAPI();
vi.mocked(api.getViewManager().getView).mockReturnValue(
createView({
camera: 'camera.office',
}),
);
const store = createStore([
{
cameraID: 'camera.office',
capabilities: new Capabilities({ ptz: { left: ['relative'] } }),
},
]);
vi.mocked(api.getCameraManager).mockReturnValue(createCameraManager(store));
const action = new PTZAction(
{},
{
action: 'fire-dom-event',
frigate_card_action: 'ptz',
ptz_action: 'left',
camera: 'camera.office',
},
);
await action.execute(api);
expect(api.getCameraManager().executePTZAction).toBeCalledWith(
'camera.office',
'left',
{
phase: undefined,
preset: undefined,
},
);
});
describe('without explicit camera', () => {
it('when current camera supports PTZ', async () => {
const api = createCardAPI();
const store = createStore([
{
cameraID: 'camera.office',
capabilities: new Capabilities({ ptz: { left: ['relative'] } }),
},
]);
vi.mocked(api.getCameraManager).mockReturnValue(createCameraManager(store));
vi.mocked(api.getViewManager().getView).mockReturnValue(
createView({ camera: 'camera.office' }),
);
const action = new PTZAction(
{},
{
action: 'fire-dom-event',
frigate_card_action: 'ptz',
ptz_action: 'left',
},
);
await action.execute(api);
expect(api.getCameraManager().executePTZAction).toBeCalledWith(
'camera.office',
'left',
{
phase: undefined,
preset: undefined,
},
);
});
it('when substream supports PTZ', async () => {
const api = createCardAPI();
const store = createStore([
{
cameraID: 'camera.office',
config: createCameraConfig({
dependencies: { cameras: ['camera.office_hd'] },
}),
},
{
cameraID: 'camera.office_hd',
capabilities: new Capabilities({ ptz: { left: ['relative'] } }),
},
]);
vi.mocked(api.getCameraManager).mockReturnValue(createCameraManager(store));
vi.mocked(api.getViewManager().getView).mockReturnValue(
createView({
camera: 'camera.office',
context: {
live: {
overrides: new Map([['camera.office', 'camera.office_hd']]),
},
},
}),
);
const action = new PTZAction(
{},
{
action: 'fire-dom-event',
frigate_card_action: 'ptz',
ptz_action: 'left',
},
);
await action.execute(api);
expect(api.getCameraManager().executePTZAction).toBeCalledWith(
'camera.office_hd',
'left',
{
phase: undefined,
preset: undefined,
},
);
});
it('when no camera supports PTZ', async () => {
const api = createCardAPI();
vi.mocked(api.getCameraManager).mockReturnValue(createCameraManager());
vi.mocked(api.getViewManager().getView).mockReturnValue(
createView({ camera: 'camera.office' }),
);
const action = new PTZAction(
{},
{
action: 'fire-dom-event',
frigate_card_action: 'ptz',
ptz_action: 'left',
},
);
await action.execute(api);
expect(api.getCameraManager().executePTZAction).not.toBeCalled();
});
});
it('when there is no view', async () => {
const api = createCardAPI();
vi.mocked(api.getCameraManager).mockReturnValue(createCameraManager());
const action = new PTZAction(
{},
{
action: 'fire-dom-event',
frigate_card_action: 'ptz',
ptz_action: 'left',
},
);
await action.execute(api);
expect(api.getCameraManager().executePTZAction).not.toBeCalled();
});
describe('when there is no action', () => {
it('should call first preset', async () => {
const api = createCardAPI();
const store = createStore([
{
cameraID: 'camera.office',
capabilities: new Capabilities({ ptz: { presets: ['home'] } }),
},
]);
vi.mocked(api.getCameraManager).mockReturnValue(createCameraManager(store));
vi.mocked(api.getViewManager().getView).mockReturnValue(
createView({ camera: 'camera.office' }),
);
const action = new PTZAction(
{},
{
action: 'fire-dom-event',
frigate_card_action: 'ptz',
},
);
await action.execute(api);
expect(api.getCameraManager().executePTZAction).toBeCalledWith(
'camera.office',
'preset',
{
phase: undefined,
preset: 'home',
},
);
});
it('should not call preset when there are no presets', async () => {
const api = createCardAPI();
const store = createStore([
{
cameraID: 'camera.office',
capabilities: new Capabilities({
ptz: {
left: ['relative'],
presets: [],
},
}),
},
]);
vi.mocked(api.getCameraManager).mockReturnValue(createCameraManager(store));
vi.mocked(api.getViewManager().getView).mockReturnValue(
createView({ camera: 'camera.office' }),
);
const action = new PTZAction(
{},
{
action: 'fire-dom-event',
frigate_card_action: 'ptz',
},
);
await action.execute(api);
expect(api.getCameraManager().executePTZAction).not.toBeCalled();
});
});
it('should execute preset', async () => {
const api = createCardAPI();
const store = createStore([
{
cameraID: 'camera.office',
capabilities: new Capabilities({
ptz: {
presets: ['window'],
},
}),
},
]);
vi.mocked(api.getCameraManager).mockReturnValue(createCameraManager(store));
vi.mocked(api.getViewManager().getView).mockReturnValue(
createView({ camera: 'camera.office' }),
);
const action = new PTZAction(
{},
{
action: 'fire-dom-event',
frigate_card_action: 'ptz',
ptz_action: 'preset',
ptz_preset: 'window',
},
);
await action.execute(api);
expect(api.getCameraManager().executePTZAction).toBeCalledWith(
'camera.office',
'preset',
{
phase: undefined,
preset: 'window',
},
);
});
it('should execute action with phase', async () => {
const api = createCardAPI();
const store = createStore([
{
cameraID: 'camera.office',
capabilities: new Capabilities({
ptz: {
left: ['continuous'],
},
}),
},
]);
vi.mocked(api.getCameraManager).mockReturnValue(createCameraManager(store));
vi.mocked(api.getViewManager().getView).mockReturnValue(
createView({ camera: 'camera.office' }),
);
const action = new PTZAction(
{},
{
action: 'fire-dom-event',
frigate_card_action: 'ptz',
ptz_action: 'left',
ptz_phase: 'start',
},
);
await action.execute(api);
expect(api.getCameraManager().executePTZAction).toBeCalledWith(
'camera.office',
'left',
{
phase: 'start',
},
);
});
// @vitest-environment jsdom
describe('when relative is requested but unsupported', () => {
beforeAll(() => {
vi.useFakeTimers();
});
afterAll(() => {
vi.useRealTimers();
});
it('should emulate relative', async () => {
const api = createCardAPI();
const store = createStore([
{
cameraID: 'camera.office',
capabilities: new Capabilities({
ptz: {
left: ['continuous'],
},
}),
},
]);
vi.mocked(api.getCameraManager).mockReturnValue(createCameraManager(store));
vi.mocked(api.getViewManager().getView).mockReturnValue(
createView({ camera: 'camera.office' }),
);
const action = new PTZAction(
{},
{
action: 'fire-dom-event',
frigate_card_action: 'ptz',
ptz_action: 'left',
},
);
await action.execute(api);
expect(api.getCameraManager().executePTZAction).toBeCalledWith(
'camera.office',
'left',
{
phase: 'start',
},
);
vi.runOnlyPendingTimers();
expect(api.getCameraManager().executePTZAction).toBeCalledWith(
'camera.office',
'left',
{
phase: 'stop',
},
);
});
it('should honor stop', async () => {
const api = createCardAPI();
const store = createStore([
{
cameraID: 'camera.office',
capabilities: new Capabilities({
ptz: {
left: ['continuous'],
},
}),
},
]);
vi.mocked(api.getCameraManager).mockReturnValue(createCameraManager(store));
vi.mocked(api.getViewManager().getView).mockReturnValue(
createView({ camera: 'camera.office' }),
);
const context = {};
const action = new PTZAction(context, {
action: 'fire-dom-event',
frigate_card_action: 'ptz',
ptz_action: 'left',
});
await action.execute(api);
expect(api.getCameraManager().executePTZAction).toBeCalledTimes(1);
action.stop();
vi.runOnlyPendingTimers();
expect(api.getCameraManager().executePTZAction).toBeCalledTimes(1);
});
});
describe('when continuous is requested but unsupported', () => {
beforeAll(() => {
vi.useFakeTimers();
});
afterAll(() => {
vi.useRealTimers();
});
it('should emulate continuous', async () => {
const api = createCardAPI();
const store = createStore([
{
cameraID: 'camera.office',
capabilities: new Capabilities({
ptz: {
left: ['relative'],
},
}),
},
]);
vi.mocked(api.getCameraManager).mockReturnValue(createCameraManager(store));
vi.mocked(api.getViewManager().getView).mockReturnValue(
createView({ camera: 'camera.office' }),
);
const context = {};
const startAction = new PTZAction(context, {
action: 'fire-dom-event',
frigate_card_action: 'ptz',
ptz_action: 'left',
ptz_phase: 'start',
});
await startAction.execute(api);
expect(api.getCameraManager().executePTZAction).toBeCalledWith(
'camera.office',
'left',
{
phase: undefined,
},
);
expect(api.getCameraManager().executePTZAction).toBeCalledTimes(1);
await vi.runOnlyPendingTimersAsync();
expect(api.getCameraManager().executePTZAction).toBeCalledTimes(2);
await vi.runOnlyPendingTimersAsync();
expect(api.getCameraManager().executePTZAction).toBeCalledTimes(3);
const stopAction = new PTZAction(context, {
action: 'fire-dom-event',
frigate_card_action: 'ptz',
ptz_action: 'left',
ptz_phase: 'stop',
});
await stopAction.execute(api);
// There should be no additional calls.
await vi.runOnlyPendingTimersAsync();
expect(api.getCameraManager().executePTZAction).toBeCalledTimes(3);
});
it('should honor stop', async () => {
const api = createCardAPI();
const store = createStore([
{
cameraID: 'camera.office',
capabilities: new Capabilities({
ptz: {
left: ['relative'],
},
}),
},
]);
vi.mocked(api.getCameraManager).mockReturnValue(createCameraManager(store));
vi.mocked(api.getViewManager().getView).mockReturnValue(
createView({ camera: 'camera.office' }),
);
const context = {};
const action = new PTZAction(context, {
action: 'fire-dom-event',
frigate_card_action: 'ptz',
ptz_action: 'left',
ptz_phase: 'start',
});
await action.execute(api);
expect(api.getCameraManager().executePTZAction).toBeCalledTimes(1);
await vi.runOnlyPendingTimersAsync();
expect(api.getCameraManager().executePTZAction).toBeCalledTimes(2);
action.stop();
await vi.runOnlyPendingTimersAsync();
// There should be no additional calls.
expect(api.getCameraManager().executePTZAction).toBeCalledTimes(2);
});
});
});