@borgar/fx
Version:
Utilities for working with Excel formulas
142 lines (115 loc) • 4.41 kB
text/typescript
import { describe, test, expect } from 'vitest';
import { CONTEXT, FUNCTION, FX_PREFIX, OPERATOR, REF_RANGE, REF_BEAM, REF_NAMED, REF_TERNARY, UNKNOWN } from './constants.ts';
import { mergeRefTokens } from './mergeRefTokens.ts';
import { tokenize } from './tokenize.ts';
describe('mergeRefTokens', () => {
test('merges reference tokens and preserves metadata', () => {
const list = tokenize('=SUM([Wb1]Sheet1!A1:B2)', { mergeRefs: false, withLocation: true });
expect(list).toEqual([
{ type: FX_PREFIX, value: '=', loc: [ 0, 1 ] },
{ type: FUNCTION, value: 'SUM', loc: [ 1, 4 ] },
{ type: OPERATOR, value: '(', loc: [ 4, 5 ] },
{ type: CONTEXT, value: '[Wb1]Sheet1', loc: [ 5, 16 ] },
{ type: OPERATOR, value: '!', loc: [ 16, 17 ] },
{ type: REF_RANGE, value: 'A1', loc: [ 17, 19 ] },
{ type: OPERATOR, value: ':', loc: [ 19, 20 ] },
{ type: REF_RANGE, value: 'B2', loc: [ 20, 22 ] },
{ type: OPERATOR, value: ')', loc: [ 22, 23 ] }
]);
// set IDs on all tokens about to be joined
list[3].id = 'id1';
list[4].id = 'id2';
list[5].id = 'id3';
list[6].id = 'id4';
list[7].id = 'id5';
const mergedList = mergeRefTokens(list);
expect(mergedList).toEqual([
{ type: FX_PREFIX, value: '=', loc: [ 0, 1 ] },
{ type: FUNCTION, value: 'SUM', loc: [ 1, 4 ] },
{ type: OPERATOR, value: '(', loc: [ 4, 5 ] },
{ type: REF_RANGE,
id: 'id5', // token has the id of the first one
value: '[Wb1]Sheet1!A1:B2',
loc: [ 5, 22 ] },
{ type: OPERATOR, value: ')', loc: [ 22, 23 ] }
]);
});
describe('tokenize with mergeRefs enabled', () => {
const opts = { mergeRefs: true, allowTernary: true };
test('basic cell references', () => {
expect(tokenize('A1', opts)).toEqual([
{ type: REF_RANGE, value: 'A1' }
]);
expect(tokenize('A1:A1', opts)).toEqual([
{ type: REF_RANGE, value: 'A1:A1' }
]);
});
test('beam references', () => {
expect(tokenize('A:A', opts)).toEqual([
{ type: REF_BEAM, value: 'A:A' }
]);
});
test('ternary references', () => {
expect(tokenize('A1:A', opts)).toEqual([
{ type: REF_TERNARY, value: 'A1:A' }
]);
});
test('quoted sheet references', () => {
expect(tokenize('\'Sheet1\'!A1', opts)).toEqual([
{ type: REF_RANGE, value: '\'Sheet1\'!A1' }
]);
expect(tokenize('\'Sheet1\'!A:A', opts)).toEqual([
{ type: REF_BEAM, value: '\'Sheet1\'!A:A' }
]);
expect(tokenize('\'Sheet1\'!A1:A', opts)).toEqual([
{ type: REF_TERNARY, value: '\'Sheet1\'!A1:A' }
]);
});
test('unquoted sheet references', () => {
expect(tokenize('Sheet1!A1', opts)).toEqual([
{ type: REF_RANGE, value: 'Sheet1!A1' }
]);
expect(tokenize('Sheet1!A:A', opts)).toEqual([
{ type: REF_BEAM, value: 'Sheet1!A:A' }
]);
expect(tokenize('Sheet1!A1:A', opts)).toEqual([
{ type: REF_TERNARY, value: 'Sheet1!A1:A' }
]);
});
test('workbook references', () => {
expect(tokenize('[WB]Sheet1!A1', opts)).toEqual([
{ type: REF_RANGE, value: '[WB]Sheet1!A1' }
]);
expect(tokenize('[WB]Sheet1!A:A', opts)).toEqual([
{ type: REF_BEAM, value: '[WB]Sheet1!A:A' }
]);
expect(tokenize('[WB]Sheet1!A1:A', opts)).toEqual([
{ type: REF_TERNARY, value: '[WB]Sheet1!A1:A' }
]);
expect(tokenize('[WB]Sheet1!A1.:.C3', opts)).toEqual([
{ type: REF_RANGE, value: '[WB]Sheet1!A1.:.C3' }
]);
});
test('named references', () => {
expect(tokenize('foo', opts)).toEqual([
{ type: REF_NAMED, value: 'foo' }
]);
expect(tokenize('\'quoted\'!foo', opts)).toEqual([
{ type: REF_NAMED, value: '\'quoted\'!foo' }
]);
expect(tokenize('Sheet1!foo', opts)).toEqual([
{ type: REF_NAMED, value: 'Sheet1!foo' }
]);
});
test('path references with different formats', () => {
expect(tokenize('[path]!foo', opts)).toEqual([
{ type: UNKNOWN, value: '[path]' },
{ type: OPERATOR, value: '!' },
{ type: REF_NAMED, value: 'foo' }
]);
expect(tokenize('[path]prefix!foo', opts)).toEqual([
{ type: REF_NAMED, value: '[path]prefix!foo' }
]);
});
});
});