├── .gitignore ├── README.mkd ├── Rakefile ├── public └── javascripts │ ├── backbone-factory.js │ ├── backbone-min.js │ ├── test-setup.js │ └── underscore-min.js └── spec └── javascripts ├── BackboneFactorySpec.js ├── helpers └── SpecHelper.js └── support ├── jasmine.yml ├── jasmine_config.rb └── jasmine_runner.rb /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle 2 | db/*.qlite3 3 | log/*.log 4 | tmp/**/* 5 | config/database.yml 6 | tmp/* 7 | doc/api 8 | doc/app 9 | *.swp 10 | *.swo 11 | *.un~ 12 | *˜ 13 | .DS_Store 14 | schema.rb 15 | .tags 16 | emails/* 17 | public/uploads/* 18 | -------------------------------------------------------------------------------- /README.mkd: -------------------------------------------------------------------------------- 1 | Backbone Factory 2 | ================ 3 | 4 | Introduction 5 | ------------ 6 | 7 | Backbone Factory is a small javascript library for creating [Backbone.js](http://documentcloud.github.com/backbone/) objects for testing your code. It has no external dependency. 8 | 9 | The API is heavily inspired by the awesome [Factory Girl](https://github.com/thoughtbot/factory_girl). 10 | 11 | 12 | Installation 13 | ------------ 14 | 15 | To use it, just [download](https://github.com/SupportBee/Backbone-Factory/raw/master/public/javascripts/backbone-factory.js) the file and include it in your testing setup. 16 | Usage 17 | ----- 18 | 19 | Lets say you have two Backbone models, Post and User 20 | 21 | ```javascript 22 | var User = Backbone.Model.extend({ 23 | 24 | name: null, 25 | email: null 26 | 27 | }); 28 | 29 | var Post = Backbone.Model.extend({ 30 | 31 | defaults: { 32 | title: 'Default Title' 33 | } 34 | 35 | }); 36 | ``` 37 | 38 | ### Defining Factories 39 | 40 | To define factories for them 41 | 42 | ```javascript 43 | var postFactory = BackboneFactory.define('post', Post); 44 | var userFactory = BackboneFactory.define('user', User); 45 | ``` 46 | 47 | ### Using Factories 48 | 49 | To use these factories, 50 | 51 | ```javscript 52 | this.postObject = BackboneFactory.create('post'); 53 | this.userObject = BackboneFactory.create('user'); 54 | ``` 55 | 56 | This will create objects using the [defaults](http://documentcloud.github.com/backbone/#Model-defaults) you have in your class definitions. 57 | 58 | 59 | ### Defining Sequences 60 | 61 | ```javascript 62 | var emailSequence = BackboneFactory.define_sequence('email', function(n){ 63 | return "person"+n+"@example.com"; 64 | }); 65 | ``` 66 | 67 | ### Using Sequences 68 | 69 | ```javascript 70 | var email = BackboneFactory.next('email') // person1@example.com 71 | ``` 72 | 73 | ### Defining Factories with defaults 74 | 75 | ```javascript 76 | var userFactory = BackboneFactory.define('user', User, function(){ 77 | return { 78 | name : 'Backbone User', 79 | email: BackboneFactory.next('person_email') 80 | }; 81 | } 82 | ); 83 | ``` 84 | 85 | ### Overriding defaults when creating objects 86 | 87 | ```javascript 88 | var userWithEmail = BackboneFactory.create('user', function(){ 89 | return { 90 | email: 'overriden@example.com' 91 | }; 92 | }); 93 | ``` 94 | 95 | Contributing 96 | ------------ 97 | 98 | 1. Open a ticket on GitHub, maybe someone else has the problem too 99 | 2. Make a fork of my GitHub repository 100 | 3. Make a spec driven change to the code base (we use [jasmine](http://pivotal.github.com/jasmine/)) 101 | 5. Make sure it works and all specs pass 102 | 6. Update the README if needed to reflect your change / addition 103 | 7. With all specs passing push your changes back to your fork 104 | 8. Send me a pull request 105 | 106 | 107 | License 108 | ------- 109 | 110 | (The MIT License) 111 | 112 | Copyright (c) 2009, 2010, 2011 113 | 114 | Permission is hereby granted, free of charge, to any person obtaining 115 | a copy of this software and associated documentation files (the 116 | 'Software'), to deal in the Software without restriction, including 117 | without limitation the rights to use, copy, modify, merge, publish, 118 | distribute, sublicense, and/or sell copies of the Software, and to 119 | permit persons to whom the Software is furnished to do so, subject to 120 | the following conditions: 121 | 122 | The above copyright notice and this permission notice shall be 123 | included in all copies or substantial portions of the Software. 124 | 125 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 126 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 127 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 128 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 129 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 130 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 131 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 132 | 133 | 134 | 135 | 136 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | 2 | begin 3 | require 'jasmine' 4 | load 'jasmine/tasks/jasmine.rake' 5 | rescue LoadError 6 | task :jasmine do 7 | abort "Jasmine is not available. In order to run jasmine, you must: (sudo) gem install jasmine" 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /public/javascripts/backbone-factory.js: -------------------------------------------------------------------------------- 1 | // Backbone Factory JS 2 | // https://github.com/SupportBee/Backbone-Factory 3 | 4 | (function(){ 5 | window.BackboneFactory = { 6 | 7 | factories: {}, 8 | sequences: {}, 9 | 10 | define: function(factory_name, klass, defaults){ 11 | 12 | // Check for arguments' sanity 13 | if(factory_name.match(/[^\w-_]+/)){ 14 | throw "Factory name should not contain spaces or other funky characters"; 15 | } 16 | 17 | if(defaults === undefined) defaults = function(){return {}}; 18 | 19 | // The object creator 20 | this.factories[factory_name] = function(options){ 21 | if(options === undefined) options = function(){return {}}; 22 | arguments = _.extend({}, {id: BackboneFactory.next("_" + factory_name + "_id")}, defaults.call(), options.call()); 23 | return new klass(arguments); 24 | }; 25 | 26 | // Lets define a sequence for id 27 | BackboneFactory.define_sequence("_"+ factory_name +"_id", function(n){ 28 | return n 29 | }); 30 | }, 31 | 32 | create: function(factory_name, options){ 33 | if(this.factories[factory_name] === undefined){ 34 | throw "Factory with name " + factory_name + " does not exist"; 35 | } 36 | return this.factories[factory_name].apply(null, [options]); 37 | }, 38 | 39 | define_sequence: function(sequence_name, callback){ 40 | this.sequences[sequence_name] = {} 41 | this.sequences[sequence_name]['counter'] = 0; 42 | this.sequences[sequence_name]['callback'] = callback; 43 | }, 44 | 45 | next: function(sequence_name){ 46 | if(this.sequences[sequence_name] === undefined){ 47 | throw "Sequence with name " + sequence_name + " does not exist"; 48 | } 49 | this.sequences[sequence_name]['counter'] += 1; 50 | return this.sequences[sequence_name]['callback'].apply(null, [this.sequences[sequence_name]['counter']]); //= callback; 51 | } 52 | } 53 | })(); 54 | -------------------------------------------------------------------------------- /public/javascripts/backbone-min.js: -------------------------------------------------------------------------------- 1 | // Backbone.js 0.3.3 2 | // (c) 2010 Jeremy Ashkenas, DocumentCloud Inc. 3 | // Backbone may be freely distributed under the MIT license. 4 | // For all details and documentation: 5 | // http://documentcloud.github.com/backbone 6 | (function(){var e;e=typeof exports!=="undefined"?exports:this.Backbone={};e.VERSION="0.3.3";var f=this._;if(!f&&typeof require!=="undefined")f=require("underscore")._;var h=this.jQuery||this.Zepto;e.emulateHTTP=false;e.emulateJSON=false;e.Events={bind:function(a,b){this._callbacks||(this._callbacks={});(this._callbacks[a]||(this._callbacks[a]=[])).push(b);return this},unbind:function(a,b){var c;if(a){if(c=this._callbacks)if(b){c=c[a];if(!c)return this;for(var d=0,g=c.length;d/g,">").replace(/"/g, 9 | """)},set:function(a,b){b||(b={});if(!a)return this;if(a.attributes)a=a.attributes;var c=this.attributes,d=this._escapedAttributes;if(!b.silent&&this.validate&&!this._performValidation(a,b))return false;if("id"in a)this.id=a.id;for(var g in a){var i=a[g];if(!f.isEqual(c[g],i)){c[g]=i;delete d[g];if(!b.silent){this._changed=true;this.trigger("change:"+g,this,i,b)}}}!b.silent&&this._changed&&this.change(b);return this},unset:function(a,b){b||(b={});var c={};c[a]=void 0;if(!b.silent&&this.validate&& 10 | !this._performValidation(c,b))return false;delete this.attributes[a];delete this._escapedAttributes[a];if(!b.silent){this._changed=true;this.trigger("change:"+a,this,void 0,b);this.change(b)}return this},clear:function(a){a||(a={});var b=this.attributes,c={};for(attr in b)c[attr]=void 0;if(!a.silent&&this.validate&&!this._performValidation(c,a))return false;this.attributes={};this._escapedAttributes={};if(!a.silent){this._changed=true;for(attr in b)this.trigger("change:"+attr,this,void 0,a);this.change(a)}return this}, 11 | fetch:function(a){a||(a={});var b=this,c=j(a.error,b,a);(this.sync||e.sync)("read",this,function(d){if(!b.set(b.parse(d),a))return false;a.success&&a.success(b,d)},c);return this},save:function(a,b){b||(b={});if(a&&!this.set(a,b))return false;var c=this,d=j(b.error,c,b),g=this.isNew()?"create":"update";(this.sync||e.sync)(g,this,function(i){if(!c.set(c.parse(i),b))return false;b.success&&b.success(c,i)},d);return this},destroy:function(a){a||(a={});var b=this,c=j(a.error,b,a);(this.sync||e.sync)("delete", 12 | this,function(d){b.collection&&b.collection.remove(b);a.success&&a.success(b,d)},c);return this},url:function(){var a=k(this.collection);if(this.isNew())return a;return a+(a.charAt(a.length-1)=="/"?"":"/")+this.id},parse:function(a){return a},clone:function(){return new this.constructor(this)},isNew:function(){return!this.id},change:function(a){this.trigger("change",this,a);this._previousAttributes=f.clone(this.attributes);this._changed=false},hasChanged:function(a){if(a)return this._previousAttributes[a]!= 13 | this.attributes[a];return this._changed},changedAttributes:function(a){a||(a=this.attributes);var b=this._previousAttributes,c=false,d;for(d in a)if(!f.isEqual(b[d],a[d])){c=c||{};c[d]=a[d]}return c},previous:function(a){if(!a||!this._previousAttributes)return null;return this._previousAttributes[a]},previousAttributes:function(){return f.clone(this._previousAttributes)},_performValidation:function(a,b){var c=this.validate(a);if(c){b.error?b.error(this,c):this.trigger("error",this,c,b);return false}return true}}); 14 | e.Collection=function(a,b){b||(b={});if(b.comparator){this.comparator=b.comparator;delete b.comparator}this._boundOnModelEvent=f.bind(this._onModelEvent,this);this._reset();a&&this.refresh(a,{silent:true});this.initialize(a,b)};f.extend(e.Collection.prototype,e.Events,{model:e.Model,initialize:function(){},toJSON:function(){return this.map(function(a){return a.toJSON()})},add:function(a,b){if(f.isArray(a))for(var c=0,d=a.length;c').hide().appendTo("body")[0].contentWindow; 22 | "onhashchange"in window&&!a?h(window).bind("hashchange",this.checkUrl):setInterval(this.checkUrl,this.interval);return this.loadUrl()},route:function(a,b){this.handlers.push({route:a,callback:b})},checkUrl:function(){var a=this.getFragment();if(a==this.fragment&&this.iframe)a=this.getFragment(this.iframe.location);if(a==this.fragment||a==decodeURIComponent(this.fragment))return false;if(this.iframe)window.location.hash=this.iframe.location.hash=a;this.loadUrl()},loadUrl:function(){var a=this.fragment= 23 | this.getFragment();return f.any(this.handlers,function(b){if(b.route.test(a)){b.callback(a);return true}})},saveLocation:function(a){a=(a||"").replace(l,"");if(this.fragment!=a){window.location.hash=this.fragment=a;if(this.iframe&&a!=this.getFragment(this.iframe.location)){this.iframe.document.open().close();this.iframe.location.hash=a}}}});e.View=function(a){this._configure(a||{});this._ensureElement();this.delegateEvents();this.initialize(a)};var q=/^(\w+)\s*(.*)$/;f.extend(e.View.prototype,e.Events, 24 | {tagName:"div",$:function(a){return h(a,this.el)},initialize:function(){},render:function(){return this},remove:function(){h(this.el).remove();return this},make:function(a,b,c){a=document.createElement(a);b&&h(a).attr(b);c&&h(a).html(c);return a},delegateEvents:function(a){if(a||(a=this.events)){h(this.el).unbind();for(var b in a){var c=a[b],d=b.match(q),g=d[1];d=d[2];c=f.bind(this[c],this);d===""?h(this.el).bind(g,c):h(this.el).delegate(d,g,c)}}},_configure:function(a){if(this.options)a=f.extend({}, 25 | this.options,a);if(a.model)this.model=a.model;if(a.collection)this.collection=a.collection;if(a.el)this.el=a.el;if(a.id)this.id=a.id;if(a.className)this.className=a.className;if(a.tagName)this.tagName=a.tagName;this.options=a},_ensureElement:function(){if(!this.el){var a={};if(this.id)a.id=this.id;if(this.className)a["class"]=this.className;this.el=this.make(this.tagName,a)}}});var m=function(a,b){var c=r(this,a,b);c.extend=m;return c};e.Model.extend=e.Collection.extend=e.Controller.extend=e.View.extend= 26 | m;var s={create:"POST",update:"PUT","delete":"DELETE",read:"GET"};e.sync=function(a,b,c,d){var g=s[a];a=a==="create"||a==="update"?JSON.stringify(b.toJSON()):null;b={url:k(b),type:g,contentType:"application/json",data:a,dataType:"json",processData:false,success:c,error:d};if(e.emulateJSON){b.contentType="application/x-www-form-urlencoded";b.processData=true;b.data=a?{model:a}:{}}if(e.emulateHTTP)if(g==="PUT"||g==="DELETE"){if(e.emulateJSON)b.data._method=g;b.type="POST";b.beforeSend=function(i){i.setRequestHeader("X-HTTP-Method-Override", 27 | g)}}h.ajax(b)};var n=function(){},r=function(a,b,c){var d;d=b&&b.hasOwnProperty("constructor")?b.constructor:function(){return a.apply(this,arguments)};n.prototype=a.prototype;d.prototype=new n;b&&f.extend(d.prototype,b);c&&f.extend(d,c);d.prototype.constructor=d;d.__super__=a.prototype;return d},k=function(a){if(!(a&&a.url))throw Error("A 'url' property or function must be specified");return f.isFunction(a.url)?a.url():a.url},j=function(a,b,c){return function(d){a?a(b,d):b.trigger("error",b,d,c)}}})(); 28 | -------------------------------------------------------------------------------- /public/javascripts/test-setup.js: -------------------------------------------------------------------------------- 1 | //Models for testing out Backbone Factory 2 | 3 | var User = Backbone.Model.extend({ 4 | 5 | name: null, 6 | email: null 7 | 8 | }); 9 | 10 | var Post = Backbone.Model.extend({ 11 | 12 | defaults: { 13 | title: 'Default Title' 14 | } 15 | 16 | }); 17 | -------------------------------------------------------------------------------- /public/javascripts/underscore-min.js: -------------------------------------------------------------------------------- 1 | // Underscore.js 1.1.6 2 | // (c) 2011 Jeremy Ashkenas, DocumentCloud Inc. 3 | // Underscore is freely distributable under the MIT license. 4 | // Portions of Underscore are inspired or borrowed from Prototype, 5 | // Oliver Steele's Functional, and John Resig's Micro-Templating. 6 | // For all details and documentation: 7 | // http://documentcloud.github.com/underscore 8 | (function(){var p=this,C=p._,m={},i=Array.prototype,n=Object.prototype,f=i.slice,D=i.unshift,E=n.toString,l=n.hasOwnProperty,s=i.forEach,t=i.map,u=i.reduce,v=i.reduceRight,w=i.filter,x=i.every,y=i.some,o=i.indexOf,z=i.lastIndexOf;n=Array.isArray;var F=Object.keys,q=Function.prototype.bind,b=function(a){return new j(a)};typeof module!=="undefined"&&module.exports?(module.exports=b,b._=b):p._=b;b.VERSION="1.1.6";var h=b.each=b.forEach=function(a,c,d){if(a!=null)if(s&&a.forEach===s)a.forEach(c,d);else if(b.isNumber(a.length))for(var e= 9 | 0,k=a.length;e=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a, 13 | c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);var e={computed:Infinity};h(a,function(a,b,f){b=c?c.call(d,a,b,f):a;bd?1:0}),"value")};b.sortedIndex=function(a,c,d){d||(d=b.identity);for(var e=0,f=a.length;e>1;d(a[g])=0})})};b.zip=function(){for(var a=f.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c), 16 | e=0;e=0;d--)b=[a[d].apply(this,b)];return b[0]}};b.after=function(a,b){return function(){if(--a<1)return b.apply(this,arguments)}};b.keys=F||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var b=[],d;for(d in a)l.call(a,d)&&(b[b.length]=d);return b};b.values=function(a){return b.map(a, 20 | b.identity)};b.functions=b.methods=function(a){return b.filter(b.keys(a),function(c){return b.isFunction(a[c])}).sort()};b.extend=function(a){h(f.call(arguments,1),function(b){for(var d in b)b[d]!==void 0&&(a[d]=b[d])});return a};b.defaults=function(a){h(f.call(arguments,1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,c){if(a===c)return!0;var d=typeof a;if(d!= 21 | typeof c)return!1;if(a==c)return!0;if(!a&&c||a&&!c)return!1;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(a.isEqual)return a.isEqual(c);if(b.isDate(a)&&b.isDate(c))return a.getTime()===c.getTime();if(b.isNaN(a)&&b.isNaN(c))return!1;if(b.isRegExp(a)&&b.isRegExp(c))return a.source===c.source&&a.global===c.global&&a.ignoreCase===c.ignoreCase&&a.multiline===c.multiline;if(d!=="object")return!1;if(a.length&&a.length!==c.length)return!1;d=b.keys(a);var e=b.keys(c);if(d.length!=e.length)return!1; 22 | for(var f in a)if(!(f in c)||!b.isEqual(a[f],c[f]))return!1;return!0};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(l.call(a,c))return!1;return!0};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=n||function(a){return E.call(a)==="[object Array]"};b.isArguments=function(a){return!(!a||!l.call(a,"callee"))};b.isFunction=function(a){return!(!a||!a.constructor||!a.call||!a.apply)};b.isString=function(a){return!!(a===""||a&&a.charCodeAt&&a.substr)}; 23 | b.isNumber=function(a){return!!(a===0||a&&a.toExponential&&a.toFixed)};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===!0||a===!1};b.isDate=function(a){return!(!a||!a.getTimezoneOffset||!a.setUTCFullYear)};b.isRegExp=function(a){return!(!a||!a.test||!a.exec||!(a.ignoreCase||a.ignoreCase===!1))};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.noConflict=function(){p._=C;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e= 24 | 0;e/g,interpolate:/<%=([\s\S]+?)%>/g};b.template=function(a,c){var d=b.templateSettings;d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.interpolate,function(a,b){return"',"+b.replace(/\\'/g,"'")+",'"}).replace(d.evaluate|| 25 | null,function(a,b){return"');"+b.replace(/\\'/g,"'").replace(/[\r\n\t]/g," ")+"__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');";d=new Function("obj",d);return c?d(c):d};var j=function(a){this._wrapped=a};b.prototype=j.prototype;var r=function(a,c){return c?b(a).chain():a},H=function(a,c){j.prototype[a]=function(){var a=f.call(arguments);D.call(a,this._wrapped);return r(c.apply(b,a),this._chain)}};b.mixin(b);h(["pop","push","reverse","shift","sort", 26 | "splice","unshift"],function(a){var b=i[a];j.prototype[a]=function(){b.apply(this._wrapped,arguments);return r(this._wrapped,this._chain)}});h(["concat","join","slice"],function(a){var b=i[a];j.prototype[a]=function(){return r(b.apply(this._wrapped,arguments),this._chain)}});j.prototype.chain=function(){this._chain=!0;return this};j.prototype.value=function(){return this._wrapped}})(); 27 | -------------------------------------------------------------------------------- /spec/javascripts/BackboneFactorySpec.js: -------------------------------------------------------------------------------- 1 | describe("Backbone Factory", function() { 2 | 3 | describe("Defining and using Sequences", function(){ 4 | 5 | beforeEach(function() { 6 | var emailSequence = BackboneFactory.define_sequence('email', function(n){ 7 | return "person"+n+"@example.com"; 8 | }); 9 | }); 10 | 11 | it("should increment the sequence on successive calls", function(){ 12 | expect(BackboneFactory.next('email')).toBe('person1@example.com'); 13 | expect(BackboneFactory.next('email')).toBe('person2@example.com'); 14 | }); 15 | 16 | }); 17 | 18 | describe("Defining and using Factories", function(){ 19 | 20 | beforeEach(function() { 21 | var emailSequence = BackboneFactory.define_sequence('person_email', function(n){ 22 | return "person"+n+"@example.com"; 23 | }); 24 | var postFactory = BackboneFactory.define('post', Post, function(){ 25 | return { 26 | author: BackboneFactory.create('user') 27 | }; 28 | } 29 | ); 30 | var userFactory = BackboneFactory.define('user', User, function(){ 31 | return { 32 | name : 'Backbone User', 33 | email: BackboneFactory.next('person_email') 34 | }; 35 | } 36 | ); 37 | 38 | this.postObject = BackboneFactory.create('post'); 39 | this.userObject = BackboneFactory.create('user'); 40 | }); 41 | 42 | 43 | it("return an instance of the Backbone Object requested", function() { 44 | expect(this.postObject instanceof Post).toBeTruthy(); 45 | expect(this.userObject instanceof User).toBeTruthy(); 46 | }); 47 | 48 | // Not sure if this test is needed. But what the hell! 49 | it("should preserve the defaults if not overriden", function() { 50 | expect(this.postObject.get('title')).toBe('Default Title'); 51 | }); 52 | 53 | 54 | 55 | it("should use the defaults supplied when creating objects", function() { 56 | expect(this.userObject.get('name')).toBe('Backbone User'); 57 | }); 58 | 59 | it("should work with sequences", function(){ 60 | expect(this.userObject.get('email')).toBe('person2@example.com'); 61 | var anotherUser = BackboneFactory.create('user'); 62 | expect(anotherUser.get('email')).toBe('person3@example.com'); 63 | }); 64 | 65 | it("should work if other factories are passed", function(){ 66 | expect(this.postObject.get('author') instanceof User).toBeTruthy(); 67 | }) 68 | 69 | it("should override defaults if arguments are passed on creation", function(){ 70 | var userWithEmail = BackboneFactory.create('user', function(){ 71 | return { 72 | email: 'overriden@example.com' 73 | }; 74 | }); 75 | expect(userWithEmail.get('email')).toBe('overriden@example.com'); 76 | }); 77 | 78 | it("should have an id", function() { 79 | expect(this.userObject.id).toBeDefined(); 80 | }); 81 | 82 | it("should have an id that increments on creation", function(){ 83 | var firstID = BackboneFactory.create('user').id; 84 | var secondID = BackboneFactory.create('user').id; 85 | expect(secondID).toBe(firstID + 1); 86 | }); 87 | 88 | describe("Error Messages", function() { 89 | 90 | it("should throw an error if factory_name is not proper", function() { 91 | expect(function(){BackboneFactory.define('wrong name', Post)}).toThrow("Factory name should not contain spaces or other funky characters"); 92 | }); 93 | 94 | it("should not throw an error if factory_name has a hyphen", function() { 95 | expect(function(){BackboneFactory.define('okay-name', Post)}).not.toThrow(); 96 | }); 97 | 98 | it("should throw an error if you try to use an undefined factory", function() { 99 | expect(function(){BackboneFactory.create('undefined_factory')}).toThrow("Factory with name undefined_factory does not exist"); 100 | }); 101 | 102 | it("should throw an error if you try to use an undefined sequence", function() { 103 | expect(function(){BackboneFactory.next('undefined_sequence')}).toThrow("Sequence with name undefined_sequence does not exist"); 104 | }); 105 | 106 | }); 107 | 108 | }); 109 | 110 | }); 111 | 112 | -------------------------------------------------------------------------------- /spec/javascripts/helpers/SpecHelper.js: -------------------------------------------------------------------------------- 1 | beforeEach(function() { 2 | this.addMatchers({ 3 | toBePlaying: function(expectedSong) { 4 | var player = this.actual; 5 | return player.currentlyPlayingSong === expectedSong 6 | && player.isPlaying; 7 | } 8 | }) 9 | }); 10 | -------------------------------------------------------------------------------- /spec/javascripts/support/jasmine.yml: -------------------------------------------------------------------------------- 1 | # src_files 2 | # 3 | # Return an array of filepaths relative to src_dir to include before jasmine specs. 4 | # Default: [] 5 | # 6 | # EXAMPLE: 7 | # 8 | # src_files: 9 | # - lib/source1.js 10 | # - lib/source2.js 11 | # - dist/**/*.js 12 | # 13 | src_files: 14 | - public/javascripts/underscore-min.js 15 | - public/javascripts/backbone-min.js 16 | - public/javascripts/test-setup.js 17 | - public/javascripts/backbone-factory.js 18 | #- public/javascripts/**/*.js 19 | 20 | # stylesheets 21 | # 22 | # Return an array of stylesheet filepaths relative to src_dir to include before jasmine specs. 23 | # Default: [] 24 | # 25 | # EXAMPLE: 26 | # 27 | # stylesheets: 28 | # - css/style.css 29 | # - stylesheets/*.css 30 | # 31 | stylesheets: 32 | 33 | # helpers 34 | # 35 | # Return an array of filepaths relative to spec_dir to include before jasmine specs. 36 | # Default: ["helpers/**/*.js"] 37 | # 38 | # EXAMPLE: 39 | # 40 | # helpers: 41 | # - helpers/**/*.js 42 | # 43 | helpers: 44 | 45 | # spec_files 46 | # 47 | # Return an array of filepaths relative to spec_dir to include. 48 | # Default: ["**/*[sS]pec.js"] 49 | # 50 | # EXAMPLE: 51 | # 52 | # spec_files: 53 | # - **/*[sS]pec.js 54 | # 55 | spec_files: 56 | - **/*[sS]pec.js 57 | 58 | # src_dir 59 | # 60 | # Source directory path. Your src_files must be returned relative to this path. Will use root if left blank. 61 | # Default: project root 62 | # 63 | # EXAMPLE: 64 | # 65 | # src_dir: public 66 | # 67 | src_dir: 68 | 69 | # spec_dir 70 | # 71 | # Spec directory path. Your spec_files must be returned relative to this path. 72 | # Default: spec/javascripts 73 | # 74 | # EXAMPLE: 75 | # 76 | # spec_dir: spec/javascripts 77 | # 78 | spec_dir: 79 | -------------------------------------------------------------------------------- /spec/javascripts/support/jasmine_config.rb: -------------------------------------------------------------------------------- 1 | module Jasmine 2 | class Config 3 | 4 | # Add your overrides or custom config code here 5 | 6 | end 7 | end 8 | 9 | 10 | # Note - this is necessary for rspec2, which has removed the backtrace 11 | module Jasmine 12 | class SpecBuilder 13 | def declare_spec(parent, spec) 14 | me = self 15 | example_name = spec["name"] 16 | @spec_ids << spec["id"] 17 | backtrace = @example_locations[parent.description + " " + example_name] 18 | parent.it example_name, {} do 19 | me.report_spec(spec["id"]) 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/javascripts/support/jasmine_runner.rb: -------------------------------------------------------------------------------- 1 | $:.unshift(ENV['JASMINE_GEM_PATH']) if ENV['JASMINE_GEM_PATH'] # for gem testing purposes 2 | 3 | require 'rubygems' 4 | require 'jasmine' 5 | jasmine_config_overrides = File.expand_path(File.join(File.dirname(__FILE__), 'jasmine_config.rb')) 6 | require jasmine_config_overrides if File.exist?(jasmine_config_overrides) 7 | if Jasmine::rspec2? 8 | require 'rspec' 9 | else 10 | require 'spec' 11 | end 12 | 13 | jasmine_config = Jasmine::Config.new 14 | spec_builder = Jasmine::SpecBuilder.new(jasmine_config) 15 | 16 | should_stop = false 17 | 18 | if Jasmine::rspec2? 19 | RSpec.configuration.after(:suite) do 20 | spec_builder.stop if should_stop 21 | end 22 | else 23 | Spec::Runner.configure do |config| 24 | config.after(:suite) do 25 | spec_builder.stop if should_stop 26 | end 27 | end 28 | end 29 | 30 | spec_builder.start 31 | should_stop = true 32 | spec_builder.declare_suites --------------------------------------------------------------------------------