From d9c39dcab0beae8ae0617e025d4331b294e8f8df Mon Sep 17 00:00:00 2001 From: Kara Erickson Date: Tue, 16 Apr 2019 19:03:40 -0700 Subject: [PATCH] fix(ivy): directiveInject should fall back to inject (#29948) If a component has its definition set by defineComponent (as opposed to JIT getter), then it will generate a factory that uses directiveInject() to retrieve its dependencies. This can be problematic in test code because tests could use the injection utility before bootstrapping the component, and directiveInject() relies on the view having been created. This commit tweaks directiveInject() to fall back to inject() if the view has not been created. This will allow injection to work in tests even if it is called before the component is bootstrapped. PR Close #29948 --- packages/core/src/render3/instructions/di.ts | 10 ++++++++-- packages/core/test/acceptance/providers_spec.ts | 13 ++++++++++++- .../test/bundling/todo/bundle.golden_symbols.json | 9 +++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/packages/core/src/render3/instructions/di.ts b/packages/core/src/render3/instructions/di.ts index 49f60e7a1f..7bb92341a7 100644 --- a/packages/core/src/render3/instructions/di.ts +++ b/packages/core/src/render3/instructions/di.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ import {InjectFlags, InjectionToken, resolveForwardRef} from '../../di'; +import {ɵɵinject} from '../../di/injector_compatibility'; import {Type} from '../../interface/type'; import {getOrCreateInjectable, injectAttributeImpl} from '../di'; import {TContainerNode, TElementContainerNode, TElementNode} from '../interfaces/node'; @@ -40,9 +41,14 @@ export function ɵɵdirectiveInject(token: Type| InjectionToken, flags: export function ɵɵdirectiveInject( token: Type| InjectionToken, flags = InjectFlags.Default): T|null { token = resolveForwardRef(token); + const lView = getLView(); + // Fall back to inject() if view hasn't been created. This situation can happen in tests + // if inject utilities are used before bootstrapping. + if (lView == null) return ɵɵinject(token, flags); + return getOrCreateInjectable( - getPreviousOrParentTNode() as TElementNode | TContainerNode | TElementContainerNode, - getLView(), token, flags); + getPreviousOrParentTNode() as TElementNode | TContainerNode | TElementContainerNode, lView, + token, flags); } /** diff --git a/packages/core/test/acceptance/providers_spec.ts b/packages/core/test/acceptance/providers_spec.ts index 9f91576bb8..41060ea77f 100644 --- a/packages/core/test/acceptance/providers_spec.ts +++ b/packages/core/test/acceptance/providers_spec.ts @@ -7,7 +7,7 @@ */ import {Component, Directive, Inject, Injectable, InjectionToken} from '@angular/core'; -import {TestBed} from '@angular/core/testing'; +import {TestBed, async, inject} from '@angular/core/testing'; import {By} from '@angular/platform-browser'; import {onlyInIvy} from '@angular/private/testing'; @@ -271,5 +271,16 @@ describe('providers', () => { const myCompInstance = fixture.debugElement.query(By.css('div')).injector.get(MyDir); expect(myCompInstance.svc.value).toEqual('some value'); }); + + describe('injection without bootstrapping', () => { + beforeEach(() => { + TestBed.configureTestingModule({declarations: [MyComp], providers: [MyComp, MyService]}); + }); + + it('should support injecting without bootstrapping', + async(inject([MyComp, MyService], (comp: MyComp, service: MyService) => { + expect(comp.svc.value).toEqual('some value'); + }))); + }); }); }); diff --git a/packages/core/test/bundling/todo/bundle.golden_symbols.json b/packages/core/test/bundling/todo/bundle.golden_symbols.json index 8b4cd066a4..5aa942157e 100644 --- a/packages/core/test/bundling/todo/bundle.golden_symbols.json +++ b/packages/core/test/bundling/todo/bundle.golden_symbols.json @@ -359,6 +359,9 @@ { "name": "_c9" }, + { + "name": "_currentInjector" + }, { "name": "_currentNamespace" }, @@ -902,6 +905,9 @@ { "name": "injectElementRef" }, + { + "name": "injectInjectorOnly" + }, { "name": "injectRootLimpMode" }, @@ -1355,6 +1361,9 @@ { "name": "ɵɵgetCurrentView" }, + { + "name": "ɵɵinject" + }, { "name": "ɵɵinterpolation1" },