|
|
@@ -1,33 +1,37 @@
|
|
|
// NOTE: This test is implemented based on the case of `runtime-core/__test__/componentProps.spec.ts`.
|
|
|
|
|
|
import {
|
|
|
- createComponent,
|
|
|
- defineComponent,
|
|
|
- getCurrentInstance,
|
|
|
+ // currentInstance,
|
|
|
+ inject,
|
|
|
nextTick,
|
|
|
+ provide,
|
|
|
ref,
|
|
|
- setText,
|
|
|
- template,
|
|
|
toRefs,
|
|
|
watch,
|
|
|
- watchEffect,
|
|
|
-} from '../src/_old'
|
|
|
+} from '@vue/runtime-dom'
|
|
|
+import {
|
|
|
+ createComponent,
|
|
|
+ defineVaporComponent,
|
|
|
+ renderEffect,
|
|
|
+ setText,
|
|
|
+ template,
|
|
|
+} from '../src'
|
|
|
import { makeRender } from './_utils'
|
|
|
+import type { RawProps } from '../src/componentProps'
|
|
|
|
|
|
const define = makeRender<any>()
|
|
|
|
|
|
describe('component: props', () => {
|
|
|
- // NOTE: no proxy
|
|
|
test('stateful', () => {
|
|
|
let props: any
|
|
|
let attrs: any
|
|
|
|
|
|
const { render } = define({
|
|
|
props: ['fooBar', 'barBaz'],
|
|
|
- render() {
|
|
|
- const instance = getCurrentInstance()!
|
|
|
- props = instance.props
|
|
|
- attrs = instance.attrs
|
|
|
+ setup(_props: any, { attrs: _attrs }: any) {
|
|
|
+ props = _props
|
|
|
+ attrs = _attrs
|
|
|
+ return []
|
|
|
},
|
|
|
})
|
|
|
|
|
|
@@ -51,17 +55,16 @@ describe('component: props', () => {
|
|
|
expect(attrs).toEqual({ qux: 5 })
|
|
|
})
|
|
|
|
|
|
- test.fails('stateful with setup', () => {
|
|
|
+ test('stateful with setup', () => {
|
|
|
let props: any
|
|
|
let attrs: any
|
|
|
|
|
|
const { render } = define({
|
|
|
props: ['foo'],
|
|
|
setup(_props: any, { attrs: _attrs }: any) {
|
|
|
- return () => {
|
|
|
- props = _props
|
|
|
- attrs = _attrs
|
|
|
- }
|
|
|
+ props = _props
|
|
|
+ attrs = _attrs
|
|
|
+ return []
|
|
|
},
|
|
|
})
|
|
|
|
|
|
@@ -82,12 +85,13 @@ describe('component: props', () => {
|
|
|
let props: any
|
|
|
let attrs: any
|
|
|
|
|
|
- const { component: Comp, render } = define((_props: any) => {
|
|
|
- const instance = getCurrentInstance()!
|
|
|
- props = instance.props
|
|
|
- attrs = instance.attrs
|
|
|
- return {}
|
|
|
- })
|
|
|
+ const { component: Comp, render } = define(
|
|
|
+ (_props: any, { attrs: _attrs }: any) => {
|
|
|
+ props = _props
|
|
|
+ attrs = _attrs
|
|
|
+ return []
|
|
|
+ },
|
|
|
+ )
|
|
|
Comp.props = ['foo']
|
|
|
|
|
|
render({ foo: () => 1, bar: () => 2 })
|
|
|
@@ -108,10 +112,9 @@ describe('component: props', () => {
|
|
|
let attrs: any
|
|
|
|
|
|
const { render } = define((_props: any, { attrs: _attrs }: any) => {
|
|
|
- const instance = getCurrentInstance()!
|
|
|
- props = instance.props
|
|
|
- attrs = instance.attrs
|
|
|
- return {}
|
|
|
+ props = _props
|
|
|
+ attrs = _attrs
|
|
|
+ return []
|
|
|
})
|
|
|
|
|
|
render({ foo: () => 1 })
|
|
|
@@ -134,9 +137,9 @@ describe('component: props', () => {
|
|
|
baz: Boolean,
|
|
|
qux: Boolean,
|
|
|
},
|
|
|
- render() {
|
|
|
- const instance = getCurrentInstance()!
|
|
|
- props = instance.props
|
|
|
+ setup(_props: any) {
|
|
|
+ props = _props
|
|
|
+ return []
|
|
|
},
|
|
|
})
|
|
|
|
|
|
@@ -151,7 +154,7 @@ describe('component: props', () => {
|
|
|
expect(props.bar).toBe(true)
|
|
|
expect(props.baz).toBe(true)
|
|
|
expect(props.qux).toBe('ok')
|
|
|
- // expect('type check failed for prop "qux"').toHaveBeenWarned()
|
|
|
+ expect('type check failed for prop "qux"').toHaveBeenWarned()
|
|
|
})
|
|
|
|
|
|
test('default value', () => {
|
|
|
@@ -172,9 +175,9 @@ describe('component: props', () => {
|
|
|
default: defaultBaz,
|
|
|
},
|
|
|
},
|
|
|
- render() {
|
|
|
- const instance = getCurrentInstance()!
|
|
|
- props = instance.props
|
|
|
+ setup(_props: any) {
|
|
|
+ props = _props
|
|
|
+ return []
|
|
|
},
|
|
|
})
|
|
|
|
|
|
@@ -214,18 +217,40 @@ describe('component: props', () => {
|
|
|
// expect(defaultFn).toHaveBeenCalledTimes(1) // failed: caching is not supported (called 2 times)
|
|
|
})
|
|
|
|
|
|
- test.todo('using inject in default value factory', () => {
|
|
|
- // TODO: impl inject
|
|
|
+ test('using inject in default value factory', () => {
|
|
|
+ let props: any
|
|
|
+
|
|
|
+ const Child = defineVaporComponent({
|
|
|
+ props: {
|
|
|
+ test: {
|
|
|
+ default: () => inject('test', 'default'),
|
|
|
+ },
|
|
|
+ },
|
|
|
+ setup(_props) {
|
|
|
+ props = _props
|
|
|
+ return []
|
|
|
+ },
|
|
|
+ })
|
|
|
+
|
|
|
+ const { render } = define({
|
|
|
+ setup() {
|
|
|
+ provide('test', 'injected')
|
|
|
+ return createComponent(Child)
|
|
|
+ },
|
|
|
+ })
|
|
|
+
|
|
|
+ render()
|
|
|
+
|
|
|
+ expect(props.test).toBe('injected')
|
|
|
})
|
|
|
|
|
|
test('optimized props updates', async () => {
|
|
|
const t0 = template('<div>')
|
|
|
const { component: Child } = define({
|
|
|
props: ['foo'],
|
|
|
- render() {
|
|
|
- const instance = getCurrentInstance()!
|
|
|
+ setup(props: any) {
|
|
|
const n0 = t0()
|
|
|
- watchEffect(() => setText(n0, instance.props.foo))
|
|
|
+ renderEffect(() => setText(n0, props.foo))
|
|
|
return n0
|
|
|
},
|
|
|
})
|
|
|
@@ -278,7 +303,7 @@ describe('component: props', () => {
|
|
|
type: Number,
|
|
|
},
|
|
|
},
|
|
|
- render() {
|
|
|
+ setup() {
|
|
|
return t0()
|
|
|
},
|
|
|
}).render(props)
|
|
|
@@ -286,46 +311,54 @@ describe('component: props', () => {
|
|
|
expect(mockFn).toHaveBeenCalledWith(1, { foo: 1, bar: 2 })
|
|
|
})
|
|
|
|
|
|
- // TODO: impl setter and warnner
|
|
|
- test.todo(
|
|
|
- 'validator should not be able to mutate other props',
|
|
|
- async () => {
|
|
|
- const mockFn = vi.fn((...args: any[]) => true)
|
|
|
- defineComponent({
|
|
|
- props: {
|
|
|
- foo: {
|
|
|
- type: Number,
|
|
|
- validator: (value: any, props: any) => !!(props.bar = 1),
|
|
|
- },
|
|
|
- bar: {
|
|
|
- type: Number,
|
|
|
- validator: (value: any) => mockFn(value),
|
|
|
- },
|
|
|
- },
|
|
|
- render() {
|
|
|
- const t0 = template('<div/>')
|
|
|
- const n0 = t0()
|
|
|
- return n0
|
|
|
- },
|
|
|
- }).render!({
|
|
|
- foo() {
|
|
|
- return 1
|
|
|
+ test('validator should not be able to mutate other props', async () => {
|
|
|
+ const mockFn = vi.fn((...args: any[]) => true)
|
|
|
+ define({
|
|
|
+ props: {
|
|
|
+ foo: {
|
|
|
+ type: Number,
|
|
|
+ validator: (value: any, props: any) => !!(props.bar = 1),
|
|
|
},
|
|
|
- bar() {
|
|
|
- return 2
|
|
|
+ bar: {
|
|
|
+ type: Number,
|
|
|
+ validator: (value: any) => mockFn(value),
|
|
|
},
|
|
|
- })
|
|
|
+ },
|
|
|
+ setup() {
|
|
|
+ const t0 = template('<div/>')
|
|
|
+ const n0 = t0()
|
|
|
+ return n0
|
|
|
+ },
|
|
|
+ }).render!({
|
|
|
+ foo() {
|
|
|
+ return 1
|
|
|
+ },
|
|
|
+ bar() {
|
|
|
+ return 2
|
|
|
+ },
|
|
|
+ })
|
|
|
|
|
|
- expect(
|
|
|
- `Set operation on key "bar" failed: taris readonly.`,
|
|
|
- ).toHaveBeenWarnedLast()
|
|
|
- expect(mockFn).toHaveBeenCalledWith(2)
|
|
|
- },
|
|
|
- )
|
|
|
+ expect(
|
|
|
+ `Set operation on key "bar" failed: target is readonly.`,
|
|
|
+ ).toHaveBeenWarnedLast()
|
|
|
+ expect(mockFn).toHaveBeenCalledWith(2)
|
|
|
+ })
|
|
|
})
|
|
|
|
|
|
- test.todo('warn props mutation', () => {
|
|
|
- // TODO: impl warn
|
|
|
+ test('warn props mutation', () => {
|
|
|
+ let props: any
|
|
|
+ const { render } = define({
|
|
|
+ props: ['foo'],
|
|
|
+ setup(_props: any) {
|
|
|
+ props = _props
|
|
|
+ return []
|
|
|
+ },
|
|
|
+ })
|
|
|
+ render({ foo: () => 1 })
|
|
|
+ expect(props.foo).toBe(1)
|
|
|
+
|
|
|
+ props.foo = 2
|
|
|
+ expect(`Attempt to mutate prop "foo" failed`).toHaveBeenWarned()
|
|
|
})
|
|
|
|
|
|
test('warn absent required props', () => {
|
|
|
@@ -336,7 +369,7 @@ describe('component: props', () => {
|
|
|
num: { type: Number, required: true },
|
|
|
},
|
|
|
setup() {
|
|
|
- return () => null
|
|
|
+ return []
|
|
|
},
|
|
|
}).render()
|
|
|
expect(`Missing required prop: "bool"`).toHaveBeenWarned()
|
|
|
@@ -354,7 +387,7 @@ describe('component: props', () => {
|
|
|
fooBar: { type: String, required: true },
|
|
|
},
|
|
|
setup() {
|
|
|
- return () => null
|
|
|
+ return []
|
|
|
},
|
|
|
}).render({
|
|
|
['foo-bar']: () => 'hello',
|
|
|
@@ -368,10 +401,9 @@ describe('component: props', () => {
|
|
|
props: {
|
|
|
foo: BigInt,
|
|
|
},
|
|
|
- render() {
|
|
|
- const instance = getCurrentInstance()!
|
|
|
+ setup(props: any) {
|
|
|
const n0 = t0()
|
|
|
- watchEffect(() => setText(n0, instance.props.foo))
|
|
|
+ renderEffect(() => setText(n0, props.foo))
|
|
|
return n0
|
|
|
},
|
|
|
}).render({
|
|
|
@@ -382,10 +414,44 @@ describe('component: props', () => {
|
|
|
})
|
|
|
|
|
|
// #3474
|
|
|
- test.todo(
|
|
|
- 'should cache the value returned from the default factory to avoid unnecessary watcher trigger',
|
|
|
- () => {},
|
|
|
- )
|
|
|
+ test('should cache the value returned from the default factory to avoid unnecessary watcher trigger', async () => {
|
|
|
+ let count = 0
|
|
|
+
|
|
|
+ const { render, html } = define({
|
|
|
+ props: {
|
|
|
+ foo: {
|
|
|
+ type: Object,
|
|
|
+ default: () => ({ val: 1 }),
|
|
|
+ },
|
|
|
+ bar: Number,
|
|
|
+ },
|
|
|
+ setup(props: any) {
|
|
|
+ watch(
|
|
|
+ () => props.foo,
|
|
|
+ () => {
|
|
|
+ count++
|
|
|
+ },
|
|
|
+ )
|
|
|
+ const t0 = template('<h1></h1>')
|
|
|
+ const n0 = t0()
|
|
|
+ renderEffect(() => {
|
|
|
+ setText(n0, props.foo.val, props.bar)
|
|
|
+ })
|
|
|
+ return n0
|
|
|
+ },
|
|
|
+ })
|
|
|
+
|
|
|
+ const foo = ref()
|
|
|
+ const bar = ref(0)
|
|
|
+ render({ foo: () => foo.value, bar: () => bar.value })
|
|
|
+ expect(html()).toBe(`<h1>10</h1>`)
|
|
|
+ expect(count).toBe(0)
|
|
|
+
|
|
|
+ bar.value++
|
|
|
+ await nextTick()
|
|
|
+ expect(html()).toBe(`<h1>11</h1>`)
|
|
|
+ expect(count).toBe(0)
|
|
|
+ })
|
|
|
|
|
|
// #3288
|
|
|
test('declared prop key should be present even if not passed', async () => {
|
|
|
@@ -394,7 +460,6 @@ describe('component: props', () => {
|
|
|
const passFoo = ref(false)
|
|
|
|
|
|
const Comp: any = {
|
|
|
- render() {},
|
|
|
props: {
|
|
|
foo: String,
|
|
|
},
|
|
|
@@ -402,11 +467,14 @@ describe('component: props', () => {
|
|
|
initialKeys = Object.keys(props)
|
|
|
const { foo } = toRefs(props)
|
|
|
watch(foo, changeSpy)
|
|
|
+ return []
|
|
|
},
|
|
|
}
|
|
|
|
|
|
define(() =>
|
|
|
- createComponent(Comp, [() => (passFoo.value ? { foo: () => 'ok' } : {})]),
|
|
|
+ createComponent(Comp, {
|
|
|
+ $: [() => (passFoo.value ? { foo: 'ok' } : {})],
|
|
|
+ } as RawProps),
|
|
|
).render()
|
|
|
|
|
|
expect(initialKeys).toMatchObject(['foo'])
|
|
|
@@ -417,16 +485,17 @@ describe('component: props', () => {
|
|
|
|
|
|
// #3371
|
|
|
test.todo(`avoid double-setting props when casting`, async () => {
|
|
|
- // TODO: proide, slots
|
|
|
+ // TODO: provide, slots
|
|
|
})
|
|
|
|
|
|
- // NOTE: type check is not supported
|
|
|
- test.todo('support null in required + multiple-type declarations', () => {
|
|
|
+ test('support null in required + multiple-type declarations', () => {
|
|
|
const { render } = define({
|
|
|
props: {
|
|
|
foo: { type: [Function, null], required: true },
|
|
|
},
|
|
|
- render() {},
|
|
|
+ setup() {
|
|
|
+ return []
|
|
|
+ },
|
|
|
})
|
|
|
|
|
|
expect(() => {
|
|
|
@@ -442,15 +511,11 @@ describe('component: props', () => {
|
|
|
test('handling attr with undefined value', () => {
|
|
|
const { render, host } = define({
|
|
|
inheritAttrs: false,
|
|
|
- render() {
|
|
|
- const instance = getCurrentInstance()!
|
|
|
+ setup(_: any, { attrs }: any) {
|
|
|
const t0 = template('<div></div>')
|
|
|
const n0 = t0()
|
|
|
- watchEffect(() =>
|
|
|
- setText(
|
|
|
- n0,
|
|
|
- JSON.stringify(instance.attrs) + Object.keys(instance.attrs),
|
|
|
- ),
|
|
|
+ renderEffect(() =>
|
|
|
+ setText(n0, JSON.stringify(attrs) + Object.keys(attrs)),
|
|
|
)
|
|
|
return n0
|
|
|
},
|
|
|
@@ -471,7 +536,7 @@ describe('component: props', () => {
|
|
|
type: String,
|
|
|
},
|
|
|
}
|
|
|
- define({ props, render() {} }).render({ msg: () => 'test' })
|
|
|
+ define({ props, setup: () => [] }).render({ msg: () => 'test' })
|
|
|
|
|
|
expect(Object.keys(props.msg).length).toBe(1)
|
|
|
})
|
|
|
@@ -481,7 +546,7 @@ describe('component: props', () => {
|
|
|
props: {
|
|
|
$foo: String,
|
|
|
},
|
|
|
- render() {},
|
|
|
+ setup: () => [],
|
|
|
})
|
|
|
|
|
|
render({ msg: () => 'test' })
|