@pnp/spfx-controls-react
Version:
Reusable React controls for SharePoint Framework solutions
248 lines (233 loc) • 9.87 kB
text/typescript
///<reference types="jest" />
import { assert } from "chai";
import { FormulaEvaluation } from "../../src/common/utilities/FormulaEvaluation";
import { ASTNode, Token } from "../../src/common/utilities/FormulaEvaluation.types";
const formulaEvaluation = new FormulaEvaluation();
const validate = formulaEvaluation.validate.bind(formulaEvaluation);
const tokenize = formulaEvaluation.tokenize.bind(formulaEvaluation);
const shuntingYard = formulaEvaluation.shuntingYard.bind(formulaEvaluation);
const buildAST = formulaEvaluation.buildAST.bind(formulaEvaluation);
const evaluateAST = formulaEvaluation.evaluateASTNode.bind(formulaEvaluation);
const evaluate = formulaEvaluation.evaluate.bind(formulaEvaluation);
describe('SharePoint Functionality', () => {
it('Should get a user image URL from an email address', () => {
const result = evaluate(`getUserImage('user@contoso.com')`);
assert.equal(result, `/_layouts/15/userphoto.aspx?size=L&accountname=user.com`);
});
it('Should get a thumbnail image URL from an image file URL', () => {
const result = evaluate(`getThumbnailImage('https://merseycarenhsuk.sharepoint.com/sites/IM/SiteAssets/CyberAwareness.png')`);
assert.equal(result, `https://merseycarenhsuk.sharepoint.com/sites/IM/SiteAssets/_t/CyberAwareness_png.jpg`);
});
});
describe('Validation', () => {
it('should validate possible expressions', () => {
assert.isTrue(validate("1 + 2"));
assert.isTrue(validate("1 + 2 > 3"));
assert.isTrue(validate("1 + 2 > 3 ? true : false"));
assert.isTrue(validate("if(1 + 2 > 3, true, false)"));
assert.isTrue(validate("toDateString(toDate('2023-09-01'))"));
assert.isFalse(validate("test(abcdef"));
});
});
describe('Algorithms', () => {
describe('Tokenization', () => {
it('should tokenize basic expressions', () => {
const result = tokenize("1 + 2");
assert.deepEqual(result, [
new Token("NUMBER", "1"),
new Token("OPERATOR", "+"),
new Token("NUMBER", "2")
]);
});
});
describe('Shunting Yard', () => {
it('should convert basic expressions to postfix', () => {
const result = shuntingYard(tokenize("1 + 2 > 3"));
assert.deepEqual(result, [
new Token("NUMBER", "1"),
new Token("NUMBER", "2"),
new Token("OPERATOR", "+"),
new Token("NUMBER", "3"),
new Token("OPERATOR", ">"),
]);
});
});
describe('AST Generation', () => {
it('should generate AST for basic expressions', () => {
const tokens = [
new Token("NUMBER", "1"),
new Token("NUMBER", "2"),
new Token("OPERATOR", "+")
];
const ast = buildAST(tokens);
assert.deepEqual(ast, {
type: "OPERATOR",
value: "+",
operands: [
new Token("NUMBER", "1"),
new Token("NUMBER", "2")
]
} as ASTNode);
});
});
describe('Evaluation', () => {
it('should evaluate basic expressions', () => {
const context = {};
const ast: ASTNode = {
type: "OPERATOR",
value: "+",
operands: [
new Token("NUMBER", "1"),
new Token("NUMBER", "2")
]
};
const result = evaluateAST(ast, context);
assert.equal(result, 3);
});
});
});
describe('Basic Expression Evaluation', () => {
it('should evaluate a basic expression correctly obeying BODMAS/PEMDAS', () => {
const result = evaluate("1 + 2 * 3");
assert.equal(result, 7);
});
});
describe('Advanced Expression Evaluation', () => {
it('should evaluate an if expression with basic arithmetic', () => {
const result = evaluate("if(1 + 2 > 3, true, false)");
assert.equal(result, false);
});
it('should evaluate an if expression with basic arithmetic and variables', () => {
const context = { x: 2, y: 2, z: 3 };
const result = evaluate("if([x] + [y] > [z], true, false)", context);
assert.equal(result, true);
});
it('should evaluate a ternary expression with basic arithmetic', () => {
const result = evaluate("1 + 2 > 3 ? true : false");
assert.equal(result, false);
});
it('should evaluate a complex nested expression (1)', () => {
const result = evaluate('((1 + 2 * 3) + 3 > 8 ? 5 : 0) == 5 ? 1 : 0');
assert.equal(result, 1);
});
it('should evaluate a complex nested expression (2)', () => {
const result = evaluate('(cos(1 + 2 * 3) + 3 > 0.5 ? sin(5) : 0) < 0 ? 1 : 0');
assert.equal(result, 1);
});
});
describe('Math Expression Evaluation', () => {
it('Should floor a number', () => {
const result = evaluate("floor(1.5)");
assert.equal(result, 1);
});
it('Should ceil a number', () => {
const result = evaluate("ceiling(1.5)");
assert.equal(result, 2);
});
it('Should raise a number to the power of another', () => {
const result = evaluate("pow(2, 3)");
assert.equal(result, 8);
});
it('Should return the cosine of a number', () => {
const result = evaluate("cos(0.5)");
assert.approximately(result, 0.87758, 0.00001);
});
it('Should return the sine of a number', () => {
const result = evaluate("sin(0.5)");
assert.approximately(result, 0.47942, 0.00001);
});
});
describe('String Expression Evaluation', () => {
it('Should return the index of a substring', () => {
const result = evaluate("indexOf('Hello World', 'World')");
assert.equal(result, 6);
});
it('Should return the last index of a substring', () => {
const result = evaluate("lastIndexOf('Hello World', 'l')");
assert.equal(result, 9);
});
it('Should join an array of strings', () => {
const result = evaluate("join(['Hello', 'World'], ', ')");
assert.equal(result, 'Hello, World');
});
it('Should return a substring', () => {
const result = evaluate("substring('Hello World', 6, 5)");
assert.equal(result, 'World');
});
it('Should return a string in uppercase', () => {
const result = evaluate("toUpperCase('Hello World')");
assert.equal(result, 'HELLO WORLD');
});
it('Should return a string in lowercase', () => {
const result = evaluate("toLowerCase('Hello World')");
assert.equal(result, 'hello world');
});
it('Should determine if a string starts with a substring', () => {
const result = evaluate("startsWith('Hello World', 'Hello')");
assert.isTrue(result);
});
it('Should determine if a string ends with a substring', () => {
const result = evaluate("endsWith('Hello World', 'World')");
assert.isTrue(result);
});
it('Should replace a substring', () => {
const result = evaluate("replace('Hello World', 'World', 'Earth')");
assert.equal(result, 'Hello Earth');
});
it('Should replace all instances of a substring', () => {
const result = evaluate("replaceAll('Hello World', 'l', 'L')");
assert.equal(result, 'HeLLo WorLd');
});
it('Should pad the start of a string with a substring', () => {
const result = evaluate("padStart('Hello World', 15, 'x')");
assert.equal(result, 'xxxxHello World');
});
it('Should pad the end of a string with a substring', () => {
const result = evaluate("padEnd('Hello World', 15, 'x')");
assert.equal(result, 'Hello Worldxxxx');
});
it('Should split a string and return an array', () => {
const result = evaluate("split('Hello World', ' ')");
assert.deepEqual(result, ['Hello', 'World']);
});
});
describe('Date Expression Evaluation', () => {
it('Should convert a string to a date', () => {
const result = evaluate("toDate('2023-09-01')");
assert.equal(result.toISOString(), '2023-09-01T00:00:00.000Z');
});
it('Should convert a date to a date string', () => {
const result = evaluate("toDateString(toDate('2023-09-01'))");
assert.equal(result, 'Fri Sep 01 2023');
});
it('Should get the date component of a date', () => {
const result = evaluate("getDate(toDate('2023-09-01'))");
assert.equal(result, 1);
});
it('Should get the month component of a date', () => {
const result = evaluate("getMonth(toDate('2023-09-01'))");
assert.equal(result, 8);
});
it('Should get the year component of a date', () => {
const result = evaluate("getYear(toDate('2023-09-01'))");
assert.equal(result, 2023);
});
it('Should add days to a date', () => {
const result = evaluate("addDays(toDate('2023-09-01'), 1)");
assert.equal(result.toISOString(), '2023-09-02T00:00:00.000Z');
});
it('Should add minutes to a date', () => {
const result = evaluate("addMinutes(toDate('2023-09-01'), 1)");
assert.equal(result.toISOString(), '2023-09-01T00:01:00.000Z');
});
});
describe('Array Expression Evaluation', () => {
it('Should append an item to an array', () => {
const result = evaluate("appendTo(['Hello', 'World'], 'Earth')");
assert.deepEqual(result, ['Hello', 'World', 'Earth']);
});
it('Should remove an item from an array', () => {
const result = evaluate("removeFrom(['Hello', 'World'], 'World')");
assert.deepEqual(result, ['Hello']);
});
});