vdomAttrsFallthrough.spec.ts 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. // using DOM renderer because this case is mostly DOM-specific
  2. import {
  3. createVNode as h,
  4. render,
  5. nextTick,
  6. mergeProps,
  7. ref,
  8. onUpdated
  9. } from '@vue/runtime-dom'
  10. describe('attribute fallthrough', () => {
  11. it('everything should be in props when component has no declared props', async () => {
  12. const click = jest.fn()
  13. const childUpdated = jest.fn()
  14. const Hello = {
  15. setup() {
  16. const count = ref(0)
  17. function inc() {
  18. count.value++
  19. click()
  20. }
  21. return () =>
  22. h(Child, {
  23. foo: 1,
  24. id: 'test',
  25. class: 'c' + count.value,
  26. style: { color: count.value ? 'red' : 'green' },
  27. onClick: inc
  28. })
  29. }
  30. }
  31. const Child = {
  32. setup(props: any) {
  33. onUpdated(childUpdated)
  34. return () =>
  35. h(
  36. 'div',
  37. mergeProps(
  38. {
  39. class: 'c2',
  40. style: { fontWeight: 'bold' }
  41. },
  42. props
  43. ),
  44. props.foo
  45. )
  46. }
  47. }
  48. const root = document.createElement('div')
  49. document.body.appendChild(root)
  50. render(h(Hello), root)
  51. const node = root.children[0] as HTMLElement
  52. expect(node.getAttribute('id')).toBe('test')
  53. expect(node.getAttribute('foo')).toBe('1')
  54. expect(node.getAttribute('class')).toBe('c2 c0')
  55. expect(node.style.color).toBe('green')
  56. expect(node.style.fontWeight).toBe('bold')
  57. node.dispatchEvent(new CustomEvent('click'))
  58. expect(click).toHaveBeenCalled()
  59. await nextTick()
  60. expect(childUpdated).toHaveBeenCalled()
  61. expect(node.getAttribute('id')).toBe('test')
  62. expect(node.getAttribute('foo')).toBe('1')
  63. expect(node.getAttribute('class')).toBe('c2 c1')
  64. expect(node.style.color).toBe('red')
  65. expect(node.style.fontWeight).toBe('bold')
  66. })
  67. // it('should separate in attrs when component has declared props', async () => {
  68. // const click = jest.fn()
  69. // const childUpdated = jest.fn()
  70. // class Hello extends Component {
  71. // count = 0
  72. // inc() {
  73. // this.count++
  74. // click()
  75. // }
  76. // render() {
  77. // return h(Child, {
  78. // foo: 123,
  79. // id: 'test',
  80. // class: 'c' + this.count,
  81. // style: { color: this.count ? 'red' : 'green' },
  82. // onClick: this.inc
  83. // })
  84. // }
  85. // }
  86. // class Child extends Component<{ [key: string]: any; foo: number }> {
  87. // static props = {
  88. // foo: Number
  89. // }
  90. // updated() {
  91. // childUpdated()
  92. // }
  93. // render() {
  94. // return cloneVNode(
  95. // h(
  96. // 'div',
  97. // {
  98. // class: 'c2',
  99. // style: { fontWeight: 'bold' }
  100. // },
  101. // this.$props.foo
  102. // ),
  103. // this.$attrs
  104. // )
  105. // }
  106. // }
  107. // const root = document.createElement('div')
  108. // document.body.appendChild(root)
  109. // await render(h(Hello), root)
  110. // const node = root.children[0] as HTMLElement
  111. // // with declared props, any parent attr that isn't a prop falls through
  112. // expect(node.getAttribute('id')).toBe('test')
  113. // expect(node.getAttribute('class')).toBe('c2 c0')
  114. // expect(node.style.color).toBe('green')
  115. // expect(node.style.fontWeight).toBe('bold')
  116. // node.dispatchEvent(new CustomEvent('click'))
  117. // expect(click).toHaveBeenCalled()
  118. // // ...while declared ones remain props
  119. // expect(node.hasAttribute('foo')).toBe(false)
  120. // await nextTick()
  121. // expect(childUpdated).toHaveBeenCalled()
  122. // expect(node.getAttribute('id')).toBe('test')
  123. // expect(node.getAttribute('class')).toBe('c2 c1')
  124. // expect(node.style.color).toBe('red')
  125. // expect(node.style.fontWeight).toBe('bold')
  126. // expect(node.hasAttribute('foo')).toBe(false)
  127. // })
  128. // it('should fallthrough on multi-nested components', async () => {
  129. // const click = jest.fn()
  130. // const childUpdated = jest.fn()
  131. // const grandChildUpdated = jest.fn()
  132. // class Hello extends Component {
  133. // count = 0
  134. // inc() {
  135. // this.count++
  136. // click()
  137. // }
  138. // render() {
  139. // return h(Child, {
  140. // foo: 1,
  141. // id: 'test',
  142. // class: 'c' + this.count,
  143. // style: { color: this.count ? 'red' : 'green' },
  144. // onClick: this.inc
  145. // })
  146. // }
  147. // }
  148. // class Child extends Component<{ [key: string]: any; foo: number }> {
  149. // updated() {
  150. // childUpdated()
  151. // }
  152. // render() {
  153. // return h(GrandChild, this.$props)
  154. // }
  155. // }
  156. // class GrandChild extends Component<{ [key: string]: any; foo: number }> {
  157. // static props = {
  158. // foo: Number
  159. // }
  160. // updated() {
  161. // grandChildUpdated()
  162. // }
  163. // render(props: any) {
  164. // return cloneVNode(
  165. // h(
  166. // 'div',
  167. // {
  168. // class: 'c2',
  169. // style: { fontWeight: 'bold' }
  170. // },
  171. // props.foo
  172. // ),
  173. // this.$attrs
  174. // )
  175. // }
  176. // }
  177. // const root = document.createElement('div')
  178. // document.body.appendChild(root)
  179. // await render(h(Hello), root)
  180. // const node = root.children[0] as HTMLElement
  181. // // with declared props, any parent attr that isn't a prop falls through
  182. // expect(node.getAttribute('id')).toBe('test')
  183. // expect(node.getAttribute('class')).toBe('c2 c0')
  184. // expect(node.style.color).toBe('green')
  185. // expect(node.style.fontWeight).toBe('bold')
  186. // node.dispatchEvent(new CustomEvent('click'))
  187. // expect(click).toHaveBeenCalled()
  188. // // ...while declared ones remain props
  189. // expect(node.hasAttribute('foo')).toBe(false)
  190. // await nextTick()
  191. // expect(childUpdated).toHaveBeenCalled()
  192. // expect(grandChildUpdated).toHaveBeenCalled()
  193. // expect(node.getAttribute('id')).toBe('test')
  194. // expect(node.getAttribute('class')).toBe('c2 c1')
  195. // expect(node.style.color).toBe('red')
  196. // expect(node.style.fontWeight).toBe('bold')
  197. // expect(node.hasAttribute('foo')).toBe(false)
  198. // })
  199. })