Skip to content
This repository
Browse code

Merge branch 'master' into local-helpers

Conflicts:
	test/src/thorax.js
  • Loading branch information...
commit 2e0065e61ff70cb9ec498154ed5cd7cba5f045ea 2 parents 4726b7e + 34ca9a9
Ryan Eastridge authored

Showing 36 changed files with 429 additions and 13,686 deletions. Show diff stats Hide diff stats

  1. +9 0 Jakefile
  2. +0 16 bin/test-server.js
  3. +7 16 build.json
  4. +2 0  lumbar.json
  5. +5 0 mock-server.json
  6. +4 11 package.json
  7. +16 15 src/collection.js
  8. +85 74 src/data-object.js
  9. +1 5 src/event.js
  10. +9 10 src/form.js
  11. +6 9 src/loading.js
  12. +15 17 src/model.js
  13. +4 3 src/thorax.js
  14. +1 16 src/util.js
  15. +0 3,655 test/lib/chai.js
  16. +0 7 test/lib/mocha-override.css
  17. +0 227 test/lib/mocha.css
  18. +0 4,994 test/lib/mocha.js
  19. +0 35 test/lib/runner.js
  20. +0 18 test/lib/sinon-backbone.js
  21. +0 121 test/lib/sinon-chai.js
  22. +0 41 test/lib/sinon-qunit.js
  23. +0 4,157 test/lib/sinon.js
  24. +3 2 test/src/{test.collection.js → collection.js}
  25. +3 3 test/src/{test.event.js → event.js}
  26. +3 1 test/src/{test.form.js → form.js}
  27. +5 35 test/src/{test.helpers.js → helpers/button-link.js}
  28. +6 0 test/src/helpers/collection.js
  29. +33 0 test/src/helpers/element.js
  30. +37 0 test/src/helpers/super.js
  31. +13 0 test/src/helpers/url.js
  32. +154 0 test/src/helpers/view.js
  33. 0  test/src/{test.layout.js → layout.js}
  34. +7 7 test/src/{test.loading.js → loading.js}
  35. 0  test/src/{test.model.js → model.js}
  36. +1 191 test/src/{test.js → thorax.js}
9 Jakefile
... ... @@ -0,0 +1,9 @@
  1 +var build = require('phoenix-build');
  2 +
  3 +build.lumbarFile = 'build.json';
  4 +build.projectDir = __dirname;
  5 +build.mochaTests = true;
  6 +build.testPlatforms = [
  7 + {platform: 'jquery'},
  8 + {platform: 'zepto'}
  9 +];
