Files
advanced-camera-card/tests/condition-trigger/conditions/conditions/numeric-state.test.ts
T

263 lines
7.8 KiB
TypeScript

import { describe, expect, it, vi } from 'vitest';
import { TemplateManager } from '../../../../src/card-controller/templates';
import { createConditionEvaluator } from '../../../../src/condition-trigger/conditions/factory';
import {
createHASS,
createMockTemplateRenderer,
createStateEntity,
stubConnectedHomeAssistant,
} from '../../../test-utils';
import { createEvaluatorContext } from './test-utils';
// @vitest-environment jsdom
describe('numeric state condition', () => {
it('should match above a threshold', () => {
const evaluator = createConditionEvaluator(
{ condition: 'numeric_state' as const, entity_id: 'sensor.foo', above: 10 },
createEvaluatorContext(),
);
expect(evaluator.evaluate({}).result).toBeFalsy();
expect(
evaluator.evaluate({
hass: createHASS({ 'sensor.foo': createStateEntity({ state: '11' }) }),
}).result,
).toBeTruthy();
expect(
evaluator.evaluate({
hass: createHASS({ 'binary_sensor.foo': createStateEntity({ state: '9' }) }),
}).result,
).toBeFalsy();
});
it('should match below a threshold', () => {
const evaluator = createConditionEvaluator(
{ condition: 'numeric_state' as const, entity_id: 'sensor.foo', below: 10 },
createEvaluatorContext(),
);
expect(evaluator.evaluate({}).result).toBeFalsy();
expect(
evaluator.evaluate({
hass: createHASS({ 'sensor.foo': createStateEntity({ state: '11' }) }),
}).result,
).toBeFalsy();
expect(
evaluator.evaluate({
hass: createHASS({ 'sensor.foo': createStateEntity({ state: '9' }) }),
}).result,
).toBeTruthy();
});
it('should accept the entity field', () => {
const evaluator = createConditionEvaluator(
{ condition: 'numeric_state' as const, entity: 'sensor.foo', above: 10 },
createEvaluatorContext(),
);
expect(
evaluator.evaluate({
hass: createHASS({ 'sensor.foo': createStateEntity({ state: '11' }) }),
}).result,
).toBeTruthy();
});
it('should match a list of entities only when all of them match', () => {
const evaluator = createConditionEvaluator(
{
condition: 'numeric_state' as const,
entity_id: ['sensor.a', 'sensor.b'],
above: 10,
},
createEvaluatorContext(),
);
// Both above -> match.
expect(
evaluator.evaluate({
hass: createHASS({
'sensor.a': createStateEntity({ state: '11' }),
'sensor.b': createStateEntity({ state: '12' }),
}),
}).result,
).toBeTruthy();
// One below -> no match.
expect(
evaluator.evaluate({
hass: createHASS({
'sensor.a': createStateEntity({ state: '11' }),
'sensor.b': createStateEntity({ state: '9' }),
}),
}).result,
).toBeFalsy();
// One absent -> no match.
expect(
evaluator.evaluate({
hass: createHASS({
'sensor.a': createStateEntity({ state: '11' }),
}),
}).result,
).toBeFalsy();
});
it('should match against an attribute instead of the state', () => {
const evaluator = createConditionEvaluator(
{
condition: 'numeric_state' as const,
entity_id: 'sensor.foo',
attribute: 'battery',
below: 20,
},
createEvaluatorContext(),
);
expect(
evaluator.evaluate({
hass: createHASS({
'sensor.foo': createStateEntity({ state: 'on', attributes: { battery: 15 } }),
}),
}).result,
).toBeTruthy();
expect(
evaluator.evaluate({
hass: createHASS({
'sensor.foo': createStateEntity({ state: 'on', attributes: { battery: 50 } }),
}),
}).result,
).toBeFalsy();
});
it('should compare the rendered value_template instead of the state', async () => {
// This case renders a real value_template, so load the lazily-imported
// engine for the synchronous renderer.
const templateManager = new TemplateManager();
stubConnectedHomeAssistant();
await templateManager.loadRenderer();
const evaluator = createConditionEvaluator(
{
condition: 'numeric_state' as const,
entity_id: 'sensor.foo',
value_template: '{{ 11 }}',
above: 10,
},
createEvaluatorContext({ templateRenderer: templateManager }),
);
// The entity state (0) would fail; the template value (11) passes.
expect(
evaluator.evaluate({
hass: createHASS({ 'sensor.foo': createStateEntity({ state: '0' }) }),
}).result,
).toBeTruthy();
});
it('should not match a value_template until the renderer has loaded', () => {
const unloadedRenderer = createMockTemplateRenderer();
vi.mocked(unloadedRenderer).isLoaded.mockReturnValue(false);
const evaluator = createConditionEvaluator(
{
condition: 'numeric_state' as const,
entity_id: 'sensor.foo',
value_template: '{{ 11 }}',
above: 10,
},
createEvaluatorContext({ templateRenderer: unloadedRenderer }),
);
// The entity state (11) would pass, but the value_template cannot be
// evaluated before the renderer loads, so the condition does not match.
expect(
evaluator.evaluate({
hass: createHASS({ 'sensor.foo': createStateEntity({ state: '11' }) }),
}).result,
).toBeFalsy();
});
it('should not match when the value is not numeric', () => {
const evaluator = createConditionEvaluator(
{ condition: 'numeric_state' as const, entity_id: 'sensor.foo', above: 10 },
createEvaluatorContext(),
);
expect(
evaluator.evaluate({
hass: createHASS({ 'sensor.foo': createStateEntity({ state: 'unavailable' }) }),
}).result,
).toBeFalsy();
});
it('should not match without a condition state', () => {
const evaluator = createConditionEvaluator(
{ condition: 'numeric_state' as const, entity_id: 'sensor.foo', above: 10 },
createEvaluatorContext(),
);
expect(evaluator.evaluate().result).toBeFalsy();
});
it('should not match when the entity is absent', () => {
const evaluator = createConditionEvaluator(
{ condition: 'numeric_state' as const, entity_id: 'sensor.missing', above: 10 },
createEvaluatorContext(),
);
expect(
evaluator.evaluate({
hass: createHASS({ 'sensor.foo': createStateEntity({ state: '11' }) }),
}).result,
).toBeFalsy();
});
it('should resolve an entity-id reference as the above/below threshold', () => {
const evaluator = createConditionEvaluator(
{
condition: 'numeric_state' as const,
entity_id: 'sensor.foo',
above: 'input_number.limit',
},
createEvaluatorContext(),
);
// sensor.foo (20) > input_number.limit (10) -> true.
expect(
evaluator.evaluate({
hass: createHASS({
'sensor.foo': createStateEntity({ state: '20' }),
'input_number.limit': createStateEntity({ state: '10' }),
}),
}).result,
).toBeTruthy();
// sensor.foo (5) > input_number.limit (10) -> false.
expect(
evaluator.evaluate({
hass: createHASS({
'sensor.foo': createStateEntity({ state: '5' }),
'input_number.limit': createStateEntity({ state: '10' }),
}),
}).result,
).toBeFalsy();
});
it('should not match when a threshold entity is unresolvable', () => {
const evaluator = createConditionEvaluator(
{
condition: 'numeric_state' as const,
entity_id: 'sensor.foo',
below: 'input_number.limit',
},
createEvaluatorContext(),
);
expect(
evaluator.evaluate({
hass: createHASS({
'sensor.foo': createStateEntity({ state: '5' }),
'input_number.limit': createStateEntity({ state: 'unavailable' }),
}),
}).result,
).toBeFalsy();
});
});