aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Rakefile1
-rw-r--r--src/Angular.js6
-rw-r--r--src/AngularPublic.js1
-rw-r--r--src/Compiler.js7
-rw-r--r--src/Injector.js59
-rw-r--r--src/Scope.js61
-rw-r--r--src/angular-bootstrap.js1
-rw-r--r--src/services.js67
-rw-r--r--src/widgets.js6
-rw-r--r--test/InjectorSpec.js71
-rw-r--r--test/ResourceSpec.js20
-rw-r--r--test/ScenarioSpec.js5
-rw-r--r--test/ScopeSpec.js75
-rw-r--r--test/ValidatorsTest.js3
-rw-r--r--test/directivesSpec.js2
-rw-r--r--test/servicesSpec.js171
-rw-r--r--test/widgetsSpec.js4
17 files changed, 347 insertions, 213 deletions
diff --git a/Rakefile b/Rakefile
index 7d5ff7eb..c28fc686 100644
--- a/Rakefile
+++ b/Rakefile
@@ -29,6 +29,7 @@ task :compile_scenario do
src/jqLite.js \
src/JSON.js \
src/Scope.js \
+ src/Injector.js \
src/Parser.js \
src/Resource.js \
src/Browser.js \
diff --git a/src/Angular.js b/src/Angular.js
index f7351bdc..5bffac53 100644
--- a/src/Angular.js
+++ b/src/Angular.js
@@ -330,6 +330,10 @@ function escapeAttr(html) {
'"');
}
+function concat(array1, array2, index) {
+ return array1.concat(slice.call(array2, index, array2.length));
+}
+
function bind(self, fn) {
var curryArgs = arguments.length > 2 ? slice.call(arguments, 2, arguments.length) : [];
if (typeof fn == $function) {
@@ -403,7 +407,7 @@ function angularInit(config){
// TODO default to the source of angular.js
var scope = compile(window.document, _null, {'$config':config});
if (config.css)
- scope.$browser.addCss(config.base_url + config.css);
+ scope.$inject('$browser').addCss(config.base_url + config.css);
scope.$init();
}
}
diff --git a/src/AngularPublic.js b/src/AngularPublic.js
index 617a7e2e..2b5d4fbc 100644
--- a/src/AngularPublic.js
+++ b/src/AngularPublic.js
@@ -21,6 +21,7 @@ extend(angular, {
'extend': extend,
'equals': equals,
'foreach': foreach,
+ 'injector': createInjector,
'noop':noop,
'bind':bind,
'toJson': toJson,
diff --git a/src/Compiler.js b/src/Compiler.js
index 0c80d3fc..a78a04c9 100644
--- a/src/Compiler.js
+++ b/src/Compiler.js
@@ -34,7 +34,7 @@ Template.prototype = {
foreach(this.inits, function(fn) {
queue.push(function() {
childScope.$tryEval(function(){
- return fn.call(childScope, element);
+ return childScope.$inject(fn, childScope, element);
}, element);
});
});
@@ -96,8 +96,7 @@ Compiler.prototype = {
return function(element, parentScope){
element = jqLite(element);
var scope = parentScope && parentScope.$eval ?
- parentScope :
- createScope(parentScope || {}, angularService);
+ parentScope : createScope(parentScope);
return extend(scope, {
$element:element,
$init: function() {
@@ -124,7 +123,7 @@ Compiler.prototype = {
text:function(text) {return jqLite(document.createTextNode(text));},
descend: function(value){ if(isDefined(value)) descend = value; return descend;},
directives: function(value){ if(isDefined(value)) directives = value; return directives;},
- scope: function(value){ if(isDefined(value)) template.newScope = template.newScope || value ; return template.newScope;}
+ scope: function(value){ if(isDefined(value)) template.newScope = template.newScope || value; return template.newScope;}
};
try {
priority = element.attr('ng:eval-order') || priority || 0;
diff --git a/src/Injector.js b/src/Injector.js
new file mode 100644
index 00000000..554e9089
--- /dev/null
+++ b/src/Injector.js
@@ -0,0 +1,59 @@
+/**
+ * Create an inject method
+ * @param providerScope provider's "this"
+ * @param providers a function(name) which returns provider function
+ * @param cache place where instances are saved for reuse
+ * @returns {Function}
+ */
+function createInjector(providerScope, providers, cache) {
+ providers = providers || angularService;
+ cache = cache || {};
+ providerScope = providerScope || {};
+ /**
+ * injection function
+ * @param value: string, array, object or function.
+ * @param scope: optional function "this"
+ * @param args: optional arguments to pass to function after injection
+ * parameters
+ * @returns depends on value:
+ * string: return an instance for the injection key.
+ * array of keys: returns an array of instances.
+ * function: look at $inject property of function to determine instances
+ * and then call the function with instances and scope. Any
+ * additional arguments are passed on to function.
+ * object: initialize eager providers and publish them the ones with publish here.
+ * none: same as object but use providerScope as place to publish.
+ */
+ return function inject(value, scope, args){
+ var returnValue, provider, creation;
+ if (isString(value)) {
+ if (!cache.hasOwnProperty(value)) {
+ provider = providers[value];
+ if (!provider) throw "Unknown provider for '"+value+"'.";
+ cache[value] = inject(provider, providerScope);
+ }
+ returnValue = cache[value];
+ } else if (isArray(value)) {
+ returnValue = [];
+ foreach(value, function(name) {
+ returnValue.push(inject(name));
+ });
+ } else if (isFunction(value)) {
+ returnValue = inject(value.$inject || []);
+ returnValue = value.apply(scope, concat(returnValue, arguments, 2));
+ } else if (isObject(value)) {
+ foreach(providers, function(provider, name){
+ creation = provider.$creation;
+ if (creation == 'eager') {
+ inject(name);
+ }
+ if (creation == 'eager-published') {
+ setter(value, name, inject(name));
+ }
+ });
+ } else {
+ returnValue = inject(providerScope);
+ }
+ return returnValue;
+ };
+} \ No newline at end of file
diff --git a/src/Scope.js b/src/Scope.js
index 3aaff361..ed608e95 100644
--- a/src/Scope.js
+++ b/src/Scope.js
@@ -108,20 +108,14 @@ function errorHandlerFor(element, error) {
elementError(element, NG_EXCEPTION, isDefined(error) ? toJson(error) : error);
}
-function createScope(parent, services, existing) {
+function createScope(parent, providers, instanceCache) {
function Parent(){}
- function API(){}
- function Behavior(){}
-
parent = Parent.prototype = (parent || {});
+ var instance = new Parent();
var evalLists = {sorted:[]};
var postList = [], postHash = {}, postId = 0;
- var servicesCache = extend({}, existing);
- var api = API.prototype = new Parent();
- var behavior = Behavior.prototype = new API();
- var instance = new Behavior();
- extend(api, {
+ extend(instance, {
'this': instance,
$id: (scopeId++),
$parent: parent,
@@ -227,46 +221,29 @@ function createScope(parent, services, existing) {
},
$become: function(Class) {
- // remove existing
- foreach(behavior, function(value, key){ delete behavior[key]; });
- foreach((Class || noop).prototype, function(fn, name){
- behavior[name] = bind(instance, fn);
- });
- (Class || noop).call(instance);
-
- //TODO: backwards compatibility hack, remove when Feedback's init methods are removed
- if (behavior.hasOwnProperty('init')) {
- behavior.init();
+ if (isFunction(Class)) {
+ instance.constructor = Class;
+ foreach(Class.prototype, function(fn, name){
+ instance[name] = bind(instance, fn);
+ });
+ instance.$inject.apply(instance, concat([Class, instance], arguments, 1));
}
+ },
+
+ $new: function(Class) {
+ var child = createScope(instance);
+ child.$become.apply(instance, concat([Class], arguments, 1));
+ instance.$onEval(child.$eval);
+ return child;
}
});
if (!parent.$root) {
- api.$root = instance;
- api.$parent = instance;
- }
-
- function inject(name){
- var service = servicesCache[name], factory, args = [];
- if (isUndefined(service)) {
- factory = services[name];
- if (!isFunction(factory))
- throw "Don't know how to inject '" + name + "'.";
- foreach(factory.inject, function(dependency){
- args.push(inject(dependency));
- });
- servicesCache[name] = service = factory.apply(instance, args);
- }
- return service;
+ instance.$root = instance;
+ instance.$parent = instance;
+ (instance.$inject = createInjector(instance, providers, instanceCache))();
}
- foreach(services, function(_, name){
- var service = inject(name);
- if (service) {
- setter(instance, name, service);
- }
- });
-
return instance;
}
diff --git a/src/angular-bootstrap.js b/src/angular-bootstrap.js
index 1f03b8a3..581f4ff9 100644
--- a/src/angular-bootstrap.js
+++ b/src/angular-bootstrap.js
@@ -41,6 +41,7 @@
addScript("/JSON.js");
addScript("/Compiler.js");
addScript("/Scope.js");
+ addScript("/Injector.js");
addScript("/jqLite.js");
addScript("/Parser.js");
addScript("/Resource.js");
diff --git a/src/services.js b/src/services.js
index 9dc3b456..5b65f8fd 100644
--- a/src/services.js
+++ b/src/services.js
@@ -1,13 +1,19 @@
var URL_MATCH = /^(file|ftp|http|https):\/\/(\w+:{0,1}\w*@)?([\w\.-]*)(:([0-9]+))?([^\?#]+)(\?([^#]*))?(#(.*))?$/,
HASH_MATCH = /^([^\?]*)?(\?([^\?]*))?$/,
- DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp':21};
+ DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp':21},
+ EAGER = 'eager',
+ EAGER_PUBLISHED = EAGER + '-published';
-angularService("$window", bind(window, identity, window));
-angularService("$document", function(window){
+function angularServiceInject(name, fn, inject, eager) {
+ angularService(name, fn, {$inject:inject, $creation:eager});
+}
+
+angularServiceInject("$window", bind(window, identity, window), [], EAGER_PUBLISHED);
+angularServiceInject("$document", function(window){
return jqLite(window.document);
-}, {inject:['$window']});
+}, ['$window'], EAGER_PUBLISHED);
-angularService("$location", function(browser){
+angularServiceInject("$location", function(browser){
var scope = this,
location = {parse:parseUrl, toString:toString, update:update},
lastLocation = {};
@@ -92,9 +98,9 @@ angularService("$location", function(browser){
update();
return location.href;
}
-}, {inject: ['$browser']});
+}, ['$browser'], EAGER_PUBLISHED);
-angularService("$log", function($window){
+angularServiceInject("$log", function($window){
var console = $window.console || {log: noop, warn: noop, info: noop, error: noop},
log = console.log || noop;
return {
@@ -103,15 +109,15 @@ angularService("$log", function($window){
info: bind(console, console.info || log),
error: bind(console, console.error || log)
};
-}, {inject:['$window']});
+}, ['$window'], EAGER_PUBLISHED);
-angularService('$exceptionHandler', function($log){
+angularServiceInject('$exceptionHandler', function($log){
return function(e) {
$log.error(e);
};
-}, {inject:['$log']});
+}, ['$log'], EAGER_PUBLISHED);
-angularService("$hover", function(browser, document) {
+angularServiceInject("$hover", function(browser, document) {
var tooltip, self = this, error, width = 300, arrowWidth = 10, body = jqLite(document[0].body);
browser.hover(function(element, show){
if (show && (error = element.attr(NG_EXCEPTION) || element.attr(NG_VALIDATION_ERROR))) {
@@ -155,14 +161,13 @@ angularService("$hover", function(browser, document) {
tooltip = _null;
}
});
-}, {inject:['$browser', '$document']});
-
+}, ['$browser', '$document'], EAGER);
/* Keeps references to all invalid widgets found during validation. Can be queried to find if there
* are invalid widgets currently displayed
*/
-angularService("$invalidWidgets", function(){
+angularServiceInject("$invalidWidgets", function(){
var invalidWidgets = [];
@@ -217,7 +222,7 @@ angularService("$invalidWidgets", function(){
}
return invalidWidgets;
-});
+}, [], EAGER_PUBLISHED);
@@ -244,7 +249,7 @@ function switchRouteMatcher(on, when, dstName) {
return match ? dst : _null;
}
-angularService('$route', function(location){
+angularServiceInject('$route', function(location){
var routes = {},
onChange = [],
matcher = switchRouteMatcher,
@@ -284,9 +289,9 @@ angularService('$route', function(location){
}
this.$watch(function(){return dirty + location.hash;}, updateRoute);
return $route;
-}, {inject: ['$location']});
+}, ['$location']);
-angularService('$xhr', function($browser, $error, $log){
+angularServiceInject('$xhr', function($browser, $error, $log){
var self = this;
return function(method, url, post, callback){
if (isFunction(post)) {
@@ -315,15 +320,15 @@ angularService('$xhr', function($browser, $error, $log){
}
});
};
-}, {inject:['$browser', '$xhr.error', '$log']});
+}, ['$browser', '$xhr.error', '$log']);
-angularService('$xhr.error', function($log){
+angularServiceInject('$xhr.error', function($log){
return function(request, response){
$log.error('ERROR: XHR: ' + request.url, request, response);
};
-}, {inject:['$log']});
+}, ['$log']);
-angularService('$xhr.bulk', function($xhr, $error, $log){
+angularServiceInject('$xhr.bulk', function($xhr, $error, $log){
var requests = [],
scope = this;
function bulkXHR(method, url, post, callback) {
@@ -371,9 +376,9 @@ angularService('$xhr.bulk', function($xhr, $error, $log){
};
this.$onEval(PRIORITY_LAST, bulkXHR.flush);
return bulkXHR;
-}, {inject:['$xhr', '$xhr.error', '$log']});
+}, ['$xhr', '$xhr.error', '$log']);
-angularService('$xhr.cache', function($xhr){
+angularServiceInject('$xhr.cache', function($xhr){
var inflight = {}, self = this;
function cache(method, url, post, callback, verifyCache){
if (isFunction(post)) {
@@ -415,12 +420,12 @@ angularService('$xhr.cache', function($xhr){
cache.data = {};
cache.delegate = $xhr;
return cache;
-}, {inject:['$xhr.bulk']});
+}, ['$xhr.bulk']);
-angularService('$resource', function($xhr){
+angularServiceInject('$resource', function($xhr){
var resource = new ResourceFactory($xhr);
return bind(resource, resource.route);
-}, {inject: ['$xhr.cache']});
+}, ['$xhr.cache']);
/**
@@ -430,7 +435,7 @@ angularService('$resource', function($xhr){
* Only a simple Object is exposed and by adding or removing properties to/from this object, new
* cookies are created or deleted from the browser at the end of the current eval.
*/
-angularService('$cookies', function($browser) {
+angularServiceInject('$cookies', function($browser) {
var rootScope = this,
cookies = {},
lastCookies = {},
@@ -499,14 +504,14 @@ angularService('$cookies', function($browser) {
}
}
}
-}, {inject: ['$browser']});
+}, ['$browser'], EAGER_PUBLISHED);
/**
* $cookieStore provides a key-value (string-object) storage that is backed by session cookies.
* Objects put or retrieved from this storage are automatically serialized or deserialized.
*/
-angularService('$cookieStore', function($store) {
+angularServiceInject('$cookieStore', function($store) {
return {
get: function(/**string*/key) {
@@ -522,4 +527,4 @@ angularService('$cookieStore', function($store) {
}
};
-}, {inject: ['$cookies']});
+}, ['$cookies'], EAGER_PUBLISHED);
diff --git a/src/widgets.js b/src/widgets.js
index 90f6837d..c9dfab99 100644
--- a/src/widgets.js
+++ b/src/widgets.js
@@ -253,7 +253,7 @@ angularWidget('ng:include', function(element){
this.directives(true);
} else {
element[0]['ng:compiled'] = true;
- return function(element){
+ return extend(function(xhr, element){
var scope = this, childScope;
var changeCounter = 0;
function incrementChange(){ changeCounter++;}
@@ -266,7 +266,7 @@ angularWidget('ng:include', function(element){
var src = this.$eval(srcExp),
useScope = this.$eval(scopeExp);
if (src) {
- scope.$xhr.cache('GET', src, function(code, response){
+ xhr('GET', src, function(code, response){
element.html(response);
childScope = useScope || createScope(scope);
compiler.compile(element)(element, childScope);
@@ -276,7 +276,7 @@ angularWidget('ng:include', function(element){
element.html('');
}
});
- };
+ }, {$inject:['$xhr.cache']});
}
});
diff --git a/test/InjectorSpec.js b/test/InjectorSpec.js
new file mode 100644
index 00000000..ba0f27f7
--- /dev/null
+++ b/test/InjectorSpec.js
@@ -0,0 +1,71 @@
+describe('injector', function(){
+ var providers;
+ var cache;
+ var inject;
+ var scope;
+
+ beforeEach(function(){
+ providers = extensionMap({}, 'providers');
+ cache = {};
+ scope = {};
+ inject = createInjector(scope, providers, cache);
+ });
+
+ it("should return same instance from calling provider", function(){
+ providers('text', function(){ return scope.name; });
+ scope.name = 'abc';
+ expect(inject('text')).toEqual('abc');
+ expect(cache.text).toEqual('abc');
+ scope.name = 'deleted';
+ expect(inject('text')).toEqual('abc');
+ });
+
+ it("should return an array of instances", function(){
+ cache.a = 0;
+ providers('b', function(){return 2;});
+ expect(inject(['a', 'b'])).toEqual([0,2]);
+ });
+
+ it("should call function", function(){
+ providers('a', function(){return 1;});
+ providers('b', function(){return 2;});
+ var args;
+ function fn(a, b, c, d) {
+ args = [this, a, b, c, d];
+ }
+ fn.$inject = ['a', 'b'];
+ inject(fn, {name:"this"}, 3, 4);
+ expect(args).toEqual([{name:'this'}, 1, 2, 3, 4]);
+ });
+
+ it('should inject providers', function(){
+ providers('a', function(){return this.mi = 'Mi';});
+ providers('b', function(mi){return this.name = mi+'sko';}, {$inject:['a']});
+ expect(inject('b')).toEqual('Misko');
+ expect(scope).toEqual({mi:'Mi', name:'Misko'});
+ });
+
+ it('should provide usefull message if no provider', function(){
+ assertThrows("Unknown provider for 'idontexist'.", function(){
+ inject('idontexist');
+ });
+ });
+
+ it('should autostart eager services', function(){
+ var log = '';
+ providers('eager', function(){log += 'eager;';}, {$creation: 'eager'});
+ inject();
+ expect(log).toEqual('eager;');
+ expect(scope.eager).not.toBeDefined();
+ });
+
+
+ it('should return a list of published objects', function(){
+ var log = '';
+ providers('eager', function(){log += 'eager;'; return 'pub'; }, {$creation: 'eager-published'});
+ inject();
+ expect(log).toEqual('eager;');
+ expect(scope.eager).toEqual('pub');
+
+ });
+}); \ No newline at end of file
diff --git a/test/ResourceSpec.js b/test/ResourceSpec.js
index 435176b0..e258d5a3 100644
--- a/test/ResourceSpec.js
+++ b/test/ResourceSpec.js
@@ -162,26 +162,30 @@ describe("resource", function() {
it('should excersize full stack', function(){
var scope = angular.compile('<div></div>');
- var Person = scope.$resource('/Person/:id');
- scope.$browser.xhr.expectGET('/Person/123').respond('\n{\nname:\n"misko"\n}\n');
+ var $browser = scope.$inject('$browser');
+ var $resource = scope.$inject('$resource');
+ var Person = $resource('/Person/:id');
+ $browser.xhr.expectGET('/Person/123').respond('\n{\nname:\n"misko"\n}\n');
var person = Person.get({id:123});
- scope.$browser.xhr.flush();
+ $browser.xhr.flush();
expect(person.name).toEqual('misko');
});
it('should return the same object when verifying the cache', function(){
var scope = angular.compile('<div></div>');
- var Person = scope.$resource('/Person/:id', null, {query: {method:'GET', isArray: true, verifyCache: true}});
- scope.$browser.xhr.expectGET('/Person/123').respond('[\n{\nname:\n"misko"\n}\n]');
+ var $browser = scope.$inject('$browser');
+ var $resource = scope.$inject('$resource');
+ var Person = $resource('/Person/:id', null, {query: {method:'GET', isArray: true, verifyCache: true}});
+ $browser.xhr.expectGET('/Person/123').respond('[\n{\nname:\n"misko"\n}\n]');
var person = Person.query({id:123});
- scope.$browser.xhr.flush();
+ $browser.xhr.flush();
expect(person[0].name).toEqual('misko');
- scope.$browser.xhr.expectGET('/Person/123').respond('[\n{\nname:\n"rob"\n}\n]');
+ $browser.xhr.expectGET('/Person/123').respond('[\n{\nname:\n"rob"\n}\n]');
var person2 = Person.query({id:123});
expect(person2[0].name).toEqual('misko');
var person2Cache = person2;
- scope.$browser.xhr.flush();
+ $browser.xhr.flush();
expect(person2Cache).toEqual(person2);
expect(person2[0].name).toEqual('rob');
});
diff --git a/test/ScenarioSpec.js b/test/ScenarioSpec.js
index ede49a49..730019a2 100644
--- a/test/ScenarioSpec.js
+++ b/test/ScenarioSpec.js
@@ -43,9 +43,10 @@ describe("ScenarioSpec: configuration", function(){
var url = "http://server/#?book=moby";
var scope = compile("<div>{{$location}}</div>");
var $location = scope.$location;
+ var $browser = scope.$inject('$browser');
expect($location.hashSearch.book).toBeUndefined();
- scope.$browser.setUrl(url);
- scope.$browser.poll();
+ $browser.setUrl(url);
+ $browser.poll();
expect($location.hashSearch.book).toEqual('moby');
});
});
diff --git a/test/ScopeSpec.js b/test/ScopeSpec.js
index 66e9d489..4a207bf0 100644
--- a/test/ScopeSpec.js
+++ b/test/ScopeSpec.js
@@ -1,11 +1,27 @@
describe('scope/model', function(){
+ var temp;
+
+ beforeEach(function() {
+ temp = window.temp = {};
+ temp.InjectController = function(exampleService, extra) {
+ this.localService = exampleService;
+ this.extra = extra;
+ this.$root.injectController = this;
+ };
+ temp.InjectController.$inject = ["exampleService"];
+ });
+
+ afterEach(function() {
+ window.temp = undefined;
+ });
+
it('should create a scope with parent', function(){
var model = createScope({name:'Misko'});
expect(model.name).toEqual('Misko');
});
- it('should have $get/set$/parent$', function(){
+ it('should have $get/$set/$parent', function(){
var parent = {};
var model = createScope(parent);
model.$set('name', 'adam');
@@ -138,40 +154,6 @@ describe('scope/model', function(){
});
});
- describe('service injection', function(){
- it('should inject services', function(){
- var scope = createScope(null, {
- service:function(){
- return "ABC";
- }
- });
- expect(scope.service).toEqual("ABC");
- });
-
- it('should inject arugments', function(){
- var scope = createScope(null, {
- name:function(){
- return "misko";
- },
- greet: extend(function(name) {
- return 'hello ' + name;
- }, {inject:['name']})
- });
- expect(scope.greet).toEqual("hello misko");
- });
-
- it('should throw error on missing dependency', function(){
- try {
- createScope(null, {
- greet: extend(function(name) {
- }, {inject:['name']})
- });
- } catch(e) {
- expect(e).toEqual("Don't know how to inject 'name'.");
- }
- });
- });
-
describe('getterFn', function(){
it('should get chain', function(){
expect(getterFn('a.b')(undefined)).toEqual(undefined);
@@ -215,4 +197,27 @@ describe('scope/model', function(){
});
});
+ describe('$new', function(){
+ it('should $new should create new child scope and $become controller', function(){
+ var parent = createScope(null, {exampleService: function(){return 'Example Service';}});
+ var child = parent.$new(temp.InjectController, 10);
+ expect(child.localService).toEqual('Example Service');
+ expect(child.extra).toEqual(10);
+
+ child.$onEval(function(){ this.run = true; });
+ parent.$eval();
+ expect(child.run).toEqual(true);
+ });
+ });
+
+ describe('$become', function(){
+ it('should inject properties on controller defined in $inject', function(){
+ var parent = createScope(null, {exampleService: function(){return 'Example Service';}});
+ var child = createScope(parent);
+ child.$become(temp.InjectController, 10);
+ expect(child.localService).toEqual('Example Service');
+ expect(child.extra).toEqual(10);
+ });
+ });
+
});
diff --git a/test/ValidatorsTest.js b/test/ValidatorsTest.js
index 07f3e488..463e05de 100644
--- a/test/ValidatorsTest.js
+++ b/test/ValidatorsTest.js
@@ -112,6 +112,9 @@ describe('Validator:asynchronous', function(){
if (self.$element) self.$element.remove();
var oldCache = jqCache;
jqCache = {};
+ if (size(oldCache)) {
+ dump(oldCache);
+ }
expect(size(oldCache)).toEqual(0);
});
diff --git a/test/directivesSpec.js b/test/directivesSpec.js
index 42a4879a..d8f04b29 100644
--- a/test/directivesSpec.js
+++ b/test/directivesSpec.js
@@ -282,7 +282,7 @@ describe("directives", function(){
});
it('should support nested controllers', function(){
- temp.ChildGreeter = function() {
+ temp.ChildGreeter = function(){
this.greeting = 'hey';
this.$root.childGreeter = this;
};
diff --git a/test/servicesSpec.js b/test/servicesSpec.js
index c0101098..6c586d0a 100644
--- a/test/servicesSpec.js
+++ b/test/servicesSpec.js
@@ -1,13 +1,19 @@
describe("service", function(){
- var scope, $xhrError, $log, mockServices;
+ var scope, $xhrError, $log, mockServices, inject, $browser, $browserXhr, $xhrBulk, $xhr, $route;
beforeEach(function(){
$xhrError = jasmine.createSpy('$xhr.error');
$log = {};
- scope = createScope(null, angularService, {
+ scope = createScope({}, angularService, {
'$xhr.error': $xhrError,
'$log': $log
});
+ inject = scope.$inject;
+ $browser = inject('$browser');
+ $browserXhr = $browser.xhr;
+ $xhrBulk = scope.$inject('$xhr.bulk');
+ $xhr = scope.$inject('$xhr');
+ $route = scope.$inject('$route');
});
afterEach(function(){
@@ -38,7 +44,7 @@ describe("service", function(){
function warn(){ logger+= 'warn;'; }
function info(){ logger+= 'info;'; }
function error(){ logger+= 'error;'; }
- var scope = createScope(null, angularService, {$window: {console:{log:log, warn:warn, info:info, error:error}}, $document:[{cookie:''}]});
+ var scope = createScope({}, angularService, {$window: {console:{log:log, warn:warn, info:info, error:error}}, $document:[{cookie:''}]});
scope.$log.log();
scope.$log.warn();
scope.$log.info();
@@ -49,7 +55,7 @@ describe("service", function(){
it('should use console.log if other not present', function(){
var logger = "";
function log(){ logger+= 'log;'; }
- var scope = createScope(null, angularService, {$window: {console:{log:log}}, $document:[{cookie:''}]});
+ var scope = createScope({}, angularService, {$window: {console:{log:log}}, $document:[{cookie:''}]});
scope.$log.log();
scope.$log.warn();
scope.$log.info();
@@ -58,7 +64,7 @@ describe("service", function(){
});
it('should use noop if no console', function(){
- var scope = createScope(null, angularService, {$window: {}, $document:[{cookie:''}]});
+ var scope = createScope({}, angularService, {$window: {}, $document:[{cookie:''}]});
scope.$log.log();
scope.$log.warn();
scope.$log.info();
@@ -182,49 +188,47 @@ describe("service", function(){
function BookChapter() {
this.log = '<init>';
}
- BookChapter.prototype.init = function(){
- log += 'init();';
- };
var scope = compile('<div></div>').$init();
- scope.$route.when('/Book/:book/Chapter/:chapter', {controller: BookChapter, template:'Chapter.html'});
- scope.$route.when('/Blank');
- scope.$route.onChange(function(){
+ var $route = scope.$inject('$route');
+ $route.when('/Book/:book/Chapter/:chapter', {controller: BookChapter, template:'Chapter.html'});
+ $route.when('/Blank');
+ $route.onChange(function(){
log += 'onChange();';
});
scope.$location.parse('http://server#/Book/Moby/Chapter/Intro?p=123');
scope.$eval();
- expect(log).toEqual('onChange();init();');
- expect(scope.$route.current.params).toEqual({book:'Moby', chapter:'Intro', p:'123'});
- expect(scope.$route.current.scope.log).toEqual('<init>');
- var lastId = scope.$route.current.scope.$id;
+ expect(log).toEqual('onChange();');
+ expect($route.current.params).toEqual({book:'Moby', chapter:'Intro', p:'123'});
+ expect($route.current.scope.log).toEqual('<init>');
+ var lastId = $route.current.scope.$id;
log = '';
scope.$location.parse('http://server#/Blank?ignore');
scope.$eval();
expect(log).toEqual('onChange();');
- expect(scope.$route.current.params).toEqual({ignore:true});
- expect(scope.$route.current.scope.$id).not.toEqual(lastId);
+ expect($route.current.params).toEqual({ignore:true});
+ expect($route.current.scope.$id).not.toEqual(lastId);
log = '';
scope.$location.parse('http://server#/NONE');
scope.$eval();
expect(log).toEqual('onChange();');
- expect(scope.$route.current).toEqual(null);
+ expect($route.current).toEqual(null);
- scope.$route.when('/NONE', {template:'instant update'});
+ $route.when('/NONE', {template:'instant update'});
scope.$eval();
- expect(scope.$route.current.template).toEqual('instant update');
+ expect($route.current.template).toEqual('instant update');
});
});
describe('$resource', function(){
it('should publish to root scope', function(){
- expect(scope.$resource).toBeTruthy();
+ expect(scope.$inject('$resource')).toBeTruthy();
});
});
describe('$xhr', function(){
- var log, xhr;
+ var log;
function callback(code, response) {
expect(code).toEqual(200);
log = log + toJson(response) + ';';
@@ -232,27 +236,26 @@ describe("service", function(){
beforeEach(function(){
log = '';
- xhr = scope.$browser.xhr;
});
it('should forward the request to $browser and decode JSON', function(){
- xhr.expectGET('/reqGET').respond('first');
- xhr.expectGET('/reqGETjson').respond('["second"]');
- xhr.expectPOST('/reqPOST', {post:'data'}).respond('third');
+ $browserXhr.expectGET('/reqGET').respond('first');
+ $browserXhr.expectGET('/reqGETjson').respond('["second"]');
+ $browserXhr.expectPOST('/reqPOST', {post:'data'}).respond('third');
- scope.$xhr('GET', '/reqGET', null, callback);
- scope.$xhr('GET', '/reqGETjson', null, callback);
- scope.$xhr('POST', '/reqPOST', {post:'data'}, callback);
+ $xhr('GET', '/reqGET', null, callback);
+ $xhr('GET', '/reqGETjson', null, callback);
+ $xhr('POST', '/reqPOST', {post:'data'}, callback);
- xhr.flush();
+ $browserXhr.flush();
expect(log).toEqual('"third";["second"];"first";');
});
it('should handle non 200 status codes by forwarding to error handler', function(){
- xhr.expectPOST('/req', 'MyData').respond(500, 'MyError');
- scope.$xhr('POST', '/req', 'MyData', callback);
- xhr.flush();
+ $browserXhr.expectPOST('/req', 'MyData').respond(500, 'MyError');
+ $xhr('POST', '/req', 'MyData', callback);
+ $browserXhr.flush();
var cb = $xhrError.mostRecentCall.args[0].callback;
expect(typeof cb).toEqual($function);
expect($xhrError).wasCalledWith(
@@ -262,45 +265,45 @@ describe("service", function(){
it('should handle exceptions in callback', function(){
$log.error = jasmine.createSpy('$log.error');
- xhr.expectGET('/reqGET').respond('first');
- scope.$xhr('GET', '/reqGET', null, function(){ throw "MyException"; });
- xhr.flush();
+ $browserXhr.expectGET('/reqGET').respond('first');
+ $xhr('GET', '/reqGET', null, function(){ throw "MyException"; });
+ $browserXhr.flush();
expect($log.error).wasCalledWith("MyException");
});
describe('bulk', function(){
it('should collect requests', function(){
- scope.$xhr.bulk.urls["/"] = {match:/.*/};
- scope.$xhr.bulk('GET', '/req1', null, callback);
- scope.$xhr.bulk('POST', '/req2', {post:'data'}, callback);
+ $xhrBulk.urls["/"] = {match:/.*/};
+ $xhrBulk('GET', '/req1', null, callback);
+ $xhrBulk('POST', '/req2', {post:'data'}, callback);
- xhr.expectPOST('/', {
+ $browserXhr.expectPOST('/', {
requests:[{method:'GET', url:'/req1', data: null},
{method:'POST', url:'/req2', data:{post:'data'} }]
}).respond([
{status:200, response:'first'},
{status:200, response:'second'}
]);
- scope.$xhr.bulk.flush(function(){ log += 'DONE';});
- xhr.flush();
+ $xhrBulk.flush(function(){ log += 'DONE';});
+ $browserXhr.flush();
expect(log).toEqual('"first";"second";DONE');
});
it('should handle non 200 status code by forwarding to error handler', function(){
- scope.$xhr.bulk.urls['/'] = {match:/.*/};
- scope.$xhr.bulk('GET', '/req1', null, callback);
- scope.$xhr.bulk('POST', '/req2', {post:'data'}, callback);
+ $xhrBulk.urls['/'] = {match:/.*/};
+ $xhrBulk('GET', '/req1', null, callback);
+ $xhrBulk('POST', '/req2', {post:'data'}, callback);
- xhr.expectPOST('/', {
+ $browserXhr.expectPOST('/', {
requests:[{method:'GET', url:'/req1', data: null},
{method:'POST', url:'/req2', data:{post:'data'} }]
}).respond([
{status:404, response:'NotFound'},
{status:200, response:'second'}
]);
- scope.$xhr.bulk.flush(function(){ log += 'DONE';});
- xhr.flush();
+ $xhrBulk.flush(function(){ log += 'DONE';});
+ $browserXhr.flush();
expect($xhrError).wasCalled();
var cb = $xhrError.mostRecentCall.args[0].callback;
@@ -315,29 +318,29 @@ describe("service", function(){
describe('cache', function(){
var cache;
- beforeEach(function(){ cache = scope.$xhr.cache; });
+ beforeEach(function(){ cache = scope.$inject('$xhr.cache'); });
it('should cache requests', function(){
- xhr.expectGET('/url').respond('first');
+ $browserXhr.expectGET('/url').respond('first');
cache('GET', '/url', null, callback);
- xhr.flush();
- xhr.expectGET('/url').respond('ERROR');
+ $browserXhr.flush();
+ $browserXhr.expectGET('/url').respond('ERROR');
cache('GET', '/url', null, callback);
- xhr.flush();
+ $browserXhr.flush();
expect(log).toEqual('"first";"first";');
cache('GET', '/url', null, callback, false);
- xhr.flush();
+ $browserXhr.flush();
expect(log).toEqual('"first";"first";"first";');
});
it('should first return cache request, then return server request', function(){
- xhr.expectGET('/url').respond('first');
+ $browserXhr.expectGET('/url').respond('first');
cache('GET', '/url', null, callback, true);
- xhr.flush();
- xhr.expectGET('/url').respond('ERROR');
+ $browserXhr.flush();
+ $browserXhr.expectGET('/url').respond('ERROR');
cache('GET', '/url', null, callback, true);
expect(log).toEqual('"first";"first";');
- xhr.flush();
+ $browserXhr.flush();
expect(log).toEqual('"first";"first";"ERROR";');
});
@@ -350,12 +353,12 @@ describe("service", function(){
});
it('should keep track of in flight requests and request only once', function(){
- scope.$xhr.bulk.urls['/bulk'] = {
+ scope.$inject('$xhr.bulk').urls['/bulk'] = {
match:function(url){
return url == '/url';
}
};
- xhr.expectPOST('/bulk', {
+ $browserXhr.expectPOST('/bulk', {
requests:[{method:'GET', url:'/url', data: null}]
}).respond([
{status:200, response:'123'}
@@ -363,12 +366,12 @@ describe("service", function(){
cache('GET', '/url', null, callback);
cache('GET', '/url', null, callback);
cache.delegate.flush();
- xhr.flush();
+ $browserXhr.flush();
expect(log).toEqual('"123";"123";');
});
it('should clear cache on non GET', function(){
- xhr.expectPOST('abc', {}).respond({});
+ $browserXhr.expectPOST('abc', {}).respond({});
cache.data.url = {value:123};
cache('POST', 'abc', {});
expect(cache.data.url).toBeUndefined();
@@ -380,12 +383,12 @@ describe("service", function(){
describe('$cookies', function() {
- var scope;
+ var scope, $browser;
beforeEach(function() {
- var browser = new MockBrowser();
- browser.cookieHash['preexisting'] = 'oldCookie';
- scope = createScope(null, angularService, {$browser: browser});
+ $browser = new MockBrowser();
+ $browser.cookieHash['preexisting'] = 'oldCookie';
+ scope = createScope(null, angularService, {$browser: $browser});
});
@@ -393,19 +396,19 @@ describe("service", function(){
function(){
expect(scope.$cookies).toEqual({'preexisting': 'oldCookie'});
- // access internal cookie storage of the browser mock directly to simulate behavior of
+ // access internal cookie storage of the browser mock directly to simulate behavior of
// document.cookie
- scope.$browser.cookieHash['brandNew'] = 'cookie';
- scope.$browser.poll();
+ $browser.cookieHash['brandNew'] = 'cookie';
+ $browser.poll();
expect(scope.$cookies).toEqual({'preexisting': 'oldCookie', 'brandNew':'cookie'});
- scope.$browser.cookieHash['brandNew'] = 'cookie2';
- scope.$browser.poll();
+ $browser.cookieHash['brandNew'] = 'cookie2';
+ $browser.poll();
expect(scope.$cookies).toEqual({'preexisting': 'oldCookie', 'brandNew':'cookie2'});
- delete scope.$browser.cookieHash['brandNew'];
- scope.$browser.poll();
+ delete $browser.cookieHash['brandNew'];
+ $browser.poll();
expect(scope.$cookies).toEqual({'preexisting': 'oldCookie'});
});
@@ -414,13 +417,13 @@ describe("service", function(){
scope.$cookies.oatmealCookie = 'nom nom';
scope.$eval();
- expect(scope.$browser.cookies()).
+ expect($browser.cookies()).
toEqual({'preexisting': 'oldCookie', 'oatmealCookie':'nom nom'});
scope.$cookies.oatmealCookie = 'gone';
scope.$eval();
- expect(scope.$browser.cookies()).
+ expect($browser.cookies()).
toEqual({'preexisting': 'oldCookie', 'oatmealCookie': 'gone'});
});
@@ -428,7 +431,7 @@ describe("service", function(){
it('should ignore non-string values when asked to create a cookie', function() {
scope.$cookies.nonString = [1, 2, 3];
scope.$eval();
- expect(scope.$browser.cookies()).toEqual({'preexisting': 'oldCookie'});
+ expect($browser.cookies()).toEqual({'preexisting': 'oldCookie'});
expect(scope.$cookies).toEqual({'preexisting': 'oldCookie'});
});
@@ -438,21 +441,21 @@ describe("service", function(){
scope.$cookies.undefVal = undefined;
scope.$eval();
- expect(scope.$browser.cookies()).toEqual({'preexisting': 'oldCookie'});
+ expect($browser.cookies()).toEqual({'preexisting': 'oldCookie'});
});
it('should remove a cookie when a $cookies property is deleted', function() {
scope.$cookies.oatmealCookie = 'nom nom';
scope.$eval();
- scope.$browser.poll();
- expect(scope.$browser.cookies()).
+ $browser.poll();
+ expect($browser.cookies()).
toEqual({'preexisting': 'oldCookie', 'oatmealCookie':'nom nom'});
delete scope.$cookies.oatmealCookie;
scope.$eval();
- expect(scope.$browser.cookies()).toEqual({'preexisting': 'oldCookie'});
+ expect($browser.cookies()).toEqual({'preexisting': 'oldCookie'});
});
@@ -485,13 +488,13 @@ describe("service", function(){
it('should serialize objects to json', function() {
scope.$cookieStore.put('objectCookie', {id: 123, name: 'blah'});
scope.$eval(); //force eval in test
- expect(scope.$browser.cookies()).toEqual({'objectCookie': '{"id":123,"name":"blah"}'});
+ expect($browser.cookies()).toEqual({'objectCookie': '{"id":123,"name":"blah"}'});
});
it('should deserialize json to object', function() {
- scope.$browser.cookies('objectCookie', '{"id":123,"name":"blah"}');
- scope.$browser.poll();
+ $browser.cookies('objectCookie', '{"id":123,"name":"blah"}');
+ $browser.poll();
expect(scope.$cookieStore.get('objectCookie')).toEqual({id: 123, name: 'blah'});
});
@@ -499,7 +502,7 @@ describe("service", function(){
it('should delete objects from the store when remove is called', function() {
scope.$cookieStore.put('gonner', { "I'll":"Be Back"});
scope.$eval(); //force eval in test
- expect(scope.$browser.cookies()).toEqual({'gonner': '{"I\'ll":"Be Back"}'});
+ expect($browser.cookies()).toEqual({'gonner': '{"I\'ll":"Be Back"}'});
});
});
diff --git a/test/widgetsSpec.js b/test/widgetsSpec.js
index 80acc928..8f6ccaea 100644
--- a/test/widgetsSpec.js
+++ b/test/widgetsSpec.js
@@ -476,7 +476,7 @@ describe("widget", function(){
scope.childScope = createScope();
scope.childScope.name = 'misko';
scope.url = 'myUrl';
- scope.$xhr.cache.data.myUrl = {value:'{{name}}'};
+ scope.$inject('$xhr.cache').data.myUrl = {value:'{{name}}'};
scope.$init();
expect(element.text()).toEqual('misko');
});
@@ -487,7 +487,7 @@ describe("widget", function(){
scope.childScope = createScope();
scope.childScope.name = 'igor';
scope.url = 'myUrl';
- scope.$xhr.cache.data.myUrl = {value:'{{name}}'};
+ scope.$inject('$xhr.cache').data.myUrl = {value:'{{name}}'};
scope.$init();
expect(element.text()).toEqual('igor');