lifecycle_spec.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. var Vue = require('src')
  2. var compiler = require('src/compiler')
  3. describe('Lifecycle API', function () {
  4. describe('$mount', function () {
  5. var el, frag
  6. beforeEach(function () {
  7. el = document.createElement('div')
  8. el.textContent = '{{test}}'
  9. frag = document.createDocumentFragment()
  10. frag.appendChild(el)
  11. })
  12. it('normal', function () {
  13. var vm = new Vue({
  14. data: {
  15. test: 'foo'
  16. }
  17. })
  18. vm.$mount(el)
  19. expect(vm.$el).toBe(el)
  20. expect(el.__vue__).toBe(vm)
  21. expect(el.textContent).toBe('foo')
  22. })
  23. it('auto-create', function () {
  24. var vm = new Vue({
  25. template: '{{a}}',
  26. data: {
  27. a: 123
  28. }
  29. })
  30. vm.$mount()
  31. expect(vm.$el).toBeTruthy()
  32. expect(vm.$el.tagName).toBe('DIV')
  33. expect(vm.$el.textContent).toBe('123')
  34. })
  35. it('selector', function () {
  36. el.id = 'mount-test'
  37. document.body.appendChild(el)
  38. var vm = new Vue({
  39. data: { test: 'foo' }
  40. })
  41. vm.$mount('#mount-test')
  42. expect(vm.$el).toBe(el)
  43. expect(el.__vue__).toBe(vm)
  44. expect(el.textContent).toBe('foo')
  45. document.body.removeChild(el)
  46. })
  47. it('warn invalid selector', function () {
  48. var vm = new Vue()
  49. vm.$mount('#none-exist')
  50. expect('Cannot find element').toHaveBeenWarned()
  51. })
  52. it('replace', function () {
  53. el.className = 'replace-test'
  54. document.body.appendChild(el)
  55. var vm = new Vue({
  56. replace: true,
  57. data: { test: 'foo' },
  58. template: '<div>{{test}}</div>'
  59. })
  60. vm.$mount(el)
  61. expect(vm.$el).not.toBe(el)
  62. expect(vm.$el.textContent).toBe('foo')
  63. expect(document.body.contains(el)).toBe(false)
  64. expect(document.body.lastChild).toBe(vm.$el)
  65. expect(vm.$el.className).toBe('replace-test')
  66. document.body.removeChild(vm.$el)
  67. })
  68. it('precompiled linker', function () {
  69. var linker = compiler.compile(el, Vue.options)
  70. var vm = new Vue({
  71. _linker: linker,
  72. data: {
  73. test: 'foo'
  74. }
  75. })
  76. vm.$mount(el)
  77. expect(vm.$el).toBe(el)
  78. expect(el.__vue__).toBe(vm)
  79. expect(el.textContent).toBe('foo')
  80. })
  81. it('mount to fragment', function () {
  82. var vm = new Vue({
  83. data: { test: 'frag' }
  84. })
  85. vm.$mount(frag)
  86. expect(vm._fragment).toBe(frag)
  87. expect(vm.$el.nextSibling.textContent).toBe('frag')
  88. })
  89. it('replace fragment', function () {
  90. document.body.appendChild(el)
  91. var vm = new Vue({
  92. replace: true,
  93. data: { test: 'foo' },
  94. template: '<div>{{test}}</div><div>{{test + "bar"}}</div>'
  95. })
  96. vm.$mount(el)
  97. expect(vm.$el).not.toBe(el)
  98. expect(vm.$el.nextSibling.textContent).toBe('foo')
  99. expect(vm.$el.nextSibling.nextSibling.textContent).toBe('foobar')
  100. expect(document.body.contains(el)).toBe(false)
  101. expect(document.body.lastChild).toBe(vm._fragmentEnd)
  102. vm.$remove()
  103. })
  104. it('hooks', function () {
  105. var hooks = ['created', 'beforeCompile', 'compiled', 'attached', 'ready']
  106. var options = {
  107. data: {
  108. test: 'foo'
  109. }
  110. }
  111. hooks.forEach(function (hook) {
  112. options[hook] = jasmine.createSpy(hook)
  113. })
  114. var vm = new Vue(options)
  115. expect(options.created).toHaveBeenCalled()
  116. expect(options.beforeCompile).not.toHaveBeenCalled()
  117. vm.$mount(el)
  118. expect(options.beforeCompile).toHaveBeenCalled()
  119. expect(options.compiled).toHaveBeenCalled()
  120. expect(options.attached).not.toHaveBeenCalled()
  121. expect(options.ready).not.toHaveBeenCalled()
  122. vm.$appendTo(document.body)
  123. expect(options.attached).toHaveBeenCalled()
  124. expect(options.ready).toHaveBeenCalled()
  125. vm.$remove()
  126. })
  127. it('warn against multiple calls', function () {
  128. var vm = new Vue({
  129. el: el
  130. })
  131. vm.$mount(el)
  132. expect('$mount() should be called only once').toHaveBeenWarned()
  133. })
  134. })
  135. describe('$destroy', function () {
  136. it('normal', function () {
  137. var vm = new Vue()
  138. expect(vm._isDestroyed).toBe(false)
  139. var data = vm._data
  140. expect(data.__ob__.vms.length).toBe(1)
  141. vm.$destroy()
  142. expect(data.__ob__.vms.length).toBe(0)
  143. expect(vm._isDestroyed).toBe(true)
  144. expect(vm._watchers).toBeNull()
  145. expect(vm.$el).toBeNull()
  146. expect(vm.$parent).toBeNull()
  147. expect(vm.$root).toBeNull()
  148. expect(vm.$children).toBeNull()
  149. expect(vm._directives).toBeNull()
  150. expect(Object.keys(vm._events).length).toBe(0)
  151. })
  152. it('remove element', function () {
  153. var el = document.createElement('div')
  154. var parent = document.createElement('div')
  155. parent.appendChild(el)
  156. var vm = new Vue({ el: el })
  157. vm.$destroy(true)
  158. expect(parent.childNodes.length).toBe(0)
  159. expect(el.__vue__).toBeNull()
  160. })
  161. it('hooks', function () {
  162. var opts = {
  163. beforeDestroy: jasmine.createSpy(),
  164. destroyed: jasmine.createSpy(),
  165. detached: jasmine.createSpy()
  166. }
  167. var el = opts.el = document.createElement('div')
  168. document.body.appendChild(el)
  169. var vm = new Vue(opts)
  170. vm.$destroy(true)
  171. expect(opts.beforeDestroy).toHaveBeenCalled()
  172. expect(opts.destroyed).toHaveBeenCalled()
  173. expect(opts.detached).toHaveBeenCalled()
  174. })
  175. // #1966
  176. it('grandchild hooks', function () {
  177. var grandChildBeforeDestroy = jasmine.createSpy()
  178. var grandChildDestroyed = jasmine.createSpy()
  179. var grandChildDetached = jasmine.createSpy()
  180. var opts = {
  181. template: '<div><test></test></div>',
  182. components: {
  183. test: {
  184. template: '<div><test-inner></test-inner></div>',
  185. components: {
  186. 'test-inner': {
  187. beforeDestroy: grandChildBeforeDestroy,
  188. destroyed: grandChildDestroyed,
  189. detached: grandChildDetached
  190. }
  191. }
  192. }
  193. }
  194. }
  195. var el = opts.el = document.createElement('div')
  196. document.body.appendChild(el)
  197. var vm = new Vue(opts)
  198. vm.$destroy(true)
  199. expect(grandChildBeforeDestroy).toHaveBeenCalled()
  200. expect(grandChildDestroyed).toHaveBeenCalled()
  201. expect(grandChildDetached).toHaveBeenCalled()
  202. })
  203. it('parent', function () {
  204. var parent = new Vue()
  205. var child = new Vue({ parent: parent })
  206. var child2 = new Vue({ parent: parent })
  207. expect(parent.$children.length).toBe(2)
  208. child.$destroy()
  209. expect(parent.$children.length).toBe(1)
  210. child2.$destroy()
  211. expect(parent.$children.length).toBe(0)
  212. })
  213. it('children', function () {
  214. var parent = new Vue()
  215. var child = new Vue({ parent: parent })
  216. parent.$destroy()
  217. expect(child._isDestroyed).toBe(true)
  218. })
  219. it('directives', function () {
  220. var spy = jasmine.createSpy('directive unbind')
  221. var vm = new Vue({
  222. el: document.createElement('div'),
  223. template: '<div v-test></div>',
  224. directives: {
  225. test: {
  226. unbind: spy
  227. }
  228. }
  229. })
  230. vm.$destroy()
  231. expect(spy).toHaveBeenCalled()
  232. })
  233. it('watchers', function () {
  234. var vm = new Vue({
  235. el: document.createElement('div'),
  236. template: '{{a}}',
  237. data: { a: 1 }
  238. })
  239. vm.$watch('a', function () {})
  240. var dirWatcher = vm._watchers[0]
  241. var userWatcher = vm._watchers[1]
  242. vm.$destroy()
  243. expect(dirWatcher.active).toBe(false)
  244. expect(userWatcher.active).toBe(false)
  245. })
  246. it('refuse multiple calls', function () {
  247. var spy = jasmine.createSpy()
  248. var vm = new Vue({
  249. beforeDestroy: spy
  250. })
  251. vm.$destroy()
  252. vm.$destroy()
  253. expect(spy.calls.count()).toBe(1)
  254. })
  255. it('safely teardown partial compilation', function () {
  256. var vm = new Vue({
  257. template: '<test><partial name="hello"></partial></test>',
  258. partials: {
  259. hello: 'Hello {{name}}'
  260. },
  261. components: {
  262. test: {
  263. template: '<slot></slot>'
  264. }
  265. }
  266. }).$mount()
  267. expect(function () {
  268. vm.$destroy()
  269. }).not.toThrow()
  270. })
  271. })
  272. describe('$compile', function () {
  273. it('should partial compile and teardown stuff', function (done) {
  274. var el = document.createElement('div')
  275. var vm = new Vue({
  276. el: el,
  277. template: '{{a}}',
  278. data: {
  279. a: 'foo'
  280. }
  281. })
  282. expect(vm._directives.length).toBe(1)
  283. var partial = document.createElement('span')
  284. partial.textContent = '{{a}}'
  285. var decompile = vm.$compile(partial)
  286. expect(partial.textContent).toBe('foo')
  287. expect(vm._directives.length).toBe(2)
  288. decompile()
  289. expect(vm._directives.length).toBe(1)
  290. vm.a = 'bar'
  291. Vue.nextTick(function () {
  292. expect(el.textContent).toBe('bar')
  293. expect(partial.textContent).toBe('foo')
  294. done()
  295. })
  296. })
  297. })
  298. })