build: reformat repo to new clang@1.4.0 (#36613)

PR Close #36613
This commit is contained in:
Joey Perrott
2020-04-13 16:40:21 -07:00
committed by atscott
parent 5e80e7e216
commit 698b0288be
1160 changed files with 31667 additions and 24000 deletions

View File

@ -6,18 +6,17 @@
* found in the LICENSE file at https://angular.io/license
*/
import {COMPILER_OPTIONS, Component, NgModule, ViewEncapsulation, destroyPlatform} from '@angular/core';
import {COMPILER_OPTIONS, Component, destroyPlatform, NgModule, ViewEncapsulation} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {onlyInIvy, withBody} from '@angular/private/testing';
describe('bootstrap', () => {
beforeEach(destroyPlatform);
afterEach(destroyPlatform);
it('should bootstrap using #id selector',
withBody('<div>before|</div><button id="my-app"></button>', async() => {
withBody('<div>before|</div><button id="my-app"></button>', async () => {
try {
const ngModuleRef = await platformBrowserDynamic().bootstrapModule(IdSelectorAppModule);
expect(document.body.textContent).toEqual('before|works!');
@ -28,7 +27,7 @@ describe('bootstrap', () => {
}));
it('should bootstrap using one of selectors from the list',
withBody('<div>before|</div><div class="bar"></div>', async() => {
withBody('<div>before|</div><div class="bar"></div>', async () => {
try {
const ngModuleRef =
await platformBrowserDynamic().bootstrapModule(MultipleSelectorsAppModule);
@ -66,7 +65,7 @@ describe('bootstrap', () => {
}
it('should use ViewEncapsulation.Emulated as default',
withBody('<my-app></my-app>', async() => {
withBody('<my-app></my-app>', async () => {
const TestModule = createComponentAndModule();
const ngModuleRef = await platformBrowserDynamic().bootstrapModule(TestModule);
@ -75,7 +74,7 @@ describe('bootstrap', () => {
}));
it('should allow setting defaultEncapsulation using bootstrap option',
withBody('<my-app></my-app>', async() => {
withBody('<my-app></my-app>', async () => {
const TestModule = createComponentAndModule();
const ngModuleRef = await platformBrowserDynamic().bootstrapModule(
@ -86,7 +85,7 @@ describe('bootstrap', () => {
}));
it('should allow setting defaultEncapsulation using compiler option',
withBody('<my-app></my-app>', async() => {
withBody('<my-app></my-app>', async () => {
const TestModule = createComponentAndModule();
const ngModuleRef = await platformBrowserDynamic([{
@ -100,7 +99,7 @@ describe('bootstrap', () => {
}));
it('should prefer encapsulation on component over bootstrap option',
withBody('<my-app></my-app>', async() => {
withBody('<my-app></my-app>', async () => {
const TestModule = createComponentAndModule({encapsulation: ViewEncapsulation.Emulated});
const ngModuleRef = await platformBrowserDynamic().bootstrapModule(
@ -110,7 +109,7 @@ describe('bootstrap', () => {
}));
it('should use preserveWhitespaces: false as default',
withBody('<my-app></my-app>', async() => {
withBody('<my-app></my-app>', async () => {
const TestModule = createComponentAndModule();
const ngModuleRef = await platformBrowserDynamic().bootstrapModule(TestModule);
@ -119,7 +118,7 @@ describe('bootstrap', () => {
}));
it('should allow setting preserveWhitespaces using bootstrap option',
withBody('<my-app></my-app>', async() => {
withBody('<my-app></my-app>', async () => {
const TestModule = createComponentAndModule();
const ngModuleRef = await platformBrowserDynamic().bootstrapModule(
@ -129,7 +128,7 @@ describe('bootstrap', () => {
}));
it('should allow setting preserveWhitespaces using compiler option',
withBody('<my-app></my-app>', async() => {
withBody('<my-app></my-app>', async () => {
const TestModule = createComponentAndModule();
const ngModuleRef =
@ -141,7 +140,7 @@ describe('bootstrap', () => {
}));
it('should prefer preserveWhitespaces on component over bootstrap option',
withBody('<my-app></my-app>', async() => {
withBody('<my-app></my-app>', async () => {
const TestModule = createComponentAndModule({preserveWhitespaces: false});
const ngModuleRef = await platformBrowserDynamic().bootstrapModule(
@ -151,10 +150,12 @@ describe('bootstrap', () => {
}));
onlyInIvy('options cannot be changed in Ivy').describe('changing bootstrap options', () => {
beforeEach(() => { spyOn(console, 'error'); });
beforeEach(() => {
spyOn(console, 'error');
});
it('should log an error when changing defaultEncapsulation bootstrap options',
withBody('<my-app></my-app>', async() => {
withBody('<my-app></my-app>', async () => {
const TestModule = createComponentAndModule();
const platformRef = platformBrowserDynamic();
@ -175,7 +176,7 @@ describe('bootstrap', () => {
}));
it('should log an error when changing preserveWhitespaces bootstrap options',
withBody('<my-app></my-app>', async() => {
withBody('<my-app></my-app>', async () => {
const TestModule = createComponentAndModule();
const platformRef = platformBrowserDynamic();
@ -196,7 +197,7 @@ describe('bootstrap', () => {
}));
it('should log an error when changing defaultEncapsulation to its default',
withBody('<my-app></my-app>', async() => {
withBody('<my-app></my-app>', async () => {
const TestModule = createComponentAndModule();
const platformRef = platformBrowserDynamic();
@ -215,7 +216,7 @@ describe('bootstrap', () => {
}));
it('should log an error when changing preserveWhitespaces to its default',
withBody('<my-app></my-app>', async() => {
withBody('<my-app></my-app>', async () => {
const TestModule = createComponentAndModule();
const platformRef = platformBrowserDynamic();
@ -234,7 +235,7 @@ describe('bootstrap', () => {
}));
it('should not log an error when passing identical bootstrap options',
withBody('<my-app></my-app>', async() => {
withBody('<my-app></my-app>', async () => {
const TestModule = createComponentAndModule();
const platformRef = platformBrowserDynamic();

View File

@ -16,16 +16,16 @@ import {ivyEnabled} from '@angular/private/testing';
import {BehaviorSubject} from 'rxjs';
describe('change detection', () => {
describe('embedded views', () => {
@Directive({selector: '[viewManipulation]', exportAs: 'vm'})
class ViewManipulation {
constructor(
private _tplRef: TemplateRef<{}>, public vcRef: ViewContainerRef,
private _appRef: ApplicationRef) {}
insertIntoVcRef() { return this.vcRef.createEmbeddedView(this._tplRef); }
insertIntoVcRef() {
return this.vcRef.createEmbeddedView(this._tplRef);
}
insertIntoAppRef(): EmbeddedViewRef<{}> {
const viewRef = this._tplRef.createEmbeddedView({});
@ -80,7 +80,9 @@ describe('change detection', () => {
changeDetection: ChangeDetectionStrategy.OnPush
})
class App {
increment(counter: 'componentView'|'embeddedView') { counters[counter]++; }
increment(counter: 'componentView'|'embeddedView') {
counters[counter]++;
}
noop() {}
}
@ -129,7 +131,9 @@ describe('change detection', () => {
changeDetection: ChangeDetectionStrategy.OnPush
})
class DynamicComp {
increment() { counter++; }
increment() {
counter++;
}
noop() {}
}
@ -170,13 +174,10 @@ describe('change detection', () => {
expect(counter).toBe(3);
});
});
describe('markForCheck', () => {
it('should mark OnPush ancestor of dynamically created component views as dirty', () => {
@Component({
selector: `test-cmpt`,
template: `{{counter}}|<ng-template #vc></ng-template>`,
@ -184,7 +185,7 @@ describe('change detection', () => {
})
class TestCmpt {
counter = 0;
@ViewChild('vc', {read: ViewContainerRef}) vcRef !: ViewContainerRef;
@ViewChild('vc', {read: ViewContainerRef}) vcRef!: ViewContainerRef;
constructor(private _cfr: ComponentFactoryResolver) {}
@ -252,11 +253,13 @@ describe('change detection', () => {
/**
* @internal
*/
private _input !: number;
private _input!: number;
constructor(private cdr: ChangeDetectorRef) {}
get input() { return this._input; }
get input() {
return this._input;
}
set input(value: number) {
this._input = value;
@ -286,18 +289,19 @@ describe('change detection', () => {
template: `{{ doCheckCount }} - {{ name }} <button (click)="onClick()"></button>`
})
class MyComponent implements DoCheck {
@Input()
name = 'Nancy';
@Input() name = 'Nancy';
doCheckCount = 0;
ngDoCheck(): void { this.doCheckCount++; }
ngDoCheck(): void {
this.doCheckCount++;
}
onClick() {}
}
@Component({selector: 'my-app', template: '<my-comp [name]="name"></my-comp>'})
class MyApp {
@ViewChild(MyComponent) comp !: MyComponent;
@ViewChild(MyComponent) comp!: MyComponent;
name: string = 'Nancy';
}
@ -369,7 +373,7 @@ describe('change detection', () => {
expect(fixture.componentInstance.comp.doCheckCount).toEqual(1);
expect(fixture.nativeElement.textContent.trim()).toEqual('1 - Nancy');
const button = fixture.nativeElement.querySelector('button') !;
const button = fixture.nativeElement.querySelector('button')!;
button.click();
// No ticks should have been scheduled.
@ -389,7 +393,7 @@ describe('change detection', () => {
template: '<my-comp></my-comp><button id="parent" (click)="noop()"></button>'
})
class ButtonParent {
@ViewChild(MyComponent) comp !: MyComponent;
@ViewChild(MyComponent) comp!: MyComponent;
noop() {}
}
@ -415,16 +419,18 @@ describe('change detection', () => {
changeDetection: ChangeDetectionStrategy.OnPush
})
class ButtonParent implements DoCheck {
@ViewChild(MyComponent) comp !: MyComponent;
@ViewChild(MyComponent) comp!: MyComponent;
noop() {}
doCheckCount = 0;
ngDoCheck(): void { this.doCheckCount++; }
ngDoCheck(): void {
this.doCheckCount++;
}
}
@Component({selector: 'my-button-app', template: '<button-parent></button-parent>'})
class MyButtonApp {
@ViewChild(ButtonParent) parent !: ButtonParent;
@ViewChild(ButtonParent) parent!: ButtonParent;
}
TestBed.configureTestingModule({declarations: [MyButtonApp, MyComponent, ButtonParent]});
@ -456,7 +462,6 @@ describe('change detection', () => {
expect(comp.doCheckCount).toEqual(2);
expect(fixture.nativeElement.textContent.trim()).toEqual('3 - 2 - Nancy');
});
});
describe('ChangeDetectorRef', () => {
@ -472,18 +477,22 @@ describe('change detection', () => {
constructor(public cdr: ChangeDetectorRef) {}
ngDoCheck() { this.doCheckCount++; }
ngDoCheck() {
this.doCheckCount++;
}
}
@Component({selector: 'parent-comp', template: `{{ doCheckCount}} - <my-comp></my-comp>`})
class ParentComp implements DoCheck {
@ViewChild(MyComp) myComp !: MyComp;
@ViewChild(MyComp) myComp!: MyComp;
doCheckCount = 0;
constructor(public cdr: ChangeDetectorRef) {}
ngDoCheck() { this.doCheckCount++; }
ngDoCheck() {
this.doCheckCount++;
}
}
@Directive({selector: '[dir]'})
@ -562,8 +571,8 @@ describe('change detection', () => {
it('should check component view when called by directive on component node', () => {
@Component({template: '<my-comp dir></my-comp>'})
class MyApp {
@ViewChild(MyComp) myComp !: MyComp;
@ViewChild(Dir) dir !: Dir;
@ViewChild(MyComp) myComp!: MyComp;
@ViewChild(Dir) dir!: Dir;
}
TestBed.configureTestingModule({declarations: [MyComp, Dir, MyApp]});
@ -580,8 +589,8 @@ describe('change detection', () => {
it('should check host component when called by directive on element node', () => {
@Component({template: '{{ value }}<div dir></div>'})
class MyApp {
@ViewChild(MyComp) myComp !: MyComp;
@ViewChild(Dir) dir !: Dir;
@ViewChild(MyComp) myComp!: MyComp;
@ViewChild(Dir) dir!: Dir;
value = '';
}
@ -601,7 +610,7 @@ describe('change detection', () => {
it('should check the host component when called from EmbeddedViewRef', () => {
@Component({template: '{{ name }}<div *ngIf="showing" dir></div>'})
class MyApp {
@ViewChild(Dir) dir !: Dir;
@ViewChild(Dir) dir!: Dir;
showing = true;
name = 'Amelia';
}
@ -642,21 +651,30 @@ describe('change detection', () => {
@Component({template: '<child-comp [inp]="true"></child-comp>'})
class ParentComp {
constructor(public cdr: ChangeDetectorRef) {}
triggerChangeDetection() { this.cdr.detectChanges(); }
triggerChangeDetection() {
this.cdr.detectChanges();
}
}
@Component({template: '{{inp}}', selector: 'child-comp'})
class ChildComp {
@Input()
inp: any = '';
@Input() inp: any = '';
count = 0;
constructor(public parentComp: ParentComp) {}
ngOnInit() { this.check('OnInit'); }
ngAfterContentInit() { this.check('AfterContentInit'); }
ngAfterViewInit() { this.check('AfterViewInit'); }
ngOnChanges() { this.check('OnChanges'); }
ngOnInit() {
this.check('OnInit');
}
ngAfterContentInit() {
this.check('AfterContentInit');
}
ngAfterViewInit() {
this.check('AfterViewInit');
}
ngOnChanges() {
this.check('OnChanges');
}
check(h: string) {
if (h === hook) {
@ -704,8 +722,7 @@ describe('change detection', () => {
`
})
class App {
@ViewChildren('ref')
ref !: QueryList<any>;
@ViewChildren('ref') ref!: QueryList<any>;
visible = false;
@ -737,13 +754,14 @@ describe('change detection', () => {
describe('dynamic views', () => {
@Component({selector: 'structural-comp', template: '{{ value }}'})
class StructuralComp {
@Input()
tmp !: TemplateRef<any>;
@Input() tmp!: TemplateRef<any>;
value = 'one';
constructor(public vcr: ViewContainerRef) {}
create() { return this.vcr.createEmbeddedView(this.tmp, {ctx: this}); }
create() {
return this.vcr.createEmbeddedView(this.tmp, {ctx: this});
}
}
it('should support ViewRef.detectChanges()', () => {
@ -752,7 +770,7 @@ describe('change detection', () => {
'<ng-template #foo let-ctx="ctx">{{ ctx.value }}</ng-template><structural-comp [tmp]="foo"></structural-comp>'
})
class App {
@ViewChild(StructuralComp) structuralComp !: StructuralComp;
@ViewChild(StructuralComp) structuralComp!: StructuralComp;
}
TestBed.configureTestingModule({declarations: [App, StructuralComp]});
@ -781,7 +799,7 @@ describe('change detection', () => {
template: '<ng-template #foo>Template text</ng-template><structural-comp [tmp]="foo">'
})
class App {
@ViewChild(StructuralComp) structuralComp !: StructuralComp;
@ViewChild(StructuralComp) structuralComp!: StructuralComp;
}
TestBed.configureTestingModule({declarations: [App, StructuralComp]});
@ -794,9 +812,7 @@ describe('change detection', () => {
viewRef.detectChanges();
expect(fixture.nativeElement.textContent).toEqual('oneTemplate text');
});
});
});
describe('attach/detach', () => {
@ -807,12 +823,14 @@ describe('change detection', () => {
constructor(public cdr: ChangeDetectorRef) {}
ngDoCheck() { this.doCheckCount++; }
ngDoCheck() {
this.doCheckCount++;
}
}
@Component({template: '<detached-comp></detached-comp>'})
class MyApp {
@ViewChild(DetachedComp) comp !: DetachedComp;
@ViewChild(DetachedComp) comp!: DetachedComp;
constructor(public cdr: ChangeDetectorRef) {}
}
@ -908,22 +926,20 @@ describe('change detection', () => {
});
it('should detach OnPush components properly', () => {
@Component({
selector: 'on-push-comp',
template: '{{ value }}',
changeDetection: ChangeDetectionStrategy.OnPush
})
class OnPushComp {
@Input()
value !: string;
@Input() value!: string;
constructor(public cdr: ChangeDetectorRef) {}
}
@Component({template: '<on-push-comp [value]="value"></on-push-comp>'})
class OnPushApp {
@ViewChild(OnPushComp) onPushComp !: OnPushComp;
@ViewChild(OnPushComp) onPushComp!: OnPushComp;
value = '';
}
@ -946,7 +962,6 @@ describe('change detection', () => {
fixture.detectChanges();
expect(fixture.nativeElement.textContent).toEqual('two');
});
});
describe('markForCheck()', () => {
@ -962,7 +977,9 @@ describe('change detection', () => {
constructor(public cdr: ChangeDetectorRef) {}
ngDoCheck() { this.doCheckCount++; }
ngDoCheck() {
this.doCheckCount++;
}
}
@Component({
@ -970,7 +987,7 @@ describe('change detection', () => {
changeDetection: ChangeDetectionStrategy.OnPush
})
class OnPushParent {
@ViewChild(OnPushComp) comp !: OnPushComp;
@ViewChild(OnPushComp) comp!: OnPushComp;
value = 'one';
}
@ -1023,7 +1040,6 @@ describe('change detection', () => {
fixture.componentInstance.comp.cdr.markForCheck();
fixture.detectChanges();
expect(fixture.nativeElement.textContent).toEqual('two - one');
});
it('should ensure OnPush components in embedded views are checked', () => {
@ -1032,7 +1048,7 @@ describe('change detection', () => {
changeDetection: ChangeDetectionStrategy.OnPush
})
class EmbeddedViewParent {
@ViewChild(OnPushComp) comp !: OnPushComp;
@ViewChild(OnPushComp) comp!: OnPushComp;
value = 'one';
showing = true;
}
@ -1112,13 +1128,21 @@ describe('change detection', () => {
contentCheckCount = 0;
viewCheckCount = 0;
ngDoCheck() { this.doCheckCount++; }
ngDoCheck() {
this.doCheckCount++;
}
ngAfterContentChecked() { this.contentCheckCount++; }
ngAfterContentChecked() {
this.contentCheckCount++;
}
ngAfterViewChecked() { this.viewCheckCount++; }
ngAfterViewChecked() {
this.viewCheckCount++;
}
constructor(public cdr: ChangeDetectorRef) { comp = this; }
constructor(public cdr: ChangeDetectorRef) {
comp = this;
}
}
@Component({template: '{{ value }} - <no-changes-comp></no-changes-comp>'})
@ -1131,7 +1155,9 @@ describe('change detection', () => {
// Custom error handler that just rethrows all the errors from the
// view, rather than logging them out. Used to keep our logs clean.
class RethrowErrorHandler extends ErrorHandler {
handleError(error: any) { throw error; }
handleError(error: any) {
throw error;
}
}
it('should throw if bindings in current view have changed', () => {
@ -1141,7 +1167,9 @@ describe('change detection', () => {
});
const fixture = TestBed.createComponent(NoChangesComp);
expect(() => { fixture.componentInstance.cdr.checkNoChanges(); })
expect(() => {
fixture.componentInstance.cdr.checkNoChanges();
})
.toThrowError(
/ExpressionChangedAfterItHasBeenCheckedError: .+ Previous value: '.*undefined'. Current value: '.*1'/gi);
});
@ -1196,9 +1224,7 @@ describe('change detection', () => {
expect(comp.contentCheckCount).toEqual(1);
expect(comp.viewCheckCount).toEqual(1);
});
});
});
describe('transplanted views', () => {
@ -1216,13 +1242,20 @@ describe('change detection', () => {
</div>
`
})
class InsertComp implements DoCheck,
AfterViewChecked {
get template(): TemplateRef<any> { return declareComp.myTmpl; }
class InsertComp implements DoCheck, AfterViewChecked {
get template(): TemplateRef<any> {
return declareComp.myTmpl;
}
greeting: string = 'Hello';
constructor(public changeDetectorRef: ChangeDetectorRef) { insertComp = this; }
ngDoCheck(): void { logValue = 'Insert'; }
ngAfterViewChecked(): void { logValue = null; }
constructor(public changeDetectorRef: ChangeDetectorRef) {
insertComp = this;
}
ngDoCheck(): void {
logValue = 'Insert';
}
ngAfterViewChecked(): void {
logValue = null;
}
}
@Component({
@ -1234,20 +1267,24 @@ describe('change detection', () => {
</ng-template>
`
})
class DeclareComp implements DoCheck,
AfterViewChecked {
@ViewChild('myTmpl')
myTmpl !: TemplateRef<any>;
class DeclareComp implements DoCheck, AfterViewChecked {
@ViewChild('myTmpl') myTmpl!: TemplateRef<any>;
name: string = 'world';
constructor() { declareComp = this; }
ngDoCheck(): void { logValue = 'Declare'; }
constructor() {
declareComp = this;
}
ngDoCheck(): void {
logValue = 'Declare';
}
logName() {
// This will log when the embedded view gets CD. The `logValue` will show if the CD was from
// `Insert` or from `Declare` component.
log.push(logValue !);
log.push(logValue!);
return this.name;
}
ngAfterViewChecked(): void { logValue = null; }
ngAfterViewChecked(): void {
logValue = null;
}
}
@Component({
@ -1259,15 +1296,17 @@ describe('change detection', () => {
class AppComp {
showDeclare: boolean = true;
showInsert: boolean = true;
constructor() { appComp = this; }
constructor() {
appComp = this;
}
}
let log !: string[];
let logValue !: string | null;
let fixture !: ComponentFixture<AppComp>;
let appComp !: AppComp;
let insertComp !: InsertComp;
let declareComp !: DeclareComp;
let log!: string[];
let logValue!: string|null;
let fixture!: ComponentFixture<AppComp>;
let appComp!: AppComp;
let insertComp!: InsertComp;
let declareComp!: DeclareComp;
beforeEach(() => {
TestBed.configureTestingModule({
@ -1376,16 +1415,16 @@ describe('change detection', () => {
class OnPushComp {
text = 'initial';
constructor(private _cdRef: ChangeDetectorRef){}
constructor(private _cdRef: ChangeDetectorRef) {}
[hookName]() {
[hookName]() {
this._cdRef.markForCheck();
}
}
@Component({template: `<on-push-comp></on-push-comp>`})
class TestApp {
@ViewChild(OnPushComp) onPushComp !: OnPushComp;
@ViewChild(OnPushComp) onPushComp!: OnPushComp;
}
TestBed.configureTestingModule(
@ -1419,16 +1458,16 @@ describe('change detection', () => {
class OnPushComp {
text = 'initial';
constructor(private _cdRef: ChangeDetectorRef){}
constructor(private _cdRef: ChangeDetectorRef) {}
[hookName]() {
[hookName]() {
this._cdRef.markForCheck();
}
}
@Component({template: `<on-push-comp></on-push-comp>`})
class TestApp {
@ViewChild(OnPushComp) onPushComp !: OnPushComp;
@ViewChild(OnPushComp) onPushComp!: OnPushComp;
}
TestBed.configureTestingModule(
@ -1478,7 +1517,9 @@ describe('change detection', () => {
return fixture;
}
function initWithTemplate(template: string) { return initComponent({template}); }
function initWithTemplate(template: string) {
return initComponent({template});
}
function initWithHostBindings(bindings: {[key: string]: string}) {
return initComponent({host: bindings});
}
@ -1612,6 +1653,6 @@ describe('change detection', () => {
});
});
function trim(text: string | null): string {
function trim(text: string|null): string {
return text ? text.replace(/[\s\n]+/gm, ' ').trim() : '';
}

View File

@ -11,7 +11,6 @@ import {TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
describe('@angular/common integration', () => {
describe('NgForOf', () => {
@Directive({selector: '[dir]'})
class MyDirective {
@ -244,7 +243,9 @@ describe('@angular/common integration', () => {
name = 'app';
events: string[] = [];
onClick(value: string, name: string) { this.events.push(value, name); }
onClick(value: string, name: string) {
this.events.push(value, name);
}
}
TestBed.configureTestingModule({declarations: [MultiLevelWithListenerComponent]});
@ -485,7 +486,6 @@ describe('@angular/common integration', () => {
});
describe('NgTemplateOutlet', () => {
it('should create and remove embedded views', () => {
@Component({
selector: 'app-multi',

View File

@ -28,7 +28,9 @@ describe('component', () => {
providers: [{provide: testToken, useExisting: ParentWithOnDestroy}]
})
class ParentWithOnDestroy {
ngOnDestroy() { destroyCalls++; }
ngOnDestroy() {
destroyCalls++;
}
}
@Component({selector: 'child', template: ''})
@ -75,7 +77,7 @@ describe('component', () => {
entryComponents: [OtherComponent]
})
class TestComponent {
@ViewChild('vc', {read: ViewContainerRef, static: true}) vcref !: ViewContainerRef;
@ViewChild('vc', {read: ViewContainerRef, static: true}) vcref!: ViewContainerRef;
constructor(private _cfr: ComponentFactoryResolver) {}
@ -152,7 +154,8 @@ describe('component', () => {
expect(match).toBeDefined();
expect(match.length).toEqual(2);
expect(html).toMatch(
`<leaf ${match[0].replace('_nghost', '_ngcontent')}="" ${match[1]}=""><span ${match[1].replace('_nghost', '_ngcontent')}="">bar</span></leaf></div>`);
`<leaf ${match[0].replace('_nghost', '_ngcontent')}="" ${match[1]}=""><span ${
match[1].replace('_nghost', '_ngcontent')}="">bar</span></leaf></div>`);
});
});
@ -162,7 +165,9 @@ describe('component', () => {
@Component({selector: 'comp-with-destroy', template: ``})
class ComponentWithOnDestroy implements OnDestroy {
ngOnDestroy() { wasOnDestroyCalled = true; }
ngOnDestroy() {
wasOnDestroyCalled = true;
}
}
// This test asserts that the view tree is set up correctly based on the knowledge that this
@ -244,7 +249,7 @@ describe('component', () => {
encapsulation: ViewEncapsulation.Emulated,
})
class Parent {
@ViewChild(Child) childInstance !: Child;
@ViewChild(Child) childInstance!: Child;
constructor(public renderer: Renderer2) {}
}
@ -266,7 +271,9 @@ describe('component', () => {
})
class CompA {
@Input() a: string = '';
ngDoCheck() { log.push('CompA:ngDoCheck'); }
ngDoCheck() {
log.push('CompA:ngDoCheck');
}
}
@Component({
@ -275,7 +282,9 @@ describe('component', () => {
})
class CompB {
@Input() b: string = '';
ngDoCheck() { log.push('CompB:ngDoCheck'); }
ngDoCheck() {
log.push('CompB:ngDoCheck');
}
}
@Component({template: `<span></span>`})
@ -435,14 +444,14 @@ describe('component', () => {
fixture.detectChanges();
// Create an instance of DynamicComponent and provide host element *reference*
let targetEl = document.getElementById('dynamic-comp-root-a') !;
let targetEl = document.getElementById('dynamic-comp-root-a')!;
fixture.componentInstance.createDynamicComponent(targetEl);
fixture.detectChanges();
expect(targetEl.innerHTML).not.toContain('Existing content in slot A');
expect(targetEl.innerHTML).toContain('DynamicComponent Content');
// Create an instance of DynamicComponent and provide host element *selector*
targetEl = document.getElementById('dynamic-comp-root-b') !;
targetEl = document.getElementById('dynamic-comp-root-b')!;
fixture.componentInstance.createDynamicComponent('#dynamic-comp-root-b');
fixture.detectChanges();
expect(targetEl.innerHTML).not.toContain('Existing content in slot B');
@ -453,7 +462,8 @@ describe('component', () => {
() => runTestWithRenderer([{provide: RendererFactory2, useClass: DomRendererFactory2}]));
onlyInIvy('Renderer3 is supported only in Ivy')
.it('with Renderer3', () => runTestWithRenderer(
[{provide: RendererFactory2, useValue: domRendererFactory3}]));
.it('with Renderer3',
() =>
runTestWithRenderer([{provide: RendererFactory2, useValue: domRendererFactory3}]));
});
});

View File

@ -14,7 +14,6 @@ import {By} from '@angular/platform-browser';
import {expect} from '@angular/platform-browser/testing/src/matchers';
describe('projection', () => {
function getElementHtml(element: HTMLElement) {
return element.innerHTML.replace(/<!--(\W|\w)*?-->/g, '')
.replace(/\sng-reflect-\S*="[^"]*"/g, '');
@ -328,11 +327,13 @@ describe('projection', () => {
@Directive({selector: '[trigger]'})
class Trigger {
@Input() trigger !: Comp;
@Input() trigger!: Comp;
constructor(public vcr: ViewContainerRef) {}
open() { this.vcr.createEmbeddedView(this.trigger.template); }
open() {
this.vcr.createEmbeddedView(this.trigger.template);
}
}
@Component({
@ -861,7 +862,6 @@ describe('projection', () => {
expect(getElementHtml(fixture.nativeElement))
.toEqual('<child><span title="Some title">Has title</span></child>');
});
it('should match selectors against projected containers', () => {
@ -934,7 +934,9 @@ describe('projection', () => {
it('should project content if the change detector has been detached', () => {
@Component({selector: 'my-comp', template: '<ng-content></ng-content>'})
class MyComp {
constructor(changeDetectorRef: ChangeDetectorRef) { changeDetectorRef.detach(); }
constructor(changeDetectorRef: ChangeDetectorRef) {
changeDetectorRef.detach();
}
}
@Component({
@ -1108,7 +1110,9 @@ describe('projection', () => {
@Directive({selector: 'div'})
class DivDirective {
constructor() { divDirectives++; }
constructor() {
divDirectives++;
}
}
@Component({
@ -1135,7 +1139,9 @@ describe('projection', () => {
@Directive({selector: '[x]'})
class XDirective {
constructor() { xDirectives++; }
constructor() {
xDirectives++;
}
}
@Component({
@ -1162,7 +1168,9 @@ describe('projection', () => {
@Directive({selector: '.x'})
class XDirective {
constructor() { xDirectives++; }
constructor() {
xDirectives++;
}
}
@Component({
@ -1199,7 +1207,9 @@ describe('projection', () => {
{id: 2, name: 'two'},
{id: 3, name: 'three'},
];
getItemId(item: {id: number}) { return item.id; }
getItemId(item: {id: number}) {
return item.id;
}
}
TestBed.configureTestingModule({declarations: [SelectedNgContentComp, SelectorMainComp]});
@ -1265,7 +1275,9 @@ describe('projection', () => {
@Directive({selector: '[x]'})
class XDirective {
constructor() { xDirectives++; }
constructor() {
xDirectives++;
}
}
@Component({
@ -1293,7 +1305,9 @@ describe('projection', () => {
@Directive({selector: '.x'})
class XDirective {
constructor() { xDirectives++; }
constructor() {
xDirectives++;
}
}
@Component({

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Component, NgModule, ɵɵCopyDefinitionFeature as CopyDefinitionFeature, ɵɵInheritDefinitionFeature as InheritDefinitionFeature, ɵɵdefineComponent as defineComponent} from '@angular/core';
import {Component, NgModule, ɵɵCopyDefinitionFeature as CopyDefinitionFeature, ɵɵdefineComponent as defineComponent, ɵɵInheritDefinitionFeature as InheritDefinitionFeature} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {onlyInIvy} from '@angular/private/testing';
@ -14,20 +14,22 @@ describe('Ivy CopyDefinitionFeature', () => {
onlyInIvy('this feature is not required in View Engine')
.it('should copy the template function of a component definition from parent to child',
() => {
// It would be nice if the base component could be JIT compiled. However, this creates
// a getter for ɵcmp which precludes adding a static definition of that field for the
// child class.
// TODO(alxhub): see if there's a cleaner way to do this.
class BaseComponent {
name !: string;
name!: string;
static ɵcmp = defineComponent({
type: BaseComponent,
selectors: [['some-cmp']],
decls: 0,
vars: 0,
inputs: {name: 'name'},
template: function BaseComponent_Template(rf, ctx) { ctx.rendered = true; },
template:
function BaseComponent_Template(rf, ctx) {
ctx.rendered = true;
},
encapsulation: 2
});
static ɵfac = function BaseComponent_Factory(t: any) {

View File

@ -14,9 +14,7 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
import {onlyInIvy} from '@angular/private/testing';
describe('Debug Representation', () => {
onlyInIvy('Ivy specific').it('should generate a human readable version', () => {
@Component({selector: 'my-comp', template: '<div id="123">Hello World</div>'})
class MyComponent {
}
@ -25,11 +23,11 @@ describe('Debug Representation', () => {
const fixture = TestBed.createComponent(MyComponent);
fixture.detectChanges();
const hostView = toDebug(getLContext(fixture.componentInstance) !.lView);
const hostView = toDebug(getLContext(fixture.componentInstance)!.lView);
expect(hostView.host).toEqual(null);
const myCompView = hostView.childViews[0] as LViewDebug;
expect(myCompView.host).toContain('<div id="123">Hello World</div>');
expect(myCompView.nodes ![0].html).toEqual('<div id="123">');
expect(myCompView.nodes ![0].nodes ![0].html).toEqual('Hello World');
expect(myCompView.nodes![0].html).toEqual('<div id="123">');
expect(myCompView.nodes![0].nodes![0].html).toEqual('Hello World');
});
});

View File

@ -7,7 +7,7 @@
*/
import {CommonModule} from '@angular/common';
import {Attribute, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, EventEmitter, Host, HostBinding, INJECTOR, Inject, Injectable, InjectionToken, Injector, Input, LOCALE_ID, ModuleWithProviders, NgModule, NgZone, Optional, Output, Pipe, PipeTransform, Self, SkipSelf, TemplateRef, ViewChild, ViewContainerRef, forwardRef, ɵDEFAULT_LOCALE_ID as DEFAULT_LOCALE_ID} from '@angular/core';
import {Attribute, ChangeDetectorRef, Component, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, EventEmitter, forwardRef, Host, HostBinding, Inject, Injectable, InjectionToken, INJECTOR, Injector, Input, LOCALE_ID, ModuleWithProviders, NgModule, NgZone, Optional, Output, Pipe, PipeTransform, Self, SkipSelf, TemplateRef, ViewChild, ViewContainerRef, ɵDEFAULT_LOCALE_ID as DEFAULT_LOCALE_ID} from '@angular/core';
import {ɵINJECTOR_SCOPE} from '@angular/core/src/core';
import {ViewRef} from '@angular/core/src/render3/view_ref';
import {TestBed} from '@angular/core/testing';
@ -60,14 +60,15 @@ describe('di', () => {
});
describe('directive injection', () => {
let log: string[] = [];
@Directive({selector: '[dirB]', exportAs: 'dirB'})
class DirectiveB {
@Input() value = 'DirB';
constructor() { log.push(this.value); }
constructor() {
log.push(this.value);
}
}
beforeEach(() => log = []);
@ -82,7 +83,9 @@ describe('di', () => {
class DirectiveC {
value: string;
constructor(dirA: DirectiveA, dirB: DirectiveB) { this.value = dirA.value + dirB.value; }
constructor(dirA: DirectiveA, dirB: DirectiveB) {
this.value = dirA.value + dirB.value;
}
}
@Component({
@ -108,7 +111,9 @@ describe('di', () => {
class DirectiveA {
value = 'dirA';
constructor(dirB: DirectiveB) { log.push(`DirA (dep: ${dirB.value})`); }
constructor(dirB: DirectiveB) {
log.push(`DirA (dep: ${dirB.value})`);
}
}
@Component({template: '<div dirA dirB></div>'})
@ -127,7 +132,9 @@ describe('di', () => {
class DirectiveA {
value = 'dirA';
constructor(dirB: DirectiveB) { log.push(`DirA (dep: ${dirB.value})`); }
constructor(dirB: DirectiveB) {
log.push(`DirA (dep: ${dirB.value})`);
}
}
// - dirB is know to the node injectors
@ -150,7 +157,9 @@ describe('di', () => {
it('should instantiate injected directives before components', () => {
@Component({selector: 'my-comp', template: ''})
class MyComp {
constructor(dirB: DirectiveB) { log.push(`Comp (dep: ${dirB.value})`); }
constructor(dirB: DirectiveB) {
log.push(`Comp (dep: ${dirB.value})`);
}
}
@Component({template: '<my-comp dirB></my-comp>'})
@ -167,7 +176,9 @@ describe('di', () => {
it('should inject directives in the correct order in a for loop', () => {
@Directive({selector: '[dirA]'})
class DirectiveA {
constructor(dir: DirectiveB) { log.push(`DirA (dep: ${dir.value})`); }
constructor(dir: DirectiveB) {
log.push(`DirA (dep: ${dir.value})`);
}
}
@Component({template: '<div dirA dirB *ngFor="let i of array"></div>'})
@ -188,14 +199,18 @@ describe('di', () => {
class DirectiveA {
value = 'DirA';
constructor() { log.push(this.value); }
constructor() {
log.push(this.value);
}
}
@Directive({selector: '[dirC]'})
class DirectiveC {
value = 'DirC';
constructor() { log.push(this.value); }
constructor() {
log.push(this.value);
}
}
@Directive({selector: '[dirB]'})
@ -221,26 +236,34 @@ describe('di', () => {
class DirectiveC {
value = 'DirC';
constructor(dirB: DirectiveB) { log.push(`DirC (dep: ${dirB.value})`); }
constructor(dirB: DirectiveB) {
log.push(`DirC (dep: ${dirB.value})`);
}
}
@Directive({selector: '[dirA]'})
class DirectiveA {
value = 'DirA';
constructor(dirC: DirectiveC) { log.push(`DirA (dep: ${dirC.value})`); }
constructor(dirC: DirectiveC) {
log.push(`DirA (dep: ${dirC.value})`);
}
}
@Directive({selector: '[dirD]'})
class DirectiveD {
value = 'DirD';
constructor(dirA: DirectiveA) { log.push(`DirD (dep: ${dirA.value})`); }
constructor(dirA: DirectiveA) {
log.push(`DirD (dep: ${dirA.value})`);
}
}
@Component({selector: 'my-comp', template: ''})
class MyComp {
constructor(dirD: DirectiveD) { log.push(`Comp (dep: ${dirD.value})`); }
constructor(dirD: DirectiveD) {
log.push(`Comp (dep: ${dirD.value})`);
}
}
@Component({template: '<my-comp dirA dirB dirC dirD></my-comp>'})
@ -291,7 +314,9 @@ describe('di', () => {
@Directive({selector: '[dirA]'})
class DirectiveA {
constructor(dirB: DirectiveB) { log.push(`DirA (dep: DirB - ${dirB.count})`); }
constructor(dirB: DirectiveB) {
log.push(`DirA (dep: DirB - ${dirB.count})`);
}
}
@Component({selector: 'my-comp', template: '<div dirA dirB></div>'})
@ -310,7 +335,6 @@ describe('di', () => {
});
describe('dependencies in parent views', () => {
@Directive({selector: '[dirA]', exportAs: 'dirA'})
class DirectiveA {
injector: Injector;
@ -385,11 +409,13 @@ describe('di', () => {
it('should find dependencies in declaration tree of ng-template (not insertion tree)', () => {
@Directive({selector: '[structuralDir]'})
class StructuralDirective {
@Input() tmp !: TemplateRef<any>;
@Input() tmp!: TemplateRef<any>;
constructor(public vcr: ViewContainerRef) {}
create() { this.vcr.createEmbeddedView(this.tmp); }
create() {
this.vcr.createEmbeddedView(this.tmp);
}
}
@Component({
@ -405,7 +431,7 @@ describe('di', () => {
</div>`
})
class MyComp {
@ViewChild(StructuralDirective) structuralDir !: StructuralDirective;
@ViewChild(StructuralDirective) structuralDir!: StructuralDirective;
}
TestBed.configureTestingModule(
@ -449,8 +475,8 @@ describe('di', () => {
</div>`
})
class MyApp {
@ViewChild(HostBindingDirective) hostBindingDir !: HostBindingDirective;
@ViewChild(DirectiveA) dirA !: DirectiveA;
@ViewChild(HostBindingDirective) hostBindingDir!: HostBindingDirective;
@ViewChild(DirectiveA) dirA!: DirectiveA;
}
TestBed.configureTestingModule(
@ -501,15 +527,19 @@ describe('di', () => {
})
class MyApp {
@ViewChild('childOrigin', {read: ViewContainerRef, static: true})
childOrigin !: ViewContainerRef;
childOrigin!: ViewContainerRef;
@ViewChild('childOriginWithDirB', {read: ViewContainerRef, static: true})
childOriginWithDirB !: ViewContainerRef;
childOriginWithDirB!: ViewContainerRef;
childFactory = this.resolver.resolveComponentFactory(Child);
constructor(readonly resolver: ComponentFactoryResolver, readonly injector: Injector) {}
addChild() { return this.childOrigin.createComponent(this.childFactory); }
addChildWithDirB() { return this.childOriginWithDirB.createComponent(this.childFactory); }
addChild() {
return this.childOrigin.createComponent(this.childFactory);
}
addChildWithDirB() {
return this.childOriginWithDirB.createComponent(this.childFactory);
}
}
const fixture =
@ -604,24 +634,21 @@ describe('di', () => {
});
describe('flags', () => {
@Directive({selector: '[dirB]'})
class DirectiveB {
@Input('dirB') value = '';
}
describe('Optional', () => {
@Directive({selector: '[dirA]'})
class DirectiveA {
constructor(@Optional() public dirB: DirectiveB) {}
}
it('should not throw if dependency is @Optional (module injector)', () => {
@Component({template: '<div dirA></div>'})
class MyComp {
@ViewChild(DirectiveA) dirA !: DirectiveA;
@ViewChild(DirectiveA) dirA!: DirectiveA;
}
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp]});
@ -633,7 +660,6 @@ describe('di', () => {
});
it('should return null if @Optional dependency has @Self flag', () => {
@Directive({selector: '[dirC]'})
class DirectiveC {
constructor(@Optional() @Self() public dirB: DirectiveB) {}
@ -641,7 +667,7 @@ describe('di', () => {
@Component({template: '<div dirC></div>'})
class MyComp {
@ViewChild(DirectiveC) dirC !: DirectiveC;
@ViewChild(DirectiveC) dirC!: DirectiveC;
}
TestBed.configureTestingModule({declarations: [DirectiveC, MyComp]});
@ -653,7 +679,6 @@ describe('di', () => {
});
it('should not throw if dependency is @Optional but defined elsewhere', () => {
@Directive({selector: '[dirC]'})
class DirectiveC {
constructor(@Optional() public dirB: DirectiveB) {}
@ -661,7 +686,7 @@ describe('di', () => {
@Component({template: '<div dirB></div><div dirC></div>'})
class MyComp {
@ViewChild(DirectiveC) dirC !: DirectiveC;
@ViewChild(DirectiveC) dirC!: DirectiveC;
}
TestBed.configureTestingModule({declarations: [DirectiveB, DirectiveC, MyComp]});
@ -674,7 +699,6 @@ describe('di', () => {
});
it('should skip the current node with @SkipSelf', () => {
@Directive({selector: '[dirA]'})
class DirectiveA {
constructor(@SkipSelf() public dirB: DirectiveB) {}
@ -682,12 +706,12 @@ describe('di', () => {
@Component({selector: 'my-comp', template: '<div dirA dirB="self"></div>'})
class MyComp {
@ViewChild(DirectiveA) dirA !: DirectiveA;
@ViewChild(DirectiveA) dirA!: DirectiveA;
}
@Component({template: '<my-comp dirB="parent"></my-comp>'})
class MyApp {
@ViewChild(MyComp) myComp !: MyComp;
@ViewChild(MyComp) myComp!: MyComp;
}
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyComp, MyApp]});
@ -700,7 +724,6 @@ describe('di', () => {
onlyInIvy('Ivy has different error message when dependency is not found')
.it('should check only the current node with @Self', () => {
@Directive({selector: '[dirA]'})
class DirectiveA {
constructor(@Self() public dirB: DirectiveB) {}
@ -732,12 +755,12 @@ describe('di', () => {
viewProviders: [{provide: String, useValue: 'Foo'}]
})
class MyComp {
@ViewChild(DirectiveString) dirString !: DirectiveString;
@ViewChild(DirectiveString) dirString!: DirectiveString;
}
@Component({template: '<my-comp></my-comp>'})
class MyApp {
@ViewChild(MyComp) myComp !: MyComp;
@ViewChild(MyComp) myComp!: MyComp;
}
TestBed.configureTestingModule({declarations: [DirectiveString, MyComp, MyApp]});
@ -756,12 +779,12 @@ describe('di', () => {
@Component({selector: 'my-comp', template: '<div dirComp></div>'})
class MyComp {
@ViewChild(DirectiveComp) dirComp !: DirectiveComp;
@ViewChild(DirectiveComp) dirComp!: DirectiveComp;
}
@Component({template: '<my-comp></my-comp>'})
class MyApp {
@ViewChild(MyComp) myComp !: MyComp;
@ViewChild(MyComp) myComp!: MyComp;
}
TestBed.configureTestingModule({declarations: [DirectiveComp, MyComp, MyApp]});
@ -820,7 +843,7 @@ describe('di', () => {
@Component({template: '<my-comp dirB></my-comp>'})
class MyApp {
@ViewChild(MyComp) myComp !: MyComp;
@ViewChild(MyComp) myComp!: MyComp;
}
TestBed.configureTestingModule(
@ -837,8 +860,8 @@ describe('di', () => {
@Component({template: '<div dirB><div *ngIf="showing" dirA></div></div>'})
class MyApp {
showing = false;
@ViewChild(DirectiveA) dirA !: DirectiveA;
@ViewChild(DirectiveB) dirB !: DirectiveB;
@ViewChild(DirectiveA) dirA!: DirectiveA;
@ViewChild(DirectiveB) dirB!: DirectiveB;
}
TestBed.configureTestingModule({declarations: [DirectiveA, DirectiveB, MyApp]});
@ -885,7 +908,9 @@ describe('di', () => {
providers: [{provide: ControlContainer, useExisting: GroupDirective}]
})
class GroupDirective {
constructor() { controlContainers.push(this); }
constructor() {
controlContainers.push(this);
}
}
@Directive({selector: '[control]'})
@ -919,7 +944,7 @@ describe('di', () => {
const fixture = TestBed.createComponent(MyApp);
expect(fixture.nativeElement.innerHTML)
.toBe('<div group=""><my-comp><input control=""></my-comp></div>');
expect(controlContainers).toEqual([injectedControlContainer !]);
expect(controlContainers).toEqual([injectedControlContainer!]);
});
});
});
@ -958,7 +983,6 @@ describe('di', () => {
});
describe('service injection', () => {
it('should create instance even when no injector present', () => {
@Injectable({providedIn: 'root'})
class MyService {
@ -1063,7 +1087,6 @@ describe('di', () => {
expect(fixture.componentInstance.myService instanceof MyService).toBe(true);
expect(fixture.componentInstance.myOtherService instanceof MyOtherService).toBe(true);
});
});
describe('service injection with useClass', () => {
@ -1075,7 +1098,9 @@ describe('di', () => {
@Injectable({providedIn: 'root'})
class BarService {
constructor(public dep: BarServiceDep) {}
getMessage() { return 'bar'; }
getMessage() {
return 'bar';
}
}
@Injectable({providedIn: 'root'})
@ -1086,7 +1111,9 @@ describe('di', () => {
@Injectable({providedIn: 'root', useClass: BarService})
class FooService {
constructor(public dep: FooServiceDep) {}
getMessage() { return 'foo'; }
getMessage() {
return 'foo';
}
}
it('should use @Injectable useClass config when token is not provided', () => {
@ -1094,18 +1121,20 @@ describe('di', () => {
@Component({template: ''})
class App {
constructor(service: FooService) { provider = service; }
constructor(service: FooService) {
provider = service;
}
}
TestBed.configureTestingModule({declarations: [App]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
expect(provider !.getMessage()).toBe('bar');
expect(provider!.getMessage()).toBe('bar');
// ViewEngine incorrectly uses the original class DI config, instead of the one from useClass.
if (ivyEnabled) {
expect(provider !.dep.name).toBe('BarServiceDep');
expect(provider!.dep.name).toBe('BarServiceDep');
}
});
@ -1115,7 +1144,9 @@ describe('di', () => {
@Component({template: ''})
class App {
constructor(service: FooService) { provider = service; }
constructor(service: FooService) {
provider = service;
}
}
TestBed.configureTestingModule(
@ -1123,7 +1154,7 @@ describe('di', () => {
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
expect(provider !.getMessage()).toBe('foo');
expect(provider!.getMessage()).toBe('foo');
});
@ -1144,13 +1175,13 @@ describe('di', () => {
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
expect(directProvider !.getMessage()).toBe('bar');
expect(overriddenProvider !.getMessage()).toBe('foo');
expect(directProvider!.getMessage()).toBe('bar');
expect(overriddenProvider!.getMessage()).toBe('foo');
// ViewEngine incorrectly uses the original class DI config, instead of the one from useClass.
if (ivyEnabled) {
expect(directProvider !.dep.name).toBe('BarServiceDep');
expect(overriddenProvider !.dep.name).toBe('FooServiceDep');
expect(directProvider!.dep.name).toBe('BarServiceDep');
expect(overriddenProvider!.dep.name).toBe('FooServiceDep');
}
});
@ -1160,20 +1191,21 @@ describe('di', () => {
@Component({template: ''})
class App {
constructor(service: FooService) { provider = service; }
constructor(service: FooService) {
provider = service;
}
}
TestBed.configureTestingModule({declarations: [App], providers: [FooService]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
expect(provider !.getMessage()).toBe('foo');
expect(provider !.dep.name).toBe('FooServiceDep');
expect(provider!.getMessage()).toBe('foo');
expect(provider!.dep.name).toBe('FooServiceDep');
});
});
describe('inject', () => {
it('should inject from parent view', () => {
@Directive({selector: '[parentDir]'})
class ParentDirective {
@ -1182,7 +1214,9 @@ describe('di', () => {
@Directive({selector: '[childDir]', exportAs: 'childDir'})
class ChildDirective {
value: string;
constructor(public parent: ParentDirective) { this.value = parent.constructor.name; }
constructor(public parent: ParentDirective) {
this.value = parent.constructor.name;
}
}
@Directive({selector: '[child2Dir]', exportAs: 'child2Dir'})
@ -1214,9 +1248,7 @@ describe('di', () => {
});
describe('Special tokens', () => {
describe('Injector', () => {
it('should inject the injector', () => {
@Directive({selector: '[injectorDir]'})
class InjectorDir {
@ -1230,8 +1262,8 @@ describe('di', () => {
@Component({template: '<div injectorDir otherInjectorDir></div>'})
class MyComp {
@ViewChild(InjectorDir) injectorDir !: InjectorDir;
@ViewChild(OtherInjectorDir) otherInjectorDir !: OtherInjectorDir;
@ViewChild(InjectorDir) injectorDir!: InjectorDir;
@ViewChild(OtherInjectorDir) otherInjectorDir!: OtherInjectorDir;
}
TestBed.configureTestingModule({declarations: [InjectorDir, OtherInjectorDir, MyComp]});
@ -1256,7 +1288,7 @@ describe('di', () => {
@Component({template: '<div injectorDir></div>'})
class MyComp {
@ViewChild(InjectorDir) injectorDir !: InjectorDir;
@ViewChild(InjectorDir) injectorDir!: InjectorDir;
}
TestBed.configureTestingModule({declarations: [InjectorDir, MyComp]});
@ -1273,7 +1305,6 @@ describe('di', () => {
});
describe('ElementRef', () => {
it('should create directive with ElementRef dependencies', () => {
@Directive({selector: '[dir]'})
class MyDir {
@ -1293,8 +1324,8 @@ describe('di', () => {
@Component({template: '<div dir otherDir></div>'})
class MyComp {
@ViewChild(MyDir) directive !: MyDir;
@ViewChild(MyOtherDir) otherDirective !: MyOtherDir;
@ViewChild(MyDir) directive!: MyDir;
@ViewChild(MyOtherDir) otherDirective!: MyOtherDir;
}
TestBed.configureTestingModule({declarations: [MyDir, MyOtherDir, MyComp]});
@ -1325,7 +1356,7 @@ describe('di', () => {
@Component({template: '<ng-template dir></ng-template>'})
class MyComp {
@ViewChild(MyDir) directive !: MyDir;
@ViewChild(MyDir) directive!: MyDir;
}
TestBed.configureTestingModule({declarations: [MyDir, MyComp]});
@ -1346,7 +1377,9 @@ describe('di', () => {
constructor(protected zone: NgZone) {
this.subject = new BehaviorSubject<any>(1);
// trigger change detection
zone.run(() => { this.subject.next(2); });
zone.run(() => {
this.subject.next(2);
});
}
}
@ -1360,7 +1393,7 @@ describe('di', () => {
template: `<div id="test-id" dir></div>`,
})
class ChildComp {
@ViewChild(DirectiveA) directive !: DirectiveA;
@ViewChild(DirectiveA) directive!: DirectiveA;
}
@Component({
@ -1368,7 +1401,7 @@ describe('di', () => {
template: '...',
})
class RootComp {
public childCompRef !: ComponentRef<ChildComp>;
public childCompRef!: ComponentRef<ChildComp>;
constructor(
public factoryResolver: ComponentFactoryResolver, public vcr: ViewContainerRef) {}
@ -1404,7 +1437,6 @@ describe('di', () => {
});
describe('TemplateRef', () => {
@Directive({selector: '[dir]', exportAs: 'dir'})
class MyDir {
value: string;
@ -1426,8 +1458,8 @@ describe('di', () => {
template: '<ng-template dir otherDir #dir="dir" #otherDir="otherDir"></ng-template>'
})
class MyComp {
@ViewChild(MyDir) directive !: MyDir;
@ViewChild(MyOtherDir) otherDirective !: MyOtherDir;
@ViewChild(MyDir) directive!: MyDir;
@ViewChild(MyOtherDir) otherDirective!: MyOtherDir;
}
TestBed.configureTestingModule({declarations: [MyDir, MyOtherDir, MyComp]});
@ -1470,7 +1502,7 @@ describe('di', () => {
}
@Component({template: '<div optionalDir></div>'})
class MyComp {
@ViewChild(OptionalDir) directive !: OptionalDir;
@ViewChild(OptionalDir) directive!: OptionalDir;
}
TestBed.configureTestingModule({declarations: [OptionalDir, MyComp]});
@ -1499,8 +1531,8 @@ describe('di', () => {
}
@Component({template: '<div dir otherDir #dir="dir" #otherDir="otherDir"></div>'})
class MyComp {
@ViewChild(MyDir) directive !: MyDir;
@ViewChild(MyOtherDir) otherDirective !: MyOtherDir;
@ViewChild(MyDir) directive!: MyDir;
@ViewChild(MyOtherDir) otherDirective!: MyOtherDir;
}
TestBed.configureTestingModule({declarations: [MyDir, MyOtherDir, MyComp]});
@ -1524,12 +1556,13 @@ describe('di', () => {
template: `<ng-template #tmpl>Test</ng-template>`,
})
class Root {
@ViewChild(TemplateRef, {static: true})
tmpl !: TemplateRef<any>;
@ViewChild(TemplateRef, {static: true}) tmpl!: TemplateRef<any>;
constructor(public vcr: ViewContainerRef, public vcr2: ViewContainerRef) {}
ngOnInit(): void { this.vcr.createEmbeddedView(this.tmpl); }
ngOnInit(): void {
this.vcr.createEmbeddedView(this.tmpl);
}
}
TestBed.configureTestingModule({
@ -1551,11 +1584,12 @@ describe('di', () => {
});
describe('ChangeDetectorRef', () => {
@Directive({selector: '[dir]', exportAs: 'dir'})
class MyDir {
value: string;
constructor(public cdr: ChangeDetectorRef) { this.value = (cdr.constructor as any).name; }
constructor(public cdr: ChangeDetectorRef) {
this.value = (cdr.constructor as any).name;
}
}
@Directive({selector: '[otherDir]', exportAs: 'otherDir'})
class MyOtherDir {
@ -1571,9 +1605,13 @@ describe('di', () => {
@Pipe({name: 'pipe'})
class MyPipe implements PipeTransform {
constructor(public cdr: ChangeDetectorRef) { pipeInstance = this; }
constructor(public cdr: ChangeDetectorRef) {
pipeInstance = this;
}
transform(value: any): any { return value; }
transform(value: any): any {
return value;
}
}
@Component({
@ -1589,29 +1627,29 @@ describe('di', () => {
TestBed.configureTestingModule({declarations: [MyApp, MyPipe], imports: [CommonModule]});
const fixture = TestBed.createComponent(MyApp);
fixture.detectChanges();
expect((pipeInstance !.cdr as ViewRef<MyApp>).context).toBe(fixture.componentInstance);
expect((pipeInstance!.cdr as ViewRef<MyApp>).context).toBe(fixture.componentInstance);
});
it('should inject current component ChangeDetectorRef into directives on the same node as components',
() => {
@Component({selector: 'my-app', template: '<my-comp dir otherDir #dir="dir"></my-comp>'})
class MyApp {
@ViewChild(MyComp) component !: MyComp;
@ViewChild(MyDir) directive !: MyDir;
@ViewChild(MyOtherDir) otherDirective !: MyOtherDir;
@ViewChild(MyComp) component!: MyComp;
@ViewChild(MyDir) directive!: MyDir;
@ViewChild(MyOtherDir) otherDirective!: MyOtherDir;
}
TestBed.configureTestingModule({declarations: [MyApp, MyComp, MyDir, MyOtherDir]});
const fixture = TestBed.createComponent(MyApp);
fixture.detectChanges();
const app = fixture.componentInstance;
const comp = fixture.componentInstance.component;
expect((comp !.cdr as ViewRef<MyComp>).context).toBe(comp);
expect((comp!.cdr as ViewRef<MyComp>).context).toBe(comp);
// ChangeDetectorRef is the token, ViewRef has historically been the constructor
expect(app.directive.value).toContain('ViewRef');
// Each ChangeDetectorRef instance should be unique
expect(app.directive !.cdr).not.toBe(comp !.cdr);
expect(app.directive !.cdr).not.toBe(app.otherDirective !.cdr);
expect(app.directive!.cdr).not.toBe(comp!.cdr);
expect(app.directive!.cdr).not.toBe(app.otherDirective!.cdr);
});
it('should inject host component ChangeDetectorRef into directives on normal elements',
@ -1619,20 +1657,20 @@ describe('di', () => {
@Component({selector: 'my-comp', template: '<div dir otherDir #dir="dir"></div>'})
class MyComp {
constructor(public cdr: ChangeDetectorRef) {}
@ViewChild(MyDir) directive !: MyDir;
@ViewChild(MyOtherDir) otherDirective !: MyOtherDir;
@ViewChild(MyDir) directive!: MyDir;
@ViewChild(MyOtherDir) otherDirective!: MyOtherDir;
}
TestBed.configureTestingModule({declarations: [MyComp, MyDir, MyOtherDir]});
const fixture = TestBed.createComponent(MyComp);
fixture.detectChanges();
const comp = fixture.componentInstance;
expect((comp !.cdr as ViewRef<MyComp>).context).toBe(comp);
expect((comp!.cdr as ViewRef<MyComp>).context).toBe(comp);
// ChangeDetectorRef is the token, ViewRef has historically been the constructor
expect(comp.directive.value).toContain('ViewRef');
// Each ChangeDetectorRef instance should be unique
expect(comp.directive !.cdr).not.toBe(comp.cdr);
expect(comp.directive !.cdr).not.toBe(comp.otherDirective !.cdr);
expect(comp.directive!.cdr).not.toBe(comp.cdr);
expect(comp.directive!.cdr).not.toBe(comp.otherDirective!.cdr);
});
it('should inject host component ChangeDetectorRef into directives in a component\'s ContentChildren',
@ -1646,22 +1684,22 @@ describe('di', () => {
})
class MyApp {
constructor(public cdr: ChangeDetectorRef) {}
@ViewChild(MyComp) component !: MyComp;
@ViewChild(MyDir) directive !: MyDir;
@ViewChild(MyOtherDir) otherDirective !: MyOtherDir;
@ViewChild(MyComp) component!: MyComp;
@ViewChild(MyDir) directive!: MyDir;
@ViewChild(MyOtherDir) otherDirective!: MyOtherDir;
}
TestBed.configureTestingModule({declarations: [MyApp, MyComp, MyDir, MyOtherDir]});
const fixture = TestBed.createComponent(MyApp);
fixture.detectChanges();
const app = fixture.componentInstance;
expect((app !.cdr as ViewRef<MyApp>).context).toBe(app);
expect((app!.cdr as ViewRef<MyApp>).context).toBe(app);
const comp = fixture.componentInstance.component;
// ChangeDetectorRef is the token, ViewRef has historically been the constructor
expect(app.directive.value).toContain('ViewRef');
// Each ChangeDetectorRef instance should be unique
expect(app.directive !.cdr).not.toBe(comp.cdr);
expect(app.directive !.cdr).not.toBe(app.otherDirective !.cdr);
expect(app.directive!.cdr).not.toBe(comp.cdr);
expect(app.directive!.cdr).not.toBe(app.otherDirective!.cdr);
});
it('should inject host component ChangeDetectorRef into directives in embedded views', () => {
@ -1674,21 +1712,21 @@ describe('di', () => {
class MyComp {
showing = true;
constructor(public cdr: ChangeDetectorRef) {}
@ViewChild(MyDir) directive !: MyDir;
@ViewChild(MyOtherDir) otherDirective !: MyOtherDir;
@ViewChild(MyDir) directive!: MyDir;
@ViewChild(MyOtherDir) otherDirective!: MyOtherDir;
}
TestBed.configureTestingModule({declarations: [MyComp, MyDir, MyOtherDir]});
const fixture = TestBed.createComponent(MyComp);
fixture.detectChanges();
const comp = fixture.componentInstance;
expect((comp !.cdr as ViewRef<MyComp>).context).toBe(comp);
expect((comp!.cdr as ViewRef<MyComp>).context).toBe(comp);
// ChangeDetectorRef is the token, ViewRef has historically been the constructor
expect(comp.directive.value).toContain('ViewRef');
// Each ChangeDetectorRef instance should be unique
expect(comp.directive !.cdr).not.toBe(comp.cdr);
expect(comp.directive !.cdr).not.toBe(comp.otherDirective !.cdr);
expect(comp.directive!.cdr).not.toBe(comp.cdr);
expect(comp.directive!.cdr).not.toBe(comp.otherDirective!.cdr);
});
it('should inject host component ChangeDetectorRef into directives on containers', () => {
@ -1697,21 +1735,21 @@ describe('di', () => {
class MyComp {
showing = true;
constructor(public cdr: ChangeDetectorRef) {}
@ViewChild(MyDir) directive !: MyDir;
@ViewChild(MyOtherDir) otherDirective !: MyOtherDir;
@ViewChild(MyDir) directive!: MyDir;
@ViewChild(MyOtherDir) otherDirective!: MyOtherDir;
}
TestBed.configureTestingModule({declarations: [MyComp, MyDir, MyOtherDir]});
const fixture = TestBed.createComponent(MyComp);
fixture.detectChanges();
const comp = fixture.componentInstance;
expect((comp !.cdr as ViewRef<MyComp>).context).toBe(comp);
expect((comp!.cdr as ViewRef<MyComp>).context).toBe(comp);
// ChangeDetectorRef is the token, ViewRef has historically been the constructor
expect(comp.directive.value).toContain('ViewRef');
// Each ChangeDetectorRef instance should be unique
expect(comp.directive !.cdr).not.toBe(comp.cdr);
expect(comp.directive !.cdr).not.toBe(comp.otherDirective !.cdr);
expect(comp.directive!.cdr).not.toBe(comp.cdr);
expect(comp.directive!.cdr).not.toBe(comp.otherDirective!.cdr);
});
it('should inject host component ChangeDetectorRef into directives on ng-container', () => {
@ -1719,7 +1757,9 @@ describe('di', () => {
@Directive({selector: '[getCDR]'})
class MyDirective {
constructor(public cdr: ChangeDetectorRef) { dirInstance = this; }
constructor(public cdr: ChangeDetectorRef) {
dirInstance = this;
}
}
@Component({
@ -1733,7 +1773,7 @@ describe('di', () => {
TestBed.configureTestingModule({declarations: [MyApp, MyDirective]});
const fixture = TestBed.createComponent(MyApp);
fixture.detectChanges();
expect((dirInstance !.cdr as ViewRef<MyApp>).context).toBe(fixture.componentInstance);
expect((dirInstance!.cdr as ViewRef<MyApp>).context).toBe(fixture.componentInstance);
});
});
});
@ -1747,7 +1787,7 @@ describe('di', () => {
@Component({template: '<div injectorDir></div>'})
class MyComp {
@ViewChild(InjectorDir) injectorDirInstance !: InjectorDir;
@ViewChild(InjectorDir) injectorDirInstance!: InjectorDir;
}
TestBed.configureTestingModule({declarations: [InjectorDir, MyComp]});
@ -1840,7 +1880,7 @@ describe('di', () => {
providers: [{provide: LOCALE_ID, useValue: 'en-GB'}]
})
class MyComp {
@ViewChild(MyDir) myDir !: MyDir;
@ViewChild(MyDir) myDir!: MyDir;
constructor(@Inject(LOCALE_ID) public localeId: string) {}
}
@ -1851,7 +1891,6 @@ describe('di', () => {
});
describe('@Attribute', () => {
it('should inject attributes', () => {
@Directive({selector: '[dir]'})
class MyDir {
@ -1862,7 +1901,7 @@ describe('di', () => {
@Component({template: '<div dir exist="existValue" other="ignore"></div>'})
class MyComp {
@ViewChild(MyDir) directiveInstance !: MyDir;
@ViewChild(MyDir) directiveInstance!: MyDir;
}
TestBed.configureTestingModule({declarations: [MyDir, MyComp]});
@ -1886,7 +1925,7 @@ describe('di', () => {
@Component(
{template: '<ng-template dir="initial" exist="existValue" other="ignore"></ng-template>'})
class MyComp {
@ViewChild(MyDir) directiveInstance !: MyDir;
@ViewChild(MyDir) directiveInstance!: MyDir;
}
TestBed.configureTestingModule({declarations: [MyDir, MyComp]});
@ -1911,7 +1950,7 @@ describe('di', () => {
template: '<ng-container dir="initial" exist="existValue" other="ignore"></ng-container>'
})
class MyComp {
@ViewChild(MyDir) directiveInstance !: MyDir;
@ViewChild(MyDir) directiveInstance!: MyDir;
}
TestBed.configureTestingModule({declarations: [MyDir, MyComp]});
@ -1938,7 +1977,7 @@ describe('di', () => {
'<div dir style="margin: 1px; color: red;" class="hello there" other-attr="value"></div>'
})
class MyComp {
@ViewChild(MyDir) directiveInstance !: MyDir;
@ViewChild(MyDir) directiveInstance!: MyDir;
}
TestBed.configureTestingModule({declarations: [MyDir, MyComp]});
@ -1966,7 +2005,7 @@ describe('di', () => {
template: '<div dir exist="existValue" svg:exist="testExistValue" other="otherValue"></div>'
})
class MyComp {
@ViewChild(MyDir) directiveInstance !: MyDir;
@ViewChild(MyDir) directiveInstance!: MyDir;
}
TestBed.configureTestingModule({declarations: [MyDir, MyComp]});
@ -1983,7 +2022,7 @@ describe('di', () => {
it('should not inject attributes representing bindings and outputs', () => {
@Directive({selector: '[dir]'})
class MyDir {
@Input() binding !: string;
@Input() binding!: string;
@Output() output = new EventEmitter();
constructor(
@Attribute('exist') public exist: string,
@ -1997,7 +2036,7 @@ describe('di', () => {
'<div dir exist="existValue" [binding]="bindingValue" (output)="outputValue" other="otherValue" ignore="ignoreValue"></div>'
})
class MyComp {
@ViewChild(MyDir) directiveInstance !: MyDir;
@ViewChild(MyDir) directiveInstance!: MyDir;
}
TestBed.configureTestingModule({declarations: [MyDir, MyComp]});
@ -2016,13 +2055,17 @@ describe('di', () => {
it('should support dependencies in Pipes used inside ICUs', () => {
@Injectable()
class MyService {
transform(value: string): string { return `${value} (transformed)`; }
transform(value: string): string {
return `${value} (transformed)`;
}
}
@Pipe({name: 'somePipe'})
class MyPipe {
constructor(private service: MyService) {}
transform(value: any): any { return this.service.transform(value); }
transform(value: any): any {
return this.service.transform(value);
}
}
@Component({
@ -2051,13 +2094,17 @@ describe('di', () => {
it('should support dependencies in Pipes used inside i18n blocks', () => {
@Injectable()
class MyService {
transform(value: string): string { return `${value} (transformed)`; }
transform(value: string): string {
return `${value} (transformed)`;
}
}
@Pipe({name: 'somePipe'})
class MyPipe {
constructor(private service: MyService) {}
transform(value: any): any { return this.service.transform(value); }
transform(value: any): any {
return this.service.transform(value);
}
}
@Component({
@ -2071,10 +2118,12 @@ describe('di', () => {
class MyComp {
count = '2';
@ViewChild('target', {read: ViewContainerRef}) target !: ViewContainerRef;
@ViewChild('source', {read: TemplateRef}) source !: TemplateRef<any>;
@ViewChild('target', {read: ViewContainerRef}) target!: ViewContainerRef;
@ViewChild('source', {read: TemplateRef}) source!: TemplateRef<any>;
create() { this.target.createEmbeddedView(this.source); }
create() {
this.target.createEmbeddedView(this.source);
}
}
TestBed.configureTestingModule({
@ -2109,8 +2158,8 @@ describe('di', () => {
@Component({template: `<div dirA> <child></child> </div>`})
class App {
@ViewChild(DirA) dirA !: DirA;
@ViewChild(Child) child !: Child;
@ViewChild(DirA) dirA!: DirA;
@ViewChild(Child) child!: Child;
}
const fixture = TestBed.configureTestingModule({declarations: [DirA, DirB, App, Child]})

View File

@ -13,9 +13,7 @@ import {TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
describe('directives', () => {
describe('matching', () => {
@Directive({selector: 'ng-template[test]'})
class TestDirective {
constructor(public templateRef: TemplateRef<any>) {}
@ -241,7 +239,9 @@ describe('directives', () => {
@Directive({selector: '[dir]'})
class MyDir {
ngOnInit() { calls.push('MyDir.ngOnInit'); }
ngOnInit() {
calls.push('MyDir.ngOnInit');
}
}
@Component({
@ -266,7 +266,9 @@ describe('directives', () => {
@Directive({selector: 'svg[dir]'})
class MyDir {
constructor(private el: ElementRef) {}
ngOnInit() { calls.push(`MyDir.ngOnInit: ${this.el.nativeElement.tagName}`); }
ngOnInit() {
calls.push(`MyDir.ngOnInit: ${this.el.nativeElement.tagName}`);
}
}
@Component({
@ -289,7 +291,9 @@ describe('directives', () => {
@Directive({selector: 'text[dir]'})
class MyDir {
constructor(private el: ElementRef) {}
ngOnInit() { calls.push(`MyDir.ngOnInit: ${this.el.nativeElement.tagName}`); }
ngOnInit() {
calls.push(`MyDir.ngOnInit: ${this.el.nativeElement.tagName}`);
}
}
@Component({
@ -311,13 +315,13 @@ describe('directives', () => {
@Directive({selector: '[test]'})
class MyDir {
constructor() { logs.push('MyDir.contructor'); }
constructor() {
logs.push('MyDir.contructor');
}
@Input('test')
myInput = '';
@Input('test') myInput = '';
@Input('disabled')
myInput2 = '';
@Input('disabled') myInput2 = '';
}
@Component({
@ -338,7 +342,6 @@ describe('directives', () => {
expect(logs).toEqual(['MyDir.contructor']);
});
});
describe('inputs', () => {
@ -346,7 +349,9 @@ describe('directives', () => {
let dirInstance: WithInput;
@Directive({selector: '[dir]'})
class WithInput {
constructor() { dirInstance = this; }
constructor() {
dirInstance = this;
}
@Input() dir: string = '';
}
@ -362,14 +367,16 @@ describe('directives', () => {
const fixture = TestBed.createComponent(TestComp);
fixture.detectChanges();
expect(dirInstance !.dir).toBe('Hello');
expect(dirInstance!.dir).toBe('Hello');
});
it('should allow directive inputs (as an interpolated prop) on <ng-template>', () => {
let dirInstance: WithInput;
@Directive({selector: '[dir]'})
class WithInput {
constructor() { dirInstance = this; }
constructor() {
dirInstance = this;
}
@Input() dir: string = '';
}
@ -385,7 +392,7 @@ describe('directives', () => {
const fixture = TestBed.createComponent(TestComp);
fixture.detectChanges();
expect(dirInstance !.dir).toBe('Hello');
expect(dirInstance!.dir).toBe('Hello');
});
it('should allow directive inputs (as an interpolated prop) on <ng-template> with structural directives',
@ -393,7 +400,9 @@ describe('directives', () => {
let dirInstance: WithInput;
@Directive({selector: '[dir]'})
class WithInput {
constructor() { dirInstance = this; }
constructor() {
dirInstance = this;
}
@Input() dir: string = '';
}
@ -409,7 +418,7 @@ describe('directives', () => {
const fixture = TestBed.createComponent(TestComp);
fixture.detectChanges();
expect(dirInstance !.dir).toBe('Hello');
expect(dirInstance!.dir).toBe('Hello');
});
});
@ -433,7 +442,7 @@ describe('directives', () => {
expect(fixture.componentInstance.testDir).toBeTruthy();
expect(fixture.componentInstance.value).toBe(false);
fixture.componentInstance.testDir !.out.emit();
fixture.componentInstance.testDir!.out.emit();
fixture.detectChanges();
expect(fixture.componentInstance.value).toBe(true);
});
@ -459,7 +468,6 @@ describe('directives', () => {
fixture.detectChanges();
expect(fixture.componentInstance.value).toBeTruthy();
});
});
describe('attribute shadowing behaviors', () => {
@ -471,8 +479,7 @@ describe('directives', () => {
selector: '[dir-with-title]',
})
class DirWithTitle {
@Input()
title = '';
@Input() title = '';
}
it('should set both the div attribute and the directive input for `title="value"`', () => {
@ -711,8 +718,12 @@ describe('directives', () => {
const log: string[] = [];
@Directive({selector: '[dir]'})
class DirectiveA {
constructor() { log.push('DirectiveA.constructor'); }
ngOnInit() { log.push('DirectiveA.ngOnInit'); }
constructor() {
log.push('DirectiveA.constructor');
}
ngOnInit() {
log.push('DirectiveA.ngOnInit');
}
}
@NgModule({
@ -724,8 +735,12 @@ describe('directives', () => {
@Directive({selector: '[dir]'})
class DirectiveB {
constructor() { log.push('DirectiveB.constructor'); }
ngOnInit() { log.push('DirectiveB.ngOnInit'); }
constructor() {
log.push('DirectiveB.constructor');
}
ngOnInit() {
log.push('DirectiveB.ngOnInit');
}
}
@Component({
@ -752,8 +767,12 @@ describe('directives', () => {
const log: string[] = [];
@Directive({selector: '[dir]'})
class DirectiveA {
constructor() { log.push('DirectiveA.constructor'); }
ngOnInit() { log.push('DirectiveA.ngOnInit'); }
constructor() {
log.push('DirectiveA.constructor');
}
ngOnInit() {
log.push('DirectiveA.ngOnInit');
}
}
@NgModule({
@ -765,8 +784,12 @@ describe('directives', () => {
@Directive({selector: '[dir]'})
class DirectiveB {
constructor() { log.push('DirectiveB.constructor'); }
ngOnInit() { log.push('DirectiveB.ngOnInit'); }
constructor() {
log.push('DirectiveB.constructor');
}
ngOnInit() {
log.push('DirectiveB.ngOnInit');
}
}
@NgModule({
@ -795,6 +818,5 @@ describe('directives', () => {
'DirectiveB.ngOnInit'
]);
});
});
});

View File

@ -48,12 +48,16 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => {
@Component(
{selector: 'child', template: '<p></p>', providers: [{provide: String, useValue: 'Child'}]})
class Child {
constructor() { childComponent.push(this); }
constructor() {
childComponent.push(this);
}
}
@Directive({selector: '[dirA]', exportAs: 'dirA'})
class DirectiveA {
constructor() { dirA.push(this); }
constructor() {
dirA.push(this);
}
}
@Component({
@ -69,9 +73,13 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => {
})
class MyApp {
text: string = 'INIT';
constructor() { myApp = this; }
constructor() {
myApp = this;
}
log(event: any) { log.push(event); }
log(event: any) {
log.push(event);
}
}
describe('getComponent', () => {
@ -112,7 +120,7 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => {
});
it('should return context from element', () => {
expect(getContext<MyApp>(child[0])).toEqual(myApp);
expect(getContext<{$implicit: boolean}>(child[2]) !.$implicit).toEqual(true);
expect(getContext<{$implicit: boolean}>(child[2])!.$implicit).toEqual(true);
expect(getContext<Child>(p[0])).toEqual(childComponent[0]);
});
});
@ -264,7 +272,7 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => {
it('should work on templates', () => {
const templateComment = Array.from((fixture.nativeElement as HTMLElement).childNodes)
.find((node: ChildNode) => node.nodeType === Node.COMMENT_NODE) !;
.find((node: ChildNode) => node.nodeType === Node.COMMENT_NODE)!;
const lContext = loadLContext(templateComment);
expect(lContext).toBeDefined();
expect(lContext.native as any).toBe(templateComment);
@ -274,7 +282,7 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => {
const ngContainerComment = Array.from((fixture.nativeElement as HTMLElement).childNodes)
.find(
(node: ChildNode) => node.nodeType === Node.COMMENT_NODE &&
node.textContent === `ng-container`) !;
node.textContent === `ng-container`)!;
const lContext = loadLContext(ngContainerComment);
expect(lContext).toBeDefined();
expect(lContext.native as any).toBe(ngContainerComment);
@ -285,7 +293,6 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils', () => {
onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () => {
describe('getRootComponents()', () => {
it('should return a list of the root components of the application from an element', () => {
@Component({selector: 'inner-comp', template: '<div></div>'})
class InnerComp {
}
@ -299,13 +306,13 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () =>
fixture.detectChanges();
const hostElm = fixture.nativeElement;
const innerElm = hostElm.querySelector('inner-comp') !;
const divElm = hostElm.querySelector('div') !;
const innerElm = hostElm.querySelector('inner-comp')!;
const divElm = hostElm.querySelector('div')!;
const component = fixture.componentInstance;
expect(getRootComponents(hostElm) !).toEqual([component]);
expect(getRootComponents(innerElm) !).toEqual([component]);
expect(getRootComponents(divElm) !).toEqual([component]);
expect(getRootComponents(hostElm)!).toEqual([component]);
expect(getRootComponents(innerElm)!).toEqual([component]);
expect(getRootComponents(divElm)!).toEqual([component]);
});
});
@ -331,9 +338,9 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () =>
`
})
class Comp {
@ViewChild(MyDir1) myDir1Instance !: MyDir1;
@ViewChild(MyDir2) myDir2Instance !: MyDir2;
@ViewChild(MyDir3) myDir3Instance !: MyDir3;
@ViewChild(MyDir1) myDir1Instance!: MyDir1;
@ViewChild(MyDir2) myDir2Instance!: MyDir2;
@ViewChild(MyDir3) myDir3Instance!: MyDir3;
}
TestBed.configureTestingModule({declarations: [Comp, MyDir1, MyDir2, MyDir3]});
@ -345,19 +352,17 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () =>
const elm1 = elements[0];
const elm1Dirs = getDirectives(elm1);
expect(elm1Dirs).toContain(fixture.componentInstance.myDir1Instance !);
expect(elm1Dirs).toContain(fixture.componentInstance.myDir2Instance !);
expect(elm1Dirs).toContain(fixture.componentInstance.myDir1Instance!);
expect(elm1Dirs).toContain(fixture.componentInstance.myDir2Instance!);
const elm2 = elements[1];
const elm2Dirs = getDirectives(elm2);
expect(elm2Dirs).toContain(fixture.componentInstance.myDir3Instance !);
expect(elm2Dirs).toContain(fixture.componentInstance.myDir3Instance!);
});
});
describe('getInjector', () => {
it('should return an injector that can return directive instances', () => {
@Component({template: ''})
class Comp {
}
@ -386,7 +391,6 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () =>
describe('getLocalRefs', () => {
it('should return a map of local refs for an element', () => {
@Directive({selector: '[myDir]', exportAs: 'myDir'})
class MyDir {
}
@ -399,7 +403,7 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () =>
const fixture = TestBed.createComponent(Comp);
fixture.detectChanges();
const divEl = fixture.nativeElement.querySelector('div') !;
const divEl = fixture.nativeElement.querySelector('div')!;
const localRefs = getLocalRefs(divEl);
expect(localRefs.elRef.tagName.toLowerCase()).toBe('div');
@ -416,7 +420,7 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () =>
const fixture = TestBed.createComponent(Comp);
fixture.detectChanges();
const divEl = fixture.nativeElement.querySelector('div') !;
const divEl = fixture.nativeElement.querySelector('div')!;
const localRefs = getLocalRefs(divEl);
expect(localRefs.elRef.tagName.toLowerCase()).toBe('div');
@ -439,11 +443,11 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () =>
const fixture = TestBed.createComponent(Comp);
fixture.detectChanges();
const parent = fixture.nativeElement.querySelector('.parent') !;
const child = fixture.nativeElement.querySelector('.child') !;
const parent = fixture.nativeElement.querySelector('.parent')!;
const child = fixture.nativeElement.querySelector('.child')!;
const parentDebug = getDebugNode(parent) !;
const childDebug = getDebugNode(child) !;
const parentDebug = getDebugNode(parent)!;
const childDebug = getDebugNode(child)!;
expect(parentDebug.native).toBe(parent);
expect(childDebug.native).toBe(child);
@ -472,8 +476,8 @@ onlyInIvy('Ivy-specific utilities').describe('discovery utils deprecated', () =>
const fixture = TestBed.createComponent(Comp);
fixture.detectChanges();
const child = fixture.nativeElement.querySelector('child-comp') !;
const childDebug = getDebugNode(child) !;
const child = fixture.nativeElement.querySelector('child-comp')!;
const childDebug = getDebugNode(child)!;
expect(childDebug.native).toBe(child);
expect(getElementStyles(child)).toEqual({

View File

@ -10,7 +10,6 @@ import {Component, Input} from '@angular/core';
import {TestBed} from '@angular/core/testing';
describe('embedded views', () => {
it('should correctly resolve the implicit receiver in expressions', () => {
const items: string[] = [];
@ -27,7 +26,9 @@ describe('embedded views', () => {
})
class TestCmp {
item: string = 'CmpItem';
addItem() { items.push(this.item); }
addItem() {
items.push(this.item);
}
}
TestBed.configureTestingModule({declarations: [ChildCmp, TestCmp]});
@ -36,8 +37,8 @@ describe('embedded views', () => {
const childCmp: ChildCmp = fixture.debugElement.children[0].componentInstance;
childCmp.addItemFn !();
childCmp.addItemFn !();
childCmp.addItemFn!();
childCmp.addItemFn!();
expect(items).toEqual(['CmpItem', 'CmpItem']);
});
@ -71,5 +72,4 @@ describe('embedded views', () => {
expect(fixture.nativeElement.textContent).toBe('HelloHello');
});
});

View File

@ -247,7 +247,13 @@ class DirWithCompInput {
class DirToReferenceWithPreOrderHooks implements OnInit, OnChanges, DoCheck {
@Input() in : any = null;
name = 'Drew';
ngOnChanges(changes: SimpleChanges) { this.name += '!'; }
ngOnInit() { this.name += '?'; }
ngDoCheck() { this.name += '@'; }
ngOnChanges(changes: SimpleChanges) {
this.name += '!';
}
ngOnInit() {
this.name += '?';
}
ngDoCheck() {
this.name += '@';
}
}

View File

@ -121,10 +121,9 @@ describe('host bindings', () => {
class ParentCmp {
private _prop = '';
@ViewChild('template', {read: ViewContainerRef})
vcr: ViewContainerRef = null !;
@ViewChild('template', {read: ViewContainerRef}) vcr: ViewContainerRef = null!;
private child: ComponentRef<ChildCmp> = null !;
private child: ComponentRef<ChildCmp> = null!;
@Input()
set prop(value: string) {
@ -137,10 +136,11 @@ describe('host bindings', () => {
}
}
get prop() { return this._prop; }
get prop() {
return this._prop;
}
@Input()
prop2 = 0;
@Input() prop2 = 0;
ngAfterViewInit() {
const factory = this.componentFactoryResolver.resolveComponentFactory(ChildCmp);
@ -212,7 +212,6 @@ describe('host bindings', () => {
onlyInIvy('[style.prop] and [class.name] prioritization is a new feature')
.it('should prioritize styling present in the order of directive hostBinding evaluation, but consider sub-classed directive styling to be the most important',
() => {
@Component({template: '<div child-dir sibling-dir></div>'})
class MyApp {
}
@ -220,37 +219,55 @@ describe('host bindings', () => {
@Directive({selector: '[parent-dir]'})
class ParentDir {
@HostBinding('style.width')
get width1() { return '100px'; }
get width1() {
return '100px';
}
@HostBinding('style.height')
get height1() { return '100px'; }
get height1() {
return '100px';
}
@HostBinding('style.color')
get color1() { return 'red'; }
get color1() {
return 'red';
}
}
@Directive({selector: '[child-dir]'})
class ChildDir extends ParentDir {
@HostBinding('style.width')
get width2() { return '200px'; }
get width2() {
return '200px';
}
@HostBinding('style.height')
get height2() { return '200px'; }
get height2() {
return '200px';
}
}
@Directive({selector: '[sibling-dir]'})
class SiblingDir {
@HostBinding('style.width')
get width3() { return '300px'; }
get width3() {
return '300px';
}
@HostBinding('style.height')
get height3() { return '300px'; }
get height3() {
return '300px';
}
@HostBinding('style.opacity')
get opacity3() { return '0.5'; }
get opacity3() {
return '0.5';
}
@HostBinding('style.color')
get color1() { return 'blue'; }
get color1() {
return 'blue';
}
}
TestBed.configureTestingModule(
@ -295,25 +312,22 @@ describe('host bindings', () => {
fixture.detectChanges();
}).not.toThrow();
});
});
@Directive({selector: '[hostBindingDir]'})
class HostBindingDir {
@HostBinding()
id = 'foo';
@HostBinding() id = 'foo';
}
it('should support host bindings in directives', () => {
@Directive({selector: '[dir]'})
class Dir {
@HostBinding('className')
klass = 'foo';
@HostBinding('className') klass = 'foo';
}
@Component({template: '<span dir></span>'})
class App {
@ViewChild(Dir) directiveInstance !: Dir;
@ViewChild(Dir) directiveInstance!: Dir;
}
TestBed.configureTestingModule({declarations: [App, Dir]});
@ -333,8 +347,7 @@ describe('host bindings', () => {
it('should support host bindings on root component', () => {
@Component({template: ''})
class HostBindingComp {
@HostBinding()
title = 'my-title';
@HostBinding() title = 'my-title';
}
TestBed.configureTestingModule({declarations: [HostBindingComp]});
@ -365,8 +378,7 @@ describe('host bindings', () => {
class App {
constructor(public serviceOne: ServiceOne, public serviceTwo: ServiceTwo) {}
@HostBinding()
title = 'my-title';
@HostBinding() title = 'my-title';
}
TestBed.configureTestingModule({declarations: [App]});
@ -390,8 +402,7 @@ describe('host bindings', () => {
@Component({selector: 'host-title-comp', template: ''})
class HostTitleComp {
@HostBinding()
title = 'my-title';
@HostBinding() title = 'my-title';
}
@Component({
@ -402,7 +413,7 @@ describe('host bindings', () => {
`
})
class App {
@ViewChild(HostBindingDir) hostBindingDir !: HostBindingDir;
@ViewChild(HostBindingDir) hostBindingDir!: HostBindingDir;
}
TestBed.configureTestingModule({declarations: [App, SomeDir, HostTitleComp, HostBindingDir]});
@ -415,7 +426,7 @@ describe('host bindings', () => {
expect(hostBindingDiv.id).toEqual('foo');
expect(hostTitleComp.title).toEqual('my-title');
fixture.componentInstance.hostBindingDir !.id = 'bar';
fixture.componentInstance.hostBindingDir!.id = 'bar';
fixture.detectChanges();
expect(hostBindingDiv.id).toEqual('bar');
});
@ -423,8 +434,7 @@ describe('host bindings', () => {
it('should support consecutive components with host bindings', () => {
@Component({selector: 'host-binding-comp', template: ''})
class HostBindingComp {
@HostBinding()
id = 'blue';
@HostBinding() id = 'blue';
}
@Component({
@ -434,7 +444,7 @@ describe('host bindings', () => {
`
})
class App {
@ViewChildren(HostBindingComp) hostBindingComp !: QueryList<HostBindingComp>;
@ViewChildren(HostBindingComp) hostBindingComp!: QueryList<HostBindingComp>;
}
TestBed.configureTestingModule({declarations: [App, HostBindingComp]});
@ -470,7 +480,7 @@ describe('host bindings', () => {
@Component({template: '<div someDir hostBindingDir></div>'})
class App {
@ViewChild(HostBindingDir) hostBindingDir !: HostBindingDir;
@ViewChild(HostBindingDir) hostBindingDir!: HostBindingDir;
}
TestBed.configureTestingModule({declarations: [App, SomeDir, HostBindingDir]});
@ -480,7 +490,7 @@ describe('host bindings', () => {
const hostBindingDiv = fixture.nativeElement.querySelector('div') as HTMLElement;
expect(hostBindingDiv.id).toEqual('foo');
fixture.componentInstance.hostBindingDir !.id = 'bar';
fixture.componentInstance.hostBindingDir!.id = 'bar';
fixture.detectChanges();
expect(hostBindingDiv.id).toEqual('bar');
});
@ -490,18 +500,23 @@ describe('host bindings', () => {
it('should support host bindings that rely on values from init hooks', () => {
@Component({template: '', selector: 'init-hook-comp'})
class InitHookComp implements OnInit, OnChanges, DoCheck {
@Input()
inputValue = '';
@Input() inputValue = '';
changesValue = '';
initValue = '';
checkValue = '';
ngOnChanges() { this.changesValue = 'changes'; }
ngOnChanges() {
this.changesValue = 'changes';
}
ngOnInit() { this.initValue = 'init'; }
ngOnInit() {
this.initValue = 'init';
}
ngDoCheck() { this.checkValue = 'check'; }
ngDoCheck() {
this.checkValue = 'check';
}
@HostBinding('title')
get value() {
@ -529,16 +544,14 @@ describe('host bindings', () => {
it('should support host bindings with the same name as inputs', () => {
@Directive({selector: '[hostBindingDir]'})
class HostBindingInputDir {
@Input()
disabled = false;
@Input() disabled = false;
@HostBinding('disabled')
hostDisabled = false;
@HostBinding('disabled') hostDisabled = false;
}
@Component({template: '<input hostBindingDir [disabled]="isDisabled">'})
class App {
@ViewChild(HostBindingInputDir) hostBindingInputDir !: HostBindingInputDir;
@ViewChild(HostBindingInputDir) hostBindingInputDir!: HostBindingInputDir;
isDisabled = true;
}
@ -611,14 +624,12 @@ describe('host bindings', () => {
it('should support component with host bindings and array literals', () => {
@Component({selector: 'host-binding-comp', template: ''})
class HostBindingComp {
@HostBinding()
id = 'my-id';
@HostBinding() id = 'my-id';
}
@Component({selector: 'name-comp', template: ''})
class NameComp {
@Input()
names !: string[];
@Input() names!: string[];
}
@Component({
@ -628,7 +639,7 @@ describe('host bindings', () => {
`
})
class App {
@ViewChild(NameComp) nameComp !: NameComp;
@ViewChild(NameComp) nameComp!: NameComp;
name = '';
}
@ -661,8 +672,7 @@ describe('host bindings', () => {
it('should support host bindings that contain array literals', () => {
@Component({selector: 'name-comp', template: ''})
class NameComp {
@Input()
names !: string[];
@Input() names!: string[];
}
@Component({
@ -684,8 +694,8 @@ describe('host bindings', () => {
`
})
class App {
@ViewChild(HostBindingComp) hostBindingComp !: HostBindingComp;
@ViewChild(NameComp) nameComp !: NameComp;
@ViewChild(HostBindingComp) hostBindingComp!: HostBindingComp;
@ViewChild(NameComp) nameComp!: NameComp;
name = '';
otherName = '';
}
@ -703,11 +713,11 @@ describe('host bindings', () => {
expect(hostBindingEl.id).toBe('red,blue');
expect(hostBindingEl.dir).toBe('ltr');
expect(hostBindingEl.title).toBe('my title,other title');
expect(nameComp !.names).toEqual(['Frank', 'Nancy', 'Joe']);
expect(nameComp!.names).toEqual(['Frank', 'Nancy', 'Joe']);
const firstArray = nameComp !.names;
const firstArray = nameComp!.names;
fixture.detectChanges();
expect(firstArray).toBe(nameComp !.names);
expect(firstArray).toBe(nameComp!.names);
hostBindingComp.id = 'green';
hostBindingComp.dir = 'rtl';
@ -729,7 +739,9 @@ describe('host bindings', () => {
@Directive({selector: '[hostListenerDir]'})
class HostListenerDir {
@HostListener('click')
onClick() { events.push('click!'); }
onClick() {
events.push('click!');
}
}
@Component({template: '<button hostListenerDir hostDir>Click</button>'})
@ -740,7 +752,7 @@ describe('host bindings', () => {
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const button = fixture.nativeElement.querySelector('button') !;
const button = fixture.nativeElement.querySelector('button')!;
button.click();
expect(events).toEqual(['click!']);
expect(button.title).toEqual('my title,other title');
@ -759,8 +771,8 @@ describe('host bindings', () => {
@Component({template: '<host-binding-comp hostDir></host-binding-comp>'})
class App {
@ViewChild(HostBindingComp) hostBindingComp !: HostBindingComp;
@ViewChild(HostBindingDir) hostBindingDir !: HostBindingDir;
@ViewChild(HostBindingComp) hostBindingComp!: HostBindingComp;
@ViewChild(HostBindingDir) hostBindingDir!: HostBindingDir;
}
TestBed.configureTestingModule({declarations: [App, HostBindingComp, HostBindingDir]});
@ -798,7 +810,7 @@ describe('host bindings', () => {
@Component({template: `<host-binding-comp></host-binding-comp>{{ name }}`})
class App {
@ViewChild(HostBindingComp) hostBindingComp !: HostBindingComp;
@ViewChild(HostBindingComp) hostBindingComp!: HostBindingComp;
name = '';
}
@ -872,8 +884,8 @@ describe('host bindings', () => {
`
})
class App {
@ViewChild(SubDirective) subDir !: SubDirective;
@ViewChild(SuperDirective) superDir !: SuperDirective;
@ViewChild(SubDirective) subDir!: SubDirective;
@ViewChild(SuperDirective) superDir!: SuperDirective;
}
TestBed.configureTestingModule({declarations: [App, SuperDirective, SubDirective]});
@ -927,8 +939,7 @@ describe('host bindings', () => {
host: {'[id]': 'foos.length'}
})
class HostBindingWithContentChildren {
@ContentChildren('foo')
foos !: QueryList<any>;
@ContentChildren('foo') foos!: QueryList<any>;
}
@Component({
@ -955,7 +966,9 @@ describe('host bindings', () => {
class HostBindingWithContentHooks implements AfterContentInit {
myValue = 'initial';
ngAfterContentInit() { this.myValue = 'after-content'; }
ngAfterContentInit() {
this.myValue = 'after-content';
}
}
@Component({template: '<host-binding-comp></host-binding-comp>'})
@ -971,7 +984,6 @@ describe('host bindings', () => {
});
describe('styles', () => {
it('should bind to host styles', () => {
@Component(
{selector: 'host-binding-to-styles', host: {'[style.width.px]': 'width'}, template: ''})
@ -981,7 +993,7 @@ describe('host bindings', () => {
@Component({template: '<host-binding-to-styles></host-binding-to-styles>'})
class App {
@ViewChild(HostBindingToStyles) hostBindingDir !: HostBindingToStyles;
@ViewChild(HostBindingToStyles) hostBindingDir!: HostBindingToStyles;
}
TestBed.configureTestingModule({declarations: [App, HostBindingToStyles]});
@ -1010,7 +1022,7 @@ describe('host bindings', () => {
@Component({template: '<div hostStyles containerDir></div>'})
class App {
@ViewChild(HostBindingToStyles) hostBindingDir !: HostBindingToStyles;
@ViewChild(HostBindingToStyles) hostBindingDir!: HostBindingToStyles;
}
TestBed.configureTestingModule({declarations: [App, HostBindingToStyles, ContainerDir]});
@ -1044,10 +1056,12 @@ describe('host bindings', () => {
});
describe('sanitization', () => {
function identity(value: any) { return value; }
function verify(
tag: string, prop: string, value: any, expectedSanitizedValue: any, bypassFn: Function,
isAttribute: boolean = true, throws: boolean = false) {
function identity(value: any) {
return value;
}
function verify(tag: string, prop: string, value: any, expectedSanitizedValue: any,
bypassFn: Function, isAttribute: boolean = true,
throws: boolean = false) {
it(`should sanitize <${tag} ${prop}> ${isAttribute ? 'properties' : 'attributes'}`, () => {
@Directive({
selector: '[unsafeUrlHostBindingDir]',
@ -1061,13 +1075,13 @@ describe('host bindings', () => {
@Component({template: `<${tag} unsafeUrlHostBindingDir></${tag}>`})
class App {
@ViewChild(UnsafeDir) unsafeDir !: UnsafeDir;
@ViewChild(UnsafeDir) unsafeDir!: UnsafeDir;
}
TestBed.configureTestingModule({declarations: [App, UnsafeDir]});
const fixture = TestBed.createComponent(App);
fixture.detectChanges();
const el = fixture.nativeElement.querySelector(tag) !;
const el = fixture.nativeElement.querySelector(tag)!;
const current = () => isAttribute ? el.getAttribute(prop) : (el as any)[prop];
fixture.componentInstance.unsafeDir.value = value;
@ -1103,5 +1117,4 @@ describe('host bindings', () => {
'<img src="unsafe:javascript:alert(3)">', bypassSanitizationTrustHtml,
/* isAttribute */ false);
});
});

View File

@ -8,16 +8,17 @@
// Make the `$localize()` global function available to the compiled templates, and the direct calls
// below. This would normally be done inside the application `polyfills.ts` file.
import '@angular/localize/init';
import {CommonModule, registerLocaleData} from '@angular/common';
import localeRo from '@angular/common/locales/ro';
import {Component, ContentChild, ElementRef, ContentChildren, Directive, HostBinding, Input, LOCALE_ID, QueryList, TemplateRef, Type, ViewChild, ViewContainerRef, Pipe, PipeTransform, NO_ERRORS_SCHEMA} from '@angular/core';
import {computeMsgId} from '@angular/compiler';
import {Component, ContentChild, ContentChildren, Directive, ElementRef, HostBinding, Input, LOCALE_ID, NO_ERRORS_SCHEMA, Pipe, PipeTransform, QueryList, TemplateRef, Type, ViewChild, ViewContainerRef} from '@angular/core';
import {setDelayProjection} from '@angular/core/src/render3/instructions/projection';
import {TestBed} from '@angular/core/testing';
import {loadTranslations, clearTranslations} from '@angular/localize';
import {clearTranslations, loadTranslations} from '@angular/localize';
import {By} from '@angular/platform-browser';
import {expect} from '@angular/platform-browser/testing/src/matchers';
import {onlyInIvy} from '@angular/private/testing';
import {computeMsgId} from '@angular/compiler';
import {BehaviorSubject} from 'rxjs';
@ -72,7 +73,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
it('should support interpolations with custom interpolation config', () => {
loadTranslations({[computeMsgId('Hello {$INTERPOLATION}')]: 'Bonjour {$INTERPOLATION}'});
const interpolation = ['{%', '%}'] as[string, string];
const interpolation = ['{%', '%}'] as [string, string];
TestBed.overrideComponent(AppComp, {set: {interpolation}});
const fixture = initWithTemplate(AppComp, `<div i18n>Hello {% name %}</div>`);
@ -277,7 +278,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
name = `Angular`;
clicks = 0;
onClick() { this.clicks++; }
onClick() {
this.clicks++;
}
}
TestBed.configureTestingModule({declarations: [ListenerComp]});
@ -619,7 +622,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
[computeMsgId('{VAR_SELECT, select, 10 {ten} other {{INTERPOLATION}}}')]:
'{VAR_SELECT, select, 10 {dix} other {{INTERPOLATION}}}'
});
const interpolation = ['{%', '%}'] as[string, string];
const interpolation = ['{%', '%}'] as [string, string];
TestBed.overrideComponent(AppComp, {set: {interpolation}});
const fixture =
initWithTemplate(AppComp, `<div i18n>{count, select, 10 {ten} other {{% name %}}}</div>`);
@ -704,7 +707,8 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
'{VAR_SELECT, select, 10 {dix} 20 {vingt} other {autre}}'
});
const fixture = initWithTemplate(
AppComp, `
AppComp,
`
<ng-template i18n tplRef>` +
`{count, select, 10 {ten} 20 {twenty} other {other}}` +
`</ng-template>
@ -864,7 +868,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
private readonly viewContainerRef: ViewContainerRef,
private readonly templateRef: TemplateRef<any>) {}
ngOnInit() { this.viewContainerRef.createEmbeddedView(this.templateRef); }
ngOnInit() {
this.viewContainerRef.createEmbeddedView(this.templateRef);
}
}
@Component({
@ -941,7 +947,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
dir = this;
}
attachEmbeddedView() { this.viewContainerRef.createEmbeddedView(this.templateRef); }
attachEmbeddedView() {
this.viewContainerRef.createEmbeddedView(this.templateRef);
}
}
@Component({
@ -983,7 +991,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
expect(fixture.debugElement.nativeElement.innerHTML)
.toBe('<my-cmp><!--container--></my-cmp>');
dir !.attachEmbeddedView();
dir!.attachEmbeddedView();
fixture.detectChanges();
expect(fixture.debugElement.nativeElement.innerHTML)
.toBe(
@ -1019,7 +1027,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
class Comp {
type = 'A';
visible = true;
isVisible() { return true; }
isVisible() {
return true;
}
}
TestBed.configureTestingModule({declarations: [Comp]});
@ -1042,7 +1052,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
'{VAR_SELECT, select, A {A - {PH_A}} ' +
'B {B - {PH_B}} other {other - {PH_WITH_SPACES}}}')]:
'{VAR_SELECT, select, A {A (translated) - {PH_A}} ' +
'B {B (translated) - {PH_B}} other {other (translated) - {PH_WITH_SPACES}}}',
'B {B (translated) - {PH_B}} other {other (translated) - {PH_WITH_SPACES}}}',
});
@Component({
selector: 'comp',
@ -1326,7 +1336,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
it('with custom interpolation config', () => {
loadTranslations({[computeMsgId('Hello {$INTERPOLATION}', 'm')]: 'Bonjour {$INTERPOLATION}'});
const interpolation = ['{%', '%}'] as[string, string];
const interpolation = ['{%', '%}'] as [string, string];
TestBed.overrideComponent(AppComp, {set: {interpolation}});
const fixture =
initWithTemplate(AppComp, `<div i18n-title="m|d" title="Hello {% name %}"></div>`);
@ -1367,7 +1377,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
})
class TitleDir {
@Input() title = '';
constructor() { titleDirInstances.push(this); }
constructor() {
titleDirInstances.push(this);
}
}
@Component({
@ -1397,7 +1409,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
@Directive({selector: '[title]'})
class TitleDir {
@Input() title: string = '';
constructor(public elRef: ElementRef) { titleDirInstances.push(this); }
constructor(public elRef: ElementRef) {
titleDirInstances.push(this);
}
}
@Component({
@ -1429,7 +1443,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
let dirInstance: WithInput;
@Directive({selector: '[dir]'})
class WithInput {
constructor() { dirInstance = this; }
constructor() {
dirInstance = this;
}
@Input() dir: string = '';
}
@ -1445,7 +1461,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
const fixture = TestBed.createComponent(TestComp);
fixture.detectChanges();
expect(dirInstance !.dir).toBe('Bonjour Angular');
expect(dirInstance!.dir).toBe('Bonjour Angular');
});
it('should allow directive inputs (as interpolated props)' +
@ -1456,7 +1472,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
let dirInstance: WithInput;
@Directive({selector: '[dir]'})
class WithInput {
constructor() { dirInstance = this; }
constructor() {
dirInstance = this;
}
@Input() dir: string = '';
}
@ -1472,7 +1490,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
const fixture = TestBed.createComponent(TestComp);
fixture.detectChanges();
expect(dirInstance !.dir).toBe('Bonjour Angular');
expect(dirInstance!.dir).toBe('Bonjour Angular');
});
it('should apply i18n attributes during second template pass', () => {
@ -1537,7 +1555,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
template: '{{ messageText }}',
})
class WelcomeComp {
@Input() messageText !: string;
@Input() messageText!: string;
}
@Component({
@ -1598,10 +1616,11 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
@Directive({selector: '[test]'})
class ClsDir {
@HostBinding('className')
klass = 'foo';
@HostBinding('className') klass = 'foo';
constructor() { directiveInstances.push(this); }
constructor() {
directiveInstances.push(this);
}
}
@Component({
@ -1641,7 +1660,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
// but IE does not.
expect(outerDiv.getAttribute('title')).toBe('début 2 milieu 1 fin');
expect(outerDiv.getAttribute('class')).toBe('foo');
expect(outerDiv.textContent !.trim()).toBe('traduction: un email');
expect(outerDiv.textContent!.trim()).toBe('traduction: un email');
expect(innerDiv.getAttribute('class')).toBe('foo');
directiveInstances.forEach(instance => instance.klass = 'bar');
@ -1651,7 +1670,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
expect(outerDiv.getAttribute('title')).toBe('début 3 milieu 2 fin');
expect(outerDiv.getAttribute('class')).toBe('bar');
expect(outerDiv.textContent !.trim()).toBe('traduction: 2 emails');
expect(outerDiv.textContent!.trim()).toBe('traduction: 2 emails');
expect(innerDiv.getAttribute('class')).toBe('bar');
});
@ -1660,21 +1679,25 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
let calledValue = false;
@Component({selector: 'my-comp', template: ''})
class MyComp {
t !: string;
t!: string;
@Input()
get title() { return this.t; }
get title() {
return this.t;
}
set title(title) {
calledTitle = true;
this.t = title;
}
@Input()
get value() { return this.val; }
get value() {
return this.val;
}
set value(value: string) {
calledValue = true;
this.val = value;
}
val !: string;
val!: string;
}
TestBed.configureTestingModule({declarations: [AppComp, MyComp]});
@ -2050,7 +2073,6 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
});
it('should display/destroy projected i18n content', () => {
loadTranslations({
[computeMsgId('{VAR_SELECT, select, A {A} B {B} other {other}}')]:
'{VAR_SELECT, select, A {A} B {B} other {other}}'
@ -2109,7 +2131,7 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
@Directive({selector: '[text]', inputs: ['text'], exportAs: 'textDir'})
class TextDirective {
// TODO(issue/24571): remove '!'.
text !: string;
text!: string;
constructor() {}
}
@ -2119,16 +2141,18 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
@ContentChild(TemplateRef, {static: true}) template !: TemplateRef<any>;
// TODO(issue/24571): remove '!'.
@ViewChild('vc', {read: ViewContainerRef, static: true})
vc !: ViewContainerRef;
@ViewChild('vc', {read: ViewContainerRef, static: true}) vc!: ViewContainerRef;
// TODO(issue/24571): remove '!'.
@ContentChildren(TextDirective, {descendants: true})
query !: QueryList<TextDirective>;
@ContentChildren(TextDirective, {descendants: true}) query!: QueryList<TextDirective>;
create() { this.vc.createEmbeddedView(this.template); }
create() {
this.vc.createEmbeddedView(this.template);
}
destroy() { this.vc.clear(); }
destroy() {
this.vc.clear();
}
}
TestBed.configureTestingModule({declarations: [TextDirective, DivQuery]});
@ -2224,7 +2248,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
@Directive({selector: 'input'})
class InputsDir {
constructor(private elementRef: ElementRef) {}
ngOnInit() { this.elementRef.nativeElement.value = 'value set in Directive.ngOnInit'; }
ngOnInit() {
this.elementRef.nativeElement.value = 'value set in Directive.ngOnInit';
}
}
@Component({
@ -2248,7 +2274,9 @@ onlyInIvy('Ivy i18n logic').describe('runtime i18n', () => {
@Directive({selector: 'input'})
class InputsDir {
constructor(private elementRef: ElementRef) {}
ngOnInit() { this.elementRef.nativeElement.value = 'value set in Directive.ngOnInit'; }
ngOnInit() {
this.elementRef.nativeElement.value = 'value set in Directive.ngOnInit';
}
}
@Component({
@ -2401,12 +2429,16 @@ class AppCompWithWhitespaces {
})
class DirectiveWithTplRef {
constructor(public vcRef: ViewContainerRef, public tplRef: TemplateRef<{}>) {}
ngOnInit() { this.vcRef.createEmbeddedView(this.tplRef, {}); }
ngOnInit() {
this.vcRef.createEmbeddedView(this.tplRef, {});
}
}
@Pipe({name: 'uppercase'})
class UppercasePipe implements PipeTransform {
transform(value: string) { return value.toUpperCase(); }
transform(value: string) {
return value.toUpperCase();
}
}
@Directive({selector: `[dialog]`})

View File

@ -16,10 +16,11 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
import {ivyEnabled, onlyInIvy} from '@angular/private/testing';
describe('acceptance integration tests', () => {
function stripHtmlComments(str: string) { return str.replace(/<!--[\s\S]*?-->/g, ''); }
function stripHtmlComments(str: string) {
return str.replace(/<!--[\s\S]*?-->/g, '');
}
describe('render', () => {
it('should render basic template', () => {
@Component({template: '<span title="Hello">Greetings</span>'})
class App {
@ -64,12 +65,10 @@ describe('acceptance integration tests', () => {
tView: 2,
tNode: 4,
});
});
});
describe('ng-container', () => {
it('should insert as a child of a regular element', () => {
@Component(
{template: '<div>before|<ng-container>Greetings<span></span></ng-container>|after</div>'})
@ -108,7 +107,6 @@ describe('acceptance integration tests', () => {
});
it('should add and remove DOM nodes when ng-container is a child of an embedded view', () => {
@Component({template: '<ng-container *ngIf="render">content</ng-container>'})
class App {
render = false;
@ -131,21 +129,24 @@ describe('acceptance integration tests', () => {
// https://stackblitz.com/edit/angular-tfhcz1?file=src%2Fapp%2Fapp.component.ts
it('should add and remove DOM nodes when ng-container is a child of a delayed embedded view',
() => {
@Directive({selector: '[testDirective]'})
class TestDirective {
constructor(private _tplRef: TemplateRef<any>, private _vcRef: ViewContainerRef) {}
createAndInsert() { this._vcRef.insert(this._tplRef.createEmbeddedView({})); }
createAndInsert() {
this._vcRef.insert(this._tplRef.createEmbeddedView({}));
}
clear() { this._vcRef.clear(); }
clear() {
this._vcRef.clear();
}
}
@Component({
template: '<ng-template testDirective><ng-container>content</ng-container></ng-template>'
})
class App {
@ViewChild(TestDirective, {static: true}) testDirective !: TestDirective;
@ViewChild(TestDirective, {static: true}) testDirective!: TestDirective;
}
TestBed.configureTestingModule({declarations: [App, TestDirective]});
@ -205,9 +206,13 @@ describe('acceptance integration tests', () => {
class TestDirective {
constructor(private _tplRef: TemplateRef<any>, private _vcRef: ViewContainerRef) {}
createAndInsert() { this._vcRef.insert(this._tplRef.createEmbeddedView({})); }
createAndInsert() {
this._vcRef.insert(this._tplRef.createEmbeddedView({}));
}
clear() { this._vcRef.clear(); }
clear() {
this._vcRef.clear();
}
}
@Component({
@ -215,7 +220,7 @@ describe('acceptance integration tests', () => {
'<ng-template testDirective><ng-container><ng-container><ng-container>content</ng-container></ng-container></ng-container></ng-template>'
})
class App {
@ViewChild(TestDirective, {static: true}) testDirective !: TestDirective;
@ViewChild(TestDirective, {static: true}) testDirective!: TestDirective;
}
TestBed.configureTestingModule({declarations: [App, TestDirective]});
@ -245,7 +250,7 @@ describe('acceptance integration tests', () => {
@Component({template: '<div><ng-container dir></ng-container></div>'})
class App {
@ViewChild(TestDirective) testDirective !: TestDirective;
@ViewChild(TestDirective) testDirective!: TestDirective;
}
TestBed.configureTestingModule({declarations: [App, TestDirective]});
@ -260,14 +265,17 @@ describe('acceptance integration tests', () => {
it('should support ViewContainerRef when ng-container is at the root of a view', () => {
@Directive({selector: '[dir]'})
class TestDirective {
@Input()
contentTpl: TemplateRef<{}>|null = null;
@Input() contentTpl: TemplateRef<{}>|null = null;
constructor(private _vcRef: ViewContainerRef) {}
insertView() { this._vcRef.createEmbeddedView(this.contentTpl as TemplateRef<{}>); }
insertView() {
this._vcRef.createEmbeddedView(this.contentTpl as TemplateRef<{}>);
}
clear() { this._vcRef.clear(); }
clear() {
this._vcRef.clear();
}
}
@Component({
@ -275,7 +283,7 @@ describe('acceptance integration tests', () => {
'<ng-container dir [contentTpl]="content"><ng-template #content>Content</ng-template></ng-container>'
})
class App {
@ViewChild(TestDirective) testDirective !: TestDirective;
@ViewChild(TestDirective) testDirective!: TestDirective;
}
TestBed.configureTestingModule({declarations: [App, TestDirective]});
@ -298,14 +306,18 @@ describe('acceptance integration tests', () => {
class TestDirective {
constructor(private _tplRef: TemplateRef<{}>, private _vcRef: ViewContainerRef) {}
insertView() { this._vcRef.createEmbeddedView(this._tplRef); }
insertView() {
this._vcRef.createEmbeddedView(this._tplRef);
}
clear() { this._vcRef.clear(); }
clear() {
this._vcRef.clear();
}
}
@Component({template: '<ng-container><ng-template dir>Content</ng-template></ng-container>'})
class App {
@ViewChild(TestDirective) testDirective !: TestDirective;
@ViewChild(TestDirective) testDirective!: TestDirective;
}
TestBed.configureTestingModule({declarations: [App, TestDirective]});
@ -334,7 +346,6 @@ describe('acceptance integration tests', () => {
expect(stripHtmlComments(fixture.nativeElement.innerHTML)).toEqual('<div></div>');
});
});
describe('text bindings', () => {
@ -373,11 +384,12 @@ describe('acceptance integration tests', () => {
expect(fixture.nativeElement.innerHTML).toEqual('');
});
});
describe('ngNonBindable handling', () => {
function stripNgNonBindable(str: string) { return str.replace(/ ngnonbindable=""/i, ''); }
function stripNgNonBindable(str: string) {
return str.replace(/ ngnonbindable=""/i, '');
}
it('should keep local ref for host element', () => {
@Component({
@ -404,7 +416,9 @@ describe('acceptance integration tests', () => {
@Directive({selector: '[directive]'})
class TestDirective implements OnInit {
ngOnInit() { directiveInvoked = true; }
ngOnInit() {
directiveInvoked = true;
}
}
@Component({
@ -432,7 +446,9 @@ describe('acceptance integration tests', () => {
@Directive({selector: '[directive]'})
class TestDirective implements OnInit {
ngOnInit() { directiveInvoked = true; }
ngOnInit() {
directiveInvoked = true;
}
}
@Component({
@ -604,14 +620,12 @@ describe('acceptance integration tests', () => {
it('should support a component with binding on host element', () => {
@Component({selector: 'todo', template: '{{title}}'})
class TodoComponentHostBinding {
@HostBinding()
title = 'one';
@HostBinding() title = 'one';
}
@Component({template: '<todo></todo>'})
class App {
@ViewChild(TodoComponentHostBinding)
todoComponentHostBinding !: TodoComponentHostBinding;
@ViewChild(TodoComponentHostBinding) todoComponentHostBinding!: TodoComponentHostBinding;
}
TestBed.configureTestingModule({declarations: [App, TodoComponentHostBinding]});
@ -658,8 +672,7 @@ describe('acceptance integration tests', () => {
it('should support a component with sub-views', () => {
@Component({selector: 'comp', template: '<div *ngIf="condition">text</div>'})
class MyComp {
@Input()
condition !: boolean;
@Input() condition!: boolean;
}
@Component({template: '<comp [condition]="condition"></comp>'})
@ -680,7 +693,6 @@ describe('acceptance integration tests', () => {
fixture.detectChanges();
expect(stripHtmlComments(compElement.innerHTML)).toEqual('');
});
});
describe('element bindings', () => {
@ -814,7 +826,7 @@ describe('acceptance integration tests', () => {
fixture.detectChanges();
const span: HTMLSpanElement = fixture.nativeElement.querySelector('span');
const bold: HTMLElement = span.querySelector('b') !;
const bold: HTMLElement = span.querySelector('b')!;
fixture.componentInstance.title = 'Hello';
fixture.detectChanges();
@ -840,13 +852,12 @@ describe('acceptance integration tests', () => {
it('should support host attribute bindings', () => {
@Directive({selector: '[hostBindingDir]'})
class HostBindingDir {
@HostBinding('attr.aria-label')
label = 'some label';
@HostBinding('attr.aria-label') label = 'some label';
}
@Component({template: '<div hostBindingDir></div>'})
class App {
@ViewChild(HostBindingDir) hostBindingDir !: HostBindingDir;
@ViewChild(HostBindingDir) hostBindingDir!: HostBindingDir;
}
TestBed.configureTestingModule({declarations: [App, HostBindingDir]});
@ -999,12 +1010,13 @@ describe('acceptance integration tests', () => {
it('should apply classes properly when nodes have containers', () => {
@Component({selector: 'structural-comp', template: 'Comp Content'})
class StructuralComp {
@Input()
tmp !: TemplateRef<any>;
@Input() tmp!: TemplateRef<any>;
constructor(public vcr: ViewContainerRef) {}
create() { this.vcr.createEmbeddedView(this.tmp); }
create() {
this.vcr.createEmbeddedView(this.tmp);
}
}
@Component({
@ -1014,7 +1026,7 @@ describe('acceptance integration tests', () => {
`
})
class App {
@ViewChild(StructuralComp) structuralComp !: StructuralComp;
@ViewChild(StructuralComp) structuralComp!: StructuralComp;
value: any;
}
@ -1040,7 +1052,9 @@ describe('acceptance integration tests', () => {
public classesVal: string = '';
@Input('class')
set klass(value: string) { this.classesVal = value; }
set klass(value: string) {
this.classesVal = value;
}
}
@Directive({selector: '[DirWithStyle]'})
@ -1048,15 +1062,16 @@ describe('acceptance integration tests', () => {
public stylesVal: any = '';
@Input()
set style(value: any) { this.stylesVal = value; }
set style(value: any) {
this.stylesVal = value;
}
}
it('should delegate initial classes to a [class] input binding if present on a directive on the same element',
() => {
@Component({template: '<div class="apple orange banana" DirWithClass></div>'})
class App {
@ViewChild(DirWithClassDirective)
mockClassDirective !: DirWithClassDirective;
@ViewChild(DirWithClassDirective) mockClassDirective!: DirWithClassDirective;
}
TestBed.configureTestingModule({declarations: [App, DirWithClassDirective]});
@ -1073,8 +1088,7 @@ describe('acceptance integration tests', () => {
() => {
@Component({template: '<div style="width: 100px; height: 200px" DirWithStyle></div>'})
class App {
@ViewChild(DirWithStyleDirective)
mockStyleDirective !: DirWithStyleDirective;
@ViewChild(DirWithStyleDirective) mockStyleDirective!: DirWithStyleDirective;
}
TestBed.configureTestingModule({declarations: [App, DirWithStyleDirective]});
@ -1092,8 +1106,7 @@ describe('acceptance integration tests', () => {
() => {
@Component({template: '<div DirWithClass [class]="value"></div>'})
class App {
@ViewChild(DirWithClassDirective)
mockClassDirective !: DirWithClassDirective;
@ViewChild(DirWithClassDirective) mockClassDirective!: DirWithClassDirective;
value = '';
}
@ -1111,9 +1124,8 @@ describe('acceptance integration tests', () => {
() => {
@Component({template: '<div DirWithStyle [style]="value"></div>'})
class App {
@ViewChild(DirWithStyleDirective)
mockStyleDirective !: DirWithStyleDirective;
value !: {[key: string]: string};
@ViewChild(DirWithStyleDirective) mockStyleDirective!: DirWithStyleDirective;
value!: {[key: string]: string};
}
TestBed.configureTestingModule({declarations: [App, DirWithStyleDirective]});
@ -1155,7 +1167,7 @@ describe('acceptance integration tests', () => {
fixture.detectChanges();
const target: HTMLDivElement = fixture.nativeElement.querySelector('div');
const classes = target.getAttribute('class') !.split(/\s+/).sort();
const classes = target.getAttribute('class')!.split(/\s+/).sort();
expect(classes).toEqual(['big', 'golden', 'heavy']);
expect(target.getAttribute('title')).toEqual('foo');
@ -1189,7 +1201,7 @@ describe('acceptance integration tests', () => {
})
class App {
@ViewChild(DirWithSingleStylingBindings)
dirInstance !: DirWithSingleStylingBindings;
dirInstance!: DirWithSingleStylingBindings;
}
TestBed.configureTestingModule({declarations: [App, DirWithSingleStylingBindings]});
@ -1244,8 +1256,8 @@ describe('acceptance integration tests', () => {
@Component(
{template: '<div Dir1WithStyle Dir2WithStyle [style.width]="width"></div>'})
class App {
@ViewChild(Dir1WithStyle) dir1Instance !: Dir1WithStyle;
@ViewChild(Dir2WithStyle) dir2Instance !: Dir2WithStyle;
@ViewChild(Dir1WithStyle) dir1Instance!: Dir1WithStyle;
@ViewChild(Dir2WithStyle) dir2Instance!: Dir2WithStyle;
width: string|null|undefined = undefined;
}
@ -1309,8 +1321,8 @@ describe('acceptance integration tests', () => {
'<div Dir1WithStyling Dir2WithStyling [style]="stylesExp" [class]="classesExp"></div>'
})
class App {
@ViewChild(Dir1WithStyling) dir1Instance !: Dir1WithStyling;
@ViewChild(Dir2WithStyling) dir2Instance !: Dir2WithStyling;
@ViewChild(Dir1WithStyling) dir1Instance!: Dir1WithStyling;
@ViewChild(Dir2WithStyling) dir2Instance!: Dir2WithStyling;
stylesExp: any = {};
classesExp: any = {};
}
@ -1321,7 +1333,7 @@ describe('acceptance integration tests', () => {
fixture.detectChanges();
const {dir1Instance, dir2Instance} = fixture.componentInstance;
const target = fixture.nativeElement.querySelector('div') !;
const target = fixture.nativeElement.querySelector('div')!;
expect(target.style.getPropertyValue('width')).toEqual('111px');
const compInstance = fixture.componentInstance;
@ -1393,7 +1405,7 @@ describe('acceptance integration tests', () => {
TestBed.configureTestingModule({declarations: [App]});
const fixture = TestBed.createComponent(App);
const target = fixture.nativeElement.querySelector('div') !;
const target = fixture.nativeElement.querySelector('div')!;
expect(target.classList.contains('-fred-36-')).toBeFalsy();
@ -1510,7 +1522,6 @@ describe('acceptance integration tests', () => {
// The ViewEngine error has a typo, whereas the Ivy one fixes it.
/^Unexpected value 'SomeModule' imported by the module 'ModuleWithImportedModule'\. Please add (a|an) @NgModule annotation\.$/);
});
});
it('should only call inherited host listeners once', () => {
@ -1519,7 +1530,9 @@ describe('acceptance integration tests', () => {
@Component({template: ''})
class ButtonSuperClass {
@HostListener('click')
clicked() { clicks++; }
clicked() {
clicks++;
}
}
@Component({selector: 'button[custom-button]', template: ''})
@ -1548,7 +1561,7 @@ describe('acceptance integration tests', () => {
@Component({template: '<div someDir></div>'})
class SuperComp {
@ViewChildren(SomeDir) dirs !: QueryList<SomeDir>;
@ViewChildren(SomeDir) dirs!: QueryList<SomeDir>;
}
@Component({selector: 'button[custom-button]', template: '<div someDir></div>'})
@ -1576,7 +1589,9 @@ describe('acceptance integration tests', () => {
private _isDestroyed = false;
@Input()
get value() { return this._value; }
get value() {
return this._value;
}
set value(newValue: any) {
if (this._isDestroyed) {
throw Error('Cannot assign to value after destroy.');
@ -1586,7 +1601,9 @@ describe('acceptance integration tests', () => {
}
private _value: any;
ngOnDestroy() { this._isDestroyed = true; }
ngOnDestroy() {
this._isDestroyed = true;
}
}
@Component({template: '<div no-assign-after-destroy [value]="directiveValue"></div>'})
@ -1608,7 +1625,7 @@ describe('acceptance integration tests', () => {
@Component(
{selector: 'test-component', template: `foo`, host: {'[attr.aria-disabled]': 'true'}})
class TestComponent {
@ContentChild(TemplateRef, {static: true}) tpl !: TemplateRef<any>;
@ContentChild(TemplateRef, {static: true}) tpl!: TemplateRef<any>;
}
TestBed.configureTestingModule({declarations: [TestComponent]});
@ -1621,7 +1638,7 @@ describe('acceptance integration tests', () => {
it('should inherit inputs from undecorated superclasses', () => {
class ButtonSuperClass {
@Input() isDisabled !: boolean;
@Input() isDisabled!: boolean;
}
@Component({selector: 'button[custom-button]', template: ''})
@ -1651,7 +1668,9 @@ describe('acceptance integration tests', () => {
class ButtonSuperClass {
@Output() clicked = new EventEmitter<void>();
emitClick() { this.clicked.emit(); }
emitClick() {
this.clicked.emit();
}
}
@Component({selector: 'button[custom-button]', template: ''})
@ -1660,7 +1679,9 @@ describe('acceptance integration tests', () => {
@Component({template: '<button custom-button (clicked)="handleClick()"></button>'})
class MyApp {
handleClick() { clicks++; }
handleClick() {
clicks++;
}
}
TestBed.configureTestingModule({declarations: [MyApp, ButtonSubClass]});
@ -1675,8 +1696,7 @@ describe('acceptance integration tests', () => {
it('should inherit host bindings from undecorated superclasses', () => {
class BaseButton {
@HostBinding('attr.tabindex')
tabindex = -1;
@HostBinding('attr.tabindex') tabindex = -1;
}
@Component({selector: '[sub-button]', template: '<ng-content></ng-content>'})
@ -1702,8 +1722,7 @@ describe('acceptance integration tests', () => {
it('should inherit host bindings from undecorated grand superclasses', () => {
class SuperBaseButton {
@HostBinding('attr.tabindex')
tabindex = -1;
@HostBinding('attr.tabindex') tabindex = -1;
}
class BaseButton extends SuperBaseButton {}
@ -1734,7 +1753,9 @@ describe('acceptance integration tests', () => {
class BaseButton {
@HostListener('click')
handleClick() { clicks++; }
handleClick() {
clicks++;
}
}
@Component({selector: '[sub-button]', template: '<ng-content></ng-content>'})
@ -1761,7 +1782,9 @@ describe('acceptance integration tests', () => {
@Directive({selector: '[baseButton]'})
class BaseButton {
@HostListener('click')
handleClick() { clicks++; }
handleClick() {
clicks++;
}
}
@Component({selector: '[subButton]', template: '<ng-content></ng-content>'})
@ -1788,7 +1811,9 @@ describe('acceptance integration tests', () => {
@Directive({selector: '[superBaseButton]'})
class SuperBaseButton {
@HostListener('click')
handleClick() { clicks++; }
handleClick() {
clicks++;
}
}
@Directive({selector: '[baseButton]'})
@ -1819,7 +1844,9 @@ describe('acceptance integration tests', () => {
@Directive({selector: '[superSuperBaseButton]'})
class SuperSuperBaseButton {
@HostListener('click')
handleClick() { clicks++; }
handleClick() {
clicks++;
}
}
@Directive({selector: '[superBaseButton]'})
@ -1855,9 +1882,13 @@ describe('acceptance integration tests', () => {
inputs: ['dir'],
})
class Dir {
get dir(): any { return null; }
get dir(): any {
return null;
}
set dir(value: any) { throw new Error('this error is expected'); }
set dir(value: any) {
throw new Error('this error is expected');
}
}
@Component({
@ -1919,7 +1950,7 @@ describe('acceptance integration tests', () => {
});
const fixture = TestBed.createComponent(Cmp);
fixture.detectChanges(false);
const element = fixture.nativeElement.querySelector('div') !;
const element = fixture.nativeElement.querySelector('div')!;
assertAttrValues(element, 'first-update-pass');

File diff suppressed because it is too large Load Diff

View File

@ -16,36 +16,40 @@ function getNoOfNativeListeners(): number {
}
describe('event listeners', () => {
describe('coalescing', () => {
@Component({
selector: 'with-clicks-cmpt',
template: `<button likes-clicks (click)="count()" md-button>Click me!</button>`
})
class WithClicksCmpt {
counter = 0;
count() { this.counter++; }
count() {
this.counter++;
}
}
@Directive({selector: '[md-button]'})
class MdButton {
counter = 0;
@HostListener('click')
count() { this.counter++; }
count() {
this.counter++;
}
}
@Directive({selector: '[likes-clicks]'})
class LikesClicks {
counter = 0;
@HostListener('click')
count() { this.counter++; }
count() {
this.counter++;
}
}
@Directive({selector: '[returns-false]'})
class ReturnsFalse {
counter = 0;
event !: Event;
event!: Event;
handlerShouldReturn: boolean|undefined = undefined;
@HostListener('click', ['$event'])
@ -65,7 +69,6 @@ describe('event listeners', () => {
onlyInIvy('ngDevMode.rendererAddEventListener counters are only available in ivy')
.it('should coalesce multiple event listeners for the same event on the same element',
() => {
@Component({
selector: 'test-cmpt',
template:
@ -107,7 +110,6 @@ describe('event listeners', () => {
onlyInIvy('ngDevMode.rendererAddEventListener counters are only available in ivy')
.it('should coalesce multiple event listeners in presence of queries', () => {
@Component({
selector: 'test-cmpt',
template: `<button likes-clicks (click)="counter = counter+1">Click me!</button>`
@ -115,7 +117,7 @@ describe('event listeners', () => {
class TestCmpt {
counter = 0;
@ViewChildren('nothing') nothing !: QueryList<any>;
@ViewChildren('nothing') nothing!: QueryList<any>;
}
TestBed.configureTestingModule({declarations: [TestCmpt, LikesClicks]});
@ -136,11 +138,12 @@ describe('event listeners', () => {
it('should try to execute remaining coalesced listeners if one of the listeners throws', () => {
@Directive({selector: '[throws-on-clicks]'})
class ThrowsOnClicks {
@HostListener('click')
dontCount() { throw new Error('I was clicked and I don\'t like it!'); }
dontCount() {
throw new Error('I was clicked and I don\'t like it!');
}
}
@Component(
@ -151,7 +154,9 @@ describe('event listeners', () => {
let noOfErrors = 0;
class CountingErrorHandler extends ErrorHandler {
handleError(error: any): void { noOfErrors++; }
handleError(error: any): void {
noOfErrors++;
}
}
TestBed.configureTestingModule({
@ -209,7 +214,9 @@ describe('event listeners', () => {
@Input('foo') model: any;
@Output('fooChange') update = new EventEmitter();
updateValue(value: any) { this.update.emit(value); }
updateValue(value: any) {
this.update.emit(value);
}
}
@Component({
@ -222,9 +229,13 @@ describe('event listeners', () => {
@ViewChild(FooDirective) fooDirective: FooDirective|null = null;
fooChange() { this.count++; }
fooChange() {
this.count++;
}
triggerUpdate(value: any) { this.fooDirective !.updateValue(value); }
triggerUpdate(value: any) {
this.fooDirective!.updateValue(value);
}
}
TestBed.configureTestingModule({declarations: [TestComponent, FooDirective]});
@ -247,19 +258,25 @@ describe('event listeners', () => {
})
class MyComp {
counter = 0;
count() { log.push('component.click'); }
count() {
log.push('component.click');
}
}
@Directive({selector: '[dirA]'})
class DirA {
@HostListener('click')
count() { log.push('dirA.click'); }
count() {
log.push('dirA.click');
}
}
@Directive({selector: '[dirB]'})
class DirB {
@HostListener('click')
count() { log.push('dirB.click'); }
count() {
log.push('dirB.click');
}
}
TestBed.configureTestingModule({declarations: [MyComp, DirA, DirB]});

View File

@ -7,7 +7,7 @@
*/
import {CommonModule} from '@angular/common';
import {CUSTOM_ELEMENTS_SCHEMA, Component, Injectable, InjectionToken, NO_ERRORS_SCHEMA, NgModule, NgModuleRef, ɵsetClassMetadata as setClassMetadata, ɵɵdefineComponent as defineComponent, ɵɵdefineInjector as defineInjector, ɵɵdefineNgModule as defineNgModule, ɵɵelement as element} from '@angular/core';
import {Component, CUSTOM_ELEMENTS_SCHEMA, Injectable, InjectionToken, NgModule, NgModuleRef, NO_ERRORS_SCHEMA, ɵsetClassMetadata as setClassMetadata, ɵɵdefineComponent as defineComponent, ɵɵdefineInjector as defineInjector, ɵɵdefineNgModule as defineNgModule, ɵɵelement as element} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {expect} from '@angular/platform-browser/testing/src/matchers';
import {modifiedInIvy, onlyInIvy} from '@angular/private/testing';
@ -84,12 +84,16 @@ describe('NgModule', () => {
}
@NgModule({providers: [Service]})
class RoutesModule {
constructor(service: Service) { service.initializations.push('RoutesModule'); }
constructor(service: Service) {
service.initializations.push('RoutesModule');
}
}
@NgModule({imports: [RoutesModule]})
class AppModule {
constructor(service: Service) { service.initializations.push('AppModule'); }
constructor(service: Service) {
service.initializations.push('AppModule');
}
}
TestBed.configureTestingModule({imports: [AppModule]});
@ -188,7 +192,6 @@ describe('NgModule', () => {
onlyInIvy('unknown element check logs a warning rather than throwing')
.it('should warn about unknown element without CUSTOM_ELEMENTS_SCHEMA for element with dash in tag name',
() => {
@Component({template: `<custom-el></custom-el>`})
class MyComp {
}
@ -262,11 +265,12 @@ describe('NgModule', () => {
selectors: [['comp']],
decls: 1,
vars: 0,
template: function MyComp_Template(rf, ctx) {
if (rf & 1) {
element(0, 'custom-el');
}
},
template:
function MyComp_Template(rf, ctx) {
if (rf & 1) {
element(0, 'custom-el');
}
},
encapsulation: 2
});
}
@ -472,7 +476,6 @@ describe('NgModule', () => {
fixture.detectChanges();
}).not.toThrow();
});
});
it('should be able to use DI through the NgModuleRef inside the module constructor', () => {
@ -484,7 +487,9 @@ describe('NgModule', () => {
providers: [{provide: token, useValue: 'foo'}],
})
class TestModule {
constructor(ngRef: NgModuleRef<TestModule>) { value = ngRef.injector.get(token); }
constructor(ngRef: NgModuleRef<TestModule>) {
value = ngRef.injector.get(token);
}
}
TestBed.configureTestingModule({imports: [TestModule], declarations: [TestCmp]});
@ -514,5 +519,4 @@ describe('NgModule', () => {
TestBed.createComponent(TestCmp);
expect(componentInstance).toBeAnInstanceOf(TestCmp);
});
});

View File

@ -40,10 +40,9 @@ onlyInIvy('Debug information exist in ivy only').describe('ngDevMode debug', ()
const element: HTMLElement = fixture.nativeElement;
fixture.detectChanges();
const li = element.querySelector('li') !;
const li = element.querySelector('li')!;
const embeddedLView = loadLContext(li).lView;
expect(embeddedLView.constructor.name).toEqual('LEmbeddedView_MyApp_li_1');
});
});
});

View File

@ -13,29 +13,27 @@ import {TestBed} from '@angular/core/testing';
describe('outputs', () => {
@Component({selector: 'button-toggle', template: ''})
class ButtonToggle {
@Output('change')
change = new EventEmitter();
@Output('change') change = new EventEmitter();
@Output('reset')
resetStream = new EventEmitter();
@Output('reset') resetStream = new EventEmitter();
}
@Directive({selector: '[otherDir]'})
class OtherDir {
@Output('change')
changeStream = new EventEmitter();
@Output('change') changeStream = new EventEmitter();
}
@Component({selector: 'destroy-comp', template: ''})
class DestroyComp implements OnDestroy {
events: string[] = [];
ngOnDestroy() { this.events.push('destroy'); }
ngOnDestroy() {
this.events.push('destroy');
}
}
@Directive({selector: '[myButton]'})
class MyButton {
@Output()
click = new EventEmitter();
@Output() click = new EventEmitter();
}
it('should call component output function when event is emitted', () => {
@ -43,8 +41,10 @@ describe('outputs', () => {
@Component({template: '<button-toggle (change)="onChange()"></button-toggle>'})
class App {
@ViewChild(ButtonToggle) buttonToggle !: ButtonToggle;
onChange() { counter++; }
@ViewChild(ButtonToggle) buttonToggle!: ButtonToggle;
onChange() {
counter++;
}
}
TestBed.configureTestingModule({declarations: [App, ButtonToggle]});
const fixture = TestBed.createComponent(App);
@ -64,9 +64,13 @@ describe('outputs', () => {
@Component(
{template: '<button-toggle (change)="onChange()" (reset)="onReset()"></button-toggle>'})
class App {
@ViewChild(ButtonToggle) buttonToggle !: ButtonToggle;
onChange() { counter++; }
onReset() { resetCounter++; }
@ViewChild(ButtonToggle) buttonToggle!: ButtonToggle;
onChange() {
counter++;
}
onReset() {
resetCounter++;
}
}
TestBed.configureTestingModule({declarations: [App, ButtonToggle]});
const fixture = TestBed.createComponent(App);
@ -82,7 +86,7 @@ describe('outputs', () => {
it('should eval component output expression when event is emitted', () => {
@Component({template: '<button-toggle (change)="counter = counter + 1"></button-toggle>'})
class App {
@ViewChild(ButtonToggle) buttonToggle !: ButtonToggle;
@ViewChild(ButtonToggle) buttonToggle!: ButtonToggle;
counter = 0;
}
TestBed.configureTestingModule({declarations: [App, ButtonToggle]});
@ -102,10 +106,12 @@ describe('outputs', () => {
@Component(
{template: '<button-toggle *ngIf="condition" (change)="onChange()"></button-toggle>'})
class App {
@ViewChild(ButtonToggle) buttonToggle !: ButtonToggle;
@ViewChild(ButtonToggle) buttonToggle!: ButtonToggle;
condition = true;
onChange() { counter++; }
onChange() {
counter++;
}
}
TestBed.configureTestingModule({imports: [CommonModule], declarations: [App, ButtonToggle]});
const fixture = TestBed.createComponent(App);
@ -133,11 +139,13 @@ describe('outputs', () => {
`
})
class App {
@ViewChild(ButtonToggle) buttonToggle !: ButtonToggle;
@ViewChild(ButtonToggle) buttonToggle!: ButtonToggle;
condition = true;
condition2 = true;
onChange() { counter++; }
onChange() {
counter++;
}
}
TestBed.configureTestingModule({imports: [CommonModule], declarations: [App, ButtonToggle]});
const fixture = TestBed.createComponent(App);
@ -168,12 +176,16 @@ describe('outputs', () => {
`
})
class App {
@ViewChild(ButtonToggle) buttonToggle !: ButtonToggle;
@ViewChild(DestroyComp) destroyComp !: DestroyComp;
@ViewChild(ButtonToggle) buttonToggle!: ButtonToggle;
@ViewChild(DestroyComp) destroyComp!: DestroyComp;
condition = true;
onClick() { clickCounter++; }
onChange() { changeCounter++; }
onClick() {
clickCounter++;
}
onChange() {
changeCounter++;
}
}
TestBed.configureTestingModule(
{imports: [CommonModule], declarations: [App, ButtonToggle, DestroyComp]});
@ -206,8 +218,10 @@ describe('outputs', () => {
@Component({template: '<button myButton (click)="onClick()">Click me</button>'})
class App {
@ViewChild(MyButton) buttonDir !: MyButton;
onClick() { counter++; }
@ViewChild(MyButton) buttonDir!: MyButton;
onClick() {
counter++;
}
}
TestBed.configureTestingModule({declarations: [App, MyButton]});
const fixture = TestBed.createComponent(App);
@ -228,9 +242,11 @@ describe('outputs', () => {
@Component({template: '<button-toggle (change)="onChange()" otherDir></button-toggle>'})
class App {
@ViewChild(ButtonToggle) buttonToggle !: ButtonToggle;
@ViewChild(OtherDir) otherDir !: OtherDir;
onChange() { counter++; }
@ViewChild(ButtonToggle) buttonToggle!: ButtonToggle;
@ViewChild(OtherDir) otherDir!: OtherDir;
onChange() {
counter++;
}
}
TestBed.configureTestingModule({declarations: [App, ButtonToggle, OtherDir]});
const fixture = TestBed.createComponent(App);
@ -248,8 +264,7 @@ describe('outputs', () => {
@Directive({selector: '[otherChangeDir]'})
class OtherChangeDir {
@Input()
change !: boolean;
@Input() change!: boolean;
}
@Component({
@ -257,11 +272,13 @@ describe('outputs', () => {
'<button-toggle (change)="onChange()" otherChangeDir [change]="change"></button-toggle>'
})
class App {
@ViewChild(ButtonToggle) buttonToggle !: ButtonToggle;
@ViewChild(OtherChangeDir) otherDir !: OtherChangeDir;
@ViewChild(ButtonToggle) buttonToggle!: ButtonToggle;
@ViewChild(OtherChangeDir) otherDir!: OtherChangeDir;
change = true;
onChange() { counter++; }
onChange() {
counter++;
}
}
TestBed.configureTestingModule({declarations: [App, ButtonToggle, OtherChangeDir]});
const fixture = TestBed.createComponent(App);
@ -278,5 +295,4 @@ describe('outputs', () => {
buttonToggle.change.next();
expect(counter).toBe(1);
});
});

View File

@ -14,7 +14,9 @@ describe('pipe', () => {
@Pipe({name: 'countingPipe'})
class CountingPipe implements PipeTransform {
state: number = 0;
transform(value: any) { return `${value} state:${this.state++}`; }
transform(value: any) {
return `${value} state:${this.state++}`;
}
}
@Pipe({name: 'multiArgPipe'})
@ -57,20 +59,21 @@ describe('pipe', () => {
it('should support bindings', () => {
@Directive({selector: '[my-dir]'})
class Dir {
@Input()
dirProp: string = '';
@Input() dirProp: string = '';
}
@Pipe({name: 'double'})
class DoublePipe implements PipeTransform {
transform(value: any) { return `${value}${value}`; }
transform(value: any) {
return `${value}${value}`;
}
}
@Component({
template: `<div my-dir [dirProp]="'a'|double"></div>`,
})
class App {
@ViewChild(Dir) directive !: Dir;
@ViewChild(Dir) directive!: Dir;
}
TestBed.configureTestingModule({declarations: [App, DoublePipe, Dir]});
@ -113,7 +116,9 @@ describe('pipe', () => {
it('should pick a Pipe defined in `declarations` over imported Pipes', () => {
@Pipe({name: 'number'})
class PipeA implements PipeTransform {
transform(value: any) { return `PipeA: ${value}`; }
transform(value: any) {
return `PipeA: ${value}`;
}
}
@NgModule({
@ -125,7 +130,9 @@ describe('pipe', () => {
@Pipe({name: 'number'})
class PipeB implements PipeTransform {
transform(value: any) { return `PipeB: ${value}`; }
transform(value: any) {
return `PipeB: ${value}`;
}
}
@Component({
@ -150,7 +157,9 @@ describe('pipe', () => {
() => {
@Pipe({name: 'number'})
class PipeA implements PipeTransform {
transform(value: any) { return `PipeA: ${value}`; }
transform(value: any) {
return `PipeA: ${value}`;
}
}
@NgModule({
@ -162,7 +171,9 @@ describe('pipe', () => {
@Pipe({name: 'number'})
class PipeB implements PipeTransform {
transform(value: any) { return `PipeB: ${value}`; }
transform(value: any) {
return `PipeB: ${value}`;
}
}
@NgModule({
@ -222,12 +233,16 @@ describe('pipe', () => {
it('should support duplicates by using the later entry', () => {
@Pipe({name: 'duplicatePipe'})
class DuplicatePipe1 implements PipeTransform {
transform(value: any) { return `${value} from duplicate 1`; }
transform(value: any) {
return `${value} from duplicate 1`;
}
}
@Pipe({name: 'duplicatePipe'})
class DuplicatePipe2 implements PipeTransform {
transform(value: any) { return `${value} from duplicate 2`; }
transform(value: any) {
return `${value} from duplicate 2`;
}
}
@Component({
@ -247,7 +262,9 @@ describe('pipe', () => {
it('should support pipe in context of ternary operator', () => {
@Pipe({name: 'pipe'})
class MyPipe implements PipeTransform {
transform(value: any): any { return value; }
transform(value: any): any {
return value;
}
}
@Component({
@ -313,8 +330,12 @@ describe('pipe', () => {
@Pipe({name: 'countingImpurePipe', pure: false})
class CountingImpurePipe implements PipeTransform {
state: number = 0;
transform(value: any) { return `${value} state:${this.state++}`; }
constructor() { impurePipeInstances.push(this); }
transform(value: any) {
return `${value} state:${this.state++}`;
}
constructor() {
impurePipeInstances.push(this);
}
}
beforeEach(() => impurePipeInstances = []);
@ -372,8 +393,12 @@ describe('pipe', () => {
@Pipe({name: 'pipeWithOnDestroy'})
class PipeWithOnDestroy implements PipeTransform, OnDestroy {
ngOnDestroy() { destroyCalls++; }
transform(value: any): any { return null; }
ngOnDestroy() {
destroyCalls++;
}
transform(value: any): any {
return null;
}
}
@Component({
@ -401,7 +426,9 @@ describe('pipe', () => {
@Pipe({name: 'myConcatPipe'})
class ConcatPipe implements PipeTransform {
constructor(public service: Service) {}
transform(value: string): string { return `${value} - ${this.service.title}`; }
transform(value: string): string {
return `${value} - ${this.service.title}`;
}
}
@Component({
@ -428,7 +455,9 @@ describe('pipe', () => {
@Pipe({name: 'myConcatPipe'})
class ConcatPipe implements PipeTransform {
constructor(@Inject(token) public service: Service) {}
transform(value: string): string { return `${value} - ${this.service.title}`; }
transform(value: string): string {
return `${value} - ${this.service.title}`;
}
}
@Component({
@ -461,7 +490,9 @@ describe('pipe', () => {
@Pipe({name: 'myConcatPipe'})
class ConcatPipe implements PipeTransform {
constructor(public service: Service) {}
transform(value: string): string { return `${value} - ${this.service.title}`; }
transform(value: string): string {
return `${value} - ${this.service.title}`;
}
}
@Component({
@ -501,7 +532,7 @@ describe('pipe', () => {
})
class App {
@Input() something: any;
@ViewChild(SomeComp) comp !: SomeComp;
@ViewChild(SomeComp) comp!: SomeComp;
pipeValue = 10;
displayValue = 0;
}
@ -512,7 +543,9 @@ describe('pipe', () => {
pipeChangeDetectorRef = changeDetectorRef;
}
transform() { return ''; }
transform() {
return '';
}
}
TestBed.configureTestingModule({declarations: [App, SomeComp, TestPipe]});
@ -521,7 +554,7 @@ describe('pipe', () => {
fixture.componentInstance.displayValue = 1;
fixture.componentInstance.comp.displayValue = 1;
pipeChangeDetectorRef !.markForCheck();
pipeChangeDetectorRef!.markForCheck();
fixture.detectChanges();
expect(fixture.nativeElement.textContent).toContain('Outer value: "1"');
@ -553,7 +586,7 @@ describe('pipe', () => {
})
class App {
@Input() something: any;
@ViewChild(SomeComp) comp !: SomeComp;
@ViewChild(SomeComp) comp!: SomeComp;
pipeValue = 10;
displayValue = 0;
}
@ -564,7 +597,9 @@ describe('pipe', () => {
pipeChangeDetectorRef = changeDetectorRef;
}
transform() { return ''; }
transform() {
return '';
}
}
TestBed.configureTestingModule({declarations: [App, SomeComp, TestPipe]});
@ -573,22 +608,21 @@ describe('pipe', () => {
fixture.componentInstance.displayValue = 1;
fixture.componentInstance.comp.displayValue = 1;
pipeChangeDetectorRef !.markForCheck();
pipeChangeDetectorRef!.markForCheck();
fixture.detectChanges();
expect(fixture.nativeElement.textContent).toContain('Outer value: "1"');
expect(fixture.nativeElement.textContent).toContain('Inner value: "0"');
});
});
describe('pure pipe error handling', () => {
it('should not re-invoke pure pipes if it fails initially', () => {
@Pipe({name: 'throwPipe', pure: true})
class ThrowPipe implements PipeTransform {
transform(): never { throw new Error('ThrowPipeError'); }
transform(): never {
throw new Error('ThrowPipeError');
}
}
@Component({template: `{{val | throwPipe}}`})
class App {
@ -607,7 +641,6 @@ describe('pipe', () => {
it('should display the last known result from a pure pipe when it throws', () => {
@Pipe({name: 'throwPipe', pure: true})
class ThrowPipe implements PipeTransform {
transform(value: string): string {
@ -647,7 +680,8 @@ describe('pipe', () => {
describe('pure pipe error handling with multiple arguments', () => {
const args: string[] = new Array(10).fill(':0');
for (let numberOfPipeArgs = 0; numberOfPipeArgs < args.length; numberOfPipeArgs++) {
it(`should not invoke ${numberOfPipeArgs} argument pure pipe second time if it throws unless input changes`,
it(`should not invoke ${
numberOfPipeArgs} argument pure pipe second time if it throws unless input changes`,
() => {
// https://stackblitz.com/edit/angular-mbx2pg
const log: string[] = [];
@ -685,8 +719,5 @@ describe('pipe', () => {
});
}
});
});
});

View File

@ -91,7 +91,6 @@ describe('property bindings', () => {
it('should not map properties whose names do not correspond to their attribute names, ' +
'if they correspond to inputs',
() => {
@Component({template: '', selector: 'my-comp'})
class MyComp {
@Input() for !:string;
@ -393,7 +392,6 @@ describe('property bindings', () => {
});
describe('attributes and input properties', () => {
@Directive({selector: '[myDir]', exportAs: 'myDir'})
class MyDir {
@Input() role: string|undefined;
@ -596,7 +594,6 @@ describe('property bindings', () => {
expect(comp2.children[0].getAttribute('role')).toBe('button');
expect(comp2.textContent).toBe('role: button');
});
});
it('should not throw on synthetic property bindings when a directive on the same element injects ViewContainerRef',
@ -626,5 +623,4 @@ describe('property bindings', () => {
fixture.detectChanges();
}).not.toThrow();
});
});

View File

@ -8,7 +8,7 @@
import {Component} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
import {of } from 'rxjs';
import {of} from 'rxjs';
describe('property interpolation', () => {
it('should handle all flavors of interpolated properties', () => {
@ -67,7 +67,7 @@ describe('property interpolation', () => {
`
})
class App {
details = of ({
details = of({
title: 'cool image',
url: 'http://somecooldomain:1234/cool_image.png',
});
@ -93,7 +93,11 @@ describe('property interpolation', () => {
/** Clearly this is a doctor of heavy metals. */
leadSurgeon = {
getCommonInfo() {
return {getPhotoUrl() { return 'http://somecooldomain:1234/cool_image.png'; }};
return {
getPhotoUrl() {
return 'http://somecooldomain:1234/cool_image.png';
}
};
}
};
}

View File

@ -18,8 +18,7 @@ describe('components using pure function instructions internally', () => {
template: ``,
})
class MyComp {
@Input()
names: string[] = [];
@Input() names: string[] = [];
}
@ -60,7 +59,7 @@ describe('components using pure function instructions internally', () => {
myComp.names = ['should not be overwritten'];
fixture.detectChanges();
expect(myComp !.names).toEqual(['should not be overwritten']);
expect(myComp!.names).toEqual(['should not be overwritten']);
});
@ -91,11 +90,9 @@ describe('components using pure function instructions internally', () => {
template: ``,
})
class ManyPropComp {
@Input()
names1: string[] = [];
@Input() names1: string[] = [];
@Input()
names2: string[] = [];
@Input() names2: string[] = [];
}
@Component({
@ -117,14 +114,14 @@ describe('components using pure function instructions internally', () => {
fixture.detectChanges();
const manyPropComp = fixture.debugElement.query(By.directive(ManyPropComp)).componentInstance;
expect(manyPropComp !.names1).toEqual(['Nancy', 'Carson']);
expect(manyPropComp !.names2).toEqual(['George']);
expect(manyPropComp!.names1).toEqual(['Nancy', 'Carson']);
expect(manyPropComp!.names2).toEqual(['George']);
fixture.componentInstance.customName = 'George';
fixture.componentInstance.customName2 = 'Carson';
fixture.detectChanges();
expect(manyPropComp !.names1).toEqual(['Nancy', 'George']);
expect(manyPropComp !.names2).toEqual(['Carson']);
expect(manyPropComp!.names1).toEqual(['Nancy', 'George']);
expect(manyPropComp!.names2).toEqual(['Carson']);
});
@ -222,7 +219,6 @@ describe('components using pure function instructions internally', () => {
it('should work up to 8 bindings', () => {
@Component({
template: `
<my-comp [names]="['a', 'b', 'c', 'd', 'e', 'f', 'g', v8]"></my-comp>
@ -346,8 +342,7 @@ describe('components using pure function instructions internally', () => {
template: ``,
})
class ObjectComp {
@Input()
config: any = [];
@Input() config: any = [];
}
it('should support an object literal', () => {
@ -495,7 +490,7 @@ describe('components using pure function instructions internally', () => {
`
})
class App {
@ViewChildren(Dir) directives !: QueryList<Dir>;
@ViewChildren(Dir) directives!: QueryList<Dir>;
}
TestBed.configureTestingModule({declarations: [Dir, App]});
@ -514,7 +509,7 @@ describe('components using pure function instructions internally', () => {
`
})
class App {
@ViewChildren(Dir) directives !: QueryList<Dir>;
@ViewChildren(Dir) directives!: QueryList<Dir>;
}
TestBed.configureTestingModule({declarations: [Dir, App]});
@ -528,7 +523,7 @@ describe('components using pure function instructions internally', () => {
it('should not share object literals across component instances', () => {
@Component({template: `<div [dir]="{}"></div>`})
class App {
@ViewChild(Dir) directive !: Dir;
@ViewChild(Dir) directive!: Dir;
}
TestBed.configureTestingModule({declarations: [Dir, App]});
@ -545,7 +540,7 @@ describe('components using pure function instructions internally', () => {
it('should not share array literals across component instances', () => {
@Component({template: `<div [dir]="[]"></div>`})
class App {
@ViewChild(Dir) directive !: Dir;
@ViewChild(Dir) directive!: Dir;
}
TestBed.configureTestingModule({declarations: [Dir, App]});
@ -567,7 +562,7 @@ describe('components using pure function instructions internally', () => {
`
})
class App {
@ViewChildren(Dir) directives !: QueryList<Dir>;
@ViewChildren(Dir) directives!: QueryList<Dir>;
}
TestBed.configureTestingModule({declarations: [Dir, App]});
@ -586,7 +581,7 @@ describe('components using pure function instructions internally', () => {
`
})
class App {
@ViewChildren(Dir) directives !: QueryList<Dir>;
@ViewChildren(Dir) directives!: QueryList<Dir>;
}
TestBed.configureTestingModule({declarations: [Dir, App]});
@ -605,8 +600,10 @@ describe('components using pure function instructions internally', () => {
`
})
class App {
@ViewChildren(Dir) directives !: QueryList<Dir>;
getFoo() { return 'foo!'; }
@ViewChildren(Dir) directives!: QueryList<Dir>;
getFoo() {
return 'foo!';
}
}
TestBed.configureTestingModule({declarations: [Dir, App]});
@ -616,8 +613,5 @@ describe('components using pure function instructions internally', () => {
expect(values).toEqual([{foo: null}, {foo: 'foo!'}]);
});
});
});

View File

@ -7,7 +7,7 @@
*/
import {CommonModule} from '@angular/common';
import {AfterViewInit, Component, ContentChild, ContentChildren, Directive, ElementRef, Input, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef, ViewRef, forwardRef} from '@angular/core';
import {AfterViewInit, Component, ContentChild, ContentChildren, Directive, ElementRef, forwardRef, Input, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef, ViewRef} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
import {expect} from '@angular/platform-browser/testing/src/matchers';
@ -215,7 +215,7 @@ describe('query logic', () => {
}
class MyComp {
@ViewChildren(SomeDir) foo !: QueryList<SomeDir>;
@ViewChildren(SomeDir) foo!: QueryList<SomeDir>;
}
@Component({
@ -242,7 +242,7 @@ describe('query logic', () => {
}
class MySuperComp {
@ViewChildren(SomeDir) foo !: QueryList<SomeDir>;
@ViewChildren(SomeDir) foo!: QueryList<SomeDir>;
}
class MyComp extends MySuperComp {}
@ -275,7 +275,7 @@ describe('query logic', () => {
template: `<ng-container [ngTemplateOutlet]="content"></ng-container>`
})
class Insertion {
@Input() content !: TemplateRef<{}>;
@Input() content!: TemplateRef<{}>;
}
@Component({
@ -287,7 +287,7 @@ describe('query logic', () => {
`
})
class App {
@ViewChild(Required) requiredEl !: Required;
@ViewChild(Required) requiredEl!: Required;
viewChildAvailableInAfterViewInit?: boolean;
ngAfterViewInit() {
@ -300,7 +300,6 @@ describe('query logic', () => {
fixture.detectChanges();
expect(fixture.componentInstance.viewChildAvailableInAfterViewInit).toBe(true);
});
});
describe('content queries', () => {
@ -538,7 +537,7 @@ describe('query logic', () => {
@Component({template: '<sub-comp><div #foo></div></sub-comp>'})
class App {
@ViewChild(SubComp) subComp !: SubComp;
@ViewChild(SubComp) subComp!: SubComp;
}
TestBed.configureTestingModule({declarations: [App, SubComp]});
@ -561,7 +560,7 @@ describe('query logic', () => {
@Component({template: '<sub-comp><div #foo></div></sub-comp>'})
class App {
@ViewChild(SubComp) subComp !: SubComp;
@ViewChild(SubComp) subComp!: SubComp;
}
TestBed.configureTestingModule({declarations: [App, SubComp]});
@ -577,7 +576,7 @@ describe('query logic', () => {
}
class MyComp {
@ContentChildren(SomeDir) foo !: QueryList<SomeDir>;
@ContentChildren(SomeDir) foo!: QueryList<SomeDir>;
}
@Component({selector: 'sub-comp', template: '<ng-content></ng-content>'})
@ -593,7 +592,7 @@ describe('query logic', () => {
`
})
class App {
@ViewChild(SubComp) subComp !: SubComp;
@ViewChild(SubComp) subComp!: SubComp;
}
TestBed.configureTestingModule({declarations: [App, SubComp, SomeDir]});
@ -610,7 +609,7 @@ describe('query logic', () => {
}
class MySuperComp {
@ContentChildren(SomeDir) foo !: QueryList<SomeDir>;
@ContentChildren(SomeDir) foo!: QueryList<SomeDir>;
}
class MyComp extends MySuperComp {}
@ -628,7 +627,7 @@ describe('query logic', () => {
`
})
class App {
@ViewChild(SubComp) subComp !: SubComp;
@ViewChild(SubComp) subComp!: SubComp;
}
TestBed.configureTestingModule({declarations: [App, SubComp, SomeDir]});
@ -657,7 +656,7 @@ describe('query logic', () => {
template: '',
})
class ShallowComp {
@ContentChildren('foo', {descendants: false}) foos !: QueryList<ElementRef>;
@ContentChildren('foo', {descendants: false}) foos!: QueryList<ElementRef>;
}
TestBed.configureTestingModule(
@ -666,7 +665,7 @@ describe('query logic', () => {
fixture.detectChanges();
const shallowComp = fixture.debugElement.query(By.directive(ShallowComp)).componentInstance;
const queryList = shallowComp !.foos;
const queryList = shallowComp!.foos;
expect(queryList.length).toBe(0);
fixture.componentInstance.showing = true;
@ -691,24 +690,24 @@ describe('query logic', () => {
});
describe('descendants: false (default)', () => {
/**
* A helper function to check if a given object looks like ElementRef. It is used in place of
* the `instanceof ElementRef` check since ivy returns a type that looks like ElementRef (have
* the same properties but doesn't pass the instanceof ElementRef test)
*/
function isElementRefLike(result: any): boolean { return result.nativeElement != null; }
function isElementRefLike(result: any): boolean {
return result.nativeElement != null;
}
it('should match directives on elements that used to be wrapped by a required parent in HTML parser',
() => {
@Directive({selector: '[myDef]'})
class MyDef {
}
@Component({selector: 'my-container', template: ``})
class MyContainer {
@ContentChildren(MyDef) myDefs !: QueryList<MyDef>;
@ContentChildren(MyDef) myDefs!: QueryList<MyDef>;
}
@Component(
{selector: 'test-cmpt', template: `<my-container><tr myDef></tr></my-container>`})
@ -724,10 +723,9 @@ describe('query logic', () => {
});
it('should match elements with local refs inside <ng-container>', () => {
@Component({selector: 'needs-target', template: ``})
class NeedsTarget {
@ContentChildren('target') targets !: QueryList<ElementRef>;
@ContentChildren('target') targets!: QueryList<ElementRef>;
}
@Component({
selector: 'test-cmpt',
@ -752,10 +750,9 @@ describe('query logic', () => {
});
it('should match elements with local refs inside nested <ng-container>', () => {
@Component({selector: 'needs-target', template: ``})
class NeedsTarget {
@ContentChildren('target') targets !: QueryList<ElementRef>;
@ContentChildren('target') targets!: QueryList<ElementRef>;
}
@Component({
@ -791,7 +788,7 @@ describe('query logic', () => {
@Component({selector: 'needs-target', template: ``})
class NeedsTarget {
@ContentChildren(TargetDir) targets !: QueryList<HTMLElement>;
@ContentChildren(TargetDir) targets!: QueryList<HTMLElement>;
}
@Component({
@ -823,7 +820,7 @@ describe('query logic', () => {
@Component({selector: 'needs-target', template: ``})
class NeedsTarget {
@ContentChildren(TargetDir) targets !: QueryList<HTMLElement>;
@ContentChildren(TargetDir) targets!: QueryList<HTMLElement>;
}
@Component({
@ -859,7 +856,7 @@ describe('query logic', () => {
@Directive({selector: '[needs-target]'})
class NeedsTarget {
@ContentChildren(TargetDir) targets !: QueryList<HTMLElement>;
@ContentChildren(TargetDir) targets!: QueryList<HTMLElement>;
}
@Component({
@ -893,8 +890,8 @@ describe('query logic', () => {
@Component({selector: 'needs-target', template: ``})
class NeedsTarget {
@ContentChildren(TargetDir) dirTargets !: QueryList<TargetDir>;
@ContentChildren('target') localRefsTargets !: QueryList<ElementRef>;
@ContentChildren(TargetDir) dirTargets!: QueryList<TargetDir>;
@ContentChildren('target') localRefsTargets!: QueryList<ElementRef>;
}
@Component({
@ -931,7 +928,7 @@ describe('query logic', () => {
@Component({selector: 'needs-target', template: ``})
class NeedsTarget {
@ContentChildren(TargetDir) targets !: QueryList<HTMLElement>;
@ContentChildren(TargetDir) targets!: QueryList<HTMLElement>;
}
@Component({
@ -963,7 +960,6 @@ describe('query logic', () => {
describe('observable interface', () => {
it('should allow observing changes to query list', () => {
const fixture = TestBed.createComponent(QueryCompWithChanges);
let changes = 0;
@ -983,13 +979,10 @@ describe('query logic', () => {
fixture.detectChanges();
expect(changes).toBe(1);
});
});
describe('view boundaries', () => {
describe('ViewContainerRef', () => {
@Directive({selector: '[vc]', exportAs: 'vc'})
class ViewContainerManipulatorDirective {
constructor(private _vcRef: ViewContainerRef) {}
@ -998,9 +991,13 @@ describe('query logic', () => {
return this._vcRef.createEmbeddedView(tpl, ctx, idx);
}
remove(index?: number) { this._vcRef.remove(index); }
remove(index?: number) {
this._vcRef.remove(index);
}
move(viewRef: ViewRef, index: number) { this._vcRef.move(viewRef, index); }
move(viewRef: ViewRef, index: number) {
this._vcRef.move(viewRef, index);
}
}
it('should report results in views inserted / removed by ngIf', () => {
@ -1014,7 +1011,7 @@ describe('query logic', () => {
})
class TestComponent {
value: boolean = false;
@ViewChildren('foo') query !: QueryList<any>;
@ViewChildren('foo') query!: QueryList<any>;
}
TestBed.configureTestingModule({declarations: [TestComponent]});
@ -1045,7 +1042,7 @@ describe('query logic', () => {
})
class TestComponent {
value: string[]|undefined;
@ViewChildren('foo') query !: QueryList<any>;
@ViewChildren('foo') query!: QueryList<any>;
}
TestBed.configureTestingModule({declarations: [TestComponent]});
@ -1082,7 +1079,6 @@ describe('query logic', () => {
*/
it('should notify on changes when a given view is removed and re-inserted at the same index',
() => {
@Component({
selector: 'test-comp',
template: `
@ -1093,10 +1089,9 @@ describe('query logic', () => {
class TestComponent implements AfterViewInit {
queryListNotificationCounter = 0;
@ViewChild(ViewContainerManipulatorDirective)
vc !: ViewContainerManipulatorDirective;
@ViewChild('tpl') tpl !: TemplateRef<any>;
@ViewChildren('foo') query !: QueryList<any>;
@ViewChild(ViewContainerManipulatorDirective) vc!: ViewContainerManipulatorDirective;
@ViewChild('tpl') tpl!: TemplateRef<any>;
@ViewChildren('foo') query!: QueryList<any>;
ngAfterViewInit() {
this.query.changes.subscribe(() => this.queryListNotificationCounter++);
@ -1125,13 +1120,13 @@ describe('query logic', () => {
it('should support a mix of content queries from the declaration and embedded view', () => {
@Directive({selector: '[query-for-lots-of-content]'})
class QueryForLotsOfContent {
@ContentChildren('foo', {descendants: true}) foos1 !: QueryList<ElementRef>;
@ContentChildren('foo', {descendants: true}) foos2 !: QueryList<ElementRef>;
@ContentChildren('foo', {descendants: true}) foos1!: QueryList<ElementRef>;
@ContentChildren('foo', {descendants: true}) foos2!: QueryList<ElementRef>;
}
@Directive({selector: '[query-for-content]'})
class QueryForContent {
@ContentChildren('foo') foos !: QueryList<ElementRef>;
@ContentChildren('foo') foos!: QueryList<ElementRef>;
}
@Component({
@ -1192,11 +1187,10 @@ describe('query logic', () => {
`,
})
class TestComponent {
@ViewChild(ViewContainerManipulatorDirective)
vc !: ViewContainerManipulatorDirective;
@ViewChild('tpl1') tpl1 !: TemplateRef<any>;
@ViewChild('tpl2') tpl2 !: TemplateRef<any>;
@ViewChildren('foo') query !: QueryList<any>;
@ViewChild(ViewContainerManipulatorDirective) vc!: ViewContainerManipulatorDirective;
@ViewChild('tpl1') tpl1!: TemplateRef<any>;
@ViewChild('tpl2') tpl2!: TemplateRef<any>;
@ViewChildren('foo') query!: QueryList<any>;
}
TestBed.configureTestingModule(
@ -1210,8 +1204,8 @@ describe('query logic', () => {
expect(queryList.length).toBe(1);
expect(queryList.first.nativeElement.getAttribute('id')).toBe('middle');
vc.insertTpl(tpl1 !, {idx: 0}, 0);
vc.insertTpl(tpl2 !, {idx: 1}, 1);
vc.insertTpl(tpl1!, {idx: 0}, 0);
vc.insertTpl(tpl2!, {idx: 1}, 1);
fixture.detectChanges();
expect(queryList.length).toBe(3);
@ -1220,7 +1214,7 @@ describe('query logic', () => {
expect(qListArr[1].nativeElement.getAttribute('id')).toBe('middle');
expect(qListArr[2].nativeElement.getAttribute('id')).toBe('foo2_1');
vc.insertTpl(tpl1 !, {idx: 1}, 1);
vc.insertTpl(tpl1!, {idx: 1}, 1);
fixture.detectChanges();
expect(queryList.length).toBe(4);
@ -1264,10 +1258,10 @@ describe('query logic', () => {
`,
})
class TestComponent {
@ViewChild('tpl') tpl !: TemplateRef<any>;
@ViewChild('vi0') vi0 !: ViewContainerManipulatorDirective;
@ViewChild('vi1') vi1 !: ViewContainerManipulatorDirective;
@ViewChildren('foo') query !: QueryList<any>;
@ViewChild('tpl') tpl!: TemplateRef<any>;
@ViewChild('vi0') vi0!: ViewContainerManipulatorDirective;
@ViewChild('vi1') vi1!: ViewContainerManipulatorDirective;
@ViewChildren('foo') query!: QueryList<any>;
}
TestBed.configureTestingModule(
@ -1280,8 +1274,8 @@ describe('query logic', () => {
expect(queryList.length).toBe(0);
vi0.insertTpl(tpl !, {idx: 0, container_idx: 0}, 0);
vi1.insertTpl(tpl !, {idx: 0, container_idx: 1}, 0);
vi0.insertTpl(tpl!, {idx: 0, container_idx: 0}, 0);
vi1.insertTpl(tpl!, {idx: 0, container_idx: 1}, 0);
fixture.detectChanges();
expect(queryList.length).toBe(2);
@ -1315,7 +1309,7 @@ describe('query logic', () => {
})
class MyApp {
show = false;
@ViewChildren('foo') query !: QueryList<any>;
@ViewChildren('foo') query!: QueryList<any>;
}
TestBed.configureTestingModule({declarations: [MyApp], imports: [CommonModule]});
@ -1334,14 +1328,11 @@ describe('query logic', () => {
fixture.detectChanges();
expect(queryList.length).toBe(0);
});
});
});
describe('non-regression', () => {
it('should query by provider super-type in an embedded view', () => {
@Directive({selector: '[child]'})
class Child {
}
@ -1356,7 +1347,7 @@ describe('query logic', () => {
`<ng-template [ngIf]="true"><ng-template [ngIf]="true"><div parent></div></ng-template></ng-template>`
})
class TestCmpt {
@ViewChildren(Child) instances !: QueryList<Child>;
@ViewChildren(Child) instances!: QueryList<Child>;
}
TestBed.configureTestingModule({declarations: [TestCmpt, Parent, Child]});
@ -1367,7 +1358,6 @@ describe('query logic', () => {
});
it('should flatten multi-provider results', () => {
class MyClass {}
@Component({
@ -1381,7 +1371,7 @@ describe('query logic', () => {
@Component({selector: 'test-cmpt', template: `<with-multi-provider></with-multi-provider>`})
class TestCmpt {
@ViewChildren(MyClass) queryResults !: QueryList<WithMultiProvider>;
@ViewChildren(MyClass) queryResults!: QueryList<WithMultiProvider>;
}
TestBed.configureTestingModule({declarations: [TestCmpt, WithMultiProvider]});
@ -1393,7 +1383,6 @@ describe('query logic', () => {
});
it('should flatten multi-provider results when crossing ng-template', () => {
class MyClass {}
@Component({
@ -1413,7 +1402,7 @@ describe('query logic', () => {
`
})
class TestCmpt {
@ViewChildren(MyClass) queryResults !: QueryList<WithMultiProvider>;
@ViewChildren(MyClass) queryResults!: QueryList<WithMultiProvider>;
}
TestBed.configureTestingModule({declarations: [TestCmpt, WithMultiProvider]});
@ -1444,7 +1433,7 @@ describe('query logic', () => {
`
})
class App {
@ViewChild(GroupDir) group !: GroupDir;
@ViewChild(GroupDir) group!: GroupDir;
}
TestBed.configureTestingModule(
@ -1481,7 +1470,7 @@ describe('query logic', () => {
`
})
class App {
@ViewChildren(GroupDir) groups !: QueryList<GroupDir>;
@ViewChildren(GroupDir) groups!: QueryList<GroupDir>;
}
TestBed.configureTestingModule(
@ -1497,9 +1486,7 @@ describe('query logic', () => {
expect(groups[1]).toBeAnInstanceOf(GroupDir);
expect(groups[2]).toBeUndefined();
});
});
});
function initWithTemplate(compType: Type<any>, template: string) {
@ -1511,11 +1498,11 @@ function initWithTemplate(compType: Type<any>, template: string) {
@Component({selector: 'local-ref-query-component', template: '<ng-content></ng-content>'})
class QueryComp {
@ViewChild('viewQuery') viewChild !: any;
@ContentChild('contentQuery') contentChild !: any;
@ViewChild('viewQuery') viewChild!: any;
@ContentChild('contentQuery') contentChild!: any;
@ViewChildren('viewQuery') viewChildren !: QueryList<any>;
@ContentChildren('contentQuery') contentChildren !: QueryList<any>;
@ViewChildren('viewQuery') viewChildren!: QueryList<any>;
@ContentChildren('contentQuery') contentChildren!: QueryList<any>;
}
@Component({selector: 'app-comp', template: ``})
@ -1543,12 +1530,14 @@ class TextDirective {
`
})
class StaticViewQueryComp {
private _textDir !: TextDirective;
private _foo !: ElementRef;
private _textDir!: TextDirective;
private _foo!: ElementRef;
setEvents: string[] = [];
@ViewChild(TextDirective, {static: true})
get textDir(): TextDirective { return this._textDir; }
get textDir(): TextDirective {
return this._textDir;
}
set textDir(value: TextDirective) {
this.setEvents.push('textDir set');
@ -1556,7 +1545,9 @@ class StaticViewQueryComp {
}
@ViewChild('foo')
get foo(): ElementRef { return this._foo; }
get foo(): ElementRef {
return this._foo;
}
set foo(value: ElementRef) {
this.setEvents.push('foo set');
@ -1577,22 +1568,22 @@ class StaticViewQueryComp {
`
})
class SubclassStaticViewQueryComp extends StaticViewQueryComp {
@ViewChild('bar', {static: true})
bar !: ElementRef;
@ViewChild('bar', {static: true}) bar!: ElementRef;
@ViewChild('baz')
baz !: ElementRef;
@ViewChild('baz') baz!: ElementRef;
}
@Component({selector: 'static-content-query-comp', template: `<ng-content></ng-content>`})
class StaticContentQueryComp {
private _textDir !: TextDirective;
private _foo !: ElementRef;
private _textDir!: TextDirective;
private _foo!: ElementRef;
setEvents: string[] = [];
@ContentChild(TextDirective, {static: true})
get textDir(): TextDirective { return this._textDir; }
get textDir(): TextDirective {
return this._textDir;
}
set textDir(value: TextDirective) {
this.setEvents.push('textDir set');
@ -1600,7 +1591,9 @@ class StaticContentQueryComp {
}
@ContentChild('foo')
get foo(): ElementRef { return this._foo; }
get foo(): ElementRef {
return this._foo;
}
set foo(value: ElementRef) {
this.setEvents.push('foo set');
@ -1610,12 +1603,14 @@ class StaticContentQueryComp {
@Directive({selector: '[staticContentQueryDir]'})
class StaticContentQueryDir {
private _textDir !: TextDirective;
private _foo !: ElementRef;
private _textDir!: TextDirective;
private _foo!: ElementRef;
setEvents: string[] = [];
@ContentChild(TextDirective, {static: true})
get textDir(): TextDirective { return this._textDir; }
get textDir(): TextDirective {
return this._textDir;
}
set textDir(value: TextDirective) {
this.setEvents.push('textDir set');
@ -1623,7 +1618,9 @@ class StaticContentQueryDir {
}
@ContentChild('foo')
get foo(): ElementRef { return this._foo; }
get foo(): ElementRef {
return this._foo;
}
set foo(value: ElementRef) {
this.setEvents.push('foo set');
@ -1633,11 +1630,9 @@ class StaticContentQueryDir {
@Component({selector: 'subclass-static-content-query-comp', template: `<ng-content></ng-content>`})
class SubclassStaticContentQueryComp extends StaticContentQueryComp {
@ContentChild('bar', {static: true})
bar !: ElementRef;
@ContentChild('bar', {static: true}) bar!: ElementRef;
@ContentChild('baz')
baz !: ElementRef;
@ContentChild('baz') baz!: ElementRef;
}
@Component({
@ -1647,7 +1642,7 @@ class SubclassStaticContentQueryComp extends StaticContentQueryComp {
`
})
export class QueryCompWithChanges {
@ViewChildren('foo') foos !: QueryList<any>;
@ViewChildren('foo') foos!: QueryList<any>;
showing = false;
}
@ -1658,7 +1653,7 @@ class SuperDirectiveQueryTarget {
@Directive({selector: '[super-directive]'})
class SuperDirective {
@ViewChildren(SuperDirectiveQueryTarget) headers !: QueryList<SuperDirectiveQueryTarget>;
@ViewChildren(SuperDirectiveQueryTarget) headers!: QueryList<SuperDirectiveQueryTarget>;
}
@Component({

View File

@ -24,19 +24,29 @@ describe('renderer factory lifecycle', () => {
@Component({selector: 'some-component', template: `foo`})
class SomeComponent implements DoCheck {
ngOnInit() { logs.push('some_component create'); }
ngDoCheck() { logs.push('some_component update'); }
ngOnInit() {
logs.push('some_component create');
}
ngDoCheck() {
logs.push('some_component update');
}
}
@Component({selector: 'some-component-with-error', template: `With error`})
class SomeComponentWhichThrows {
ngOnInit() { throw new Error('SomeComponentWhichThrows threw'); }
ngOnInit() {
throw new Error('SomeComponentWhichThrows threw');
}
}
@Component({selector: 'lol', template: `<some-component></some-component>`})
class TestComponent implements DoCheck {
ngOnInit() { logs.push('test_component create'); }
ngDoCheck() { logs.push('test_component update'); }
ngOnInit() {
logs.push('test_component create');
}
ngDoCheck() {
logs.push('test_component update');
}
}
/** Creates a patched renderer factory that pushes entries to the test log */
@ -44,7 +54,7 @@ describe('renderer factory lifecycle', () => {
let rendererFactory = getRendererFactory2(document);
const createRender = rendererFactory.createRenderer;
rendererFactory.createRenderer = (hostElement: any, type: RendererType2 | null) => {
rendererFactory.createRenderer = (hostElement: any, type: RendererType2|null) => {
logs.push('create');
return createRender.apply(rendererFactory, [hostElement, type]);
};
@ -168,7 +178,7 @@ describe('animation renderer factory', () => {
]);
player.finish();
rendererFactory !.whenRenderingDone !().then(() => {
rendererFactory!.whenRenderingDone!().then(() => {
expect(eventLogs).toEqual(['void - start', 'void - done', 'on - start', 'on - done']);
done();
});

View File

@ -440,11 +440,12 @@ describe('styling', () => {
@Directive({selector: '[dir-shadows-class-input]'})
class DirectiveShadowsClassInput {
@Input('class')
klass: string|undefined;
@Input('class') klass: string|undefined;
@HostBinding('class')
get hostClasses() { return `${this.klass} SUFFIX`; }
get hostClasses() {
return `${this.klass} SUFFIX`;
}
}
TestBed.configureTestingModule({declarations: [Cmp, DirectiveShadowsClassInput]});
@ -671,7 +672,7 @@ describe('styling', () => {
it('should be able to bind zero', () => {
@Component({template: '<div #div [style.opacity]="opacity"></div>'})
class App {
@ViewChild('div') div !: ElementRef<HTMLElement>;
@ViewChild('div') div!: ElementRef<HTMLElement>;
opacity = 0;
}
@ -685,7 +686,7 @@ describe('styling', () => {
it('should be able to bind a SafeValue to backgroundImage', () => {
@Component({template: '<div [style.backgroundImage]="image"></div>'})
class Cmp {
image !: SafeStyle;
image!: SafeStyle;
}
TestBed.configureTestingModule({declarations: [Cmp]});
@ -724,7 +725,7 @@ describe('styling', () => {
it('should be able to bind a SafeValue to clip-path', () => {
@Component({template: '<div [style.clip-path]="path"></div>'})
class Cmp {
path !: SafeStyle;
path!: SafeStyle;
}
TestBed.configureTestingModule({declarations: [Cmp]});
@ -1011,15 +1012,15 @@ describe('styling', () => {
expect(capturedClassBindingCount).toEqual(1);
expect(capturedClassBindingValue as any).toEqual(null);
expect(capturedMyClassBindingCount).toEqual(1);
expect(capturedMyClassBindingValue !).toEqual('foo');
expect(capturedMyClassBindingValue!).toEqual('foo');
fixture.componentInstance.c = 'dynamic-value';
fixture.detectChanges();
expect(capturedClassBindingCount).toEqual(2);
expect(capturedClassBindingValue !).toEqual('dynamic-value');
expect(capturedClassBindingValue!).toEqual('dynamic-value');
expect(capturedMyClassBindingCount).toEqual(1);
expect(capturedMyClassBindingValue !).toEqual('foo');
expect(capturedMyClassBindingValue!).toEqual('foo');
fixture.componentInstance.c = null;
fixture.detectChanges();
@ -1027,7 +1028,7 @@ describe('styling', () => {
expect(capturedClassBindingCount).toEqual(3);
expect(capturedClassBindingValue as any).toEqual(null);
expect(capturedMyClassBindingCount).toEqual(1);
expect(capturedMyClassBindingValue !).toEqual('foo');
expect(capturedMyClassBindingValue!).toEqual('foo');
fixture.componentInstance.c = '';
fixture.detectChanges();
@ -1035,7 +1036,7 @@ describe('styling', () => {
expect(capturedClassBindingCount).toEqual(4);
expect(capturedClassBindingValue as any).toEqual('');
expect(capturedMyClassBindingCount).toEqual(1);
expect(capturedMyClassBindingValue !).toEqual('foo');
expect(capturedMyClassBindingValue!).toEqual('foo');
});
it('should write to [class] binding during `update` mode if there is an instantiation-level value',
@ -1069,7 +1070,7 @@ describe('styling', () => {
fixture.detectChanges();
expect(capturedClassBindingCount).toEqual(2);
expect(capturedClassBindingValue !).toEqual('dynamic-bar');
expect(capturedClassBindingValue!).toEqual('dynamic-bar');
});
it('should write to a `class` input binding if there is a static class value', () => {
@ -1102,9 +1103,9 @@ describe('styling', () => {
const fixture = TestBed.createComponent(Cmp);
fixture.detectChanges();
expect(capturedClassBindingValue !).toEqual('static-val');
expect(capturedClassBindingValue!).toEqual('static-val');
expect(capturedClassBindingCount).toEqual(1);
expect(capturedMyClassBindingValue !).toEqual('foo');
expect(capturedMyClassBindingValue!).toEqual('foo');
expect(capturedMyClassBindingCount).toEqual(1);
});
@ -1195,19 +1196,19 @@ describe('styling', () => {
// would check if we possibly have this situation on every `advance()`
// instruction. We don't think this is worth it, and we are just going to live
// with this.
);
expect(capturedClassBindingValue !).toEqual('static-val');
);
expect(capturedClassBindingValue!).toEqual('static-val');
expect(capturedMyClassBindingCount).toEqual(1);
expect(capturedMyClassBindingValue !).toEqual('foo');
expect(capturedMyClassBindingValue!).toEqual('foo');
capturedClassBindingCount = 0;
fixture.componentInstance.c = 'dynamic-val';
fixture.detectChanges();
expect(capturedClassBindingCount).toEqual(1);
expect(capturedClassBindingValue !).toEqual('static-val dynamic-val');
expect(capturedClassBindingValue!).toEqual('static-val dynamic-val');
expect(capturedMyClassBindingCount).toEqual(1);
expect(capturedMyClassBindingValue !).toEqual('foo');
expect(capturedMyClassBindingValue!).toEqual('foo');
});
onlyInIvy('only ivy balances styling across directives and component host bindings')
@ -1240,7 +1241,6 @@ describe('styling', () => {
});
describe('NgClass', () => {
// We had a bug where NgClass would not allocate sufficient slots for host bindings,
// so it would overwrite information about other directives nearby. This test checks
// that TestDir's injector is not overwritten by NgClass, so TestDir should still
@ -1297,7 +1297,7 @@ describe('styling', () => {
template: '<span dir [classesInSchool]="classes" [styleOfClothing]="style"></span>',
})
class App {
@ViewChild(Dir) dir !: Dir;
@ViewChild(Dir) dir!: Dir;
classes = 'math';
style = '80s';
@ -1315,8 +1315,7 @@ describe('styling', () => {
it('should be able to bind to `className`', () => {
@Component({template: ''})
class App {
@HostBinding('className')
klass = 'one two';
@HostBinding('className') klass = 'one two';
}
TestBed.configureTestingModule({declarations: [App]});
@ -1471,8 +1470,7 @@ describe('styling', () => {
opacity: string|null = '0.5';
@ViewChild(CompWithStyling, {static: true})
compWithStyling: CompWithStyling|null = null;
@ViewChild(DirWithStyling, {static: true})
dirWithStyling: DirWithStyling|null = null;
@ViewChild(DirWithStyling, {static: true}) dirWithStyling: DirWithStyling|null = null;
}
TestBed.configureTestingModule({declarations: [Cmp, DirWithStyling, CompWithStyling]});
@ -1488,13 +1486,13 @@ describe('styling', () => {
expect(element.style.fontSize).toEqual('100px');
// once for the template flush and again for the host bindings
expect(ngDevMode !.rendererSetStyle).toEqual(4);
expect(ngDevMode!.rendererSetStyle).toEqual(4);
ngDevModeResetPerfCounters();
component.opacity = '0.6';
component.compWithStyling !.height = '100px';
component.compWithStyling !.width = '100px';
component.dirWithStyling !.fontSize = '50px';
component.compWithStyling!.height = '100px';
component.compWithStyling!.width = '100px';
component.dirWithStyling!.fontSize = '50px';
fixture.detectChanges();
expect(element.style.opacity).toEqual('0.6');
@ -1503,7 +1501,7 @@ describe('styling', () => {
expect(element.style.fontSize).toEqual('50px');
// once for the template flush and again for the host bindings
expect(ngDevMode !.rendererSetStyle).toEqual(4);
expect(ngDevMode!.rendererSetStyle).toEqual(4);
});
onlyInIvy('ivy resolves styling across directives, components and templates in unison')
@ -1634,8 +1632,12 @@ describe('styling', () => {
public c: {[key: string]: any}|null = null;
updateClasses(classes: string) {
const c = this.c || (this.c = {});
Object.keys(this.c).forEach(className => { c[className] = false; });
classes.split(/\s+/).forEach(className => { c[className] = true; });
Object.keys(this.c).forEach(className => {
c[className] = false;
});
classes.split(/\s+/).forEach(className => {
c[className] = true;
});
}
public s: {[key: string]: any}|null = null;
@ -1694,8 +1696,7 @@ describe('styling', () => {
map: any = {width: '111px', opacity: '0.5'};
width: string|null|undefined = '555px';
@ViewChild('dir', {read: DirThatSetsStyling, static: true})
dir !: DirThatSetsStyling;
@ViewChild('dir', {read: DirThatSetsStyling, static: true}) dir!: DirThatSetsStyling;
}
TestBed.configureTestingModule({declarations: [Cmp, DirThatSetsStyling]});
@ -1763,8 +1764,7 @@ describe('styling', () => {
map: any = {width: '555px', height: '555px'};
@ViewChild('dir', {read: DirThatSetsStyling, static: true})
dir !: DirThatSetsStyling;
@ViewChild('dir', {read: DirThatSetsStyling, static: true}) dir!: DirThatSetsStyling;
}
TestBed.configureTestingModule({declarations: [Cmp, DirThatSetsStyling]});
@ -1991,7 +1991,7 @@ describe('styling', () => {
it('should be able to bind a SafeValue to clip-path', () => {
@Component({template: '<div [style.clip-path]="path"></div>'})
class Cmp {
path !: SafeStyle;
path!: SafeStyle;
}
TestBed.configureTestingModule({declarations: [Cmp]});
@ -2028,12 +2028,22 @@ describe('styling', () => {
public background: string = '1.png';
public color: string = 'red';
private getSafeStyle(value: string) { return this.sanitizer.bypassSecurityTrustStyle(value); }
private getSafeStyle(value: string) {
return this.sanitizer.bypassSecurityTrustStyle(value);
}
getBackgroundSafe() { return this.getSafeStyle(`url("/${this.background}")`); }
getWidthSafe() { return this.getSafeStyle(this.width); }
getHeightSafe() { return this.getSafeStyle(this.height); }
getColorUnsafe() { return this.color; }
getBackgroundSafe() {
return this.getSafeStyle(`url("/${this.background}")`);
}
getWidthSafe() {
return this.getSafeStyle(this.width);
}
getHeightSafe() {
return this.getSafeStyle(this.height);
}
getColorUnsafe() {
return this.color;
}
}
TestBed.configureTestingModule({
@ -2089,11 +2099,9 @@ describe('styling', () => {
class Cmp {
map: any = null;
@ViewChild('div', {read: DirWithStyleMap, static: true})
dir1 !: DirWithStyleMap;
@ViewChild('div', {read: DirWithStyleMap, static: true}) dir1!: DirWithStyleMap;
@ViewChild('div', {read: DirWithStyleMapPart2, static: true})
dir2 !: DirWithStyleMapPart2;
@ViewChild('div', {read: DirWithStyleMapPart2, static: true}) dir2!: DirWithStyleMapPart2;
}
TestBed.configureTestingModule(
@ -2352,8 +2360,8 @@ describe('styling', () => {
@Component({template: '<div #target one two></div>'})
class Cmp {
@ViewChild('target', {read: DirOne, static: true}) one !: DirOne;
@ViewChild('target', {read: DirTwo, static: true}) two !: DirTwo;
@ViewChild('target', {read: DirOne, static: true}) one!: DirOne;
@ViewChild('target', {read: DirTwo, static: true}) two!: DirTwo;
}
TestBed.configureTestingModule({declarations: [Cmp, DirOne, DirTwo]});
@ -2443,7 +2451,9 @@ describe('styling', () => {
class Cmp {
s: any = {opacity: '1'};
clearStyle(): void { this.s = null; }
clearStyle(): void {
this.s = null;
}
}
TestBed.configureTestingModule({declarations: [Cmp]});
@ -2471,8 +2481,7 @@ describe('styling', () => {
class ChildCmp {
readyTpl = false;
@HostBinding('class.ready-host')
readyHost = false;
@HostBinding('class.ready-host') readyHost = false;
}
@Component({
@ -2490,10 +2499,9 @@ describe('styling', () => {
class ParentCmp {
private _prop = '';
@ViewChild('template', {read: ViewContainerRef})
vcr: ViewContainerRef = null !;
@ViewChild('template', {read: ViewContainerRef}) vcr: ViewContainerRef = null!;
private child: ComponentRef<ChildCmp> = null !;
private child: ComponentRef<ChildCmp> = null!;
@Input()
set prop(value: string) {
@ -2505,7 +2513,9 @@ describe('styling', () => {
}
}
get prop() { return this._prop; }
get prop() {
return this._prop;
}
ngAfterViewInit() {
const factory = this.componentFactoryResolver.resolveComponentFactory(ChildCmp);
@ -2559,8 +2569,7 @@ describe('styling', () => {
class ChildCmp {
readyTpl = false;
@HostBinding('class.ready-host')
readyHost = false;
@HostBinding('class.ready-host') readyHost = false;
}
@Component({
@ -2575,10 +2584,9 @@ describe('styling', () => {
class ParentCmp {
updateChild = false;
@ViewChild('template', {read: ViewContainerRef})
vcr: ViewContainerRef = null !;
@ViewChild('template', {read: ViewContainerRef}) vcr: ViewContainerRef = null!;
private child: ComponentRef<ChildCmp> = null !;
private child: ComponentRef<ChildCmp> = null!;
ngDoCheck() {
if (this.updateChild) {
@ -2619,7 +2627,7 @@ describe('styling', () => {
expect(readyHost).toBeFalsy();
expect(readyChild).toBeFalsy();
const parent = fixture.componentInstance.parent !;
const parent = fixture.componentInstance.parent!;
parent.updateChild = true;
fixture.detectChanges(false);
@ -2675,7 +2683,9 @@ describe('styling', () => {
selector: '[dir]',
})
class Dir {
constructor(public elementRef: ElementRef, public renderer: Renderer2) { dirInstance = this; }
constructor(public elementRef: ElementRef, public renderer: Renderer2) {
dirInstance = this;
}
setStyles() {
const nativeEl = this.elementRef.nativeElement;
@ -2730,11 +2740,9 @@ describe('styling', () => {
() => {
@Directive({selector: '[blockStyles]'})
class StylesDirective {
@HostBinding('style.border')
border = '1px solid red';
@HostBinding('style.border') border = '1px solid red';
@HostBinding('style.background')
background = 'white';
@HostBinding('style.background') background = 'white';
}
@Component({
@ -2863,7 +2871,7 @@ describe('styling', () => {
TestBed.configureTestingModule({declarations: [MyComp]}).createComponent(MyComp);
fixture.detectChanges();
const div = fixture.nativeElement.querySelector('div') !;
const div = fixture.nativeElement.querySelector('div')!;
div.className += ' abc';
expect(splitSortJoin(div.className)).toEqual('abc');
@ -2877,7 +2885,9 @@ describe('styling', () => {
expect(splitSortJoin(div.className)).toEqual('4 5 6 7 abc');
function splitSortJoin(s: string) { return s.split(/\s+/).sort().join(' ').trim(); }
function splitSortJoin(s: string) {
return s.split(/\s+/).sort().join(' ').trim();
}
});
describe('ExpressionChangedAfterItHasBeenCheckedError', () => {
@ -2885,7 +2895,9 @@ describe('styling', () => {
@Component({template: `<div [style.background-image]="iconSafe"></div>`})
class MyComp {
icon = 'https://i.imgur.com/4AiXzf8.jpg';
get iconSafe() { return this.sanitizer.bypassSecurityTrustStyle(`url("${this.icon}")`); }
get iconSafe() {
return this.sanitizer.bypassSecurityTrustStyle(`url("${this.icon}")`);
}
constructor(private sanitizer: DomSanitizer) {}
}
@ -2951,7 +2963,7 @@ describe('styling', () => {
TestBed.configureTestingModule({declarations: [MyComp, DirThatSetsStyling]})
.createComponent(MyComp);
fixture.detectChanges();
const div = fixture.nativeElement.querySelector('div') !;
const div = fixture.nativeElement.querySelector('div')!;
expect(div.style.getPropertyValue('width')).toEqual('100px');
expect(div.style.getPropertyValue('height')).toEqual('200px');
expect(div.style.getPropertyValue('font-size')).toEqual('300px');
@ -3051,20 +3063,16 @@ describe('styling', () => {
() => {
@Component({selector: 'my-comp-with-styling', template: ''})
class MyCompWithStyling {
@HostBinding('style')
myStyles: any = {width: '300px'};
@HostBinding('style') myStyles: any = {width: '300px'};
@HostBinding('style.height')
myHeight: any = '305px';
@HostBinding('style.height') myHeight: any = '305px';
}
@Directive({selector: '[my-dir-with-styling]'})
class MyDirWithStyling {
@HostBinding('style')
myStyles: any = {width: '200px'};
@HostBinding('style') myStyles: any = {width: '200px'};
@HostBinding('style.height')
myHeight: any = '205px';
@HostBinding('style.height') myHeight: any = '205px';
}
@Component({
@ -3081,15 +3089,15 @@ describe('styling', () => {
myStyles: {width?: string} = {width: '100px'};
myHeight: string|null|undefined = '100px';
@ViewChild(MyDirWithStyling) dir !: MyDirWithStyling;
@ViewChild(MyCompWithStyling) comp !: MyCompWithStyling;
@ViewChild(MyDirWithStyling) dir!: MyDirWithStyling;
@ViewChild(MyCompWithStyling) comp!: MyCompWithStyling;
}
TestBed.configureTestingModule(
{declarations: [MyComp, MyCompWithStyling, MyDirWithStyling]});
const fixture = TestBed.createComponent(MyComp);
const comp = fixture.componentInstance;
const elm = fixture.nativeElement.querySelector('my-comp-with-styling') !;
const elm = fixture.nativeElement.querySelector('my-comp-with-styling')!;
fixture.detectChanges();
expect(elm.style.width).toEqual('100px');
@ -3131,7 +3139,7 @@ describe('styling', () => {
TestBed.configureTestingModule(
{declarations: [MyComp, MyCompWithStyling, MyDirWithStyling]});
const fixture = TestBed.createComponent(MyComp);
const elm = fixture.nativeElement.querySelector('my-comp-with-styling') !;
const elm = fixture.nativeElement.querySelector('my-comp-with-styling')!;
fixture.detectChanges();
expect(elm.style.color).toEqual('red');
@ -3139,7 +3147,6 @@ describe('styling', () => {
it('should combine host class.foo bindings from multiple directives', () => {
@Directive({
selector: '[dir-that-sets-one-two]',
exportAs: 'one',
@ -3197,8 +3204,8 @@ describe('styling', () => {
expect(div2.className).toBe('');
const comp = fixture.componentInstance;
comp.dirOneA !.one = comp.dirOneB !.one = true;
comp.dirOneA !.two = comp.dirOneB !.two = true;
comp.dirOneA!.one = comp.dirOneB!.one = true;
comp.dirOneA!.two = comp.dirOneB!.two = true;
fixture.detectChanges();
expect(div1.classList.contains('one')).toBeTruthy();
@ -3211,8 +3218,8 @@ describe('styling', () => {
expect(div2.classList.contains('four')).toBeFalsy();
expect(div2.classList.contains('zero')).toBeFalsy();
comp.dirTwoA !.three = comp.dirTwoB !.three = true;
comp.dirTwoA !.four = comp.dirTwoB !.four = true;
comp.dirTwoA!.three = comp.dirTwoB!.three = true;
comp.dirTwoA!.four = comp.dirTwoB!.four = true;
fixture.detectChanges();
expect(div1.classList.contains('one')).toBeTruthy();
@ -3242,7 +3249,9 @@ describe('styling', () => {
it('should combine static host classes with component "class" host attribute', () => {
@Component({selector: 'comp-with-classes', template: '', host: {'class': 'host'}})
class CompWithClasses {
constructor(ref: ElementRef) { ref.nativeElement.classList.add('custom'); }
constructor(ref: ElementRef) {
ref.nativeElement.classList.add('custom');
}
}
@Component({
@ -3282,8 +3291,7 @@ describe('styling', () => {
@Directive({selector: '[single-host-style-dir]'})
class SingleHostStyleDir {
@HostBinding('style.width')
width = '100px';
@HostBinding('style.width') width = '100px';
}
TestBed.configureTestingModule({declarations: [Cmp, SingleHostStyleDir]});
@ -3340,7 +3348,9 @@ describe('styling', () => {
@Directive({selector: '[test]'})
class MyDir {
@Input('class')
set className(value: string) { logs.push(value); }
set className(value: string) {
logs.push(value);
}
}
@Component({
@ -3456,8 +3466,7 @@ describe('styling', () => {
template: `className = {{className}}`,
})
class MyCmp {
@Input()
className: string = 'unbound';
@Input() className: string = 'unbound';
}
@Component({template: `<my-cmp [class]="'bound'"></my-cmp>`})
class MyApp {
@ -3475,8 +3484,7 @@ describe('styling', () => {
template: `className = {{className}}`,
})
class MyCmp {
@Input()
className: string = 'unbound';
@Input() className: string = 'unbound';
}
@Component({template: `<my-cmp class="bound"></my-cmp>`})
class MyApp {
@ -3491,8 +3499,8 @@ describe('styling', () => {
});
function assertStyleCounters(countForSet: number, countForRemove: number) {
expect(ngDevMode !.rendererSetStyle).toEqual(countForSet);
expect(ngDevMode !.rendererRemoveStyle).toEqual(countForRemove);
expect(ngDevMode!.rendererSetStyle).toEqual(countForSet);
expect(ngDevMode!.rendererRemoveStyle).toEqual(countForRemove);
}
function assertStyle(element: HTMLElement, prop: string, value: any) {

View File

@ -13,11 +13,9 @@ import {ivyEnabled, onlyInIvy} from '@angular/private/testing';
describe('TemplateRef', () => {
describe('rootNodes', () => {
@Component({template: `<ng-template #templateRef></ng-template>`})
class App {
@ViewChild('templateRef', {static: true})
templateRef !: TemplateRef<any>;
@ViewChild('templateRef', {static: true}) templateRef!: TemplateRef<any>;
minutes = 0;
}
@ -80,7 +78,7 @@ describe('TemplateRef', () => {
`
})
class App {
@ViewChild(MenuContent) content !: MenuContent;
@ViewChild(MenuContent) content!: MenuContent;
constructor(public viewContainerRef: ViewContainerRef) {}
}

View File

@ -7,7 +7,7 @@
*/
import {Component} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {of } from 'rxjs';
import {of} from 'rxjs';
describe('text instructions', () => {
it('should handle all flavors of interpolated text', () => {
@ -66,8 +66,8 @@ describe('text instructions', () => {
`
})
class App {
who = of ('Sally');
item = of ({
who = of('Sally');
item = of({
what: 'seashells',
where: 'seashore',
});

View File

@ -8,7 +8,7 @@
import {CommonModule, DOCUMENT} from '@angular/common';
import {computeMsgId} from '@angular/compiler';
import {Compiler, Component, ComponentFactoryResolver, Directive, DoCheck, ElementRef, EmbeddedViewRef, ErrorHandler, Injector, NO_ERRORS_SCHEMA, NgModule, OnInit, Pipe, PipeTransform, QueryList, RendererFactory2, RendererType2, Sanitizer, TemplateRef, ViewChild, ViewChildren, ViewContainerRef, ɵsetDocument} from '@angular/core';
import {Compiler, Component, ComponentFactoryResolver, Directive, DoCheck, ElementRef, EmbeddedViewRef, ErrorHandler, Injector, NgModule, NO_ERRORS_SCHEMA, OnInit, Pipe, PipeTransform, QueryList, RendererFactory2, RendererType2, Sanitizer, TemplateRef, ViewChild, ViewChildren, ViewContainerRef, ɵsetDocument} from '@angular/core';
import {Input} from '@angular/core/src/metadata';
import {ngDevModeResetPerfCounters} from '@angular/core/src/util/ng_dev_mode';
import {TestBed, TestComponentRenderer} from '@angular/core/testing';
@ -18,7 +18,6 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
import {ivyEnabled, onlyInIvy} from '@angular/private/testing';
describe('ViewContainerRef', () => {
/**
* Gets the inner HTML of the given element with all HTML comments and Angular internal
* reflect attributes omitted. This makes HTML comparisons easier and less verbose.
@ -40,7 +39,6 @@ describe('ViewContainerRef', () => {
afterEach(() => clearTranslations());
describe('create', () => {
it('should support view queries inside embedded views created in dir constructors', () => {
const fixture = TestBed.createComponent(ConstructorApp);
fixture.detectChanges();
@ -87,7 +85,7 @@ describe('ViewContainerRef', () => {
`
})
class TestComp {
@ViewChild(VCRefDirective, {static: true}) vcRefDir !: VCRefDirective;
@ViewChild(VCRefDirective, {static: true}) vcRefDir!: VCRefDirective;
}
TestBed.configureTestingModule(
@ -141,7 +139,7 @@ describe('ViewContainerRef', () => {
`
})
class TestComp {
@ViewChild('container', {read: ViewContainerRef}) vcRef !: ViewContainerRef;
@ViewChild('container', {read: ViewContainerRef}) vcRef!: ViewContainerRef;
constructor(public cfr: ComponentFactoryResolver) {}
@ -196,8 +194,8 @@ describe('ViewContainerRef', () => {
`
})
class TestComp {
@ViewChild('svg', {read: ViewContainerRef}) svgVCRef !: ViewContainerRef;
@ViewChild('mathml', {read: ViewContainerRef}) mathMLVCRef !: ViewContainerRef;
@ViewChild('svg', {read: ViewContainerRef}) svgVCRef!: ViewContainerRef;
@ViewChild('mathml', {read: ViewContainerRef}) mathMLVCRef!: ViewContainerRef;
constructor(public cfr: ComponentFactoryResolver) {}
@ -273,7 +271,7 @@ describe('ViewContainerRef', () => {
`
})
class TestComp {
@ViewChild('container', {read: ViewContainerRef}) vcRef !: ViewContainerRef;
@ViewChild('container', {read: ViewContainerRef}) vcRef!: ViewContainerRef;
private helloCompFactory = this.cfr.resolveComponentFactory(HelloComp);
@ -357,7 +355,9 @@ describe('ViewContainerRef', () => {
// Insert the view again at the same index
viewContainerRef.insert(ref0, 0);
expect(() => { fixture.destroy(); }).not.toThrow();
expect(() => {
fixture.destroy();
}).not.toThrow();
expect(fixture.nativeElement.textContent).toEqual('0');
});
@ -405,7 +405,6 @@ describe('ViewContainerRef', () => {
});
it('should insert a view already inserted into another container', () => {
@Component({
selector: 'test-cmpt',
template: `
@ -414,9 +413,9 @@ describe('ViewContainerRef', () => {
`
})
class TestComponent {
@ViewChild('t', {static: true}) t !: TemplateRef<{}>;
@ViewChild('c1', {static: true, read: ViewContainerRef}) c1 !: ViewContainerRef;
@ViewChild('c2', {static: true, read: ViewContainerRef}) c2 !: ViewContainerRef;
@ViewChild('t', {static: true}) t!: TemplateRef<{}>;
@ViewChild('c1', {static: true, read: ViewContainerRef}) c1!: ViewContainerRef;
@ViewChild('c2', {static: true, read: ViewContainerRef}) c2!: ViewContainerRef;
}
TestBed.configureTestingModule({declarations: [TestComponent]});
@ -660,7 +659,6 @@ describe('ViewContainerRef', () => {
});
describe('get and indexOf', () => {
beforeEach(() => {
TestBed.configureTestingModule({declarations: [EmbeddedViewInsertionComp, VCRefDirective]});
});
@ -677,13 +675,13 @@ describe('ViewContainerRef', () => {
fixture.detectChanges();
let viewRef = vcRefDir.vcref.get(0);
expect(vcRefDir.vcref.indexOf(viewRef !)).toEqual(0);
expect(vcRefDir.vcref.indexOf(viewRef!)).toEqual(0);
viewRef = vcRefDir.vcref.get(1);
expect(vcRefDir.vcref.indexOf(viewRef !)).toEqual(1);
expect(vcRefDir.vcref.indexOf(viewRef!)).toEqual(1);
viewRef = vcRefDir.vcref.get(2);
expect(vcRefDir.vcref.indexOf(viewRef !)).toEqual(2);
expect(vcRefDir.vcref.indexOf(viewRef!)).toEqual(2);
});
it('should handle out of bounds cases', () => {
@ -700,7 +698,7 @@ describe('ViewContainerRef', () => {
const viewRef = vcRefDir.vcref.get(0);
vcRefDir.vcref.remove(0);
expect(vcRefDir.vcref.indexOf(viewRef !)).toEqual(-1);
expect(vcRefDir.vcref.indexOf(viewRef!)).toEqual(-1);
});
it('should return -1 as indexOf when no views were inserted', () => {
@ -742,21 +740,21 @@ describe('ViewContainerRef', () => {
expect(getElementHtml(fixture.nativeElement)).toEqual('<p vcref=""></p>**A**BC');
let viewRef = vcRefDir.vcref.get(0);
vcRefDir.vcref.move(viewRef !, 2);
vcRefDir.vcref.move(viewRef!, 2);
fixture.detectChanges();
expect(getElementHtml(fixture.nativeElement)).toEqual('<p vcref=""></p>BC**A**');
vcRefDir.vcref.move(viewRef !, 0);
vcRefDir.vcref.move(viewRef!, 0);
fixture.detectChanges();
expect(getElementHtml(fixture.nativeElement)).toEqual('<p vcref=""></p>**A**BC');
vcRefDir.vcref.move(viewRef !, 1);
vcRefDir.vcref.move(viewRef!, 1);
fixture.detectChanges();
expect(getElementHtml(fixture.nativeElement)).toEqual('<p vcref=""></p>B**A**C');
// Invalid indices when detaching throws an exception in Ivy: FW-1330.
ivyEnabled && expect(() => vcRefDir.vcref.move(viewRef !, -1)).toThrow();
ivyEnabled && expect(() => vcRefDir.vcref.move(viewRef !, 42)).toThrow();
ivyEnabled && expect(() => vcRefDir.vcref.move(viewRef!, -1)).toThrow();
ivyEnabled && expect(() => vcRefDir.vcref.move(viewRef!, 42)).toThrow();
});
});
@ -769,7 +767,7 @@ describe('ViewContainerRef', () => {
`
})
class TestComponent {
@ViewChild(VCRefDirective, {static: true}) vcRefDir !: VCRefDirective;
@ViewChild(VCRefDirective, {static: true}) vcRefDir!: VCRefDirective;
}
TestBed.configureTestingModule({declarations: [VCRefDirective, TestComponent]});
@ -789,7 +787,6 @@ describe('ViewContainerRef', () => {
});
describe('detach', () => {
beforeEach(() => {
TestBed.configureTestingModule({declarations: [EmbeddedViewInsertionComp, VCRefDirective]});
@ -825,7 +822,7 @@ describe('ViewContainerRef', () => {
// Invalid indices when detaching throws an exception in Ivy: FW-1330.
ivyEnabled && expect(() => vcRefDir.vcref.detach(-1)).toThrow();
ivyEnabled && expect(() => vcRefDir.vcref.detach(42)).toThrow();
ivyEnabled && expect(ngDevMode !.rendererDestroyNode).toBe(0);
ivyEnabled && expect(ngDevMode!.rendererDestroyNode).toBe(0);
});
it('should detach the last embedded view when no index is specified', () => {
@ -846,7 +843,7 @@ describe('ViewContainerRef', () => {
fixture.detectChanges();
expect(getElementHtml(fixture.nativeElement)).toEqual('<p vcref=""></p>ABCD');
expect(viewE.destroyed).toBeFalsy();
ivyEnabled && expect(ngDevMode !.rendererDestroyNode).toBe(0);
ivyEnabled && expect(ngDevMode!.rendererDestroyNode).toBe(0);
});
});
@ -895,7 +892,7 @@ describe('ViewContainerRef', () => {
// Invalid indices when detaching throws an exception in Ivy: FW-1330.
ivyEnabled && expect(() => vcRefDir.vcref.remove(-1)).toThrow();
ivyEnabled && expect(() => vcRefDir.vcref.remove(42)).toThrow();
ivyEnabled && expect(ngDevMode !.rendererDestroyNode).toBe(2);
ivyEnabled && expect(ngDevMode!.rendererDestroyNode).toBe(2);
});
it('should remove the last embedded view when no index is specified', () => {
@ -916,7 +913,7 @@ describe('ViewContainerRef', () => {
fixture.detectChanges();
expect(getElementHtml(fixture.nativeElement)).toEqual('<p vcref=""></p>ABCD');
expect(viewE.destroyed).toBeTruthy();
ivyEnabled && expect(ngDevMode !.rendererDestroyNode).toBe(1);
ivyEnabled && expect(ngDevMode!.rendererDestroyNode).toBe(1);
});
it('should throw when trying to insert a removed or destroyed view', () => {
@ -1063,7 +1060,7 @@ describe('ViewContainerRef', () => {
`
})
class TestComponent {
@ViewChild(VCRefDirective, {static: true}) vcRef !: VCRefDirective;
@ViewChild(VCRefDirective, {static: true}) vcRef!: VCRefDirective;
}
TestBed.configureTestingModule({declarations: [TestComponent, VCRefDirective]});
@ -1087,8 +1084,8 @@ describe('ViewContainerRef', () => {
expect(getElementHtml(fixture.nativeElement)).toEqual('YABC<footer></footer>');
// Invalid indices when detaching throws an exception in Ivy: FW-1330.
ivyEnabled && expect(() => vcRef !.createView('Z', -1)).toThrow();
ivyEnabled && expect(() => vcRef !.createView('Z', 5)).toThrow();
ivyEnabled && expect(() => vcRef!.createView('Z', -1)).toThrow();
ivyEnabled && expect(() => vcRef!.createView('Z', 5)).toThrow();
});
it('should apply directives and pipes of the host view to the TemplateRef', () => {
@ -1099,7 +1096,9 @@ describe('ViewContainerRef', () => {
@Pipe({name: 'starPipe'})
class StarPipe implements PipeTransform {
transform(value: any) { return `**${value}**`; }
transform(value: any) {
return `**${value}**`;
}
}
@Component({
@ -1121,15 +1120,14 @@ describe('ViewContainerRef', () => {
fixture.debugElement.query(By.directive(VCRefDirective)).injector.get(VCRefDirective);
fixture.detectChanges();
vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef !);
vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef !);
vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef!);
vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef!);
fixture.detectChanges();
expect(getElementHtml(fixture.nativeElement))
.toEqual(
'<child vcref="">**A**</child><child>**C**</child><child>**C**</child><child>**B**</child>');
});
});
describe('createComponent', () => {
@ -1140,9 +1138,13 @@ describe('ViewContainerRef', () => {
it('should work without Injector and NgModuleRef', () => {
@Component({selector: 'embedded-cmp', template: `foo`})
class EmbeddedComponent implements DoCheck, OnInit {
ngOnInit() { templateExecutionCounter++; }
ngOnInit() {
templateExecutionCounter++;
}
ngDoCheck() { templateExecutionCounter++; }
ngDoCheck() {
templateExecutionCounter++;
}
}
@NgModule({entryComponents: [EmbeddedComponent], declarations: [EmbeddedComponent]})
@ -1185,13 +1187,16 @@ describe('ViewContainerRef', () => {
selector: 'embedded-cmp',
template: `foo`,
})
class EmbeddedComponent implements DoCheck,
OnInit {
class EmbeddedComponent implements DoCheck, OnInit {
constructor(public s: String) {}
ngOnInit() { templateExecutionCounter++; }
ngOnInit() {
templateExecutionCounter++;
}
ngDoCheck() { templateExecutionCounter++; }
ngDoCheck() {
templateExecutionCounter++;
}
}
@NgModule({entryComponents: [EmbeddedComponent], declarations: [EmbeddedComponent]})
@ -1423,7 +1428,7 @@ describe('ViewContainerRef', () => {
const makeComponentFactory = (componentType: any) => ({
create: () => TestBed.createComponent(componentType).componentRef,
});
this.viewContainerRef !.createComponent(makeComponentFactory(Child) as any);
this.viewContainerRef!.createComponent(makeComponentFactory(Child) as any);
}
}
@ -1493,8 +1498,8 @@ describe('ViewContainerRef', () => {
`,
})
class LoopComp {
@Input() tpl !: TemplateRef<any>;
@Input() rows !: any[];
@Input() tpl!: TemplateRef<any>;
@Input() rows!: any[];
name = 'Loop';
}
@ -1715,7 +1720,6 @@ describe('ViewContainerRef', () => {
});
describe('lifecycle hooks', () => {
// Angular 5 reference: https://stackblitz.com/edit/lifecycle-hooks-vcref
const log: string[] = [];
@ -1723,19 +1727,37 @@ describe('ViewContainerRef', () => {
class ComponentWithHooks {
@Input() name: string|undefined;
private log(msg: string) { log.push(msg); }
private log(msg: string) {
log.push(msg);
}
ngOnChanges() { this.log('onChanges-' + this.name); }
ngOnInit() { this.log('onInit-' + this.name); }
ngDoCheck() { this.log('doCheck-' + this.name); }
ngOnChanges() {
this.log('onChanges-' + this.name);
}
ngOnInit() {
this.log('onInit-' + this.name);
}
ngDoCheck() {
this.log('doCheck-' + this.name);
}
ngAfterContentInit() { this.log('afterContentInit-' + this.name); }
ngAfterContentChecked() { this.log('afterContentChecked-' + this.name); }
ngAfterContentInit() {
this.log('afterContentInit-' + this.name);
}
ngAfterContentChecked() {
this.log('afterContentChecked-' + this.name);
}
ngAfterViewInit() { this.log('afterViewInit-' + this.name); }
ngAfterViewChecked() { this.log('afterViewChecked-' + this.name); }
ngAfterViewInit() {
this.log('afterViewInit-' + this.name);
}
ngAfterViewChecked() {
this.log('afterViewChecked-' + this.name);
}
ngOnDestroy() { this.log('onDestroy-' + this.name); }
ngOnDestroy() {
this.log('onDestroy-' + this.name);
}
}
@NgModule({
@ -1784,7 +1806,7 @@ describe('ViewContainerRef', () => {
]);
log.length = 0;
vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef !);
vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef!);
expect(getElementHtml(fixture.nativeElement))
.toEqual('<hooks vcref="">A</hooks><hooks></hooks><hooks>B</hooks>');
expect(log).toEqual([]);
@ -1815,7 +1837,7 @@ describe('ViewContainerRef', () => {
]);
log.length = 0;
vcRefDir.vcref.insert(viewRef !);
vcRefDir.vcref.insert(viewRef!);
fixture.detectChanges();
expect(log).toEqual([
'doCheck-A', 'doCheck-B', 'doCheck-C', 'afterContentChecked-C', 'afterViewChecked-C',
@ -1898,7 +1920,7 @@ describe('ViewContainerRef', () => {
]);
log.length = 0;
vcRefDir.vcref.insert(viewRef !);
vcRefDir.vcref.insert(viewRef!);
fixture.detectChanges();
expect(log).toEqual([
'doCheck-A', 'doCheck-B', 'doCheck-D', 'afterContentChecked-D', 'afterViewChecked-D',
@ -1916,7 +1938,6 @@ describe('ViewContainerRef', () => {
});
describe('host bindings', () => {
it('should support host bindings on dynamically created components', () => {
@Component(
{selector: 'host-bindings', host: {'id': 'attribute', '[title]': 'title'}, template: ``})
@ -1926,7 +1947,7 @@ describe('ViewContainerRef', () => {
@Component({template: `<ng-template vcref></ng-template>`})
class TestComponent {
@ViewChild(VCRefDirective, {static: true}) vcRefDir !: VCRefDirective;
@ViewChild(VCRefDirective, {static: true}) vcRefDir!: VCRefDirective;
}
@NgModule({declarations: [HostBindingCmpt], entryComponents: [HostBindingCmpt]})
@ -1956,11 +1977,9 @@ describe('ViewContainerRef', () => {
expect(fixture.nativeElement.children[0].getAttribute('id')).toBe('attribute');
expect(fixture.nativeElement.children[0].getAttribute('title')).toBe('changed');
});
});
describe('projection', () => {
it('should project the ViewContainerRef content along its host, in an element', () => {
@Component({selector: 'child', template: '<div><ng-content></ng-content></div>'})
class Child {
@ -1990,7 +2009,7 @@ describe('ViewContainerRef', () => {
expect(getElementHtml(fixture.nativeElement))
.toEqual('<child><div><header vcref="">blah</header></div></child>');
vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef !);
vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef!);
fixture.detectChanges();
expect(getElementHtml(fixture.nativeElement))
.toEqual('<child><div><header vcref="">blah</header><span>bar</span></div></child>');
@ -2031,7 +2050,7 @@ describe('ViewContainerRef', () => {
.toEqual(
'<child-with-view>Before (inside)- Before projected <header vcref="">blah</header> After projected -After (inside)</child-with-view>');
vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef !);
vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef!);
fixture.detectChanges();
expect(getElementHtml(fixture.nativeElement))
@ -2067,7 +2086,6 @@ describe('ViewContainerRef', () => {
});
describe('with select', () => {
@Component({
selector: 'child-with-selector',
template: `
@ -2105,7 +2123,7 @@ describe('ViewContainerRef', () => {
.toEqual(
'<child-with-selector><p class="a"><header vcref="">blah</header></p><p class="b"></p></child-with-selector>');
vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef !);
vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef!);
fixture.detectChanges();
expect(getElementHtml(fixture.nativeElement))
@ -2132,13 +2150,13 @@ describe('ViewContainerRef', () => {
`
})
class MyComp {
@ViewChild('source', {static: true})
source !: TemplateRef<{}>;
@ViewChild('source', {static: true}) source!: TemplateRef<{}>;
@ViewChild('target', {read: ViewContainerRef, static: true})
target !: ViewContainerRef;
@ViewChild('target', {read: ViewContainerRef, static: true}) target!: ViewContainerRef;
ngOnInit() { this.target.createEmbeddedView(this.source); }
ngOnInit() {
this.target.createEmbeddedView(this.source);
}
}
TestBed.configureTestingModule({declarations: [MyComp, ContentComp]});
@ -2175,7 +2193,7 @@ describe('ViewContainerRef', () => {
.toEqual(
'<child-with-selector><p class="a"></p><p class="b"><footer vcref="">blah</footer></p></child-with-selector>');
vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef !);
vcRefDir.vcref.createEmbeddedView(vcRefDir.tplRef!);
fixture.detectChanges();
expect(getElementHtml(fixture.nativeElement))
@ -2183,7 +2201,6 @@ describe('ViewContainerRef', () => {
'<child-with-selector><p class="a"></p><p class="b"><footer vcref="">blah</footer><span>bar</span></p></child-with-selector>');
});
});
});
describe('root view container ref', () => {
@ -2204,7 +2221,7 @@ describe('ViewContainerRef', () => {
containerEl = document.createElement('div');
document.body.appendChild(containerEl);
containerEl !.appendChild(rootEl);
containerEl!.appendChild(rootEl);
}
};
}
@ -2223,7 +2240,9 @@ describe('ViewContainerRef', () => {
class DynamicCompWithBindings implements DoCheck {
checkCount = 0;
ngDoCheck() { this.checkCount++; }
ngDoCheck() {
this.checkCount++;
}
}
@Component({template: ``})
@ -2248,21 +2267,21 @@ describe('ViewContainerRef', () => {
// Ivy inserts a comment for the root view container ref instance. This is not
// the case for view engine and we need to adjust the assertions.
expect(containerEl !.childNodes.length).toBe(ivyEnabled ? 2 : 1);
ivyEnabled && expect(containerEl !.childNodes[1].nodeType).toBe(Node.COMMENT_NODE);
expect(containerEl!.childNodes.length).toBe(ivyEnabled ? 2 : 1);
ivyEnabled && expect(containerEl!.childNodes[1].nodeType).toBe(Node.COMMENT_NODE);
expect((containerEl !.childNodes[0] as Element).tagName).toBe('DIV');
expect((containerEl!.childNodes[0] as Element).tagName).toBe('DIV');
vcRef.createComponent(cfResolver.resolveComponentFactory(DynamicCompWithBindings));
fixture.detectChanges();
expect(containerEl !.childNodes.length).toBe(ivyEnabled ? 3 : 2);
expect(containerEl !.childNodes[1].textContent).toBe('check count: 1');
expect(containerEl!.childNodes.length).toBe(ivyEnabled ? 3 : 2);
expect(containerEl!.childNodes[1].textContent).toBe('check count: 1');
fixture.detectChanges();
expect(containerEl !.childNodes.length).toBe(ivyEnabled ? 3 : 2);
expect(containerEl !.childNodes[1].textContent).toBe('check count: 2');
expect(containerEl!.childNodes.length).toBe(ivyEnabled ? 3 : 2);
expect(containerEl!.childNodes[1].textContent).toBe('check count: 2');
});
it('should create deep DOM tree immediately for dynamically created components', () => {
@ -2299,21 +2318,21 @@ describe('ViewContainerRef', () => {
// Ivy inserts a comment for the root view container ref instance. This is not
// the case for view engine and we need to adjust the assertions.
expect(containerEl !.childNodes.length).toBe(ivyEnabled ? 2 : 1);
ivyEnabled && expect(containerEl !.childNodes[1].nodeType).toBe(Node.COMMENT_NODE);
expect(containerEl!.childNodes.length).toBe(ivyEnabled ? 2 : 1);
ivyEnabled && expect(containerEl!.childNodes[1].nodeType).toBe(Node.COMMENT_NODE);
expect((containerEl !.childNodes[0] as Element).tagName).toBe('DIV');
expect((containerEl!.childNodes[0] as Element).tagName).toBe('DIV');
vcRef.createComponent(cfResolver.resolveComponentFactory(DynamicCompWithChildren));
expect(containerEl !.childNodes.length).toBe(ivyEnabled ? 3 : 2);
expect(getElementHtml(containerEl !.childNodes[1] as Element))
expect(containerEl!.childNodes.length).toBe(ivyEnabled ? 3 : 2);
expect(getElementHtml(containerEl!.childNodes[1] as Element))
.toBe('<child><div></div></child>');
fixture.detectChanges();
expect(containerEl !.childNodes.length).toBe(ivyEnabled ? 3 : 2);
expect(getElementHtml(containerEl !.childNodes[1] as Element))
expect(containerEl!.childNodes.length).toBe(ivyEnabled ? 3 : 2);
expect(getElementHtml(containerEl!.childNodes[1] as Element))
.toBe(`<child><div>text</div></child>`);
});
});
@ -2374,7 +2393,7 @@ class EmbeddedComponentWithNgZoneModule {
`
})
class ViewContainerRefComp {
@ViewChildren(TemplateRef) templates !: QueryList<TemplateRef<any>>;
@ViewChildren(TemplateRef) templates!: QueryList<TemplateRef<any>>;
constructor(public vcr: ViewContainerRef) {}
}
@ -2386,21 +2405,25 @@ class ViewContainerRefComp {
`
})
class ViewContainerRefApp {
@ViewChild(ViewContainerRefComp) vcrComp !: ViewContainerRefComp;
@ViewChild(ViewContainerRefComp) vcrComp!: ViewContainerRefComp;
}
@Directive({selector: '[structDir]'})
export class StructDir {
constructor(private vcref: ViewContainerRef, private tplRef: TemplateRef<any>) {}
create() { this.vcref.createEmbeddedView(this.tplRef); }
create() {
this.vcref.createEmbeddedView(this.tplRef);
}
destroy() { this.vcref.clear(); }
destroy() {
this.vcref.clear();
}
}
@Component({selector: 'destroy-cases', template: ` `})
class DestroyCasesComp {
@ViewChildren(StructDir) structDirs !: QueryList<StructDir>;
@ViewChildren(StructDir) structDirs!: QueryList<StructDir>;
}
@Directive({selector: '[constructorDir]'})
@ -2419,7 +2442,7 @@ class ConstructorDir {
`
})
class ConstructorApp {
@ViewChild('foo', {static: true}) foo !: ElementRef;
@ViewChild('foo', {static: true}) foo!: ElementRef;
}
@Component({
@ -2431,5 +2454,5 @@ class ConstructorApp {
`
})
class ConstructorAppWithQueries {
@ViewChild('foo', {static: true}) foo !: TemplateRef<any>;
@ViewChild('foo', {static: true}) foo!: TemplateRef<any>;
}

View File

@ -33,15 +33,14 @@ describe('view insertion', () => {
})
class App {
@ViewChild('container', {read: ViewContainerRef, static: true})
container: ViewContainerRef = null !;
container: ViewContainerRef = null!;
@ViewChild('simple', {read: TemplateRef, static: true})
simple: TemplateRef<any> = null !;
@ViewChild('simple', {read: TemplateRef, static: true}) simple: TemplateRef<any> = null!;
view0: EmbeddedViewRef<any> = null !;
view1: EmbeddedViewRef<any> = null !;
view2: EmbeddedViewRef<any> = null !;
view3: EmbeddedViewRef<any> = null !;
view0: EmbeddedViewRef<any> = null!;
view1: EmbeddedViewRef<any> = null!;
view2: EmbeddedViewRef<any> = null!;
view3: EmbeddedViewRef<any> = null!;
constructor(public changeDetector: ChangeDetectorRef) {}
@ -90,16 +89,14 @@ describe('view insertion', () => {
`
})
class App {
@ViewChild('container', {read: ViewContainerRef})
container: ViewContainerRef = null !;
@ViewChild('container', {read: ViewContainerRef}) container: ViewContainerRef = null!;
@ViewChild('empty', {read: TemplateRef})
empty: TemplateRef<any> = null !;
@ViewChild('empty', {read: TemplateRef}) empty: TemplateRef<any> = null!;
view0: EmbeddedViewRef<any> = null !;
view1: EmbeddedViewRef<any> = null !;
view2: EmbeddedViewRef<any> = null !;
view3: EmbeddedViewRef<any> = null !;
view0: EmbeddedViewRef<any> = null!;
view1: EmbeddedViewRef<any> = null!;
view2: EmbeddedViewRef<any> = null!;
view3: EmbeddedViewRef<any> = null!;
ngAfterViewInit() {
// insert at the front
@ -140,16 +137,14 @@ describe('view insertion', () => {
`
})
class Comp {
@ViewChild('container', {read: ViewContainerRef})
container: ViewContainerRef = null !;
@ViewChild('container', {read: ViewContainerRef}) container: ViewContainerRef = null!;
@ViewChild('projection', {read: TemplateRef})
projection: TemplateRef<any> = null !;
@ViewChild('projection', {read: TemplateRef}) projection: TemplateRef<any> = null!;
view0: EmbeddedViewRef<any> = null !;
view1: EmbeddedViewRef<any> = null !;
view2: EmbeddedViewRef<any> = null !;
view3: EmbeddedViewRef<any> = null !;
view0: EmbeddedViewRef<any> = null!;
view1: EmbeddedViewRef<any> = null!;
view2: EmbeddedViewRef<any> = null!;
view3: EmbeddedViewRef<any> = null!;
ngAfterViewInit() {
// insert at the front
@ -201,16 +196,14 @@ describe('view insertion', () => {
`
})
class App {
@ViewChild('container', {read: ViewContainerRef})
container: ViewContainerRef = null !;
@ViewChild('container', {read: ViewContainerRef}) container: ViewContainerRef = null!;
@ViewChild('subContainer', {read: TemplateRef})
subContainer: TemplateRef<any> = null !;
@ViewChild('subContainer', {read: TemplateRef}) subContainer: TemplateRef<any> = null!;
view0: EmbeddedViewRef<any> = null !;
view1: EmbeddedViewRef<any> = null !;
view2: EmbeddedViewRef<any> = null !;
view3: EmbeddedViewRef<any> = null !;
view0: EmbeddedViewRef<any> = null!;
view1: EmbeddedViewRef<any> = null!;
view2: EmbeddedViewRef<any> = null!;
view3: EmbeddedViewRef<any> = null!;
constructor(public changeDetectorRef: ChangeDetectorRef) {}
@ -265,9 +258,9 @@ describe('view insertion', () => {
describe('before embedded view', () => {
@Component({selector: 'test-cmpt', template: ''})
class TestCmpt {
@ViewChild('before', {static: true}) beforeTpl !: TemplateRef<{}>;
@ViewChild('insert', {static: true}) insertTpl !: TemplateRef<{}>;
@ViewChild('vi', {static: true}) viewInsertingDir !: ViewInsertingDir;
@ViewChild('before', {static: true}) beforeTpl!: TemplateRef<{}>;
@ViewChild('insert', {static: true}) insertTpl!: TemplateRef<{}>;
@ViewChild('vi', {static: true}) viewInsertingDir!: ViewInsertingDir;
minutes = 10;
@ -303,8 +296,9 @@ describe('view insertion', () => {
}
it('should insert before a view with the text node as the first root node',
() => { expect(createAndInsertViews('|before').textContent).toBe('insert|before'); });
it('should insert before a view with the text node as the first root node', () => {
expect(createAndInsertViews('|before').textContent).toBe('insert|before');
});
it('should insert before a view with the element as the first root node', () => {
expect(createAndInsertViews('<span>|before</span>').textContent).toBe('insert|before');
@ -336,13 +330,11 @@ describe('view insertion', () => {
it('should insert before a view with a container as the first root node', () => {
expect(createAndInsertViews(`<ng-template [ngIf]="true">|before</ng-template>`).textContent)
.toBe('insert|before');
});
it('should insert before a view with an empty container as the first root node', () => {
expect(createAndInsertViews(`<ng-template [ngIf]="true"></ng-template>|before`).textContent)
.toBe('insert|before');
});
onlyInIvy('VE incorrectly inserts views before ng-container content')
@ -353,7 +345,6 @@ describe('view insertion', () => {
<ng-template #after>|after</ng-template>
`).textContent)
.toBe('insert|before|after');
});
@ -363,7 +354,6 @@ describe('view insertion', () => {
<ng-template #after>|after</ng-template>
`).textContent)
.toBe('insert|before|after');
});
it('should insert before a view with an empty projection as the first root node', () => {
@ -414,11 +404,9 @@ describe('view insertion', () => {
fixture.detectChanges();
expect(fixture.debugElement.nativeElement.textContent).toBe('start|testtest|end');
});
});
describe('before embedded view with projection', () => {
@Component({
selector: 'with-content',
template: `
@ -428,9 +416,9 @@ describe('view insertion', () => {
`
})
class WithContentCmpt {
@ViewChild('insert', {static: true}) insertTpl !: TemplateRef<{}>;
@ViewChild('before', {static: true}) beforeTpl !: TemplateRef<{}>;
@ViewChild('vi', {static: true}) viewInsertingDir !: ViewInsertingDir;
@ViewChild('insert', {static: true}) insertTpl!: TemplateRef<{}>;
@ViewChild('before', {static: true}) beforeTpl!: TemplateRef<{}>;
@ViewChild('vi', {static: true}) viewInsertingDir!: ViewInsertingDir;
insert() {
const beforeView = this.beforeTpl.createEmbeddedView({});
@ -442,7 +430,7 @@ describe('view insertion', () => {
@Component({selector: 'test-cmpt', template: ''})
class TestCmpt {
@ViewChild('wc', {static: true}) withContentCmpt !: WithContentCmpt;
@ViewChild('wc', {static: true}) withContentCmpt!: WithContentCmpt;
}
beforeEach(() => {
@ -476,7 +464,6 @@ describe('view insertion', () => {
expect(fixture.nativeElement.textContent).toBe('insert|before');
});
});
describe('before component view', () => {
@ -503,8 +490,8 @@ describe('view insertion', () => {
`
})
class TestCmpt {
@ViewChild('insert', {static: true}) insertTpl !: TemplateRef<{}>;
@ViewChild('vi', {static: true}) viewInsertingDir !: ViewInsertingDir;
@ViewChild('insert', {static: true}) insertTpl!: TemplateRef<{}>;
@ViewChild('vi', {static: true}) viewInsertingDir!: ViewInsertingDir;
constructor(private _cfr: ComponentFactoryResolver, private _injector: Injector) {}
@ -537,16 +524,13 @@ describe('view insertion', () => {
expect(fixture.nativeElement.textContent).toBe('insert|before');
});
});
});
describe('non-regression', () => {
// https://github.com/angular/angular/issues/31971
it('should insert component views into ViewContainerRef injected by querying <ng-container>',
() => {
@Component({selector: 'dynamic-cmpt', template: 'dynamic'})
class DynamicComponent {
}
@ -562,8 +546,7 @@ describe('view insertion', () => {
`
})
class AppComponent {
@ViewChild('container', {read: ViewContainerRef, static: true})
vcr !: ViewContainerRef;
@ViewChild('container', {read: ViewContainerRef, static: true}) vcr!: ViewContainerRef;
constructor(private _cfr: ComponentFactoryResolver) {}
@ -595,7 +578,6 @@ describe('view insertion', () => {
// https://github.com/angular/angular/issues/33679
it('should insert embedded views into ViewContainerRef injected by querying <ng-container>',
() => {
@Component({
selector: 'app-root',
template: `
@ -608,12 +590,13 @@ describe('view insertion', () => {
`
})
class AppComponent {
@ViewChild('container', {read: ViewContainerRef, static: true})
vcr !: ViewContainerRef;
@ViewChild('container', {read: ViewContainerRef, static: true}) vcr!: ViewContainerRef;
@ViewChild('template', {read: TemplateRef, static: true}) template !: TemplateRef<any>;
click() { this.vcr.createEmbeddedView(this.template, undefined, 0); }
click() {
this.vcr.createEmbeddedView(this.template, undefined, 0);
}
}
TestBed.configureTestingModule({
@ -659,6 +642,5 @@ describe('view insertion', () => {
expect(fixture.nativeElement.textContent.trim()).toContain('2 1');
});
});
});

View File

@ -13,7 +13,6 @@ import {TestBed} from '@angular/core/testing';
describe('ViewRef', () => {
it('should remove nodes from DOM when the view is detached from app ref', () => {
@Component({selector: 'dynamic-cpt', template: '<div></div>'})
class DynamicComponent {
constructor(public elRef: ElementRef) {}
@ -21,7 +20,7 @@ describe('ViewRef', () => {
@Component({template: `<span></span>`})
class App {
componentRef !: ComponentRef<DynamicComponent>;
componentRef!: ComponentRef<DynamicComponent>;
constructor(
public appRef: ApplicationRef, private cfr: ComponentFactoryResolver,
private injector: Injector) {}
@ -33,7 +32,9 @@ describe('ViewRef', () => {
document.body.appendChild(this.componentRef.instance.elRef.nativeElement);
}
destroy() { (this.componentRef.hostView as InternalViewRef).detachFromAppRef(); }
destroy() {
(this.componentRef.hostView as InternalViewRef).detachFromAppRef();
}
}
@NgModule({declarations: [App, DynamicComponent], entryComponents: [DynamicComponent]})