test(vapor): enable more test cases
This commit is contained in:
parent
6979952613
commit
e49c5a17da
|
@ -9,9 +9,10 @@ import {
|
|||
isPromise,
|
||||
} from '@vue/shared'
|
||||
import {
|
||||
type ComponentInternalInstance,
|
||||
type SetupContext,
|
||||
createSetupContext,
|
||||
getCurrentInstance,
|
||||
getCurrentGenericInstance,
|
||||
setCurrentInstance,
|
||||
unsetCurrentInstance,
|
||||
} from './component'
|
||||
|
@ -381,6 +382,7 @@ export function withDefaults<
|
|||
return null as any
|
||||
}
|
||||
|
||||
// TODO return type for Vapor components
|
||||
export function useSlots(): SetupContext['slots'] {
|
||||
return getContext().slots
|
||||
}
|
||||
|
@ -390,11 +392,16 @@ export function useAttrs(): SetupContext['attrs'] {
|
|||
}
|
||||
|
||||
function getContext(): SetupContext {
|
||||
const i = getCurrentInstance()!
|
||||
const i = getCurrentGenericInstance()!
|
||||
if (__DEV__ && !i) {
|
||||
warn(`useContext() called without active instance.`)
|
||||
}
|
||||
return i.setupContext || (i.setupContext = createSetupContext(i))
|
||||
if (i.vapor) {
|
||||
return i as any // vapor instance act as its own setup context
|
||||
} else {
|
||||
const ii = i as ComponentInternalInstance
|
||||
return ii.setupContext || (ii.setupContext = createSetupContext(ii))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -496,7 +503,7 @@ export function createPropsRestProxy(
|
|||
* @internal
|
||||
*/
|
||||
export function withAsyncContext(getAwaitable: () => any): [any, () => void] {
|
||||
const ctx = getCurrentInstance()!
|
||||
const ctx = getCurrentGenericInstance()!
|
||||
if (__DEV__ && !ctx) {
|
||||
warn(
|
||||
`withAsyncContext called without active current instance. ` +
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
import { createComponent, defineVaporComponent, template } from '../src'
|
||||
import { ref, useAttrs, useSlots } from '@vue/runtime-dom'
|
||||
import {
|
||||
currentInstance,
|
||||
onMounted,
|
||||
ref,
|
||||
useAttrs,
|
||||
useSlots,
|
||||
withAsyncContext,
|
||||
} from '@vue/runtime-dom'
|
||||
import { makeRender } from './_utils'
|
||||
import type { VaporComponentInstance } from '../src/component'
|
||||
|
||||
const define = makeRender<any>()
|
||||
|
||||
describe.todo('SFC <script setup> helpers', () => {
|
||||
test.todo('should warn runtime usage', () => {})
|
||||
|
||||
describe('SFC <script setup> helpers', () => {
|
||||
test('useSlots / useAttrs (no args)', () => {
|
||||
let slots: VaporComponentInstance['slots'] | undefined
|
||||
let attrs: VaporComponentInstance['attrs'] | undefined
|
||||
|
@ -59,23 +64,51 @@ describe.todo('SFC <script setup> helpers', () => {
|
|||
expect(attrs).toBe(ctx!.attrs)
|
||||
})
|
||||
|
||||
describe.todo('mergeDefaults', () => {
|
||||
test.todo('object syntax', () => {})
|
||||
test.todo('array syntax', () => {})
|
||||
test.todo('merging with skipFactory', () => {})
|
||||
test.todo('should warn missing', () => {})
|
||||
})
|
||||
|
||||
describe('mergeModels', () => {
|
||||
test.todo('array syntax', () => {})
|
||||
test.todo('object syntax', () => {})
|
||||
test.todo('overwrite', () => {})
|
||||
})
|
||||
|
||||
test.todo('createPropsRestProxy', () => {})
|
||||
|
||||
describe.todo('withAsyncContext', () => {
|
||||
test.todo('basic', async () => {})
|
||||
test('basic', async () => {
|
||||
const spy = vi.fn()
|
||||
|
||||
let beforeInstance: VaporComponentInstance | null = null
|
||||
let afterInstance: VaporComponentInstance | null = null
|
||||
let resolve: (msg: string) => void
|
||||
|
||||
const Comp = defineVaporComponent({
|
||||
async setup() {
|
||||
let __temp: any, __restore: any
|
||||
|
||||
beforeInstance = currentInstance as VaporComponentInstance
|
||||
|
||||
const msg =
|
||||
(([__temp, __restore] = withAsyncContext(
|
||||
() =>
|
||||
new Promise(r => {
|
||||
resolve = r
|
||||
}),
|
||||
)),
|
||||
(__temp = await __temp),
|
||||
__restore(),
|
||||
__temp)
|
||||
|
||||
// register the lifecycle after an await statement
|
||||
onMounted(spy)
|
||||
afterInstance = currentInstance as VaporComponentInstance
|
||||
return document.createTextNode(msg)
|
||||
},
|
||||
})
|
||||
|
||||
const { html } = define(Comp).render()
|
||||
|
||||
expect(spy).not.toHaveBeenCalled()
|
||||
resolve!('hello')
|
||||
// wait a macro task tick for all micro ticks to resolve
|
||||
await new Promise(r => setTimeout(r))
|
||||
// mount hook should have been called
|
||||
expect(spy).toHaveBeenCalled()
|
||||
// should retain same instance before/after the await call
|
||||
expect(beforeInstance).toBe(afterInstance)
|
||||
expect(html()).toBe('hello')
|
||||
})
|
||||
|
||||
test.todo('error handling', async () => {})
|
||||
test.todo('should not leak instance on multiple awaits', async () => {})
|
||||
test.todo('should not leak on multiple awaits + error', async () => {})
|
||||
|
|
|
@ -8,43 +8,50 @@ import {
|
|||
watch,
|
||||
watchEffect,
|
||||
} from '@vue/runtime-dom'
|
||||
import { createComponent, defineVaporComponent, renderEffect } from '../src'
|
||||
import {
|
||||
createComponent,
|
||||
createIf,
|
||||
createTemplateRefSetter,
|
||||
defineVaporComponent,
|
||||
renderEffect,
|
||||
template,
|
||||
} from '../src'
|
||||
import { makeRender } from './_utils'
|
||||
import type { VaporComponentInstance } from '../src/component'
|
||||
import type { RefEl } from '../src/apiTemplateRef'
|
||||
|
||||
const define = makeRender()
|
||||
|
||||
// only need to port test cases related to in-component usage
|
||||
describe('apiWatch', () => {
|
||||
// #7030
|
||||
it.todo(
|
||||
// need if support
|
||||
'should not fire on child component unmount w/ flush: pre',
|
||||
async () => {
|
||||
const visible = ref(true)
|
||||
const cb = vi.fn()
|
||||
const Parent = defineVaporComponent({
|
||||
props: ['visible'],
|
||||
setup() {
|
||||
// @ts-expect-error
|
||||
return visible.value ? h(Comp) : null
|
||||
},
|
||||
})
|
||||
const Comp = {
|
||||
setup() {
|
||||
watch(visible, cb, { flush: 'pre' })
|
||||
return []
|
||||
},
|
||||
}
|
||||
define(Parent).render({
|
||||
visible: () => visible.value,
|
||||
})
|
||||
expect(cb).not.toHaveBeenCalled()
|
||||
visible.value = false
|
||||
await nextTick()
|
||||
expect(cb).not.toHaveBeenCalled()
|
||||
},
|
||||
)
|
||||
it(// need if support
|
||||
'should not fire on child component unmount w/ flush: pre', async () => {
|
||||
const visible = ref(true)
|
||||
const cb = vi.fn()
|
||||
const Parent = defineVaporComponent({
|
||||
props: ['visible'],
|
||||
setup() {
|
||||
return createIf(
|
||||
() => visible.value,
|
||||
() => createComponent(Comp),
|
||||
)
|
||||
},
|
||||
})
|
||||
const Comp = {
|
||||
setup() {
|
||||
watch(visible, cb, { flush: 'pre' })
|
||||
return []
|
||||
},
|
||||
}
|
||||
define(Parent).render({
|
||||
visible: () => visible.value,
|
||||
})
|
||||
expect(cb).not.toHaveBeenCalled()
|
||||
visible.value = false
|
||||
await nextTick()
|
||||
expect(cb).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
// #7030
|
||||
it('flush: pre watcher in child component should not fire before parent update', async () => {
|
||||
|
@ -184,41 +191,41 @@ describe('apiWatch', () => {
|
|||
})
|
||||
|
||||
// #1852
|
||||
it.todo(
|
||||
// need if + templateRef
|
||||
'flush: post watcher should fire after template refs updated',
|
||||
async () => {
|
||||
const toggle = ref(false)
|
||||
let dom: Element | null = null
|
||||
it('flush: post watcher should fire after template refs updated', async () => {
|
||||
const toggle = ref(false)
|
||||
let dom: Element | null = null
|
||||
|
||||
const App = {
|
||||
setup() {
|
||||
const domRef = ref<Element | null>(null)
|
||||
const App = {
|
||||
setup() {
|
||||
const domRef = ref<Element | null>(null)
|
||||
|
||||
watch(
|
||||
toggle,
|
||||
() => {
|
||||
dom = domRef.value
|
||||
},
|
||||
{ flush: 'post' },
|
||||
)
|
||||
watch(
|
||||
toggle,
|
||||
() => {
|
||||
dom = domRef.value
|
||||
},
|
||||
{ flush: 'post' },
|
||||
)
|
||||
|
||||
return () => {
|
||||
// @ts-expect-error
|
||||
return toggle.value ? h('p', { ref: domRef }) : null
|
||||
}
|
||||
},
|
||||
}
|
||||
const setRef = createTemplateRefSetter()
|
||||
return createIf(
|
||||
() => toggle.value,
|
||||
() => {
|
||||
const n = template('<p>')()
|
||||
setRef(n as RefEl, domRef)
|
||||
return n
|
||||
},
|
||||
)
|
||||
},
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
render(h(App), nodeOps.createElement('div'))
|
||||
expect(dom).toBe(null)
|
||||
define(App).render()
|
||||
expect(dom).toBe(null)
|
||||
|
||||
toggle.value = true
|
||||
await nextTick()
|
||||
expect(dom!.tagName).toBe('P')
|
||||
},
|
||||
)
|
||||
toggle.value = true
|
||||
await nextTick()
|
||||
expect(dom!.tagName).toBe('P')
|
||||
})
|
||||
|
||||
test('should not leak `this.proxy` to setup()', () => {
|
||||
const source = vi.fn()
|
||||
|
|
|
@ -87,7 +87,7 @@ describe('component: slots', () => {
|
|||
expect(instance.slots).toHaveProperty('two')
|
||||
})
|
||||
|
||||
test.todo('should work with createFlorSlots', async () => {
|
||||
test('should work with createFlorSlots', async () => {
|
||||
const loop = ref([1, 2, 3])
|
||||
|
||||
let instance: any
|
||||
|
@ -101,7 +101,6 @@ describe('component: slots', () => {
|
|||
return createComponent(Child, null, {
|
||||
$: [
|
||||
() =>
|
||||
// @ts-expect-error
|
||||
createForSlots(loop.value, (item, i) => ({
|
||||
name: item,
|
||||
fn: () => template(item + i)(),
|
||||
|
|
|
@ -406,7 +406,7 @@ describe('api: template ref', () => {
|
|||
})
|
||||
|
||||
// compiled output of v-for + template ref
|
||||
test.todo('ref in v-for', async () => {
|
||||
test('ref in v-for', async () => {
|
||||
const show = ref(true)
|
||||
const list = reactive([1, 2, 3])
|
||||
const listRefs = ref([])
|
||||
|
@ -466,7 +466,7 @@ describe('api: template ref', () => {
|
|||
expect(mapRefs()).toMatchObject(['2', '3', '4'])
|
||||
})
|
||||
|
||||
test.todo('named ref in v-for', async () => {
|
||||
test('named ref in v-for', async () => {
|
||||
const show = ref(true)
|
||||
const list = reactive([1, 2, 3])
|
||||
const listRefs = ref([])
|
||||
|
@ -530,67 +530,64 @@ describe('api: template ref', () => {
|
|||
})
|
||||
|
||||
// #6697 v-for ref behaves differently under production and development
|
||||
test.todo(
|
||||
'named ref in v-for , should be responsive when rendering',
|
||||
async () => {
|
||||
const list = ref([1, 2, 3])
|
||||
const listRefs = ref([])
|
||||
test('named ref in v-for , should be responsive when rendering', async () => {
|
||||
const list = ref([1, 2, 3])
|
||||
const listRefs = ref([])
|
||||
|
||||
const t0 = template('<div><div></div><ul></ul></div>')
|
||||
const t1 = template('<li></li>')
|
||||
const { render } = define({
|
||||
setup() {
|
||||
return { listRefs }
|
||||
},
|
||||
render() {
|
||||
const n0 = t0()
|
||||
const n1 = n0.firstChild
|
||||
const n2 = n1!.nextSibling!
|
||||
const n3 = createFor(
|
||||
() => list.value,
|
||||
state => {
|
||||
const n4 = t1()
|
||||
createTemplateRefSetter()(
|
||||
n4 as Element,
|
||||
'listRefs',
|
||||
undefined,
|
||||
true,
|
||||
)
|
||||
renderEffect(() => {
|
||||
const [item] = state
|
||||
setText(n4, item)
|
||||
})
|
||||
return n4
|
||||
},
|
||||
)
|
||||
insert(n3, n2 as unknown as ParentNode)
|
||||
renderEffect(() => {
|
||||
setText(n1!, String(listRefs.value))
|
||||
})
|
||||
return n0
|
||||
},
|
||||
})
|
||||
const t0 = template('<div><div></div><ul></ul></div>')
|
||||
const t1 = template('<li></li>')
|
||||
const { render } = define({
|
||||
setup() {
|
||||
return { listRefs }
|
||||
},
|
||||
render() {
|
||||
const n0 = t0()
|
||||
const n1 = n0.firstChild
|
||||
const n2 = n1!.nextSibling!
|
||||
const n3 = createFor(
|
||||
() => list.value,
|
||||
state => {
|
||||
const n4 = t1()
|
||||
createTemplateRefSetter()(
|
||||
n4 as Element,
|
||||
'listRefs',
|
||||
undefined,
|
||||
true,
|
||||
)
|
||||
renderEffect(() => {
|
||||
const [item] = state
|
||||
setText(n4, item)
|
||||
})
|
||||
return n4
|
||||
},
|
||||
)
|
||||
insert(n3, n2 as unknown as ParentNode)
|
||||
renderEffect(() => {
|
||||
setText(n1!, String(listRefs.value))
|
||||
})
|
||||
return n0
|
||||
},
|
||||
})
|
||||
|
||||
const { host } = render()
|
||||
const { host } = render()
|
||||
|
||||
await nextTick()
|
||||
expect(String(listRefs.value)).toBe(
|
||||
'[object HTMLLIElement],[object HTMLLIElement],[object HTMLLIElement]',
|
||||
)
|
||||
expect(host.innerHTML).toBe(
|
||||
'<div><div>[object HTMLLIElement],[object HTMLLIElement],[object HTMLLIElement]</div><ul><li>1</li><li>2</li><li>3</li><!--for--></ul></div>',
|
||||
)
|
||||
await nextTick()
|
||||
expect(String(listRefs.value)).toBe(
|
||||
'[object HTMLLIElement],[object HTMLLIElement],[object HTMLLIElement]',
|
||||
)
|
||||
expect(host.innerHTML).toBe(
|
||||
'<div><div>[object HTMLLIElement],[object HTMLLIElement],[object HTMLLIElement]</div><ul><li>1</li><li>2</li><li>3</li><!--for--></ul></div>',
|
||||
)
|
||||
|
||||
list.value.splice(0, 1)
|
||||
await nextTick()
|
||||
expect(String(listRefs.value)).toBe(
|
||||
'[object HTMLLIElement],[object HTMLLIElement]',
|
||||
)
|
||||
expect(host.innerHTML).toBe(
|
||||
'<div><div>[object HTMLLIElement],[object HTMLLIElement]</div><ul><li>2</li><li>3</li><!--for--></ul></div>',
|
||||
)
|
||||
},
|
||||
)
|
||||
list.value.splice(0, 1)
|
||||
await nextTick()
|
||||
expect(String(listRefs.value)).toBe(
|
||||
'[object HTMLLIElement],[object HTMLLIElement]',
|
||||
)
|
||||
expect(host.innerHTML).toBe(
|
||||
'<div><div>[object HTMLLIElement],[object HTMLLIElement]</div><ul><li>2</li><li>3</li><!--for--></ul></div>',
|
||||
)
|
||||
})
|
||||
|
||||
test('string ref inside slots', () => {
|
||||
const { component: Child } = define({
|
||||
|
|
|
@ -209,7 +209,7 @@ describe('error handling', () => {
|
|||
expect(fn).toHaveBeenCalledWith(err, 'render function')
|
||||
})
|
||||
|
||||
test.todo('in function ref', () => {
|
||||
test('in function ref', () => {
|
||||
const err = new Error('foo')
|
||||
const ref = () => {
|
||||
throw err
|
||||
|
|
|
@ -319,7 +319,7 @@ export const createFor = (
|
|||
}
|
||||
|
||||
export function createForSlots(
|
||||
source: any[] | Record<any, any> | number | Set<any> | Map<any, any>,
|
||||
source: Source,
|
||||
getSlot: (item: any, key: any, index?: number) => DynamicSlot,
|
||||
): DynamicSlot[] {
|
||||
const sourceLength = getLength(source)
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
isVaporComponent,
|
||||
} from './component'
|
||||
import {
|
||||
ErrorCodes,
|
||||
type SchedulerJob,
|
||||
callWithErrorHandling,
|
||||
queuePostFlushCb,
|
||||
|
@ -67,13 +68,10 @@ export function setRef(
|
|||
|
||||
if (isFunction(ref)) {
|
||||
const invokeRefSetter = (value?: Element | Record<string, any>) => {
|
||||
callWithErrorHandling(
|
||||
ref,
|
||||
currentInstance,
|
||||
// @ts-expect-error
|
||||
null,
|
||||
[value, refs],
|
||||
)
|
||||
callWithErrorHandling(ref, currentInstance, ErrorCodes.FUNCTION_REF, [
|
||||
value,
|
||||
refs,
|
||||
])
|
||||
}
|
||||
|
||||
invokeRefSetter(refValue)
|
||||
|
|
|
@ -35,16 +35,17 @@ export const dynamicSlotsProxyHandlers: ProxyHandler<RawSlots> = {
|
|||
}
|
||||
},
|
||||
ownKeys(target) {
|
||||
const keys = Object.keys(target)
|
||||
let keys = Object.keys(target)
|
||||
const dynamicSources = target.$
|
||||
if (dynamicSources) {
|
||||
keys = keys.filter(k => k !== '$')
|
||||
for (const source of dynamicSources) {
|
||||
if (isFunction(source)) {
|
||||
const slot = source()
|
||||
if (isArray(slot)) {
|
||||
for (const s of slot) keys.push(s.name)
|
||||
for (const s of slot) keys.push(String(s.name))
|
||||
} else {
|
||||
keys.push(slot.name)
|
||||
keys.push(String(slot.name))
|
||||
}
|
||||
} else {
|
||||
keys.push(...Object.keys(source))
|
||||
|
@ -73,9 +74,9 @@ export function getSlot(
|
|||
if (slot) {
|
||||
if (isArray(slot)) {
|
||||
for (const s of slot) {
|
||||
if (s.name === key) return s.fn
|
||||
if (String(s.name) === key) return s.fn
|
||||
}
|
||||
} else if (slot.name === key) {
|
||||
} else if (String(slot.name) === key) {
|
||||
return slot.fn
|
||||
}
|
||||
}
|
||||
|
@ -150,6 +151,3 @@ export function createSlot(
|
|||
|
||||
return fragment
|
||||
}
|
||||
|
||||
// TODO
|
||||
export function createForSlots(): any {}
|
||||
|
|
|
@ -6,7 +6,7 @@ export { defineVaporComponent } from './apiDefineComponent'
|
|||
export { insert, prepend, remove } from './block'
|
||||
export { createComponent, createComponentWithFallback } from './component'
|
||||
export { renderEffect } from './renderEffect'
|
||||
export { createSlot, createForSlots } from './componentSlots'
|
||||
export { createSlot } from './componentSlots'
|
||||
export { template, children, next } from './dom/template'
|
||||
export { createTextNode } from './dom/node'
|
||||
export {
|
||||
|
@ -22,5 +22,5 @@ export {
|
|||
} from './dom/prop'
|
||||
export { on, delegate, delegateEvents, setDynamicEvents } from './dom/event'
|
||||
export { createIf } from './apiCreateIf'
|
||||
export { createFor } from './apiCreateFor'
|
||||
export { createFor, createForSlots } from './apiCreateFor'
|
||||
export { createTemplateRefSetter } from './apiTemplateRef'
|
||||
|
|
Loading…
Reference in New Issue