From: daiwei Date: Tue, 20 May 2025 09:27:22 +0000 (+0800) Subject: test: add more tests X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=6e7bb790a28ff4b6bba80eac686d967a627e6549;p=thirdparty%2Fvuejs%2Fcore.git test: add more tests --- diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index 5930d6d511..3a68b4493e 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -2387,6 +2387,24 @@ function baseCreateRenderer( instance.isUnmounted = true }, parentSuspense) + // A component with async dep inside a pending suspense is unmounted before + // its async dep resolves. This should remove the dep from the suspense, and + // cause the suspense to resolve immediately if that was the last dep. + if ( + __FEATURE_SUSPENSE__ && + parentSuspense && + parentSuspense.pendingBranch && + !parentSuspense.isUnmounted && + instance.asyncDep && + !instance.asyncResolved && + instance.suspenseId === parentSuspense.pendingId + ) { + parentSuspense.deps-- + if (parentSuspense.deps === 0) { + parentSuspense.resolve() + } + } + if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) { devtoolsComponentRemoved(instance) } diff --git a/packages/runtime-vapor/__tests__/components/Suspense.spec.ts b/packages/runtime-vapor/__tests__/components/Suspense.spec.ts index 36cafc55f3..18d33795a0 100644 --- a/packages/runtime-vapor/__tests__/components/Suspense.spec.ts +++ b/packages/runtime-vapor/__tests__/components/Suspense.spec.ts @@ -1,4 +1,4 @@ -import { nextTick } from 'vue' +import { nextTick, reactive } from 'vue' import { compile, runtimeDom, runtimeVapor } from '../_utils' describe.todo('VaporSuspense', () => {}) @@ -111,4 +111,134 @@ describe('vapor / vdom interop', () => { await nextTick() expect(container.innerHTML).toBe(`
inner
`) }) + + test('vdom suspense: content update before suspense resolve', async () => { + const data = reactive({ msg: 'foo', deps: [] }) + const { container } = await testSuspense( + ` + `, + { + VaporChild: withAsyncScript( + ``, + ), + }, + data, + ) + + expect(container.innerHTML).toBe(`fallback foo`) + + data.msg = 'bar' + await nextTick() + expect(container.innerHTML).toBe(`fallback bar`) + + await Promise.all(data.deps) + await nextTick() + expect(container.innerHTML).toBe(`
bar
`) + }) + + test('vdom suspense: unmount before suspense resolve', async () => { + const data = reactive({ show: true, deps: [] }) + const { container } = await testSuspense( + ` + `, + { + VaporChild: withAsyncScript(``), + }, + data, + ) + + expect(container.innerHTML).toBe(`fallback`) + + data.show = false + await nextTick() + expect(container.innerHTML).toBe(``) + + await Promise.all(data.deps) + await nextTick() + expect(container.innerHTML).toBe(``) + }) + + test('vdom suspense: unmount suspense after resolve', async () => { + const data = reactive({ show: true, deps: [] }) + const { container } = await testSuspense( + ` + `, + { + VaporChild: withAsyncScript(``), + }, + data, + ) + + expect(container.innerHTML).toBe(`fallback`) + + await Promise.all(data.deps) + await nextTick() + expect(container.innerHTML).toBe(`
child
`) + + data.show = false + await nextTick() + expect(container.innerHTML).toBe(``) + }) + + test('vdom suspense: unmount suspense before resolve', async () => { + const data = reactive({ show: true, deps: [] }) + const { container } = await testSuspense( + ` + `, + { + VaporChild: withAsyncScript(``), + }, + data, + ) + + expect(container.innerHTML).toBe(`fallback`) + + data.show = false + await nextTick() + expect(container.innerHTML).toBe(``) + + await Promise.all(data.deps) + await nextTick() + expect(container.innerHTML).toBe(``) + }) }) diff --git a/packages/runtime-vapor/src/vdomInterop.ts b/packages/runtime-vapor/src/vdomInterop.ts index fed9666e0a..b93eb903f6 100644 --- a/packages/runtime-vapor/src/vdomInterop.ts +++ b/packages/runtime-vapor/src/vdomInterop.ts @@ -85,8 +85,30 @@ const vaporInteropImpl: Omit< unmount(vnode, doRemove) { const container = doRemove ? vnode.anchor!.parentNode : undefined - if (vnode.component) { - unmountComponent(vnode.component as any, container) + const instance = vnode.component as any as VaporComponentInstance + if (instance) { + // the async component may not be resolved yet, block is null + if (instance.block) { + unmountComponent(instance, container) + } + // A component with async dep inside a pending suspense is unmounted before + // its async dep resolves. This should remove the dep from the suspense, and + // cause the suspense to resolve immediately if that was the last dep. + const parentSuspense = instance.suspense + if ( + __FEATURE_SUSPENSE__ && + parentSuspense && + parentSuspense.pendingBranch && + !parentSuspense.isUnmounted && + instance.asyncDep && + !instance.asyncResolved && + instance.suspenseId === parentSuspense.pendingId + ) { + parentSuspense.deps-- + if (parentSuspense.deps === 0) { + parentSuspense.resolve() + } + } } else if (vnode.vb) { remove(vnode.vb, container) }