expression-language
Version:
Javascript implementation of symfony/expression-language
103 lines (100 loc) • 5.38 kB
JavaScript
;
var _Lexer = require("../Lexer");
var _TokenStream = require("../TokenStream");
test('current/last getters track position', () => {
let stream = (0, _Lexer.tokenize)("1");
expect(stream.current.type).toBe(_TokenStream.Token.NUMBER_TYPE);
expect(stream.current.value).toBe(1);
expect(stream.last).toBeUndefined();
stream.next();
expect(stream.current.type).toBe(_TokenStream.Token.EOF_TYPE);
expect(stream.last.type).toBe(_TokenStream.Token.NUMBER_TYPE);
});
test('next throws a SyntaxError at the end of the token stream', () => {
let stream = (0, _Lexer.tokenize)("1");
stream.next(); // now at EOF
expect(() => stream.next()).toThrow('Unexpected end of expression');
});
test('expect advances past a matching token', () => {
let stream = (0, _Lexer.tokenize)("1");
expect(() => stream.expect(_TokenStream.Token.NUMBER_TYPE, null)).not.toThrow();
expect(stream.current.type).toBe(_TokenStream.Token.EOF_TYPE);
});
test('expect throws a SyntaxError describing the mismatch, with an optional message and value', () => {
let stream = (0, _Lexer.tokenize)("1");
expect(() => stream.expect(_TokenStream.Token.STRING_TYPE)).toThrow('Unexpected token "number" of value "1" ("string" expected)');
let stream2 = (0, _Lexer.tokenize)("1");
expect(() => stream2.expect(_TokenStream.Token.STRING_TYPE, null, "Custom context")).toThrow('Custom context. Unexpected token "number" of value "1" ("string" expected)');
let stream3 = (0, _Lexer.tokenize)("1");
expect(() => stream3.expect(_TokenStream.Token.STRING_TYPE, "hello")).toThrow('Unexpected token "number" of value "1" ("string" expected with value "hello")');
});
test('isEOF reflects whether the current token is the EOF token', () => {
let stream = (0, _Lexer.tokenize)("1");
expect(stream.isEOF()).toBe(false);
stream.next();
expect(stream.isEOF()).toBe(true);
});
test('toString joins the tokens using each token\'s own toString', () => {
let stream = (0, _Lexer.tokenize)("1");
expect(stream.toString()).toBe(stream.tokens.map(t => t.toString()).join("\n"));
});
test('isEqualTo is false for null, undefined, or a stream with a different token count', () => {
let stream = (0, _Lexer.tokenize)("1 + 2");
expect(stream.isEqualTo(null)).toBe(false);
expect(stream.isEqualTo(undefined)).toBe(false);
expect(stream.isEqualTo((0, _Lexer.tokenize)("1"))).toBe(false);
});
test('isEqualTo is true for two streams tokenized from the same expression', () => {
let streamA = (0, _Lexer.tokenize)("1 + 2");
let streamB = (0, _Lexer.tokenize)("1 + 2");
expect(streamA.isEqualTo(streamB)).toBe(true);
});
test('isEqualTo is false when any token differs, and restores the compared stream\'s position', () => {
let streamA = (0, _Lexer.tokenize)("1 + 2");
let streamB = (0, _Lexer.tokenize)("1 + 3");
streamB.next(); // move position to make sure it gets restored
expect(streamA.isEqualTo(streamB)).toBe(false);
expect(streamB.position).toBe(1);
});
test('diff returns an empty array for equal streams', () => {
let streamA = (0, _Lexer.tokenize)("1 + 2");
let streamB = (0, _Lexer.tokenize)("1 + 2");
expect(streamA.diff(streamB)).toEqual([]);
});
test('diff reports the index and details of mismatched tokens', () => {
let streamA = (0, _Lexer.tokenize)("1 + 2");
let streamB = (0, _Lexer.tokenize)("1 + 3");
let diff = streamA.diff(streamB);
expect(diff).toHaveLength(1);
expect(diff[0].index).toBe(2);
expect(diff[0].diff.some(line => line.includes('Value'))).toBe(true);
});
test('Token.test matches by type alone or by type and value', () => {
let token = new _TokenStream.Token(_TokenStream.Token.NUMBER_TYPE, 5, 1);
expect(token.test(_TokenStream.Token.NUMBER_TYPE)).toBe(true);
expect(token.test(_TokenStream.Token.NUMBER_TYPE, 5)).toBe(true);
expect(token.test(_TokenStream.Token.NUMBER_TYPE, 6)).toBe(false);
expect(token.test(_TokenStream.Token.STRING_TYPE)).toBe(false);
});
test('Token.toString formats cursor, type, and value', () => {
let token = new _TokenStream.Token(_TokenStream.Token.NUMBER_TYPE, 5, 1);
expect(token.toString()).toBe('1 [number] 5');
});
test('Token.isEqualTo is false for null/undefined and for any mismatched field', () => {
let token = new _TokenStream.Token(_TokenStream.Token.NUMBER_TYPE, 5, 1);
expect(token.isEqualTo(null)).toBe(false);
expect(token.isEqualTo(undefined)).toBe(false);
expect(token.isEqualTo(new _TokenStream.Token(_TokenStream.Token.NUMBER_TYPE, 6, 1))).toBe(false);
expect(token.isEqualTo(new _TokenStream.Token(_TokenStream.Token.STRING_TYPE, 5, 1))).toBe(false);
expect(token.isEqualTo(new _TokenStream.Token(_TokenStream.Token.NUMBER_TYPE, 5, 2))).toBe(false);
expect(token.isEqualTo(new _TokenStream.Token(_TokenStream.Token.NUMBER_TYPE, 5, 1))).toBe(true);
});
test('Token.diff lists each mismatched field and is empty for equal tokens', () => {
let token = new _TokenStream.Token(_TokenStream.Token.NUMBER_TYPE, 5, 1);
expect(token.diff(new _TokenStream.Token(_TokenStream.Token.NUMBER_TYPE, 5, 1))).toEqual([]);
let diff = token.diff(new _TokenStream.Token(_TokenStream.Token.STRING_TYPE, 6, 2));
expect(diff).toHaveLength(3);
expect(diff.some(line => line.startsWith('Value'))).toBe(true);
expect(diff.some(line => line.startsWith('Cursor'))).toBe(true);
expect(diff.some(line => line.startsWith('Type'))).toBe(true);
});