test(ivy): finish root cause analysis for @angular/core TestBed failing tests (#27510)

PR Close #27510
This commit is contained in:
Pawel Kozlowski 2018-12-06 15:20:11 +01:00 committed by Igor Minar
parent 8fa7e93c30
commit 913563a41d
9 changed files with 130 additions and 123 deletions

View File

@ -9,7 +9,7 @@
import {ApplicationRef, Component, DoCheck, NgModule, OnInit, TestabilityRegistry, ɵivyEnabled as ivyEnabled} from '@angular/core';
import {getTestBed} from '@angular/core/testing';
import {BrowserModule} from '@angular/platform-browser';
import {fixmeIvy, withBody} from '@angular/private/testing';
import {withBody} from '@angular/private/testing';
import {NgModuleFactory} from '../src/render3/ng_module_ref';
@ -35,26 +35,27 @@ ivyEnabled && describe('ApplicationRef bootstrap', () => {
class MyAppModule {
}
fixmeIvy('unknown').it(
'should bootstrap hello world', withBody('<hello-world></hello-world>', async() => {
const MyAppModuleFactory = new NgModuleFactory(MyAppModule);
const moduleRef = await getTestBed().platform.bootstrapModuleFactory(
MyAppModuleFactory, {ngZone: 'noop'});
const appRef = moduleRef.injector.get(ApplicationRef);
const helloWorldComponent = appRef.components[0].instance as HelloWorldComponent;
expect(document.body.innerHTML)
.toEqual('<hello-world><div>Hello World</div></hello-world>');
expect(helloWorldComponent.log).toEqual(['OnInit', 'DoCheck']);
it('should bootstrap hello world', withBody('<hello-world></hello-world>', async() => {
const MyAppModuleFactory = new NgModuleFactory(MyAppModule);
const moduleRef =
await getTestBed().platform.bootstrapModuleFactory(MyAppModuleFactory, {ngZone: 'noop'});
const appRef = moduleRef.injector.get(ApplicationRef);
const helloWorldComponent = appRef.components[0].instance as HelloWorldComponent;
expect(document.body.innerHTML)
.toEqual(
'<hello-world ng-version="0.0.0-PLACEHOLDER"><div>Hello World</div></hello-world>');
expect(helloWorldComponent.log).toEqual(['OnInit', 'DoCheck']);
helloWorldComponent.name = 'Mundo';
appRef.tick();
expect(document.body.innerHTML)
.toEqual('<hello-world><div>Hello Mundo</div></hello-world>');
expect(helloWorldComponent.log).toEqual(['OnInit', 'DoCheck', 'DoCheck']);
helloWorldComponent.name = 'Mundo';
appRef.tick();
expect(document.body.innerHTML)
.toEqual(
'<hello-world ng-version="0.0.0-PLACEHOLDER"><div>Hello Mundo</div></hello-world>');
expect(helloWorldComponent.log).toEqual(['OnInit', 'DoCheck', 'DoCheck']);
// Cleanup TestabilityRegistry
const registry: TestabilityRegistry = getTestBed().get(TestabilityRegistry);
registry.unregisterAllApplications();
}));
// Cleanup TestabilityRegistry
const registry: TestabilityRegistry = getTestBed().get(TestabilityRegistry);
registry.unregisterAllApplications();
}));
});

View File

@ -33,10 +33,11 @@ const ProxyZoneSpec: {assertPresent: () => void} = (Zone as any)['ProxyZoneSpec'
})('foo', 'bar');
});
fixmeIvy('unknown').it(
'should work with inject()', fakeAsync(inject([Parser], (parser: any /** TODO #9100 */) => {
expect(parser).toBeAnInstanceOf(Parser);
})));
fixmeIvy('FW-806: Ivy\'s TestBed implementation doesn\'t inject compiler-related tokens')
.it('should work with inject()',
fakeAsync(inject([Parser], (parser: any /** TODO #9100 */) => {
expect(parser).toBeAnInstanceOf(Parser);
})));
it('should throw on nested calls', () => {
expect(() => {

View File

@ -69,20 +69,22 @@ function declareTests(config?: {useJit: boolean}) {
expect(childComp.cfr.resolveComponentFactory(ChildComp) !.componentType).toBe(ChildComp);
});
fixmeIvy('unknown').it(
'should not be able to get components from a parent component (content hierarchy)', () => {
TestBed.overrideComponent(
MainComp, {set: {template: '<child><nested></nested></child>'}});
TestBed.overrideComponent(ChildComp, {set: {template: '<ng-content></ng-content>'}});
fixmeIvy(
'FW-805: Ivy\'s implementation of ComponentFactoryResolver doesn\'t have checks present in the view engine')
.it('should not be able to get components from a parent component (content hierarchy)',
() => {
TestBed.overrideComponent(
MainComp, {set: {template: '<child><nested></nested></child>'}});
TestBed.overrideComponent(ChildComp, {set: {template: '<ng-content></ng-content>'}});
const compFixture = TestBed.createComponent(MainComp);
const nestedChildCompEl = compFixture.debugElement.children[0].children[0];
const nestedChildComp: NestedChildComp = nestedChildCompEl.componentInstance;
expect(nestedChildComp.cfr.resolveComponentFactory(ChildComp) !.componentType)
.toBe(ChildComp);
expect(() => nestedChildComp.cfr.resolveComponentFactory(NestedChildComp))
.toThrow(noComponentFactoryError(NestedChildComp));
});
const compFixture = TestBed.createComponent(MainComp);
const nestedChildCompEl = compFixture.debugElement.children[0].children[0];
const nestedChildComp: NestedChildComp = nestedChildCompEl.componentInstance;
expect(nestedChildComp.cfr.resolveComponentFactory(ChildComp) !.componentType)
.toBe(ChildComp);
expect(() => nestedChildComp.cfr.resolveComponentFactory(NestedChildComp))
.toThrow(noComponentFactoryError(NestedChildComp));
});
});
}

