diff --git a/modules/@angular/core/test/linker/query_integration_spec.ts b/modules/@angular/core/test/linker/query_integration_spec.ts index 329a814c0e..4dda3df143 100644 --- a/modules/@angular/core/test/linker/query_integration_spec.ts +++ b/modules/@angular/core/test/linker/query_integration_spec.ts @@ -8,6 +8,7 @@ import {NgFor, NgIf} from '@angular/common'; import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, Component, ContentChild, ContentChildren, Directive, QueryList, TemplateRef, ViewChild, ViewChildren, ViewContainerRef, asNativeElements} from '@angular/core'; +import {TestBed, async} from '@angular/core/testing'; import {AsyncTestCompleter, TestComponentBuilder, beforeEach, ddescribe, describe, iit, inject, it, xit} from '@angular/core/testing/testing_internal'; import {expect} from '@angular/platform-browser/testing/matchers'; @@ -15,767 +16,621 @@ import {isPresent, stringify} from '../../src/facade/lang'; export function main() { describe('Query API', () => { + + beforeEach(() => TestBed.configureTestingModule({ + declarations: [ + MyComp0, + NeedsQuery, + NeedsQueryDesc, + NeedsQueryByLabel, + NeedsQueryByTwoLabels, + NeedsQueryAndProject, + NeedsViewQuery, + NeedsViewQueryIf, + NeedsViewQueryNestedIf, + NeedsViewQueryOrder, + NeedsViewQueryByLabel, + NeedsViewQueryOrderWithParent, + NeedsContentChildren, + NeedsViewChildren, + NeedsViewChild, + NeedsStaticContentAndViewChild, + NeedsContentChild, + NeedsTpl, + NeedsNamedTpl, + TextDirective, + InertDirective, + NeedsFourQueries, + NeedsContentChildrenWithRead, + NeedsContentChildWithRead, + NeedsViewChildrenWithRead, + NeedsViewChildWithRead, + NeedsViewContainerWithRead + ] + })); + describe('querying by directive type', () => { - it('should contain all direct child directives in the light dom (constructor)', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = '
' + - '
' + - '
' + - '
' + - '
'; + it('should contain all direct child directives in the light dom (constructor)', () => { + const template = '
' + + '
' + + '
' + + '
' + + '
'; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - view.detectChanges(); + view.detectChanges(); - expect(asNativeElements(view.debugElement.children)).toHaveText('2|3|'); + expect(asNativeElements(view.debugElement.children)).toHaveText('2|3|'); + }); - async.done(); - }); - })); + it('should contain all direct child directives in the content dom', () => { + const template = + '
'; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - it('should contain all direct child directives in the content dom', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = - '
'; + view.detectChanges(); - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - view.detectChanges(); + var q = view.debugElement.children[0].references['q']; - var q = view.debugElement.children[0].references['q']; + view.detectChanges(); - view.detectChanges(); + expect(q.textDirChildren.length).toEqual(1); + expect(q.numberOfChildrenAfterContentInit).toEqual(1); + }); - expect(q.textDirChildren.length).toEqual(1); - expect(q.numberOfChildrenAfterContentInit).toEqual(1); + it('should contain the first content child', () => { + const template = + '
'; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - async.done(); - }); - })); + view.debugElement.componentInstance.shouldShow = true; + view.detectChanges(); - it('should contain the first content child', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = - '
'; + var q: NeedsContentChild = view.debugElement.children[0].references['q']; - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - view.debugElement.componentInstance.shouldShow = true; - view.detectChanges(); + expect(q.logs).toEqual([['setter', 'foo'], ['init', 'foo'], ['check', 'foo']]); - var q: NeedsContentChild = view.debugElement.children[0].references['q']; + view.debugElement.componentInstance.shouldShow = false; + view.detectChanges(); - expect(q.logs).toEqual([['setter', 'foo'], ['init', 'foo'], ['check', 'foo']]); + expect(q.logs).toEqual([ + ['setter', 'foo'], ['init', 'foo'], ['check', 'foo'], ['setter', null], ['check', null] + ]); + }); - view.debugElement.componentInstance.shouldShow = false; - view.detectChanges(); + it('should contain the first view child', () => { + const template = ''; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - expect(q.logs).toEqual([ - ['setter', 'foo'], ['init', 'foo'], ['check', 'foo'], ['setter', null], - ['check', null] - ]); + view.detectChanges(); + var q: NeedsViewChild = view.debugElement.children[0].references['q']; - async.done(); - }); - })); + expect(q.logs).toEqual([['setter', 'foo'], ['init', 'foo'], ['check', 'foo']]); - it('should contain the first view child', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = ''; + q.shouldShow = false; + view.detectChanges(); - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - view.detectChanges(); - var q: NeedsViewChild = view.debugElement.children[0].references['q']; + expect(q.logs).toEqual([ + ['setter', 'foo'], ['init', 'foo'], ['check', 'foo'], ['setter', null], ['check', null] + ]); + }); - expect(q.logs).toEqual([['setter', 'foo'], ['init', 'foo'], ['check', 'foo']]); + it('should set static view and content children already after the constructor call', () => { + const template = + '
'; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - q.shouldShow = false; - view.detectChanges(); + var q: NeedsStaticContentAndViewChild = view.debugElement.children[0].references['q']; + expect(q.contentChild.text).toBeFalsy(); + expect(q.viewChild.text).toBeFalsy(); - expect(q.logs).toEqual([ - ['setter', 'foo'], ['init', 'foo'], ['check', 'foo'], ['setter', null], - ['check', null] - ]); + view.detectChanges(); - async.done(); - }); - })); + expect(q.contentChild.text).toEqual('contentFoo'); + expect(q.viewChild.text).toEqual('viewFoo'); + }); - it('should set static view and content children already after the constructor call', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = - '
'; + it('should contain the first view child accross embedded views', () => { + TestBed.overrideComponent( + MyComp0, {set: {template: ''}}); + TestBed.overrideComponent(NeedsViewChild, { + set: { + template: + '
' + } + }); + const view = TestBed.createComponent(MyComp0); - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - var q: NeedsStaticContentAndViewChild = - view.debugElement.children[0].references['q']; - expect(q.contentChild.text).toBeFalsy(); - expect(q.viewChild.text).toBeFalsy(); + view.detectChanges(); + var q: NeedsViewChild = view.debugElement.children[0].references['q']; - view.detectChanges(); + expect(q.logs).toEqual([['setter', 'foo'], ['init', 'foo'], ['check', 'foo']]); - expect(q.contentChild.text).toEqual('contentFoo'); - expect(q.viewChild.text).toEqual('viewFoo'); + q.shouldShow = false; + q.shouldShow2 = true; + q.logs = []; + view.detectChanges(); - async.done(); - }); - })); + expect(q.logs).toEqual([['setter', 'bar'], ['check', 'bar']]); - it('should contain the first view child accross embedded views', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = ''; - tcb.overrideTemplate(MyComp0, template) - .overrideTemplate( - NeedsViewChild, - '
') - .createAsync(MyComp0) - .then((view) => { - view.detectChanges(); - var q: NeedsViewChild = view.debugElement.children[0].references['q']; + q.shouldShow = false; + q.shouldShow2 = false; + q.logs = []; + view.detectChanges(); - expect(q.logs).toEqual([['setter', 'foo'], ['init', 'foo'], ['check', 'foo']]); + expect(q.logs).toEqual([['setter', null], ['check', null]]); + }); - q.shouldShow = false; - q.shouldShow2 = true; - q.logs = []; - view.detectChanges(); + it('should contain all directives in the light dom when descendants flag is used', () => { + const template = '
' + + '
' + + '
' + + '
' + + '
'; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - expect(q.logs).toEqual([['setter', 'bar'], ['check', 'bar']]); + view.detectChanges(); + expect(asNativeElements(view.debugElement.children)).toHaveText('2|3|4|'); + }); - q.shouldShow = false; - q.shouldShow2 = false; - q.logs = []; - view.detectChanges(); + it('should contain all directives in the light dom', () => { + const template = '
' + + '
' + + '
'; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - expect(q.logs).toEqual([['setter', null], ['check', null]]); + view.detectChanges(); + expect(asNativeElements(view.debugElement.children)).toHaveText('2|3|'); + }); - async.done(); - }); - })); + it('should reflect dynamically inserted directives', () => { + const template = '
' + + '
' + + '
'; + ; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - it('should contain all directives in the light dom when descendants flag is used', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = '
' + - '
' + - '
' + - '
' + - '
'; + view.detectChanges(); + expect(asNativeElements(view.debugElement.children)).toHaveText('2|'); - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - view.detectChanges(); - expect(asNativeElements(view.debugElement.children)).toHaveText('2|3|4|'); + view.debugElement.componentInstance.shouldShow = true; + view.detectChanges(); + expect(asNativeElements(view.debugElement.children)).toHaveText('2|3|'); + }); - async.done(); - }); - })); + it('should be cleanly destroyed when a query crosses view boundaries', () => { + const template = '
' + + '
' + + '
'; + ; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const fixture = TestBed.createComponent(MyComp0); - it('should contain all directives in the light dom', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = '
' + - '
' + - '
'; + fixture.debugElement.componentInstance.shouldShow = true; + fixture.detectChanges(); + fixture.destroy(); + }); - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - view.detectChanges(); - expect(asNativeElements(view.debugElement.children)).toHaveText('2|3|'); + it('should reflect moved directives', () => { + const template = '
' + + '
' + + '
'; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - async.done(); - }); - })); + view.detectChanges(); - it('should reflect dynamically inserted directives', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = '
' + - '
' + - '
'; + expect(asNativeElements(view.debugElement.children)).toHaveText('2|1d|2d|3d|'); - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { + view.debugElement.componentInstance.list = ['3d', '2d']; + view.detectChanges(); + expect(asNativeElements(view.debugElement.children)).toHaveText('2|3d|2d|'); + }); - view.detectChanges(); - expect(asNativeElements(view.debugElement.children)).toHaveText('2|'); - - view.debugElement.componentInstance.shouldShow = true; - view.detectChanges(); - expect(asNativeElements(view.debugElement.children)).toHaveText('2|3|'); - - async.done(); - }); - })); - - it('should be cleanly destroyed when a query crosses view boundaries', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = '
' + - '
' + - '
'; - - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((fixture) => { - fixture.debugElement.componentInstance.shouldShow = true; - fixture.detectChanges(); - fixture.destroy(); - - async.done(); - }); - })); - - it('should reflect moved directives', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = '
' + - '
' + - '
'; - - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - view.detectChanges(); - - expect(asNativeElements(view.debugElement.children)).toHaveText('2|1d|2d|3d|'); - - view.debugElement.componentInstance.list = ['3d', '2d']; - view.detectChanges(); - expect(asNativeElements(view.debugElement.children)).toHaveText('2|3d|2d|'); - - async.done(); - }); - })); - - it('should throw with descriptive error when query selectors are not present', - inject([TestComponentBuilder], (tcb: TestComponentBuilder) => { - expect( - () => tcb.overrideTemplate( - MyCompBroken0, '') - .createAsync(MyCompBroken0)) - .toThrowError( - `Can't construct a query for the property "errorTrigger" of "${stringify(HasNullQueryCondition)}" since the query selector wasn't defined.`); - })); + it('should throw with descriptive error when query selectors are not present', () => { + TestBed.configureTestingModule({declarations: [MyCompBroken0, HasNullQueryCondition]}); + const template = ''; + TestBed.overrideComponent(MyCompBroken0, {set: {template}}); + expect(() => TestBed.createComponent(MyCompBroken0)) + .toThrowError( + `Can't construct a query for the property "errorTrigger" of "${stringify(HasNullQueryCondition)}" since the query selector wasn't defined.`); + }); }); describe('query for TemplateRef', () => { - it('should find TemplateRefs in the light and shadow dom', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = ''; - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - view.detectChanges(); - var needsTpl: NeedsTpl = view.debugElement.children[0].injector.get(NeedsTpl); + it('should find TemplateRefs in the light and shadow dom', () => { + const template = ''; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - expect(needsTpl.vc.createEmbeddedView(needsTpl.query.first).rootNodes[0]) - .toHaveText('light'); - expect(needsTpl.vc.createEmbeddedView(needsTpl.viewQuery.first).rootNodes[0]) - .toHaveText('shadow'); + view.detectChanges(); + var needsTpl: NeedsTpl = view.debugElement.children[0].injector.get(NeedsTpl); - async.done(); - }); - })); + expect(needsTpl.vc.createEmbeddedView(needsTpl.query.first).rootNodes[0]) + .toHaveText('light'); + expect(needsTpl.vc.createEmbeddedView(needsTpl.viewQuery.first).rootNodes[0]) + .toHaveText('shadow'); + }); - it('should find named TemplateRefs', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = - ''; - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - view.detectChanges(); - var needsTpl: NeedsNamedTpl = - view.debugElement.children[0].injector.get(NeedsNamedTpl); - expect(needsTpl.vc.createEmbeddedView(needsTpl.contentTpl).rootNodes[0]) - .toHaveText('light'); - expect(needsTpl.vc.createEmbeddedView(needsTpl.viewTpl).rootNodes[0]) - .toHaveText('shadow'); + it('should find named TemplateRefs', () => { + const template = + ''; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - async.done(); - }); - })); + view.detectChanges(); + var needsTpl: NeedsNamedTpl = view.debugElement.children[0].injector.get(NeedsNamedTpl); + expect(needsTpl.vc.createEmbeddedView(needsTpl.contentTpl).rootNodes[0]) + .toHaveText('light'); + expect(needsTpl.vc.createEmbeddedView(needsTpl.viewTpl).rootNodes[0]).toHaveText('shadow'); + }); }); describe('read a different token', () => { - it('should contain all content children', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = - '
'; + it('should contain all content children', () => { + const template = + '
'; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - view.detectChanges(); + view.detectChanges(); - var comp: NeedsContentChildrenWithRead = - view.debugElement.children[0].injector.get(NeedsContentChildrenWithRead); - expect(comp.textDirChildren.map(textDirective => textDirective.text)).toEqual([ - 'ca', 'cb' - ]); + var comp: NeedsContentChildrenWithRead = + view.debugElement.children[0].injector.get(NeedsContentChildrenWithRead); + expect(comp.textDirChildren.map(textDirective => textDirective.text)).toEqual(['ca', 'cb']); + }); - async.done(); - }); - })); + it('should contain the first content child', () => { + const template = + '
'; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - it('should contain the first content child', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = - '
'; + view.detectChanges(); - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - view.detectChanges(); + var comp: NeedsContentChildWithRead = + view.debugElement.children[0].injector.get(NeedsContentChildWithRead); + expect(comp.textDirChild.text).toEqual('ca'); + }); - var comp: NeedsContentChildWithRead = - view.debugElement.children[0].injector.get(NeedsContentChildWithRead); - expect(comp.textDirChild.text).toEqual('ca'); + it('should contain the first view child', () => { + const template = ''; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - async.done(); - }); - })); + view.detectChanges(); - it('should contain the first view child', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = ''; + var comp: NeedsViewChildWithRead = + view.debugElement.children[0].injector.get(NeedsViewChildWithRead); + expect(comp.textDirChild.text).toEqual('va'); + }); - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - view.detectChanges(); + it('should contain all child directives in the view', () => { + const template = ''; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - var comp: NeedsViewChildWithRead = - view.debugElement.children[0].injector.get(NeedsViewChildWithRead); - expect(comp.textDirChild.text).toEqual('va'); + view.detectChanges(); - async.done(); - }); - })); + var comp: NeedsViewChildrenWithRead = + view.debugElement.children[0].injector.get(NeedsViewChildrenWithRead); + expect(comp.textDirChildren.map(textDirective => textDirective.text)).toEqual(['va', 'vb']); + }); - it('should contain all child directives in the view', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = ''; + it('should support reading a ViewContainer', () => { + const template = + ''; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - view.detectChanges(); + view.detectChanges(); - var comp: NeedsViewChildrenWithRead = - view.debugElement.children[0].injector.get(NeedsViewChildrenWithRead); - expect(comp.textDirChildren.map(textDirective => textDirective.text)).toEqual([ - 'va', 'vb' - ]); - - async.done(); - }); - })); - - it('should support reading a ViewContainer', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = - ''; - - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - view.detectChanges(); - - var comp: NeedsViewContainerWithRead = - view.debugElement.children[0].injector.get(NeedsViewContainerWithRead); - comp.createView(); - expect(view.debugElement.children[0].nativeElement).toHaveText('hello'); - - async.done(); - }); - })); + var comp: NeedsViewContainerWithRead = + view.debugElement.children[0].injector.get(NeedsViewContainerWithRead); + comp.createView(); + expect(view.debugElement.children[0].nativeElement).toHaveText('hello'); + }); }); describe('changes', () => { - it('should notify query on change', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = '' + - '
' + - '
' + - '
'; + it('should notify query on change', async(() => { + const template = '' + + '
' + + '
' + + '
'; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - var q = view.debugElement.children[0].references['q']; - view.detectChanges(); + var q = view.debugElement.children[0].references['q']; + view.detectChanges(); - q.query.changes.subscribe({ - next: () => { - expect(q.query.first.text).toEqual('1'); - expect(q.query.last.text).toEqual('2'); - async.done(); - } - }); + q.query.changes.subscribe({ + next: () => { + expect(q.query.first.text).toEqual('1'); + expect(q.query.last.text).toEqual('2'); + } + }); - view.debugElement.componentInstance.shouldShow = true; - view.detectChanges(); - }); - })); + view.debugElement.componentInstance.shouldShow = true; + view.detectChanges(); + })); it('should correctly clean-up when destroyed together with the directives it is querying', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = - '
'; + () => { + const template = + '
'; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - view.debugElement.componentInstance.shouldShow = true; - view.detectChanges(); + view.debugElement.componentInstance.shouldShow = true; + view.detectChanges(); - var q: NeedsQuery = view.debugElement.children[0].references['q']; + var q: NeedsQuery = view.debugElement.children[0].references['q']; - expect(q.query.length).toEqual(1); + expect(q.query.length).toEqual(1); - view.debugElement.componentInstance.shouldShow = false; - view.detectChanges(); + view.debugElement.componentInstance.shouldShow = false; + view.detectChanges(); - view.debugElement.componentInstance.shouldShow = true; - view.detectChanges(); + view.debugElement.componentInstance.shouldShow = true; + view.detectChanges(); - var q2: NeedsQuery = view.debugElement.children[0].references['q']; + var q2: NeedsQuery = view.debugElement.children[0].references['q']; - expect(q2.query.length).toEqual(1); - - async.done(); - }); - })); + expect(q2.query.length).toEqual(1); + }); }); describe('querying by var binding', () => { it('should contain all the child directives in the light dom with the given var binding', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = '' + - '
' + - '
'; + () => { + const template = '' + + '
' + + '
'; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - var q = view.debugElement.children[0].references['q']; + var q = view.debugElement.children[0].references['q']; - view.debugElement.componentInstance.list = ['1d', '2d']; + view.debugElement.componentInstance.list = ['1d', '2d']; - view.detectChanges(); + view.detectChanges(); - expect(q.query.first.text).toEqual('1d'); - expect(q.query.last.text).toEqual('2d'); + expect(q.query.first.text).toEqual('1d'); + expect(q.query.last.text).toEqual('2d'); + }); - async.done(); - }); - })); + it('should support querying by multiple var bindings', () => { + const template = '' + + '
' + + '
' + + '
'; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - it('should support querying by multiple var bindings', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = '' + - '
' + - '
' + - '
'; + var q = view.debugElement.children[0].references['q']; + view.detectChanges(); - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - var q = view.debugElement.children[0].references['q']; - view.detectChanges(); + expect(q.query.first.text).toEqual('one'); + expect(q.query.last.text).toEqual('two'); + }); - expect(q.query.first.text).toEqual('one'); - expect(q.query.last.text).toEqual('two'); + it('should support dynamically inserted directives', () => { + const template = '' + + '
' + + '
'; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - async.done(); - }); - })); + var q = view.debugElement.children[0].references['q']; - it('should support dynamically inserted directives', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = '' + - '
' + - '
'; + view.debugElement.componentInstance.list = ['1d', '2d']; - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - var q = view.debugElement.children[0].references['q']; + view.detectChanges(); - view.debugElement.componentInstance.list = ['1d', '2d']; + view.debugElement.componentInstance.list = ['2d', '1d']; - view.detectChanges(); + view.detectChanges(); - view.debugElement.componentInstance.list = ['2d', '1d']; + expect(q.query.last.text).toEqual('1d'); + }); - view.detectChanges(); + it('should contain all the elements in the light dom with the given var binding', () => { + const template = '' + + '
' + + '
{{item}}
' + + '
' + + '
'; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - expect(q.query.last.text).toEqual('1d'); + var q = view.debugElement.children[0].references['q']; - async.done(); - }); - })); + view.debugElement.componentInstance.list = ['1d', '2d']; - it('should contain all the elements in the light dom with the given var binding', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = '' + - '
' + - '
{{item}}
' + - '
' + - '
'; + view.detectChanges(); - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - var q = view.debugElement.children[0].references['q']; + expect(q.query.first.nativeElement).toHaveText('1d'); + expect(q.query.last.nativeElement).toHaveText('2d'); + }); - view.debugElement.componentInstance.list = ['1d', '2d']; + it('should contain all the elements in the light dom even if they get projected', () => { + const template = '' + + '
' + + '
'; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - view.detectChanges(); + view.detectChanges(); - expect(q.query.first.nativeElement).toHaveText('1d'); - expect(q.query.last.nativeElement).toHaveText('2d'); + expect(asNativeElements(view.debugElement.children)).toHaveText('hello|world|'); + }); - async.done(); - }); - })); + it('should support querying the view by using a view query', () => { + const template = ''; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - it('should contain all the elements in the light dom even if they get projected', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = '' + - '
' + - '
'; + var q: NeedsViewQueryByLabel = view.debugElement.children[0].references['q']; + view.detectChanges(); - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - view.detectChanges(); + expect(q.query.first.nativeElement).toHaveText('text'); + }); - expect(asNativeElements(view.debugElement.children)).toHaveText('hello|world|'); + it('should contain all child directives in the view dom', () => { + const template = ''; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - async.done(); - }); - })); + view.detectChanges(); - it('should support querying the view by using a view query', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = - ''; + var q = view.debugElement.children[0].references['q']; - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - var q: NeedsViewQueryByLabel = view.debugElement.children[0].references['q']; - view.detectChanges(); - - expect(q.query.first.nativeElement).toHaveText('text'); - - async.done(); - }); - })); - - it('should contain all child directives in the view dom', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = ''; - - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - view.detectChanges(); - - var q = view.debugElement.children[0].references['q']; - - view.detectChanges(); - - expect(q.textDirChildren.length).toEqual(1); - expect(q.numberOfChildrenAfterViewInit).toEqual(1); - - async.done(); - }); - })); + view.detectChanges(); + expect(q.textDirChildren.length).toEqual(1); + expect(q.numberOfChildrenAfterViewInit).toEqual(1); + }); }); describe('querying in the view', () => { - it('should contain all the elements in the view with that have the given directive', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = '
'; + it('should contain all the elements in the view with that have the given directive', () => { + const template = '
'; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - var q: NeedsViewQuery = view.debugElement.children[0].references['q']; + var q: NeedsViewQuery = view.debugElement.children[0].references['q']; - view.detectChanges(); + view.detectChanges(); - expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']); + expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']); + }); - async.done(); - }); - })); + it('should not include directive present on the host element', () => { + const template = ''; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - it('should not include directive present on the host element', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = ''; + var q: NeedsViewQuery = view.debugElement.children[0].references['q']; - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - var q: NeedsViewQuery = view.debugElement.children[0].references['q']; + view.detectChanges(); - view.detectChanges(); + expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']); + }); - expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']); + it('should reflect changes in the component', () => { + const template = ''; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - async.done(); - }); - })); + var q: NeedsViewQueryIf = view.debugElement.children[0].references['q']; - it('should reflect changes in the component', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = ''; + view.detectChanges(); - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - var q: NeedsViewQueryIf = view.debugElement.children[0].references['q']; + expect(q.query.length).toBe(0); - view.detectChanges(); + q.show = true; + view.detectChanges(); + expect(q.query.length).toBe(1); - expect(q.query.length).toBe(0); + expect(q.query.first.text).toEqual('1'); + }); - q.show = true; - view.detectChanges(); - expect(q.query.length).toBe(1); + it('should not be affected by other changes in the component', () => { + const template = ''; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - expect(q.query.first.text).toEqual('1'); + var q: NeedsViewQueryNestedIf = view.debugElement.children[0].references['q']; - async.done(); - }); - })); + view.detectChanges(); - it('should not be affected by other changes in the component', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = ''; + expect(q.query.length).toEqual(1); + expect(q.query.first.text).toEqual('1'); - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - var q: NeedsViewQueryNestedIf = view.debugElement.children[0].references['q']; - - view.detectChanges(); - - expect(q.query.length).toEqual(1); - expect(q.query.first.text).toEqual('1'); - - q.show = false; - view.detectChanges(); - - expect(q.query.length).toEqual(1); - expect(q.query.first.text).toEqual('1'); - - async.done(); - }); - })); + q.show = false; + view.detectChanges(); + expect(q.query.length).toEqual(1); + expect(q.query.first.text).toEqual('1'); + }); it('should maintain directives in pre-order depth-first DOM order after dynamic insertion', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = ''; + () => { + const template = ''; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - var q: NeedsViewQueryOrder = view.debugElement.children[0].references['q']; + var q: NeedsViewQueryOrder = view.debugElement.children[0].references['q']; - view.detectChanges(); + view.detectChanges(); - expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']); + expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']); - q.list = ['-3', '2']; - view.detectChanges(); + q.list = ['-3', '2']; + view.detectChanges(); - expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '-3', '2', '4']); - - async.done(); - }); - })); + expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '-3', '2', '4']); + }); it('should maintain directives in pre-order depth-first DOM order after dynamic insertion', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = ''; + () => { + const template = ''; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - var q: NeedsViewQueryOrderWithParent = - view.debugElement.children[0].references['q']; + var q: NeedsViewQueryOrderWithParent = view.debugElement.children[0].references['q']; - view.detectChanges(); + view.detectChanges(); - expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']); + expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']); - q.list = ['-3', '2']; - view.detectChanges(); + q.list = ['-3', '2']; + view.detectChanges(); - expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '-3', '2', '4']); + expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '-3', '2', '4']); + }); - async.done(); - }); - })); + it('should handle long ngFor cycles', () => { + const template = ''; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - it('should handle long ngFor cycles', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = ''; + var q: NeedsViewQueryOrder = view.debugElement.children[0].references['q']; - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - var q: NeedsViewQueryOrder = view.debugElement.children[0].references['q']; + // no significance to 50, just a reasonably large cycle. + for (var i = 0; i < 50; i++) { + var newString = i.toString(); + q.list = [newString]; + view.detectChanges(); - // no significance to 50, just a reasonably large cycle. - for (var i = 0; i < 50; i++) { - var newString = i.toString(); - q.list = [newString]; - view.detectChanges(); + expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', newString, '4']); + } + }); - expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', newString, '4']); - } + it('should support more than three queries', () => { + const template = '
'; + TestBed.overrideComponent(MyComp0, {set: {template}}); + const view = TestBed.createComponent(MyComp0); - async.done(); - }); - })); + view.detectChanges(); - it('should support more than three queries', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var template = '
'; - - tcb.overrideTemplate(MyComp0, template).createAsync(MyComp0).then((view) => { - view.detectChanges(); - - var q = view.debugElement.children[0].references['q']; - expect(q.query1).toBeDefined(); - expect(q.query2).toBeDefined(); - expect(q.query3).toBeDefined(); - expect(q.query4).toBeDefined(); - - async.done(); - }); - })); + var q = view.debugElement.children[0].references['q']; + expect(q.query1).toBeDefined(); + expect(q.query2).toBeDefined(); + expect(q.query3).toBeDefined(); + expect(q.query4).toBeDefined(); + }); }); }); } @@ -794,8 +649,7 @@ class NeedsContentChildren implements AfterContentInit { ngAfterContentInit() { this.numberOfChildrenAfterContentInit = this.textDirChildren.length; } } -@Component( - {selector: 'needs-view-children', template: '
', directives: [TextDirective]}) +@Component({selector: 'needs-view-children', template: '
'}) class NeedsViewChildren implements AfterViewInit { @ViewChildren(TextDirective) textDirChildren: QueryList; numberOfChildrenAfterViewInit: number; @@ -828,8 +682,7 @@ class NeedsContentChild implements AfterContentInit, AfterContentChecked { selector: 'needs-view-child', template: `
- `, - directives: [NgIf, TextDirective] + ` }) class NeedsViewChild implements AfterViewInit, AfterViewChecked { @@ -858,8 +711,7 @@ class NeedsViewChild implements AfterViewInit, selector: 'needs-static-content-view-child', template: `
- `, - directives: [TextDirective] + ` }) class NeedsStaticContentAndViewChild { @ContentChild(TextDirective) contentChild: TextDirective; @@ -874,7 +726,6 @@ class InertDirective { @Component({ selector: 'needs-query', - directives: [NgFor, TextDirective], template: '
{{dir.text}}|' }) class NeedsQuery { @@ -891,7 +742,6 @@ class NeedsFourQueries { @Component({ selector: 'needs-query-desc', - directives: [NgFor], template: '
{{dir.text}}|
' }) class NeedsQueryDesc { @@ -903,11 +753,7 @@ class NeedsQueryByLabel { @ContentChildren('textLabel', {descendants: true}) query: QueryList; } -@Component({ - selector: 'needs-view-query-by-ref-binding', - directives: [], - template: '
text
' -}) +@Component({selector: 'needs-view-query-by-ref-binding', template: '
text
'}) class NeedsViewQueryByLabel { @ViewChildren('textLabel') query: QueryList; } @@ -919,7 +765,6 @@ class NeedsQueryByTwoLabels { @Component({ selector: 'needs-query-and-project', - directives: [NgFor], template: '
{{dir.text}}|
' }) class NeedsQueryAndProject { @@ -928,7 +773,6 @@ class NeedsQueryAndProject { @Component({ selector: 'needs-view-query', - directives: [TextDirective], template: '
' + '
' }) @@ -936,21 +780,15 @@ class NeedsViewQuery { @ViewChildren(TextDirective) query: QueryList; } -@Component({ - selector: 'needs-view-query-if', - directives: [NgIf, TextDirective], - template: '
' -}) +@Component({selector: 'needs-view-query-if', template: '
'}) class NeedsViewQueryIf { show: boolean; @ViewChildren(TextDirective) query: QueryList; constructor() { this.show = false; } } - @Component({ selector: 'needs-view-query-nested-if', - directives: [NgIf, InertDirective, TextDirective], template: '
' }) class NeedsViewQueryNestedIf { @@ -961,7 +799,6 @@ class NeedsViewQueryNestedIf { @Component({ selector: 'needs-view-query-order', - directives: [NgFor, TextDirective, InertDirective], template: '
' + '
' + '
' @@ -974,7 +811,6 @@ class NeedsViewQueryOrder { @Component({ selector: 'needs-view-query-order-with-p', - directives: [NgFor, TextDirective, InertDirective], template: '
' + '
' + '
' @@ -1014,7 +850,6 @@ class NeedsContentChildWithRead { @Component({ selector: 'needs-view-children-read', template: '
', - directives: [TextDirective] }) class NeedsViewChildrenWithRead { @ViewChildren('q,w', {read: TextDirective}) textDirChildren: QueryList; @@ -1024,7 +859,6 @@ class NeedsViewChildrenWithRead { @Component({ selector: 'needs-view-child-read', template: '
', - directives: [TextDirective] }) class NeedsViewChildWithRead { @ViewChild('q', {read: TextDirective}) textDirChild: TextDirective; @@ -1042,43 +876,10 @@ class NeedsViewContainerWithRead { @Component({selector: 'has-null-query-condition', template: '
'}) class HasNullQueryCondition { - @ContentChildren(null) errorTrigger: any /** TODO #9100 */; + @ContentChildren(null) errorTrigger: any; } -@Component({ - selector: 'my-comp', - directives: [ - NeedsQuery, - NeedsQueryDesc, - NeedsQueryByLabel, - NeedsQueryByTwoLabels, - NeedsQueryAndProject, - NeedsViewQuery, - NeedsViewQueryIf, - NeedsViewQueryNestedIf, - NeedsViewQueryOrder, - NeedsViewQueryByLabel, - NeedsViewQueryOrderWithParent, - NeedsContentChildren, - NeedsViewChildren, - NeedsViewChild, - NeedsStaticContentAndViewChild, - NeedsContentChild, - NeedsTpl, - NeedsNamedTpl, - TextDirective, - InertDirective, - NgIf, - NgFor, - NeedsFourQueries, - NeedsContentChildrenWithRead, - NeedsContentChildWithRead, - NeedsViewChildrenWithRead, - NeedsViewChildWithRead, - NeedsViewContainerWithRead - ], - template: '' -}) +@Component({selector: 'my-comp', template: ''}) class MyComp0 { shouldShow: boolean; list: any /** TODO #9100 */; @@ -1088,6 +889,6 @@ class MyComp0 { } } -@Component({selector: 'my-comp', directives: [HasNullQueryCondition], template: ''}) +@Component({selector: 'my-comp', template: ''}) class MyCompBroken0 { } diff --git a/modules/@angular/core/test/linker/regression_integration_spec.ts b/modules/@angular/core/test/linker/regression_integration_spec.ts index 9749707a77..93dc0f9a6f 100644 --- a/modules/@angular/core/test/linker/regression_integration_spec.ts +++ b/modules/@angular/core/test/linker/regression_integration_spec.ts @@ -10,7 +10,7 @@ import {NgClass, NgIf} from '@angular/common'; import {Component, Injector, OpaqueToken, Pipe, PipeTransform, forwardRef} from '@angular/core'; import {ViewMetadata} from '@angular/core/src/metadata/view'; import {TestBed} from '@angular/core/testing'; -import {AsyncTestCompleter, TestComponentBuilder, beforeEach, beforeEachProviders, ddescribe, describe, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal'; +import {beforeEach, beforeEachProviders, ddescribe, describe, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal'; import {expect} from '@angular/platform-browser/testing/matchers'; export function main() { @@ -23,199 +23,134 @@ function declareTests({useJit}: {useJit: boolean}) { // Place to put reproductions for regressions describe('regressions', () => { - describe('platform pipes', () => { - beforeEach(() => { - TestBed.configureCompiler({useJit: useJit}); - TestBed.configureTestingModule({declarations: [PlatformPipe]}); - }); + beforeEach(() => { TestBed.configureTestingModule({declarations: [MyComp1, PlatformPipe]}); }); - it('should overwrite them by custom pipes', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - tcb.overrideView( - MyComp1, - new ViewMetadata({template: '{{true | somePipe}}', pipes: [CustomPipe]})) - .createAsync(MyComp1) - .then((fixture) => { - fixture.detectChanges(); - expect(fixture.nativeElement).toHaveText('someCustomPipe'); - async.done(); - }); - })); + describe('platform pipes', () => { + beforeEach(() => { TestBed.configureCompiler({useJit: useJit}); }); + + it('should overwrite them by custom pipes', () => { + TestBed.configureTestingModule({declarations: [CustomPipe]}); + const template = '{{true | somePipe}}'; + TestBed.overrideComponent(MyComp1, {set: {template}}); + const fixture = TestBed.createComponent(MyComp1); + + fixture.detectChanges(); + expect(fixture.nativeElement).toHaveText('someCustomPipe'); + }); }); describe('expressions', () => { + it('should evaluate conditional and boolean operators with right precedence - #8244', () => { + const template = `{{'red' + (true ? ' border' : '')}}`; + TestBed.overrideComponent(MyComp1, {set: {template}}); + const fixture = TestBed.createComponent(MyComp1); - it('should evaluate conditional and boolean operators with right precedence - #8244', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - tcb.overrideView( - MyComp1, new ViewMetadata({template: `{{'red' + (true ? ' border' : '')}}`})) - .createAsync(MyComp1) - .then((fixture) => { - fixture.detectChanges(); - expect(fixture.nativeElement).toHaveText('red border'); - async.done(); - }); - })); + fixture.detectChanges(); + expect(fixture.nativeElement).toHaveText('red border'); + }); - it('should evaluate conditional and unary operators with right precedence - #8235', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - tcb.overrideView(MyComp1, new ViewMetadata({template: `{{!null?.length}}`})) - .createAsync(MyComp1) - .then((fixture) => { - fixture.detectChanges(); - expect(fixture.nativeElement).toHaveText('true'); - async.done(); - }); - })); + it('should evaluate conditional and unary operators with right precedence - #8235', () => { + const template = `{{!null?.length}}`; + TestBed.overrideComponent(MyComp1, {set: {template}}); + const fixture = TestBed.createComponent(MyComp1); - it('should only evaluate stateful pipes once - #10639', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - tcb.overrideView( - MyComp1, - new ViewMetadata( - {template: '{{(null|countingPipe)?.value}}', pipes: [CountingPipe]})) - .createAsync(MyComp1) - .then(fixture => { - CountingPipe.reset(); - fixture.detectChanges(/* checkNoChanges */ false); - expect(fixture.nativeElement).toHaveText('counting pipe value'); - expect(CountingPipe.calls).toBe(1); - async.done(); - }); - })); + fixture.detectChanges(); + expect(fixture.nativeElement).toHaveText('true'); + }); - it('should only evaluate methods once - #10639', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - tcb.overrideView(MyCountingComp, new ViewMetadata({template: '{{method()?.value}}'})) - .createAsync(MyCountingComp) - .then(fixture => { - MyCountingComp.reset(); - fixture.detectChanges(/* checkNoChanges */ false); - expect(fixture.nativeElement).toHaveText('counting method value'); - expect(MyCountingComp.calls).toBe(1); - async.done(); - }); - })); + it('should only evaluate stateful pipes once - #10639', () => { + TestBed.configureTestingModule({declarations: [CountingPipe]}); + const template = '{{(null|countingPipe)?.value}}'; + TestBed.overrideComponent(MyComp1, {set: {template}}); + const fixture = TestBed.createComponent(MyComp1); + + CountingPipe.reset(); + fixture.detectChanges(/* checkNoChanges */ false); + expect(fixture.nativeElement).toHaveText('counting pipe value'); + expect(CountingPipe.calls).toBe(1); + }); + + it('should only evaluate methods once - #10639', () => { + TestBed.configureTestingModule({declarations: [MyCountingComp]}); + const template = '{{method()?.value}}'; + TestBed.overrideComponent(MyCountingComp, {set: {template}}); + const fixture = TestBed.createComponent(MyCountingComp); + + MyCountingComp.reset(); + fixture.detectChanges(/* checkNoChanges */ false); + expect(fixture.nativeElement).toHaveText('counting method value'); + expect(MyCountingComp.calls).toBe(1); + }); }); describe('providers', () => { - function createInjector(tcb: TestComponentBuilder, proviers: any[]): Promise { - return tcb.overrideProviders(MyComp1, [proviers]) - .createAsync(MyComp1) - .then((fixture) => fixture.componentInstance.injector); + function createInjector(providers: any[]): Injector { + TestBed.overrideComponent(MyComp1, {add: {providers}}); + return TestBed.createComponent(MyComp1).componentInstance.injector; } - it('should support providers with an OpaqueToken that contains a `.` in the name', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var token = new OpaqueToken('a.b'); - var tokenValue = 1; - createInjector(tcb, [ - {provide: token, useValue: tokenValue} - ]).then((injector: Injector) => { - expect(injector.get(token)).toEqual(tokenValue); - async.done(); - }); - })); + it('should support providers with an OpaqueToken that contains a `.` in the name', () => { + var token = new OpaqueToken('a.b'); + var tokenValue = 1; + const injector = createInjector([{provide: token, useValue: tokenValue}]); + expect(injector.get(token)).toEqual(tokenValue); + }); - it('should support providers with string token with a `.` in it', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var token = 'a.b'; - var tokenValue = 1; - createInjector(tcb, [ - {provide: token, useValue: tokenValue} - ]).then((injector: Injector) => { - expect(injector.get(token)).toEqual(tokenValue); - async.done(); - }); - })); + it('should support providers with string token with a `.` in it', () => { + var token = 'a.b'; + var tokenValue = 1; + const injector = createInjector([{provide: token, useValue: tokenValue}]); - it('should support providers with an anonymous function', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var token = () => true; - var tokenValue = 1; - createInjector(tcb, [ - {provide: token, useValue: tokenValue} - ]).then((injector: Injector) => { - expect(injector.get(token)).toEqual(tokenValue); - async.done(); - }); - })); + expect(injector.get(token)).toEqual(tokenValue); + }); - it('should support providers with an OpaqueToken that has a StringMap as value', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - var token1 = new OpaqueToken('someToken'); - var token2 = new OpaqueToken('someToken'); - var tokenValue1 = {'a': 1}; - var tokenValue2 = {'a': 1}; - createInjector(tcb, [ - {provide: token1, useValue: tokenValue1}, - {provide: token2, useValue: tokenValue2} - ]).then((injector: Injector) => { - expect(injector.get(token1)).toEqual(tokenValue1); - expect(injector.get(token2)).toEqual(tokenValue2); - async.done(); - }); - })); + it('should support providers with an anonymous function', () => { + var token = () => true; + var tokenValue = 1; + const injector = createInjector([{provide: token, useValue: tokenValue}]); + + expect(injector.get(token)).toEqual(tokenValue); + }); + + it('should support providers with an OpaqueToken that has a StringMap as value', () => { + var token1 = new OpaqueToken('someToken'); + var token2 = new OpaqueToken('someToken'); + var tokenValue1 = {'a': 1}; + var tokenValue2 = {'a': 1}; + const injector = createInjector( + [{provide: token1, useValue: tokenValue1}, {provide: token2, useValue: tokenValue2}]); + + expect(injector.get(token1)).toEqual(tokenValue1); + expect(injector.get(token2)).toEqual(tokenValue2); + }); }); - it('should allow logging a previous elements class binding via interpolation', - inject( - [TestComponentBuilder, AsyncTestCompleter], - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - tcb.overrideTemplate( - MyComp1, `
Class: {{el.className}}
`) - .createAsync(MyComp1) - .then((fixture) => { - fixture.detectChanges(); - expect(fixture.nativeElement).toHaveText('Class: a'); - async.done(); - }); - })); + it('should allow logging a previous elements class binding via interpolation', () => { + const template = `
Class: {{el.className}}
`; + TestBed.overrideComponent(MyComp1, {set: {template}}); + const fixture = TestBed.createComponent(MyComp1); - it('should support ngClass before a component and content projection inside of an ngIf', - inject( - [TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async: any) => { - tcb.overrideView( - MyComp1, new ViewMetadata({ - template: `ABC`, - directives: [NgClass, NgIf, CmpWithNgContent] - })) - .createAsync(MyComp1) - .then((fixture) => { - fixture.detectChanges(); - expect(fixture.nativeElement).toHaveText('ABC'); - async.done(); - }); - })); + fixture.detectChanges(); + expect(fixture.nativeElement).toHaveText('Class: a'); + }); - it('should handle mutual recursion entered from multiple sides - #7084', - inject( - [TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async: any) => { - tcb.createAsync(FakeRecursiveComp).then((fixture) => { - fixture.detectChanges(); - expect(fixture.nativeElement).toHaveText('[]'); - async.done(); - }); - })); + it('should support ngClass before a component and content projection inside of an ngIf', () => { + TestBed.configureTestingModule({declarations: [CmpWithNgContent]}); + const template = `ABC`; + TestBed.overrideComponent(MyComp1, {set: {template}}); + const fixture = TestBed.createComponent(MyComp1); + fixture.detectChanges(); + expect(fixture.nativeElement).toHaveText('ABC'); + }); + + it('should handle mutual recursion entered from multiple sides - #7084', () => { + TestBed.configureTestingModule({declarations: [FakeRecursiveComp, LeftComp, RightComp]}); + const fixture = TestBed.createComponent(FakeRecursiveComp); + + fixture.detectChanges(); + expect(fixture.nativeElement).toHaveText('[]'); + }); }); } @@ -262,10 +197,6 @@ class CountingPipe implements PipeTransform { @Component({ selector: 'left', template: `L`, - directives: [ - NgIf, - forwardRef(() => RightComp), - ] }) class LeftComp { } @@ -273,10 +204,6 @@ class LeftComp { @Component({ selector: 'right', template: `R`, - directives: [ - NgIf, - forwardRef(() => LeftComp), - ] }) class RightComp { } @@ -284,11 +211,6 @@ class RightComp { @Component({ selector: 'fakeRecursiveComp', template: `[]`, - directives: [ - NgIf, - forwardRef(() => LeftComp), - forwardRef(() => RightComp), - ] }) export class FakeRecursiveComp { } diff --git a/modules/@angular/core/test/linker/security_integration_spec.ts b/modules/@angular/core/test/linker/security_integration_spec.ts index 7244aaae6d..7d695f183a 100644 --- a/modules/@angular/core/test/linker/security_integration_spec.ts +++ b/modules/@angular/core/test/linker/security_integration_spec.ts @@ -7,42 +7,30 @@ */ import {Component} from '@angular/core/src/metadata'; -import {TestBed} from '@angular/core/testing'; -import {AsyncTestCompleter, TestComponentBuilder, afterEach, beforeEach, beforeEachProviders, ddescribe, describe, expect, inject, it} from '@angular/core/testing/testing_internal'; +import {TestBed, getTestBed} from '@angular/core/testing'; +import {afterEach, beforeEach, beforeEachProviders, ddescribe, describe, expect, inject, it} from '@angular/core/testing/testing_internal'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {DomSanitizationService} from '@angular/platform-browser/src/security/dom_sanitization_service'; - export function main() { describe('jit', () => { declareTests({useJit: true}); }); describe('no jit', () => { declareTests({useJit: false}); }); } -@Component({selector: 'my-comp', template: '', directives: []}) +@Component({selector: 'my-comp', template: ''}) class SecuredComponent { ctxProp: string; constructor() { this.ctxProp = 'some value'; } } -function itAsync(msg: string, injections: Function[], f: Function): void; -function itAsync( - msg: string, f: (tcb: TestComponentBuilder, atc: AsyncTestCompleter) => void): void; -function itAsync( - msg: string, f: Function[] | ((tcb: TestComponentBuilder, atc: AsyncTestCompleter) => void), - fn?: Function): void { - if (f instanceof Function) { - it(msg, inject([TestComponentBuilder, AsyncTestCompleter], f)); - } else { - let injections = f; - it(msg, inject(injections, fn)); - } -} - function declareTests({useJit}: {useJit: boolean}) { describe('security integration tests', function() { - beforeEach(() => { TestBed.configureCompiler({useJit: useJit}); }); + beforeEach(() => { + TestBed.configureCompiler({useJit: useJit}); + TestBed.configureTestingModule({declarations: [SecuredComponent]}); + }); let originalLog: (msg: any) => any; beforeEach(() => { @@ -52,175 +40,137 @@ function declareTests({useJit}: {useJit: boolean}) { afterEach(() => { getDOM().log = originalLog; }); - itAsync( - 'should disallow binding on*', (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - let tpl = `
`; - tcb.overrideTemplate(SecuredComponent, tpl) - .createAsync(SecuredComponent) - .then(v => async.done(new Error('unexpected success'))) - .catch((e) => { - expect(e.message).toContain( - `Template parse errors:\n` + - `Binding to event attribute 'onclick' is disallowed ` + - `for security reasons, please use (click)=... `); - async.done(); - return null; - }); - }); + it('should disallow binding on*', () => { + const template = `
`; + TestBed.overrideComponent(SecuredComponent, {set: {template}}); + try { + TestBed.createComponent(SecuredComponent); + throw 'Should throw'; + } catch (e) { + expect(e.message).toContain( + `Template parse errors:\n` + + `Binding to event attribute 'onclick' is disallowed ` + + `for security reasons, please use (click)=... `); + } + }); describe('safe HTML values', function() { - itAsync( - 'should not escape values marked as trusted', - [TestComponentBuilder, AsyncTestCompleter, DomSanitizationService], - (tcb: TestComponentBuilder, async: AsyncTestCompleter, - sanitizer: DomSanitizationService) => { - let tpl = `Link Title`; - tcb.overrideTemplate(SecuredComponent, tpl) - .createAsync(SecuredComponent) - .then((fixture) => { - let e = fixture.debugElement.children[0].nativeElement; - let ci = fixture.debugElement.componentInstance; - let trusted = sanitizer.bypassSecurityTrustUrl('javascript:alert(1)'); - ci.ctxProp = trusted; - fixture.detectChanges(); - expect(getDOM().getProperty(e, 'href')).toEqual('javascript:alert(1)'); + it('should not escape values marked as trusted', () => { + const template = `Link Title`; + TestBed.overrideComponent(SecuredComponent, {set: {template}}); + const fixture = TestBed.createComponent(SecuredComponent); + const sanitizer: DomSanitizationService = getTestBed().get(DomSanitizationService); - async.done(); - }); - }); + let e = fixture.debugElement.children[0].nativeElement; + let ci = fixture.debugElement.componentInstance; + let trusted = sanitizer.bypassSecurityTrustUrl('javascript:alert(1)'); + ci.ctxProp = trusted; + fixture.detectChanges(); + expect(getDOM().getProperty(e, 'href')).toEqual('javascript:alert(1)'); + }); - itAsync( - 'should error when using the wrong trusted value', - [TestComponentBuilder, AsyncTestCompleter, DomSanitizationService], - (tcb: TestComponentBuilder, async: AsyncTestCompleter, - sanitizer: DomSanitizationService) => { - let tpl = `Link Title`; - tcb.overrideTemplate(SecuredComponent, tpl) - .createAsync(SecuredComponent) - .then((fixture) => { - let trusted = sanitizer.bypassSecurityTrustScript('javascript:alert(1)'); - let ci = fixture.debugElement.componentInstance; - ci.ctxProp = trusted; - expect(() => fixture.detectChanges()) - .toThrowError(/Required a safe URL, got a Script/); + it('should error when using the wrong trusted value', () => { + const template = `Link Title`; + TestBed.overrideComponent(SecuredComponent, {set: {template}}); + const fixture = TestBed.createComponent(SecuredComponent); + const sanitizer: DomSanitizationService = getTestBed().get(DomSanitizationService); - async.done(); - }); - }); + let trusted = sanitizer.bypassSecurityTrustScript('javascript:alert(1)'); + let ci = fixture.debugElement.componentInstance; + ci.ctxProp = trusted; + expect(() => fixture.detectChanges()).toThrowError(/Required a safe URL, got a Script/); + }); - itAsync( - 'should warn when using in string interpolation', - [TestComponentBuilder, AsyncTestCompleter, DomSanitizationService], - (tcb: TestComponentBuilder, async: AsyncTestCompleter, - sanitizer: DomSanitizationService) => { - let tpl = `Link Title`; - tcb.overrideTemplate(SecuredComponent, tpl) - .createAsync(SecuredComponent) - .then((fixture) => { - let e = fixture.debugElement.children[0].nativeElement; - let trusted = sanitizer.bypassSecurityTrustUrl('bar/baz'); - let ci = fixture.debugElement.componentInstance; - ci.ctxProp = trusted; - fixture.detectChanges(); - expect(getDOM().getProperty(e, 'href')).toMatch(/SafeValue(%20| )must(%20| )use/); + it('should warn when using in string interpolation', () => { + const template = `Link Title`; + TestBed.overrideComponent(SecuredComponent, {set: {template}}); + const fixture = TestBed.createComponent(SecuredComponent); + const sanitizer: DomSanitizationService = getTestBed().get(DomSanitizationService); - async.done(); - }); - }); + let e = fixture.debugElement.children[0].nativeElement; + let trusted = sanitizer.bypassSecurityTrustUrl('bar/baz'); + let ci = fixture.debugElement.componentInstance; + ci.ctxProp = trusted; + fixture.detectChanges(); + expect(getDOM().getProperty(e, 'href')).toMatch(/SafeValue(%20| )must(%20| )use/); + }); }); describe('sanitizing', () => { - itAsync( - 'should escape unsafe attributes', - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - let tpl = `Link Title`; - tcb.overrideTemplate(SecuredComponent, tpl) - .createAsync(SecuredComponent) - .then((fixture) => { - let e = fixture.debugElement.children[0].nativeElement; - let ci = fixture.debugElement.componentInstance; - ci.ctxProp = 'hello'; - fixture.detectChanges(); - // In the browser, reading href returns an absolute URL. On the server side, - // it just echoes back the property. - expect(getDOM().getProperty(e, 'href')).toMatch(/.*\/?hello$/); + it('should escape unsafe attributes', () => { + const template = `Link Title`; + TestBed.overrideComponent(SecuredComponent, {set: {template}}); + const fixture = TestBed.createComponent(SecuredComponent); - ci.ctxProp = 'javascript:alert(1)'; - fixture.detectChanges(); - expect(getDOM().getProperty(e, 'href')).toEqual('unsafe:javascript:alert(1)'); + let e = fixture.debugElement.children[0].nativeElement; + let ci = fixture.debugElement.componentInstance; + ci.ctxProp = 'hello'; + fixture.detectChanges(); + // In the browser, reading href returns an absolute URL. On the server side, + // it just echoes back the property. + expect(getDOM().getProperty(e, 'href')).toMatch(/.*\/?hello$/); - async.done(); - }); - }); + ci.ctxProp = 'javascript:alert(1)'; + fixture.detectChanges(); + expect(getDOM().getProperty(e, 'href')).toEqual('unsafe:javascript:alert(1)'); + }); - itAsync( - 'should escape unsafe style values', - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - let tpl = `
Text
`; - tcb.overrideTemplate(SecuredComponent, tpl) - .createAsync(SecuredComponent) - .then((fixture) => { - let e = fixture.debugElement.children[0].nativeElement; - let ci = fixture.debugElement.componentInstance; - // Make sure binding harmless values works. - ci.ctxProp = 'red'; - fixture.detectChanges(); - // In some browsers, this will contain the full background specification, not just - // the color. - expect(getDOM().getStyle(e, 'background')).toMatch(/red.*/); + it('should escape unsafe style values', () => { + const template = `
Text
`; + TestBed.overrideComponent(SecuredComponent, {set: {template}}); + const fixture = TestBed.createComponent(SecuredComponent); - ci.ctxProp = 'url(javascript:evil())'; - fixture.detectChanges(); - // Updated value gets rejected, no value change. - expect(getDOM().getStyle(e, 'background')).not.toContain('javascript'); + let e = fixture.debugElement.children[0].nativeElement; + let ci = fixture.debugElement.componentInstance; + // Make sure binding harmless values works. + ci.ctxProp = 'red'; + fixture.detectChanges(); + // In some browsers, this will contain the full background specification, not just + // the color. + expect(getDOM().getStyle(e, 'background')).toMatch(/red.*/); - async.done(); - }); - }); + ci.ctxProp = 'url(javascript:evil())'; + fixture.detectChanges(); + // Updated value gets rejected, no value change. + expect(getDOM().getStyle(e, 'background')).not.toContain('javascript'); + }); - itAsync( - 'should escape unsafe SVG attributes', - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - let tpl = `Text`; - tcb.overrideTemplate(SecuredComponent, tpl) - .createAsync(SecuredComponent) - .then(v => async.done(new Error('unexpected success'))) - .catch((e) => { - expect(e.message).toContain(`Can't bind to 'xlink:href'`); - async.done(); - return null; - }); - }); + it('should escape unsafe SVG attributes', () => { + const template = `Text`; + TestBed.overrideComponent(SecuredComponent, {set: {template}}); - itAsync( - 'should escape unsafe HTML values', - (tcb: TestComponentBuilder, async: AsyncTestCompleter) => { - let tpl = `
Text
`; - tcb.overrideTemplate(SecuredComponent, tpl) - .createAsync(SecuredComponent) - .then((fixture) => { - let e = fixture.debugElement.children[0].nativeElement; - let ci = fixture.debugElement.componentInstance; - // Make sure binding harmless values works. - ci.ctxProp = 'some

text

'; - fixture.detectChanges(); - expect(getDOM().getInnerHTML(e)).toEqual('some

text

'); + try { + TestBed.createComponent(SecuredComponent); + throw 'Should throw'; + } catch (e) { + expect(e.message).toContain(`Can't bind to 'xlink:href'`); + } + }); - ci.ctxProp = 'ha '; - fixture.detectChanges(); - expect(getDOM().getInnerHTML(e)).toEqual('ha evil()'); + it('should escape unsafe HTML values', () => { + const template = `
Text
`; + TestBed.overrideComponent(SecuredComponent, {set: {template}}); + const fixture = TestBed.createComponent(SecuredComponent); - ci.ctxProp = 'also evil'; - fixture.detectChanges(); - expect(getDOM().getInnerHTML(e)).toEqual('also evil'); + let e = fixture.debugElement.children[0].nativeElement; + let ci = fixture.debugElement.componentInstance; + // Make sure binding harmless values works. + ci.ctxProp = 'some

text

'; + fixture.detectChanges(); + expect(getDOM().getInnerHTML(e)).toEqual('some

text

'); - ci.ctxProp = 'also evil'; - fixture.detectChanges(); - expect(getDOM().getInnerHTML(e)).toEqual('also evil'); + ci.ctxProp = 'ha '; + fixture.detectChanges(); + expect(getDOM().getInnerHTML(e)).toEqual('ha evil()'); - async.done(); - }); - }); + ci.ctxProp = 'also evil'; + fixture.detectChanges(); + expect(getDOM().getInnerHTML(e)).toEqual('also evil'); + + ci.ctxProp = 'also evil'; + fixture.detectChanges(); + expect(getDOM().getInnerHTML(e)).toEqual('also evil'); + }); }); }); } diff --git a/modules/@angular/core/test/linker/view_injector_integration_spec.ts b/modules/@angular/core/test/linker/view_injector_integration_spec.ts index 0cb707b2bc..b75b6c0050 100644 --- a/modules/@angular/core/test/linker/view_injector_integration_spec.ts +++ b/modules/@angular/core/test/linker/view_injector_integration_spec.ts @@ -9,63 +9,19 @@ import {NgFor, NgIf} from '@angular/common'; import {Attribute, ChangeDetectionStrategy, ChangeDetectorRef, Component, DebugElement, Directive, ElementRef, Host, Inject, InjectMetadata, Input, Optional, Pipe, PipeTransform, Self, SkipSelfMetadata, TemplateRef, Type, ViewContainerRef, forwardRef} from '@angular/core'; import {ViewMetadata} from '@angular/core/src/metadata/view'; -import {ComponentFixture, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing'; -import {TestComponentBuilder, beforeEach, beforeEachProviders, ddescribe, describe, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal'; +import {ComponentFixture, TestBed, fakeAsync, flushMicrotasks, tick} from '@angular/core/testing'; +import {beforeEach, beforeEachProviders, ddescribe, describe, iit, inject, it, xdescribe, xit} from '@angular/core/testing/testing_internal'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {expect} from '@angular/platform-browser/testing/matchers'; import {isBlank} from '../../src/facade/lang'; -const ALL_DIRECTIVES = [ - forwardRef(() => SimpleDirective), - forwardRef(() => CycleDirective), - forwardRef(() => SimpleComponent), - forwardRef(() => SomeOtherDirective), - forwardRef(() => NeedsDirectiveFromSelf), - forwardRef(() => NeedsServiceComponent), - forwardRef(() => OptionallyNeedsDirective), - forwardRef(() => NeedsComponentFromHost), - forwardRef(() => NeedsDirectiveFromHost), - forwardRef(() => NeedsDirective), - forwardRef(() => NeedsService), - forwardRef(() => NeedsAppService), - forwardRef(() => NeedsAttribute), - forwardRef(() => NeedsAttributeNoType), - forwardRef(() => NeedsElementRef), - forwardRef(() => NeedsViewContainerRef), - forwardRef(() => NeedsTemplateRef), - forwardRef(() => OptionallyNeedsTemplateRef), - forwardRef(() => DirectiveNeedsChangeDetectorRef), - forwardRef(() => PushComponentNeedsChangeDetectorRef), - forwardRef(() => NeedsServiceFromHost), - forwardRef(() => NeedsAttribute), - forwardRef(() => NeedsAttributeNoType), - forwardRef(() => NeedsElementRef), - forwardRef(() => NeedsViewContainerRef), - forwardRef(() => NeedsTemplateRef), - forwardRef(() => OptionallyNeedsTemplateRef), - forwardRef(() => DirectiveNeedsChangeDetectorRef), - forwardRef(() => PushComponentNeedsChangeDetectorRef), - forwardRef(() => NeedsHostAppService), - NgIf, - NgFor -]; - -const ALL_PIPES = [ - forwardRef(() => PipeNeedsChangeDetectorRef), - forwardRef(() => PipeNeedsService), - forwardRef(() => PurePipe), - forwardRef(() => ImpurePipe), - forwardRef(() => DuplicatePipe1), - forwardRef(() => DuplicatePipe2), -]; - @Directive({selector: '[simpleDirective]'}) class SimpleDirective { @Input('simpleDirective') value: any = null; } -@Component({selector: '[simpleComponent]', template: '', directives: ALL_DIRECTIVES}) +@Component({selector: '[simpleComponent]', template: ''}) class SimpleComponent { } @@ -111,40 +67,38 @@ class NeedsDirective { @Directive({selector: '[needsService]'}) class NeedsService { service: any; - constructor(@Inject('service') service: any /** TODO #9100 */) { this.service = service; } + constructor(@Inject('service') service: any) { this.service = service; } } @Directive({selector: '[needsAppService]'}) class NeedsAppService { service: any; - constructor(@Inject('appService') service: any /** TODO #9100 */) { this.service = service; } + constructor(@Inject('appService') service: any) { this.service = service; } } -@Component({selector: '[needsHostAppService]', template: '', directives: ALL_DIRECTIVES}) +@Component({selector: '[needsHostAppService]', template: ''}) class NeedsHostAppService { service: any; - constructor(@Host() @Inject('appService') service: any /** TODO #9100 */) { - this.service = service; - } + constructor(@Host() @Inject('appService') service: any) { this.service = service; } } @Component({selector: '[needsServiceComponent]', template: ''}) class NeedsServiceComponent { service: any; - constructor(@Inject('service') service: any /** TODO #9100 */) { this.service = service; } + constructor(@Inject('service') service: any) { this.service = service; } } @Directive({selector: '[needsServiceFromHost]'}) class NeedsServiceFromHost { service: any; - constructor(@Host() @Inject('service') service: any /** TODO #9100 */) { this.service = service; } + constructor(@Host() @Inject('service') service: any) { this.service = service; } } @Directive({selector: '[needsAttribute]'}) class NeedsAttribute { - typeAttribute: any /** TODO #9100 */; - titleAttribute: any /** TODO #9100 */; - fooAttribute: any /** TODO #9100 */; + typeAttribute: any; + titleAttribute: any; + fooAttribute: any; constructor( @Attribute('type') typeAttribute: String, @Attribute('title') titleAttribute: String, @Attribute('foo') fooAttribute: String) { @@ -156,33 +110,31 @@ class NeedsAttribute { @Directive({selector: '[needsAttributeNoType]'}) class NeedsAttributeNoType { - fooAttribute: any /** TODO #9100 */; - constructor(@Attribute('foo') fooAttribute: any /** TODO #9100 */) { - this.fooAttribute = fooAttribute; - } + fooAttribute: any; + constructor(@Attribute('foo') fooAttribute: any) { this.fooAttribute = fooAttribute; } } @Directive({selector: '[needsElementRef]'}) class NeedsElementRef { - elementRef: any /** TODO #9100 */; + elementRef: any; constructor(ref: ElementRef) { this.elementRef = ref; } } @Directive({selector: '[needsViewContainerRef]'}) class NeedsViewContainerRef { - viewContainer: any /** TODO #9100 */; + viewContainer: any; constructor(vc: ViewContainerRef) { this.viewContainer = vc; } } @Directive({selector: '[needsTemplateRef]'}) class NeedsTemplateRef { - templateRef: any /** TODO #9100 */; + templateRef: any; constructor(ref: TemplateRef) { this.templateRef = ref; } } @Directive({selector: '[optionallyNeedsTemplateRef]'}) class OptionallyNeedsTemplateRef { - templateRef: any /** TODO #9100 */; + templateRef: any; constructor(@Optional() ref: TemplateRef) { this.templateRef = ref; } } @@ -194,7 +146,6 @@ class DirectiveNeedsChangeDetectorRef { @Component({ selector: '[componentNeedsChangeDetectorRef]', template: '{{counter}}', - directives: ALL_DIRECTIVES, changeDetection: ChangeDetectionStrategy.OnPush }) class PushComponentNeedsChangeDetectorRef { @@ -223,7 +174,7 @@ class PipeNeedsChangeDetectorRef { @Pipe({name: 'pipeNeedsService'}) export class PipeNeedsService implements PipeTransform { service: any; - constructor(@Inject('service') service: any /** TODO #9100 */) { this.service = service; } + constructor(@Inject('service') service: any) { this.service = service; } transform(value: any): any { return this; } } @@ -237,493 +188,543 @@ export class DuplicatePipe2 implements PipeTransform { transform(value: any): any { return this; } } -@Component({selector: 'root'}) +@Component({selector: 'root', template: ''}) class TestComp { } export function main() { - var tcb: TestComponentBuilder; - - function createCompFixture( - template: string, tcb: TestComponentBuilder, comp: Type = null): ComponentFixture { - if (isBlank(comp)) { + function createComponentFixture( + template: string, providers: any[] = null, comp: Type = null): ComponentFixture { + if (!comp) { comp = TestComp; } - return tcb - .overrideView( - comp, - new ViewMetadata({template: template, directives: ALL_DIRECTIVES, pipes: ALL_PIPES})) - .createFakeAsync(comp); + TestBed.overrideComponent(comp, {set: {template}}); + if (providers && providers.length) { + TestBed.overrideComponent(comp, {add: {providers: providers}}); + } + return TestBed.createComponent(comp); } - function createComp( - template: string, tcb: TestComponentBuilder, comp: Type = null): DebugElement { - var fixture = createCompFixture(template, tcb, comp); + function createComponent( + template: string, providers: any[] = null, comp: Type = null): DebugElement { + const fixture = createComponentFixture(template, providers, comp); fixture.detectChanges(); return fixture.debugElement; } - describe('View Injector', () => { + describe('View injector', () => { // On CJS fakeAsync is not supported... if (!getDOM().supportsDOMEvents()) return; + beforeEach(() => TestBed.configureTestingModule({declarations: [TestComp]})); + beforeEachProviders(() => [{provide: 'appService', useValue: 'appService'}]); - beforeEach( - fakeAsync(inject([TestComponentBuilder], (_tcb: TestComponentBuilder) => { tcb = _tcb; }))); - describe('injection', () => { - it('should instantiate directives that have no dependencies', fakeAsync(() => { - var el = createComp('
', tcb); - expect(el.children[0].injector.get(SimpleDirective)).toBeAnInstanceOf(SimpleDirective); - })); + it('should instantiate directives that have no dependencies', () => { + TestBed.configureTestingModule({declarations: [SimpleDirective]}); + const el = createComponent('
'); + expect(el.children[0].injector.get(SimpleDirective)).toBeAnInstanceOf(SimpleDirective); + }); - it('should instantiate directives that depend on another directive', fakeAsync(() => { - var el = createComp('
', tcb); + it('should instantiate directives that depend on another directive', () => { + TestBed.configureTestingModule({declarations: [SimpleDirective, NeedsDirective]}); + const el = createComponent('
'); - var d = el.children[0].injector.get(NeedsDirective); + var d = el.children[0].injector.get(NeedsDirective); - expect(d).toBeAnInstanceOf(NeedsDirective); - expect(d.dependency).toBeAnInstanceOf(SimpleDirective); - })); + expect(d).toBeAnInstanceOf(NeedsDirective); + expect(d.dependency).toBeAnInstanceOf(SimpleDirective); + }); - it('should support useValue with different values', fakeAsync(() => { - var el = createComp('', tcb.overrideProviders(TestComp, [ - {provide: 'numLiteral', useValue: 0}, - {provide: 'boolLiteral', useValue: true}, - {provide: 'strLiteral', useValue: 'a'}, - {provide: 'null', useValue: null}, - {provide: 'array', useValue: [1]}, - {provide: 'map', useValue: {'a': 1}}, - {provide: 'instance', useValue: new TestValue('a')}, - {provide: 'nested', useValue: [{'a': [1]}, new TestValue('b')]}, - ])); - expect(el.injector.get('numLiteral')).toBe(0); - expect(el.injector.get('boolLiteral')).toBe(true); - expect(el.injector.get('strLiteral')).toBe('a'); - expect(el.injector.get('null')).toBe(null); - expect(el.injector.get('array')).toEqual([1]); - expect(el.injector.get('map')).toEqual({'a': 1}); - expect(el.injector.get('instance')).toEqual(new TestValue('a')); - expect(el.injector.get('nested')).toEqual([{'a': [1]}, new TestValue('b')]); - })); + it('should support useValue with different values', () => { + const el = createComponent('', [ + {provide: 'numLiteral', useValue: 0}, + {provide: 'boolLiteral', useValue: true}, + {provide: 'strLiteral', useValue: 'a'}, + {provide: 'null', useValue: null}, + {provide: 'array', useValue: [1]}, + {provide: 'map', useValue: {'a': 1}}, + {provide: 'instance', useValue: new TestValue('a')}, + {provide: 'nested', useValue: [{'a': [1]}, new TestValue('b')]}, + ]); + expect(el.injector.get('numLiteral')).toBe(0); + expect(el.injector.get('boolLiteral')).toBe(true); + expect(el.injector.get('strLiteral')).toBe('a'); + expect(el.injector.get('null')).toBe(null); + expect(el.injector.get('array')).toEqual([1]); + expect(el.injector.get('map')).toEqual({'a': 1}); + expect(el.injector.get('instance')).toEqual(new TestValue('a')); + expect(el.injector.get('nested')).toEqual([{'a': [1]}, new TestValue('b')]); + }); - it('should instantiate providers that have dependencies with SkipSelf', fakeAsync(() => { - var el = createComp( - '
', - tcb.overrideProviders( - SimpleDirective, [{provide: 'injectable1', useValue: 'injectable1'}]) - .overrideProviders(SomeOtherDirective, [ - {provide: 'injectable1', useValue: 'new-injectable1'}, { - provide: 'injectable2', - useFactory: (val: any /** TODO #9100 */) => `${val}-injectable2`, - deps: [[new InjectMetadata('injectable1'), new SkipSelfMetadata()]] - } - ])); - expect(el.children[0].children[0].injector.get('injectable2')) - .toEqual('injectable1-injectable2'); - })); + it('should instantiate providers that have dependencies with SkipSelf', () => { + TestBed.configureTestingModule({declarations: [SimpleDirective, SomeOtherDirective]}); + TestBed.overrideDirective( + SimpleDirective, + {add: {providers: [{provide: 'injectable1', useValue: 'injectable1'}]}}); + TestBed.overrideDirective(SomeOtherDirective, { + add: { + providers: [ + {provide: 'injectable1', useValue: 'new-injectable1'}, { + provide: 'injectable2', + useFactory: (val: any) => `${val}-injectable2`, + deps: [[new InjectMetadata('injectable1'), new SkipSelfMetadata()]] + } + ] + } + }); + const el = createComponent('
'); + expect(el.children[0].children[0].injector.get('injectable2')) + .toEqual('injectable1-injectable2'); + }); - it('should instantiate providers that have dependencies', fakeAsync(() => { - var providers = [ - {provide: 'injectable1', useValue: 'injectable1'}, { - provide: 'injectable2', - useFactory: (val: any /** TODO #9100 */) => `${val}-injectable2`, - deps: ['injectable1'] - } - ]; - var el = createComp( - '
', tcb.overrideProviders(SimpleDirective, providers)); - expect(el.children[0].injector.get('injectable2')).toEqual('injectable1-injectable2'); - })); + it('should instantiate providers that have dependencies', () => { + TestBed.configureTestingModule({declarations: [SimpleDirective]}); + const providers = [ + {provide: 'injectable1', useValue: 'injectable1'}, { + provide: 'injectable2', + useFactory: (val: any) => `${val}-injectable2`, + deps: ['injectable1'] + } + ]; + TestBed.overrideDirective(SimpleDirective, {add: {providers}}); + var el = createComponent('
'); + expect(el.children[0].injector.get('injectable2')).toEqual('injectable1-injectable2'); + }); - it('should instantiate viewProviders that have dependencies', fakeAsync(() => { - var viewProviders = [ - {provide: 'injectable1', useValue: 'injectable1'}, { - provide: 'injectable2', - useFactory: (val: any /** TODO #9100 */) => `${val}-injectable2`, - deps: ['injectable1'] - } - ]; + it('should instantiate viewProviders that have dependencies', () => { + TestBed.configureTestingModule({declarations: [SimpleComponent]}); + const viewProviders = [ + {provide: 'injectable1', useValue: 'injectable1'}, { + provide: 'injectable2', + useFactory: (val: any) => `${val}-injectable2`, + deps: ['injectable1'] + } + ]; + TestBed.overrideComponent(SimpleComponent, {set: {viewProviders}}); + const el = createComponent('
'); + expect(el.children[0].injector.get('injectable2')).toEqual('injectable1-injectable2'); + }); - var el = createComp( - '
', - tcb.overrideViewProviders(SimpleComponent, viewProviders)); - expect(el.children[0].injector.get('injectable2')).toEqual('injectable1-injectable2'); - })); + it('should instantiate components that depend on viewProviders providers', () => { + TestBed.configureTestingModule({declarations: [NeedsServiceComponent]}); + TestBed.overrideComponent( + NeedsServiceComponent, {set: {providers: [{provide: 'service', useValue: 'service'}]}}); + const el = createComponent('
'); + expect(el.children[0].injector.get(NeedsServiceComponent).service).toEqual('service'); + }); - it('should instantiate components that depend on viewProviders providers', fakeAsync(() => { - var el = createComp( - '
', - tcb.overrideViewProviders( - NeedsServiceComponent, [{provide: 'service', useValue: 'service'}])); - expect(el.children[0].injector.get(NeedsServiceComponent).service).toEqual('service'); - })); + it('should instantiate multi providers', () => { + TestBed.configureTestingModule({declarations: [SimpleDirective]}); + const providers = [ + {provide: 'injectable1', useValue: 'injectable11', multi: true}, + {provide: 'injectable1', useValue: 'injectable12', multi: true} + ]; + TestBed.overrideDirective(SimpleDirective, {set: {providers}}); + const el = createComponent('
'); + expect(el.children[0].injector.get('injectable1')).toEqual([ + 'injectable11', 'injectable12' + ]); + }); - it('should instantiate multi providers', fakeAsync(() => { - var providers = [ - {provide: 'injectable1', useValue: 'injectable11', multi: true}, - {provide: 'injectable1', useValue: 'injectable12', multi: true} - ]; - var el = createComp( - '
', tcb.overrideProviders(SimpleDirective, providers)); - expect(el.children[0].injector.get('injectable1')).toEqual([ - 'injectable11', 'injectable12' - ]); - })); + it('should instantiate providers lazily', () => { + TestBed.configureTestingModule({declarations: [SimpleDirective]}); - it('should instantiate providers lazily', fakeAsync(() => { - var created = false; - var el = createComp( - '
', - tcb.overrideProviders( - SimpleDirective, [{provide: 'service', useFactory: () => created = true}])); + let created = false; + TestBed.overrideDirective( + SimpleDirective, + {set: {providers: [{provide: 'service', useFactory: () => created = true}]}}); + const el = createComponent('
'); - expect(created).toBe(false); + expect(created).toBe(false); - el.children[0].injector.get('service'); + el.children[0].injector.get('service'); - expect(created).toBe(true); - })); + expect(created).toBe(true); + }); - it('should instantiate providers with a lifecycle hook eagerly', fakeAsync(() => { - var created = false; - class SomeInjectable { - constructor() { created = true; } - ngOnDestroy() {} - } + it('should instantiate providers lazily', () => { + TestBed.configureTestingModule({declarations: [SimpleDirective]}); + let created = false; + TestBed.overrideDirective( + SimpleDirective, + {set: {providers: [{provide: 'service', useFactory: () => created = true}]}}); - var el = createComp( - '
', - tcb.overrideProviders(SimpleDirective, [SomeInjectable])); + const el = createComponent('
'); - expect(created).toBe(true); - })); + expect(created).toBe(false); - it('should instantiate view providers lazily', fakeAsync(() => { - var created = false; - var el = createComp( - '
', - tcb.overrideViewProviders( - SimpleComponent, [{provide: 'service', useFactory: () => created = true}])); + el.children[0].injector.get('service'); - expect(created).toBe(false); + expect(created).toBe(true); + }); - el.children[0].injector.get('service'); + it('should instantiate providers with a lifecycle hook eagerly', () => { + let created = false; + class SomeInjectable { + constructor() { created = true; } + ngOnDestroy() {} + } + TestBed.configureTestingModule({declarations: [SimpleDirective]}); + TestBed.overrideDirective(SimpleDirective, {set: {providers: [SomeInjectable]}}); - expect(created).toBe(true); - })); + const el = createComponent('
'); + + expect(created).toBe(true); + }); + + it('should instantiate view providers lazily', () => { + let created = false; + TestBed.configureTestingModule({declarations: [SimpleComponent]}); + TestBed.overrideComponent( + SimpleComponent, + {set: {viewProviders: [{provide: 'service', useFactory: () => created = true}]}}); + const el = createComponent('
'); + + expect(created).toBe(false); + + el.children[0].injector.get('service'); + + expect(created).toBe(true); + }); it('should not instantiate other directives that depend on viewProviders providers (same element)', - fakeAsync(() => { - expect( - () => createComp( - '
', - tcb.overrideViewProviders( - SimpleComponent, [{provide: 'service', useValue: 'service'}]))) + () => { + TestBed.configureTestingModule({declarations: [SimpleComponent, NeedsService]}); + TestBed.overrideComponent( + SimpleComponent, + {set: {viewProviders: [{provide: 'service', useValue: 'service'}]}}); + expect(() => createComponent('
')) .toThrowError(/No provider for service!/); - })); + }); it('should not instantiate other directives that depend on viewProviders providers (child element)', - fakeAsync(() => { - expect( - () => createComp( - '
', - tcb.overrideViewProviders( - SimpleComponent, [{provide: 'service', useValue: 'service'}]))) + () => { + TestBed.configureTestingModule({declarations: [SimpleComponent, NeedsService]}); + TestBed.overrideComponent( + SimpleComponent, + {set: {viewProviders: [{provide: 'service', useValue: 'service'}]}}); + expect(() => createComponent('
')) .toThrowError(/No provider for service!/); - })); + }); - it('should instantiate directives that depend on providers of other directives', - fakeAsync(() => { - var el = createComp( - '
', - tcb.overrideProviders( - SimpleDirective, [{provide: 'service', useValue: 'parentService'}])); - expect(el.children[0].children[0].injector.get(NeedsService).service) - .toEqual('parentService'); - })); + it('should instantiate directives that depend on providers of other directives', () => { + TestBed.configureTestingModule({declarations: [SimpleDirective, NeedsService]}); + TestBed.overrideDirective( + SimpleDirective, {set: {providers: [{provide: 'service', useValue: 'parentService'}]}}); - it('should instantiate directives that depend on providers in a parent view', - fakeAsync(() => { - var el = createComp( - '
', - tcb.overrideProviders( - SimpleDirective, [{provide: 'service', useValue: 'parentService'}])); - expect(el.children[0].children[0].injector.get(NeedsService).service) - .toEqual('parentService'); - })); + const el = createComponent('
'); + expect(el.children[0].children[0].injector.get(NeedsService).service) + .toEqual('parentService'); + }); - it('should instantiate directives that depend on providers of a component', fakeAsync(() => { - var el = createComp( - '
', - tcb.overrideTemplate(SimpleComponent, '
') - .overrideProviders( - SimpleComponent, [{provide: 'service', useValue: 'hostService'}])); - expect(el.children[0].children[0].injector.get(NeedsService).service) - .toEqual('hostService'); - })); + it('should instantiate directives that depend on providers in a parent view', () => { + TestBed.configureTestingModule({declarations: [SimpleDirective, NeedsService]}); + TestBed.overrideDirective( + SimpleDirective, {set: {providers: [{provide: 'service', useValue: 'parentService'}]}}); + const el = createComponent( + '
'); + expect(el.children[0].children[0].injector.get(NeedsService).service) + .toEqual('parentService'); + }); - it('should instantiate directives that depend on view providers of a component', - fakeAsync(() => { - var el = createComp( - '
', - tcb.overrideTemplate(SimpleComponent, '
') - .overrideViewProviders( - SimpleComponent, [{provide: 'service', useValue: 'hostService'}])); - expect(el.children[0].children[0].injector.get(NeedsService).service) - .toEqual('hostService'); - })); + it('should instantiate directives that depend on providers of a component', () => { + TestBed.configureTestingModule({declarations: [SimpleComponent, NeedsService]}); + TestBed.overrideComponent( + SimpleComponent, {set: {providers: [{provide: 'service', useValue: 'hostService'}]}}); + TestBed.overrideComponent(SimpleComponent, {set: {template: '
'}}); + const el = createComponent('
'); + expect(el.children[0].children[0].injector.get(NeedsService).service) + .toEqual('hostService'); + }); + + it('should instantiate directives that depend on view providers of a component', () => { + TestBed.configureTestingModule({declarations: [SimpleComponent, NeedsService]}); + TestBed.overrideComponent( + SimpleComponent, {set: {providers: [{provide: 'service', useValue: 'hostService'}]}}); + TestBed.overrideComponent(SimpleComponent, {set: {template: '
'}}); + const el = createComponent('
'); + expect(el.children[0].children[0].injector.get(NeedsService).service) + .toEqual('hostService'); + }); it('should instantiate directives in a root embedded view that depend on view providers of a component', - fakeAsync(() => { - var el = createComp( - '
', - tcb.overrideTemplate(SimpleComponent, '
') - .overrideViewProviders( - SimpleComponent, [{provide: 'service', useValue: 'hostService'}])); + () => { + TestBed.configureTestingModule({declarations: [SimpleComponent, NeedsService]}); + TestBed.overrideComponent( + SimpleComponent, + {set: {providers: [{provide: 'service', useValue: 'hostService'}]}}); + TestBed.overrideComponent( + SimpleComponent, {set: {template: '
'}}); + const el = createComponent('
'); expect(el.children[0].children[0].injector.get(NeedsService).service) .toEqual('hostService'); - })); + }); - it('should instantiate directives that depend on instances in the app injector', - fakeAsync(() => { - var el = createComp('
', tcb); - expect(el.children[0].injector.get(NeedsAppService).service).toEqual('appService'); - })); + it('should instantiate directives that depend on instances in the app injector', () => { + TestBed.configureTestingModule({declarations: [NeedsAppService]}); + const el = createComponent('
'); + expect(el.children[0].injector.get(NeedsAppService).service).toEqual('appService'); + }); - it('should not instantiate a directive with cyclic dependencies', fakeAsync(() => { - expect(() => createComp('
', tcb)) - .toThrowError( - 'Template parse errors:\nCannot instantiate cyclic dependency! CycleDirective ("[ERROR ->]
"): TestComp@0:0'); - })); + it('should not instantiate a directive with cyclic dependencies', () => { + TestBed.configureTestingModule({declarations: [CycleDirective]}); + expect(() => createComponent('
')) + .toThrowError( + 'Template parse errors:\nCannot instantiate cyclic dependency! CycleDirective ("[ERROR ->]
"): TestComp@0:0'); + }); it('should not instantiate a directive in a view that has a host dependency on providers' + ' of the component', - fakeAsync(() => { - expect( - () => createComp( - '
', - tcb.overrideProviders( - SimpleComponent, [{provide: 'service', useValue: 'hostService'}]) - .overrideTemplate(SimpleComponent, '
'))) + () => { + TestBed.configureTestingModule({declarations: [SimpleComponent, NeedsServiceFromHost]}); + TestBed.overrideComponent( + SimpleComponent, + {set: {providers: [{provide: 'service', useValue: 'hostService'}]}}); + TestBed.overrideComponent( + SimpleComponent, {set: {template: '
'}}); + + expect(() => createComponent('
')) .toThrowError( `Template parse errors:\nNo provider for service ("[ERROR ->]
"): SimpleComponent@0:0`); - })); + }); it('should not instantiate a directive in a view that has a host dependency on providers' + ' of a decorator directive', - fakeAsync(() => { - expect( - () => createComp( - '
', - tcb.overrideProviders( - SomeOtherDirective, [{provide: 'service', useValue: 'hostService'}]) - .overrideTemplate(SimpleComponent, '
'))) + () => { + TestBed.configureTestingModule( + {declarations: [SimpleComponent, SomeOtherDirective, NeedsServiceFromHost]}); + TestBed.overrideComponent( + SimpleComponent, + {set: {providers: [{provide: 'service', useValue: 'hostService'}]}}); + TestBed.overrideComponent( + SimpleComponent, {set: {template: '
'}}); + + expect(() => createComponent('
')) .toThrowError( `Template parse errors:\nNo provider for service ("[ERROR ->]
"): SimpleComponent@0:0`); - })); + }); it('should not instantiate a directive in a view that has a self dependency on a parent directive', - fakeAsync(() => { + () => { + TestBed.configureTestingModule( + {declarations: [SimpleDirective, NeedsDirectiveFromSelf]}); expect( () => - createComp('
', tcb)) + createComponent('
')) .toThrowError( `Template parse errors:\nNo provider for SimpleDirective ("
[ERROR ->]
"): TestComp@0:21`); - })); + }); it('should instantiate directives that depend on other directives', fakeAsync(() => { - var el = createComp('
', tcb); - var d = el.children[0].children[0].injector.get(NeedsDirective); + TestBed.configureTestingModule({declarations: [SimpleDirective, NeedsDirective]}); + const el = createComponent('
'); + const d = el.children[0].children[0].injector.get(NeedsDirective); expect(d).toBeAnInstanceOf(NeedsDirective); expect(d.dependency).toBeAnInstanceOf(SimpleDirective); })); it('should throw when a dependency cannot be resolved', fakeAsync(() => { - expect(() => createComp('
', tcb)) + TestBed.configureTestingModule({declarations: [NeedsService]}); + + expect(() => createComponent('
')) .toThrowError(/No provider for service!/); })); - it('should inject null when an optional dependency cannot be resolved', fakeAsync(() => { - var el = createComp('
', tcb); - var d = el.children[0].injector.get(OptionallyNeedsDirective); - expect(d.dependency).toEqual(null); - })); + it('should inject null when an optional dependency cannot be resolved', () => { + TestBed.configureTestingModule({declarations: [OptionallyNeedsDirective]}); + const el = createComponent('
'); + const d = el.children[0].injector.get(OptionallyNeedsDirective); + expect(d.dependency).toEqual(null); + }); - it('should instantiate directives that depends on the host component', fakeAsync(() => { - var el = createComp( - '
', - tcb.overrideTemplate(SimpleComponent, '
')); - var d = el.children[0].children[0].injector.get(NeedsComponentFromHost); - expect(d.dependency).toBeAnInstanceOf(SimpleComponent); - })); + it('should instantiate directives that depends on the host component', () => { + TestBed.configureTestingModule({declarations: [SimpleComponent, NeedsComponentFromHost]}); + TestBed.overrideComponent( + SimpleComponent, {set: {template: '
'}}); + const el = createComponent('
'); + const d = el.children[0].children[0].injector.get(NeedsComponentFromHost); + expect(d.dependency).toBeAnInstanceOf(SimpleComponent); + }); - it('should instantiate host views for components that have a @Host dependency ', - fakeAsync(() => { - var el = createComp('', tcb, NeedsHostAppService); - expect(el.componentInstance.service).toEqual('appService'); - })); + it('should instantiate host views for components that have a @Host dependency ', () => { + TestBed.configureTestingModule({declarations: [NeedsHostAppService]}); + const el = createComponent('', [], NeedsHostAppService); + expect(el.componentInstance.service).toEqual('appService'); + }); - it('should not instantiate directives that depend on other directives on the host element', - fakeAsync(() => { - expect( - () => createComp( - '
', - tcb.overrideTemplate(SimpleComponent, '
'))) - .toThrowError( - `Template parse errors:\nNo provider for SimpleDirective ("[ERROR ->]
"): SimpleComponent@0:0`); - })); + it('should not instantiate directives that depend on other directives on the host element', () => { + TestBed.configureTestingModule( + {declarations: [SimpleComponent, SimpleDirective, NeedsDirectiveFromHost]}); + TestBed.overrideComponent( + SimpleComponent, {set: {template: '
'}}); + expect(() => createComponent('
')) + .toThrowError( + `Template parse errors:\nNo provider for SimpleDirective ("[ERROR ->]
"): SimpleComponent@0:0`); + }); }); describe('static attributes', () => { - it('should be injectable', fakeAsync(() => { - var el = createComp('
', tcb); - var needsAttribute = el.children[0].injector.get(NeedsAttribute); + it('should be injectable', () => { + TestBed.configureTestingModule({declarations: [NeedsAttribute]}); + const el = createComponent('
'); + const needsAttribute = el.children[0].injector.get(NeedsAttribute); - expect(needsAttribute.typeAttribute).toEqual('text'); - expect(needsAttribute.titleAttribute).toEqual(''); - expect(needsAttribute.fooAttribute).toEqual(null); - })); + expect(needsAttribute.typeAttribute).toEqual('text'); + expect(needsAttribute.titleAttribute).toEqual(''); + expect(needsAttribute.fooAttribute).toEqual(null); + }); - it('should be injectable without type annotation', fakeAsync(() => { - var el = createComp('
', tcb); - var needsAttribute = el.children[0].injector.get(NeedsAttributeNoType); + it('should be injectable without type annotation', () => { + TestBed.configureTestingModule({declarations: [NeedsAttributeNoType]}); + const el = createComponent('
'); + const needsAttribute = el.children[0].injector.get(NeedsAttributeNoType); - expect(needsAttribute.fooAttribute).toEqual('bar'); - })); + expect(needsAttribute.fooAttribute).toEqual('bar'); + }); }); describe('refs', () => { - it('should inject ElementRef', fakeAsync(() => { - var el = createComp('
', tcb); - expect(el.children[0].injector.get(NeedsElementRef).elementRef.nativeElement) - .toBe(el.children[0].nativeElement); - })); + it('should inject ElementRef', () => { + TestBed.configureTestingModule({declarations: [NeedsElementRef]}); + const el = createComponent('
'); + expect(el.children[0].injector.get(NeedsElementRef).elementRef.nativeElement) + .toBe(el.children[0].nativeElement); + }); it('should inject ChangeDetectorRef of the component\'s view into the component via a proxy', - fakeAsync(() => { - var cf = createCompFixture('
', tcb); + () => { + TestBed.configureTestingModule({declarations: [PushComponentNeedsChangeDetectorRef]}); + const cf = createComponentFixture('
'); cf.detectChanges(); - var compEl = cf.debugElement.children[0]; - var comp = compEl.injector.get(PushComponentNeedsChangeDetectorRef); + const compEl = cf.debugElement.children[0]; + const comp = compEl.injector.get(PushComponentNeedsChangeDetectorRef); comp.counter = 1; cf.detectChanges(); expect(compEl.nativeElement).toHaveText('0'); comp.changeDetectorRef.markForCheck(); cf.detectChanges(); expect(compEl.nativeElement).toHaveText('1'); - })); + }); - it('should inject ChangeDetectorRef of the containing component into directives', - fakeAsync(() => { - var cf = createCompFixture( - '
', - tcb.overrideTemplate( - PushComponentNeedsChangeDetectorRef, - '{{counter}}
')); - cf.detectChanges(); - var compEl = cf.debugElement.children[0]; - var comp: PushComponentNeedsChangeDetectorRef = - compEl.injector.get(PushComponentNeedsChangeDetectorRef); - comp.counter = 1; - cf.detectChanges(); - expect(compEl.nativeElement).toHaveText('0'); - expect( - compEl.children[0].injector.get(DirectiveNeedsChangeDetectorRef).changeDetectorRef) - .toBe(comp.changeDetectorRef); - expect( - compEl.children[1].injector.get(DirectiveNeedsChangeDetectorRef).changeDetectorRef) - .toBe(comp.changeDetectorRef); - comp.changeDetectorRef.markForCheck(); - cf.detectChanges(); - expect(compEl.nativeElement).toHaveText('1'); - })); + it('should inject ChangeDetectorRef of the containing component into directives', () => { + TestBed.configureTestingModule( + {declarations: [PushComponentNeedsChangeDetectorRef, DirectiveNeedsChangeDetectorRef]}); + TestBed.overrideComponent(PushComponentNeedsChangeDetectorRef, { + set: { + template: + '{{counter}}
' + } + }); + const cf = createComponentFixture('
'); + cf.detectChanges(); + const compEl = cf.debugElement.children[0]; + const comp: PushComponentNeedsChangeDetectorRef = + compEl.injector.get(PushComponentNeedsChangeDetectorRef); + comp.counter = 1; + cf.detectChanges(); + expect(compEl.nativeElement).toHaveText('0'); + expect(compEl.children[0].injector.get(DirectiveNeedsChangeDetectorRef).changeDetectorRef) + .toBe(comp.changeDetectorRef); + expect(compEl.children[1].injector.get(DirectiveNeedsChangeDetectorRef).changeDetectorRef) + .toBe(comp.changeDetectorRef); + comp.changeDetectorRef.markForCheck(); + cf.detectChanges(); + expect(compEl.nativeElement).toHaveText('1'); + }); - it('should inject ViewContainerRef', fakeAsync(() => { - var el = createComp('
', tcb); - expect(el.children[0] - .injector.get(NeedsViewContainerRef) - .viewContainer.element.nativeElement) - .toBe(el.children[0].nativeElement); - })); + it('should inject ViewContainerRef', () => { + TestBed.configureTestingModule({declarations: [NeedsViewContainerRef]}); + const el = createComponent('
'); + expect( + el.children[0].injector.get(NeedsViewContainerRef).viewContainer.element.nativeElement) + .toBe(el.children[0].nativeElement); + }); - it('should inject TemplateRef', fakeAsync(() => { - var el = createComp('', tcb); - expect(el.childNodes[0].injector.get(NeedsTemplateRef).templateRef.elementRef) - .toEqual(el.childNodes[0].injector.get(NeedsViewContainerRef).viewContainer.element); - })); + it('should inject TemplateRef', () => { + TestBed.configureTestingModule({declarations: [NeedsViewContainerRef, NeedsTemplateRef]}); + const el = createComponent(''); + expect(el.childNodes[0].injector.get(NeedsTemplateRef).templateRef.elementRef) + .toEqual(el.childNodes[0].injector.get(NeedsViewContainerRef).viewContainer.element); + }); - it('should throw if there is no TemplateRef', fakeAsync(() => { - expect(() => createComp('
', tcb)) - .toThrowError(/No provider for TemplateRef!/); - })); + it('should throw if there is no TemplateRef', () => { + TestBed.configureTestingModule({declarations: [NeedsTemplateRef]}); + expect(() => createComponent('
')) + .toThrowError(/No provider for TemplateRef!/); + }); - it('should inject null if there is no TemplateRef when the dependency is optional', - fakeAsync(() => { - var el = createComp('
', tcb); - var instance = el.children[0].injector.get(OptionallyNeedsTemplateRef); - expect(instance.templateRef).toBeNull(); - })); + it('should inject null if there is no TemplateRef when the dependency is optional', () => { + TestBed.configureTestingModule({declarations: [OptionallyNeedsTemplateRef]}); + const el = createComponent('
'); + const instance = el.children[0].injector.get(OptionallyNeedsTemplateRef); + expect(instance.templateRef).toBeNull(); + }); }); describe('pipes', () => { - it('should instantiate pipes that have dependencies', fakeAsync(() => { - var el = createComp( - '
', - tcb.overrideProviders(TestComp, [{provide: 'service', useValue: 'pipeService'}])); - expect(el.children[0].injector.get(SimpleDirective).value.service) - .toEqual('pipeService'); - })); + it('should instantiate pipes that have dependencies', () => { + TestBed.configureTestingModule({declarations: [SimpleDirective, PipeNeedsService]}); - it('should overwrite pipes with later entry in the pipes array', fakeAsync(() => { - var el = createComp('
', tcb); - expect(el.children[0].injector.get(SimpleDirective).value) - .toBeAnInstanceOf(DuplicatePipe2); - })); + const el = createComponent( + '
', + [{provide: 'service', useValue: 'pipeService'}]); + expect(el.children[0].injector.get(SimpleDirective).value.service).toEqual('pipeService'); + }); - it('should inject ChangeDetectorRef into pipes', fakeAsync(() => { - var el = createComp( - '
', - tcb); - var cdRef = - el.children[0].injector.get(DirectiveNeedsChangeDetectorRef).changeDetectorRef; - expect(el.children[0].injector.get(SimpleDirective).value.changeDetectorRef).toBe(cdRef); - })); + it('should overwrite pipes with later entry in the pipes array', () => { + TestBed.configureTestingModule( + {declarations: [SimpleDirective, DuplicatePipe1, DuplicatePipe2]}); + const el = createComponent('
'); + expect(el.children[0].injector.get(SimpleDirective).value).toBeAnInstanceOf(DuplicatePipe2); + }); - it('should cache pure pipes', fakeAsync(() => { - var el = createComp( - '
' + - '
', - tcb); - var purePipe1 = el.children[0].injector.get(SimpleDirective).value; - var purePipe2 = el.children[1].injector.get(SimpleDirective).value; - var purePipe3 = el.children[2].injector.get(SimpleDirective).value; - var purePipe4 = el.children[3].injector.get(SimpleDirective).value; - expect(purePipe1).toBeAnInstanceOf(PurePipe); - expect(purePipe2).toBe(purePipe1); - expect(purePipe3).toBe(purePipe1); - expect(purePipe4).toBe(purePipe1); - })); + it('should inject ChangeDetectorRef into pipes', () => { + TestBed.configureTestingModule({ + declarations: + [SimpleDirective, PipeNeedsChangeDetectorRef, DirectiveNeedsChangeDetectorRef] + }); + const el = createComponent( + '
'); + const cdRef = + el.children[0].injector.get(DirectiveNeedsChangeDetectorRef).changeDetectorRef; + expect(el.children[0].injector.get(SimpleDirective).value.changeDetectorRef).toBe(cdRef); + }); - it('should not cache impure pipes', fakeAsync(() => { - var el = createComp( - '
' + - '
', - tcb); - var purePipe1 = el.children[0].injector.get(SimpleDirective).value; - var purePipe2 = el.children[1].injector.get(SimpleDirective).value; - var purePipe3 = el.children[2].injector.get(SimpleDirective).value; - var purePipe4 = el.children[3].injector.get(SimpleDirective).value; - expect(purePipe1).toBeAnInstanceOf(ImpurePipe); - expect(purePipe2).toBeAnInstanceOf(ImpurePipe); - expect(purePipe2).not.toBe(purePipe1); - expect(purePipe3).toBeAnInstanceOf(ImpurePipe); - expect(purePipe3).not.toBe(purePipe1); - expect(purePipe4).toBeAnInstanceOf(ImpurePipe); - expect(purePipe4).not.toBe(purePipe1); - })); + it('should cache pure pipes', () => { + TestBed.configureTestingModule({declarations: [SimpleDirective, PurePipe]}); + const el = createComponent( + '
' + + '
'); + const purePipe1 = el.children[0].injector.get(SimpleDirective).value; + const purePipe2 = el.children[1].injector.get(SimpleDirective).value; + const purePipe3 = el.children[2].injector.get(SimpleDirective).value; + const purePipe4 = el.children[3].injector.get(SimpleDirective).value; + expect(purePipe1).toBeAnInstanceOf(PurePipe); + expect(purePipe2).toBe(purePipe1); + expect(purePipe3).toBe(purePipe1); + expect(purePipe4).toBe(purePipe1); + }); + + it('should not cache impure pipes', () => { + TestBed.configureTestingModule({declarations: [SimpleDirective, ImpurePipe]}); + const el = createComponent( + '
' + + '
'); + const impurePipe1 = el.children[0].injector.get(SimpleDirective).value; + const impurePipe2 = el.children[1].injector.get(SimpleDirective).value; + const impurePipe3 = el.children[2].injector.get(SimpleDirective).value; + const impurePipe4 = el.children[3].injector.get(SimpleDirective).value; + expect(impurePipe1).toBeAnInstanceOf(ImpurePipe); + expect(impurePipe2).toBeAnInstanceOf(ImpurePipe); + expect(impurePipe2).not.toBe(impurePipe1); + expect(impurePipe3).toBeAnInstanceOf(ImpurePipe); + expect(impurePipe3).not.toBe(impurePipe1); + expect(impurePipe4).toBeAnInstanceOf(ImpurePipe); + expect(impurePipe4).not.toBe(impurePipe1); + }); }); }); }