scope_spec.js 9.0 KB

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