lifecycle_spec.js 8.9 KB

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