2019-08-27 04:14:23 +08:00
|
|
|
import {
|
|
|
|
h,
|
|
|
|
provide,
|
|
|
|
inject,
|
|
|
|
InjectionKey,
|
|
|
|
ref,
|
|
|
|
nextTick,
|
|
|
|
Ref,
|
|
|
|
readonly,
|
2021-06-10 00:02:05 +08:00
|
|
|
reactive,
|
|
|
|
defineComponent
|
2019-08-27 04:14:23 +08:00
|
|
|
} from '../src/index'
|
2020-01-22 22:29:35 +08:00
|
|
|
import { render, nodeOps, serialize } from '@vue/runtime-test'
|
2019-08-27 04:14:23 +08:00
|
|
|
|
2019-08-23 10:15:39 +08:00
|
|
|
// reference: https://vue-composition-api-rfc.netlify.com/api.html#provide-inject
|
2019-08-24 03:32:19 +08:00
|
|
|
describe('api: provide/inject', () => {
|
2019-08-27 04:14:23 +08:00
|
|
|
it('string keys', () => {
|
|
|
|
const Provider = {
|
|
|
|
setup() {
|
|
|
|
provide('foo', 1)
|
|
|
|
return () => h(Middle)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const Middle = {
|
|
|
|
render: () => h(Consumer)
|
|
|
|
}
|
|
|
|
|
|
|
|
const Consumer = {
|
|
|
|
setup() {
|
|
|
|
const foo = inject('foo')
|
|
|
|
return () => foo
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const root = nodeOps.createElement('div')
|
|
|
|
render(h(Provider), root)
|
|
|
|
expect(serialize(root)).toBe(`<div>1</div>`)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('symbol keys', () => {
|
|
|
|
// also verifies InjectionKey type sync
|
|
|
|
const key: InjectionKey<number> = Symbol()
|
|
|
|
|
|
|
|
const Provider = {
|
|
|
|
setup() {
|
|
|
|
provide(key, 1)
|
|
|
|
return () => h(Middle)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const Middle = {
|
|
|
|
render: () => h(Consumer)
|
|
|
|
}
|
|
|
|
|
|
|
|
const Consumer = {
|
|
|
|
setup() {
|
|
|
|
const foo = inject(key) || 1
|
|
|
|
return () => foo + 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const root = nodeOps.createElement('div')
|
|
|
|
render(h(Provider), root)
|
|
|
|
expect(serialize(root)).toBe(`<div>2</div>`)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('default values', () => {
|
|
|
|
const Provider = {
|
|
|
|
setup() {
|
|
|
|
provide('foo', 'foo')
|
|
|
|
return () => h(Middle)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const Middle = {
|
|
|
|
render: () => h(Consumer)
|
|
|
|
}
|
|
|
|
|
|
|
|
const Consumer = {
|
|
|
|
setup() {
|
|
|
|
// default value should be ignored if value is provided
|
|
|
|
const foo = inject('foo', 'fooDefault')
|
|
|
|
// default value should be used if value is not provided
|
|
|
|
const bar = inject('bar', 'bar')
|
|
|
|
return () => foo + bar
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const root = nodeOps.createElement('div')
|
|
|
|
render(h(Provider), root)
|
|
|
|
expect(serialize(root)).toBe(`<div>foobar</div>`)
|
|
|
|
})
|
|
|
|
|
2021-06-10 00:02:05 +08:00
|
|
|
it('bound to instance', () => {
|
|
|
|
const Provider = {
|
|
|
|
setup() {
|
|
|
|
return () => h(Consumer)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const Consumer = defineComponent({
|
|
|
|
name: 'Consumer',
|
|
|
|
inject: {
|
|
|
|
foo: {
|
|
|
|
from: 'foo',
|
|
|
|
default() {
|
|
|
|
return this!.$options.name
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
render() {
|
|
|
|
// @ts-ignore
|
|
|
|
return this.foo
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
const root = nodeOps.createElement('div')
|
|
|
|
render(h(Provider), root)
|
|
|
|
expect(serialize(root)).toBe(`<div>Consumer</div>`)
|
|
|
|
})
|
|
|
|
|
2019-08-27 04:14:23 +08:00
|
|
|
it('nested providers', () => {
|
|
|
|
const ProviderOne = {
|
|
|
|
setup() {
|
|
|
|
provide('foo', 'foo')
|
|
|
|
provide('bar', 'bar')
|
|
|
|
return () => h(ProviderTwo)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const ProviderTwo = {
|
|
|
|
setup() {
|
|
|
|
// override parent value
|
|
|
|
provide('foo', 'fooOverride')
|
|
|
|
provide('baz', 'baz')
|
|
|
|
return () => h(Consumer)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const Consumer = {
|
|
|
|
setup() {
|
|
|
|
const foo = inject('foo')
|
|
|
|
const bar = inject('bar')
|
|
|
|
const baz = inject('baz')
|
|
|
|
return () => [foo, bar, baz].join(',')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const root = nodeOps.createElement('div')
|
|
|
|
render(h(ProviderOne), root)
|
|
|
|
expect(serialize(root)).toBe(`<div>fooOverride,bar,baz</div>`)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('reactivity with refs', async () => {
|
|
|
|
const count = ref(1)
|
|
|
|
|
|
|
|
const Provider = {
|
|
|
|
setup() {
|
|
|
|
provide('count', count)
|
|
|
|
return () => h(Middle)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const Middle = {
|
|
|
|
render: () => h(Consumer)
|
|
|
|
}
|
|
|
|
|
|
|
|
const Consumer = {
|
|
|
|
setup() {
|
2021-02-06 02:55:23 +08:00
|
|
|
const count = inject<Ref<number>>('count')!
|
2019-08-27 04:14:23 +08:00
|
|
|
return () => count.value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const root = nodeOps.createElement('div')
|
|
|
|
render(h(Provider), root)
|
|
|
|
expect(serialize(root)).toBe(`<div>1</div>`)
|
|
|
|
|
|
|
|
count.value++
|
|
|
|
await nextTick()
|
|
|
|
expect(serialize(root)).toBe(`<div>2</div>`)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('reactivity with readonly refs', async () => {
|
|
|
|
const count = ref(1)
|
|
|
|
|
|
|
|
const Provider = {
|
|
|
|
setup() {
|
|
|
|
provide('count', readonly(count))
|
|
|
|
return () => h(Middle)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const Middle = {
|
|
|
|
render: () => h(Consumer)
|
|
|
|
}
|
|
|
|
|
|
|
|
const Consumer = {
|
|
|
|
setup() {
|
2021-02-06 02:55:23 +08:00
|
|
|
const count = inject<Ref<number>>('count')!
|
2019-08-27 04:14:23 +08:00
|
|
|
// should not work
|
|
|
|
count.value++
|
|
|
|
return () => count.value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const root = nodeOps.createElement('div')
|
|
|
|
render(h(Provider), root)
|
|
|
|
expect(serialize(root)).toBe(`<div>1</div>`)
|
|
|
|
|
|
|
|
expect(
|
|
|
|
`Set operation on key "value" failed: target is readonly`
|
|
|
|
).toHaveBeenWarned()
|
|
|
|
|
|
|
|
// source mutation should still work
|
|
|
|
count.value++
|
|
|
|
await nextTick()
|
|
|
|
expect(serialize(root)).toBe(`<div>2</div>`)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('reactivity with objects', async () => {
|
|
|
|
const rootState = reactive({ count: 1 })
|
|
|
|
|
|
|
|
const Provider = {
|
|
|
|
setup() {
|
|
|
|
provide('state', rootState)
|
|
|
|
return () => h(Middle)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const Middle = {
|
|
|
|
render: () => h(Consumer)
|
|
|
|
}
|
|
|
|
|
|
|
|
const Consumer = {
|
|
|
|
setup() {
|
2021-02-06 02:55:23 +08:00
|
|
|
const state = inject<typeof rootState>('state')!
|
2019-08-27 04:14:23 +08:00
|
|
|
return () => state.count
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const root = nodeOps.createElement('div')
|
|
|
|
render(h(Provider), root)
|
|
|
|
expect(serialize(root)).toBe(`<div>1</div>`)
|
|
|
|
|
|
|
|
rootState.count++
|
|
|
|
await nextTick()
|
|
|
|
expect(serialize(root)).toBe(`<div>2</div>`)
|
|
|
|
})
|
|
|
|
|
|
|
|
it('reactivity with readonly objects', async () => {
|
|
|
|
const rootState = reactive({ count: 1 })
|
|
|
|
|
|
|
|
const Provider = {
|
|
|
|
setup() {
|
|
|
|
provide('state', readonly(rootState))
|
|
|
|
return () => h(Middle)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const Middle = {
|
|
|
|
render: () => h(Consumer)
|
|
|
|
}
|
|
|
|
|
|
|
|
const Consumer = {
|
|
|
|
setup() {
|
2021-02-06 02:55:23 +08:00
|
|
|
const state = inject<typeof rootState>('state')!
|
2019-08-27 04:14:23 +08:00
|
|
|
// should not work
|
|
|
|
state.count++
|
|
|
|
return () => state.count
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const root = nodeOps.createElement('div')
|
|
|
|
render(h(Provider), root)
|
|
|
|
expect(serialize(root)).toBe(`<div>1</div>`)
|
|
|
|
|
|
|
|
expect(
|
|
|
|
`Set operation on key "count" failed: target is readonly`
|
|
|
|
).toHaveBeenWarned()
|
|
|
|
|
|
|
|
rootState.count++
|
|
|
|
await nextTick()
|
|
|
|
expect(serialize(root)).toBe(`<div>2</div>`)
|
|
|
|
})
|
2019-08-28 02:42:05 +08:00
|
|
|
|
|
|
|
it('should warn unfound', () => {
|
|
|
|
const Provider = {
|
|
|
|
setup() {
|
|
|
|
return () => h(Middle)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const Middle = {
|
|
|
|
render: () => h(Consumer)
|
|
|
|
}
|
|
|
|
|
|
|
|
const Consumer = {
|
|
|
|
setup() {
|
|
|
|
const foo = inject('foo')
|
|
|
|
expect(foo).toBeUndefined()
|
|
|
|
return () => foo
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const root = nodeOps.createElement('div')
|
|
|
|
render(h(Provider), root)
|
|
|
|
expect(serialize(root)).toBe(`<div><!----></div>`)
|
|
|
|
expect(`injection "foo" not found.`).toHaveBeenWarned()
|
|
|
|
})
|
2020-03-31 03:24:55 +08:00
|
|
|
|
|
|
|
it('should not warn when default value is undefined', () => {
|
|
|
|
const Provider = {
|
|
|
|
setup() {
|
|
|
|
return () => h(Middle)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const Middle = {
|
|
|
|
render: () => h(Consumer)
|
|
|
|
}
|
|
|
|
|
|
|
|
const Consumer = {
|
|
|
|
setup() {
|
|
|
|
const foo = inject('foo', undefined)
|
|
|
|
return () => foo
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const root = nodeOps.createElement('div')
|
|
|
|
render(h(Provider), root)
|
|
|
|
expect(`injection "foo" not found.`).not.toHaveBeenWarned()
|
|
|
|
})
|
2020-10-20 08:45:48 +08:00
|
|
|
|
|
|
|
// #2400
|
|
|
|
it('should not self-inject', () => {
|
|
|
|
const Comp = {
|
|
|
|
setup() {
|
|
|
|
provide('foo', 'foo')
|
|
|
|
const injection = inject('foo', null)
|
|
|
|
return () => injection
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const root = nodeOps.createElement('div')
|
|
|
|
render(h(Comp), root)
|
|
|
|
expect(serialize(root)).toBe(`<div><!----></div>`)
|
|
|
|
})
|
2019-08-24 03:32:19 +08:00
|
|
|
})
|