vdomAttrsFallthrough.spec.ts 5.7 KB

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