Skip to content

Commit 430f367

Browse files
committed
fix(upgrade): make ngUpgrade work with testability API
Closes angular#7603
1 parent d272f96 commit 430f367

File tree

4 files changed

+143
-20
lines changed

4 files changed

+143
-20
lines changed

modules/angular2/src/upgrade/angular_js.ts

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,16 +97,33 @@ export interface IControllerService {
9797

9898
export interface IInjectorService { get(key: string): any; }
9999

100+
export interface ITestabilityService {
101+
findBindings(element: Element, expression: string, opt_exactMatch?: boolean): Element[];
102+
findModels(element: Element, expression: string, opt_exactMatch?: boolean): Element[];
103+
getLocation(): string;
104+
setLocation(url: string): void;
105+
whenStable(callback: Function): void;
106+
}
107+
100108
function noNg() {
101109
throw new Error('AngularJS v1.x is not loaded!');
102110
}
103111

104-
var angular: {
105-
bootstrap: (e: Element, modules: string[], config: IAngularBootstrapConfig) => void,
106-
module: (prefix: string, dependencies?: string[]) => IModule,
107-
element: (e: Element) => IAugmentedJQuery,
108-
version: {major: number}
109-
} = <any>{bootstrap: noNg, module: noNg, element: noNg, version: noNg};
112+
var angular:
113+
{
114+
bootstrap: (e: Element, modules: string[], config: IAngularBootstrapConfig) => void,
115+
module: (prefix: string, dependencies?: string[]) => IModule,
116+
element: (e: Element) => IAugmentedJQuery,
117+
version: {major: number}, resumeBootstrap?: () => void,
118+
getTestability: (e: Element) => ITestabilityService
119+
} = <any>{
120+
bootstrap: noNg,
121+
module: noNg,
122+
element: noNg,
123+
version: noNg,
124+
resumeBootstrap: noNg,
125+
getTestability: noNg
126+
};
110127

111128

112129
try {
@@ -121,3 +138,5 @@ export var bootstrap = angular.bootstrap;
121138
export var module = angular.module;
122139
export var element = angular.element;
123140
export var version = angular.version;
141+
export var resumeBootstrap = angular.resumeBootstrap;
142+
export var getTestability = angular.getTestability;

modules/angular2/src/upgrade/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@ export const NG1_HTTP_BACKEND = '$httpBackend';
1212
export const NG1_INJECTOR = '$injector';
1313
export const NG1_PARSE = '$parse';
1414
export const NG1_TEMPLATE_CACHE = '$templateCache';
15+
export const NG1_TESTABILITY = '$$testability';
1516
export const REQUIRE_INJECTOR = '^' + NG2_INJECTOR;

modules/angular2/src/upgrade/upgrade_adapter.ts

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ import {
1010
HostViewFactoryRef,
1111
Provider,
1212
Type,
13+
Testability,
1314
APPLICATION_COMMON_PROVIDERS
1415
} from 'angular2/core';
16+
import {global} from 'angular2/src/facade/lang';
1517
import {ObservableWrapper} from 'angular2/src/facade/async';
1618
import {BROWSER_PROVIDERS, BROWSER_APP_PROVIDERS} from 'angular2/platform/browser';
1719

@@ -23,6 +25,7 @@ import {
2325
NG1_PARSE,
2426
NG1_ROOT_SCOPE,
2527
NG1_SCOPE,
28+
NG1_TESTABILITY,
2629
NG2_APP_VIEW_MANAGER,
2730
NG2_COMPILER,
2831
NG2_INJECTOR,
@@ -309,6 +312,7 @@ export class UpgradeAdapter {
309312
var rootScope: angular.IRootScopeService;
310313
var hostViewFactoryRefMap: HostViewFactoryRefMap = {};
311314
var ng1Module = angular.module(this.idPrefix, modules);
315+
var ng1BootstrapPromise: Promise<any> = null;
312316
var ng1compilePromise: Promise<any> = null;
313317
ng1Module.value(NG2_INJECTOR, injector)
314318
.value(NG2_ZONE, ngZone)
@@ -331,23 +335,68 @@ export class UpgradeAdapter {
331335
return rootScope = rootScopeDelegate;
332336
}
333337
]);
334-
}
335-
])
336-
.run([
337-
'$injector',
338-
'$rootScope',
339-
(injector: angular.IInjectorService, rootScope: angular.IRootScopeService) => {
340-
ng1Injector = injector;
341-
ObservableWrapper.subscribe(ngZone.onMicrotaskEmpty,
342-
(_) => ngZone.runOutsideAngular(() => rootScope.$apply()));
343-
ng1compilePromise =
344-
UpgradeNg1ComponentAdapterBuilder.resolve(this.downgradedComponents, injector);
338+
provide.decorator(NG1_TESTABILITY, [
339+
'$delegate',
340+
function(testabilityDelegate: angular.ITestabilityService) {
341+
var ng2Testability: Testability = injector.get(Testability);
342+
343+
var origonalWhenStable: Function = testabilityDelegate.whenStable;
344+
var newWhenStable = (callback: Function): void => {
345+
var whenStableContext: any = this;
346+
origonalWhenStable.call(this, function() {
347+
if (ng2Testability.isStable()) {
348+
callback.apply(this, arguments);
349+
} else {
350+
ng2Testability.whenStable(newWhenStable.bind(whenStableContext, callback));
351+
}
352+
});
353+
};
354+
355+
testabilityDelegate.whenStable = newWhenStable;
356+
return testabilityDelegate;
357+
}
358+
]);
345359
}
346360
]);
347361

