apiTemplateRef.spec.ts 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. import {
  2. ref,
  3. nodeOps,
  4. h,
  5. render,
  6. nextTick,
  7. Ref,
  8. defineComponent,
  9. reactive
  10. } from '@vue/runtime-test'
  11. // reference: https://vue-composition-api-rfc.netlify.com/api.html#template-refs
  12. describe('api: template refs', () => {
  13. it('string ref mount', () => {
  14. const root = nodeOps.createElement('div')
  15. const el = ref(null)
  16. const Comp = {
  17. setup() {
  18. return {
  19. refKey: el
  20. }
  21. },
  22. render() {
  23. return h('div', { ref: 'refKey' })
  24. }
  25. }
  26. render(h(Comp), root)
  27. expect(el.value).toBe(root.children[0])
  28. })
  29. it('string ref update', async () => {
  30. const root = nodeOps.createElement('div')
  31. const fooEl = ref(null)
  32. const barEl = ref(null)
  33. const refKey = ref('foo')
  34. const Comp = {
  35. setup() {
  36. return {
  37. foo: fooEl,
  38. bar: barEl
  39. }
  40. },
  41. render() {
  42. return h('div', { ref: refKey.value })
  43. }
  44. }
  45. render(h(Comp), root)
  46. expect(fooEl.value).toBe(root.children[0])
  47. expect(barEl.value).toBe(null)
  48. refKey.value = 'bar'
  49. await nextTick()
  50. expect(fooEl.value).toBe(null)
  51. expect(barEl.value).toBe(root.children[0])
  52. })
  53. it('string ref unmount', async () => {
  54. const root = nodeOps.createElement('div')
  55. const el = ref(null)
  56. const toggle = ref(true)
  57. const Comp = {
  58. setup() {
  59. return {
  60. refKey: el
  61. }
  62. },
  63. render() {
  64. return toggle.value ? h('div', { ref: 'refKey' }) : null
  65. }
  66. }
  67. render(h(Comp), root)
  68. expect(el.value).toBe(root.children[0])
  69. toggle.value = false
  70. await nextTick()
  71. expect(el.value).toBe(null)
  72. })
  73. it('function ref mount', () => {
  74. const root = nodeOps.createElement('div')
  75. const fn = jest.fn()
  76. const Comp = defineComponent(() => () => h('div', { ref: fn }))
  77. render(h(Comp), root)
  78. expect(fn.mock.calls[0][0]).toBe(root.children[0])
  79. })
  80. it('function ref update', async () => {
  81. const root = nodeOps.createElement('div')
  82. const fn1 = jest.fn()
  83. const fn2 = jest.fn()
  84. const fn = ref(fn1)
  85. const Comp = defineComponent(() => () => h('div', { ref: fn.value }))
  86. render(h(Comp), root)
  87. expect(fn1.mock.calls).toHaveLength(1)
  88. expect(fn1.mock.calls[0][0]).toBe(root.children[0])
  89. expect(fn2.mock.calls).toHaveLength(0)
  90. fn.value = fn2
  91. await nextTick()
  92. expect(fn1.mock.calls).toHaveLength(1)
  93. expect(fn2.mock.calls).toHaveLength(1)
  94. expect(fn2.mock.calls[0][0]).toBe(root.children[0])
  95. })
  96. it('function ref unmount', async () => {
  97. const root = nodeOps.createElement('div')
  98. const fn = jest.fn()
  99. const toggle = ref(true)
  100. const Comp = defineComponent(() => () =>
  101. toggle.value ? h('div', { ref: fn }) : null
  102. )
  103. render(h(Comp), root)
  104. expect(fn.mock.calls[0][0]).toBe(root.children[0])
  105. toggle.value = false
  106. await nextTick()
  107. expect(fn.mock.calls[1][0]).toBe(null)
  108. })
  109. it('render function ref mount', () => {
  110. const root = nodeOps.createElement('div')
  111. const el = ref(null)
  112. const Comp = {
  113. setup() {
  114. return () => h('div', { ref: el })
  115. }
  116. }
  117. render(h(Comp), root)
  118. expect(el.value).toBe(root.children[0])
  119. })
  120. it('render function ref update', async () => {
  121. const root = nodeOps.createElement('div')
  122. const refs = {
  123. foo: ref(null),
  124. bar: ref(null)
  125. }
  126. const refKey = ref('foo') as Ref<keyof typeof refs>
  127. const Comp = {
  128. setup() {
  129. return () => h('div', { ref: refs[refKey.value] })
  130. }
  131. }
  132. render(h(Comp), root)
  133. expect(refs.foo.value).toBe(root.children[0])
  134. expect(refs.bar.value).toBe(null)
  135. refKey.value = 'bar'
  136. await nextTick()
  137. expect(refs.foo.value).toBe(null)
  138. expect(refs.bar.value).toBe(root.children[0])
  139. })
  140. it('render function ref unmount', async () => {
  141. const root = nodeOps.createElement('div')
  142. const el = ref(null)
  143. const toggle = ref(true)
  144. const Comp = {
  145. setup() {
  146. return () => (toggle.value ? h('div', { ref: el }) : null)
  147. }
  148. }
  149. render(h(Comp), root)
  150. expect(el.value).toBe(root.children[0])
  151. toggle.value = false
  152. await nextTick()
  153. expect(el.value).toBe(null)
  154. })
  155. test('string ref inside slots', async () => {
  156. const root = nodeOps.createElement('div')
  157. const spy = jest.fn()
  158. const Child = {
  159. render(this: any) {
  160. return this.$slots.default()
  161. }
  162. }
  163. const Comp = {
  164. render() {
  165. return h(Child, () => {
  166. return h('div', { ref: 'foo' })
  167. })
  168. },
  169. mounted(this: any) {
  170. spy(this.$refs.foo.tag)
  171. }
  172. }
  173. render(h(Comp), root)
  174. expect(spy).toHaveBeenCalledWith('div')
  175. })
  176. it('should work with direct reactive property', () => {
  177. const root = nodeOps.createElement('div')
  178. const state = reactive({
  179. refKey: null
  180. })
  181. const Comp = {
  182. setup() {
  183. return state
  184. },
  185. render() {
  186. return h('div', { ref: 'refKey' })
  187. }
  188. }
  189. render(h(Comp), root)
  190. expect(state.refKey).toBe(root.children[0])
  191. })
  192. })