mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-16 12:53:32 +00:00
refactor: handle non numeric comparisons
This commit is contained in:
committed by
Carlos Valente
parent
efe5ac16f2
commit
8ba65fa2bd
@@ -100,66 +100,88 @@ describe('testConditions()', () => {
|
|||||||
expect(result).toBe(true);
|
expect(result).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should compare two equal values', () => {
|
describe('equals operator', () => {
|
||||||
const mockStore = makeRuntimeStateData({ clock: 10 });
|
it('should compare two equal values', () => {
|
||||||
const result = testConditions([{ field: 'clock', operator: 'equals', value: '10' }], 'all', mockStore);
|
const mockStore = makeRuntimeStateData({ clock: 10 });
|
||||||
expect(result).toBe(true);
|
const result = testConditions([{ field: 'clock', operator: 'equals', value: '10' }], 'all', mockStore);
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should check if a value does not exist', () => {
|
||||||
|
const mockStore = makeRuntimeStateData({ eventNow: null });
|
||||||
|
const result = testConditions([{ field: 'eventNow.title', operator: 'equals', value: '' }], 'all', mockStore);
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should check if a value does not exist', () => {
|
describe('not_equals operator', () => {
|
||||||
const mockStore = makeRuntimeStateData({ eventNow: null });
|
it('should check if two values are different', () => {
|
||||||
const result = testConditions([{ field: 'eventNow.title', operator: 'equals', value: '' }], 'all', mockStore);
|
const mockStore = makeRuntimeStateData({ clock: 10 });
|
||||||
expect(result).toBe(true);
|
const result = testConditions([{ field: 'clock', operator: 'not_equals', value: '11' }], 'all', mockStore);
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should check if two values are different', () => {
|
describe('greater_than operator', () => {
|
||||||
const mockStore = makeRuntimeStateData({ clock: 10 });
|
it('should check if the given value is smaller', () => {
|
||||||
const result = testConditions([{ field: 'clock', operator: 'not_equals', value: '11' }], 'all', mockStore);
|
const mockStore = makeRuntimeStateData({ clock: 10 });
|
||||||
expect(result).toBe(true);
|
const result = testConditions([{ field: 'clock', operator: 'greater_than', value: '9' }], 'all', mockStore);
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
it('should handle values which are not numbers', () => {
|
||||||
|
const mockStore = makeRuntimeStateData({ clock: 10 });
|
||||||
|
const result = testConditions([{ field: 'clock', operator: 'greater_than', value: 'testing' }], 'all', mockStore);
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should check if the given value is smaller', () => {
|
describe('less_than operator', () => {
|
||||||
const mockStore = makeRuntimeStateData({ clock: 10 });
|
it('should check if the given value is larger', () => {
|
||||||
const result = testConditions([{ field: 'clock', operator: 'greater_than', value: '9' }], 'all', mockStore);
|
const mockStore = makeRuntimeStateData({ clock: 10 });
|
||||||
expect(result).toBe(true);
|
const result = testConditions([{ field: 'clock', operator: 'less_than', value: '11' }], 'all', mockStore);
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
it('should handle values which are not numbers', () => {
|
||||||
|
const mockStore = makeRuntimeStateData({ clock: 10 });
|
||||||
|
const result = testConditions([{ field: 'clock', operator: 'less_than', value: 'testing' }], 'all', mockStore);
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should check if the given value is larger', () => {
|
describe('contains operator', () => {
|
||||||
const mockStore = makeRuntimeStateData({ clock: 10 });
|
it('should check if value contains given string', () => {
|
||||||
const result = testConditions([{ field: 'clock', operator: 'less_than', value: '11' }], 'all', mockStore);
|
const result = testConditions(
|
||||||
expect(result).toBe(true);
|
[{ field: 'eventNow.title', operator: 'contains', value: 'lighting' }],
|
||||||
|
'all',
|
||||||
|
makeRuntimeStateData({ eventNow: makeOntimeEvent({ title: 'test-lighting' }) as PlayableEvent }),
|
||||||
|
);
|
||||||
|
expect(result).toBe(true);
|
||||||
|
|
||||||
|
const result2 = testConditions(
|
||||||
|
[{ field: 'eventNow.title', operator: 'contains', value: 'sound' }],
|
||||||
|
'all',
|
||||||
|
makeRuntimeStateData({ eventNow: makeOntimeEvent({ title: 'test-lighting' }) as PlayableEvent }),
|
||||||
|
);
|
||||||
|
expect(result2).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should check if value contains given string', () => {
|
describe('not_contains operator', () => {
|
||||||
const result = testConditions(
|
it('should check if value does not contain given string', () => {
|
||||||
[{ field: 'eventNow.title', operator: 'contains', value: 'lighting' }],
|
const result = testConditions(
|
||||||
'all',
|
[{ field: 'eventNow.title', operator: 'not_contains', value: 'lighting' }],
|
||||||
makeRuntimeStateData({ eventNow: makeOntimeEvent({ title: 'test-lighting' }) as PlayableEvent }),
|
'all',
|
||||||
);
|
makeRuntimeStateData({ eventNow: makeOntimeEvent({ title: 'test-lighting' }) as PlayableEvent }),
|
||||||
expect(result).toBe(true);
|
);
|
||||||
|
expect(result).toBe(false);
|
||||||
|
|
||||||
const result2 = testConditions(
|
const result2 = testConditions(
|
||||||
[{ field: 'eventNow.title', operator: 'contains', value: 'sound' }],
|
[{ field: 'eventNow.title', operator: 'not_contains', value: 'sound' }],
|
||||||
'all',
|
'all',
|
||||||
makeRuntimeStateData({ eventNow: makeOntimeEvent({ title: 'test-lighting' }) as PlayableEvent }),
|
makeRuntimeStateData({ eventNow: makeOntimeEvent({ title: 'test-lighting' }) as PlayableEvent }),
|
||||||
);
|
);
|
||||||
expect(result2).toBe(false);
|
expect(result2).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should check if value does not contain given string', () => {
|
|
||||||
const result = testConditions(
|
|
||||||
[{ field: 'eventNow.title', operator: 'not_contains', value: 'lighting' }],
|
|
||||||
'all',
|
|
||||||
makeRuntimeStateData({ eventNow: makeOntimeEvent({ title: 'test-lighting' }) as PlayableEvent }),
|
|
||||||
);
|
|
||||||
expect(result).toBe(false);
|
|
||||||
|
|
||||||
const result2 = testConditions(
|
|
||||||
[{ field: 'eventNow.title', operator: 'not_contains', value: 'sound' }],
|
|
||||||
'all',
|
|
||||||
makeRuntimeStateData({ eventNow: makeOntimeEvent({ title: 'test-lighting' }) as PlayableEvent }),
|
|
||||||
);
|
|
||||||
expect(result2).toBe(true);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('for all filter rule', () => {
|
describe('for all filter rule', () => {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { isOntimeCloud } from '../../externals.js';
|
|||||||
import { emitOSC } from './clients/osc.client.js';
|
import { emitOSC } from './clients/osc.client.js';
|
||||||
import { emitHTTP } from './clients/http.client.js';
|
import { emitHTTP } from './clients/http.client.js';
|
||||||
import { getAutomationsEnabled, getAutomations, getAutomationTriggers } from './automation.dao.js';
|
import { getAutomationsEnabled, getAutomations, getAutomationTriggers } from './automation.dao.js';
|
||||||
|
import { isGreaterThan, isLessThan } from './automation.utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exposes a method for triggering actions based on a TimerLifeCycle event
|
* Exposes a method for triggering actions based on a TimerLifeCycle event
|
||||||
@@ -87,9 +88,15 @@ export function testConditions(
|
|||||||
case 'not_equals':
|
case 'not_equals':
|
||||||
return fieldValue != value;
|
return fieldValue != value;
|
||||||
case 'greater_than':
|
case 'greater_than':
|
||||||
return fieldValue > value;
|
if (typeof fieldValue !== 'number') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return isGreaterThan(fieldValue, value);
|
||||||
case 'less_than':
|
case 'less_than':
|
||||||
return fieldValue < value;
|
if (typeof fieldValue !== 'number') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return isLessThan(fieldValue, value);
|
||||||
case 'contains':
|
case 'contains':
|
||||||
return typeof fieldValue === 'string' && fieldValue.includes(value);
|
return typeof fieldValue === 'string' && fieldValue.includes(value);
|
||||||
case 'not_contains':
|
case 'not_contains':
|
||||||
|
|||||||
@@ -133,3 +133,41 @@ const quickAliases: AliasesDefinition = {
|
|||||||
},
|
},
|
||||||
startedAt: { key: 'timer.startedAt', cb: (value: string) => formatDisplayFromString(value) },
|
startedAt: { key: 'timer.startedAt', cb: (value: string) => formatDisplayFromString(value) },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility encapsulates logic for comparing two strings which may encode numbers
|
||||||
|
* @example isGreaterThan('10', '5') // true
|
||||||
|
* @example isGreaterThan('5', '10') // false
|
||||||
|
* @example isGreaterThan('Ontime', 'Cool') // false
|
||||||
|
*/
|
||||||
|
export function isGreaterThan(a: string, b: string): boolean {
|
||||||
|
const aValue = Number(a);
|
||||||
|
const bValue = Number(b);
|
||||||
|
|
||||||
|
// we check if the values encore numbers and compare them
|
||||||
|
if (!isNaN(aValue) && !isNaN(bValue)) {
|
||||||
|
return aValue > bValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If either value is not a number, there is no logical comparison to be made
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility encapsulates logic for comparing two strings which may encode numbers
|
||||||
|
* @example isLessThan('10', '5') // false
|
||||||
|
* @example isLessThan('5', '10') // true
|
||||||
|
* @example isLessThan('Ontime', 'Cool') // false
|
||||||
|
*/
|
||||||
|
export function isLessThan(a: string, b: string): boolean {
|
||||||
|
const aValue = Number(a);
|
||||||
|
const bValue = Number(b);
|
||||||
|
|
||||||
|
// we check if the values encore numbers and compare them
|
||||||
|
if (!isNaN(aValue) && !isNaN(bValue)) {
|
||||||
|
return aValue < bValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If either value is not a number, there is no logical comparison to be made
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* Extracts a value from a nested object using a dot-separated path
|
* Extracts a value from a nested object using a dot-separated path
|
||||||
*/
|
*/
|
||||||
export function getPropertyFromPath<T extends object>(path: string, obj: T): any | undefined {
|
export function getPropertyFromPath<T extends object>(path: string, obj: T): unknown | undefined {
|
||||||
const keys = path.split('.');
|
const keys = path.split('.');
|
||||||
let result: any = obj;
|
let result: any = obj;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user