diff --git a/test/core/linker/query_integration_test.dart b/test/core/linker/query_integration_test.dart index 0e7c711a71..cd208fb353 100644 --- a/test/core/linker/query_integration_test.dart +++ b/test/core/linker/query_integration_test.dart @@ -1,1137 +1,898 @@ -@TestOn('browser && !js') +@TestOn('browser') + import 'package:angular2/angular2.dart'; -import "package:angular2/src/testing/internal.dart"; +import 'package:angular_test/angular_test.dart'; import 'package:test/test.dart'; void main() { - group("Query API", () { - group("querying by directive type", () { - test( - "should contain all direct child directives in the light dom (constructor)", - () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = "
" + - "
" + - "
" + - "
" + - "
"; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - view.detectChanges(); - expect(asNativeElements(view.debugElement.children), - hasTextContent("2|3|")); - completer.done(); - }); - }); - }); - test("should contain all direct child directives in the content dom", - () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = - "
"; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - view.detectChanges(); - var q = view.debugElement.children[0].getLocal("q"); - view.detectChanges(); - expect(q.textDirChildren.length, 1); - expect(q.numberOfChildrenAfterContentInit, 1); - completer.done(); - }); - }); - }); - test("should contain the first content child", () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = - "
"; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - view.debugElement.componentInstance.shouldShow = true; - view.detectChanges(); - NeedsContentChild q = view.debugElement.children[0].getLocal("q"); - expect(q.log, [ - ["setter", "foo"], - ["init", "foo"], - ["check", "foo"] - ]); - view.debugElement.componentInstance.shouldShow = false; - view.detectChanges(); - expect(q.log, [ - ["setter", "foo"], - ["init", "foo"], - ["check", "foo"], - ["setter", null], - ["check", null] - ]); - completer.done(); - }); - }); - }); - test("should contain the first view child", () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = ""; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - view.detectChanges(); - NeedsViewChild q = view.debugElement.children[0].getLocal("q"); - expect(q.log, [ - ["setter", "foo"], - ["init", "foo"], - ["check", "foo"] - ]); - q.shouldShow = false; - view.detectChanges(); - expect(q.log, [ - ["setter", "foo"], - ["init", "foo"], - ["check", "foo"], - ["setter", null], - ["check", null] - ]); - completer.done(); - }); - }); - }); - test( - "should set static view and content children already after the constructor call", - () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = - "
"; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - NeedsStaticContentAndViewChild q = - view.debugElement.children[0].getLocal("q"); - expect(q.contentChild.text, isNull); - expect(q.viewChild.text, isNull); - view.detectChanges(); - expect(q.contentChild.text, "contentFoo"); - expect(q.viewChild.text, "viewFoo"); - completer.done(); - }); - }); - }); - test("should contain the first view child accross embedded views", - () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = ""; - tcb - .overrideTemplate(MyComp, template) - .overrideTemplate(NeedsViewChild, - "
") - .createAsync(MyComp) - .then((view) { - view.detectChanges(); - NeedsViewChild q = view.debugElement.children[0].getLocal("q"); - expect(q.log, [ - ["setter", "foo"], - ["init", "foo"], - ["check", "foo"] - ]); - q.shouldShow = false; - q.shouldShow2 = true; - q.log = []; - view.detectChanges(); - expect(q.log, [ - ["setter", "bar"], - ["check", "bar"] - ]); - q.shouldShow = false; - q.shouldShow2 = false; - q.log = []; - view.detectChanges(); - expect(q.log, [ - ["setter", null], - ["check", null] - ]); - completer.done(); - }); - }); - }); - test( - "should contain all directives in the light dom when descendants flag is used", - () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = "
" + - "
" + - "
" + - "
" + - "
"; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - view.detectChanges(); - expect(asNativeElements(view.debugElement.children), - hasTextContent("2|3|4|")); - completer.done(); - }); - }); - }); - test("should contain all directives in the light dom", () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = "
" + - "
" + - "
"; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - view.detectChanges(); - expect(asNativeElements(view.debugElement.children), - hasTextContent("2|3|")); - completer.done(); - }); - }); - }); - test("should reflect dynamically inserted directives", () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = "
" + - "
" + - "
"; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - view.detectChanges(); - expect(asNativeElements(view.debugElement.children), - hasTextContent("2|")); - view.debugElement.componentInstance.shouldShow = true; - view.detectChanges(); - expect(asNativeElements(view.debugElement.children), - hasTextContent("2|3|")); - completer.done(); - }); - }); - }); - test("should be cleanly destroyed when a query crosses view boundaries", - () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = "
" + - "
" + - "
"; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((fixture) { - fixture.debugElement.componentInstance.shouldShow = true; - fixture.detectChanges(); - fixture.destroy(); - completer.done(); - }); - }); - }); - test("should reflect moved directives", () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = "
" + - "
" + - "
"; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - view.detectChanges(); - expect(asNativeElements(view.debugElement.children), - hasTextContent("2|1d|2d|3d|")); - view.debugElement.componentInstance.list = ["3d", "2d"]; - view.detectChanges(); - expect(asNativeElements(view.debugElement.children), - hasTextContent("2|3d|2d|")); - completer.done(); - }); - }); - }); + tearDown(disposeAnyRunningTest); + + group('query for Directive', () { + test('should contain all direct content children', () async { + final testBed = new NgTestBed(); + final testFixture = await testBed.create(); + expect(testFixture.text.trim(), '2|3|5'); }); - group("query for TemplateRef", () { - test("should find TemplateRefs in the light and shadow dom", () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = - ""; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - view.detectChanges(); - NeedsTpl needsTpl = view.debugElement.children[0].inject(NeedsTpl); - expect( - needsTpl.vc - .createEmbeddedView(needsTpl.query.first) - .hasLocal("light"), - isTrue); - expect( - needsTpl.vc - .createEmbeddedView(needsTpl.viewQuery.first) - .hasLocal("shadow"), - isTrue); - completer.done(); - }); - }); - }); - test("should find named TemplateRefs", () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = - ""; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - view.detectChanges(); - NeedsNamedTpl needsTpl = - view.debugElement.children[0].inject(NeedsNamedTpl); - expect( - needsTpl.vc - .createEmbeddedView(needsTpl.contentTpl) - .hasLocal("light"), - isTrue); - expect( - needsTpl.vc - .createEmbeddedView(needsTpl.viewTpl) - .hasLocal("shadow"), - isTrue); - completer.done(); - }); - }); - }); + + test('should contain all content children', () async { + final testBed = new NgTestBed(); + final testFixture = await testBed.create(); + expect(testFixture.text.trim(), '2|3|4|5'); }); - group("read a different token", () { - test("should contain all content children", () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = - "
"; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - view.detectChanges(); - NeedsContentChildrenWithRead comp = view.debugElement.children[0] - .inject(NeedsContentChildrenWithRead); - expect( - comp.textDirChildren.map((textDirective) => textDirective.text), - ["ca", "cb"]); - completer.done(); - }); - }); - }); - test("should contain the first content child", () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = - "
"; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - view.detectChanges(); - NeedsContentChildWithRead comp = - view.debugElement.children[0].inject(NeedsContentChildWithRead); - expect(comp.textDirChild.text, "ca"); - completer.done(); - }); - }); - }); - test("should contain the first view child", () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = ""; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - view.detectChanges(); - NeedsViewChildWithRead comp = - view.debugElement.children[0].inject(NeedsViewChildWithRead); - expect(comp.textDirChild.text, "va"); - completer.done(); - }); - }); - }); - test("should contain all child directives in the view", () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = - ""; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - view.detectChanges(); - NeedsViewChildrenWithRead comp = - view.debugElement.children[0].inject(NeedsViewChildrenWithRead); - expect( - comp.textDirChildren.map((textDirective) => textDirective.text), - ["va", "vb"]); - completer.done(); - }); - }); - }); - test("should support reading a ViewContainer", () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = - ""; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - view.detectChanges(); - NeedsViewContainerWithRead comp = view.debugElement.children[0] - .inject(NeedsViewContainerWithRead); - comp.createView(); - expect(view.debugElement.children[0].nativeElement, - hasTextContent("hello")); - completer.done(); - }); - }); - }); + + test('should contain first content child', () async { + final testBed = new NgTestBed(); + final testFixture = await testBed.create(); + expect(testFixture.text.trim(), '2'); }); - group("changes", () { - test("should notify query on change", () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = "" + - "
" + - "
" + - "
"; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - var q = view.debugElement.children[0].getLocal("q"); - view.detectChanges(); - q.query.changes.listen((_) { - expect(q.query.first.text, "1"); - expect(q.query.last.text, "2"); - completer.done(); - }); - view.debugElement.componentInstance.shouldShow = true; - view.detectChanges(); - }); - }); - }); - test( - "should correctly clean-up when destroyed together with the directives it is querying", - () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = - "
"; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - view.debugElement.componentInstance.shouldShow = true; - view.detectChanges(); - NeedsQuery q = view.debugElement.children[0].getLocal("q"); - expect(q.query.length, 1); - view.debugElement.componentInstance.shouldShow = false; - view.detectChanges(); - view.debugElement.componentInstance.shouldShow = true; - view.detectChanges(); - NeedsQuery q2 = view.debugElement.children[0].getLocal("q"); - expect(q2.query.length, 1); - completer.done(); - }); - }); - }); + + test('should contain all view children', () async { + final testBed = new NgTestBed(); + final testFixture = await testBed.create(); + expect(testFixture.text.trim(), 'a|b|c'); }); - group("querying by var binding", () { - test( - "should contain all the child directives in the light dom with the given var binding", - () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = "" + - "
" + - "
"; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - var q = view.debugElement.children[0].getLocal("q"); - view.debugElement.componentInstance.list = ["1d", "2d"]; - view.detectChanges(); - expect(q.query.first.text, "1d"); - expect(q.query.last.text, "2d"); - completer.done(); - }); - }); - }); - test("should support querying by multiple var bindings", () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = "" + - "
" + - "
" + - "
"; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - var q = view.debugElement.children[0].getLocal("q"); - view.detectChanges(); - expect(q.query.first.text, "one"); - expect(q.query.last.text, "two"); - completer.done(); - }); - }); - }); - test("should support dynamically inserted directives", () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = "" + - "
" + - "
"; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - var q = view.debugElement.children[0].getLocal("q"); - view.debugElement.componentInstance.list = ["1d", "2d"]; - view.detectChanges(); - view.debugElement.componentInstance.list = ["2d", "1d"]; - view.detectChanges(); - expect(q.query.last.text, "1d"); - completer.done(); - }); - }); - }); - test( - "should contain all the elements in the light dom with the given var binding", - () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = "" + - "
" + - "
{{item}}
" + - "
" + - "
"; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - var q = view.debugElement.children[0].getLocal("q"); - view.debugElement.componentInstance.list = ["1d", "2d"]; - view.detectChanges(); - expect(q.query.first.nativeElement, hasTextContent("1d")); - expect(q.query.last.nativeElement, hasTextContent("2d")); - completer.done(); - }); - }); - }); - test( - "should contain all the elements in the light dom even if they get projected", - () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = "" + - "
" + - "
"; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - view.detectChanges(); - expect(asNativeElements(view.debugElement.children), - hasTextContent("hello|world|")); - completer.done(); - }); - }); - }); - test("should support querying the view by using a view query", () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = - ""; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - NeedsViewQueryByLabel q = - view.debugElement.children[0].getLocal("q"); - view.detectChanges(); - expect(q.query.first.nativeElement, hasTextContent("text")); - completer.done(); - }); - }); - }); - test("should contain all child directives in the view dom", () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = ""; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - view.detectChanges(); - var q = view.debugElement.children[0].getLocal("q"); - view.detectChanges(); - expect(q.textDirChildren.length, 1); - expect(q.numberOfChildrenAfterViewInit, 1); - completer.done(); - }); - }); - }); + + test('should contain first view child', () async { + final testBed = new NgTestBed(); + final testFixture = await testBed.create(); + expect(testFixture.text.trim(), 'a'); }); - group("querying in the view", () { - test( - "should contain all the elements in the view with that have the given directive", - () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = - "
"; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - NeedsViewQuery q = view.debugElement.children[0].getLocal("q"); - view.detectChanges(); - expect( - q.query.map((TextDirective d) => d.text), ["1", "2", "3", "4"]); - completer.done(); - }); - }); - }); - test("should not include directive present on the host element", - () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = - ""; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - NeedsViewQuery q = view.debugElement.children[0].getLocal("q"); - view.detectChanges(); - expect( - q.query.map((TextDirective d) => d.text), ["1", "2", "3", "4"]); - completer.done(); - }); - }); - }); - test("should reflect changes in the component", () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = ""; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - NeedsViewQueryIf q = view.debugElement.children[0].getLocal("q"); - view.detectChanges(); - expect(q.query, hasLength(0)); - q.show = true; - view.detectChanges(); - expect(q.query, hasLength(1)); - expect(q.query.first.text, "1"); - completer.done(); - }); - }); - }); - test("should not be affected by other changes in the component", - () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = - ""; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - NeedsViewQueryNestedIf q = - view.debugElement.children[0].getLocal("q"); - view.detectChanges(); - expect(q.query.length, 1); - expect(q.query.first.text, "1"); - q.show = false; - view.detectChanges(); - expect(q.query.length, 1); - expect(q.query.first.text, "1"); - completer.done(); - }); - }); - }); - test( - "should maintain directives in pre-order depth-first DOM order after dynamic insertion", - () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = ""; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - NeedsViewQueryOrder q = view.debugElement.children[0].getLocal("q"); - view.detectChanges(); - expect( - q.query.map((TextDirective d) => d.text), ["1", "2", "3", "4"]); - q.list = ["-3", "2"]; - view.detectChanges(); - expect(q.query.map((TextDirective d) => d.text), - ["1", "-3", "2", "4"]); - completer.done(); - }); - }); - }); - test( - "should maintain directives in pre-order depth-first DOM order after dynamic insertion", - () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = - ""; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - NeedsViewQueryOrderWithParent q = - view.debugElement.children[0].getLocal("q"); - view.detectChanges(); - expect( - q.query.map((TextDirective d) => d.text), ["1", "2", "3", "4"]); - q.list = ["-3", "2"]; - view.detectChanges(); - expect(q.query.map((TextDirective d) => d.text), - ["1", "-3", "2", "4"]); - completer.done(); - }); - }); + + test('should contain all direct content children in embedded view', + () async { + final testBed = new NgTestBed(); + final testFixture = await testBed.create(); + expect(testFixture.text.trim(), '1'); + await testFixture.update((component) => component.showContent = true); + expect(testFixture.text.trim(), '1|2|4'); + }); + + test('should contain all content children in embedded view', () async { + final testBed = + new NgTestBed(); + final testFixture = await testBed.create(); + expect(testFixture.text.trim(), '1'); + await testFixture.update((component) => component.showContent = true); + expect(testFixture.text.trim(), '1|2|3|4'); + }); + + test('should contain first content child in embedded view', () async { + final testBed = new NgTestBed(); + final testFixture = await testBed.create(); + expect(testFixture.text.trim(), isEmpty); + await testFixture.update((component) => component.showContent = true); + expect(testFixture.text.trim(), '1'); + }); + + test('should contain all view children in embedded view', () async { + final testBed = new NgTestBed(); + final testFixture = await testBed.create(); + expect(testFixture.text.trim(), isEmpty); + await testFixture.update((component) => component.showView = true); + expect(testFixture.text.trim(), 'a|b|c'); + }); + + test('should contain first view child in embedded view', () async { + final testBed = new NgTestBed(); + final testFixture = await testBed.create(); + expect(testFixture.text.trim(), 'c'); + await testFixture.update((component) => component.showView = true); + expect(testFixture.text.trim(), 'a'); + }); + + test('should handle moved directives', () async { + final testBed = new NgTestBed(); + final testFixture = await testBed.create(); + expect(testFixture.text.trim(), '1|2|3'); + await testFixture.update((component) => component.list = ['3', '2']); + expect(testFixture.text.trim(), '3|2'); + }); + + test('should support transclusion', () async { + final testBed = new NgTestBed(); + final testFixture = await testBed.create(); + expect(testFixture.text.trim(), '2|7'); + }); + + test('should not be affected by unrelated changes', () async { + final testBed = new NgTestBed(); + final testFixture = await testBed.create(); + expect(testFixture.text.trim(), '1'); + await testFixture.update((component) { + component.showInertDirective = false; }); - test("should handle long ngFor cycles", () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = ""; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - NeedsViewQueryOrder q = view.debugElement.children[0].getLocal("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(); - expect(q.query.map((TextDirective d) => d.text), - ["1", newString, "4"]); - } - completer.done(); - }); + expect(testFixture.text.trim(), '1'); + }); + + test('should handle long ngFor cycles', () async { + final testBed = new NgTestBed(); + final testFixture = await testBed.create(); + // No significance to 50, just a reasonably long cycle. + for (var i = 0; i < 50; i++) { + await testFixture.update((component) { + component.list = ['$i', '${i + 1}']; }); + expect(testFixture.text.trim(), '$i|${i + 1}'); + } + }); + + test('should support more than three queries', () async { + final testBed = new NgTestBed(); + final testFixture = await testBed.create(); + expect(testFixture.text.trim(), '1|1|1|1'); + }); + }); + + group('query for TemplateRef', () { + test('should find content and view children', () async { + final testBed = new NgTestBed(); + final testFixture = await testBed.create(); + expect(testFixture.rootElement.querySelectorAll('.embedded-from-content'), + hasLength(2)); + expect(testFixture.rootElement.querySelectorAll('.embedded-from-view'), + hasLength(2)); + }); + + test('should find named content child and named view child', () async { + final testBed = new NgTestBed(); + final testFixture = await testBed.create(); + expect(testFixture.rootElement.querySelectorAll('.embedded-from-content'), + hasLength(1)); + expect(testFixture.rootElement.querySelectorAll('.embedded-from-view'), + hasLength(1)); + }); + }); + + group('query for a different token via read', () { + test('should contain all content children', () async { + final testBed = new NgTestBed(); + final testFixture = await testBed.create(); + expect(testFixture.text.trim(), '1|3'); + }); + + test('should contain the first content child', () async { + final testBed = new NgTestBed(); + final testFixture = await testBed.create(); + expect(testFixture.text.trim(), '2'); + }); + + test('should contain all view children', () async { + final testBed = new NgTestBed(); + final testFixture = await testBed.create(); + expect(testFixture.text.trim(), '2|3'); + }); + + test('should contain the first view child', () async { + final testBed = new NgTestBed(); + final testFixture = await testBed.create(); + expect(testFixture.text.trim(), '2'); + }); + + test('should support ViewContainer', () async { + final testBed = new NgTestBed(); + final testFixture = await testBed.create(); + expect(testFixture.text.trim(), 'Embedded in view container!'); + }); + }); + + group('changes', () { + test('should update query results', () async { + final testBed = new NgTestBed(); + final testFixture = await testBed.create(); + expect(testFixture.text.trim(), '1|2|3'); + await testFixture.update((component) { + component.x = '3'; + component.z = '1'; }); - test("should support more than three queries", () async { - return inject([TestComponentBuilder, AsyncTestCompleter], - (TestComponentBuilder tcb, AsyncTestCompleter completer) { - var template = - "
"; - tcb - .overrideTemplate(MyComp, template) - .createAsync(MyComp) - .then((view) { - view.detectChanges(); - var q = view.debugElement.children[0].getLocal("q"); - expect(q.query1, isNotNull); - expect(q.query2, isNotNull); - expect(q.query3, isNotNull); - expect(q.query4, isNotNull); - completer.done(); - }); - }); + expect(testFixture.text.trim(), '3|2|1'); + }); + + test('should remove destroyed directives from query results', () async { + final testBed = new NgTestBed(); + var component; + final testFixture = await testBed.create( + beforeChangeDetection: (instance) => component = instance); + expect(component.textDirectives, hasLength(1)); + await testFixture.update((component) => component.showView = false); + expect(component.textDirectives, hasLength(0)); + await testFixture.update((component) => component.showView = true); + expect(component.textDirectives, hasLength(1)); + }); + }); + + group('query for variable binding', () { + test('should contain all view children', () async { + final testBed = new NgTestBed(); + final testFixture = await testBed.create(); + expect(testFixture.text.trim(), '1|2|4|8'); + }); + + test('should support multiple variables', () async { + final testBed = new NgTestBed(); + final testFixture = await testBed.create(); + expect(testFixture.text.trim(), '0|1'); + }); + + test('should support changes', () async { + final testBed = new NgTestBed(); + final testFixture = await testBed.create(); + await testFixture.update((component) { + component.list = ['8', '4', '2', '1']; }); + expect(testFixture.text.trim(), '8|4|2|1'); + }); + + test('should support element binding', () async { + var component; + final testBed = new NgTestBed(); + await testBed.create(beforeChangeDetection: (comp) => component = comp); + final divIt = component.elementRefs.iterator; + final itemIt = component.list.iterator; + + while (divIt.moveNext()) { + itemIt.moveNext(); + expect(divIt.current.nativeElement.text, itemIt.current); + } + + expect(itemIt.moveNext(), isFalse); }); }); } -@Directive(selector: "[text]", inputs: const ["text"], exportAs: "textDir") -@Injectable() +@Directive( + selector: '[text]', + exportAs: 'textDirective', +) class TextDirective { + @Input() String text; - TextDirective(); } -@Component(selector: "needs-content-children", template: "") -class NeedsContentChildren implements AfterContentInit { - @ContentChildren(TextDirective) - QueryList textDirChildren; - num numberOfChildrenAfterContentInit; - @override - ngAfterContentInit() { - this.numberOfChildrenAfterContentInit = this.textDirChildren.length; - } +abstract class TextDirectivesRenderer { + Iterable get textDirectives; + + String get text => textDirectives.map((dir) => dir.text).join('|'); } @Component( - selector: "needs-view-children", - template: "
", - directives: const [TextDirective]) -class NeedsViewChildren implements AfterViewInit { - @ViewChildren(TextDirective) - QueryList textDirChildren; - num numberOfChildrenAfterViewInit; - @override - ngAfterViewInit() { - this.numberOfChildrenAfterViewInit = this.textDirChildren.length; - } + selector: 'content-children', + template: '
{{text}}
', +) +class ContentChildrenComponent extends TextDirectivesRenderer { + @ContentChildren(TextDirective) + QueryList textDirectives; } -@Component(selector: "needs-content-child", template: "") -class NeedsContentChild implements AfterContentInit, AfterContentChecked { - TextDirective _child; - @ContentChild(TextDirective) - set child(value) { - this._child = value; - this.log.add(["setter", value != null ? value.text : null]); - } - - get child { - return this._child; - } - - var log = []; - @override - ngAfterContentInit() { - this.log.add(["init", child != null ? child.text : null]); - } +@Component( + selector: 'tests-content-children', + template: ''' +
+ +
+
+
+
+
+
''', + directives: const [ + ContentChildrenComponent, + TextDirective, + ], +) +class TestsContentChildrenComponent {} - @override - ngAfterContentChecked() { - this.log.add(["check", child != null ? child.text : null]); - } +@Component( + selector: 'content-children-descendants', + template: '
{{text}}
', +) +class ContentChildrenDescendantsComponent extends TextDirectivesRenderer { + @ContentChildren(TextDirective, descendants: true) + QueryList textDirectives; } @Component( - selector: "needs-view-child", - template: ''' -
- ''', - directives: const [NgIf, TextDirective]) -class NeedsViewChild implements AfterViewInit, AfterViewChecked { - bool shouldShow = true; - bool shouldShow2 = false; - TextDirective _child; - @ViewChild(TextDirective) - set child(value) { - this._child = value; - this.log.add(["setter", value != null ? value.text : null]); - } + selector: 'tests-content-children-descendants', + template: ''' +
+ +
+
+
+
+
+
''', + directives: const [ + ContentChildrenDescendantsComponent, + TextDirective, + ], +) +class TestsContentChildrenDescendantsComponent {} - get child { - return this._child; - } +@Component( + selector: 'content-child', + template: '
{{textDirective?.text}}
', +) +class ContentChildComponent { + @ContentChild(TextDirective) + TextDirective textDirective; +} - var log = []; - @override - ngAfterViewInit() { - this.log.add(["init", child != null ? child.text : null]); - } +@Component( + selector: 'tests-content-child', + template: ''' +
+ +
+
+
+
+
+
''', + directives: const [ + ContentChildComponent, + TextDirective, + ], +) +class TestsContentChildComponent {} - @override - ngAfterViewChecked() { - this.log.add(["check", child != null ? child.text : null]); - } +@Component( + selector: 'view-children', + template: ''' +
+
+
+
{{text}}
+
''', + directives: const [ + TextDirective, + ], +) +class ViewChildrenComponent extends TextDirectivesRenderer { + @ViewChildren(TextDirective) + QueryList textDirectives; } @Component( - selector: "needs-static-content-view-child", - template: ''' -
- ''', - directives: const [TextDirective]) -class NeedsStaticContentAndViewChild { - @ContentChild(TextDirective) - TextDirective contentChild; + selector: 'tests-view-children', + template: ''' +
+ +
+
+
''', + directives: const [ + TextDirective, + ViewChildrenComponent, + ], +) +class TestsViewChildrenComponent {} + +@Component( + selector: 'view-child', + template: ''' +
+
+
+
{{textDirective.text}}
+
''', + directives: const [ + TextDirective, + ], +) +class ViewChildComponent { @ViewChild(TextDirective) - TextDirective viewChild; + TextDirective textDirective; } -@Directive(selector: "[dir]") -@Injectable() -class InertDirective { - InertDirective(); -} +@Component( + selector: 'tests-view-children', + template: ''' +
+ +
+
+
''', + directives: const [ + TextDirective, + ViewChildComponent, + ], +) +class TestsViewChildComponent {} @Component( - selector: "needs-query", - directives: const [NgFor, TextDirective], - template: - "
{{dir.text}}|") -@Injectable() -class NeedsQuery { - @ContentChildren(TextDirective) - QueryList query; + selector: 'tests-embedded-content-children', + template: ''' + + +''', + directives: const [ + ContentChildrenComponent, + NgIf, + TextDirective, + ], +) +class TestsEmbeddedContentChildrenComponent { + bool showContent = false; } -@Component(selector: "needs-four-queries", template: "") -class NeedsFourQueries { - @ContentChild(TextDirective) - TextDirective query1; - @ContentChild(TextDirective) - TextDirective query2; - @ContentChild(TextDirective) - TextDirective query3; - @ContentChild(TextDirective) - TextDirective query4; +@Component( + selector: 'tests-embedded-content-children', + template: ''' + + +''', + directives: const [ + ContentChildrenDescendantsComponent, + NgIf, + TextDirective, + ], +) +class TestsEmbeddedContentChildrenDescendantsComponent { + bool showContent = false; } @Component( - selector: "needs-query-desc", - directives: const [NgFor], - template: "
{{dir.text}}|
") -@Injectable() -class NeedsQueryDesc { - @ContentChildren(TextDirective, descendants: true) - QueryList query; + selector: 'tests-embedded-content-children', + template: ''' + +
+
+
+
''', + directives: const [ + ContentChildComponent, + NgIf, + TextDirective, + ], +) +class TestsEmbeddedContentChildComponent { + bool showContent = false; } @Component( - selector: "needs-query-by-ref-binding", - directives: const [], - template: "") -@Injectable() -class NeedsQueryByLabel { - @ContentChildren("textLabel", descendants: true) - QueryList query; + selector: 'embedded-view-children', + template: ''' + +
{{text}}
''', + directives: const [ + NgIf, + TextDirective, + ], +) +class TestsEmbeddedViewChildrenComponent extends TextDirectivesRenderer { + bool showView = false; + + @ViewChildren(TextDirective) + QueryList textDirectives; } @Component( - selector: "needs-view-query-by-ref-binding", - directives: const [], - template: "
text
") -@Injectable() -class NeedsViewQueryByLabel { - @ViewChildren("textLabel") - QueryList query; + selector: 'embedded-view-child', + template: ''' +
+
+
+
+
{{textDirective?.text}}
''', + directives: const [ + NgIf, + TextDirective, + ], +) +class TestsEmbeddedViewChildComponent { + bool showView = false; + + @ViewChild(TextDirective) + TextDirective textDirective; } @Component( - selector: "needs-query-by-ref-bindings", - directives: const [], - template: "") -@Injectable() -class NeedsQueryByTwoLabels { - @ContentChildren("textLabel1,textLabel2", descendants: true) - QueryList query; + selector: 'moves-directive', + template: ''' + +
+
''', + directives: const [ + ContentChildrenComponent, + NgFor, + TextDirective, + ], +) +class MovesDirectiveComponent { + List list = ['1', '2', '3']; } @Component( - selector: "needs-query-and-project", - directives: const [NgFor], - template: - "
{{dir.text}}|
") -@Injectable() -class NeedsQueryAndProject { + selector: 'transcluded-content-children', + template: '
{{text}}
', +) +class TranscludedContentChildrenComponent extends TextDirectivesRenderer { @ContentChildren(TextDirective) - QueryList query; + QueryList textDirectives; } @Component( - selector: "needs-view-query", - directives: const [TextDirective], - template: "
" + - "
") -@Injectable() -class NeedsViewQuery { + selector: 'tests-transcluded-content-children', + template: ''' + +
+
+
''', + directives: const [ + TextDirective, + TranscludedContentChildrenComponent, + ], +) +class TestsTranscludedContentChildrenComponent {} + +@Directive(selector: '[inert]') +class InertDirective {} + +@Component( + selector: 'unrelated-changes', + template: ''' +
+
+
{{text}}
+ ''', + directives: const [ + InertDirective, + NgIf, + TextDirective, + ]) +class UnrelatedChangesComponent extends TextDirectivesRenderer { + bool showInertDirective = true; + @ViewChildren(TextDirective) - QueryList query; + QueryList textDirectives; } @Component( - selector: "needs-view-query-if", - directives: const [NgIf, TextDirective], - template: "
") -@Injectable() -class NeedsViewQueryIf { - bool show; + selector: 'long-ng-for-cycle', + template: ''' +
+
{{text}}
+''', + directives: const [ + NgFor, + TextDirective, + ], +) +class LongNgForCycleComponent extends TextDirectivesRenderer { + List list = []; + @ViewChildren(TextDirective) - QueryList query; - NeedsViewQueryIf() { - this.show = false; - } + QueryList textDirectives; } @Component( - selector: "needs-view-query-nested-if", - directives: const [NgIf, InertDirective, TextDirective], - template: "
") -@Injectable() -class NeedsViewQueryNestedIf { - bool show; - @ViewChildren(TextDirective) - QueryList query; - NeedsViewQueryNestedIf() { - this.show = true; - } + selector: 'four-queries', + template: ''' +
+
{{q1.text}}|{{q2.text}}|{{q3.text}}|{{q4.text}}
''', + directives: const [ + TextDirective, + ], +) +class FourQueriesComponent { + @ViewChild(TextDirective) + TextDirective q1; + + @ViewChild(TextDirective) + TextDirective q2; + + @ViewChild(TextDirective) + TextDirective q3; + + @ViewChild(TextDirective) + TextDirective q4; } @Component( - selector: "needs-view-query-order", - directives: const [NgFor, TextDirective, InertDirective], - template: "
" + - "
" + - "
") -@Injectable() -class NeedsViewQueryOrder { - @ViewChildren(TextDirective) - QueryList query; - List list; - NeedsViewQueryOrder() { - this.list = ["2", "3"]; + selector: 'template-ref', + template: ''' + + +''', +) +class TemplateRefComponent implements AfterViewInit { + final ViewContainerRef viewContainerRef; + + @ContentChildren(TemplateRef) + QueryList contentTemplateRefs; + + @ViewChildren(TemplateRef) + QueryList viewTemplateRefs; + + TemplateRefComponent(this.viewContainerRef); + + @override + ngAfterViewInit() { + createEmbeddedViewsFrom(contentTemplateRefs); + createEmbeddedViewsFrom(viewTemplateRefs); + } + + void createEmbeddedViewsFrom(Iterable templateRefs) { + for (var templateRef in templateRefs) { + viewContainerRef.createEmbeddedView(templateRef); + } } } @Component( - selector: "needs-view-query-order-with-p", - directives: const [NgFor, TextDirective, InertDirective], - template: "
" + - "
" + - "
") -@Injectable() -class NeedsViewQueryOrderWithParent { - @ViewChildren(TextDirective) - QueryList query; - List list; - NeedsViewQueryOrderWithParent() { - this.list = ["2", "3"]; + selector: 'tests-template-ref', + template: ''' + + + +''', + directives: const [ + TemplateRefComponent, + ], +) +class TestsTemplateRefComponent {} + +@Component( + selector: 'named-template-ref', + template: ''' + +''', +) +class NamedTemplateRefComponent implements AfterViewInit { + final ViewContainerRef viewContainerRef; + + @ContentChild('templateName') + TemplateRef contentTemplateRef; + + @ViewChild('templateName') + TemplateRef viewTemplateRef; + + NamedTemplateRefComponent(this.viewContainerRef); + + @override + ngAfterViewInit() { + viewContainerRef.createEmbeddedView(contentTemplateRef); + viewContainerRef.createEmbeddedView(viewTemplateRef); } } @Component( - selector: "needs-tpl", template: "") -class NeedsTpl { - ViewContainerRef vc; - @ViewChildren(TemplateRef) - QueryList viewQuery; - @ContentChildren(TemplateRef) - QueryList query; - NeedsTpl(this.vc); -} + selector: 'tests-named-template-ref', + template: ''' + + +''', + directives: const [ + NamedTemplateRefComponent, + ], +) +class TestsNamedTemplateRefComponent {} @Component( - selector: "needs-named-tpl", - template: "") -class NeedsNamedTpl { - ViewContainerRef vc; - @ViewChild("tpl") - TemplateRef viewTpl; - @ContentChild("tpl") - TemplateRef contentTpl; - NeedsNamedTpl(this.vc); + selector: 'reads-content-children', + template: '
{{text}}
', +) +class ReadsContentChildrenComponent extends TextDirectivesRenderer { + @ContentChildren('hasText', read: TextDirective) + QueryList textDirectives; } -@Component(selector: "needs-content-children-read", template: "") -class NeedsContentChildrenWithRead { - @ContentChildren("q", read: TextDirective) - QueryList textDirChildren; - @ContentChildren("nonExisting", read: TextDirective) - QueryList nonExistingVar; -} +@Component( + selector: 'tests-reads-content-children', + template: ''' + +
+
+
''', + directives: const [ + ReadsContentChildrenComponent, + TextDirective, + ], +) +class TestsReadsContentChildrenComponent {} -@Component(selector: "needs-content-child-read", template: "") -class NeedsContentChildWithRead { - @ContentChild("q", read: TextDirective) - TextDirective textDirChild; - @ContentChild("nonExisting", read: TextDirective) - TextDirective nonExistingVar; +@Component( + selector: 'reads-content-child', + template: '
{{textDirective.text}}
', +) +class ReadsContentChildComponent { + @ContentChild('hasText', read: TextDirective) + TextDirective textDirective; } @Component( - selector: "needs-view-children-read", - template: "
", - directives: const [TextDirective]) -class NeedsViewChildrenWithRead { - @ViewChildren("q", read: TextDirective) - QueryList textDirChildren; - @ViewChildren("nonExisting", read: TextDirective) - QueryList nonExistingVar; + selector: 'tests-reads-content-child', + template: ''' + +
+
+
+''', + directives: const [ + ReadsContentChildComponent, + TextDirective, + ], +) +class TestsReadsContentChildComponent {} + +@Component( + selector: 'reads-view-children', + template: ''' +
+
+
{{text}}
+
''', + directives: const [ + TextDirective, + ], +) +class ReadsViewChildrenComponent extends TextDirectivesRenderer { + @ViewChildren('hasText', read: TextDirective) + QueryList textDirectives; } @Component( - selector: "needs-view-child-read", - template: "
", - directives: const [TextDirective]) -class NeedsViewChildWithRead { - @ViewChild("q", read: TextDirective) - TextDirective textDirChild; - @ViewChild("nonExisting", read: TextDirective) - TextDirective nonExistingVar; + selector: 'reads-view-child', + template: ''' +
+
+
{{textDirective.text}}
+
''', + directives: const [ + TextDirective, + ], +) +class ReadsViewChildComponent { + @ViewChild('hasText', read: TextDirective) + TextDirective textDirective; } -@Component(selector: "needs-viewcontainer-read", template: "
") -class NeedsViewContainerWithRead { - @ViewChild("q", read: ViewContainerRef) - ViewContainerRef vc; - @ViewChild("nonExisting", read: ViewContainerRef) - ViewContainerRef nonExistingVar; +@Component( + selector: 'reads-view-container-ref', + template: '
', +) +class ReadsViewContainerRefComponent implements AfterViewInit { @ContentChild(TemplateRef) - TemplateRef template; - createView() { - this.vc.createEmbeddedView(this.template); + TemplateRef templateRef; + + @ViewChild('hasViewContainerRef', read: ViewContainerRef) + ViewContainerRef viewContainerRef; + + @override + ngAfterViewInit() { + viewContainerRef.createEmbeddedView(templateRef); } } @Component( - selector: "my-comp", - directives: const [ - 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: "") -@Injectable() -class MyComp { - bool shouldShow; - var list; - MyComp() { - this.shouldShow = false; - this.list = ["1d", "2d", "3d"]; - } + selector: 'tests-reads-view-container-ref', + template: ''' + + +''', + directives: const [ + ReadsViewContainerRefComponent, + ], +) +class TestsReadsViewContainerRefComponent {} + +@Component( + selector: 'changes-view-children', + template: ''' +
+
+
+
+
{{text}}
+''', + directives: const [ + TextDirective, + ], +) +class ChangesViewChildrenComponent extends TextDirectivesRenderer { + String x = '1'; + String y = '2'; + String z = '3'; + + @ViewChildren(TextDirective) + QueryList textDirectives; +} + +@Component( + selector: 'destroys-view-children', + template: ''' +''', + directives: const [ + NgIf, + TextDirective, + ], +) +class DestroysViewChildrenComponent { + bool showView = true; + + @ViewChildren(TextDirective) + QueryList textDirectives; +} + +@Component( + selector: 'labeled-view-children', + template: ''' +
+
+
{{text}}
''', + directives: const [ + NgFor, + TextDirective, + ], +) +class LabeledViewChildrenComponent extends TextDirectivesRenderer { + List list = ['1', '2', '4', '8']; + + @ViewChildren('textLabel') + QueryList textDirectives; +} + +@Component( + selector: 'multiple-labeled-view-children', + template: ''' +
+
+
{{text}}
''', + directives: const [ + NgFor, + TextDirective, + ], +) +class MultipleLabeledViewChildrenComponent extends TextDirectivesRenderer { + @ViewChildren('textLabel1,textLabel2') + QueryList textDirectives; +} + +@Component( + selector: 'labeled-element-view-children', + template: ''' +
+
{{item}}
+
''', + directives: const [ + NgFor, + ], +) +class LabeledElementViewChildrenComponent { + List list = ['3', '1', '4']; + + @ViewChildren('divLabel') + QueryList elementRefs; }