import Parser, { mergeDeps } from './parser'; import { ok, failure } from './helper/result'; import { ERROR, ERROR_REF } from './error'; import { sum } from 'valor-app-utils'; import * as R from 'rambda'; describe('Parser', () => { let parser: Parser; beforeEach(() => { parser = new Parser({} as any); }); afterEach(() => (parser = null as any)); describe('.parse()', () => { it('should be defined', () => expect(parser!.parse).toBeInstanceOf(Function)); it('空字符串不应调用parse', () => { parser!.parser.parse = jest.fn(); parser!.parse(''); expect(parser!.parser.parse).not.toHaveBeenCalled(); }); it('parser抛出未知异常时, 将返回`#Error`', () => { parser!.parser.parse = jest.fn(() => { throw new Error('some error'); }); expect(parser!.parse('foo')).toEqual({ error: '#ERROR!', result: null }); }); it('parser抛出`#ERROR`时, 返回`#Error`', () => { parser.parser.parse = jest.fn(() => { throw new Error('#ERROR!'); }); expect(parser!.parse('foo')).toEqual({ error: '#ERROR!', result: null }); }); it('当parser抛出`#DIV/0!`时, 返回`#DIV/0!`', () => { parser.parser.parse = jest.fn(() => { throw new Error('#DIV/0!'); }); expect(parser!.parse('foo')).toEqual({ error: '#DIV/0!', result: null }); }); it('当parser抛出`#NAME?`时, 返回`#NAME?`', () => { parser.parser.parse = jest.fn(() => { throw new Error('#NAME?'); }); expect(parser!.parse('foo')).toEqual({ error: '#NAME?', result: null }); }); it('当parser抛出`#N/A`时, 返回`#N/A`', () => { parser.parser.parse = jest.fn(() => { throw new Error('#N/A'); }); expect(parser!.parse('foo')).toEqual({ error: '#N/A', result: null }); }); it('当parser抛出`#NULL!`时, 返回`#NULL!`', () => { parser.parser.parse = jest.fn(() => { throw new Error('#NULL!'); }); expect(parser!.parse('foo')).toEqual({ error: '#NULL!', result: null }); }); it('当parser抛出`#NUM!`时, 返回`#NUM!`', () => { parser.parser.parse = jest.fn(() => { throw new Error('#NUM!'); }); expect(parser!.parse('foo')).toEqual({ error: '#NUM!', result: null }); }); it('当parser抛出`#REF!`时, 返回`#REF!`', () => { parser.parser.parse = jest.fn(() => { throw new Error('#REF!'); }); expect(parser!.parse('foo')).toEqual({ error: '#REF!', result: null }); }); it('当parser抛出`#VALUE!`时, 返回`#VALUE!`', () => { parser.parser.parse = jest.fn(() => { throw new Error('#VALUE!'); }); expect(parser!.parse('foo')).toEqual({ error: '#VALUE!', result: null }); }); it('当parser抛出`ERROR!`时, 返回`ERROR!`', () => { parser.parser.parse = jest.fn(() => { throw new Error('ERROR'); }); expect(parser!.parse('foo')).toEqual({ error: '#ERROR!', result: null }); }); it('当parser抛出`#ERROR!`时, 返回`#ERROR!`', () => { parser.parser.parse = jest.fn(() => { throw new Error('#ERROR!'); }); expect(parser!.parse('foo')).toEqual({ error: '#ERROR!', result: null }); }); }); describe('.setVariable()/.getVariable()', () => { it('返回缺省值', () => { expect(parser.getVariable('TRUE')).toBe(true); expect(parser.getVariable('FALSE')).toBe(false); expect(parser.getVariable('NULL')).toBe(null); expect(() => parser.getVariable('foo')).toThrow(); }); it('返回定制变量', () => { parser.setVariable('foo', 1234); parser.setVariable('bar', '1234'); parser.setVariable('baz', [1, 2]); expect(parser.getVariable('foo')).toBe(1234); expect(parser.getVariable('bar')).toBe('1234'); expect(parser.getVariable('baz')).toEqual([1, 2]); }); }); describe('.setFunction()/.getFunction()', () => { it('将返回定制函数', () => { parser.setFunction('foo', () => 1234); parser.setFunction('bar', (params: any) => params[0] + params[1]); expect(parser.getFunction('foo')()).toBe(1234); expect(parser.getFunction('bar')([1, 2])).toBe(3); }); }); describe('.基本计算', () => { it('number', () => { expect(parser.parse('1')).toEqual(ok(1)); }); it('string', () => { expect(parser.parse('"abc"')).toEqual(ok('abc')); expect(parser.parse("'abc'")).toEqual(ok('abc')); }); it('=', () => { expect(parser.parse('5=3')).toEqual(ok(false)); expect(parser.parse('3=3')).toEqual(ok(true)); expect(parser.parse('"a"="b"')).toEqual(ok(false)); expect(parser.parse('"a"="a"')).toEqual(ok(true)); }); it('&', () => { expect(parser.parse('3&5')).toEqual(ok('35')); expect(parser.parse('3&5&1')).toEqual(ok('351')); expect(parser.parse('"3"&5&1')).toEqual(ok('351')); }); it('+', () => { expect(parser.parse('3+2')).toEqual(ok(5)); expect(parser.parse('"3"+8')).toEqual(ok(11)); expect(parser.parse('"3"+8+1')).toEqual(ok(12)); }); it('-', () => { expect(parser.parse('3-2')).toEqual(ok(1)); expect(parser.parse('"3"-8-1')).toEqual(ok(-6)); }); it('*', () => { expect(parser.parse('3*2')).toEqual(ok(6)); expect(parser.parse('"3"*8*1')).toEqual(ok(24)); }); it('/', () => { expect(parser.parse('3/2')).toEqual(ok(1.5)); expect(parser.parse('"3"/3/1')).toEqual(ok(1)); }); it('^', () => { expect(parser.parse('3^2')).toEqual(ok(9)); }); it('-号', () => { expect(parser.parse('-3')).toEqual(ok(-3)); expect(parser.parse('-3-3')).toEqual(ok(-6)); }); it('+号', () => { expect(parser.parse('+3')).toEqual(ok(3)); expect(parser.parse('+3+3')).toEqual(ok(6)); }); it('()', () => { expect(parser.parse('(3+3)')).toEqual(ok(6)); expect(parser.parse('5*(3+3)')).toEqual(ok(30)); expect(parser.parse('1+(4+5*(3+3))')).toEqual(ok(35)); }); it('优先级', () => { expect(parser.parse('(3+3)-1')).toEqual(ok(5)); expect(parser.parse('5*(3+3)')).toEqual(ok(30)); expect(parser.parse('5*3+3')).toEqual(ok(18)); expect(parser.parse('5*3+3/2')).toEqual(ok(16.5)); expect(parser.parse('5*3+4*3^2')).toEqual(ok(51)); }); it('>', () => { expect(parser.parse('3>2')).toEqual(ok(true)); expect(parser.parse('"3">"2"')).toEqual(ok(true)); expect(parser.parse('"b">"a"')).toEqual(ok(true)); }); it('>=', () => { expect(parser.parse('3>=2')).toEqual(ok(true)); expect(parser.parse('3>=3')).toEqual(ok(true)); expect(parser.parse('3>=4')).toEqual(ok(false)); expect(parser.parse('"3">="2"')).toEqual(ok(true)); expect(parser.parse('"b">="a"')).toEqual(ok(true)); }); it('<', () => { expect(parser.parse('3<2')).toEqual(ok(false)); expect(parser.parse('"3"<"2"')).toEqual(ok(false)); expect(parser.parse('"b"<"a"')).toEqual(ok(false)); }); it('<=', () => { expect(parser.parse('3<=2')).toEqual(ok(false)); expect(parser.parse('3<=3')).toEqual(ok(true)); expect(parser.parse('3<=4')).toEqual(ok(true)); expect(parser.parse('"3"<="2"')).toEqual(ok(false)); expect(parser.parse('"b"<="a"')).toEqual(ok(false)); }); it('%', () => { expect(parser.parse('3%')).toEqual(ok(0.03)); expect(parser.parse('3%*100')).toEqual(ok(3)); }); it('小数', () => { expect(parser.parse('3.3')).toEqual(ok(3.3)); expect(parser.parse('3.1*5')).toEqual(ok(15.5)); }); }); describe('函数', () => { it('无参函数', () => { parser.setFunction('x1', () => 1); expect(parser.parse('x1()')).toEqual(ok(1)); }); it('单参函数', () => { parser.setFunction('x2', (params: number[]) => { return 1 + params[0]; }); expect(parser.parse('x2(1)')).toEqual(ok(2)); }); it('数组函数', () => { parser.setFunction('x3', (params: number[]) => { return 1 + params[0] + params[1]; }); expect(parser.parse('x3([1,2])')).toEqual(ok(4)); }); it('逗号分隔参数', () => { parser.setFunction('x2', (params: number[]) => { return 1 + params[0] + params[1]; }); expect(parser.parse('x2(1,2)')).toEqual(ok(4)); expect(parser.parse('x2(1;2)')).toEqual(ok(4)); }); }); describe('变量', () => { it('默认变量', () => { parser.setVariable('x', 5); expect(parser.parse('TRUE')).toEqual(ok(true)); expect(parser.parse('FALSE')).toEqual(ok(false)); expect(parser.parse('NULL')).toEqual(ok(null)); expect(parser.parse('true')).toEqual(ok(true)); expect(parser.parse('x')).toEqual(ok(5)); expect(parser.parse('not_exist')).toEqual(failure('#NAME?')); }); it('变量计算', () => { parser.setVariable('x', 5); expect(parser.parse('x+5')).toEqual(ok(10)); }); }); describe('cellvalue', () => { const parser = new Parser({ getCellValue: (_cell: string) => { let result = -1; if (_cell === 'A5') { result = 0; } else if (_cell === '{{id123}}') { result = 1; } else if (_cell === '{{id_123}}') { result = 2; } else if (_cell === '{{总价:小计}}') { result = 3; } else if (_cell === '{{总价}}') { result = 4; } else { throw Error(ERROR_REF); } return { result, error: null, deps: {} }; }, getRangeValue: (args: [string, string]) => { const [_startCell, _endCell] = args; let result = []; if (_startCell === 'A5' && _endCell === '{{id123}}') { result = [0]; } else if (_startCell === '{{id123}}' && _endCell === '{{总价}}') { result = [1]; } else { throw Error(ERROR_REF); } return { result, error: null, deps: {} }; }, }); it('单个cell', () => { expect(parser.parse('A5')).toEqual(ok(0)); expect(parser.parse('{{id123}}')).toEqual(ok(1)); expect(parser.parse('{{id_123}}')).toEqual(ok(2)); expect(parser.parse('{{总价:小计}}')).toEqual(ok(3)); expect(parser.parse('{{总价}}')).toEqual(ok(4)); expect(parser.parse('{{not_exit}}')).toEqual(failure('#REF!')); }); it('cell范围', () => { // range value , 得到的 一定是一个数组 // 比如 sum(a5:a6), 相当于 sum([1,2]) expect(parser.parse('A5:{{id123}}')).toEqual(ok([0])); expect(parser.parse('{{id123}}:{{总价}}')).toEqual(ok([1])); expect(parser.parse('{{id_123}}:{{not_exitst}}')).toEqual(failure('#REF!')); }); }); describe('综合计算', () => { const parser = new Parser({ getRangeValue: ([_cell]: [string, string]) => { return { result: [1], error: null, deps: {} }; }, }); parser.setFunction('sum', (s: number[]) => { return sum(R.flatten(s)) + 1; }); parser.setVariable('k', 1); expect(parser.parse('a5:a8')).toEqual(ok([1])); expect(parser.parse('sum(a5:a8)')).toEqual(ok(2)); expect(parser.parse('100-k*(sum(a5:a8)+33)')).toEqual(ok(65)); }); describe('依赖', () => { const parser = new Parser({ getCellValue: (sCell: string) => { return { result: 1, error: null, deps: { cells: [sCell] } }; }, }); parser.setVariable('kk', 1); // 注意不要考虑a5:a8这样的范围表达式, 估计用不上 expect(parser.parseWithDeps('a5').deps).toEqual({ cells: ['a5'], variables: [] }); expect(parser.parseWithDeps('a5+a5').deps).toEqual({ cells: ['a5'], variables: [] }); expect(parser.parseWithDeps('a5+a5-sum(a6)').deps).toEqual({ cells: ['a5', 'a6'], variables: [], }); expect(parser.parseWithDeps('{{id1}}+a5-sum(a6)').deps).toEqual({ cells: ['a5', 'a6', '{{id1}}'], variables: [], }); expect(parser.parseWithDeps('kk+{{id1}}+a5-sum(a6)').deps).toEqual({ cells: ['a5', 'a6', '{{id1}}'], variables: ['kk'], }); expect(parser.parseWithDeps('kk+kk+{{id1}}+a5-sum(a6)').deps).toEqual({ cells: ['a5', 'a6', '{{id1}}'], variables: ['kk'], }); }); }); describe('mergeDeps', () => { it('case0: 旧deps为空', () => { const o = {} as any; const n = { cells: ['1'], variables: ['2'], }; const expected = { cells: ['1'], variables: ['2'], }; expect(mergeDeps(o, n)).toEqual(expected); }); it('case1: 均有值, 需去重', () => { const o = { cells: ['1'], variables: ['2'], }; const n = { cells: ['1', '3'], variables: ['4'], }; const expected = { cells: ['1', '3'], variables: ['2', '4'], }; expect(mergeDeps(o, n)).toEqual(expected); }); });