From 3fb78aaaccf32b72a7b9eff55f144fb39e74474f Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Fri, 22 Mar 2019 09:42:52 +0000 Subject: [PATCH] feat(upgrade): provide unit test helpers for wiring up injectors (#16848) Adds two new helper functions that can be used when unit testing Angular services that depend upon upgraded AngularJS services, or vice versa. The functions return a module (AngularJS or NgModule) that is configured to wire up the Angular and AngularJS injectors without the need to actually bootstrap a full hybrid application. This makes it simpler and faster to unit test services. PR Close #16848 --- .../transforms/angular-api-package/index.js | 1 + .../transforms/authors-package/api-package.js | 2 +- integration/typings_test_ts34/include-all.ts | 4 +- .../upgrade/static/ts/full/module.spec.ts | 46 ++++++++ .../examples/upgrade/static/ts/full/module.ts | 14 +-- packages/examples/upgrade/upgrade_example.bzl | 3 + packages/upgrade/BUILD.bazel | 2 + packages/upgrade/src/common/src/angular1.ts | 5 + packages/upgrade/static/testing/BUILD.bazel | 19 ++++ packages/upgrade/static/testing/index.ts | 9 ++ packages/upgrade/static/testing/package.json | 11 ++ packages/upgrade/static/testing/public_api.ts | 10 ++ .../src/create_angular_testing_module.ts | 99 +++++++++++++++++ .../src/create_angularjs_testing_module.ts | 100 ++++++++++++++++++ .../upgrade/static/testing/test/BUILD.bazel | 29 +++++ .../create_angular_testing_module_spec.ts | 48 +++++++++ .../create_angularjs_testing_module_spec.ts | 33 ++++++ packages/upgrade/static/testing/test/mocks.ts | 76 +++++++++++++ test-main.js | 1 + .../upgrade/static/testing.d.ts | 3 + 20 files changed, 506 insertions(+), 9 deletions(-) create mode 100644 packages/examples/upgrade/static/ts/full/module.spec.ts create mode 100644 packages/upgrade/static/testing/BUILD.bazel create mode 100755 packages/upgrade/static/testing/index.ts create mode 100644 packages/upgrade/static/testing/package.json create mode 100755 packages/upgrade/static/testing/public_api.ts create mode 100644 packages/upgrade/static/testing/src/create_angular_testing_module.ts create mode 100644 packages/upgrade/static/testing/src/create_angularjs_testing_module.ts create mode 100644 packages/upgrade/static/testing/test/BUILD.bazel create mode 100644 packages/upgrade/static/testing/test/create_angular_testing_module_spec.ts create mode 100644 packages/upgrade/static/testing/test/create_angularjs_testing_module_spec.ts create mode 100644 packages/upgrade/static/testing/test/mocks.ts create mode 100644 tools/public_api_guard/upgrade/static/testing.d.ts diff --git a/aio/tools/transforms/angular-api-package/index.js b/aio/tools/transforms/angular-api-package/index.js index 55baa84af05b0e..93cbbf37c7f047 100644 --- a/aio/tools/transforms/angular-api-package/index.js +++ b/aio/tools/transforms/angular-api-package/index.js @@ -115,6 +115,7 @@ module.exports = 'service-worker/index.ts', 'upgrade/index.ts', 'upgrade/static/index.ts', + 'upgrade/static/testing/index.ts', ]; readFilesProcessor.fileReaders.push(packageContentFileReader); diff --git a/aio/tools/transforms/authors-package/api-package.js b/aio/tools/transforms/authors-package/api-package.js index aff578cf67333c..92b85ab2edd488 100644 --- a/aio/tools/transforms/authors-package/api-package.js +++ b/aio/tools/transforms/authors-package/api-package.js @@ -24,7 +24,7 @@ const packageMap = { 'platform-webworker-dynamic': ['platform-webworker-dynamic/index.ts'], router: ['router/index.ts', 'router/testing/index.ts', 'router/upgrade/index.ts'], 'service-worker': ['service-worker/index.ts'], - upgrade: ['upgrade/index.ts', 'upgrade/static/index.ts'] + upgrade: ['upgrade/index.ts', 'upgrade/static/index.ts', 'upgrade/static/testing/index.ts'] }; diff --git a/integration/typings_test_ts34/include-all.ts b/integration/typings_test_ts34/include-all.ts index 66a5e0ffc3ee16..116ba30903cdc4 100644 --- a/integration/typings_test_ts34/include-all.ts +++ b/integration/typings_test_ts34/include-all.ts @@ -39,6 +39,7 @@ import * as routerUpgrade from '@angular/router/upgrade'; import * as serviceWorker from '@angular/service-worker'; import * as upgrade from '@angular/upgrade'; import * as upgradeStatic from '@angular/upgrade/static'; +import * as upgradeTesting from '@angular/upgrade/static/testing'; export default { animations, @@ -71,5 +72,6 @@ export default { routerUpgrade, serviceWorker, upgrade, - upgradeStatic + upgradeStatic, + upgradeTesting, }; diff --git a/packages/examples/upgrade/static/ts/full/module.spec.ts b/packages/examples/upgrade/static/ts/full/module.spec.ts new file mode 100644 index 00000000000000..d9067534d120e4 --- /dev/null +++ b/packages/examples/upgrade/static/ts/full/module.spec.ts @@ -0,0 +1,46 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +// #docregion angular-setup +import {TestBed} from '@angular/core/testing'; +import {createAngularJSTestingModule, createAngularTestingModule} from '@angular/upgrade/static/testing'; + +import {HeroesService, Ng2AppModule, ng1AppModule} from './module'; + +const {module, inject} = (window as any).angular.mock; + +// #enddocregion angular-setup +describe('HeroesService (from Angular)', () => { + + // #docregion angular-setup + beforeEach(() => { + TestBed.configureTestingModule( + {imports: [createAngularTestingModule([ng1AppModule.name]), Ng2AppModule]}); + }); + // #enddocregion angular-setup + + // #docregion angular-spec + it('should have access to the HeroesService', () => { + const heroesService = TestBed.get(HeroesService) as HeroesService; + expect(heroesService).toBeDefined(); + }); + // #enddocregion angular-spec +}); + + +describe('HeroesService (from AngularJS)', () => { + // #docregion angularjs-setup + beforeEach(module(createAngularJSTestingModule([Ng2AppModule]))); + beforeEach(module(ng1AppModule.name)); + // #enddocregion angularjs-setup + + // #docregion angularjs-spec + it('should have access to the HeroesService', + inject((heroesService: HeroesService) => { expect(heroesService).toBeDefined(); })); + // #enddocregion angularjs-spec +}); diff --git a/packages/examples/upgrade/static/ts/full/module.ts b/packages/examples/upgrade/static/ts/full/module.ts index 1e7c155dbfcc7d..217de1a4d6ad14 100644 --- a/packages/examples/upgrade/static/ts/full/module.ts +++ b/packages/examples/upgrade/static/ts/full/module.ts @@ -13,13 +13,13 @@ import {UpgradeComponent, UpgradeModule, downgradeComponent, downgradeInjectable declare var angular: ng.IAngularStatic; -interface Hero { +export interface Hero { name: string; description: string; } // #docregion ng1-text-formatter-service -class TextFormatter { +export class TextFormatter { titleCase(value: string) { return value.replace(/((^|\s)[a-z])/g, (_, c) => c.toUpperCase()); } } @@ -38,7 +38,7 @@ class TextFormatter { `, }) -class Ng2HeroesComponent { +export class Ng2HeroesComponent { @Input() heroes !: Hero[]; @Output() addHero = new EventEmitter(); @Output() removeHero = new EventEmitter(); @@ -48,7 +48,7 @@ class Ng2HeroesComponent { // #docregion ng2-heroes-service // This Angular service will be "downgraded" to be used in AngularJS @Injectable() -class HeroesService { +export class HeroesService { heroes: Hero[] = [ {name: 'superman', description: 'The man of steel'}, {name: 'wonder woman', description: 'Princess of the Amazons'}, @@ -74,7 +74,7 @@ class HeroesService { // #docregion ng1-hero-wrapper // This Angular directive will act as an interface to the "upgraded" AngularJS component @Directive({selector: 'ng1-hero'}) -class Ng1HeroComponentWrapper extends UpgradeComponent { +export class Ng1HeroComponentWrapper extends UpgradeComponent { // The names of the input and output properties here must match the names of the // `<` and `&` bindings in the AngularJS component that is being wrapped @Input() hero !: Hero; @@ -104,7 +104,7 @@ class Ng1HeroComponentWrapper extends UpgradeComponent { imports: [BrowserModule, UpgradeModule] }) // #docregion bootstrap-ng1 -class Ng2AppModule { +export class Ng2AppModule { // #enddocregion ng2-module constructor(private upgrade: UpgradeModule) {} @@ -122,7 +122,7 @@ class Ng2AppModule { // #docregion Angular 1 Stuff // #docregion ng1-module // This Angular 1 module represents the AngularJS pieces of the application -const ng1AppModule = angular.module('ng1AppModule', []); +export const ng1AppModule: ng.IModule = angular.module('ng1AppModule', []); // #enddocregion // #docregion ng1-hero diff --git a/packages/examples/upgrade/upgrade_example.bzl b/packages/examples/upgrade/upgrade_example.bzl index ad1b2c21518fd7..7391c9fff146dd 100644 --- a/packages/examples/upgrade/upgrade_example.bzl +++ b/packages/examples/upgrade/upgrade_example.bzl @@ -17,10 +17,13 @@ def create_upgrade_example_targets(name, srcs, e2e_srcs, entry_module, assets = type_check = False, deps = [ "@npm//@types/angular", + "@npm//@types/jasmine", "//packages/core", "//packages/platform-browser", "//packages/platform-browser-dynamic", "//packages/upgrade/static", + "//packages/core/testing", + "//packages/upgrade/static/testing", ], tsconfig = "//packages/examples/upgrade:tsconfig-build.json", ) diff --git a/packages/upgrade/BUILD.bazel b/packages/upgrade/BUILD.bazel index 0af8a7bb7e6d16..7e680f40f8279b 100644 --- a/packages/upgrade/BUILD.bazel +++ b/packages/upgrade/BUILD.bazel @@ -23,6 +23,7 @@ ng_package( srcs = [ "package.json", "//packages/upgrade/static:package.json", + "//packages/upgrade/static/testing:package.json", ], entry_point = ":index.ts", tags = [ @@ -34,5 +35,6 @@ ng_package( deps = [ ":upgrade", "//packages/upgrade/static", + "//packages/upgrade/static/testing", ], ) diff --git a/packages/upgrade/src/common/src/angular1.ts b/packages/upgrade/src/common/src/angular1.ts index f4adaad23c33ac..78832354f0c3c1 100644 --- a/packages/upgrade/src/common/src/angular1.ts +++ b/packages/upgrade/src/common/src/angular1.ts @@ -234,6 +234,7 @@ let angular: { (e: string | Element | Document | IAugmentedJQuery): IAugmentedJQuery; cleanData: (nodes: Node[] | NodeList) => void; }, + injector: (modules: Array, strictDi?: boolean) => IInjectorService, version: {major: number}, resumeBootstrap: () => void, getTestability: (e: Element) => ITestabilityService @@ -241,6 +242,7 @@ let angular: { bootstrap: noNg, module: noNg, element: noNgElement, + injector: noNg, version: undefined as any, resumeBootstrap: noNg, getTestability: noNg @@ -304,6 +306,9 @@ export const module_: typeof angular.module = (prefix, dependencies?) => export const element: typeof angular.element = (e => angular.element(e)) as typeof angular.element; element.cleanData = nodes => angular.element.cleanData(nodes); +export const injector: typeof angular.injector = + (modules: Array, strictDi?: boolean) => angular.injector(modules, strictDi); + export const resumeBootstrap: typeof angular.resumeBootstrap = () => angular.resumeBootstrap(); export const getTestability: typeof angular.getTestability = e => angular.getTestability(e); diff --git a/packages/upgrade/static/testing/BUILD.bazel b/packages/upgrade/static/testing/BUILD.bazel new file mode 100644 index 00000000000000..9cece1502f0543 --- /dev/null +++ b/packages/upgrade/static/testing/BUILD.bazel @@ -0,0 +1,19 @@ +load("//tools:defaults.bzl", "ng_module") + +package(default_visibility = ["//visibility:public"]) + +exports_files(["package.json"]) + +ng_module( + name = "testing", + srcs = glob( + [ + "*.ts", + "src/*.ts", + ], + ), + deps = [ + "//packages/core/testing", + "//packages/upgrade/src/common", + ], +) diff --git a/packages/upgrade/static/testing/index.ts b/packages/upgrade/static/testing/index.ts new file mode 100755 index 00000000000000..f93e7c31d56448 --- /dev/null +++ b/packages/upgrade/static/testing/index.ts @@ -0,0 +1,9 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +export * from './public_api'; diff --git a/packages/upgrade/static/testing/package.json b/packages/upgrade/static/testing/package.json new file mode 100644 index 00000000000000..eecbcda4703950 --- /dev/null +++ b/packages/upgrade/static/testing/package.json @@ -0,0 +1,11 @@ +{ + "name": "@angular/upgrade/static/testing", + "main": "../../bundles/upgrade-static-testing.umd.js", + "module": "../../fesm5/static/testing.js", + "es2015": "../../fesm2015/static/testing.js", + "esm5": "../../esm5/static/testing/testing.js", + "esm2015": "../../esm2015/static/testing/testing.js", + "fesm5": "../../fesm5/static/testing.js", + "fesm2015": "../../fesm2015/static/testing.js", + "typings": "./testing.d.ts" +} diff --git a/packages/upgrade/static/testing/public_api.ts b/packages/upgrade/static/testing/public_api.ts new file mode 100755 index 00000000000000..871ad430b11a47 --- /dev/null +++ b/packages/upgrade/static/testing/public_api.ts @@ -0,0 +1,10 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +export {createAngularTestingModule} from './src/create_angular_testing_module'; +export {createAngularJSTestingModule} from './src/create_angularjs_testing_module'; \ No newline at end of file diff --git a/packages/upgrade/static/testing/src/create_angular_testing_module.ts b/packages/upgrade/static/testing/src/create_angular_testing_module.ts new file mode 100644 index 00000000000000..bbaa2f5c8f1b1b --- /dev/null +++ b/packages/upgrade/static/testing/src/create_angular_testing_module.ts @@ -0,0 +1,99 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Injector, NgModule, Type} from '@angular/core'; + +import * as angular from '../../../src/common/src/angular1'; +import {$INJECTOR, INJECTOR_KEY, UPGRADE_APP_TYPE_KEY} from '../../../src/common/src/constants'; +import {UpgradeAppType} from '../../../src/common/src/util'; + +export let $injector: angular.IInjectorService|null = null; +let injector: Injector; + +export function $injectorFactory() { + return $injector; +} + +@NgModule({providers: [{provide: $INJECTOR, useFactory: $injectorFactory}]}) +export class AngularTestingModule { + constructor(i: Injector) { injector = i; } +} + +/** + * A helper function to use when unit testing Angular services that depend upon upgraded AngularJS + * services. + * + * This function returns an `NgModule` decorated class that is configured to wire up the Angular + * and AngularJS injectors without the need to actually bootstrap a hybrid application. + * This makes it simpler and faster to unit test services. + * + * Use the returned class as an "import" when configuring the `TestBed`. + * + * In the following code snippet, we are configuring the TestBed with two imports. + * The `Ng2AppModule` is the Angular part of our hybrid application and the `ng1AppModule` is the + * AngularJS part. + * + * + * + * Once this is done we can get hold of services via the Angular `Injector` as normal. + * Services that are (or have dependencies on) an upgraded AngularJS service, will be instantiated + * as needed by the AngularJS `$injector`. + * + * In the following code snippet, `HeroesService` is an Angular service that depends upon an + * AngularJS service, `titleCase`. + * + * + * + *
+ * + * This helper is for testing services not Components. + * For Component testing you must still bootstrap a hybrid app. See `UpgradeModule` or + * `downgradeModule` for more information. + * + *
+ * + *
+ * + * The resulting configuration does not wire up AngularJS digests to Zone hooks. It is the + * responsibility of the test writer to call `$rootScope.$apply`, as necessary, to trigger + * AngularJS handlers of async events from Angular. + * + *
+ * + *
+ * + * The helper sets up global variables to hold the shared Angular and AngularJS injectors. + * + * * Only call this helper once per spec. + * * Do not use `createAngularTestingModule` in the same spec as `createAngularJSTestingModule`. + * + *
+ * + * Here is the example application and its unit tests that use `createAngularTestingModule` + * and `createAngularJSTestingModule`. + * + * + * + * + * + * + * + * @param angularJSModules a collection of the names of AngularJS modules to include in the + * configuration. + * @param [strictDi] whether the AngularJS injector should have `strictDI` enabled. + * + * @publicApi + */ +export function createAngularTestingModule( + angularJSModules: string[], strictDi?: boolean): Type { + angular.module_('$$angularJSTestingModule', angularJSModules) + .constant(UPGRADE_APP_TYPE_KEY, UpgradeAppType.Static) + .factory(INJECTOR_KEY, () => injector); + $injector = angular.injector(['ng', '$$angularJSTestingModule'], strictDi); + return AngularTestingModule; +} diff --git a/packages/upgrade/static/testing/src/create_angularjs_testing_module.ts b/packages/upgrade/static/testing/src/create_angularjs_testing_module.ts new file mode 100644 index 00000000000000..85a6d9adcf925e --- /dev/null +++ b/packages/upgrade/static/testing/src/create_angularjs_testing_module.ts @@ -0,0 +1,100 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Injector} from '@angular/core'; +import {TestBed} from '@angular/core/testing'; + +import * as ng from '../../../src/common/src/angular1'; +import {$INJECTOR, INJECTOR_KEY, UPGRADE_APP_TYPE_KEY} from '../../../src/common/src/constants'; +import {UpgradeAppType} from '../../../src/common/src/util'; + + +/** + * A helper function to use when unit testing AngularJS services that depend upon downgraded Angular + * services. + * + * This function returns an AngularJS module that is configured to wire up the AngularJS and Angular + * injectors without the need to actually bootstrap a hybrid application. + * This makes it simpler and faster to unit test services. + * + * Use the returned AngularJS module in a call to + * [`angular.mocks.module`](https://docs.angularjs.org/api/ngMock/function/angular.mock.module) to + * include this module in the unit test injector. + * + * In the following code snippet, we are configuring the `$injector` with two modules: + * The AngularJS `ng1AppModule`, which is the AngularJS part of our hybrid application and the + * `Ng2AppModule`, which is the Angular part. + * + * + * + * Once this is done we can get hold of services via the AngularJS `$injector` as normal. + * Services that are (or have dependencies on) a downgraded Angular service, will be instantiated as + * needed by the Angular root `Injector`. + * + * In the following code snippet, `heroesService` is a downgraded Angular service that we are + * accessing from AngularJS. + * + * + * + *
+ * + * This helper is for testing services not components. + * For Component testing you must still bootstrap a hybrid app. See `UpgradeModule` or + * `downgradeModule` for more information. + * + *
+ * + *
+ * + * The resulting configuration does not wire up AngularJS digests to Zone hooks. It is the + * responsibility of the test writer to call `$rootScope.$apply`, as necessary, to trigger + * AngularJS handlers of async events from Angular. + * + *
+ * + *
+ * + * The helper sets up global variables to hold the shared Angular and AngularJS injectors. + * + * * Only call this helper once per spec. + * * Do not use `createAngularJSTestingModule` in the same spec as `createAngularTestingModule`. + * + *
+ * + * Here is the example application and its unit tests that use `createAngularTestingModule` + * and `createAngularJSTestingModule`. + * + * + * + * + * + * + * + * @param angularModules a collection of Angular modules to include in the configuration. + * + * @publicApi + */ +export function createAngularJSTestingModule(angularModules: any[]): string { + return ng.module_('$$angularJSTestingModule', []) + .constant(UPGRADE_APP_TYPE_KEY, UpgradeAppType.Static) + .factory( + INJECTOR_KEY, + [ + $INJECTOR, + ($injector: ng.IInjectorService) => { + TestBed.configureTestingModule({ + imports: angularModules, + providers: [{provide: $INJECTOR, useValue: $injector}] + }); + return TestBed.get(Injector); + } + ]) + .name; +} diff --git a/packages/upgrade/static/testing/test/BUILD.bazel b/packages/upgrade/static/testing/test/BUILD.bazel new file mode 100644 index 00000000000000..96aa6c731a4ea4 --- /dev/null +++ b/packages/upgrade/static/testing/test/BUILD.bazel @@ -0,0 +1,29 @@ +package(default_visibility = ["//visibility:public"]) + +load("//tools:defaults.bzl", "ts_library", "ts_web_test_suite") + +ts_library( + name = "test_lib", + testonly = True, + srcs = glob([ + "**/*.ts", + ]), + deps = [ + "//packages/core", + "//packages/core/testing", + "//packages/upgrade/src/common", + "//packages/upgrade/src/common/test/helpers", + "//packages/upgrade/static", + "//packages/upgrade/static/testing", + ], +) + +ts_web_test_suite( + name = "test", + static_files = [ + "//:angularjs_scripts", + ], + deps = [ + ":test_lib", + ], +) diff --git a/packages/upgrade/static/testing/test/create_angular_testing_module_spec.ts b/packages/upgrade/static/testing/test/create_angular_testing_module_spec.ts new file mode 100644 index 00000000000000..13d3629434f8f1 --- /dev/null +++ b/packages/upgrade/static/testing/test/create_angular_testing_module_spec.ts @@ -0,0 +1,48 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Injector} from '@angular/core'; +import {TestBed} from '@angular/core/testing'; + +import {$INJECTOR} from '../../../src/common/src/constants'; +import {withEachNg1Version} from '../../../src/common/test/helpers/common_test_helpers'; +import {createAngularTestingModule} from '../src/create_angular_testing_module'; + +import {AppModule, Inventory, defineAppModule, serverRequestInstance} from './mocks'; + +withEachNg1Version(() => { + describe('Angular entry point', () => { + it('should allow us to get an upgraded AngularJS service from an Angular service', () => { + defineAppModule(); + // Configure an NgModule that has the Angular and AngularJS injectors wired up + TestBed.configureTestingModule({imports: [createAngularTestingModule(['app']), AppModule]}); + const inventory = TestBed.get(Inventory) as Inventory; + expect(inventory.serverRequest).toBe(serverRequestInstance); + }); + + it('should create new injectors when we re-use the helper', () => { + defineAppModule(); + TestBed.configureTestingModule({imports: [createAngularTestingModule(['app']), AppModule]}); + // Check that the injectors are wired up correctly + TestBed.get(Inventory) as Inventory; + + // Grab references to the current injectors + const injector = TestBed.get(Injector); + const $injector = TestBed.get($INJECTOR); + + TestBed.resetTestingModule(); + TestBed.configureTestingModule({imports: [createAngularTestingModule(['app']), AppModule]}); + // Check that the injectors are wired up correctly + TestBed.get(Inventory) as Inventory; + + // Check that the new injectors are different to the previous ones. + expect(TestBed.get(Injector)).not.toBe(injector); + expect(TestBed.get($INJECTOR)).not.toBe($injector); + }); + }); +}); diff --git a/packages/upgrade/static/testing/test/create_angularjs_testing_module_spec.ts b/packages/upgrade/static/testing/test/create_angularjs_testing_module_spec.ts new file mode 100644 index 00000000000000..0e55b9e1db5098 --- /dev/null +++ b/packages/upgrade/static/testing/test/create_angularjs_testing_module_spec.ts @@ -0,0 +1,33 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {getAngularJSGlobal} from '../../../src/common/src/angular1'; +import {withEachNg1Version} from '../../../src/common/test/helpers/common_test_helpers'; +import {createAngularJSTestingModule} from '../src/create_angularjs_testing_module'; + +import {AppModule, Inventory, defineAppModule} from './mocks'; + + +withEachNg1Version(() => { + describe('AngularJS entry point', () => { + it('should allow us to get a downgraded Angular service from an AngularJS service', () => { + defineAppModule(); + // We have to get the `mock` object from the global `angular` variable, rather than trying to + // import it from `@angular/upgrade/src/common/angular1`, because that file doesn't export + // `ngMock` helpers. + const {inject, module} = getAngularJSGlobal().mock; + // Load the AngularJS bits of the application + module('app'); + // Configure an AngularJS module that has the AngularJS and Angular injector wired up + module(createAngularJSTestingModule([AppModule])); + let inventory: any = undefined; + inject(function(shoppingCart: any) { inventory = shoppingCart.inventory; }); + expect(inventory).toEqual(jasmine.any(Inventory)); + }); + }); +}); \ No newline at end of file diff --git a/packages/upgrade/static/testing/test/mocks.ts b/packages/upgrade/static/testing/test/mocks.ts new file mode 100644 index 00000000000000..f563a84ab4b1d4 --- /dev/null +++ b/packages/upgrade/static/testing/test/mocks.ts @@ -0,0 +1,76 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Inject, Injectable, NgModule} from '@angular/core'; +import {downgradeInjectable} from '@angular/upgrade/static'; +import * as ng from '../../../src/common/src/angular1'; +/* + * This mock application code contains the following services and their dependencies: + * + * shoppingCart (AngularJS) + * -> Inventory (Angular - downgraded) + * -> serverRequest (AngularJS - upgraded) + * -> Logger (Angular - downgraded) + * + * This allows us to test two scenarios: + * * AngularJS -> Angular -> AngularJS + * * Angular -> AngularJS -> Angular + */ + +/* START: Angular bits */ +@Injectable() +export class Logger { + warn() {} +} + +@Injectable() +export class Inventory { + constructor(@Inject('serverRequest') public serverRequest: any) {} +} + +export function serverRequestFactory(i: ng.IInjectorService) { + return i.get('serverRequest'); +} + +@NgModule({ + providers: [ + Logger, + Inventory, + {provide: 'serverRequest', useFactory: serverRequestFactory, deps: ['$injector']}, + ] +}) +export class AppModule { +} +/* END: Angular bits */ + +/* START: AngularJS bits */ +export const serverRequestInstance: {logger?: Logger} = {}; +export const shoppingCartInstance: {inventory?: Inventory} = {}; + +export function defineAppModule() { + ng.module_('app', []) + .factory('logger', downgradeInjectable(Logger)) + .factory('inventory', downgradeInjectable(Inventory)) + .factory( + 'serverRequest', + [ + 'logger', + function(logger: Logger) { + serverRequestInstance.logger = logger; + return serverRequestInstance; + } + ]) + .factory('shoppingCart', [ + 'inventory', + function(inventory: Inventory) { + shoppingCartInstance.inventory = inventory; + return shoppingCartInstance; + } + ]); +} +/* END: AngularJS bits */ diff --git a/test-main.js b/test-main.js index 59e8c28c028a85..6e8f44e40069c2 100644 --- a/test-main.js +++ b/test-main.js @@ -51,6 +51,7 @@ System.config({ '@angular/router': {main: 'index.js', defaultExtension: 'js'}, '@angular/http/testing': {main: 'index.js', defaultExtension: 'js'}, '@angular/http': {main: 'index.js', defaultExtension: 'js'}, + '@angular/upgrade/static/testing': {main: 'index.js', defaultExtension: 'js'}, '@angular/upgrade/static': {main: 'index.js', defaultExtension: 'js'}, '@angular/upgrade': {main: 'index.js', defaultExtension: 'js'}, '@angular/platform-browser/animations/testing': {main: 'index.js', defaultExtension: 'js'}, diff --git a/tools/public_api_guard/upgrade/static/testing.d.ts b/tools/public_api_guard/upgrade/static/testing.d.ts new file mode 100644 index 00000000000000..f905c680b64360 --- /dev/null +++ b/tools/public_api_guard/upgrade/static/testing.d.ts @@ -0,0 +1,3 @@ +export declare function createAngularJSTestingModule(angularModules: any[]): string; + +export declare function createAngularTestingModule(angularJSModules: string[], strictDi?: boolean): Type;