observer.js 18 KB

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