import Vue from 'vue' describe('Component', () => { it('static', () => { const vm = new Vue({ template: '', components: { test: { data() { return { a: 123 } }, template: '{{a}}' } } }).$mount() expect(vm.$el.tagName).toBe('SPAN') expect(vm.$el.innerHTML).toBe('123') }) it('using component in restricted elements', () => { const vm = new Vue({ template: '
', components: { test: { data() { return { a: 123 } }, template: '{{a}}' } } }).$mount() expect(vm.$el.innerHTML).toBe( '
123
' ) }) it('"is" attribute', () => { const vm = new Vue({ template: '
', components: { test: { data() { return { a: 123 } }, template: '{{a}}' } } }).$mount() expect(vm.$el.innerHTML).toBe( '
123
' ) }) it('inline-template', () => { const vm = new Vue({ template: '
{{a}}
', data: { a: 'parent' }, components: { test: { data() { return { a: 'child' } } } } }).$mount() expect(vm.$el.innerHTML).toBe('child') }) it('fragment instance warning', () => { new Vue({ template: '', components: { test: { data() { return { a: 123, b: 234 } }, template: '

{{a}}

{{b}}

' } } }).$mount() expect( 'Component template should contain exactly one root element' ).toHaveBeenWarned() }) it('dynamic', done => { const vm = new Vue({ template: '', data: { view: 'view-a' }, components: { 'view-a': { template: '
foo {{view}}
', data() { return { view: 'a' } } }, 'view-b': { template: '
bar {{view}}
', data() { return { view: 'b' } } } } }).$mount() expect(vm.$el.outerHTML).toBe('
foo a
') vm.view = 'view-b' waitForUpdate(() => { expect(vm.$el.outerHTML).toBe('
bar b
') vm.view = '' }) .then(() => { expect(vm.$el.nodeType).toBe(8) expect(vm.$el.data).toBe('') }) .then(done) }) it('dynamic with props', done => { const vm = new Vue({ template: '', data: { view: 'view-a' }, components: { 'view-a': { template: '
foo {{view}}
', props: ['view'] }, 'view-b': { template: '
bar {{view}}
', props: ['view'] } } }).$mount() expect(vm.$el.outerHTML).toBe('
foo view-a
') vm.view = 'view-b' waitForUpdate(() => { expect(vm.$el.outerHTML).toBe('
bar view-b
') vm.view = '' }) .then(() => { expect(vm.$el.nodeType).toBe(8) expect(vm.$el.data).toBe('') }) .then(done) }) it(':is using raw component constructor', () => { const vm = new Vue({ template: '
' + '' + '' + '
', components: { test: { template: 'foo' }, async: function (resolve) { resolve({ template: 'bar' }) } } }).$mount() expect(vm.$el.innerHTML).toBe('foobar') }) it('dynamic combined with v-for', done => { const vm = new Vue({ template: '
' + '' + '
', data: { comps: [{ type: 'one' }, { type: 'two' }] }, components: { one: { template: 'one' }, two: { template: 'two' } } }).$mount() expect(vm.$el.innerHTML).toBe('onetwo') vm.comps[1].type = 'one' waitForUpdate(() => { expect(vm.$el.innerHTML).toBe('oneone') }).then(done) }) it('dynamic elements with domProps', done => { const vm = new Vue({ template: '', data: { view: 'input', val: 'hello' } }).$mount() expect(vm.$el.tagName).toBe('INPUT') expect(vm.$el.value).toBe('hello') vm.view = 'textarea' vm.val += ' world' waitForUpdate(() => { expect(vm.$el.tagName).toBe('TEXTAREA') expect(vm.$el.value).toBe('hello world') vm.view = '' }).then(done) }) it('should compile parent template directives & content in parent scope', done => { const vm = new Vue({ data: { ok: false, message: 'hello' }, template: '{{message}}', components: { test: { template: '
{{message}}
', data() { return { message: 'world' } } } } }).$mount() expect(vm.$el.style.display).toBe('none') expect(vm.$el.textContent).toBe('hello world') vm.ok = true vm.message = 'bye' waitForUpdate(() => { expect(vm.$el.style.display).toBe('') expect(vm.$el.textContent).toBe('bye world') }).then(done) }) it('parent content + v-if', done => { const vm = new Vue({ data: { ok: false, message: 'hello' }, template: '{{message}}', components: { test: { template: '
{{message}}
', data() { return { message: 'world' } } } } }).$mount() expect(vm.$el.textContent).toBe('') expect(vm.$children.length).toBe(0) vm.ok = true waitForUpdate(() => { expect(vm.$children.length).toBe(1) expect(vm.$el.textContent).toBe('hello world') }).then(done) }) it('props', () => { const vm = new Vue({ data: { list: [{ a: 1 }, { a: 2 }] }, template: '', components: { test: { template: '', props: ['collection'] } } }).$mount() expect(vm.$el.outerHTML).toBe('') }) it('should warn when using camelCased props in in-DOM template', () => { new Vue({ data: { list: [{ a: 1 }, { a: 2 }] }, template: '', // <-- simulate lowercased template components: { test: { template: '', props: ['someCollection'] } } }).$mount() expect( 'You should probably use "some-collection" instead of "someCollection".' ).toHaveBeenTipped() }) it('should warn when using camelCased events in in-DOM template', () => { new Vue({ template: '', // <-- simulate lowercased template components: { test: { template: '
', created() { this.$emit('fooBar') } } } }).$mount() expect( 'You should probably use "foo-bar" instead of "fooBar".' ).toHaveBeenTipped() }) it('not found component should not throw', () => { expect(function () { new Vue({ template: '
' }) }).not.toThrow() }) it('properly update replaced higher-order component root node', done => { const vm = new Vue({ data: { color: 'red' }, template: '', components: { test: { data() { return { tag: 'div' } }, render(h) { return h(this.tag, { class: 'test' }, 'hi') } } } }).$mount() expect(vm.$el.tagName).toBe('DIV') expect(vm.$el.id).toBe('foo') expect(vm.$el.className).toBe('test red') vm.color = 'green' waitForUpdate(() => { expect(vm.$el.tagName).toBe('DIV') expect(vm.$el.id).toBe('foo') expect(vm.$el.className).toBe('test green') vm.$children[0].tag = 'p' }) .then(() => { expect(vm.$el.tagName).toBe('P') expect(vm.$el.id).toBe('foo') expect(vm.$el.className).toBe('test green') vm.color = 'red' }) .then(() => { expect(vm.$el.tagName).toBe('P') expect(vm.$el.id).toBe('foo') expect(vm.$el.className).toBe('test red') }) .then(done) }) it('catch component render error and preserve previous vnode', done => { const spy = vi.fn() Vue.config.errorHandler = spy const vm = new Vue({ data: { a: { b: 123 } }, render(h) { return h('div', [this.a.b]) } }).$mount() expect(vm.$el.textContent).toBe('123') expect(spy).not.toHaveBeenCalled() vm.a = null waitForUpdate(() => { expect(spy).toHaveBeenCalled() expect(vm.$el.textContent).toBe('123') // should preserve rendered DOM vm.a = { b: 234 } }) .then(() => { expect(vm.$el.textContent).toBe('234') // should be able to recover Vue.config.errorHandler = undefined }) .then(done) }) it('relocates node without error', done => { const el = document.createElement('div') document.body.appendChild(el) const target = document.createElement('div') document.body.appendChild(target) const Test = { render(h) { return h('div', { class: 'test' }, this.$slots.default) }, mounted() { target.appendChild(this.$el) }, beforeDestroy() { const parent = this.$el.parentNode if (parent) { parent.removeChild(this.$el) } } } const vm = new Vue({ data() { return { view: true } }, template: `
Test
`, components: { test: Test } }).$mount(el) expect(el.outerHTML).toBe('
') expect(target.outerHTML).toBe('
Test
') vm.view = false waitForUpdate(() => { expect(el.outerHTML).toBe('
') expect(target.outerHTML).toBe('
') vm.$destroy() }).then(done) }) it('render vnode with ' } } }).$mount() expect(vm.$el.nodeName).toBe('#comment') expect( 'Templates should only be responsible for mapping the state' ).toHaveBeenWarned() }) })