| Index: third_party/pkg/angular/test/core/parser/parser_spec.dart
|
| diff --git a/third_party/pkg/angular/test/core/parser/parser_spec.dart b/third_party/pkg/angular/test/core/parser/parser_spec.dart
|
| index 6db4a5b3fc5977aac042de390b8843bea5b6dbfe..0957470a1c33b00849ccae23a74773c5fde68df4 100644
|
| --- a/third_party/pkg/angular/test/core/parser/parser_spec.dart
|
| +++ b/third_party/pkg/angular/test/core/parser/parser_spec.dart
|
| @@ -10,6 +10,8 @@ class TestData {
|
| set str(x) => _str = x;
|
|
|
| method() => "testMethod";
|
| + sub1(a, {b: 0}) => a - b;
|
| + sub2({a: 0, b: 0}) => a - b;
|
| }
|
|
|
| class Ident {
|
| @@ -47,18 +49,20 @@ main() {
|
| describe('parse', () {
|
| Map<String, dynamic> context;
|
| Parser<Expression> parser;
|
| - FilterMap filters;
|
| - beforeEach(module((Module module) {
|
| + FormatterMap formatters;
|
| +
|
| + beforeEachModule((Module module) {
|
| module.type(IncrementFilter);
|
| module.type(SubstringFilter);
|
| - }));
|
| - beforeEach(inject((Parser injectedParser, FilterMap injectedFilters) {
|
| + });
|
| +
|
| + beforeEach((Parser injectedParser, FormatterMap injectedFilters) {
|
| parser = injectedParser;
|
| - filters = injectedFilters;
|
| - }));
|
| + formatters = injectedFilters;
|
| + });
|
|
|
| - eval(String text, [FilterMap f])
|
| - => parser(text).eval(context, f == null ? filters : f);
|
| + eval(String text, [FormatterMap f]) =>
|
| + parser(text).eval(context, f == null ? formatters : f);
|
| expectEval(String expr) => expect(() => eval(expr));
|
|
|
| beforeEach((){ context = {}; });
|
| @@ -154,9 +158,9 @@ main() {
|
| describe('error handling', () {
|
| Parser<Expression> parser;
|
|
|
| - beforeEach(inject((Parser p) {
|
| + beforeEach((Parser p) {
|
| parser = p;
|
| - }));
|
| + });
|
|
|
| // We only care about the error strings in the DynamicParser.
|
| var errStr = (x) {
|
| @@ -190,6 +194,10 @@ main() {
|
| expectEval("4()").toThrow('4 is not a function');
|
| });
|
|
|
| + it("should throw on an unexpected token", (){
|
| + expectEval("[1,2] trac")
|
| + .toThrow('Parser Error: \'trac\' is an unexpected token at column 7 in [[1,2] trac]');
|
| + });
|
|
|
| it('should fail gracefully when invoking non-function', () {
|
| expect(() {
|
| @@ -274,7 +282,8 @@ main() {
|
| context['map'] = {};
|
|
|
| expect(eval('null')).toBe(null);
|
| - expect(eval('map.null')).toBe(null);
|
| + expect(() => eval('map.null'))
|
| + .toThrow("Identifier 'null' is a reserved word.");
|
| });
|
|
|
|
|
| @@ -283,6 +292,20 @@ main() {
|
| });
|
|
|
|
|
| + it('should eval binary operators with null as null', () {
|
| + expect(eval("null < 0")).toEqual(null);
|
| + expect(eval("null * 3")).toEqual(null);
|
| +
|
| + // But + and - are special cases.
|
| + expect(eval("null + 6")).toEqual(6);
|
| + expect(eval("5 + null")).toEqual(5);
|
| + expect(eval("null - 4")).toEqual(-4);
|
| + expect(eval("3 - null")).toEqual(3);
|
| + expect(eval("null + null")).toEqual(0);
|
| + expect(eval("null - null")).toEqual(0);
|
| + });
|
| +
|
| +
|
| it('should pass exceptions through getters', () {
|
| expect(() {
|
| parser('boo').eval(new ScopeWithErrors());
|
| @@ -290,10 +313,13 @@ main() {
|
| });
|
|
|
|
|
| - it('should pass noSuchMethExceptions through getters', () {
|
| + it('should pass noSuchMethodExceptions through getters', () {
|
| expect(() {
|
| parser('getNoSuchMethod').eval(new ScopeWithErrors());
|
| - }).toThrow("iDontExist");
|
| + }).toThrow("null");
|
| + // Dartium throws: The null object does not have a method 'iDontExist'
|
| + // Chrome throws: NullError: Cannot call "iDontExist$0" on null
|
| + // Firefox throws: NullError: null has no properties
|
| });
|
|
|
|
|
| @@ -310,6 +336,7 @@ main() {
|
| }).toThrow("notAProperty");
|
| });
|
|
|
| +
|
| it('should fail on private field access', () {
|
| expect(parser('publicField').eval(new WithPrivateField())).toEqual(4);
|
| // On Dartium, this fails with "NoSuchMethod: no instance getter"
|
| @@ -319,6 +346,26 @@ main() {
|
| parser('_privateField').eval(new WithPrivateField());
|
| }).toThrow();
|
| });
|
| +
|
| +
|
| + it('should only allow identifier or keyword as formatter names', () {
|
| + expect(() => parser('"Foo"|(')).toThrow('identifier or keyword');
|
| + expect(() => parser('"Foo"|1234')).toThrow('identifier or keyword');
|
| + expect(() => parser('"Foo"|"uppercase"')).toThrow('identifier or keyword');
|
| + });
|
| +
|
| +
|
| + it('should only allow identifier or keyword as member names', () {
|
| + expect(() => parser('x.(')).toThrow('identifier or keyword');
|
| + expect(() => parser('x. 1234')).toThrow('identifier or keyword');
|
| + expect(() => parser('x."foo"')).toThrow('identifier or keyword');
|
| + });
|
| +
|
| +
|
| + it('should only allow identifier, string, or keyword as object literal key', () {
|
| + expect(() => parser('{(:0}')).toThrow('expected identifier, keyword, or string');
|
| + expect(() => parser('{1234:0}')).toThrow('expected identifier, keyword, or string');
|
| + });
|
| });
|
|
|
| describe('setters', () {
|
| @@ -397,8 +444,8 @@ main() {
|
| });
|
| });
|
|
|
| - describe('reserved words', () {
|
| - it('should support reserved words in member get access', () {
|
| + xdescribe('reserved words', () {
|
| + iit('should support reserved words in member get access', () {
|
| for (String reserved in RESERVED_WORDS) {
|
| expect(parser("o.$reserved").eval({ 'o': new Object() })).toEqual(null);
|
| expect(parser("o.$reserved").eval({ 'o': { reserved: reserved }})).toEqual(reserved);
|
| @@ -632,16 +679,15 @@ main() {
|
| expect(eval("constN()")).toEqual(123);
|
| });
|
|
|
| +
|
| it('should access a protected keyword on scope', () {
|
| context['const'] = 3;
|
| - expect(eval('const')).toEqual(3);
|
| + expect(eval('this["const"]')).toEqual(3);
|
| });
|
|
|
|
|
| - it('should evaluate function call with arguments', () {
|
| - context["add"] = (a,b) {
|
| - return a+b;
|
| - };
|
| + it('should evaluate scope call with arguments', () {
|
| + context["add"] = (a,b) => a + b;
|
| expect(eval("add(1,2)")).toEqual(3);
|
| });
|
|
|
| @@ -900,6 +946,7 @@ main() {
|
| });
|
| });
|
|
|
| +
|
| describe('assignable', () {
|
| it('should expose assignment function', () {
|
| var fn = parser('a');
|
| @@ -910,6 +957,7 @@ main() {
|
| });
|
| });
|
|
|
| +
|
| describe('locals', () {
|
| it('should expose local variables', () {
|
| expect(parser('a').bind({'a': 6}, ScopeLocals.wrapper)({'a': 1})).toEqual(1);
|
| @@ -925,10 +973,10 @@ main() {
|
| });
|
|
|
|
|
| - it('should work with scopes', inject((Scope scope) {
|
| + it('should work with scopes', (Scope scope) {
|
| scope.context['a'] = {'b': 6};
|
| expect(parser('a.b').bind(scope.context, ScopeLocals.wrapper)({'a': {'b':1}})).toEqual(1);
|
| - }));
|
| + });
|
|
|
| it('should expose assignment function', () {
|
| var fn = parser('a.b');
|
| @@ -942,51 +990,176 @@ main() {
|
| });
|
|
|
|
|
| - describe('filters', () {
|
| - it('should call a filter', () {
|
| - expect(eval("'Foo'|uppercase", filters)).toEqual("FOO");
|
| - expect(eval("'fOo'|uppercase|lowercase", filters)).toEqual("foo");
|
| + describe('named arguments', () {
|
| + it('should be supported for scope calls', () {
|
| + var data = new TestData();
|
| + expect(parser("sub1(1)").eval(data)).toEqual(1);
|
| + expect(parser("sub1(3, b: 2)").eval(data)).toEqual(1);
|
| +
|
| + expect(parser("sub2()").eval(data)).toEqual(0);
|
| + expect(parser("sub2(a: 3)").eval(data)).toEqual(3);
|
| + expect(parser("sub2(a: 3, b: 2)").eval(data)).toEqual(1);
|
| + expect(parser("sub2(b: 4)").eval(data)).toEqual(-4);
|
| + });
|
| +
|
| +
|
| + it('should be supported for scope calls (map)', () {
|
| + context["sub1"] = (a, {b: 0}) => a - b;
|
| + expect(eval("sub1(1)")).toEqual(1);
|
| + expect(eval("sub1(3, b: 2)")).toEqual(1);
|
| +
|
| + context["sub2"] = ({a: 0, b: 0}) => a - b;
|
| + expect(eval("sub2()")).toEqual(0);
|
| + expect(eval("sub2(a: 3)")).toEqual(3);
|
| + expect(eval("sub2(a: 3, b: 2)")).toEqual(1);
|
| + expect(eval("sub2(b: 4)")).toEqual(-4);
|
| + });
|
| +
|
| +
|
| + it('should be supported for member calls', () {
|
| + context['o'] = new TestData();
|
| + expect(eval("o.sub1(1)")).toEqual(1);
|
| + expect(eval("o.sub1(3, b: 2)")).toEqual(1);
|
| +
|
| + expect(eval("o.sub2()")).toEqual(0);
|
| + expect(eval("o.sub2(a: 3)")).toEqual(3);
|
| + expect(eval("o.sub2(a: 3, b: 2)")).toEqual(1);
|
| + expect(eval("o.sub2(b: 4)")).toEqual(-4);
|
| + });
|
| +
|
| +
|
| + it('should be supported for member calls (map)', () {
|
| + context['o'] = {
|
| + 'sub1': (a, {b: 0}) => a - b,
|
| + 'sub2': ({a: 0, b: 0}) => a - b
|
| + };
|
| + expect(eval("o.sub1(1)")).toEqual(1);
|
| + expect(eval("o.sub1(3, b: 2)")).toEqual(1);
|
| +
|
| + expect(eval("o.sub2()")).toEqual(0);
|
| + expect(eval("o.sub2(a: 3)")).toEqual(3);
|
| + expect(eval("o.sub2(a: 3, b: 2)")).toEqual(1);
|
| + expect(eval("o.sub2(b: 4)")).toEqual(-4);
|
| + });
|
| +
|
| +
|
| + it('should be supported for function calls', () {
|
| + context["sub1"] = (a, {b: 0}) => a - b;
|
| + expect(eval("(sub1)(1)")).toEqual(1);
|
| + expect(eval("(sub1)(3, b: 2)")).toEqual(1);
|
| +
|
| + context["sub2"] = ({a: 0, b: 0}) => a - b;
|
| + expect(eval("(sub2)()")).toEqual(0);
|
| + expect(eval("(sub2)(a: 3)")).toEqual(3);
|
| + expect(eval("(sub2)(a: 3, b: 2)")).toEqual(1);
|
| + expect(eval("(sub2)(b: 4)")).toEqual(-4);
|
| + });
|
| +
|
| +
|
| + it('should be an error to use the same name twice', () {
|
| + expect(() => parser('foo(a: 0, a: 1)')).toThrow("Duplicate argument named 'a' at column 11");
|
| + expect(() => parser('foo(a: 0, b: 1, a: 2)')).toThrow("Duplicate argument named 'a' at column 17");
|
| + expect(() => parser('foo(0, a: 1, a: 2)')).toThrow("Duplicate argument named 'a' at column 14");
|
| + expect(() => parser('foo(0, a: 1, b: 2, a: 3)')).toThrow("Duplicate argument named 'a' at column 20");
|
| + });
|
| +
|
| +
|
| + it('should be an error to use Dart reserved words as names', () {
|
| + expect(() => parser('foo(if: 0)')).toThrow("Cannot use Dart reserved word 'if' as named argument at column 5");
|
| + expect(() => parser('foo(a: 0, class: 0)')).toThrow("Cannot use Dart reserved word 'class' as named argument at column 11");
|
| + });
|
| +
|
| +
|
| + it('should pretty print scope calls correctly', () {
|
| + expect(parser('foo(a: 0)').toString()).toEqual('foo(a: 0)');
|
| + expect(parser('foo(a: 0, b: 1)').toString()).toEqual('foo(a: 0, b: 1)');
|
| + expect(parser('foo(b: 1, a: 0)').toString()).toEqual('foo(b: 1, a: 0)');
|
| +
|
| + expect(parser('foo(0)').toString()).toEqual('foo(0)');
|
| + expect(parser('foo(0, a: 0)').toString()).toEqual('foo(0, a: 0)');
|
| + expect(parser('foo(0, a: 0, b: 1)').toString()).toEqual('foo(0, a: 0, b: 1)');
|
| + expect(parser('foo(0, b: 1, a: 0)').toString()).toEqual('foo(0, b: 1, a: 0)');
|
| + });
|
| +
|
| +
|
| + it('should pretty print member calls correctly', () {
|
| + expect(parser('o.foo(a: 0)').toString()).toEqual('o.foo(a: 0)');
|
| + expect(parser('o.foo(a: 0, b: 1)').toString()).toEqual('o.foo(a: 0, b: 1)');
|
| + expect(parser('o.foo(b: 1, a: 0)').toString()).toEqual('o.foo(b: 1, a: 0)');
|
| +
|
| + expect(parser('o.foo(0)').toString()).toEqual('o.foo(0)');
|
| + expect(parser('o.foo(0, a: 0)').toString()).toEqual('o.foo(0, a: 0)');
|
| + expect(parser('o.foo(0, a: 0, b: 1)').toString()).toEqual('o.foo(0, a: 0, b: 1)');
|
| + expect(parser('o.foo(0, b: 1, a: 0)').toString()).toEqual('o.foo(0, b: 1, a: 0)');
|
| + });
|
| +
|
| +
|
| + it('should pretty print function calls correctly', () {
|
| + expect(parser('(foo)(a: 0)').toString()).toEqual('(foo)(a: 0)');
|
| + expect(parser('(foo)(a: 0, b: 1)').toString()).toEqual('(foo)(a: 0, b: 1)');
|
| + expect(parser('(foo)(b: 1, a: 0)').toString()).toEqual('(foo)(b: 1, a: 0)');
|
| +
|
| + expect(parser('(foo)(0)').toString()).toEqual('(foo)(0)');
|
| + expect(parser('(foo)(0, a: 0)').toString()).toEqual('(foo)(0, a: 0)');
|
| + expect(parser('(foo)(0, a: 0, b: 1)').toString()).toEqual('(foo)(0, a: 0, b: 1)');
|
| + expect(parser('(foo)(0, b: 1, a: 0)').toString()).toEqual('(foo)(0, b: 1, a: 0)');
|
| + });
|
| + });
|
| +
|
| +
|
| + describe('formatters', () {
|
| + it('should call a formatter', () {
|
| + expect(eval("'Foo'|uppercase", formatters)).toEqual("FOO");
|
| + // Re-enable after static parser is removed
|
| + //expect(eval("'f' + ('o'|uppercase) + 'o'", formatters)).toEqual("fOo");
|
| + expect(eval("'fOo'|uppercase|lowercase", formatters)).toEqual("foo");
|
| + });
|
| +
|
| + it('should call a formatter with arguments', () {
|
| + expect(eval("1|increment:2", formatters)).toEqual(3);
|
| });
|
|
|
| - it('should call a filter with arguments', () {
|
| - expect(eval("1|increment:2", filters)).toEqual(3);
|
| + it('should evaluate grouped formatters', () {
|
| + context = {'name': 'MISKO'};
|
| + expect(eval('n = (name|lowercase)', formatters)).toEqual('misko');
|
| + expect(eval('n')).toEqual('misko');
|
| });
|
|
|
| - it('should parse filters', () {
|
| + it('should parse formatters', () {
|
| expect(() {
|
| eval("1|nonexistent");
|
| - }).toThrow('No NgFilter: nonexistent found!');
|
| + }).toThrow('No Formatter: nonexistent found!');
|
| expect(() {
|
| - eval("1|nonexistent", filters);
|
| - }).toThrow('No NgFilter: nonexistent found!');
|
| + eval("1|nonexistent", formatters);
|
| + }).toThrow('No Formatter: nonexistent found!');
|
|
|
| context['offset'] = 3;
|
| expect(eval("'abcd'|substring:1:offset")).toEqual("bc");
|
| expect(eval("'abcd'|substring:1:3|uppercase")).toEqual("BC");
|
| });
|
|
|
| - it('should only use filters that are passed as an argument', inject((Injector injector) {
|
| + it('should only use formatters that are passed as an argument', (Injector injector) {
|
| var expression = parser("'World'|hello");
|
| expect(() {
|
| - expression.eval({}, filters);
|
| - }).toThrow('No NgFilter: hello found!');
|
| + expression.eval({}, formatters);
|
| + }).toThrow('No Formatter: hello found!');
|
|
|
| var module = new Module()
|
| ..type(HelloFilter);
|
| var childInjector = injector.createChild([module],
|
| - forceNewInstances: [FilterMap]);
|
| - var newFilters = childInjector.get(FilterMap);
|
| + forceNewInstances: [FormatterMap]);
|
| + var newFilters = childInjector.get(FormatterMap);
|
|
|
| expect(expression.eval({}, newFilters)).toEqual('Hello, World!');
|
| - }));
|
| + });
|
|
|
| - it('should not allow filters in a chain', () {
|
| + it('should not allow formatters in a chain', () {
|
| expect(() {
|
| parser("1;'World'|hello");
|
| - }).toThrow('cannot have a filter in a chain the end of the expression [1;\'World\'|hello]');
|
| + }).toThrow('Cannot have a formatter in a chain the end of the expression [1;\'World\'|hello]');
|
| expect(() {
|
| parser("'World'|hello;1");
|
| - }).toThrow('cannot have a filter in a chain at column 15 in [\'World\'|hello;1]');
|
| + }).toThrow('Cannot have a formatter in a chain at column 15 in [\'World\'|hello;1]');
|
| });
|
| });
|
| });
|
| @@ -1024,19 +1197,19 @@ class ScopeWithErrors {
|
| get getNoSuchMethod => null.iDontExist();
|
| }
|
|
|
| -@NgFilter(name:'increment')
|
| +@Formatter(name:'increment')
|
| class IncrementFilter {
|
| call(a, b) => a + b;
|
| }
|
|
|
| -@NgFilter(name:'substring')
|
| +@Formatter(name:'substring')
|
| class SubstringFilter {
|
| call(String str, startIndex, [endIndex]) {
|
| return str.substring(startIndex, endIndex);
|
| }
|
| }
|
|
|
| -@NgFilter(name:'hello')
|
| +@Formatter(name:'hello')
|
| class HelloFilter {
|
| call(String str) {
|
| return 'Hello, $str!';
|
|
|