@ -14,17 +14,25 @@ import {obsoleteInIvy} from '@angular/private/testing';
|
||||
|
||||
|
||||
if (ivyEnabled) {
|
||||
describe('ivy', () => { declareTests(); });
|
||||
describe('ivy', () => {
|
||||
declareTests();
|
||||
});
|
||||
} else {
|
||||
describe('jit', () => { declareTests({useJit: true}); });
|
||||
describe('no jit', () => { declareTests({useJit: false}); });
|
||||
describe('jit', () => {
|
||||
declareTests({useJit: true});
|
||||
});
|
||||
describe('no jit', () => {
|
||||
declareTests({useJit: false});
|
||||
});
|
||||
}
|
||||
|
||||
class DummyConsole implements Console {
|
||||
public warnings: string[] = [];
|
||||
|
||||
log(message: string) {}
|
||||
warn(message: string) { this.warnings.push(message); }
|
||||
warn(message: string) {
|
||||
this.warnings.push(message);
|
||||
}
|
||||
}
|
||||
|
||||
function declareTests(config?: {useJit: boolean}) {
|
||||
@ -40,7 +48,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
const compFixture = TestBed.createComponent(MainComp);
|
||||
const mainComp: MainComp = compFixture.componentInstance;
|
||||
expect(compFixture.componentRef.injector.get(ComponentFactoryResolver)).toBe(mainComp.cfr);
|
||||
const cf = mainComp.cfr.resolveComponentFactory(ChildComp) !;
|
||||
const cf = mainComp.cfr.resolveComponentFactory(ChildComp)!;
|
||||
expect(cf.componentType).toBe(ChildComp);
|
||||
});
|
||||
|
||||
@ -52,8 +60,8 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
const mainComp: CompWithAnalyzeEntryComponentsProvider = compFixture.componentInstance;
|
||||
const cfr: ComponentFactoryResolver =
|
||||
compFixture.componentRef.injector.get(ComponentFactoryResolver);
|
||||
expect(cfr.resolveComponentFactory(ChildComp) !.componentType).toBe(ChildComp);
|
||||
expect(cfr.resolveComponentFactory(NestedChildComp) !.componentType).toBe(NestedChildComp);
|
||||
expect(cfr.resolveComponentFactory(ChildComp)!.componentType).toBe(ChildComp);
|
||||
expect(cfr.resolveComponentFactory(NestedChildComp)!.componentType).toBe(NestedChildComp);
|
||||
});
|
||||
|
||||
it('should be able to get a component form a parent component (view hierarchy)', () => {
|
||||
@ -63,10 +71,10 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
const childCompEl = compFixture.debugElement.children[0];
|
||||
const childComp: ChildComp = childCompEl.componentInstance;
|
||||
// declared on ChildComp directly
|
||||
expect(childComp.cfr.resolveComponentFactory(NestedChildComp) !.componentType)
|
||||
expect(childComp.cfr.resolveComponentFactory(NestedChildComp)!.componentType)
|
||||
.toBe(NestedChildComp);
|
||||
// inherited from MainComp
|
||||
expect(childComp.cfr.resolveComponentFactory(ChildComp) !.componentType).toBe(ChildComp);
|
||||
expect(childComp.cfr.resolveComponentFactory(ChildComp)!.componentType).toBe(ChildComp);
|
||||
});
|
||||
|
||||
obsoleteInIvy('In Ivy, the ComponentFactoryResolver can resolve any component factory')
|
||||
@ -79,12 +87,11 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
const compFixture = TestBed.createComponent(MainComp);
|
||||
const nestedChildCompEl = compFixture.debugElement.children[0].children[0];
|
||||
const nestedChildComp: NestedChildComp = nestedChildCompEl.componentInstance;
|
||||
expect(nestedChildComp.cfr.resolveComponentFactory(ChildComp) !.componentType)
|
||||
expect(nestedChildComp.cfr.resolveComponentFactory(ChildComp)!.componentType)
|
||||
.toBe(ChildComp);
|
||||
expect(() => nestedChildComp.cfr.resolveComponentFactory(NestedChildComp))
|
||||
.toThrow(noComponentFactoryError(NestedChildComp));
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -17,8 +17,7 @@ class DirectiveA {
|
||||
|
||||
@Directive({selector: '[directiveB]'})
|
||||
class DirectiveB {
|
||||
@HostBinding('title')
|
||||
title = 'DirectiveB Title';
|
||||
@HostBinding('title') title = 'DirectiveB Title';
|
||||
}
|
||||
|
||||
@Component({selector: 'component-a', template: 'ComponentA Template'})
|
||||
@ -34,8 +33,7 @@ class ComponentWithNoAnnotation extends ComponentA {}
|
||||
|
||||
@Directive({selector: '[directiveExtendsComponent]'})
|
||||
class DirectiveExtendsComponent extends ComponentA {
|
||||
@HostBinding('title')
|
||||
title = 'DirectiveExtendsComponent Title';
|
||||
@HostBinding('title') title = 'DirectiveExtendsComponent Title';
|
||||
}
|
||||
|
||||
class DirectiveWithNoAnnotation extends DirectiveB {}
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {CommonModule, DOCUMENT, ɵgetDOM as getDOM} from '@angular/common';
|
||||
import {Compiler, ComponentFactory, ComponentRef, ErrorHandler, EventEmitter, Host, Inject, Injectable, InjectionToken, Injector, NO_ERRORS_SCHEMA, NgModule, NgModuleRef, OnDestroy, SkipSelf, ViewRef, ɵivyEnabled as ivyEnabled} from '@angular/core';
|
||||
import {Compiler, ComponentFactory, ComponentRef, ErrorHandler, EventEmitter, Host, Inject, Injectable, InjectionToken, Injector, NgModule, NgModuleRef, NO_ERRORS_SCHEMA, OnDestroy, SkipSelf, ViewRef, ɵivyEnabled as ivyEnabled} from '@angular/core';
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, PipeTransform} from '@angular/core/src/change_detection/change_detection';
|
||||
import {getDebugContext} from '@angular/core/src/errors';
|
||||
import {ComponentFactoryResolver} from '@angular/core/src/linker/component_factory_resolver';
|
||||
@ -17,7 +17,7 @@ import {TemplateRef} from '@angular/core/src/linker/template_ref';
|
||||
import {ViewContainerRef} from '@angular/core/src/linker/view_container_ref';
|
||||
import {EmbeddedViewRef} from '@angular/core/src/linker/view_ref';
|
||||
import {Attribute, Component, ContentChildren, Directive, HostBinding, HostListener, Input, Output, Pipe} from '@angular/core/src/metadata';
|
||||
import {TestBed, async, fakeAsync, getTestBed, tick} from '@angular/core/testing';
|
||||
import {async, fakeAsync, getTestBed, TestBed, tick} from '@angular/core/testing';
|
||||
import {createMouseEvent, dispatchEvent, el, isCommentNode} from '@angular/platform-browser/testing/src/browser_util';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
import {modifiedInIvy, obsoleteInIvy, onlyInIvy} from '@angular/private/testing';
|
||||
@ -27,16 +27,23 @@ import {stringify} from '../../src/util/stringify';
|
||||
const ANCHOR_ELEMENT = new InjectionToken('AnchorElement');
|
||||
|
||||
if (ivyEnabled) {
|
||||
describe('ivy', () => { declareTests(); });
|
||||
describe('ivy', () => {
|
||||
declareTests();
|
||||
});
|
||||
} else {
|
||||
describe('jit', () => { declareTests({useJit: true}); });
|
||||
describe('no jit', () => { declareTests({useJit: false}); });
|
||||
describe('jit', () => {
|
||||
declareTests({useJit: true});
|
||||
});
|
||||
describe('no jit', () => {
|
||||
declareTests({useJit: false});
|
||||
});
|
||||
}
|
||||
|
||||
function declareTests(config?: {useJit: boolean}) {
|
||||
describe('integration tests', function() {
|
||||
|
||||
beforeEach(() => { TestBed.configureCompiler({...config}); });
|
||||
beforeEach(() => {
|
||||
TestBed.configureCompiler({...config});
|
||||
});
|
||||
|
||||
describe('react to record changes', function() {
|
||||
it('should consume text node changes', () => {
|
||||
@ -55,7 +62,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
const template = '<div>{{null}}{{ctxProp}}</div>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
fixture.componentInstance.ctxProp = null !;
|
||||
fixture.componentInstance.ctxProp = null!;
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('');
|
||||
@ -133,7 +140,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.children[0].nativeElement.getAttribute('foo')).toEqual('bar');
|
||||
|
||||
fixture.componentInstance.ctxProp = null !;
|
||||
fixture.componentInstance.ctxProp = null!;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.children[0].nativeElement.hasAttribute('foo')).toBeFalsy();
|
||||
});
|
||||
@ -148,7 +155,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.children[0].nativeElement.style['height']).toEqual('10px');
|
||||
|
||||
fixture.componentInstance.ctxProp = null !;
|
||||
fixture.componentInstance.ctxProp = null!;
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.children[0].nativeElement.style['height']).toEqual('');
|
||||
});
|
||||
@ -265,7 +272,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
fixture.componentInstance.ctxProp = 'a';
|
||||
fixture.detectChanges();
|
||||
|
||||
const dir = fixture.debugElement.children[0].references !['dir'];
|
||||
const dir = fixture.debugElement.children[0].references!['dir'];
|
||||
expect(dir.dirProp).toEqual('aa');
|
||||
});
|
||||
});
|
||||
@ -344,7 +351,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
it('should display correct error message for uninitialized @Output', () => {
|
||||
@Component({selector: 'my-uninitialized-output', template: '<p>It works!</p>'})
|
||||
class UninitializedOutputComp {
|
||||
@Output() customEvent !: EventEmitter<any>;
|
||||
@Output() customEvent!: EventEmitter<any>;
|
||||
}
|
||||
|
||||
const template =
|
||||
@ -406,7 +413,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
ngIfEl.childNodes
|
||||
.find(
|
||||
debugElement => debugElement.nativeNode.nodeType ===
|
||||
Node.COMMENT_NODE) !.injector.get(SomeViewport);
|
||||
Node.COMMENT_NODE)!.injector.get(SomeViewport);
|
||||
expect(someViewport.container.length).toBe(2);
|
||||
expect(ngIfEl.children.length).toBe(2);
|
||||
|
||||
@ -455,7 +462,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
|
||||
expect(fixture.debugElement.children[0].children[0].references !['alice'])
|
||||
expect(fixture.debugElement.children[0].children[0].references!['alice'])
|
||||
.toBeAnInstanceOf(ChildComp);
|
||||
});
|
||||
|
||||
@ -465,7 +472,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
|
||||
expect(fixture.debugElement.children[0].children[0].references !['localdir'])
|
||||
expect(fixture.debugElement.children[0].children[0].references!['localdir'])
|
||||
.toBeAnInstanceOf(ExportDir);
|
||||
});
|
||||
|
||||
@ -477,9 +484,9 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
expect(fixture.debugElement.children[0].references !['x'])
|
||||
expect(fixture.debugElement.children[0].references!['x'])
|
||||
.toBeAnInstanceOf(DirectiveWithMultipleExportAsNames);
|
||||
expect(fixture.debugElement.children[0].references !['y'])
|
||||
expect(fixture.debugElement.children[0].references!['y'])
|
||||
.toBeAnInstanceOf(DirectiveWithMultipleExportAsNames);
|
||||
});
|
||||
|
||||
@ -505,8 +512,8 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
|
||||
const pEl = fixture.debugElement.children[0];
|
||||
|
||||
const alice = pEl.children[0].references !['alice'];
|
||||
const bob = pEl.children[1].references !['bob'];
|
||||
const alice = pEl.children[0].references!['alice'];
|
||||
const bob = pEl.children[1].references!['bob'];
|
||||
expect(alice).toBeAnInstanceOf(ChildComp);
|
||||
expect(bob).toBeAnInstanceOf(ChildComp);
|
||||
expect(alice).not.toBe(bob);
|
||||
@ -518,8 +525,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
|
||||
expect(fixture.debugElement.children[0].references !['alice'])
|
||||
.toBeAnInstanceOf(ChildComp);
|
||||
expect(fixture.debugElement.children[0].references!['alice']).toBeAnInstanceOf(ChildComp);
|
||||
});
|
||||
|
||||
it('should assign the element instance to a user-defined variable', () => {
|
||||
@ -528,7 +534,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
|
||||
const value = fixture.debugElement.children[0].children[0].references !['alice'];
|
||||
const value = fixture.debugElement.children[0].children[0].references!['alice'];
|
||||
expect(value).not.toBe(null);
|
||||
expect(value.tagName.toLowerCase()).toEqual('div');
|
||||
});
|
||||
@ -540,7 +546,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
MyComp, {set: {template: '<ng-template ref-alice></ng-template>'}})
|
||||
.createComponent(MyComp);
|
||||
|
||||
const value = fixture.debugElement.childNodes[0].references !['alice'];
|
||||
const value = fixture.debugElement.childNodes[0].references!['alice'];
|
||||
expect(value.createEmbeddedView).toBeTruthy();
|
||||
});
|
||||
|
||||
@ -550,7 +556,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
|
||||
expect(fixture.debugElement.children[0].children[0].references !['superAlice'])
|
||||
expect(fixture.debugElement.children[0].children[0].references!['superAlice'])
|
||||
.toBeAnInstanceOf(ChildComp);
|
||||
});
|
||||
});
|
||||
@ -573,14 +579,13 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
});
|
||||
|
||||
describe('OnPush components', () => {
|
||||
|
||||
it('should use ChangeDetectorRef to manually request a check', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp, [[PushCmpWithRef]]]});
|
||||
const template = '<push-cmp-with-ref #cmp></push-cmp-with-ref>';
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
|
||||
const cmp = fixture.debugElement.children[0].references !['cmp'];
|
||||
const cmp = fixture.debugElement.children[0].references!['cmp'];
|
||||
|
||||
fixture.detectChanges();
|
||||
expect(cmp.numberOfChecks).toEqual(1);
|
||||
@ -601,7 +606,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
|
||||
const cmp = fixture.debugElement.children[0].references !['cmp'];
|
||||
const cmp = fixture.debugElement.children[0].references!['cmp'];
|
||||
|
||||
fixture.componentInstance.ctxProp = 'one';
|
||||
fixture.detectChanges();
|
||||
@ -675,7 +680,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
|
||||
const cmp = fixture.debugElement.children[0].references !['cmp'];
|
||||
const cmp = fixture.debugElement.children[0].references!['cmp'];
|
||||
|
||||
fixture.componentInstance.ctxProp = 'one';
|
||||
fixture.detectChanges();
|
||||
@ -695,7 +700,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
|
||||
tick();
|
||||
|
||||
const cmp: PushCmpWithAsyncPipe = fixture.debugElement.children[0].references !['cmp'];
|
||||
const cmp: PushCmpWithAsyncPipe = fixture.debugElement.children[0].references!['cmp'];
|
||||
fixture.detectChanges();
|
||||
expect(cmp.numberOfChecks).toEqual(1);
|
||||
|
||||
@ -726,7 +731,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
|
||||
const childComponent =
|
||||
fixture.debugElement.children[0].children[0].children[0].references !['child'];
|
||||
fixture.debugElement.children[0].children[0].children[0].references!['child'];
|
||||
expect(childComponent.myHost).toBeAnInstanceOf(SomeDirective);
|
||||
});
|
||||
|
||||
@ -748,7 +753,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
|
||||
const tc = fixture.debugElement.children[0].children[0].children[0];
|
||||
|
||||
const childComponent = tc.references !['child'];
|
||||
const childComponent = tc.references!['child'];
|
||||
expect(childComponent.myHost).toBeAnInstanceOf(SomeDirective);
|
||||
});
|
||||
|
||||
@ -795,7 +800,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
})
|
||||
.createComponent(MyComp);
|
||||
const tc = fixture.debugElement.childNodes.find(
|
||||
debugElement => debugElement.nativeNode.nodeType === Node.COMMENT_NODE) !;
|
||||
debugElement => debugElement.nativeNode.nodeType === Node.COMMENT_NODE)!;
|
||||
|
||||
const emitter = tc.injector.get(DirectiveEmittingEvent);
|
||||
const myComp = fixture.debugElement.injector.get(MyComp);
|
||||
@ -827,8 +832,11 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
|
||||
expect(dir.control).toEqual('one');
|
||||
|
||||
dir.controlChange.subscribe(
|
||||
{next: () => { expect(fixture.componentInstance.ctxProp).toEqual('two'); }});
|
||||
dir.controlChange.subscribe({
|
||||
next: () => {
|
||||
expect(fixture.componentInstance.ctxProp).toEqual('two');
|
||||
}
|
||||
});
|
||||
|
||||
dir.triggerChange('two');
|
||||
}));
|
||||
@ -954,9 +962,11 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
class DirectiveWithHostListener {
|
||||
id = 'one';
|
||||
// TODO(issue/24571): remove '!'.
|
||||
receivedArgs !: any[];
|
||||
receivedArgs!: any[];
|
||||
|
||||
doIt(...args: any[]) { this.receivedArgs = args; }
|
||||
doIt(...args: any[]) {
|
||||
this.receivedArgs = args;
|
||||
}
|
||||
}
|
||||
|
||||
const fixture =
|
||||
@ -1239,7 +1249,6 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
}).toThrowError('Cannot move a destroyed View in a ViewContainer!');
|
||||
}));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should support static attributes', () => {
|
||||
@ -1292,7 +1301,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
|
||||
const comp = fixture.debugElement.children[0].children[0].references !['consuming'];
|
||||
const comp = fixture.debugElement.children[0].children[0].references!['consuming'];
|
||||
expect(comp.injectable).toBeAnInstanceOf(InjectableService);
|
||||
});
|
||||
|
||||
@ -1308,7 +1317,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
TestBed.overrideComponent(DirectiveProvidingInjectableInView, {set: {template}});
|
||||
const fixture = TestBed.createComponent(DirectiveProvidingInjectableInView);
|
||||
|
||||
const comp = fixture.debugElement.children[0].references !['consuming'];
|
||||
const comp = fixture.debugElement.children[0].references!['consuming'];
|
||||
expect(comp.injectable).toBeAnInstanceOf(InjectableService);
|
||||
});
|
||||
|
||||
@ -1336,7 +1345,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
|
||||
const comp = fixture.debugElement.children[0].children[0].references !['dir'];
|
||||
const comp = fixture.debugElement.children[0].children[0].references!['dir'];
|
||||
expect(comp.directive.injectable).toBeAnInstanceOf(InjectableService);
|
||||
});
|
||||
|
||||
@ -1386,7 +1395,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
TestBed.overrideComponent(MyComp, {set: {template}});
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
|
||||
const providing = fixture.debugElement.children[0].references !['providing'];
|
||||
const providing = fixture.debugElement.children[0].references!['providing'];
|
||||
expect(providing.created).toBe(false);
|
||||
|
||||
fixture.componentInstance.ctxBoolProp = true;
|
||||
@ -1469,7 +1478,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
});
|
||||
|
||||
it('should use a default element name for components without selectors', () => {
|
||||
let noSelectorComponentFactory: ComponentFactory<SomeComponent> = undefined !;
|
||||
let noSelectorComponentFactory: ComponentFactory<SomeComponent> = undefined!;
|
||||
|
||||
@Component({template: '----'})
|
||||
class NoSelectorComponent {
|
||||
@ -1480,7 +1489,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
constructor(componentFactoryResolver: ComponentFactoryResolver) {
|
||||
// grab its own component factory
|
||||
noSelectorComponentFactory =
|
||||
componentFactoryResolver.resolveComponentFactory(NoSelectorComponent) !;
|
||||
componentFactoryResolver.resolveComponentFactory(NoSelectorComponent)!;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1502,8 +1511,9 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
TestBed.configureTestingModule({declarations: [MyComp, SomeDirectiveMissingAnnotation]});
|
||||
|
||||
expect(() => TestBed.createComponent(MyComp))
|
||||
.toThrowError(
|
||||
`Unexpected value '${stringify(SomeDirectiveMissingAnnotation)}' declared by the module 'DynamicTestModule'. Please add a @Pipe/@Directive/@Component annotation.`);
|
||||
.toThrowError(`Unexpected value '${
|
||||
stringify(
|
||||
SomeDirectiveMissingAnnotation)}' declared by the module 'DynamicTestModule'. Please add a @Pipe/@Directive/@Component annotation.`);
|
||||
});
|
||||
|
||||
it('should report a meaningful error when a component is missing view annotation', () => {
|
||||
@ -1822,7 +1832,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
|
||||
expect(fixture.nativeElement.innerHTML).toContain('ng-reflect-dir-prop="hello"');
|
||||
|
||||
fixture.componentInstance.ctxProp = undefined !;
|
||||
fixture.componentInstance.ctxProp = undefined!;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.nativeElement.innerHTML).not.toContain('ng-reflect-');
|
||||
@ -1839,7 +1849,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
|
||||
expect(fixture.nativeElement.innerHTML).toContain('ng-reflect-dir-prop="hello"');
|
||||
|
||||
fixture.componentInstance.ctxProp = null !;
|
||||
fixture.componentInstance.ctxProp = null!;
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.nativeElement.innerHTML).not.toContain('ng-reflect-');
|
||||
@ -1871,7 +1881,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
expect(html).toContain('bindings={');
|
||||
expect(html).toContain('"ng-reflect-ng-if": "true"');
|
||||
|
||||
fixture.componentInstance.ctxBoolProp = undefined !;
|
||||
fixture.componentInstance.ctxBoolProp = undefined!;
|
||||
fixture.detectChanges();
|
||||
|
||||
html = fixture.nativeElement.innerHTML;
|
||||
@ -1893,14 +1903,13 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
expect(html).toContain('bindings={');
|
||||
expect(html).toContain('"ng-reflect-ng-if": "true"');
|
||||
|
||||
fixture.componentInstance.ctxBoolProp = null !;
|
||||
fixture.componentInstance.ctxBoolProp = null!;
|
||||
fixture.detectChanges();
|
||||
|
||||
html = fixture.nativeElement.innerHTML;
|
||||
expect(html).toContain('bindings={');
|
||||
expect(html).toContain('"ng-reflect-ng-if": null');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('property decorators', () => {
|
||||
@ -2079,7 +2088,6 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
});
|
||||
|
||||
describe('attributes', () => {
|
||||
|
||||
it('should support attributes with namespace', () => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp, SomeCmp]});
|
||||
const template = '<svg:use xlink:href="#id" />';
|
||||
@ -2142,7 +2150,9 @@ class ComponentWithCustomInterpolationB {
|
||||
@Injectable()
|
||||
class MyService {
|
||||
greeting: string;
|
||||
constructor() { this.greeting = 'hello'; }
|
||||
constructor() {
|
||||
this.greeting = 'hello';
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'simple-imp-cmp', template: ''})
|
||||
@ -2165,14 +2175,16 @@ class DynamicViewport {
|
||||
|
||||
this.injector = Injector.create([{provide: MyService, useValue: myService}], vc.injector);
|
||||
this.componentFactory =
|
||||
componentFactoryResolver.resolveComponentFactory(ChildCompUsingService) !;
|
||||
componentFactoryResolver.resolveComponentFactory(ChildCompUsingService)!;
|
||||
}
|
||||
|
||||
create(): ComponentRef<ChildCompUsingService> {
|
||||
return this.vc.createComponent(this.componentFactory, this.vc.length, this.injector);
|
||||
}
|
||||
|
||||
insert(viewRef: ViewRef, index?: number): ViewRef { return this.vc.insert(viewRef, index); }
|
||||
insert(viewRef: ViewRef, index?: number): ViewRef {
|
||||
return this.vc.insert(viewRef, index);
|
||||
}
|
||||
|
||||
move(viewRef: ViewRef, currentIndex: number): ViewRef {
|
||||
return this.vc.move(viewRef, currentIndex);
|
||||
@ -2182,25 +2194,29 @@ class DynamicViewport {
|
||||
@Directive({selector: '[my-dir]', inputs: ['dirProp: elprop'], exportAs: 'mydir'})
|
||||
class MyDir {
|
||||
dirProp: string;
|
||||
constructor() { this.dirProp = ''; }
|
||||
constructor() {
|
||||
this.dirProp = '';
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[my-dir2]', inputs: ['dirProp2: elprop'], exportAs: 'mydir2'})
|
||||
class MyDir2 {
|
||||
dirProp2: string;
|
||||
constructor() { this.dirProp2 = ''; }
|
||||
constructor() {
|
||||
this.dirProp2 = '';
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[title]', inputs: ['title']})
|
||||
class DirectiveWithTitle {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
title !: string;
|
||||
title!: string;
|
||||
}
|
||||
|
||||
@Directive({selector: '[title]', inputs: ['title'], host: {'[title]': 'title'}})
|
||||
class DirectiveWithTitleAndHostProperty {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
title !: string;
|
||||
title!: string;
|
||||
}
|
||||
|
||||
@Component({selector: 'event-cmp', template: '<div (click)="noop()"></div>'})
|
||||
@ -2220,7 +2236,9 @@ class PushCmp {
|
||||
numberOfChecks: number;
|
||||
prop: any;
|
||||
|
||||
constructor() { this.numberOfChecks = 0; }
|
||||
constructor() {
|
||||
this.numberOfChecks = 0;
|
||||
}
|
||||
|
||||
noop() {}
|
||||
|
||||
@ -2251,7 +2269,9 @@ class PushCmpWithRef {
|
||||
return 'fixed';
|
||||
}
|
||||
|
||||
propagate() { this.ref.markForCheck(); }
|
||||
propagate() {
|
||||
this.ref.markForCheck();
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -2272,11 +2292,13 @@ class PushCmpWithHostEvent {
|
||||
class PushCmpWithAsyncPipe {
|
||||
numberOfChecks: number = 0;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
resolve !: (result: any) => void;
|
||||
resolve!: (result: any) => void;
|
||||
promise: Promise<any>;
|
||||
|
||||
constructor() {
|
||||
this.promise = new Promise((resolve) => { this.resolve = resolve; });
|
||||
this.promise = new Promise((resolve) => {
|
||||
this.resolve = resolve;
|
||||
});
|
||||
}
|
||||
|
||||
get field() {
|
||||
@ -2291,7 +2313,11 @@ class MyComp {
|
||||
ctxNumProp: number;
|
||||
ctxBoolProp: boolean;
|
||||
ctxArrProp: number[];
|
||||
toStringThrow = {toString: function() { throw 'boom'; }};
|
||||
toStringThrow = {
|
||||
toString: function() {
|
||||
throw 'boom';
|
||||
}
|
||||
};
|
||||
|
||||
constructor() {
|
||||
this.ctxProp = 'initial value';
|
||||
@ -2300,7 +2326,9 @@ class MyComp {
|
||||
this.ctxArrProp = [0, 1, 2];
|
||||
}
|
||||
|
||||
throwError() { throw 'boom'; }
|
||||
throwError() {
|
||||
throw 'boom';
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -2326,7 +2354,9 @@ class ChildCompNoTemplate {
|
||||
@Component({selector: 'child-cmp-svc', template: '{{ctxProp}}'})
|
||||
class ChildCompUsingService {
|
||||
ctxProp: string;
|
||||
constructor(service: MyService) { this.ctxProp = service.greeting; }
|
||||
constructor(service: MyService) {
|
||||
this.ctxProp = service.greeting;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: 'some-directive'})
|
||||
@ -2341,7 +2371,9 @@ class SomeDirectiveMissingAnnotation {}
|
||||
})
|
||||
class CompWithHost {
|
||||
myHost: SomeDirective;
|
||||
constructor(@Host() someComp: SomeDirective) { this.myHost = someComp; }
|
||||
constructor(@Host() someComp: SomeDirective) {
|
||||
this.myHost = someComp;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: '[child-cmp2]', viewProviders: [MyService]})
|
||||
@ -2384,7 +2416,9 @@ class NoContext {
|
||||
@Pipe({name: 'double'})
|
||||
class DoublePipe implements PipeTransform, OnDestroy {
|
||||
ngOnDestroy() {}
|
||||
transform(value: any) { return `${value}${value}`; }
|
||||
transform(value: any) {
|
||||
return `${value}${value}`;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[emitter]', outputs: ['event']})
|
||||
@ -2397,7 +2431,9 @@ class DirectiveEmittingEvent {
|
||||
this.event = new EventEmitter();
|
||||
}
|
||||
|
||||
fireEvent(msg: string) { this.event.emit(msg); }
|
||||
fireEvent(msg: string) {
|
||||
this.event.emit(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[update-host-attributes]', host: {'role': 'button'}})
|
||||
@ -2408,16 +2444,22 @@ class DirectiveUpdatingHostAttributes {
|
||||
class DirectiveUpdatingHostProperties {
|
||||
id: string;
|
||||
|
||||
constructor() { this.id = 'one'; }
|
||||
constructor() {
|
||||
this.id = 'one';
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[listener]', host: {'(event)': 'onEvent($event)'}})
|
||||
class DirectiveListeningEvent {
|
||||
msg: string;
|
||||
|
||||
constructor() { this.msg = ''; }
|
||||
constructor() {
|
||||
this.msg = '';
|
||||
}
|
||||
|
||||
onEvent(msg: string) { this.msg = msg; }
|
||||
onEvent(msg: string) {
|
||||
this.msg = msg;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({
|
||||
@ -2431,17 +2473,27 @@ class DirectiveListeningEvent {
|
||||
})
|
||||
class DirectiveListeningDomEvent {
|
||||
eventTypes: string[] = [];
|
||||
onEvent(eventType: string) { this.eventTypes.push(eventType); }
|
||||
onWindowEvent(eventType: string) { this.eventTypes.push('window_' + eventType); }
|
||||
onDocumentEvent(eventType: string) { this.eventTypes.push('document_' + eventType); }
|
||||
onBodyEvent(eventType: string) { this.eventTypes.push('body_' + eventType); }
|
||||
onEvent(eventType: string) {
|
||||
this.eventTypes.push(eventType);
|
||||
}
|
||||
onWindowEvent(eventType: string) {
|
||||
this.eventTypes.push('window_' + eventType);
|
||||
}
|
||||
onDocumentEvent(eventType: string) {
|
||||
this.eventTypes.push('document_' + eventType);
|
||||
}
|
||||
onBodyEvent(eventType: string) {
|
||||
this.eventTypes.push('body_' + eventType);
|
||||
}
|
||||
}
|
||||
|
||||
let globalCounter = 0;
|
||||
@Directive({selector: '[listenerother]', host: {'(window:domEvent)': 'onEvent($event.type)'}})
|
||||
class DirectiveListeningDomEventOther {
|
||||
eventType: string;
|
||||
constructor() { this.eventType = ''; }
|
||||
constructor() {
|
||||
this.eventType = '';
|
||||
}
|
||||
onEvent(eventType: string) {
|
||||
globalCounter++;
|
||||
this.eventType = 'other_' + eventType;
|
||||
@ -2450,18 +2502,22 @@ class DirectiveListeningDomEventOther {
|
||||
|
||||
@Directive({selector: '[listenerprevent]', host: {'(click)': 'onEvent($event)'}})
|
||||
class DirectiveListeningDomEventPrevent {
|
||||
onEvent(event: any) { return false; }
|
||||
onEvent(event: any) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[listenernoprevent]', host: {'(click)': 'onEvent($event)'}})
|
||||
class DirectiveListeningDomEventNoPrevent {
|
||||
onEvent(event: any) { return true; }
|
||||
onEvent(event: any) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[id]', inputs: ['id']})
|
||||
class IdDir {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
id !: string;
|
||||
id!: string;
|
||||
}
|
||||
|
||||
@Directive({selector: '[customEvent]'})
|
||||
@ -2497,7 +2553,9 @@ class PrivateImpl extends PublicApi {
|
||||
|
||||
@Directive({selector: '[needs-public-api]'})
|
||||
class NeedsPublicApi {
|
||||
constructor(@Host() api: PublicApi) { expect(api instanceof PrivateImpl).toBe(true); }
|
||||
constructor(@Host() api: PublicApi) {
|
||||
expect(api instanceof PrivateImpl).toBe(true);
|
||||
}
|
||||
}
|
||||
|
||||
class ToolbarContext {
|
||||
@ -2507,7 +2565,9 @@ class ToolbarContext {
|
||||
@Directive({selector: '[toolbarpart]'})
|
||||
class ToolbarPart {
|
||||
templateRef: TemplateRef<ToolbarContext>;
|
||||
constructor(templateRef: TemplateRef<ToolbarContext>) { this.templateRef = templateRef; }
|
||||
constructor(templateRef: TemplateRef<ToolbarContext>) {
|
||||
this.templateRef = templateRef;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[toolbarVc]', inputs: ['toolbarVc']})
|
||||
@ -2525,7 +2585,7 @@ class ToolbarViewContainer {
|
||||
})
|
||||
class ToolbarComponent {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChildren(ToolbarPart) query !: QueryList<ToolbarPart>;
|
||||
@ContentChildren(ToolbarPart) query!: QueryList<ToolbarPart>;
|
||||
ctxProp: string = 'hello world';
|
||||
|
||||
constructor() {}
|
||||
@ -2536,7 +2596,9 @@ class DirectiveWithTwoWayBinding {
|
||||
controlChange = new EventEmitter();
|
||||
control: any = null;
|
||||
|
||||
triggerChange(value: any) { this.controlChange.emit(value); }
|
||||
triggerChange(value: any) {
|
||||
this.controlChange.emit(value);
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
@ -2585,7 +2647,9 @@ class DirectiveProvidingInjectableInHostAndView {
|
||||
class DirectiveConsumingInjectable {
|
||||
injectable: any;
|
||||
|
||||
constructor(@Host() @Inject(InjectableService) injectable: any) { this.injectable = injectable; }
|
||||
constructor(@Host() @Inject(InjectableService) injectable: any) {
|
||||
this.injectable = injectable;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2620,12 +2684,14 @@ class EventBus {
|
||||
|
||||
@Directive({
|
||||
selector: 'grand-parent-providing-event-bus',
|
||||
providers: [{provide: EventBus, useValue: new EventBus(null !, 'grandparent')}]
|
||||
providers: [{provide: EventBus, useValue: new EventBus(null!, 'grandparent')}]
|
||||
})
|
||||
class GrandParentProvidingEventBus {
|
||||
bus: EventBus;
|
||||
|
||||
constructor(bus: EventBus) { this.bus = bus; }
|
||||
constructor(bus: EventBus) {
|
||||
this.bus = bus;
|
||||
}
|
||||
}
|
||||
|
||||
function createParentBus(peb: EventBus) {
|
||||
@ -2651,7 +2717,9 @@ class ParentProvidingEventBus {
|
||||
class ChildConsumingEventBus {
|
||||
bus: EventBus;
|
||||
|
||||
constructor(@SkipSelf() bus: EventBus) { this.bus = bus; }
|
||||
constructor(@SkipSelf() bus: EventBus) {
|
||||
this.bus = bus;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[someImpvp]', inputs: ['someImpvp']})
|
||||
@ -2695,17 +2763,23 @@ class ComponentWithoutView {
|
||||
|
||||
@Directive({selector: '[no-duplicate]'})
|
||||
class DuplicateDir {
|
||||
constructor(elRef: ElementRef) { elRef.nativeElement.textContent += 'noduplicate'; }
|
||||
constructor(elRef: ElementRef) {
|
||||
elRef.nativeElement.textContent += 'noduplicate';
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[no-duplicate]'})
|
||||
class OtherDuplicateDir {
|
||||
constructor(elRef: ElementRef) { elRef.nativeElement.textContent += 'othernoduplicate'; }
|
||||
constructor(elRef: ElementRef) {
|
||||
elRef.nativeElement.textContent += 'othernoduplicate';
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: 'directive-throwing-error'})
|
||||
class DirectiveThrowingAnError {
|
||||
constructor() { throw new Error('BOOM'); }
|
||||
constructor() {
|
||||
throw new Error('BOOM');
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -2721,15 +2795,19 @@ class DirectiveWithPropDecorators {
|
||||
target: any;
|
||||
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input('elProp') dirProp !: string;
|
||||
@Input('elProp') dirProp!: string;
|
||||
@Output('elEvent') event = new EventEmitter();
|
||||
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@HostBinding('attr.my-attr') myAttr !: string;
|
||||
@HostBinding('attr.my-attr') myAttr!: string;
|
||||
@HostListener('click', ['$event.target'])
|
||||
onClick(target: any) { this.target = target; }
|
||||
onClick(target: any) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
fireEvent(msg: any) { this.event.emit(msg); }
|
||||
fireEvent(msg: any) {
|
||||
this.event.emit(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'some-cmp'})
|
||||
|
@ -10,7 +10,7 @@ import {ResourceLoader} from '@angular/compiler';
|
||||
import {CompileMetadataResolver} from '@angular/compiler/src/metadata_resolver';
|
||||
import {MockResourceLoader} from '@angular/compiler/testing/src/resource_loader_mock';
|
||||
import {Component, Directive, Injectable, NgModule, OnDestroy, Pipe} from '@angular/core';
|
||||
import {TestBed, async, getTestBed} from '@angular/core/testing';
|
||||
import {async, getTestBed, TestBed} from '@angular/core/testing';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
import {obsoleteInIvy} from '@angular/private/testing';
|
||||
|
||||
@ -32,7 +32,7 @@ import {obsoleteInIvy} from '@angular/private/testing';
|
||||
}
|
||||
|
||||
function expectInstanceCreated(type: any) {
|
||||
const instance = instances.get(type) !;
|
||||
const instance = instances.get(type)!;
|
||||
expect(instance).toBeDefined();
|
||||
expect(instance.dep instanceof SomeDep).toBe(true);
|
||||
}
|
||||
@ -46,7 +46,9 @@ import {obsoleteInIvy} from '@angular/private/testing';
|
||||
class SomeDirective extends Base {}
|
||||
|
||||
class SomePipe extends Base {
|
||||
transform(value: any) { return value; }
|
||||
transform(value: any) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
class SomeService extends Base {}
|
||||
@ -138,7 +140,9 @@ import {obsoleteInIvy} from '@angular/private/testing';
|
||||
createSummaries().then(s => summaries = s);
|
||||
}));
|
||||
|
||||
afterEach(() => { resetTestEnvironmentWithSummaries(); });
|
||||
afterEach(() => {
|
||||
resetTestEnvironmentWithSummaries();
|
||||
});
|
||||
|
||||
it('should use directive metadata from summaries', () => {
|
||||
resetTestEnvironmentWithSummaries(summaries);
|
||||
|
@ -15,15 +15,20 @@ import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
import {modifiedInIvy} from '@angular/private/testing';
|
||||
|
||||
if (ivyEnabled) {
|
||||
describe('ivy', () => { declareTests(); });
|
||||
describe('ivy', () => {
|
||||
declareTests();
|
||||
});
|
||||
} else {
|
||||
describe('jit', () => { declareTests({useJit: true}); });
|
||||
describe('no jit', () => { declareTests({useJit: false}); });
|
||||
describe('jit', () => {
|
||||
declareTests({useJit: true});
|
||||
});
|
||||
describe('no jit', () => {
|
||||
declareTests({useJit: false});
|
||||
});
|
||||
}
|
||||
|
||||
function declareTests(config?: {useJit: boolean}) {
|
||||
describe('<ng-container>', function() {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureCompiler({...config});
|
||||
TestBed.configureTestingModule({
|
||||
@ -140,7 +145,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
|
||||
fixture.detectChanges();
|
||||
const q = fixture.debugElement.children[0].references !['q'];
|
||||
const q = fixture.debugElement.children[0].references!['q'];
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(q.textDirChildren.length).toEqual(1);
|
||||
@ -153,7 +158,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
const fixture = TestBed.createComponent(MyComp);
|
||||
|
||||
fixture.detectChanges();
|
||||
const q = fixture.debugElement.children[0].references !['q'];
|
||||
const q = fixture.debugElement.children[0].references!['q'];
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(q.textDirChildren.length).toEqual(1);
|
||||
@ -170,21 +175,25 @@ class TextDirective {
|
||||
@Component({selector: 'needs-content-children', template: ''})
|
||||
class NeedsContentChildren implements AfterContentInit {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChildren(TextDirective) textDirChildren !: QueryList<TextDirective>;
|
||||
@ContentChildren(TextDirective) textDirChildren!: QueryList<TextDirective>;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
numberOfChildrenAfterContentInit !: number;
|
||||
numberOfChildrenAfterContentInit!: number;
|
||||
|
||||
ngAfterContentInit() { this.numberOfChildrenAfterContentInit = this.textDirChildren.length; }
|
||||
ngAfterContentInit() {
|
||||
this.numberOfChildrenAfterContentInit = this.textDirChildren.length;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-view-children', template: '<div text></div>'})
|
||||
class NeedsViewChildren implements AfterViewInit {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ViewChildren(TextDirective) textDirChildren !: QueryList<TextDirective>;
|
||||
@ViewChildren(TextDirective) textDirChildren!: QueryList<TextDirective>;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
numberOfChildrenAfterViewInit !: number;
|
||||
numberOfChildrenAfterViewInit!: number;
|
||||
|
||||
ngAfterViewInit() { this.numberOfChildrenAfterViewInit = this.textDirChildren.length; }
|
||||
ngAfterViewInit() {
|
||||
this.numberOfChildrenAfterViewInit = this.textDirChildren.length;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'simple', template: 'SIMPLE(<ng-content></ng-content>)'})
|
||||
|
@ -6,13 +6,13 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ANALYZE_FOR_ENTRY_COMPONENTS, CUSTOM_ELEMENTS_SCHEMA, ChangeDetectorRef, Compiler, Component, ComponentFactoryResolver, Directive, HostBinding, Inject, Injectable, InjectionToken, Injector, Input, NgModule, NgModuleRef, Optional, Pipe, Provider, Self, Type, forwardRef, getModuleFactory, ɵivyEnabled as ivyEnabled, ɵɵdefineNgModule as defineNgModule} from '@angular/core';
|
||||
import {ANALYZE_FOR_ENTRY_COMPONENTS, ChangeDetectorRef, Compiler, Component, ComponentFactoryResolver, CUSTOM_ELEMENTS_SCHEMA, Directive, forwardRef, getModuleFactory, HostBinding, Inject, Injectable, InjectionToken, Injector, Input, NgModule, NgModuleRef, Optional, Pipe, Provider, Self, Type, ɵivyEnabled as ivyEnabled, ɵɵdefineNgModule as defineNgModule} from '@angular/core';
|
||||
import {Console} from '@angular/core/src/console';
|
||||
import {ɵɵInjectableDef, ɵɵdefineInjectable} from '@angular/core/src/di/interface/defs';
|
||||
import {ɵɵdefineInjectable, ɵɵInjectableDef} from '@angular/core/src/di/interface/defs';
|
||||
import {getNgModuleDef} from '@angular/core/src/render3/definition';
|
||||
import {NgModuleData} from '@angular/core/src/view/types';
|
||||
import {tokenKey} from '@angular/core/src/view/util';
|
||||
import {ComponentFixture, TestBed, inject} from '@angular/core/testing';
|
||||
import {ComponentFixture, inject, TestBed} from '@angular/core/testing';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
import {modifiedInIvy, obsoleteInIvy, onlyInIvy} from '@angular/private/testing';
|
||||
|
||||
@ -23,7 +23,9 @@ import {stringify} from '../../src/util/stringify';
|
||||
class Engine {}
|
||||
|
||||
class BrokenEngine {
|
||||
constructor() { throw new Error('Broken Engine'); }
|
||||
constructor() {
|
||||
throw new Error('Broken Engine');
|
||||
}
|
||||
}
|
||||
|
||||
class DashboardSoftware {}
|
||||
@ -53,7 +55,9 @@ class CarWithDashboard {
|
||||
|
||||
@Injectable()
|
||||
class SportsCar extends Car {
|
||||
constructor(engine: Engine) { super(engine); }
|
||||
constructor(engine: Engine) {
|
||||
super(engine);
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
@ -79,13 +83,14 @@ class SomeComp {
|
||||
@Directive({selector: '[someDir]'})
|
||||
class SomeDirective {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@HostBinding('title') @Input()
|
||||
someDir !: string;
|
||||
@HostBinding('title') @Input() someDir!: string;
|
||||
}
|
||||
|
||||
@Pipe({name: 'somePipe'})
|
||||
class SomePipe {
|
||||
transform(value: string): any { return `transformed ${value}`; }
|
||||
transform(value: string): any {
|
||||
return `transformed ${value}`;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'comp', template: `<div [someDir]="'someValue' | somePipe"></div>`})
|
||||
@ -94,10 +99,16 @@ class CompUsingModuleDirectiveAndPipe {
|
||||
|
||||
{
|
||||
if (ivyEnabled) {
|
||||
describe('ivy', () => { declareTests(); });
|
||||
describe('ivy', () => {
|
||||
declareTests();
|
||||
});
|
||||
} else {
|
||||
describe('jit', () => { declareTests({useJit: true}); });
|
||||
describe('no jit', () => { declareTests({useJit: false}); });
|
||||
describe('jit', () => {
|
||||
declareTests({useJit: true});
|
||||
});
|
||||
describe('no jit', () => {
|
||||
declareTests({useJit: false});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,7 +117,9 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
let compiler: Compiler;
|
||||
let injector: Injector;
|
||||
|
||||
beforeEach(() => { TestBed.configureCompiler(config || {}); });
|
||||
beforeEach(() => {
|
||||
TestBed.configureCompiler(config || {});
|
||||
});
|
||||
|
||||
beforeEach(inject([Compiler, Injector], (_compiler: Compiler, _injector: Injector) => {
|
||||
compiler = _compiler;
|
||||
@ -117,8 +130,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
return compiler.compileModuleSync(moduleType);
|
||||
}
|
||||
|
||||
function createModule<T>(
|
||||
moduleType: Type<T>, parentInjector?: Injector | null): NgModuleRef<T> {
|
||||
function createModule<T>(moduleType: Type<T>, parentInjector?: Injector|null): NgModuleRef<T> {
|
||||
// Read the `ngModuleDef` to cause it to be compiled and any errors thrown.
|
||||
getNgModuleDef(moduleType);
|
||||
return createModuleFactory(moduleType).create(parentInjector || null);
|
||||
@ -136,11 +148,11 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
|
||||
const ngModule = createModule(moduleType, injector);
|
||||
|
||||
const cf = ngModule.componentFactoryResolver.resolveComponentFactory(compType) !;
|
||||
const cf = ngModule.componentFactoryResolver.resolveComponentFactory(compType)!;
|
||||
|
||||
const comp = cf.create(Injector.NULL);
|
||||
|
||||
return new ComponentFixture(comp, null !, false);
|
||||
return new ComponentFixture(comp, null!, false);
|
||||
}
|
||||
|
||||
describe('errors', () => {
|
||||
@ -150,8 +162,8 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
}
|
||||
|
||||
expect(() => createModule(SomeModule))
|
||||
.toThrowError(
|
||||
`Can't export directive ${stringify(SomeDirective)} from ${stringify(SomeModule)} as it was neither declared nor imported!`);
|
||||
.toThrowError(`Can't export directive ${stringify(SomeDirective)} from ${
|
||||
stringify(SomeModule)} as it was neither declared nor imported!`);
|
||||
});
|
||||
|
||||
it('should error when exporting a pipe that was neither declared nor imported', () => {
|
||||
@ -160,8 +172,8 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
}
|
||||
|
||||
expect(() => createModule(SomeModule))
|
||||
.toThrowError(
|
||||
`Can't export pipe ${stringify(SomePipe)} from ${stringify(SomeModule)} as it was neither declared nor imported!`);
|
||||
.toThrowError(`Can't export pipe ${stringify(SomePipe)} from ${
|
||||
stringify(SomeModule)} as it was neither declared nor imported!`);
|
||||
});
|
||||
|
||||
it('should error if a directive is declared in more than 1 module', () => {
|
||||
@ -177,9 +189,14 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
|
||||
expect(() => createModule(Module2))
|
||||
.toThrowError(
|
||||
`Type ${stringify(SomeDirective)} is part of the declarations of 2 modules: ${stringify(Module1)} and ${stringify(Module2)}! ` +
|
||||
`Please consider moving ${stringify(SomeDirective)} to a higher module that imports ${stringify(Module1)} and ${stringify(Module2)}. ` +
|
||||
`You can also create a new NgModule that exports and includes ${stringify(SomeDirective)} then import that NgModule in ${stringify(Module1)} and ${stringify(Module2)}.`);
|
||||
`Type ${stringify(SomeDirective)} is part of the declarations of 2 modules: ${
|
||||
stringify(Module1)} and ${stringify(Module2)}! ` +
|
||||
`Please consider moving ${
|
||||
stringify(SomeDirective)} to a higher module that imports ${
|
||||
stringify(Module1)} and ${stringify(Module2)}. ` +
|
||||
`You can also create a new NgModule that exports and includes ${
|
||||
stringify(SomeDirective)} then import that NgModule in ${
|
||||
stringify(Module1)} and ${stringify(Module2)}.`);
|
||||
});
|
||||
|
||||
it('should error if a directive is declared in more than 1 module also if the module declaring it is imported',
|
||||
@ -194,9 +211,14 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
|
||||
expect(() => createModule(Module2))
|
||||
.toThrowError(
|
||||
`Type ${stringify(SomeDirective)} is part of the declarations of 2 modules: ${stringify(Module1)} and ${stringify(Module2)}! ` +
|
||||
`Please consider moving ${stringify(SomeDirective)} to a higher module that imports ${stringify(Module1)} and ${stringify(Module2)}. ` +
|
||||
`You can also create a new NgModule that exports and includes ${stringify(SomeDirective)} then import that NgModule in ${stringify(Module1)} and ${stringify(Module2)}.`);
|
||||
`Type ${stringify(SomeDirective)} is part of the declarations of 2 modules: ${
|
||||
stringify(Module1)} and ${stringify(Module2)}! ` +
|
||||
`Please consider moving ${
|
||||
stringify(SomeDirective)} to a higher module that imports ${
|
||||
stringify(Module1)} and ${stringify(Module2)}. ` +
|
||||
`You can also create a new NgModule that exports and includes ${
|
||||
stringify(SomeDirective)} then import that NgModule in ${
|
||||
stringify(Module1)} and ${stringify(Module2)}.`);
|
||||
});
|
||||
|
||||
it('should error if a pipe is declared in more than 1 module', () => {
|
||||
@ -212,9 +234,13 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
|
||||
expect(() => createModule(Module2))
|
||||
.toThrowError(
|
||||
`Type ${stringify(SomePipe)} is part of the declarations of 2 modules: ${stringify(Module1)} and ${stringify(Module2)}! ` +
|
||||
`Please consider moving ${stringify(SomePipe)} to a higher module that imports ${stringify(Module1)} and ${stringify(Module2)}. ` +
|
||||
`You can also create a new NgModule that exports and includes ${stringify(SomePipe)} then import that NgModule in ${stringify(Module1)} and ${stringify(Module2)}.`);
|
||||
`Type ${stringify(SomePipe)} is part of the declarations of 2 modules: ${
|
||||
stringify(Module1)} and ${stringify(Module2)}! ` +
|
||||
`Please consider moving ${stringify(SomePipe)} to a higher module that imports ${
|
||||
stringify(Module1)} and ${stringify(Module2)}. ` +
|
||||
`You can also create a new NgModule that exports and includes ${
|
||||
stringify(SomePipe)} then import that NgModule in ${stringify(Module1)} and ${
|
||||
stringify(Module2)}.`);
|
||||
});
|
||||
|
||||
it('should error if a pipe is declared in more than 1 module also if the module declaring it is imported',
|
||||
@ -229,11 +255,14 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
|
||||
expect(() => createModule(Module2))
|
||||
.toThrowError(
|
||||
`Type ${stringify(SomePipe)} is part of the declarations of 2 modules: ${stringify(Module1)} and ${stringify(Module2)}! ` +
|
||||
`Please consider moving ${stringify(SomePipe)} to a higher module that imports ${stringify(Module1)} and ${stringify(Module2)}. ` +
|
||||
`You can also create a new NgModule that exports and includes ${stringify(SomePipe)} then import that NgModule in ${stringify(Module1)} and ${stringify(Module2)}.`);
|
||||
`Type ${stringify(SomePipe)} is part of the declarations of 2 modules: ${
|
||||
stringify(Module1)} and ${stringify(Module2)}! ` +
|
||||
`Please consider moving ${stringify(SomePipe)} to a higher module that imports ${
|
||||
stringify(Module1)} and ${stringify(Module2)}. ` +
|
||||
`You can also create a new NgModule that exports and includes ${
|
||||
stringify(SomePipe)} then import that NgModule in ${
|
||||
stringify(Module1)} and ${stringify(Module2)}.`);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('schemas', () => {
|
||||
@ -361,7 +390,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
}
|
||||
|
||||
const ngModule = createModule(SomeModule);
|
||||
expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp) !.componentType)
|
||||
expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp)!.componentType)
|
||||
.toBe(SomeComp);
|
||||
expect(ngModule.injector.get(ComponentFactoryResolver)
|
||||
.resolveComponentFactory(SomeComp)
|
||||
@ -411,7 +440,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
}
|
||||
|
||||
const ngModule = createModule(SomeModule);
|
||||
expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp) !.componentType)
|
||||
expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp)!.componentType)
|
||||
.toBe(SomeComp);
|
||||
expect(ngModule.injector.get(ComponentFactoryResolver)
|
||||
.resolveComponentFactory(SomeComp)
|
||||
@ -429,7 +458,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
}
|
||||
|
||||
const ngModule = createModule(SomeModule);
|
||||
expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp) !.componentType)
|
||||
expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp)!.componentType)
|
||||
.toBe(SomeComp);
|
||||
expect(ngModule.injector.get(ComponentFactoryResolver)
|
||||
.resolveComponentFactory(SomeComp)
|
||||
@ -447,14 +476,13 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
}
|
||||
|
||||
const ngModule = createModule(SomeModule);
|
||||
expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp) !.componentType)
|
||||
expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp)!.componentType)
|
||||
.toBe(SomeComp);
|
||||
expect(ngModule.injector.get(ComponentFactoryResolver)
|
||||
.resolveComponentFactory(SomeComp)
|
||||
.componentType)
|
||||
.toBe(SomeComp);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('bootstrap components', () => {
|
||||
@ -464,7 +492,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
}
|
||||
|
||||
const ngModule = createModule(SomeModule);
|
||||
expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp) !.componentType)
|
||||
expect(ngModule.componentFactoryResolver.resolveComponentFactory(SomeComp)!.componentType)
|
||||
.toBe(SomeComp);
|
||||
});
|
||||
|
||||
@ -477,7 +505,6 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
expect(ngModule._bootstrapComponents.length).toBe(1);
|
||||
expect(ngModule._bootstrapComponents[0]).toBe(SomeComp);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('directives and pipes', () => {
|
||||
@ -542,7 +569,6 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
});
|
||||
|
||||
describe('import/export', () => {
|
||||
|
||||
it('should support exported directives and pipes', () => {
|
||||
@NgModule({declarations: [SomeDirective, SomePipe], exports: [SomeDirective, SomePipe]})
|
||||
class SomeImportedModule {
|
||||
@ -677,7 +703,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
let moduleType: any = null;
|
||||
|
||||
|
||||
function createInjector(providers: Provider[], parent?: Injector | null): Injector {
|
||||
function createInjector(providers: Provider[], parent?: Injector|null): Injector {
|
||||
@NgModule({providers: providers})
|
||||
class SomeModule {
|
||||
}
|
||||
@ -687,8 +713,9 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
return createModule(SomeModule, parent).injector;
|
||||
}
|
||||
|
||||
it('should provide the module',
|
||||
() => { expect(createInjector([]).get(moduleType)).toBeAnInstanceOf(moduleType); });
|
||||
it('should provide the module', () => {
|
||||
expect(createInjector([]).get(moduleType)).toBeAnInstanceOf(moduleType);
|
||||
});
|
||||
|
||||
it('should instantiate a class without dependencies', () => {
|
||||
const injector = createInjector([Engine]);
|
||||
@ -741,7 +768,9 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
});
|
||||
|
||||
it('should provide to a factory', () => {
|
||||
function sportsCarFactory(e: Engine) { return new SportsCar(e); }
|
||||
function sportsCarFactory(e: Engine) {
|
||||
return new SportsCar(e);
|
||||
}
|
||||
|
||||
const injector =
|
||||
createInjector([Engine, {provide: Car, useFactory: sportsCarFactory, deps: [Engine]}]);
|
||||
@ -759,8 +788,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
|
||||
it('should provide to an alias', () => {
|
||||
const injector = createInjector([
|
||||
Engine, {provide: SportsCar, useClass: SportsCar},
|
||||
{provide: Car, useExisting: SportsCar}
|
||||
Engine, {provide: SportsCar, useClass: SportsCar}, {provide: Car, useExisting: SportsCar}
|
||||
]);
|
||||
|
||||
const car = injector.get(Car);
|
||||
@ -879,7 +907,6 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
});
|
||||
|
||||
describe('injecting lazy providers into an eager provider via Injector.get', () => {
|
||||
|
||||
it('should inject providers that were declared before it', () => {
|
||||
@NgModule({
|
||||
providers: [
|
||||
@ -920,7 +947,6 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
});
|
||||
|
||||
describe('injecting eager providers into an eager provider via Injector.get', () => {
|
||||
|
||||
it('should inject providers that were declared before it', () => {
|
||||
@NgModule({
|
||||
providers: [
|
||||
@ -1039,7 +1065,6 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
expect(engineFromParent).not.toBe(engineFromChild);
|
||||
expect(engineFromChild).toBeAnInstanceOf(TurboEngine);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('depedency resolution', () => {
|
||||
@ -1075,7 +1100,9 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
|
||||
@NgModule()
|
||||
class ImportedModule {
|
||||
constructor() { created = true; }
|
||||
constructor() {
|
||||
created = true;
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({imports: [ImportedModule]})
|
||||
@ -1105,7 +1132,9 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
let destroyed = false;
|
||||
|
||||
class SomeInjectable {
|
||||
ngOnDestroy() { destroyed = true; }
|
||||
ngOnDestroy() {
|
||||
destroyed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({providers: [SomeInjectable]})
|
||||
@ -1125,8 +1154,12 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
let destroyed = false;
|
||||
|
||||
class SomeInjectable {
|
||||
constructor() { created = true; }
|
||||
ngOnDestroy() { destroyed = true; }
|
||||
constructor() {
|
||||
created = true;
|
||||
}
|
||||
ngOnDestroy() {
|
||||
destroyed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({providers: [SomeInjectable]})
|
||||
@ -1174,9 +1207,8 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
{ngModule: ImportedModule, providers: [{provide: 'token1', useValue: 'imported'}]}
|
||||
]
|
||||
imports:
|
||||
[{ngModule: ImportedModule, providers: [{provide: 'token1', useValue: 'imported'}]}]
|
||||
})
|
||||
class SomeModule {
|
||||
}
|
||||
@ -1210,9 +1242,8 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
|
||||
@NgModule({
|
||||
providers: [{provide: 'token1', useValue: 'direct'}],
|
||||
imports: [
|
||||
{ngModule: ImportedModule, providers: [{provide: 'token1', useValue: 'imported'}]}
|
||||
]
|
||||
imports:
|
||||
[{ngModule: ImportedModule, providers: [{provide: 'token1', useValue: 'imported'}]}]
|
||||
})
|
||||
class SomeModule {
|
||||
}
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {CommonModule, ɵgetDOM as getDOM} from '@angular/common';
|
||||
import {Component, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, Injector, Input, NO_ERRORS_SCHEMA, NgModule, OnInit, TemplateRef, ViewChild, ViewContainerRef, ViewEncapsulation} from '@angular/core';
|
||||
import {Component, ComponentFactoryResolver, ComponentRef, Directive, ElementRef, Injector, Input, NgModule, NO_ERRORS_SCHEMA, OnInit, TemplateRef, ViewChild, ViewContainerRef, ViewEncapsulation} from '@angular/core';
|
||||
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {By} from '@angular/platform-browser/src/dom/debug/by';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
@ -50,9 +50,7 @@ describe('projection', () => {
|
||||
it('should support projecting text interpolation to a non bound element with other bound elements after it',
|
||||
() => {
|
||||
TestBed.overrideComponent(Simple, {
|
||||
set: {
|
||||
template: 'SIMPLE(<div><ng-content></ng-content></div><div [tabIndex]="0">EL</div>)'
|
||||
}
|
||||
set: {template: 'SIMPLE(<div><ng-content></ng-content></div><div [tabIndex]="0">EL</div>)'}
|
||||
});
|
||||
TestBed.overrideComponent(MainComp, {set: {template: '<simple>{{text}}</simple>'}});
|
||||
const main = TestBed.createComponent(MainComp);
|
||||
@ -244,8 +242,7 @@ describe('projection', () => {
|
||||
|
||||
it('should support nesting with content being direct child of a nested component', () => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations:
|
||||
[InnerComponent, InnerInnerComponent, OuterComponent, ManualViewportDirective]
|
||||
declarations: [InnerComponent, InnerInnerComponent, OuterComponent, ManualViewportDirective]
|
||||
});
|
||||
TestBed.overrideComponent(MainComp, {
|
||||
set: {
|
||||
@ -304,7 +301,7 @@ describe('projection', () => {
|
||||
`<ng-content></ng-content>(<ng-template [ngIf]="showing"><ng-content select="div"></ng-content></ng-template>)`
|
||||
})
|
||||
class Child {
|
||||
@Input() showing !: boolean;
|
||||
@Input() showing!: boolean;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -361,11 +358,13 @@ describe('projection', () => {
|
||||
});
|
||||
|
||||
it('should support moving non projected light dom around', () => {
|
||||
let sourceDirective: ManualViewportDirective = undefined !;
|
||||
let sourceDirective: ManualViewportDirective = undefined!;
|
||||
|
||||
@Directive({selector: '[manual]'})
|
||||
class ManualViewportDirective {
|
||||
constructor(public templateRef: TemplateRef<Object>) { sourceDirective = this; }
|
||||
constructor(public templateRef: TemplateRef<Object>) {
|
||||
sourceDirective = this;
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule(
|
||||
@ -594,7 +593,6 @@ describe('projection', () => {
|
||||
|
||||
it('should project nodes into nested templates when the main template doesn\'t have <ng-content>',
|
||||
() => {
|
||||
|
||||
@Component({
|
||||
selector: 'content-in-template',
|
||||
template:
|
||||
@ -622,7 +620,6 @@ describe('projection', () => {
|
||||
});
|
||||
|
||||
it('should project nodes into nested templates and the main template', () => {
|
||||
|
||||
@Component({
|
||||
selector: 'content-in-main-and-template',
|
||||
template:
|
||||
@ -720,7 +717,6 @@ describe('projection', () => {
|
||||
});
|
||||
|
||||
describe('projectable nodes', () => {
|
||||
|
||||
@Component({selector: 'test', template: ''})
|
||||
class TestComponent {
|
||||
constructor(public cfr: ComponentFactoryResolver) {}
|
||||
@ -739,14 +735,20 @@ describe('projection', () => {
|
||||
class InsertTplRef implements OnInit {
|
||||
constructor(private _vcRef: ViewContainerRef, private _tplRef: TemplateRef<{}>) {}
|
||||
|
||||
ngOnInit() { this._vcRef.createEmbeddedView(this._tplRef); }
|
||||
ngOnInit() {
|
||||
this._vcRef.createEmbeddedView(this._tplRef);
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[delayedInsert]', exportAs: 'delayedInsert'})
|
||||
class DelayedInsertTplRef {
|
||||
constructor(public vc: ViewContainerRef, public templateRef: TemplateRef<Object>) {}
|
||||
show() { this.vc.createEmbeddedView(this.templateRef); }
|
||||
hide() { this.vc.clear(); }
|
||||
show() {
|
||||
this.vc.createEmbeddedView(this.templateRef);
|
||||
}
|
||||
hide() {
|
||||
this.vc.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
@ -893,15 +895,23 @@ class SingleContentTagComponent {
|
||||
@Directive({selector: '[manual]'})
|
||||
class ManualViewportDirective {
|
||||
constructor(public vc: ViewContainerRef, public templateRef: TemplateRef<Object>) {}
|
||||
show() { this.vc.createEmbeddedView(this.templateRef); }
|
||||
hide() { this.vc.clear(); }
|
||||
show() {
|
||||
this.vc.createEmbeddedView(this.templateRef);
|
||||
}
|
||||
hide() {
|
||||
this.vc.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[project]'})
|
||||
class ProjectDirective {
|
||||
constructor(public vc: ViewContainerRef) {}
|
||||
show(templateRef: TemplateRef<Object>) { this.vc.createEmbeddedView(templateRef); }
|
||||
hide() { this.vc.clear(); }
|
||||
show(templateRef: TemplateRef<Object>) {
|
||||
this.vc.createEmbeddedView(templateRef);
|
||||
}
|
||||
hide() {
|
||||
this.vc.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1034,5 +1044,5 @@ class CmpA2 {
|
||||
}
|
||||
|
||||
function supportsNativeShadowDOM(): boolean {
|
||||
return typeof(<any>document.body).createShadowRoot === 'function';
|
||||
return typeof (<any>document.body).createShadowRoot === 'function';
|
||||
}
|
||||
|
@ -6,9 +6,9 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, Component, ContentChild, ContentChildren, Directive, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef, asNativeElements} from '@angular/core';
|
||||
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, asNativeElements, Component, ContentChild, ContentChildren, Directive, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core';
|
||||
import {ElementRef} from '@angular/core/src/core';
|
||||
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
||||
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
import {ivyEnabled, modifiedInIvy, onlyInIvy} from '@angular/private/testing';
|
||||
import {Subject} from 'rxjs';
|
||||
@ -16,7 +16,6 @@ import {Subject} from 'rxjs';
|
||||
import {stringify} from '../../src/util/stringify';
|
||||
|
||||
describe('Query API', () => {
|
||||
|
||||
beforeEach(() => TestBed.configureTestingModule({
|
||||
declarations: [
|
||||
MyComp0,
|
||||
@ -76,7 +75,7 @@ describe('Query API', () => {
|
||||
const template = '<needs-content-children #q><div text="foo"></div></needs-content-children>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
|
||||
const q = view.debugElement.children[0].references !['q'];
|
||||
const q = view.debugElement.children[0].references!['q'];
|
||||
view.detectChanges();
|
||||
expect(q.textDirChildren.length).toEqual(1);
|
||||
expect(q.numberOfChildrenAfterContentInit).toEqual(1);
|
||||
@ -88,7 +87,7 @@ describe('Query API', () => {
|
||||
const view = createTestCmp(MyComp0, template);
|
||||
view.componentInstance.shouldShow = true;
|
||||
view.detectChanges();
|
||||
const q: NeedsContentChild = view.debugElement.children[0].references !['q'];
|
||||
const q: NeedsContentChild = view.debugElement.children[0].references!['q'];
|
||||
expect(q.logs).toEqual([['setter', 'foo'], ['init', 'foo'], ['check', 'foo']]);
|
||||
|
||||
view.componentInstance.shouldShow = false;
|
||||
@ -114,7 +113,7 @@ describe('Query API', () => {
|
||||
`;
|
||||
const view = createTestCmp(MyComp0, template);
|
||||
view.detectChanges();
|
||||
const q: NeedsContentChild = view.debugElement.children[1].references !['q'];
|
||||
const q: NeedsContentChild = view.debugElement.children[1].references!['q'];
|
||||
expect(q.child.text).toEqual('foo');
|
||||
const directive: DirectiveNeedsContentChild =
|
||||
view.debugElement.children[0].injector.get(DirectiveNeedsContentChild);
|
||||
@ -125,7 +124,7 @@ describe('Query API', () => {
|
||||
const template = '<needs-view-child #q></needs-view-child>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
|
||||
const q: NeedsViewChild = view.debugElement.children[0].references !['q'];
|
||||
const q: NeedsViewChild = view.debugElement.children[0].references!['q'];
|
||||
expect(q.logs).toEqual([['setter', 'foo'], ['init', 'foo'], ['check', 'foo']]);
|
||||
|
||||
q.shouldShow = false;
|
||||
@ -140,7 +139,7 @@ describe('Query API', () => {
|
||||
const template =
|
||||
'<needs-static-content-view-child #q><div text="contentFoo"></div></needs-static-content-view-child>';
|
||||
const view = createTestCmp(MyComp0, template);
|
||||
const q: NeedsStaticContentAndViewChild = view.debugElement.children[0].references !['q'];
|
||||
const q: NeedsStaticContentAndViewChild = view.debugElement.children[0].references!['q'];
|
||||
expect(q.contentChild.text).toBeFalsy();
|
||||
expect(q.viewChild.text).toBeFalsy();
|
||||
|
||||
@ -161,7 +160,7 @@ describe('Query API', () => {
|
||||
const view = TestBed.createComponent(MyComp0);
|
||||
|
||||
view.detectChanges();
|
||||
const q: NeedsViewChild = view.debugElement.children[0].references !['q'];
|
||||
const q: NeedsViewChild = view.debugElement.children[0].references!['q'];
|
||||
expect(q.logs).toEqual([['setter', 'foo'], ['init', 'foo'], ['check', 'foo']]);
|
||||
|
||||
q.shouldShow = false;
|
||||
@ -252,8 +251,8 @@ describe('Query API', () => {
|
||||
const template = '<has-null-query-condition></has-null-query-condition>';
|
||||
TestBed.overrideComponent(MyCompBroken0, {set: {template}});
|
||||
expect(() => TestBed.createComponent(MyCompBroken0))
|
||||
.toThrowError(
|
||||
`Can't construct a query for the property "errorTrigger" of "${stringify(HasNullQueryCondition)}" since the query selector wasn't defined.`);
|
||||
.toThrowError(`Can't construct a query for the property "errorTrigger" of "${
|
||||
stringify(HasNullQueryCondition)}" since the query selector wasn't defined.`);
|
||||
});
|
||||
});
|
||||
|
||||
@ -422,7 +421,7 @@ describe('Query API', () => {
|
||||
'</needs-query>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
|
||||
const q = view.debugElement.children[0].references !['q'];
|
||||
const q = view.debugElement.children[0].references!['q'];
|
||||
|
||||
q.query.changes.subscribe({
|
||||
next: () => {
|
||||
@ -444,11 +443,13 @@ describe('Query API', () => {
|
||||
|
||||
let isQueryListCompleted = false;
|
||||
|
||||
const q: NeedsQuery = view.debugElement.children[0].references !['q'];
|
||||
const q: NeedsQuery = view.debugElement.children[0].references!['q'];
|
||||
const changes = <Subject<any>>q.query.changes;
|
||||
expect(q.query.length).toEqual(1);
|
||||
expect(changes.closed).toBeFalsy();
|
||||
changes.subscribe(() => {}, () => {}, () => { isQueryListCompleted = true; });
|
||||
changes.subscribe(() => {}, () => {}, () => {
|
||||
isQueryListCompleted = true;
|
||||
});
|
||||
|
||||
view.componentInstance.shouldShow = false;
|
||||
view.detectChanges();
|
||||
@ -457,7 +458,7 @@ describe('Query API', () => {
|
||||
|
||||
view.componentInstance.shouldShow = true;
|
||||
view.detectChanges();
|
||||
const q2: NeedsQuery = view.debugElement.children[0].references !['q'];
|
||||
const q2: NeedsQuery = view.debugElement.children[0].references!['q'];
|
||||
|
||||
expect(q2.query.length).toEqual(1);
|
||||
expect(changes.closed).toBeTruthy();
|
||||
@ -472,7 +473,7 @@ describe('Query API', () => {
|
||||
'<div *ngFor="let item of list" [text]="item" #textLabel="textDir"></div>' +
|
||||
'</needs-query-by-ref-binding>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
const q = view.debugElement.children[0].references !['q'];
|
||||
const q = view.debugElement.children[0].references!['q'];
|
||||
|
||||
view.componentInstance.list = ['1d', '2d'];
|
||||
view.detectChanges();
|
||||
@ -486,7 +487,7 @@ describe('Query API', () => {
|
||||
'<div text="two" #textLabel2="textDir"></div>' +
|
||||
'</needs-query-by-ref-bindings>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
const q = view.debugElement.children[0].references !['q'];
|
||||
const q = view.debugElement.children[0].references!['q'];
|
||||
|
||||
expect(q.query.first.text).toEqual('one');
|
||||
expect(q.query.last.text).toEqual('two');
|
||||
@ -497,7 +498,7 @@ describe('Query API', () => {
|
||||
'<div *ngFor="let item of list" [text]="item" #textLabel="textDir"></div>' +
|
||||
'</needs-query-by-ref-binding>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
const q = view.debugElement.children[0].references !['q'];
|
||||
const q = view.debugElement.children[0].references!['q'];
|
||||
|
||||
view.componentInstance.list = ['1d', '2d'];
|
||||
view.detectChanges();
|
||||
@ -513,7 +514,7 @@ describe('Query API', () => {
|
||||
'</div>' +
|
||||
'</needs-query-by-ref-binding>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
const q = view.debugElement.children[0].references !['q'];
|
||||
const q = view.debugElement.children[0].references!['q'];
|
||||
|
||||
view.componentInstance.list = ['1d', '2d'];
|
||||
view.detectChanges();
|
||||
@ -534,14 +535,14 @@ describe('Query API', () => {
|
||||
const template = '<needs-view-query-by-ref-binding #q></needs-view-query-by-ref-binding>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
|
||||
const q: NeedsViewQueryByLabel = view.debugElement.children[0].references !['q'];
|
||||
const q: NeedsViewQueryByLabel = view.debugElement.children[0].references!['q'];
|
||||
expect(q.query.first.nativeElement).toHaveText('text');
|
||||
});
|
||||
|
||||
it('should contain all child directives in the view dom', () => {
|
||||
const template = '<needs-view-children #q></needs-view-children>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
const q = view.debugElement.children[0].references !['q'];
|
||||
const q = view.debugElement.children[0].references!['q'];
|
||||
expect(q.textDirChildren.length).toEqual(1);
|
||||
expect(q.numberOfChildrenAfterViewInit).toEqual(1);
|
||||
});
|
||||
@ -551,21 +552,21 @@ describe('Query API', () => {
|
||||
it('should contain all the elements in the view with that have the given directive', () => {
|
||||
const template = '<needs-view-query #q><div text="ignoreme"></div></needs-view-query>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
const q: NeedsViewQuery = view.debugElement.children[0].references !['q'];
|
||||
const q: NeedsViewQuery = view.debugElement.children[0].references!['q'];
|
||||
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']);
|
||||
});
|
||||
|
||||
it('should not include directive present on the host element', () => {
|
||||
const template = '<needs-view-query #q text="self"></needs-view-query>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
const q: NeedsViewQuery = view.debugElement.children[0].references !['q'];
|
||||
const q: NeedsViewQuery = view.debugElement.children[0].references!['q'];
|
||||
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']);
|
||||
});
|
||||
|
||||
it('should reflect changes in the component', () => {
|
||||
const template = '<needs-view-query-if #q></needs-view-query-if>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
const q: NeedsViewQueryIf = view.debugElement.children[0].references !['q'];
|
||||
const q: NeedsViewQueryIf = view.debugElement.children[0].references!['q'];
|
||||
expect(q.query.length).toBe(0);
|
||||
|
||||
q.show = true;
|
||||
@ -577,7 +578,7 @@ describe('Query API', () => {
|
||||
it('should not be affected by other changes in the component', () => {
|
||||
const template = '<needs-view-query-nested-if #q></needs-view-query-nested-if>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
const q: NeedsViewQueryNestedIf = view.debugElement.children[0].references !['q'];
|
||||
const q: NeedsViewQueryNestedIf = view.debugElement.children[0].references!['q'];
|
||||
|
||||
expect(q.query.length).toEqual(1);
|
||||
expect(q.query.first.text).toEqual('1');
|
||||
@ -592,7 +593,7 @@ describe('Query API', () => {
|
||||
() => {
|
||||
const template = '<needs-view-query-order #q></needs-view-query-order>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
const q: NeedsViewQueryOrder = view.debugElement.children[0].references !['q'];
|
||||
const q: NeedsViewQueryOrder = view.debugElement.children[0].references!['q'];
|
||||
|
||||
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']);
|
||||
|
||||
@ -605,7 +606,7 @@ describe('Query API', () => {
|
||||
() => {
|
||||
const template = '<needs-view-query-order-with-p #q></needs-view-query-order-with-p>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
const q: NeedsViewQueryOrderWithParent = view.debugElement.children[0].references !['q'];
|
||||
const q: NeedsViewQueryOrderWithParent = view.debugElement.children[0].references!['q'];
|
||||
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']);
|
||||
|
||||
q.list = ['-3', '2'];
|
||||
@ -616,7 +617,7 @@ describe('Query API', () => {
|
||||
it('should handle long ngFor cycles', () => {
|
||||
const template = '<needs-view-query-order #q></needs-view-query-order>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
const q: NeedsViewQueryOrder = view.debugElement.children[0].references !['q'];
|
||||
const q: NeedsViewQueryOrder = view.debugElement.children[0].references!['q'];
|
||||
|
||||
// no significance to 50, just a reasonably large cycle.
|
||||
for (let i = 0; i < 50; i++) {
|
||||
@ -630,7 +631,7 @@ describe('Query API', () => {
|
||||
it('should support more than three queries', () => {
|
||||
const template = '<needs-four-queries #q><div text="1"></div></needs-four-queries>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
const q = view.debugElement.children[0].references !['q'];
|
||||
const q = view.debugElement.children[0].references!['q'];
|
||||
expect(q.query1).toBeDefined();
|
||||
expect(q.query2).toBeDefined();
|
||||
expect(q.query3).toBeDefined();
|
||||
@ -643,7 +644,7 @@ describe('Query API', () => {
|
||||
const template =
|
||||
'<manual-projecting #q><ng-template><div text="1"></div></ng-template></manual-projecting>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
const q = view.debugElement.children[0].references !['q'];
|
||||
const q = view.debugElement.children[0].references!['q'];
|
||||
expect(q.query.length).toBe(0);
|
||||
|
||||
q.create();
|
||||
@ -665,7 +666,7 @@ describe('Query API', () => {
|
||||
</ng-template>
|
||||
</manual-projecting>`;
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
const q = view.debugElement.children[0].references !['q'] as ManualProjecting;
|
||||
const q = view.debugElement.children[0].references!['q'] as ManualProjecting;
|
||||
expect(q.query.length).toBe(0);
|
||||
|
||||
const view1 = q.vc.createEmbeddedView(q.template, {'x': '1'});
|
||||
@ -694,7 +695,7 @@ describe('Query API', () => {
|
||||
</ng-template>
|
||||
</manual-projecting>`;
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
const q = view.debugElement.children[0].references !['q'] as ManualProjecting;
|
||||
const q = view.debugElement.children[0].references!['q'] as ManualProjecting;
|
||||
expect(q.query.length).toBe(0);
|
||||
|
||||
const view1 = q.vc.createEmbeddedView(q.template, {'x': '1'});
|
||||
@ -729,7 +730,7 @@ describe('Query API', () => {
|
||||
</div>
|
||||
`;
|
||||
const view = createTestCmp(MyComp0, template);
|
||||
const q = view.debugElement.children[0].references !['q'];
|
||||
const q = view.debugElement.children[0].references!['q'];
|
||||
view.componentInstance.shouldShow = true;
|
||||
view.detectChanges();
|
||||
|
||||
@ -751,13 +752,11 @@ describe('Query API', () => {
|
||||
class AutoProjecting {
|
||||
// TODO(issue/24571):
|
||||
// remove '!'.
|
||||
@ContentChild(TemplateRef)
|
||||
content !: TemplateRef<any>;
|
||||
@ContentChild(TemplateRef) content!: TemplateRef<any>;
|
||||
|
||||
// TODO(issue/24571):
|
||||
// remove '!'.
|
||||
@ContentChildren(TextDirective)
|
||||
query !: QueryList<TextDirective>;
|
||||
@ContentChildren(TextDirective) query!: QueryList<TextDirective>;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [AutoProjecting]});
|
||||
@ -765,7 +764,7 @@ describe('Query API', () => {
|
||||
'<auto-projecting #q><ng-template><div text="1"></div></ng-template></auto-projecting>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
|
||||
const q = view.debugElement.children[0].references !['q'];
|
||||
const q = view.debugElement.children[0].references!['q'];
|
||||
// This should be 1, but due to
|
||||
// https://github.com/angular/angular/issues/15117
|
||||
// this is 0.
|
||||
@ -783,13 +782,11 @@ describe('Query API', () => {
|
||||
class AutoProjecting {
|
||||
// TODO(issue/24571):
|
||||
// remove '!'.
|
||||
@ContentChild(TemplateRef)
|
||||
content !: TemplateRef<any>;
|
||||
@ContentChild(TemplateRef) content!: TemplateRef<any>;
|
||||
|
||||
// TODO(issue/24571):
|
||||
// remove '!'.
|
||||
@ContentChildren(TextDirective)
|
||||
query !: QueryList<TextDirective>;
|
||||
@ContentChildren(TextDirective) query!: QueryList<TextDirective>;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [AutoProjecting]});
|
||||
@ -797,7 +794,7 @@ describe('Query API', () => {
|
||||
'<auto-projecting #q><ng-template><div text="1"></div></ng-template></auto-projecting>';
|
||||
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||
|
||||
const q = view.debugElement.children[0].references !['q'];
|
||||
const q = view.debugElement.children[0].references!['q'];
|
||||
expect(q.query.length).toBe(1);
|
||||
});
|
||||
}
|
||||
@ -808,35 +805,39 @@ describe('Query API', () => {
|
||||
@Directive({selector: '[text]', inputs: ['text'], exportAs: 'textDir'})
|
||||
class TextDirective {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
text !: string;
|
||||
text!: string;
|
||||
constructor() {}
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-content-children', template: ''})
|
||||
class NeedsContentChildren implements AfterContentInit {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChildren(TextDirective) textDirChildren !: QueryList<TextDirective>;
|
||||
@ContentChildren(TextDirective) textDirChildren!: QueryList<TextDirective>;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
numberOfChildrenAfterContentInit !: number;
|
||||
numberOfChildrenAfterContentInit!: number;
|
||||
|
||||
ngAfterContentInit() { this.numberOfChildrenAfterContentInit = this.textDirChildren.length; }
|
||||
ngAfterContentInit() {
|
||||
this.numberOfChildrenAfterContentInit = this.textDirChildren.length;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-view-children', template: '<div text></div>'})
|
||||
class NeedsViewChildren implements AfterViewInit {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ViewChildren(TextDirective) textDirChildren !: QueryList<TextDirective>;
|
||||
@ViewChildren(TextDirective) textDirChildren!: QueryList<TextDirective>;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
numberOfChildrenAfterViewInit !: number;
|
||||
numberOfChildrenAfterViewInit!: number;
|
||||
|
||||
ngAfterViewInit() { this.numberOfChildrenAfterViewInit = this.textDirChildren.length; }
|
||||
ngAfterViewInit() {
|
||||
this.numberOfChildrenAfterViewInit = this.textDirChildren.length;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-content-child', template: ''})
|
||||
class NeedsContentChild implements AfterContentInit, AfterContentChecked {
|
||||
/** @internal */
|
||||
// TODO(issue/24571): remove '!'.
|
||||
_child !: TextDirective;
|
||||
_child!: TextDirective;
|
||||
|
||||
@ContentChild(TextDirective)
|
||||
set child(value) {
|
||||
@ -844,18 +845,24 @@ class NeedsContentChild implements AfterContentInit, AfterContentChecked {
|
||||
this.logs.push(['setter', value ? value.text : null]);
|
||||
}
|
||||
|
||||
get child() { return this._child; }
|
||||
get child() {
|
||||
return this._child;
|
||||
}
|
||||
logs: any[] /** TODO #9100 */ = [];
|
||||
|
||||
ngAfterContentInit() { this.logs.push(['init', this.child ? this.child.text : null]); }
|
||||
ngAfterContentInit() {
|
||||
this.logs.push(['init', this.child ? this.child.text : null]);
|
||||
}
|
||||
|
||||
ngAfterContentChecked() { this.logs.push(['check', this.child ? this.child.text : null]); }
|
||||
ngAfterContentChecked() {
|
||||
this.logs.push(['check', this.child ? this.child.text : null]);
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[directive-needs-content-child]'})
|
||||
class DirectiveNeedsContentChild {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChild(TextDirective) child !: TextDirective;
|
||||
@ContentChild(TextDirective) child!: TextDirective;
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-view-child', template: `<div *ngIf="shouldShow" text="foo"></div>`})
|
||||
@ -864,7 +871,7 @@ class NeedsViewChild implements AfterViewInit, AfterViewChecked {
|
||||
shouldShow2: boolean = false;
|
||||
/** @internal */
|
||||
// TODO(issue/24571): remove '!'.
|
||||
_child !: TextDirective;
|
||||
_child!: TextDirective;
|
||||
|
||||
@ViewChild(TextDirective)
|
||||
set child(value) {
|
||||
@ -872,12 +879,18 @@ class NeedsViewChild implements AfterViewInit, AfterViewChecked {
|
||||
this.logs.push(['setter', value ? value.text : null]);
|
||||
}
|
||||
|
||||
get child() { return this._child; }
|
||||
get child() {
|
||||
return this._child;
|
||||
}
|
||||
logs: any[] /** TODO #9100 */ = [];
|
||||
|
||||
ngAfterViewInit() { this.logs.push(['init', this.child ? this.child.text : null]); }
|
||||
ngAfterViewInit() {
|
||||
this.logs.push(['init', this.child ? this.child.text : null]);
|
||||
}
|
||||
|
||||
ngAfterViewChecked() { this.logs.push(['check', this.child ? this.child.text : null]); }
|
||||
ngAfterViewChecked() {
|
||||
this.logs.push(['check', this.child ? this.child.text : null]);
|
||||
}
|
||||
}
|
||||
|
||||
function createTestCmp<T>(type: Type<T>, template: string): ComponentFixture<T> {
|
||||
@ -895,9 +908,9 @@ function createTestCmpAndDetectChanges<T>(type: Type<T>, template: string): Comp
|
||||
@Component({selector: 'needs-static-content-view-child', template: `<div text="viewFoo"></div>`})
|
||||
class NeedsStaticContentAndViewChild {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChild(TextDirective, {static: true}) contentChild !: TextDirective;
|
||||
@ContentChild(TextDirective, {static: true}) contentChild!: TextDirective;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ViewChild(TextDirective, {static: true}) viewChild !: TextDirective;
|
||||
@ViewChild(TextDirective, {static: true}) viewChild!: TextDirective;
|
||||
}
|
||||
|
||||
@Directive({selector: '[dir]'})
|
||||
@ -910,19 +923,19 @@ class InertDirective {
|
||||
})
|
||||
class NeedsQuery {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChildren(TextDirective) query !: QueryList<TextDirective>;
|
||||
@ContentChildren(TextDirective) query!: QueryList<TextDirective>;
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-four-queries', template: ''})
|
||||
class NeedsFourQueries {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChild(TextDirective) query1 !: TextDirective;
|
||||
@ContentChild(TextDirective) query1!: TextDirective;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChild(TextDirective) query2 !: TextDirective;
|
||||
@ContentChild(TextDirective) query2!: TextDirective;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChild(TextDirective) query3 !: TextDirective;
|
||||
@ContentChild(TextDirective) query3!: TextDirective;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChild(TextDirective) query4 !: TextDirective;
|
||||
@ContentChild(TextDirective) query4!: TextDirective;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -931,25 +944,25 @@ class NeedsFourQueries {
|
||||
})
|
||||
class NeedsQueryDesc {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChildren(TextDirective, {descendants: true}) query !: QueryList<TextDirective>;
|
||||
@ContentChildren(TextDirective, {descendants: true}) query!: QueryList<TextDirective>;
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-query-by-ref-binding', template: '<ng-content>'})
|
||||
class NeedsQueryByLabel {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChildren('textLabel', {descendants: true}) query !: QueryList<any>;
|
||||
@ContentChildren('textLabel', {descendants: true}) query!: QueryList<any>;
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-view-query-by-ref-binding', template: '<div #textLabel>text</div>'})
|
||||
class NeedsViewQueryByLabel {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ViewChildren('textLabel') query !: QueryList<any>;
|
||||
@ViewChildren('textLabel') query!: QueryList<any>;
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-query-by-ref-bindings', template: '<ng-content>'})
|
||||
class NeedsQueryByTwoLabels {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChildren('textLabel1,textLabel2', {descendants: true}) query !: QueryList<any>;
|
||||
@ContentChildren('textLabel1,textLabel2', {descendants: true}) query!: QueryList<any>;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -958,7 +971,7 @@ class NeedsQueryByTwoLabels {
|
||||
})
|
||||
class NeedsQueryAndProject {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChildren(TextDirective) query !: QueryList<TextDirective>;
|
||||
@ContentChildren(TextDirective) query!: QueryList<TextDirective>;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -967,14 +980,14 @@ class NeedsQueryAndProject {
|
||||
})
|
||||
class NeedsViewQuery {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ViewChildren(TextDirective) query !: QueryList<TextDirective>;
|
||||
@ViewChildren(TextDirective) query!: QueryList<TextDirective>;
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-view-query-if', template: '<div *ngIf="show" text="1"></div>'})
|
||||
class NeedsViewQueryIf {
|
||||
show: boolean = false;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ViewChildren(TextDirective) query !: QueryList<TextDirective>;
|
||||
@ViewChildren(TextDirective) query!: QueryList<TextDirective>;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -984,7 +997,7 @@ class NeedsViewQueryIf {
|
||||
class NeedsViewQueryNestedIf {
|
||||
show: boolean = true;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ViewChildren(TextDirective) query !: QueryList<TextDirective>;
|
||||
@ViewChildren(TextDirective) query!: QueryList<TextDirective>;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -995,7 +1008,7 @@ class NeedsViewQueryNestedIf {
|
||||
})
|
||||
class NeedsViewQueryOrder {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ViewChildren(TextDirective) query !: QueryList<TextDirective>;
|
||||
@ViewChildren(TextDirective) query!: QueryList<TextDirective>;
|
||||
list: string[] = ['2', '3'];
|
||||
}
|
||||
|
||||
@ -1007,16 +1020,16 @@ class NeedsViewQueryOrder {
|
||||
})
|
||||
class NeedsViewQueryOrderWithParent {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ViewChildren(TextDirective) query !: QueryList<TextDirective>;
|
||||
@ViewChildren(TextDirective) query!: QueryList<TextDirective>;
|
||||
list: string[] = ['2', '3'];
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-tpl', template: '<ng-template><div>shadow</div></ng-template>'})
|
||||
class NeedsTpl {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ViewChildren(TemplateRef) viewQuery !: QueryList<TemplateRef<Object>>;
|
||||
@ViewChildren(TemplateRef) viewQuery!: QueryList<TemplateRef<Object>>;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChildren(TemplateRef) query !: QueryList<TemplateRef<Object>>;
|
||||
@ContentChildren(TemplateRef) query!: QueryList<TemplateRef<Object>>;
|
||||
constructor(public vc: ViewContainerRef) {}
|
||||
}
|
||||
|
||||
@ -1024,33 +1037,31 @@ class NeedsTpl {
|
||||
{selector: 'needs-named-tpl', template: '<ng-template #tpl><div>shadow</div></ng-template>'})
|
||||
class NeedsNamedTpl {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ViewChild('tpl', {static: true}) viewTpl !: TemplateRef<Object>;
|
||||
@ViewChild('tpl', {static: true}) viewTpl!: TemplateRef<Object>;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChild('tpl', {static: true}) contentTpl !: TemplateRef<Object>;
|
||||
@ContentChild('tpl', {static: true}) contentTpl!: TemplateRef<Object>;
|
||||
constructor(public vc: ViewContainerRef) {}
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-content-children-read', template: ''})
|
||||
class NeedsContentChildrenWithRead {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChildren('q', {read: TextDirective}) textDirChildren !: QueryList<TextDirective>;
|
||||
@ContentChildren('q', {read: TextDirective}) textDirChildren!: QueryList<TextDirective>;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChildren('nonExisting', {read: TextDirective}) nonExistingVar !: QueryList<TextDirective>;
|
||||
@ContentChildren('nonExisting', {read: TextDirective}) nonExistingVar!: QueryList<TextDirective>;
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-content-child-read', template: ''})
|
||||
class NeedsContentChildWithRead {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChild('q', {read: TextDirective}) textDirChild !: TextDirective;
|
||||
@ContentChild('q', {read: TextDirective}) textDirChild!: TextDirective;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChild('nonExisting', {read: TextDirective})
|
||||
nonExistingVar !: TextDirective;
|
||||
@ContentChild('nonExisting', {read: TextDirective}) nonExistingVar!: TextDirective;
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-content-children-shallow', template: ''})
|
||||
class NeedsContentChildrenShallow {
|
||||
@ContentChildren('q', {descendants: false})
|
||||
children !: QueryList<ElementRef>;
|
||||
@ContentChildren('q', {descendants: false}) children!: QueryList<ElementRef>;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1059,7 +1070,7 @@ class NeedsContentChildrenShallow {
|
||||
})
|
||||
class NeedsContentChildTemplateRef {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChild(TemplateRef, {static: true}) templateRef !: TemplateRef<any>;
|
||||
@ContentChild(TemplateRef, {static: true}) templateRef!: TemplateRef<any>;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1077,9 +1088,9 @@ class NeedsContentChildTemplateRefApp {
|
||||
})
|
||||
class NeedsViewChildrenWithRead {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ViewChildren('q,w', {read: TextDirective}) textDirChildren !: QueryList<TextDirective>;
|
||||
@ViewChildren('q,w', {read: TextDirective}) textDirChildren!: QueryList<TextDirective>;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ViewChildren('nonExisting', {read: TextDirective}) nonExistingVar !: QueryList<TextDirective>;
|
||||
@ViewChildren('nonExisting', {read: TextDirective}) nonExistingVar!: QueryList<TextDirective>;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -1088,27 +1099,28 @@ class NeedsViewChildrenWithRead {
|
||||
})
|
||||
class NeedsViewChildWithRead {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ViewChild('q', {read: TextDirective}) textDirChild !: TextDirective;
|
||||
@ViewChild('q', {read: TextDirective}) textDirChild!: TextDirective;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ViewChild('nonExisting', {read: TextDirective}) nonExistingVar !: TextDirective;
|
||||
@ViewChild('nonExisting', {read: TextDirective}) nonExistingVar!: TextDirective;
|
||||
}
|
||||
|
||||
@Component({selector: 'needs-viewcontainer-read', template: '<div #q></div>'})
|
||||
class NeedsViewContainerWithRead {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ViewChild('q', {read: ViewContainerRef}) vc !: ViewContainerRef;
|
||||
@ViewChild('q', {read: ViewContainerRef}) vc!: ViewContainerRef;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ViewChild('nonExisting', {read: ViewContainerRef})
|
||||
nonExistingVar !: ViewContainerRef;
|
||||
@ViewChild('nonExisting', {read: ViewContainerRef}) nonExistingVar!: ViewContainerRef;
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChild(TemplateRef, {static: true}) template !: TemplateRef<Object>;
|
||||
|
||||
createView() { this.vc.createEmbeddedView(this.template); }
|
||||
createView() {
|
||||
this.vc.createEmbeddedView(this.template);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'has-null-query-condition', template: '<div></div>'})
|
||||
class HasNullQueryCondition {
|
||||
@ContentChildren(null !) errorTrigger: any;
|
||||
@ContentChildren(null!) errorTrigger: any;
|
||||
}
|
||||
|
||||
@Component({selector: 'my-comp', template: ''})
|
||||
@ -1127,14 +1139,16 @@ class ManualProjecting {
|
||||
@ContentChild(TemplateRef, {static: true}) template !: TemplateRef<any>;
|
||||
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ViewChild('vc', {read: ViewContainerRef})
|
||||
vc !: ViewContainerRef;
|
||||
@ViewChild('vc', {read: ViewContainerRef}) vc!: ViewContainerRef;
|
||||
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ContentChildren(TextDirective)
|
||||
query !: QueryList<TextDirective>;
|
||||
@ContentChildren(TextDirective) query!: QueryList<TextDirective>;
|
||||
|
||||
create() { this.vc.createEmbeddedView(this.template); }
|
||||
create() {
|
||||
this.vc.createEmbeddedView(this.template);
|
||||
}
|
||||
|
||||
destroy() { this.vc.clear(); }
|
||||
destroy() {
|
||||
this.vc.clear();
|
||||
}
|
||||
}
|
||||
|
@ -21,10 +21,11 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testin
|
||||
log = '';
|
||||
});
|
||||
|
||||
function logAppend(item: any /** TODO #9100 */) { log += (log.length == 0 ? '' : ', ') + item; }
|
||||
function logAppend(item: any /** TODO #9100 */) {
|
||||
log += (log.length == 0 ? '' : ', ') + item;
|
||||
}
|
||||
|
||||
describe('dirty and reset', () => {
|
||||
|
||||
it('should initially be dirty and empty', () => {
|
||||
expect(queryList.dirty).toBeTruthy();
|
||||
expect(queryList.length).toBe(0);
|
||||
@ -36,7 +37,6 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testin
|
||||
expect(queryList.dirty).toBeFalsy();
|
||||
expect(queryList.length).toBe(2);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should support resetting and iterating over the new objects', () => {
|
||||
@ -146,7 +146,7 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testin
|
||||
|
||||
// For loops use the iteration protocol.
|
||||
for (const value of queryListAsIterable) {
|
||||
expect(value).toBe(data.shift() !);
|
||||
expect(value).toBe(data.shift()!);
|
||||
}
|
||||
expect(data.length).toBe(0);
|
||||
});
|
||||
@ -155,7 +155,11 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testin
|
||||
describe('simple observable interface', () => {
|
||||
it('should fire callbacks on change', fakeAsync(() => {
|
||||
let fires = 0;
|
||||
queryList.changes.subscribe({next: (_) => { fires += 1; }});
|
||||
queryList.changes.subscribe({
|
||||
next: (_) => {
|
||||
fires += 1;
|
||||
}
|
||||
});
|
||||
|
||||
queryList.notifyOnChanges();
|
||||
tick();
|
||||
@ -170,7 +174,11 @@ import {beforeEach, describe, expect, it} from '@angular/core/testing/src/testin
|
||||
|
||||
it('should provides query list as an argument', fakeAsync(() => {
|
||||
let recorded: any /** TODO #9100 */;
|
||||
queryList.changes.subscribe({next: (v: any) => { recorded = v; }});
|
||||
queryList.changes.subscribe({
|
||||
next: (v: any) => {
|
||||
recorded = v;
|
||||
}
|
||||
});
|
||||
|
||||
queryList.reset(['one']);
|
||||
queryList.notifyOnChanges();
|
||||
|
@ -7,18 +7,24 @@
|
||||
*/
|
||||
|
||||
import {DOCUMENT, ɵgetDOM as getDOM} from '@angular/common';
|
||||
import {ANALYZE_FOR_ENTRY_COMPONENTS, ApplicationRef, Component, ComponentRef, ContentChild, Directive, ErrorHandler, EventEmitter, HostListener, InjectionToken, Injector, Input, NgModule, NgModuleRef, NgZone, Output, Pipe, PipeTransform, Provider, QueryList, Renderer2, SimpleChanges, TemplateRef, ViewChild, ViewChildren, ViewContainerRef, destroyPlatform, ɵivyEnabled as ivyEnabled} from '@angular/core';
|
||||
import {TestBed, fakeAsync, inject, tick} from '@angular/core/testing';
|
||||
import {ANALYZE_FOR_ENTRY_COMPONENTS, ApplicationRef, Component, ComponentRef, ContentChild, destroyPlatform, Directive, ErrorHandler, EventEmitter, HostListener, InjectionToken, Injector, Input, NgModule, NgModuleRef, NgZone, Output, Pipe, PipeTransform, Provider, QueryList, Renderer2, SimpleChanges, TemplateRef, ViewChild, ViewChildren, ViewContainerRef, ɵivyEnabled as ivyEnabled} from '@angular/core';
|
||||
import {fakeAsync, inject, TestBed, tick} from '@angular/core/testing';
|
||||
import {BrowserModule, By} from '@angular/platform-browser';
|
||||
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
import {modifiedInIvy, onlyInIvy} from '@angular/private/testing';
|
||||
|
||||
if (ivyEnabled) {
|
||||
describe('ivy', () => { declareTests(); });
|
||||
describe('ivy', () => {
|
||||
declareTests();
|
||||
});
|
||||
} else {
|
||||
describe('jit', () => { declareTests({useJit: true}); });
|
||||
describe('no jit', () => { declareTests({useJit: false}); });
|
||||
describe('jit', () => {
|
||||
declareTests({useJit: true});
|
||||
});
|
||||
describe('no jit', () => {
|
||||
declareTests({useJit: false});
|
||||
});
|
||||
}
|
||||
|
||||
declareTestsUsingBootstrap();
|
||||
@ -26,11 +32,14 @@ declareTestsUsingBootstrap();
|
||||
function declareTests(config?: {useJit: boolean}) {
|
||||
// Place to put reproductions for regressions
|
||||
describe('regressions', () => {
|
||||
|
||||
beforeEach(() => { TestBed.configureTestingModule({declarations: [MyComp1, PlatformPipe]}); });
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({declarations: [MyComp1, PlatformPipe]});
|
||||
});
|
||||
|
||||
describe('platform pipes', () => {
|
||||
beforeEach(() => { TestBed.configureCompiler({...config}); });
|
||||
beforeEach(() => {
|
||||
TestBed.configureCompiler({...config});
|
||||
});
|
||||
|
||||
it('should overwrite them by custom pipes', () => {
|
||||
TestBed.configureTestingModule({declarations: [CustomPipe]});
|
||||
@ -84,14 +93,20 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
class MyDir {
|
||||
setterCalls: {[key: string]: any} = {};
|
||||
// TODO(issue/24571): remove '!'.
|
||||
changes !: SimpleChanges;
|
||||
changes!: SimpleChanges;
|
||||
|
||||
@Input()
|
||||
set a(v: number) { this.setterCalls['a'] = v; }
|
||||
set a(v: number) {
|
||||
this.setterCalls['a'] = v;
|
||||
}
|
||||
@Input()
|
||||
set b(v: number) { this.setterCalls['b'] = v; }
|
||||
set b(v: number) {
|
||||
this.setterCalls['b'] = v;
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) { this.changes = changes; }
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
this.changes = changes;
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [MyDir, MyComp]});
|
||||
@ -128,7 +143,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
@Component({selector: 'some-comp', template: '<p (click)="nullValue?.click()"></p>'})
|
||||
class SomeComponent {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
nullValue !: SomeReferencedClass;
|
||||
nullValue!: SomeReferencedClass;
|
||||
}
|
||||
|
||||
class SomeReferencedClass {
|
||||
@ -195,20 +210,17 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
});
|
||||
|
||||
describe('ANALYZE_FOR_ENTRY_COMPONENTS providers', () => {
|
||||
|
||||
it('should support class instances', () => {
|
||||
class SomeObject {
|
||||
someMethod() {}
|
||||
}
|
||||
|
||||
expect(
|
||||
() => createInjector([
|
||||
{provide: ANALYZE_FOR_ENTRY_COMPONENTS, useValue: new SomeObject(), multi: true}
|
||||
]))
|
||||
expect(() => createInjector([
|
||||
{provide: ANALYZE_FOR_ENTRY_COMPONENTS, useValue: new SomeObject(), multi: true}
|
||||
]))
|
||||
.not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should allow logging a previous elements class binding via interpolation', () => {
|
||||
@ -298,8 +310,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
@Component({template: '<div #vc></div><div *ngIf="show" #vc></div>'})
|
||||
class MyComp {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@ViewChildren('vc', {read: ViewContainerRef})
|
||||
viewContainers !: QueryList<ViewContainerRef>;
|
||||
@ViewChildren('vc', {read: ViewContainerRef}) viewContainers!: QueryList<ViewContainerRef>;
|
||||
|
||||
show = true;
|
||||
}
|
||||
@ -361,7 +372,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
@Directive({selector: 'test'})
|
||||
class Test {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@Input() @ContentChild(TemplateRef, {static: true}) tpl !: TemplateRef<any>;
|
||||
@Input() @ContentChild(TemplateRef, {static: true}) tpl!: TemplateRef<any>;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -391,7 +402,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
.it('should throw if @ContentChild and @Input are on the same property', () => {
|
||||
@Directive({selector: 'test'})
|
||||
class Test {
|
||||
@Input() @ContentChild(TemplateRef, {static: true}) tpl !: TemplateRef<any>;
|
||||
@Input() @ContentChild(TemplateRef, {static: true}) tpl!: TemplateRef<any>;
|
||||
}
|
||||
|
||||
@Component({selector: 'my-app', template: `<test></test>`})
|
||||
@ -412,8 +423,8 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
class MyModule {
|
||||
}
|
||||
|
||||
const modRef = TestBed.configureTestingModule({imports: [MyModule]})
|
||||
.get(NgModuleRef) as NgModuleRef<MyModule>;
|
||||
const modRef = TestBed.configureTestingModule({imports: [MyModule]}).get(NgModuleRef) as
|
||||
NgModuleRef<MyModule>;
|
||||
const compRef =
|
||||
modRef.componentFactoryResolver.resolveComponentFactory(App).create(Injector.NULL);
|
||||
|
||||
@ -429,7 +440,9 @@ function declareTestsUsingBootstrap() {
|
||||
|
||||
class MockConsole {
|
||||
errors: any[][] = [];
|
||||
error(...s: any[]): void { this.errors.push(s); }
|
||||
error(...s: any[]): void {
|
||||
this.errors.push(s);
|
||||
}
|
||||
}
|
||||
|
||||
let logger: MockConsole;
|
||||
@ -445,7 +458,9 @@ function declareTestsUsingBootstrap() {
|
||||
(errorHandler as any)._console = logger as any;
|
||||
}));
|
||||
|
||||
afterEach(() => { destroyPlatform(); });
|
||||
afterEach(() => {
|
||||
destroyPlatform();
|
||||
});
|
||||
|
||||
if (getDOM().supportsDOMEvents()) {
|
||||
// This test needs a real DOM....
|
||||
@ -459,7 +474,9 @@ function declareTestsUsingBootstrap() {
|
||||
class ErrorComp {
|
||||
value = 0;
|
||||
thrownValue = 0;
|
||||
next() { this.value++; }
|
||||
next() {
|
||||
this.value++;
|
||||
}
|
||||
nextAndThrow() {
|
||||
this.value++;
|
||||
this.throwIfNeeded();
|
||||
@ -475,11 +492,12 @@ function declareTestsUsingBootstrap() {
|
||||
|
||||
@Directive({selector: '[dirClick]'})
|
||||
class EventDir {
|
||||
@Output()
|
||||
dirClick = new EventEmitter();
|
||||
@Output() dirClick = new EventEmitter();
|
||||
|
||||
@HostListener('click', ['$event'])
|
||||
onClick(event: any) { this.dirClick.next(event); }
|
||||
onClick(event: any) {
|
||||
this.dirClick.next(event);
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
@ -552,12 +570,16 @@ class MyComp1 {
|
||||
|
||||
@Pipe({name: 'somePipe', pure: true})
|
||||
class PlatformPipe implements PipeTransform {
|
||||
transform(value: any): any { return 'somePlatformPipe'; }
|
||||
transform(value: any): any {
|
||||
return 'somePlatformPipe';
|
||||
}
|
||||
}
|
||||
|
||||
@Pipe({name: 'somePipe', pure: true})
|
||||
class CustomPipe implements PipeTransform {
|
||||
transform(value: any): any { return 'someCustomPipe'; }
|
||||
transform(value: any): any {
|
||||
return 'someCustomPipe';
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'cmp-content', template: `<ng-content></ng-content>`})
|
||||
@ -571,7 +593,9 @@ class MyCountingComp {
|
||||
return {value: 'counting method value'};
|
||||
}
|
||||
|
||||
static reset() { MyCountingComp.calls = 0; }
|
||||
static reset() {
|
||||
MyCountingComp.calls = 0;
|
||||
}
|
||||
static calls = 0;
|
||||
}
|
||||
|
||||
@ -581,7 +605,9 @@ class CountingPipe implements PipeTransform {
|
||||
CountingPipe.calls++;
|
||||
return {value: 'counting pipe value'};
|
||||
}
|
||||
static reset() { CountingPipe.calls = 0; }
|
||||
static reset() {
|
||||
CountingPipe.calls = 0;
|
||||
}
|
||||
static calls = 0;
|
||||
}
|
||||
|
||||
|
@ -8,16 +8,22 @@
|
||||
|
||||
import {ɵgetDOM as getDOM} from '@angular/common';
|
||||
import {Component, Directive, HostBinding, Input, NO_ERRORS_SCHEMA, ɵivyEnabled as ivyEnabled} from '@angular/core';
|
||||
import {ComponentFixture, TestBed, getTestBed} from '@angular/core/testing';
|
||||
import {ComponentFixture, getTestBed, TestBed} from '@angular/core/testing';
|
||||
import {DomSanitizer} from '@angular/platform-browser/src/security/dom_sanitization_service';
|
||||
import {modifiedInIvy, onlyInIvy} from '@angular/private/testing';
|
||||
|
||||
{
|
||||
if (ivyEnabled) {
|
||||
describe('ivy', () => { declareTests(); });
|
||||
describe('ivy', () => {
|
||||
declareTests();
|
||||
});
|
||||
} else {
|
||||
describe('jit', () => { declareTests({useJit: true}); });
|
||||
describe('no jit', () => { declareTests({useJit: false}); });
|
||||
describe('jit', () => {
|
||||
declareTests({useJit: true});
|
||||
});
|
||||
describe('no jit', () => {
|
||||
declareTests({useJit: false});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,7 +40,6 @@ class OnPrefixDir {
|
||||
|
||||
function declareTests(config?: {useJit: boolean}) {
|
||||
describe('security integration tests', function() {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureCompiler({...config}).configureTestingModule({
|
||||
declarations: [
|
||||
@ -49,7 +54,9 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
originalLog = getDOM().log;
|
||||
getDOM().log = (msg) => { /* disable logging */ };
|
||||
});
|
||||
afterEach(() => { getDOM().log = originalLog; });
|
||||
afterEach(() => {
|
||||
getDOM().log = originalLog;
|
||||
});
|
||||
|
||||
describe('events', () => {
|
||||
modifiedInIvy('on-prefixed attributes validation happens at runtime in Ivy')
|
||||
@ -113,7 +120,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
});
|
||||
|
||||
// should not throw for inputs starting with "on"
|
||||
let cmp: ComponentFixture<SecuredComponent> = undefined !;
|
||||
let cmp: ComponentFixture<SecuredComponent> = undefined!;
|
||||
expect(() => cmp = TestBed.createComponent(SecuredComponent)).not.toThrow();
|
||||
|
||||
// must bind to the directive not to the property of the div
|
||||
@ -124,7 +131,6 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
expect(getDOM().getProperty(div.nativeElement, 'onclick')).not.toBe(value);
|
||||
expect(div.nativeElement.hasAttribute('onclick')).toEqual(false);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('safe HTML values', function() {
|
||||
@ -206,8 +212,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
@Directive({selector: '[dirHref]'})
|
||||
class HrefDirective {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@HostBinding('href') @Input()
|
||||
dirHref !: string;
|
||||
@HostBinding('href') @Input() dirHref!: string;
|
||||
}
|
||||
|
||||
const template = `<a [dirHref]="ctxProp">Link Title</a>`;
|
||||
@ -222,8 +227,7 @@ function declareTests(config?: {useJit: boolean}) {
|
||||
@Directive({selector: '[dirHref]'})
|
||||
class HrefDirective {
|
||||
// TODO(issue/24571): remove '!'.
|
||||
@HostBinding('attr.href') @Input()
|
||||
dirHref !: string;
|
||||
@HostBinding('attr.href') @Input() dirHref!: string;
|
||||
}
|
||||
|
||||
const template = `<a [dirHref]="ctxProp">Link Title</a>`;
|
||||
|
@ -16,7 +16,7 @@ import {Attribute, Component, Directive, ErrorHandler, ɵglobal} from '@angular/
|
||||
import {CompilerFacade, ExportedCompilerFacade} from '@angular/core/src/compiler/compiler_facade';
|
||||
import {getErrorLogger} from '@angular/core/src/errors';
|
||||
import {resolveComponentResources} from '@angular/core/src/metadata/resource_loading';
|
||||
import {TestBed, fakeAsync, tick} from '@angular/core/testing';
|
||||
import {fakeAsync, TestBed, tick} from '@angular/core/testing';
|
||||
import {modifiedInIvy, onlyInIvy} from '@angular/private/testing';
|
||||
|
||||
describe('jit source mapping', () => {
|
||||
@ -44,7 +44,9 @@ describe('jit source mapping', () => {
|
||||
.describe('(View Engine)', () => {
|
||||
describe('inline templates', () => {
|
||||
const ngUrl = 'ng:///DynamicTestModule/MyComp.html';
|
||||
function templateDecorator(template: string) { return {template}; }
|
||||
function templateDecorator(template: string) {
|
||||
return {template};
|
||||
}
|
||||
declareTests({ngUrl, templateDecorator});
|
||||
});
|
||||
|
||||
@ -66,7 +68,9 @@ describe('jit source mapping', () => {
|
||||
class MyComp {
|
||||
}
|
||||
|
||||
expect(() => { compileAndCreateComponent(MyComp); })
|
||||
expect(() => {
|
||||
compileAndCreateComponent(MyComp);
|
||||
})
|
||||
.toThrowError(
|
||||
new RegExp(`Template parse errors[\\s\\S]*${escapeRegExp(ngUrl)}@1:2`));
|
||||
}));
|
||||
@ -76,7 +80,9 @@ describe('jit source mapping', () => {
|
||||
class MyComp {
|
||||
}
|
||||
|
||||
expect(() => { compileAndCreateComponent(MyComp); })
|
||||
expect(() => {
|
||||
compileAndCreateComponent(MyComp);
|
||||
})
|
||||
.toThrowError(
|
||||
new RegExp(`Template parse errors[\\s\\S]*${escapeRegExp(ngUrl)}@1:7`));
|
||||
}));
|
||||
@ -105,7 +111,9 @@ describe('jit source mapping', () => {
|
||||
|
||||
@Directive({selector: '[someDir]'})
|
||||
class SomeDir {
|
||||
constructor() { throw new Error('Test'); }
|
||||
constructor() {
|
||||
throw new Error('Test');
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [SomeDir]});
|
||||
@ -163,7 +171,9 @@ describe('jit source mapping', () => {
|
||||
|
||||
@Component({...templateDecorator(template)})
|
||||
class MyComp {
|
||||
createError() { throw new Error('Test'); }
|
||||
createError() {
|
||||
throw new Error('Test');
|
||||
}
|
||||
}
|
||||
|
||||
const comp = compileAndCreateComponent(MyComp);
|
||||
@ -195,7 +205,9 @@ describe('jit source mapping', () => {
|
||||
|
||||
@Component({...templateDecorator(template)})
|
||||
class MyComp {
|
||||
createError() { throw new Error('Test'); }
|
||||
createError() {
|
||||
throw new Error('Test');
|
||||
}
|
||||
}
|
||||
|
||||
const comp = compileAndCreateComponent(MyComp);
|
||||
@ -219,19 +231,19 @@ describe('jit source mapping', () => {
|
||||
column: 4,
|
||||
source: ngUrl,
|
||||
});
|
||||
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
onlyInIvy('Generated filenames and stack traces have changed in ivy').describe('(Ivy)', () => {
|
||||
|
||||
beforeEach(() => overrideCompilerFacade());
|
||||
afterEach(() => restoreCompilerFacade());
|
||||
|
||||
describe('inline templates', () => {
|
||||
const ngUrl = 'ng:///MyComp/template.html';
|
||||
function templateDecorator(template: string) { return {template}; }
|
||||
function templateDecorator(template: string) {
|
||||
return {template};
|
||||
}
|
||||
declareTests({ngUrl, templateDecorator});
|
||||
});
|
||||
|
||||
@ -268,7 +280,9 @@ describe('jit source mapping', () => {
|
||||
class MyComp {
|
||||
}
|
||||
|
||||
expect(() => { resolveCompileAndCreateComponent(MyComp, template); })
|
||||
expect(() => {
|
||||
resolveCompileAndCreateComponent(MyComp, template);
|
||||
})
|
||||
.toThrowError(
|
||||
new RegExp(`Template parse errors[\\s\\S]*${escapeRegExp(ngUrl)}@1:7`));
|
||||
}));
|
||||
@ -297,7 +311,9 @@ describe('jit source mapping', () => {
|
||||
|
||||
@Directive({selector: '[someDir]'})
|
||||
class SomeDir {
|
||||
constructor() { throw new Error('Test'); }
|
||||
constructor() {
|
||||
throw new Error('Test');
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [SomeDir]});
|
||||
@ -351,7 +367,9 @@ describe('jit source mapping', () => {
|
||||
|
||||
@Component({...templateDecorator(template)})
|
||||
class MyComp {
|
||||
createError() { throw new Error('Test'); }
|
||||
createError() {
|
||||
throw new Error('Test');
|
||||
}
|
||||
}
|
||||
|
||||
const comp = resolveCompileAndCreateComponent(MyComp, template);
|
||||
@ -375,7 +393,9 @@ describe('jit source mapping', () => {
|
||||
|
||||
@Component({...templateDecorator(template)})
|
||||
class MyComp {
|
||||
createError() { throw new Error('Test'); }
|
||||
createError() {
|
||||
throw new Error('Test');
|
||||
}
|
||||
}
|
||||
|
||||
const comp = resolveCompileAndCreateComponent(MyComp, template);
|
||||
@ -414,7 +434,9 @@ describe('jit source mapping', () => {
|
||||
return TestBed.createComponent(comType);
|
||||
}
|
||||
|
||||
function createResolver(contents: string) { return (_url: string) => Promise.resolve(contents); }
|
||||
function createResolver(contents: string) {
|
||||
return (_url: string) => Promise.resolve(contents);
|
||||
}
|
||||
|
||||
function resolveCompileAndCreateComponent(comType: any, template: string) {
|
||||
resolveComponentResources(createResolver(template));
|
||||
@ -438,7 +460,9 @@ describe('jit source mapping', () => {
|
||||
|
||||
interface TestConfig {
|
||||
ngUrl: string;
|
||||
templateDecorator: (template: string) => { [key: string]: any };
|
||||
templateDecorator: (template: string) => {
|
||||
[key: string]: any
|
||||
};
|
||||
}
|
||||
|
||||
interface SourcePos {
|
||||
@ -448,8 +472,8 @@ describe('jit source mapping', () => {
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper class that captures the sources that have been JIT compiled.
|
||||
*/
|
||||
* A helper class that captures the sources that have been JIT compiled.
|
||||
*/
|
||||
class MockJitEvaluator extends JitEvaluator {
|
||||
sources: string[] = [];
|
||||
|
||||
@ -466,7 +490,7 @@ describe('jit source mapping', () => {
|
||||
*/
|
||||
getSourceMap(genFile: string): SourceMap {
|
||||
return this.sources.map(source => extractSourceMap(source))
|
||||
.find(map => !!(map && map.file === genFile)) !;
|
||||
.find(map => !!(map && map.file === genFile))!;
|
||||
}
|
||||
|
||||
getSourcePositionForStack(stack: string, genFile: string): SourcePos {
|
||||
@ -475,9 +499,9 @@ describe('jit source mapping', () => {
|
||||
.map(line => urlRegexp.exec(line))
|
||||
.filter(match => !!match)
|
||||
.map(match => ({
|
||||
file: match ![1],
|
||||
line: parseInt(match ![2], 10),
|
||||
column: parseInt(match ![3], 10)
|
||||
file: match![1],
|
||||
line: parseInt(match![2], 10),
|
||||
column: parseInt(match![3], 10)
|
||||
}))
|
||||
.shift();
|
||||
if (!pos) {
|
||||
@ -489,8 +513,8 @@ describe('jit source mapping', () => {
|
||||
}
|
||||
|
||||
function getErrorLoggerStack(e: Error): string {
|
||||
let logStack: string = undefined !;
|
||||
getErrorLogger(e)(<any>{error: () => logStack = new Error().stack !}, e.message);
|
||||
let logStack: string = undefined!;
|
||||
getErrorLogger(e)(<any>{error: () => logStack = new Error().stack!}, e.message);
|
||||
return logStack;
|
||||
}
|
||||
});
|
||||
|
@ -32,12 +32,15 @@ describe('SystemJsNgModuleLoader', () => {
|
||||
'prefixed/test/suffixed': {'NamedNgFactory': 'test module factory'}
|
||||
});
|
||||
});
|
||||
afterEach(() => { global['System'] = oldSystem; });
|
||||
afterEach(() => {
|
||||
global['System'] = oldSystem;
|
||||
});
|
||||
|
||||
it('loads a default factory by appending the factory suffix', async(() => {
|
||||
const loader = new SystemJsNgModuleLoader(new Compiler());
|
||||
loader.load('test').then(
|
||||
contents => { expect(contents).toBe('test module factory' as any); });
|
||||
loader.load('test').then(contents => {
|
||||
expect(contents).toBe('test module factory' as any);
|
||||
});
|
||||
}));
|
||||
it('loads a named factory by appending the factory suffix', async(() => {
|
||||
const loader = new SystemJsNgModuleLoader(new Compiler());
|
||||
@ -63,12 +66,15 @@ describe('SystemJsNgModuleLoader', () => {
|
||||
'test': {'default': 'test module', 'NamedModule': 'test NamedModule'},
|
||||
});
|
||||
});
|
||||
afterEach(() => { global['System'] = oldSystem; });
|
||||
afterEach(() => {
|
||||
global['System'] = oldSystem;
|
||||
});
|
||||
|
||||
it('loads a default module', async(() => {
|
||||
const loader = new SystemJsNgModuleLoader(new Compiler());
|
||||
loader.load('test').then(
|
||||
contents => { expect(contents.moduleType).toBe('test module' as any); });
|
||||
loader.load('test').then(contents => {
|
||||
expect(contents.moduleType).toBe('test module' as any);
|
||||
});
|
||||
}));
|
||||
it('loads a named module', async(() => {
|
||||
const loader = new SystemJsNgModuleLoader(new Compiler());
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
import {Attribute, ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentFactoryResolver, DebugElement, Directive, ElementRef, EmbeddedViewRef, Host, Inject, InjectionToken, Injector, Input, NgModule, Optional, Pipe, PipeTransform, Provider, Self, SkipSelf, TemplateRef, Type, ViewContainerRef} from '@angular/core';
|
||||
import {ComponentFixture, TestBed, fakeAsync} from '@angular/core/testing';
|
||||
import {ComponentFixture, fakeAsync, TestBed} from '@angular/core/testing';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
import {ivyEnabled, modifiedInIvy, obsoleteInIvy, onlyInIvy} from '@angular/private/testing';
|
||||
|
||||
@ -32,61 +32,81 @@ class CycleDirective {
|
||||
@Directive({selector: '[needsDirectiveFromSelf]'})
|
||||
class NeedsDirectiveFromSelf {
|
||||
dependency: SimpleDirective;
|
||||
constructor(@Self() dependency: SimpleDirective) { this.dependency = dependency; }
|
||||
constructor(@Self() dependency: SimpleDirective) {
|
||||
this.dependency = dependency;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[optionallyNeedsDirective]'})
|
||||
class OptionallyNeedsDirective {
|
||||
dependency: SimpleDirective;
|
||||
constructor(@Self() @Optional() dependency: SimpleDirective) { this.dependency = dependency; }
|
||||
constructor(@Self() @Optional() dependency: SimpleDirective) {
|
||||
this.dependency = dependency;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[needsComponentFromHost]'})
|
||||
class NeedsComponentFromHost {
|
||||
dependency: SimpleComponent;
|
||||
constructor(@Host() dependency: SimpleComponent) { this.dependency = dependency; }
|
||||
constructor(@Host() dependency: SimpleComponent) {
|
||||
this.dependency = dependency;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[needsDirectiveFromHost]'})
|
||||
class NeedsDirectiveFromHost {
|
||||
dependency: SimpleDirective;
|
||||
constructor(@Host() dependency: SimpleDirective) { this.dependency = dependency; }
|
||||
constructor(@Host() dependency: SimpleDirective) {
|
||||
this.dependency = dependency;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[needsDirective]'})
|
||||
class NeedsDirective {
|
||||
dependency: SimpleDirective;
|
||||
constructor(dependency: SimpleDirective) { this.dependency = dependency; }
|
||||
constructor(dependency: SimpleDirective) {
|
||||
this.dependency = dependency;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[needsService]'})
|
||||
class NeedsService {
|
||||
service: any;
|
||||
constructor(@Inject('service') service: any) { this.service = service; }
|
||||
constructor(@Inject('service') service: any) {
|
||||
this.service = service;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[needsAppService]'})
|
||||
class NeedsAppService {
|
||||
service: any;
|
||||
constructor(@Inject('appService') service: any) { this.service = service; }
|
||||
constructor(@Inject('appService') service: any) {
|
||||
this.service = service;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: '[needsHostAppService]', template: ''})
|
||||
class NeedsHostAppService {
|
||||
service: any;
|
||||
constructor(@Host() @Inject('appService') service: any) { this.service = service; }
|
||||
constructor(@Host() @Inject('appService') service: any) {
|
||||
this.service = service;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: '[needsServiceComponent]', template: ''})
|
||||
class NeedsServiceComponent {
|
||||
service: any;
|
||||
constructor(@Inject('service') service: any) { this.service = service; }
|
||||
constructor(@Inject('service') service: any) {
|
||||
this.service = service;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[needsServiceFromHost]'})
|
||||
class NeedsServiceFromHost {
|
||||
service: any;
|
||||
constructor(@Host() @Inject('service') service: any) { this.service = service; }
|
||||
constructor(@Host() @Inject('service') service: any) {
|
||||
this.service = service;
|
||||
}
|
||||
}
|
||||
|
||||
@Directive({selector: '[needsAttribute]'})
|
||||
@ -146,36 +166,50 @@ class PushComponentNeedsChangeDetectorRef {
|
||||
@Pipe({name: 'purePipe', pure: true})
|
||||
class PurePipe implements PipeTransform {
|
||||
constructor() {}
|
||||
transform(value: any): any { return this; }
|
||||
transform(value: any): any {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@Pipe({name: 'impurePipe', pure: false})
|
||||
class ImpurePipe implements PipeTransform {
|
||||
constructor() {}
|
||||
transform(value: any): any { return this; }
|
||||
transform(value: any): any {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@Pipe({name: 'pipeNeedsChangeDetectorRef'})
|
||||
class PipeNeedsChangeDetectorRef {
|
||||
constructor(public changeDetectorRef: ChangeDetectorRef) {}
|
||||
transform(value: any): any { return this; }
|
||||
transform(value: any): any {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@Pipe({name: 'pipeNeedsService'})
|
||||
export class PipeNeedsService implements PipeTransform {
|
||||
service: any;
|
||||
constructor(@Inject('service') service: any) { this.service = service; }
|
||||
transform(value: any): any { return this; }
|
||||
constructor(@Inject('service') service: any) {
|
||||
this.service = service;
|
||||
}
|
||||
transform(value: any): any {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@Pipe({name: 'duplicatePipe'})
|
||||
export class DuplicatePipe1 implements PipeTransform {
|
||||
transform(value: any): any { return this; }
|
||||
transform(value: any): any {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@Pipe({name: 'duplicatePipe'})
|
||||
export class DuplicatePipe2 implements PipeTransform {
|
||||
transform(value: any): any { return this; }
|
||||
transform(value: any): any {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({selector: 'root', template: ''})
|
||||
@ -183,15 +217,15 @@ class TestComp {
|
||||
}
|
||||
|
||||
function createComponentFixture<T>(
|
||||
template: string, providers?: Provider[] | null, comp?: Type<T>): ComponentFixture<T> {
|
||||
template: string, providers?: Provider[]|null, comp?: Type<T>): ComponentFixture<T> {
|
||||
if (!comp) {
|
||||
comp = <any>TestComp;
|
||||
}
|
||||
TestBed.overrideComponent(comp !, {set: {template}});
|
||||
TestBed.overrideComponent(comp!, {set: {template}});
|
||||
if (providers && providers.length) {
|
||||
TestBed.overrideComponent(comp !, {add: {providers: providers}});
|
||||
TestBed.overrideComponent(comp!, {add: {providers: providers}});
|
||||
}
|
||||
return TestBed.createComponent(comp !);
|
||||
return TestBed.createComponent(comp!);
|
||||
}
|
||||
|
||||
function createComponent(template: string, providers?: Provider[], comp?: Type<any>): DebugElement {
|
||||
@ -351,7 +385,6 @@ describe('View injector', () => {
|
||||
});
|
||||
|
||||
describe('injecting lazy providers into an eager provider via Injector.get', () => {
|
||||
|
||||
it('should inject providers that were declared before it', () => {
|
||||
@Component({
|
||||
template: '',
|
||||
@ -448,7 +481,9 @@ describe('View injector', () => {
|
||||
@Component({providers: [{provide: 'a', useFactory: () => 'aValue'}], template: ''})
|
||||
class SomeComponent {
|
||||
public a: string;
|
||||
constructor(injector: Injector) { this.a = injector.get('a'); }
|
||||
constructor(injector: Injector) {
|
||||
this.a = injector.get('a');
|
||||
}
|
||||
}
|
||||
|
||||
const comp = TestBed.configureTestingModule({declarations: [SomeComponent]})
|
||||
@ -461,8 +496,12 @@ describe('View injector', () => {
|
||||
let destroyed = false;
|
||||
|
||||
class SomeInjectable {
|
||||
constructor() { created = true; }
|
||||
ngOnDestroy() { destroyed = true; }
|
||||
constructor() {
|
||||
created = true;
|
||||
}
|
||||
ngOnDestroy() {
|
||||
destroyed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({providers: [SomeInjectable], template: ''})
|
||||
@ -910,7 +949,7 @@ describe('View injector', () => {
|
||||
|
||||
const testInjector = <Injector>{
|
||||
get: (token: any, notFoundValue: any) =>
|
||||
token === 'someToken' ? 'someNewValue' : notFoundValue
|
||||
token === 'someToken' ? 'someNewValue' : notFoundValue
|
||||
};
|
||||
|
||||
const compFactory = TestBed.configureTestingModule({imports: [TestModule]})
|
||||
@ -961,8 +1000,7 @@ describe('View injector', () => {
|
||||
|
||||
it('should inject ChangeDetectorRef into pipes', () => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations:
|
||||
[SimpleDirective, PipeNeedsChangeDetectorRef, DirectiveNeedsChangeDetectorRef]
|
||||
declarations: [SimpleDirective, PipeNeedsChangeDetectorRef, DirectiveNeedsChangeDetectorRef]
|
||||
});
|
||||
const el = createComponent(
|
||||
'<div [simpleDirective]="true | pipeNeedsChangeDetectorRef" directiveNeedsChangeDetectorRef></div>');
|
||||
|
Reference in New Issue
Block a user