rendererComponent.spec.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. import {
  2. ref,
  3. h,
  4. render,
  5. nodeOps,
  6. serializeInner,
  7. nextTick,
  8. VNode,
  9. provide,
  10. inject,
  11. Ref
  12. } from '@vue/runtime-test'
  13. describe('renderer: component', () => {
  14. test('should update parent(hoc) component host el when child component self update', async () => {
  15. const value = ref(true)
  16. let parentVnode: VNode
  17. let childVnode1: VNode
  18. let childVnode2: VNode
  19. const Parent = {
  20. render: () => {
  21. // let Parent first rerender
  22. return (parentVnode = h(Child))
  23. }
  24. }
  25. const Child = {
  26. render: () => {
  27. return value.value
  28. ? (childVnode1 = h('div'))
  29. : (childVnode2 = h('span'))
  30. }
  31. }
  32. const root = nodeOps.createElement('div')
  33. render(h(Parent), root)
  34. expect(serializeInner(root)).toBe(`<div></div>`)
  35. expect(parentVnode!.el).toBe(childVnode1!.el)
  36. value.value = false
  37. await nextTick()
  38. expect(serializeInner(root)).toBe(`<span></span>`)
  39. expect(parentVnode!.el).toBe(childVnode2!.el)
  40. })
  41. it('should create an Component with props', () => {
  42. const Comp = {
  43. render: () => {
  44. return h('div')
  45. }
  46. }
  47. const root = nodeOps.createElement('div')
  48. render(h(Comp, { id: 'foo', class: 'bar' }), root)
  49. expect(serializeInner(root)).toBe(`<div id="foo" class="bar"></div>`)
  50. })
  51. it('should create an Component with direct text children', () => {
  52. const Comp = {
  53. render: () => {
  54. return h('div', 'test')
  55. }
  56. }
  57. const root = nodeOps.createElement('div')
  58. render(h(Comp, { id: 'foo', class: 'bar' }), root)
  59. expect(serializeInner(root)).toBe(`<div id="foo" class="bar">test</div>`)
  60. })
  61. it('should update an Component tag which is already mounted', () => {
  62. const Comp1 = {
  63. render: () => {
  64. return h('div', 'foo')
  65. }
  66. }
  67. const root = nodeOps.createElement('div')
  68. render(h(Comp1), root)
  69. expect(serializeInner(root)).toBe('<div>foo</div>')
  70. const Comp2 = {
  71. render: () => {
  72. return h('span', 'foo')
  73. }
  74. }
  75. render(h(Comp2), root)
  76. expect(serializeInner(root)).toBe('<span>foo</span>')
  77. })
  78. // #2072
  79. it('should not update Component if only changed props are declared emit listeners', () => {
  80. const Comp1 = {
  81. emits: ['foo'],
  82. updated: jest.fn(),
  83. render: () => null
  84. }
  85. const root = nodeOps.createElement('div')
  86. render(
  87. h(Comp1, {
  88. onFoo: () => {}
  89. }),
  90. root
  91. )
  92. render(
  93. h(Comp1, {
  94. onFoo: () => {}
  95. }),
  96. root
  97. )
  98. expect(Comp1.updated).not.toHaveBeenCalled()
  99. })
  100. // #2043
  101. test('component child synchronously updating parent state should trigger parent re-render', async () => {
  102. const App = {
  103. setup() {
  104. const n = ref(0)
  105. provide('foo', n)
  106. return () => {
  107. return [h('div', n.value), h(Child)]
  108. }
  109. }
  110. }
  111. const Child = {
  112. setup() {
  113. const n = inject<Ref<number>>('foo')!
  114. n.value++
  115. return () => {
  116. return h('div', n.value)
  117. }
  118. }
  119. }
  120. const root = nodeOps.createElement('div')
  121. render(h(App), root)
  122. expect(serializeInner(root)).toBe(`<div>0</div><div>1</div>`)
  123. await nextTick()
  124. expect(serializeInner(root)).toBe(`<div>1</div><div>1</div>`)
  125. })
  126. })