observer.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  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('$remove (index)', function () {
  267. var emitted = false,
  268. index = ~~(Math.random() * arr.length),
  269. expected = arr[index] = { a: 1 }
  270. ob.once('mutate', function (key, array, mutation) {
  271. emitted = true
  272. assert.strictEqual(mutation.method, 'splice')
  273. assert.strictEqual(mutation.args.length, 2)
  274. assert.strictEqual(mutation.args[0], index)
  275. })
  276. var r = arr.$remove(index)
  277. assert.ok(emitted)
  278. assert.strictEqual(r, expected)
  279. })
  280. it('$remove (object)', function () {
  281. var emitted = false,
  282. index = ~~(Math.random() * arr.length),
  283. expected = arr[index] = { a: 1 }
  284. ob.once('mutate', function (key, array, mutation) {
  285. emitted = true
  286. assert.strictEqual(mutation.method, 'splice')
  287. assert.strictEqual(mutation.args.length, 2)
  288. assert.strictEqual(mutation.args[0], index)
  289. })
  290. var r = arr.$remove(expected)
  291. assert.ok(emitted)
  292. assert.strictEqual(r, expected)
  293. })
  294. it('$remove (function)', function () {
  295. var expected = [1001, 1002]
  296. arr.push.apply(arr, expected)
  297. var filter = function (e) {
  298. return e > 1000
  299. },
  300. copy = arr.filter(function (e) {
  301. return e <= 1000
  302. })
  303. var removed = arr.$remove(filter)
  304. assert.deepEqual(arr, copy)
  305. assert.deepEqual(expected, removed)
  306. })
  307. it('$replace (index)', function () {
  308. var emitted = false,
  309. index = ~~(Math.random() * arr.length),
  310. expected = arr[index] = { a: 1 },
  311. arg = 34567
  312. ob.once('mutate', function (key, array, mutation) {
  313. emitted = true
  314. assert.strictEqual(mutation.method, 'splice')
  315. assert.strictEqual(mutation.args.length, 3)
  316. assert.strictEqual(mutation.args[0], index)
  317. })
  318. var r = arr.$replace(index, arg)
  319. assert.ok(emitted)
  320. assert.strictEqual(r, expected)
  321. assert.strictEqual(arr[index], arg)
  322. })
  323. it('$replace (object)', function () {
  324. var emitted = false,
  325. index = ~~(Math.random() * arr.length),
  326. expected = arr[index] = { a: 1 },
  327. arg = 45678
  328. ob.once('mutate', function (key, array, mutation) {
  329. emitted = true
  330. assert.strictEqual(mutation.method, 'splice')
  331. assert.strictEqual(mutation.args.length, 3)
  332. assert.strictEqual(mutation.args[0], index)
  333. })
  334. var r = arr.$replace(expected, arg)
  335. assert.ok(emitted)
  336. assert.strictEqual(r, expected)
  337. assert.strictEqual(arr[index], arg)
  338. })
  339. it('$replace (function)', function () {
  340. arr[0] = 1
  341. arr[1] = 2
  342. arr[2] = 3
  343. var expected = [2, 3, 3],
  344. expectRet = [1, 2]
  345. var replaced = arr.$replace(function (e) {
  346. if (e < 3) return e + 1
  347. })
  348. assert.deepEqual(arr, expected)
  349. assert.deepEqual(replaced, expectRet)
  350. })
  351. })
  352. describe('Link/Unlink', function () {
  353. var arr = [{a:1}]
  354. Observer.convert(arr)
  355. Observer.watch(arr)
  356. it('should emit empty set when inner objects change', function () {
  357. var emitted = false
  358. arr.__emitter__.on('set', function (key) {
  359. assert.strictEqual(key, '')
  360. emitted = true
  361. })
  362. arr[0].a = 2
  363. assert.ok(emitted)
  364. arr.__emitter__.off()
  365. })
  366. it('should emit for objects added later too', function () {
  367. var emitCount = 0,
  368. a = {c:1}, b = {c:1}, c = {c:1}
  369. arr.__emitter__.on('set', function () {
  370. emitCount++
  371. })
  372. arr.push(a)
  373. arr.unshift(b)
  374. arr.splice(0, 0, c)
  375. a.c = b.c = c.c = 2
  376. assert.strictEqual(emitCount, 3)
  377. })
  378. it('should remove itself from unlinked elements', function () {
  379. var removed = arr.pop(),
  380. index = removed.__emitter__.owners.indexOf(arr)
  381. assert.strictEqual(index, -1)
  382. })
  383. })
  384. })
  385. describe('Multiple observers', function () {
  386. var ob1 = new Emitter(),
  387. ob2 = new Emitter(),
  388. obj = {a:1}
  389. Observer.observe(obj, 'test', ob1)
  390. Observer.observe(obj, 'test', ob2)
  391. var ob1Called = false,
  392. ob2Called = false
  393. ob1.on('set', function () {
  394. ob1Called = true
  395. })
  396. ob2.on('set', function () {
  397. ob2Called = true
  398. })
  399. it('should trigger events for multiple observers observing the same object', function () {
  400. obj.a = 2
  401. assert.ok(ob1Called)
  402. assert.ok(ob2Called)
  403. })
  404. })
  405. describe('.unobserve()', function () {
  406. var ob1 = new Emitter(),
  407. ob2 = new Emitter(),
  408. obj = {a:1}
  409. Observer.observe(obj, 'test', ob1)
  410. Observer.observe(obj, 'test', ob2)
  411. Observer.unobserve(obj, 'test', ob1)
  412. var ob1Called = false
  413. ob1.on('set', function () {
  414. ob1Called = true
  415. })
  416. var ob2Called = false
  417. ob2.on('set', function () {
  418. ob2Called = true
  419. })
  420. it('should set observer proxies path to null', function () {
  421. assert.strictEqual(ob1.proxies['test.'], null)
  422. })
  423. it('should turn off corresponding event listeners', function () {
  424. var callbacks = obj.__emitter__._callbacks
  425. for (var e in callbacks) {
  426. assert.strictEqual(callbacks[e].length, 1)
  427. }
  428. })
  429. it('should no longer emit events', function () {
  430. obj.a = 2
  431. assert.notOk(ob1Called)
  432. assert.ok(ob2Called)
  433. })
  434. })
  435. describe('.ensurePath()', function () {
  436. it('should ensure a path can be accessed without error', function () {
  437. var obj = {},
  438. path = 'a.b.c'
  439. Observer.ensurePath(obj, path)
  440. assert.strictEqual(obj.a.b.c, undefined)
  441. })
  442. })
  443. function setTestFactory (opts) {
  444. return function () {
  445. var ob = new Emitter(),
  446. i = 0,
  447. obj = opts.obj,
  448. expects = opts.expects
  449. Observer.observe(obj, opts.path, ob)
  450. ob.on('set', function (key, val) {
  451. var expect = expects[i]
  452. assert.strictEqual(key, expect.key)
  453. assert.strictEqual(val, expect.val)
  454. i++
  455. })
  456. expects.forEach(function (expect) {
  457. if (expect.skip) return
  458. var path = expect.key.split('.'),
  459. j = 1,
  460. data = obj
  461. while (j < path.length - 1) {
  462. data = data[path[j]]
  463. j++
  464. }
  465. data[path[j]] = expect.val
  466. })
  467. assert.strictEqual(i, expects.length)
  468. }
  469. }
  470. })