feat: add support for the safe navigation (aka Elvis) operator

fixes #791
This commit is contained in:
Victor Berchet
2015-05-26 10:19:47 +02:00
parent ec2d8cc2c8
commit a9be2ebf1b
11 changed files with 153 additions and 18 deletions

View File

@ -151,7 +151,7 @@ export function main() {
expect(executeWatch('const', '"a\n\nb"')).toEqual(['const=a\n\nb']);
});
it('simple chained property access', () => {
it('should support simple chained property access', () => {
var address = new Address('Grenoble');
var person = new Person('Victor', address);
@ -159,6 +159,18 @@ export function main() {
.toEqual(['address.city=Grenoble']);
});
it('should support the safe navigation operator', () => {
var person = new Person('Victor', null);
expect(executeWatch('city', 'address?.city', person)).toEqual(['city=null']);
expect(executeWatch('city', 'address?.toString()', person)).toEqual(['city=null']);
person.address = new Address('MTV');
expect(executeWatch('city', 'address?.city', person)).toEqual(['city=MTV']);
expect(executeWatch('city', 'address?.toString()', person)).toEqual(['city=MTV']);
});
it("should support method calls", () => {
var person = new Person('Victor');
expect(executeWatch('m', 'sayHi("Jim")', person)).toEqual(['m=Hi, Jim']);
@ -976,7 +988,7 @@ class Address {
city: string;
constructor(city: string) { this.city = city; }
toString(): string { return this.city; }
toString(): string { return isBlank(this.city) ? '-' : this.city }
}
class Uninitialized {

View File

@ -126,14 +126,14 @@ export function main() {
expectIdentifierToken(tokens[1], 8, 'b');
});
it('should tokenize quoted string', function() {
it('should tokenize quoted string', () => {
var str = "['\\'', \"\\\"\"]";
var tokens: List<Token> = lex(str);
expectStringToken(tokens[1], 1, "'");
expectStringToken(tokens[3], 7, '"');
});
it('should tokenize escaped quoted string', function() {
it('should tokenize escaped quoted string', () => {
var str = '"\\"\\n\\f\\r\\t\\v\\u00A0"';
var tokens: List<Token> = lex(str);
expect(tokens.length).toEqual(1);
@ -203,7 +203,7 @@ export function main() {
});
// NOTE(deboer): NOT A LEXER TEST
// it('should tokenize negative number', function() {
// it('should tokenize negative number', () => {
// var tokens:List<Token> = lex("-0.5");
// expectNumberToken(tokens[0], 0, -0.5);
// });
@ -240,6 +240,11 @@ export function main() {
expectOperatorToken(tokens[0], 0, '#');
});
it('should tokenize ?. as operator', () => {
var tokens: List<Token> = lex('?.');
expectOperatorToken(tokens[0], 0, '?.');
});
});
});
}

View File

@ -1,4 +1,4 @@
import {ddescribe, describe, it, xit, iit, expect, beforeEach} from 'angular2/test_lib';
import {ddescribe, describe, it, xit, iit, expect, beforeEach, IS_DARTIUM} from 'angular2/test_lib';
import {BaseException, isBlank, isPresent} from 'angular2/src/facade/lang';
import {reflector} from 'angular2/src/reflection/reflection';
import {MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
@ -202,6 +202,33 @@ export function main() {
});
});
describe('safe navigation operator', () => {
it('should parse field access', () => {
expectEval('a?.a', td(td(999))).toEqual(999);
expectEval('a.a?.a', td(td(td(999)))).toEqual(999);
});
it('should return null when accessing a field on null',
() => { expect(() => { expectEval('null?.a', td()).toEqual(null); }).not.toThrow(); });
it('should have the same priority as .', () => {
expect(() => { expectEval('null?.a.a', td()).toEqual(null); }).toThrowError();
});
if (!IS_DARTIUM) {
it('should return null when accessing a field on undefined', () => {
expect(() => { expectEval('_undefined?.a', td()).toEqual(null); }).not.toThrow();
});
}
it('should evaluate method calls',
() => { expectEval('a?.add(1,2)', td(td())).toEqual(3); });
it('should return null when accessing a method on null', () => {
expect(() => { expectEval('null?.add(1, 2)', td()).toEqual(null); }).not.toThrow();
});
});
describe("method calls", () => {
it("should evaluate method calls", () => {
expectEval("fn()", td(0, 0, "constant")).toEqual("constant");