observer.js 18 KB

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