observer.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. describe('UNIT: Observer', function () {
  2. var Observer = require('vue/src/observer'),
  3. Emitter = require('emitter')
  4. describe('Observing Object', function () {
  5. it('should not watch a ViewModel instance', function () {
  6. var obj = new Vue(), ob = new Emitter()
  7. Observer.observe(obj, 'test', ob)
  8. assert.notOk(obj.__observer__)
  9. })
  10. it('should attach hidden observer and values to the object', function () {
  11. var obj = {}, ob = new Emitter()
  12. Observer.observe(obj, 'test', ob)
  13. assert.ok(obj.__observer__ instanceof Emitter)
  14. assert.ok(obj.__observer__.values)
  15. })
  16. it('should emit set events with correct path', setTestFactory({
  17. obj: { a: 1, b: { c: 2 } },
  18. expects: [
  19. { key: 'test.a', val: 1 },
  20. { key: 'test.b.c', val: 3 }
  21. ],
  22. path: 'test'
  23. }))
  24. it('should emit multiple events when a nested object is set', setTestFactory({
  25. obj: { a: 1, b: { c: 2 } },
  26. expects: [
  27. { key: 'test.b', val: { c: 3 } },
  28. { key: 'test.b.c', val: 3, skip: true }
  29. ],
  30. path: 'test'
  31. }))
  32. it('should emit get events on tip values', function () {
  33. Observer.shouldGet = true
  34. getTestFactory({
  35. obj: { a: 1, b: { c: 2 } },
  36. expects: [
  37. 'test.a',
  38. 'test.b.c'
  39. ],
  40. path: 'test'
  41. })()
  42. Observer.shouldGet = false
  43. })
  44. it('should emit set when first observing', function () {
  45. var obj = { a: 1, b: { c: 2} },
  46. ob = new Emitter(), i = 0
  47. var expects = [
  48. { key: 'test.a', val: obj.a },
  49. { key: 'test.b', val: obj.b },
  50. { key: 'test.b.c', val: obj.b.c }
  51. ]
  52. ob.on('set', function (key, val) {
  53. var exp = expects[i]
  54. assert.strictEqual(key, exp.key)
  55. assert.strictEqual(val, exp.val)
  56. i++
  57. })
  58. Observer.observe(obj, 'test', ob)
  59. assert.strictEqual(i, expects.length)
  60. })
  61. it('should emit set when watching an already observed object', function () {
  62. var obj = { a: 1, b: { c: 2} },
  63. ob1 = new Emitter(),
  64. ob2 = new Emitter(),
  65. i = 0
  66. Observer.observe(obj, 'test', ob1) // watch first time
  67. var expects = [
  68. { key: 'test.a', val: obj.a },
  69. { key: 'test.b', val: obj.b },
  70. { key: 'test.b.c', val: obj.b.c }
  71. ]
  72. ob2.on('set', function (key, val) {
  73. var exp = expects[i]
  74. assert.strictEqual(key, exp.key)
  75. assert.strictEqual(val, exp.val)
  76. i++
  77. })
  78. Observer.observe(obj, 'test', ob2) // watch again
  79. assert.strictEqual(i, expects.length)
  80. })
  81. })
  82. describe('Observing Array', function () {
  83. var arr = [],
  84. ob = new Emitter()
  85. Observer.observe(arr, 'test', ob)
  86. it('should attach the hidden observer', function () {
  87. assert.ok(arr.__observer__ instanceof Emitter)
  88. })
  89. it('should overwrite the native array mutator methods', function () {
  90. ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(function (method) {
  91. assert.notStrictEqual(arr[method], Array.prototype[method])
  92. })
  93. })
  94. it('should emit set for .length when it mutates', function () {
  95. var emitted = false
  96. ob.once('set', function (key, val) {
  97. assert.strictEqual(key, 'test.length')
  98. assert.strictEqual(val, 1)
  99. emitted = true
  100. })
  101. arr.push(1)
  102. assert.ok(emitted)
  103. })
  104. describe('Mutator Methods', function () {
  105. it('push', function () {
  106. var arg1 = 123,
  107. arg2 = 234,
  108. emitted = false
  109. ob.once('mutate', function (key, array, mutation) {
  110. assert.strictEqual(key, 'test')
  111. assert.strictEqual(array, arr)
  112. assert.strictEqual(array.length, 3)
  113. assert.strictEqual(mutation.method, 'push')
  114. assert.strictEqual(mutation.args.length, 2)
  115. assert.strictEqual(mutation.args[0], arg1)
  116. assert.strictEqual(mutation.args[1], arg2)
  117. assert.strictEqual(mutation.result, arr.length)
  118. emitted = true
  119. })
  120. var r = arr.push(arg1, arg2)
  121. assert.ok(emitted)
  122. assert.strictEqual(r, arr.length)
  123. })
  124. it('pop', function () {
  125. var emitted = false,
  126. expected = arr[arr.length - 1]
  127. ob.once('mutate', function (key, array, mutation) {
  128. assert.strictEqual(key, 'test')
  129. assert.strictEqual(array, arr)
  130. assert.strictEqual(array.length, 2)
  131. assert.strictEqual(mutation.method, 'pop')
  132. assert.strictEqual(mutation.args.length, 0)
  133. assert.strictEqual(mutation.result, expected)
  134. emitted = true
  135. })
  136. var r = arr.pop()
  137. assert.ok(emitted)
  138. assert.strictEqual(r, expected)
  139. })
  140. it('shift', function () {
  141. var emitted = false,
  142. expected = arr[0]
  143. ob.once('mutate', function (key, array, mutation) {
  144. assert.strictEqual(key, 'test')
  145. assert.strictEqual(array, arr)
  146. assert.strictEqual(array.length, 1)
  147. assert.strictEqual(mutation.method, 'shift')
  148. assert.strictEqual(mutation.args.length, 0)
  149. assert.strictEqual(mutation.result, expected)
  150. emitted = true
  151. })
  152. var r = arr.shift()
  153. assert.ok(emitted)
  154. assert.strictEqual(r, expected)
  155. })
  156. it('unshift', function () {
  157. var emitted = false,
  158. arg1 = 456,
  159. arg2 = 678
  160. ob.once('mutate', function (key, array, mutation) {
  161. assert.strictEqual(key, 'test')
  162. assert.strictEqual(array, arr)
  163. assert.strictEqual(array.length, 3)
  164. assert.strictEqual(mutation.method, 'unshift')
  165. assert.strictEqual(mutation.args.length, 2)
  166. assert.strictEqual(mutation.args[0], arg1)
  167. assert.strictEqual(mutation.args[1], arg2)
  168. assert.strictEqual(mutation.result, arr.length)
  169. emitted = true
  170. })
  171. var r = arr.unshift(arg1, arg2)
  172. assert.ok(emitted)
  173. assert.strictEqual(r, arr.length)
  174. })
  175. it('splice', function () {
  176. var emitted = false,
  177. arg1 = 789,
  178. arg2 = 910,
  179. expected = arr[1]
  180. ob.once('mutate', function (key, array, mutation) {
  181. assert.strictEqual(key, 'test')
  182. assert.strictEqual(array, arr)
  183. assert.strictEqual(array.length, 4)
  184. assert.strictEqual(mutation.method, 'splice')
  185. assert.strictEqual(mutation.args.length, 4)
  186. assert.strictEqual(mutation.args[0], 1)
  187. assert.strictEqual(mutation.args[1], 1)
  188. assert.strictEqual(mutation.args[2], arg1)
  189. assert.strictEqual(mutation.args[3], arg2)
  190. assert.strictEqual(mutation.result.length, 1)
  191. assert.strictEqual(mutation.result[0], expected)
  192. emitted = true
  193. })
  194. var r = arr.splice(1, 1, arg1, arg2)
  195. assert.ok(emitted)
  196. assert.strictEqual(r.length, 1)
  197. assert.strictEqual(r[0], expected)
  198. })
  199. it('sort', function () {
  200. var emitted = false,
  201. sorter = function (a, b) {
  202. return a > b ? -1 : 1
  203. },
  204. copy = arr.slice().sort(sorter)
  205. ob.once('mutate', function (key, array, mutation) {
  206. assert.strictEqual(key, 'test')
  207. assert.strictEqual(array, arr)
  208. assert.strictEqual(mutation.method, 'sort')
  209. assert.strictEqual(mutation.args.length, 1)
  210. assert.strictEqual(mutation.result, arr)
  211. for (var i = 0; i < copy.length; i++) {
  212. assert.strictEqual(array[i], copy[i])
  213. }
  214. emitted = true
  215. })
  216. var r = arr.sort(sorter)
  217. assert.ok(emitted)
  218. assert.strictEqual(r, arr)
  219. })
  220. it('reverse', function () {
  221. var emitted = false,
  222. copy = arr.slice().reverse()
  223. ob.once('mutate', function (key, array, mutation) {
  224. assert.strictEqual(key, 'test')
  225. assert.strictEqual(array, arr)
  226. assert.strictEqual(mutation.method, 'reverse')
  227. assert.strictEqual(mutation.args.length, 0)
  228. assert.strictEqual(mutation.result, arr)
  229. for (var i = 0; i < copy.length; i++) {
  230. assert.strictEqual(array[i], copy[i])
  231. }
  232. emitted = true
  233. })
  234. var r = arr.reverse()
  235. assert.ok(emitted)
  236. assert.strictEqual(r, arr)
  237. })
  238. })
  239. describe('Augmentations', function () {
  240. it('remove (index)', function () {
  241. var emitted = false,
  242. index = ~~(Math.random() * arr.length),
  243. expected = arr[index] = { a: 1 }
  244. ob.once('mutate', function (key, array, mutation) {
  245. emitted = true
  246. assert.strictEqual(mutation.method, 'splice')
  247. assert.strictEqual(mutation.args.length, 2)
  248. assert.strictEqual(mutation.args[0], index)
  249. })
  250. var r = arr.remove(index)
  251. assert.ok(emitted)
  252. assert.strictEqual(r, expected)
  253. })
  254. it('remove (object)', function () {
  255. var emitted = false,
  256. index = ~~(Math.random() * arr.length),
  257. expected = arr[index] = { a: 1 }
  258. ob.once('mutate', function (key, array, mutation) {
  259. emitted = true
  260. assert.strictEqual(mutation.method, 'splice')
  261. assert.strictEqual(mutation.args.length, 2)
  262. assert.strictEqual(mutation.args[0], index)
  263. })
  264. var r = arr.remove(expected)
  265. assert.ok(emitted)
  266. assert.strictEqual(r, expected)
  267. })
  268. it('remove (function)', function () {
  269. var expected = [1001, 1002]
  270. arr.push.apply(arr, expected)
  271. var filter = function (e) {
  272. return e > 1000
  273. },
  274. copy = arr.filter(function (e) {
  275. return e <= 1000
  276. })
  277. var removed = arr.remove(filter)
  278. assert.deepEqual(arr, copy)
  279. assert.deepEqual(expected, removed)
  280. })
  281. it('replace (index)', function () {
  282. var emitted = false,
  283. index = ~~(Math.random() * arr.length),
  284. expected = arr[index] = { a: 1 },
  285. arg = 34567
  286. ob.once('mutate', function (key, array, mutation) {
  287. emitted = true
  288. assert.strictEqual(mutation.method, 'splice')
  289. assert.strictEqual(mutation.args.length, 3)
  290. assert.strictEqual(mutation.args[0], index)
  291. })
  292. var r = arr.replace(index, arg)
  293. assert.ok(emitted)
  294. assert.strictEqual(r, expected)
  295. assert.strictEqual(arr[index], arg)
  296. })
  297. it('replace (object)', function () {
  298. var emitted = false,
  299. index = ~~(Math.random() * arr.length),
  300. expected = arr[index] = { a: 1 },
  301. arg = 45678
  302. ob.once('mutate', function (key, array, mutation) {
  303. emitted = true
  304. assert.strictEqual(mutation.method, 'splice')
  305. assert.strictEqual(mutation.args.length, 3)
  306. assert.strictEqual(mutation.args[0], index)
  307. })
  308. var r = arr.replace(expected, arg)
  309. assert.ok(emitted)
  310. assert.strictEqual(r, expected)
  311. assert.strictEqual(arr[index], arg)
  312. })
  313. it('replace (function)', function () {
  314. arr[0] = 1
  315. arr[1] = 2
  316. arr[2] = 3
  317. var expected = [2, 3, 3],
  318. expectRet = [1, 2]
  319. var replaced = arr.replace(function (e) {
  320. if (e < 3) return e + 1
  321. })
  322. assert.deepEqual(arr, expected)
  323. assert.deepEqual(replaced, expectRet)
  324. })
  325. })
  326. })
  327. describe('Multiple observers', function () {
  328. var ob1 = new Emitter(),
  329. ob2 = new Emitter(),
  330. obj = {a:1}
  331. Observer.observe(obj, 'test', ob1)
  332. Observer.observe(obj, 'test', ob2)
  333. var ob1Called = false,
  334. ob2Called = false
  335. ob1.on('set', function () {
  336. ob1Called = true
  337. })
  338. ob2.on('set', function () {
  339. ob2Called = true
  340. })
  341. it('should trigger events for multiple observers observing the same object', function () {
  342. obj.a = 2
  343. assert.ok(ob1Called)
  344. assert.ok(ob2Called)
  345. })
  346. })
  347. describe('.unobserve()', function () {
  348. var ob1 = new Emitter(),
  349. ob2 = new Emitter(),
  350. obj = {a:1}
  351. Observer.observe(obj, 'test', ob1)
  352. Observer.observe(obj, 'test', ob2)
  353. Observer.unobserve(obj, 'test', ob1)
  354. var ob1Called = false
  355. ob1.on('set', function () {
  356. ob1Called = true
  357. })
  358. var ob2Called = false
  359. ob2.on('set', function () {
  360. ob2Called = true
  361. })
  362. it('should set observer proxies path to null', function () {
  363. assert.strictEqual(ob1.proxies['test.'], null)
  364. })
  365. it('should turn off corresponding event listeners', function () {
  366. var callbacks = obj.__observer__._callbacks
  367. for (var e in callbacks) {
  368. assert.strictEqual(callbacks[e].length, 1)
  369. }
  370. })
  371. it('should no longer emit events', function () {
  372. obj.a = 2
  373. assert.notOk(ob1Called)
  374. assert.ok(ob2Called)
  375. })
  376. })
  377. describe('.ensurePath()', function () {
  378. it('should ensure a path can be accessed without error', function () {
  379. var obj = {},
  380. path = 'a.b.c'
  381. Observer.ensurePath(obj, path)
  382. assert.strictEqual(obj.a.b.c, undefined)
  383. })
  384. })
  385. // describe('.copyPaths()', function () {
  386. // it('should ensure path for all paths that start with the given key', function () {
  387. // var key = 'a',
  388. // obj = {},
  389. // paths = {
  390. // 'a.b.c': 1,
  391. // 'a.d': 2,
  392. // 'e.f': 3,
  393. // 'g': 4
  394. // }
  395. // Observer.ensurePaths(key, obj, paths)
  396. // assert.strictEqual(obj.b.c, undefined)
  397. // assert.strictEqual(obj.d, undefined)
  398. // assert.notOk('f' in obj)
  399. // assert.strictEqual(Object.keys(obj).length, 2)
  400. // })
  401. // })
  402. function setTestFactory (opts) {
  403. return function () {
  404. var ob = new Emitter(),
  405. i = 0,
  406. obj = opts.obj,
  407. expects = opts.expects
  408. Observer.observe(obj, opts.path, ob)
  409. ob.on('set', function (key, val) {
  410. var expect = expects[i]
  411. assert.strictEqual(key, expect.key)
  412. assert.strictEqual(val, expect.val)
  413. i++
  414. })
  415. expects.forEach(function (expect) {
  416. if (expect.skip) return
  417. var path = expect.key.split('.'),
  418. j = 1,
  419. data = obj
  420. while (j < path.length - 1) {
  421. data = data[path[j]]
  422. j++
  423. }
  424. data[path[j]] = expect.val
  425. })
  426. assert.strictEqual(i, expects.length)
  427. }
  428. }
  429. function getTestFactory (opts) {
  430. return function () {
  431. var ob = new Emitter(),
  432. i = 0,
  433. obj = opts.obj,
  434. expects = opts.expects
  435. Observer.observe(obj, opts.path, ob)
  436. ob.on('get', function (key) {
  437. var expected = expects[i]
  438. assert.strictEqual(key, expected)
  439. i++
  440. })
  441. expects.forEach(function (key) {
  442. var path = key.split('.'),
  443. j = 1,
  444. data = obj
  445. while (j < path.length) {
  446. data = data[path[j]]
  447. j++
  448. }
  449. })
  450. assert.strictEqual(i, expects.length)
  451. }
  452. }
  453. })