test(runtime-vapor): add apiSetupContext unit test (#237)

Co-authored-by: 三咲智子 Kevin Deng <sxzz@sxzz.moe>
This commit is contained in:
XiaoDong 2024-06-19 01:00:16 +08:00 committed by GitHub
parent bc04592ca9
commit bbde386a7c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 221 additions and 3 deletions

View File

@ -123,10 +123,10 @@ export function generate(
push('}')
}
const deligates = genDeligates(context)
const delegates = genDelegates(context)
const templates = genTemplates(ir.template, context)
const imports = genHelperImports(context)
const preamble = imports + templates + deligates
const preamble = imports + templates + delegates
const newlineCount = [...preamble].filter(c => c === '\n').length
if (newlineCount && !inline) {
@ -148,7 +148,7 @@ export function generate(
}
}
function genDeligates({ delegates, vaporHelper }: CodegenContext) {
function genDelegates({ delegates, vaporHelper }: CodegenContext) {
return delegates.size
? genCall(
vaporHelper('delegateEvents'),

View File

@ -52,6 +52,10 @@ export function makeRender<Component = ObjectComponent | SetupFn>(
return res()
}
function html() {
return host.innerHTML
}
const res = () => ({
component,
host,
@ -61,6 +65,7 @@ export function makeRender<Component = ObjectComponent | SetupFn>(
mount,
render,
resetHost,
html,
})
return res()

View File

@ -0,0 +1,213 @@
import {
createComponent,
createSlot,
createTextNode,
defineComponent,
delegate,
delegateEvents,
insert,
nextTick,
reactive,
ref,
renderEffect,
setDynamicProps,
template,
watchEffect,
} from '../src'
import { makeRender } from './_utils'
const define = makeRender()
describe('api: setup context', () => {
it('should expose return values to template render context', () => {
const { html } = define({
setup() {
return {
ref: ref('foo'),
object: reactive({ msg: 'bar' }),
value: 'baz',
}
},
render(ctx) {
return createTextNode([`${ctx.ref} ${ctx.object.msg} ${ctx.value}`])
},
}).render()
expect(html()).toMatch(`foo bar baz`)
})
it('should support returning render function', () => {
const { html } = define({
setup() {
return createTextNode([`hello`])
},
}).render()
expect(html()).toMatch(`hello`)
})
it('props', async () => {
const count = ref(0)
let dummy
const Child = defineComponent({
props: { count: Number },
setup(props) {
watchEffect(() => {
dummy = props.count
})
return createTextNode(() => [props.count])
},
})
const { html } = define({
render: () => createComponent(Child, { count: () => count.value }),
}).render()
expect(html()).toMatch(`0`)
count.value++
await nextTick()
expect(dummy).toBe(1)
expect(html()).toMatch(`1`)
})
it('context.attrs', async () => {
const toggle = ref(true)
const Child = defineComponent({
inheritAttrs: false,
setup(props, { attrs }) {
const el = document.createElement('div')
renderEffect(() => {
setDynamicProps(el, attrs)
})
return el
},
})
const { html } = define({
render: () =>
createComponent(Child, () =>
toggle.value ? { id: 'foo' } : { class: 'baz' },
),
}).render()
expect(html()).toMatch(`<div id="foo"></div>`)
toggle.value = false
await nextTick()
expect(html()).toMatch(`<div class="baz"></div>`)
})
// #4161
it('context.attrs in child component slots', async () => {
const toggle = ref(true)
const Wrapper = defineComponent({
setup(_, { slots }) {
return slots.default!()
},
})
const Child = defineComponent({
inheritAttrs: false,
setup(_: any, { attrs }: any) {
return createComponent(Wrapper, null, {
default: () => {
const n0 = template('<div>')() as HTMLDivElement
renderEffect(() => {
setDynamicProps(n0, attrs)
})
return n0
},
})
},
})
const { html } = define({
render: () =>
createComponent(Child, () =>
toggle.value ? { id: 'foo' } : { class: 'baz' },
),
}).render()
expect(html()).toMatch(`<div id="foo"></div>`)
// should update even though it's not reactive
toggle.value = false
await nextTick()
expect(html()).toMatch(`<div class="baz"></div>`)
})
it('context.slots', async () => {
const id = ref('foo')
const Child = defineComponent({
render() {
return [createSlot('foo'), createSlot('bar')]
},
})
const { html } = define({
render() {
return createComponent(Child, null, null, [
() => ({
name: 'foo',
fn: () => createTextNode(() => [id.value]),
}),
() => ({
name: 'bar',
fn: () => createTextNode(['bar']),
}),
])
},
}).render()
expect(html()).toMatch(`foo<!--slot-->bar<!--slot-->`)
id.value = 'baz'
await nextTick()
expect(html()).toMatch(`baz<!--slot-->bar<!--slot-->`)
})
it('context.emit', async () => {
const count = ref(0)
const spy = vi.fn()
delegateEvents('click')
const Child = defineComponent({
props: {
count: { type: Number, default: 1 },
},
setup(props, { emit }) {
const n0 = template('<div>')() as HTMLDivElement
delegate(n0, 'click', () => () => {
emit('inc', props.count + 1)
})
insert(
createTextNode(() => [props.count]),
n0,
)
return n0
},
})
const { host, html } = define({
render: () =>
createComponent(Child, {
count: () => count.value,
onInc: () => (newVal: number) => {
spy()
count.value = newVal
},
}),
}).render()
expect(html()).toMatch(`<div>0</div>`)
;(host.children[0] as HTMLDivElement).click()
expect(spy).toHaveBeenCalled()
await nextTick()
expect(html()).toMatch(`<div>1</div>`)
})
})