Skip to content

Commit

Permalink
feat(core): support object-based DI flags in TestBed.inject()
Browse files Browse the repository at this point in the history
This commit applies the changes similar to the ones performed for the `inject()` function in df246bb.

The `TestBed.inject` function is updated to use previously added object-based API for options: now the flags argument supports passing an object which configures injection flags.

DEPRECATED:

The bit field signature of `TestBed.inject()` has been deprecated, in favor of the new options object.
  • Loading branch information
AndrewKushnir committed Jul 13, 2022
1 parent ccac3f5 commit c64f54d
Show file tree
Hide file tree
Showing 17 changed files with 117 additions and 12 deletions.
4 changes: 2 additions & 2 deletions aio/tests/e2e/src/api-pages.e2e-spec.ts
Expand Up @@ -76,11 +76,11 @@ describe('Api pages', () => {

it('should show all overloads of interface methods', async () => {
await page.navigateTo('api/core/testing/TestBedStatic');
expect(await (await page.getInstanceMethodOverloads('inject')).length).toEqual(2);
expect(await (await page.getInstanceMethodOverloads('inject')).length).toEqual(4);
});

it('should show all overloads of pseudo-class methods', async () => {
await page.navigateTo('api/core/testing/TestBed');
expect(await (await page.getInstanceMethodOverloads('inject')).length).toEqual(2);
expect(await (await page.getInstanceMethodOverloads('inject')).length).toEqual(4);
});
});
21 changes: 19 additions & 2 deletions goldens/public-api/core/testing/index.md
Expand Up @@ -12,6 +12,7 @@ import { Directive } from '@angular/core';
import { ElementRef } from '@angular/core';
import { InjectFlags } from '@angular/core';
import { InjectionToken } from '@angular/core';
import { InjectOptions } from '@angular/core';
import { NgModule } from '@angular/core';
import { NgZone } from '@angular/core';
import { Pipe } from '@angular/core';
Expand Down Expand Up @@ -115,8 +116,16 @@ export interface TestBed {
get(token: any, notFoundValue?: any): any;
initTestEnvironment(ngModule: Type<any> | Type<any>[], platform: PlatformRef, options?: TestEnvironmentOptions): void;
// (undocumented)
inject<T>(token: ProviderToken<T>, notFoundValue?: T, flags?: InjectFlags): T;
inject<T>(token: ProviderToken<T>, notFoundValue: undefined, options: InjectOptions & {
optional: true;
}): T | null;
// (undocumented)
inject<T>(token: ProviderToken<T>, notFoundValue?: T, options?: InjectOptions): T;
// (undocumented)
inject<T>(token: ProviderToken<T>, notFoundValue: null, options?: InjectOptions): T | null;
// @deprecated (undocumented)
inject<T>(token: ProviderToken<T>, notFoundValue?: T, flags?: InjectFlags): T;
// @deprecated (undocumented)
inject<T>(token: ProviderToken<T>, notFoundValue: null, flags?: InjectFlags): T | null;
// (undocumented)
ngModule: Type<any> | Type<any>[];
Expand Down Expand Up @@ -172,8 +181,16 @@ export interface TestBedStatic {
get(token: any, notFoundValue?: any): any;
initTestEnvironment(ngModule: Type<any> | Type<any>[], platform: PlatformRef, options?: TestEnvironmentOptions): TestBed;
// (undocumented)
inject<T>(token: ProviderToken<T>, notFoundValue?: T, flags?: InjectFlags): T;
inject<T>(token: ProviderToken<T>, notFoundValue: undefined, options: InjectOptions & {
optional: true;
}): T | null;
// (undocumented)
inject<T>(token: ProviderToken<T>, notFoundValue?: T, options?: InjectOptions): T;
// (undocumented)
inject<T>(token: ProviderToken<T>, notFoundValue: null, options?: InjectOptions): T | null;
// @deprecated (undocumented)
inject<T>(token: ProviderToken<T>, notFoundValue?: T, flags?: InjectFlags): T;
// @deprecated (undocumented)
inject<T>(token: ProviderToken<T>, notFoundValue: null, flags?: InjectFlags): T | null;
// (undocumented)
overrideComponent(component: Type<any>, override: MetadataOverride<Component>): TestBedStatic;
Expand Down
2 changes: 1 addition & 1 deletion goldens/size-tracking/integration-payloads.json
Expand Up @@ -41,7 +41,7 @@
"forms": {
"uncompressed": {
"runtime": 1063,
"main": 157479,
"main": 157996,
"polyfills": 33804
}
},
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/core_private_export.ts
Expand Up @@ -12,7 +12,7 @@ export {defaultIterableDiffers as ɵdefaultIterableDiffers, defaultKeyValueDiffe
export {ChangeDetectorStatus as ɵChangeDetectorStatus, isDefaultChangeDetectionStrategy as ɵisDefaultChangeDetectionStrategy} from './change_detection/constants';
export {Console as ɵConsole} from './console';
export {getDebugNodeR2 as ɵgetDebugNodeR2} from './debug/debug_node';
export {setCurrentInjector as ɵsetCurrentInjector} from './di/injector_compatibility';
export {convertToBitFlags as ɵconvertToBitFlags, setCurrentInjector as ɵsetCurrentInjector} from './di/injector_compatibility';
export {getInjectableDef as ɵgetInjectableDef, ɵɵInjectableDeclaration, ɵɵInjectorDef} from './di/interface/defs';
export {INJECTOR_SCOPE as ɵINJECTOR_SCOPE} from './di/scope';
export {RuntimeError as ɵRuntimeError} from './errors';
Expand Down
6 changes: 6 additions & 0 deletions packages/core/src/di/injector.ts
Expand Up @@ -45,6 +45,12 @@ export abstract class Injector {
static THROW_IF_NOT_FOUND = THROW_IF_NOT_FOUND;
static NULL: Injector = (/* @__PURE__ */ new NullInjector());

/**
* Internal note on the `options?: InjectOptions|InjectFlags` override of the `get`
* method: consider dropping the `InjectFlags` part in one of the major versions.
* It can **not** be done in minor/patch, since it's breaking for custom injectors
* that only implement the old `InjectorFlags` interface.
*/
/**
* Retrieves an instance from the injector based on the provided token.
* @returns The instance from the injector if defined, otherwise the `notFoundValue`.
Expand Down
Expand Up @@ -659,6 +659,9 @@
{
"name": "containsElement"
},
{
"name": "convertToBitFlags"
},
{
"name": "convertToMap"
},
Expand Down
Expand Up @@ -467,6 +467,9 @@
{
"name": "connectableObservableDescriptor"
},
{
"name": "convertToBitFlags"
},
{
"name": "createElementNode"
},
Expand Down
Expand Up @@ -686,6 +686,9 @@
{
"name": "controlPath"
},
{
"name": "convertToBitFlags"
},
{
"name": "createDirectivesInstances"
},
Expand Down
Expand Up @@ -659,6 +659,9 @@
{
"name": "controlPath"
},
{
"name": "convertToBitFlags"
},
{
"name": "createDirectivesInstances"
},
Expand Down
Expand Up @@ -329,6 +329,9 @@
{
"name": "connectableObservableDescriptor"
},
{
"name": "convertToBitFlags"
},
{
"name": "createElementRef"
},
Expand Down
3 changes: 3 additions & 0 deletions packages/core/test/bundling/router/bundle.golden_symbols.json
Expand Up @@ -920,6 +920,9 @@
{
"name": "containsTree"
},
{
"name": "convertToBitFlags"
},
{
"name": "convertToParamMap"
},
Expand Down
Expand Up @@ -398,6 +398,9 @@
{
"name": "connectableObservableDescriptor"
},
{
"name": "convertToBitFlags"
},
{
"name": "createElementRef"
},
Expand Down
3 changes: 3 additions & 0 deletions packages/core/test/bundling/todo/bundle.golden_symbols.json
Expand Up @@ -569,6 +569,9 @@
{
"name": "connectableObservableDescriptor"
},
{
"name": "convertToBitFlags"
},
{
"name": "createDirectivesInstances"
},
Expand Down
29 changes: 28 additions & 1 deletion packages/core/test/test_bed_spec.ts
Expand Up @@ -7,7 +7,7 @@
*/

import {CommonModule} from '@angular/common';
import {APP_INITIALIZER, ChangeDetectorRef, Compiler, Component, Directive, ElementRef, ErrorHandler, getNgModuleById, Inject, Injectable, InjectionToken, Injector, Input, LOCALE_ID, ModuleWithProviders, NgModule, Optional, Pipe, Type, ViewChild, ɵsetClassMetadata as setClassMetadata, ɵɵdefineComponent as defineComponent, ɵɵdefineInjector as defineInjector, ɵɵdefineNgModule as defineNgModule, ɵɵelementEnd as elementEnd, ɵɵelementStart as elementStart, ɵɵsetNgModuleScope as setNgModuleScope, ɵɵtext as text} from '@angular/core';
import {APP_INITIALIZER, ChangeDetectorRef, Compiler, Component, Directive, ElementRef, ErrorHandler, getNgModuleById, Inject, Injectable, InjectFlags, InjectionToken, Injector, Input, LOCALE_ID, ModuleWithProviders, NgModule, Optional, Pipe, Type, ViewChild, ɵsetClassMetadata as setClassMetadata, ɵɵdefineComponent as defineComponent, ɵɵdefineInjector as defineInjector, ɵɵdefineNgModule as defineNgModule, ɵɵelementEnd as elementEnd, ɵɵelementStart as elementStart, ɵɵsetNgModuleScope as setNgModuleScope, ɵɵtext as text} from '@angular/core';
import {getTestBed, TestBed} from '@angular/core/testing/src/test_bed';
import {By} from '@angular/platform-browser';
import {expect} from '@angular/platform-browser/testing/src/matchers';
Expand Down Expand Up @@ -1855,6 +1855,33 @@ describe('TestBed', () => {
fixture.detectChanges();
expect(fixture!.nativeElement.textContent).toContain('changed');
});

describe('TestBed.inject', () => {
describe('injection flags', () => {
it('should be able to optionally inject a token', () => {
const TOKEN = new InjectionToken<string>('TOKEN');

expect(TestBed.inject(TOKEN, undefined, {optional: true})).toBeNull();
expect(TestBed.inject(TOKEN, undefined, InjectFlags.Optional)).toBeNull();

expect(TestBed.inject(TOKEN, undefined, {optional: true})).toBeNull();
expect(TestBed.inject(TOKEN, undefined, InjectFlags.Optional)).toBeNull();
});

it('should be able to use skipSelf injection', () => {
const TOKEN = new InjectionToken<string>('TOKEN');
TestBed.configureTestingModule({
providers: [{provide: TOKEN, useValue: 'from TestBed'}],
});

expect(TestBed.inject(TOKEN)).toBe('from TestBed');

expect(TestBed.inject(TOKEN, undefined, {skipSelf: true, optional: true})).toBeNull();
expect(TestBed.inject(TOKEN, undefined, InjectFlags.SkipSelf | InjectFlags.Optional))
.toBeNull();
});
});
});
});


Expand Down
23 changes: 20 additions & 3 deletions packages/core/testing/src/r3_test_bed.ts
Expand Up @@ -16,13 +16,15 @@ import {
Directive,
InjectFlags,
InjectionToken,
InjectOptions,
Injector,
NgModule,
NgZone,
Pipe,
PlatformRef,
ProviderToken,
Type,
ɵconvertToBitFlags as convertToBitFlags,
ɵflushModuleScopingQueueAsMuchAsPossible as flushModuleScopingQueueAsMuchAsPossible,
ɵgetUnknownElementStrictMode as getUnknownElementStrictMode,
ɵgetUnknownPropertyStrictMode as getUnknownPropertyStrictMode,
Expand Down Expand Up @@ -210,10 +212,16 @@ export class TestBedRender3 implements TestBed {
return TestBedRender3 as any as TestBedStatic;
}

static inject<T>(token: ProviderToken<T>, notFoundValue?: T, options?: InjectOptions): T;
static inject<T>(token: ProviderToken<T>, notFoundValue?: T, options?: InjectOptions&{
optional?: false
}): T;
static inject<T>(token: ProviderToken<T>, notFoundValue: null, options?: InjectOptions): T|null;
static inject<T>(token: ProviderToken<T>, notFoundValue?: T, flags?: InjectFlags): T;
static inject<T>(token: ProviderToken<T>, notFoundValue: null, flags?: InjectFlags): T|null;
static inject<T>(token: ProviderToken<T>, notFoundValue?: T|null, flags?: InjectFlags): T|null {
return _getTestBedRender3().inject(token, notFoundValue, flags);
static inject<T>(
token: ProviderToken<T>, notFoundValue?: T|null, flags?: InjectFlags|InjectOptions): T|null {
return _getTestBedRender3().inject(token, notFoundValue, convertToBitFlags(flags));
}

/** @deprecated from v9.0.0 use TestBed.inject */
Expand Down Expand Up @@ -376,12 +384,21 @@ export class TestBedRender3 implements TestBed {
return this.compiler.compileComponents();
}

inject<T>(token: ProviderToken<T>, notFoundValue: undefined, options: InjectOptions&{
optional: true
}): T|null;
inject<T>(token: ProviderToken<T>, notFoundValue?: T, options?: InjectOptions): T;
inject<T>(token: ProviderToken<T>, notFoundValue: null, options?: InjectOptions): T|null;
/** @deprecated use object-based flags (`InjectOptions`) instead. */
inject<T>(token: ProviderToken<T>, notFoundValue?: T, flags?: InjectFlags): T;
/** @deprecated use object-based flags (`InjectOptions`) instead. */
inject<T>(token: ProviderToken<T>, notFoundValue: null, flags?: InjectFlags): T|null;
inject<T>(token: ProviderToken<T>, notFoundValue?: T|null, flags?: InjectFlags): T|null {
inject<T>(token: ProviderToken<T>, notFoundValue?: T|null, flags?: InjectFlags|InjectOptions): T
|null {
if (token as unknown === TestBedRender3) {
return this as any;
}
flags = convertToBitFlags(flags);
const UNDEFINED = {} as unknown as T;
const result = this.testModuleRef.injector.get(token, UNDEFINED, flags);
return result === UNDEFINED ? this.compiler.injector.get(token, notFoundValue, flags) as any :
Expand Down
9 changes: 8 additions & 1 deletion packages/core/testing/src/test_bed.ts
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Component, Directive, InjectFlags, NgModule, Pipe, PlatformRef, ProviderToken, Type} from '@angular/core';
import {Component, Directive, InjectFlags, InjectOptions, NgModule, Pipe, PlatformRef, ProviderToken, Type} from '@angular/core';

import {ComponentFixture} from './component_fixture';
import {MetadataOverride} from './metadata_override';
Expand Down Expand Up @@ -49,7 +49,14 @@ export interface TestBed {

compileComponents(): Promise<any>;

inject<T>(token: ProviderToken<T>, notFoundValue: undefined, options: InjectOptions&{
optional: true
}): T|null;
inject<T>(token: ProviderToken<T>, notFoundValue?: T, options?: InjectOptions): T;
inject<T>(token: ProviderToken<T>, notFoundValue: null, options?: InjectOptions): T|null;
/** @deprecated use object-based flags (`InjectOptions`) instead. */
inject<T>(token: ProviderToken<T>, notFoundValue?: T, flags?: InjectFlags): T;
/** @deprecated use object-based flags (`InjectOptions`) instead. */
inject<T>(token: ProviderToken<T>, notFoundValue: null, flags?: InjectFlags): T|null;

/** @deprecated from v9.0.0 use TestBed.inject */
Expand Down
9 changes: 8 additions & 1 deletion packages/core/testing/src/test_bed_common.ts
Expand Up @@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/

import {Component, Directive, InjectFlags, InjectionToken, NgModule, Pipe, PlatformRef, ProviderToken, SchemaMetadata, Type} from '@angular/core';
import {Component, Directive, InjectFlags, InjectionToken, InjectOptions, NgModule, Pipe, PlatformRef, ProviderToken, SchemaMetadata, Type} from '@angular/core';

import {ComponentFixture} from './component_fixture';
import {MetadataOverride} from './metadata_override';
Expand Down Expand Up @@ -186,7 +186,14 @@ export interface TestBedStatic {
deps?: any[],
}): TestBedStatic;

inject<T>(token: ProviderToken<T>, notFoundValue: undefined, options: InjectOptions&{
optional: true
}): T|null;
inject<T>(token: ProviderToken<T>, notFoundValue?: T, options?: InjectOptions): T;
inject<T>(token: ProviderToken<T>, notFoundValue: null, options?: InjectOptions): T|null;
/** @deprecated use object-based flags (`InjectOptions`) instead. */
inject<T>(token: ProviderToken<T>, notFoundValue?: T, flags?: InjectFlags): T;
/** @deprecated use object-based flags (`InjectOptions`) instead. */
inject<T>(token: ProviderToken<T>, notFoundValue: null, flags?: InjectFlags): T|null;

/** @deprecated from v9.0.0 use TestBed.inject */
Expand Down

0 comments on commit c64f54d

Please sign in to comment.