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
This commit is contained in:
marc
2015-01-28 15:12:14 +00:00
committed by Peter Bacon Darwin
parent 6ec5946094
commit 0bcd0872d8
4 changed files with 152 additions and 11 deletions
+5 -1
View File
@@ -1408,8 +1408,12 @@ function bootstrap(element, modules, config) {
forEach(extraModules, function(module) {
modules.push(module);
});
doBootstrap();
return doBootstrap();
};
if (isFunction(angular.resumeDeferredBootstrap)) {
angular.resumeDeferredBootstrap();
}
}
/**
+30 -10
View File
@@ -68,19 +68,31 @@ angular.scenario.Application.prototype.navigateTo = function(url, loadFn, errorF
try {
var $window = self.getWindow_();
if ($window.angular) {
// Disable animations
$window.angular.resumeBootstrap([['$provide', function($provide) {
return ['$animate', function($animate) {
$animate.enabled(false);
}];
}]]);
if (!$window.angular) {
self.executeAction(loadFn);
return;
}
if (!$window.angular.resumeBootstrap) {
$window.angular.resumeDeferredBootstrap = resumeDeferredBootstrap;
} else {
resumeDeferredBootstrap();
}
self.executeAction(loadFn);
} catch (e) {
errorFn(e);
}
function resumeDeferredBootstrap() {
// Disable animations
var $injector = $window.angular.resumeBootstrap([['$provide', function($provide) {
return ['$animate', function($animate) {
$animate.enabled(false);
}];
}]]);
self.rootElement = $injector.get('$rootElement')[0];
self.executeAction(loadFn);
}
}).attr('src', url);
// for IE compatibility set the name *after* setting the frame url
@@ -105,7 +117,15 @@ angular.scenario.Application.prototype.executeAction = function(action) {
if (!$window.angular) {
return action.call(this, $window, _jQuery($window.document));
}
angularInit($window.document, function(element) {
if (!!this.rootElement) {
executeWithElement(this.rootElement);
}
else {
angularInit($window.document, angular.bind(this, executeWithElement));
}
function executeWithElement(element) {
var $injector = $window.angular.element(element).injector();
var $element = _jQuery(element);
@@ -118,5 +138,5 @@ angular.scenario.Application.prototype.executeAction = function(action) {
action.call(self, $window, $element);
});
});
});
}
};
+20
View File
@@ -1081,6 +1081,26 @@ describe('angular', function() {
window.name = originalName;
});
it('should provide injector for deferred bootstrap', function() {
var injector;
window.name = 'NG_DEFER_BOOTSTRAP!';
injector = angular.bootstrap(element);
expect(injector).toBeUndefined();
injector = angular.resumeBootstrap();
expect(injector).toBeDefined();
});
it('should resume deferred bootstrap, if defined', function() {
var injector;
window.name = 'NG_DEFER_BOOTSTRAP!';
angular.resumeDeferredBootstrap = noop;
var spy = spyOn(angular, "resumeDeferredBootstrap");
injector = angular.bootstrap(element);
expect(spy).toHaveBeenCalled();
});
it('should wait for extra modules', function() {
window.name = 'NG_DEFER_BOOTSTRAP!';
+97
View File
@@ -118,6 +118,75 @@ describe('angular.scenario.Application', function() {
expect(called).toBeTruthy();
});
it('should set rootElement when navigateTo instigates bootstrap', inject(function($injector, $browser) {
var called;
var testWindow = {
document: jqLite('<div class="test-foo"></div>')[0],
angular: {
element: jqLite,
service: {},
resumeBootstrap: noop
}
};
jqLite(testWindow.document).data('$injector', $injector);
var resumeBootstrapSpy = spyOn(testWindow.angular, 'resumeBootstrap').andReturn($injector);
var injectorGet = $injector.get;
spyOn($injector, 'get').andCallFake(function(name) {
switch (name) {
case "$rootElement": return jqLite(testWindow.document);
default: return injectorGet(name);
}
});
app.getWindow_ = function() {
return testWindow;
};
app.navigateTo('http://localhost/', noop);
callLoadHandlers(app);
expect(app.rootElement).toBe(testWindow.document);
expect(resumeBootstrapSpy).toHaveBeenCalled();
dealoc(testWindow.document);
}));
it('should set setup resumeDeferredBootstrap if resumeBootstrap is not yet defined', inject(function($injector, $browser) {
var called;
var testWindow = {
document: jqLite('<div class="test-foo"></div>')[0],
angular: {
element: jqLite,
service: {},
resumeBootstrap: null
}
};
jqLite(testWindow.document).data('$injector', $injector);
var injectorGet = $injector.get;
var injectorSpy = spyOn($injector, 'get').andCallFake(function(name) {
switch (name) {
case "$rootElement": return jqLite(testWindow.document);
default: return injectorGet(name);
}
});
app.getWindow_ = function() {
return testWindow;
};
app.navigateTo('http://localhost/', noop);
expect(testWindow.angular.resumeDeferredBootstrap).toBeUndefined();
callLoadHandlers(app);
expect(testWindow.angular.resumeDeferredBootstrap).toBeDefined();
expect(app.rootElement).toBeUndefined;
expect(injectorSpy).not.toHaveBeenCalled();
var resumeBootstrapSpy = spyOn(testWindow.angular, 'resumeBootstrap').andReturn($injector);
testWindow.angular.resumeDeferredBootstrap();
expect(app.rootElement).toBe(testWindow.document);
expect(resumeBootstrapSpy).toHaveBeenCalled();
expect(injectorSpy).toHaveBeenCalledWith("$rootElement");
dealoc(testWindow.document);
}));
it('should wait for pending requests in executeAction', inject(function($injector, $browser) {
var called, polled;
var handlers = [];
@@ -144,4 +213,32 @@ describe('angular.scenario.Application', function() {
handlers[0]();
dealoc(testWindow.document);
}));
it('should allow explicit rootElement', inject(function($injector, $browser) {
var called, polled;
var handlers = [];
var testWindow = {
document: jqLite('<div class="test-foo"></div>')[0],
angular: {
element: jqLite,
service: {}
}
};
$browser.notifyWhenNoOutstandingRequests = function(fn) {
handlers.push(fn);
};
app.rootElement = testWindow.document;
jqLite(testWindow.document).data('$injector', $injector);
app.getWindow_ = function() {
return testWindow;
};
app.executeAction(function($window, $document) {
expect($window).toEqual(testWindow);
expect($document).toBeDefined();
expect($document[0].className).toEqual('test-foo');
});
expect(handlers.length).toEqual(1);
handlers[0]();
dealoc(testWindow.document);
}));
});