362+
ng1compilePromise = new Promise((resolve, reject) => {
363+
ng1Module.run([
364+
'$injector',
365+
'$rootScope',
366+
(injector: angular.IInjectorService, rootScope: angular.IRootScopeService) => {
367+
ng1Injector = injector;
368+
ObservableWrapper.subscribe(ngZone.onMicrotaskEmpty,
369+
(_) => ngZone.runOutsideAngular(() => rootScope.$apply()));
370+
UpgradeNg1ComponentAdapterBuilder.resolve(this.downgradedComponents, injector)
371+
.then(resolve, reject);
372+
}
373+
]);
374+
});
375+
376+
// Make sure resumeBootstrap() only exists if the current bootstrap is deferred
377+
var windowAngular = (<any>global).angular;
378+
windowAngular.resumeBootstrap = undefined;
379+
348380
angular.element(element).data(controllerKey(NG2_INJECTOR), injector);
349381
ngZone.run(() => { angular.bootstrap(element, [this.idPrefix], config); });
350-
Promise.all([this.compileNg2Components(compiler, hostViewFactoryRefMap), ng1compilePromise])
382+
ng1BootstrapPromise = new Promise((resolve, reject) => {
383+
if (windowAngular.resumeBootstrap) {
384+
var originalResumeBootstrap: () => void = windowAngular.resumeBootstrap;
385+
windowAngular.resumeBootstrap = function() {
386+
windowAngular.resumeBootstrap = originalResumeBootstrap;
387+
windowAngular.resumeBootstrap.apply(this, arguments);
388+
resolve();
389+
};
390+
} else {
391+
resolve();
392+
}
393+
});
394+
395+
Promise.all([
396+
this.compileNg2Components(compiler, hostViewFactoryRefMap),
397+
ng1BootstrapPromise,
398+
ng1compilePromise
399+
])
351400
.then(() => {
352401
ngZone.run(() => {
353402
if (rootScopePrototype) {

modules/angular2/test/upgrade/upgrade_spec.ts

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,16 @@ import {
1212
} from 'angular2/testing_internal';
1313
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
1414

15-
import {Component, Class, Inject, EventEmitter, ApplicationRef, provide} from 'angular2/core';
15+
import {global} from 'angular2/src/facade/lang';
16+
import {
17+
Component,
18+
Class,
19+
Inject,
20+
EventEmitter,
21+
ApplicationRef,
22+
provide,
23+
Testability,
24+
} from 'angular2/core';
1625
import {UpgradeAdapter} from 'angular2/upgrade';
1726
import * as angular from 'angular2/src/upgrade/angular_js';
1827

@@ -559,6 +568,52 @@ export function main() {
559568
}));
560569
});
561570

571+
describe('testability', () => {
572+
it('should handle deferred bootstrap', inject([AsyncTestCompleter], (async) => {
573+
var adapter: UpgradeAdapter = new UpgradeAdapter();
574+
var ng1Module = angular.module('ng1', []);
575+
var bootstrapResumed: boolean = false;
576+
577+
var element = html("<div></div>");
578+
window.name = 'NG_DEFER_BOOTSTRAP!' + window.name;
579+
580+
adapter.bootstrap(element, ['ng1'])
581+
.ready((ref) => {
582+
expect(bootstrapResumed).toEqual(true);
583+
ref.dispose();
584+
async.done();
585+
});
586+
587+
setTimeout(() => {
588+
bootstrapResumed = true;
589+
(<any>global).angular.resumeBootstrap();
590+
}, 100);
591+
}));
592+
593+
it('should wait for ng2 testability', inject([AsyncTestCompleter], (async) => {
594+
var adapter: UpgradeAdapter = new UpgradeAdapter();
595+
var ng1Module = angular.module('ng1', []);
596+
var element = html("<div></div>");
597+
adapter.bootstrap(element, ['ng1'])
598+
.ready((ref) => {
599+
var ng2Testability: Testability = ref.ng2Injector.get(Testability);
600+
ng2Testability.increasePendingRequestCount();
601+
var ng2Stable = false;
602+
603+
angular.getTestability(element).whenStable(function() {
604+
expect(ng2Stable).toEqual(true);
605+
ref.dispose();
606+
async.done();
607+
});
608+
609+
setTimeout(() => {
610+
ng2Stable = true;
611+
ng2Testability.decreasePendingRequestCount();
612+
}, 100);
613+
});
614+
}));
615+
});
616+
562617
describe('examples', () => {
563618
it('should verify UpgradeAdapter example', inject([AsyncTestCompleter], (async) => {
564619
var adapter = new UpgradeAdapter();
@@ -594,7 +649,6 @@ export function main() {
594649
});
595650
}));
596651
});
597-
598652
});
599653
}
600654

0 commit comments

Comments
 (0)