Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit c69caa7

Browse files
marcfallowspetebacondarwin
authored andcommittedJan 29, 2015
fix(ngScenario): Allow ngScenario to handle lazy-loaded and manually bootstrapped applications
I know protractor is preferred, and ngScenario is only in maintenance mode. But, we are limited to ngScenario based on the devices/browsers we are targeting (no web-driver available). So, we need to address the bug where ngScenario does not work with manual bootstrap and also has issues if angular.resumeBootstrap is not yet defined (race condition when lazy-loading). Closes #10723
1 parent 8c46919 commit c69caa7

File tree

4 files changed

+152
-11
lines changed

4 files changed

+152
-11
lines changed
 

‎src/Angular.js

+5-1
Original file line numberDiff line numberDiff line change
@@ -1403,8 +1403,12 @@ function bootstrap(element, modules, config) {
14031403
forEach(extraModules, function(module) {
14041404
modules.push(module);
14051405
});
1406-
doBootstrap();
1406+
return doBootstrap();
14071407
};
1408+
1409+
if (isFunction(angular.resumeDeferredBootstrap)) {
1410+
angular.resumeDeferredBootstrap();
1411+
}
14081412
}
14091413

14101414
/**

‎src/ngScenario/Application.js

+30-10
Original file line numberDiff line numberDiff line change
@@ -68,19 +68,31 @@ angular.scenario.Application.prototype.navigateTo = function(url, loadFn, errorF
6868
try {
6969
var $window = self.getWindow_();
7070

71-
if ($window.angular) {
72-
// Disable animations
73-
$window.angular.resumeBootstrap([['$provide', function($provide) {
74-
return ['$animate', function($animate) {
75-
$animate.enabled(false);
76-
}];
77-
}]]);
71+
if (!$window.angular) {
72+
self.executeAction(loadFn);
73+
return;
74+
}
75+
76+
if (!$window.angular.resumeBootstrap) {
77+
$window.angular.resumeDeferredBootstrap = resumeDeferredBootstrap;
78+
} else {
79+
resumeDeferredBootstrap();
7880
}
7981

80-
self.executeAction(loadFn);
8182
} catch (e) {
8283
errorFn(e);
8384
}
85+
86+
function resumeDeferredBootstrap() {
87+
// Disable animations
88+
var $injector = $window.angular.resumeBootstrap([['$provide', function($provide) {
89+
return ['$animate', function($animate) {
90+
$animate.enabled(false);
91+
}];
92+
}]]);
93+
self.rootElement = $injector.get('$rootElement')[0];
94+
self.executeAction(loadFn);
95+
}
8496
}).attr('src', url);
8597

8698
// for IE compatibility set the name *after* setting the frame url
@@ -105,7 +117,15 @@ angular.scenario.Application.prototype.executeAction = function(action) {
105117
if (!$window.angular) {
106118
return action.call(this, $window, _jQuery($window.document));
107119
}
108-
angularInit($window.document, function(element) {
120+
121+
if (!!this.rootElement) {
122+
executeWithElement(this.rootElement);
123+
}
124+
else {
125+
angularInit($window.document, angular.bind(this, executeWithElement));
126+
}
127+
128+
function executeWithElement(element) {
109129
var $injector = $window.angular.element(element).injector();
110130
var $element = _jQuery(element);
111131

@@ -118,5 +138,5 @@ angular.scenario.Application.prototype.executeAction = function(action) {
118138
action.call(self, $window, $element);
119139
});
120140
});
121-
});
141+
}
122142
};

‎test/AngularSpec.js

+20
Original file line numberDiff line numberDiff line change
@@ -1074,6 +1074,26 @@ describe('angular', function() {
10741074
window.name = originalName;
10751075
});
10761076

1077+
it('should provide injector for deferred bootstrap', function() {
1078+
var injector;
1079+
window.name = 'NG_DEFER_BOOTSTRAP!';
1080+
1081+
injector = angular.bootstrap(element);
1082+
expect(injector).toBeUndefined();
1083+
1084+
injector = angular.resumeBootstrap();
1085+
expect(injector).toBeDefined();
1086+
});
1087+
1088+
it('should resume deferred bootstrap, if defined', function() {
1089+
var injector;
1090+
window.name = 'NG_DEFER_BOOTSTRAP!';
1091+
1092+
angular.resumeDeferredBootstrap = noop;
1093+
var spy = spyOn(angular, "resumeDeferredBootstrap");
1094+
injector = angular.bootstrap(element);
1095+
expect(spy).toHaveBeenCalled();
1096+
});
10771097

10781098
it('should wait for extra modules', function() {
10791099
window.name = 'NG_DEFER_BOOTSTRAP!';

‎test/ngScenario/ApplicationSpec.js

+97
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,75 @@ describe('angular.scenario.Application', function() {
118118
expect(called).toBeTruthy();
119119
});
120120

121+
it('should set rootElement when navigateTo instigates bootstrap', inject(function($injector, $browser) {
122+
var called;
123+
var testWindow = {
124+
document: jqLite('<div class="test-foo"></div>')[0],
125+
angular: {
126+
element: jqLite,
127+
service: {},
128+
resumeBootstrap: noop
129+
}
130+
};
131+
jqLite(testWindow.document).data('$injector', $injector);
132+
var resumeBootstrapSpy = spyOn(testWindow.angular, 'resumeBootstrap').andReturn($injector);
133+
134+
var injectorGet = $injector.get;
135+
spyOn($injector, 'get').andCallFake(function(name) {
136+
switch (name) {
137+
case "$rootElement": return jqLite(testWindow.document);
138+
default: return injectorGet(name);
139+
}
140+
});
141+
142+
app.getWindow_ = function() {
143+
return testWindow;
144+
};
145+
app.navigateTo('http://localhost/', noop);
146+
callLoadHandlers(app);
147+
expect(app.rootElement).toBe(testWindow.document);
148+
expect(resumeBootstrapSpy).toHaveBeenCalled();
149+
dealoc(testWindow.document);
150+
}));
151+
152+
it('should set setup resumeDeferredBootstrap if resumeBootstrap is not yet defined', inject(function($injector, $browser) {
153+
var called;
154+
var testWindow = {
155+
document: jqLite('<div class="test-foo"></div>')[0],
156+
angular: {
157+
element: jqLite,
158+
service: {},
159+
resumeBootstrap: null
160+
}
161+
};
162+
jqLite(testWindow.document).data('$injector', $injector);
163+
164+
var injectorGet = $injector.get;
165+
var injectorSpy = spyOn($injector, 'get').andCallFake(function(name) {
166+
switch (name) {
167+
case "$rootElement": return jqLite(testWindow.document);
168+
default: return injectorGet(name);
169+
}
170+
});
171+
172+
app.getWindow_ = function() {
173+
return testWindow;
174+
};
175+
app.navigateTo('http://localhost/', noop);
176+
expect(testWindow.angular.resumeDeferredBootstrap).toBeUndefined();
177+
callLoadHandlers(app);
178+
expect(testWindow.angular.resumeDeferredBootstrap).toBeDefined();
179+
expect(app.rootElement).toBeUndefined;
180+
expect(injectorSpy).not.toHaveBeenCalled();
181+
182+
var resumeBootstrapSpy = spyOn(testWindow.angular, 'resumeBootstrap').andReturn($injector);
183+
testWindow.angular.resumeDeferredBootstrap();
184+
expect(app.rootElement).toBe(testWindow.document);
185+
expect(resumeBootstrapSpy).toHaveBeenCalled();
186+
expect(injectorSpy).toHaveBeenCalledWith("$rootElement");
187+
dealoc(testWindow.document);
188+
}));
189+
121190
it('should wait for pending requests in executeAction', inject(function($injector, $browser) {
122191
var called, polled;
123192
var handlers = [];
@@ -144,4 +213,32 @@ describe('angular.scenario.Application', function() {
144213
handlers[0]();
145214
dealoc(testWindow.document);
146215
}));
216+
217+
it('should allow explicit rootElement', inject(function($injector, $browser) {
218+
var called, polled;
219+
var handlers = [];
220+
var testWindow = {
221+
document: jqLite('<div class="test-foo"></div>')[0],
222+
angular: {
223+
element: jqLite,
224+
service: {}
225+
}
226+
};
227+
$browser.notifyWhenNoOutstandingRequests = function(fn) {
228+
handlers.push(fn);
229+
};
230+
app.rootElement = testWindow.document;
231+
jqLite(testWindow.document).data('$injector', $injector);
232+
app.getWindow_ = function() {
233+
return testWindow;
234+
};
235+
app.executeAction(function($window, $document) {
236+
expect($window).toEqual(testWindow);
237+
expect($document).toBeDefined();
238+
expect($document[0].className).toEqual('test-foo');
239+
});
240+
expect(handlers.length).toEqual(1);
241+
handlers[0]();
242+
dealoc(testWindow.document);
243+
}));
147244
});

0 commit comments

Comments
 (0)
This repository has been archived.