/js/evented.test.js

https://github.com/production-minds/flock · JavaScript · 298 lines · 213 code · 60 blank · 25 comment · 1 complexity · 1e7a5ba3fd7afd7aa7ef63b306465c4f MD5 · raw file

  1. /*global window, flock, module, test, expect, stop, start, ok, equal, deepEqual, raises */
  2. (function ($evented, Single) {
  3. module("Event");
  4. var
  5. root = {
  6. hi: 'There!',
  7. hello: {
  8. world: {
  9. center: "!!"
  10. },
  11. all: "hey"
  12. },
  13. bybye: {
  14. world: {}
  15. }
  16. },
  17. // creating evented datastore object explicitly from flock.single
  18. ds = $evented.create(Single.create(root));
  19. test("Subscription", function () {
  20. function testHandler() {
  21. }
  22. ds.on('hello.world', 'testEvent', testHandler);
  23. ds.on('hello', 'otherEvent', testHandler);
  24. equal(ds.lookup['hello.world']['testEvent'][0], testHandler, "Event handler added");
  25. equal(ds.lookup['hello']['otherEvent'][0], testHandler, "Other event handler added");
  26. ds.off('hello.world', 'testEvent', testHandler);
  27. equal(ds.lookup['hello.world']['testEvent'].length, 0, "Event handler removed");
  28. ds.off('hello.world', 'testEvent');
  29. equal(ds.lookup['hello.world'].hasOwnProperty('testEvent'), false, "Event handlers removed for given event");
  30. ds.off('hello.world');
  31. equal(ds.lookup.hasOwnProperty('hello.world'), false, "All event handlers removed from node");
  32. });
  33. test("Triggering", function () {
  34. var i = 0, j = 0, k = 0,
  35. eventData = "eventData";
  36. function testHandler() {
  37. i++;
  38. }
  39. function otherHandler() {
  40. j++;
  41. }
  42. function topHandler() {
  43. k++;
  44. }
  45. function stopHandler() {
  46. i++;
  47. return false;
  48. }
  49. ds.on('hello.world', 'testEvent', testHandler);
  50. ds.on('hello', 'testEvent', otherHandler);
  51. ds.on('', 'testEvent', topHandler);
  52. ds.on('hello.world', 'otherEvent', otherHandler);
  53. ds.on('hello.world', 'argTesterEvent', function (event, data) {
  54. equal(event.name, 'argTesterEvent', "Event name passed to handler checks out");
  55. equal(event.path, 'hello.world', "Event path passed to handler checks out");
  56. equal(event.target, 'hello.world', "Event target passed to handler checks out");
  57. equal(data, eventData, "Custom event data passed to handler checks out");
  58. return false;
  59. });
  60. // checking arguments passed to event handler
  61. ds.trigger('hello.world', 'argTesterEvent', {data: eventData});
  62. i = j = 0;
  63. ds.trigger('hello.world', 'otherEvent');
  64. equal(j, 1, "Event triggered on single subscribed node");
  65. i = j = k = 0;
  66. ds.trigger('hello.world', 'testEvent');
  67. equal(i, 1, "Event triggered on source node (source and parent both have handlers)");
  68. equal(j, 1, "> Event bubbled to parent");
  69. equal(k, 1, "> Event bubbled to root");
  70. j = 0;
  71. ds.off('hello.world');
  72. ds.trigger('hello.world', 'testEvent');
  73. equal(j, 1, "Event bubbled to parent from non-capturing node");
  74. i = j = 0;
  75. ds.on('hello.world', 'testEvent', stopHandler);
  76. ds.trigger('hello.world', 'testEvent');
  77. equal(i, 1, "Event triggered on source node with handler that returns false");
  78. equal(j, 0, "> Event didn't bubble bubble to parent");
  79. // one-time events
  80. i = 0;
  81. ds.off('hello.world');
  82. ds.one('hello.world', 'testEvent', testHandler);
  83. ds.trigger('hello.world', 'testEvent');
  84. equal(i, 1, "One-time event triggered handler");
  85. ds.trigger('hello.world', 'testEvent');
  86. equal(i, 1, "Handler triggered no more upon one-time event");
  87. });
  88. test("Delegation", function () {
  89. var i;
  90. function testHandler() {
  91. i++;
  92. }
  93. i = 0;
  94. ds.delegate('', 'testEvent', ['hello', 'world'], testHandler);
  95. ds.trigger('hello.world', 'testEvent');
  96. equal(i, 1, "Delegated event fired when triggered on right path");
  97. ds.trigger('hello', 'testEvent');
  98. equal(i, 1, "Delegated event did not fire when triggered on wrong path");
  99. // path patterns
  100. i = 0;
  101. ds.off('', 'testEvent');
  102. ds.delegate('', 'otherEvent', ['*', 'world'], testHandler);
  103. ds.trigger('hello.world', 'otherEvent');
  104. equal(i, 1, "Pattern delegated event fired on matching node");
  105. ds.trigger('bybye.world', 'otherEvent');
  106. equal(i, 2, "Pattern delegated event fired on other matching node");
  107. });
  108. test("Getting", function () {
  109. equal(ds.get('hi').root, "There!", "Normal chained get");
  110. equal(ds.get('hi', {nochaining: true}), "There!", "No chaining with full options object");
  111. equal(ds.get('hi', {nochaining: true}), "There!", "No chaining with full options object");
  112. equal(ds.get('hi', true), "There!", "No chaining with legacy argument");
  113. });
  114. test("Access", function accessTest() {
  115. expect(11);
  116. // general access handling
  117. ds.on('', flock.ACCESS, function (event, data) {
  118. equal(event.name, flock.ACCESS, "Event name (flock.ACCESS) ok.");
  119. equal(event.name, flock.ACCESS, "Event name (flock.ACCESS) ok.");
  120. equal(event.target, 'hello.world.blahblah', "Event target ok.");
  121. equal(event.path, "", "Event path ok.");
  122. equal(typeof data.value, 'undefined', "Value ok on non-existing node");
  123. equal(data.data, 'test', "Custom data ok.");
  124. equal(data.caller, accessTest, "Caller identification ok.");
  125. });
  126. ds.get('hello.world.blahblah', {data: 'test'});
  127. ds.off('', flock.ACCESS);
  128. // access handling with repeat
  129. ds.on('not.existing.path', flock.ACCESS, function (event, data) {
  130. // fixing failure
  131. ds.set('not.existing.path', 'hello', {trigger: false});
  132. // stopping test b/c of settimout
  133. stop();
  134. // repeating original call that invoked the access event
  135. window.setTimeout(data.rerun, 10);
  136. });
  137. var i = 0;
  138. (function (arg) {
  139. equal(arg, 'test', "Caller arguments are ok.");
  140. var value = ds.get('not.existing.path').root;
  141. switch (i) {
  142. case 0:
  143. equal(typeof value, 'undefined', "Value initially doesn't exist");
  144. break;
  145. case 1:
  146. equal(value, 'hello', "Value exists after event handler ran");
  147. break;
  148. }
  149. i++;
  150. start();
  151. }('test'));
  152. ds.off('not.existing.path');
  153. });
  154. /**
  155. * Exemplifies loading data on demand and returning with it.
  156. */
  157. test("On demand loading", function () {
  158. expect(1);
  159. /**
  160. * Pretends to load data associated with a cache path.
  161. * @param path {string} Cache path.
  162. * @param handler {function} Loader handler.
  163. */
  164. function mockLoader(path, handler) {
  165. handler(path, 'blah');
  166. }
  167. // subscribing to access event
  168. ds.on('hello.world', flock.ACCESS, function (event, data) {
  169. var handler = data.data;
  170. // loading missing data
  171. mockLoader(event.target, function (path, value) {
  172. handler(event.target, value);
  173. });
  174. // preventing further event propagation
  175. return false;
  176. });
  177. // attempting to load data from non-existing node
  178. ds.get('hello.world.blahblah', function (path, value) {
  179. equal(value, 'blah', "Data node loaded and accessed on previously missing node");
  180. });
  181. ds.off('hello.world', flock.ACCESS);
  182. });
  183. test("Setting", function () {
  184. // checking handler arguments
  185. ds.on('', flock.CHANGE, function (event, data) {
  186. equal(event.name, flock.CHANGE, "Event name ok.");
  187. equal(event.target, 'hello.world.center', "Event target ok");
  188. equal(data.before, "!!", "Before value ok");
  189. equal(data.after, "!!!", "After value ok");
  190. equal(data.data, "customData", "Custom data ok");
  191. });
  192. ds.set(['hello', 'world', 'center'], "!!!", {data: "customData"});
  193. ds.off('', flock.CHANGE);
  194. var i, j;
  195. function onChange() {
  196. i++;
  197. }
  198. function onAdd() {
  199. j += 2;
  200. }
  201. ds.on('', flock.ADD, onAdd);
  202. ds.on('', flock.CHANGE, onChange);
  203. // testing data update
  204. i = 0;
  205. ds.set(['hello', 'world', 'center'], {data: "blah"});
  206. equal(i, 1, "Update triggers flock.CHANGE event");
  207. i = 0;
  208. ds.set(['hello', 'world', 'center'], "boo", {data: {foo: "bar"}});
  209. i = 0;
  210. ds.set(['hello', 'world', 'center'], "boo", {trigger: false});
  211. equal(i, 0, "Non-triggering call to event.set()");
  212. // testing data addition
  213. j = 0;
  214. ds.set(['hello', 'world', 'whatever'], "blah");
  215. equal(j, 2, "Addition triggers flock.ADD event");
  216. ds.off('', flock.ADD, onAdd);
  217. ds.off('', flock.CHANGE, onChange);
  218. });
  219. test("Unsetting", function () {
  220. var i;
  221. function onRemove() {
  222. i++;
  223. }
  224. ds.on('', flock.REMOVE, onRemove);
  225. i = 0;
  226. root.hello.world.center = "a";
  227. ds.unset(['hello', 'world', 'center']);
  228. equal(i, 1, "Unsetting triggers flock.REMOVE event");
  229. root.hello.world.center = "a";
  230. ds.unset(['hello', 'world', 'center'], {trigger: false});
  231. equal(i, 1, "Non-triggering call to event.unset()");
  232. ds.unset(['hello', 'world', 'center']);
  233. equal(i, 1, "Unsetting non-existing path doesn't trigger event");
  234. });
  235. }(
  236. flock.evented,
  237. flock.Single
  238. ));