http-backend-proxy
Version:
A proxy that allows configuring the AngularJs e2e $httpBackend service from within protractor based tests and enables protractor-to-browser data sharing.
245 lines (178 loc) • 6.66 kB
JavaScript
/**
* http-backend-proxy - https://github.com/kbaltrinic/http-backend-proxy
* (c) 2014 Kenneth Baltrinic. http://www.baltrinic.com
* License: MIT
*/
;
var Proxy = function(browser, options){
var DEFAULT_CONTEXT_FIELD_NAME = 'context';
options || (options = {});
options.buffer || (options.buffer = false);
//This is for backward compatibility since we changed the API from 1.1.1 to 1.2
if(options.contextField === false){
console.warn("Setting contextField: false is deprected. Set contextAutoSync: false instead.");
options.contextAutoSync = false;
}
options.contextAutoSync = typeof(options.contextAutoSync) === 'undefined' || !!options.contextAutoSync;
options.contextField || (options.contextField = DEFAULT_CONTEXT_FIELD_NAME);
var proxy = this;
var buffer = [];
createMethods(this, 'when', buildWhenFunction);
function createMethods(proxy, prefix, functionBuilder) {
['', 'GET', 'PUT', 'HEAD', 'POST', 'DELETE', 'PATCH', 'JSONP'].forEach(function(method) {
proxy[prefix + method] = functionBuilder(prefix + method);
});
}
function buildWhenFunction(funcName){
return function(){
var whenJS = '$httpBackend.' + funcName + '(' + stringifyArgs(arguments) + ')';
return {
respond: function() {
var fullJS = whenJS +'.respond(' + stringifyArgs(arguments) + ');'
return executeOrBuffer(fullJS);
},
passThrough: function() {
return executeOrBuffer(whenJS +'.passThrough(' + stringifyArgs(arguments) + ');');
}
};
};
};
var wrapBrowserGet = function(){};
function executeOrBuffer(script){
if(options.buffer){
buffer.push(script);
if(buffer.length == 1) wrapBrowserGet();
} else {
script = wrapScriptWithinInjectorInvoke(getContextDefinitionScript() + script);
return browser.executeScript(script);
}
}
function wrapScriptWithinInjectorInvoke(script){
return '\
var el = document.querySelector("' + browser.rootEl + '");\
angular.element(el).injector().invoke(["$httpBackend", function($httpBackend){\
' + script + '}]);';
}
if(arguments.length < 3){
this[options.contextField] = {};
this.flush = function(){
if(buffer.length > 0){
var script = wrapScriptWithinInjectorInvoke(getContextDefinitionScript() + buffer.join('\n'));
buffer = [];
return browser.executeScript(script);
} else {
var deferred = protractor.promise.defer();
deferred.fulfill();
return deferred.promise;
}
}
this.syncContext = function(context){
if(typeof(context) !== 'undefined'){
//If and only if both are simple objects, merge them
if(Object.prototype.toString.call(proxy[options.contextField]) === '[object Object]'
&& Object.prototype.toString.call(context) === '[object Object]'){
for (var key in context) {
if (context.hasOwnProperty(key)) {
proxy[options.contextField][key] = context[key];
}
}
context = proxy[options.contextField];
} else {
proxy[options.contextField] = context;
}
} else {
if(typeof(proxy[options.contextField]) === 'undefined'){
proxy[options.contextField] = {};
}
context = proxy[options.contextField];
}
return browser.executeScript(
wrapScriptWithinInjectorInvoke(
getContextDefinitionScript(context)));
}
var onLoad;
this.__defineGetter__("onLoad", function(){
if(onLoad) return onLoad;
var _options_ = { buffer: true, contextField: options.contextField };
return onLoad = new Proxy(browser, _options_, proxy);
});
} else {
var parent = arguments[2];
var buildModuleScript = function (){
var script = getContextDefinitionScript(parent[options.contextField]) + buffer.join('\n');
return 'angular.module("http-backend-proxy", ["ngMockE2E"]).run(["$httpBackend", function($httpBackend){' +
script.replace(/window\.\$httpBackend/g, '$httpBackend') + '}]);'
}
wrapBrowserGet = function(){
var get = browser.get;
browser.get = function(){
if(buffer.length > 0){
if(browser.get.addedOnce && browser.removeMockModule){ browser.removeMockModule('http-backend-proxy') };
browser.addMockModule('http-backend-proxy', buildModuleScript());
//addedOnce is a workaround for Protractor issue #764
browser.get.addedOnce = true;
}
return get.apply(browser, arguments);
};
browser.get.__super__ = get;
browser.get.addedOnce = false;
}
this.reset = function() {
buffer = [];
if(browser.get.__super__){
if(browser.get.addedOnce && browser.removeMockModule){ browser.removeMockModule('http-backend-proxy') };
browser.get = browser.get.__super__;
}
};
}
function stringifyArgs(args){
var i, s = [];
for(i = 0; i < args.length; i++){
s.push(stringifyObject(args[i]));
}
return s.join(', ');
}
function getContextDefinitionScript(context){
if(options.contextAutoSync){
context = context || proxy[options.contextField];
}
if(typeof(context) !== 'undefined'){
return '$httpBackend.' + options.contextField + '=' + stringifyObject(context) + ';';
} else {
return '';
}
}
function stringifyObject(obj){
if(obj === null)
return 'null';
if(typeof obj === 'function')
return obj.toString();
if(obj instanceof Date)
return 'new Date(' + obj.valueOf() + ')';
if(obj instanceof RegExp){
var regexToString = obj.toString();
var regexEndIndex = regexToString.lastIndexOf("/");
var expression = JSON.stringify(regexToString.slice(1, regexEndIndex));
var modifiers = regexToString.substring(regexEndIndex + 1);
if(modifiers.length > 0) modifiers = "," + JSON.stringify(modifiers);
return 'new RegExp(' + expression + modifiers + ')';
}
if(obj instanceof Array){
var elements = []
obj.forEach(function(element){
elements.push(stringifyObject(element));
});
return '[' + elements.join(',') + ']';
} else if(typeof(obj) === 'object'){
var fields = [];
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
fields.push(JSON.stringify(key) + ':' + stringifyObject(obj[key]));
}
}
return '{' + fields.join(',') + '}';
}
return JSON.stringify(obj);
}
};
module.exports = Proxy;