feat: support decorator chaining and class creation in ES5
Closes #2534
This commit is contained in:
@ -0,0 +1,5 @@
|
||||
library angular2.test.core.annotations.decorators_dart_spec;
|
||||
|
||||
main() {
|
||||
// not relavant for dart.
|
||||
}
|
28
modules/angular2/test/core/annotations/decorators_spec.ts
Normal file
28
modules/angular2/test/core/annotations/decorators_spec.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {Component, View, Directive} from 'angular2/angular2';
|
||||
|
||||
export function main() {
|
||||
describe('es5 decorators', () => {
|
||||
it('should declare directive class', () => {
|
||||
var MyDirective = Directive({}).Class({constructor: function() { this.works = true; }});
|
||||
expect(new MyDirective().works).toEqual(true);
|
||||
});
|
||||
|
||||
it('should declare Component class', () => {
|
||||
var MyComponent =
|
||||
Component({}).View({}).View({}).Class({constructor: function() { this.works = true; }});
|
||||
expect(new MyComponent().works).toEqual(true);
|
||||
});
|
||||
});
|
||||
}
|
@ -451,17 +451,17 @@ function createRenderViewportElementBinder(nestedProtoView) {
|
||||
class MainComponent {
|
||||
}
|
||||
|
||||
@Component()
|
||||
@Component({selector: 'nested'})
|
||||
class NestedComponent {
|
||||
}
|
||||
|
||||
class RecursiveComponent {}
|
||||
|
||||
@Component()
|
||||
@Component({selector: 'some-dynamic'})
|
||||
class SomeDynamicComponentDirective {
|
||||
}
|
||||
|
||||
@Directive()
|
||||
@Directive({selector: 'some'})
|
||||
class SomeDirective {
|
||||
}
|
||||
|
||||
@ -481,7 +481,7 @@ class DirectiveWithProperties {
|
||||
class DirectiveWithBind {
|
||||
}
|
||||
|
||||
@Directive()
|
||||
@Directive({selector: 'directive-with-accts'})
|
||||
class DirectiveWithAttributes {
|
||||
constructor(@Attribute('someAttr') someAttr: String) {}
|
||||
}
|
||||
|
@ -21,4 +21,4 @@ export function paramDecorator(value) {
|
||||
}
|
||||
|
||||
export var ClassDecorator = makeDecorator(ClassDecoratorImpl);
|
||||
export var ParamDecorator = makeParamDecorator(ParamDecoratorImpl);
|
||||
export var ParamDecorator = makeParamDecorator(ParamDecoratorImpl);
|
||||
|
5
modules/angular2/test/util/decorators_spec.dart
Normal file
5
modules/angular2/test/util/decorators_spec.dart
Normal file
@ -0,0 +1,5 @@
|
||||
library angular2.test.util.decorators_dart_spec;
|
||||
|
||||
main() {
|
||||
// not relavant for dart.
|
||||
}
|
121
modules/angular2/test/util/decorators_spec.ts
Normal file
121
modules/angular2/test/util/decorators_spec.ts
Normal file
@ -0,0 +1,121 @@
|
||||
import {
|
||||
AsyncTestCompleter,
|
||||
beforeEach,
|
||||
ddescribe,
|
||||
describe,
|
||||
expect,
|
||||
iit,
|
||||
inject,
|
||||
it,
|
||||
xit,
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {makeDecorator, makeParamDecorator, Class} from 'angular2/src/util/decorators';
|
||||
import {global} from 'angular2/src/facade/lang';
|
||||
import {Inject} from 'angular2/angular2';
|
||||
import {reflector} from 'angular2/src/reflection/reflection';
|
||||
|
||||
class TestAnnotation {
|
||||
constructor(public arg: any) {}
|
||||
}
|
||||
|
||||
class TerminalAnnotation {
|
||||
terminal = true;
|
||||
}
|
||||
|
||||
export function main() {
|
||||
var Reflect = global.Reflect;
|
||||
|
||||
var TerminalDecorator = makeDecorator(TerminalAnnotation);
|
||||
var TestDecorator = makeDecorator(TestAnnotation, (fn: any) => fn.Terminal = TerminalDecorator);
|
||||
var TestParamDecorator = makeParamDecorator(TestAnnotation);
|
||||
|
||||
describe('decorators', () => {
|
||||
it('shoulld invoke as decorator', () => {
|
||||
function Type(){};
|
||||
TestDecorator({marker: 'WORKS'})(Type);
|
||||
var annotations = Reflect.getMetadata('annotations', Type);
|
||||
expect(annotations[0].arg.marker).toEqual('WORKS');
|
||||
});
|
||||
|
||||
it('should invoke as new', () => {
|
||||
var annotation = new (<any>TestDecorator)({marker: 'WORKS'});
|
||||
expect(annotation instanceof TestAnnotation).toEqual(true);
|
||||
expect(annotation.arg.marker).toEqual('WORKS');
|
||||
});
|
||||
|
||||
it('should invoke as chain', () => {
|
||||
var chain: any = TestDecorator({marker: 'WORKS'});
|
||||
expect(typeof chain.Terminal).toEqual('function');
|
||||
chain = chain.Terminal();
|
||||
expect(chain.annotations[0] instanceof TestAnnotation).toEqual(true);
|
||||
expect(chain.annotations[0].arg.marker).toEqual('WORKS');
|
||||
expect(chain.annotations[1] instanceof TerminalAnnotation).toEqual(true);
|
||||
});
|
||||
|
||||
describe('Class', () => {
|
||||
it('should create a class', () => {
|
||||
var i0, i1;
|
||||
var MyClass = Class({
|
||||
extends: Class({
|
||||
constructor: function() {},
|
||||
extendWorks: function() { return 'extend ' + this.arg; }
|
||||
}),
|
||||
constructor: [String, function(arg) { this.arg = arg; }],
|
||||
methodA: [i0 = new Inject(String), [i1 = Inject(String), Number], function(a, b) {}],
|
||||
works: function() { return this.arg; },
|
||||
prototype: 'IGNORE'
|
||||
});
|
||||
var obj: any = new MyClass('WORKS');
|
||||
expect(obj.arg).toEqual('WORKS');
|
||||
expect(obj.works()).toEqual('WORKS');
|
||||
expect(obj.extendWorks()).toEqual('extend WORKS');
|
||||
expect(reflector.parameters(MyClass)).toEqual([[String]]);
|
||||
expect(reflector.parameters(obj.methodA)).toEqual([[i0], [i1.annotation, Number]]);
|
||||
|
||||
var proto = (<Function>MyClass).prototype;
|
||||
expect(proto.extends).toEqual(undefined);
|
||||
expect(proto.prototype).toEqual(undefined);
|
||||
});
|
||||
|
||||
describe('errors', () => {
|
||||
it('should ensure that last constructor is required', () => {
|
||||
expect(() => { (<Function>Class)({}); })
|
||||
.toThrowError(
|
||||
"Only Function or Array is supported in Class definition for key 'constructor' is 'undefined'");
|
||||
});
|
||||
|
||||
|
||||
it('should ensure that we dont accidently patch native objects', () => {
|
||||
expect(() => { (<Function>Class)({constructor: Object}); })
|
||||
.toThrowError("Can not use native Object as constructor");
|
||||
});
|
||||
|
||||
|
||||
it('should ensure that last possition is function', () => {
|
||||
expect(() => {Class({constructor: []})})
|
||||
.toThrowError(
|
||||
"Last position of Class method array must be Function in key constructor was 'undefined'");
|
||||
});
|
||||
|
||||
it('should ensure that annotation count matches paramaters count', () => {
|
||||
expect(() => {Class({constructor: [String, function MyType() {}]})})
|
||||
.toThrowError(
|
||||
"Number of annotations (1) does not match number of arguments (0) in the function: MyType");
|
||||
});
|
||||
|
||||
it('should ensure that only Function|Arrays are supported', () => {
|
||||
expect(() => { Class({constructor: function() {}, method: 'non_function'}); })
|
||||
.toThrowError(
|
||||
"Only Function or Array is supported in Class definition for key 'method' is 'non_function'");
|
||||
});
|
||||
|
||||
it('should ensure that extends is a Function', () => {
|
||||
expect(() => {(<Function>Class)({extends: 'non_type', constructor: function() {}})})
|
||||
.toThrowError(
|
||||
"Class definition 'extends' property must be a constructor function was: non_type");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
Reference in New Issue
Block a user