From 913563a41d7fbd999cd4496b0956290bb4ae7daa Mon Sep 17 00:00:00 2001 From: Pawel Kozlowski Date: Thu, 6 Dec 2018 15:20:11 +0100 Subject: [PATCH] test(ivy): finish root cause analysis for @angular/core TestBed failing tests (#27510) PR Close #27510 --- .../test/application_ref_integration_spec.ts | 41 ++++++----- packages/core/test/fake_async_spec.ts | 9 ++- .../entry_components_integration_spec.ts | 28 +++---- packages/core/test/linker/integration_spec.ts | 25 ++++--- .../test/linker/ng_module_integration_spec.ts | 11 ++- .../linker/regression_integration_spec.ts | 61 ++++++++-------- packages/core/test/view/provider_spec.ts | 73 ++++++++++--------- packages/core/testing/src/r3_test_bed.ts | 2 +- packages/core/testing/src/test_bed.ts | 3 +- 9 files changed, 130 insertions(+), 123 deletions(-) diff --git a/packages/core/test/application_ref_integration_spec.ts b/packages/core/test/application_ref_integration_spec.ts index f66e946cfb..bf17e89a56 100644 --- a/packages/core/test/application_ref_integration_spec.ts +++ b/packages/core/test/application_ref_integration_spec.ts @@ -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('', 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
'); - expect(helloWorldComponent.log).toEqual(['OnInit', 'DoCheck']); + it('should bootstrap hello world', withBody('', 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
'); + expect(helloWorldComponent.log).toEqual(['OnInit', 'DoCheck']); - helloWorldComponent.name = 'Mundo'; - appRef.tick(); - expect(document.body.innerHTML) - .toEqual('
Hello Mundo
'); - expect(helloWorldComponent.log).toEqual(['OnInit', 'DoCheck', 'DoCheck']); + helloWorldComponent.name = 'Mundo'; + appRef.tick(); + expect(document.body.innerHTML) + .toEqual( + '
Hello Mundo
'); + 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(); + })); }); diff --git a/packages/core/test/fake_async_spec.ts b/packages/core/test/fake_async_spec.ts index 84c9c05fdb..c98498fd22 100644 --- a/packages/core/test/fake_async_spec.ts +++ b/packages/core/test/fake_async_spec.ts @@ -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(() => { diff --git a/packages/core/test/linker/entry_components_integration_spec.ts b/packages/core/test/linker/entry_components_integration_spec.ts index ec9bf38a37..8ee1bf9ccb 100644 --- a/packages/core/test/linker/entry_components_integration_spec.ts +++ b/packages/core/test/linker/entry_components_integration_spec.ts @@ -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: ''}}); - TestBed.overrideComponent(ChildComp, {set: {template: ''}}); + 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: ''}}); + TestBed.overrideComponent(ChildComp, {set: {template: ''}}); - 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)); + }); }); } diff --git a/packages/core/test/linker/integration_spec.ts b/packages/core/test/linker/integration_spec.ts index c94cfbd785..63a7186d81 100644 --- a/packages/core/test/linker/integration_spec.ts +++ b/packages/core/test/linker/integration_spec.ts @@ -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 = ` `; - 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({ diff --git a/packages/core/test/linker/ng_module_integration_spec.ts b/packages/core/test/linker/ng_module_integration_spec.ts index 2846d8286a..eef50b93db 100644 --- a/packages/core/test/linker/ng_module_integration_spec.ts +++ b/packages/core/test/linker/ng_module_integration_spec.ts @@ -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([ diff --git a/packages/core/test/linker/regression_integration_spec.ts b/packages/core/test/linker/regression_integration_spec.ts index aee4ea8f01..02f5fa71be 100644 --- a/packages/core/test/linker/regression_integration_spec.ts +++ b/packages/core/test/linker/regression_integration_spec.ts @@ -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: '
'}) - class MyComp { - p = Promise.resolve(1); - } + it('should only update the bound property when using asyncPipe - #15205', fakeAsync(() => { + @Component({template: '
'}) + 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]}); diff --git a/packages/core/test/view/provider_spec.ts b/packages/core/test/view/provider_spec.ts index 95fe2d790c..52d044a946 100644 --- a/packages/core/test/view/provider_spec.ts +++ b/packages/core/test/view/provider_spec.ts @@ -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([ diff --git a/packages/core/testing/src/r3_test_bed.ts b/packages/core/testing/src/r3_test_bed.ts index 634a61b25d..398f5a3203 100644 --- a/packages/core/testing/src/r3_test_bed.ts +++ b/packages/core/testing/src/r3_test_bed.ts @@ -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'; diff --git a/packages/core/testing/src/test_bed.ts b/packages/core/testing/src/test_bed.ts index 52c6bccdf3..40edac623e 100644 --- a/packages/core/testing/src/test_bed.ts +++ b/packages/core/testing/src/test_bed.ts @@ -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();