expect(serializeInner(root)).toBe(expected)
})
+ //#8678
+ test('nested suspense (child suspense update before parent suspense resolve)', async () => {
+ const calls: string[] = []
+
+ const InnerA = defineAsyncComponent(
+ {
+ setup: () => {
+ calls.push('innerA created')
+ onMounted(() => {
+ calls.push('innerA mounted')
+ })
+ return () => h('div', 'innerA')
+ },
+ },
+ 10,
+ )
+
+ const InnerB = defineAsyncComponent(
+ {
+ setup: () => {
+ calls.push('innerB created')
+ onMounted(() => {
+ calls.push('innerB mounted')
+ })
+ return () => h('div', 'innerB')
+ },
+ },
+ 10,
+ )
+
+ const OuterA = defineAsyncComponent(
+ {
+ setup: (_, { slots }: any) => {
+ calls.push('outerA created')
+ onMounted(() => {
+ calls.push('outerA mounted')
+ })
+ return () =>
+ h(Fragment, null, [h('div', 'outerA'), slots.default?.()])
+ },
+ },
+ 5,
+ )
+
+ const OuterB = defineAsyncComponent(
+ {
+ setup: (_, { slots }: any) => {
+ calls.push('outerB created')
+ onMounted(() => {
+ calls.push('outerB mounted')
+ })
+ return () =>
+ h(Fragment, null, [h('div', 'outerB'), slots.default?.()])
+ },
+ },
+ 5,
+ )
+
+ const outerToggle = ref(false)
+ const innerToggle = ref(false)
+
+ /**
+ * <Suspense>
+ * <component :is="outerToggle ? outerB : outerA">
+ * <Suspense>
+ * <component :is="innerToggle ? innerB : innerA" />
+ * </Suspense>
+ * </component>
+ * </Suspense>
+ */
+ const Comp = {
+ setup() {
+ return () =>
+ h(Suspense, null, {
+ default: [
+ h(outerToggle.value ? OuterB : OuterA, null, {
+ default: () =>
+ h(Suspense, null, {
+ default: h(innerToggle.value ? InnerB : InnerA),
+ }),
+ }),
+ ],
+ fallback: h('div', 'fallback outer'),
+ })
+ },
+ }
+
+ const root = nodeOps.createElement('div')
+ render(h(Comp), root)
+ expect(serializeInner(root)).toBe(`<div>fallback outer</div>`)
+
+ // mount outer component
+ await Promise.all(deps)
+ await nextTick()
+
+ expect(serializeInner(root)).toBe(`<div>outerA</div><!---->`)
+ expect(calls).toEqual([`outerA created`, `outerA mounted`])
+
+ // mount inner component
+ await Promise.all(deps)
+ await nextTick()
+ expect(serializeInner(root)).toBe(`<div>outerA</div><div>innerA</div>`)
+
+ expect(calls).toEqual([
+ 'outerA created',
+ 'outerA mounted',
+ 'innerA created',
+ 'innerA mounted',
+ ])
+
+ calls.length = 0
+ deps.length = 0
+
+ // toggle both outer and inner components
+ outerToggle.value = true
+ innerToggle.value = true
+ await nextTick()
+
+ await Promise.all(deps)
+ await nextTick()
+ expect(serializeInner(root)).toBe(`<div>outerB</div><!---->`)
+
+ await Promise.all(deps)
+ await nextTick()
+ expect(serializeInner(root)).toBe(`<div>outerB</div><div>innerB</div>`)
+
+ // innerB only mount once
+ expect(calls).toEqual([
+ 'outerB created',
+ 'outerB mounted',
+ 'innerB created',
+ 'innerB mounted',
+ ])
+ })
+
// #6416
test('KeepAlive with Suspense', async () => {
const Async = defineAsyncComponent({