16 bin/test-server.js
... ... @@ -1,16 +0,0 @@
1   -// Shamelessly pulled from Modernizr
2   -// https://github.com/Modernizr/Modernizr/blob/master/test/js/server.js
3   -var express = require('express'),
4   - path = require('path'),
5   - fs = require('fs'),
6   - args = process.argv.slice(2),
7   - root = path.join(__dirname, '..'),
8   - folder = path.join(root, 'build'),
9   - port = args[1] || '80';
10   -
11   -var server = express.createServer();
12   -server.use(express.static(folder));
13   -
14   -server.listen(port);
15   -
16   -console.log("Server started on port %s in %s", port, folder);
23 build.json
@@ -40,33 +40,24 @@
40 40 },
41 41
42 42 "test": {
  43 + "mixins": [
  44 + "test",
  45 + "loaded-test-runner"
  46 + ],
43 47 "scripts": [
44   - {"src": "test/lib/mocha.js", "global": true},
45   - {"src": "test/lib/chai.js", "global": true},
46   - {"src": "test/lib/sinon-chai.js", "global": true},
47   - {"src": "test/lib/sinon.js", "global": true},
48   - {"src": "test/lib/sinon-backbone.js", "global": true},
49   - "test/lib/runner.js",
50   -
51 48 "test/src/"
52 49 ],
53   - "styles": [
54   - "test/lib/mocha.css"
55   - ],
56 50 "static": [
57   - {"src": "test/jquery.html", "dest": "jquery.html"},
58   - {"src": "test/zepto.html", "dest": "zepto.html"}
  51 + {"src": "test/jquery.html", "dest": "jquery/test.html"},
  52 + {"src": "test/zepto.html", "dest": "zepto/test.html"}
59 53 ]
60 54 }
61 55 },
62 56 "mixins": [
  57 + "node_modules/phoenix-build/mixin",
63 58 "."
64 59 ],
65 60 "scope": {
66 61 "template": "src/fragments/scope.handlebars"
67   - },
68   -
69   - "test": {
70   - "auto-include": "test/src/test."
71 62 }
72 63 }
2  lumbar.json
... ... @@ -1,4 +1,6 @@
1 1 {
  2 + "name": "thorax",
  3 +
2 4 "mixins": {
3 5 "thorax-dep-jquery": {
4 6 "scripts": [
5 mock-server.json
... ... @@ -0,0 +1,5 @@
  1 +{
  2 + "build-targets": [
  3 + {"name": "Test", "path": "build/dev"}
  4 + ]
  5 +}
15 package.json
@@ -15,21 +15,14 @@
15 15 "node": ">=0.4.7"
16 16 },
17 17 "dependencies": {
18   - "fs-watch-tree": "0.2.0",
19   - "handlebars": "1.0.x"
  18 + "fs-watch-tree": "0.2.0"
20 19 },
21 20 "devDependencies": {
22   - "express": "2.5.11",
23   - "lumbar": "~2",
24   - "mocha-phantomjs": "~1.1"
  21 + "jake": "~0.5",
  22 + "phoenix-build": "~1"
25 23 },
26 24 "scripts": {
27   - "watch": "./node_modules/.bin/lumbar watch ./build.json ./build",
28   -
29   - "start": "node bin/test-server.js . 8083",
30   -
31   - "pretest": "./node_modules/.bin/lumbar build ./build.json ./build",
32   - "test": "./node_modules/.bin/mocha-phantomjs 'http://localhost:8083/zepto.html' && ./node_modules/.bin/mocha-phantomjs 'http://localhost:8083/jquery.html'"
  25 + "test": "./node_modules/.bin/jake test"
33 26 },
34 27 "bin": {
35 28 "thorax": "bin/thorax"
31 src/collection.js
@@ -38,15 +38,15 @@ Thorax.Collections = {};
38 38 createRegistryWrapper(Thorax.Collection, Thorax.Collections);
39 39
40 40 dataObject('collection', {
41   - name: '_collectionEvents',
42   - array: '_collections',
43   - hash: '_collectionOptionsByCid',
44 41 set: 'setCollection',
45 42 setCallback: afterSetCollection,
46   - bind: 'bindCollection',
47   - unbind: 'unbindCollection',
48   - options: '_setCollectionOptions',
49   - change: '_onCollectionReset',
  43 + defaultOptions: {
  44 + render: true,
  45 + fetch: true,
  46 + success: false,
  47 + errors: true
  48 + },
  49 + change: onCollectionReset,
50 50 $el: 'getCollectionElement',
51 51 cidAttrName: collectionCidAttributeName
52 52 });
@@ -188,15 +188,10 @@ _.extend(Thorax.View.prototype, {
188 188 var element = this.$(this._collectionSelector);
189 189 return element.length === 0 ? this.$el : element;
190 190 },
191   - _onCollectionReset: function(collection) {
192   - if (collection === this.collection && this._collectionOptionsByCid[this.collection.cid].render) {
193   - this.renderCollection();
194   - }
195   - },
196 191 // Events that will only be bound to "this.collection"
197 192 _collectionRenderingEvents: {
198   - reset: '_onCollectionReset',
199   - sort: '_onCollectionReset',
  193 + reset: onCollectionReset,
  194 + sort: onCollectionReset,
200 195 filter: function() {
201 196 applyVisibilityFilter.call(this);
202 197 },
@@ -233,13 +228,19 @@ _.extend(Thorax.View.prototype, {
233 228 Thorax.View.on({
234 229 collection: {
235 230 error: function(collection, message) {
236   - if (this._collectionOptionsByCid[collection.cid].errors) {
  231 + if (this._objectOptionsByCid[collection.cid].errors) {
237 232 this.trigger('error', message, collection);
238 233 }
239 234 }
240 235 }
241 236 });
242 237
  238 +function onCollectionReset(collection) {
  239 + if (collection === this.collection && this._objectOptionsByCid[this.collection.cid].render) {
  240 + this.renderCollection();
  241 + }
  242 +}
  243 +
243 244 function afterSetCollection(collection) {
244 245 if (!collectionHelperPresentForPrimaryCollection.call(this) && collection) {
245 246 _.each(this._collectionRenderingEvents, function(callback, eventName) {
159 src/data-object.js
... ... @@ -1,6 +1,10 @@
1 1 /*global getValue, inheritVars, walkInheritTree */
  2 +
2 3 function dataObject(type, spec) {
3   - spec = inheritVars[type] = _.defaults({event: true}, spec);
  4 + spec = inheritVars[type] = _.defaults({
  5 + name: '_' + type + 'Events',
  6 + event: true
  7 + }, spec);
4 8
5 9 // Add a callback in the view constructor
6 10 spec.ctor = function() {
@@ -13,110 +17,117 @@ function dataObject(type, spec) {
13 17 }
14 18 };
15 19
16   - function bindEvents(target, source) {
17   - var context = this;
18   - walkInheritTree(source, spec.name, true, function(event) {
19   - // getEventCallback will resolve if it is a string or a method
20   - // and return a method
21   - context.listenTo(target, event[0], _.bind(getEventCallback(event[1], context), event[2] || context));
22   - });
23   - }
  20 + function setObject(dataObject, options) {
  21 + var old = this[type],
  22 + $el = getValue(this, spec.$el);
  23 +
  24 + if (dataObject === old) {
  25 + return this;
  26 + }
  27 + if (old) {
  28 + this.unbindDataObject(old);
  29 + }
  30 +
  31 + if (dataObject) {
  32 + this[type] = dataObject;
  33 +
  34 + if (spec.loading) {
  35 + spec.loading.call(this);
  36 + }
24 37
25   - function loadObject(dataObject, options) {
26   - if (dataObject.load) {
27   - dataObject.load(function() {
28   - options && options.success && options.success(dataObject);
29   - }, options);
  38 + this.bindDataObject(dataObject, _.extend({}, this.options, options));
  39 + $el.attr(spec.cidAttrName, dataObject.cid);
  40 + dataObject.trigger('set', dataObject, old);
30 41 } else {
31   - dataObject.fetch(options);
  42 + this[type] = false;
  43 + if (spec.change) {
  44 + spec.change.call(this, false);
  45 + }
  46 + $el.removeAttr(spec.cidAttrName);
32 47 }
  48 + spec.setCallback && spec.setCallback.call(this, dataObject, options);
  49 + return this;
33 50 }
34 51
35   - function bindObject(dataObject, options) {
36   - if (this[spec.array].indexOf(dataObject) !== -1) {
  52 + Thorax.View.prototype[spec.set] = setObject;
  53 +}
  54 +
  55 +_.extend(Thorax.View.prototype, {
  56 + bindDataObject: function(dataObject, options) {
  57 + var type = getDataObjectType(dataObject);
  58 + if (this._boundDataObjects.indexOf(dataObject) !== -1) {
37 59 return false;
38 60 }
39 61 // Collections do not have a cid attribute by default
40 62 ensureDataObjectCid(type, dataObject);
41   - this[spec.array].push(dataObject);
  63 + this._boundDataObjects.push(dataObject);
42 64
43   - var options = this[spec.options](dataObject, options);
  65 + var options = this._modifyDataObjectOptions(dataObject, _.extend({}, inheritVars[type].defaultOptions, options));
  66 + this._objectOptionsByCid[dataObject.cid] = options;
44 67
45   - bindEvents.call(this, dataObject, this.constructor);
46   - bindEvents.call(this, dataObject, this);
  68 + bindEvents.call(this, type, dataObject, this.constructor);
  69 + bindEvents.call(this, type, dataObject, this);
47 70
48 71 if (Thorax.Util.shouldFetch(dataObject, options)) {
49 72 loadObject(dataObject, options);
50   - } else {
  73 + } else if (inheritVars[type].change) {
51 74 // want to trigger built in rendering without triggering event on model
52   - this[spec.change](dataObject, options);
  75 + inheritVars[type].change.call(this, dataObject, options);
53 76 }
54 77 return true;
55   - }
  78 + },
56 79
57   - function unbindObject(dataObject) {
58   - if (this[spec.array].indexOf(dataObject) === -1) {
  80 + unbindDataObject: function (dataObject) {
  81 + if (this._boundDataObjects.indexOf(dataObject) === -1) {
59 82 return false;
60 83 }
61   - this[spec.array] = _.without(this[spec.array], dataObject);
  84 + this._boundDataObjects = _.without(this._boundDataObjects, dataObject);
62 85 dataObject.trigger('freeze');
63 86 this.stopListening(dataObject);
64   - delete this[spec.hash][dataObject.cid];
  87 + delete this._objectOptionsByCid[dataObject.cid];
65 88 return true;
66   - }
  89 + },
67 90
68   - function objectOptions(dataObject, options) {
69   - if (!this[spec.hash][dataObject.cid]) {
70   - this[spec.hash][dataObject.cid] = {
71   - render: true,
72   - fetch: true,
73   - success: false,
74   - errors: true
75   - };
76   - }
77   - _.extend(this[spec.hash][dataObject.cid], options || {});
78   - return this[spec.hash][dataObject.cid];
  91 + _modifyDataObjectOptions: function(dataObject, options) {
  92 + return options;
79 93 }
  94 +});
80 95
81   - function setObject(dataObject, options) {
82   - var old = this[type],
83   - $el = getValue(this, spec.$el);
  96 +function getDataObjectType(dataObject) {
  97 + if (isModel(dataObject)) {
  98 + return 'model';
  99 + } else if (isCollection(dataObject)) {
  100 + return 'collection';
  101 + } else {
  102 + throw new Error('Unknown data object bound: ' + (typeof dataObject));
  103 + }
  104 +}
84 105
85   - if (dataObject === old) {
86   - return this;
87   - }
88   - if (old) {
89   - this[spec.unbind](old);
90   - }
  106 +function isModel(model) {
  107 + return model && model.attributes && model.set;
  108 +}
91 109
92   - if (dataObject) {
93   - this[type] = dataObject;
  110 +function isCollection(collection) {
  111 + return collection && collection.indexOf && collection.models;
  112 +}
94 113
95   - if (spec.loading) {
96   - spec.loading.call(this);
97   - }
  114 +function bindEvents(type, target, source) {
  115 + var context = this;
  116 + walkInheritTree(source, '_' + type + 'Events', true, function(event) {
  117 + // getEventCallback will resolve if it is a string or a method
  118 + // and return a method
  119 + context.listenTo(target, event[0], _.bind(getEventCallback(event[1], context), event[2] || context));
  120 + });
  121 +}
98 122
99   - this[spec.bind](dataObject, _.extend({}, this.options, options));
100   - $el.attr(spec.cidAttrName, dataObject.cid);
101   - dataObject.trigger('set', dataObject, old);
102   - } else {
103   - this[type] = false;
104   - if (spec.change) {
105   - this[spec.change](false);
106   - }
107   - $el.removeAttr(spec.cidAttrName);
108   - }
109   - spec.setCallback && spec.setCallback.call(this, dataObject, options);
110   - return this;
  123 +function loadObject(dataObject, options) {
  124 + if (dataObject.load) {
  125 + dataObject.load(function() {
  126 + options && options.success && options.success(dataObject);
  127 + }, options);
  128 + } else {
  129 + dataObject.fetch(options);
111 130 }
112   -
113   - var extend = {};
114   - extend[spec.bind] = bindObject;
115   - extend[spec.unbind] = unbindObject;
116   - extend[spec.set] = setObject;
117   - extend[spec.options] = objectOptions;
118   -
119   - _.extend(Thorax.View.prototype, extend);
120 131 }
121 132
122 133 function getEventCallback(callback, context) {
6 src/event.js
@@ -46,11 +46,7 @@ _.extend(Thorax.View, {
46 46
47 47 _.extend(Thorax.View.prototype, {
48 48 freeze: function(options) {
49   - _.each(inheritVars, function(obj) {
50   - if (obj.unbind) {
51   - _.each(this[obj.array], this[obj.unbind], this);
52   - }
53   - }, this);
  49 + _.each(this._boundDataObjects, this.unbindDataObject, this);
54 50 options = _.defaults(options || {}, {
55 51 dom: true,
56 52 children: true
19 src/form.js
... ... @@ -1,18 +1,17 @@
1   -/*global extendOptions, extendViewMember */
  1 +/*global inheritVars */
2 2
3   -extendOptions('_setModelOptions', function() {
4   - return {
5   - populate: true
6   - };
7   -});
  3 +inheritVars.model.defaultOptions.populate = true;
8 4
9   -extendViewMember('_onModelChange', function(model) {
  5 +var oldModelChange = inheritVars.model.change;
  6 +inheritVars.model.change = function() {
  7 + oldModelChange.apply(this, arguments);
10 8 // TODO : What can we do to remove this duplication?
11   - var modelOptions = model && this._modelOptionsByCid[model.cid];
  9 + var modelOptions = this.model && this._objectOptionsByCid[this.model.cid];
12 10 if (modelOptions && modelOptions.populate) {
13   - this.populate(model.attributes, modelOptions.populate === true ? {} : modelOptions.populate);
  11 + this.populate(this.model.attributes, modelOptions.populate === true ? {} : modelOptions.populate);
14 12 }
15   -});
  13 +};
  14 +inheritVars.model.defaultOptions.populate = true;
16 15
17 16 _.extend(Thorax.View.prototype, {
18 17 //serializes a form present in the view, returning the serialized data
15 src/loading.js
... ... @@ -1,4 +1,4 @@
1   -/*global collectionOptionNames, extendOptions, inheritVars */
  1 +/*global collectionOptionNames, inheritVars */
2 2 var loadStart = 'load:start',
3 3 loadEnd = 'load:end',
4 4 rootObject;
@@ -333,14 +333,11 @@ if (Thorax.Router) {
333 333 }
334 334
335 335 // Propagates loading view parameters to the AJAX layer
336   -function loadingDataOptions() {
337   - return {
338   - ignoreErrors: this.ignoreFetchError,
339   - background: this.nonBlockingLoad
340   - };
341   -}
342   -extendOptions('_setModelOptions', loadingDataOptions);
343   -extendOptions('_setCollectionOptions', loadingDataOptions);
  336 +Thorax.View.prototype._modifyDataObjectOptions = function(dataObject, options) {
  337 + options.ignoreErrors = this.ignoreFetchError;
  338 + options.background = this.nonBlockingLoad;
  339 + return options;
  340 +};
344 341
345 342 inheritVars.collection.loading = function() {
346 343 var loadingView = this.loadingView,
32 src/model.js
@@ -24,37 +24,35 @@ Thorax.Models = {};
24 24 createRegistryWrapper(Thorax.Model, Thorax.Models);
25 25
26 26 dataObject('model', {
27   - name: '_modelEvents',
28   - array: '_models',
29   - hash: '_modelOptionsByCid',
30 27 set: 'setModel',
31   - bind: 'bindModel',
32   - unbind: 'unbindModel',
33   - options: '_setModelOptions',
34   - change: '_onModelChange',
  28 + defaultOptions: {
  29 + render: true,
  30 + fetch: true,
  31 + success: false,
  32 + errors: true
  33 + },
  34 + change: onModelChange,
35 35 $el: '$el',
36 36 cidAttrName: modelCidAttributeName
37 37 });
38 38
39   -_.extend(Thorax.View.prototype, {
40   - _onModelChange: function(model) {
41   - var modelOptions = model && this._modelOptionsByCid[model.cid];
42   - // !modelOptions will be true when setModel(false) is called
43   - if (!modelOptions || (modelOptions && modelOptions.render)) {
44   - this.render();
45   - }
  39 +function onModelChange(model) {
  40 + var modelOptions = model && this._objectOptionsByCid[model.cid];
  41 + // !modelOptions will be true when setModel(false) is called
  42 + if (!modelOptions || (modelOptions && modelOptions.render)) {
  43 + this.render();
46 44 }
47   -});
  45 +}
48 46
49 47 Thorax.View.on({
50 48 model: {
51 49 error: function(model, errors) {
52   - if (this._modelOptionsByCid[model.cid].errors) {
  50 + if (this._objectOptionsByCid[model.cid].errors) {
53 51 this.trigger('error', errors, model);
54 52 }
55 53 },
56 54 change: function(model) {
57   - this._onModelChange(model);
  55 + onModelChange.call(this, model);
58 56 }
59 57 }
60 58 });
7 src/thorax.js
@@ -45,11 +45,12 @@ Thorax.View = Backbone.View.extend({
45 45 _configure: function(options) {
46 46 var self = this;
47 47
  48 + this._objectOptionsByCid = {};
  49 + this._boundDataObjects = [];
  50 +
48 51 // Setup object event tracking
49 52 _.each(inheritVars, function(obj) {
50 53 self[obj.name] = [];
51   - if (obj.array) { self[obj.array] = []; }
52   - if (obj.hash) { self[obj.hash] = {}; }
53 54 });
54 55
55 56 viewsIndexedByCid[this.cid] = this;
@@ -204,7 +205,7 @@ Thorax.View = Backbone.View.extend({
204 205 } else {
205 206 this.el.innerHTML = "";
206 207 var element;
207   - if (this.collection && this._collectionOptionsByCid[this.collection.cid] && this._renderCount) {
  208 + if (this.collection && this._objectOptionsByCid[this.collection.cid] && this._renderCount) {
208 209 // preserveCollectionElement calls the callback after it has a reference
209 210 // to the collection element, calls the callback, then re-appends the element
210 211 preserveCollectionElement.call(this, function() {
17 src/util.js
@@ -83,7 +83,7 @@ function objectEvents(target, eventName, callback, context) {
83 83 if (_.isObject(callback)) {
84 84 var spec = inheritVars[eventName];
85 85 if (spec && spec.event) {
86   - addEvents(target[spec.name], callback, context);
  86 + addEvents(target['_' + eventName + 'Events'], callback, context);
87 87 return true;
88 88 }
89 89 }
@@ -100,21 +100,6 @@ function addEvents(target, source, context) {
100 100 });
101 101 }
102 102
103   -function extendViewMember(name, callback) {
104   - var $super = Thorax.View.prototype[name];
105   - Thorax.View.prototype[name] = function() {
106   - var ret = $super.apply(this, arguments);
107   - callback.apply(this, arguments);
108   - return ret;
109   - };
110   -}
111   -function extendOptions(name, callback) {
112   - var $super = Thorax.View.prototype[name];
113   - Thorax.View.prototype[name] = function(dataObject, options) {
114   - return $super.call(this, dataObject, _.extend(callback.call(this, dataObject, options), options));
115   - };
116   -}
117   -
118 103 function getOptionsData(options) {
119 104 if (!options || !options.data) {
120 105 throw new Error('Handlebars template compiled without data, use: Handlebars.compile(template, {data: true})');
3,655 test/lib/chai.js
0 additions, 3,655 deletions not shown
7 test/lib/mocha-override.css
... ... @@ -1,7 +0,0 @@
1   -#mocha-stats {
2   - position: static;
3   -}
4   -
5   -#mocha .test a.replay {
6   - right: 0;
7   -}
227 test/lib/mocha.css
... ... @@ -1,227 +0,0 @@
1   -@charset "UTF-8";
2   -body {
3   - font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif;
4   - padding: 60px 50px;
5   -}
6   -
7   -#mocha ul, #mocha li {
8   - margin: 0;
9   - padding: 0;
10   -}
11   -
12   -#mocha ul {
13   - list-style: none;
14   -}
15   -
16   -#mocha h1, #mocha h2 {
17   - margin: 0;
18   -}
19   -
20   -#mocha h1 {
21   - margin-top: 15px;
22   - font-size: 1em;
23   - font-weight: 200;
24   -}
25   -
26   -#mocha h1 a {
27   - text-decoration: none;
28   - color: inherit;
29   -}
30   -
31   -#mocha h1 a:hover {
32   - text-decoration: underline;
33   -}
34   -
35   -#mocha .suite .suite h1 {
36   - margin-top: 0;
37   - font-size: .8em;
38   -}
39   -
40   -.hidden {
41   - display: none;
42   -}
43   -
44   -#mocha h2 {
45   - font-size: 12px;
46   - font-weight: normal;
47   - cursor: pointer;
48   -}
49   -
50   -#mocha .suite {
51   - margin-left: 15px;
52   -}
53   -
54   -#mocha .test {
55   - margin-left: 15px;
56   -}
57   -
58   -#mocha .test.pending:hover h2::after {
59   - content: '(pending)';
60   - font-family: arial;
61   -}
62   -
63   -#mocha .test.pass.medium .duration {
64   - background: #C09853;
65   -}
66   -
67   -#mocha .test.pass.slow .duration {
68   - background: #B94A48;
69   -}
70   -
71   -#mocha .test.pass::before {
72   - content: '✓';
73   - font-size: 12px;
74   - display: block;
75   - float: left;
76   - margin-right: 5px;
77   - color: #00d6b2;
78   -}
79   -
80   -#mocha .test.pass .duration {
81   - font-size: 9px;
82   - margin-left: 5px;
83   - padding: 2px 5px;
84   - color: white;
85   - -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
86   - -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
87   - box-shadow: inset 0 1px 1px rgba(0,0,0,.2);
88   - -webkit-border-radius: 5px;
89   - -moz-border-radius: 5px;
90   - -ms-border-radius: 5px;
91   - -o-border-radius: 5px;
92   - border-radius: 5px;
93   -}
94   -
95   -#mocha .test.pass.fast .duration {
96   - display: none;
97   -}
98   -
99   -#mocha .test.pending {
100   - color: #0b97c4;
101   -}
102   -
103   -#mocha .test.pending::before {
104   - content: '◦';
105   - color: #0b97c4;
106   -}
107   -
108   -#mocha .test.fail {
109   - color: #c00;
110   -}
111   -
112   -#mocha .test.fail pre {
113   - color: black;
114   -}
115   -
116   -#mocha .test.fail::before {
117   - content: '✖';
118   - font-size: 12px;
119   - display: block;
120   - float: left;
121   - margin-right: 5px;
122   - color: #c00;
123   -}
124   -
125   -#mocha .test pre.error {
126   - color: #c00;
127   - max-height: 300px;
128   - overflow: auto;
129   -}
130   -
131   -#mocha .test pre {
132   - display: inline-block;
133   - font: 12px/1.5 monaco, monospace;
134   - margin: 5px;
135   - padding: 15px;
136   - border: 1px solid #eee;
137   - border-bottom-color: #ddd;
138   - -webkit-border-radius: 3px;
139   - -webkit-box-shadow: 0 1px 3px #eee;
140   - -moz-border-radius: 3px;
141   - -moz-box-shadow: 0 1px 3px #eee;
142   -}
143   -
144   -#mocha .test h2 {
145   - position: relative;
146   -}
147   -
148   -#mocha .test a.replay {
149   - position: absolute;
150   - top: 3px;
151   - right: -20px;
152   - text-decoration: none;
153   - vertical-align: middle;
154   - display: block;
155   - width: 15px;
156   - height: 15px;
157   - line-height: 15px;
158   - text-align: center;
159   - background: #eee;
160   - font-size: 15px;
161   - -moz-border-radius: 15px;
162   - border-radius: 15px;
163   - -webkit-transition: opacity 200ms;
164   - -moz-transition: opacity 200ms;
165   - transition: opacity 200ms;
166   - opacity: 0.2;
167   - color: #888;
168   -}
169   -
170   -#mocha .test:hover a.replay {
171   - opacity: 1;
172   -}
173   -
174   -#mocha-report.pass .test.fail {
175   - display: none;
176   -}
177   -
178   -#mocha-report.fail .test.pass {
179   - display: none;
180   -}
181   -
182   -#mocha-error {
183   - color: #c00;
184   - font-size: 1.5 em;
185   - font-weight: 100;
186   - letter-spacing: 1px;
187   -}
188   -
189   -#mocha-stats {
190   - position: fixed;
191   - top: 15px;
192   - right: 10px;
193   - font-size: 12px;
194   - margin: 0;
195   - color: #888;
196   -}
197   -
198   -#mocha-stats .progress {
199   - float: right;
200   - padding-top: 0;
201   -}
202   -
203   -#mocha-stats em {
204   - color: black;
205   -}
206   -
207   -#mocha-stats a {
208   - text-decoration: none;
209   - color: inherit;
210   -}
211   -
212   -#mocha-stats a:hover {
213   - border-bottom: 1px solid #eee;
214   -}
215   -
216   -#mocha-stats li {
217   - display: inline-block;
218   - margin: 0 5px;
219   - list-style: none;
220   - padding-top: 11px;
221   -}
222   -
223   -code .comment { color: #ddd }
224   -code .init { color: #2F6FAD }
225   -code .string { color: #5890AD }
226   -code .keyword { color: #8A6343 }
227   -code .number { color: #2F6FAD }
4,994 test/lib/mocha.js
0 additions, 4,994 deletions not shown
35 test/lib/runner.js
... ... @@ -1,35 +0,0 @@
1   -/*global chai, mocha, mochaPhantomJS */
2   -mocha.setup({
3   - ui: 'bdd',
4   - globals: ['addEventListener', 'removeEventListener']
5   -});
6   -
7   -window.expect = chai.expect;
8   -
9   -chai.Assertion.includeStack = true;
10   -
11   -sinon.config = {
12   - injectIntoThis: true,
13   - injectInto: null,
14   - properties: ['spy', 'stub', 'mock', 'clock', 'sandbox', 'server', 'requests', 'on'],
15   - useFakeTimers: [10],
16   - useFakeServer: true
17   -};
18   -
19   -beforeEach(function() {
20   - var config = sinon.getConfig(sinon.config);
21   - config.injectInto = this;
22   - this.sandbox = sinon.sandbox.create(config);
23   -});
24   -afterEach(function() {
25   - this.clock.tick(1000);
26   - this.sandbox.verifyAndRestore();
27   -});
28   -
29   -$(document).ready(function() {
30   - if (window.mochaPhantomJS) {
31   - mochaPhantomJS.run();
32   - } else {
33   - mocha.run();
34   - }
35   -});
18 test/lib/sinon-backbone.js
... ... @@ -1,18 +0,0 @@
1   -(function() {
2   - // Allow for simple backbone event cleanup
3   - var inject = sinon.sandbox.inject;
4   - sinon.sandbox.inject = function(obj) {
5   - obj = inject.call(this, obj);
6   -
7   - obj.on = function(obj, event, callback) {
8   - var spy = this.spy(callback);
9   - spy.restore = function() {
10   - obj.off(event, spy);
11   - };
12   - obj.on(event, spy);
13   - return spy;
14   - };
15   -
16   - return obj;
17   - };
18   -})();
121 test/lib/sinon-chai.js
... ... @@ -1,121 +0,0 @@
1   -/*
2   - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
3   - Version 2, December 2004
4   -
5   - Copyright (C) 2012 Domenic Denicola <domenic@domenicdenicola.com>
6   -
7   - Everyone is permitted to copy and distribute verbatim or modified
8   - copies of this license document, and changing it is allowed as long
9   - as the name is changed.
10   -
11   - DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
12   - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
13   -
14   - 0. You just DO WHAT THE FUCK YOU WANT TO.
15   -*/
16   -(function (sinonChai) {
17   - "use strict";
18   -
19   - // Module systems magic dance.
20   -
21   - if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
22   - // NodeJS
23   - module.exports = sinonChai;
24   - } else if (typeof define === "function" && define.amd) {
25   - // AMD
26   - define(function () {
27   - return sinonChai;
28   - });
29   - } else {
30   - // Other environment (usually <script> tag): plug in to global chai instance directly.
31   - chai.use(sinonChai);
32   - }
33   -}(function sinonChai(chai, utils) {
34   - "use strict";
35   -
36   - var slice = Array.prototype.slice;
37   -
38   - function isSpy(putativeSpy) {
39   - return typeof putativeSpy === "function" &&
40   - typeof putativeSpy.getCall === "function" &&
41   - typeof putativeSpy.calledWithExactly === "function";
42   - }
43   -
44   - function isCall(putativeCall) {
45   - return putativeCall && isSpy(putativeCall.proxy);
46   - }
47   -
48   - function assertCanWorkWith(assertion) {
49   - if (!isSpy(assertion._obj) && !isCall(assertion._obj)) {
50   - throw new TypeError(utils.inspect(assertion._obj) + " is not a spy or a call to a spy!");
51   - }
52   - }
53   -
54   - function getMessages(spy, action, nonNegatedSuffix, always, args) {
55   - var verbPhrase = always ? "always have " : "have ";
56   - nonNegatedSuffix = nonNegatedSuffix || "";
57   - spy = spy.proxy || spy;
58   -
59   - function printfArray(array) {
60   - return spy.printf.apply(spy, array);
61   - }
62   -
63   - return {
64   - affirmative: printfArray(["expected %n to " + verbPhrase + action + nonNegatedSuffix].concat(args)),
65   - negative: printfArray(["expected %n to not " + verbPhrase + action].concat(args))
66   - };
67   - }
68   -
69   - function sinonProperty(name, action, nonNegatedSuffix) {
70   - utils.addProperty(chai.Assertion.prototype, name, function () {
71   - assertCanWorkWith(this);
72   -
73   - var messages = getMessages(this._obj, action, nonNegatedSuffix, false);
74   - this.assert(this._obj[name], messages.affirmative, messages.negative);
75   - });
76   - }
77   -
78   - function createSinonMethodHandler(sinonName, action, nonNegatedSuffix) {
79   - return function () {
80   - assertCanWorkWith(this);
81   -
82   - var alwaysSinonMethod = "always" + sinonName[0].toUpperCase() + sinonName.substring(1);
83   - var shouldBeAlways = utils.flag(this, "always") && typeof this._obj[alwaysSinonMethod] === "function";
84   - var sinonMethod = shouldBeAlways ? alwaysSinonMethod : sinonName;
85   -
86   - var messages = getMessages(this._obj, action, nonNegatedSuffix, shouldBeAlways, slice.call(arguments));
87   - this.assert(this._obj[sinonMethod].apply(this._obj, arguments), messages.affirmative, messages.negative);
88   - };
89   - }
90   -
91   - function sinonMethodAsProperty(name, action, nonNegatedSuffix) {
92   - var handler = createSinonMethodHandler(name, action, nonNegatedSuffix);
93   - utils.addProperty(chai.Assertion.prototype, name, handler);
94   - }
95   -
96   - function exceptionalSinonMethod(chaiName, sinonName, action, nonNegatedSuffix) {
97   - var handler = createSinonMethodHandler(sinonName, action, nonNegatedSuffix);
98   - utils.addMethod(chai.Assertion.prototype, chaiName, handler);
99   - }
100   -
101   - function sinonMethod(name, action, nonNegatedSuffix) {
102   - exceptionalSinonMethod(name, name, action, nonNegatedSuffix);
103   - }
104   -
105   - utils.addProperty(chai.Assertion.prototype, "always", function () {
106   - utils.flag(this, "always", true);
107   - });
108   -
109   - sinonProperty("called", "been called", " at least once, but it was never called");
110   - sinonProperty("calledOnce", "been called exactly once", ", but it was called %c%C");
111   - sinonProperty("calledTwice", "been called exactly twice", ", but it was called %c%C");
112   - sinonProperty("calledThrice", "been called exactly thrice", ", but it was called %c%C");
113   - sinonMethodAsProperty("calledWithNew", "been called with new");
114   - sinonMethod("calledBefore", "been called before %1");
115   - sinonMethod("calledAfter", "been called after %1");
116   - sinonMethod("calledOn", "been called with %1 as this", ", but it was called with %t instead");
117   - sinonMethod("calledWith", "been called with arguments %*", "%C");
118   - sinonMethod("calledWithExactly", "been called with exact arguments %*", "%C");
119   - sinonMethod("returned", "returned %1");
120   - exceptionalSinonMethod("thrown", "threw", "thrown %1");
121   -}));
41 test/lib/sinon-qunit.js
... ... @@ -1,41 +0,0 @@
1   -/*global sinon, QUnit, test*/
2   -sinon.assert.fail = function (msg) {
3   - QUnit.ok(false, msg);
4   -};
5   -
6   -sinon.assert.pass = function (assertion) {
7   - QUnit.ok(true, assertion);
8   -};
9   -
10   -sinon.config = {
11   - injectIntoThis: true,
12   - injectInto: null,
13   - properties: ['spy', 'stub', 'mock', 'clock', 'sandbox', 'server', 'requests'],
14   - useFakeTimers: [10],
15   - useFakeServer: true
16   -};
17   -
18   -(function (global) {
19   - var module = QUnit.module;
20   -
21   - QUnit.module = function(moduleName, env) {
22   - var sandbox;
23   -
24   - module.call(this, moduleName, {
25   - setup: function() {
26   - var config = sinon.getConfig(sinon.config);
27   - config.injectInto = this;
28   - sandbox = sinon.sandbox.create(config);
29   -
30   - env && env.setup && env.setup.call(this);
31   - },
32   - teardown: function() {
33   - env && env.teardown && env.teardown.call(this);
34   - sandbox.verifyAndRestore();
35   - }
36   - });
37   - };
38   -
39   - // Make sure that we are seeded
40   - QUnit.module('', {});
41   -}(this));
4,157 test/lib/sinon.js
0 additions, 4,157 deletions not shown
5 test/src/test.collection.js → test/src/collection.js
@@ -76,6 +76,7 @@ describe('collection', function() {
76 76 clonedLetterCollection.remove(clonedLetterCollection.models);
77 77 expect(view.$('li')[0].innerHTML).to.equal('empty', msg + 'empty collection renders empty');
78 78 clonedLetterCollection.add(new LetterModel({letter: 'a'}));
  79 +
79 80 expect(view.$('li').length).to.equal(1 * indexMultiplier, msg + 'transition from empty to one item');
80 81 expect(view.$('li')[0 * indexMultiplier].innerHTML).to.equal('a', msg + 'transition from empty to one item');
81 82 expect(renderedCount).to.equal(1, msg + 'rendered event count');
@@ -93,7 +94,7 @@ describe('collection', function() {
93 94
94 95 clonedLetterCollection.remove(clonedLetterCollection.models);
95 96 expect(renderedEmptyCount).to.equal(1, msg + 'rendered:empty event count');
96   - expect(view.$('li')[0 * indexMultiplier].innerHTML).to.equal('a', msg + 'transition from empty to one item');
  97 + expect(view.$('li')[0 * indexMultiplier].innerHTML).to.equal('a', msg + 'transition from empty to one item after freeze');
97 98 }
98 99
99 100 runCollectionTests(new LetterCollectionView(), 1, 'base');
@@ -256,7 +257,7 @@ describe('collection', function() {
256 257 expect(view.$('li').eq(0).html()).to.equal('d');
257 258 });
258 259
259   - it("_bindCollection or model.set can be called in context()", function() {
  260 + it("bindDataObject or model.set can be called in context()", function() {
260 261 //this causes recursion
261 262 var view = new Thorax.View({
262 263 model: new Thorax.Model(),
6 test/src/test.event.js → test/src/event.js
@@ -157,7 +157,7 @@ describe('event', function() {
157 157 expect(e).to.equal(2);
158 158 });
159 159
160   - it("unbindModel / unbindCollection stops events from being triggered", function() {
  160 + it("unbindDataObject stops events from being triggered", function() {
161 161 var spy = this.spy();
162 162 var view = new Thorax.View({
163 163 events: {
@@ -167,11 +167,11 @@ describe('event', function() {
167 167 }
168 168 });
169 169 view.myModel = new Thorax.Model({key: 'value'});
170   - view.bindModel(view.myModel, {render: false});
  170 + view.bindDataObject(view.myModel, {render: false});
171 171 expect(spy.callCount).to.equal(0);
172 172 view.myModel.trigger('test');
173 173 expect(spy.callCount).to.equal(1);
174   - view.unbindModel(view.myModel);
  174 + view.unbindDataObject(view.myModel);
175 175 view.myModel.trigger('test');
176 176 expect(spy.callCount).to.equal(1);
177 177 });
4 test/src/test.form.js → test/src/form.js
@@ -2,7 +2,9 @@ describe('form', function() {
2 2 it("serialize() / populate()", function() {
3 3 var FormView = Thorax.View.extend({
4 4 name: 'form',
5   - template: Handlebars.compile('<form><input name="one"/><select name="two"><option value="a">a</option><option value="b">b</option></select><input name="three[four]"/><input name="five" value="A" type="checkbox" /><input name="five" value="B" type="checkbox" checked /><input name="five" value="C" type="checkbox" checked /><input name="six" value="LOL" type="checkbox" checked /></form>', {data: true})
  5 + template: function() {
  6 + return '<form><input name="one"/><select name="two"><option value="a">a</option><option value="b">b</option></select><input name="three[four]"/><input name="five" value="A" type="checkbox" /><input name="five" value="B" type="checkbox" checked /><input name="five" value="C" type="checkbox" checked /><input name="six" value="LOL" type="checkbox" checked /></form>';
  7 + }
6 8 });
7 9
8 10 var model = new Thorax.Model({
40 test/src/test.helpers.js → test/src/helpers/button-link.js
... ... @@ -1,34 +1,9 @@
@@ -74,9 +49,4 @@ describe('helpers', function() {
6 test/src/helpers/collection.js
... ... @@ -0,0 +1,6 @@
  1 +describe('collection helper', function() {
  2 + it('should have access to handlebars noop', function() {
  3 + // Explicit verification that Handlebars is exposing this field.
  4 + expect(Handlebars.VM.noop).to.exist;
  5 + });
  6 +});
33 test/src/helpers/element.js
... ... @@ -0,0 +1,33 @@
  1 +describe('element helper', function() {
  2 + it("element helper", function() {
  3 + var a = document.createElement('li');
  4 + a.innerHTML = 'one';
  5 + var view = new Thorax.View({
  6 + template: '<ul>{{element a tag="li"}}{{element b tag="li"}}{{element c}}{{element d}}</ul>',
  7 + a: a,
  8 + b: function() {
  9 + var li = document.createElement('li');
  10 + li.innerHTML = 'two';
  11 + return li;
  12 + },
  13 + c: function() {
  14 + return $('<li>three</li><li>four</li>');
  15 + },
  16 + d: $('<li>five</li>')
  17 + });
  18 + view.render();
  19 + expect(view.$('li')[0].innerHTML).to.equal('one');
  20 + expect(view.$('li')[1].innerHTML).to.equal('two');
  21 + expect(view.$('li')[2].innerHTML).to.equal('three');
  22 + expect(view.$('li')[3].innerHTML).to.equal('four');
  23 + expect(view.$('li')[4].innerHTML).to.equal('five');
  24 + view.html('');
  25 + expect(view.$('li').length).to.equal(0);
  26 + view.render();
  27 + expect(view.$('li')[0].innerHTML).to.equal('one');
  28 + expect(view.$('li')[1].innerHTML).to.equal('two');
  29 + expect(view.$('li')[2].innerHTML).to.equal('three');
  30 + expect(view.$('li')[3].innerHTML).to.equal('four');
  31 + expect(view.$('li')[4].innerHTML).to.equal('five');
  32 + });
  33 +});