mirror of https://github.com/artf/grapesjs.git
Browse Source
* Add data condition variable * Format --------- Co-authored-by: Artur Arseniev <artur.catch@hotmail.it>revert-6247-release-docs-v0.21.14
committed by
GitHub
14 changed files with 839 additions and 0 deletions
@ -0,0 +1,13 @@ |
|||
import { Operator } from './operators'; |
|||
|
|||
export class ConditionStatement { |
|||
constructor( |
|||
private leftValue: any, |
|||
private operator: Operator, |
|||
private rightValue: any, |
|||
) {} |
|||
|
|||
evaluate(): boolean { |
|||
return this.operator.evaluate(this.leftValue, this.rightValue); |
|||
} |
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
import { NumberOperation } from './operators/NumberOperator'; |
|||
import { StringOperation } from './operators/StringOperations'; |
|||
import { GenericOperation } from './operators/GenericOperator'; |
|||
import { Model } from '../../../common'; |
|||
import { LogicalOperation } from './operators/LogicalOperator'; |
|||
import { evaluateCondition } from './evaluateCondition'; |
|||
|
|||
export const ConditionalVariableType = 'conditional-variable'; |
|||
export type Expression = { |
|||
left: any; |
|||
operator: GenericOperation | StringOperation | NumberOperation; |
|||
right: any; |
|||
}; |
|||
|
|||
export type LogicGroup = { |
|||
logicalOperator: LogicalOperation; |
|||
statements: (Expression | LogicGroup | boolean)[]; |
|||
}; |
|||
|
|||
export class DataCondition extends Model { |
|||
private conditionResult: boolean; |
|||
|
|||
defaults() { |
|||
return { |
|||
type: ConditionalVariableType, |
|||
condition: false, |
|||
}; |
|||
} |
|||
|
|||
constructor( |
|||
private condition: Expression | LogicGroup | boolean, |
|||
private ifTrue: any, |
|||
private ifFalse: any, |
|||
) { |
|||
super(); |
|||
this.conditionResult = this.evaluate(); |
|||
} |
|||
|
|||
evaluate() { |
|||
return evaluateCondition(this.condition); |
|||
} |
|||
|
|||
getDataValue(): any { |
|||
return this.conditionResult ? this.ifTrue : this.ifFalse; |
|||
} |
|||
|
|||
reevaluate(): void { |
|||
this.conditionResult = this.evaluate(); |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
import { LogicalOperator } from './operators/LogicalOperator'; |
|||
import { Expression, LogicGroup } from './DataCondition'; |
|||
import { evaluateCondition } from './evaluateCondition'; |
|||
|
|||
export class LogicalGroupStatement { |
|||
constructor( |
|||
private operator: LogicalOperator, |
|||
private statements: (Expression | LogicGroup | boolean)[], |
|||
) {} |
|||
|
|||
evaluate(): boolean { |
|||
const results = this.statements.map((statement) => evaluateCondition(statement)); |
|||
return this.operator.evaluate(results); |
|||
} |
|||
} |
|||
@ -0,0 +1,53 @@ |
|||
import { ConditionStatement } from './ConditionStatement'; |
|||
import { Expression, LogicGroup } from './DataCondition'; |
|||
import { LogicalGroupStatement } from './LogicalGroupStatement'; |
|||
import { Operator } from './operators'; |
|||
import { GenericOperation, GenericOperator } from './operators/GenericOperator'; |
|||
import { LogicalOperator } from './operators/LogicalOperator'; |
|||
import { NumberOperation, NumberOperator } from './operators/NumberOperator'; |
|||
import { StringOperation, StringOperator } from './operators/StringOperations'; |
|||
|
|||
export function evaluateCondition(condition: any): boolean { |
|||
if (typeof condition === 'boolean') { |
|||
return condition; |
|||
} |
|||
|
|||
if (isLogicGroup(condition)) { |
|||
const { logicalOperator, statements } = condition; |
|||
const op = new LogicalOperator(logicalOperator); |
|||
const logicalGroup = new LogicalGroupStatement(op, statements); |
|||
return logicalGroup.evaluate(); |
|||
} |
|||
|
|||
if (isCondition(condition)) { |
|||
const { left, operator, right } = condition; |
|||
const op = operatorFactory(left, operator); |
|||
const statement = new ConditionStatement(left, op, right); |
|||
return statement.evaluate(); |
|||
} |
|||
|
|||
throw new Error('Invalid condition type.'); |
|||
} |
|||
|
|||
function operatorFactory(left: any, operator: string): Operator { |
|||
if (isOperatorInEnum(operator, GenericOperation)) { |
|||
return new GenericOperator(operator as GenericOperation); |
|||
} else if (typeof left === 'number') { |
|||
return new NumberOperator(operator as NumberOperation); |
|||
} else if (typeof left === 'string') { |
|||
return new StringOperator(operator as StringOperation); |
|||
} |
|||
throw new Error(`Unsupported data type: ${typeof left}`); |
|||
} |
|||
|
|||
function isOperatorInEnum(operator: string, enumObject: any): boolean { |
|||
return Object.values(enumObject).includes(operator); |
|||
} |
|||
|
|||
function isLogicGroup(condition: any): condition is LogicGroup { |
|||
return condition && typeof condition.logicalOperator !== 'undefined' && Array.isArray(condition.statements); |
|||
} |
|||
|
|||
function isCondition(condition: any): condition is Expression { |
|||
return condition && typeof condition.left !== 'undefined' && typeof condition.operator === 'string'; |
|||
} |
|||
@ -0,0 +1,54 @@ |
|||
import DataVariable from '../../DataVariable'; |
|||
import { Operator } from '.'; |
|||
|
|||
export enum GenericOperation { |
|||
equals = 'equals', |
|||
isTruthy = 'isTruthy', |
|||
isFalsy = 'isFalsy', |
|||
isDefined = 'isDefined', |
|||
isNull = 'isNull', |
|||
isUndefined = 'isUndefined', |
|||
isArray = 'isArray', |
|||
isObject = 'isObject', |
|||
isString = 'isString', |
|||
isNumber = 'isNumber', |
|||
isBoolean = 'isBoolean', |
|||
isDefaultValue = 'isDefaultValue', // For Datasource variables
|
|||
} |
|||
|
|||
export class GenericOperator extends Operator { |
|||
constructor(private operator: GenericOperation) { |
|||
super(); |
|||
} |
|||
|
|||
evaluate(left: any, right: any): boolean { |
|||
switch (this.operator) { |
|||
case 'equals': |
|||
return left === right; |
|||
case 'isTruthy': |
|||
return !!left; |
|||
case 'isFalsy': |
|||
return !left; |
|||
case 'isDefined': |
|||
return left !== undefined && left !== null; |
|||
case 'isNull': |
|||
return left === null; |
|||
case 'isUndefined': |
|||
return left === undefined; |
|||
case 'isArray': |
|||
return Array.isArray(left); |
|||
case 'isObject': |
|||
return typeof left === 'object' && left !== null; |
|||
case 'isString': |
|||
return typeof left === 'string'; |
|||
case 'isNumber': |
|||
return typeof left === 'number'; |
|||
case 'isBoolean': |
|||
return typeof left === 'boolean'; |
|||
case 'isDefaultValue': |
|||
return left instanceof DataVariable && left.get('default') === right; |
|||
default: |
|||
throw new Error(`Unsupported generic operator: ${this.operator}`); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,28 @@ |
|||
import { Operator } from '.'; |
|||
|
|||
export enum LogicalOperation { |
|||
and = 'and', |
|||
or = 'or', |
|||
xor = 'xor', |
|||
} |
|||
|
|||
export class LogicalOperator extends Operator { |
|||
constructor(private operator: LogicalOperation) { |
|||
super(); |
|||
} |
|||
|
|||
evaluate(statements: boolean[]): boolean { |
|||
if (!statements.length) throw new Error('Expected one or more statments, got none'); |
|||
|
|||
switch (this.operator) { |
|||
case LogicalOperation.and: |
|||
return statements.every(Boolean); |
|||
case LogicalOperation.or: |
|||
return statements.some(Boolean); |
|||
case LogicalOperation.xor: |
|||
return statements.filter(Boolean).length === 1; |
|||
default: |
|||
throw new Error(`Unsupported logical operator: ${this.operator}`); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
import { Operator } from '.'; |
|||
|
|||
export enum NumberOperation { |
|||
greaterThan = '>', |
|||
lessThan = '<', |
|||
greaterThanOrEqual = '>=', |
|||
lessThanOrEqual = '<=', |
|||
equals = '=', |
|||
notEquals = '!=', |
|||
} |
|||
|
|||
export class NumberOperator extends Operator { |
|||
constructor(private operator: NumberOperation) { |
|||
super(); |
|||
} |
|||
|
|||
evaluate(left: number, right: number): boolean { |
|||
switch (this.operator) { |
|||
case NumberOperation.greaterThan: |
|||
return left > right; |
|||
case NumberOperation.lessThan: |
|||
return left < right; |
|||
case NumberOperation.greaterThanOrEqual: |
|||
return left >= right; |
|||
case NumberOperation.lessThanOrEqual: |
|||
return left <= right; |
|||
case NumberOperation.equals: |
|||
return left === right; |
|||
case NumberOperation.notEquals: |
|||
return left !== right; |
|||
default: |
|||
throw new Error(`Unsupported number operator: ${this.operator}`); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
import { Operator } from '.'; |
|||
|
|||
export enum StringOperation { |
|||
contains = 'contains', |
|||
startsWith = 'startsWith', |
|||
endsWith = 'endsWith', |
|||
matchesRegex = 'matchesRegex', |
|||
} |
|||
|
|||
export class StringOperator extends Operator { |
|||
constructor(private operator: StringOperation) { |
|||
super(); |
|||
} |
|||
|
|||
evaluate(left: string, right: string): boolean { |
|||
switch (this.operator) { |
|||
case StringOperation.contains: |
|||
return left.includes(right); |
|||
case StringOperation.startsWith: |
|||
return left.startsWith(right); |
|||
case StringOperation.endsWith: |
|||
return left.endsWith(right); |
|||
case StringOperation.matchesRegex: |
|||
return new RegExp(right).test(left); |
|||
default: |
|||
throw new Error(`Unsupported string operator: ${this.operator}`); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
export abstract class Operator { |
|||
abstract evaluate(left: any, right: any): boolean; |
|||
} |
|||
@ -0,0 +1,174 @@ |
|||
import { |
|||
DataCondition, |
|||
Expression, |
|||
LogicGroup, |
|||
} from '../../../../../src/data_sources/model/conditional_variables/DataCondition'; |
|||
import { GenericOperation } from '../../../../../src/data_sources/model/conditional_variables/operators/GenericOperator'; |
|||
import { LogicalOperation } from '../../../../../src/data_sources/model/conditional_variables/operators/LogicalOperator'; |
|||
import { NumberOperation } from '../../../../../src/data_sources/model/conditional_variables/operators/NumberOperator'; |
|||
import { StringOperation } from '../../../../../src/data_sources/model/conditional_variables/operators/StringOperations'; |
|||
|
|||
describe('DataCondition', () => { |
|||
describe('Basic Functionality Tests', () => { |
|||
test('should evaluate a simple boolean condition', () => { |
|||
const condition = true; |
|||
const dataCondition = new DataCondition(condition, 'Yes', 'No'); |
|||
|
|||
expect(dataCondition.getDataValue()).toBe('Yes'); |
|||
}); |
|||
|
|||
test('should return ifFalse when condition evaluates to false', () => { |
|||
const condition = false; |
|||
const dataCondition = new DataCondition(condition, 'Yes', 'No'); |
|||
|
|||
expect(dataCondition.getDataValue()).toBe('No'); |
|||
}); |
|||
}); |
|||
|
|||
describe('Operator Tests', () => { |
|||
test('should evaluate using GenericOperation operators', () => { |
|||
const condition: Expression = { left: 5, operator: GenericOperation.equals, right: 5 }; |
|||
const dataCondition = new DataCondition(condition, 'Equal', 'Not Equal'); |
|||
|
|||
expect(dataCondition.getDataValue()).toBe('Equal'); |
|||
}); |
|||
|
|||
test('equals (false)', () => { |
|||
const condition: Expression = { |
|||
left: 'hello', |
|||
operator: GenericOperation.equals, |
|||
right: 'world', |
|||
}; |
|||
const dataCondition = new DataCondition(condition, 'true', 'false'); |
|||
expect(dataCondition.evaluate()).toBe(false); |
|||
}); |
|||
|
|||
test('should evaluate using StringOperation operators', () => { |
|||
const condition: Expression = { left: 'apple', operator: StringOperation.contains, right: 'app' }; |
|||
const dataCondition = new DataCondition(condition, 'Contains', "Doesn't contain"); |
|||
|
|||
expect(dataCondition.getDataValue()).toBe('Contains'); |
|||
}); |
|||
|
|||
test('should evaluate using NumberOperation operators', () => { |
|||
const condition: Expression = { left: 10, operator: NumberOperation.lessThan, right: 15 }; |
|||
const dataCondition = new DataCondition(condition, 'Valid', 'Invalid'); |
|||
|
|||
expect(dataCondition.getDataValue()).toBe('Valid'); |
|||
}); |
|||
|
|||
test('should evaluate using LogicalOperation operators', () => { |
|||
const logicGroup: LogicGroup = { |
|||
logicalOperator: LogicalOperation.and, |
|||
statements: [ |
|||
{ left: true, operator: GenericOperation.equals, right: true }, |
|||
{ left: 5, operator: NumberOperation.greaterThan, right: 3 }, |
|||
], |
|||
}; |
|||
|
|||
const dataCondition = new DataCondition(logicGroup, 'Pass', 'Fail'); |
|||
expect(dataCondition.getDataValue()).toBe('Pass'); |
|||
}); |
|||
}); |
|||
|
|||
describe('Edge Case Tests', () => { |
|||
test('should throw error for invalid condition type', () => { |
|||
const invalidCondition: any = { randomField: 'randomValue' }; |
|||
expect(() => new DataCondition(invalidCondition, 'Yes', 'No')).toThrow('Invalid condition type.'); |
|||
}); |
|||
|
|||
test('should evaluate complex nested conditions', () => { |
|||
const nestedLogicGroup: LogicGroup = { |
|||
logicalOperator: LogicalOperation.or, |
|||
statements: [ |
|||
{ |
|||
logicalOperator: LogicalOperation.and, |
|||
statements: [ |
|||
{ left: 1, operator: NumberOperation.lessThan, right: 5 }, |
|||
{ left: 'test', operator: GenericOperation.equals, right: 'test' }, |
|||
], |
|||
}, |
|||
{ left: 10, operator: NumberOperation.greaterThan, right: 100 }, |
|||
], |
|||
}; |
|||
|
|||
const dataCondition = new DataCondition(nestedLogicGroup, 'Nested Pass', 'Nested Fail'); |
|||
expect(dataCondition.getDataValue()).toBe('Nested Pass'); |
|||
}); |
|||
}); |
|||
|
|||
describe('LogicalGroup Tests', () => { |
|||
test('should correctly handle AND logical operator', () => { |
|||
const logicGroup: LogicGroup = { |
|||
logicalOperator: LogicalOperation.and, |
|||
statements: [ |
|||
{ left: true, operator: GenericOperation.equals, right: true }, |
|||
{ left: 5, operator: NumberOperation.greaterThan, right: 3 }, |
|||
], |
|||
}; |
|||
|
|||
const dataCondition = new DataCondition(logicGroup, 'All true', 'One or more false'); |
|||
expect(dataCondition.getDataValue()).toBe('All true'); |
|||
}); |
|||
|
|||
test('should correctly handle OR logical operator', () => { |
|||
const logicGroup: LogicGroup = { |
|||
logicalOperator: LogicalOperation.or, |
|||
statements: [ |
|||
{ left: true, operator: GenericOperation.equals, right: false }, |
|||
{ left: 5, operator: NumberOperation.greaterThan, right: 3 }, |
|||
], |
|||
}; |
|||
|
|||
const dataCondition = new DataCondition(logicGroup, 'At least one true', 'All false'); |
|||
expect(dataCondition.getDataValue()).toBe('At least one true'); |
|||
}); |
|||
|
|||
test('should correctly handle XOR logical operator', () => { |
|||
const logicGroup: LogicGroup = { |
|||
logicalOperator: LogicalOperation.xor, |
|||
statements: [ |
|||
{ left: true, operator: GenericOperation.equals, right: true }, |
|||
{ left: 5, operator: NumberOperation.lessThan, right: 3 }, |
|||
{ left: false, operator: GenericOperation.equals, right: true }, |
|||
], |
|||
}; |
|||
|
|||
const dataCondition = new DataCondition(logicGroup, 'Exactly one true', 'Multiple true or all false'); |
|||
expect(dataCondition.getDataValue()).toBe('Exactly one true'); |
|||
}); |
|||
|
|||
test('should handle nested logical groups', () => { |
|||
const logicGroup: LogicGroup = { |
|||
logicalOperator: LogicalOperation.and, |
|||
statements: [ |
|||
{ left: true, operator: GenericOperation.equals, right: true }, |
|||
{ |
|||
logicalOperator: LogicalOperation.or, |
|||
statements: [ |
|||
{ left: 5, operator: NumberOperation.greaterThan, right: 3 }, |
|||
{ left: false, operator: GenericOperation.equals, right: true }, |
|||
], |
|||
}, |
|||
], |
|||
}; |
|||
|
|||
const dataCondition = new DataCondition(logicGroup, 'All true', 'One or more false'); |
|||
expect(dataCondition.getDataValue()).toBe('All true'); |
|||
}); |
|||
|
|||
test('should handle groups with false conditions', () => { |
|||
const logicGroup: LogicGroup = { |
|||
logicalOperator: LogicalOperation.and, |
|||
statements: [ |
|||
{ left: true, operator: GenericOperation.equals, right: true }, |
|||
{ left: false, operator: GenericOperation.equals, right: true }, |
|||
{ left: 5, operator: NumberOperation.greaterThan, right: 3 }, |
|||
], |
|||
}; |
|||
|
|||
const dataCondition = new DataCondition(logicGroup, 'All true', 'One or more false'); |
|||
expect(dataCondition.getDataValue()).toBe('One or more false'); |
|||
}); |
|||
}); |
|||
}); |
|||
@ -0,0 +1,150 @@ |
|||
import { |
|||
GenericOperator, |
|||
GenericOperation, |
|||
} from '../../../../../../src/data_sources/model/conditional_variables/operators/GenericOperator'; |
|||
|
|||
describe('GenericOperator', () => { |
|||
describe('Operator: equals', () => { |
|||
test('should return true when values are equal', () => { |
|||
const operator = new GenericOperator(GenericOperation.equals); |
|||
expect(operator.evaluate(5, 5)).toBe(true); |
|||
}); |
|||
|
|||
test('should return false when values are not equal', () => { |
|||
const operator = new GenericOperator(GenericOperation.equals); |
|||
expect(operator.evaluate(5, 10)).toBe(false); |
|||
}); |
|||
}); |
|||
|
|||
describe('Operator: isTruthy', () => { |
|||
test('should return true for truthy value', () => { |
|||
const operator = new GenericOperator(GenericOperation.isTruthy); |
|||
expect(operator.evaluate('non-empty', null)).toBe(true); |
|||
}); |
|||
|
|||
test('should return false for falsy value', () => { |
|||
const operator = new GenericOperator(GenericOperation.isTruthy); |
|||
expect(operator.evaluate('', null)).toBe(false); |
|||
}); |
|||
}); |
|||
|
|||
describe('Operator: isFalsy', () => { |
|||
test('should return true for falsy value', () => { |
|||
const operator = new GenericOperator(GenericOperation.isFalsy); |
|||
expect(operator.evaluate(0, null)).toBe(true); |
|||
}); |
|||
|
|||
test('should return false for truthy value', () => { |
|||
const operator = new GenericOperator(GenericOperation.isFalsy); |
|||
expect(operator.evaluate(1, null)).toBe(false); |
|||
}); |
|||
}); |
|||
|
|||
describe('Operator: isDefined', () => { |
|||
test('should return true for defined value', () => { |
|||
const operator = new GenericOperator(GenericOperation.isDefined); |
|||
expect(operator.evaluate(10, null)).toBe(true); |
|||
}); |
|||
|
|||
test('should return false for undefined value', () => { |
|||
const operator = new GenericOperator(GenericOperation.isDefined); |
|||
expect(operator.evaluate(undefined, null)).toBe(false); |
|||
}); |
|||
}); |
|||
|
|||
describe('Operator: isNull', () => { |
|||
test('should return true for null value', () => { |
|||
const operator = new GenericOperator(GenericOperation.isNull); |
|||
expect(operator.evaluate(null, null)).toBe(true); |
|||
}); |
|||
|
|||
test('should return false for non-null value', () => { |
|||
const operator = new GenericOperator(GenericOperation.isNull); |
|||
expect(operator.evaluate(0, null)).toBe(false); |
|||
}); |
|||
}); |
|||
|
|||
describe('Operator: isUndefined', () => { |
|||
test('should return true for undefined value', () => { |
|||
const operator = new GenericOperator(GenericOperation.isUndefined); |
|||
expect(operator.evaluate(undefined, null)).toBe(true); |
|||
}); |
|||
|
|||
test('should return false for defined value', () => { |
|||
const operator = new GenericOperator(GenericOperation.isUndefined); |
|||
expect(operator.evaluate(0, null)).toBe(false); |
|||
}); |
|||
}); |
|||
|
|||
describe('Operator: isArray', () => { |
|||
test('should return true for array', () => { |
|||
const operator = new GenericOperator(GenericOperation.isArray); |
|||
expect(operator.evaluate([1, 2, 3], null)).toBe(true); |
|||
}); |
|||
|
|||
test('should return false for non-array', () => { |
|||
const operator = new GenericOperator(GenericOperation.isArray); |
|||
expect(operator.evaluate('not an array', null)).toBe(false); |
|||
}); |
|||
}); |
|||
|
|||
describe('Operator: isObject', () => { |
|||
test('should return true for object', () => { |
|||
const operator = new GenericOperator(GenericOperation.isObject); |
|||
expect(operator.evaluate({ key: 'value' }, null)).toBe(true); |
|||
}); |
|||
|
|||
test('should return false for non-object', () => { |
|||
const operator = new GenericOperator(GenericOperation.isObject); |
|||
expect(operator.evaluate(42, null)).toBe(false); |
|||
}); |
|||
}); |
|||
|
|||
describe('Operator: isString', () => { |
|||
test('should return true for string', () => { |
|||
const operator = new GenericOperator(GenericOperation.isString); |
|||
expect(operator.evaluate('Hello', null)).toBe(true); |
|||
}); |
|||
|
|||
test('should return false for non-string', () => { |
|||
const operator = new GenericOperator(GenericOperation.isString); |
|||
expect(operator.evaluate(42, null)).toBe(false); |
|||
}); |
|||
}); |
|||
|
|||
describe('Operator: isNumber', () => { |
|||
test('should return true for number', () => { |
|||
const operator = new GenericOperator(GenericOperation.isNumber); |
|||
expect(operator.evaluate(42, null)).toBe(true); |
|||
}); |
|||
|
|||
test('should return false for non-number', () => { |
|||
const operator = new GenericOperator(GenericOperation.isNumber); |
|||
expect(operator.evaluate('not a number', null)).toBe(false); |
|||
}); |
|||
}); |
|||
|
|||
describe('Operator: isBoolean', () => { |
|||
test('should return true for boolean', () => { |
|||
const operator = new GenericOperator(GenericOperation.isBoolean); |
|||
expect(operator.evaluate(true, null)).toBe(true); |
|||
}); |
|||
|
|||
test('should return false for non-boolean', () => { |
|||
const operator = new GenericOperator(GenericOperation.isBoolean); |
|||
expect(operator.evaluate(1, null)).toBe(false); |
|||
}); |
|||
}); |
|||
|
|||
describe('Edge Case Tests', () => { |
|||
test('should handle null as input gracefully', () => { |
|||
const operator = new GenericOperator(GenericOperation.isNull); |
|||
expect(operator.evaluate(null, null)).toBe(true); |
|||
}); |
|||
|
|||
test('should throw error for unsupported operator', () => { |
|||
const operator = new GenericOperator('unsupported' as GenericOperation); |
|||
expect(() => operator.evaluate(1, 2)).toThrow('Unsupported generic operator: unsupported'); |
|||
}); |
|||
}); |
|||
}); |
|||
@ -0,0 +1,59 @@ |
|||
import { |
|||
LogicalOperator, |
|||
LogicalOperation, |
|||
} from '../../../../../../src/data_sources/model/conditional_variables/operators/LogicalOperator'; |
|||
|
|||
describe('LogicalOperator', () => { |
|||
describe('Operator: and', () => { |
|||
test('should return true when all statements are true', () => { |
|||
const operator = new LogicalOperator(LogicalOperation.and); |
|||
expect(operator.evaluate([true, true, true])).toBe(true); |
|||
}); |
|||
|
|||
test('should return false when at least one statement is false', () => { |
|||
const operator = new LogicalOperator(LogicalOperation.and); |
|||
expect(operator.evaluate([true, false, true])).toBe(false); |
|||
}); |
|||
}); |
|||
|
|||
describe('Operator: or', () => { |
|||
test('should return true when at least one statement is true', () => { |
|||
const operator = new LogicalOperator(LogicalOperation.or); |
|||
expect(operator.evaluate([false, true, false])).toBe(true); |
|||
}); |
|||
|
|||
test('should return false when all statements are false', () => { |
|||
const operator = new LogicalOperator(LogicalOperation.or); |
|||
expect(operator.evaluate([false, false, false])).toBe(false); |
|||
}); |
|||
}); |
|||
|
|||
describe('Operator: xor', () => { |
|||
test('should return true when exactly one statement is true', () => { |
|||
const operator = new LogicalOperator(LogicalOperation.xor); |
|||
expect(operator.evaluate([true, false, false])).toBe(true); |
|||
}); |
|||
|
|||
test('should return false when more than one statement is true', () => { |
|||
const operator = new LogicalOperator(LogicalOperation.xor); |
|||
expect(operator.evaluate([true, true, false])).toBe(false); |
|||
}); |
|||
|
|||
test('should return false when no statement is true', () => { |
|||
const operator = new LogicalOperator(LogicalOperation.xor); |
|||
expect(operator.evaluate([false, false, false])).toBe(false); |
|||
}); |
|||
}); |
|||
|
|||
describe('Edge Case Tests', () => { |
|||
test('should return false for xor with all false inputs', () => { |
|||
const operator = new LogicalOperator(LogicalOperation.xor); |
|||
expect(operator.evaluate([false, false])).toBe(false); |
|||
}); |
|||
|
|||
test('should throw error for unsupported operator', () => { |
|||
const operator = new LogicalOperator('unsupported' as LogicalOperation); |
|||
expect(() => operator.evaluate([true, false])).toThrow('Unsupported logical operator: unsupported'); |
|||
}); |
|||
}); |
|||
}); |
|||
@ -0,0 +1,95 @@ |
|||
import { |
|||
NumberOperator, |
|||
NumberOperation, |
|||
} from '../../../../../../src/data_sources/model/conditional_variables/operators/NumberOperator'; |
|||
|
|||
describe('NumberOperator', () => { |
|||
describe('Operator: greaterThan', () => { |
|||
test('should return true when left is greater than right', () => { |
|||
const operator = new NumberOperator(NumberOperation.greaterThan); |
|||
expect(operator.evaluate(5, 3)).toBe(true); |
|||
}); |
|||
|
|||
test('should return false when left is not greater than right', () => { |
|||
const operator = new NumberOperator(NumberOperation.greaterThan); |
|||
expect(operator.evaluate(2, 3)).toBe(false); |
|||
}); |
|||
}); |
|||
|
|||
describe('Operator: lessThan', () => { |
|||
test('should return true when left is less than right', () => { |
|||
const operator = new NumberOperator(NumberOperation.lessThan); |
|||
expect(operator.evaluate(2, 3)).toBe(true); |
|||
}); |
|||
|
|||
test('should return false when left is not less than right', () => { |
|||
const operator = new NumberOperator(NumberOperation.lessThan); |
|||
expect(operator.evaluate(5, 3)).toBe(false); |
|||
}); |
|||
}); |
|||
|
|||
describe('Operator: greaterThanOrEqual', () => { |
|||
test('should return true when left is greater than or equal to right', () => { |
|||
const operator = new NumberOperator(NumberOperation.greaterThanOrEqual); |
|||
expect(operator.evaluate(3, 3)).toBe(true); |
|||
}); |
|||
|
|||
test('should return false when left is not greater than or equal to right', () => { |
|||
const operator = new NumberOperator(NumberOperation.greaterThanOrEqual); |
|||
expect(operator.evaluate(2, 3)).toBe(false); |
|||
}); |
|||
}); |
|||
|
|||
describe('Operator: lessThanOrEqual', () => { |
|||
test('should return true when left is less than or equal to right', () => { |
|||
const operator = new NumberOperator(NumberOperation.lessThanOrEqual); |
|||
expect(operator.evaluate(3, 3)).toBe(true); |
|||
}); |
|||
|
|||
test('should return false when left is not less than or equal to right', () => { |
|||
const operator = new NumberOperator(NumberOperation.lessThanOrEqual); |
|||
expect(operator.evaluate(5, 3)).toBe(false); |
|||
}); |
|||
}); |
|||
|
|||
describe('Operator: equals', () => { |
|||
test('should return true when numbers are equal', () => { |
|||
const operator = new NumberOperator(NumberOperation.equals); |
|||
expect(operator.evaluate(4, 4)).toBe(true); |
|||
}); |
|||
|
|||
test('should return false when numbers are not equal', () => { |
|||
const operator = new NumberOperator(NumberOperation.equals); |
|||
expect(operator.evaluate(4, 5)).toBe(false); |
|||
}); |
|||
}); |
|||
|
|||
describe('Operator: notEquals', () => { |
|||
test('should return true when numbers are not equal', () => { |
|||
const operator = new NumberOperator(NumberOperation.notEquals); |
|||
expect(operator.evaluate(4, 5)).toBe(true); |
|||
}); |
|||
|
|||
test('should return false when numbers are equal', () => { |
|||
const operator = new NumberOperator(NumberOperation.notEquals); |
|||
expect(operator.evaluate(4, 4)).toBe(false); |
|||
}); |
|||
}); |
|||
|
|||
describe('Edge Case Tests', () => { |
|||
test('should handle boundary values correctly', () => { |
|||
const operator = new NumberOperator(NumberOperation.lessThan); |
|||
expect(operator.evaluate(Number.MIN_VALUE, 1)).toBe(true); |
|||
}); |
|||
|
|||
test('should return false for NaN comparisons', () => { |
|||
const operator = new NumberOperator(NumberOperation.equals); |
|||
expect(operator.evaluate(NaN, NaN)).toBe(false); |
|||
}); |
|||
|
|||
test('should throw error for unsupported operator', () => { |
|||
const operator = new NumberOperator('unsupported' as NumberOperation); |
|||
expect(() => operator.evaluate(1, 2)).toThrow('Unsupported number operator: unsupported'); |
|||
}); |
|||
}); |
|||
}); |
|||
@ -0,0 +1,81 @@ |
|||
import { |
|||
StringOperator, |
|||
StringOperation, |
|||
} from '../../../../../../src/data_sources/model/conditional_variables/operators/StringOperations'; |
|||
|
|||
describe('StringOperator', () => { |
|||
describe('Operator: contains', () => { |
|||
test('should return true when left contains right', () => { |
|||
const operator = new StringOperator(StringOperation.contains); |
|||
expect(operator.evaluate('hello world', 'world')).toBe(true); |
|||
}); |
|||
|
|||
test('should return false when left does not contain right', () => { |
|||
const operator = new StringOperator(StringOperation.contains); |
|||
expect(operator.evaluate('hello world', 'moon')).toBe(false); |
|||
}); |
|||
}); |
|||
|
|||
describe('Operator: startsWith', () => { |
|||
test('should return true when left starts with right', () => { |
|||
const operator = new StringOperator(StringOperation.startsWith); |
|||
expect(operator.evaluate('hello world', 'hello')).toBe(true); |
|||
}); |
|||
|
|||
test('should return false when left does not start with right', () => { |
|||
const operator = new StringOperator(StringOperation.startsWith); |
|||
expect(operator.evaluate('hello world', 'world')).toBe(false); |
|||
}); |
|||
}); |
|||
|
|||
describe('Operator: endsWith', () => { |
|||
test('should return true when left ends with right', () => { |
|||
const operator = new StringOperator(StringOperation.endsWith); |
|||
expect(operator.evaluate('hello world', 'world')).toBe(true); |
|||
}); |
|||
|
|||
test('should return false when left does not end with right', () => { |
|||
const operator = new StringOperator(StringOperation.endsWith); |
|||
expect(operator.evaluate('hello world', 'hello')).toBe(false); |
|||
}); |
|||
}); |
|||
|
|||
describe('Operator: matchesRegex', () => { |
|||
test('should return true when left matches the regex right', () => { |
|||
const operator = new StringOperator(StringOperation.matchesRegex); |
|||
expect(operator.evaluate('hello world', '^hello')).toBe(true); |
|||
}); |
|||
|
|||
test('should return false when left does not match the regex right', () => { |
|||
const operator = new StringOperator(StringOperation.matchesRegex); |
|||
expect(operator.evaluate('hello world', '^world')).toBe(false); |
|||
}); |
|||
}); |
|||
|
|||
describe('Edge Case Tests', () => { |
|||
test('should return false for contains with empty right string', () => { |
|||
const operator = new StringOperator(StringOperation.contains); |
|||
expect(operator.evaluate('hello world', '')).toBe(true); // Empty string is included in any string
|
|||
}); |
|||
|
|||
test('should return true for startsWith with empty right string', () => { |
|||
const operator = new StringOperator(StringOperation.startsWith); |
|||
expect(operator.evaluate('hello world', '')).toBe(true); // Any string starts with an empty string
|
|||
}); |
|||
|
|||
test('should return true for endsWith with empty right string', () => { |
|||
const operator = new StringOperator(StringOperation.endsWith); |
|||
expect(operator.evaluate('hello world', '')).toBe(true); // Any string ends with an empty string
|
|||
}); |
|||
|
|||
test('should throw error for invalid regex', () => { |
|||
const operator = new StringOperator(StringOperation.matchesRegex); |
|||
expect(() => operator.evaluate('hello world', '[')).toThrow(); |
|||
}); |
|||
|
|||
test('should throw error for unsupported operator', () => { |
|||
const operator = new StringOperator('unsupported' as StringOperation); |
|||
expect(() => operator.evaluate('test', 'test')).toThrow('Unsupported string operator: unsupported'); |
|||
}); |
|||
}); |
|||
}); |
|||
Loading…
Reference in new issue