refsSpec.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. 'use strict';
  2. var expect = require('chai').expect;
  3. var Refs = require('../../');
  4. var hasProperty = require('./has-property');
  5. describe('refs', function() {
  6. describe('one-to-one', function() {
  7. var refs = new Refs({ name: 'foo' }, { name: 'bar' });
  8. it('should keep already set property', function() {
  9. // given
  10. var b = { };
  11. var a = { foo: b };
  12. // when
  13. refs.bind(a, 'foo');
  14. // then
  15. expect(a.foo).to.equal(b);
  16. });
  17. it('should define property invisibly', function() {
  18. // given
  19. var a = {};
  20. // when
  21. refs.bind(a, 'foo');
  22. // then
  23. expect(a.foo).not.to.exist;
  24. expect(hasProperty(a, 'foo')).to.equal(true);
  25. });
  26. it('should define non-enumerable property per default', function() {
  27. // given
  28. var a = {};
  29. refs.bind(a, 'foo');
  30. a.foo = {};
  31. // when
  32. var json = JSON.stringify(a);
  33. expect(json).to.equal('{}');
  34. });
  35. it('should define enumerable property', function() {
  36. // given
  37. var enumerableRefs = new Refs({ name: 'foo', enumerable: true }, { name: 'bar' });
  38. var a = {};
  39. enumerableRefs.bind(a, 'foo');
  40. a.foo = {};
  41. // when
  42. var json = JSON.stringify(a);
  43. expect(json).to.equal('{"foo":{}}');
  44. });
  45. it('should define non-configurable property per default', function() {
  46. // given
  47. var refs = new Refs({ name: 'foo' }, { name: 'bar' });
  48. var a = {};
  49. refs.bind(a, 'foo');
  50. a.foo = {};
  51. // when
  52. expect(function() {
  53. delete a.foo;
  54. }).to.throw;
  55. });
  56. it('should define configurable property', function() {
  57. // given
  58. var configurableRefs = new Refs(
  59. { name: 'foo', configurable: true },
  60. { name: 'bar' }
  61. );
  62. var a = {};
  63. configurableRefs.bind(a, 'foo');
  64. a.foo = {};
  65. // when
  66. delete a.foo;
  67. expect(a.foo).to.not.exist;
  68. });
  69. it('should create bi-directional configurable property', function() {
  70. // given
  71. var configurableRefs = new Refs(
  72. { name: 'foo', configurable: true },
  73. { name: 'bar', configurable: true }
  74. );
  75. var a = {};
  76. configurableRefs.bind(a, 'foo');
  77. a.foo = {};
  78. // when
  79. delete a.foo.bar;
  80. expect(a.foo.bar).to.not.exist;
  81. });
  82. it('should create bi-directional reference', function() {
  83. // given
  84. var a = {}, b = {};
  85. refs.bind(a, 'foo');
  86. refs.bind(b, 'bar');
  87. // when
  88. a.foo = b;
  89. // then
  90. expect(a.foo).to.equal(b);
  91. expect(b.bar).to.equal(a);
  92. });
  93. it('should unset property', function() {
  94. // given
  95. var a = {}, b = {};
  96. refs.bind(a, 'foo');
  97. refs.bind(b, 'bar');
  98. // when
  99. a.foo = b;
  100. a.foo = null;
  101. expect(a.foo).to.equal(null);
  102. expect(b.bar).to.equal(undefined);
  103. });
  104. it('should transitively define properties', function() {
  105. // given
  106. var a = {}, b = {};
  107. refs.bind(a, 'foo');
  108. // when
  109. a.foo = b;
  110. b.bar = null;
  111. // then
  112. expect(a.foo).to.equal(undefined);
  113. });
  114. it('should not allow property deletion', function() {
  115. // given
  116. var a = {};
  117. refs.bind(a, 'foo');
  118. expect(function() {
  119. // when
  120. delete a.foo;
  121. }).to.throw('Cannot delete property \'foo\' of #<Object>');
  122. });
  123. });
  124. describe('one-to-many', function() {
  125. var refs = new Refs({ name: 'foos', collection: true }, { name: 'bar' });
  126. it('should define non-enumerable property per default', function() {
  127. // given
  128. var a = {};
  129. refs.bind(a, 'foos');
  130. a.foos.push({});
  131. // when
  132. var json = JSON.stringify(a);
  133. expect(json).to.equal('{}');
  134. });
  135. it('should define enumerable property', function() {
  136. // given
  137. var enumerableRefs = new Refs({ name: 'foos', enumerable: true, collection: true }, { name: 'bar' });
  138. var a = {};
  139. enumerableRefs.bind(a, 'foos');
  140. a.foos.push({});
  141. // when
  142. var json = JSON.stringify(a);
  143. expect(json).to.equal('{"foos":[{}]}');
  144. });
  145. it('should keep already set property', function() {
  146. // given
  147. var b = { };
  148. var a = { foos: [ b ] };
  149. // when
  150. refs.bind(a, 'foos');
  151. // then
  152. expect(a.foos).to.eql([ b ]);
  153. });
  154. it('should define collection property as array', function() {
  155. // given
  156. var a = {};
  157. // when
  158. refs.bind(a, 'foos');
  159. // then
  160. expect(a.foos).to.eql([]);
  161. });
  162. it('should auto-populate collection', function() {
  163. // given
  164. var a = {};
  165. var b1 = {}, b2 = {};
  166. refs.bind(b1, 'bar');
  167. refs.bind(b2, 'bar');
  168. // when
  169. b1.bar = a;
  170. b2.bar = a;
  171. // then
  172. expect(a.foos).to.eql([ b1, b2 ]);
  173. });
  174. it('should auto-remove from collection', function() {
  175. // given
  176. var a = {};
  177. var b1 = {}, b2 = {};
  178. refs.bind(b1, 'bar');
  179. refs.bind(b2, 'bar');
  180. b1.bar = a;
  181. b2.bar = a;
  182. // when
  183. b1.bar = null;
  184. // then
  185. expect(a.foos).to.eql([ b2 ]);
  186. });
  187. it('should transitively bind one property', function() {
  188. // given
  189. var a = {}, b = {};
  190. refs.bind(a, 'foos');
  191. // when
  192. a.foos.add(b);
  193. b.bar = null;
  194. // then
  195. expect(a.foos).to.eql([]);
  196. });
  197. it('should transitively bind many property', function() {
  198. // given
  199. var b = { };
  200. var c = { };
  201. var a = { foos: [ c ] };
  202. refs.bind(b, 'bar');
  203. // when
  204. b.bar = a;
  205. // then
  206. expect(a.foos).to.eql([ c, b ]);
  207. });
  208. it('should transitively bind many-to-one inverse reference', function() {
  209. // given
  210. var b = { };
  211. var c = { };
  212. var a = { foos: [ b, c ] };
  213. // when
  214. refs.bind(a, 'foos');
  215. // then
  216. expect(b.bar).to.eql(a);
  217. expect(c.bar).to.eql(a);
  218. expect(a.foos).to.eql([ b, c ]);
  219. });
  220. });
  221. describe('many-to-many', function() {
  222. var refs = new Refs({ name: 'foos', collection: true }, { name: 'bars', collection: true });
  223. it('should define property invisibly', function() {
  224. // given
  225. var b = {};
  226. // when
  227. refs.bind(b, 'bars');
  228. // then
  229. expect(b.bars).to.eql([]);
  230. });
  231. it('should inverse add', function() {
  232. // given
  233. var a = {}, b = {};
  234. refs.bind(a, 'foos');
  235. refs.bind(b, 'bars');
  236. // when
  237. a.foos.add(b);
  238. // then
  239. expect(b.bars).to.eql([ a ]);
  240. });
  241. it('should add many-to-many', function() {
  242. // given
  243. var a1 = {}, a2 = {},
  244. b1 = {}, b2 = {};
  245. refs.bind(a1, 'foos');
  246. refs.bind(a2, 'foos');
  247. // when
  248. a1.foos.add(b1);
  249. a1.foos.add(b2);
  250. a2.foos.add(b2);
  251. // then
  252. expect(b2.bars.contains(a2)).to.equal(true);
  253. expect(b1.bars).to.eql([ a1 ]);
  254. expect(b2.bars).to.eql([ a1, a2 ]);
  255. });
  256. it('should inverse remove', function() {
  257. // given
  258. var a1 = {}, a2 = {},
  259. b1 = {}, b2 = {};
  260. refs.bind(a1, 'foos');
  261. refs.bind(a2, 'foos');
  262. // when
  263. a1.foos.add(b1);
  264. a1.foos.add(b2);
  265. a2.foos.add(b2);
  266. b2.bars.remove(a1);
  267. // then
  268. expect(b1.bars).to.eql([ a1 ]);
  269. expect(b2.bars).to.eql([ a2 ]);
  270. });
  271. it('should transitively define properties', function() {
  272. // given
  273. var a = {}, b = {};
  274. refs.bind(a, 'foos');
  275. // when
  276. a.foos.add(b);
  277. b.bars.remove(a);
  278. // then
  279. expect(a.foos).to.eql([]);
  280. expect(b.bars).to.eql([]);
  281. });
  282. });
  283. });