Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(runtime-vapor): component props #99

Merged
merged 23 commits into from
Feb 4, 2024
Merged
Changes from 12 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
76de206
test(runtime-vapor): component props
ubugeeei Jan 28, 2024
da5c838
test(runtime-vapor): component props (stateful)
ubugeeei Jan 28, 2024
22c7964
test(runtime-vapor): component props (functional)
ubugeeei Jan 28, 2024
14eb3c1
test(runtime-vapor): component props (boolean casting)
ubugeeei Jan 28, 2024
0f8fb87
test(runtime-vapor): component props (default value)
ubugeeei Jan 28, 2024
0c4a980
test(runtime-vapor): component props (props type support BigInt)
ubugeeei Jan 28, 2024
319f985
chore: add comment
ubugeeei Jan 28, 2024
6e35c1c
chore: rename file
ubugeeei Jan 28, 2024
2a70530
test(runtime-vapor): component props (support null in required + mult…
ubugeeei Jan 29, 2024
6492a56
test(runtime-vapor): component props (should not mutate original prop…
ubugeeei Jan 29, 2024
9950467
chore: remove unused var
ubugeeei Jan 29, 2024
8462b22
chore: comment out unimplemented test
ubugeeei Jan 29, 2024
34a6fc4
test(runtime-vapor): fix: component props (boolean casting)
ubugeeei Jan 30, 2024
f10e396
Merge branch 'main' of github.com:vuejs/core-vapor into ubugeeei/test…
ubugeeei Jan 30, 2024
99ed6b3
test(runtime-vapor): fix: component props (default value)
ubugeeei Jan 30, 2024
f06a64b
Merge branch 'main' of github.com:vuejs/core-vapor into ubugeeei/test…
ubugeeei Jan 31, 2024
2bf2b60
fix: remove import from "vue"
ubugeeei Jan 31, 2024
ab94425
chore: add some test cases (todo)
ubugeeei Jan 31, 2024
7751465
test(runtime-vapor): component props (optimized props updates)
ubugeeei Jan 31, 2024
c36257d
chore: test.todo
ubugeeei Jan 31, 2024
f685ccb
Merge branch 'main' of github.com:vuejs/core-vapor into ubugeeei/test…
ubugeeei Feb 3, 2024
e10a7b4
chore: fix test
ubugeeei Feb 3, 2024
b4063ba
chore: update issue number
sxzz Feb 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
399 changes: 399 additions & 0 deletions packages/runtime-vapor/__tests__/componentProps.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,399 @@
// NOTE: This test is implemented based on the case of `runtime-core/__test__/componentProps.spec.ts`.

import { defineComponent, watchEffect } from 'vue'
ubugeeei marked this conversation as resolved.
Show resolved Hide resolved

import type { FunctionalComponent } from '../src/component'
import { getCurrentInstance } from '../src/component'
import { render } from '../src/render'
import { template } from '../src/template'
import { children, setText } from '../src/dom'

let host: HTMLElement
const initHost = () => {
host = document.createElement('div')
host.setAttribute('id', 'host')
document.body.appendChild(host)
}
beforeEach(() => initHost())
afterEach(() => host.remove())

describe('component props (vapor)', () => {
test('stateful', () => {
let props: any
// TODO: attrs

const Comp = defineComponent({
props: ['fooBar', 'barBaz'],
render() {
const instance = getCurrentInstance()!
props = instance.props
},
})

render(
Comp as any,
{
get fooBar() {
return 1
},
},
host,
)
expect(props.fooBar).toEqual(1)

// test passing kebab-case and resolving to camelCase
render(
Comp as any,
{
get ['foo-bar']() {
return 2
},
},
host,
)
expect(props.fooBar).toEqual(2)

// test updating kebab-case should not delete it (#955)
render(
Comp as any,
{
get ['foo-bar']() {
return 3
},
get barBaz() {
return 5
},
},
host,
)
expect(props.fooBar).toEqual(3)
expect(props.barBaz).toEqual(5)

render(Comp as any, {}, host)
expect(props.fooBar).toBeUndefined()
expect(props.barBaz).toBeUndefined()
// expect(props.qux).toEqual(5) // TODO: attrs
})

test('stateful with setup', () => {
// FIXME: is it necessary?
})
ubugeeei marked this conversation as resolved.
Show resolved Hide resolved

test('functional with declaration', () => {
let props: any
// TODO: attrs

const Comp: FunctionalComponent = (_props) => {
const instance = getCurrentInstance()!
props = instance.props
return {}
}
Comp.props = ['foo']
Comp.render = (() => {}) as any

render(
Comp as any,
{
get foo() {
return 1
},
},
host,
)
expect(props.foo).toEqual(1)

render(
Comp as any,
{
get foo() {
return 2
},
},
host,
)
expect(props.foo).toEqual(2)

render(Comp as any, {}, host)
expect(props.foo).toBeUndefined()
})

test('functional without declaration', () => {
let props: any
// TODO: attrs

const Comp: FunctionalComponent = (_props) => {
const instance = getCurrentInstance()!
props = instance.props
return {}
}
Comp.props = undefined as any
Comp.render = (() => {}) as any

render(
Comp as any,
{
get foo() {
return 1
},
},
host,
)
expect(props.foo).toBeUndefined()

render(
Comp as any,
{
get foo() {
return 2
},
},
host,
)
expect(props.foo).toBeUndefined()
})

test('boolean casting', () => {
let props: any
const Comp = {
props: {
foo: Boolean,
bar: Boolean,
baz: Boolean,
qux: Boolean,
},
render() {
const instance = getCurrentInstance()!
props = instance.props
},
}

render(
Comp as any,
{
// absent should cast to false
bar: '', // empty string should cast to true
baz: 1, // same string should cast to true
qux: 'ok', // other values should be left in-tact (but raise warning)
},
host,
)

expect(props.foo).toBe(false)
expect(props.bar).toBe(true)
// expect(props.baz).toBe(true) // FIXME: failed
expect(props.qux).toBe('ok')
})

test('default value', () => {
let props: any
const defaultFn = vi.fn(() => ({ a: 1 }))
const defaultBaz = vi.fn(() => ({ b: 1 }))

const Comp = {
props: {
foo: {
default: 1,
},
bar: {
default: defaultFn,
},
baz: {
type: Function,
default: defaultBaz,
},
},
render() {
const instance = getCurrentInstance()!
props = instance.props
},
}

render(
Comp as any,
{
get foo() {
return 2
},
},
host,
)
expect(props.foo).toBe(2)
const prevBar = props.bar
// expect(props.bar).toEqual({ a: 1 }) // FIXME: failed
expect(props.baz).toEqual(defaultBaz)
// expect(defaultFn).toHaveBeenCalledTimes(1) // FIXME: failed
expect(defaultBaz).toHaveBeenCalledTimes(0)

// #999: updates should not cause default factory of unchanged prop to be
// called again
render(
Comp as any,
{
get foo() {
return 3
},
},
host,
)
expect(props.foo).toBe(3)
// expect(props.bar).toEqual({ a: 1 }) // FIXME: failed
expect(props.bar).toBe(prevBar)
// expect(defaultFn).toHaveBeenCalledTimes(1) // FIXME: failed

render(
Comp as any,
{
get bar() {
return { b: 2 }
},
},
host,
)
expect(props.foo).toBe(1)
expect(props.bar).toEqual({ b: 2 })
// expect(defaultFn).toHaveBeenCalledTimes(1) // FIXME: failed

render(
Comp as any,
{
get foo() {
return 3
},
get bar() {
return { b: 3 }
},
},
host,
)
expect(props.foo).toBe(3)
expect(props.bar).toEqual({ b: 3 })
// expect(defaultFn).toHaveBeenCalledTimes(1) // FIXME: failed

render(
Comp as any,
{
get bar() {
return { b: 4 }
},
},
host,
)
expect(props.foo).toBe(1)
expect(props.bar).toEqual({ b: 4 })
// expect(defaultFn).toHaveBeenCalledTimes(1) // FIXME: failed
})

test('using inject in default value factory', () => {
// FIXME: is it necessary?
})
ubugeeei marked this conversation as resolved.
Show resolved Hide resolved

test('props type support BigInt', () => {
const Comp = {
props: {
foo: BigInt,
},
render() {
const instance = getCurrentInstance()!
const t0 = template('<div></div>')
const n0 = t0()
const {
0: [n1],
} = children(n0)
watchEffect(() => {
setText(n1, instance.props.foo)
})
return n0
},
}

render(
Comp as any,
{
get foo() {
return (
BigInt(BigInt(100000111)) + BigInt(2000000000) * BigInt(30000000)
)
},
},
'#host',
)
expect(host.innerHTML).toBe('<div>60000000100000111</div>')
})

// #3288
// test('declared prop key should be present even if not passed', async () => {
// let initialKeys: string[] = []
// const changeSpy = vi.fn()
// const passFoo = ref(false)
// const Comp = {
// props: ['foo'],
// setup() {
// const instance = getCurrentInstance()!
// initialKeys = Object.keys(instance.props)
// watchEffect(changeSpy)
// return {}
// },
// render() {
// return {}
// },
// }
// const Parent = createIf(
// () => passFoo.value,
// () => {
// return render(Comp as any, { foo: 1 }, host) // TODO: createComponent fn
// },
// )
// // expect(changeSpy).toHaveBeenCalledTimes(1)
// })

// #3371
// test(`avoid double-setting props when casting`, async () => {
// // TODO: proide, slots
// })

test('support null in required + multiple-type declarations', () => {
const Comp = {
props: {
foo: { type: [Function, null], required: true },
},
render() {},
}

expect(() => {
render(Comp as any, { foo: () => {} }, host)
}).not.toThrow()

expect(() => {
render(Comp as any, { foo: null }, host)
}).not.toThrow()
})

// // #5016
// test('handling attr with undefined value', () => {
// // TODO: attrs
// })

// #691ef
test('should not mutate original props long-form definition object', () => {
const props = {
msg: {
type: String,
},
}
const Comp = defineComponent({
props,
render() {},
})

render(Comp as any, { msg: 'test' }, host)

expect(Object.keys(props.msg).length).toBe(1)
})

// NOTE: not supported
// optimized props update
// mixins
// validator
// warn
// caching
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is my understanding correct about this area?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for optimized props update: maybe it's unnecessary (I'm not sure for now). But Vapor can also pass the test. So let's add it first and then remove it if it's useless at the end of the development of Vapor.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validator should be implemented as well. Let's add it as test.todo

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

warn: What the reason is that you think it should be not supported? IMHO it should be also included.

Copy link
Member

@sxzz sxzz Jan 31, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the rest, let's drop them.

})
Loading