From 79f564be461b4b57ed80d2ea7a6738c11fe50063 Mon Sep 17 00:00:00 2001 From: Tobias Bosch Date: Tue, 26 May 2015 09:45:15 -0700 Subject: [PATCH] =?UTF-8?q?refactor(core):=20ts=E2=80=99ify=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/angular2/src/dom/dom_adapter.ts | 2 +- modules/angular2/src/test_lib/utils.ts | 4 +- ...nnotations_spec.js => annotations_spec.ts} | 10 +- .../angular2/test/core/application_spec.js | 192 ----- .../angular2/test/core/application_spec.ts | 198 +++++ .../test/core/compiler/compiler_spec.js | 560 -------------- .../test/core/compiler/compiler_spec.ts | 545 ++++++++++++++ ...r_spec.js => component_url_mapper_spec.ts} | 0 ...c.js => directive_metadata_reader_spec.ts} | 20 +- .../compiler/dynamic_component_loader_spec.js | 344 --------- .../compiler/dynamic_component_loader_spec.ts | 332 +++++++++ ...ector_spec.js => element_injector_spec.ts} | 685 +++++++++--------- .../test/core/compiler/integration_spec.ts | 160 ++-- ...ory_spec.js => proto_view_factory_spec.ts} | 54 +- .../core/compiler/query_integration_spec.js | 125 ---- .../core/compiler/query_integration_spec.ts | 117 +++ ...{query_list_spec.js => query_list_spec.ts} | 6 +- .../compiler/reflection_capabilities_spec.js | 147 ---- ...ref_spec.js => view_container_ref_spec.ts} | 25 +- ...w_manager_spec.js => view_manager_spec.ts} | 379 +++++----- ...ils_spec.js => view_manager_utils_spec.ts} | 182 +++-- .../{view_pool_spec.js => view_pool_spec.ts} | 21 +- .../core/forward_ref_integration_spec.es6 | 84 --- .../test/core/forward_ref_integration_spec.ts | 68 ++ ...estability_spec.js => testability_spec.ts} | 5 +- .../angular2/test/core/zone/ng_zone_spec.js | 595 --------------- .../angular2/test/core/zone/ng_zone_spec.ts | 554 ++++++++++++++ 27 files changed, 2558 insertions(+), 2856 deletions(-) rename modules/angular2/test/core/annotations/{annotations_spec.js => annotations_spec.ts} (71%) delete mode 100644 modules/angular2/test/core/application_spec.js create mode 100644 modules/angular2/test/core/application_spec.ts delete mode 100644 modules/angular2/test/core/compiler/compiler_spec.js create mode 100644 modules/angular2/test/core/compiler/compiler_spec.ts rename modules/angular2/test/core/compiler/{component_url_mapper_spec.js => component_url_mapper_spec.ts} (100%) rename modules/angular2/test/core/compiler/{directive_metadata_reader_spec.js => directive_metadata_reader_spec.ts} (51%) delete mode 100644 modules/angular2/test/core/compiler/dynamic_component_loader_spec.js create mode 100644 modules/angular2/test/core/compiler/dynamic_component_loader_spec.ts rename modules/angular2/test/core/compiler/{element_injector_spec.js => element_injector_spec.ts} (59%) rename modules/angular2/test/core/compiler/{proto_view_factory_spec.js => proto_view_factory_spec.ts} (64%) delete mode 100644 modules/angular2/test/core/compiler/query_integration_spec.js create mode 100644 modules/angular2/test/core/compiler/query_integration_spec.ts rename modules/angular2/test/core/compiler/{query_list_spec.js => query_list_spec.ts} (92%) delete mode 100644 modules/angular2/test/core/compiler/reflection_capabilities_spec.js rename modules/angular2/test/core/compiler/{view_container_ref_spec.js => view_container_ref_spec.ts} (76%) rename modules/angular2/test/core/compiler/{view_manager_spec.js => view_manager_spec.ts} (60%) rename modules/angular2/test/core/compiler/{view_manager_utils_spec.js => view_manager_utils_spec.ts} (59%) rename modules/angular2/test/core/compiler/{view_pool_spec.js => view_pool_spec.ts} (76%) delete mode 100644 modules/angular2/test/core/forward_ref_integration_spec.es6 create mode 100644 modules/angular2/test/core/forward_ref_integration_spec.ts rename modules/angular2/test/core/testability/{testability_spec.js => testability_spec.ts} (90%) delete mode 100644 modules/angular2/test/core/zone/ng_zone_spec.js create mode 100644 modules/angular2/test/core/zone/ng_zone_spec.ts diff --git a/modules/angular2/src/dom/dom_adapter.ts b/modules/angular2/src/dom/dom_adapter.ts index 3ecf47ea5d..d0eec48bc0 100644 --- a/modules/angular2/src/dom/dom_adapter.ts +++ b/modules/angular2/src/dom/dom_adapter.ts @@ -114,5 +114,5 @@ export class DomAdapter { getHistory() { throw _abstract(); } getLocation() { throw _abstract(); } getBaseHref() { throw _abstract(); } - getUserAgent() { throw _abstract(); } + getUserAgent(): string { throw _abstract(); } } diff --git a/modules/angular2/src/test_lib/utils.ts b/modules/angular2/src/test_lib/utils.ts index 9e2d8a86a8..0b4ba76023 100644 --- a/modules/angular2/src/test_lib/utils.ts +++ b/modules/angular2/src/test_lib/utils.ts @@ -45,6 +45,6 @@ export function el(html: string) { var _RE_SPECIAL_CHARS = ['-', '[', ']', '/', '{', '}', '\\', '(', ')', '*', '+', '?', '.', '^', '$', '|']; var _ESCAPE_RE = RegExpWrapper.create(`[\\${_RE_SPECIAL_CHARS.join('\\')}]`); 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]}`)); } - diff --git a/modules/angular2/test/core/annotations/annotations_spec.js b/modules/angular2/test/core/annotations/annotations_spec.ts similarity index 71% rename from modules/angular2/test/core/annotations/annotations_spec.js rename to modules/angular2/test/core/annotations/annotations_spec.ts index e2b210652e..6b0f0fe6e9 100644 --- a/modules/angular2/test/core/annotations/annotations_spec.js +++ b/modules/angular2/test/core/annotations/annotations_spec.ts @@ -1,25 +1,21 @@ import {ddescribe, describe, it, iit, expect, beforeEach} from 'angular2/test_lib'; import {Directive, onChange} from 'angular2/src/core/annotations_impl/annotations'; -class DummyDirective extends Directive { - constructor({lifecycle} = {}) { super({lifecycle: lifecycle}); } -} - export function main() { describe("Directive", () => { describe("lifecycle", () => { it("should be false when no lifecycle specified", () => { - var d = new DummyDirective(); + var d = new Directive(); expect(d.hasLifecycleHook(onChange)).toBe(false); }); 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); }); it("should be true otherwise", () => { - var d = new DummyDirective({lifecycle:[onChange]}); + var d = new Directive({lifecycle: [onChange]}); expect(d.hasLifecycleHook(onChange)).toBe(true); }); }); diff --git a/modules/angular2/test/core/application_spec.js b/modules/angular2/test/core/application_spec.js deleted file mode 100644 index f40ed39212..0000000000 --- a/modules/angular2/test/core/application_spec.js +++ /dev/null @@ -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: 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(); - }); - }); - })); - }); -} diff --git a/modules/angular2/test/core/application_spec.ts b/modules/angular2/test/core/application_spec.ts new file mode 100644 index 0000000000..ea363b4c40 --- /dev/null +++ b/modules/angular2/test/core/application_spec.ts @@ -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: 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(); + }); + }); + })); + }); +} diff --git a/modules/angular2/test/core/compiler/compiler_spec.js b/modules/angular2/test/core/compiler/compiler_spec.js deleted file mode 100644 index 280e7b7316..0000000000 --- a/modules/angular2/test/core/compiler/compiler_spec.js +++ /dev/null @@ -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) { - 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 { - 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 { - return captureTemplate(new View({template: '
', 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: '
'})).then( (renderTpl) => { - expect(renderTpl.componentId).toEqual(stringify(MainComponent)); - async.done(); - }); - })); - - it('should fill inline template', inject([AsyncTestCompleter], (async) => { - captureTemplate(new View({template: '
'})).then( (renderTpl) => { - expect(renderTpl.template).toEqual('
'); - async.done(); - }); - })); - - it('should fill absUrl given inline templates', inject([AsyncTestCompleter], (async) => { - cmpUrlMapper.setComponentUrl(MainComponent, '/mainComponent'); - captureTemplate(new View({template: '
'})).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: '
'})); - 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: '
'})); - 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: '
', - 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: '
'})); - 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: '
'})); - tplResolver.setView(NestedComponent, new View({template: '
'})); - 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: '
'})); - tplResolver.setView(NestedComponent, new View({template: '
'})); - 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: '
'})); - 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: '
'})); - 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: '
'})); - 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: '
'})); - 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):List { - ListWrapper.push(this.requests, [componentBinding, renderProtoView, directives]); - return ListWrapper.removeAt(this._results, 0); - } -} diff --git a/modules/angular2/test/core/compiler/compiler_spec.ts b/modules/angular2/test/core/compiler/compiler_spec.ts new file mode 100644 index 0000000000..5b3ebcc772 --- /dev/null +++ b/modules/angular2/test/core/compiler/compiler_spec.ts @@ -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, + protoViewFactoryResults: List>) { + 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 { + 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 { + return captureTemplate( + new viewAnn.View({template: '
', 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: '
'})) + .then((renderTpl) => { + expect(renderTpl.componentId).toEqual(stringify(MainComponent)); + async.done(); + }); + })); + + it('should fill inline template', inject([AsyncTestCompleter], (async) => { + captureTemplate(new viewAnn.View({template: '
'})) + .then((renderTpl) => { + expect(renderTpl.template).toEqual('
'); + async.done(); + }); + })); + + it('should fill absUrl given inline templates', inject([AsyncTestCompleter], (async) => { + cmpUrlMapper.setComponentUrl(MainComponent, '/mainComponent'); + captureTemplate(new viewAnn.View({template: '
'})) + .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: '
'})); + 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: '
'})); + 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: '
', 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: '
'})); + 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: '
'})); + tplResolver.setView(NestedComponent, new viewAnn.View({template: '
'})); + 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: '
'})); + tplResolver.setView(NestedComponent, new viewAnn.View({template: '
'})); + 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: '
'})); + 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: '
'})); + 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: '
'})); + 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: '
'})); + 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): 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>; + + constructor(public results: List>) { + super(null); + this.requests = []; + } + + createAppProtoViews(componentBinding: DirectiveBinding, renderProtoView: renderApi.ProtoViewDto, + directives: List): List { + ListWrapper.push(this.requests, [componentBinding, renderProtoView, directives]); + return ListWrapper.removeAt(this.results, 0); + } +} diff --git a/modules/angular2/test/core/compiler/component_url_mapper_spec.js b/modules/angular2/test/core/compiler/component_url_mapper_spec.ts similarity index 100% rename from modules/angular2/test/core/compiler/component_url_mapper_spec.js rename to modules/angular2/test/core/compiler/component_url_mapper_spec.ts diff --git a/modules/angular2/test/core/compiler/directive_metadata_reader_spec.js b/modules/angular2/test/core/compiler/directive_metadata_reader_spec.ts similarity index 51% rename from modules/angular2/test/core/compiler/directive_metadata_reader_spec.js rename to modules/angular2/test/core/compiler/directive_metadata_reader_spec.ts index 4a2af8cb09..019174c3d6 100644 --- a/modules/angular2/test/core/compiler/directive_metadata_reader_spec.js +++ b/modules/angular2/test/core/compiler/directive_metadata_reader_spec.ts @@ -1,30 +1,28 @@ import {ddescribe, describe, it, iit, expect, beforeEach} from 'angular2/test_lib'; 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'}) -class SomeDirective {} - -class SomeDirectiveWithoutAnnotation { +class SomeDirective { } +class SomeDirectiveWithoutAnnotation {} + export function main() { describe("DirectiveResolver", () => { var reader; - beforeEach(() => { - reader = new DirectiveResolver(); - }); + beforeEach(() => { reader = new DirectiveResolver(); }); it('should read out the Directive annotation', () => { 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', () => { - expect(() => { - reader.resolve(SomeDirectiveWithoutAnnotation); - }).toThrowError('No Directive annotation found on SomeDirectiveWithoutAnnotation'); + expect(() => { reader.resolve(SomeDirectiveWithoutAnnotation); }) + .toThrowError('No Directive annotation found on SomeDirectiveWithoutAnnotation'); }); }); } diff --git a/modules/angular2/test/core/compiler/dynamic_component_loader_spec.js b/modules/angular2/test/core/compiler/dynamic_component_loader_spec.js deleted file mode 100644 index 4ed3a0f2fe..0000000000 --- a/modules/angular2/test/core/compiler/dynamic_component_loader_spec.js +++ /dev/null @@ -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: '', - 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: '', - 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: '
', - 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: '
', - 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: '
', - 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: '
', - 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: '', - 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(''); - 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('
'); - 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; - } -} diff --git a/modules/angular2/test/core/compiler/dynamic_component_loader_spec.ts b/modules/angular2/test/core/compiler/dynamic_component_loader_spec.ts new file mode 100644 index 0000000000..dfd6eb0cde --- /dev/null +++ b/modules/angular2/test/core/compiler/dynamic_component_loader_spec.ts @@ -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: '', + 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: '', + 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: + '
', + 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: '
', 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: '
', 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: '
', 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: '', + 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(''); + 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('
'); + 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; } +} diff --git a/modules/angular2/test/core/compiler/element_injector_spec.js b/modules/angular2/test/core/compiler/element_injector_spec.ts similarity index 59% rename from modules/angular2/test/core/compiler/element_injector_spec.js rename to modules/angular2/test/core/compiler/element_injector_spec.ts index 5fbf61d8ec..ce45074155 100644 --- a/modules/angular2/test/core/compiler/element_injector_spec.js +++ b/modules/angular2/test/core/compiler/element_injector_spec.ts @@ -1,27 +1,57 @@ -import {describe, ddescribe, it, iit, xit, xdescribe, expect, beforeEach, SpyObject, proxy, el} from 'angular2/test_lib'; -import {isBlank, isPresent, IMPLEMENTS} from 'angular2/src/facade/lang'; -import {ListWrapper, MapWrapper, List, StringMapWrapper, iterateListLike} from 'angular2/src/facade/collection'; -import {ProtoElementInjector, ElementInjector, PreBuiltObjects, DirectiveBinding, TreeNode} - from 'angular2/src/core/compiler/element_injector'; -import {Parent, Ancestor, Unbounded} from 'angular2/src/core/annotations_impl/visibility'; -import {Attribute, Query} from 'angular2/src/core/annotations_impl/di'; -import {Component, Directive, onDestroy} from 'angular2/src/core/annotations_impl/annotations'; -import {bind, Injector, Binding, resolveBindings} from 'angular2/di'; -import {Optional, Inject} from 'angular2/src/di/annotations_impl'; +// TODO(tbosch): clang-format screws this up, see https://github.com/angular/clang-format/issues/11. +// Enable clang-format here again when this is fixed. +// clang-format off +import { + describe, + ddescribe, + it, + iit, + xit, + xdescribe, + expect, + beforeEach, + SpyObject, + proxy, + el, + containsRegexp +} from 'angular2/test_lib'; +import {isBlank, isPresent, IMPLEMENTS, stringify} from 'angular2/src/facade/lang'; +import { + ListWrapper, + MapWrapper, + List, + StringMapWrapper, + iterateListLike +} from 'angular2/src/facade/collection'; +import { + ProtoElementInjector, + ElementInjector, + PreBuiltObjects, + DirectiveBinding, + TreeNode +} from 'angular2/src/core/compiler/element_injector'; +import * as dirAnn from 'angular2/src/core/annotations_impl/annotations'; +import { + Parent, + Ancestor, + Unbounded, + Attribute, + Query, + Component, + Directive, + onDestroy +} from 'angular2/annotations'; +import * as ngDiAnn from 'angular2/src/core/annotations_impl/visibility'; +import {bind, Injector, Binding, resolveBindings, Optional, Inject, Injectable} from 'angular2/di'; +import * as diAnn from 'angular2/src/di/annotations_impl'; import {AppProtoView, AppView} from 'angular2/src/core/compiler/view'; import {ViewContainerRef} from 'angular2/src/core/compiler/view_container_ref'; import {ProtoViewRef} from 'angular2/src/core/compiler/view_ref'; import {ElementRef} from 'angular2/src/core/compiler/element_ref'; import {DynamicChangeDetector, ChangeDetectorRef, Parser, Lexer} from 'angular2/change_detection'; -import {ViewRef, Renderer} from 'angular2/src/render/api'; +import {Renderer} from 'angular2/src/render/api'; import {QueryList} from 'angular2/src/core/compiler/query_list'; -class DummyDirective extends Component { - constructor({lifecycle, events, hostActions, hostInjector, viewInjector} = {}) { - super({lifecycle: lifecycle, events: events, hostActions: hostActions, hostInjector: hostInjector, viewInjector: viewInjector}); - } -} - @proxy @IMPLEMENTS(AppView) class DummyView extends SpyObject { @@ -32,18 +62,15 @@ class DummyView extends SpyObject { this.componentChildViews = []; this.changeDetector = null; } - noSuchMethod(m){return super.noSuchMethod(m);} + noSuchMethod(m) { return super.noSuchMethod(m); } } -class SimpleDirective { -} +class SimpleDirective {} -class SimpleService { -} +class SimpleService {} -class SomeOtherDirective { -} +class SomeOtherDirective {} var _constructionCount = 0; class CountingDirective { @@ -55,156 +82,132 @@ class CountingDirective { } class FancyCountingDirective extends CountingDirective { - constructor() { - super(); - } + constructor() { super(); } } +@Injectable() class NeedsDirective { - dependency:SimpleDirective; - constructor(dependency:SimpleDirective){ - this.dependency = dependency; - } + dependency: SimpleDirective; + constructor(dependency: SimpleDirective) { this.dependency = dependency; } } +@Injectable() class OptionallyNeedsDirective { - dependency:SimpleDirective; - constructor(@Optional() dependency:SimpleDirective){ - this.dependency = dependency; - } + dependency: SimpleDirective; + constructor(@Optional() dependency: SimpleDirective) { this.dependency = dependency; } } +@Injectable() class NeedsDirectiveFromParent { - dependency:SimpleDirective; - constructor(@Parent() dependency:SimpleDirective){ - this.dependency = dependency; - } + dependency: SimpleDirective; + constructor(@Parent() dependency: SimpleDirective) { this.dependency = dependency; } } +@Injectable() class NeedsDirectiveFromAncestor { - dependency:SimpleDirective; - constructor(@Ancestor() dependency:SimpleDirective){ - this.dependency = dependency; - } + dependency: SimpleDirective; + constructor(@Ancestor() dependency: SimpleDirective) { this.dependency = dependency; } } +@Injectable() class NeedsDirectiveFromAnAncestorShadowDom { - dependency:SimpleDirective; - constructor(@Unbounded() dependency:SimpleDirective){ - this.dependency = dependency; - } + dependency: SimpleDirective; + constructor(@Unbounded() dependency: SimpleDirective) { this.dependency = dependency; } } +@Injectable() class NeedsService { - service:any; - constructor(@Inject("service") service) { - this.service = service; - } + service: any; + constructor(@Inject("service") service) { this.service = service; } } class HasEventEmitter { emitter; - constructor() { - this.emitter = "emitter"; - } + constructor() { this.emitter = "emitter"; } } class HasHostAction { hostActionName; - constructor() { - this.hostActionName = "hostAction"; - } + constructor() { this.hostActionName = "hostAction"; } } class NeedsAttribute { typeAttribute; titleAttribute; fooAttribute; - constructor(@Attribute('type') typeAttribute: String, @Attribute('title') titleAttribute: String, @Attribute('foo') fooAttribute: String) { + constructor(@Attribute('type') typeAttribute: String, @Attribute('title') titleAttribute: String, + @Attribute('foo') fooAttribute: String) { this.typeAttribute = typeAttribute; this.titleAttribute = titleAttribute; this.fooAttribute = fooAttribute; } } +@Injectable() class NeedsAttributeNoType { fooAttribute; - constructor(@Attribute('foo') fooAttribute) { - this.fooAttribute = fooAttribute; - } + constructor(@Attribute('foo') fooAttribute) { this.fooAttribute = fooAttribute; } } +@Injectable() class NeedsQuery { query: QueryList; - constructor(@Query(CountingDirective) query: QueryList) { - this.query = query; - } + constructor(@Query(CountingDirective) query: QueryList) { this.query = query; } } +@Injectable() class NeedsElementRef { elementRef; - constructor(ref:ElementRef) { - this.elementRef = ref; - } + constructor(ref: ElementRef) { this.elementRef = ref; } } +@Injectable() class NeedsViewContainer { viewContainer; - constructor(vc:ViewContainerRef) { - this.viewContainer = vc; - } + constructor(vc: ViewContainerRef) { this.viewContainer = vc; } } +@Injectable() class NeedsProtoViewRef { protoViewRef; - constructor(ref:ProtoViewRef) { - this.protoViewRef = ref; - } + constructor(ref: ProtoViewRef) { this.protoViewRef = ref; } } +@Injectable() class OptionallyInjectsProtoViewRef { protoViewRef; - constructor(@Optional() ref:ProtoViewRef) { - this.protoViewRef = ref; - } + constructor(@Optional() ref: ProtoViewRef) { this.protoViewRef = ref; } } +@Injectable() class NeedsChangeDetectorRef { changeDetectorRef; - constructor(cdr:ChangeDetectorRef) { - this.changeDetectorRef = cdr; - } + constructor(cdr: ChangeDetectorRef) { this.changeDetectorRef = cdr; } } class A_Needs_B { - constructor(dep){} + constructor(dep) {} } class B_Needs_A { - constructor(dep){} + constructor(dep) {} } class DirectiveWithDestroy { - onDestroyCounter:number; + onDestroyCounter: number; - constructor(){ - this.onDestroyCounter = 0; - } + constructor() { this.onDestroyCounter = 0; } - onDestroy() { - this.onDestroyCounter ++; - } + onDestroy() { this.onDestroyCounter++; } } -class TestNode extends TreeNode { +class TestNode extends TreeNode { message: string; - constructor(parent:TestNode, message) { + constructor(parent: TestNode, message) { super(parent); this.message = message; } - toString() { - return this.message; - } + toString() { return this.message; } } export function main() { @@ -219,7 +222,7 @@ export function main() { } function createPei(parent, index, bindings, distance = 1, hasShadowRoot = false) { - var directiveBinding = ListWrapper.map(bindings, b => { + var directiveBinding = ListWrapper.map(bindings, b => { if (b instanceof DirectiveBinding) return b; if (b instanceof Binding) return DirectiveBinding.createFromBinding(b, null); return DirectiveBinding.createFromType(b, null); @@ -227,21 +230,20 @@ export function main() { return ProtoElementInjector.create(parent, index, directiveBinding, hasShadowRoot, distance); } - function humanize(tree, names:List) { + function humanize(tree, names: List>) { var lookupName = (item) => - ListWrapper.last( - ListWrapper.find(names, (pair) => pair[0] === item)); + ListWrapper.last(ListWrapper.find(names, (pair) => pair[0] === item)); if (tree.children.length == 0) return lookupName(tree); var children = tree.children.map(m => humanize(m, names)); return [lookupName(tree), children]; } - function injector(bindings, lightDomAppInjector = null, - isComponent:bool = false, preBuiltObjects = null, attributes = null) { + function injector(bindings, lightDomAppInjector = null, isComponent: boolean = false, + preBuiltObjects = null, attributes = null) { if (isBlank(lightDomAppInjector)) lightDomAppInjector = appInjector; - var proto = createPei(null, 0, bindings, 0, isComponent); + var proto = createPei(null, 0, bindings, 0, isComponent); proto.attributes = attributes; var inj = proto.instantiate(null); @@ -256,26 +258,27 @@ export function main() { var inj = Injector.resolveAndCreate([]); - var protoParent = createPei(null, 0, parentBindings); + var protoParent = createPei(null, 0, parentBindings); var parent = protoParent.instantiate(null); parent.hydrate(inj, null, parentPreBuildObjects); - var protoChild = createPei(protoParent, 1, childBindings, 1, false); + var protoChild = createPei(protoParent, 1, childBindings, 1, false); var child = protoChild.instantiate(parent); child.hydrate(inj, null, defaultPreBuiltObjects); return child; } - function hostShadowInjectors(hostBindings:List, shadowBindings:List):ElementInjector { + function hostShadowInjectors(hostBindings: List, + shadowBindings: List): ElementInjector { var inj = Injector.resolveAndCreate([]); - var protoHost = createPei(null, 0, hostBindings, 0, true); + var protoHost = createPei(null, 0, hostBindings, 0, true); var host = protoHost.instantiate(null); host.hydrate(inj, null, defaultPreBuiltObjects); - var protoShadow = createPei(null, 0, shadowBindings, 0, false); + var protoShadow = createPei(null, 0, shadowBindings, 0, false); var shadow = protoShadow.instantiate(null); shadow.hydrate(host.getShadowDomAppInjector(), host, null); @@ -312,15 +315,12 @@ export function main() { function logWalk(node) { var log = ''; - walk(node, (n) => { - log += (log.length != 0 ? ', ' : '') + n.toString(); - }); + walk(node, (n) => { log += (log.length != 0 ? ', ' : '') + n.toString(); }); return log; } - it('should support listing children', () => { - expect(logWalk(root)).toEqual('root, p1, c1, c2, p2, c3'); - }); + it('should support listing children', + () => { expect(logWalk(root)).toEqual('root, p1, c1, c2, p2, c3'); }); it('should support removing the first child node', () => { firstParent.remove(); @@ -382,10 +382,8 @@ export function main() { var proto = createPei(null, 0, [bind(SimpleDirective).toClass(SimpleDirective)]); expect(proto.getBindingAtIndex(0)).toBeAnInstanceOf(DirectiveBinding); - expect(() => proto.getBindingAtIndex(-1)).toThrowError( - 'Index -1 is out-of-bounds.'); - expect(() => proto.getBindingAtIndex(10)).toThrowError( - 'Index 10 is out-of-bounds.'); + expect(() => proto.getBindingAtIndex(-1)).toThrowError('Index -1 is out-of-bounds.'); + expect(() => proto.getBindingAtIndex(10)).toThrowError('Index 10 is out-of-bounds.'); }); }); @@ -394,18 +392,17 @@ export function main() { var proto = createPei(null, 0, dynamicBindings); expect(proto.getBindingAtIndex(0)).toBeAnInstanceOf(DirectiveBinding); - expect(() => proto.getBindingAtIndex(-1)).toThrowError( - 'Index -1 is out-of-bounds.'); + expect(() => proto.getBindingAtIndex(-1)).toThrowError('Index -1 is out-of-bounds.'); expect(() => proto.getBindingAtIndex(dynamicBindings.length - 1)).not.toThrow(); - expect(() => proto.getBindingAtIndex(dynamicBindings.length)).toThrowError( - `Index ${dynamicBindings.length} is out-of-bounds.`); + expect(() => proto.getBindingAtIndex(dynamicBindings.length)) + .toThrowError(`Index ${dynamicBindings.length} is out-of-bounds.`); }); }); describe('event emitters', () => { it('should return a list of event accessors', () => { - var binding = DirectiveBinding.createFromType( - HasEventEmitter, new DummyDirective({events: ['emitter']})); + var binding = DirectiveBinding.createFromType(HasEventEmitter, + new dirAnn.Directive({events: ['emitter']})); var inj = createPei(null, 0, [binding]); expect(inj.eventEmitterAccessors.length).toEqual(1); @@ -417,7 +414,7 @@ export function main() { it('should return a list of hostAction accessors', () => { var binding = DirectiveBinding.createFromType( - HasEventEmitter, new DummyDirective({hostActions: {'hostActionName' : 'onAction'}})); + HasEventEmitter, new dirAnn.Directive({hostActions: {'hostActionName': 'onAction'}})); var inj = createPei(null, 0, [binding]); expect(inj.hostActionAccessors.length).toEqual(1); @@ -432,10 +429,12 @@ export function main() { describe(".create", () => { it("should collect hostInjector injectables from all directives", () => { var pei = createPei(null, 0, [ - DirectiveBinding.createFromType(SimpleDirective, - new DummyDirective({hostInjector: [bind('injectable1').toValue('injectable1')]})), - DirectiveBinding.createFromType(SomeOtherDirective, - new DummyDirective({hostInjector: [bind('injectable2').toValue('injectable2')]})) + DirectiveBinding.createFromType( + SimpleDirective, + new dirAnn.Component({hostInjector: [bind('injectable1').toValue('injectable1')]})), + DirectiveBinding.createFromType(SomeOtherDirective, new dirAnn.Component({ + hostInjector: [bind('injectable2').toValue('injectable2')] + })) ]); expect(pei.getBindingAtIndex(0).key.token).toBe(SimpleDirective); @@ -445,10 +444,11 @@ export function main() { }); it("should collect viewInjector injectables from the component", () => { - var pei = createPei(null, 0, [ - DirectiveBinding.createFromType(SimpleDirective, - new DummyDirective({viewInjector: [bind('injectable1').toValue('injectable1')]})) - ], 0, true); + var pei = createPei(null, 0, + [DirectiveBinding.createFromType(SimpleDirective, new dirAnn.Component({ + viewInjector: [bind('injectable1').toValue('injectable1')] + }))], + 0, true); expect(pei.getBindingAtIndex(0).key.token).toBe(SimpleDirective); expect(pei.getBindingAtIndex(1).key.token).toEqual("injectable1"); @@ -476,11 +476,8 @@ export function main() { var c1 = protoChild1.instantiate(p); var c2 = protoChild2.instantiate(p); - expect(humanize(p, [ - [p, 'parent'], - [c1, 'child1'], - [c2, 'child2'] - ])).toEqual(["parent", ["child1", "child2"]]); + expect(humanize(p, [[p, 'parent'], [c1, 'child1'], [c2, 'child2']])) + .toEqual(["parent", ["child1", "child2"]]); }); describe("direct parent", () => { @@ -521,19 +518,16 @@ export function main() { }); describe("hasInstances", () => { - it("should be false when no directives are instantiated", () => { - expect(injector([]).hasInstances()).toBe(false); - }); + it("should be false when no directives are instantiated", + () => { expect(injector([]).hasInstances()).toBe(false); }); - it("should be true when directives are instantiated", () => { - expect(injector([SimpleDirective]).hasInstances()).toBe(true); - }); + it("should be true when directives are instantiated", + () => { expect(injector([SimpleDirective]).hasInstances()).toBe(true); }); }); - [ - { strategy: 'inline', bindings: []}, - { strategy: 'dynamic', bindings: dynamicBindings} - ].forEach((context) => { + [{ strategy: 'inline', bindings: [] }, { strategy: 'dynamic', + bindings: dynamicBindings }].forEach((context) => { + var extraBindings = context['bindings']; describe(`${context['strategy']} strategy`, () => { describe("hydrate", () => { @@ -554,58 +548,65 @@ export function main() { }); - it("should instantiate hostInjector injectables that have dependencies with set visibility", function () { - var childInj= parentChildInjectors(ListWrapper.concat([ - DirectiveBinding.createFromType(SimpleDirective, - new DummyDirective({hostInjector: [ - bind('injectable1').toValue('injectable1') - ]})) - ], extraBindings), [ - DirectiveBinding.createFromType(SimpleDirective, - new DummyDirective({hostInjector: [ - bind('injectable1').toValue('new-injectable1'), - bind('injectable2').toFactory((val) => `${val}-injectable2`, [[new Inject('injectable1'), new Parent()]]) - ]})) - ]); - expect(childInj.get('injectable2')).toEqual('injectable1-injectable2'); - }); + it("should instantiate hostInjector injectables that have dependencies with set visibility", + function() { + var childInj = parentChildInjectors( + ListWrapper.concat( + [DirectiveBinding.createFromType(SimpleDirective, new dirAnn.Component({ + hostInjector: [bind('injectable1').toValue('injectable1')] + }))], + extraBindings), + [DirectiveBinding.createFromType(SimpleDirective, new dirAnn.Component({ + hostInjector: [ + bind('injectable1') + .toValue('new-injectable1'), + bind('injectable2') + .toFactory( + (val) => `${val}-injectable2`, + [[new diAnn.Inject('injectable1'), new ngDiAnn.Parent()]]) + ] + }))]); + expect(childInj.get('injectable2')).toEqual('injectable1-injectable2'); + }); - it("should instantiate components that depends on viewInjector dependencies", function () { - var inj = injector([ - DirectiveBinding.createFromType(NeedsService, - new DummyDirective({viewInjector: [ - bind('service').toValue('service') - ]})) - ], null, true); + it("should instantiate components that depends on viewInjector dependencies", function() { + var inj = injector( + [DirectiveBinding.createFromType( + NeedsService, + new dirAnn.Component({viewInjector: [bind('service').toValue('service')]}))], + null, true); expect(inj.get(NeedsService).service).toEqual('service'); }); it("should instantiate hostInjector injectables that have dependencies", () => { - var inj = injector(ListWrapper.concat([ - DirectiveBinding.createFromType(SimpleDirective, - new DummyDirective({hostInjector: [ - bind('injectable1').toValue('injectable1'), - bind('injectable2').toFactory((val) => `${val}-injectable2`, ['injectable1']) - ]}))], - extraBindings - )); + var inj = injector(ListWrapper.concat( + [DirectiveBinding.createFromType(SimpleDirective, new dirAnn.Directive({ + hostInjector: [ + bind('injectable1') + .toValue('injectable1'), + bind('injectable2') + .toFactory( + (val) => `${val}-injectable2`, + ['injectable1']) + ] + }))], + extraBindings)); expect(inj.get('injectable2')).toEqual('injectable1-injectable2'); }); - it("should instantiate components that depends on viewInjector dependencies", function () { - var inj = injector(ListWrapper.concat([ - DirectiveBinding.createFromType(NeedsService, - new DummyDirective({viewInjector: [ - bind('service').toValue('service') - ]}))], - extraBindings), - null, true); + it("should instantiate components that depends on viewInjector dependencies", function() { + var inj = injector( + ListWrapper.concat([DirectiveBinding.createFromType(NeedsService, new dirAnn.Component({ + viewInjector: [bind('service').toValue('service')] + }))], + extraBindings), + null, true); expect(inj.get(NeedsService).service).toEqual('service'); }); it("should instantiate directives that depend on app services", () => { - var appInjector = Injector.resolveAndCreate(ListWrapper.concat([ - bind("service").toValue("service")], extraBindings)); + var appInjector = Injector.resolveAndCreate( + ListWrapper.concat([bind("service").toValue("service")], extraBindings)); var inj = injector([NeedsService], appInjector); var d = inj.get(NeedsService); @@ -622,18 +623,16 @@ export function main() { }); it("should return app services", () => { - var appInjector = Injector.resolveAndCreate(ListWrapper.concat([ - bind("service").toValue("service")], extraBindings - )); + var appInjector = Injector.resolveAndCreate( + ListWrapper.concat([bind("service").toValue("service")], extraBindings)); var inj = injector([], appInjector); expect(inj.get('service')).toEqual('service'); }); it("should get directives from parent", () => { - var child = parentChildInjectors( - ListWrapper.concat([SimpleDirective], extraBindings), - [NeedsDirectiveFromParent]); + var child = parentChildInjectors(ListWrapper.concat([SimpleDirective], extraBindings), + [NeedsDirectiveFromParent]); var d = child.get(NeedsDirectiveFromParent); @@ -643,15 +642,13 @@ export function main() { it("should not return parent's directives on self", () => { expect(() => { - injector(ListWrapper.concat( - [SimpleDirective, NeedsDirectiveFromParent], extraBindings)); - }).toThrowError(new RegExp("No provider for SimpleDirective")); + injector(ListWrapper.concat([SimpleDirective, NeedsDirectiveFromParent], extraBindings)); + }).toThrowError(containsRegexp(`No provider for ${stringify(SimpleDirective) }`)); }); it("should get directives from ancestor", () => { - var child = parentChildInjectors( - ListWrapper.concat([SimpleDirective], extraBindings), - [NeedsDirectiveFromAncestor]); + var child = parentChildInjectors(ListWrapper.concat([SimpleDirective], extraBindings), + [NeedsDirectiveFromAncestor]); var d = child.get(NeedsDirectiveFromAncestor); @@ -671,8 +668,9 @@ export function main() { }); it("should throw when a depenency cannot be resolved", () => { - expect(() => injector(ListWrapper.concat([NeedsDirectiveFromParent], extraBindings))). - toThrowError('No provider for SimpleDirective! (NeedsDirectiveFromParent -> SimpleDirective)'); + expect(() => injector(ListWrapper.concat([NeedsDirectiveFromParent], extraBindings))) + .toThrowError(containsRegexp( + `No provider for ${stringify(SimpleDirective) }! (${stringify(NeedsDirectiveFromParent) } -> ${stringify(SimpleDirective) })`)); }); it("should inject null when an optional dependency cannot be resolved", () => { @@ -682,109 +680,96 @@ export function main() { }); it("should accept bindings instead types", () => { - var inj = injector(ListWrapper.concat([ - bind(SimpleDirective).toClass(SimpleDirective)], - extraBindings - )); + var inj = injector( + ListWrapper.concat([bind(SimpleDirective).toClass(SimpleDirective)], extraBindings)); expect(inj.get(SimpleDirective)).toBeAnInstanceOf(SimpleDirective); }); it("should allow for direct access using getDirectiveAtIndex", () => { - var bindings = ListWrapper.concat([ - bind(SimpleDirective).toClass(SimpleDirective)], - extraBindings - ); + var bindings = + ListWrapper.concat([bind(SimpleDirective).toClass(SimpleDirective)], extraBindings); var inj = injector(bindings); var firsIndexOut = bindings.length > 10 ? bindings.length : 10; expect(inj.getDirectiveAtIndex(0)).toBeAnInstanceOf(SimpleDirective); - expect(() => inj.getDirectiveAtIndex(-1)).toThrowError( - 'Index -1 is out-of-bounds.'); - expect(() => inj.getDirectiveAtIndex(firsIndexOut)).toThrowError( - `Index ${firsIndexOut} is out-of-bounds.`); + expect(() => inj.getDirectiveAtIndex(-1)).toThrowError('Index -1 is out-of-bounds.'); + expect(() => inj.getDirectiveAtIndex(firsIndexOut)) + .toThrowError(`Index ${firsIndexOut} is out-of-bounds.`); }); - it("should handle cyclic dependencies", () => { - expect(() => { - var bAneedsB = bind(A_Needs_B).toFactory((a) => new A_Needs_B(a), [B_Needs_A]); - var bBneedsA = bind(B_Needs_A).toFactory((a) => new B_Needs_A(a), [A_Needs_B]); - - injector(ListWrapper.concat([bAneedsB, bBneedsA], extraBindings)); - }).toThrowError('Cannot instantiate cyclic dependency! ' + - '(A_Needs_B -> B_Needs_A -> A_Needs_B)'); - }); - describe("shadow DOM components", () => { it("should instantiate directives that depend on the containing component", () => { - var directiveBinding = DirectiveBinding.createFromType(SimpleDirective, new Component()); - var shadow = hostShadowInjectors( - ListWrapper.concat([directiveBinding], extraBindings), - [NeedsDirective]); + var directiveBinding = + DirectiveBinding.createFromType(SimpleDirective, new dirAnn.Component()); + var shadow = hostShadowInjectors(ListWrapper.concat([directiveBinding], extraBindings), + [NeedsDirective]); var d = shadow.get(NeedsDirective); expect(d).toBeAnInstanceOf(NeedsDirective); expect(d.dependency).toBeAnInstanceOf(SimpleDirective); }); - it("should not instantiate directives that depend on other directives in the containing component's ElementInjector", () => { - var directiveBinding = DirectiveBinding.createFromType(SomeOtherDirective, new Component()); - expect(() => { - hostShadowInjectors( - ListWrapper.concat([directiveBinding, SimpleDirective], extraBindings), - [NeedsDirective]); - }).toThrowError('No provider for SimpleDirective! (NeedsDirective -> SimpleDirective)'); - }); + it("should not instantiate directives that depend on other directives in the containing component's ElementInjector", + () => { + var directiveBinding = + DirectiveBinding.createFromType(SomeOtherDirective, new dirAnn.Component()); + expect(() => + { + hostShadowInjectors( + ListWrapper.concat([directiveBinding, SimpleDirective], extraBindings), + [NeedsDirective]); + }) + .toThrowError(containsRegexp( + `No provider for ${stringify(SimpleDirective) }! (${stringify(NeedsDirective) } -> ${stringify(SimpleDirective) })`)); + }); - it("should instantiate component directives that depend on app services in the shadow app injector", () => { - var directiveAnnotation = new Component({ - appInjector: ListWrapper.concat([ - bind("service").toValue("service")], - extraBindings - ) - }); - var componentDirective = DirectiveBinding.createFromType( - NeedsService, directiveAnnotation); - var inj = injector([componentDirective], null, true); + it("should instantiate component directives that depend on app services in the shadow app injector", + () => { + var directiveAnnotation = new dirAnn.Component({ + appInjector: ListWrapper.concat([bind("service").toValue("service")], extraBindings) + }); + var componentDirective = + DirectiveBinding.createFromType(NeedsService, directiveAnnotation); + var inj = injector([componentDirective], null, true); - var d = inj.get(NeedsService); - expect(d).toBeAnInstanceOf(NeedsService); - expect(d.service).toEqual("service"); - }); + var d = inj.get(NeedsService); + expect(d).toBeAnInstanceOf(NeedsService); + expect(d.service).toEqual("service"); + }); - it("should not instantiate other directives that depend on app services in the shadow app injector", () => { - var directiveAnnotation = new Component({ - appInjector: ListWrapper.concat([ - bind("service").toValue("service")], - extraBindings - ) - }); - var componentDirective = DirectiveBinding.createFromType(SimpleDirective, directiveAnnotation); - expect(() => { - injector([componentDirective, NeedsService], null); - }).toThrowError('No provider for service! (NeedsService -> service)'); - }); + it("should not instantiate other directives that depend on app services in the shadow app injector", + () => { + var directiveAnnotation = new dirAnn.Component({ + appInjector: ListWrapper.concat([bind("service").toValue("service")], extraBindings) + }); + var componentDirective = + DirectiveBinding.createFromType(SimpleDirective, directiveAnnotation); + expect(() => { injector([componentDirective, NeedsService], null); }) + .toThrowError(containsRegexp( + `No provider for service! (${stringify(NeedsService) } -> service)`)); + }); }); }); describe("lifecycle", () => { it("should call onDestroy on directives subscribed to this event", function() { - var inj = injector(ListWrapper.concat([ - DirectiveBinding.createFromType(DirectiveWithDestroy, new DummyDirective({lifecycle: [onDestroy]}))], - extraBindings - )); + var inj = injector(ListWrapper.concat( + [DirectiveBinding.createFromType(DirectiveWithDestroy, + new dirAnn.Directive({lifecycle: [onDestroy]}))], + extraBindings)); var destroy = inj.get(DirectiveWithDestroy); inj.dehydrate(); expect(destroy.onDestroyCounter).toBe(1); }); it("should work with services", function() { - var inj = injector(ListWrapper.concat([ - DirectiveBinding.createFromType(SimpleDirective, new DummyDirective({hostInjector: [SimpleService]}))], - extraBindings - )); + var inj = injector(ListWrapper.concat( + [DirectiveBinding.createFromType( + SimpleDirective, new dirAnn.Directive({hostInjector: [SimpleService]}))], + extraBindings)); inj.dehydrate(); }); }); @@ -793,72 +778,74 @@ export function main() { it("should create a component dynamically", () => { var inj = injector(extraBindings); - inj.dynamicallyCreateComponent(DirectiveBinding.createFromType(SimpleDirective, null), appInjector); + inj.dynamicallyCreateComponent(DirectiveBinding.createFromType(SimpleDirective, null), + appInjector); expect(inj.getDynamicallyLoadedComponent()).toBeAnInstanceOf(SimpleDirective); expect(inj.get(SimpleDirective)).toBeAnInstanceOf(SimpleDirective); }); it("should inject parent dependencies into the dynamically-loaded component", () => { - var inj = parentChildInjectors( - ListWrapper.concat([SimpleDirective], extraBindings), - []); - inj.dynamicallyCreateComponent(DirectiveBinding.createFromType(NeedsDirectiveFromAncestor, null), appInjector); + var inj = parentChildInjectors(ListWrapper.concat([SimpleDirective], extraBindings), []); + inj.dynamicallyCreateComponent( + DirectiveBinding.createFromType(NeedsDirectiveFromAncestor, null), appInjector); expect(inj.getDynamicallyLoadedComponent()).toBeAnInstanceOf(NeedsDirectiveFromAncestor); expect(inj.getDynamicallyLoadedComponent().dependency).toBeAnInstanceOf(SimpleDirective); }); - it("should not inject the proxy component into the children of the dynamically-loaded component", () => { - var injWithDynamicallyLoadedComponent = injector([SimpleDirective]); - injWithDynamicallyLoadedComponent.dynamicallyCreateComponent(DirectiveBinding.createFromType(SomeOtherDirective, null), appInjector); + it("should not inject the proxy component into the children of the dynamically-loaded component", + () => { + var injWithDynamicallyLoadedComponent = injector([SimpleDirective]); + injWithDynamicallyLoadedComponent.dynamicallyCreateComponent( + DirectiveBinding.createFromType(SomeOtherDirective, null), appInjector); - var shadowDomProtoInjector = createPei( - null, 0, ListWrapper.concat([NeedsDirectiveFromAncestor], extraBindings)); - var shadowDomInj = shadowDomProtoInjector.instantiate(null); + var shadowDomProtoInjector = + createPei(null, 0, ListWrapper.concat([NeedsDirectiveFromAncestor], extraBindings)); + var shadowDomInj = shadowDomProtoInjector.instantiate(null); - expect(() => - shadowDomInj.hydrate(appInjector, injWithDynamicallyLoadedComponent, defaultPreBuiltObjects)). - toThrowError(new RegExp("No provider for SimpleDirective")); - }); + expect(() => shadowDomInj.hydrate(appInjector, injWithDynamicallyLoadedComponent, + defaultPreBuiltObjects)) + .toThrowError(containsRegexp(`No provider for ${stringify(SimpleDirective) }`)); + }); - it("should not inject the dynamically-loaded component into directives on the same element", () => { - var dynamicComp = DirectiveBinding.createFromType(SomeOtherDirective, new Component()); - var proto = createPei( - null, 0, ListWrapper.concat([dynamicComp, NeedsDirective], extraBindings), 1, true); - var inj = proto.instantiate(null); - inj.dynamicallyCreateComponent( - DirectiveBinding.createFromType(SimpleDirective, null), appInjector); + it("should not inject the dynamically-loaded component into directives on the same element", + () => { + var dynamicComp = + DirectiveBinding.createFromType(SomeOtherDirective, new dirAnn.Component()); + var proto = createPei( + null, 0, ListWrapper.concat([dynamicComp, NeedsDirective], extraBindings), 1, true); + var inj = proto.instantiate(null); + inj.dynamicallyCreateComponent(DirectiveBinding.createFromType(SimpleDirective, null), + appInjector); - var error = null; - try { - inj.hydrate(Injector.resolveAndCreate([]), null, null); - } catch(e) { - error = e; - } + expect(() => inj.hydrate(Injector.resolveAndCreate([]), null, null)) + .toThrowError( + `No provider for SimpleDirective! (${stringify(NeedsDirective) } -> ${stringify(SimpleDirective) })`); + }); - expect(error.message).toEqual("No provider for SimpleDirective! (NeedsDirective -> SimpleDirective)"); - }); + it("should inject the dynamically-loaded component into the children of the dynamically-loaded component", + () => { + var componentDirective = DirectiveBinding.createFromType(SimpleDirective, null); + var injWithDynamicallyLoadedComponent = injector([]); + injWithDynamicallyLoadedComponent.dynamicallyCreateComponent(componentDirective, + appInjector); - it("should inject the dynamically-loaded component into the children of the dynamically-loaded component", () => { - var componentDirective = DirectiveBinding.createFromType(SimpleDirective, null); - var injWithDynamicallyLoadedComponent = injector([]); - injWithDynamicallyLoadedComponent.dynamicallyCreateComponent(componentDirective, appInjector); + var shadowDomProtoInjector = + createPei(null, 0, ListWrapper.concat([NeedsDirectiveFromAncestor], extraBindings)); + var shadowDomInjector = shadowDomProtoInjector.instantiate(null); + shadowDomInjector.hydrate(appInjector, injWithDynamicallyLoadedComponent, + defaultPreBuiltObjects); - var shadowDomProtoInjector = createPei( - null, 0, ListWrapper.concat([NeedsDirectiveFromAncestor], extraBindings)); - var shadowDomInjector = shadowDomProtoInjector.instantiate(null); - shadowDomInjector.hydrate(appInjector, injWithDynamicallyLoadedComponent, defaultPreBuiltObjects); - - expect(shadowDomInjector.get(NeedsDirectiveFromAncestor)).toBeAnInstanceOf(NeedsDirectiveFromAncestor); - expect(shadowDomInjector.get(NeedsDirectiveFromAncestor).dependency).toBeAnInstanceOf(SimpleDirective); - }); + expect(shadowDomInjector.get(NeedsDirectiveFromAncestor)) + .toBeAnInstanceOf(NeedsDirectiveFromAncestor); + expect(shadowDomInjector.get(NeedsDirectiveFromAncestor).dependency) + .toBeAnInstanceOf(SimpleDirective); + }); it("should remove the dynamically-loaded component when dehydrating", () => { var inj = injector(extraBindings); inj.dynamicallyCreateComponent( - DirectiveBinding.createFromType( - DirectiveWithDestroy, - new DummyDirective({lifecycle: [onDestroy]}) - ), + DirectiveBinding.createFromType(DirectiveWithDestroy, + new dirAnn.Directive({lifecycle: [onDestroy]})), appInjector); var dir = inj.getDynamicallyLoadedComponent(); @@ -875,7 +862,8 @@ export function main() { it("should inject services of the dynamically-loaded component", () => { var inj = injector(extraBindings); var appInjector = Injector.resolveAndCreate([bind("service").toValue("Service")]); - inj.dynamicallyCreateComponent(DirectiveBinding.createFromType(NeedsService, null), appInjector); + inj.dynamicallyCreateComponent(DirectiveBinding.createFromType(NeedsService, null), + appInjector); expect(inj.getDynamicallyLoadedComponent().service).toEqual("Service"); }); }); @@ -886,8 +874,8 @@ export function main() { MapWrapper.set(attributes, 'type', 'text'); MapWrapper.set(attributes, 'title', ''); - var inj = injector( - ListWrapper.concat([NeedsAttribute], extraBindings), null, false, null, attributes); + var inj = injector(ListWrapper.concat([NeedsAttribute], extraBindings), null, false, null, + attributes); var needsAttribute = inj.get(NeedsAttribute); expect(needsAttribute.typeAttribute).toEqual('text'); @@ -899,8 +887,8 @@ export function main() { var attributes = MapWrapper.create(); MapWrapper.set(attributes, 'foo', 'bar'); - var inj = injector( - ListWrapper.concat([NeedsAttributeNoType], extraBindings), null, false, null, attributes); + var inj = injector(ListWrapper.concat([NeedsAttributeNoType], extraBindings), null, false, + null, attributes); var needsAttribute = inj.get(NeedsAttributeNoType); expect(needsAttribute.fooAttribute).toEqual('bar'); @@ -915,12 +903,12 @@ export function main() { it('should inject ChangeDetectorRef', () => { var cd = new DynamicChangeDetector(null, null, null, [], []); - var view = new DummyView(); + var view = new DummyView(); var childView = new DummyView(); childView.changeDetector = cd; view.componentChildViews = [childView]; - var inj = injector(ListWrapper.concat([NeedsChangeDetectorRef], extraBindings), - null, false, new PreBuiltObjects(null, view, null)); + var inj = injector(ListWrapper.concat([NeedsChangeDetectorRef], extraBindings), null, false, + new PreBuiltObjects(null, view, null)); expect(inj.get(NeedsChangeDetectorRef).changeDetectorRef).toBe(cd.ref); }); @@ -932,21 +920,20 @@ export function main() { it("should inject ProtoViewRef", () => { var protoView = new AppProtoView(null, null, null); - var inj = injector(ListWrapper.concat([NeedsProtoViewRef], extraBindings), - null, false, new PreBuiltObjects(null, null, protoView)); + var inj = injector(ListWrapper.concat([NeedsProtoViewRef], extraBindings), null, false, + new PreBuiltObjects(null, null, protoView)); expect(inj.get(NeedsProtoViewRef).protoViewRef).toEqual(new ProtoViewRef(protoView)); }); it("should throw if there is no ProtoViewRef", () => { - expect( - () => injector(ListWrapper.concat([NeedsProtoViewRef], extraBindings)) - ).toThrowError('No provider for ProtoViewRef! (NeedsProtoViewRef -> ProtoViewRef)'); + expect(() => injector(ListWrapper.concat([NeedsProtoViewRef], extraBindings))) + .toThrowError( + `No provider for ProtoViewRef! (${stringify(NeedsProtoViewRef) } -> ProtoViewRef)`); }); it('should inject null if there is no ProtoViewRef when the dependency is optional', () => { - var inj = injector( - ListWrapper.concat([OptionallyInjectsProtoViewRef], extraBindings)); + var inj = injector(ListWrapper.concat([OptionallyInjectsProtoViewRef], extraBindings)); var instance = inj.get(OptionallyInjectsProtoViewRef); expect(instance.protoViewRef).toBeNull(); }); @@ -954,9 +941,7 @@ export function main() { describe('directive queries', () => { var preBuildObjects = defaultPreBuiltObjects; - beforeEach(() => { - _constructionCount = 0; - }); + beforeEach(() => { _constructionCount = 0; }); function expectDirectives(query, type, expectedIndex) { var currentCount = 0; @@ -968,14 +953,14 @@ export function main() { } it('should be injectable', () => { - var inj = injector(ListWrapper.concat([NeedsQuery], extraBindings), - null, false, preBuildObjects); + var inj = + injector(ListWrapper.concat([NeedsQuery], extraBindings), null, false, preBuildObjects); expect(inj.get(NeedsQuery).query).toBeAnInstanceOf(QueryList); }); it('should contain directives on the same injector', () => { - var inj = injector(ListWrapper.concat([NeedsQuery, CountingDirective], extraBindings), - null, false, preBuildObjects); + var inj = injector(ListWrapper.concat([NeedsQuery, CountingDirective], extraBindings), null, + false, preBuildObjects); expectDirectives(inj.get(NeedsQuery).query, CountingDirective, [0]); }); @@ -992,21 +977,21 @@ export function main() { it('should contain directives on the same and a child injector in construction order', () => { var protoParent = createPei(null, 0, [NeedsQuery, CountingDirective]); - var protoChild = createPei( - protoParent, 1, ListWrapper.concat([CountingDirective], extraBindings)); + var protoChild = + createPei(protoParent, 1, ListWrapper.concat([CountingDirective], extraBindings)); var parent = protoParent.instantiate(null); var child = protoChild.instantiate(parent); parent.hydrate(Injector.resolveAndCreate([]), null, preBuildObjects); child.hydrate(Injector.resolveAndCreate([]), null, preBuildObjects); - expectDirectives(parent.get(NeedsQuery).query, CountingDirective, [0,1]); + expectDirectives(parent.get(NeedsQuery).query, CountingDirective, [0, 1]); }); it('should reflect unlinking an injector', () => { var protoParent = createPei(null, 0, [NeedsQuery, CountingDirective]); - var protoChild = createPei(protoParent, 1, - ListWrapper.concat([CountingDirective], extraBindings)); + var protoChild = + createPei(protoParent, 1, ListWrapper.concat([CountingDirective], extraBindings)); var parent = protoParent.instantiate(null); var child = protoChild.instantiate(parent); @@ -1021,8 +1006,8 @@ export function main() { it('should reflect moving an injector as a last child', () => { var protoParent = createPei(null, 0, [NeedsQuery, CountingDirective]); var protoChild1 = createPei(protoParent, 1, [CountingDirective]); - var protoChild2 = createPei(protoParent, 1, - ListWrapper.concat([CountingDirective], extraBindings)); + var protoChild2 = + createPei(protoParent, 1, ListWrapper.concat([CountingDirective], extraBindings)); var parent = protoParent.instantiate(null); var child1 = protoChild1.instantiate(parent); @@ -1042,8 +1027,8 @@ export function main() { it('should reflect moving an injector as a first child', () => { var protoParent = createPei(null, 0, [NeedsQuery, CountingDirective]); var protoChild1 = createPei(protoParent, 1, [CountingDirective]); - var protoChild2 = createPei(protoParent, 1, - ListWrapper.concat([CountingDirective], extraBindings)); + var protoChild2 = + createPei(protoParent, 1, ListWrapper.concat([CountingDirective], extraBindings)); var parent = protoParent.instantiate(null); var child1 = protoChild1.instantiate(parent); @@ -1063,8 +1048,8 @@ export function main() { it('should support two concurrent queries for the same directive', () => { var protoGrandParent = createPei(null, 0, [NeedsQuery]); var protoParent = createPei(null, 0, [NeedsQuery]); - var protoChild = createPei(protoParent, 1, - ListWrapper.concat([CountingDirective], extraBindings)); + var protoChild = + createPei(protoParent, 1, ListWrapper.concat([CountingDirective], extraBindings)); var grandParent = protoGrandParent.instantiate(null); var parent = protoParent.instantiate(grandParent); @@ -1087,19 +1072,16 @@ export function main() { }); }); }); - }); } class ContextWithHandler { handler; - constructor(handler) { - this.handler = handler; - } + constructor(handler) { this.handler = handler; } } class FakeRenderer extends Renderer { - log:List; + log: List>; constructor() { super(); this.log = []; @@ -1107,5 +1089,4 @@ class FakeRenderer extends Renderer { setElementProperty(viewRef, elementIndex, propertyName, value) { ListWrapper.push(this.log, [viewRef, elementIndex, propertyName, value]); } - } diff --git a/modules/angular2/test/core/compiler/integration_spec.ts b/modules/angular2/test/core/compiler/integration_spec.ts index f2dfc01ac1..58e5620545 100644 --- a/modules/angular2/test/core/compiler/integration_spec.ts +++ b/modules/angular2/test/core/compiler/integration_spec.ts @@ -12,7 +12,8 @@ import { IS_DARTIUM, beforeEachBindings, it, - xit + xit, + containsRegexp } from 'angular2/test_lib'; @@ -25,7 +26,8 @@ import { BaseException, assertionsEnabled, isJsObject, - global + global, + stringify } from 'angular2/src/facade/lang'; import {PromiseWrapper, EventEmitter, ObservableWrapper} from 'angular2/src/facade/async'; @@ -989,94 +991,88 @@ 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((MyService).name) && (MyService).name.length > 0); - 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) => { + tb.overrideView(MyComp, + new viewAnn.View({directives: [SomeDirectiveMissingAnnotation]})); + + PromiseWrapper.catchError(tb.createView(MyComp, {context: ctx}), (e) => { + expect(e.message).toEqual( + `No Directive annotation found on ${stringify(SomeDirectiveMissingAnnotation)}`); + async.done(); + }); + })); + + it('should report a meaningful error when a directive is null', + inject([TestBed, AsyncTestCompleter], (tb, async) => { + + tb.overrideView(MyComp, new viewAnn.View({directives: [[null]]})); + + PromiseWrapper.catchError(tb.createView(MyComp, {context: ctx}), (e) => { + expect(e.message).toEqual( + `Unexpected directive value 'null' on the View of component '${stringify(MyComp)}'`); + async.done(); + }); + })); + + if (!IS_DARTIUM) { + it('should report a meaningful error when a directive is undefined', inject([TestBed, AsyncTestCompleter], (tb, async) => { - tb.overrideView(MyComp, - new viewAnn.View({directives: [SomeDirectiveMissingAnnotation]})); + + var undefinedValue; + + tb.overrideView(MyComp, new viewAnn.View({directives: [undefinedValue]})); PromiseWrapper.catchError(tb.createView(MyComp, {context: ctx}), (e) => { - expect(e.message) - .toEqual('No Directive annotation found on SomeDirectiveMissingAnnotation'); + expect(e.message).toEqual( + `Unexpected directive value 'undefined' on the View of component '${stringify(MyComp)}'`); async.done(); }); })); - - it('should report a meaningful error when a directive is null', - inject([TestBed, AsyncTestCompleter], (tb, async) => { - - tb.overrideView(MyComp, new viewAnn.View({directives: [[null]]})); - - PromiseWrapper.catchError(tb.createView(MyComp, {context: ctx}), (e) => { - expect(e.message) - .toEqual("Unexpected directive value 'null' on the View of component 'MyComp'"); - async.done(); - }); - })); - - if (!IS_DARTIUM) { - it('should report a meaningful error when a directive is undefined', - inject([TestBed, AsyncTestCompleter], (tb, async) => { - - var undefinedValue; - - tb.overrideView(MyComp, new viewAnn.View({directives: [undefinedValue]})); - - PromiseWrapper.catchError(tb.createView(MyComp, {context: ctx}), (e) => { - expect(e.message).toEqual( - "Unexpected directive value 'undefined' on the View of component 'MyComp'"); - async.done(); - }); - })); - } - - it('should specify a location of an error that happened during change detection (text)', - inject([TestBed, AsyncTestCompleter], (tb, async) => { - - tb.overrideView(MyComp, new viewAnn.View({template: '{{a.b}}'})); - - tb.createView(MyComp, {context: ctx}) - .then((view) => { - expect(() => view.detectChanges()).toThrowError(new RegExp('{{a.b}} in MyComp')); - async.done(); - }) - })); - - it('should specify a location of an error that happened during change detection (element property)', - inject([TestBed, AsyncTestCompleter], (tb, async) => { - - tb.overrideView(MyComp, new viewAnn.View({template: '
'})); - - tb.createView(MyComp, {context: ctx}) - .then((view) => { - expect(() => view.detectChanges()).toThrowError(new RegExp('a.b in MyComp')); - async.done(); - }) - })); - - it('should specify a location of an error that happened during change detection (directive property)', - inject([TestBed, AsyncTestCompleter], (tb, async) => { - - tb.overrideView( - MyComp, - new viewAnn.View( - {template: '', directives: [ChildComp]})); - - tb.createView(MyComp, {context: ctx}) - .then((view) => { - expect(() => view.detectChanges()).toThrowError(new RegExp('a.b in MyComp')); - async.done(); - }) - })); } + + it('should specify a location of an error that happened during change detection (text)', + inject([TestBed, AsyncTestCompleter], (tb, async) => { + + tb.overrideView(MyComp, new viewAnn.View({template: '{{a.b}}'})); + + tb.createView(MyComp, {context: ctx}) + .then((view) => { + expect(() => view.detectChanges()) + .toThrowError(containsRegexp(`{{a.b}} in ${stringify(MyComp)}`)); + async.done(); + }) + })); + + it('should specify a location of an error that happened during change detection (element property)', + inject([TestBed, AsyncTestCompleter], (tb, async) => { + + tb.overrideView(MyComp, new viewAnn.View({template: '
'})); + + tb.createView(MyComp, {context: ctx}) + .then((view) => { + expect(() => view.detectChanges()) + .toThrowError(containsRegexp(`a.b in ${stringify(MyComp)}`)); + async.done(); + }) + })); + + it('should specify a location of an error that happened during change detection (directive property)', + inject([TestBed, AsyncTestCompleter], (tb, async) => { + + tb.overrideView( + MyComp, + new viewAnn.View( + {template: '', directives: [ChildComp]})); + + tb.createView(MyComp, {context: ctx}) + .then((view) => { + expect(() => view.detectChanges()) + .toThrowError(containsRegexp(`a.b in ${stringify(MyComp)}`)); + async.done(); + }) + })); }); it('should support imperative views', inject([TestBed, AsyncTestCompleter], (tb, async) => { diff --git a/modules/angular2/test/core/compiler/proto_view_factory_spec.js b/modules/angular2/test/core/compiler/proto_view_factory_spec.ts similarity index 64% rename from modules/angular2/test/core/compiler/proto_view_factory_spec.js rename to modules/angular2/test/core/compiler/proto_view_factory_spec.ts index 786aa89312..71444f4dfc 100644 --- a/modules/angular2/test/core/compiler/proto_view_factory_spec.js +++ b/modules/angular2/test/core/compiler/proto_view_factory_spec.ts @@ -10,15 +10,19 @@ import { inject, IS_DARTIUM, it, - SpyObject, proxy + SpyObject, + proxy } 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 {ChangeDetection, ChangeDetectorDefinition} from 'angular2/change_detection'; -import {ProtoViewFactory, getChangeDetectorDefinitions} from 'angular2/src/core/compiler/proto_view_factory'; -import {Component, Directive} from 'angular2/src/core/annotations_impl/annotations'; +import { + 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 {DirectiveBinding} from 'angular2/src/core/compiler/element_injector'; import * as renderApi from 'angular2/src/render/api'; @@ -31,7 +35,7 @@ export function main() { var protoViewFactory; var directiveResolver; - beforeEach( () => { + beforeEach(() => { directiveResolver = new DirectiveResolver(); changeDetection = new ChangeDetectionSpy(); protoViewFactory = new ProtoViewFactory(changeDetection); @@ -45,10 +49,10 @@ export function main() { it('should create a ChangeDetectorDefinition for the root render proto view', () => { var renderPv = createRenderProtoView(); - var defs = getChangeDetectorDefinitions(bindDirective(MainComponent).metadata, - renderPv, []); + var defs = + getChangeDetectorDefinitions(bindDirective(MainComponent).metadata, renderPv, []); 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', () => { var renderPv = createRenderProtoView(); - var pvs = protoViewFactory.createAppProtoViews(bindDirective(MainComponent), - renderPv, []); + var pvs = protoViewFactory.createAppProtoViews(bindDirective(MainComponent), renderPv, []); expect(pvs.length).toBe(1); 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)) { type = renderApi.ProtoViewDto.COMPONENT_VIEW_TYPE; } if (isBlank(elementBinders)) { elementBinders = []; } - return new renderApi.ProtoViewDto({ - elementBinders: elementBinders, - type: type, - variableBindings: MapWrapper.create() - }); + return new renderApi.ProtoViewDto( + {elementBinders: elementBinders, type: type, variableBindings: MapWrapper.create()}); } function createRenderComponentElementBinder(directiveIndex) { - return new renderApi.ElementBinder({ - directives: [new renderApi.DirectiveBinder({ - directiveIndex: directiveIndex - })] - }); + return new renderApi.ElementBinder( + {directives: [new renderApi.DirectiveBinder({directiveIndex: directiveIndex})]}); } function createRenderViewportElementBinder(nestedProtoView) { - return new renderApi.ElementBinder({ - nestedProtoView: nestedProtoView - }); + return new renderApi.ElementBinder({nestedProtoView: nestedProtoView}); } @proxy @IMPLEMENTS(ChangeDetection) class ChangeDetectionSpy extends SpyObject { - constructor(){super(ChangeDetection);} - noSuchMethod(m){return super.noSuchMethod(m)} + constructor() { super(ChangeDetection); } + noSuchMethod(m) { return super.noSuchMethod(m) } } -@Component({ - selector: 'main-comp' -}) -class MainComponent {} +@Component({selector: 'main-comp'}) +class MainComponent { +} diff --git a/modules/angular2/test/core/compiler/query_integration_spec.js b/modules/angular2/test/core/compiler/query_integration_spec.js deleted file mode 100644 index 6ac232c022..0000000000 --- a/modules/angular2/test/core/compiler/query_integration_spec.js +++ /dev/null @@ -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 = - '
' + - '
' + - '
'; - - 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 = - '
' + - '
' + - '
'; - - 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 = - '
' + - '
' + - '
'; - - 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: '
{{dir.text}}|
' -}) -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']; - } -} diff --git a/modules/angular2/test/core/compiler/query_integration_spec.ts b/modules/angular2/test/core/compiler/query_integration_spec.ts new file mode 100644 index 0000000000..8994618027 --- /dev/null +++ b/modules/angular2/test/core/compiler/query_integration_spec.ts @@ -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 = '
' + + '
' + + '
'; + + 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 = + '
' + + '
' + + '
'; + + 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 = + '
' + + '
' + + '
'; + + 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: '
{{dir.text}}|
'}) +@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']; + } +} diff --git a/modules/angular2/test/core/compiler/query_list_spec.js b/modules/angular2/test/core/compiler/query_list_spec.ts similarity index 92% rename from modules/angular2/test/core/compiler/query_list_spec.js rename to modules/angular2/test/core/compiler/query_list_spec.ts index 07f2fbd441..d5eb4ea0f9 100644 --- a/modules/angular2/test/core/compiler/query_list_spec.js +++ b/modules/angular2/test/core/compiler/query_list_spec.ts @@ -12,9 +12,7 @@ export function main() { log = ''; }); - function logAppend(item) { - log += (log.length == 0 ? '' : ', ') + item; - } + function logAppend(item) { log += (log.length == 0 ? '' : ', ') + item; } it('should support adding objects and iterating over them', () => { queryList.add('one'); @@ -35,7 +33,7 @@ export function main() { describe('simple observable interface', () => { it('should fire callbacks on change', () => { var fires = 0; - queryList.onChange(() => {fires += 1;}); + queryList.onChange(() => { fires += 1; }); queryList.fireCallbacks(); expect(fires).toEqual(0); diff --git a/modules/angular2/test/core/compiler/reflection_capabilities_spec.js b/modules/angular2/test/core/compiler/reflection_capabilities_spec.js deleted file mode 100644 index 648c92da21..0000000000 --- a/modules/angular2/test/core/compiler/reflection_capabilities_spec.js +++ /dev/null @@ -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 {} diff --git a/modules/angular2/test/core/compiler/view_container_ref_spec.js b/modules/angular2/test/core/compiler/view_container_ref_spec.ts similarity index 76% rename from modules/angular2/test/core/compiler/view_container_ref_spec.js rename to modules/angular2/test/core/compiler/view_container_ref_spec.ts index 12326d3bdd..e0fc388f4f 100644 --- a/modules/angular2/test/core/compiler/view_container_ref_spec.js +++ b/modules/angular2/test/core/compiler/view_container_ref_spec.ts @@ -12,7 +12,8 @@ import { beforeEachBindings, it, xit, - SpyObject, proxy + SpyObject, + proxy } from 'angular2/test_lib'; import {MapWrapper} from 'angular2/src/facade/collection'; @@ -32,23 +33,15 @@ export function main() { var view; var viewManager; - function wrapView(view:AppView):ViewRef { - return new ViewRef(view); - } + function wrapView(view: AppView): ViewRef { return new ViewRef(view); } - function createProtoView() { - return new AppProtoView(null, null, null); - } + function createProtoView() { return new AppProtoView(null, null, null); } - function createView() { - return new AppView(null, createProtoView(), MapWrapper.create()); - } + function createView() { return new AppView(null, createProtoView(), MapWrapper.create()); } - function createViewContainer() { - return new ViewContainerRef(viewManager, location); - } + function createViewContainer() { return new ViewContainerRef(viewManager, location); } - beforeEach( () => { + beforeEach(() => { viewManager = new AppViewManagerSpy(); view = createView(); view.viewContainers = [null]; @@ -79,6 +72,6 @@ export function main() { @proxy @IMPLEMENTS(AppViewManager) class AppViewManagerSpy extends SpyObject { - constructor(){super(AppViewManager);} - noSuchMethod(m){return super.noSuchMethod(m)} + constructor() { super(AppViewManager); } + noSuchMethod(m) { return super.noSuchMethod(m) } } diff --git a/modules/angular2/test/core/compiler/view_manager_spec.js b/modules/angular2/test/core/compiler/view_manager_spec.ts similarity index 60% rename from modules/angular2/test/core/compiler/view_manager_spec.js rename to modules/angular2/test/core/compiler/view_manager_spec.ts index e5dbb63bb4..d3709b5815 100644 --- a/modules/angular2/test/core/compiler/view_manager_spec.js +++ b/modules/angular2/test/core/compiler/view_manager_spec.ts @@ -12,7 +12,8 @@ import { beforeEachBindings, it, xit, - SpyObject, proxy + SpyObject, + proxy } from 'angular2/test_lib'; import {Injector, bind} from 'angular2/di'; 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 {ProtoViewRef, ViewRef, internalView} from 'angular2/src/core/compiler/view_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 {DirectiveBinding, ElementInjector} from 'angular2/src/core/compiler/element_injector'; 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 {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils'; import {AppViewPool} from 'angular2/src/core/compiler/view_pool'; @@ -42,13 +43,9 @@ export function main() { var createdViews; var createdRenderViews; - function wrapPv(protoView:AppProtoView):ProtoViewRef { - return new ProtoViewRef(protoView); - } + function wrapPv(protoView: AppProtoView): ProtoViewRef { return new ProtoViewRef(protoView); } - function wrapView(view:AppView):ViewRef { - return new ViewRef(view); - } + function wrapView(view: AppView): ViewRef { return new ViewRef(view); } function elementRef(parentView, boundElementIndex) { return new ElementRef(parentView, boundElementIndex); @@ -59,9 +56,7 @@ export function main() { return DirectiveBinding.createFromType(type, annotation); } - function createEmptyElBinder() { - return new ElementBinder(0, null, 0, null, null); - } + function createEmptyElBinder() { return new ElementBinder(0, null, 0, null, null); } function createComponentElBinder(nestedProtoView = null) { var binding = createDirectiveBinding(SomeComponent); @@ -86,15 +81,17 @@ export function main() { } function createElementInjector() { - return SpyObject.stub(new SpyElementInjector(), { - 'isExportingComponent' : false, - 'isExportingElement' : false, - 'getEventEmitterAccessors' : [], - 'getComponent' : null - }, {}); + return SpyObject.stub(new SpyElementInjector(), + { + 'isExportingComponent': false, + 'isExportingElement': false, + 'getEventEmitterAccessors': [], + 'getComponent': null + }, + {}); } - function createView(pv=null, renderViewRef=null) { + function createView(pv = null, renderViewRef = null) { if (isBlank(pv)) { pv = createProtoView(); } @@ -104,19 +101,15 @@ export function main() { var view = new AppView(renderer, pv, MapWrapper.create()); view.render = renderViewRef; var elementInjectors = ListWrapper.createFixedSize(pv.elementBinders.length); - for (var i=0; i { + beforeEach(() => { directiveResolver = new DirectiveResolver(); renderer = new SpyRenderer(); utils = new SpyAppViewManagerUtils(); @@ -125,92 +118,104 @@ export function main() { createdViews = []; createdRenderViews = []; - utils.spy('createView').andCallFake( (proto, renderViewRef, _a, _b) => { - var view = createView(proto, renderViewRef); - ListWrapper.push(createdViews, view); - return view; - }); - utils.spy('attachComponentView').andCallFake( (hostView, elementIndex, childView) => { - hostView.componentChildViews[elementIndex] = childView; - }); - utils.spy('attachViewInContainer').andCallFake( (parentView, elementIndex, _a, _b, atIndex, childView) => { - var viewContainer = parentView.viewContainers[elementIndex]; - if (isBlank(viewContainer)) { - viewContainer = new AppViewContainer(); - parentView.viewContainers[elementIndex] = viewContainer; - } - ListWrapper.insert(viewContainer.views, atIndex, childView); - }); - renderer.spy('createRootHostView').andCallFake( (_b, _c) => { - var rv = new RenderViewRef(); - ListWrapper.push(createdRenderViews, rv); - return rv; - }); - renderer.spy('createView').andCallFake( (_a) => { - var rv = new RenderViewRef(); - ListWrapper.push(createdRenderViews, rv); - return rv; - }); + utils.spy('createView') + .andCallFake((proto, renderViewRef, _a, _b) => { + var view = createView(proto, renderViewRef); + ListWrapper.push(createdViews, view); + return view; + }); + utils.spy('attachComponentView') + .andCallFake((hostView, elementIndex, childView) => { + hostView.componentChildViews[elementIndex] = childView; + }); + utils.spy('attachViewInContainer') + .andCallFake((parentView, elementIndex, _a, _b, atIndex, childView) => { + var viewContainer = parentView.viewContainers[elementIndex]; + if (isBlank(viewContainer)) { + viewContainer = new AppViewContainer(); + parentView.viewContainers[elementIndex] = viewContainer; + } + ListWrapper.insert(viewContainer.views, atIndex, childView); + }); + renderer.spy('createRootHostView') + .andCallFake((_b, _c) => { + var rv = new RenderViewRef(); + ListWrapper.push(createdRenderViews, rv); + return rv; + }); + renderer.spy('createView') + .andCallFake((_a) => { + var rv = new RenderViewRef(); + ListWrapper.push(createdRenderViews, rv); + return rv; + }); }); describe('createDynamicComponentView', () => { describe('basic functionality', () => { var hostView, componentProtoView; - beforeEach( () => { - hostView = createView(createProtoView( - [createComponentElBinder(null)] - )); + beforeEach(() => { + hostView = createView(createProtoView([createComponentElBinder(null)])); componentProtoView = createProtoView(); }); it('should create the view', () => { - expect( - internalView(manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null)) - ).toBe(createdViews[0]); + expect(internalView(manager.createDynamicComponentView( + elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null))) + .toBe(createdViews[0]); expect(createdViews[0].proto).toBe(componentProtoView); }); it('should get the view from the pool', () => { var createdView; - viewPool.spy('getView').andCallFake( (protoView) => { + viewPool.spy('getView').andCallFake((protoView) => { createdView = createView(protoView); return createdView; }); - expect( - internalView(manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null)) - ).toBe(createdView); + expect(internalView(manager.createDynamicComponentView( + elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null))) + .toBe(createdView); expect(utils.spy('createView')).not.toHaveBeenCalled(); expect(renderer.spy('createView')).not.toHaveBeenCalled(); }); it('should attach the view', () => { - manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null) - expect(utils.spy('attachComponentView')).toHaveBeenCalledWith(hostView, 0, createdViews[0]); - expect(renderer.spy('attachComponentView')).toHaveBeenCalledWith(hostView.render, 0, createdViews[0].render); + manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), + wrapPv(componentProtoView), null, null) + 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', () => { var injector = new Injector([], null, false); var componentBinding = bind(SomeComponent).toClass(SomeComponent); - manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), componentBinding, injector); - expect(utils.spy('hydrateDynamicComponentInElementInjector')).toHaveBeenCalledWith(hostView, 0, componentBinding, injector); + manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), + wrapPv(componentProtoView), componentBinding, + injector); + expect(utils.spy('hydrateDynamicComponentInElementInjector')) + .toHaveBeenCalledWith(hostView, 0, componentBinding, injector); }); 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(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render); }); 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(createdViews[0].render).toBe(createdRenderViews[0]); }); 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]; expect(renderer.spy('setEventDispatcher')).toHaveBeenCalledWith(cmpView.render, cmpView); }); @@ -219,30 +224,26 @@ export function main() { describe('error cases', () => { it('should not allow to use non component indices', () => { - var hostView = createView(createProtoView( - [createEmptyElBinder()] - )); + var hostView = createView(createProtoView([createEmptyElBinder()])); var componentProtoView = createProtoView(); - expect( - () => manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null) - ).toThrowError('There is no dynamic component directive at element 0'); + expect(() => manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), + wrapPv(componentProtoView), null, null)) + .toThrowError('There is no dynamic component directive at element 0'); }); it('should not allow to use static component indices', () => { - var hostView = createView(createProtoView( - [createComponentElBinder(createProtoView())] - )); + var hostView = createView(createProtoView([createComponentElBinder(createProtoView())])); var componentProtoView = createProtoView(); - expect( - () => manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null) - ).toThrowError('There is no dynamic component directive at element 0'); + expect(() => manager.createDynamicComponentView(elementRef(wrapView(hostView), 0), + wrapPv(componentProtoView), null, null)) + .toThrowError('There is no dynamic component directive at element 0'); }); }); describe('recursively destroy dynamic child component views', () => { - // TODO - }); + // TODO + }); }); @@ -250,47 +251,48 @@ export function main() { describe('recursively create when not cached', () => { var hostView, componentProtoView, nestedProtoView; - beforeEach( () => { - hostView = createView(createProtoView( - [createComponentElBinder(null)] - )); + beforeEach(() => { + hostView = createView(createProtoView([createComponentElBinder(null)])); nestedProtoView = createProtoView(); - componentProtoView = createProtoView([ - createComponentElBinder(nestedProtoView) - ]); + componentProtoView = createProtoView([createComponentElBinder(nestedProtoView)]); }); 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[1].proto).toBe(nestedProtoView); }); 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(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render); }); 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]) }); 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]; expect(renderer.spy('setEventDispatcher')).toHaveBeenCalledWith(cmpView.render, cmpView); }); }); - describe('recursively hydrate when getting from from the cache', () => { - // TODO(tbosch): implement this - }); + describe('recursively hydrate when getting from from the cache', + () => { + // TODO(tbosch): implement this + }); describe('recursively dehydrate', () => { - // TODO(tbosch): implement this - }); + // TODO(tbosch): implement this + }); }); @@ -301,39 +303,39 @@ export function main() { describe('basic functionality', () => { var parentHostView, parentView, hostProtoView; - beforeEach( () => { - parentHostView = createView(createProtoView( - [createComponentElBinder(null)] - )); + beforeEach(() => { + parentHostView = createView(createProtoView([createComponentElBinder(null)])); parentView = createView(); utils.attachComponentView(parentHostView, 0, parentView); - hostProtoView = createProtoView( - [createComponentElBinder(null)] - ); + hostProtoView = createProtoView([createComponentElBinder(null)]); }); it('should create the view', () => { - expect( - internalView(manager.createFreeHostView(elementRef(wrapView(parentHostView), 0), wrapPv(hostProtoView), null)) - ).toBe(createdViews[0]); + expect(internalView(manager.createFreeHostView(elementRef(wrapView(parentHostView), 0), + wrapPv(hostProtoView), null))) + .toBe(createdViews[0]); expect(createdViews[0].proto).toBe(hostProtoView); }); it('should attachAndHydrate the view', () => { var injector = new Injector([], null, false); - manager.createFreeHostView(elementRef(wrapView(parentHostView), 0), wrapPv(hostProtoView), injector); - expect(utils.spy('attachAndHydrateFreeHostView')).toHaveBeenCalledWith(parentHostView, 0, createdViews[0], injector); + manager.createFreeHostView(elementRef(wrapView(parentHostView), 0), wrapPv(hostProtoView), + injector); + expect(utils.spy('attachAndHydrateFreeHostView')) + .toHaveBeenCalledWith(parentHostView, 0, createdViews[0], injector); expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render); }); 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(createdViews[0].render).toBe(createdRenderViews[0]); }); 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]; expect(renderer.spy('setEventDispatcher')).toHaveBeenCalledWith(cmpView.render, cmpView); }); @@ -345,16 +347,13 @@ export function main() { describe('destroyFreeHostView', () => { describe('basic functionality', () => { var parentHostView, parentView, hostProtoView, hostView, hostRenderViewRef; - beforeEach( () => { - parentHostView = createView(createProtoView( - [createComponentElBinder(null)] - )); + beforeEach(() => { + parentHostView = createView(createProtoView([createComponentElBinder(null)])); parentView = createView(); utils.attachComponentView(parentHostView, 0, parentView); - hostProtoView = createProtoView( - [createComponentElBinder(null)] - ); - hostView = internalView(manager.createFreeHostView(elementRef(wrapView(parentHostView), 0), wrapPv(hostProtoView), null)); + hostProtoView = createProtoView([createComponentElBinder(null)]); + hostView = internalView(manager.createFreeHostView( + elementRef(wrapView(parentHostView), 0), wrapPv(hostProtoView), null)); hostRenderViewRef = hostView.render; }); @@ -371,7 +370,8 @@ export function main() { it('should detach the render view', () => { 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', () => { @@ -381,24 +381,19 @@ export function main() { }); describe('recursively destroy inPlaceHostViews', () => { - // TODO - }); + // TODO + }); }); describe('createRootHostView', () => { var hostProtoView; - beforeEach( () => { - hostProtoView = createProtoView( - [createComponentElBinder(null)] - ); - }); + beforeEach(() => { hostProtoView = createProtoView([createComponentElBinder(null)]); }); it('should create the view', () => { - expect( - internalView(manager.createRootHostView(wrapPv(hostProtoView), null, null)) - ).toBe(createdViews[0]); + expect(internalView(manager.createRootHostView(wrapPv(hostProtoView), null, null))) + .toBe(createdViews[0]); 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', () => { 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]); }); it('should allow to override the selector', () => { var selector = 'someOtherSelector'; 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', () => { @@ -432,10 +429,8 @@ export function main() { describe('destroyRootHostView', () => { var hostProtoView, hostView, hostRenderViewRef; - beforeEach( () => { - hostProtoView = createProtoView( - [createComponentElBinder(null)] - ); + beforeEach(() => { + hostProtoView = createProtoView([createComponentElBinder(null)]); hostView = internalView(manager.createRootHostView(wrapPv(hostProtoView), null, null)); hostRenderViewRef = hostView.render; }); @@ -462,52 +457,59 @@ export function main() { describe('basic functionality', () => { var parentView, childProtoView; - beforeEach( () => { - parentView = createView(createProtoView( - [createEmptyElBinder()] - )); + beforeEach(() => { + parentView = createView(createProtoView([createEmptyElBinder()])); childProtoView = createProtoView(); }); 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(); }); it('should create the view', () => { - expect( - internalView(manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0, wrapPv(childProtoView), null)) - ).toBe(createdViews[0]); + expect(internalView(manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0, + wrapPv(childProtoView), null))) + .toBe(createdViews[0]); expect(createdViews[0].proto).toBe(childProtoView); }); it('should attach the view', () => { var contextView = createView(); - manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0, wrapPv(childProtoView), - elementRef(wrapView(contextView), 1), null); - expect(utils.spy('attachViewInContainer')).toHaveBeenCalledWith(parentView, 0, contextView, 1, 0, createdViews[0]); - expect(renderer.spy('attachViewInContainer')).toHaveBeenCalledWith(parentView.render, 0, 0, createdViews[0].render); + manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0, + wrapPv(childProtoView), + elementRef(wrapView(contextView), 1), null); + expect(utils.spy('attachViewInContainer')) + .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', () => { var injector = new Injector([], null, false); var contextView = createView(); - manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0, wrapPv(childProtoView), - elementRef(wrapView(contextView), 1), injector); - expect(utils.spy('hydrateViewInContainer')).toHaveBeenCalledWith(parentView, 0, contextView, 1, 0, injector); + manager.createViewInContainer(elementRef(wrapView(parentView), 0), 0, + wrapPv(childProtoView), + elementRef(wrapView(contextView), 1), injector); + expect(utils.spy('hydrateViewInContainer')) + .toHaveBeenCalledWith(parentView, 0, contextView, 1, 0, injector); expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render); }); 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(createdViews[0].render).toBe(createdRenderViews[0]); }); 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]; - 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', () => { var parentView, childProtoView, childView; - beforeEach( () => { - parentView = createView(createProtoView( - [createEmptyElBinder()] - )); + beforeEach(() => { + parentView = createView(createProtoView([createEmptyElBinder()])); 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', () => { 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); }); it('should detach', () => { manager.destroyViewInContainer(elementRef(wrapView(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', () => { @@ -545,24 +548,25 @@ export function main() { describe('recursively destroy views in ViewContainers', () => { var parentView, childProtoView, childView; - beforeEach( () => { - parentView = createView(createProtoView( - [createEmptyElBinder()] - )); + beforeEach(() => { + parentView = createView(createProtoView([createEmptyElBinder()])); 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', () => { 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); }); it('should detach', () => { manager.destroyRootHostView(wrapView(parentView)); 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', () => { @@ -576,50 +580,51 @@ export function main() { describe('attachViewInContainer', () => { - }); + }); describe('detachViewInContainer', () => { - }); + }); }); } class MockProtoViewRef extends RenderProtoViewRef { - nestedComponentCount:number; - constructor(nestedComponentCount:number) { + nestedComponentCount: number; + constructor(nestedComponentCount: number) { super(); this.nestedComponentCount = nestedComponentCount; } } -@Component({ selector: 'someComponent' }) -class SomeComponent {} +@Component({selector: 'someComponent'}) +class SomeComponent { +} @proxy @IMPLEMENTS(Renderer) class SpyRenderer extends SpyObject { - constructor(){super(Renderer);} - noSuchMethod(m){return super.noSuchMethod(m)} + constructor() { super(Renderer); } + noSuchMethod(m) { return super.noSuchMethod(m) } } @proxy @IMPLEMENTS(AppViewPool) class SpyAppViewPool extends SpyObject { - constructor(){super(AppViewPool);} - noSuchMethod(m){return super.noSuchMethod(m)} + constructor() { super(AppViewPool); } + noSuchMethod(m) { return super.noSuchMethod(m) } } @proxy @IMPLEMENTS(AppViewManagerUtils) class SpyAppViewManagerUtils extends SpyObject { - constructor(){super(AppViewManagerUtils);} - noSuchMethod(m){return super.noSuchMethod(m)} + constructor() { super(AppViewManagerUtils); } + noSuchMethod(m) { return super.noSuchMethod(m) } } @proxy @IMPLEMENTS(ElementInjector) class SpyElementInjector extends SpyObject { - constructor(){super(ElementInjector);} - noSuchMethod(m){return super.noSuchMethod(m)} + constructor() { super(ElementInjector); } + noSuchMethod(m) { return super.noSuchMethod(m) } } diff --git a/modules/angular2/test/core/compiler/view_manager_utils_spec.js b/modules/angular2/test/core/compiler/view_manager_utils_spec.ts similarity index 59% rename from modules/angular2/test/core/compiler/view_manager_utils_spec.js rename to modules/angular2/test/core/compiler/view_manager_utils_spec.ts index 42ffa7b171..318083e498 100644 --- a/modules/angular2/test/core/compiler/view_manager_utils_spec.js +++ b/modules/angular2/test/core/compiler/view_manager_utils_spec.ts @@ -12,7 +12,8 @@ import { beforeEachBindings, it, xit, - SpyObject, proxy, + SpyObject, + proxy, Log } 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 {ChangeDetector} from 'angular2/change_detection'; 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 {Component} from 'angular2/src/core/annotations_impl/annotations'; +import {Component} from 'angular2/annotations'; import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils'; export function main() { @@ -36,18 +41,14 @@ export function main() { var directiveResolver; var utils; - function createInjector() { - return new Injector([], null, false); - } + function createInjector() { return new Injector([], null, false); } function createDirectiveBinding(type) { var annotation = directiveResolver.resolve(type); return DirectiveBinding.createFromType(type, annotation); } - function createEmptyElBinder() { - return new ElementBinder(0, null, 0, null, null); - } + function createEmptyElBinder() { return new ElementBinder(0, null, 0, null, null); } function createComponentElBinder(nestedProtoView = null) { var binding = createDirectiveBinding(SomeComponent); @@ -67,38 +68,36 @@ export function main() { function createElementInjector() { var host = new SpyElementInjector(); - return SpyObject.stub(new SpyElementInjector(), { - 'isExportingComponent' : false, - 'isExportingElement' : false, - 'getEventEmitterAccessors' : [], - 'getHostActionAccessors' : [], - 'getComponent' : null, - 'getDynamicallyLoadedComponent': null, - 'getHost': host - }, {}); + return SpyObject.stub(new SpyElementInjector(), + { + 'isExportingComponent': false, + 'isExportingElement': false, + 'getEventEmitterAccessors': [], + 'getHostActionAccessors': [], + 'getComponent': null, + 'getDynamicallyLoadedComponent': null, + 'getHost': host + }, + {}); } - function createView(pv=null) { + function createView(pv = null) { if (isBlank(pv)) { pv = createProtoView(); } var view = new AppView(null, pv, MapWrapper.create()); var elementInjectors = ListWrapper.createFixedSize(pv.elementBinders.length); var preBuiltObjects = ListWrapper.createFixedSize(pv.elementBinders.length); - for (var i=0; inew SpyChangeDetector(), elementInjectors, elementInjectors, preBuiltObjects, + ListWrapper.createFixedSize(pv.elementBinders.length)); return view; } - beforeEach( () => { + beforeEach(() => { directiveResolver = new DirectiveResolver(); utils = new AppViewManagerUtils(directiveResolver); }); @@ -106,16 +105,13 @@ export function main() { describe('hydrateDynamicComponentInElementInjector', () => { it('should not allow to overwrite an existing component', () => { - var hostView = createView(createProtoView( - [createComponentElBinder(createProtoView())] - )); + var hostView = createView(createProtoView([createComponentElBinder(createProtoView())])); var componentBinding = bind(SomeComponent).toClass(SomeComponent); - SpyObject.stub(hostView.elementInjectors[0], { - 'getDynamicallyLoadedComponent': new SomeComponent() - }); - expect( - () => utils.hydrateDynamicComponentInElementInjector(hostView, 0, componentBinding, null) - ).toThrowError('There already is a dynamic component loaded at element 0'); + SpyObject.stub(hostView.elementInjectors[0], + {'getDynamicallyLoadedComponent': new SomeComponent()}); + expect(() => utils.hydrateDynamicComponentInElementInjector(hostView, 0, componentBinding, + null)) + .toThrowError('There already is a dynamic component loaded at element 0'); }); }); @@ -129,16 +125,16 @@ export function main() { var hostView = createView(createProtoView([createComponentElBinder(createProtoView())])); hostView.componentChildViews = [componentView]; - // (() => () nonsense is required until our transpiler supports type casting - var spyEi = (() => componentView.elementInjectors[0])(); + var spyEi = componentView.elementInjectors[0]; spyEi.spy('hydrate').andCallFake(log.fn('hydrate')); - var spyCd = (() => componentView.changeDetector)(); + var spyCd = componentView.changeDetector; spyCd.spy('hydrate').andCallFake(log.fn('hydrateCD')); 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", () => { var dir = new Object(); - var hostPv = createProtoView([ - createComponentElBinder(null), - createEmptyElBinder() - ]); + var hostPv = createProtoView([createComponentElBinder(null), createEmptyElBinder()]); var hostView = createView(hostPv); - var spyEventAccessor1 = SpyObject.stub({"subscribe" : null}); + var spyEventAccessor1 = SpyObject.stub({"subscribe": null}); SpyObject.stub(hostView.elementInjectors[0], { 'getHostActionAccessors': [], 'getEventEmitterAccessors': [[spyEventAccessor1]], 'getDirectiveAtIndex': dir }); - var spyEventAccessor2 = SpyObject.stub({"subscribe" : null}); + var spyEventAccessor2 = SpyObject.stub({"subscribe": null}); SpyObject.stub(hostView.elementInjectors[1], { 'getHostActionAccessors': [], 'getEventEmitterAccessors': [[spyEventAccessor2]], @@ -178,18 +171,15 @@ export function main() { it("should set up host action listeners", () => { var dir = new Object(); - var hostPv = createProtoView([ - createComponentElBinder(null), - createEmptyElBinder() - ]); + var hostPv = createProtoView([createComponentElBinder(null), createEmptyElBinder()]); var hostView = createView(hostPv); - var spyActionAccessor1 = SpyObject.stub({"subscribe" : null}); + var spyActionAccessor1 = SpyObject.stub({"subscribe": null}); SpyObject.stub(hostView.elementInjectors[0], { 'getHostActionAccessors': [[spyActionAccessor1]], 'getEventEmitterAccessors': [], 'getDirectiveAtIndex': dir }); - var spyActionAccessor2 = SpyObject.stub({"subscribe" : null}); + var spyActionAccessor2 = SpyObject.stub({"subscribe": null}); SpyObject.stub(hostView.elementInjectors[1], { 'getHostActionAccessors': [[spyActionAccessor2]], 'getEventEmitterAccessors': [], @@ -211,28 +201,23 @@ export function main() { var parentView, contextView, childView; function createViews() { - var parentPv = createProtoView([ - createEmptyElBinder() - ]); + var parentPv = createProtoView([createEmptyElBinder()]); parentView = createView(parentPv); - var contextPv = createProtoView([ - createEmptyElBinder() - ]); + var contextPv = createProtoView([createEmptyElBinder()]); contextView = createView(contextPv); - var childPv = createProtoView([ - createEmptyElBinder() - ]); + var childPv = createProtoView([createEmptyElBinder()]); childView = createView(childPv); } - it('should link the views rootElementInjectors after the elementInjector at the given context', () => { - createViews(); - utils.attachViewInContainer(parentView, 0, contextView, 0, 0, childView); - expect(childView.rootElementInjectors[0].spy('linkAfter')) - .toHaveBeenCalledWith(contextView.elementInjectors[0], null); - }); + it('should link the views rootElementInjectors after the elementInjector at the given context', + () => { + createViews(); + utils.attachViewInContainer(parentView, 0, contextView, 0, 0, childView); + expect(childView.rootElementInjectors[0].spy('linkAfter')) + .toHaveBeenCalledWith(contextView.elementInjectors[0], null); + }); }); @@ -240,30 +225,26 @@ export function main() { var parentView, contextView, childView; function createViews() { - var parentPv = createProtoView([ - createEmptyElBinder() - ]); + var parentPv = createProtoView([createEmptyElBinder()]); parentView = createView(parentPv); - var contextPv = createProtoView([ - createEmptyElBinder() - ]); + var contextPv = createProtoView([createEmptyElBinder()]); contextView = createView(contextPv); - var childPv = createProtoView([ - createEmptyElBinder() - ]); + var childPv = createProtoView([createEmptyElBinder()]); childView = createView(childPv); utils.attachViewInContainer(parentView, 0, contextView, 0, 0, childView); } - it("should instantiate the elementInjectors with the host of the context's elementInjector", () => { - createViews(); + it("should instantiate the elementInjectors with the host of the context's elementInjector", + () => { + createViews(); - utils.hydrateViewInContainer(parentView, 0, contextView, 0, 0, null); - expect(childView.rootElementInjectors[0].spy('hydrate')) - .toHaveBeenCalledWith(null, contextView.elementInjectors[0].getHost(), childView.preBuiltObjects[0]); - }); + utils.hydrateViewInContainer(parentView, 0, contextView, 0, 0, null); + expect(childView.rootElementInjectors[0].spy('hydrate')) + .toHaveBeenCalledWith(null, contextView.elementInjectors[0].getHost(), + childView.preBuiltObjects[0]); + }); }); @@ -271,47 +252,46 @@ export function main() { var hostView; function createViews() { - var hostPv = createProtoView([ - createComponentElBinder() - ]); + var hostPv = createProtoView([createComponentElBinder()]); hostView = createView(hostPv); } - it("should instantiate the elementInjectors with the given injector and an empty host element injector", () => { - var injector = createInjector(); - createViews(); + it("should instantiate the elementInjectors with the given injector and an empty host element injector", + () => { + var injector = createInjector(); + createViews(); - utils.hydrateRootHostView(hostView, injector); - expect(hostView.rootElementInjectors[0].spy('hydrate')) - .toHaveBeenCalledWith(injector, null, hostView.preBuiltObjects[0]); - }); + utils.hydrateRootHostView(hostView, injector); + expect(hostView.rootElementInjectors[0].spy('hydrate')) + .toHaveBeenCalledWith(injector, null, hostView.preBuiltObjects[0]); + }); }); }); - } -@Component({ selector: 'someComponent' }) -class SomeComponent {} +@Component({selector: 'someComponent'}) +class SomeComponent { +} @proxy @IMPLEMENTS(ElementInjector) class SpyElementInjector extends SpyObject { - constructor(){super(ElementInjector);} - noSuchMethod(m){return super.noSuchMethod(m)} + constructor() { super(ElementInjector); } + noSuchMethod(m) { return super.noSuchMethod(m) } } @proxy @IMPLEMENTS(ChangeDetector) class SpyChangeDetector extends SpyObject { - constructor(){super(ChangeDetector);} - noSuchMethod(m){return super.noSuchMethod(m)} + constructor() { super(ChangeDetector); } + noSuchMethod(m) { return super.noSuchMethod(m) } } @proxy @IMPLEMENTS(PreBuiltObjects) class SpyPreBuiltObjects extends SpyObject { - constructor(){super(PreBuiltObjects);} - noSuchMethod(m){return super.noSuchMethod(m)} + constructor() { super(PreBuiltObjects); } + noSuchMethod(m) { return super.noSuchMethod(m) } } diff --git a/modules/angular2/test/core/compiler/view_pool_spec.js b/modules/angular2/test/core/compiler/view_pool_spec.ts similarity index 76% rename from modules/angular2/test/core/compiler/view_pool_spec.js rename to modules/angular2/test/core/compiler/view_pool_spec.ts index d14f8abf46..78f9dd1627 100644 --- a/modules/angular2/test/core/compiler/view_pool_spec.js +++ b/modules/angular2/test/core/compiler/view_pool_spec.ts @@ -12,7 +12,8 @@ import { beforeEachBindings, it, xit, - SpyObject, proxy + SpyObject, + proxy } from 'angular2/test_lib'; import {AppViewPool} from 'angular2/src/core/compiler/view_pool'; import {AppProtoView, AppView} from 'angular2/src/core/compiler/view'; @@ -21,20 +22,14 @@ import {MapWrapper, Map} from 'angular2/src/facade/collection'; export function main() { describe('AppViewPool', () => { - function createViewPool({capacity}):AppViewPool { - return new AppViewPool(capacity); - } + function createViewPool({capacity}): AppViewPool { return new AppViewPool(capacity); } - function createProtoView() { - return new AppProtoView(null, null, null); - } + function createProtoView() { return new AppProtoView(null, null, null); } - function createView(pv) { - return new AppView(null, pv, MapWrapper.create()); - } + function createView(pv) { return new AppView(null, pv, MapWrapper.create()); } it('should support multiple AppProtoViews', () => { - var vf = createViewPool({ capacity: 2 }); + var vf = createViewPool({capacity: 2}); var pv1 = createProtoView(); var pv2 = createProtoView(); var view1 = createView(pv1); @@ -48,7 +43,7 @@ export function main() { it('should reuse the newest view that has been returned', () => { var pv = createProtoView(); - var vf = createViewPool({ capacity: 2 }); + var vf = createViewPool({capacity: 2}); var view1 = createView(pv); var view2 = createView(pv); vf.returnView(view1); @@ -59,7 +54,7 @@ export function main() { it('should not add views when the capacity has been reached', () => { var pv = createProtoView(); - var vf = createViewPool({ capacity: 2 }); + var vf = createViewPool({capacity: 2}); var view1 = createView(pv); var view2 = createView(pv); var view3 = createView(pv); diff --git a/modules/angular2/test/core/forward_ref_integration_spec.es6 b/modules/angular2/test/core/forward_ref_integration_spec.es6 deleted file mode 100644 index 9b802baca4..0000000000 --- a/modules/angular2/test/core/forward_ref_integration_spec.es6 +++ /dev/null @@ -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: ``, - directives: [ - bind(forwardRef(() => Door)).toClass(forwardRef(() => Door)), - bind(forwardRef(() => Lock)).toClass(forwardRef(() => Lock)) - ] -}) -class App { -} - -@Component({ - selector: 'Lock' -}) -@View({ - directives: [NgFor], - template: `{{frame.name}}({{lock.name}})` -}) -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'; - } -} diff --git a/modules/angular2/test/core/forward_ref_integration_spec.ts b/modules/angular2/test/core/forward_ref_integration_spec.ts new file mode 100644 index 0000000000..85ce082662 --- /dev/null +++ b/modules/angular2/test/core/forward_ref_integration_spec.ts @@ -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: ``, + directives: [ + bind(forwardRef(() => Door)) + .toClass(forwardRef(() => Door)), + bind(forwardRef(() => Lock)).toClass(forwardRef(() => Lock)) + ] +}) +class App { +} + +@Component({selector: 'Lock'}) +@View({ + directives: [NgFor], + template: `{{frame.name}}({{lock.name}})` +}) +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'; } +} diff --git a/modules/angular2/test/core/testability/testability_spec.js b/modules/angular2/test/core/testability/testability_spec.ts similarity index 90% rename from modules/angular2/test/core/testability/testability_spec.js rename to modules/angular2/test/core/testability/testability_spec.ts index ef5ea244bf..6d18a7a4da 100644 --- a/modules/angular2/test/core/testability/testability_spec.js +++ b/modules/angular2/test/core/testability/testability_spec.ts @@ -11,9 +11,8 @@ export function main() { executed = false; }); - it('should start with a pending count of 0', () => { - expect(testability.getPendingCount()).toEqual(0); - }); + it('should start with a pending count of 0', + () => { expect(testability.getPendingCount()).toEqual(0); }); it('should fire whenstable callbacks if pending count is 0', () => { testability.whenStable(() => executed = true); diff --git a/modules/angular2/test/core/zone/ng_zone_spec.js b/modules/angular2/test/core/zone/ng_zone_spec.js deleted file mode 100644 index a78249e0c1..0000000000 --- a/modules/angular2/test/core/zone/ng_zone_spec.js +++ /dev/null @@ -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); - })); - }); -} diff --git a/modules/angular2/test/core/zone/ng_zone_spec.ts b/modules/angular2/test/core/zone/ng_zone_spec.ts new file mode 100644 index 0000000000..71419602e5 --- /dev/null +++ b/modules/angular2/test/core/zone/ng_zone_spec.ts @@ -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); + })); + }); +}