vdomAttrsFallthrough.spec.ts 5.7 KB

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