create-element.spec.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. import Vue from 'vue'
  2. import { createEmptyVNode } from 'core/vdom/vnode'
  3. describe('create-element', () => {
  4. it('render vnode with basic reserved tag using createElement', () => {
  5. const vm = new Vue({
  6. data: { msg: 'hello world' }
  7. })
  8. const h = vm.$createElement
  9. const vnode = h('p', {})
  10. expect(vnode.tag).toBe('p')
  11. expect(vnode.data).toEqual({})
  12. expect(vnode.children).toBeUndefined()
  13. expect(vnode.text).toBeUndefined()
  14. expect(vnode.elm).toBeUndefined()
  15. expect(vnode.ns).toBeUndefined()
  16. expect(vnode.context).toEqual(vm)
  17. })
  18. it('render vnode with component using createElement', () => {
  19. const vm = new Vue({
  20. data: { message: 'hello world' },
  21. components: {
  22. 'my-component': {
  23. props: ['msg']
  24. }
  25. }
  26. })
  27. const h = vm.$createElement
  28. const vnode = h('my-component', { props: { msg: vm.message }})
  29. expect(vnode.tag).toMatch(/vue-component-[0-9]+/)
  30. expect(vnode.componentOptions.propsData).toEqual({ msg: vm.message })
  31. expect(vnode.children).toBeUndefined()
  32. expect(vnode.text).toBeUndefined()
  33. expect(vnode.elm).toBeUndefined()
  34. expect(vnode.ns).toBeUndefined()
  35. expect(vnode.context).toEqual(vm)
  36. })
  37. it('render vnode with custom tag using createElement', () => {
  38. const vm = new Vue({
  39. data: { msg: 'hello world' }
  40. })
  41. const h = vm.$createElement
  42. const tag = 'custom-tag'
  43. const vnode = h(tag, {})
  44. expect(vnode.tag).toBe('custom-tag')
  45. expect(vnode.data).toEqual({})
  46. expect(vnode.children).toBeUndefined()
  47. expect(vnode.text).toBeUndefined()
  48. expect(vnode.elm).toBeUndefined()
  49. expect(vnode.ns).toBeUndefined()
  50. expect(vnode.context).toEqual(vm)
  51. expect(vnode.componentOptions).toBeUndefined()
  52. })
  53. it('render empty vnode with falsy tag using createElement', () => {
  54. const vm = new Vue({
  55. data: { msg: 'hello world' }
  56. })
  57. const h = vm.$createElement
  58. const vnode = h(null, {})
  59. expect(vnode).toEqual(createEmptyVNode())
  60. })
  61. it('render vnode with not string tag using createElement', () => {
  62. const vm = new Vue({
  63. data: { msg: 'hello world' }
  64. })
  65. const h = vm.$createElement
  66. const vnode = h(Vue.extend({ // Component class
  67. props: ['msg']
  68. }), { props: { msg: vm.message }})
  69. expect(vnode.tag).toMatch(/vue-component-[0-9]+/)
  70. expect(vnode.componentOptions.propsData).toEqual({ msg: vm.message })
  71. expect(vnode.children).toBeUndefined()
  72. expect(vnode.text).toBeUndefined()
  73. expect(vnode.elm).toBeUndefined()
  74. expect(vnode.ns).toBeUndefined()
  75. expect(vnode.context).toEqual(vm)
  76. })
  77. it('render vnode with createElement with children', () => {
  78. const vm = new Vue({})
  79. const h = vm.$createElement
  80. const vnode = h('p', void 0, [h('br'), 'hello world', h('br')])
  81. expect(vnode.children[0].tag).toBe('br')
  82. expect(vnode.children[1].text).toBe('hello world')
  83. expect(vnode.children[2].tag).toBe('br')
  84. })
  85. it('render vnode with children, omitting data', () => {
  86. const vm = new Vue({})
  87. const h = vm.$createElement
  88. const vnode = h('p', [h('br'), 'hello world', h('br')])
  89. expect(vnode.children[0].tag).toBe('br')
  90. expect(vnode.children[1].text).toBe('hello world')
  91. expect(vnode.children[2].tag).toBe('br')
  92. })
  93. it('render vnode with children, including boolean and null type', () => {
  94. const vm = new Vue({})
  95. const h = vm.$createElement
  96. const vnode = h('p', [h('br'), true, 123, h('br'), 'abc', null])
  97. expect(vnode.children.length).toBe(4)
  98. expect(vnode.children[0].tag).toBe('br')
  99. expect(vnode.children[1].text).toBe('123')
  100. expect(vnode.children[2].tag).toBe('br')
  101. expect(vnode.children[3].text).toBe('abc')
  102. })
  103. it('render svg elements with correct namespace', () => {
  104. const vm = new Vue({})
  105. const h = vm.$createElement
  106. const vnode = h('svg', [h('a', [h('foo', [h('bar')])])])
  107. expect(vnode.ns).toBe('svg')
  108. // should apply ns to children recursively
  109. expect(vnode.children[0].ns).toBe('svg')
  110. expect(vnode.children[0].children[0].ns).toBe('svg')
  111. expect(vnode.children[0].children[0].children[0].ns).toBe('svg')
  112. })
  113. it('render MathML elements with correct namespace', () => {
  114. const vm = new Vue({})
  115. const h = vm.$createElement
  116. const vnode = h('math', [h('matrix')])
  117. expect(vnode.ns).toBe('math')
  118. // should apply ns to children
  119. expect(vnode.children[0].ns).toBe('math')
  120. // although not explicitly listed, elements nested under <math>
  121. // should not be treated as component
  122. expect(vnode.children[0].componentOptions).toBeUndefined()
  123. })
  124. it('render svg foreignObject with correct namespace', () => {
  125. const vm = new Vue({})
  126. const h = vm.$createElement
  127. const vnode = h('svg', [h('foreignObject', [h('p'), h('svg')])])
  128. expect(vnode.ns).toBe('svg')
  129. expect(vnode.children[0].ns).toBe('svg')
  130. expect(vnode.children[0].children[0].ns).toBeUndefined()
  131. // #7330
  132. expect(vnode.children[0].children[1].ns).toBe('svg')
  133. })
  134. // #6642
  135. it('render svg foreignObject component with correct namespace', () => {
  136. const vm = new Vue({
  137. template: `
  138. <svg>
  139. <test></test>
  140. </svg>
  141. `,
  142. components: {
  143. test: {
  144. template: `
  145. <foreignObject>
  146. <p xmlns="http://www.w3.org/1999/xhtml"></p>
  147. </foreignObject>
  148. `
  149. }
  150. }
  151. }).$mount()
  152. const testComp = vm.$children[0]
  153. expect(testComp.$vnode.ns).toBe('svg')
  154. expect(testComp._vnode.tag).toBe('foreignObject')
  155. expect(testComp._vnode.ns).toBe('svg')
  156. expect(testComp._vnode.children[0].tag).toBe('p')
  157. expect(testComp._vnode.children[0].ns).toBeUndefined()
  158. })
  159. // #6506
  160. it('render SVGAElement in a component correctly', () => {
  161. const vm = new Vue({
  162. template: `
  163. <svg>
  164. <test></test>
  165. </svg>
  166. `,
  167. components: {
  168. test: { render: h => h('a') }
  169. }
  170. }).$mount()
  171. const testComp = vm.$children[0]
  172. expect(testComp.$vnode.ns).toBe('svg')
  173. expect(testComp._vnode.tag).toBe('a')
  174. expect(testComp._vnode.ns).toBe('svg')
  175. })
  176. it('warn observed data objects', () => {
  177. new Vue({
  178. data: {
  179. data: {}
  180. },
  181. render (h) {
  182. return h('div', this.data)
  183. }
  184. }).$mount()
  185. expect('Avoid using observed data object as vnode data').toHaveBeenWarned()
  186. })
  187. it('warn non-primitive key', () => {
  188. new Vue({
  189. render (h) {
  190. return h('div', { key: {}})
  191. }
  192. }).$mount()
  193. expect('Avoid using non-primitive value as key').toHaveBeenWarned()
  194. })
  195. it('doesn\'t warn boolean key', () => {
  196. new Vue({
  197. render (h) {
  198. return h('div', { key: true })
  199. }
  200. }).$mount()
  201. expect('Avoid using non-primitive value as key').not.toHaveBeenWarned()
  202. })
  203. it('doesn\'t warn symbol key', () => {
  204. new Vue({
  205. render (h) {
  206. return h('div', { key: Symbol('symbol') })
  207. }
  208. }).$mount()
  209. expect('Avoid using non-primitive value as key').not.toHaveBeenWarned()
  210. })
  211. it('nested child elements should be updated correctly', done => {
  212. const vm = new Vue({
  213. data: { n: 1 },
  214. render (h) {
  215. const list = []
  216. for (let i = 0; i < this.n; i++) {
  217. list.push(h('span', i))
  218. }
  219. const input = h('input', {
  220. attrs: {
  221. value: 'a',
  222. type: 'text'
  223. }
  224. })
  225. return h('div', [[...list, input]])
  226. }
  227. }).$mount()
  228. expect(vm.$el.innerHTML).toContain('<span>0</span><input')
  229. const el = vm.$el.querySelector('input')
  230. el.value = 'b'
  231. vm.n++
  232. waitForUpdate(() => {
  233. expect(vm.$el.innerHTML).toContain('<span>0</span><span>1</span><input')
  234. expect(vm.$el.querySelector('input')).toBe(el)
  235. expect(vm.$el.querySelector('input').value).toBe('b')
  236. }).then(done)
  237. })
  238. // #7786
  239. it('creates element with vnode reference in :class or :style', () => {
  240. const vm = new Vue({
  241. components: {
  242. foo: {
  243. render (h) {
  244. return h('div', {
  245. class: {
  246. 'has-vnode': this.$vnode
  247. }
  248. }, 'foo')
  249. }
  250. }
  251. },
  252. render: h => h('foo')
  253. }).$mount()
  254. expect(vm.$el.innerHTML).toContain('foo')
  255. expect(vm.$el.classList.contains('has-vnode')).toBe(true)
  256. })
  257. })