View File

@ -1331,21 +1331,24 @@ function declareTests(config?: {useJit: boolean}) {
expect(comp.injectable).toBeAnInstanceOf(InjectableService);
});
fixmeIvy('unknown').it('should support viewProviders', () => {
TestBed.configureTestingModule({
declarations: [MyComp, DirectiveProvidingInjectableInView, DirectiveConsumingInjectable],
schemas: [NO_ERRORS_SCHEMA],
});
const template = `
fixmeIvy(
'FW-804: Injection of view providers with the @Host annotation works differently in ivy')
.it('should support viewProviders', () => {
TestBed.configureTestingModule({
declarations:
[MyComp, DirectiveProvidingInjectableInView, DirectiveConsumingInjectable],
schemas: [NO_ERRORS_SCHEMA],
});
const template = `
<directive-consuming-injectable #consuming>
</directive-consuming-injectable>
`;
TestBed.overrideComponent(DirectiveProvidingInjectableInView, {set: {template}});
const fixture = TestBed.createComponent(DirectiveProvidingInjectableInView);
TestBed.overrideComponent(DirectiveProvidingInjectableInView, {set: {template}});
const fixture = TestBed.createComponent(DirectiveProvidingInjectableInView);
const comp = fixture.debugElement.children[0].references !['consuming'];
expect(comp.injectable).toBeAnInstanceOf(InjectableService);
});
const comp = fixture.debugElement.children[0].references !['consuming'];
expect(comp.injectable).toBeAnInstanceOf(InjectableService);
});
it('should support unbounded lookup', () => {
TestBed.configureTestingModule({

View File

@ -745,12 +745,11 @@ function declareTests(config?: {useJit: boolean}) {
expect(cars[0]).toBe(injector.get(SportsCar));
});
fixmeIvy('FW-682: Compiler error handling')
.it('should throw when the aliased provider does not exist', () => {
const injector = createInjector([{provide: 'car', useExisting: SportsCar}]);
const e = `NullInjectorError: No provider for ${stringify(SportsCar)}!`;
expect(() => injector.get('car')).toThrowError(e);
});
it('should throw when the aliased provider does not exist', () => {
const injector = createInjector([{provide: 'car', useExisting: SportsCar}]);
const e = `NullInjectorError: No provider for ${stringify(SportsCar)}!`;
expect(() => injector.get('car')).toThrowError(e);
});
it('should handle forwardRef in useExisting', () => {
const injector = createInjector([

View File

@ -75,46 +75,43 @@ function declareTests(config?: {useJit: boolean}) {
expect(CountingPipe.calls).toBe(1);
});
fixmeIvy('FW-756: Pipes and directives from imported modules are not taken into account')
.it('should only update the bound property when using asyncPipe - #15205',
fakeAsync(() => {
@Component({template: '<div myDir [a]="p | async" [b]="2"></div>'})
class MyComp {
p = Promise.resolve(1);
}
it('should only update the bound property when using asyncPipe - #15205', fakeAsync(() => {
@Component({template: '<div myDir [a]="p | async" [b]="2"></div>'})
class MyComp {
p = Promise.resolve(1);
}
@Directive({selector: '[myDir]'})
class MyDir {
setterCalls: {[key: string]: any} = {};
// TODO(issue/24571): remove '!'.
changes !: SimpleChanges;
@Directive({selector: '[myDir]'})
class MyDir {
setterCalls: {[key: string]: any} = {};
// TODO(issue/24571): remove '!'.
changes !: SimpleChanges;
@Input()
set a(v: number) { this.setterCalls['a'] = v; }
@Input()
set b(v: number) { this.setterCalls['b'] = v; }
@Input()
set a(v: number) { this.setterCalls['a'] = v; }
@Input()
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]});
const fixture = TestBed.createComponent(MyComp);
const dir =
fixture.debugElement.query(By.directive(MyDir)).injector.get(MyDir) as MyDir;
TestBed.configureTestingModule({declarations: [MyDir, MyComp]});
const fixture = TestBed.createComponent(MyComp);
const dir = fixture.debugElement.query(By.directive(MyDir)).injector.get(MyDir) as MyDir;
fixture.detectChanges();
expect(dir.setterCalls).toEqual({'a': null, 'b': 2});
expect(Object.keys(dir.changes)).toEqual(['a', 'b']);
fixture.detectChanges();
expect(dir.setterCalls).toEqual({'a': null, 'b': 2});
expect(Object.keys(dir.changes)).toEqual(['a', 'b']);
dir.setterCalls = {};
dir.changes = {};
dir.setterCalls = {};
dir.changes = {};
tick();
fixture.detectChanges();
tick();
fixture.detectChanges();
expect(dir.setterCalls).toEqual({'a': 1});
expect(Object.keys(dir.changes)).toEqual(['a']);
}));
expect(dir.setterCalls).toEqual({'a': 1});
expect(Object.keys(dir.changes)).toEqual(['a']);
}));
it('should only evaluate methods once - #10639', () => {
TestBed.configureTestingModule({declarations: [MyCountingComp]});

View File

@ -138,34 +138,36 @@ import {fixmeIvy} from '@angular/private/testing';
expect(instance.dep instanceof Dep).toBeTruthy();
});
fixmeIvy('unknown').it('should not inject deps from sibling root elements', () => {
const rootElNodes = [
elementDef(0, NodeFlags.None, null, null, 1, 'span'),
directiveDef(1, NodeFlags.None, null, 0, Dep, []),
elementDef(2, NodeFlags.None, null, null, 1, 'span'),
directiveDef(3, NodeFlags.None, null, 0, SomeService, [Dep]),
];
fixmeIvy(
'FW-807: NgModule injector doesn\'t report full search path if a token is not found')
.it('should not inject deps from sibling root elements', () => {
const rootElNodes = [
elementDef(0, NodeFlags.None, null, null, 1, 'span'),
directiveDef(1, NodeFlags.None, null, 0, Dep, []),
elementDef(2, NodeFlags.None, null, null, 1, 'span'),
directiveDef(3, NodeFlags.None, null, 0, SomeService, [Dep]),
];
expect(() => createAndGetRootNodes(compViewDef(rootElNodes)))
.toThrowError(
'StaticInjectorError(DynamicTestModule)[SomeService -> Dep]: \n' +
' StaticInjectorError(Platform: core)[SomeService -> Dep]: \n' +
' NullInjectorError: No provider for Dep!');
expect(() => createAndGetRootNodes(compViewDef(rootElNodes)))
.toThrowError(
'StaticInjectorError(DynamicTestModule)[SomeService -> Dep]: \n' +
' StaticInjectorError(Platform: core)[SomeService -> Dep]: \n' +
' NullInjectorError: No provider for Dep!');
const nonRootElNodes = [
elementDef(0, NodeFlags.None, null, null, 4, 'span'),
elementDef(1, NodeFlags.None, null, null, 1, 'span'),
directiveDef(2, NodeFlags.None, null, 0, Dep, []),
elementDef(3, NodeFlags.None, null, null, 1, 'span'),
directiveDef(4, NodeFlags.None, null, 0, SomeService, [Dep]),
];
const nonRootElNodes = [
elementDef(0, NodeFlags.None, null, null, 4, 'span'),
elementDef(1, NodeFlags.None, null, null, 1, 'span'),
directiveDef(2, NodeFlags.None, null, 0, Dep, []),
elementDef(3, NodeFlags.None, null, null, 1, 'span'),
directiveDef(4, NodeFlags.None, null, 0, SomeService, [Dep]),
];
expect(() => createAndGetRootNodes(compViewDef(nonRootElNodes)))
.toThrowError(
'StaticInjectorError(DynamicTestModule)[SomeService -> Dep]: \n' +
' StaticInjectorError(Platform: core)[SomeService -> Dep]: \n' +
' NullInjectorError: No provider for Dep!');
});
expect(() => createAndGetRootNodes(compViewDef(nonRootElNodes)))
.toThrowError(
'StaticInjectorError(DynamicTestModule)[SomeService -> Dep]: \n' +
' StaticInjectorError(Platform: core)[SomeService -> Dep]: \n' +
' NullInjectorError: No provider for Dep!');
});
it('should inject from a parent element in a parent view', () => {
createAndGetRootNodes(compViewDef([
@ -181,16 +183,17 @@ import {fixmeIvy} from '@angular/private/testing';
expect(instance.dep instanceof Dep).toBeTruthy();
});
fixmeIvy('unknown').it('should throw for missing dependencies', () => {
expect(() => createAndGetRootNodes(compViewDef([
elementDef(0, NodeFlags.None, null, null, 1, 'span'),
directiveDef(1, NodeFlags.None, null, 0, SomeService, ['nonExistingDep'])
])))
.toThrowError(
'StaticInjectorError(DynamicTestModule)[nonExistingDep]: \n' +
' StaticInjectorError(Platform: core)[nonExistingDep]: \n' +
' NullInjectorError: No provider for nonExistingDep!');
});
fixmeIvy('FW-807: NgModule injector don\'t report full search path if a token is not found')
.it('should throw for missing dependencies', () => {
expect(() => createAndGetRootNodes(compViewDef([
elementDef(0, NodeFlags.None, null, null, 1, 'span'),
directiveDef(1, NodeFlags.None, null, 0, SomeService, ['nonExistingDep'])
])))
.toThrowError(
'StaticInjectorError(DynamicTestModule)[nonExistingDep]: \n' +
' StaticInjectorError(Platform: core)[nonExistingDep]: \n' +
' NullInjectorError: No provider for nonExistingDep!');
});
it('should use null for optional missing dependencies', () => {
createAndGetRootNodes(compViewDef([

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ApplicationInitStatus, Component, Directive, Injector, NgModule, NgZone, Pipe, PlatformRef, Provider, RendererFactory2, SchemaMetadata, Type, ɵInjectableDef as InjectableDef, ɵNgModuleDef as NgModuleDef, ɵNgModuleTransitiveScopes as NgModuleTransitiveScopes, ɵRender3ComponentFactory as ComponentFactory, ɵRender3NgModuleRef as NgModuleRef, ɵcompileComponent as compileComponent, ɵcompileDirective as compileDirective, ɵcompileNgModuleDefs as compileNgModuleDefs, ɵcompilePipe as compilePipe, ɵgetInjectableDef as getInjectableDef, ɵpatchComponentDefWithScope as patchComponentDefWithScope, ɵstringify as stringify} from '@angular/core';
import {ApplicationInitStatus, Component, Directive, Injector, NgModule, NgZone, Pipe, PlatformRef, Provider, SchemaMetadata, Type, ɵInjectableDef as InjectableDef, ɵNgModuleDef as NgModuleDef, ɵNgModuleTransitiveScopes as NgModuleTransitiveScopes, ɵRender3ComponentFactory as ComponentFactory, ɵRender3NgModuleRef as NgModuleRef, ɵcompileComponent as compileComponent, ɵcompileDirective as compileDirective, ɵcompileNgModuleDefs as compileNgModuleDefs, ɵcompilePipe as compilePipe, ɵgetInjectableDef as getInjectableDef, ɵpatchComponentDefWithScope as patchComponentDefWithScope, ɵstringify as stringify} from '@angular/core';
import {ComponentFixture} from './component_fixture';
import {MetadataOverride} from './metadata_override';

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ApplicationInitStatus, CompilerOptions, Component, Directive, Injector, NgModule, NgModuleFactory, NgModuleRef, NgZone, Optional, Pipe, PlatformRef, Provider, SchemaMetadata, SkipSelf, StaticProvider, Type, ɵAPP_ROOT as APP_ROOT, ɵDepFlags as DepFlags, ɵInjectableDef as InjectableDef, ɵNodeFlags as NodeFlags, ɵclearOverrides as clearOverrides, ɵgetComponentViewDefinitionFactory as getComponentViewDefinitionFactory, ɵgetInjectableDef as getInjectableDef, ɵivyEnabled as ivyEnabled, ɵoverrideComponentView as overrideComponentView, ɵoverrideProvider as overrideProvider, ɵstringify as stringify} from '@angular/core';
import {ApplicationInitStatus, CompilerOptions, Component, Directive, Injector, NgModule, NgModuleFactory, NgModuleRef, NgZone, Optional, Pipe, PlatformRef, Provider, SchemaMetadata, SkipSelf, StaticProvider, Type, ɵAPP_ROOT as APP_ROOT, ɵDepFlags as DepFlags, ɵInjectableDef as InjectableDef, ɵNodeFlags as NodeFlags, ɵclearOverrides as clearOverrides, ɵgetInjectableDef as getInjectableDef, ɵivyEnabled as ivyEnabled, ɵoverrideComponentView as overrideComponentView, ɵoverrideProvider as overrideProvider, ɵstringify as stringify} from '@angular/core';
import {AsyncTestCompleter} from './async_test_completer';
import {ComponentFixture} from './component_fixture';
@ -15,6 +15,7 @@ import {TestBedRender3, _getTestBedRender3} from './r3_test_bed';
import {ComponentFixtureAutoDetect, ComponentFixtureNoNgZone, TestBedStatic, TestComponentRenderer, TestModuleMetadata} from './test_bed_common';
import {TestingCompiler, TestingCompilerFactory} from './test_compiler';
const UNDEFINED = new Object();