| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368 |
- import {
- ref,
- nodeOps,
- h,
- render,
- nextTick,
- defineComponent,
- reactive,
- serializeInner,
- shallowRef
- } from '@vue/runtime-test'
- // reference: https://vue-composition-api-rfc.netlify.com/api.html#template-refs
- describe('api: template refs', () => {
- it('string ref mount', () => {
- const root = nodeOps.createElement('div')
- const el = ref(null)
- const Comp = {
- setup() {
- return {
- refKey: el
- }
- },
- render() {
- return h('div', { ref: 'refKey' })
- }
- }
- render(h(Comp), root)
- expect(el.value).toBe(root.children[0])
- })
- it('string ref update', async () => {
- const root = nodeOps.createElement('div')
- const fooEl = ref(null)
- const barEl = ref(null)
- const refKey = ref('foo')
- const Comp = {
- setup() {
- return {
- foo: fooEl,
- bar: barEl
- }
- },
- render() {
- return h('div', { ref: refKey.value })
- }
- }
- render(h(Comp), root)
- expect(fooEl.value).toBe(root.children[0])
- expect(barEl.value).toBe(null)
- refKey.value = 'bar'
- await nextTick()
- expect(fooEl.value).toBe(null)
- expect(barEl.value).toBe(root.children[0])
- })
- it('string ref unmount', async () => {
- const root = nodeOps.createElement('div')
- const el = ref(null)
- const toggle = ref(true)
- const Comp = {
- setup() {
- return {
- refKey: el
- }
- },
- render() {
- return toggle.value ? h('div', { ref: 'refKey' }) : null
- }
- }
- render(h(Comp), root)
- expect(el.value).toBe(root.children[0])
- toggle.value = false
- await nextTick()
- expect(el.value).toBe(null)
- })
- it('function ref mount', () => {
- const root = nodeOps.createElement('div')
- const fn = jest.fn()
- const Comp = defineComponent(() => () => h('div', { ref: fn }))
- render(h(Comp), root)
- expect(fn.mock.calls[0][0]).toBe(root.children[0])
- })
- it('function ref update', async () => {
- const root = nodeOps.createElement('div')
- const fn1 = jest.fn()
- const fn2 = jest.fn()
- const fn = ref(fn1)
- const Comp = defineComponent(() => () => h('div', { ref: fn.value }))
- render(h(Comp), root)
- expect(fn1.mock.calls).toHaveLength(1)
- expect(fn1.mock.calls[0][0]).toBe(root.children[0])
- expect(fn2.mock.calls).toHaveLength(0)
- fn.value = fn2
- await nextTick()
- expect(fn1.mock.calls).toHaveLength(1)
- expect(fn2.mock.calls).toHaveLength(1)
- expect(fn2.mock.calls[0][0]).toBe(root.children[0])
- })
- it('function ref unmount', async () => {
- const root = nodeOps.createElement('div')
- const fn = jest.fn()
- const toggle = ref(true)
- const Comp = defineComponent(
- () => () => toggle.value ? h('div', { ref: fn }) : null
- )
- render(h(Comp), root)
- expect(fn.mock.calls[0][0]).toBe(root.children[0])
- toggle.value = false
- await nextTick()
- expect(fn.mock.calls[1][0]).toBe(null)
- })
- it('render function ref mount', () => {
- const root = nodeOps.createElement('div')
- const el = ref(null)
- const Comp = {
- setup() {
- return () => h('div', { ref: el })
- }
- }
- render(h(Comp), root)
- expect(el.value).toBe(root.children[0])
- })
- it('render function ref update', async () => {
- const root = nodeOps.createElement('div')
- const refs = {
- foo: ref(null),
- bar: ref(null)
- }
- const refKey = ref<keyof typeof refs>('foo')
- const Comp = {
- setup() {
- return () => h('div', { ref: refs[refKey.value] })
- }
- }
- render(h(Comp), root)
- expect(refs.foo.value).toBe(root.children[0])
- expect(refs.bar.value).toBe(null)
- refKey.value = 'bar'
- await nextTick()
- expect(refs.foo.value).toBe(null)
- expect(refs.bar.value).toBe(root.children[0])
- })
- it('render function ref unmount', async () => {
- const root = nodeOps.createElement('div')
- const el = ref(null)
- const toggle = ref(true)
- const Comp = {
- setup() {
- return () => (toggle.value ? h('div', { ref: el }) : null)
- }
- }
- render(h(Comp), root)
- expect(el.value).toBe(root.children[0])
- toggle.value = false
- await nextTick()
- expect(el.value).toBe(null)
- })
- test('string ref inside slots', async () => {
- const root = nodeOps.createElement('div')
- const spy = jest.fn()
- const Child = {
- render(this: any) {
- return this.$slots.default()
- }
- }
- const Comp = {
- render() {
- return h(Child, () => {
- return h('div', { ref: 'foo' })
- })
- },
- mounted(this: any) {
- spy(this.$refs.foo.tag)
- }
- }
- render(h(Comp), root)
- expect(spy).toHaveBeenCalledWith('div')
- })
- it('should work with direct reactive property', () => {
- const root = nodeOps.createElement('div')
- const state = reactive({
- refKey: null
- })
- const Comp = {
- setup() {
- return state
- },
- render() {
- return h('div', { ref: 'refKey' })
- }
- }
- render(h(Comp), root)
- expect(state.refKey).toBe(root.children[0])
- })
- test('multiple root refs', () => {
- const root = nodeOps.createElement('div')
- const refKey1 = ref(null)
- const refKey2 = ref(null)
- const refKey3 = ref(null)
- const Comp = {
- setup() {
- return {
- refKey1,
- refKey2,
- refKey3
- }
- },
- render() {
- return [
- h('div', { ref: 'refKey1' }),
- h('div', { ref: 'refKey2' }),
- h('div', { ref: 'refKey3' })
- ]
- }
- }
- render(h(Comp), root)
- expect(refKey1.value).toBe(root.children[1])
- expect(refKey2.value).toBe(root.children[2])
- expect(refKey3.value).toBe(root.children[3])
- })
- // #1505
- test('reactive template ref in the same template', async () => {
- const Comp = {
- setup() {
- const el = ref()
- return { el }
- },
- render(this: any) {
- return h('div', { id: 'foo', ref: 'el' }, this.el && this.el.props.id)
- }
- }
- const root = nodeOps.createElement('div')
- render(h(Comp), root)
- // ref not ready on first render, but should queue an update immediately
- expect(serializeInner(root)).toBe(`<div id="foo"></div>`)
- await nextTick()
- // ref should be updated
- expect(serializeInner(root)).toBe(`<div id="foo">foo</div>`)
- })
- // #1834
- test('exchange refs', async () => {
- const refToggle = ref(false)
- const spy = jest.fn()
- const Comp = {
- render(this: any) {
- return [
- h('p', { ref: refToggle.value ? 'foo' : 'bar' }),
- h('i', { ref: refToggle.value ? 'bar' : 'foo' })
- ]
- },
- mounted(this: any) {
- spy(this.$refs.foo.tag, this.$refs.bar.tag)
- },
- updated(this: any) {
- spy(this.$refs.foo.tag, this.$refs.bar.tag)
- }
- }
- const root = nodeOps.createElement('div')
- render(h(Comp), root)
- expect(spy.mock.calls[0][0]).toBe('i')
- expect(spy.mock.calls[0][1]).toBe('p')
- refToggle.value = true
- await nextTick()
- expect(spy.mock.calls[1][0]).toBe('p')
- expect(spy.mock.calls[1][1]).toBe('i')
- })
- // #1789
- test('toggle the same ref to different elements', async () => {
- const refToggle = ref(false)
- const spy = jest.fn()
- const Comp = {
- render(this: any) {
- return refToggle.value ? h('p', { ref: 'foo' }) : h('i', { ref: 'foo' })
- },
- mounted(this: any) {
- spy(this.$refs.foo.tag)
- },
- updated(this: any) {
- spy(this.$refs.foo.tag)
- }
- }
- const root = nodeOps.createElement('div')
- render(h(Comp), root)
- expect(spy.mock.calls[0][0]).toBe('i')
- refToggle.value = true
- await nextTick()
- expect(spy.mock.calls[1][0]).toBe('p')
- })
- // #2078
- test('handling multiple merged refs', async () => {
- const Foo = {
- render: () => h('div', 'foo')
- }
- const Bar = {
- render: () => h('div', 'bar')
- }
- const viewRef = shallowRef<any>(Foo)
- const elRef1 = ref()
- const elRef2 = ref()
- const App = {
- render() {
- if (!viewRef.value) {
- return null
- }
- const view = h(viewRef.value, { ref: elRef1 })
- return h(view, { ref: elRef2 })
- }
- }
- const root = nodeOps.createElement('div')
- render(h(App), root)
- expect(serializeInner(elRef1.value.$el)).toBe('foo')
- expect(elRef1.value).toBe(elRef2.value)
- viewRef.value = Bar
- await nextTick()
- expect(serializeInner(elRef1.value.$el)).toBe('bar')
- expect(elRef1.value).toBe(elRef2.value)
- viewRef.value = null
- await nextTick()
- expect(elRef1.value).toBeNull()
- expect(elRef1.value).toBe(elRef2.value)
- })
- })
|