Files
advanced-camera-card/tests/card-controller/config/load-control-entities.test.ts
T

570 lines
17 KiB
TypeScript

import { describe, expect, it, vi } from 'vitest';
import { setRemoteControlEntityFromConfig } from '../../../src/card-controller/config/load-control-entities';
import {
INTERNAL_CALLBACK_ACTION,
InternalCallbackActionConfig,
} from '../../../src/config/schema/actions/custom/internal';
import { isAdvancedCameraCardCustomAction } from '../../../src/utils/action';
import {
createCardAPI,
createConfig,
createHASS,
createStateEntity,
createStore,
createView,
} from '../../test-utils';
describe('setRemoteControlEntityFromConfig', () => {
it('without control entity', () => {
const api = createCardAPI();
setRemoteControlEntityFromConfig(api);
expect(api.getAutomationsManager().deleteAutomations).toBeCalled();
expect(api.getAutomationsManager().addAutomations).not.toBeCalled();
});
it('with control entity and card priority', () => {
const api = createCardAPI();
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
createConfig({
remote_control: {
entities: {
camera: 'input_select.camera',
camera_priority: 'card',
},
},
}),
);
setRemoteControlEntityFromConfig(api);
expect(api.getAutomationsManager().deleteAutomations).toBeCalled();
expect(api.getAutomationsManager().addAutomations).toBeCalledWith([
{
actions: [
{
action: 'fire-dom-event',
advanced_camera_card_action: '__INTERNAL_CALLBACK_ACTION__',
callback: expect.any(Function),
},
],
conditions: [
{
condition: 'config',
paths: ['cameras', 'remote_control.entities.camera'],
},
],
tag: setRemoteControlEntityFromConfig,
},
{
actions: [
{
action: 'fire-dom-event',
advanced_camera_card_action: '__INTERNAL_CALLBACK_ACTION__',
callback: expect.any(Function),
},
],
conditions: [
{
condition: 'camera',
},
],
tag: setRemoteControlEntityFromConfig,
},
{
actions: [
{
action: 'fire-dom-event',
advanced_camera_card_action: '__INTERNAL_CALLBACK_ACTION__',
callback: expect.any(Function),
},
],
conditions: [
{
condition: 'initialized',
},
],
tag: setRemoteControlEntityFromConfig,
},
{
actions: [
{
action: 'fire-dom-event',
advanced_camera_card_action: 'camera_select',
camera: '{{ advanced_camera_card.trigger.state.to }}',
},
],
conditions: [
{
condition: 'state',
entity: 'input_select.camera',
},
],
tag: setRemoteControlEntityFromConfig,
},
]);
});
it('with control entity and entity priority', () => {
const api = createCardAPI();
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
createConfig({
remote_control: {
entities: {
camera: 'input_select.camera',
camera_priority: 'entity',
},
},
}),
);
setRemoteControlEntityFromConfig(api);
expect(api.getAutomationsManager().deleteAutomations).toBeCalled();
expect(api.getAutomationsManager().addAutomations).toBeCalledWith([
{
actions: [
{
action: 'fire-dom-event',
advanced_camera_card_action: '__INTERNAL_CALLBACK_ACTION__',
callback: expect.any(Function),
},
],
conditions: [
{
condition: 'config',
paths: ['cameras', 'remote_control.entities.camera'],
},
],
tag: setRemoteControlEntityFromConfig,
},
{
actions: [
{
action: 'fire-dom-event',
advanced_camera_card_action: '__INTERNAL_CALLBACK_ACTION__',
callback: expect.any(Function),
},
],
conditions: [
{
condition: 'camera',
},
],
tag: setRemoteControlEntityFromConfig,
},
{
actions: [
{
action: 'fire-dom-event',
advanced_camera_card_action: 'camera_select',
camera: '{{ hass.states["input_select.camera"].state }}',
},
],
conditions: [
{
condition: 'initialized',
},
],
tag: setRemoteControlEntityFromConfig,
},
{
actions: [
{
action: 'fire-dom-event',
advanced_camera_card_action: 'camera_select',
camera: '{{ advanced_camera_card.trigger.state.to }}',
},
],
conditions: [
{
condition: 'state',
entity: 'input_select.camera',
},
],
tag: setRemoteControlEntityFromConfig,
},
]);
});
describe('should set options', () => {
it('should set options when they are incorrect', () => {
const hass = createHASS();
const api = createCardAPI();
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(hass);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
createConfig({
remote_control: {
entities: {
camera: 'input_select.camera',
},
},
}),
);
const store = createStore([
{
cameraID: 'camera.one',
},
{
cameraID: 'camera.two',
},
]);
vi.mocked(api.getCameraManager().getStore).mockReturnValue(store);
setRemoteControlEntityFromConfig(api);
const addOptionsAction = vi.mocked(api.getAutomationsManager().addAutomations).mock
.calls[0][0][0].actions?.[0] as InternalCallbackActionConfig;
expect(addOptionsAction).toBeTruthy();
expect(isAdvancedCameraCardCustomAction(addOptionsAction)).toBeTruthy();
expect(addOptionsAction.advanced_camera_card_action).toBe(
INTERNAL_CALLBACK_ACTION,
);
addOptionsAction.callback(api);
expect(hass.callService).toBeCalledWith(
'input_select',
'set_options',
{
options: ['camera.one', 'camera.two'],
},
{
entity_id: 'input_select.camera',
},
);
});
it('should not set options when they are already correct', () => {
const hass = createHASS({
'input_select.camera': createStateEntity({
attributes: { options: ['camera.one', 'camera.two'] },
}),
});
const api = createCardAPI();
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(hass);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
createConfig({
remote_control: {
entities: {
camera: 'input_select.camera',
},
},
}),
);
const store = createStore([
{
cameraID: 'camera.one',
},
{
cameraID: 'camera.two',
},
]);
vi.mocked(api.getCameraManager().getStore).mockReturnValue(store);
setRemoteControlEntityFromConfig(api);
const addOptionsAction = vi.mocked(api.getAutomationsManager().addAutomations).mock
.calls[0][0][0].actions?.[0] as InternalCallbackActionConfig;
expect(addOptionsAction).toBeTruthy();
expect(isAdvancedCameraCardCustomAction(addOptionsAction)).toBeTruthy();
expect(addOptionsAction.advanced_camera_card_action).toBe(
INTERNAL_CALLBACK_ACTION,
);
addOptionsAction.callback(api);
expect(hass.callService).not.toBeCalled();
});
it('should not throw when hass is undefined setting options', () => {
const api = createCardAPI();
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(null);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
createConfig({
remote_control: {
entities: {
camera: 'input_select.camera',
},
},
}),
);
const store = createStore([
{
cameraID: 'camera.one',
},
{
cameraID: 'camera.two',
},
]);
vi.mocked(api.getCameraManager().getStore).mockReturnValue(store);
setRemoteControlEntityFromConfig(api);
const addOptionsAction = vi.mocked(api.getAutomationsManager().addAutomations).mock
.calls[0][0][0].actions?.[0] as InternalCallbackActionConfig;
expect(addOptionsAction).toBeTruthy();
expect(isAdvancedCameraCardCustomAction(addOptionsAction)).toBeTruthy();
expect(addOptionsAction.advanced_camera_card_action).toBe(
INTERNAL_CALLBACK_ACTION,
);
// Should not throw
addOptionsAction.callback(api);
});
});
describe('should select option on entity', () => {
it('should select option when camera differs from entity state', () => {
const hass = createHASS({
'input_select.camera': createStateEntity({
state: 'camera.one',
}),
});
const api = createCardAPI();
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(hass);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
createConfig({
remote_control: {
entities: {
camera: 'input_select.camera',
camera_priority: 'card',
},
},
}),
);
vi.mocked(api.getViewManager().getView).mockReturnValue(
createView({
camera: 'camera.two',
view: 'live',
}),
);
setRemoteControlEntityFromConfig(api);
// Get the camera sync callback (automation index 1)
const cameraSyncAction = vi.mocked(api.getAutomationsManager().addAutomations).mock
.calls[0][0][1].actions?.[0] as InternalCallbackActionConfig;
expect(cameraSyncAction).toBeTruthy();
expect(isAdvancedCameraCardCustomAction(cameraSyncAction)).toBeTruthy();
expect(cameraSyncAction.advanced_camera_card_action).toBe(
INTERNAL_CALLBACK_ACTION,
);
cameraSyncAction.callback(api);
expect(hass.callService).toBeCalledWith(
'input_select',
'select_option',
{
option: 'camera.two',
},
{
entity_id: 'input_select.camera',
},
);
});
it('should not select option when camera matches entity state', () => {
const hass = createHASS({
'input_select.camera': createStateEntity({
state: 'camera.one',
}),
});
const api = createCardAPI();
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(hass);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
createConfig({
remote_control: {
entities: {
camera: 'input_select.camera',
camera_priority: 'card',
},
},
}),
);
vi.mocked(api.getViewManager().getView).mockReturnValue(
createView({
camera: 'camera.one',
view: 'live',
}),
);
setRemoteControlEntityFromConfig(api);
// Get the camera sync callback (automation index 1)
const cameraSyncAction = vi.mocked(api.getAutomationsManager().addAutomations).mock
.calls[0][0][1].actions?.[0] as InternalCallbackActionConfig;
expect(cameraSyncAction).toBeTruthy();
expect(isAdvancedCameraCardCustomAction(cameraSyncAction)).toBeTruthy();
expect(cameraSyncAction.advanced_camera_card_action).toBe(
INTERNAL_CALLBACK_ACTION,
);
cameraSyncAction.callback(api);
// Should NOT call select_option since entity already shows camera.one
expect(hass.callService).not.toBeCalled();
});
it('should not select option when camera is undefined', () => {
const hass = createHASS({
'input_select.camera': createStateEntity({
state: 'camera.one',
}),
});
const api = createCardAPI();
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(hass);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
createConfig({
remote_control: {
entities: {
camera: 'input_select.camera',
camera_priority: 'card',
},
},
}),
);
vi.mocked(api.getViewManager().getView).mockReturnValue(null);
setRemoteControlEntityFromConfig(api);
// Get the camera sync callback (automation index 1)
const cameraSyncAction = vi.mocked(api.getAutomationsManager().addAutomations).mock
.calls[0][0][1].actions?.[0] as InternalCallbackActionConfig;
expect(cameraSyncAction).toBeTruthy();
expect(isAdvancedCameraCardCustomAction(cameraSyncAction)).toBeTruthy();
expect(cameraSyncAction.advanced_camera_card_action).toBe(
INTERNAL_CALLBACK_ACTION,
);
cameraSyncAction.callback(api);
// Should NOT call select_option since camera is undefined
expect(hass.callService).not.toBeCalled();
});
it('should not throw when hass is undefined', () => {
const api = createCardAPI();
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(null);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
createConfig({
remote_control: {
entities: {
camera: 'input_select.camera',
camera_priority: 'card',
},
},
}),
);
vi.mocked(api.getViewManager().getView).mockReturnValue(
createView({
camera: 'camera.two',
view: 'live',
}),
);
setRemoteControlEntityFromConfig(api);
const cameraSyncAction = vi.mocked(api.getAutomationsManager().addAutomations).mock
.calls[0][0][1].actions?.[0] as InternalCallbackActionConfig;
expect(cameraSyncAction).toBeTruthy();
expect(isAdvancedCameraCardCustomAction(cameraSyncAction)).toBeTruthy();
expect(cameraSyncAction.advanced_camera_card_action).toBe(
INTERNAL_CALLBACK_ACTION,
);
// Should not throw and obviously not call service (as hass is null)
cameraSyncAction.callback(api);
});
it('should select option when entity state is undefined', () => {
const hass = createHASS({});
const api = createCardAPI();
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(hass);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
createConfig({
remote_control: {
entities: {
camera: 'input_select.camera',
camera_priority: 'card',
},
},
}),
);
vi.mocked(api.getViewManager().getView).mockReturnValue(
createView({
camera: 'camera.two',
view: 'live',
}),
);
setRemoteControlEntityFromConfig(api);
const cameraSyncAction = vi.mocked(api.getAutomationsManager().addAutomations).mock
.calls[0][0][1].actions?.[0] as InternalCallbackActionConfig;
expect(cameraSyncAction).toBeTruthy();
expect(isAdvancedCameraCardCustomAction(cameraSyncAction)).toBeTruthy();
expect(cameraSyncAction.advanced_camera_card_action).toBe(
INTERNAL_CALLBACK_ACTION,
);
cameraSyncAction.callback(api);
expect(hass.callService).toBeCalledWith(
'input_select',
'select_option',
{
option: 'camera.two',
},
{
entity_id: 'input_select.camera',
},
);
});
it('should select option on initialization with card priority', () => {
const hass = createHASS({
'input_select.camera': createStateEntity({
state: 'camera.one',
}),
});
const api = createCardAPI();
vi.mocked(api.getHASSManager().getHASS).mockReturnValue(hass);
vi.mocked(api.getConfigManager().getConfig).mockReturnValue(
createConfig({
remote_control: {
entities: {
camera: 'input_select.camera',
camera_priority: 'card',
},
},
}),
);
vi.mocked(api.getViewManager().getView).mockReturnValue(
createView({
camera: 'camera.two',
view: 'live',
}),
);
setRemoteControlEntityFromConfig(api);
// Get the initialization callback (automation index 2)
const initAction = vi.mocked(api.getAutomationsManager().addAutomations).mock
.calls[0][0][2].actions?.[0] as InternalCallbackActionConfig;
expect(initAction).toBeTruthy();
expect(isAdvancedCameraCardCustomAction(initAction)).toBeTruthy();
expect(initAction.advanced_camera_card_action).toBe(INTERNAL_CALLBACK_ACTION);
initAction.callback(api);
expect(hass.callService).toBeCalledWith(
'input_select',
'select_option',
{
option: 'camera.two',
},
{
entity_id: 'input_select.camera',
},
);
});
});
});