viewmodel.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. describe('UNIT: ViewModel', function () {
  2. mock('vm-test', '{{a.b.c}}')
  3. var data = {
  4. b: {
  5. c: 12345
  6. }
  7. },
  8. arr = [1, 2, 3],
  9. vm = new Seed({
  10. el: '#vm-test',
  11. scope: {
  12. a: data,
  13. b: arr
  14. }
  15. })
  16. describe('.$get()', function () {
  17. it('should retrieve correct value', function () {
  18. assert.strictEqual(vm.$get('a.b.c'), data.b.c)
  19. })
  20. })
  21. describe('.$set()', function () {
  22. vm.$set('a.b.c', 54321)
  23. it('should set correct value', function () {
  24. assert.strictEqual(data.b.c, 54321)
  25. })
  26. })
  27. describe('.$watch()', function () {
  28. it('should trigger callback when a plain value changes', function () {
  29. var val
  30. vm.$watch('a.b.c', function (newVal) {
  31. val = newVal
  32. })
  33. data.b.c = 'new value!'
  34. assert.strictEqual(val, data.b.c)
  35. })
  36. it('should trigger callback when an object value changes', function () {
  37. var val, subVal, rootVal,
  38. target = { c: 'hohoho' }
  39. vm.$watch('a.b', function (newVal) {
  40. val = newVal
  41. })
  42. vm.$watch('a.b.c', function (newVal) {
  43. subVal = newVal
  44. })
  45. vm.$watch('a', function (newVal) {
  46. rootVal = newVal
  47. })
  48. data.b = target
  49. assert.strictEqual(val, target)
  50. assert.strictEqual(subVal, target.c)
  51. vm.a = 'hehehe'
  52. assert.strictEqual(rootVal, 'hehehe')
  53. })
  54. it('should trigger callback when an array mutates', function () {
  55. var val, mut
  56. vm.$watch('b', function (array, mutation) {
  57. val = array
  58. mut = mutation
  59. })
  60. arr.push(4)
  61. assert.strictEqual(val, arr)
  62. assert.strictEqual(mut.method, 'push')
  63. assert.strictEqual(mut.args.length, 1)
  64. assert.strictEqual(mut.args[0], 4)
  65. })
  66. })
  67. describe('.$unwatch()', function () {
  68. it('should unwatch the stuff', function () {
  69. var triggered = false
  70. vm.$watch('a.b.c', function () {
  71. triggered = true
  72. })
  73. vm.$watch('a', function () {
  74. triggered = true
  75. })
  76. vm.$watch('b', function () {
  77. triggered = true
  78. })
  79. vm.$unwatch('a')
  80. vm.$unwatch('b')
  81. vm.$unwatch('a.b.c')
  82. vm.a = { b: { c:123123 }}
  83. vm.b.push(5)
  84. assert.notOk(triggered)
  85. })
  86. })
  87. describe('.$on', function () {
  88. it('should register listener on vm\'s compiler\'s emitter', function () {
  89. var t = new Seed(),
  90. triggered = false,
  91. msg = 'on test'
  92. t.$on('test', function (m) {
  93. assert.strictEqual(m, msg)
  94. triggered = true
  95. })
  96. t.$compiler.emitter.emit('test', msg)
  97. assert.ok(triggered)
  98. })
  99. })
  100. describe('.$once', function () {
  101. it('should invoke the listener only once', function () {
  102. var t = new Seed(),
  103. triggered = 0,
  104. msg = 'on once'
  105. t.$once('test', function (m) {
  106. assert.strictEqual(m, msg)
  107. triggered++
  108. })
  109. t.$compiler.emitter.emit('test', msg)
  110. t.$compiler.emitter.emit('test', msg)
  111. assert.strictEqual(triggered, 1)
  112. })
  113. })
  114. describe('$off', function () {
  115. it('should turn off the listener', function () {
  116. var t = new Seed(),
  117. triggered1 = false,
  118. triggered2 = false,
  119. f1 = function () {
  120. triggered1 = true
  121. },
  122. f2 = function () {
  123. triggered2 = true
  124. }
  125. t.$on('test', f1)
  126. t.$on('test', f2)
  127. t.$off('test', f1)
  128. t.$compiler.emitter.emit('test')
  129. assert.notOk(triggered1)
  130. assert.ok(triggered2)
  131. })
  132. })
  133. describe('.$broadcast()', function () {
  134. it('should notify all child VMs', function () {
  135. var triggered = 0,
  136. msg = 'broadcast test'
  137. var Child = Seed.extend({
  138. init: function () {
  139. this.$on('hello', function (m) {
  140. assert.strictEqual(m, msg)
  141. triggered++
  142. })
  143. }
  144. })
  145. var Test = Seed.extend({
  146. template: '<div sd-component="test"></div><div sd-component="test"></div>',
  147. components: {
  148. test: Child
  149. }
  150. })
  151. var t = new Test()
  152. t.$broadcast('hello', msg)
  153. assert.strictEqual(triggered, 2)
  154. })
  155. })
  156. describe('.$emit', function () {
  157. it('should notify all ancestor VMs', function (done) {
  158. var topTriggered = false,
  159. midTriggered = false,
  160. msg = 'emit test'
  161. var Bottom = Seed.extend({
  162. init: function () {
  163. var self = this
  164. setTimeout(function () {
  165. self.$emit('hello', msg)
  166. assert.ok(topTriggered)
  167. assert.ok(midTriggered)
  168. done()
  169. }, 0)
  170. }
  171. })
  172. var Middle = Seed.extend({
  173. template: '<div sd-component="bottom"></div>',
  174. components: { bottom: Bottom },
  175. init: function () {
  176. this.$on('hello', function (m) {
  177. assert.strictEqual(m, msg)
  178. midTriggered = true
  179. })
  180. }
  181. })
  182. var Top = Seed.extend({
  183. template: '<div sd-component="middle"></div>',
  184. components: { middle: Middle },
  185. init: function () {
  186. this.$on('hello', function (m) {
  187. assert.strictEqual(m, msg)
  188. topTriggered = true
  189. })
  190. }
  191. })
  192. new Top()
  193. })
  194. })
  195. describe('.$destroy', function () {
  196. // since this simply delegates to Compiler.prototype.destroy(),
  197. // that's what we are actually testing here.
  198. var destroy = require('seed/src/compiler').prototype.destroy
  199. var tearDownCalled = false,
  200. observerOffCalled = false,
  201. emitterOffCalled = false,
  202. dirUnbindCalled = false,
  203. expUnbindCalled = false,
  204. bindingUnbindCalled = false,
  205. unobserveCalled = 0,
  206. elRemoved = false,
  207. externalBindingUnbindCalled = false
  208. var dirMock = {
  209. binding: {
  210. compiler: null,
  211. instances: []
  212. },
  213. unbind: function () {
  214. dirUnbindCalled = true
  215. }
  216. }
  217. dirMock.binding.instances.push(dirMock)
  218. var bindingsMock = Object.create({
  219. 'test2': {
  220. unbind: function () {
  221. externalBindingUnbindCalled = true
  222. }
  223. }
  224. })
  225. bindingsMock.test = {
  226. root: true,
  227. key: 'test',
  228. value: {
  229. __observer__: {
  230. off: function () {
  231. unobserveCalled++
  232. return this
  233. }
  234. }
  235. },
  236. unbind: function () {
  237. bindingUnbindCalled = true
  238. }
  239. }
  240. var compilerMock = {
  241. options: {
  242. teardown: function () {
  243. tearDownCalled = true
  244. }
  245. },
  246. observer: {
  247. off: function () {
  248. observerOffCalled = true
  249. },
  250. proxies: {
  251. 'test.': {}
  252. }
  253. },
  254. emitter: {
  255. off: function () {
  256. emitterOffCalled = true
  257. }
  258. },
  259. dirs: [dirMock],
  260. exps: [{
  261. unbind: function () {
  262. expUnbindCalled = true
  263. }
  264. }],
  265. bindings: bindingsMock,
  266. childId: 'test',
  267. parentCompiler: {
  268. childCompilers: [],
  269. vm: {
  270. $: {
  271. 'test': true
  272. }
  273. }
  274. },
  275. el: {
  276. getAttribute: function () {},
  277. parentNode: {
  278. removeChild: function () {
  279. elRemoved = true
  280. }
  281. }
  282. }
  283. }
  284. compilerMock.parentCompiler.childCompilers.push(compilerMock)
  285. destroy.call(compilerMock)
  286. it('should call the teardown option', function () {
  287. assert.ok(tearDownCalled)
  288. })
  289. it('should turn observer and emitter off', function () {
  290. assert.ok(observerOffCalled)
  291. assert.ok(emitterOffCalled)
  292. })
  293. it('should unbind all directives', function () {
  294. assert.ok(dirUnbindCalled)
  295. })
  296. it('should remove directives from external bindings', function () {
  297. assert.strictEqual(dirMock.binding.instances.indexOf(dirMock), -1)
  298. })
  299. it('should unbind all expressions', function () {
  300. assert.ok(expUnbindCalled)
  301. })
  302. it('should unbind and unobserve own bindings', function () {
  303. assert.ok(bindingUnbindCalled)
  304. assert.strictEqual(unobserveCalled, 3)
  305. })
  306. it('should not unbind external bindings', function () {
  307. assert.notOk(externalBindingUnbindCalled)
  308. })
  309. it('should remove self from parentCompiler', function () {
  310. var parent = compilerMock.parentCompiler
  311. assert.ok(parent.childCompilers.indexOf(compilerMock), -1)
  312. assert.strictEqual(parent.vm.$[compilerMock.childId], undefined)
  313. })
  314. it('should remove the dom element', function () {
  315. assert.ok(elRemoved)
  316. })
  317. })
  318. })