observer.js 17 KB

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