fix(upgrade): make ngUpgrade work with testability API

Closes #7603
This commit is contained in:
Sammy Jelin 2016-03-14 14:36:41 -07:00
parent d272f96e23
commit 430f367c2f
4 changed files with 143 additions and 20 deletions

View File

@ -97,16 +97,33 @@ export interface IControllerService {
export interface IInjectorService { get(key: string): any; }
export interface ITestabilityService {
findBindings(element: Element, expression: string, opt_exactMatch?: boolean): Element[];
findModels(element: Element, expression: string, opt_exactMatch?: boolean): Element[];
getLocation(): string;
setLocation(url: string): void;
whenStable(callback: Function): void;
}
function noNg() {
throw new Error('AngularJS v1.x is not loaded!');
}
var angular: {
bootstrap: (e: Element, modules: string[], config: IAngularBootstrapConfig) => void,
module: (prefix: string, dependencies?: string[]) => IModule,
element: (e: Element) => IAugmentedJQuery,
version: {major: number}
} = <any>{bootstrap: noNg, module: noNg, element: noNg, version: noNg};
var angular:
{
bootstrap: (e: Element, modules: string[], config: IAngularBootstrapConfig) => void,
module: (prefix: string, dependencies?: string[]) => IModule,
element: (e: Element) => IAugmentedJQuery,
version: {major: number}, resumeBootstrap?: () => void,
getTestability: (e: Element) => ITestabilityService
} = <any>{
bootstrap: noNg,
module: noNg,
element: noNg,
version: noNg,
resumeBootstrap: noNg,
getTestability: noNg
};
try {
@ -121,3 +138,5 @@ export var bootstrap = angular.bootstrap;
export var module = angular.module;
export var element = angular.element;
export var version = angular.version;
export var resumeBootstrap = angular.resumeBootstrap;
export var getTestability = angular.getTestability;

View File

@ -12,4 +12,5 @@ export const NG1_HTTP_BACKEND = '$httpBackend';
export const NG1_INJECTOR = '$injector';
export const NG1_PARSE = '$parse';
export const NG1_TEMPLATE_CACHE = '$templateCache';
export const NG1_TESTABILITY = '$$testability';
export const REQUIRE_INJECTOR = '^' + NG2_INJECTOR;

View File

@ -10,8 +10,10 @@ import {
HostViewFactoryRef,
Provider,
Type,
Testability,
APPLICATION_COMMON_PROVIDERS
} from 'angular2/core';
import {global} from 'angular2/src/facade/lang';
import {ObservableWrapper} from 'angular2/src/facade/async';
import {BROWSER_PROVIDERS, BROWSER_APP_PROVIDERS} from 'angular2/platform/browser';
@ -23,6 +25,7 @@ import {
NG1_PARSE,
NG1_ROOT_SCOPE,
NG1_SCOPE,
NG1_TESTABILITY,
NG2_APP_VIEW_MANAGER,
NG2_COMPILER,
NG2_INJECTOR,
@ -309,6 +312,7 @@ export class UpgradeAdapter {
var rootScope: angular.IRootScopeService;
var hostViewFactoryRefMap: HostViewFactoryRefMap = {};
var ng1Module = angular.module(this.idPrefix, modules);
var ng1BootstrapPromise: Promise<any> = null;
var ng1compilePromise: Promise<any> = null;
ng1Module.value(NG2_INJECTOR, injector)
.value(NG2_ZONE, ngZone)
@ -331,23 +335,68 @@ export class UpgradeAdapter {
return rootScope = rootScopeDelegate;
}
]);
}
])
.run([
'$injector',
'$rootScope',
(injector: angular.IInjectorService, rootScope: angular.IRootScopeService) => {
ng1Injector = injector;
ObservableWrapper.subscribe(ngZone.onMicrotaskEmpty,
(_) => ngZone.runOutsideAngular(() => rootScope.$apply()));
ng1compilePromise =
UpgradeNg1ComponentAdapterBuilder.resolve(this.downgradedComponents, injector);
provide.decorator(NG1_TESTABILITY, [
'$delegate',
function(testabilityDelegate: angular.ITestabilityService) {
var ng2Testability: Testability = injector.get(Testability);
var origonalWhenStable: Function = testabilityDelegate.whenStable;
var newWhenStable = (callback: Function): void => {
var whenStableContext: any = this;
origonalWhenStable.call(this, function() {
if (ng2Testability.isStable()) {
callback.apply(this, arguments);
} else {
ng2Testability.whenStable(newWhenStable.bind(whenStableContext, callback));
}
});
};
testabilityDelegate.whenStable = newWhenStable;
return testabilityDelegate;
}
]);
}
]);
ng1compilePromise = new Promise((resolve, reject) => {
ng1Module.run([
'$injector',
'$rootScope',
(injector: angular.IInjectorService, rootScope: angular.IRootScopeService) => {
ng1Injector = injector;
ObservableWrapper.subscribe(ngZone.onMicrotaskEmpty,
(_) => ngZone.runOutsideAngular(() => rootScope.$apply()));
UpgradeNg1ComponentAdapterBuilder.resolve(this.downgradedComponents, injector)
.then(resolve, reject);
}
]);
});
// Make sure resumeBootstrap() only exists if the current bootstrap is deferred
var windowAngular = (<any>global).angular;
windowAngular.resumeBootstrap = undefined;
angular.element(element).data(controllerKey(NG2_INJECTOR), injector);
ngZone.run(() => { angular.bootstrap(element, [this.idPrefix], config); });
Promise.all([this.compileNg2Components(compiler, hostViewFactoryRefMap), ng1compilePromise])
ng1BootstrapPromise = new Promise((resolve, reject) => {
if (windowAngular.resumeBootstrap) {
var originalResumeBootstrap: () => void = windowAngular.resumeBootstrap;
windowAngular.resumeBootstrap = function() {
windowAngular.resumeBootstrap = originalResumeBootstrap;
windowAngular.resumeBootstrap.apply(this, arguments);
resolve();
};
} else {
resolve();
}
});
Promise.all([
this.compileNg2Components(compiler, hostViewFactoryRefMap),
ng1BootstrapPromise,
ng1compilePromise
])
.then(() => {
ngZone.run(() => {
if (rootScopePrototype) {

View File

@ -12,7 +12,16 @@ import {
} from 'angular2/testing_internal';
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
import {Component, Class, Inject, EventEmitter, ApplicationRef, provide} from 'angular2/core';
import {global} from 'angular2/src/facade/lang';
import {
Component,
Class,
Inject,
EventEmitter,
ApplicationRef,
provide,
Testability,
} from 'angular2/core';
import {UpgradeAdapter} from 'angular2/upgrade';
import * as angular from 'angular2/src/upgrade/angular_js';
@ -559,6 +568,52 @@ export function main() {
}));
});
describe('testability', () => {
it('should handle deferred bootstrap', inject([AsyncTestCompleter], (async) => {
var adapter: UpgradeAdapter = new UpgradeAdapter();
var ng1Module = angular.module('ng1', []);
var bootstrapResumed: boolean = false;
var element = html("<div></div>");
window.name = 'NG_DEFER_BOOTSTRAP!' + window.name;
adapter.bootstrap(element, ['ng1'])
.ready((ref) => {
expect(bootstrapResumed).toEqual(true);
ref.dispose();
async.done();
});
setTimeout(() => {
bootstrapResumed = true;
(<any>global).angular.resumeBootstrap();
}, 100);
}));
it('should wait for ng2 testability', inject([AsyncTestCompleter], (async) => {
var adapter: UpgradeAdapter = new UpgradeAdapter();
var ng1Module = angular.module('ng1', []);
var element = html("<div></div>");
adapter.bootstrap(element, ['ng1'])
.ready((ref) => {
var ng2Testability: Testability = ref.ng2Injector.get(Testability);
ng2Testability.increasePendingRequestCount();
var ng2Stable = false;
angular.getTestability(element).whenStable(function() {
expect(ng2Stable).toEqual(true);
ref.dispose();
async.done();
});
setTimeout(() => {
ng2Stable = true;
ng2Testability.decreasePendingRequestCount();
}, 100);
});
}));
});
describe('examples', () => {
it('should verify UpgradeAdapter example', inject([AsyncTestCompleter], (async) => {
var adapter = new UpgradeAdapter();
@ -594,7 +649,6 @@ export function main() {
});
}));
});
});
}