mirror of https://github.com/vuejs/vue.git
				
				
				
			
		
			
				
	
	
		
			340 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
			
		
		
	
	
			340 lines
		
	
	
		
			8.3 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
| import Vue from 'vue'
 | |
| 
 | |
| describe('Options lifecycle hooks', () => {
 | |
|   let spy
 | |
|   beforeEach(() => {
 | |
|     spy = vi.fn()
 | |
|   })
 | |
| 
 | |
|   describe('beforeCreate', () => {
 | |
|     it('should allow modifying options', () => {
 | |
|       const vm = new Vue({
 | |
|         data: {
 | |
|           a: 1
 | |
|         },
 | |
|         beforeCreate () {
 | |
|           spy()
 | |
|           expect(this.a).toBeUndefined()
 | |
|           this.$options.computed = {
 | |
|             b () {
 | |
|               return this.a + 1
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       })
 | |
|       expect(spy).toHaveBeenCalled()
 | |
|       expect(vm.b).toBe(2)
 | |
|     })
 | |
|   })
 | |
| 
 | |
|   describe('created', () => {
 | |
|     it('should have completed observation', () => {
 | |
|       new Vue({
 | |
|         data: {
 | |
|           a: 1
 | |
|         },
 | |
|         created () {
 | |
|           expect(this.a).toBe(1)
 | |
|           spy()
 | |
|         }
 | |
|       })
 | |
|       expect(spy).toHaveBeenCalled()
 | |
|     })
 | |
|   })
 | |
| 
 | |
|   describe('beforeMount', () => {
 | |
|     it('should not have mounted', () => {
 | |
|       const vm = new Vue({
 | |
|         render () {},
 | |
|         beforeMount () {
 | |
|           spy()
 | |
|           expect(this._isMounted).toBe(false)
 | |
|           expect(this.$el).toBeUndefined() // due to empty mount
 | |
|           expect(this._vnode).toBeNull()
 | |
|           expect(this._watcher).toBeNull()
 | |
|         }
 | |
|       })
 | |
|       expect(spy).not.toHaveBeenCalled()
 | |
|       vm.$mount()
 | |
|       expect(spy).toHaveBeenCalled()
 | |
|     })
 | |
|   })
 | |
| 
 | |
|   describe('mounted', () => {
 | |
|     it('should have mounted', () => {
 | |
|       const vm = new Vue({
 | |
|         template: '<div></div>',
 | |
|         mounted () {
 | |
|           spy()
 | |
|           expect(this._isMounted).toBe(true)
 | |
|           expect(this.$el.tagName).toBe('DIV')
 | |
|           expect(this._vnode.tag).toBe('div')
 | |
|         }
 | |
|       })
 | |
|       expect(spy).not.toHaveBeenCalled()
 | |
|       vm.$mount()
 | |
|       expect(spy).toHaveBeenCalled()
 | |
|     })
 | |
| 
 | |
|     // #3898
 | |
|     it('should call for manually mounted instance with parent', () => {
 | |
|       // @ts-expect-error
 | |
|       const vm = new Vue()
 | |
|       expect(spy).not.toHaveBeenCalled()
 | |
|       new Vue({
 | |
|         parent,
 | |
|         template: '<div></div>',
 | |
|         mounted () {
 | |
|           spy()
 | |
|         }
 | |
|       }).$mount()
 | |
|       expect(spy).toHaveBeenCalled()
 | |
|     })
 | |
| 
 | |
|     it('should mount child parent in correct order', () => {
 | |
|       const calls: any[] = []
 | |
|       new Vue({
 | |
|         template: '<div><test></test></div>',
 | |
|         mounted () {
 | |
|           calls.push('parent')
 | |
|         },
 | |
|         components: {
 | |
|           test: {
 | |
|             template: '<nested></nested>',
 | |
|             mounted () {
 | |
|               expect(this.$el.parentNode).toBeTruthy()
 | |
|               calls.push('child')
 | |
|             },
 | |
|             components: {
 | |
|               nested: {
 | |
|                 template: '<div></div>',
 | |
|                 mounted () {
 | |
|                   expect(this.$el.parentNode).toBeTruthy()
 | |
|                   calls.push('nested')
 | |
|                 }
 | |
|               }
 | |
|             }
 | |
|           }
 | |
|         }
 | |
|       }).$mount()
 | |
|       expect(calls).toEqual(['nested', 'child', 'parent'])
 | |
|     })
 | |
|   })
 | |
| 
 | |
|   describe('beforeUpdate', () => {
 | |
|     it('should be called before update', done => {
 | |
|       const vm = new Vue({
 | |
|         template: '<div>{{ msg }}</div>',
 | |
|         data: { msg: 'foo' },
 | |
|         beforeUpdate () {
 | |
|           spy()
 | |
|           expect(this.$el.textContent).toBe('foo')
 | |
|         }
 | |
|       }).$mount()
 | |
|       expect(spy).not.toHaveBeenCalled()
 | |
|       vm.msg = 'bar'
 | |
|       expect(spy).not.toHaveBeenCalled() // should be async
 | |
|       waitForUpdate(() => {
 | |
|         expect(spy).toHaveBeenCalled()
 | |
|       }).then(done)
 | |
|     })
 | |
| 
 | |
|     it('should be called before render and allow mutating state', done => {
 | |
|       const vm = new Vue({
 | |
|         template: '<div>{{ msg }}</div>',
 | |
|         data: { msg: 'foo' },
 | |
|         beforeUpdate () {
 | |
|           this.msg += '!'
 | |
|         }
 | |
|       }).$mount()
 | |
|       expect(vm.$el.textContent).toBe('foo')
 | |
|       vm.msg = 'bar'
 | |
|       waitForUpdate(() => {
 | |
|         expect(vm.$el.textContent).toBe('bar!')
 | |
|       }).then(done)
 | |
|     })
 | |
| 
 | |
|     // #8076
 | |
|     it('should not be called after destroy', done => {
 | |
|       const beforeUpdate = vi.fn()
 | |
|       const destroyed = vi.fn()
 | |
| 
 | |
|       Vue.component('todo', {
 | |
|         template: '<div>{{todo.done}}</div>',
 | |
|         props: ['todo'],
 | |
|         destroyed,
 | |
|         beforeUpdate
 | |
|       })
 | |
| 
 | |
|       const vm = new Vue({
 | |
|         template: `
 | |
|           <div>
 | |
|             <todo v-for="t in pendingTodos" :todo="t" :key="t.id"></todo>
 | |
|           </div>
 | |
|         `,
 | |
|         data () {
 | |
|           return {
 | |
|             todos: [{ id: 1, done: false }]
 | |
|           }
 | |
|         },
 | |
|         computed: {
 | |
|           pendingTodos () {
 | |
|             return this.todos.filter(t => !t.done)
 | |
|           }
 | |
|         }
 | |
|       }).$mount()
 | |
| 
 | |
|       vm.todos[0].done = true
 | |
|       waitForUpdate(() => {
 | |
|         expect(destroyed).toHaveBeenCalled()
 | |
|         expect(beforeUpdate).not.toHaveBeenCalled()
 | |
|       }).then(done)
 | |
|     })
 | |
|   })
 | |
| 
 | |
|   describe('updated', () => {
 | |
|     it('should be called after update', done => {
 | |
|       const vm = new Vue({
 | |
|         template: '<div>{{ msg }}</div>',
 | |
|         data: { msg: 'foo' },
 | |
|         updated () {
 | |
|           spy()
 | |
|           expect(this.$el.textContent).toBe('bar')
 | |
|         }
 | |
|       }).$mount()
 | |
|       expect(spy).not.toHaveBeenCalled()
 | |
|       vm.msg = 'bar'
 | |
|       expect(spy).not.toHaveBeenCalled() // should be async
 | |
|       waitForUpdate(() => {
 | |
|         expect(spy).toHaveBeenCalled()
 | |
|       }).then(done)
 | |
|     })
 | |
| 
 | |
|     it('should be called after children are updated', done => {
 | |
|       const calls: any[] = []
 | |
|       const vm = new Vue({
 | |
|         template: '<div><test ref="child">{{ msg }}</test></div>',
 | |
|         data: { msg: 'foo' },
 | |
|         components: {
 | |
|           test: {
 | |
|             template: `<div><slot></slot></div>`,
 | |
|             updated () {
 | |
|               expect(this.$el.textContent).toBe('bar')
 | |
|               calls.push('child')
 | |
|             }
 | |
|           }
 | |
|         },
 | |
|         updated () {
 | |
|           expect(this.$el.textContent).toBe('bar')
 | |
|           calls.push('parent')
 | |
|         }
 | |
|       }).$mount()
 | |
| 
 | |
|       expect(calls).toEqual([])
 | |
|       vm.msg = 'bar'
 | |
|       expect(calls).toEqual([])
 | |
|       waitForUpdate(() => {
 | |
|         expect(calls).toEqual(['child', 'parent'])
 | |
|       }).then(done)
 | |
|     })
 | |
| 
 | |
|     // #8076
 | |
|     it('should not be called after destroy', done => {
 | |
|       const updated = vi.fn()
 | |
|       const destroyed = vi.fn()
 | |
| 
 | |
|       Vue.component('todo', {
 | |
|         template: '<div>{{todo.done}}</div>',
 | |
|         props: ['todo'],
 | |
|         destroyed,
 | |
|         updated
 | |
|       })
 | |
| 
 | |
|       const vm = new Vue({
 | |
|         template: `
 | |
|           <div>
 | |
|             <todo v-for="t in pendingTodos" :todo="t" :key="t.id"></todo>
 | |
|           </div>
 | |
|         `,
 | |
|         data () {
 | |
|           return {
 | |
|             todos: [{ id: 1, done: false }]
 | |
|           }
 | |
|         },
 | |
|         computed: {
 | |
|           pendingTodos () {
 | |
|             return this.todos.filter(t => !t.done)
 | |
|           }
 | |
|         }
 | |
|       }).$mount()
 | |
| 
 | |
|       vm.todos[0].done = true
 | |
|       waitForUpdate(() => {
 | |
|         expect(destroyed).toHaveBeenCalled()
 | |
|         expect(updated).not.toHaveBeenCalled()
 | |
|       }).then(done)
 | |
|     })
 | |
|   })
 | |
| 
 | |
|   describe('beforeDestroy', () => {
 | |
|     it('should be called before destroy', () => {
 | |
|       const vm = new Vue({
 | |
|         render () {},
 | |
|         beforeDestroy () {
 | |
|           spy()
 | |
|           expect(this._isBeingDestroyed).toBe(false)
 | |
|           expect(this._isDestroyed).toBe(false)
 | |
|         }
 | |
|       }).$mount()
 | |
|       expect(spy).not.toHaveBeenCalled()
 | |
|       vm.$destroy()
 | |
|       vm.$destroy()
 | |
|       expect(spy).toHaveBeenCalled()
 | |
|       expect(spy.mock.calls.length).toBe(1)
 | |
|     })
 | |
|   })
 | |
| 
 | |
|   describe('destroyed', () => {
 | |
|     it('should be called after destroy', () => {
 | |
|       const vm = new Vue({
 | |
|         render () {},
 | |
|         destroyed () {
 | |
|           spy()
 | |
|           expect(this._isBeingDestroyed).toBe(true)
 | |
|           expect(this._isDestroyed).toBe(true)
 | |
|         }
 | |
|       }).$mount()
 | |
|       expect(spy).not.toHaveBeenCalled()
 | |
|       vm.$destroy()
 | |
|       vm.$destroy()
 | |
|       expect(spy).toHaveBeenCalled()
 | |
|       expect(spy.mock.calls.length).toBe(1)
 | |
|     })
 | |
|   })
 | |
| 
 | |
|   it('should emit hook events', () => {
 | |
|     const created = vi.fn()
 | |
|     const mounted = vi.fn()
 | |
|     const destroyed = vi.fn()
 | |
|     const vm = new Vue({
 | |
|       render () {},
 | |
|       beforeCreate () {
 | |
|         this.$on('hook:created', created)
 | |
|         this.$on('hook:mounted', mounted)
 | |
|         this.$on('hook:destroyed', destroyed)
 | |
|       }
 | |
|     })
 | |
| 
 | |
|     expect(created).toHaveBeenCalled()
 | |
|     expect(mounted).not.toHaveBeenCalled()
 | |
|     expect(destroyed).not.toHaveBeenCalled()
 | |
| 
 | |
|     vm.$mount()
 | |
|     expect(mounted).toHaveBeenCalled()
 | |
|     expect(destroyed).not.toHaveBeenCalled()
 | |
| 
 | |
|     vm.$destroy()
 | |
|     expect(destroyed).toHaveBeenCalled()
 | |
|   })
 | |
| })
 |