fix(ivy): support providing components and dirs in tests (#29945)

Previous to this commit, providing a component or directive in a test
module without @Injectable() would throw because the injectable factory
would not be found. Providing components in tests in addition to declaring
or importing them is not necessary, but it should not throw an error.

This commit ensures factory data is saved when component defs and directive
defs are created, which allows them to be processed by the module injector.

Note that bootstrapping is still required for this setup to work because
directiveInject() does not support cases where the view has not been
created. This case will be handled in a future commit.

PR Close #29945
This commit is contained in:
Kara Erickson
2019-04-16 16:58:44 -07:00
committed by Alex Rickabaugh
parent ab6036272c
commit ca2462cff7
7 changed files with 129 additions and 53 deletions

View File

@ -9,6 +9,7 @@
import '../util/ng_dev_mode';
import {ChangeDetectionStrategy} from '../change_detection/constants';
import {NG_INJECTABLE_DEF, ɵɵdefineInjectable} from '../di/interface/defs';
import {Mutable, Type} from '../interface/type';
import {NgModuleDef} from '../metadata/ng_module';
import {SchemaMetadata} from '../metadata/schema';
@ -300,7 +301,17 @@ export function ɵɵdefineComponent<T>(componentDefinition: {
def.pipeDefs = pipeTypes ?
() => (typeof pipeTypes === 'function' ? pipeTypes() : pipeTypes).map(extractPipeDef) :
null;
// Add ngInjectableDef so components are reachable through the module injector by default
// (unless it has already been set by the @Injectable decorator). This is mostly to
// support injecting components in tests. In real application code, components should
// be retrieved through the node injector, so this isn't a problem.
if (!type.hasOwnProperty(NG_INJECTABLE_DEF)) {
(type as any)[NG_INJECTABLE_DEF] =
ɵɵdefineInjectable<T>({factory: componentDefinition.factory as() => T});
}
}) as never;
return def as never;
}

View File

@ -9,6 +9,7 @@
import {R3DirectiveMetadataFacade, getCompilerFacade} from '../../compiler/compiler_facade';
import {R3ComponentMetadataFacade, R3QueryMetadataFacade} from '../../compiler/compiler_facade_interface';
import {resolveForwardRef} from '../../di/forward_ref';
import {compileInjectable} from '../../di/jit/injectable';
import {getReflect, reflectDependencies} from '../../di/jit/util';
import {Type} from '../../interface/type';
import {Query} from '../../metadata/di';
@ -93,6 +94,12 @@ export function compileComponent(type: Type<any>, metadata: Component): void {
// Make the property configurable in dev mode to allow overriding in tests
configurable: !!ngDevMode,
});
// Add ngInjectableDef so components are reachable through the module injector by default
// This is mostly to support injecting components in tests. In real application code,
// components should be retrieved through the node injector, so this isn't a problem.
compileInjectable(type);
}
function hasSelectorScope<T>(component: Type<T>): component is Type<T>&
@ -125,6 +132,11 @@ export function compileDirective(type: Type<any>, directive: Directive): void {
// Make the property configurable in dev mode to allow overriding in tests
configurable: !!ngDevMode,
});
// Add ngInjectableDef so directives are reachable through the module injector by default
// This is mostly to support injecting directives in tests. In real application code,
// directives should be retrieved through the node injector, so this isn't a problem.
compileInjectable(type);
}
export function extendsDirectlyFromObject(type: Type<any>): boolean {