refactor(core): ts’ify tests
This commit is contained in:
parent
23d59df81a
commit
79f564be46
|
@ -114,5 +114,5 @@ export class DomAdapter {
|
||||||
getHistory() { throw _abstract(); }
|
getHistory() { throw _abstract(); }
|
||||||
getLocation() { throw _abstract(); }
|
getLocation() { throw _abstract(); }
|
||||||
getBaseHref() { throw _abstract(); }
|
getBaseHref() { throw _abstract(); }
|
||||||
getUserAgent() { throw _abstract(); }
|
getUserAgent(): string { throw _abstract(); }
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,6 @@ export function el(html: string) {
|
||||||
var _RE_SPECIAL_CHARS = ['-', '[', ']', '/', '{', '}', '\\', '(', ')', '*', '+', '?', '.', '^', '$', '|'];
|
var _RE_SPECIAL_CHARS = ['-', '[', ']', '/', '{', '}', '\\', '(', ')', '*', '+', '?', '.', '^', '$', '|'];
|
||||||
var _ESCAPE_RE = RegExpWrapper.create(`[\\${_RE_SPECIAL_CHARS.join('\\')}]`);
|
var _ESCAPE_RE = RegExpWrapper.create(`[\\${_RE_SPECIAL_CHARS.join('\\')}]`);
|
||||||
export function containsRegexp(input: string): RegExp {
|
export function containsRegexp(input: string): RegExp {
|
||||||
return RegExpWrapper.create(StringWrapper.replaceAllMapped(input, _ESCAPE_RE, (match) => `\\${match[0]}`));
|
return RegExpWrapper.create(
|
||||||
|
StringWrapper.replaceAllMapped(input, _ESCAPE_RE, (match) => `\\${match[0]}`));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,25 +1,21 @@
|
||||||
import {ddescribe, describe, it, iit, expect, beforeEach} from 'angular2/test_lib';
|
import {ddescribe, describe, it, iit, expect, beforeEach} from 'angular2/test_lib';
|
||||||
import {Directive, onChange} from 'angular2/src/core/annotations_impl/annotations';
|
import {Directive, onChange} from 'angular2/src/core/annotations_impl/annotations';
|
||||||
|
|
||||||
class DummyDirective extends Directive {
|
|
||||||
constructor({lifecycle} = {}) { super({lifecycle: lifecycle}); }
|
|
||||||
}
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe("Directive", () => {
|
describe("Directive", () => {
|
||||||
describe("lifecycle", () => {
|
describe("lifecycle", () => {
|
||||||
it("should be false when no lifecycle specified", () => {
|
it("should be false when no lifecycle specified", () => {
|
||||||
var d = new DummyDirective();
|
var d = new Directive();
|
||||||
expect(d.hasLifecycleHook(onChange)).toBe(false);
|
expect(d.hasLifecycleHook(onChange)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be false when the lifecycle does not contain the hook", () => {
|
it("should be false when the lifecycle does not contain the hook", () => {
|
||||||
var d = new DummyDirective({lifecycle:[]});
|
var d = new Directive({lifecycle: []});
|
||||||
expect(d.hasLifecycleHook(onChange)).toBe(false);
|
expect(d.hasLifecycleHook(onChange)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should be true otherwise", () => {
|
it("should be true otherwise", () => {
|
||||||
var d = new DummyDirective({lifecycle:[onChange]});
|
var d = new Directive({lifecycle: [onChange]});
|
||||||
expect(d.hasLifecycleHook(onChange)).toBe(true);
|
expect(d.hasLifecycleHook(onChange)).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
|
@ -1,192 +0,0 @@
|
||||||
import {
|
|
||||||
AsyncTestCompleter,
|
|
||||||
beforeEach,
|
|
||||||
ddescribe,
|
|
||||||
describe,
|
|
||||||
expect,
|
|
||||||
iit,
|
|
||||||
inject,
|
|
||||||
it,
|
|
||||||
xdescribe,
|
|
||||||
xit,
|
|
||||||
} from 'angular2/test_lib';
|
|
||||||
import {bootstrap} from 'angular2/src/core/application';
|
|
||||||
import {Component, Directive} from 'angular2/src/core/annotations_impl/annotations';
|
|
||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
|
||||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
|
||||||
import {PromiseWrapper} from 'angular2/src/facade/async';
|
|
||||||
import {bind} from 'angular2/di';
|
|
||||||
import {Inject} from 'angular2/src/di/annotations_impl';
|
|
||||||
import {View} from 'angular2/src/core/annotations_impl/view';
|
|
||||||
import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle';
|
|
||||||
import {Testability, TestabilityRegistry} from 'angular2/src/core/testability/testability';
|
|
||||||
import {DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
|
|
||||||
|
|
||||||
@Component({selector: 'hello-app'})
|
|
||||||
@View({template: '{{greeting}} world!'})
|
|
||||||
class HelloRootCmp {
|
|
||||||
greeting:string;
|
|
||||||
constructor() {
|
|
||||||
this.greeting = 'hello';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({selector: 'hello-app'})
|
|
||||||
@View({template: 'before: <content></content> after: done'})
|
|
||||||
class HelloRootCmpContent {
|
|
||||||
constructor() { }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({selector: 'hello-app-2'})
|
|
||||||
@View({template: '{{greeting}} world, again!'})
|
|
||||||
class HelloRootCmp2 {
|
|
||||||
greeting:string;
|
|
||||||
constructor() {
|
|
||||||
this.greeting = 'hello';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({selector: 'hello-app'})
|
|
||||||
@View({template: ''})
|
|
||||||
class HelloRootCmp3 {
|
|
||||||
appBinding;
|
|
||||||
|
|
||||||
constructor(@Inject("appBinding") appBinding) {
|
|
||||||
this.appBinding = appBinding;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({selector: 'hello-app'})
|
|
||||||
@View({template: ''})
|
|
||||||
class HelloRootCmp4 {
|
|
||||||
lc;
|
|
||||||
|
|
||||||
constructor(@Inject(LifeCycle) lc) {
|
|
||||||
this.lc = lc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({selector: 'hello-app'})
|
|
||||||
class HelloRootMissingTemplate { }
|
|
||||||
|
|
||||||
@Directive({selector: 'hello-app'})
|
|
||||||
class HelloRootDirectiveIsNotCmp { }
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
var fakeDoc, el, el2, testBindings, lightDom;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
fakeDoc = DOM.createHtmlDocument();
|
|
||||||
el = DOM.createElement('hello-app', fakeDoc);
|
|
||||||
el2 = DOM.createElement('hello-app-2', fakeDoc);
|
|
||||||
lightDom = DOM.createElement('light-dom-el', fakeDoc);
|
|
||||||
DOM.appendChild(fakeDoc.body, el);
|
|
||||||
DOM.appendChild(fakeDoc.body, el2);
|
|
||||||
DOM.appendChild(el, lightDom);
|
|
||||||
DOM.setText(lightDom, 'loading');
|
|
||||||
testBindings = [bind(DOCUMENT_TOKEN).toValue(fakeDoc)];
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('bootstrap factory method', () => {
|
|
||||||
it('should throw if bootstrapped Directive is not a Component', inject([AsyncTestCompleter], (async) => {
|
|
||||||
var refPromise = bootstrap(HelloRootDirectiveIsNotCmp, testBindings, (e,t) => {throw e;});
|
|
||||||
PromiseWrapper.then(refPromise, null, (reason) => {
|
|
||||||
expect(reason.message).toContain(`Could not load 'HelloRootDirectiveIsNotCmp' because it is not a component.`);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should throw if no element is found', inject([AsyncTestCompleter], (async) => {
|
|
||||||
var refPromise = bootstrap(HelloRootCmp, [], (e,t) => {throw e;});
|
|
||||||
PromiseWrapper.then(refPromise, null, (reason) => {
|
|
||||||
expect(reason.message).toContain(
|
|
||||||
'The selector "hello-app" did not match any elements');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should create an injector promise', () => {
|
|
||||||
var refPromise = bootstrap(HelloRootCmp, testBindings);
|
|
||||||
expect(refPromise).not.toBe(null);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should resolve an injector promise and contain bindings', inject([AsyncTestCompleter], (async) => {
|
|
||||||
var refPromise = bootstrap(HelloRootCmp, testBindings);
|
|
||||||
refPromise.then((ref) => {
|
|
||||||
expect(ref.injector.get(HelloRootCmp)).toBeAnInstanceOf(HelloRootCmp);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should provide the application component in the injector', inject([AsyncTestCompleter], (async) => {
|
|
||||||
var refPromise = bootstrap(HelloRootCmp, testBindings);
|
|
||||||
refPromise.then((ref) => {
|
|
||||||
expect(ref.injector.get(HelloRootCmp)).toBeAnInstanceOf(HelloRootCmp);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should display hello world', inject([AsyncTestCompleter], (async) => {
|
|
||||||
var refPromise = bootstrap(HelloRootCmp, testBindings);
|
|
||||||
refPromise.then((ref) => {
|
|
||||||
expect(el).toHaveText('hello world!');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should support multiple calls to bootstrap', inject([AsyncTestCompleter], (async) => {
|
|
||||||
var refPromise1 = bootstrap(HelloRootCmp, testBindings);
|
|
||||||
var refPromise2 = bootstrap(HelloRootCmp2, testBindings);
|
|
||||||
PromiseWrapper.all([refPromise1, refPromise2]).then((refs) => {
|
|
||||||
expect(el).toHaveText('hello world!');
|
|
||||||
expect(el2).toHaveText('hello world, again!');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should make the provided bindings available to the application component", inject([AsyncTestCompleter], (async) => {
|
|
||||||
var refPromise = bootstrap(HelloRootCmp3, [
|
|
||||||
testBindings,
|
|
||||||
bind("appBinding").toValue("BoundValue")
|
|
||||||
]);
|
|
||||||
|
|
||||||
refPromise.then((ref) => {
|
|
||||||
expect(ref.injector.get(HelloRootCmp3).appBinding).toEqual("BoundValue");
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should avoid cyclic dependencies when root component requires Lifecycle through DI", inject([AsyncTestCompleter], (async) => {
|
|
||||||
var refPromise = bootstrap(HelloRootCmp4, testBindings);
|
|
||||||
|
|
||||||
refPromise.then((ref) => {
|
|
||||||
expect(ref.injector.get(HelloRootCmp4).lc).toBe(ref.injector.get(LifeCycle));
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should support shadow dom content tag", inject([AsyncTestCompleter], (async) => {
|
|
||||||
var refPromise = bootstrap(HelloRootCmpContent, testBindings);
|
|
||||||
refPromise.then((ref) => {
|
|
||||||
expect(el).toHaveText('before: loading after: done');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should register each application with the testability registry', inject([AsyncTestCompleter], (async) => {
|
|
||||||
var refPromise1 = bootstrap(HelloRootCmp, testBindings);
|
|
||||||
var refPromise2 = bootstrap(HelloRootCmp2, testBindings);
|
|
||||||
|
|
||||||
PromiseWrapper.all([refPromise1, refPromise2]).then((refs) => {
|
|
||||||
var registry = refs[0].injector.get(TestabilityRegistry);
|
|
||||||
PromiseWrapper.all([
|
|
||||||
refs[0].injector.asyncGet(Testability),
|
|
||||||
refs[1].injector.asyncGet(Testability)]).then((testabilities) => {
|
|
||||||
expect(registry.findTestabilityInTree(el)).toEqual(testabilities[0]);
|
|
||||||
expect(registry.findTestabilityInTree(el2)).toEqual(testabilities[1]);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -0,0 +1,198 @@
|
||||||
|
import {
|
||||||
|
AsyncTestCompleter,
|
||||||
|
beforeEach,
|
||||||
|
ddescribe,
|
||||||
|
describe,
|
||||||
|
expect,
|
||||||
|
iit,
|
||||||
|
inject,
|
||||||
|
it,
|
||||||
|
xdescribe,
|
||||||
|
xit,
|
||||||
|
IS_DARTIUM
|
||||||
|
} from 'angular2/test_lib';
|
||||||
|
import {isPresent, stringify} from 'angular2/src/facade/lang';
|
||||||
|
import {bootstrap} from 'angular2/src/core/application';
|
||||||
|
import {Component, Directive, View} from 'angular2/annotations';
|
||||||
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
|
import {PromiseWrapper} from 'angular2/src/facade/async';
|
||||||
|
import {bind, Inject} from 'angular2/di';
|
||||||
|
import {LifeCycle} from 'angular2/src/core/life_cycle/life_cycle';
|
||||||
|
import {Testability, TestabilityRegistry} from 'angular2/src/core/testability/testability';
|
||||||
|
import {DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
|
||||||
|
|
||||||
|
@Component({selector: 'hello-app'})
|
||||||
|
@View({template: '{{greeting}} world!'})
|
||||||
|
class HelloRootCmp {
|
||||||
|
greeting: string;
|
||||||
|
constructor() { this.greeting = 'hello'; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'hello-app'})
|
||||||
|
@View({template: 'before: <content></content> after: done'})
|
||||||
|
class HelloRootCmpContent {
|
||||||
|
constructor() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'hello-app-2'})
|
||||||
|
@View({template: '{{greeting}} world, again!'})
|
||||||
|
class HelloRootCmp2 {
|
||||||
|
greeting: string;
|
||||||
|
constructor() { this.greeting = 'hello'; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'hello-app'})
|
||||||
|
@View({template: ''})
|
||||||
|
class HelloRootCmp3 {
|
||||||
|
appBinding;
|
||||||
|
|
||||||
|
constructor(@Inject("appBinding") appBinding) { this.appBinding = appBinding; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'hello-app'})
|
||||||
|
@View({template: ''})
|
||||||
|
class HelloRootCmp4 {
|
||||||
|
lc;
|
||||||
|
|
||||||
|
constructor(@Inject(LifeCycle) lc) { this.lc = lc; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'hello-app'})
|
||||||
|
class HelloRootMissingTemplate {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Directive({selector: 'hello-app'})
|
||||||
|
class HelloRootDirectiveIsNotCmp {
|
||||||
|
}
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
var fakeDoc, el, el2, testBindings, lightDom;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fakeDoc = DOM.createHtmlDocument();
|
||||||
|
el = DOM.createElement('hello-app', fakeDoc);
|
||||||
|
el2 = DOM.createElement('hello-app-2', fakeDoc);
|
||||||
|
lightDom = DOM.createElement('light-dom-el', fakeDoc);
|
||||||
|
DOM.appendChild(fakeDoc.body, el);
|
||||||
|
DOM.appendChild(fakeDoc.body, el2);
|
||||||
|
DOM.appendChild(el, lightDom);
|
||||||
|
DOM.setText(lightDom, 'loading');
|
||||||
|
testBindings = [bind(DOCUMENT_TOKEN).toValue(fakeDoc)];
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('bootstrap factory method', () => {
|
||||||
|
it('should throw if bootstrapped Directive is not a Component',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
var refPromise =
|
||||||
|
bootstrap(HelloRootDirectiveIsNotCmp, testBindings, (e, t) => { throw e; });
|
||||||
|
PromiseWrapper.then(refPromise, null, (reason) => {
|
||||||
|
expect(reason.message)
|
||||||
|
.toContain(
|
||||||
|
`Could not load '${stringify(HelloRootDirectiveIsNotCmp)}' because it is not a component.`);
|
||||||
|
async.done();
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should throw if no element is found', inject([AsyncTestCompleter], (async) => {
|
||||||
|
var refPromise = bootstrap(HelloRootCmp, [], (e, t) => { throw e; });
|
||||||
|
PromiseWrapper.then(refPromise, null, (reason) => {
|
||||||
|
expect(reason.message).toContain('The selector "hello-app" did not match any elements');
|
||||||
|
async.done();
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should create an injector promise', () => {
|
||||||
|
var refPromise = bootstrap(HelloRootCmp, testBindings);
|
||||||
|
expect(refPromise).not.toBe(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should resolve an injector promise and contain bindings',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
var refPromise = bootstrap(HelloRootCmp, testBindings);
|
||||||
|
refPromise.then((ref) => {
|
||||||
|
expect(ref.injector.get(HelloRootCmp)).toBeAnInstanceOf(HelloRootCmp);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should provide the application component in the injector',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
var refPromise = bootstrap(HelloRootCmp, testBindings);
|
||||||
|
refPromise.then((ref) => {
|
||||||
|
expect(ref.injector.get(HelloRootCmp)).toBeAnInstanceOf(HelloRootCmp);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should display hello world', inject([AsyncTestCompleter], (async) => {
|
||||||
|
var refPromise = bootstrap(HelloRootCmp, testBindings);
|
||||||
|
refPromise.then((ref) => {
|
||||||
|
expect(el).toHaveText('hello world!');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should support multiple calls to bootstrap', inject([AsyncTestCompleter], (async) => {
|
||||||
|
var refPromise1 = bootstrap(HelloRootCmp, testBindings);
|
||||||
|
var refPromise2 = bootstrap(HelloRootCmp2, testBindings);
|
||||||
|
PromiseWrapper.all([refPromise1, refPromise2])
|
||||||
|
.then((refs) => {
|
||||||
|
expect(el).toHaveText('hello world!');
|
||||||
|
expect(el2).toHaveText('hello world, again!');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it("should make the provided bindings available to the application component",
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
var refPromise =
|
||||||
|
bootstrap(HelloRootCmp3, [testBindings, bind("appBinding").toValue("BoundValue")]);
|
||||||
|
|
||||||
|
refPromise.then((ref) => {
|
||||||
|
expect(ref.injector.get(HelloRootCmp3).appBinding).toEqual("BoundValue");
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it("should avoid cyclic dependencies when root component requires Lifecycle through DI",
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
var refPromise = bootstrap(HelloRootCmp4, testBindings);
|
||||||
|
|
||||||
|
refPromise.then((ref) => {
|
||||||
|
expect(ref.injector.get(HelloRootCmp4).lc).toBe(ref.injector.get(LifeCycle));
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it("should support shadow dom content tag", inject([AsyncTestCompleter], (async) => {
|
||||||
|
var refPromise = bootstrap(HelloRootCmpContent, testBindings);
|
||||||
|
refPromise.then((ref) => {
|
||||||
|
expect(el).toHaveText('before: loading after: done');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should register each application with the testability registry',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
var refPromise1 = bootstrap(HelloRootCmp, testBindings);
|
||||||
|
var refPromise2 = bootstrap(HelloRootCmp2, testBindings);
|
||||||
|
|
||||||
|
PromiseWrapper.all([refPromise1, refPromise2])
|
||||||
|
.then((refs) => {
|
||||||
|
var registry = refs[0].injector.get(TestabilityRegistry);
|
||||||
|
PromiseWrapper.all([
|
||||||
|
refs[0]
|
||||||
|
.injector.asyncGet(Testability),
|
||||||
|
refs[1].injector.asyncGet(Testability)
|
||||||
|
])
|
||||||
|
.then((testabilities) => {
|
||||||
|
expect(registry.findTestabilityInTree(el)).toEqual(testabilities[0]);
|
||||||
|
expect(registry.findTestabilityInTree(el2)).toEqual(testabilities[1]);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,560 +0,0 @@
|
||||||
import {
|
|
||||||
AsyncTestCompleter,
|
|
||||||
beforeEach,
|
|
||||||
xdescribe,
|
|
||||||
ddescribe,
|
|
||||||
describe,
|
|
||||||
el,
|
|
||||||
expect,
|
|
||||||
iit,
|
|
||||||
inject,
|
|
||||||
IS_DARTIUM,
|
|
||||||
it,
|
|
||||||
SpyObject, proxy
|
|
||||||
} from 'angular2/test_lib';
|
|
||||||
|
|
||||||
import {List, ListWrapper, Map, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
|
||||||
import {IMPLEMENTS, Type, isBlank, stringify, isPresent} from 'angular2/src/facade/lang';
|
|
||||||
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
|
||||||
|
|
||||||
import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
|
|
||||||
import {AppProtoView} from 'angular2/src/core/compiler/view';
|
|
||||||
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
|
|
||||||
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
|
||||||
import {Component, Directive} from 'angular2/src/core/annotations_impl/annotations';
|
|
||||||
import {Attribute} from 'angular2/src/core/annotations_impl/di';
|
|
||||||
import {View} from 'angular2/src/core/annotations_impl/view';
|
|
||||||
import {internalProtoView} from 'angular2/src/core/compiler/view_ref';
|
|
||||||
import {DirectiveBinding} from 'angular2/src/core/compiler/element_injector';
|
|
||||||
import {TemplateResolver} from 'angular2/src/core/compiler/template_resolver';
|
|
||||||
import {ComponentUrlMapper, RuntimeComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
|
|
||||||
import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
|
|
||||||
|
|
||||||
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
|
||||||
import * as renderApi from 'angular2/src/render/api';
|
|
||||||
// TODO(tbosch): Spys don't support named modules...
|
|
||||||
import {RenderCompiler} from 'angular2/src/render/api';
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
describe('compiler', function() {
|
|
||||||
var directiveResolver, tplResolver, renderCompiler, protoViewFactory, cmpUrlMapper, renderCompileRequests;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
directiveResolver = new DirectiveResolver();
|
|
||||||
tplResolver = new FakeTemplateResolver();
|
|
||||||
cmpUrlMapper = new RuntimeComponentUrlMapper();
|
|
||||||
renderCompiler = new SpyRenderCompiler();
|
|
||||||
});
|
|
||||||
|
|
||||||
function createCompiler(renderCompileResults:List, protoViewFactoryResults:List<AppProtoView>) {
|
|
||||||
var urlResolver = new FakeUrlResolver();
|
|
||||||
renderCompileRequests = [];
|
|
||||||
renderCompiler.spy('compile').andCallFake( (template) => {
|
|
||||||
ListWrapper.push(renderCompileRequests, template);
|
|
||||||
return PromiseWrapper.resolve(ListWrapper.removeAt(renderCompileResults, 0));
|
|
||||||
});
|
|
||||||
|
|
||||||
protoViewFactory = new FakeProtoViewFactory(protoViewFactoryResults)
|
|
||||||
return new Compiler(
|
|
||||||
directiveResolver,
|
|
||||||
new CompilerCache(),
|
|
||||||
tplResolver,
|
|
||||||
cmpUrlMapper,
|
|
||||||
urlResolver,
|
|
||||||
renderCompiler,
|
|
||||||
protoViewFactory
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('serialize template', () => {
|
|
||||||
|
|
||||||
function captureTemplate(template:View):Promise<renderApi.ViewDefinition> {
|
|
||||||
tplResolver.setView(MainComponent, template);
|
|
||||||
var compiler = createCompiler([createRenderProtoView()], [[createProtoView()]]);
|
|
||||||
return compiler.compile(MainComponent).then( (_) => {
|
|
||||||
expect(renderCompileRequests.length).toBe(1);
|
|
||||||
return renderCompileRequests[0];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function captureDirective(directive):Promise<renderApi.DirectiveMetadata> {
|
|
||||||
return captureTemplate(new View({template: '<div></div>', directives: [directive]})).then( (renderTpl) => {
|
|
||||||
expect(renderTpl.directives.length).toBe(1);
|
|
||||||
return renderTpl.directives[0];
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should fill the componentId', inject([AsyncTestCompleter], (async) => {
|
|
||||||
captureTemplate(new View({template: '<div></div>'})).then( (renderTpl) => {
|
|
||||||
expect(renderTpl.componentId).toEqual(stringify(MainComponent));
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should fill inline template', inject([AsyncTestCompleter], (async) => {
|
|
||||||
captureTemplate(new View({template: '<div></div>'})).then( (renderTpl) => {
|
|
||||||
expect(renderTpl.template).toEqual('<div></div>');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should fill absUrl given inline templates', inject([AsyncTestCompleter], (async) => {
|
|
||||||
cmpUrlMapper.setComponentUrl(MainComponent, '/mainComponent');
|
|
||||||
captureTemplate(new View({template: '<div></div>'})).then( (renderTpl) => {
|
|
||||||
expect(renderTpl.absUrl).toEqual('http://www.app.com/mainComponent');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should not fill absUrl given no inline template or template url', inject([AsyncTestCompleter], (async) => {
|
|
||||||
cmpUrlMapper.setComponentUrl(MainComponent, '/mainComponent');
|
|
||||||
captureTemplate(new View({template: null, templateUrl: null})).then( (renderTpl) => {
|
|
||||||
expect(renderTpl.absUrl).toBe(null)
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should fill absUrl given url template', inject([AsyncTestCompleter], (async) => {
|
|
||||||
cmpUrlMapper.setComponentUrl(MainComponent, '/mainComponent');
|
|
||||||
captureTemplate(new View({templateUrl: '/someTemplate'})).then( (renderTpl) => {
|
|
||||||
expect(renderTpl.absUrl).toEqual('http://www.app.com/mainComponent/someTemplate');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should fill directive.id', inject([AsyncTestCompleter], (async) => {
|
|
||||||
captureDirective(MainComponent).then( (renderDir) => {
|
|
||||||
expect(renderDir.id).toEqual(stringify(MainComponent));
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should fill directive.selector', inject([AsyncTestCompleter], (async) => {
|
|
||||||
captureDirective(MainComponent).then( (renderDir) => {
|
|
||||||
expect(renderDir.selector).toEqual('main-comp');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should fill directive.type for components', inject([AsyncTestCompleter], (async) => {
|
|
||||||
captureDirective(MainComponent).then( (renderDir) => {
|
|
||||||
expect(renderDir.type).toEqual(renderApi.DirectiveMetadata.COMPONENT_TYPE);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should fill directive.type for dynamic components', inject([AsyncTestCompleter], (async) => {
|
|
||||||
captureDirective(SomeDynamicComponentDirective).then( (renderDir) => {
|
|
||||||
expect(renderDir.type).toEqual(renderApi.DirectiveMetadata.COMPONENT_TYPE);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should fill directive.type for decorator directives', inject([AsyncTestCompleter], (async) => {
|
|
||||||
captureDirective(SomeDirective).then( (renderDir) => {
|
|
||||||
expect(renderDir.type).toEqual(renderApi.DirectiveMetadata.DIRECTIVE_TYPE);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should set directive.compileChildren to false for other directives', inject([AsyncTestCompleter], (async) => {
|
|
||||||
captureDirective(MainComponent).then( (renderDir) => {
|
|
||||||
expect(renderDir.compileChildren).toEqual(true);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should set directive.compileChildren to true for decorator directives', inject([AsyncTestCompleter], (async) => {
|
|
||||||
captureDirective(SomeDirective).then( (renderDir) => {
|
|
||||||
expect(renderDir.compileChildren).toEqual(true);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should set directive.compileChildren to false for decorator directives', inject([AsyncTestCompleter], (async) => {
|
|
||||||
captureDirective(IgnoreChildrenDirective).then( (renderDir) => {
|
|
||||||
expect(renderDir.compileChildren).toEqual(false);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should set directive.hostListeners', inject([AsyncTestCompleter], (async) => {
|
|
||||||
captureDirective(DirectiveWithEvents).then( (renderDir) => {
|
|
||||||
expect(renderDir.hostListeners).toEqual(MapWrapper.createFromStringMap({
|
|
||||||
'someEvent': 'someAction'
|
|
||||||
}));
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should set directive.hostProperties', inject([AsyncTestCompleter], (async) => {
|
|
||||||
captureDirective(DirectiveWithProperties).then( (renderDir) => {
|
|
||||||
expect(renderDir.hostProperties).toEqual(MapWrapper.createFromStringMap({
|
|
||||||
'someField': 'someProp'
|
|
||||||
}));
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should set directive.bind', inject([AsyncTestCompleter], (async) => {
|
|
||||||
captureDirective(DirectiveWithBind).then( (renderDir) => {
|
|
||||||
expect(renderDir.properties).toEqual(MapWrapper.createFromStringMap({
|
|
||||||
'a': 'b'
|
|
||||||
}));
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should read @Attribute', inject([AsyncTestCompleter], (async) => {
|
|
||||||
captureDirective(DirectiveWithAttributes).then( (renderDir) => {
|
|
||||||
expect(renderDir.readAttributes).toEqual(['someAttr']);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('call ProtoViewFactory', () => {
|
|
||||||
|
|
||||||
it('should pass the render protoView', inject([AsyncTestCompleter], (async) => {
|
|
||||||
tplResolver.setView(MainComponent, new View({template: '<div></div>'}));
|
|
||||||
var renderProtoView = createRenderProtoView();
|
|
||||||
var expectedProtoView = createProtoView();
|
|
||||||
var compiler = createCompiler([renderProtoView], [[expectedProtoView]]);
|
|
||||||
compiler.compile(MainComponent).then( (_) => {
|
|
||||||
var request = protoViewFactory.requests[0];
|
|
||||||
expect(request[1]).toBe(renderProtoView);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should pass the component binding', inject([AsyncTestCompleter], (async) => {
|
|
||||||
tplResolver.setView(MainComponent, new View({template: '<div></div>'}));
|
|
||||||
var compiler = createCompiler([createRenderProtoView()], [[createProtoView()]]);
|
|
||||||
compiler.compile(MainComponent).then( (_) => {
|
|
||||||
var request = protoViewFactory.requests[0];
|
|
||||||
expect(request[0].key.token).toBe(MainComponent);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should pass the directive bindings', inject([AsyncTestCompleter], (async) => {
|
|
||||||
tplResolver.setView(MainComponent,
|
|
||||||
new View({
|
|
||||||
template: '<div></div>',
|
|
||||||
directives: [SomeDirective]
|
|
||||||
})
|
|
||||||
);
|
|
||||||
var compiler = createCompiler([createRenderProtoView()], [[createProtoView()]]);
|
|
||||||
compiler.compile(MainComponent).then( (_) => {
|
|
||||||
var request = protoViewFactory.requests[0];
|
|
||||||
var binding = request[2][0];
|
|
||||||
expect(binding.key.token).toBe(SomeDirective);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should use the protoView of the ProtoViewFactory', inject([AsyncTestCompleter], (async) => {
|
|
||||||
tplResolver.setView(MainComponent, new View({template: '<div></div>'}));
|
|
||||||
var renderProtoView = createRenderProtoView();
|
|
||||||
var expectedProtoView = createProtoView();
|
|
||||||
var compiler = createCompiler([renderProtoView], [[expectedProtoView]]);
|
|
||||||
compiler.compile(MainComponent).then( (protoViewRef) => {
|
|
||||||
expect(internalProtoView(protoViewRef)).toBe(expectedProtoView);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should load nested components', inject([AsyncTestCompleter], (async) => {
|
|
||||||
tplResolver.setView(MainComponent, new View({template: '<div></div>'}));
|
|
||||||
tplResolver.setView(NestedComponent, new View({template: '<div></div>'}));
|
|
||||||
var mainProtoView = createProtoView([
|
|
||||||
createComponentElementBinder(directiveResolver, NestedComponent)
|
|
||||||
]);
|
|
||||||
var nestedProtoView = createProtoView();
|
|
||||||
var compiler = createCompiler(
|
|
||||||
[
|
|
||||||
createRenderProtoView([createRenderComponentElementBinder(0)]),
|
|
||||||
createRenderProtoView()
|
|
||||||
],
|
|
||||||
[[mainProtoView], [nestedProtoView]]
|
|
||||||
);
|
|
||||||
compiler.compile(MainComponent).then( (protoViewRef) => {
|
|
||||||
expect(internalProtoView(protoViewRef)).toBe(mainProtoView);
|
|
||||||
expect(mainProtoView.elementBinders[0].nestedProtoView).toBe(nestedProtoView);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should load nested components in viewcontainers', inject([AsyncTestCompleter], (async) => {
|
|
||||||
tplResolver.setView(MainComponent, new View({template: '<div></div>'}));
|
|
||||||
tplResolver.setView(NestedComponent, new View({template: '<div></div>'}));
|
|
||||||
var mainProtoView = createProtoView([
|
|
||||||
createViewportElementBinder(null)
|
|
||||||
]);
|
|
||||||
var viewportProtoView = createProtoView([
|
|
||||||
createComponentElementBinder(directiveResolver, NestedComponent)
|
|
||||||
]);
|
|
||||||
var nestedProtoView = createProtoView();
|
|
||||||
var compiler = createCompiler(
|
|
||||||
[
|
|
||||||
createRenderProtoView([
|
|
||||||
createRenderViewportElementBinder(
|
|
||||||
createRenderProtoView([
|
|
||||||
createRenderComponentElementBinder(0)
|
|
||||||
], renderApi.ProtoViewDto.EMBEDDED_VIEW_TYPE)
|
|
||||||
)
|
|
||||||
]),
|
|
||||||
createRenderProtoView()
|
|
||||||
],
|
|
||||||
[[mainProtoView, viewportProtoView], [nestedProtoView]]
|
|
||||||
);
|
|
||||||
compiler.compile(MainComponent).then( (protoViewRef) => {
|
|
||||||
expect(internalProtoView(protoViewRef)).toBe(mainProtoView);
|
|
||||||
expect(viewportProtoView.elementBinders[0].nestedProtoView).toBe(nestedProtoView);
|
|
||||||
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should cache compiled components', inject([AsyncTestCompleter], (async) => {
|
|
||||||
tplResolver.setView(MainComponent, new View({template: '<div></div>'}));
|
|
||||||
var renderProtoView = createRenderProtoView();
|
|
||||||
var expectedProtoView = createProtoView();
|
|
||||||
var compiler = createCompiler([renderProtoView], [[expectedProtoView]]);
|
|
||||||
compiler.compile(MainComponent).then( (protoViewRef) => {
|
|
||||||
expect(internalProtoView(protoViewRef)).toBe(expectedProtoView);
|
|
||||||
return compiler.compile(MainComponent);
|
|
||||||
}).then( (protoViewRef) => {
|
|
||||||
expect(internalProtoView(protoViewRef)).toBe(expectedProtoView);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should re-use components being compiled', inject([AsyncTestCompleter], (async) => {
|
|
||||||
tplResolver.setView(MainComponent, new View({template: '<div></div>'}));
|
|
||||||
var renderProtoViewCompleter = PromiseWrapper.completer();
|
|
||||||
var expectedProtoView = createProtoView();
|
|
||||||
var compiler = createCompiler([renderProtoViewCompleter.promise], [[expectedProtoView]]);
|
|
||||||
renderProtoViewCompleter.resolve(createRenderProtoView());
|
|
||||||
PromiseWrapper.all([
|
|
||||||
compiler.compile(MainComponent),
|
|
||||||
compiler.compile(MainComponent)
|
|
||||||
]).then( (protoViewRefs) => {
|
|
||||||
expect(internalProtoView(protoViewRefs[0])).toBe(expectedProtoView);
|
|
||||||
expect(internalProtoView(protoViewRefs[1])).toBe(expectedProtoView);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should allow recursive components', inject([AsyncTestCompleter], (async) => {
|
|
||||||
tplResolver.setView(MainComponent, new View({template: '<div></div>'}));
|
|
||||||
var mainProtoView = createProtoView([
|
|
||||||
createComponentElementBinder(directiveResolver, MainComponent)
|
|
||||||
]);
|
|
||||||
var compiler = createCompiler(
|
|
||||||
[createRenderProtoView([
|
|
||||||
createRenderComponentElementBinder(0)
|
|
||||||
])],
|
|
||||||
[[mainProtoView]]
|
|
||||||
);
|
|
||||||
compiler.compile(MainComponent).then( (protoViewRef) => {
|
|
||||||
expect(internalProtoView(protoViewRef)).toBe(mainProtoView);
|
|
||||||
expect(mainProtoView.elementBinders[0].nestedProtoView).toBe(mainProtoView);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should create host proto views', inject([AsyncTestCompleter], (async) => {
|
|
||||||
renderCompiler.spy('compileHost').andCallFake( (componentId) => {
|
|
||||||
return PromiseWrapper.resolve(
|
|
||||||
createRenderProtoView([createRenderComponentElementBinder(0)], renderApi.ProtoViewDto.HOST_VIEW_TYPE)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
tplResolver.setView(MainComponent, new View({template: '<div></div>'}));
|
|
||||||
var rootProtoView = createProtoView([
|
|
||||||
createComponentElementBinder(directiveResolver, MainComponent)
|
|
||||||
]);
|
|
||||||
var mainProtoView = createProtoView();
|
|
||||||
var compiler = createCompiler(
|
|
||||||
[
|
|
||||||
createRenderProtoView()
|
|
||||||
],
|
|
||||||
[[rootProtoView], [mainProtoView]]
|
|
||||||
);
|
|
||||||
compiler.compileInHost(MainComponent).then( (protoViewRef) => {
|
|
||||||
expect(internalProtoView(protoViewRef)).toBe(rootProtoView);
|
|
||||||
expect(rootProtoView.elementBinders[0].nestedProtoView).toBe(mainProtoView);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should throw for non component types', () => {
|
|
||||||
var compiler = createCompiler([], []);
|
|
||||||
expect(
|
|
||||||
() => compiler.compile(SomeDirective)
|
|
||||||
).toThrowError(`Could not load '${stringify(SomeDirective)}' because it is not a component.`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function createDirectiveBinding(directiveResolver, type) {
|
|
||||||
var annotation = directiveResolver.resolve(type);
|
|
||||||
return DirectiveBinding.createFromType(type, annotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createProtoView(elementBinders = null) {
|
|
||||||
var pv = new AppProtoView(null, null, MapWrapper.create());
|
|
||||||
if (isBlank(elementBinders)) {
|
|
||||||
elementBinders = [];
|
|
||||||
}
|
|
||||||
pv.elementBinders = elementBinders;
|
|
||||||
return pv;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createComponentElementBinder(directiveResolver, type) {
|
|
||||||
var binding = createDirectiveBinding(directiveResolver, type);
|
|
||||||
return new ElementBinder(
|
|
||||||
0, null, 0,
|
|
||||||
null, binding
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createViewportElementBinder(nestedProtoView) {
|
|
||||||
var elBinder = new ElementBinder(
|
|
||||||
0, null, 0,
|
|
||||||
null, null
|
|
||||||
);
|
|
||||||
elBinder.nestedProtoView = nestedProtoView;
|
|
||||||
return elBinder;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createRenderProtoView(elementBinders = null, type:number = null) {
|
|
||||||
if (isBlank(type)) {
|
|
||||||
type = renderApi.ProtoViewDto.COMPONENT_VIEW_TYPE;
|
|
||||||
}
|
|
||||||
if (isBlank(elementBinders)) {
|
|
||||||
elementBinders = [];
|
|
||||||
}
|
|
||||||
return new renderApi.ProtoViewDto({
|
|
||||||
elementBinders: elementBinders,
|
|
||||||
type: type
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function createRenderComponentElementBinder(directiveIndex) {
|
|
||||||
return new renderApi.ElementBinder({
|
|
||||||
directives: [new renderApi.DirectiveBinder({
|
|
||||||
directiveIndex: directiveIndex
|
|
||||||
})]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function createRenderViewportElementBinder(nestedProtoView) {
|
|
||||||
return new renderApi.ElementBinder({
|
|
||||||
nestedProtoView: nestedProtoView
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'main-comp'
|
|
||||||
})
|
|
||||||
class MainComponent {}
|
|
||||||
|
|
||||||
@Component()
|
|
||||||
class NestedComponent {}
|
|
||||||
|
|
||||||
class RecursiveComponent {}
|
|
||||||
|
|
||||||
@Component()
|
|
||||||
class SomeDynamicComponentDirective {}
|
|
||||||
|
|
||||||
@Directive()
|
|
||||||
class SomeDirective {}
|
|
||||||
|
|
||||||
@Directive({
|
|
||||||
compileChildren: false
|
|
||||||
})
|
|
||||||
class IgnoreChildrenDirective {}
|
|
||||||
|
|
||||||
@Directive({
|
|
||||||
hostListeners: {'someEvent': 'someAction'}
|
|
||||||
})
|
|
||||||
class DirectiveWithEvents {}
|
|
||||||
|
|
||||||
@Directive({
|
|
||||||
hostProperties: {'someField': 'someProp'}
|
|
||||||
})
|
|
||||||
class DirectiveWithProperties {}
|
|
||||||
|
|
||||||
@Directive({
|
|
||||||
properties: {'a': 'b'}
|
|
||||||
})
|
|
||||||
class DirectiveWithBind {}
|
|
||||||
|
|
||||||
@Directive()
|
|
||||||
class DirectiveWithAttributes {
|
|
||||||
constructor(@Attribute('someAttr') someAttr:String) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
@proxy
|
|
||||||
@IMPLEMENTS(RenderCompiler)
|
|
||||||
class SpyRenderCompiler extends SpyObject {
|
|
||||||
constructor(){super(RenderCompiler);}
|
|
||||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
|
||||||
}
|
|
||||||
|
|
||||||
class FakeUrlResolver extends UrlResolver {
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(baseUrl: string, url: string): string {
|
|
||||||
if (baseUrl === null && url == './') {
|
|
||||||
return 'http://www.app.com';
|
|
||||||
}
|
|
||||||
|
|
||||||
return baseUrl + url;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class FakeTemplateResolver extends TemplateResolver {
|
|
||||||
_cmpTemplates: Map;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
this._cmpTemplates = MapWrapper.create();
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(component: Type): View {
|
|
||||||
var template = MapWrapper.get(this._cmpTemplates, component);
|
|
||||||
if (isBlank(template)) {
|
|
||||||
// dynamic component
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return template;
|
|
||||||
}
|
|
||||||
|
|
||||||
setView(component: Type, template: View) {
|
|
||||||
MapWrapper.set(this._cmpTemplates, component, template);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class FakeProtoViewFactory extends ProtoViewFactory {
|
|
||||||
requests:List;
|
|
||||||
_results:List;
|
|
||||||
|
|
||||||
constructor(results) {
|
|
||||||
super(null);
|
|
||||||
this.requests = [];
|
|
||||||
this._results = results;
|
|
||||||
}
|
|
||||||
|
|
||||||
createAppProtoViews(componentBinding:DirectiveBinding, renderProtoView: renderApi.ProtoViewDto,
|
|
||||||
directives:List<DirectiveBinding>):List<AppProtoView> {
|
|
||||||
ListWrapper.push(this.requests, [componentBinding, renderProtoView, directives]);
|
|
||||||
return ListWrapper.removeAt(this._results, 0);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,545 @@
|
||||||
|
import {
|
||||||
|
AsyncTestCompleter,
|
||||||
|
beforeEach,
|
||||||
|
xdescribe,
|
||||||
|
ddescribe,
|
||||||
|
describe,
|
||||||
|
el,
|
||||||
|
expect,
|
||||||
|
iit,
|
||||||
|
inject,
|
||||||
|
IS_DARTIUM,
|
||||||
|
it,
|
||||||
|
SpyObject,
|
||||||
|
proxy
|
||||||
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
|
import {List, ListWrapper, Map, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
import {IMPLEMENTS, Type, isBlank, stringify, isPresent} from 'angular2/src/facade/lang';
|
||||||
|
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||||
|
|
||||||
|
import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
|
||||||
|
import {AppProtoView} from 'angular2/src/core/compiler/view';
|
||||||
|
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
|
||||||
|
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||||
|
import {Attribute, View, Component, Directive} from 'angular2/annotations';
|
||||||
|
import * as viewAnn from 'angular2/src/core/annotations_impl/view';
|
||||||
|
import {internalProtoView} from 'angular2/src/core/compiler/view_ref';
|
||||||
|
import {DirectiveBinding} from 'angular2/src/core/compiler/element_injector';
|
||||||
|
import {TemplateResolver} from 'angular2/src/core/compiler/template_resolver';
|
||||||
|
import {
|
||||||
|
ComponentUrlMapper,
|
||||||
|
RuntimeComponentUrlMapper
|
||||||
|
} from 'angular2/src/core/compiler/component_url_mapper';
|
||||||
|
import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
|
||||||
|
|
||||||
|
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||||
|
import * as renderApi from 'angular2/src/render/api';
|
||||||
|
// TODO(tbosch): Spys don't support named modules...
|
||||||
|
import {RenderCompiler} from 'angular2/src/render/api';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe('compiler', function() {
|
||||||
|
var directiveResolver, tplResolver, renderCompiler, protoViewFactory, cmpUrlMapper,
|
||||||
|
renderCompileRequests;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
directiveResolver = new DirectiveResolver();
|
||||||
|
tplResolver = new FakeTemplateResolver();
|
||||||
|
cmpUrlMapper = new RuntimeComponentUrlMapper();
|
||||||
|
renderCompiler = new SpyRenderCompiler();
|
||||||
|
});
|
||||||
|
|
||||||
|
function createCompiler(renderCompileResults: List<renderApi.ProtoViewDto>,
|
||||||
|
protoViewFactoryResults: List<List<AppProtoView>>) {
|
||||||
|
var urlResolver = new FakeUrlResolver();
|
||||||
|
renderCompileRequests = [];
|
||||||
|
renderCompiler.spy('compile').andCallFake((template) => {
|
||||||
|
ListWrapper.push(renderCompileRequests, template);
|
||||||
|
return PromiseWrapper.resolve(ListWrapper.removeAt(renderCompileResults, 0));
|
||||||
|
});
|
||||||
|
|
||||||
|
protoViewFactory = new FakeProtoViewFactory(protoViewFactoryResults);
|
||||||
|
return new Compiler(directiveResolver, new CompilerCache(), tplResolver, cmpUrlMapper,
|
||||||
|
urlResolver, renderCompiler, protoViewFactory);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('serialize template', () => {
|
||||||
|
|
||||||
|
function captureTemplate(template: viewAnn.View): Promise<renderApi.ViewDefinition> {
|
||||||
|
tplResolver.setView(MainComponent, template);
|
||||||
|
var compiler = createCompiler([createRenderProtoView()], [[createProtoView()]]);
|
||||||
|
return compiler.compile(MainComponent)
|
||||||
|
.then((_) => {
|
||||||
|
expect(renderCompileRequests.length).toBe(1);
|
||||||
|
return renderCompileRequests[0];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function captureDirective(directive): Promise<renderApi.DirectiveMetadata> {
|
||||||
|
return captureTemplate(
|
||||||
|
new viewAnn.View({template: '<div></div>', directives: [directive]}))
|
||||||
|
.then((renderTpl) => {
|
||||||
|
expect(renderTpl.directives.length).toBe(1);
|
||||||
|
return renderTpl.directives[0];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should fill the componentId', inject([AsyncTestCompleter], (async) => {
|
||||||
|
captureTemplate(new viewAnn.View({template: '<div></div>'}))
|
||||||
|
.then((renderTpl) => {
|
||||||
|
expect(renderTpl.componentId).toEqual(stringify(MainComponent));
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should fill inline template', inject([AsyncTestCompleter], (async) => {
|
||||||
|
captureTemplate(new viewAnn.View({template: '<div></div>'}))
|
||||||
|
.then((renderTpl) => {
|
||||||
|
expect(renderTpl.template).toEqual('<div></div>');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should fill absUrl given inline templates', inject([AsyncTestCompleter], (async) => {
|
||||||
|
cmpUrlMapper.setComponentUrl(MainComponent, '/mainComponent');
|
||||||
|
captureTemplate(new viewAnn.View({template: '<div></div>'}))
|
||||||
|
.then((renderTpl) => {
|
||||||
|
expect(renderTpl.absUrl).toEqual('http://www.app.com/mainComponent');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should not fill absUrl given no inline template or template url',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
cmpUrlMapper.setComponentUrl(MainComponent, '/mainComponent');
|
||||||
|
captureTemplate(new viewAnn.View({template: null, templateUrl: null}))
|
||||||
|
.then((renderTpl) => {
|
||||||
|
expect(renderTpl.absUrl).toBe(null);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should fill absUrl given url template', inject([AsyncTestCompleter], (async) => {
|
||||||
|
cmpUrlMapper.setComponentUrl(MainComponent, '/mainComponent');
|
||||||
|
captureTemplate(new viewAnn.View({templateUrl: '/someTemplate'}))
|
||||||
|
.then((renderTpl) => {
|
||||||
|
expect(renderTpl.absUrl).toEqual('http://www.app.com/mainComponent/someTemplate');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should fill directive.id', inject([AsyncTestCompleter], (async) => {
|
||||||
|
captureDirective(MainComponent)
|
||||||
|
.then((renderDir) => {
|
||||||
|
expect(renderDir.id).toEqual(stringify(MainComponent));
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should fill directive.selector', inject([AsyncTestCompleter], (async) => {
|
||||||
|
captureDirective(MainComponent)
|
||||||
|
.then((renderDir) => {
|
||||||
|
expect(renderDir.selector).toEqual('main-comp');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should fill directive.type for components', inject([AsyncTestCompleter], (async) => {
|
||||||
|
captureDirective(MainComponent)
|
||||||
|
.then((renderDir) => {
|
||||||
|
expect(renderDir.type).toEqual(renderApi.DirectiveMetadata.COMPONENT_TYPE);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should fill directive.type for dynamic components',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
captureDirective(SomeDynamicComponentDirective)
|
||||||
|
.then((renderDir) => {
|
||||||
|
expect(renderDir.type).toEqual(renderApi.DirectiveMetadata.COMPONENT_TYPE);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should fill directive.type for decorator directives',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
captureDirective(SomeDirective)
|
||||||
|
.then((renderDir) => {
|
||||||
|
expect(renderDir.type).toEqual(renderApi.DirectiveMetadata.DIRECTIVE_TYPE);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should set directive.compileChildren to false for other directives',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
captureDirective(MainComponent)
|
||||||
|
.then((renderDir) => {
|
||||||
|
expect(renderDir.compileChildren).toEqual(true);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should set directive.compileChildren to true for decorator directives',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
captureDirective(SomeDirective)
|
||||||
|
.then((renderDir) => {
|
||||||
|
expect(renderDir.compileChildren).toEqual(true);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should set directive.compileChildren to false for decorator directives',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
captureDirective(IgnoreChildrenDirective)
|
||||||
|
.then((renderDir) => {
|
||||||
|
expect(renderDir.compileChildren).toEqual(false);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should set directive.hostListeners', inject([AsyncTestCompleter], (async) => {
|
||||||
|
captureDirective(DirectiveWithEvents)
|
||||||
|
.then((renderDir) => {
|
||||||
|
expect(renderDir.hostListeners)
|
||||||
|
.toEqual(MapWrapper.createFromStringMap({'someEvent': 'someAction'}));
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should set directive.hostProperties', inject([AsyncTestCompleter], (async) => {
|
||||||
|
captureDirective(DirectiveWithProperties)
|
||||||
|
.then((renderDir) => {
|
||||||
|
expect(renderDir.hostProperties)
|
||||||
|
.toEqual(MapWrapper.createFromStringMap({'someField': 'someProp'}));
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should set directive.bind', inject([AsyncTestCompleter], (async) => {
|
||||||
|
captureDirective(DirectiveWithBind)
|
||||||
|
.then((renderDir) => {
|
||||||
|
expect(renderDir.properties).toEqual(MapWrapper.createFromStringMap({'a': 'b'}));
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should read @Attribute', inject([AsyncTestCompleter], (async) => {
|
||||||
|
captureDirective(DirectiveWithAttributes)
|
||||||
|
.then((renderDir) => {
|
||||||
|
expect(renderDir.readAttributes).toEqual(['someAttr']);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('call ProtoViewFactory', () => {
|
||||||
|
|
||||||
|
it('should pass the render protoView', inject([AsyncTestCompleter], (async) => {
|
||||||
|
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||||
|
var renderProtoView = createRenderProtoView();
|
||||||
|
var expectedProtoView = createProtoView();
|
||||||
|
var compiler = createCompiler([renderProtoView], [[expectedProtoView]]);
|
||||||
|
compiler.compile(MainComponent)
|
||||||
|
.then((_) => {
|
||||||
|
var request = protoViewFactory.requests[0];
|
||||||
|
expect(request[1]).toBe(renderProtoView);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should pass the component binding', inject([AsyncTestCompleter], (async) => {
|
||||||
|
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||||
|
var compiler = createCompiler([createRenderProtoView()], [[createProtoView()]]);
|
||||||
|
compiler.compile(MainComponent)
|
||||||
|
.then((_) => {
|
||||||
|
var request = protoViewFactory.requests[0];
|
||||||
|
expect(request[0].key.token).toBe(MainComponent);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should pass the directive bindings', inject([AsyncTestCompleter], (async) => {
|
||||||
|
tplResolver.setView(
|
||||||
|
MainComponent,
|
||||||
|
new viewAnn.View({template: '<div></div>', directives: [SomeDirective]}));
|
||||||
|
var compiler = createCompiler([createRenderProtoView()], [[createProtoView()]]);
|
||||||
|
compiler.compile(MainComponent)
|
||||||
|
.then((_) => {
|
||||||
|
var request = protoViewFactory.requests[0];
|
||||||
|
var binding = request[2][0];
|
||||||
|
expect(binding.key.token).toBe(SomeDirective);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should use the protoView of the ProtoViewFactory',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||||
|
var renderProtoView = createRenderProtoView();
|
||||||
|
var expectedProtoView = createProtoView();
|
||||||
|
var compiler = createCompiler([renderProtoView], [[expectedProtoView]]);
|
||||||
|
compiler.compile(MainComponent)
|
||||||
|
.then((protoViewRef) => {
|
||||||
|
expect(internalProtoView(protoViewRef)).toBe(expectedProtoView);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should load nested components', inject([AsyncTestCompleter], (async) => {
|
||||||
|
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||||
|
tplResolver.setView(NestedComponent, new viewAnn.View({template: '<div></div>'}));
|
||||||
|
var mainProtoView =
|
||||||
|
createProtoView([createComponentElementBinder(directiveResolver, NestedComponent)]);
|
||||||
|
var nestedProtoView = createProtoView();
|
||||||
|
var compiler = createCompiler([
|
||||||
|
createRenderProtoView([createRenderComponentElementBinder(0)]),
|
||||||
|
createRenderProtoView()
|
||||||
|
],
|
||||||
|
[[mainProtoView], [nestedProtoView]]);
|
||||||
|
compiler.compile(MainComponent)
|
||||||
|
.then((protoViewRef) => {
|
||||||
|
expect(internalProtoView(protoViewRef)).toBe(mainProtoView);
|
||||||
|
expect(mainProtoView.elementBinders[0].nestedProtoView).toBe(nestedProtoView);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should load nested components in viewcontainers', inject([AsyncTestCompleter], (async) => {
|
||||||
|
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||||
|
tplResolver.setView(NestedComponent, new viewAnn.View({template: '<div></div>'}));
|
||||||
|
var mainProtoView = createProtoView([createViewportElementBinder(null)]);
|
||||||
|
var viewportProtoView =
|
||||||
|
createProtoView([createComponentElementBinder(directiveResolver, NestedComponent)]);
|
||||||
|
var nestedProtoView = createProtoView();
|
||||||
|
var compiler = createCompiler([
|
||||||
|
createRenderProtoView([createRenderViewportElementBinder(
|
||||||
|
createRenderProtoView([createRenderComponentElementBinder(0)],
|
||||||
|
renderApi.ProtoViewDto.EMBEDDED_VIEW_TYPE))]),
|
||||||
|
createRenderProtoView()
|
||||||
|
],
|
||||||
|
[[mainProtoView, viewportProtoView], [nestedProtoView]]);
|
||||||
|
compiler.compile(MainComponent)
|
||||||
|
.then((protoViewRef) => {
|
||||||
|
expect(internalProtoView(protoViewRef)).toBe(mainProtoView);
|
||||||
|
expect(viewportProtoView.elementBinders[0].nestedProtoView).toBe(nestedProtoView);
|
||||||
|
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should cache compiled components', inject([AsyncTestCompleter], (async) => {
|
||||||
|
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||||
|
var renderProtoView = createRenderProtoView();
|
||||||
|
var expectedProtoView = createProtoView();
|
||||||
|
var compiler = createCompiler([renderProtoView], [[expectedProtoView]]);
|
||||||
|
compiler.compile(MainComponent)
|
||||||
|
.then((protoViewRef) =>
|
||||||
|
{
|
||||||
|
expect(internalProtoView(protoViewRef)).toBe(expectedProtoView);
|
||||||
|
return compiler.compile(MainComponent);
|
||||||
|
})
|
||||||
|
.then((protoViewRef) => {
|
||||||
|
expect(internalProtoView(protoViewRef)).toBe(expectedProtoView);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should re-use components being compiled', inject([AsyncTestCompleter], (async) => {
|
||||||
|
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||||
|
var renderProtoViewCompleter = PromiseWrapper.completer();
|
||||||
|
var expectedProtoView = createProtoView();
|
||||||
|
var compiler = createCompiler([renderProtoViewCompleter.promise], [[expectedProtoView]]);
|
||||||
|
renderProtoViewCompleter.resolve(createRenderProtoView());
|
||||||
|
PromiseWrapper.all([compiler.compile(MainComponent), compiler.compile(MainComponent)])
|
||||||
|
.then((protoViewRefs) => {
|
||||||
|
expect(internalProtoView(protoViewRefs[0])).toBe(expectedProtoView);
|
||||||
|
expect(internalProtoView(protoViewRefs[1])).toBe(expectedProtoView);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should allow recursive components', inject([AsyncTestCompleter], (async) => {
|
||||||
|
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||||
|
var mainProtoView =
|
||||||
|
createProtoView([createComponentElementBinder(directiveResolver, MainComponent)]);
|
||||||
|
var compiler = createCompiler(
|
||||||
|
[createRenderProtoView([createRenderComponentElementBinder(0)])], [[mainProtoView]]);
|
||||||
|
compiler.compile(MainComponent)
|
||||||
|
.then((protoViewRef) => {
|
||||||
|
expect(internalProtoView(protoViewRef)).toBe(mainProtoView);
|
||||||
|
expect(mainProtoView.elementBinders[0].nestedProtoView).toBe(mainProtoView);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should create host proto views', inject([AsyncTestCompleter], (async) => {
|
||||||
|
renderCompiler.spy('compileHost')
|
||||||
|
.andCallFake((componentId) => {
|
||||||
|
return PromiseWrapper.resolve(createRenderProtoView(
|
||||||
|
[createRenderComponentElementBinder(0)], renderApi.ProtoViewDto.HOST_VIEW_TYPE));
|
||||||
|
});
|
||||||
|
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||||
|
var rootProtoView =
|
||||||
|
createProtoView([createComponentElementBinder(directiveResolver, MainComponent)]);
|
||||||
|
var mainProtoView = createProtoView();
|
||||||
|
var compiler =
|
||||||
|
createCompiler([createRenderProtoView()], [[rootProtoView], [mainProtoView]]);
|
||||||
|
compiler.compileInHost(MainComponent)
|
||||||
|
.then((protoViewRef) => {
|
||||||
|
expect(internalProtoView(protoViewRef)).toBe(rootProtoView);
|
||||||
|
expect(rootProtoView.elementBinders[0].nestedProtoView).toBe(mainProtoView);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should throw for non component types', () => {
|
||||||
|
var compiler = createCompiler([], []);
|
||||||
|
expect(() => compiler.compile(SomeDirective))
|
||||||
|
.toThrowError(
|
||||||
|
`Could not load '${stringify(SomeDirective)}' because it is not a component.`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createDirectiveBinding(directiveResolver, type) {
|
||||||
|
var annotation = directiveResolver.resolve(type);
|
||||||
|
return DirectiveBinding.createFromType(type, annotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createProtoView(elementBinders = null) {
|
||||||
|
var pv = new AppProtoView(null, null, MapWrapper.create());
|
||||||
|
if (isBlank(elementBinders)) {
|
||||||
|
elementBinders = [];
|
||||||
|
}
|
||||||
|
pv.elementBinders = elementBinders;
|
||||||
|
return pv;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createComponentElementBinder(directiveResolver, type) {
|
||||||
|
var binding = createDirectiveBinding(directiveResolver, type);
|
||||||
|
return new ElementBinder(0, null, 0, null, binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createViewportElementBinder(nestedProtoView) {
|
||||||
|
var elBinder = new ElementBinder(0, null, 0, null, null);
|
||||||
|
elBinder.nestedProtoView = nestedProtoView;
|
||||||
|
return elBinder;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createRenderProtoView(elementBinders = null, type: number = null) {
|
||||||
|
if (isBlank(type)) {
|
||||||
|
type = renderApi.ProtoViewDto.COMPONENT_VIEW_TYPE;
|
||||||
|
}
|
||||||
|
if (isBlank(elementBinders)) {
|
||||||
|
elementBinders = [];
|
||||||
|
}
|
||||||
|
return new renderApi.ProtoViewDto({elementBinders: elementBinders, type: type});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createRenderComponentElementBinder(directiveIndex) {
|
||||||
|
return new renderApi.ElementBinder(
|
||||||
|
{directives: [new renderApi.DirectiveBinder({directiveIndex: directiveIndex})]});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createRenderViewportElementBinder(nestedProtoView) {
|
||||||
|
return new renderApi.ElementBinder({nestedProtoView: nestedProtoView});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'main-comp'})
|
||||||
|
class MainComponent {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component()
|
||||||
|
class NestedComponent {
|
||||||
|
}
|
||||||
|
|
||||||
|
class RecursiveComponent {}
|
||||||
|
|
||||||
|
@Component()
|
||||||
|
class SomeDynamicComponentDirective {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Directive()
|
||||||
|
class SomeDirective {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Directive({compileChildren: false})
|
||||||
|
class IgnoreChildrenDirective {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Directive({hostListeners: {'someEvent': 'someAction'}})
|
||||||
|
class DirectiveWithEvents {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Directive({hostProperties: {'someField': 'someProp'}})
|
||||||
|
class DirectiveWithProperties {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Directive({properties: {'a': 'b'}})
|
||||||
|
class DirectiveWithBind {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Directive()
|
||||||
|
class DirectiveWithAttributes {
|
||||||
|
constructor(@Attribute('someAttr') someAttr: String) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@proxy
|
||||||
|
@IMPLEMENTS(RenderCompiler)
|
||||||
|
class SpyRenderCompiler extends SpyObject {
|
||||||
|
constructor() { super(RenderCompiler); }
|
||||||
|
noSuchMethod(m) { return super.noSuchMethod(m) }
|
||||||
|
}
|
||||||
|
|
||||||
|
class FakeUrlResolver extends UrlResolver {
|
||||||
|
constructor() { super(); }
|
||||||
|
|
||||||
|
resolve(baseUrl: string, url: string): string {
|
||||||
|
if (baseUrl === null && url == './') {
|
||||||
|
return 'http://www.app.com';
|
||||||
|
}
|
||||||
|
|
||||||
|
return baseUrl + url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class FakeTemplateResolver extends TemplateResolver {
|
||||||
|
_cmpTemplates: Map<Type, viewAnn.View>;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this._cmpTemplates = MapWrapper.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(component: Type): viewAnn.View {
|
||||||
|
var template = MapWrapper.get(this._cmpTemplates, component);
|
||||||
|
if (isBlank(template)) {
|
||||||
|
// dynamic component
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
|
setView(component: Type, template: viewAnn.View) {
|
||||||
|
MapWrapper.set(this._cmpTemplates, component, template);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FakeProtoViewFactory extends ProtoViewFactory {
|
||||||
|
requests: List<List<any>>;
|
||||||
|
|
||||||
|
constructor(public results: List<List<AppProtoView>>) {
|
||||||
|
super(null);
|
||||||
|
this.requests = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
createAppProtoViews(componentBinding: DirectiveBinding, renderProtoView: renderApi.ProtoViewDto,
|
||||||
|
directives: List<DirectiveBinding>): List<AppProtoView> {
|
||||||
|
ListWrapper.push(this.requests, [componentBinding, renderProtoView, directives]);
|
||||||
|
return ListWrapper.removeAt(this.results, 0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,30 +1,28 @@
|
||||||
import {ddescribe, describe, it, iit, expect, beforeEach} from 'angular2/test_lib';
|
import {ddescribe, describe, it, iit, expect, beforeEach} from 'angular2/test_lib';
|
||||||
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||||
import {Directive} from 'angular2/src/core/annotations_impl/annotations';
|
import {Directive} from 'angular2/annotations';
|
||||||
|
import * as dirAnn from 'angular2/src/core/annotations_impl/annotations';
|
||||||
|
|
||||||
@Directive({selector: 'someDirective'})
|
@Directive({selector: 'someDirective'})
|
||||||
class SomeDirective {}
|
class SomeDirective {
|
||||||
|
|
||||||
class SomeDirectiveWithoutAnnotation {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SomeDirectiveWithoutAnnotation {}
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe("DirectiveResolver", () => {
|
describe("DirectiveResolver", () => {
|
||||||
var reader;
|
var reader;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => { reader = new DirectiveResolver(); });
|
||||||
reader = new DirectiveResolver();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should read out the Directive annotation', () => {
|
it('should read out the Directive annotation', () => {
|
||||||
var directiveMetadata = reader.resolve(SomeDirective);
|
var directiveMetadata = reader.resolve(SomeDirective);
|
||||||
expect(directiveMetadata).toEqual(new Directive({selector: 'someDirective'}));
|
expect(directiveMetadata).toEqual(new dirAnn.Directive({selector: 'someDirective'}));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if not matching annotation is found', () => {
|
it('should throw if not matching annotation is found', () => {
|
||||||
expect(() => {
|
expect(() => { reader.resolve(SomeDirectiveWithoutAnnotation); })
|
||||||
reader.resolve(SomeDirectiveWithoutAnnotation);
|
.toThrowError('No Directive annotation found on SomeDirectiveWithoutAnnotation');
|
||||||
}).toThrowError('No Directive annotation found on SomeDirectiveWithoutAnnotation');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
|
@ -1,344 +0,0 @@
|
||||||
import {
|
|
||||||
AsyncTestCompleter,
|
|
||||||
beforeEach,
|
|
||||||
ddescribe,
|
|
||||||
xdescribe,
|
|
||||||
describe,
|
|
||||||
el,
|
|
||||||
dispatchEvent,
|
|
||||||
expect,
|
|
||||||
iit,
|
|
||||||
inject,
|
|
||||||
beforeEachBindings,
|
|
||||||
it,
|
|
||||||
xit,
|
|
||||||
viewRootNodes
|
|
||||||
} from 'angular2/test_lib';
|
|
||||||
|
|
||||||
import {TestBed, ViewProxy} from 'angular2/src/test_lib/test_bed';
|
|
||||||
import {Injector} from 'angular2/di';
|
|
||||||
import {Component} from 'angular2/src/core/annotations_impl/annotations';
|
|
||||||
import {View} from 'angular2/src/core/annotations_impl/view';
|
|
||||||
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
|
|
||||||
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
|
|
||||||
import {NgIf} from 'angular2/src/directives/ng_if';
|
|
||||||
import {DomRenderer, DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
|
|
||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
|
||||||
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
describe('DynamicComponentLoader', function () {
|
|
||||||
describe("loading into existing location", () => {
|
|
||||||
it('should work', inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
|
||||||
tb.overrideView(MyComp, new View({
|
|
||||||
template: '<dynamic-comp #dynamic></dynamic-comp>',
|
|
||||||
directives: [DynamicComp]
|
|
||||||
}));
|
|
||||||
|
|
||||||
tb.createView(MyComp).then((view) => {
|
|
||||||
var dynamicComponent = view.rawView.locals.get("dynamic");
|
|
||||||
expect(dynamicComponent).toBeAnInstanceOf(DynamicComp);
|
|
||||||
|
|
||||||
dynamicComponent.done.then((_) => {
|
|
||||||
view.detectChanges();
|
|
||||||
expect(view.rootNodes).toHaveText('hello');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should inject dependencies of the dynamically-loaded component', inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
|
||||||
tb.overrideView(MyComp, new View({
|
|
||||||
template: '<dynamic-comp #dynamic></dynamic-comp>',
|
|
||||||
directives: [DynamicComp]
|
|
||||||
}));
|
|
||||||
|
|
||||||
tb.createView(MyComp).then((view) => {
|
|
||||||
var dynamicComponent = view.rawView.locals.get("dynamic");
|
|
||||||
dynamicComponent.done.then((ref) => {
|
|
||||||
expect(ref.instance.dynamicallyCreatedComponentService).toBeAnInstanceOf(DynamicallyCreatedComponentService);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should allow to destroy and create them via viewcontainer directives',
|
|
||||||
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
|
||||||
tb.overrideView(MyComp, new View({
|
|
||||||
template: '<div><dynamic-comp #dynamic template="ng-if: ctxBoolProp"></dynamic-comp></div>',
|
|
||||||
directives: [DynamicComp, NgIf]
|
|
||||||
}));
|
|
||||||
|
|
||||||
tb.createView(MyComp).then((view) => {
|
|
||||||
view.context.ctxBoolProp = true;
|
|
||||||
view.detectChanges();
|
|
||||||
var dynamicComponent = view.rawView.viewContainers[0].views[0].locals.get("dynamic");
|
|
||||||
dynamicComponent.done.then((_) => {
|
|
||||||
view.detectChanges();
|
|
||||||
expect(view.rootNodes).toHaveText('hello');
|
|
||||||
|
|
||||||
view.context.ctxBoolProp = false;
|
|
||||||
view.detectChanges();
|
|
||||||
|
|
||||||
expect(view.rawView.viewContainers[0].views.length).toBe(0);
|
|
||||||
expect(view.rootNodes).toHaveText('');
|
|
||||||
|
|
||||||
view.context.ctxBoolProp = true;
|
|
||||||
view.detectChanges();
|
|
||||||
|
|
||||||
var dynamicComponent = view.rawView.viewContainers[0].views[0].locals.get("dynamic");
|
|
||||||
return dynamicComponent.done;
|
|
||||||
}).then((_) => {
|
|
||||||
view.detectChanges();
|
|
||||||
expect(view.rootNodes).toHaveText('hello');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("loading next to an existing location", () => {
|
|
||||||
it('should work', inject([DynamicComponentLoader, TestBed, AsyncTestCompleter],
|
|
||||||
(loader, tb, async) => {
|
|
||||||
tb.overrideView(MyComp, new View({
|
|
||||||
template: '<div><location #loc></location></div>',
|
|
||||||
directives: [Location]
|
|
||||||
}));
|
|
||||||
|
|
||||||
tb.createView(MyComp).then((view) => {
|
|
||||||
var location = view.rawView.locals.get("loc");
|
|
||||||
|
|
||||||
loader.loadNextToExistingLocation(DynamicallyLoaded, location.elementRef).then(ref => {
|
|
||||||
expect(view.rootNodes).toHaveText("Location;DynamicallyLoaded;")
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should return a disposable component ref', inject([DynamicComponentLoader, TestBed, AsyncTestCompleter],
|
|
||||||
(loader, tb, async) => {
|
|
||||||
tb.overrideView(MyComp, new View({
|
|
||||||
template: '<div><location #loc></location></div>',
|
|
||||||
directives: [Location]
|
|
||||||
}));
|
|
||||||
|
|
||||||
tb.createView(MyComp).then((view) => {
|
|
||||||
var location = view.rawView.locals.get("loc");
|
|
||||||
loader.loadNextToExistingLocation(DynamicallyLoaded, location.elementRef).then(ref => {
|
|
||||||
loader.loadNextToExistingLocation(DynamicallyLoaded2, location.elementRef).then(ref2 => {
|
|
||||||
expect(view.rootNodes).toHaveText("Location;DynamicallyLoaded;DynamicallyLoaded2;")
|
|
||||||
|
|
||||||
ref2.dispose();
|
|
||||||
|
|
||||||
expect(view.rootNodes).toHaveText("Location;DynamicallyLoaded;")
|
|
||||||
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should update host properties', inject([DynamicComponentLoader, TestBed, AsyncTestCompleter],
|
|
||||||
(loader, tb, async) => {
|
|
||||||
tb.overrideView(MyComp, new View({
|
|
||||||
template: '<div><location #loc></location></div>',
|
|
||||||
directives: [Location]
|
|
||||||
}));
|
|
||||||
|
|
||||||
tb.createView(MyComp).then((view) => {
|
|
||||||
var location = view.rawView.locals.get("loc");
|
|
||||||
|
|
||||||
loader.loadNextToExistingLocation(DynamicallyLoadedWithHostProps, location.elementRef).then(ref => {
|
|
||||||
ref.instance.id = "new value";
|
|
||||||
|
|
||||||
view.detectChanges();
|
|
||||||
|
|
||||||
var newlyInsertedElement = DOM.childNodesAsList(view.rootNodes[0])[1];
|
|
||||||
expect(newlyInsertedElement.id).toEqual("new value")
|
|
||||||
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('loading into a new location', () => {
|
|
||||||
it('should allow to create, update and destroy components',
|
|
||||||
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
|
||||||
tb.overrideView(MyComp, new View({
|
|
||||||
template: '<imp-ng-cmp #impview></imp-ng-cmp>',
|
|
||||||
directives: [ImperativeViewComponentUsingNgComponent]
|
|
||||||
}));
|
|
||||||
tb.createView(MyComp).then((view) => {
|
|
||||||
var userViewComponent = view.rawView.locals.get("impview");
|
|
||||||
|
|
||||||
userViewComponent.done.then((childComponentRef) => {
|
|
||||||
view.detectChanges();
|
|
||||||
|
|
||||||
expect(view.rootNodes).toHaveText('hello');
|
|
||||||
|
|
||||||
childComponentRef.instance.ctxProp = 'new';
|
|
||||||
|
|
||||||
view.detectChanges();
|
|
||||||
|
|
||||||
expect(view.rootNodes).toHaveText('new');
|
|
||||||
|
|
||||||
childComponentRef.dispose();
|
|
||||||
|
|
||||||
expect(view.rootNodes).toHaveText('');
|
|
||||||
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('loadAsRoot', () => {
|
|
||||||
|
|
||||||
it('should allow to create, update and destroy components',
|
|
||||||
inject([TestBed, AsyncTestCompleter, DynamicComponentLoader, DOCUMENT_TOKEN, Injector], (tb, async, dcl, doc, injector) => {
|
|
||||||
var rootEl = el('<child-cmp></child-cmp>');
|
|
||||||
DOM.appendChild(doc.body, rootEl);
|
|
||||||
dcl.loadAsRoot(ChildComp, null, injector).then( (componentRef) => {
|
|
||||||
var view = new ViewProxy(componentRef);
|
|
||||||
expect(rootEl.parentNode).toBe(doc.body);
|
|
||||||
|
|
||||||
view.detectChanges();
|
|
||||||
|
|
||||||
expect(rootEl).toHaveText('hello');
|
|
||||||
|
|
||||||
componentRef.instance.ctxProp = 'new';
|
|
||||||
|
|
||||||
view.detectChanges();
|
|
||||||
|
|
||||||
expect(rootEl).toHaveText('new');
|
|
||||||
|
|
||||||
componentRef.dispose();
|
|
||||||
|
|
||||||
expect(rootEl).toHaveText('');
|
|
||||||
expect(rootEl.parentNode).toBe(doc.body);
|
|
||||||
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'imp-ng-cmp'
|
|
||||||
})
|
|
||||||
@View({
|
|
||||||
template: ''
|
|
||||||
})
|
|
||||||
class ImperativeViewComponentUsingNgComponent {
|
|
||||||
done;
|
|
||||||
|
|
||||||
constructor(self:ElementRef, dynamicComponentLoader:DynamicComponentLoader, viewManager:AppViewManager, renderer:DomRenderer) {
|
|
||||||
var div = el('<div id="impHost"></div>');
|
|
||||||
var shadowViewRef = viewManager.getComponentView(self);
|
|
||||||
renderer.setComponentViewRootNodes(shadowViewRef.render, [div]);
|
|
||||||
this.done = dynamicComponentLoader.loadIntoNewLocation(ChildComp, self, null).then( (componentRef) => {
|
|
||||||
var element = renderer.getHostElement(componentRef.hostView.render);
|
|
||||||
DOM.appendChild(div, element);
|
|
||||||
return componentRef;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'child-cmp',
|
|
||||||
})
|
|
||||||
@View({
|
|
||||||
template: '{{ctxProp}}'
|
|
||||||
})
|
|
||||||
class ChildComp {
|
|
||||||
ctxProp:string;
|
|
||||||
constructor() {
|
|
||||||
this.ctxProp = 'hello';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class DynamicallyCreatedComponentService {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'dynamic-comp'
|
|
||||||
})
|
|
||||||
class DynamicComp {
|
|
||||||
done;
|
|
||||||
|
|
||||||
constructor(loader:DynamicComponentLoader, location:ElementRef) {
|
|
||||||
this.done = loader.loadIntoExistingLocation(DynamicallyCreatedCmp, location);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'hello-cmp',
|
|
||||||
appInjector: [DynamicallyCreatedComponentService]
|
|
||||||
})
|
|
||||||
@View({
|
|
||||||
template: "{{greeting}}"
|
|
||||||
})
|
|
||||||
class DynamicallyCreatedCmp {
|
|
||||||
greeting:string;
|
|
||||||
dynamicallyCreatedComponentService:DynamicallyCreatedComponentService;
|
|
||||||
|
|
||||||
constructor(a:DynamicallyCreatedComponentService) {
|
|
||||||
this.greeting = "hello";
|
|
||||||
this.dynamicallyCreatedComponentService = a;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({selector: 'dummy'})
|
|
||||||
@View({template: "DynamicallyLoaded;"})
|
|
||||||
class DynamicallyLoaded {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({selector: 'dummy'})
|
|
||||||
@View({template: "DynamicallyLoaded2;"})
|
|
||||||
class DynamicallyLoaded2 {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'dummy',
|
|
||||||
hostProperties: {'id' : 'id'}
|
|
||||||
})
|
|
||||||
@View({template: "DynamicallyLoadedWithHostProps;"})
|
|
||||||
class DynamicallyLoadedWithHostProps {
|
|
||||||
id:string;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.id = "default";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'location'
|
|
||||||
})
|
|
||||||
@View({template: "Location;"})
|
|
||||||
class Location {
|
|
||||||
elementRef:ElementRef;
|
|
||||||
|
|
||||||
constructor(elementRef:ElementRef) {
|
|
||||||
this.elementRef = elementRef;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'my-comp'
|
|
||||||
})
|
|
||||||
@View({
|
|
||||||
directives: []
|
|
||||||
})
|
|
||||||
class MyComp {
|
|
||||||
ctxBoolProp:boolean;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.ctxBoolProp = false;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,332 @@
|
||||||
|
import {
|
||||||
|
AsyncTestCompleter,
|
||||||
|
beforeEach,
|
||||||
|
ddescribe,
|
||||||
|
xdescribe,
|
||||||
|
describe,
|
||||||
|
el,
|
||||||
|
dispatchEvent,
|
||||||
|
expect,
|
||||||
|
iit,
|
||||||
|
inject,
|
||||||
|
beforeEachBindings,
|
||||||
|
it,
|
||||||
|
xit,
|
||||||
|
viewRootNodes
|
||||||
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
|
import {TestBed, ViewProxy} from 'angular2/src/test_lib/test_bed';
|
||||||
|
import {Injector} from 'angular2/di';
|
||||||
|
import {Component, View} from 'angular2/annotations';
|
||||||
|
import * as viewAnn from 'angular2/src/core/annotations_impl/view';
|
||||||
|
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
|
||||||
|
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
|
||||||
|
import {NgIf} from 'angular2/src/directives/ng_if';
|
||||||
|
import {DomRenderer, DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
|
||||||
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
|
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe('DynamicComponentLoader', function() {
|
||||||
|
describe("loading into existing location", () => {
|
||||||
|
it('should work', inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||||
|
tb.overrideView(MyComp, new viewAnn.View({
|
||||||
|
template: '<dynamic-comp #dynamic></dynamic-comp>',
|
||||||
|
directives: [DynamicComp]
|
||||||
|
}));
|
||||||
|
|
||||||
|
tb.createView(MyComp).then((view) => {
|
||||||
|
var dynamicComponent = view.rawView.locals.get("dynamic");
|
||||||
|
expect(dynamicComponent).toBeAnInstanceOf(DynamicComp);
|
||||||
|
|
||||||
|
dynamicComponent.done.then((_) => {
|
||||||
|
view.detectChanges();
|
||||||
|
expect(view.rootNodes).toHaveText('hello');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should inject dependencies of the dynamically-loaded component',
|
||||||
|
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||||
|
tb.overrideView(MyComp, new viewAnn.View({
|
||||||
|
template: '<dynamic-comp #dynamic></dynamic-comp>',
|
||||||
|
directives: [DynamicComp]
|
||||||
|
}));
|
||||||
|
|
||||||
|
tb.createView(MyComp).then((view) => {
|
||||||
|
var dynamicComponent = view.rawView.locals.get("dynamic");
|
||||||
|
dynamicComponent.done.then((ref) => {
|
||||||
|
expect(ref.instance.dynamicallyCreatedComponentService)
|
||||||
|
.toBeAnInstanceOf(DynamicallyCreatedComponentService);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should allow to destroy and create them via viewcontainer directives',
|
||||||
|
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||||
|
tb.overrideView(MyComp, new viewAnn.View({
|
||||||
|
template:
|
||||||
|
'<div><dynamic-comp #dynamic template="ng-if: ctxBoolProp"></dynamic-comp></div>',
|
||||||
|
directives: [DynamicComp, NgIf]
|
||||||
|
}));
|
||||||
|
|
||||||
|
tb.createView(MyComp).then((view) => {
|
||||||
|
view.context.ctxBoolProp = true;
|
||||||
|
view.detectChanges();
|
||||||
|
var dynamicComponent = view.rawView.viewContainers[0].views[0].locals.get("dynamic");
|
||||||
|
dynamicComponent.done.then((_) =>
|
||||||
|
{
|
||||||
|
view.detectChanges();
|
||||||
|
expect(view.rootNodes).toHaveText('hello');
|
||||||
|
|
||||||
|
view.context.ctxBoolProp = false;
|
||||||
|
view.detectChanges();
|
||||||
|
|
||||||
|
expect(view.rawView.viewContainers[0].views.length)
|
||||||
|
.toBe(0);
|
||||||
|
expect(view.rootNodes).toHaveText('');
|
||||||
|
|
||||||
|
view.context.ctxBoolProp = true;
|
||||||
|
view.detectChanges();
|
||||||
|
|
||||||
|
var dynamicComponent =
|
||||||
|
view.rawView.viewContainers[0].views[0].locals.get(
|
||||||
|
"dynamic");
|
||||||
|
return dynamicComponent.done;
|
||||||
|
})
|
||||||
|
.then((_) => {
|
||||||
|
view.detectChanges();
|
||||||
|
expect(view.rootNodes).toHaveText('hello');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("loading next to an existing location", () => {
|
||||||
|
it('should work',
|
||||||
|
inject([DynamicComponentLoader, TestBed, AsyncTestCompleter], (loader, tb, async) => {
|
||||||
|
tb.overrideView(
|
||||||
|
MyComp,
|
||||||
|
new viewAnn.View(
|
||||||
|
{template: '<div><location #loc></location></div>', directives: [Location]}));
|
||||||
|
|
||||||
|
tb.createView(MyComp).then((view) => {
|
||||||
|
var location = view.rawView.locals.get("loc");
|
||||||
|
|
||||||
|
loader.loadNextToExistingLocation(DynamicallyLoaded, location.elementRef)
|
||||||
|
.then(ref => {
|
||||||
|
expect(view.rootNodes).toHaveText("Location;DynamicallyLoaded;");
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should return a disposable component ref',
|
||||||
|
inject([DynamicComponentLoader, TestBed, AsyncTestCompleter], (loader, tb, async) => {
|
||||||
|
tb.overrideView(
|
||||||
|
MyComp,
|
||||||
|
new viewAnn.View(
|
||||||
|
{template: '<div><location #loc></location></div>', directives: [Location]}));
|
||||||
|
|
||||||
|
tb.createView(MyComp).then((view) => {
|
||||||
|
var location = view.rawView.locals.get("loc");
|
||||||
|
loader.loadNextToExistingLocation(DynamicallyLoaded, location.elementRef)
|
||||||
|
.then(ref => {
|
||||||
|
loader.loadNextToExistingLocation(DynamicallyLoaded2, location.elementRef)
|
||||||
|
.then(ref2 => {
|
||||||
|
expect(view.rootNodes)
|
||||||
|
.toHaveText("Location;DynamicallyLoaded;DynamicallyLoaded2;")
|
||||||
|
|
||||||
|
ref2.dispose();
|
||||||
|
|
||||||
|
expect(view.rootNodes)
|
||||||
|
.toHaveText("Location;DynamicallyLoaded;")
|
||||||
|
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should update host properties',
|
||||||
|
inject([DynamicComponentLoader, TestBed, AsyncTestCompleter], (loader, tb, async) => {
|
||||||
|
tb.overrideView(
|
||||||
|
MyComp,
|
||||||
|
new viewAnn.View(
|
||||||
|
{template: '<div><location #loc></location></div>', directives: [Location]}));
|
||||||
|
|
||||||
|
tb.createView(MyComp).then((view) => {
|
||||||
|
var location = view.rawView.locals.get("loc");
|
||||||
|
|
||||||
|
loader.loadNextToExistingLocation(DynamicallyLoadedWithHostProps, location.elementRef)
|
||||||
|
.then(ref => {
|
||||||
|
ref.instance.id = "new value";
|
||||||
|
|
||||||
|
view.detectChanges();
|
||||||
|
|
||||||
|
var newlyInsertedElement = DOM.childNodesAsList(view.rootNodes[0])[1];
|
||||||
|
expect(newlyInsertedElement.id)
|
||||||
|
.toEqual("new value")
|
||||||
|
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('loading into a new location', () => {
|
||||||
|
it('should allow to create, update and destroy components',
|
||||||
|
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||||
|
tb.overrideView(MyComp, new viewAnn.View({
|
||||||
|
template: '<imp-ng-cmp #impview></imp-ng-cmp>',
|
||||||
|
directives: [ImperativeViewComponentUsingNgComponent]
|
||||||
|
}));
|
||||||
|
tb.createView(MyComp).then((view) => {
|
||||||
|
var userViewComponent = view.rawView.locals.get("impview");
|
||||||
|
|
||||||
|
userViewComponent.done.then((childComponentRef) => {
|
||||||
|
view.detectChanges();
|
||||||
|
|
||||||
|
expect(view.rootNodes).toHaveText('hello');
|
||||||
|
|
||||||
|
childComponentRef.instance.ctxProp = 'new';
|
||||||
|
|
||||||
|
view.detectChanges();
|
||||||
|
|
||||||
|
expect(view.rootNodes).toHaveText('new');
|
||||||
|
|
||||||
|
childComponentRef.dispose();
|
||||||
|
|
||||||
|
expect(view.rootNodes).toHaveText('');
|
||||||
|
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('loadAsRoot', () => {
|
||||||
|
|
||||||
|
it('should allow to create, update and destroy components',
|
||||||
|
inject([TestBed, AsyncTestCompleter, DynamicComponentLoader, DOCUMENT_TOKEN, Injector],
|
||||||
|
(tb, async, dcl, doc, injector) => {
|
||||||
|
var rootEl = el('<child-cmp></child-cmp>');
|
||||||
|
DOM.appendChild(doc.body, rootEl);
|
||||||
|
dcl.loadAsRoot(ChildComp, null, injector)
|
||||||
|
.then((componentRef) => {
|
||||||
|
var view = new ViewProxy(componentRef);
|
||||||
|
expect(rootEl.parentNode).toBe(doc.body);
|
||||||
|
|
||||||
|
view.detectChanges();
|
||||||
|
|
||||||
|
expect(rootEl).toHaveText('hello');
|
||||||
|
|
||||||
|
componentRef.instance.ctxProp = 'new';
|
||||||
|
|
||||||
|
view.detectChanges();
|
||||||
|
|
||||||
|
expect(rootEl).toHaveText('new');
|
||||||
|
|
||||||
|
componentRef.dispose();
|
||||||
|
|
||||||
|
expect(rootEl).toHaveText('');
|
||||||
|
expect(rootEl.parentNode).toBe(doc.body);
|
||||||
|
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'imp-ng-cmp'})
|
||||||
|
@View({template: ''})
|
||||||
|
class ImperativeViewComponentUsingNgComponent {
|
||||||
|
done;
|
||||||
|
|
||||||
|
constructor(self: ElementRef, dynamicComponentLoader: DynamicComponentLoader,
|
||||||
|
viewManager: AppViewManager, renderer: DomRenderer) {
|
||||||
|
var div = el('<div id="impHost"></div>');
|
||||||
|
var shadowViewRef = viewManager.getComponentView(self);
|
||||||
|
renderer.setComponentViewRootNodes(shadowViewRef.render, [div]);
|
||||||
|
this.done = dynamicComponentLoader.loadIntoNewLocation(ChildComp, self, null)
|
||||||
|
.then((componentRef) => {
|
||||||
|
var element = renderer.getHostElement(componentRef.hostView.render);
|
||||||
|
DOM.appendChild(div, element);
|
||||||
|
return componentRef;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'child-cmp',
|
||||||
|
})
|
||||||
|
@View({template: '{{ctxProp}}'})
|
||||||
|
class ChildComp {
|
||||||
|
ctxProp: string;
|
||||||
|
constructor() { this.ctxProp = 'hello'; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class DynamicallyCreatedComponentService {}
|
||||||
|
|
||||||
|
@Component({selector: 'dynamic-comp'})
|
||||||
|
class DynamicComp {
|
||||||
|
done;
|
||||||
|
|
||||||
|
constructor(loader: DynamicComponentLoader, location: ElementRef) {
|
||||||
|
this.done = loader.loadIntoExistingLocation(DynamicallyCreatedCmp, location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'hello-cmp', appInjector: [DynamicallyCreatedComponentService]})
|
||||||
|
@View({template: "{{greeting}}"})
|
||||||
|
class DynamicallyCreatedCmp {
|
||||||
|
greeting: string;
|
||||||
|
dynamicallyCreatedComponentService: DynamicallyCreatedComponentService;
|
||||||
|
|
||||||
|
constructor(a: DynamicallyCreatedComponentService) {
|
||||||
|
this.greeting = "hello";
|
||||||
|
this.dynamicallyCreatedComponentService = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'dummy'})
|
||||||
|
@View({template: "DynamicallyLoaded;"})
|
||||||
|
class DynamicallyLoaded {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'dummy'})
|
||||||
|
@View({template: "DynamicallyLoaded2;"})
|
||||||
|
class DynamicallyLoaded2 {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'dummy', hostProperties: {'id': 'id'}})
|
||||||
|
@View({template: "DynamicallyLoadedWithHostProps;"})
|
||||||
|
class DynamicallyLoadedWithHostProps {
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
constructor() { this.id = "default"; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'location'})
|
||||||
|
@View({template: "Location;"})
|
||||||
|
class Location {
|
||||||
|
elementRef: ElementRef;
|
||||||
|
|
||||||
|
constructor(elementRef: ElementRef) { this.elementRef = elementRef; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'my-comp'})
|
||||||
|
@View({directives: []})
|
||||||
|
class MyComp {
|
||||||
|
ctxBoolProp: boolean;
|
||||||
|
|
||||||
|
constructor() { this.ctxBoolProp = false; }
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -12,7 +12,8 @@ import {
|
||||||
IS_DARTIUM,
|
IS_DARTIUM,
|
||||||
beforeEachBindings,
|
beforeEachBindings,
|
||||||
it,
|
it,
|
||||||
xit
|
xit,
|
||||||
|
containsRegexp
|
||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
|
|
||||||
|
@ -25,7 +26,8 @@ import {
|
||||||
BaseException,
|
BaseException,
|
||||||
assertionsEnabled,
|
assertionsEnabled,
|
||||||
isJsObject,
|
isJsObject,
|
||||||
global
|
global,
|
||||||
|
stringify
|
||||||
} from 'angular2/src/facade/lang';
|
} from 'angular2/src/facade/lang';
|
||||||
import {PromiseWrapper, EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
import {PromiseWrapper, EventEmitter, ObservableWrapper} from 'angular2/src/facade/async';
|
||||||
|
|
||||||
|
@ -989,23 +991,15 @@ export function main() {
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO(tbosch): enable this again
|
|
||||||
// Deactivated until we are fully on traceur as Typescript does not
|
|
||||||
// emit class names when transpiling to ES6 and using decorators.
|
|
||||||
// See https://github.com/Microsoft/TypeScript/pull/3063
|
|
||||||
var supportsClassNames =
|
|
||||||
IS_DARTIUM || (isPresent((<any>MyService).name) && (<any>MyService).name.length > 0);
|
|
||||||
|
|
||||||
describe("error handling", () => {
|
describe("error handling", () => {
|
||||||
if (supportsClassNames) {
|
|
||||||
it('should report a meaningful error when a directive is missing annotation',
|
it('should report a meaningful error when a directive is missing annotation',
|
||||||
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||||
tb.overrideView(MyComp,
|
tb.overrideView(MyComp,
|
||||||
new viewAnn.View({directives: [SomeDirectiveMissingAnnotation]}));
|
new viewAnn.View({directives: [SomeDirectiveMissingAnnotation]}));
|
||||||
|
|
||||||
PromiseWrapper.catchError(tb.createView(MyComp, {context: ctx}), (e) => {
|
PromiseWrapper.catchError(tb.createView(MyComp, {context: ctx}), (e) => {
|
||||||
expect(e.message)
|
expect(e.message).toEqual(
|
||||||
.toEqual('No Directive annotation found on SomeDirectiveMissingAnnotation');
|
`No Directive annotation found on ${stringify(SomeDirectiveMissingAnnotation)}`);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
@ -1016,8 +1010,8 @@ export function main() {
|
||||||
tb.overrideView(MyComp, new viewAnn.View({directives: [[null]]}));
|
tb.overrideView(MyComp, new viewAnn.View({directives: [[null]]}));
|
||||||
|
|
||||||
PromiseWrapper.catchError(tb.createView(MyComp, {context: ctx}), (e) => {
|
PromiseWrapper.catchError(tb.createView(MyComp, {context: ctx}), (e) => {
|
||||||
expect(e.message)
|
expect(e.message).toEqual(
|
||||||
.toEqual("Unexpected directive value 'null' on the View of component 'MyComp'");
|
`Unexpected directive value 'null' on the View of component '${stringify(MyComp)}'`);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
@ -1032,7 +1026,7 @@ export function main() {
|
||||||
|
|
||||||
PromiseWrapper.catchError(tb.createView(MyComp, {context: ctx}), (e) => {
|
PromiseWrapper.catchError(tb.createView(MyComp, {context: ctx}), (e) => {
|
||||||
expect(e.message).toEqual(
|
expect(e.message).toEqual(
|
||||||
"Unexpected directive value 'undefined' on the View of component 'MyComp'");
|
`Unexpected directive value 'undefined' on the View of component '${stringify(MyComp)}'`);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
@ -1045,7 +1039,8 @@ export function main() {
|
||||||
|
|
||||||
tb.createView(MyComp, {context: ctx})
|
tb.createView(MyComp, {context: ctx})
|
||||||
.then((view) => {
|
.then((view) => {
|
||||||
expect(() => view.detectChanges()).toThrowError(new RegExp('{{a.b}} in MyComp'));
|
expect(() => view.detectChanges())
|
||||||
|
.toThrowError(containsRegexp(`{{a.b}} in ${stringify(MyComp)}`));
|
||||||
async.done();
|
async.done();
|
||||||
})
|
})
|
||||||
}));
|
}));
|
||||||
|
@ -1057,7 +1052,8 @@ export function main() {
|
||||||
|
|
||||||
tb.createView(MyComp, {context: ctx})
|
tb.createView(MyComp, {context: ctx})
|
||||||
.then((view) => {
|
.then((view) => {
|
||||||
expect(() => view.detectChanges()).toThrowError(new RegExp('a.b in MyComp'));
|
expect(() => view.detectChanges())
|
||||||
|
.toThrowError(containsRegexp(`a.b in ${stringify(MyComp)}`));
|
||||||
async.done();
|
async.done();
|
||||||
})
|
})
|
||||||
}));
|
}));
|
||||||
|
@ -1072,11 +1068,11 @@ export function main() {
|
||||||
|
|
||||||
tb.createView(MyComp, {context: ctx})
|
tb.createView(MyComp, {context: ctx})
|
||||||
.then((view) => {
|
.then((view) => {
|
||||||
expect(() => view.detectChanges()).toThrowError(new RegExp('a.b in MyComp'));
|
expect(() => view.detectChanges())
|
||||||
|
.toThrowError(containsRegexp(`a.b in ${stringify(MyComp)}`));
|
||||||
async.done();
|
async.done();
|
||||||
})
|
})
|
||||||
}));
|
}));
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support imperative views', inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
it('should support imperative views', inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||||
|
|
|
@ -10,15 +10,19 @@ import {
|
||||||
inject,
|
inject,
|
||||||
IS_DARTIUM,
|
IS_DARTIUM,
|
||||||
it,
|
it,
|
||||||
SpyObject, proxy
|
SpyObject,
|
||||||
|
proxy
|
||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
import {isBlank} from 'angular2/src/facade/lang';
|
import {isBlank, IMPLEMENTS, stringify} from 'angular2/src/facade/lang';
|
||||||
import {MapWrapper} from 'angular2/src/facade/collection';
|
import {MapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
import {ChangeDetection, ChangeDetectorDefinition} from 'angular2/change_detection';
|
import {ChangeDetection, ChangeDetectorDefinition} from 'angular2/change_detection';
|
||||||
import {ProtoViewFactory, getChangeDetectorDefinitions} from 'angular2/src/core/compiler/proto_view_factory';
|
import {
|
||||||
import {Component, Directive} from 'angular2/src/core/annotations_impl/annotations';
|
ProtoViewFactory,
|
||||||
|
getChangeDetectorDefinitions
|
||||||
|
} from 'angular2/src/core/compiler/proto_view_factory';
|
||||||
|
import {Component, Directive} from 'angular2/annotations';
|
||||||
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||||
import {DirectiveBinding} from 'angular2/src/core/compiler/element_injector';
|
import {DirectiveBinding} from 'angular2/src/core/compiler/element_injector';
|
||||||
import * as renderApi from 'angular2/src/render/api';
|
import * as renderApi from 'angular2/src/render/api';
|
||||||
|
@ -31,7 +35,7 @@ export function main() {
|
||||||
var protoViewFactory;
|
var protoViewFactory;
|
||||||
var directiveResolver;
|
var directiveResolver;
|
||||||
|
|
||||||
beforeEach( () => {
|
beforeEach(() => {
|
||||||
directiveResolver = new DirectiveResolver();
|
directiveResolver = new DirectiveResolver();
|
||||||
changeDetection = new ChangeDetectionSpy();
|
changeDetection = new ChangeDetectionSpy();
|
||||||
protoViewFactory = new ProtoViewFactory(changeDetection);
|
protoViewFactory = new ProtoViewFactory(changeDetection);
|
||||||
|
@ -45,10 +49,10 @@ export function main() {
|
||||||
|
|
||||||
it('should create a ChangeDetectorDefinition for the root render proto view', () => {
|
it('should create a ChangeDetectorDefinition for the root render proto view', () => {
|
||||||
var renderPv = createRenderProtoView();
|
var renderPv = createRenderProtoView();
|
||||||
var defs = getChangeDetectorDefinitions(bindDirective(MainComponent).metadata,
|
var defs =
|
||||||
renderPv, []);
|
getChangeDetectorDefinitions(bindDirective(MainComponent).metadata, renderPv, []);
|
||||||
expect(defs.length).toBe(1);
|
expect(defs.length).toBe(1);
|
||||||
expect(defs[0].id).toEqual('MainComponent_comp_0');
|
expect(defs[0].id).toEqual(`${stringify(MainComponent)}_comp_0`);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -57,8 +61,7 @@ export function main() {
|
||||||
|
|
||||||
it('should create an AppProtoView for the root render proto view', () => {
|
it('should create an AppProtoView for the root render proto view', () => {
|
||||||
var renderPv = createRenderProtoView();
|
var renderPv = createRenderProtoView();
|
||||||
var pvs = protoViewFactory.createAppProtoViews(bindDirective(MainComponent),
|
var pvs = protoViewFactory.createAppProtoViews(bindDirective(MainComponent), renderPv, []);
|
||||||
renderPv, []);
|
|
||||||
expect(pvs.length).toBe(1);
|
expect(pvs.length).toBe(1);
|
||||||
expect(pvs[0].render).toBe(renderPv.render);
|
expect(pvs[0].render).toBe(renderPv.render);
|
||||||
});
|
});
|
||||||
|
@ -68,42 +71,33 @@ export function main() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createRenderProtoView(elementBinders = null, type:number = null) {
|
function createRenderProtoView(elementBinders = null, type: number = null) {
|
||||||
if (isBlank(type)) {
|
if (isBlank(type)) {
|
||||||
type = renderApi.ProtoViewDto.COMPONENT_VIEW_TYPE;
|
type = renderApi.ProtoViewDto.COMPONENT_VIEW_TYPE;
|
||||||
}
|
}
|
||||||
if (isBlank(elementBinders)) {
|
if (isBlank(elementBinders)) {
|
||||||
elementBinders = [];
|
elementBinders = [];
|
||||||
}
|
}
|
||||||
return new renderApi.ProtoViewDto({
|
return new renderApi.ProtoViewDto(
|
||||||
elementBinders: elementBinders,
|
{elementBinders: elementBinders, type: type, variableBindings: MapWrapper.create()});
|
||||||
type: type,
|
|
||||||
variableBindings: MapWrapper.create()
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createRenderComponentElementBinder(directiveIndex) {
|
function createRenderComponentElementBinder(directiveIndex) {
|
||||||
return new renderApi.ElementBinder({
|
return new renderApi.ElementBinder(
|
||||||
directives: [new renderApi.DirectiveBinder({
|
{directives: [new renderApi.DirectiveBinder({directiveIndex: directiveIndex})]});
|
||||||
directiveIndex: directiveIndex
|
|
||||||
})]
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createRenderViewportElementBinder(nestedProtoView) {
|
function createRenderViewportElementBinder(nestedProtoView) {
|
||||||
return new renderApi.ElementBinder({
|
return new renderApi.ElementBinder({nestedProtoView: nestedProtoView});
|
||||||
nestedProtoView: nestedProtoView
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@proxy
|
@proxy
|
||||||
@IMPLEMENTS(ChangeDetection)
|
@IMPLEMENTS(ChangeDetection)
|
||||||
class ChangeDetectionSpy extends SpyObject {
|
class ChangeDetectionSpy extends SpyObject {
|
||||||
constructor(){super(ChangeDetection);}
|
constructor() { super(ChangeDetection); }
|
||||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
noSuchMethod(m) { return super.noSuchMethod(m) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({selector: 'main-comp'})
|
||||||
selector: 'main-comp'
|
class MainComponent {
|
||||||
})
|
}
|
||||||
class MainComponent {}
|
|
|
@ -1,125 +0,0 @@
|
||||||
import {
|
|
||||||
AsyncTestCompleter,
|
|
||||||
beforeEach,
|
|
||||||
ddescribe,
|
|
||||||
describe,
|
|
||||||
el,
|
|
||||||
expect,
|
|
||||||
iit,
|
|
||||||
inject,
|
|
||||||
IS_NODEJS,
|
|
||||||
it,
|
|
||||||
xit,
|
|
||||||
} from 'angular2/test_lib';
|
|
||||||
|
|
||||||
import {TestBed} from 'angular2/src/test_lib/test_bed';
|
|
||||||
|
|
||||||
import {QueryList} from 'angular2/src/core/compiler/query_list';
|
|
||||||
import {Query} from 'angular2/src/core/annotations_impl/di';
|
|
||||||
|
|
||||||
import {NgIf, NgFor} from 'angular2/angular2';
|
|
||||||
|
|
||||||
import {Component, Directive} from 'angular2/src/core/annotations_impl/annotations';
|
|
||||||
import {View} from 'angular2/src/core/annotations_impl/view';
|
|
||||||
|
|
||||||
import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter';
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
BrowserDomAdapter.makeCurrent();
|
|
||||||
describe('Query API', () => {
|
|
||||||
|
|
||||||
it('should contain all directives in the light dom', inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
|
||||||
var template =
|
|
||||||
'<div text="1"></div>' +
|
|
||||||
'<needs-query text="2"><div text="3"></div></needs-query>' +
|
|
||||||
'<div text="4"></div>';
|
|
||||||
|
|
||||||
tb.createView(MyComp, {html: template}).then((view) => {
|
|
||||||
view.detectChanges();
|
|
||||||
expect(view.rootNodes).toHaveText('2|3|');
|
|
||||||
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should reflect dynamically inserted directives', inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
|
||||||
var template =
|
|
||||||
'<div text="1"></div>' +
|
|
||||||
'<needs-query text="2"><div *ng-if="shouldShow" [text]="\'3\'"></div></needs-query>' +
|
|
||||||
'<div text="4"></div>';
|
|
||||||
|
|
||||||
tb.createView(MyComp, {html: template}).then((view) => {
|
|
||||||
|
|
||||||
view.detectChanges();
|
|
||||||
expect(view.rootNodes).toHaveText('2|');
|
|
||||||
|
|
||||||
view.context.shouldShow = true;
|
|
||||||
view.detectChanges();
|
|
||||||
// TODO(rado): figure out why the second tick is necessary.
|
|
||||||
view.detectChanges();
|
|
||||||
expect(view.rootNodes).toHaveText('2|3|');
|
|
||||||
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should reflect moved directives', inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
|
||||||
var template =
|
|
||||||
'<div text="1"></div>' +
|
|
||||||
'<needs-query text="2"><div *ng-for="var i of list" [text]="i"></div></needs-query>' +
|
|
||||||
'<div text="4"></div>';
|
|
||||||
|
|
||||||
tb.createView(MyComp, {html: template}).then((view) => {
|
|
||||||
view.detectChanges();
|
|
||||||
view.detectChanges();
|
|
||||||
|
|
||||||
expect(view.rootNodes).toHaveText('2|1d|2d|3d|');
|
|
||||||
|
|
||||||
view.context.list = ['3d', '2d'];
|
|
||||||
view.detectChanges();
|
|
||||||
view.detectChanges();
|
|
||||||
expect(view.rootNodes).toHaveText('2|3d|2d|');
|
|
||||||
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({selector: 'needs-query'})
|
|
||||||
@View({
|
|
||||||
directives: [NgFor],
|
|
||||||
template: '<div *ng-for="var dir of query">{{dir.text}}|</div>'
|
|
||||||
})
|
|
||||||
class NeedsQuery {
|
|
||||||
query: QueryList;
|
|
||||||
constructor(@Query(TextDirective) query: QueryList) {
|
|
||||||
this.query = query;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var _constructiontext = 0;
|
|
||||||
|
|
||||||
@Directive({
|
|
||||||
selector: '[text]',
|
|
||||||
properties: {
|
|
||||||
'text': 'text'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
class TextDirective {
|
|
||||||
text: string;
|
|
||||||
constructor() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({selector: 'my-comp'})
|
|
||||||
@View({
|
|
||||||
directives: [NeedsQuery, TextDirective, NgIf, NgFor]
|
|
||||||
})
|
|
||||||
class MyComp {
|
|
||||||
shouldShow: boolean;
|
|
||||||
list;
|
|
||||||
constructor() {
|
|
||||||
this.shouldShow = false;
|
|
||||||
this.list = ['1d', '2d', '3d'];
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
import {
|
||||||
|
AsyncTestCompleter,
|
||||||
|
beforeEach,
|
||||||
|
ddescribe,
|
||||||
|
describe,
|
||||||
|
el,
|
||||||
|
expect,
|
||||||
|
iit,
|
||||||
|
inject,
|
||||||
|
it,
|
||||||
|
xit,
|
||||||
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
|
import {TestBed} from 'angular2/src/test_lib/test_bed';
|
||||||
|
|
||||||
|
import {Injectable} from 'angular2/di';
|
||||||
|
import {QueryList} from 'angular2/core';
|
||||||
|
import {Query, Component, Directive, View} from 'angular2/annotations';
|
||||||
|
|
||||||
|
import {NgIf, NgFor} from 'angular2/angular2';
|
||||||
|
|
||||||
|
import {BrowserDomAdapter} from 'angular2/src/dom/browser_adapter';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
BrowserDomAdapter.makeCurrent();
|
||||||
|
describe('Query API', () => {
|
||||||
|
|
||||||
|
it('should contain all directives in the light dom',
|
||||||
|
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||||
|
var template = '<div text="1"></div>' +
|
||||||
|
'<needs-query text="2"><div text="3"></div></needs-query>' +
|
||||||
|
'<div text="4"></div>';
|
||||||
|
|
||||||
|
tb.createView(MyComp, {html: template})
|
||||||
|
.then((view) => {
|
||||||
|
view.detectChanges();
|
||||||
|
expect(view.rootNodes).toHaveText('2|3|');
|
||||||
|
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should reflect dynamically inserted directives',
|
||||||
|
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||||
|
var template =
|
||||||
|
'<div text="1"></div>' +
|
||||||
|
'<needs-query text="2"><div *ng-if="shouldShow" [text]="\'3\'"></div></needs-query>' +
|
||||||
|
'<div text="4"></div>';
|
||||||
|
|
||||||
|
tb.createView(MyComp, {html: template})
|
||||||
|
.then((view) => {
|
||||||
|
|
||||||
|
view.detectChanges();
|
||||||
|
expect(view.rootNodes).toHaveText('2|');
|
||||||
|
|
||||||
|
view.context.shouldShow = true;
|
||||||
|
view.detectChanges();
|
||||||
|
// TODO(rado): figure out why the second tick is necessary.
|
||||||
|
view.detectChanges();
|
||||||
|
expect(view.rootNodes).toHaveText('2|3|');
|
||||||
|
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should reflect moved directives', inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||||
|
var template =
|
||||||
|
'<div text="1"></div>' +
|
||||||
|
'<needs-query text="2"><div *ng-for="var i of list" [text]="i"></div></needs-query>' +
|
||||||
|
'<div text="4"></div>';
|
||||||
|
|
||||||
|
tb.createView(MyComp, {html: template})
|
||||||
|
.then((view) => {
|
||||||
|
view.detectChanges();
|
||||||
|
view.detectChanges();
|
||||||
|
|
||||||
|
expect(view.rootNodes).toHaveText('2|1d|2d|3d|');
|
||||||
|
|
||||||
|
view.context.list = ['3d', '2d'];
|
||||||
|
view.detectChanges();
|
||||||
|
view.detectChanges();
|
||||||
|
expect(view.rootNodes).toHaveText('2|3d|2d|');
|
||||||
|
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Directive({selector: '[text]', properties: {'text': 'text'}})
|
||||||
|
@Injectable()
|
||||||
|
class TextDirective {
|
||||||
|
text: string;
|
||||||
|
constructor() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'needs-query'})
|
||||||
|
@View({directives: [NgFor], template: '<div *ng-for="var dir of query">{{dir.text}}|</div>'})
|
||||||
|
@Injectable()
|
||||||
|
class NeedsQuery {
|
||||||
|
query: QueryList;
|
||||||
|
constructor(@Query(TextDirective) query: QueryList) { this.query = query; }
|
||||||
|
}
|
||||||
|
|
||||||
|
var _constructiontext = 0;
|
||||||
|
|
||||||
|
@Component({selector: 'my-comp'})
|
||||||
|
@View({directives: [NeedsQuery, TextDirective, NgIf, NgFor]})
|
||||||
|
@Injectable()
|
||||||
|
class MyComp {
|
||||||
|
shouldShow: boolean;
|
||||||
|
list;
|
||||||
|
constructor() {
|
||||||
|
this.shouldShow = false;
|
||||||
|
this.list = ['1d', '2d', '3d'];
|
||||||
|
}
|
||||||
|
}
|
|
@ -12,9 +12,7 @@ export function main() {
|
||||||
log = '';
|
log = '';
|
||||||
});
|
});
|
||||||
|
|
||||||
function logAppend(item) {
|
function logAppend(item) { log += (log.length == 0 ? '' : ', ') + item; }
|
||||||
log += (log.length == 0 ? '' : ', ') + item;
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should support adding objects and iterating over them', () => {
|
it('should support adding objects and iterating over them', () => {
|
||||||
queryList.add('one');
|
queryList.add('one');
|
||||||
|
@ -35,7 +33,7 @@ export function main() {
|
||||||
describe('simple observable interface', () => {
|
describe('simple observable interface', () => {
|
||||||
it('should fire callbacks on change', () => {
|
it('should fire callbacks on change', () => {
|
||||||
var fires = 0;
|
var fires = 0;
|
||||||
queryList.onChange(() => {fires += 1;});
|
queryList.onChange(() => { fires += 1; });
|
||||||
|
|
||||||
queryList.fireCallbacks();
|
queryList.fireCallbacks();
|
||||||
expect(fires).toEqual(0);
|
expect(fires).toEqual(0);
|
|
@ -1,147 +0,0 @@
|
||||||
import {ddescribe, describe, it, iit, expect, beforeEach, IS_DARTIUM} from 'angular2/test_lib';
|
|
||||||
import {ReflectionCapabilities} from 'angular2/src/reflection/reflection_capabilities';
|
|
||||||
import {isPresent, global, CONST} from 'angular2/src/facade/lang';
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
var rc;
|
|
||||||
beforeEach(() => {
|
|
||||||
rc = new ReflectionCapabilities();
|
|
||||||
});
|
|
||||||
|
|
||||||
function assertTestClassAnnotations(annotations) {
|
|
||||||
expect(annotations[0]).toBeAnInstanceOf(ClassDec1);
|
|
||||||
expect(annotations[1]).toBeAnInstanceOf(ClassDec2);
|
|
||||||
}
|
|
||||||
|
|
||||||
function assertTestClassParameters(parameters) {
|
|
||||||
expect(parameters.length).toBe(4);
|
|
||||||
|
|
||||||
expect(parameters[0].length).toBe(2);
|
|
||||||
expect(parameters[0][0]).toEqual(P1);
|
|
||||||
expect(parameters[0][1]).toBeAnInstanceOf(ParamDec);
|
|
||||||
|
|
||||||
expect(parameters[1].length).toBe(1);
|
|
||||||
expect(parameters[1][0]).toEqual(P2);
|
|
||||||
|
|
||||||
|
|
||||||
expect(parameters[2].length).toBe(1);
|
|
||||||
expect(parameters[2][0]).toBeAnInstanceOf(ParamDec);
|
|
||||||
|
|
||||||
expect(parameters[3].length).toBe(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('reflection capabilities', () => {
|
|
||||||
describe("factory", () => {
|
|
||||||
it("should create a factory for a type", () => {
|
|
||||||
var f = rc.factory(ClassWithField);
|
|
||||||
expect(f("value").field).toEqual("value");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should throw when a constructor has more than 10 args", () => {
|
|
||||||
expect(() => rc.factory(ClassWith11Fields)).toThrowError(
|
|
||||||
new RegExp(`has more than 10 arguments`));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can read out class annotations through annotations key', () => {
|
|
||||||
assertTestClassAnnotations(rc.annotations(TestClass));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can read out parameter annotations through parameters key', () => {
|
|
||||||
assertTestClassParameters(rc.parameters(TestClass));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can read out parameter annotations through parameters key for types only class', () => {
|
|
||||||
expect(rc.parameters(TestClassTypesOnly)).toEqual([[P1], [P2]]);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!IS_DARTIUM) {
|
|
||||||
// Mocking in the tests below is needed because the test runs through Traceur.
|
|
||||||
// After the switch to TS the setup will have to change, where the direct key
|
|
||||||
// access will be mocked, and the tests below will be direct.
|
|
||||||
it('can read out class annotations though Reflect APIs', () => {
|
|
||||||
var rc = new ReflectionCapabilities({
|
|
||||||
'getMetadata': (key, targetCls) => {
|
|
||||||
return (targetCls == TestClassDec) ? mockDataForTestClassDec[key] : null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
assertTestClassAnnotations(rc.annotations(TestClassDec));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can read out parameter annotations though Reflect APIs', () => {
|
|
||||||
var rc = new ReflectionCapabilities({
|
|
||||||
'getMetadata': (key, targetCls) => {
|
|
||||||
return (targetCls == TestClassDec) ? mockDataForTestClassDec[key] : null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
assertTestClassParameters(rc.parameters(TestClassDec));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can read out parameter annotations though Reflect APIs for types only class', () => {
|
|
||||||
var rc = new ReflectionCapabilities({
|
|
||||||
'getMetadata': (key, targetCls) => {
|
|
||||||
return (targetCls == TestClassTypesOnlyDec) ? mockDataForTestClassTypesOnly[key] : null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
expect(rc.parameters(TestClassTypesOnlyDec)).toEqual([[P1], [P2]]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class ClassDec1 {
|
|
||||||
@CONST()
|
|
||||||
constructor() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ClassDec2 {
|
|
||||||
@CONST()
|
|
||||||
constructor() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class ParamDec {
|
|
||||||
@CONST()
|
|
||||||
constructor() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
class P1 {}
|
|
||||||
class P2 {}
|
|
||||||
|
|
||||||
@ClassDec1()
|
|
||||||
@ClassDec2()
|
|
||||||
class TestClass {
|
|
||||||
constructor(@ParamDec() a: P1, b: P2, @ParamDec() c, d) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mocking the data stored in global.Reflect as if TS was compiling TestClass above.
|
|
||||||
var mockDataForTestClassDec = {
|
|
||||||
'annotations': [new ClassDec1(), new ClassDec2()],
|
|
||||||
'parameters': [new ParamDec(), null, new ParamDec()],
|
|
||||||
'design:paramtypes': [P1, P2, Object, Object]
|
|
||||||
};
|
|
||||||
class TestClassDec {}
|
|
||||||
|
|
||||||
|
|
||||||
class TestClassTypesOnly {
|
|
||||||
constructor(a: P1, b: P2) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ClassWithField {
|
|
||||||
field;
|
|
||||||
constructor(field) {
|
|
||||||
this.field = field;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
class ClassWith11Fields {
|
|
||||||
constructor(a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mocking the data stored in global.Reflect as if TS was compiling TestClass above.
|
|
||||||
var mockDataForTestClassTypesOnly = {
|
|
||||||
'annotations': null,
|
|
||||||
'parameters': null,
|
|
||||||
'design:paramtypes': [P1, P2]
|
|
||||||
};
|
|
||||||
class TestClassTypesOnlyDec {}
|
|
|
@ -12,7 +12,8 @@ import {
|
||||||
beforeEachBindings,
|
beforeEachBindings,
|
||||||
it,
|
it,
|
||||||
xit,
|
xit,
|
||||||
SpyObject, proxy
|
SpyObject,
|
||||||
|
proxy
|
||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
import {MapWrapper} from 'angular2/src/facade/collection';
|
import {MapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
@ -32,23 +33,15 @@ export function main() {
|
||||||
var view;
|
var view;
|
||||||
var viewManager;
|
var viewManager;
|
||||||
|
|
||||||
function wrapView(view:AppView):ViewRef {
|
function wrapView(view: AppView): ViewRef { return new ViewRef(view); }
|
||||||
return new ViewRef(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createProtoView() {
|
function createProtoView() { return new AppProtoView(null, null, null); }
|
||||||
return new AppProtoView(null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createView() {
|
function createView() { return new AppView(null, createProtoView(), MapWrapper.create()); }
|
||||||
return new AppView(null, createProtoView(), MapWrapper.create());
|
|
||||||
}
|
|
||||||
|
|
||||||
function createViewContainer() {
|
function createViewContainer() { return new ViewContainerRef(viewManager, location); }
|
||||||
return new ViewContainerRef(viewManager, location);
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach( () => {
|
beforeEach(() => {
|
||||||
viewManager = new AppViewManagerSpy();
|
viewManager = new AppViewManagerSpy();
|
||||||
view = createView();
|
view = createView();
|
||||||
view.viewContainers = [null];
|
view.viewContainers = [null];
|
||||||
|
@ -79,6 +72,6 @@ export function main() {
|
||||||
@proxy
|
@proxy
|
||||||
@IMPLEMENTS(AppViewManager)
|
@IMPLEMENTS(AppViewManager)
|
||||||
class AppViewManagerSpy extends SpyObject {
|
class AppViewManagerSpy extends SpyObject {
|
||||||
constructor(){super(AppViewManager);}
|
constructor() { super(AppViewManager); }
|
||||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
noSuchMethod(m) { return super.noSuchMethod(m) }
|
||||||
}
|
}
|
|
@ -12,7 +12,8 @@ import {
|
||||||
beforeEachBindings,
|
beforeEachBindings,
|
||||||
it,
|
it,
|
||||||
xit,
|
xit,
|
||||||
SpyObject, proxy
|
SpyObject,
|
||||||
|
proxy
|
||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
import {Injector, bind} from 'angular2/di';
|
import {Injector, bind} from 'angular2/di';
|
||||||
import {IMPLEMENTS, isBlank, isPresent} from 'angular2/src/facade/lang';
|
import {IMPLEMENTS, isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||||
|
@ -21,11 +22,11 @@ import {MapWrapper, ListWrapper, StringMapWrapper} from 'angular2/src/facade/col
|
||||||
import {AppProtoView, AppView, AppViewContainer} from 'angular2/src/core/compiler/view';
|
import {AppProtoView, AppView, AppViewContainer} from 'angular2/src/core/compiler/view';
|
||||||
import {ProtoViewRef, ViewRef, internalView} from 'angular2/src/core/compiler/view_ref';
|
import {ProtoViewRef, ViewRef, internalView} from 'angular2/src/core/compiler/view_ref';
|
||||||
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
|
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
|
||||||
import {Renderer, RenderViewRef, RenderProtoViewRef, RenderViewContainerRef} from 'angular2/src/render/api';
|
import {Renderer, RenderViewRef, RenderProtoViewRef} from 'angular2/src/render/api';
|
||||||
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
|
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
|
||||||
import {DirectiveBinding, ElementInjector} from 'angular2/src/core/compiler/element_injector';
|
import {DirectiveBinding, ElementInjector} from 'angular2/src/core/compiler/element_injector';
|
||||||
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||||
import {Component} from 'angular2/src/core/annotations_impl/annotations';
|
import {Component} from 'angular2/annotations';
|
||||||
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
||||||
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
|
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
|
||||||
import {AppViewPool} from 'angular2/src/core/compiler/view_pool';
|
import {AppViewPool} from 'angular2/src/core/compiler/view_pool';
|
||||||
|
@ -42,13 +43,9 @@ export function main() {
|
||||||
var createdViews;
|
var createdViews;
|
||||||
var createdRenderViews;
|
var createdRenderViews;
|
||||||
|
|
||||||
function wrapPv(protoView:AppProtoView):ProtoViewRef {
|
function wrapPv(protoView: AppProtoView): ProtoViewRef { return new ProtoViewRef(protoView); }
|
||||||
return new ProtoViewRef(protoView);
|
|
||||||
}
|
|
||||||
|
|
||||||
function wrapView(view:AppView):ViewRef {
|
function wrapView(view: AppView): ViewRef { return new ViewRef(view); }
|
||||||
return new ViewRef(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
function elementRef(parentView, boundElementIndex) {
|
function elementRef(parentView, boundElementIndex) {
|
||||||
return new ElementRef(parentView, boundElementIndex);
|
return new ElementRef(parentView, boundElementIndex);
|
||||||
|
@ -59,9 +56,7 @@ export function main() {
|
||||||
return DirectiveBinding.createFromType(type, annotation);
|
return DirectiveBinding.createFromType(type, annotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createEmptyElBinder() {
|
function createEmptyElBinder() { return new ElementBinder(0, null, 0, null, null); }
|
||||||
return new ElementBinder(0, null, 0, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createComponentElBinder(nestedProtoView = null) {
|
function createComponentElBinder(nestedProtoView = null) {
|
||||||
var binding = createDirectiveBinding(SomeComponent);
|
var binding = createDirectiveBinding(SomeComponent);
|
||||||
|
@ -86,15 +81,17 @@ export function main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function createElementInjector() {
|
function createElementInjector() {
|
||||||
return SpyObject.stub(new SpyElementInjector(), {
|
return SpyObject.stub(new SpyElementInjector(),
|
||||||
'isExportingComponent' : false,
|
{
|
||||||
'isExportingElement' : false,
|
'isExportingComponent': false,
|
||||||
'getEventEmitterAccessors' : [],
|
'isExportingElement': false,
|
||||||
'getComponent' : null
|
'getEventEmitterAccessors': [],
|
||||||
}, {});
|
'getComponent': null
|
||||||
|
},
|
||||||
|
{});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createView(pv=null, renderViewRef=null) {
|
function createView(pv = null, renderViewRef = null) {
|
||||||
if (isBlank(pv)) {
|
if (isBlank(pv)) {
|
||||||
pv = createProtoView();
|
pv = createProtoView();
|
||||||
}
|
}
|
||||||
|
@ -104,19 +101,15 @@ export function main() {
|
||||||
var view = new AppView(renderer, pv, MapWrapper.create());
|
var view = new AppView(renderer, pv, MapWrapper.create());
|
||||||
view.render = renderViewRef;
|
view.render = renderViewRef;
|
||||||
var elementInjectors = ListWrapper.createFixedSize(pv.elementBinders.length);
|
var elementInjectors = ListWrapper.createFixedSize(pv.elementBinders.length);
|
||||||
for (var i=0; i<pv.elementBinders.length; i++) {
|
for (var i = 0; i < pv.elementBinders.length; i++) {
|
||||||
elementInjectors[i] = createElementInjector();
|
elementInjectors[i] = createElementInjector();
|
||||||
}
|
}
|
||||||
view.init(null,
|
view.init(null, elementInjectors, [], ListWrapper.createFixedSize(pv.elementBinders.length),
|
||||||
elementInjectors,
|
ListWrapper.createFixedSize(pv.elementBinders.length));
|
||||||
[],
|
|
||||||
ListWrapper.createFixedSize(pv.elementBinders.length),
|
|
||||||
ListWrapper.createFixedSize(pv.elementBinders.length)
|
|
||||||
);
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach( () => {
|
beforeEach(() => {
|
||||||
directiveResolver = new DirectiveResolver();
|
directiveResolver = new DirectiveResolver();
|
||||||
renderer = new SpyRenderer();
|
renderer = new SpyRenderer();
|
||||||
utils = new SpyAppViewManagerUtils();
|
utils = new SpyAppViewManagerUtils();
|
||||||
|
@ -125,15 +118,18 @@ export function main() {
|
||||||
createdViews = [];
|
createdViews = [];
|
||||||
createdRenderViews = [];
|
createdRenderViews = [];
|
||||||
|
|
||||||
utils.spy('createView').andCallFake( (proto, renderViewRef, _a, _b) => {
|
utils.spy('createView')
|
||||||
|
.andCallFake((proto, renderViewRef, _a, _b) => {
|
||||||
var view = createView(proto, renderViewRef);
|
var view = createView(proto, renderViewRef);
|
||||||
ListWrapper.push(createdViews, view);
|
ListWrapper.push(createdViews, view);
|
||||||
return view;
|
return view;
|
||||||
});
|
});
|
||||||
utils.spy('attachComponentView').andCallFake( (hostView, elementIndex, childView) => {
|
utils.spy('attachComponentView')
|
||||||
|
.andCallFake((hostView, elementIndex, childView) => {
|
||||||
hostView.componentChildViews[elementIndex] = childView;
|
hostView.componentChildViews[elementIndex] = childView;
|
||||||
});
|
});
|
||||||
utils.spy('attachViewInContainer').andCallFake( (parentView, elementIndex, _a, _b, atIndex, childView) => {
|
utils.spy('attachViewInContainer')
|
||||||
|
.andCallFake((parentView, elementIndex, _a, _b, atIndex, childView) => {
|
||||||
var viewContainer = parentView.viewContainers[elementIndex];
|
var viewContainer = parentView.viewContainers[elementIndex];
|
||||||
if (isBlank(viewContainer)) {
|
if (isBlank(viewContainer)) {
|
||||||
viewContainer = new AppViewContainer();
|
viewContainer = new AppViewContainer();
|
||||||
|
@ -141,12 +137,14 @@ export function main() {
|
||||||
}
|
}
|
||||||
ListWrapper.insert(viewContainer.views, atIndex, childView);
|
ListWrapper.insert(viewContainer.views, atIndex, childView);
|
||||||
});
|
});
|
||||||
renderer.spy('createRootHostView').andCallFake( (_b, _c) => {
|
renderer.spy('createRootHostView')
|
||||||
|
.andCallFake((_b, _c) => {
|
||||||
var rv = new RenderViewRef();
|
var rv = new RenderViewRef();
|
||||||
ListWrapper.push(createdRenderViews, rv);
|
ListWrapper.push(createdRenderViews, rv);
|
||||||
return rv;
|
return rv;
|
||||||
});
|
});
|
||||||
renderer.spy('createView').andCallFake( (_a) => {
|
renderer.spy('createView')
|
||||||
|
.andCallFake((_a) => {
|
||||||
var rv = new RenderViewRef();
|
var rv = new RenderViewRef();
|
||||||
ListWrapper.push(createdRenderViews, rv);
|
ListWrapper.push(createdRenderViews, rv);
|
||||||
return rv;
|
return rv;
|
||||||
|
@ -157,60 +155,67 @@ export function main() {
|
||||||
|
|
||||||
describe('basic functionality', () => {
|
describe('basic functionality', () => {
|
||||||
var hostView, componentProtoView;
|
var hostView, componentProtoView;
|
||||||
beforeEach( () => {
|
beforeEach(() => {
|
||||||
hostView = createView(createProtoView(
|
hostView = createView(createProtoView([createComponentElBinder(null)]));
|
||||||
[createComponentElBinder(null)]
|
|
||||||
));
|
|
||||||
componentProtoView = createProtoView();
|
componentProtoView = createProtoView();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create the view', () => {
|
it('should create the view', () => {
|
||||||
expect(
|
expect(internalView(manager.createDynamicComponentView(
|
||||||
internalView(manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null))
|
elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null)))
|
||||||
).toBe(createdViews[0]);
|
.toBe(createdViews[0]);
|
||||||
expect(createdViews[0].proto).toBe(componentProtoView);
|
expect(createdViews[0].proto).toBe(componentProtoView);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should get the view from the pool', () => {
|
it('should get the view from the pool', () => {
|
||||||
var createdView;
|
var createdView;
|
||||||
viewPool.spy('getView').andCallFake( (protoView) => {
|
viewPool.spy('getView').andCallFake((protoView) => {
|
||||||
createdView = createView(protoView);
|
createdView = createView(protoView);
|
||||||
return createdView;
|
return createdView;
|
||||||
});
|
});
|
||||||
expect(
|
expect(internalView(manager.createDynamicComponentView(
|
||||||
internalView(manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null))
|
elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null)))
|
||||||
).toBe(createdView);
|
.toBe(createdView);
|
||||||
expect(utils.spy('createView')).not.toHaveBeenCalled();
|
expect(utils.spy('createView')).not.toHaveBeenCalled();
|
||||||
expect(renderer.spy('createView')).not.toHaveBeenCalled();
|
expect(renderer.spy('createView')).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should attach the view', () => {
|
it('should attach the view', () => {
|
||||||
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null)
|
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0),
|
||||||
expect(utils.spy('attachComponentView')).toHaveBeenCalledWith(hostView, 0, createdViews[0]);
|
wrapPv(componentProtoView), null, null)
|
||||||
expect(renderer.spy('attachComponentView')).toHaveBeenCalledWith(hostView.render, 0, createdViews[0].render);
|
expect(utils.spy('attachComponentView'))
|
||||||
|
.toHaveBeenCalledWith(hostView, 0, createdViews[0]);
|
||||||
|
expect(renderer.spy('attachComponentView'))
|
||||||
|
.toHaveBeenCalledWith(hostView.render, 0, createdViews[0].render);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should hydrate the dynamic component', () => {
|
it('should hydrate the dynamic component', () => {
|
||||||
var injector = new Injector([], null, false);
|
var injector = new Injector([], null, false);
|
||||||
var componentBinding = bind(SomeComponent).toClass(SomeComponent);
|
var componentBinding = bind(SomeComponent).toClass(SomeComponent);
|
||||||
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), componentBinding, injector);
|
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0),
|
||||||
expect(utils.spy('hydrateDynamicComponentInElementInjector')).toHaveBeenCalledWith(hostView, 0, componentBinding, injector);
|
wrapPv(componentProtoView), componentBinding,
|
||||||
|
injector);
|
||||||
|
expect(utils.spy('hydrateDynamicComponentInElementInjector'))
|
||||||
|
.toHaveBeenCalledWith(hostView, 0, componentBinding, injector);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should hydrate the view', () => {
|
it('should hydrate the view', () => {
|
||||||
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null);
|
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0),
|
||||||
|
wrapPv(componentProtoView), null, null);
|
||||||
expect(utils.spy('hydrateComponentView')).toHaveBeenCalledWith(hostView, 0);
|
expect(utils.spy('hydrateComponentView')).toHaveBeenCalledWith(hostView, 0);
|
||||||
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render);
|
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create and set the render view', () => {
|
it('should create and set the render view', () => {
|
||||||
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null);
|
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0),
|
||||||
|
wrapPv(componentProtoView), null, null);
|
||||||
expect(renderer.spy('createView')).toHaveBeenCalledWith(componentProtoView.render);
|
expect(renderer.spy('createView')).toHaveBeenCalledWith(componentProtoView.render);
|
||||||
expect(createdViews[0].render).toBe(createdRenderViews[0]);
|
expect(createdViews[0].render).toBe(createdRenderViews[0]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set the event dispatcher', () => {
|
it('should set the event dispatcher', () => {
|
||||||
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null);
|
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0),
|
||||||
|
wrapPv(componentProtoView), null, null);
|
||||||
var cmpView = createdViews[0];
|
var cmpView = createdViews[0];
|
||||||
expect(renderer.spy('setEventDispatcher')).toHaveBeenCalledWith(cmpView.render, cmpView);
|
expect(renderer.spy('setEventDispatcher')).toHaveBeenCalledWith(cmpView.render, cmpView);
|
||||||
});
|
});
|
||||||
|
@ -219,23 +224,19 @@ export function main() {
|
||||||
describe('error cases', () => {
|
describe('error cases', () => {
|
||||||
|
|
||||||
it('should not allow to use non component indices', () => {
|
it('should not allow to use non component indices', () => {
|
||||||
var hostView = createView(createProtoView(
|
var hostView = createView(createProtoView([createEmptyElBinder()]));
|
||||||
[createEmptyElBinder()]
|
|
||||||
));
|
|
||||||
var componentProtoView = createProtoView();
|
var componentProtoView = createProtoView();
|
||||||
expect(
|
expect(() => manager.createDynamicComponentView(elementRef(wrapView(hostView), 0),
|
||||||
() => manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null)
|
wrapPv(componentProtoView), null, null))
|
||||||
).toThrowError('There is no dynamic component directive at element 0');
|
.toThrowError('There is no dynamic component directive at element 0');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not allow to use static component indices', () => {
|
it('should not allow to use static component indices', () => {
|
||||||
var hostView = createView(createProtoView(
|
var hostView = createView(createProtoView([createComponentElBinder(createProtoView())]));
|
||||||
[createComponentElBinder(createProtoView())]
|
|
||||||
));
|
|
||||||
var componentProtoView = createProtoView();
|
var componentProtoView = createProtoView();
|
||||||
expect(
|
expect(() => manager.createDynamicComponentView(elementRef(wrapView(hostView), 0),
|
||||||
() => manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null)
|
wrapPv(componentProtoView), null, null))
|
||||||
).toThrowError('There is no dynamic component directive at element 0');
|
.toThrowError('There is no dynamic component directive at element 0');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -250,41 +251,42 @@ export function main() {
|
||||||
|
|
||||||
describe('recursively create when not cached', () => {
|
describe('recursively create when not cached', () => {
|
||||||
var hostView, componentProtoView, nestedProtoView;
|
var hostView, componentProtoView, nestedProtoView;
|
||||||
beforeEach( () => {
|
beforeEach(() => {
|
||||||
hostView = createView(createProtoView(
|
hostView = createView(createProtoView([createComponentElBinder(null)]));
|
||||||
[createComponentElBinder(null)]
|
|
||||||
));
|
|
||||||
nestedProtoView = createProtoView();
|
nestedProtoView = createProtoView();
|
||||||
componentProtoView = createProtoView([
|
componentProtoView = createProtoView([createComponentElBinder(nestedProtoView)]);
|
||||||
createComponentElBinder(nestedProtoView)
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create the view', () => {
|
it('should create the view', () => {
|
||||||
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null);
|
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0),
|
||||||
|
wrapPv(componentProtoView), null, null);
|
||||||
expect(createdViews[0].proto).toBe(componentProtoView);
|
expect(createdViews[0].proto).toBe(componentProtoView);
|
||||||
expect(createdViews[1].proto).toBe(nestedProtoView);
|
expect(createdViews[1].proto).toBe(nestedProtoView);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should hydrate the view', () => {
|
it('should hydrate the view', () => {
|
||||||
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null);
|
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0),
|
||||||
|
wrapPv(componentProtoView), null, null);
|
||||||
expect(utils.spy('hydrateComponentView')).toHaveBeenCalledWith(createdViews[0], 0);
|
expect(utils.spy('hydrateComponentView')).toHaveBeenCalledWith(createdViews[0], 0);
|
||||||
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render);
|
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set the render view', () => {
|
it('should set the render view', () => {
|
||||||
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null);
|
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0),
|
||||||
|
wrapPv(componentProtoView), null, null);
|
||||||
expect(createdViews[1].render).toBe(createdRenderViews[1])
|
expect(createdViews[1].render).toBe(createdRenderViews[1])
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set the event dispatcher', () => {
|
it('should set the event dispatcher', () => {
|
||||||
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null);
|
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0),
|
||||||
|
wrapPv(componentProtoView), null, null);
|
||||||
var cmpView = createdViews[1];
|
var cmpView = createdViews[1];
|
||||||
expect(renderer.spy('setEventDispatcher')).toHaveBeenCalledWith(cmpView.render, cmpView);
|
expect(renderer.spy('setEventDispatcher')).toHaveBeenCalledWith(cmpView.render, cmpView);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('recursively hydrate when getting from from the cache', () => {
|
describe('recursively hydrate when getting from from the cache',
|
||||||
|
() => {
|
||||||
// TODO(tbosch): implement this
|
// TODO(tbosch): implement this
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -301,39 +303,39 @@ export function main() {
|
||||||
|
|
||||||
describe('basic functionality', () => {
|
describe('basic functionality', () => {
|
||||||
var parentHostView, parentView, hostProtoView;
|
var parentHostView, parentView, hostProtoView;
|
||||||
beforeEach( () => {
|
beforeEach(() => {
|
||||||
parentHostView = createView(createProtoView(
|
parentHostView = createView(createProtoView([createComponentElBinder(null)]));
|
||||||
[createComponentElBinder(null)]
|
|
||||||
));
|
|
||||||
parentView = createView();
|
parentView = createView();
|
||||||
utils.attachComponentView(parentHostView, 0, parentView);
|
utils.attachComponentView(parentHostView, 0, parentView);
|
||||||
hostProtoView = createProtoView(
|
hostProtoView = createProtoView([createComponentElBinder(null)]);
|
||||||
[createComponentElBinder(null)]
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create the view', () => {
|
it('should create the view', () => {
|
||||||
expect(
|
expect(internalView(manager.createFreeHostView(elementRef(wrapView(parentHostView), 0),
|
||||||
internalView(manager.createFreeHostView(elementRef(wrapView(parentHostView), 0), wrapPv(hostProtoView), null))
|
wrapPv(hostProtoView), null)))
|
||||||
).toBe(createdViews[0]);
|
.toBe(createdViews[0]);
|
||||||
expect(createdViews[0].proto).toBe(hostProtoView);
|
expect(createdViews[0].proto).toBe(hostProtoView);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should attachAndHydrate the view', () => {
|
it('should attachAndHydrate the view', () => {
|
||||||
var injector = new Injector([], null, false);
|
var injector = new Injector([], null, false);
|
||||||
manager.createFreeHostView(elementRef(wrapView(parentHostView), 0), wrapPv(hostProtoView), injector);
|
manager.createFreeHostView(elementRef(wrapView(parentHostView), 0), wrapPv(hostProtoView),
|
||||||
expect(utils.spy('attachAndHydrateFreeHostView')).toHaveBeenCalledWith(parentHostView, 0, createdViews[0], injector);
|
injector);
|
||||||
|
expect(utils.spy('attachAndHydrateFreeHostView'))
|
||||||
|
.toHaveBeenCalledWith(parentHostView, 0, createdViews[0], injector);
|
||||||
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render);
|
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create and set the render view', () => {
|
it('should create and set the render view', () => {
|
||||||
manager.createFreeHostView(elementRef(wrapView(parentHostView), 0), wrapPv(hostProtoView), null)
|
manager.createFreeHostView(elementRef(wrapView(parentHostView), 0), wrapPv(hostProtoView),
|
||||||
|
null);
|
||||||
expect(renderer.spy('createView')).toHaveBeenCalledWith(hostProtoView.render);
|
expect(renderer.spy('createView')).toHaveBeenCalledWith(hostProtoView.render);
|
||||||
expect(createdViews[0].render).toBe(createdRenderViews[0]);
|
expect(createdViews[0].render).toBe(createdRenderViews[0]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set the event dispatcher', () => {
|
it('should set the event dispatcher', () => {
|
||||||
manager.createFreeHostView(elementRef(wrapView(parentHostView), 0), wrapPv(hostProtoView), null);
|
manager.createFreeHostView(elementRef(wrapView(parentHostView), 0), wrapPv(hostProtoView),
|
||||||
|
null);
|
||||||
var cmpView = createdViews[0];
|
var cmpView = createdViews[0];
|
||||||
expect(renderer.spy('setEventDispatcher')).toHaveBeenCalledWith(cmpView.render, cmpView);
|
expect(renderer.spy('setEventDispatcher')).toHaveBeenCalledWith(cmpView.render, cmpView);
|
||||||
});
|
});
|
||||||
|
@ -345,16 +347,13 @@ export function main() {
|
||||||
describe('destroyFreeHostView', () => {
|
describe('destroyFreeHostView', () => {
|
||||||
describe('basic functionality', () => {
|
describe('basic functionality', () => {
|
||||||
var parentHostView, parentView, hostProtoView, hostView, hostRenderViewRef;
|
var parentHostView, parentView, hostProtoView, hostView, hostRenderViewRef;
|
||||||
beforeEach( () => {
|
beforeEach(() => {
|
||||||
parentHostView = createView(createProtoView(
|
parentHostView = createView(createProtoView([createComponentElBinder(null)]));
|
||||||
[createComponentElBinder(null)]
|
|
||||||
));
|
|
||||||
parentView = createView();
|
parentView = createView();
|
||||||
utils.attachComponentView(parentHostView, 0, parentView);
|
utils.attachComponentView(parentHostView, 0, parentView);
|
||||||
hostProtoView = createProtoView(
|
hostProtoView = createProtoView([createComponentElBinder(null)]);
|
||||||
[createComponentElBinder(null)]
|
hostView = internalView(manager.createFreeHostView(
|
||||||
);
|
elementRef(wrapView(parentHostView), 0), wrapPv(hostProtoView), null));
|
||||||
hostView = internalView(manager.createFreeHostView(elementRef(wrapView(parentHostView), 0), wrapPv(hostProtoView), null));
|
|
||||||
hostRenderViewRef = hostView.render;
|
hostRenderViewRef = hostView.render;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -371,7 +370,8 @@ export function main() {
|
||||||
|
|
||||||
it('should detach the render view', () => {
|
it('should detach the render view', () => {
|
||||||
manager.destroyFreeHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
|
manager.destroyFreeHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
|
||||||
expect(renderer.spy('detachFreeHostView')).toHaveBeenCalledWith(parentView.render, hostRenderViewRef);
|
expect(renderer.spy('detachFreeHostView'))
|
||||||
|
.toHaveBeenCalledWith(parentView.render, hostRenderViewRef);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the view to the pool', () => {
|
it('should return the view to the pool', () => {
|
||||||
|
@ -389,16 +389,11 @@ export function main() {
|
||||||
describe('createRootHostView', () => {
|
describe('createRootHostView', () => {
|
||||||
|
|
||||||
var hostProtoView;
|
var hostProtoView;
|
||||||
beforeEach( () => {
|
beforeEach(() => { hostProtoView = createProtoView([createComponentElBinder(null)]); });
|
||||||
hostProtoView = createProtoView(
|
|
||||||
[createComponentElBinder(null)]
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should create the view', () => {
|
it('should create the view', () => {
|
||||||
expect(
|
expect(internalView(manager.createRootHostView(wrapPv(hostProtoView), null, null)))
|
||||||
internalView(manager.createRootHostView(wrapPv(hostProtoView), null, null))
|
.toBe(createdViews[0]);
|
||||||
).toBe(createdViews[0]);
|
|
||||||
expect(createdViews[0].proto).toBe(hostProtoView);
|
expect(createdViews[0].proto).toBe(hostProtoView);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -411,14 +406,16 @@ export function main() {
|
||||||
|
|
||||||
it('should create and set the render view using the component selector', () => {
|
it('should create and set the render view using the component selector', () => {
|
||||||
manager.createRootHostView(wrapPv(hostProtoView), null, null)
|
manager.createRootHostView(wrapPv(hostProtoView), null, null)
|
||||||
expect(renderer.spy('createRootHostView')).toHaveBeenCalledWith(hostProtoView.render, 'someComponent');
|
expect(renderer.spy('createRootHostView'))
|
||||||
|
.toHaveBeenCalledWith(hostProtoView.render, 'someComponent');
|
||||||
expect(createdViews[0].render).toBe(createdRenderViews[0]);
|
expect(createdViews[0].render).toBe(createdRenderViews[0]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow to override the selector', () => {
|
it('should allow to override the selector', () => {
|
||||||
var selector = 'someOtherSelector';
|
var selector = 'someOtherSelector';
|
||||||
manager.createRootHostView(wrapPv(hostProtoView), selector, null)
|
manager.createRootHostView(wrapPv(hostProtoView), selector, null)
|
||||||
expect(renderer.spy('createRootHostView')).toHaveBeenCalledWith(hostProtoView.render, selector);
|
expect(renderer.spy('createRootHostView'))
|
||||||
|
.toHaveBeenCalledWith(hostProtoView.render, selector);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set the event dispatcher', () => {
|
it('should set the event dispatcher', () => {
|
||||||
|
@ -432,10 +429,8 @@ export function main() {
|
||||||
|
|
||||||
describe('destroyRootHostView', () => {
|
describe('destroyRootHostView', () => {
|
||||||
var hostProtoView, hostView, hostRenderViewRef;
|
var hostProtoView, hostView, hostRenderViewRef;
|
||||||
beforeEach( () => {
|
beforeEach(() => {
|
||||||
hostProtoView = createProtoView(
|
hostProtoView = createProtoView([createComponentElBinder(null)]);
|
||||||
[createComponentElBinder(null)]
|
|
||||||
);
|
|
||||||
hostView = internalView(manager.createRootHostView(wrapPv(hostProtoView), null, null));
|
hostView = internalView(manager.createRootHostView(wrapPv(hostProtoView), null, null));
|
||||||
hostRenderViewRef = hostView.render;
|
hostRenderViewRef = hostView.render;
|
||||||
});
|
});
|
||||||
|
@ -462,52 +457,59 @@ export function main() {
|
||||||
|
|
||||||
describe('basic functionality', () => {
|
describe('basic functionality', () => {
|
||||||
var parentView, childProtoView;
|
var parentView, childProtoView;
|
||||||
beforeEach( () => {
|
beforeEach(() => {
|
||||||
parentView = createView(createProtoView(
|
parentView = createView(createProtoView([createEmptyElBinder()]));
|
||||||
[createEmptyElBinder()]
|
|
||||||
));
|
|
||||||
childProtoView = createProtoView();
|
childProtoView = createProtoView();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create a ViewContainerRef if not yet existing', () => {
|
it('should create a ViewContainerRef if not yet existing', () => {
|
||||||
manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0, wrapPv(childProtoView), null);
|
manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0,
|
||||||
|
wrapPv(childProtoView), null);
|
||||||
expect(parentView.viewContainers[0]).toBeTruthy();
|
expect(parentView.viewContainers[0]).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create the view', () => {
|
it('should create the view', () => {
|
||||||
expect(
|
expect(internalView(manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0,
|
||||||
internalView(manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0, wrapPv(childProtoView), null))
|
wrapPv(childProtoView), null)))
|
||||||
).toBe(createdViews[0]);
|
.toBe(createdViews[0]);
|
||||||
expect(createdViews[0].proto).toBe(childProtoView);
|
expect(createdViews[0].proto).toBe(childProtoView);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should attach the view', () => {
|
it('should attach the view', () => {
|
||||||
var contextView = createView();
|
var contextView = createView();
|
||||||
manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0, wrapPv(childProtoView),
|
manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0,
|
||||||
|
wrapPv(childProtoView),
|
||||||
elementRef(wrapView(contextView), 1), null);
|
elementRef(wrapView(contextView), 1), null);
|
||||||
expect(utils.spy('attachViewInContainer')).toHaveBeenCalledWith(parentView, 0, contextView, 1, 0, createdViews[0]);
|
expect(utils.spy('attachViewInContainer'))
|
||||||
expect(renderer.spy('attachViewInContainer')).toHaveBeenCalledWith(parentView.render, 0, 0, createdViews[0].render);
|
.toHaveBeenCalledWith(parentView, 0, contextView, 1, 0, createdViews[0]);
|
||||||
|
expect(renderer.spy('attachViewInContainer'))
|
||||||
|
.toHaveBeenCalledWith(parentView.render, 0, 0, createdViews[0].render);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should hydrate the view', () => {
|
it('should hydrate the view', () => {
|
||||||
var injector = new Injector([], null, false);
|
var injector = new Injector([], null, false);
|
||||||
var contextView = createView();
|
var contextView = createView();
|
||||||
manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0, wrapPv(childProtoView),
|
manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0,
|
||||||
|
wrapPv(childProtoView),
|
||||||
elementRef(wrapView(contextView), 1), injector);
|
elementRef(wrapView(contextView), 1), injector);
|
||||||
expect(utils.spy('hydrateViewInContainer')).toHaveBeenCalledWith(parentView, 0, contextView, 1, 0, injector);
|
expect(utils.spy('hydrateViewInContainer'))
|
||||||
|
.toHaveBeenCalledWith(parentView, 0, contextView, 1, 0, injector);
|
||||||
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render);
|
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create and set the render view', () => {
|
it('should create and set the render view', () => {
|
||||||
manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0, wrapPv(childProtoView), null, null);
|
manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0,
|
||||||
|
wrapPv(childProtoView), null, null);
|
||||||
expect(renderer.spy('createView')).toHaveBeenCalledWith(childProtoView.render);
|
expect(renderer.spy('createView')).toHaveBeenCalledWith(childProtoView.render);
|
||||||
expect(createdViews[0].render).toBe(createdRenderViews[0]);
|
expect(createdViews[0].render).toBe(createdRenderViews[0]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set the event dispatcher', () => {
|
it('should set the event dispatcher', () => {
|
||||||
manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0, wrapPv(childProtoView), null, null);
|
manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0,
|
||||||
|
wrapPv(childProtoView), null, null);
|
||||||
var childView = createdViews[0];
|
var childView = createdViews[0];
|
||||||
expect(renderer.spy('setEventDispatcher')).toHaveBeenCalledWith(childView.render, childView);
|
expect(renderer.spy('setEventDispatcher'))
|
||||||
|
.toHaveBeenCalledWith(childView.render, childView);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -517,24 +519,25 @@ export function main() {
|
||||||
|
|
||||||
describe('basic functionality', () => {
|
describe('basic functionality', () => {
|
||||||
var parentView, childProtoView, childView;
|
var parentView, childProtoView, childView;
|
||||||
beforeEach( () => {
|
beforeEach(() => {
|
||||||
parentView = createView(createProtoView(
|
parentView = createView(createProtoView([createEmptyElBinder()]));
|
||||||
[createEmptyElBinder()]
|
|
||||||
));
|
|
||||||
childProtoView = createProtoView();
|
childProtoView = createProtoView();
|
||||||
childView = internalView(manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0, wrapPv(childProtoView), null));
|
childView = internalView(manager.createViewInContainer(
|
||||||
|
elementRef(wrapView(parentView), 0), 0, wrapPv(childProtoView), null));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should dehydrate', () => {
|
it('should dehydrate', () => {
|
||||||
manager.destroyViewInContainer(elementRef(wrapView(parentView), 0), 0);
|
manager.destroyViewInContainer(elementRef(wrapView(parentView), 0), 0);
|
||||||
expect(utils.spy('dehydrateView')).toHaveBeenCalledWith(parentView.viewContainers[0].views[0]);
|
expect(utils.spy('dehydrateView'))
|
||||||
|
.toHaveBeenCalledWith(parentView.viewContainers[0].views[0]);
|
||||||
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(childView.render);
|
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(childView.render);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detach', () => {
|
it('should detach', () => {
|
||||||
manager.destroyViewInContainer(elementRef(wrapView(parentView), 0), 0);
|
manager.destroyViewInContainer(elementRef(wrapView(parentView), 0), 0);
|
||||||
expect(utils.spy('detachViewInContainer')).toHaveBeenCalledWith(parentView, 0, 0);
|
expect(utils.spy('detachViewInContainer')).toHaveBeenCalledWith(parentView, 0, 0);
|
||||||
expect(renderer.spy('detachViewInContainer')).toHaveBeenCalledWith(parentView.render, 0, 0, childView.render);
|
expect(renderer.spy('detachViewInContainer'))
|
||||||
|
.toHaveBeenCalledWith(parentView.render, 0, 0, childView.render);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the view to the pool', () => {
|
it('should return the view to the pool', () => {
|
||||||
|
@ -545,24 +548,25 @@ export function main() {
|
||||||
|
|
||||||
describe('recursively destroy views in ViewContainers', () => {
|
describe('recursively destroy views in ViewContainers', () => {
|
||||||
var parentView, childProtoView, childView;
|
var parentView, childProtoView, childView;
|
||||||
beforeEach( () => {
|
beforeEach(() => {
|
||||||
parentView = createView(createProtoView(
|
parentView = createView(createProtoView([createEmptyElBinder()]));
|
||||||
[createEmptyElBinder()]
|
|
||||||
));
|
|
||||||
childProtoView = createProtoView();
|
childProtoView = createProtoView();
|
||||||
childView = internalView(manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0, wrapPv(childProtoView), null));
|
childView = internalView(manager.createViewInContainer(
|
||||||
|
elementRef(wrapView(parentView), 0), 0, wrapPv(childProtoView), null));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should dehydrate', () => {
|
it('should dehydrate', () => {
|
||||||
manager.destroyRootHostView(wrapView(parentView));
|
manager.destroyRootHostView(wrapView(parentView));
|
||||||
expect(utils.spy('dehydrateView')).toHaveBeenCalledWith(parentView.viewContainers[0].views[0]);
|
expect(utils.spy('dehydrateView'))
|
||||||
|
.toHaveBeenCalledWith(parentView.viewContainers[0].views[0]);
|
||||||
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(childView.render);
|
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(childView.render);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detach', () => {
|
it('should detach', () => {
|
||||||
manager.destroyRootHostView(wrapView(parentView));
|
manager.destroyRootHostView(wrapView(parentView));
|
||||||
expect(utils.spy('detachViewInContainer')).toHaveBeenCalledWith(parentView, 0, 0);
|
expect(utils.spy('detachViewInContainer')).toHaveBeenCalledWith(parentView, 0, 0);
|
||||||
expect(renderer.spy('detachViewInContainer')).toHaveBeenCalledWith(parentView.render, 0, 0, childView.render);
|
expect(renderer.spy('detachViewInContainer'))
|
||||||
|
.toHaveBeenCalledWith(parentView.render, 0, 0, childView.render);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return the view to the pool', () => {
|
it('should return the view to the pool', () => {
|
||||||
|
@ -586,40 +590,41 @@ export function main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockProtoViewRef extends RenderProtoViewRef {
|
class MockProtoViewRef extends RenderProtoViewRef {
|
||||||
nestedComponentCount:number;
|
nestedComponentCount: number;
|
||||||
constructor(nestedComponentCount:number) {
|
constructor(nestedComponentCount: number) {
|
||||||
super();
|
super();
|
||||||
this.nestedComponentCount = nestedComponentCount;
|
this.nestedComponentCount = nestedComponentCount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({ selector: 'someComponent' })
|
@Component({selector: 'someComponent'})
|
||||||
class SomeComponent {}
|
class SomeComponent {
|
||||||
|
}
|
||||||
|
|
||||||
@proxy
|
@proxy
|
||||||
@IMPLEMENTS(Renderer)
|
@IMPLEMENTS(Renderer)
|
||||||
class SpyRenderer extends SpyObject {
|
class SpyRenderer extends SpyObject {
|
||||||
constructor(){super(Renderer);}
|
constructor() { super(Renderer); }
|
||||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
noSuchMethod(m) { return super.noSuchMethod(m) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@proxy
|
@proxy
|
||||||
@IMPLEMENTS(AppViewPool)
|
@IMPLEMENTS(AppViewPool)
|
||||||
class SpyAppViewPool extends SpyObject {
|
class SpyAppViewPool extends SpyObject {
|
||||||
constructor(){super(AppViewPool);}
|
constructor() { super(AppViewPool); }
|
||||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
noSuchMethod(m) { return super.noSuchMethod(m) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@proxy
|
@proxy
|
||||||
@IMPLEMENTS(AppViewManagerUtils)
|
@IMPLEMENTS(AppViewManagerUtils)
|
||||||
class SpyAppViewManagerUtils extends SpyObject {
|
class SpyAppViewManagerUtils extends SpyObject {
|
||||||
constructor(){super(AppViewManagerUtils);}
|
constructor() { super(AppViewManagerUtils); }
|
||||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
noSuchMethod(m) { return super.noSuchMethod(m) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@proxy
|
@proxy
|
||||||
@IMPLEMENTS(ElementInjector)
|
@IMPLEMENTS(ElementInjector)
|
||||||
class SpyElementInjector extends SpyObject {
|
class SpyElementInjector extends SpyObject {
|
||||||
constructor(){super(ElementInjector);}
|
constructor() { super(ElementInjector); }
|
||||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
noSuchMethod(m) { return super.noSuchMethod(m) }
|
||||||
}
|
}
|
|
@ -12,7 +12,8 @@ import {
|
||||||
beforeEachBindings,
|
beforeEachBindings,
|
||||||
it,
|
it,
|
||||||
xit,
|
xit,
|
||||||
SpyObject, proxy,
|
SpyObject,
|
||||||
|
proxy,
|
||||||
Log
|
Log
|
||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
|
@ -23,9 +24,13 @@ import {MapWrapper, ListWrapper, StringMapWrapper} from 'angular2/src/facade/col
|
||||||
import {AppProtoView, AppView} from 'angular2/src/core/compiler/view';
|
import {AppProtoView, AppView} from 'angular2/src/core/compiler/view';
|
||||||
import {ChangeDetector} from 'angular2/change_detection';
|
import {ChangeDetector} from 'angular2/change_detection';
|
||||||
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
|
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
|
||||||
import {DirectiveBinding, ElementInjector, PreBuiltObjects} from 'angular2/src/core/compiler/element_injector';
|
import {
|
||||||
|
DirectiveBinding,
|
||||||
|
ElementInjector,
|
||||||
|
PreBuiltObjects
|
||||||
|
} from 'angular2/src/core/compiler/element_injector';
|
||||||
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
import {DirectiveResolver} from 'angular2/src/core/compiler/directive_resolver';
|
||||||
import {Component} from 'angular2/src/core/annotations_impl/annotations';
|
import {Component} from 'angular2/annotations';
|
||||||
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
|
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
|
@ -36,18 +41,14 @@ export function main() {
|
||||||
var directiveResolver;
|
var directiveResolver;
|
||||||
var utils;
|
var utils;
|
||||||
|
|
||||||
function createInjector() {
|
function createInjector() { return new Injector([], null, false); }
|
||||||
return new Injector([], null, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createDirectiveBinding(type) {
|
function createDirectiveBinding(type) {
|
||||||
var annotation = directiveResolver.resolve(type);
|
var annotation = directiveResolver.resolve(type);
|
||||||
return DirectiveBinding.createFromType(type, annotation);
|
return DirectiveBinding.createFromType(type, annotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createEmptyElBinder() {
|
function createEmptyElBinder() { return new ElementBinder(0, null, 0, null, null); }
|
||||||
return new ElementBinder(0, null, 0, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createComponentElBinder(nestedProtoView = null) {
|
function createComponentElBinder(nestedProtoView = null) {
|
||||||
var binding = createDirectiveBinding(SomeComponent);
|
var binding = createDirectiveBinding(SomeComponent);
|
||||||
|
@ -67,38 +68,36 @@ export function main() {
|
||||||
|
|
||||||
function createElementInjector() {
|
function createElementInjector() {
|
||||||
var host = new SpyElementInjector();
|
var host = new SpyElementInjector();
|
||||||
return SpyObject.stub(new SpyElementInjector(), {
|
return SpyObject.stub(new SpyElementInjector(),
|
||||||
'isExportingComponent' : false,
|
{
|
||||||
'isExportingElement' : false,
|
'isExportingComponent': false,
|
||||||
'getEventEmitterAccessors' : [],
|
'isExportingElement': false,
|
||||||
'getHostActionAccessors' : [],
|
'getEventEmitterAccessors': [],
|
||||||
'getComponent' : null,
|
'getHostActionAccessors': [],
|
||||||
|
'getComponent': null,
|
||||||
'getDynamicallyLoadedComponent': null,
|
'getDynamicallyLoadedComponent': null,
|
||||||
'getHost': host
|
'getHost': host
|
||||||
}, {});
|
},
|
||||||
|
{});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createView(pv=null) {
|
function createView(pv = null) {
|
||||||
if (isBlank(pv)) {
|
if (isBlank(pv)) {
|
||||||
pv = createProtoView();
|
pv = createProtoView();
|
||||||
}
|
}
|
||||||
var view = new AppView(null, pv, MapWrapper.create());
|
var view = new AppView(null, pv, MapWrapper.create());
|
||||||
var elementInjectors = ListWrapper.createFixedSize(pv.elementBinders.length);
|
var elementInjectors = ListWrapper.createFixedSize(pv.elementBinders.length);
|
||||||
var preBuiltObjects = ListWrapper.createFixedSize(pv.elementBinders.length);
|
var preBuiltObjects = ListWrapper.createFixedSize(pv.elementBinders.length);
|
||||||
for (var i=0; i<pv.elementBinders.length; i++) {
|
for (var i = 0; i < pv.elementBinders.length; i++) {
|
||||||
elementInjectors[i] = createElementInjector();
|
elementInjectors[i] = createElementInjector();
|
||||||
preBuiltObjects[i] = new SpyPreBuiltObjects();
|
preBuiltObjects[i] = new SpyPreBuiltObjects();
|
||||||
}
|
}
|
||||||
view.init(new SpyChangeDetector(),
|
view.init(<any>new SpyChangeDetector(), elementInjectors, elementInjectors, preBuiltObjects,
|
||||||
elementInjectors,
|
ListWrapper.createFixedSize(pv.elementBinders.length));
|
||||||
elementInjectors,
|
|
||||||
preBuiltObjects,
|
|
||||||
ListWrapper.createFixedSize(pv.elementBinders.length)
|
|
||||||
);
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach( () => {
|
beforeEach(() => {
|
||||||
directiveResolver = new DirectiveResolver();
|
directiveResolver = new DirectiveResolver();
|
||||||
utils = new AppViewManagerUtils(directiveResolver);
|
utils = new AppViewManagerUtils(directiveResolver);
|
||||||
});
|
});
|
||||||
|
@ -106,16 +105,13 @@ export function main() {
|
||||||
describe('hydrateDynamicComponentInElementInjector', () => {
|
describe('hydrateDynamicComponentInElementInjector', () => {
|
||||||
|
|
||||||
it('should not allow to overwrite an existing component', () => {
|
it('should not allow to overwrite an existing component', () => {
|
||||||
var hostView = createView(createProtoView(
|
var hostView = createView(createProtoView([createComponentElBinder(createProtoView())]));
|
||||||
[createComponentElBinder(createProtoView())]
|
|
||||||
));
|
|
||||||
var componentBinding = bind(SomeComponent).toClass(SomeComponent);
|
var componentBinding = bind(SomeComponent).toClass(SomeComponent);
|
||||||
SpyObject.stub(hostView.elementInjectors[0], {
|
SpyObject.stub(hostView.elementInjectors[0],
|
||||||
'getDynamicallyLoadedComponent': new SomeComponent()
|
{'getDynamicallyLoadedComponent': new SomeComponent()});
|
||||||
});
|
expect(() => utils.hydrateDynamicComponentInElementInjector(hostView, 0, componentBinding,
|
||||||
expect(
|
null))
|
||||||
() => utils.hydrateDynamicComponentInElementInjector(hostView, 0, componentBinding, null)
|
.toThrowError('There already is a dynamic component loaded at element 0');
|
||||||
).toThrowError('There already is a dynamic component loaded at element 0');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -129,16 +125,16 @@ export function main() {
|
||||||
var hostView = createView(createProtoView([createComponentElBinder(createProtoView())]));
|
var hostView = createView(createProtoView([createComponentElBinder(createProtoView())]));
|
||||||
hostView.componentChildViews = [componentView];
|
hostView.componentChildViews = [componentView];
|
||||||
|
|
||||||
// (() => () nonsense is required until our transpiler supports type casting
|
var spyEi = <any>componentView.elementInjectors[0];
|
||||||
var spyEi = (() => componentView.elementInjectors[0])();
|
|
||||||
spyEi.spy('hydrate').andCallFake(log.fn('hydrate'));
|
spyEi.spy('hydrate').andCallFake(log.fn('hydrate'));
|
||||||
|
|
||||||
var spyCd = (() => componentView.changeDetector)();
|
var spyCd = <any>componentView.changeDetector;
|
||||||
spyCd.spy('hydrate').andCallFake(log.fn('hydrateCD'));
|
spyCd.spy('hydrate').andCallFake(log.fn('hydrateCD'));
|
||||||
|
|
||||||
utils.hydrateComponentView(hostView, 0)
|
utils.hydrateComponentView(hostView, 0)
|
||||||
|
|
||||||
expect(log.result()).toEqual('hydrate; hydrateCD');
|
expect(log.result())
|
||||||
|
.toEqual('hydrate; hydrateCD');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -148,18 +144,15 @@ export function main() {
|
||||||
it("should set up event listeners", () => {
|
it("should set up event listeners", () => {
|
||||||
var dir = new Object();
|
var dir = new Object();
|
||||||
|
|
||||||
var hostPv = createProtoView([
|
var hostPv = createProtoView([createComponentElBinder(null), createEmptyElBinder()]);
|
||||||
createComponentElBinder(null),
|
|
||||||
createEmptyElBinder()
|
|
||||||
]);
|
|
||||||
var hostView = createView(hostPv);
|
var hostView = createView(hostPv);
|
||||||
var spyEventAccessor1 = SpyObject.stub({"subscribe" : null});
|
var spyEventAccessor1 = SpyObject.stub({"subscribe": null});
|
||||||
SpyObject.stub(hostView.elementInjectors[0], {
|
SpyObject.stub(hostView.elementInjectors[0], {
|
||||||
'getHostActionAccessors': [],
|
'getHostActionAccessors': [],
|
||||||
'getEventEmitterAccessors': [[spyEventAccessor1]],
|
'getEventEmitterAccessors': [[spyEventAccessor1]],
|
||||||
'getDirectiveAtIndex': dir
|
'getDirectiveAtIndex': dir
|
||||||
});
|
});
|
||||||
var spyEventAccessor2 = SpyObject.stub({"subscribe" : null});
|
var spyEventAccessor2 = SpyObject.stub({"subscribe": null});
|
||||||
SpyObject.stub(hostView.elementInjectors[1], {
|
SpyObject.stub(hostView.elementInjectors[1], {
|
||||||
'getHostActionAccessors': [],
|
'getHostActionAccessors': [],
|
||||||
'getEventEmitterAccessors': [[spyEventAccessor2]],
|
'getEventEmitterAccessors': [[spyEventAccessor2]],
|
||||||
|
@ -178,18 +171,15 @@ export function main() {
|
||||||
it("should set up host action listeners", () => {
|
it("should set up host action listeners", () => {
|
||||||
var dir = new Object();
|
var dir = new Object();
|
||||||
|
|
||||||
var hostPv = createProtoView([
|
var hostPv = createProtoView([createComponentElBinder(null), createEmptyElBinder()]);
|
||||||
createComponentElBinder(null),
|
|
||||||
createEmptyElBinder()
|
|
||||||
]);
|
|
||||||
var hostView = createView(hostPv);
|
var hostView = createView(hostPv);
|
||||||
var spyActionAccessor1 = SpyObject.stub({"subscribe" : null});
|
var spyActionAccessor1 = SpyObject.stub({"subscribe": null});
|
||||||
SpyObject.stub(hostView.elementInjectors[0], {
|
SpyObject.stub(hostView.elementInjectors[0], {
|
||||||
'getHostActionAccessors': [[spyActionAccessor1]],
|
'getHostActionAccessors': [[spyActionAccessor1]],
|
||||||
'getEventEmitterAccessors': [],
|
'getEventEmitterAccessors': [],
|
||||||
'getDirectiveAtIndex': dir
|
'getDirectiveAtIndex': dir
|
||||||
});
|
});
|
||||||
var spyActionAccessor2 = SpyObject.stub({"subscribe" : null});
|
var spyActionAccessor2 = SpyObject.stub({"subscribe": null});
|
||||||
SpyObject.stub(hostView.elementInjectors[1], {
|
SpyObject.stub(hostView.elementInjectors[1], {
|
||||||
'getHostActionAccessors': [[spyActionAccessor2]],
|
'getHostActionAccessors': [[spyActionAccessor2]],
|
||||||
'getEventEmitterAccessors': [],
|
'getEventEmitterAccessors': [],
|
||||||
|
@ -211,23 +201,18 @@ export function main() {
|
||||||
var parentView, contextView, childView;
|
var parentView, contextView, childView;
|
||||||
|
|
||||||
function createViews() {
|
function createViews() {
|
||||||
var parentPv = createProtoView([
|
var parentPv = createProtoView([createEmptyElBinder()]);
|
||||||
createEmptyElBinder()
|
|
||||||
]);
|
|
||||||
parentView = createView(parentPv);
|
parentView = createView(parentPv);
|
||||||
|
|
||||||
var contextPv = createProtoView([
|
var contextPv = createProtoView([createEmptyElBinder()]);
|
||||||
createEmptyElBinder()
|
|
||||||
]);
|
|
||||||
contextView = createView(contextPv);
|
contextView = createView(contextPv);
|
||||||
|
|
||||||
var childPv = createProtoView([
|
var childPv = createProtoView([createEmptyElBinder()]);
|
||||||
createEmptyElBinder()
|
|
||||||
]);
|
|
||||||
childView = createView(childPv);
|
childView = createView(childPv);
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should link the views rootElementInjectors after the elementInjector at the given context', () => {
|
it('should link the views rootElementInjectors after the elementInjector at the given context',
|
||||||
|
() => {
|
||||||
createViews();
|
createViews();
|
||||||
utils.attachViewInContainer(parentView, 0, contextView, 0, 0, childView);
|
utils.attachViewInContainer(parentView, 0, contextView, 0, 0, childView);
|
||||||
expect(childView.rootElementInjectors[0].spy('linkAfter'))
|
expect(childView.rootElementInjectors[0].spy('linkAfter'))
|
||||||
|
@ -240,29 +225,25 @@ export function main() {
|
||||||
var parentView, contextView, childView;
|
var parentView, contextView, childView;
|
||||||
|
|
||||||
function createViews() {
|
function createViews() {
|
||||||
var parentPv = createProtoView([
|
var parentPv = createProtoView([createEmptyElBinder()]);
|
||||||
createEmptyElBinder()
|
|
||||||
]);
|
|
||||||
parentView = createView(parentPv);
|
parentView = createView(parentPv);
|
||||||
|
|
||||||
var contextPv = createProtoView([
|
var contextPv = createProtoView([createEmptyElBinder()]);
|
||||||
createEmptyElBinder()
|
|
||||||
]);
|
|
||||||
contextView = createView(contextPv);
|
contextView = createView(contextPv);
|
||||||
|
|
||||||
var childPv = createProtoView([
|
var childPv = createProtoView([createEmptyElBinder()]);
|
||||||
createEmptyElBinder()
|
|
||||||
]);
|
|
||||||
childView = createView(childPv);
|
childView = createView(childPv);
|
||||||
utils.attachViewInContainer(parentView, 0, contextView, 0, 0, childView);
|
utils.attachViewInContainer(parentView, 0, contextView, 0, 0, childView);
|
||||||
}
|
}
|
||||||
|
|
||||||
it("should instantiate the elementInjectors with the host of the context's elementInjector", () => {
|
it("should instantiate the elementInjectors with the host of the context's elementInjector",
|
||||||
|
() => {
|
||||||
createViews();
|
createViews();
|
||||||
|
|
||||||
utils.hydrateViewInContainer(parentView, 0, contextView, 0, 0, null);
|
utils.hydrateViewInContainer(parentView, 0, contextView, 0, 0, null);
|
||||||
expect(childView.rootElementInjectors[0].spy('hydrate'))
|
expect(childView.rootElementInjectors[0].spy('hydrate'))
|
||||||
.toHaveBeenCalledWith(null, contextView.elementInjectors[0].getHost(), childView.preBuiltObjects[0]);
|
.toHaveBeenCalledWith(null, contextView.elementInjectors[0].getHost(),
|
||||||
|
childView.preBuiltObjects[0]);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -271,13 +252,12 @@ export function main() {
|
||||||
var hostView;
|
var hostView;
|
||||||
|
|
||||||
function createViews() {
|
function createViews() {
|
||||||
var hostPv = createProtoView([
|
var hostPv = createProtoView([createComponentElBinder()]);
|
||||||
createComponentElBinder()
|
|
||||||
]);
|
|
||||||
hostView = createView(hostPv);
|
hostView = createView(hostPv);
|
||||||
}
|
}
|
||||||
|
|
||||||
it("should instantiate the elementInjectors with the given injector and an empty host element injector", () => {
|
it("should instantiate the elementInjectors with the given injector and an empty host element injector",
|
||||||
|
() => {
|
||||||
var injector = createInjector();
|
var injector = createInjector();
|
||||||
createViews();
|
createViews();
|
||||||
|
|
||||||
|
@ -289,29 +269,29 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({ selector: 'someComponent' })
|
@Component({selector: 'someComponent'})
|
||||||
class SomeComponent {}
|
class SomeComponent {
|
||||||
|
}
|
||||||
|
|
||||||
@proxy
|
@proxy
|
||||||
@IMPLEMENTS(ElementInjector)
|
@IMPLEMENTS(ElementInjector)
|
||||||
class SpyElementInjector extends SpyObject {
|
class SpyElementInjector extends SpyObject {
|
||||||
constructor(){super(ElementInjector);}
|
constructor() { super(ElementInjector); }
|
||||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
noSuchMethod(m) { return super.noSuchMethod(m) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@proxy
|
@proxy
|
||||||
@IMPLEMENTS(ChangeDetector)
|
@IMPLEMENTS(ChangeDetector)
|
||||||
class SpyChangeDetector extends SpyObject {
|
class SpyChangeDetector extends SpyObject {
|
||||||
constructor(){super(ChangeDetector);}
|
constructor() { super(ChangeDetector); }
|
||||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
noSuchMethod(m) { return super.noSuchMethod(m) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@proxy
|
@proxy
|
||||||
@IMPLEMENTS(PreBuiltObjects)
|
@IMPLEMENTS(PreBuiltObjects)
|
||||||
class SpyPreBuiltObjects extends SpyObject {
|
class SpyPreBuiltObjects extends SpyObject {
|
||||||
constructor(){super(PreBuiltObjects);}
|
constructor() { super(PreBuiltObjects); }
|
||||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
noSuchMethod(m) { return super.noSuchMethod(m) }
|
||||||
}
|
}
|
|
@ -12,7 +12,8 @@ import {
|
||||||
beforeEachBindings,
|
beforeEachBindings,
|
||||||
it,
|
it,
|
||||||
xit,
|
xit,
|
||||||
SpyObject, proxy
|
SpyObject,
|
||||||
|
proxy
|
||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
import {AppViewPool} from 'angular2/src/core/compiler/view_pool';
|
import {AppViewPool} from 'angular2/src/core/compiler/view_pool';
|
||||||
import {AppProtoView, AppView} from 'angular2/src/core/compiler/view';
|
import {AppProtoView, AppView} from 'angular2/src/core/compiler/view';
|
||||||
|
@ -21,20 +22,14 @@ import {MapWrapper, Map} from 'angular2/src/facade/collection';
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('AppViewPool', () => {
|
describe('AppViewPool', () => {
|
||||||
|
|
||||||
function createViewPool({capacity}):AppViewPool {
|
function createViewPool({capacity}): AppViewPool { return new AppViewPool(capacity); }
|
||||||
return new AppViewPool(capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createProtoView() {
|
function createProtoView() { return new AppProtoView(null, null, null); }
|
||||||
return new AppProtoView(null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
function createView(pv) {
|
function createView(pv) { return new AppView(null, pv, MapWrapper.create()); }
|
||||||
return new AppView(null, pv, MapWrapper.create());
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should support multiple AppProtoViews', () => {
|
it('should support multiple AppProtoViews', () => {
|
||||||
var vf = createViewPool({ capacity: 2 });
|
var vf = createViewPool({capacity: 2});
|
||||||
var pv1 = createProtoView();
|
var pv1 = createProtoView();
|
||||||
var pv2 = createProtoView();
|
var pv2 = createProtoView();
|
||||||
var view1 = createView(pv1);
|
var view1 = createView(pv1);
|
||||||
|
@ -48,7 +43,7 @@ export function main() {
|
||||||
|
|
||||||
it('should reuse the newest view that has been returned', () => {
|
it('should reuse the newest view that has been returned', () => {
|
||||||
var pv = createProtoView();
|
var pv = createProtoView();
|
||||||
var vf = createViewPool({ capacity: 2 });
|
var vf = createViewPool({capacity: 2});
|
||||||
var view1 = createView(pv);
|
var view1 = createView(pv);
|
||||||
var view2 = createView(pv);
|
var view2 = createView(pv);
|
||||||
vf.returnView(view1);
|
vf.returnView(view1);
|
||||||
|
@ -59,7 +54,7 @@ export function main() {
|
||||||
|
|
||||||
it('should not add views when the capacity has been reached', () => {
|
it('should not add views when the capacity has been reached', () => {
|
||||||
var pv = createProtoView();
|
var pv = createProtoView();
|
||||||
var vf = createViewPool({ capacity: 2 });
|
var vf = createViewPool({capacity: 2});
|
||||||
var view1 = createView(pv);
|
var view1 = createView(pv);
|
||||||
var view2 = createView(pv);
|
var view2 = createView(pv);
|
||||||
var view3 = createView(pv);
|
var view3 = createView(pv);
|
|
@ -1,84 +0,0 @@
|
||||||
import {
|
|
||||||
AsyncTestCompleter,
|
|
||||||
beforeEach,
|
|
||||||
ddescribe,
|
|
||||||
describe,
|
|
||||||
expect,
|
|
||||||
iit,
|
|
||||||
inject,
|
|
||||||
it,
|
|
||||||
xit
|
|
||||||
} from 'angular2/test_lib';
|
|
||||||
import {TestBed} from 'angular2/src/test_lib/test_bed';
|
|
||||||
import {Directive, Component} from 'angular2/src/core/annotations_impl/annotations';
|
|
||||||
import {Query} from 'angular2/src/core/annotations_impl/di';
|
|
||||||
import {View} from 'angular2/src/core/annotations_impl/view';
|
|
||||||
import {QueryList, NgFor} from 'angular2/angular2';
|
|
||||||
import {Inject} from 'angular2/src/di/annotations_impl';
|
|
||||||
import {forwardRef, resolveForwardRef, bind} from 'angular2/di';
|
|
||||||
import {Type} from 'angular2/src/facade/lang';
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
describe("forwardRef integration", function () {
|
|
||||||
it('should instantiate components which are declared using forwardRef', inject(
|
|
||||||
[TestBed, AsyncTestCompleter],
|
|
||||||
(tb, async) => {
|
|
||||||
tb.createView(App).then((view) => {
|
|
||||||
view.detectChanges();
|
|
||||||
expect(view.rootNodes).toHaveText('frame(lock)');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app',
|
|
||||||
appInjector: [
|
|
||||||
forwardRef(() => Frame)
|
|
||||||
]
|
|
||||||
})
|
|
||||||
@View({
|
|
||||||
template: `<door><lock></lock></door>`,
|
|
||||||
directives: [
|
|
||||||
bind(forwardRef(() => Door)).toClass(forwardRef(() => Door)),
|
|
||||||
bind(forwardRef(() => Lock)).toClass(forwardRef(() => Lock))
|
|
||||||
]
|
|
||||||
})
|
|
||||||
class App {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'Lock'
|
|
||||||
})
|
|
||||||
@View({
|
|
||||||
directives: [NgFor],
|
|
||||||
template: `{{frame.name}}(<span *ng-for="var lock of locks">{{lock.name}}</span>)`
|
|
||||||
})
|
|
||||||
class Door {
|
|
||||||
locks: QueryList;
|
|
||||||
frame: Frame;
|
|
||||||
|
|
||||||
constructor(@Query(forwardRef(() => Lock)) locks: QueryList, @Inject(forwardRef(() => Frame)) frame:Frame) {
|
|
||||||
this.frame = frame;
|
|
||||||
this.locks = locks;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Frame {
|
|
||||||
name: string;
|
|
||||||
constructor() {
|
|
||||||
this.name = 'frame';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Directive({
|
|
||||||
selector: 'lock'
|
|
||||||
})
|
|
||||||
class Lock {
|
|
||||||
name: string;
|
|
||||||
constructor() {
|
|
||||||
this.name = 'lock';
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
import {
|
||||||
|
AsyncTestCompleter,
|
||||||
|
beforeEach,
|
||||||
|
ddescribe,
|
||||||
|
describe,
|
||||||
|
expect,
|
||||||
|
iit,
|
||||||
|
inject,
|
||||||
|
it,
|
||||||
|
xit
|
||||||
|
} from 'angular2/test_lib';
|
||||||
|
import {TestBed} from 'angular2/src/test_lib/test_bed';
|
||||||
|
import {Directive, Component, Query, View} from 'angular2/annotations';
|
||||||
|
import {QueryList, NgFor} from 'angular2/angular2';
|
||||||
|
import {forwardRef, resolveForwardRef, bind, Inject} from 'angular2/di';
|
||||||
|
import {Type} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe("forwardRef integration", function() {
|
||||||
|
it('should instantiate components which are declared using forwardRef',
|
||||||
|
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||||
|
tb.createView(App).then((view) => {
|
||||||
|
view.detectChanges();
|
||||||
|
expect(view.rootNodes).toHaveText('frame(lock)');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'app', appInjector: [forwardRef(() => Frame)]})
|
||||||
|
@View({
|
||||||
|
template: `<door><lock></lock></door>`,
|
||||||
|
directives: [
|
||||||
|
bind(forwardRef(() => Door))
|
||||||
|
.toClass(forwardRef(() => Door)),
|
||||||
|
bind(forwardRef(() => Lock)).toClass(forwardRef(() => Lock))
|
||||||
|
]
|
||||||
|
})
|
||||||
|
class App {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'Lock'})
|
||||||
|
@View({
|
||||||
|
directives: [NgFor],
|
||||||
|
template: `{{frame.name}}(<span *ng-for="var lock of locks">{{lock.name}}</span>)`
|
||||||
|
})
|
||||||
|
class Door {
|
||||||
|
locks: QueryList;
|
||||||
|
frame: Frame;
|
||||||
|
|
||||||
|
constructor(@Query(forwardRef(() => Lock)) locks: QueryList,
|
||||||
|
@Inject(forwardRef(() => Frame)) frame: Frame) {
|
||||||
|
this.frame = frame;
|
||||||
|
this.locks = locks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Frame {
|
||||||
|
name: string;
|
||||||
|
constructor() { this.name = 'frame'; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Directive({selector: 'lock'})
|
||||||
|
class Lock {
|
||||||
|
name: string;
|
||||||
|
constructor() { this.name = 'lock'; }
|
||||||
|
}
|
|
@ -11,9 +11,8 @@ export function main() {
|
||||||
executed = false;
|
executed = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should start with a pending count of 0', () => {
|
it('should start with a pending count of 0',
|
||||||
expect(testability.getPendingCount()).toEqual(0);
|
() => { expect(testability.getPendingCount()).toEqual(0); });
|
||||||
});
|
|
||||||
|
|
||||||
it('should fire whenstable callbacks if pending count is 0', () => {
|
it('should fire whenstable callbacks if pending count is 0', () => {
|
||||||
testability.whenStable(() => executed = true);
|
testability.whenStable(() => executed = true);
|
|
@ -1,595 +0,0 @@
|
||||||
import {
|
|
||||||
AsyncTestCompleter,
|
|
||||||
beforeEach,
|
|
||||||
ddescribe,
|
|
||||||
describe,
|
|
||||||
expect,
|
|
||||||
iit,
|
|
||||||
inject,
|
|
||||||
it,
|
|
||||||
xdescribe,
|
|
||||||
xit,
|
|
||||||
Log,
|
|
||||||
isInInnerZone
|
|
||||||
} from 'angular2/test_lib';
|
|
||||||
|
|
||||||
import {PromiseWrapper, TimerWrapper} from 'angular2/src/facade/async';
|
|
||||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
|
||||||
import {BaseException} from 'angular2/src/facade/lang';
|
|
||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
|
||||||
|
|
||||||
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
|
||||||
|
|
||||||
var isIE = DOM.getUserAgent().indexOf("Trident") > -1;
|
|
||||||
// Schedules a macrotask (using a timer)
|
|
||||||
function macroTask(fn: Function, timer = 1): void {
|
|
||||||
//adds longer timers for passing tests in IE
|
|
||||||
_zone.runOutsideAngular(() => TimerWrapper.setTimeout(fn, isIE ? timer : 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Schedules a microtasks (using a resolved promise .then())
|
|
||||||
function microTask(fn: Function): void {
|
|
||||||
PromiseWrapper.resolve(null).then((_) => { fn(); });
|
|
||||||
}
|
|
||||||
|
|
||||||
var _log;
|
|
||||||
var _errors;
|
|
||||||
var _traces;
|
|
||||||
var _zone;
|
|
||||||
|
|
||||||
function logError(error, stackTrace) {
|
|
||||||
ListWrapper.push(_errors, error);
|
|
||||||
ListWrapper.push(_traces, stackTrace);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
describe("NgZone", () => {
|
|
||||||
|
|
||||||
function createZone(enableLongStackTrace) {
|
|
||||||
var zone = new NgZone({enableLongStackTrace: enableLongStackTrace});
|
|
||||||
zone.initCallbacks({
|
|
||||||
onTurnStart: _log.fn('onTurnStart'),
|
|
||||||
onTurnDone: _log.fn('onTurnDone')
|
|
||||||
});
|
|
||||||
return zone;
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
_log = new Log();
|
|
||||||
_errors = [];
|
|
||||||
_traces = [];
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('long stack trace', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
_zone = createZone(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
commonTests();
|
|
||||||
|
|
||||||
it('should produce long stack traces', inject([AsyncTestCompleter],
|
|
||||||
(async) => {
|
|
||||||
macroTask(() => {
|
|
||||||
_zone.initCallbacks({onErrorHandler: logError});
|
|
||||||
var c = PromiseWrapper.completer();
|
|
||||||
|
|
||||||
_zone.run(() => {
|
|
||||||
TimerWrapper.setTimeout(() => {
|
|
||||||
TimerWrapper.setTimeout(() => {
|
|
||||||
c.resolve(null);
|
|
||||||
throw new BaseException('ccc');
|
|
||||||
}, 0);
|
|
||||||
}, 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
c.promise.then((_) => {
|
|
||||||
expect(_traces.length).toBe(1);
|
|
||||||
expect(_traces[0].length).toBeGreaterThan(1);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should produce long stack traces (when using microtasks)', inject(
|
|
||||||
[AsyncTestCompleter], (async) => {
|
|
||||||
macroTask(() => {
|
|
||||||
_zone.initCallbacks({onErrorHandler: logError});
|
|
||||||
var c = PromiseWrapper.completer();
|
|
||||||
|
|
||||||
_zone.run(() => {
|
|
||||||
microTask(() => {
|
|
||||||
microTask(() => {
|
|
||||||
c.resolve(null);
|
|
||||||
throw new BaseException("ddd");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
c.promise.then((_) => {
|
|
||||||
expect(_traces.length).toBe(1);
|
|
||||||
expect(_traces[0].length).toBeGreaterThan(1);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('short stack trace', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
_zone = createZone(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
commonTests();
|
|
||||||
|
|
||||||
it('should disable long stack traces', inject([AsyncTestCompleter], (async) => {
|
|
||||||
macroTask(() => {
|
|
||||||
_zone.initCallbacks({onErrorHandler: logError});
|
|
||||||
var c = PromiseWrapper.completer();
|
|
||||||
|
|
||||||
_zone.run(() => {
|
|
||||||
TimerWrapper.setTimeout(() => {
|
|
||||||
TimerWrapper.setTimeout(() => {
|
|
||||||
c.resolve(null);
|
|
||||||
throw new BaseException('ccc');
|
|
||||||
}, 0);
|
|
||||||
}, 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
c.promise.then((_) => {
|
|
||||||
expect(_traces.length).toBe(1);
|
|
||||||
expect(_traces[0].length).toEqual(1);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function commonTests() {
|
|
||||||
describe('isInInnerZone', () => {
|
|
||||||
it('should return whether the code executes in the inner zone', () => {
|
|
||||||
expect(isInInnerZone()).toEqual(false);
|
|
||||||
_zone.run(() => {
|
|
||||||
expect(isInInnerZone()).toEqual(true);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('run', () => {
|
|
||||||
it('should return the body return value from run', inject([AsyncTestCompleter], (async) => {
|
|
||||||
macroTask(() => {
|
|
||||||
expect(_zone.run(() => {
|
|
||||||
return 6;
|
|
||||||
})).toEqual(6);
|
|
||||||
});
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should call onTurnStart and onTurnDone', inject([AsyncTestCompleter], (async) => {
|
|
||||||
macroTask(() => {
|
|
||||||
_zone.run(_log.fn('run'));
|
|
||||||
});
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
expect(_log.result()).toEqual('onTurnStart; run; onTurnDone');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should call onTurnStart once before a turn and onTurnDone once after the turn',
|
|
||||||
inject([AsyncTestCompleter], (async) => {
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
_zone.run(() => {
|
|
||||||
_log.add('run start');
|
|
||||||
microTask(_log.fn('async'));
|
|
||||||
_log.add('run end');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
// The microtask (async) is executed after the macrotask (run)
|
|
||||||
expect(_log.result()).toEqual('onTurnStart; run start; run end; async; onTurnDone');
|
|
||||||
async.done();
|
|
||||||
}, 50);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should not run onTurnStart and onTurnDone for nested Zone.run',
|
|
||||||
inject([AsyncTestCompleter], (async) => {
|
|
||||||
macroTask(() => {
|
|
||||||
_zone.run(() => {
|
|
||||||
_log.add('start run');
|
|
||||||
_zone.run(() => {
|
|
||||||
_log.add('nested run');
|
|
||||||
microTask(_log.fn('nested run microtask'));
|
|
||||||
});
|
|
||||||
_log.add('end run');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
expect(_log.result()).toEqual('onTurnStart; start run; nested run; end run; nested run microtask; onTurnDone');
|
|
||||||
async.done();
|
|
||||||
}, 50);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should call onTurnStart and onTurnDone before and after each top-level run',
|
|
||||||
inject([AsyncTestCompleter], (async) => {
|
|
||||||
macroTask(() => {
|
|
||||||
_zone.run(_log.fn('run1'));
|
|
||||||
});
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
_zone.run(_log.fn('run2'));
|
|
||||||
});
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
expect(_log.result()).toEqual('onTurnStart; run1; onTurnDone; onTurnStart; run2; onTurnDone');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should call onTurnStart and onTurnDone before and after each turn',
|
|
||||||
inject([AsyncTestCompleter], (async) => {
|
|
||||||
var a;
|
|
||||||
var b;
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
_zone.run(() => {
|
|
||||||
a = PromiseWrapper.completer();
|
|
||||||
b = PromiseWrapper.completer();
|
|
||||||
|
|
||||||
_log.add('run start');
|
|
||||||
a.promise.then(_log.fn('a then'));
|
|
||||||
b.promise.then(_log.fn('b then'));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
_zone.run(() => {
|
|
||||||
a.resolve('a');
|
|
||||||
b.resolve('b');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
expect(_log.result()).toEqual('onTurnStart; run start; onTurnDone; onTurnStart; a then; b then; onTurnDone');
|
|
||||||
async.done();
|
|
||||||
}, 50);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should run a function outside of the angular zone', inject([AsyncTestCompleter], (async) => {
|
|
||||||
macroTask(() => {
|
|
||||||
_zone.runOutsideAngular(_log.fn('run'));
|
|
||||||
});
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
expect(_log.result()).toEqual('run');
|
|
||||||
async.done()
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should call onTurnStart and onTurnDone when an inner microtask is scheduled from outside angular',
|
|
||||||
inject([AsyncTestCompleter], (async) => {
|
|
||||||
var completer;
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
_zone.runOutsideAngular(() => {
|
|
||||||
completer = PromiseWrapper.completer();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
_zone.run(() => {
|
|
||||||
completer.promise.then(_log.fn('executedMicrotask'));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
_zone.runOutsideAngular(() => {
|
|
||||||
_log.add('scheduling a microtask');
|
|
||||||
completer.resolve(null);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
expect(_log.result()).toEqual(
|
|
||||||
// First VM turn => setup Promise then
|
|
||||||
'onTurnStart; onTurnDone; ' +
|
|
||||||
// Second VM turn (outside of anguler)
|
|
||||||
'scheduling a microtask; ' +
|
|
||||||
// Third VM Turn => execute the microtask (inside angular)
|
|
||||||
'onTurnStart; executedMicrotask; onTurnDone'
|
|
||||||
);
|
|
||||||
async.done();
|
|
||||||
}, 50);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should call onTurnStart before executing a microtask scheduled in onTurnDone as well as ' +
|
|
||||||
'onTurnDone after executing the task', inject([AsyncTestCompleter], (async) => {
|
|
||||||
var ran = false;
|
|
||||||
_zone.initCallbacks({
|
|
||||||
onTurnStart: _log.fn('onTurnStart'),
|
|
||||||
onTurnDone: () => {
|
|
||||||
_log.add('onTurnDone(begin)');
|
|
||||||
if (!ran) {
|
|
||||||
microTask(() => {
|
|
||||||
ran = true;
|
|
||||||
_log.add('executedMicrotask');});
|
|
||||||
}
|
|
||||||
|
|
||||||
_log.add('onTurnDone(end)');
|
|
||||||
}});
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
_zone.run(_log.fn('run'));
|
|
||||||
});
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
expect(_log.result()).toEqual(
|
|
||||||
// First VM turn => 'run' macrotask
|
|
||||||
'onTurnStart; run; onTurnDone(begin); onTurnDone(end); ' +
|
|
||||||
// Second VM Turn => microtask enqueued from onTurnDone
|
|
||||||
'onTurnStart; executedMicrotask; onTurnDone(begin); onTurnDone(end)'
|
|
||||||
);
|
|
||||||
async.done();
|
|
||||||
}, 50);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should call onTurnStart and onTurnDone for a scheduleMicrotask in onTurnDone triggered by ' +
|
|
||||||
'a scheduleMicrotask in run', inject([AsyncTestCompleter], (async) => {
|
|
||||||
var ran = false;
|
|
||||||
_zone.initCallbacks({
|
|
||||||
onTurnStart: _log.fn('onTurnStart'),
|
|
||||||
onTurnDone: () => {
|
|
||||||
_log.add('onTurnDone(begin)');
|
|
||||||
if (!ran) {
|
|
||||||
_log.add('onTurnDone(scheduleMicrotask)');
|
|
||||||
microTask(() => {
|
|
||||||
ran = true;
|
|
||||||
_log.add('onTurnDone(executeMicrotask)');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
_log.add('onTurnDone(end)');
|
|
||||||
}});
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
_zone.run(() => {
|
|
||||||
_log.add('scheduleMicrotask');
|
|
||||||
microTask(_log.fn('run(executeMicrotask)'));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
expect(_log.result()).toEqual(
|
|
||||||
// First VM Turn => a macrotask + the microtask it enqueues
|
|
||||||
'onTurnStart; scheduleMicrotask; run(executeMicrotask); onTurnDone(begin); onTurnDone(scheduleMicrotask); onTurnDone(end); ' +
|
|
||||||
// Second VM Turn => the microtask enqueued from onTurnDone
|
|
||||||
'onTurnStart; onTurnDone(executeMicrotask); onTurnDone(begin); onTurnDone(end)'
|
|
||||||
);
|
|
||||||
async.done();
|
|
||||||
}, 50);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should execute promises scheduled in onTurnStart before promises scheduled in run',
|
|
||||||
inject([AsyncTestCompleter], (async) => {
|
|
||||||
var donePromiseRan = false;
|
|
||||||
var startPromiseRan = false;
|
|
||||||
|
|
||||||
_zone.initCallbacks({
|
|
||||||
onTurnStart: () => {
|
|
||||||
_log.add('onTurnStart(begin)');
|
|
||||||
if (!startPromiseRan) {
|
|
||||||
_log.add('onTurnStart(schedulePromise)');
|
|
||||||
microTask(_log.fn('onTurnStart(executePromise)'));
|
|
||||||
startPromiseRan = true;
|
|
||||||
}
|
|
||||||
_log.add('onTurnStart(end)');
|
|
||||||
},
|
|
||||||
onTurnDone: () => {
|
|
||||||
_log.add('onTurnDone(begin)');
|
|
||||||
if (!donePromiseRan) {
|
|
||||||
_log.add('onTurnDone(schedulePromise)');
|
|
||||||
microTask(_log.fn('onTurnDone(executePromise)'));
|
|
||||||
donePromiseRan = true;
|
|
||||||
}
|
|
||||||
_log.add('onTurnDone(end)');
|
|
||||||
}});
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
_zone.run(() => {
|
|
||||||
_log.add('run start');
|
|
||||||
PromiseWrapper.resolve(null)
|
|
||||||
.then((_) => {
|
|
||||||
_log.add('promise then');
|
|
||||||
PromiseWrapper.resolve(null).then(_log.fn('promise foo'));
|
|
||||||
return PromiseWrapper.resolve(null);
|
|
||||||
})
|
|
||||||
.then(_log.fn('promise bar'));
|
|
||||||
_log.add('run end');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
expect(_log.result()).toEqual(
|
|
||||||
// First VM turn: enqueue a microtask in onTurnStart
|
|
||||||
'onTurnStart(begin); onTurnStart(schedulePromise); onTurnStart(end); ' +
|
|
||||||
// First VM turn: execute the macrotask which enqueues microtasks
|
|
||||||
'run start; run end; ' +
|
|
||||||
// First VM turn: execute enqueued microtasks
|
|
||||||
'onTurnStart(executePromise); promise then; promise foo; promise bar; ' +
|
|
||||||
// First VM turn: onTurnEnd, enqueue a microtask
|
|
||||||
'onTurnDone(begin); onTurnDone(schedulePromise); onTurnDone(end); ' +
|
|
||||||
// Second VM turn: execute the microtask from onTurnEnd
|
|
||||||
'onTurnStart(begin); onTurnStart(end); onTurnDone(executePromise); onTurnDone(begin); onTurnDone(end)'
|
|
||||||
);
|
|
||||||
async.done();
|
|
||||||
}, 50);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should call onTurnStart and onTurnDone before and after each turn, respectively',
|
|
||||||
inject([AsyncTestCompleter], (async) => {
|
|
||||||
var completerA, completerB;
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
_zone.run(() => {
|
|
||||||
completerA = PromiseWrapper.completer();
|
|
||||||
completerB = PromiseWrapper.completer();
|
|
||||||
completerA.promise.then(_log.fn('a then'));
|
|
||||||
completerB.promise.then(_log.fn('b then'));
|
|
||||||
_log.add('run start');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
_zone.run(() => {
|
|
||||||
completerA.resolve(null);
|
|
||||||
});
|
|
||||||
}, 10);
|
|
||||||
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
_zone.run(() => {
|
|
||||||
completerB.resolve(null);
|
|
||||||
});
|
|
||||||
}, 30);
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
expect(_log.result()).toEqual(
|
|
||||||
// First VM turn
|
|
||||||
'onTurnStart; run start; onTurnDone; ' +
|
|
||||||
// Second VM turn
|
|
||||||
'onTurnStart; a then; onTurnDone; ' +
|
|
||||||
// Third VM turn
|
|
||||||
'onTurnStart; b then; onTurnDone');
|
|
||||||
async.done();
|
|
||||||
}, 60);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should call onTurnStart and onTurnDone before and after (respectively) all turns in a chain',
|
|
||||||
inject([AsyncTestCompleter], (async) => {
|
|
||||||
macroTask(() => {
|
|
||||||
_zone.run(() => {
|
|
||||||
_log.add('run start');
|
|
||||||
microTask(() => {
|
|
||||||
_log.add('async1');
|
|
||||||
microTask(_log.fn('async2'));
|
|
||||||
});
|
|
||||||
_log.add('run end');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
expect(_log.result()).toEqual('onTurnStart; run start; run end; async1; async2; onTurnDone');
|
|
||||||
async.done();
|
|
||||||
}, 50);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should call onTurnStart and onTurnDone for promises created outside of run body',
|
|
||||||
inject([AsyncTestCompleter], (async) => {
|
|
||||||
var promise;
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
_zone.runOutsideAngular(() => {
|
|
||||||
promise = PromiseWrapper.resolve(4).then((x) => PromiseWrapper.resolve(x));
|
|
||||||
});
|
|
||||||
|
|
||||||
_zone.run(() => {
|
|
||||||
promise.then(_log.fn('promise then'));
|
|
||||||
_log.add('zone run');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
expect(_log.result()).toEqual('onTurnStart; zone run; onTurnDone; onTurnStart; promise then; onTurnDone');
|
|
||||||
async.done();
|
|
||||||
}, 50);
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('exceptions', () => {
|
|
||||||
it('should call the on error callback when it is defined', inject([AsyncTestCompleter], (async) => {
|
|
||||||
macroTask(() => {
|
|
||||||
_zone.initCallbacks({onErrorHandler: logError});
|
|
||||||
|
|
||||||
var exception = new BaseException('sync');
|
|
||||||
|
|
||||||
_zone.run(() => {
|
|
||||||
throw exception;
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(_errors.length).toBe(1);
|
|
||||||
expect(_errors[0]).toBe(exception);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should call onError for errors from microtasks', inject([AsyncTestCompleter], (async) => {
|
|
||||||
_zone.initCallbacks({onErrorHandler: logError});
|
|
||||||
|
|
||||||
var exception = new BaseException('async');
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
_zone.run(() => {
|
|
||||||
microTask(() => { throw exception; });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
expect(_errors.length).toBe(1);
|
|
||||||
expect(_errors[0]).toEqual(exception);
|
|
||||||
async.done();
|
|
||||||
}, 50);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should call onError when onTurnDone throws and the zone is sync',
|
|
||||||
inject([AsyncTestCompleter], (async) => {
|
|
||||||
var exception = new BaseException('fromOnTurnDone');
|
|
||||||
|
|
||||||
_zone.initCallbacks({
|
|
||||||
onErrorHandler: logError,
|
|
||||||
onTurnDone: () => { throw exception; }
|
|
||||||
});
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
_zone.run(() => { });
|
|
||||||
});
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
expect(_errors.length).toBe(1);
|
|
||||||
expect(_errors[0]).toEqual(exception);
|
|
||||||
async.done();
|
|
||||||
}, 50);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should call onError when onTurnDone throws and the zone is async',
|
|
||||||
inject([AsyncTestCompleter], (async) => {
|
|
||||||
var asyncRan = false;
|
|
||||||
|
|
||||||
var exception = new BaseException('fromOnTurnDone');
|
|
||||||
|
|
||||||
_zone.initCallbacks({
|
|
||||||
onErrorHandler: logError,
|
|
||||||
onTurnDone: () => { throw exception; }});
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
_zone.run(() => {
|
|
||||||
microTask(() => {
|
|
||||||
asyncRan = true;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
macroTask(() => {
|
|
||||||
expect(asyncRan).toBe(true);
|
|
||||||
expect(_errors.length).toBe(1);
|
|
||||||
expect(_errors[0]).toEqual(exception);
|
|
||||||
async.done();
|
|
||||||
}, 50);
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -0,0 +1,554 @@
|
||||||
|
import {
|
||||||
|
AsyncTestCompleter,
|
||||||
|
beforeEach,
|
||||||
|
ddescribe,
|
||||||
|
describe,
|
||||||
|
expect,
|
||||||
|
iit,
|
||||||
|
inject,
|
||||||
|
it,
|
||||||
|
xdescribe,
|
||||||
|
xit,
|
||||||
|
Log,
|
||||||
|
isInInnerZone
|
||||||
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
|
import {PromiseWrapper, TimerWrapper} from 'angular2/src/facade/async';
|
||||||
|
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||||
|
import {BaseException} from 'angular2/src/facade/lang';
|
||||||
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
|
|
||||||
|
import {NgZone} from 'angular2/src/core/zone/ng_zone';
|
||||||
|
|
||||||
|
var isIE = DOM.getUserAgent().indexOf("Trident") > -1;
|
||||||
|
// Schedules a macrotask (using a timer)
|
||||||
|
function macroTask(fn: Function, timer = 1): void {
|
||||||
|
// adds longer timers for passing tests in IE
|
||||||
|
_zone.runOutsideAngular(() => TimerWrapper.setTimeout(fn, isIE ? timer : 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Schedules a microtasks (using a resolved promise .then())
|
||||||
|
function microTask(fn: Function): void {
|
||||||
|
PromiseWrapper.resolve(null).then((_) => { fn(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
var _log;
|
||||||
|
var _errors;
|
||||||
|
var _traces;
|
||||||
|
var _zone;
|
||||||
|
|
||||||
|
function logError(error, stackTrace) {
|
||||||
|
ListWrapper.push(_errors, error);
|
||||||
|
ListWrapper.push(_traces, stackTrace);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe("NgZone", () => {
|
||||||
|
|
||||||
|
function createZone(enableLongStackTrace) {
|
||||||
|
var zone = new NgZone({enableLongStackTrace: enableLongStackTrace});
|
||||||
|
zone.initCallbacks({onTurnStart: _log.fn('onTurnStart'), onTurnDone: _log.fn('onTurnDone')});
|
||||||
|
return zone;
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
_log = new Log();
|
||||||
|
_errors = [];
|
||||||
|
_traces = [];
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('long stack trace', () => {
|
||||||
|
beforeEach(() => { _zone = createZone(true); });
|
||||||
|
|
||||||
|
commonTests();
|
||||||
|
|
||||||
|
it('should produce long stack traces', inject([AsyncTestCompleter], (async) => {
|
||||||
|
macroTask(() => {
|
||||||
|
_zone.initCallbacks({onErrorHandler: logError});
|
||||||
|
var c = PromiseWrapper.completer();
|
||||||
|
|
||||||
|
_zone.run(() => {
|
||||||
|
TimerWrapper.setTimeout(() => {
|
||||||
|
TimerWrapper.setTimeout(() => {
|
||||||
|
c.resolve(null);
|
||||||
|
throw new BaseException('ccc');
|
||||||
|
}, 0);
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
c.promise.then((_) => {
|
||||||
|
expect(_traces.length).toBe(1);
|
||||||
|
expect(_traces[0].length).toBeGreaterThan(1);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should produce long stack traces (when using microtasks)',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
macroTask(() => {
|
||||||
|
_zone.initCallbacks({onErrorHandler: logError});
|
||||||
|
var c = PromiseWrapper.completer();
|
||||||
|
|
||||||
|
_zone.run(() => {
|
||||||
|
microTask(() => {
|
||||||
|
microTask(() => {
|
||||||
|
c.resolve(null);
|
||||||
|
throw new BaseException("ddd");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
c.promise.then((_) => {
|
||||||
|
expect(_traces.length).toBe(1);
|
||||||
|
expect(_traces[0].length).toBeGreaterThan(1);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('short stack trace', () => {
|
||||||
|
beforeEach(() => { _zone = createZone(false); });
|
||||||
|
|
||||||
|
commonTests();
|
||||||
|
|
||||||
|
it('should disable long stack traces', inject([AsyncTestCompleter], (async) => {
|
||||||
|
macroTask(() => {
|
||||||
|
_zone.initCallbacks({onErrorHandler: logError});
|
||||||
|
var c = PromiseWrapper.completer();
|
||||||
|
|
||||||
|
_zone.run(() => {
|
||||||
|
TimerWrapper.setTimeout(() => {
|
||||||
|
TimerWrapper.setTimeout(() => {
|
||||||
|
c.resolve(null);
|
||||||
|
throw new BaseException('ccc');
|
||||||
|
}, 0);
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
c.promise.then((_) => {
|
||||||
|
expect(_traces.length).toBe(1);
|
||||||
|
expect(_traces[0].length).toEqual(1);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function commonTests() {
|
||||||
|
describe('isInInnerZone',
|
||||||
|
() => {it('should return whether the code executes in the inner zone', () => {
|
||||||
|
expect(isInInnerZone()).toEqual(false);
|
||||||
|
_zone.run(() => { expect(isInInnerZone()).toEqual(true); });
|
||||||
|
})});
|
||||||
|
|
||||||
|
describe('run', () => {
|
||||||
|
it('should return the body return value from run', inject([AsyncTestCompleter], (async) => {
|
||||||
|
macroTask(() => { expect(_zone.run(() => { return 6; })).toEqual(6); });
|
||||||
|
|
||||||
|
macroTask(() => { async.done(); });
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should call onTurnStart and onTurnDone', inject([AsyncTestCompleter], (async) => {
|
||||||
|
macroTask(() => { _zone.run(_log.fn('run')); });
|
||||||
|
|
||||||
|
macroTask(() => {
|
||||||
|
expect(_log.result()).toEqual('onTurnStart; run; onTurnDone');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should call onTurnStart once before a turn and onTurnDone once after the turn',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
|
||||||
|
macroTask(() => {
|
||||||
|
_zone.run(() => {
|
||||||
|
_log.add('run start');
|
||||||
|
microTask(_log.fn('async'));
|
||||||
|
_log.add('run end');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
macroTask(() => {
|
||||||
|
// The microtask (async) is executed after the macrotask (run)
|
||||||
|
expect(_log.result()).toEqual('onTurnStart; run start; run end; async; onTurnDone');
|
||||||
|
async.done();
|
||||||
|
}, 50);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should not run onTurnStart and onTurnDone for nested Zone.run',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
macroTask(() => {
|
||||||
|
_zone.run(() => {
|
||||||
|
_log.add('start run');
|
||||||
|
_zone.run(() => {
|
||||||
|
_log.add('nested run');
|
||||||
|
microTask(_log.fn('nested run microtask'));
|
||||||
|
});
|
||||||
|
_log.add('end run');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
macroTask(() => {
|
||||||
|
expect(_log.result())
|
||||||
|
.toEqual(
|
||||||
|
'onTurnStart; start run; nested run; end run; nested run microtask; onTurnDone');
|
||||||
|
async.done();
|
||||||
|
}, 50);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should call onTurnStart and onTurnDone before and after each top-level run',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
macroTask(() => { _zone.run(_log.fn('run1')); });
|
||||||
|
|
||||||
|
macroTask(() => { _zone.run(_log.fn('run2')); });
|
||||||
|
|
||||||
|
macroTask(() => {
|
||||||
|
expect(_log.result())
|
||||||
|
.toEqual('onTurnStart; run1; onTurnDone; onTurnStart; run2; onTurnDone');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should call onTurnStart and onTurnDone before and after each turn',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
var a;
|
||||||
|
var b;
|
||||||
|
|
||||||
|
macroTask(() => {
|
||||||
|
_zone.run(() => {
|
||||||
|
a = PromiseWrapper.completer();
|
||||||
|
b = PromiseWrapper.completer();
|
||||||
|
|
||||||
|
_log.add('run start');
|
||||||
|
a.promise.then(_log.fn('a then'));
|
||||||
|
b.promise.then(_log.fn('b then'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
macroTask(() => {
|
||||||
|
_zone.run(() => {
|
||||||
|
a.resolve('a');
|
||||||
|
b.resolve('b');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
macroTask(() => {
|
||||||
|
expect(_log.result())
|
||||||
|
.toEqual(
|
||||||
|
'onTurnStart; run start; onTurnDone; onTurnStart; a then; b then; onTurnDone');
|
||||||
|
async.done();
|
||||||
|
}, 50);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should run a function outside of the angular zone',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
macroTask(() => { _zone.runOutsideAngular(_log.fn('run')); });
|
||||||
|
|
||||||
|
macroTask(() => {
|
||||||
|
expect(_log.result()).toEqual('run');
|
||||||
|
async.done()
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should call onTurnStart and onTurnDone when an inner microtask is scheduled from outside angular',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
var completer;
|
||||||
|
|
||||||
|
macroTask(
|
||||||
|
() => { _zone.runOutsideAngular(() => { completer = PromiseWrapper.completer(); }); });
|
||||||
|
|
||||||
|
macroTask(
|
||||||
|
() => { _zone.run(() => { completer.promise.then(_log.fn('executedMicrotask')); }); });
|
||||||
|
|
||||||
|
macroTask(() => {
|
||||||
|
_zone.runOutsideAngular(() => {
|
||||||
|
_log.add('scheduling a microtask');
|
||||||
|
completer.resolve(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
macroTask(() => {
|
||||||
|
expect(_log.result())
|
||||||
|
.toEqual(
|
||||||
|
// First VM turn => setup Promise then
|
||||||
|
'onTurnStart; onTurnDone; ' +
|
||||||
|
// Second VM turn (outside of anguler)
|
||||||
|
'scheduling a microtask; ' +
|
||||||
|
// Third VM Turn => execute the microtask (inside angular)
|
||||||
|
'onTurnStart; executedMicrotask; onTurnDone');
|
||||||
|
async.done();
|
||||||
|
}, 50);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should call onTurnStart before executing a microtask scheduled in onTurnDone as well as ' +
|
||||||
|
'onTurnDone after executing the task',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
var ran = false;
|
||||||
|
_zone.initCallbacks({
|
||||||
|
onTurnStart: _log.fn('onTurnStart'),
|
||||||
|
onTurnDone: () =>
|
||||||
|
{
|
||||||
|
_log.add('onTurnDone(begin)');
|
||||||
|
if (!ran) {
|
||||||
|
microTask(() => {
|
||||||
|
ran = true;
|
||||||
|
_log.add('executedMicrotask');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_log.add('onTurnDone(end)');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
macroTask(() => { _zone.run(_log.fn('run')); });
|
||||||
|
|
||||||
|
macroTask(() => {
|
||||||
|
expect(_log.result())
|
||||||
|
.toEqual(
|
||||||
|
// First VM turn => 'run' macrotask
|
||||||
|
'onTurnStart; run; onTurnDone(begin); onTurnDone(end); ' +
|
||||||
|
// Second VM Turn => microtask enqueued from onTurnDone
|
||||||
|
'onTurnStart; executedMicrotask; onTurnDone(begin); onTurnDone(end)');
|
||||||
|
async.done();
|
||||||
|
}, 50);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should call onTurnStart and onTurnDone for a scheduleMicrotask in onTurnDone triggered by ' +
|
||||||
|
'a scheduleMicrotask in run',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
var ran = false;
|
||||||
|
_zone.initCallbacks({
|
||||||
|
onTurnStart: _log.fn('onTurnStart'),
|
||||||
|
onTurnDone: () =>
|
||||||
|
{
|
||||||
|
_log.add('onTurnDone(begin)');
|
||||||
|
if (!ran) {
|
||||||
|
_log.add('onTurnDone(scheduleMicrotask)');
|
||||||
|
microTask(() => {
|
||||||
|
ran = true;
|
||||||
|
_log.add('onTurnDone(executeMicrotask)');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_log.add('onTurnDone(end)');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
macroTask(() => {
|
||||||
|
_zone.run(() => {
|
||||||
|
_log.add('scheduleMicrotask');
|
||||||
|
microTask(_log.fn('run(executeMicrotask)'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
macroTask(() => {
|
||||||
|
expect(_log.result())
|
||||||
|
.toEqual(
|
||||||
|
// First VM Turn => a macrotask + the microtask it enqueues
|
||||||
|
'onTurnStart; scheduleMicrotask; run(executeMicrotask); onTurnDone(begin); onTurnDone(scheduleMicrotask); onTurnDone(end); ' +
|
||||||
|
// Second VM Turn => the microtask enqueued from onTurnDone
|
||||||
|
'onTurnStart; onTurnDone(executeMicrotask); onTurnDone(begin); onTurnDone(end)');
|
||||||
|
async.done();
|
||||||
|
}, 50);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should execute promises scheduled in onTurnStart before promises scheduled in run',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
var donePromiseRan = false;
|
||||||
|
var startPromiseRan = false;
|
||||||
|
|
||||||
|
_zone.initCallbacks({
|
||||||
|
onTurnStart: () =>
|
||||||
|
{
|
||||||
|
_log.add('onTurnStart(begin)');
|
||||||
|
if (!startPromiseRan) {
|
||||||
|
_log.add('onTurnStart(schedulePromise)');
|
||||||
|
microTask(_log.fn('onTurnStart(executePromise)'));
|
||||||
|
startPromiseRan = true;
|
||||||
|
}
|
||||||
|
_log.add('onTurnStart(end)');
|
||||||
|
},
|
||||||
|
onTurnDone: () =>
|
||||||
|
{
|
||||||
|
_log.add('onTurnDone(begin)');
|
||||||
|
if (!donePromiseRan) {
|
||||||
|
_log.add('onTurnDone(schedulePromise)');
|
||||||
|
microTask(_log.fn('onTurnDone(executePromise)'));
|
||||||
|
donePromiseRan = true;
|
||||||
|
}
|
||||||
|
_log.add('onTurnDone(end)');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
macroTask(() => {
|
||||||
|
_zone.run(() => {
|
||||||
|
_log.add('run start');
|
||||||
|
PromiseWrapper.resolve(null).then((_) => {
|
||||||
|
_log.add('promise then');
|
||||||
|
PromiseWrapper.resolve(null)
|
||||||
|
.then(_log.fn('promise foo'));
|
||||||
|
return PromiseWrapper.resolve(null);
|
||||||
|
}).then(_log.fn('promise bar'));
|
||||||
|
_log.add('run end');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
macroTask(() => {
|
||||||
|
expect(_log.result())
|
||||||
|
.toEqual(
|
||||||
|
// First VM turn: enqueue a microtask in onTurnStart
|
||||||
|
'onTurnStart(begin); onTurnStart(schedulePromise); onTurnStart(end); ' +
|
||||||
|
// First VM turn: execute the macrotask which enqueues microtasks
|
||||||
|
'run start; run end; ' +
|
||||||
|
// First VM turn: execute enqueued microtasks
|
||||||
|
'onTurnStart(executePromise); promise then; promise foo; promise bar; ' +
|
||||||
|
// First VM turn: onTurnEnd, enqueue a microtask
|
||||||
|
'onTurnDone(begin); onTurnDone(schedulePromise); onTurnDone(end); ' +
|
||||||
|
// Second VM turn: execute the microtask from onTurnEnd
|
||||||
|
'onTurnStart(begin); onTurnStart(end); onTurnDone(executePromise); onTurnDone(begin); onTurnDone(end)');
|
||||||
|
async.done();
|
||||||
|
}, 50);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should call onTurnStart and onTurnDone before and after each turn, respectively',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
var completerA, completerB;
|
||||||
|
|
||||||
|
macroTask(() => {
|
||||||
|
_zone.run(() => {
|
||||||
|
completerA = PromiseWrapper.completer();
|
||||||
|
completerB = PromiseWrapper.completer();
|
||||||
|
completerA.promise.then(_log.fn('a then'));
|
||||||
|
completerB.promise.then(_log.fn('b then'));
|
||||||
|
_log.add('run start');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
macroTask(() => { _zone.run(() => { completerA.resolve(null); }); }, 10);
|
||||||
|
|
||||||
|
|
||||||
|
macroTask(() => { _zone.run(() => { completerB.resolve(null); }); }, 30);
|
||||||
|
|
||||||
|
macroTask(() => {
|
||||||
|
expect(_log.result())
|
||||||
|
.toEqual(
|
||||||
|
// First VM turn
|
||||||
|
'onTurnStart; run start; onTurnDone; ' +
|
||||||
|
// Second VM turn
|
||||||
|
'onTurnStart; a then; onTurnDone; ' +
|
||||||
|
// Third VM turn
|
||||||
|
'onTurnStart; b then; onTurnDone');
|
||||||
|
async.done();
|
||||||
|
}, 60);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should call onTurnStart and onTurnDone before and after (respectively) all turns in a chain',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
macroTask(() => {
|
||||||
|
_zone.run(() => {
|
||||||
|
_log.add('run start');
|
||||||
|
microTask(() => {
|
||||||
|
_log.add('async1');
|
||||||
|
microTask(_log.fn('async2'));
|
||||||
|
});
|
||||||
|
_log.add('run end');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
macroTask(() => {
|
||||||
|
expect(_log.result())
|
||||||
|
.toEqual('onTurnStart; run start; run end; async1; async2; onTurnDone');
|
||||||
|
async.done();
|
||||||
|
}, 50);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should call onTurnStart and onTurnDone for promises created outside of run body',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
var promise;
|
||||||
|
|
||||||
|
macroTask(() => {
|
||||||
|
_zone.runOutsideAngular(() => {
|
||||||
|
promise = PromiseWrapper.resolve(4).then((x) => PromiseWrapper.resolve(x));
|
||||||
|
});
|
||||||
|
|
||||||
|
_zone.run(() => {
|
||||||
|
promise.then(_log.fn('promise then'));
|
||||||
|
_log.add('zone run');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
macroTask(() => {
|
||||||
|
expect(_log.result())
|
||||||
|
.toEqual('onTurnStart; zone run; onTurnDone; onTurnStart; promise then; onTurnDone');
|
||||||
|
async.done();
|
||||||
|
}, 50);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('exceptions', () => {
|
||||||
|
it('should call the on error callback when it is defined',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
macroTask(() => {
|
||||||
|
_zone.initCallbacks({onErrorHandler: logError});
|
||||||
|
|
||||||
|
var exception = new BaseException('sync');
|
||||||
|
|
||||||
|
_zone.run(() => { throw exception; });
|
||||||
|
|
||||||
|
expect(_errors.length).toBe(1);
|
||||||
|
expect(_errors[0]).toBe(exception);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should call onError for errors from microtasks', inject([AsyncTestCompleter], (async) => {
|
||||||
|
_zone.initCallbacks({onErrorHandler: logError});
|
||||||
|
|
||||||
|
var exception = new BaseException('async');
|
||||||
|
|
||||||
|
macroTask(() => { _zone.run(() => { microTask(() => { throw exception; }); }); });
|
||||||
|
|
||||||
|
macroTask(() => {
|
||||||
|
expect(_errors.length).toBe(1);
|
||||||
|
expect(_errors[0]).toEqual(exception);
|
||||||
|
async.done();
|
||||||
|
}, 50);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should call onError when onTurnDone throws and the zone is sync',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
var exception = new BaseException('fromOnTurnDone');
|
||||||
|
|
||||||
|
_zone.initCallbacks({onErrorHandler: logError, onTurnDone: () => { throw exception; }});
|
||||||
|
|
||||||
|
macroTask(() => { _zone.run(() => {}); });
|
||||||
|
|
||||||
|
macroTask(() => {
|
||||||
|
expect(_errors.length).toBe(1);
|
||||||
|
expect(_errors[0]).toEqual(exception);
|
||||||
|
async.done();
|
||||||
|
}, 50);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should call onError when onTurnDone throws and the zone is async',
|
||||||
|
inject([AsyncTestCompleter], (async) => {
|
||||||
|
var asyncRan = false;
|
||||||
|
|
||||||
|
var exception = new BaseException('fromOnTurnDone');
|
||||||
|
|
||||||
|
_zone.initCallbacks({onErrorHandler: logError, onTurnDone: () => { throw exception; }});
|
||||||
|
|
||||||
|
macroTask(() => { _zone.run(() => { microTask(() => { asyncRan = true; }); }); });
|
||||||
|
|
||||||
|
macroTask(() => {
|
||||||
|
expect(asyncRan).toBe(true);
|
||||||
|
expect(_errors.length).toBe(1);
|
||||||
|
expect(_errors[0]).toEqual(exception);
|
||||||
|
async.done();
|
||||||
|
}, 50);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue