2
0

scope_spec.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. /**
  2. * Test property proxy, scope inheritance,
  3. * data event propagation and data sync
  4. */
  5. var Vue = require('../../../../src/vue')
  6. var Observer = require('../../../../src/observe/observer')
  7. Observer.pathDelimiter = '.'
  8. function mockBinding () {
  9. return {
  10. _notify: jasmine.createSpy('binding')
  11. }
  12. }
  13. describe('Scope', function () {
  14. describe('basic', function () {
  15. var vm = new Vue({
  16. data: {
  17. a: 1,
  18. b: {
  19. c: 2
  20. }
  21. }
  22. })
  23. it('should proxy data properties', function () {
  24. expect(vm.a).toBe(vm.$data.a)
  25. expect(vm.b).toBe(vm.$data.b)
  26. })
  27. it('should trigger set events', function () {
  28. var spy = jasmine.createSpy('basic')
  29. vm.$observer.on('set', spy)
  30. // simple
  31. vm.a = 2
  32. expect(spy.calls.count()).toBe(1)
  33. expect(spy).toHaveBeenCalledWith('a', 2, undefined, undefined)
  34. // nested path
  35. vm.b.c = 3
  36. expect(spy.calls.count()).toBe(2)
  37. expect(spy).toHaveBeenCalledWith('b.c', 3, undefined, undefined)
  38. })
  39. it('should trigger add/delete events', function () {
  40. var spy = jasmine.createSpy('instantiation')
  41. vm.$observer
  42. .on('add', spy)
  43. .on('delete', spy)
  44. // add
  45. vm.$add('c', 123)
  46. expect(spy.calls.count()).toBe(1)
  47. expect(spy).toHaveBeenCalledWith('c', 123, undefined, undefined)
  48. // delete
  49. vm.$delete('c')
  50. expect(spy.calls.count()).toBe(2)
  51. expect(spy).toHaveBeenCalledWith('c', undefined, undefined, undefined)
  52. // meta
  53. vm._defineMeta('$index', 1)
  54. expect(spy.calls.count()).toBe(3)
  55. expect(spy).toHaveBeenCalledWith('$index', 1, undefined, undefined)
  56. })
  57. })
  58. describe('data sync', function () {
  59. var data = {
  60. a: 1,
  61. b: {
  62. c: 2
  63. }
  64. }
  65. var vm = new Vue({
  66. data: data
  67. })
  68. it('should retain data reference', function () {
  69. expect(vm.$data).toBe(data)
  70. })
  71. it('should sync set', function () {
  72. // vm -> data
  73. vm.a = 2
  74. expect(data.a).toBe(2)
  75. // data -> vm
  76. data.b = {d:3}
  77. expect(vm.b).toBe(data.b)
  78. })
  79. it('should sync add', function () {
  80. // vm -> data
  81. vm.$add('c', 123)
  82. expect(data.c).toBe(123)
  83. // data -> vm
  84. data.$add('d', 456)
  85. expect(vm.d).toBe(456)
  86. })
  87. it('should sync delete', function () {
  88. // vm -> data
  89. vm.$delete('d')
  90. expect(data.hasOwnProperty('d')).toBe(false)
  91. // data -> vm
  92. data.$delete('c')
  93. expect(vm.hasOwnProperty('c')).toBe(false)
  94. })
  95. })
  96. describe('inheritance', function () {
  97. var parent = new Vue({
  98. data: {
  99. a: 'parent a',
  100. b: { c: 2 },
  101. c: 'parent c',
  102. arr: [{a:1},{a:2}]
  103. }
  104. })
  105. var child = parent.$addChild({
  106. data: {
  107. a: 'child a'
  108. }
  109. })
  110. it('child should inherit parent data on scope', function () {
  111. expect(child.b).toBe(parent.b) // object
  112. expect(child.c).toBe(parent.c) // primitive value
  113. })
  114. it('child should shadow parent property with same key', function () {
  115. expect(parent.a).toBe('parent a')
  116. expect(child.a).toBe('child a')
  117. })
  118. it('setting scope properties on child should affect parent', function () {
  119. child.c = 'modified by child'
  120. expect(parent.c).toBe('modified by child')
  121. })
  122. it('parent event should propagate when child has same binding', function () {
  123. // object path
  124. var b = child._bindings['b.c'] = mockBinding()
  125. parent.b.c = 3
  126. expect(b._notify).toHaveBeenCalled()
  127. // array path
  128. b = child._bindings['arr.0.a'] = mockBinding()
  129. parent.arr[0].a = 2
  130. expect(b._notify).toHaveBeenCalled()
  131. // add
  132. b = child._bindings['e'] = mockBinding()
  133. parent.$add('e', 123)
  134. expect(b._notify).toHaveBeenCalled()
  135. // delete
  136. b = child._bindings['e'] = mockBinding()
  137. parent.$delete('e')
  138. expect(b._notify).toHaveBeenCalled()
  139. })
  140. it('parent event should not propagate when child has shadowing key', function () {
  141. var b = child._bindings['c'] = mockBinding()
  142. child.$add('c', 123)
  143. expect(b._notify.calls.count()).toBe(1)
  144. parent.c = 456
  145. expect(b._notify.calls.count()).toBe(1)
  146. })
  147. })
  148. describe('inheritance with data sync on parent data', function () {
  149. var parent = new Vue({
  150. data: {
  151. arr: [{a:1},{a:2}]
  152. }
  153. })
  154. var child = parent.$addChild({
  155. data: parent.arr[0]
  156. })
  157. it('should trigger proper events', function () {
  158. var parentSpy = parent._bindings['arr.0.a'] = mockBinding()
  159. var childSpy = child._bindings['arr.0.a'] = mockBinding()
  160. var childSpy2 = child._bindings['a'] = mockBinding()
  161. child.a = 3
  162. // make sure data sync is working
  163. expect(parent.arr[0].a).toBe(3)
  164. expect(parentSpy._notify).toHaveBeenCalled()
  165. expect(childSpy._notify).toHaveBeenCalled()
  166. expect(childSpy2._notify).toHaveBeenCalled()
  167. })
  168. })
  169. describe('swapping $data', function () {
  170. var oldData = { a: 1, c: 4 }
  171. var newData = { a: 2, b: 3 }
  172. var vm = new Vue({
  173. data: oldData
  174. })
  175. var vmSpy = jasmine.createSpy('vm')
  176. var vmAddSpy = jasmine.createSpy('vmAdd')
  177. var oldDataSpy = jasmine.createSpy('oldData')
  178. vm.$observer.on('set', vmSpy)
  179. vm.$observer.on('add', vmAddSpy)
  180. oldData.__ob__.on('set', oldDataSpy)
  181. vm.$data = newData
  182. it('should sync new data', function () {
  183. expect(vm._data).toBe(newData)
  184. expect(vm.a).toBe(2)
  185. expect(vm.b).toBe(3)
  186. expect(vmSpy).toHaveBeenCalledWith('a', 2, undefined, undefined)
  187. expect(vmAddSpy).toHaveBeenCalledWith('b', 3, undefined, undefined)
  188. })
  189. it('should unsync old data', function () {
  190. expect(vm.hasOwnProperty('c')).toBe(false)
  191. vm.a = 3
  192. expect(oldDataSpy.calls.count()).toBe(0)
  193. expect(oldData.a).toBe(1)
  194. expect(newData.a).toBe(3)
  195. })
  196. })
  197. describe('scope teardown', function () {
  198. var parent = new Vue({
  199. data: {
  200. a: 123
  201. }
  202. })
  203. var child = new Vue({
  204. parent: parent
  205. })
  206. var spy = jasmine.createSpy('teardown')
  207. child.$observer.on('set', spy)
  208. it('should stop relaying parent events', function () {
  209. child._teardownScope()
  210. parent.a = 234
  211. expect(spy.calls.count()).toBe(0)
  212. expect(child._data).toBeNull()
  213. })
  214. })
  215. describe('computed', function () {
  216. var vm = new Vue({
  217. data: {
  218. a: 'a',
  219. b: 'b'
  220. },
  221. computed: {
  222. c: function () {
  223. expect(this).toBe(vm)
  224. return this.a + this.b
  225. },
  226. d: {
  227. get: function () {
  228. expect(this).toBe(vm)
  229. return this.a + this.b
  230. },
  231. set: function (newVal) {
  232. expect(this).toBe(vm)
  233. var vals = newVal.split(' ')
  234. this.a = vals[0]
  235. this.b = vals[1]
  236. }
  237. }
  238. }
  239. })
  240. it('get', function () {
  241. expect(vm.c).toBe('ab')
  242. expect(vm.d).toBe('ab')
  243. })
  244. it('set', function () {
  245. vm.c = 123 // should do nothing
  246. vm.d = 'c d'
  247. expect(vm.a).toBe('c')
  248. expect(vm.b).toBe('d')
  249. expect(vm.c).toBe('cd')
  250. expect(vm.d).toBe('cd')
  251. })
  252. it('inherit', function () {
  253. var child = vm.$addChild()
  254. expect(child.c).toBe('cd')
  255. child.d = 'e f'
  256. expect(vm.a).toBe('e')
  257. expect(vm.b).toBe('f')
  258. expect(vm.c).toBe('ef')
  259. expect(vm.d).toBe('ef')
  260. expect(child.a).toBe('e')
  261. expect(child.b).toBe('f')
  262. expect(child.c).toBe('ef')
  263. expect(child.d).toBe('ef')
  264. })
  265. })
  266. describe('methods', function () {
  267. it('should work and have correct context', function () {
  268. var vm = new Vue({
  269. data: {
  270. a: 1
  271. },
  272. methods: {
  273. test: function () {
  274. expect(this instanceof Vue).toBe(true)
  275. return this.a
  276. }
  277. }
  278. })
  279. expect(vm.test()).toBe(1)
  280. var child = vm.$addChild()
  281. expect(child.test()).toBe(1)
  282. })
  283. })
  284. })