/tests/binding.js

https://github.com/thedersen/backbone.validation · JavaScript · 456 lines · 377 code · 79 blank · 0 comment · 6 complexity · 9f87898e7ad7528df28cdc8110626780 MD5 · raw file

  1. buster.testCase('Binding to view with model', {
  2. setUp: function() {
  3. var View = Backbone.View.extend({
  4. render: function() {
  5. Backbone.Validation.bind(this);
  6. }
  7. });
  8. var Model = Backbone.Model.extend({
  9. validation: {
  10. name: function(val) {
  11. if (!val) {
  12. return 'Name is invalid';
  13. }
  14. }
  15. }
  16. });
  17. this.model = new Model();
  18. this.view = new View({
  19. model: this.model
  20. });
  21. this.view.render();
  22. },
  23. tearDown: function() {
  24. this.view.remove();
  25. },
  26. "the model's validate function is defined": function() {
  27. assert.defined(this.model.validate);
  28. },
  29. "the model's isValid function is overridden": function() {
  30. refute.same(this.model.isValid, Backbone.Model.prototype.isValid);
  31. },
  32. "and passing custom callbacks with the options": {
  33. setUp: function(){
  34. this.valid = this.spy();
  35. this.invalid = this.spy();
  36. Backbone.Validation.bind(this.view, {
  37. valid: this.valid,
  38. invalid: this.invalid
  39. });
  40. },
  41. "should call valid callback passed with options": function() {
  42. this.model.set({
  43. name: 'Ben'
  44. }, {validate: true});
  45. assert.called(this.valid);
  46. },
  47. "should call invalid callback passed with options": function() {
  48. this.model.set({
  49. name: ''
  50. }, {validate: true});
  51. assert.called(this.invalid);
  52. }
  53. },
  54. "and passing custom callbacks and selector with the options": {
  55. setUp: function(){
  56. this.valid = this.spy();
  57. this.invalid = this.spy();
  58. Backbone.Validation.bind(this.view, {
  59. selector: 'some-selector',
  60. valid: this.valid,
  61. invalid: this.invalid
  62. });
  63. },
  64. "should call valid callback with correct selector": function() {
  65. this.model.set({
  66. name: 'Ben'
  67. }, {validate: true});
  68. assert.calledWith(this.valid, this.view, 'name', 'some-selector');
  69. },
  70. "should call invalid callback with correct selector": function() {
  71. this.model.set({
  72. name: ''
  73. }, {validate: true});
  74. assert.calledWith(this.invalid, this.view, 'name', 'Name is invalid', 'some-selector');
  75. }
  76. },
  77. "and unbinding":{
  78. setUp: function(){
  79. Backbone.Validation.unbind(this.view);
  80. },
  81. "the model's validate function is undefined": function() {
  82. refute.defined(this.model.validate);
  83. },
  84. "the model's preValidate function is undefined": function() {
  85. refute.defined(this.model.preValidate);
  86. },
  87. "the model's isValid function is restored": function() {
  88. assert.same(this.model.isValid, Backbone.Model.prototype.isValid);
  89. }
  90. }
  91. });
  92. buster.testCase('Binding to view with optional model', {
  93. setUp: function() {
  94. var self = this;
  95. this.valid = this.spy();
  96. this.invalid = this.spy();
  97. var Model = Backbone.Model.extend({
  98. validation: {
  99. name: function(val) {
  100. if (!val) {
  101. return 'Name is invalid';
  102. }
  103. }
  104. }
  105. });
  106. this.model = new Model();
  107. var View = Backbone.View.extend({
  108. render: function() {
  109. Backbone.Validation.bind(this, {
  110. model: self.model,
  111. valid: self.valid,
  112. invalid: self.invalid
  113. });
  114. }
  115. });
  116. this.view = new View();
  117. this.view.render();
  118. },
  119. tearDown: function() {
  120. this.view.remove();
  121. },
  122. "the model's validate function is defined": function() {
  123. assert.defined(this.model.validate);
  124. },
  125. "the model's isValid function is overridden": function() {
  126. refute.same(this.model.isValid, Backbone.Model.prototype.isValid);
  127. },
  128. "should call valid callback passed with options": function() {
  129. this.model.set({
  130. name: 'Ben'
  131. }, {validate: true});
  132. assert.called(this.valid);
  133. },
  134. "should call invalid callback passed with options": function() {
  135. this.model.set({
  136. name: ''
  137. }, {validate: true});
  138. assert.called(this.invalid);
  139. },
  140. "and unbinding":{
  141. setUp: function(){
  142. Backbone.Validation.unbind(this.view, {model: this.model});
  143. },
  144. "the model's validate function is undefined": function() {
  145. refute.defined(this.model.validate);
  146. },
  147. "the model's preValidate function is undefined": function() {
  148. refute.defined(this.model.preValidate);
  149. },
  150. "the model's isValid function is restored": function() {
  151. assert.same(this.model.isValid, Backbone.Model.prototype.isValid);
  152. }
  153. }
  154. });
  155. buster.testCase('Binding to view with collection', {
  156. setUp: function() {
  157. var View = Backbone.View.extend({
  158. render: function() {
  159. Backbone.Validation.bind(this);
  160. }
  161. });
  162. this.Model = Backbone.Model.extend({
  163. validation: {
  164. name: function(val) {
  165. if (!val) {
  166. return 'Name is invalid';
  167. }
  168. }
  169. }
  170. });
  171. var Collection = Backbone.Collection.extend({
  172. model: this.Model
  173. });
  174. this.collection = new Collection([{name: 'Tom'}, {name: 'Thea'}]);
  175. this.view = new View({
  176. collection: this.collection
  177. });
  178. this.view.render();
  179. },
  180. tearDown: function() {
  181. this.view.remove();
  182. },
  183. "binds existing models in collection when binding": function() {
  184. assert.defined(this.collection.at(0).validate);
  185. assert.defined(this.collection.at(1).validate);
  186. },
  187. "binds model that is added to the collection": function() {
  188. var model = new this.Model({name: 'Thomas'});
  189. this.collection.add(model);
  190. assert.defined(model.validate);
  191. },
  192. "binds models that are batch added to the collection": function() {
  193. var model1 = new this.Model({name: 'Thomas'});
  194. var model2 = new this.Model({name: 'Hans'});
  195. this.collection.add([model1, model2]);
  196. assert.defined(model1.validate);
  197. assert.defined(model2.validate);
  198. },
  199. "unbinds model that is removed from collection": function() {
  200. var model = this.collection.at(0);
  201. this.collection.remove(model);
  202. refute.defined(model.validate);
  203. },
  204. "unbinds models that are batch removed from collection": function() {
  205. var model1 = this.collection.at(0);
  206. var model2 = this.collection.at(1);
  207. this.collection.remove([model1, model2]);
  208. refute.defined(model1.validate);
  209. refute.defined(model2.validate);
  210. },
  211. "unbinds all models in collection when unbinding view": function() {
  212. Backbone.Validation.unbind(this.view);
  213. refute.defined(this.collection.at(0).validate);
  214. refute.defined(this.collection.at(1).validate);
  215. },
  216. "unbinds all collection events when unbinding view": function() {
  217. var that = this;
  218. Backbone.Validation.unbind(this.view);
  219. refute.exception(function() { that.collection.trigger('add'); });
  220. refute.exception(function() { that.collection.trigger('remove'); });
  221. }
  222. });
  223. buster.testCase('Binding to view with optional collection', {
  224. setUp: function() {
  225. var self = this;
  226. this.Model = Backbone.Model.extend({
  227. validation: {
  228. name: function(val) {
  229. if (!val) {
  230. return 'Name is invalid';
  231. }
  232. }
  233. }
  234. });
  235. var Collection = Backbone.Collection.extend({
  236. model: this.Model
  237. });
  238. this.collection = new Collection([{name: 'Tom'}, {name: 'Thea'}]);
  239. var View = Backbone.View.extend({
  240. render: function() {
  241. Backbone.Validation.bind(this, {collection: self.collection});
  242. }
  243. });
  244. this.view = new View();
  245. this.view.render();
  246. },
  247. tearDown: function() {
  248. this.view.remove();
  249. },
  250. "binds existing models in collection when binding": function() {
  251. assert.defined(this.collection.at(0).validate);
  252. assert.defined(this.collection.at(1).validate);
  253. },
  254. "binds model that is added to the collection": function() {
  255. var model = new this.Model({name: 'Thomas'});
  256. this.collection.add(model);
  257. assert.defined(model.validate);
  258. },
  259. "binds models that are batch added to the collection": function() {
  260. var model1 = new this.Model({name: 'Thomas'});
  261. var model2 = new this.Model({name: 'Hans'});
  262. this.collection.add([model1, model2]);
  263. assert.defined(model1.validate);
  264. assert.defined(model2.validate);
  265. },
  266. "unbinds model that is removed from collection": function() {
  267. var model = this.collection.at(0);
  268. this.collection.remove(model);
  269. refute.defined(model.validate);
  270. },
  271. "unbinds models that are batch removed from collection": function() {
  272. var model1 = this.collection.at(0);
  273. var model2 = this.collection.at(1);
  274. this.collection.remove([model1, model2]);
  275. refute.defined(model1.validate);
  276. refute.defined(model2.validate);
  277. },
  278. "unbinds all models in collection when unbinding view": function() {
  279. Backbone.Validation.unbind(this.view, {collection: this.collection});
  280. refute.defined(this.collection.at(0).validate);
  281. refute.defined(this.collection.at(1).validate);
  282. },
  283. "unbinds all collection events when unbinding view": function() {
  284. var that = this;
  285. Backbone.Validation.unbind(this.view, {collection: this.collection});
  286. refute.exception(function() { that.collection.trigger('add'); });
  287. refute.exception(function() { that.collection.trigger('remove'); });
  288. }
  289. });
  290. buster.testCase('Binding to view with no model or collection', {
  291. "throws exception": function(){
  292. assert.exception(function(){
  293. Backbone.Validation.bind(new Backbone.View());
  294. });
  295. }
  296. });
  297. buster.testCase('Binding multiple views to same model', {
  298. setUp: function() {
  299. var Model = Backbone.Model.extend({
  300. validation: {
  301. name: function(val) {
  302. if (!val) {
  303. return 'Name is invalid';
  304. }
  305. },
  306. surname: function(val) {
  307. if (!val) {
  308. return 'Surname is invalid';
  309. }
  310. }
  311. }
  312. });
  313. var View = Backbone.View.extend({
  314. initialize: function(data){
  315. this.attributeName = data.attributeName;
  316. },
  317. render: function() {
  318. var html = $('<input type="text" name="' + this.attributeName + '" />');
  319. this.$el.append(html);
  320. Backbone.Validation.bind(this);
  321. }
  322. });
  323. this.model = new Model();
  324. this.view1 = new View({
  325. attributeName: 'name',
  326. model: this.model
  327. });
  328. this.view2 = new View({
  329. attributeName: 'surname',
  330. model: this.model
  331. });
  332. this.view1.render();
  333. this.view2.render();
  334. this.name = $(this.view1.$('[name~=name]'));
  335. this.surname = $(this.view2.$('[name~=surname]'));
  336. },
  337. tearDown: function() {
  338. this.view1.remove();
  339. },
  340. "both elements receive invalid class and data-error message when validating the model": function() {
  341. this.model.validate();
  342. assert(this.name.hasClass('invalid'));
  343. assert(this.surname.hasClass('invalid'));
  344. assert.equals(this.name.data('error'), 'Name is invalid');
  345. assert.equals(this.surname.data('error'), 'Surname is invalid');
  346. },
  347. "each element validates separately": {
  348. setUp: function() {
  349. this.model.set({
  350. name: 'Rafael'
  351. });
  352. this.model.validate();
  353. },
  354. "first element should not have invalid class": function() {
  355. refute(this.name.hasClass('invalid'));
  356. },
  357. "second element should have invalid class": function() {
  358. assert(this.surname.hasClass('invalid'));
  359. }
  360. },
  361. "each view can be unbind separately from the same model": {
  362. setUp: function() {
  363. this.model.set('name', '');
  364. this.view2.render();
  365. Backbone.Validation.unbind(this.view2);
  366. this.model.validate();
  367. },
  368. "first element is invalid and has class invalid": function() {
  369. refute(this.model.isValid('name'));
  370. assert(this.name.hasClass('invalid'));
  371. },
  372. "second element is invalid and has not class invalid": function() {
  373. refute(this.model.isValid('surname'));
  374. refute(this.surname.hasClass('invalid'));
  375. }
  376. }
  377. });