diff --git a/packages/core/test/acceptance/inherit_definition_feature_spec.ts b/packages/core/test/acceptance/inherit_definition_feature_spec.ts
index cc053dea37..353588802a 100644
--- a/packages/core/test/acceptance/inherit_definition_feature_spec.ts
+++ b/packages/core/test/acceptance/inherit_definition_feature_spec.ts
@@ -6,155 +6,4445 @@
* found in the LICENSE file at https://angular.io/license
*/
-import {Component, Directive, Input, OnChanges, Type} from '@angular/core';
+import {Component, ContentChildren, Directive, EventEmitter, HostBinding, Input, OnChanges, Output, QueryList, ViewChildren} from '@angular/core';
import {TestBed} from '@angular/core/testing';
+import {By} from '@angular/platform-browser';
+import {onlyInIvy} from '@angular/private/testing';
-describe('ngOnChanges', () => {
- it('should be inherited when super is a directive', () => {
- const log: string[] = [];
+describe('inheritance', () => {
+ onlyInIvy('View Engine does not provide this check')
+ .it('should throw when trying to inherit a component from a directive', () => {
+ @Component({
+ selector: 'my-comp',
+ template: '
',
+ })
+ class MyComponent {
+ }
- @Directive({selector: '[superDir]'})
- class SuperDirective implements OnChanges {
- @Input() someInput = '';
+ @Directive({
+ selector: '[my-dir]',
+ })
+ class MyDirective extends MyComponent {
+ }
- ngOnChanges() { log.push('on changes!'); }
+ @Component({
+ template: ``,
+ })
+ class App {
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, MyComponent, MyDirective],
+ });
+
+ expect(() => {
+ TestBed.createComponent(App);
+ }).toThrowError('Directives cannot inherit Components');
+ });
+
+ describe('ngOnChanges', () => {
+ @Component({selector: 'app-comp', template: ``})
+ class AppComp {
}
- @Directive({selector: '[subDir]'})
- class SubDirective extends SuperDirective {
- }
+ it('should be inherited when super is a directive', () => {
+ const log: string[] = [];
- TestBed.configureTestingModule({declarations: [AppComp, SubDirective]});
- TestBed.overrideComponent(
- AppComp, {set: new Component({template: ''})});
- const fixture = TestBed.createComponent(AppComp);
- fixture.detectChanges();
+ @Directive({selector: '[superDir]'})
+ class SuperDirective implements OnChanges {
+ @Input() someInput = '';
- expect(log).toEqual(['on changes!']);
+ ngOnChanges() { log.push('on changes!'); }
+ }
+
+ @Directive({selector: '[subDir]'})
+ class SubDirective extends SuperDirective {
+ }
+
+ TestBed.configureTestingModule({declarations: [AppComp, SubDirective]});
+ TestBed.overrideComponent(
+ AppComp, {set: new Component({template: ''})});
+ const fixture = TestBed.createComponent(AppComp);
+ fixture.detectChanges();
+
+ expect(log).toEqual(['on changes!']);
+ });
+
+ it('should be inherited when super is a simple class', () => {
+ const log: string[] = [];
+
+ class SuperClass {
+ ngOnChanges() { log.push('on changes!'); }
+ }
+
+ @Directive({selector: '[subDir]'})
+ class SubDirective extends SuperClass {
+ @Input() someInput = '';
+ }
+
+ TestBed.configureTestingModule({declarations: [AppComp, SubDirective]});
+ TestBed.overrideComponent(
+ AppComp, {set: new Component({template: ''})});
+ const fixture = TestBed.createComponent(AppComp);
+ fixture.detectChanges();
+
+ expect(log).toEqual(['on changes!']);
+ });
+
+ it('should be inherited when super is a directive and grand-super is a directive', () => {
+ const log: string[] = [];
+
+ @Directive({selector: '[grandSuperDir]'})
+ class GrandSuperDirective implements OnChanges {
+ @Input() someInput = '';
+
+ ngOnChanges() { log.push('on changes!'); }
+ }
+
+ @Directive({selector: '[superDir]'})
+ class SuperDirective extends GrandSuperDirective {
+ }
+
+ @Directive({selector: '[subDir]'})
+ class SubDirective extends SuperDirective {
+ }
+
+ TestBed.configureTestingModule({declarations: [AppComp, SubDirective]});
+ TestBed.overrideComponent(
+ AppComp, {set: new Component({template: ''})});
+ const fixture = TestBed.createComponent(AppComp);
+ fixture.detectChanges();
+
+ expect(log).toEqual(['on changes!']);
+ });
+
+ it('should be inherited when super is a directive and grand-super is a simple class', () => {
+ const log: string[] = [];
+
+ class GrandSuperClass {
+ ngOnChanges() { log.push('on changes!'); }
+ }
+
+ @Directive({selector: '[superDir]'})
+ class SuperDirective extends GrandSuperClass {
+ @Input() someInput = '';
+ }
+
+ @Directive({selector: '[subDir]'})
+ class SubDirective extends SuperDirective {
+ }
+
+ TestBed.configureTestingModule({declarations: [AppComp, SubDirective]});
+ TestBed.overrideComponent(
+ AppComp, {set: new Component({template: ''})});
+ const fixture = TestBed.createComponent(AppComp);
+ fixture.detectChanges();
+
+ expect(log).toEqual(['on changes!']);
+ });
+
+ it('should be inherited when super is a simple class and grand-super is a directive', () => {
+ const log: string[] = [];
+
+ @Directive({selector: '[grandSuperDir]'})
+ class GrandSuperDirective implements OnChanges {
+ @Input() someInput = '';
+
+ ngOnChanges() { log.push('on changes!'); }
+ }
+
+ class SuperClass extends GrandSuperDirective {}
+
+ @Directive({selector: '[subDir]'})
+ class SubDirective extends SuperClass {
+ }
+
+ TestBed.configureTestingModule({declarations: [AppComp, SubDirective]});
+ TestBed.overrideComponent(
+ AppComp, {set: new Component({template: ''})});
+ const fixture = TestBed.createComponent(AppComp);
+ fixture.detectChanges();
+
+ expect(log).toEqual(['on changes!']);
+ });
+
+ it('should be inherited when super is a simple class and grand-super is a simple class', () => {
+ const log: string[] = [];
+
+ class GrandSuperClass {
+ ngOnChanges() { log.push('on changes!'); }
+ }
+
+ class SuperClass extends GrandSuperClass {}
+
+ @Directive({selector: '[subDir]'})
+ class SubDirective extends SuperClass {
+ @Input() someInput = '';
+ }
+
+ TestBed.configureTestingModule({declarations: [AppComp, SubDirective]});
+ TestBed.overrideComponent(
+ AppComp, {set: new Component({template: ''})});
+ const fixture = TestBed.createComponent(AppComp);
+ fixture.detectChanges();
+
+ expect(log).toEqual(['on changes!']);
+ });
});
- it('should be inherited when super is a simple class', () => {
- const log: string[] = [];
+ describe('of bare super class by a directive', () => {
+ describe('lifecycle hooks', () => {
+ const fired: string[] = [];
- class SuperClass {
- ngOnChanges() { log.push('on changes!'); }
- }
+ class SuperDirective {
+ ngOnInit() { fired.push('super init'); }
+ ngOnDestroy() { fired.push('super destroy'); }
+ ngAfterContentInit() { fired.push('super after content init'); }
+ ngAfterContentChecked() { fired.push('super after content checked'); }
+ ngAfterViewInit() { fired.push('super after view init'); }
+ ngAfterViewChecked() { fired.push('super after view checked'); }
+ ngDoCheck() { fired.push('super do check'); }
+ }
- @Directive({selector: '[subDir]'})
- class SubDirective extends SuperClass {
- @Input() someInput = '';
- }
+ beforeEach(() => fired.length = 0);
- TestBed.configureTestingModule({declarations: [AppComp, SubDirective]});
- TestBed.overrideComponent(
- AppComp, {set: new Component({template: ''})});
- const fixture = TestBed.createComponent(AppComp);
- fixture.detectChanges();
+ it('ngOnInit', () => {
+ @Directive({
+ selector: '[subDir]',
+ })
+ class SubDirective extends SuperDirective {
+ ngOnInit() { fired.push('sub init'); }
+ }
- expect(log).toEqual(['on changes!']);
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [SubDirective, App],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'sub init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngDoCheck', () => {
+ @Directive({
+ selector: '[subDir]',
+ })
+ class SubDirective extends SuperDirective {
+ ngDoCheck() { fired.push('sub do check'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [SubDirective, App],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'sub do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterContentInit', () => {
+ @Directive({
+ selector: '[subDir]',
+ })
+ class SubDirective extends SuperDirective {
+ ngAfterContentInit() { fired.push('sub after content init'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [SubDirective, App],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'sub after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterContentChecked', () => {
+ @Directive({
+ selector: '[subDir]',
+ })
+ class SubDirective extends SuperDirective {
+ ngAfterContentChecked() { fired.push('sub after content checked'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [SubDirective, App],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'sub after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterViewInit', () => {
+ @Directive({
+ selector: '[subDir]',
+ })
+ class SubDirective extends SuperDirective {
+ ngAfterViewInit() { fired.push('sub after view init'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [SubDirective, App],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'sub after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterViewChecked', () => {
+ @Directive({
+ selector: '[subDir]',
+ })
+ class SubDirective extends SuperDirective {
+ ngAfterViewChecked() { fired.push('sub after view checked'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [SubDirective, App],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'sub after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngOnDestroy', () => {
+ @Directive({
+ selector: '[subDir]',
+ })
+ class SubDirective extends SuperDirective {
+ ngOnDestroy() { fired.push('sub destroy'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [SubDirective, App],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'sub destroy',
+ ]);
+ });
+ });
+
+ it('should inherit inputs', () => {
+ class SuperDirective {
+ @Input()
+ foo = '';
+
+ @Input()
+ bar = '';
+
+ @Input()
+ baz = '';
+ }
+
+ @Directive({
+ selector: '[sub-dir]',
+ })
+ class SubDirective extends SuperDirective {
+ @Input()
+ baz = '';
+
+ @Input()
+ qux = '';
+ }
+
+ @Component({template: ``})
+ class App {
+ a = 'a';
+ b = 'b';
+ c = 'c';
+ d = 'd';
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, SubDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ const subDir =
+ fixture.debugElement.query(By.directive(SubDirective)).injector.get(SubDirective);
+
+ expect(subDir.foo).toBe('a');
+ expect(subDir.bar).toBe('b');
+ expect(subDir.baz).toBe('c');
+ expect(subDir.qux).toBe('d');
+ });
+
+
+ it('should inherit outputs', () => {
+ class SuperDirective {
+ @Output()
+ foo = new EventEmitter();
+ }
+
+ @Directive({
+ selector: '[sub-dir]',
+ })
+ class SubDirective extends SuperDirective {
+ ngOnInit() { this.foo.emit('test'); }
+ }
+
+ @Component({
+ template: `
+
+ `
+ })
+ class App {
+ foo = '';
+
+ handleFoo(event: string) { this.foo = event; }
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, SubDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+ const app = fixture.componentInstance;
+
+ expect(app.foo).toBe('test');
+ });
+
+ it('should compose host bindings for styles', () => {
+ class SuperDirective {
+ @HostBinding('style.color')
+ color = 'red';
+
+ @HostBinding('style.backgroundColor')
+ bg = 'black';
+ }
+
+ @Directive({
+ selector: '[sub-dir]',
+ })
+ class SubDirective extends SuperDirective {
+ }
+
+ @Component({
+ template: `
+ test
+ `
+ })
+ class App {
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, SubDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+ const queryResult = fixture.debugElement.query(By.directive(SubDirective));
+
+ expect(queryResult.nativeElement.tagName).toBe('P');
+ expect(queryResult.nativeElement.style.color).toBe('red');
+ expect(queryResult.nativeElement.style.backgroundColor).toBe('black');
+ });
+
+ it('should compose host bindings (non-style related)', () => {
+ class SuperDirective {
+ @HostBinding('title')
+ get boundTitle() { return this.superTitle + '!!!'; }
+
+ @Input()
+ superTitle = '';
+ }
+
+ @Directive({
+ selector: '[sub-dir]',
+ })
+ class SubDirective extends SuperDirective {
+ }
+ @Component({
+ template: `
+ test
+ `
+ })
+ class App {
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, SubDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+ const queryResult = fixture.debugElement.query(By.directive(SubDirective));
+
+ expect(queryResult.nativeElement.title).toBe('test!!!');
+ });
+
+ it('should inherit ContentChildren queries', () => {
+ let foundQueryList: QueryList;
+
+ @Directive({selector: '[child-dir]'})
+ class ChildDir {
+ }
+
+ class SuperDirective {
+ // TODO(issue/24571): remove '!'.
+ @ContentChildren(ChildDir)
+ customDirs !: QueryList;
+ }
+
+ @Directive({
+ selector: '[sub-dir]',
+ })
+ class SubDirective extends SuperDirective {
+ ngAfterViewInit() { foundQueryList = this.customDirs; }
+ }
+
+ @Component({
+ template: `
+
+ `
+ })
+ class App {
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, SubDirective, ChildDir],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(foundQueryList !.length).toBe(2);
+ });
});
- it('should be inherited when super is a directive and grand-super is a directive', () => {
- const log: string[] = [];
+ describe('of a directive by another directive', () => {
+ describe('lifecycle hooks', () => {
+ const fired: string[] = [];
- @Directive({selector: '[grandSuperDir]'})
- class GrandSuperDirective implements OnChanges {
- @Input() someInput = '';
+ @Directive({
+ selector: '[super-dir]',
+ })
+ class SuperDirective {
+ ngOnInit() { fired.push('super init'); }
+ ngOnDestroy() { fired.push('super destroy'); }
+ ngAfterContentInit() { fired.push('super after content init'); }
+ ngAfterContentChecked() { fired.push('super after content checked'); }
+ ngAfterViewInit() { fired.push('super after view init'); }
+ ngAfterViewChecked() { fired.push('super after view checked'); }
+ ngDoCheck() { fired.push('super do check'); }
+ }
- ngOnChanges() { log.push('on changes!'); }
- }
+ beforeEach(() => fired.length = 0);
- @Directive({selector: '[superDir]'})
- class SuperDirective extends GrandSuperDirective {
- }
+ it('ngOnInit', () => {
+ @Directive({
+ selector: '[subDir]',
+ })
+ class SubDirective extends SuperDirective {
+ ngOnInit() { fired.push('sub init'); }
+ }
- @Directive({selector: '[subDir]'})
- class SubDirective extends SuperDirective {
- }
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
- TestBed.configureTestingModule({declarations: [AppComp, SubDirective]});
- TestBed.overrideComponent(
- AppComp, {set: new Component({template: ''})});
- const fixture = TestBed.createComponent(AppComp);
- fixture.detectChanges();
+ TestBed.configureTestingModule({
+ declarations: [SubDirective, App, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
- expect(log).toEqual(['on changes!']);
+ expect(fired).toEqual([
+ 'sub init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngDoCheck', () => {
+ @Directive({
+ selector: '[subDir]',
+ })
+ class SubDirective extends SuperDirective {
+ ngDoCheck() { fired.push('sub do check'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [SubDirective, App, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'sub do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterContentInit', () => {
+ @Directive({
+ selector: '[subDir]',
+ })
+ class SubDirective extends SuperDirective {
+ ngAfterContentInit() { fired.push('sub after content init'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [SubDirective, App, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'sub after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterContentChecked', () => {
+ @Directive({
+ selector: '[subDir]',
+ })
+ class SubDirective extends SuperDirective {
+ ngAfterContentChecked() { fired.push('sub after content checked'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [SubDirective, App, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'sub after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterViewInit', () => {
+ @Directive({
+ selector: '[subDir]',
+ })
+ class SubDirective extends SuperDirective {
+ ngAfterViewInit() { fired.push('sub after view init'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [SubDirective, App, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'sub after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterViewChecked', () => {
+ @Directive({
+ selector: '[subDir]',
+ })
+ class SubDirective extends SuperDirective {
+ ngAfterViewChecked() { fired.push('sub after view checked'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [SubDirective, App, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'sub after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngOnDestroy', () => {
+ @Directive({
+ selector: '[subDir]',
+ })
+ class SubDirective extends SuperDirective {
+ ngOnDestroy() { fired.push('sub destroy'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [SubDirective, App, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'sub destroy',
+ ]);
+ });
+ });
+
+ it('should inherit inputs', () => {
+ @Directive({selector: '[super-dir]'})
+ class SuperDirective {
+ @Input()
+ foo = '';
+
+ @Input()
+ bar = '';
+
+ @Input()
+ baz = '';
+ }
+
+ @Directive({
+ selector: '[sub-dir]',
+ })
+ class SubDirective extends SuperDirective {
+ @Input()
+ baz = '';
+
+ @Input()
+ qux = '';
+ }
+
+ @Component({template: ``})
+ class App {
+ a = 'a';
+ b = 'b';
+ c = 'c';
+ d = 'd';
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, SubDirective, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ const subDir =
+ fixture.debugElement.query(By.directive(SubDirective)).injector.get(SubDirective);
+
+ expect(subDir.foo).toBe('a');
+ expect(subDir.bar).toBe('b');
+ expect(subDir.baz).toBe('c');
+ expect(subDir.qux).toBe('d');
+ });
+
+
+ it('should inherit outputs', () => {
+ @Directive({
+ selector: '[super-dir]',
+ })
+ class SuperDirective {
+ @Output()
+ foo = new EventEmitter();
+ }
+
+ @Directive({
+ selector: '[sub-dir]',
+ })
+ class SubDirective extends SuperDirective {
+ ngOnInit() { this.foo.emit('test'); }
+ }
+
+ @Component({
+ template: `
+
+ `
+ })
+ class App {
+ foo = '';
+
+ handleFoo(event: string) { this.foo = event; }
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, SubDirective, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+ const app = fixture.componentInstance;
+
+ expect(app.foo).toBe('test');
+ });
+
+ it('should compose host bindings for styles', () => {
+ @Directive({
+ selector: '[super-dir]',
+ })
+ class SuperDirective {
+ @HostBinding('style.color')
+ color = 'red';
+
+ @HostBinding('style.backgroundColor')
+ bg = 'black';
+ }
+
+ @Directive({
+ selector: '[sub-dir]',
+ })
+ class SubDirective extends SuperDirective {
+ }
+
+ @Component({
+ template: `
+ test
+ `
+ })
+ class App {
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, SubDirective, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+ const queryResult = fixture.debugElement.query(By.directive(SubDirective));
+
+ expect(queryResult.nativeElement.tagName).toBe('P');
+ expect(queryResult.nativeElement.style.color).toBe('red');
+ expect(queryResult.nativeElement.style.backgroundColor).toBe('black');
+ });
+
+ it('should compose host bindings (non-style related)', () => {
+ @Directive({
+ selector: '[super-dir]',
+ })
+ class SuperDirective {
+ @HostBinding('title')
+ get boundTitle() { return this.superTitle + '!!!'; }
+
+ @Input()
+ superTitle = '';
+ }
+
+ @Directive({
+ selector: '[sub-dir]',
+ })
+ class SubDirective extends SuperDirective {
+ }
+ @Component({
+ template: `
+ test
+ `
+ })
+ class App {
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, SubDirective, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+ const queryResult = fixture.debugElement.query(By.directive(SubDirective));
+
+ expect(queryResult.nativeElement.title).toBe('test!!!');
+ });
+
+ it('should inherit ContentChildren queries', () => {
+ let foundQueryList: QueryList;
+
+ @Directive({selector: '[child-dir]'})
+ class ChildDir {
+ }
+
+ @Directive({
+ selector: '[super-dir]',
+ })
+ class SuperDirective {
+ // TODO(issue/24571): remove '!'.
+ @ContentChildren(ChildDir)
+ customDirs !: QueryList;
+ }
+
+ @Directive({
+ selector: '[sub-dir]',
+ })
+ class SubDirective extends SuperDirective {
+ ngAfterViewInit() { foundQueryList = this.customDirs; }
+ }
+
+ @Component({
+ template: `
+
+ `
+ })
+ class App {
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, SubDirective, ChildDir, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(foundQueryList !.length).toBe(2);
+ });
});
- it('should be inherited when super is a directive and grand-super is a simple class', () => {
- const log: string[] = [];
+ describe('of a directive by a bare class then by another directive', () => {
+ describe('lifecycle hooks', () => {
+ const fired: string[] = [];
- class GrandSuperClass {
- ngOnChanges() { log.push('on changes!'); }
- }
+ @Directive({
+ selector: '[super-dir]',
+ })
+ class SuperSuperDirective {
+ ngOnInit() { fired.push('super init'); }
+ ngOnDestroy() { fired.push('super destroy'); }
+ ngAfterContentInit() { fired.push('super after content init'); }
+ ngAfterContentChecked() { fired.push('super after content checked'); }
+ ngAfterViewInit() { fired.push('super after view init'); }
+ ngAfterViewChecked() { fired.push('super after view checked'); }
+ ngDoCheck() { fired.push('super do check'); }
+ }
- @Directive({selector: '[superDir]'})
- class SuperDirective extends GrandSuperClass {
- @Input() someInput = '';
- }
+ class SuperDirective extends SuperSuperDirective {}
- @Directive({selector: '[subDir]'})
- class SubDirective extends SuperDirective {
- }
+ beforeEach(() => fired.length = 0);
- TestBed.configureTestingModule({declarations: [AppComp, SubDirective]});
- TestBed.overrideComponent(
- AppComp, {set: new Component({template: ''})});
- const fixture = TestBed.createComponent(AppComp);
- fixture.detectChanges();
+ it('ngOnInit', () => {
+ @Directive({
+ selector: '[subDir]',
+ })
+ class SubDirective extends SuperDirective {
+ ngOnInit() { fired.push('sub init'); }
+ }
- expect(log).toEqual(['on changes!']);
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [SubDirective, App, SuperSuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'sub init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngDoCheck', () => {
+ @Directive({
+ selector: '[subDir]',
+ })
+ class SubDirective extends SuperDirective {
+ ngDoCheck() { fired.push('sub do check'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [SubDirective, App, SuperSuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'sub do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterContentInit', () => {
+ @Directive({
+ selector: '[subDir]',
+ })
+ class SubDirective extends SuperDirective {
+ ngAfterContentInit() { fired.push('sub after content init'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [SubDirective, App, SuperSuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'sub after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterContentChecked', () => {
+ @Directive({
+ selector: '[subDir]',
+ })
+ class SubDirective extends SuperDirective {
+ ngAfterContentChecked() { fired.push('sub after content checked'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [SubDirective, App, SuperSuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'sub after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterViewInit', () => {
+ @Directive({
+ selector: '[subDir]',
+ })
+ class SubDirective extends SuperDirective {
+ ngAfterViewInit() { fired.push('sub after view init'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [SubDirective, App, SuperSuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'sub after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterViewChecked', () => {
+ @Directive({
+ selector: '[subDir]',
+ })
+ class SubDirective extends SuperDirective {
+ ngAfterViewChecked() { fired.push('sub after view checked'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [SubDirective, App, SuperSuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'sub after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngOnDestroy', () => {
+ @Directive({
+ selector: '[subDir]',
+ })
+ class SubDirective extends SuperDirective {
+ ngOnDestroy() { fired.push('sub destroy'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [SubDirective, App, SuperSuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'sub destroy',
+ ]);
+ });
+ });
+
+ it('should inherit inputs', () => {
+ @Directive({selector: '[super-dir]'})
+ class SuperSuperDirective {
+ @Input()
+ foo = '';
+
+ @Input()
+ baz = '';
+ }
+
+ class SuperDirective extends SuperSuperDirective {
+ @Input()
+ bar = '';
+ }
+
+ @Directive({
+ selector: '[sub-dir]',
+ })
+ class SubDirective extends SuperDirective {
+ @Input()
+ baz = '';
+
+ @Input()
+ qux = '';
+ }
+
+ @Component({
+ selector: 'my-app',
+ template: ``,
+ })
+ class App {
+ a = 'a';
+ b = 'b';
+ c = 'c';
+ d = 'd';
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, SubDirective, SuperDirective, SuperSuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ const subDir =
+ fixture.debugElement.query(By.directive(SubDirective)).injector.get(SubDirective);
+
+ expect(subDir.foo).toBe('a');
+ expect(subDir.bar).toBe('b');
+ expect(subDir.baz).toBe('c');
+ expect(subDir.qux).toBe('d');
+ });
+
+ it('should inherit outputs', () => {
+ @Directive({
+ selector: '[super-dir]',
+ })
+ class SuperSuperDirective {
+ @Output()
+ foo = new EventEmitter();
+ }
+
+ class SuperDirective extends SuperSuperDirective {
+ @Output()
+ bar = new EventEmitter();
+ }
+
+ @Directive({
+ selector: '[sub-dir]',
+ })
+ class SubDirective extends SuperDirective {
+ ngOnInit() {
+ this.foo.emit('test1');
+ this.bar.emit('test2');
+ }
+ }
+
+ @Component({
+ template: `
+
+ `
+ })
+ class App {
+ foo = '';
+
+ bar = '';
+
+ handleFoo(event: string) { this.foo = event; }
+
+ handleBar(event: string) { this.bar = event; }
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, SubDirective, SuperDirective, SuperSuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+ const app = fixture.componentInstance;
+
+ expect(app.foo).toBe('test1');
+ expect(app.bar).toBe('test2');
+ });
+
+ it('should compose host bindings for styles', () => {
+ @Directive({
+ selector: '[super-dir]',
+ })
+ class SuperSuperDirective {
+ @HostBinding('style.color')
+ color = 'red';
+ }
+
+ class SuperDirective extends SuperSuperDirective {
+ @HostBinding('style.backgroundColor')
+ bg = 'black';
+ }
+
+ @Directive({
+ selector: '[sub-dir]',
+ })
+ class SubDirective extends SuperDirective {
+ }
+
+ @Component({
+ template: `
+ test
+ `
+ })
+ class App {
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, SubDirective, SuperDirective, SuperSuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+ const queryResult = fixture.debugElement.query(By.directive(SubDirective));
+
+ expect(queryResult.nativeElement.tagName).toBe('P');
+ expect(queryResult.nativeElement.style.color).toBe('red');
+ expect(queryResult.nativeElement.style.backgroundColor).toBe('black');
+ });
+
+ it('should compose host bindings (non-style related)', () => {
+ @Directive({
+ selector: '[super-dir]',
+ })
+ class SuperSuperDirective {
+ @HostBinding('title')
+ get boundTitle() { return this.superTitle + '!!!'; }
+
+ @Input()
+ superTitle = '';
+ }
+
+ class SuperDirective extends SuperSuperDirective {
+ @HostBinding('accessKey')
+ get boundAltKey() { return this.superAccessKey + '???'; }
+
+ @Input()
+ superAccessKey = '';
+ }
+
+ @Directive({
+ selector: '[sub-dir]',
+ })
+ class SubDirective extends SuperDirective {
+ }
+ @Component({
+ template: `
+ test
+ `
+ })
+ class App {
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, SubDirective, SuperDirective, SuperSuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+ const p: HTMLParagraphElement =
+ fixture.debugElement.query(By.directive(SubDirective)).nativeElement;
+
+ expect(p.title).toBe('test1!!!');
+ expect(p.accessKey).toBe('test2???');
+ });
+
+ it('should inherit ContentChildren queries', () => {
+ let foundChildDir1s: QueryList;
+ let foundChildDir2s: QueryList;
+
+ @Directive({selector: '[child-dir-one]'})
+ class ChildDir1 {
+ }
+
+ @Directive({selector: '[child-dir-two]'})
+ class ChildDir2 {
+ }
+
+ @Directive({
+ selector: '[super-dir]',
+ })
+ class SuperSuperDirective {
+ // TODO(issue/24571): remove '!'.
+ @ContentChildren(ChildDir1)
+ childDir1s !: QueryList;
+ }
+
+ class SuperDirective extends SuperSuperDirective {
+ // TODO(issue/24571): remove '!'.
+ @ContentChildren(ChildDir1)
+ childDir2s !: QueryList;
+ }
+
+ @Directive({
+ selector: '[sub-dir]',
+ })
+ class SubDirective extends SuperDirective {
+ ngAfterViewInit() {
+ foundChildDir1s = this.childDir1s;
+ foundChildDir2s = this.childDir2s;
+ }
+ }
+
+ @Component({
+ template: `
+
+ `
+ })
+ class App {
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, SubDirective, ChildDir1, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(foundChildDir1s !.length).toBe(2);
+ expect(foundChildDir2s !.length).toBe(2);
+ });
});
- it('should be inherited when super is a simple class and grand-super is a directive', () => {
- const log: string[] = [];
+ describe('of bare super class by a component', () => {
+ describe('lifecycle hooks', () => {
+ const fired: string[] = [];
- @Directive({selector: '[grandSuperDir]'})
- class GrandSuperDirective implements OnChanges {
- @Input() someInput = '';
+ class SuperComponent {
+ ngOnInit() { fired.push('super init'); }
+ ngOnDestroy() { fired.push('super destroy'); }
+ ngAfterContentInit() { fired.push('super after content init'); }
+ ngAfterContentChecked() { fired.push('super after content checked'); }
+ ngAfterViewInit() { fired.push('super after view init'); }
+ ngAfterViewChecked() { fired.push('super after view checked'); }
+ ngDoCheck() { fired.push('super do check'); }
+ }
- ngOnChanges() { log.push('on changes!'); }
- }
+ beforeEach(() => fired.length = 0);
- class SuperClass extends GrandSuperDirective {}
+ it('ngOnInit', () => {
+ @Component({selector: 'my-comp', template: `test
`})
+ class MyComponent extends SuperComponent {
+ ngOnInit() { fired.push('sub init'); }
+ }
- @Directive({selector: '[subDir]'})
- class SubDirective extends SuperClass {
- }
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
- TestBed.configureTestingModule({declarations: [AppComp, SubDirective]});
- TestBed.overrideComponent(
- AppComp, {set: new Component({template: ''})});
- const fixture = TestBed.createComponent(AppComp);
- fixture.detectChanges();
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
- expect(log).toEqual(['on changes!']);
+ expect(fired).toEqual([
+ 'sub init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngDoCheck', () => {
+ @Directive({
+ selector: 'my-comp',
+ })
+ class MyComponent extends SuperComponent {
+ ngDoCheck() { fired.push('sub do check'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'sub do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterContentInit', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperComponent {
+ ngAfterContentInit() { fired.push('sub after content init'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'sub after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterContentChecked', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperComponent {
+ ngAfterContentChecked() { fired.push('sub after content checked'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'sub after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterViewInit', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperComponent {
+ ngAfterViewInit() { fired.push('sub after view init'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'sub after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterViewChecked', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperComponent {
+ ngAfterViewChecked() { fired.push('sub after view checked'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'sub after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngOnDestroy', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperComponent {
+ ngOnDestroy() { fired.push('sub destroy'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'sub destroy',
+ ]);
+ });
+ });
+
+ it('should inherit inputs', () => {
+ class SuperComponent {
+ @Input()
+ foo = '';
+
+ @Input()
+ bar = '';
+
+ @Input()
+ baz = '';
+ }
+
+ @Component({selector: 'my-comp', template: `test
`})
+ class MyComponent extends SuperComponent {
+ @Input()
+ baz = '';
+
+ @Input()
+ qux = '';
+ }
+
+ @Component({template: ``})
+ class App {
+ a = 'a';
+ b = 'b';
+ c = 'c';
+ d = 'd';
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, MyComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ const subDir: MyComponent =
+ fixture.debugElement.query(By.directive(MyComponent)).componentInstance;
+
+ expect(subDir.foo).toEqual('a');
+ expect(subDir.bar).toEqual('b');
+ expect(subDir.baz).toEqual('c');
+ expect(subDir.qux).toEqual('d');
+ });
+
+
+ it('should inherit outputs', () => {
+ class SuperComponent {
+ @Output()
+ foo = new EventEmitter();
+ }
+
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperComponent {
+ ngOnInit() { this.foo.emit('test'); }
+ }
+
+ @Component({
+ template: `
+
+ `
+ })
+ class App {
+ foo = '';
+
+ handleFoo(event: string) { this.foo = event; }
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, MyComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+ const app = fixture.componentInstance;
+
+ expect(app.foo).toBe('test');
+ });
+
+ it('should compose host bindings for styles', () => {
+ class SuperComponent {
+ @HostBinding('style.color')
+ color = 'red';
+
+ @HostBinding('style.backgroundColor')
+ bg = 'black';
+ }
+
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperComponent {
+ }
+
+ @Component({
+ template: `
+ test
+ `
+ })
+ class App {
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, MyComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+ const queryResult = fixture.debugElement.query(By.directive(MyComponent));
+
+ expect(queryResult.nativeElement.tagName).toBe('MY-COMP');
+ expect(queryResult.nativeElement.style.color).toBe('red');
+ expect(queryResult.nativeElement.style.backgroundColor).toBe('black');
+ });
+
+ it('should compose host bindings (non-style related)', () => {
+ class SuperComponent {
+ @HostBinding('title')
+ get boundTitle() { return this.superTitle + '!!!'; }
+
+ @Input()
+ superTitle = '';
+ }
+
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperComponent {
+ }
+ @Component({
+ template: `
+ test
+ `
+ })
+ class App {
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, MyComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+ const queryResult = fixture.debugElement.query(By.directive(MyComponent));
+
+ expect(queryResult.nativeElement.title).toBe('test!!!');
+ });
+
+ it('should inherit ContentChildren queries', () => {
+ let foundQueryList: QueryList;
+
+ @Directive({selector: '[child-dir]'})
+ class ChildDir {
+ }
+
+ class SuperComponent {
+ // TODO(issue/24571): remove '!'.
+ @ContentChildren(ChildDir)
+ customDirs !: QueryList;
+ }
+
+ @Component({selector: 'my-comp', template: ``})
+ class MyComponent extends SuperComponent {
+ ngAfterViewInit() { foundQueryList = this.customDirs; }
+ }
+
+ @Component({
+ template: `
+
+ one
+ two
+
+ `
+ })
+ class App {
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, MyComponent, ChildDir],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(foundQueryList !.length).toBe(2);
+ });
});
- it('should be inherited when super is a simple class and grand-super is a simple class', () => {
- const log: string[] = [];
+ describe('of a directive inherited by a component', () => {
+ describe('lifecycle hooks', () => {
+ const fired: string[] = [];
- class GrandSuperClass {
- ngOnChanges() { log.push('on changes!'); }
- }
+ @Directive({
+ selector: '[super-dir]',
+ })
+ class SuperDirective {
+ ngOnInit() { fired.push('super init'); }
+ ngOnDestroy() { fired.push('super destroy'); }
+ ngAfterContentInit() { fired.push('super after content init'); }
+ ngAfterContentChecked() { fired.push('super after content checked'); }
+ ngAfterViewInit() { fired.push('super after view init'); }
+ ngAfterViewChecked() { fired.push('super after view checked'); }
+ ngDoCheck() { fired.push('super do check'); }
+ }
- class SuperClass extends GrandSuperClass {}
+ beforeEach(() => fired.length = 0);
- @Directive({selector: '[subDir]'})
- class SubDirective extends SuperClass {
- @Input() someInput = '';
- }
+ it('ngOnInit', () => {
+ @Component({selector: 'my-comp', template: `test
`})
+ class MyComponent extends SuperDirective {
+ ngOnInit() { fired.push('sub init'); }
+ }
- TestBed.configureTestingModule({declarations: [AppComp, SubDirective]});
- TestBed.overrideComponent(
- AppComp, {set: new Component({template: ''})});
- const fixture = TestBed.createComponent(AppComp);
- fixture.detectChanges();
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
- expect(log).toEqual(['on changes!']);
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'sub init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngDoCheck', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperDirective {
+ ngDoCheck() { fired.push('sub do check'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'sub do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterContentInit', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperDirective {
+ ngAfterContentInit() { fired.push('sub after content init'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'sub after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterContentChecked', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperDirective {
+ ngAfterContentChecked() { fired.push('sub after content checked'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'sub after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterViewInit', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperDirective {
+ ngAfterViewInit() { fired.push('sub after view init'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'sub after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterViewChecked', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperDirective {
+ ngAfterViewChecked() { fired.push('sub after view checked'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'sub after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngOnDestroy', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperDirective {
+ ngOnDestroy() { fired.push('sub destroy'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'sub destroy',
+ ]);
+ });
+ });
+
+ it('should inherit inputs', () => {
+ @Directive({
+ selector: '[super-dir]',
+ })
+ class SuperDirective {
+ @Input()
+ foo = '';
+
+ @Input()
+ bar = '';
+
+ @Input()
+ baz = '';
+ }
+
+ @Component({selector: 'my-comp', template: `test
`})
+ class MyComponent extends SuperDirective {
+ @Input()
+ baz = '';
+
+ @Input()
+ qux = '';
+ }
+
+ @Component({template: ``})
+ class App {
+ a = 'a';
+ b = 'b';
+ c = 'c';
+ d = 'd';
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, MyComponent, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ const subDir: MyComponent =
+ fixture.debugElement.query(By.directive(MyComponent)).componentInstance;
+
+ expect(subDir.foo).toEqual('a');
+ expect(subDir.bar).toEqual('b');
+ expect(subDir.baz).toEqual('c');
+ expect(subDir.qux).toEqual('d');
+ });
+
+ it('should inherit outputs', () => {
+ @Directive({
+ selector: '[super-dir]',
+ })
+ class SuperDirective {
+ @Output()
+ foo = new EventEmitter();
+ }
+
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperDirective {
+ ngOnInit() { this.foo.emit('test'); }
+ }
+
+ @Component({
+ template: `
+
+ `
+ })
+ class App {
+ foo = '';
+
+ handleFoo(event: string) { this.foo = event; }
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, MyComponent, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+ const app = fixture.componentInstance;
+
+ expect(app.foo).toBe('test');
+ });
+
+ it('should compose host bindings for styles', () => {
+ @Directive({
+ selector: '[super-dir]',
+ })
+ class SuperDirective {
+ @HostBinding('style.color')
+ color = 'red';
+
+ @HostBinding('style.backgroundColor')
+ bg = 'black';
+ }
+
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperDirective {
+ }
+
+ @Component({
+ template: `
+ test
+ `
+ })
+ class App {
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, MyComponent, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+ const queryResult = fixture.debugElement.query(By.directive(MyComponent));
+
+ expect(queryResult.nativeElement.tagName).toBe('MY-COMP');
+ expect(queryResult.nativeElement.style.color).toBe('red');
+ expect(queryResult.nativeElement.style.backgroundColor).toBe('black');
+ });
+
+ it('should compose host bindings (non-style related)', () => {
+ @Directive({
+ selector: '[super-dir]',
+ })
+ class SuperDirective {
+ @HostBinding('title')
+ get boundTitle() { return this.superTitle + '!!!'; }
+
+ @Input()
+ superTitle = '';
+ }
+
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperDirective {
+ }
+ @Component({
+ template: `
+ test
+ `
+ })
+ class App {
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, MyComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+ const queryResult = fixture.debugElement.query(By.directive(MyComponent));
+
+ expect(queryResult.nativeElement.title).toBe('test!!!');
+ });
+
+ it('should inherit ContentChildren queries', () => {
+ let foundQueryList: QueryList;
+
+ @Directive({selector: '[child-dir]'})
+ class ChildDir {
+ }
+
+ @Directive({
+ selector: '[super-dir]',
+ })
+ class SuperDirective {
+ // TODO(issue/24571): remove '!'.
+ @ContentChildren(ChildDir)
+ customDirs !: QueryList;
+ }
+
+ @Component({selector: 'my-comp', template: ``})
+ class MyComponent extends SuperDirective {
+ ngAfterViewInit() { foundQueryList = this.customDirs; }
+ }
+
+ @Component({
+ template: `
+
+ one
+ two
+
+ `
+ })
+ class App {
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, MyComponent, SuperDirective, ChildDir],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(foundQueryList !.length).toBe(2);
+ });
+
+ it('should inherit ViewChildren queries', () => {
+ let foundQueryList: QueryList;
+
+ @Directive({selector: '[child-dir]'})
+ class ChildDir {
+ }
+
+ @Directive({
+ selector: '[super-dir]',
+ })
+ class SuperDirective {
+ // TODO(issue/24571): remove '!'.
+ @ViewChildren(ChildDir)
+ customDirs !: QueryList;
+ }
+
+ @Component({
+ selector: 'my-comp',
+ template: `
+
+ `,
+ })
+ class MyComponent extends SuperDirective {
+ items = [1, 2, 3, 4, 5];
+ ngAfterViewInit() { foundQueryList = this.customDirs; }
+ }
+
+ @Component({
+ template: `
+
+ `
+ })
+ class App {
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, MyComponent, ChildDir, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(foundQueryList !.length).toBe(5);
+ });
+ });
+
+ describe('of a directive inherited by a bare class and then by a component', () => {
+ describe('lifecycle hooks', () => {
+ const fired: string[] = [];
+
+ @Directive({
+ selector: '[super-dir]',
+ })
+ class SuperDirective {
+ ngOnInit() { fired.push('super init'); }
+ ngOnDestroy() { fired.push('super destroy'); }
+ ngAfterContentInit() { fired.push('super after content init'); }
+ ngAfterContentChecked() { fired.push('super after content checked'); }
+ ngAfterViewInit() { fired.push('super after view init'); }
+ ngAfterViewChecked() { fired.push('super after view checked'); }
+ ngDoCheck() { fired.push('super do check'); }
+ }
+
+ class BareClass extends SuperDirective {}
+
+ beforeEach(() => fired.length = 0);
+
+ it('ngOnInit', () => {
+ @Component({selector: 'my-comp', template: `test
`})
+ class MyComponent extends BareClass {
+ ngOnInit() { fired.push('sub init'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'sub init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngDoCheck', () => {
+ @Directive({
+ selector: 'my-comp',
+ })
+ class MyComponent extends BareClass {
+ ngDoCheck() { fired.push('sub do check'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'sub do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterContentInit', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends BareClass {
+ ngAfterContentInit() { fired.push('sub after content init'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'sub after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterContentChecked', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends BareClass {
+ ngAfterContentChecked() { fired.push('sub after content checked'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'sub after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterViewInit', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends BareClass {
+ ngAfterViewInit() { fired.push('sub after view init'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'sub after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterViewChecked', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends BareClass {
+ ngAfterViewChecked() { fired.push('sub after view checked'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'sub after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngOnDestroy', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends BareClass {
+ ngOnDestroy() { fired.push('sub destroy'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'sub destroy',
+ ]);
+ });
+ });
+
+ it('should inherit inputs', () => {
+ @Directive({
+ selector: '[super-dir]',
+ })
+ class SuperDirective {
+ @Input()
+ foo = '';
+
+ @Input()
+ baz = '';
+ }
+
+ class BareClass extends SuperDirective {
+ @Input()
+ bar = '';
+ }
+
+ @Component({selector: 'my-comp', template: `test
`})
+ class MyComponent extends BareClass {
+ @Input()
+ baz = '';
+
+ @Input()
+ qux = '';
+ }
+
+ @Component({template: ``})
+ class App {
+ a = 'a';
+ b = 'b';
+ c = 'c';
+ d = 'd';
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, MyComponent, BareClass, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ const subDir: MyComponent =
+ fixture.debugElement.query(By.directive(MyComponent)).componentInstance;
+
+ expect(subDir.foo).toEqual('a');
+ expect(subDir.bar).toEqual('b');
+ expect(subDir.baz).toEqual('c');
+ expect(subDir.qux).toEqual('d');
+ });
+
+
+ it('should inherit outputs', () => {
+ @Directive({
+ selector: '[super-dir]',
+ })
+ class SuperDirective {
+ @Output()
+ foo = new EventEmitter();
+ }
+
+ class BareClass extends SuperDirective {}
+
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends BareClass {
+ ngOnInit() { this.foo.emit('test'); }
+ }
+
+ @Component({
+ template: `
+
+ `
+ })
+ class App {
+ foo = '';
+
+ handleFoo(event: string) { this.foo = event; }
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, MyComponent, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+ const app = fixture.componentInstance;
+
+ expect(app.foo).toBe('test');
+ });
+
+ it('should compose host bindings for styles', () => {
+ @Directive({
+ selector: '[super-dir]',
+ })
+ class SuperDirective {
+ @HostBinding('style.color')
+ color = 'red';
+
+ @HostBinding('style.backgroundColor')
+ bg = 'black';
+ }
+
+ class BareClass extends SuperDirective {}
+
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends BareClass {
+ }
+
+ @Component({
+ template: `
+ test
+ `
+ })
+ class App {
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, MyComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+ const queryResult = fixture.debugElement.query(By.directive(MyComponent));
+
+ expect(queryResult.nativeElement.tagName).toBe('MY-COMP');
+ expect(queryResult.nativeElement.style.color).toBe('red');
+ expect(queryResult.nativeElement.style.backgroundColor).toBe('black');
+ });
+
+ it('should compose host bindings (non-style related)', () => {
+ @Directive({
+ selector: '[super-dir]',
+ })
+ class SuperDirective {
+ @HostBinding('title')
+ get boundTitle() { return this.superTitle + '!!!'; }
+
+ @Input()
+ superTitle = '';
+ }
+
+ class BareClass extends SuperDirective {
+ @HostBinding('accessKey')
+ get boundAccessKey() { return this.superAccessKey + '???'; }
+
+ @Input()
+ superAccessKey = '';
+ }
+
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends BareClass {
+ }
+ @Component({
+ template: `
+ test
+ `
+ })
+ class App {
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, SuperDirective, BareClass, MyComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+ const queryResult = fixture.debugElement.query(By.directive(MyComponent));
+
+ expect(queryResult.nativeElement.title).toBe('test1!!!');
+ expect(queryResult.nativeElement.accessKey).toBe('test2???');
+ });
+
+ it('should inherit ContentChildren queries', () => {
+ let foundQueryList: QueryList;
+
+ @Directive({selector: '[child-dir]'})
+ class ChildDir {
+ }
+
+ @Directive({
+ selector: '[super-dir]',
+ })
+ class SuperDirective {
+ // TODO(issue/24571): remove '!'.
+ @ContentChildren(ChildDir)
+ customDirs !: QueryList;
+ }
+
+ class BareClass extends SuperDirective {}
+
+ @Component({
+ selector: 'my-comp',
+ template: ``,
+ })
+ class MyComponent extends BareClass {
+ ngAfterViewInit() { foundQueryList = this.customDirs; }
+ }
+
+ @Component({
+ template: `
+
+ one
+ two
+
+ `
+ })
+ class App {
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, MyComponent, ChildDir, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(foundQueryList !.length).toBe(2);
+ });
+
+ it('should inherit ViewChildren queries', () => {
+ let foundQueryList: QueryList;
+
+ @Directive({selector: '[child-dir]'})
+ class ChildDir {
+ }
+
+ @Directive({
+ selector: '[super-dir]',
+ })
+ class SuperDirective {
+ // TODO(issue/24571): remove '!'.
+ @ViewChildren(ChildDir)
+ customDirs !: QueryList;
+ }
+
+ class BareClass extends SuperDirective {}
+
+ @Component({
+ selector: 'my-comp',
+ template: `
+
+ `,
+ })
+ class MyComponent extends BareClass {
+ items = [1, 2, 3, 4, 5];
+ ngAfterViewInit() { foundQueryList = this.customDirs; }
+ }
+
+ @Component({
+ template: `
+
+ `
+ })
+ class App {
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, MyComponent, ChildDir, SuperDirective],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(foundQueryList !.length).toBe(5);
+ });
+ });
+
+ describe('of a component inherited by a component', () => {
+ describe('lifecycle hooks', () => {
+ const fired: string[] = [];
+
+ @Component({
+ selector: 'super-comp',
+ template: `super
`,
+ })
+ class SuperComponent {
+ ngOnInit() { fired.push('super init'); }
+ ngOnDestroy() { fired.push('super destroy'); }
+ ngAfterContentInit() { fired.push('super after content init'); }
+ ngAfterContentChecked() { fired.push('super after content checked'); }
+ ngAfterViewInit() { fired.push('super after view init'); }
+ ngAfterViewChecked() { fired.push('super after view checked'); }
+ ngDoCheck() { fired.push('super do check'); }
+ }
+
+ beforeEach(() => fired.length = 0);
+
+ it('ngOnInit', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperComponent {
+ ngOnInit() { fired.push('sub init'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App, SuperComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'sub init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngDoCheck', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperComponent {
+ ngDoCheck() { fired.push('sub do check'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App, SuperComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'sub do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterContentInit', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperComponent {
+ ngAfterContentInit() { fired.push('sub after content init'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App, SuperComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'sub after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterContentChecked', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperComponent {
+ ngAfterContentChecked() { fired.push('sub after content checked'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App, SuperComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'sub after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterViewInit', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperComponent {
+ ngAfterViewInit() { fired.push('sub after view init'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App, SuperComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'sub after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterViewChecked', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperComponent {
+ ngAfterViewChecked() { fired.push('sub after view checked'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App, SuperComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'sub after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngOnDestroy', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperComponent {
+ ngOnDestroy() { fired.push('sub destroy'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App, SuperComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'sub destroy',
+ ]);
+ });
+ });
+
+ it('should inherit inputs', () => {
+ @Component({
+ selector: 'super-comp',
+ template: `super
`,
+ })
+ class SuperComponent {
+ @Input()
+ foo = '';
+
+ @Input()
+ bar = '';
+
+ @Input()
+ baz = '';
+ }
+
+ @Component({selector: 'my-comp', template: `test
`})
+ class MyComponent extends SuperComponent {
+ @Input()
+ baz = '';
+
+ @Input()
+ qux = '';
+ }
+
+ @Component({template: ``})
+ class App {
+ a = 'a';
+ b = 'b';
+ c = 'c';
+ d = 'd';
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, MyComponent, SuperComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ const subDir: MyComponent =
+ fixture.debugElement.query(By.directive(MyComponent)).componentInstance;
+
+ expect(subDir.foo).toEqual('a');
+ expect(subDir.bar).toEqual('b');
+ expect(subDir.baz).toEqual('c');
+ expect(subDir.qux).toEqual('d');
+ });
+
+ it('should inherit outputs', () => {
+ @Component({
+ selector: 'super-comp',
+ template: `super
`,
+ })
+ class SuperComponent {
+ @Output()
+ foo = new EventEmitter();
+ }
+
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperComponent {
+ ngOnInit() { this.foo.emit('test'); }
+ }
+
+ @Component({
+ template: `
+
+ `
+ })
+ class App {
+ foo = '';
+
+ handleFoo(event: string) { this.foo = event; }
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, MyComponent, SuperComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+ const app = fixture.componentInstance;
+
+ expect(app.foo).toBe('test');
+ });
+
+ it('should compose host bindings for styles', () => {
+ @Component({
+ selector: 'super-comp',
+ template: `super
`,
+ })
+ class SuperComponent {
+ @HostBinding('style.color')
+ color = 'red';
+
+ @HostBinding('style.backgroundColor')
+ bg = 'black';
+ }
+
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperComponent {
+ }
+
+ @Component({
+ template: `
+ test
+ `
+ })
+ class App {
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, MyComponent, SuperComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+ const queryResult = fixture.debugElement.query(By.directive(MyComponent));
+
+ expect(queryResult.nativeElement.tagName).toBe('MY-COMP');
+ expect(queryResult.nativeElement.style.color).toBe('red');
+ expect(queryResult.nativeElement.style.backgroundColor).toBe('black');
+ });
+
+ it('should compose host bindings (non-style related)', () => {
+ @Component({
+ selector: 'super-comp',
+ template: `super
`,
+ })
+ class SuperComponent {
+ @HostBinding('title')
+ get boundTitle() { return this.superTitle + '!!!'; }
+
+ @Input()
+ superTitle = '';
+ }
+
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperComponent {
+ }
+ @Component({
+ template: `
+ test
+ `
+ })
+ class App {
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, MyComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+ const queryResult = fixture.debugElement.query(By.directive(MyComponent));
+
+ expect(queryResult.nativeElement.title).toBe('test!!!');
+ });
+
+ it('should inherit ContentChildren queries', () => {
+ let foundQueryList: QueryList;
+
+ @Directive({selector: '[child-dir]'})
+ class ChildDir {
+ }
+
+ @Component({
+ selector: 'super-comp',
+ template: `super
`,
+ })
+ class SuperComponent {
+ // TODO(issue/24571): remove '!'.
+ @ContentChildren(ChildDir)
+ customDirs !: QueryList;
+ }
+
+ @Component({
+ selector: 'my-comp',
+ template: ``,
+ })
+ class MyComponent extends SuperComponent {
+ ngAfterViewInit() { foundQueryList = this.customDirs; }
+ }
+
+ @Component({
+ template: `
+
+ one
+ two
+
+ `
+ })
+ class App {
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, MyComponent, SuperComponent, ChildDir],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(foundQueryList !.length).toBe(2);
+ });
+
+ it('should inherit ViewChildren queries', () => {
+ let foundQueryList: QueryList;
+
+ @Directive({selector: '[child-dir]'})
+ class ChildDir {
+ }
+
+ @Component({
+ selector: 'super-comp',
+ template: `super
`,
+ })
+ class SuperComponent {
+ // TODO(issue/24571): remove '!'.
+ @ViewChildren(ChildDir)
+ customDirs !: QueryList;
+ }
+
+ @Component({
+ selector: 'my-comp',
+ template: `
+
+ `,
+ })
+ class MyComponent extends SuperComponent {
+ items = [1, 2, 3, 4, 5];
+ ngAfterViewInit() { foundQueryList = this.customDirs; }
+ }
+
+ @Component({
+ template: `
+
+ `
+ })
+ class App {
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, MyComponent, ChildDir, SuperComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(foundQueryList !.length).toBe(5);
+ });
+ });
+
+ describe('of a component inherited by a bare class then by a component', () => {
+ describe('lifecycle hooks', () => {
+ const fired: string[] = [];
+
+ @Component({
+ selector: 'super-comp',
+ template: `super
`,
+ })
+ class SuperSuperComponent {
+ ngOnInit() { fired.push('super init'); }
+ ngOnDestroy() { fired.push('super destroy'); }
+ ngAfterContentInit() { fired.push('super after content init'); }
+ ngAfterContentChecked() { fired.push('super after content checked'); }
+ ngAfterViewInit() { fired.push('super after view init'); }
+ ngAfterViewChecked() { fired.push('super after view checked'); }
+ ngDoCheck() { fired.push('super do check'); }
+ }
+
+ class SuperComponent extends SuperSuperComponent {}
+
+ beforeEach(() => fired.length = 0);
+
+ it('ngOnInit', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperComponent {
+ ngOnInit() { fired.push('sub init'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App, SuperComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'sub init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngDoCheck', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperComponent {
+ ngDoCheck() { fired.push('sub do check'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App, SuperComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'sub do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterContentInit', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperComponent {
+ ngAfterContentInit() { fired.push('sub after content init'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App, SuperComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'sub after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterContentChecked', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperComponent {
+ ngAfterContentChecked() { fired.push('sub after content checked'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App, SuperComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'sub after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterViewInit', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperComponent {
+ ngAfterViewInit() { fired.push('sub after view init'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App, SuperComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'sub after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngAfterViewChecked', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperComponent {
+ ngAfterViewChecked() { fired.push('sub after view checked'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App, SuperComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'sub after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super destroy',
+ ]);
+ });
+
+ it('ngOnDestroy', () => {
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperComponent {
+ ngOnDestroy() { fired.push('sub destroy'); }
+ }
+
+ @Component({
+ template: ``,
+ })
+ class App {
+ showing = true;
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [MyComponent, App, SuperComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'super init',
+ 'super do check',
+ 'super after content init',
+ 'super after content checked',
+ 'super after view init',
+ 'super after view checked',
+ ]);
+
+ fired.length = 0;
+ fixture.componentInstance.showing = false;
+ fixture.detectChanges();
+
+ expect(fired).toEqual([
+ 'sub destroy',
+ ]);
+ });
+ });
+
+ it('should inherit inputs', () => {
+ @Component({
+ selector: 'super-comp',
+ template: `super
`,
+ })
+ class SuperSuperComponent {
+ @Input()
+ foo = '';
+
+ @Input()
+ baz = '';
+ }
+
+ class BareClass extends SuperSuperComponent {
+ @Input()
+ bar = '';
+ }
+
+ @Component({selector: 'my-comp', template: `test
`})
+ class MyComponent extends BareClass {
+ @Input()
+ baz = '';
+
+ @Input()
+ qux = '';
+ }
+
+ @Component({template: ``})
+ class App {
+ a = 'a';
+ b = 'b';
+ c = 'c';
+ d = 'd';
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, MyComponent, SuperSuperComponent, BareClass],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ const myComp: MyComponent =
+ fixture.debugElement.query(By.directive(MyComponent)).componentInstance;
+
+ expect(myComp.foo).toEqual('a');
+ expect(myComp.bar).toEqual('b');
+ expect(myComp.baz).toEqual('c');
+ expect(myComp.qux).toEqual('d');
+ });
+
+ it('should inherit outputs', () => {
+ @Component({
+ selector: 'super-comp',
+ template: `super
`,
+ })
+ class SuperSuperComponent {
+ @Output()
+ foo = new EventEmitter();
+ }
+
+ class SuperComponent extends SuperSuperComponent {
+ @Output()
+ bar = new EventEmitter();
+ }
+
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperComponent {
+ ngOnInit() {
+ this.foo.emit('test1');
+ this.bar.emit('test2');
+ }
+ }
+
+ @Component({
+ template: `
+
+ `
+ })
+ class App {
+ foo = '';
+
+ handleFoo(event: string) { this.foo = event; }
+
+ bar = '';
+
+ handleBar(event: string) { this.bar = event; }
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, MyComponent, SuperComponent, SuperSuperComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+ const app = fixture.componentInstance;
+
+ expect(app.foo).toBe('test1');
+ expect(app.bar).toBe('test2');
+ });
+
+ it('should compose host bindings for styles', () => {
+ @Component({
+ selector: 'super-comp',
+ template: `super
`,
+ })
+ class SuperSuperComponent {
+ @HostBinding('style.color')
+ color = 'red';
+ }
+
+ class SuperComponent extends SuperSuperComponent {
+ @HostBinding('style.backgroundColor')
+ bg = 'black';
+ }
+
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperComponent {
+ }
+
+ @Component({
+ template: `
+ test
+ `
+ })
+ class App {
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, MyComponent, SuperComponent, SuperSuperComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+ const queryResult = fixture.debugElement.query(By.directive(MyComponent));
+
+ expect(queryResult.nativeElement.tagName).toBe('MY-COMP');
+ expect(queryResult.nativeElement.style.color).toBe('red');
+ expect(queryResult.nativeElement.style.backgroundColor).toBe('black');
+ });
+
+ it('should compose host bindings (non-style related)', () => {
+ @Component({
+ selector: 'super-comp',
+ template: `super
`,
+ })
+ class SuperSuperComponent {
+ @HostBinding('title')
+ get boundTitle() { return this.superTitle + '!!!'; }
+
+ @Input()
+ superTitle = '';
+ }
+
+ class SuperComponent extends SuperSuperComponent {
+ @HostBinding('accessKey')
+ get boundAccessKey() { return this.superAccessKey + '???'; }
+
+ @Input()
+ superAccessKey = '';
+ }
+
+ @Component({
+ selector: 'my-comp',
+ template: `test
`,
+ })
+ class MyComponent extends SuperComponent {
+ }
+ @Component({
+ template: `
+ test
+ `
+ })
+ class App {
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, MyComponent, SuperComponent, SuperSuperComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+ const queryResult = fixture.debugElement.query(By.directive(MyComponent));
+
+ expect(queryResult.nativeElement.tagName).toBe('MY-COMP');
+ expect(queryResult.nativeElement.title).toBe('test1!!!');
+ expect(queryResult.nativeElement.accessKey).toBe('test2???');
+ });
+
+ it('should inherit ContentChildren queries', () => {
+ let foundQueryList: QueryList;
+
+ @Directive({selector: '[child-dir]'})
+ class ChildDir {
+ }
+
+ @Component({
+ selector: 'super-comp',
+ template: `super
`,
+ })
+ class SuperComponent {
+ // TODO(issue/24571): remove '!'.
+ @ContentChildren(ChildDir)
+ customDirs !: QueryList;
+ }
+
+ @Component({
+ selector: 'my-comp',
+ template: ``,
+ })
+ class MyComponent extends SuperComponent {
+ ngAfterViewInit() { foundQueryList = this.customDirs; }
+ }
+
+ @Component({
+ template: `
+
+ one
+ two
+
+ `
+ })
+ class App {
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, MyComponent, SuperComponent, ChildDir],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(foundQueryList !.length).toBe(2);
+ });
+
+ it('should inherit ViewChildren queries', () => {
+ let foundQueryList: QueryList;
+
+ @Directive({selector: '[child-dir]'})
+ class ChildDir {
+ }
+
+ @Component({
+ selector: 'super-comp',
+ template: `super
`,
+ })
+ class SuperComponent {
+ // TODO(issue/24571): remove '!'.
+ @ViewChildren(ChildDir)
+ customDirs !: QueryList;
+ }
+
+ @Component({
+ selector: 'my-comp',
+ template: `
+
+ `,
+ })
+ class MyComponent extends SuperComponent {
+ items = [1, 2, 3, 4, 5];
+ ngAfterViewInit() { foundQueryList = this.customDirs; }
+ }
+
+ @Component({
+ template: `
+
+ `
+ })
+ class App {
+ }
+
+ TestBed.configureTestingModule({
+ declarations: [App, MyComponent, ChildDir, SuperComponent],
+ });
+ const fixture = TestBed.createComponent(App);
+ fixture.detectChanges();
+
+ expect(foundQueryList !.length).toBe(5);
+ });
});
});
-
-@Component({selector: 'app-comp', template: ``})
-class AppComp {
-}
diff --git a/packages/core/test/render3/inherit_definition_feature_spec.ts b/packages/core/test/render3/inherit_definition_feature_spec.ts
deleted file mode 100644
index 4d78e7eb7e..0000000000
--- a/packages/core/test/render3/inherit_definition_feature_spec.ts
+++ /dev/null
@@ -1,639 +0,0 @@
-/**
- * @license
- * Copyright Google Inc. All Rights Reserved.
- *
- * Use of this source code is governed by an MIT-style license that can be
- * found in the LICENSE file at https://angular.io/license
- */
-
-import {ElementRef, Inject, InjectionToken, QueryList, ɵAttributeMarker as AttributeMarker} from '../../src/core';
-import {ΔallocHostVars, Δbind, ComponentDef, ΔcontentQuery, ΔdefineBase, ΔdefineComponent, ΔdefineDirective, DirectiveDef, ΔdirectiveInject, Δelement, ΔelementEnd, ΔelementProperty, ΔelementStart, ΔInheritDefinitionFeature, Δload, ΔloadContentQuery, ΔloadViewQuery, ΔNgOnChangesFeature, ΔProvidersFeature, ΔqueryRefresh, RenderFlags, ΔviewQuery,} from '../../src/render3/index';
-
-import {ComponentFixture, createComponent, getDirectiveOnNode} from './render_util';
-
-describe('InheritDefinitionFeature', () => {
- it('should inherit lifecycle hooks', () => {
- class SuperDirective {
- ngOnInit() {}
- ngOnDestroy() {}
- ngAfterContentInit() {}
- ngAfterContentChecked() {}
- ngAfterViewInit() {}
- ngAfterViewChecked() {}
- ngDoCheck() {}
- }
-
- class SubDirective extends SuperDirective {
- ngAfterViewInit() {}
- ngAfterViewChecked() {}
- ngDoCheck() {}
-
- static ngDirectiveDef = ΔdefineDirective({
- type: SubDirective,
- selectors: [['', 'subDir', '']],
- factory: () => new SubDirective(),
- features: [ΔInheritDefinitionFeature]
- });
- }
-
- const finalDef = SubDirective.ngDirectiveDef as DirectiveDef;
-
-
- expect(finalDef.onInit).toBe(SuperDirective.prototype.ngOnInit);
- expect(finalDef.onDestroy).toBe(SuperDirective.prototype.ngOnDestroy);
- expect(finalDef.afterContentChecked).toBe(SuperDirective.prototype.ngAfterContentChecked);
- expect(finalDef.afterContentInit).toBe(SuperDirective.prototype.ngAfterContentInit);
- expect(finalDef.afterViewChecked).toBe(SubDirective.prototype.ngAfterViewChecked);
- expect(finalDef.afterViewInit).toBe(SubDirective.prototype.ngAfterViewInit);
- expect(finalDef.doCheck).toBe(SubDirective.prototype.ngDoCheck);
- });
-
- it('should inherit inputs', () => {
- class SuperDirective {
- static ngDirectiveDef = ΔdefineDirective({
- inputs: {
- superFoo: ['foo', 'declaredFoo'],
- superBar: 'bar',
- superBaz: 'baz',
- },
- type: SuperDirective,
- selectors: [['', 'superDir', '']],
- factory: () => new SuperDirective(),
- });
- }
-
- class SubDirective extends SuperDirective {
- static ngDirectiveDef = ΔdefineDirective({
- type: SubDirective,
- inputs: {
- subBaz: 'baz',
- subQux: 'qux',
- },
- selectors: [['', 'subDir', '']],
- factory: () => new SubDirective(),
- features: [ΔInheritDefinitionFeature]
- });
- }
-
- const subDef = SubDirective.ngDirectiveDef as DirectiveDef;
-
- expect(subDef.inputs).toEqual({
- foo: 'superFoo',
- bar: 'superBar',
- baz: 'subBaz',
- qux: 'subQux',
- });
- expect(subDef.declaredInputs).toEqual({
- foo: 'declaredFoo',
- bar: 'bar',
- baz: 'baz',
- qux: 'qux',
- });
- });
-
- it('should inherit outputs', () => {
- class SuperDirective {
- static ngDirectiveDef = ΔdefineDirective({
- outputs: {
- superFoo: 'foo',
- superBar: 'bar',
- superBaz: 'baz',
- },
- type: SuperDirective,
- selectors: [['', 'superDir', '']],
- factory: () => new SuperDirective(),
- });
- }
-
- class SubDirective extends SuperDirective {
- static ngDirectiveDef = ΔdefineDirective({
- type: SubDirective,
- outputs: {
- subBaz: 'baz',
- subQux: 'qux',
- },
- selectors: [['', 'subDir', '']],
- factory: () => new SubDirective(),
- features: [ΔInheritDefinitionFeature]
- });
- }
-
- const subDef = SubDirective.ngDirectiveDef as DirectiveDef;
-
- expect(subDef.outputs).toEqual({
- foo: 'superFoo',
- bar: 'superBar',
- baz: 'subBaz',
- qux: 'subQux',
- });
- });
-
- it('should detect EMPTY inputs and outputs', () => {
- class SuperDirective {
- static ngDirectiveDef = ΔdefineDirective({
- inputs: {
- testIn: 'testIn',
- },
- outputs: {
- testOut: 'testOut',
- },
- type: SuperDirective,
- selectors: [['', 'superDir', '']],
- factory: () => new SuperDirective(),
- });
- }
-
- class SubDirective extends SuperDirective {
- static ngDirectiveDef = ΔdefineDirective({
- type: SubDirective,
- selectors: [['', 'subDir', '']],
- factory: () => new SubDirective(),
- features: [ΔInheritDefinitionFeature]
- });
- }
-
- const subDef = SubDirective.ngDirectiveDef as DirectiveDef;
-
- expect(subDef.inputs).toEqual({
- testIn: 'testIn',
- });
- expect(subDef.outputs).toEqual({
- testOut: 'testOut',
- });
- });
-
- it('should inherit inputs from ngBaseDefs along the way', () => {
-
- class Class5 {
- input5 = 'data, so data';
-
- static ngBaseDef = ΔdefineBase({
- inputs: {
- input5: 'input5',
- },
- });
- }
-
- class Class4 extends Class5 {
- input4 = 'hehe';
-
- static ngDirectiveDef = ΔdefineDirective({
- inputs: {
- input4: 'input4',
- },
- type: Class4,
- selectors: [['', 'superDir', '']],
- factory: () => new Class4(),
- features: [ΔInheritDefinitionFeature],
- });
- }
-
- class Class3 extends Class4 {}
-
- class Class2 extends Class3 {
- input3 = 'wee';
-
- static ngBaseDef = ΔdefineBase({
- inputs: {
- input3: ['alias3', 'input3'],
- }
- }) as any;
- }
-
- class Class1 extends Class2 {
- input1 = 'test';
- input2 = 'whatever';
-
- static ngDirectiveDef = ΔdefineDirective({
- type: Class1,
- inputs: {
- input1: 'input1',
- input2: 'input2',
- },
- selectors: [['', 'subDir', '']],
- factory: () => new Class1(),
- features: [ΔInheritDefinitionFeature],
- });
- }
-
- const subDef = Class1.ngDirectiveDef as DirectiveDef;
-
- expect(subDef.inputs).toEqual({
- input1: 'input1',
- input2: 'input2',
- alias3: 'input3',
- input4: 'input4',
- input5: 'input5',
- });
- expect(subDef.declaredInputs).toEqual({
- input1: 'input1',
- input2: 'input2',
- alias3: 'input3',
- input4: 'input4',
- input5: 'input5',
- });
- });
-
- it('should inherit outputs from ngBaseDefs along the way', () => {
-
- class Class5 {
- output5 = 'data, so data';
-
- static ngBaseDef = ΔdefineBase({
- outputs: {
- output5: 'alias5',
- },
- });
- }
-
- class Class4 extends Class5 {
- output4 = 'hehe';
-
- static ngDirectiveDef = ΔdefineDirective({
- outputs: {
- output4: 'alias4',
- },
- type: Class4,
- selectors: [['', 'superDir', '']],
- factory: () => new Class4(),
- features: [ΔInheritDefinitionFeature],
- });
- }
-
- class Class3 extends Class4 {}
-
- class Class2 extends Class3 {
- output3 = 'wee';
-
- static ngBaseDef = ΔdefineBase({
- outputs: {
- output3: 'alias3',
- }
- }) as any;
- }
-
- class Class1 extends Class2 {
- output1 = 'test';
- output2 = 'whatever';
-
- static ngDirectiveDef = ΔdefineDirective({
- type: Class1,
- outputs: {
- output1: 'alias1',
- output2: 'alias2',
- },
- selectors: [['', 'subDir', '']],
- factory: () => new Class1(),
- features: [ΔInheritDefinitionFeature],
- });
- }
-
- const subDef = Class1.ngDirectiveDef as DirectiveDef;
-
- expect(subDef.outputs).toEqual({
- alias1: 'output1',
- alias2: 'output2',
- alias3: 'output3',
- alias4: 'output4',
- alias5: 'output5',
- });
- });
-
- it('should compose hostBindings', () => {
- let subDir !: SubDirective;
-
- class SuperDirective {
- id = 'my-id';
-
- static ngDirectiveDef = ΔdefineDirective({
- type: SuperDirective,
- selectors: [['', 'superDir', '']],
- hostBindings: (rf: RenderFlags, ctx: SuperDirective, elementIndex: number) => {
- if (rf & RenderFlags.Create) {
- ΔallocHostVars(1);
- }
- if (rf & RenderFlags.Update) {
- ΔelementProperty(elementIndex, 'id', Δbind(ctx.id));
- }
- },
- factory: () => new SuperDirective(),
- });
- }
-
- class SubDirective extends SuperDirective {
- title = 'my-title';
-
- static ngDirectiveDef = ΔdefineDirective({
- type: SubDirective,
- selectors: [['', 'subDir', '']],
- hostBindings: (rf: RenderFlags, ctx: SubDirective, elementIndex: number) => {
- if (rf & RenderFlags.Create) {
- ΔallocHostVars(1);
- }
- if (rf & RenderFlags.Update) {
- ΔelementProperty(elementIndex, 'title', Δbind(ctx.title));
- }
- },
- factory: () => subDir = new SubDirective(),
- features: [ΔInheritDefinitionFeature]
- });
- }
-
-
- const App = createComponent('app', (rf: RenderFlags, ctx: any) => {
- if (rf & RenderFlags.Create) {
- Δelement(0, 'div', ['subDir', '']);
- }
- }, 1, 0, [SubDirective]);
-
- const fixture = new ComponentFixture(App);
- const divEl = fixture.hostElement.querySelector('div') as HTMLElement;
-
- expect(divEl.id).toEqual('my-id');
- expect(divEl.title).toEqual('my-title');
-
- subDir.title = 'new-title';
- fixture.update();
- expect(divEl.id).toEqual('my-id');
- expect(divEl.title).toEqual('new-title');
-
- subDir.id = 'new-id';
- fixture.update();
- expect(divEl.id).toEqual('new-id');
- expect(divEl.title).toEqual('new-title');
- });
-
- describe('view query', () => {
-
- const SomeComp = createComponent('some-comp', (rf: RenderFlags, ctx: any) => {});
-
- /*
- * class SuperComponent {
- * @ViewChildren('super') superQuery;
- * }
- */
- class SuperComponent {
- superQuery?: QueryList;
- static ngComponentDef = ΔdefineComponent({
- type: SuperComponent,
- template: () => {},
- consts: 0,
- vars: 0,
- selectors: [['super-comp']],
- viewQuery: (rf: RenderFlags, ctx: any) => {
- if (rf & RenderFlags.Create) {
- ΔviewQuery(['super'], false, null);
- }
- if (rf & RenderFlags.Update) {
- let tmp: any;
- ΔqueryRefresh(tmp = ΔloadViewQuery>()) &&
- (ctx.superQuery = tmp as QueryList);
- }
- },
- factory: () => new SuperComponent(),
- });
- }
-
- /**
- *
- *
- *
- * class SubComponent extends SuperComponent {
- * @ViewChildren('sub') subQuery;
- * }
- */
- class SubComponent extends SuperComponent {
- subQuery?: QueryList;
- static ngComponentDef = ΔdefineComponent({
- type: SubComponent,
- template: (rf: RenderFlags, ctx: any) => {
- if (rf & RenderFlags.Create) {
- Δelement(0, 'div', ['id', 'sub'], ['sub', '']);
- Δelement(2, 'div', ['id', 'super'], ['super', '']);
- Δelement(4, 'some-comp');
- }
- },
- consts: 5,
- vars: 0,
- selectors: [['sub-comp']],
- viewQuery: (rf: RenderFlags, ctx: any) => {
- if (rf & RenderFlags.Create) {
- ΔviewQuery(['sub'], false, null);
- }
- if (rf & RenderFlags.Update) {
- let tmp: any;
- ΔqueryRefresh(tmp = ΔloadViewQuery>()) &&
- (ctx.subQuery = tmp as QueryList);
- }
- },
- factory: () => new SubComponent(),
- features: [ΔInheritDefinitionFeature],
- directives: [SomeComp]
- });
- }
-
-
- it('should compose viewQuery (basic mechanics check)', () => {
- const log: Array<[string, RenderFlags, any]> = [];
-
- class SuperComponent {
- static ngComponentDef = ΔdefineComponent({
- type: SuperComponent,
- template: () => {},
- consts: 0,
- vars: 0,
- selectors: [['', 'superDir', '']],
- viewQuery: (rf: RenderFlags, ctx: T) => {
- log.push(['super', rf, ctx]);
- },
- factory: () => new SuperComponent(),
- });
- }
-
- class SubComponent extends SuperComponent {
- static ngComponentDef = ΔdefineComponent({
- type: SubComponent,
- template: () => {},
- consts: 0,
- vars: 0,
- selectors: [['', 'subDir', '']],
- viewQuery: (rf: RenderFlags, ctx: SubComponent) => {
- log.push(['sub', rf, ctx]);
- },
- factory: () => new SubComponent(),
- features: [ΔInheritDefinitionFeature]
- });
- }
-
- const subDef = SubComponent.ngComponentDef as ComponentDef;
-
- const context = {foo: 'bar'};
-
- subDef.viewQuery !(RenderFlags.Create, context);
-
- expect(log).toEqual(
- [['super', RenderFlags.Create, context], ['sub', RenderFlags.Create, context]]);
- });
-
-
-
- it('should compose viewQuery (query logic check)', () => {
- const fixture = new ComponentFixture(SubComponent);
-
- const check = (key: string): void => {
- const qList = (fixture.component as any)[`${key}Query`] as QueryList;
- expect(qList.length).toBe(1);
- expect(qList.first.nativeElement).toEqual(fixture.hostElement.querySelector(`#${key}`));
- expect(qList.first.nativeElement.id).toEqual(key);
- };
-
- check('sub');
- check('super');
- });
-
- it('should work with multiple viewQuery comps', () => {
- let subCompOne !: SubComponent;
- let subCompTwo !: SubComponent;
-
- const App = createComponent('app', (rf: RenderFlags, ctx: any) => {
- if (rf & RenderFlags.Create) {
- Δelement(0, 'sub-comp');
- Δelement(1, 'sub-comp');
- }
- subCompOne = getDirectiveOnNode(0);
- subCompTwo = getDirectiveOnNode(1);
- }, 2, 0, [SubComponent, SuperComponent]);
-
- const fixture = new ComponentFixture(App);
-
- const check = (comp: SubComponent): void => {
- const qList = comp.subQuery as QueryList;
- expect(qList.length).toBe(1);
- expect(qList.first.nativeElement).toEqual(fixture.hostElement.querySelector('#sub'));
- expect(qList.first.nativeElement.id).toEqual('sub');
- };
-
- check(subCompOne);
- check(subCompTwo);
- });
-
- });
-
-
- it('should compose contentQueries (basic mechanics check)', () => {
- const log: string[] = [];
-
- class SuperDirective {
- static ngDirectiveDef = ΔdefineDirective({
- type: SuperDirective,
- selectors: [['', 'superDir', '']],
- contentQueries: () => { log.push('super'); },
- factory: () => new SuperDirective(),
- });
- }
-
- class SubDirective extends SuperDirective {
- static ngDirectiveDef = ΔdefineDirective({
- type: SubDirective,
- selectors: [['', 'subDir', '']],
- contentQueries: () => { log.push('sub'); },
- factory: () => new SubDirective(),
- features: [ΔInheritDefinitionFeature]
- });
- }
-
- const subDef = SubDirective.ngDirectiveDef as DirectiveDef;
-
- subDef.contentQueries !(RenderFlags.Create, {}, 0);
-
- expect(log).toEqual(['super', 'sub']);
- });
-
- it('should compose contentQueries (verify query sets)', () => {
- let dirInstance: SubDirective;
- class SuperDirective {
- // @ContentChildren('foo')
- foos !: QueryList;
-
- static ngDirectiveDef = ΔdefineDirective({
- type: SuperDirective,
- selectors: [['', 'super-dir', '']],
- factory: () => new SuperDirective(),
- contentQueries: (rf: RenderFlags, ctx: any, dirIndex: number) => {
- if (rf & RenderFlags.Create) {
- ΔcontentQuery(dirIndex, ['foo'], true, null);
- }
- if (rf & RenderFlags.Update) {
- let tmp: any;
- ΔqueryRefresh(tmp = ΔloadContentQuery()) && (ctx.foos = tmp);
- }
- }
- });
- }
-
- class SubDirective extends SuperDirective {
- // @ContentChildren('bar')
- bars !: QueryList;
-
- static ngDirectiveDef = ΔdefineDirective({
- type: SubDirective,
- selectors: [['', 'sub-dir', '']],
- factory: () => dirInstance = new SubDirective(),
- contentQueries: (rf: RenderFlags, ctx: any, dirIndex: number) => {
- if (rf & RenderFlags.Create) {
- ΔcontentQuery(dirIndex, ['bar'], true, null);
- }
- if (rf & RenderFlags.Update) {
- let tmp: any;
- ΔqueryRefresh(tmp = ΔloadContentQuery()) && (ctx.bars = tmp);
- }
- },
- features: [ΔInheritDefinitionFeature]
- });
- }
-
- /**
- *
- *
- *
- *
- */
- const AppComponent = createComponent('app-component', function(rf: RenderFlags, ctx: any) {
- if (rf & RenderFlags.Create) {
- ΔelementStart(0, 'div', [AttributeMarker.Bindings, 'sub-dir']);
- {
- Δelement(1, 'span', null, ['foo', '']);
- Δelement(3, 'span', null, ['bar', '']);
- }
- ΔelementEnd();
- }
- }, 5, 0, [SubDirective]);
-
- const fixture = new ComponentFixture(AppComponent);
- expect(dirInstance !.foos.length).toBe(1);
- expect(dirInstance !.bars.length).toBe(1);
- });
-
- it('should throw if inheriting a component from a directive', () => {
- class SuperComponent {
- static ngComponentDef = ΔdefineComponent({
- type: SuperComponent,
- template: () => {},
- selectors: [['', 'superDir', '']],
- consts: 0,
- vars: 0,
- factory: () => new SuperComponent()
- });
- }
-
- expect(() => {
- class SubDirective extends SuperComponent{static ngDirectiveDef = ΔdefineDirective({
- type: SubDirective,
- selectors: [['', 'subDir', '']],
- factory: () => new SubDirective(),
- features: [ΔInheritDefinitionFeature]
- });}
- }).toThrowError('Directives cannot inherit Components');
- });
-
-});