From c0cd7fc810ec20a1241842563c5a5084ebf26722 Mon Sep 17 00:00:00 2001 From: daiwei Date: Fri, 21 Mar 2025 15:42:17 +0800 Subject: [PATCH] wip: add tests --- .../__tests__/components/Teleport.spec.ts | 256 +++++++++++++++--- .../runtime-vapor/src/components/Teleport.ts | 6 +- 2 files changed, 218 insertions(+), 44 deletions(-) diff --git a/packages/runtime-vapor/__tests__/components/Teleport.spec.ts b/packages/runtime-vapor/__tests__/components/Teleport.spec.ts index 373d170bd5..bc77be2dc0 100644 --- a/packages/runtime-vapor/__tests__/components/Teleport.spec.ts +++ b/packages/runtime-vapor/__tests__/components/Teleport.spec.ts @@ -3,7 +3,12 @@ import { type VaporComponent, createComponent as originalCreateComponent, } from '../../src/component' -import { VaporTeleport, template } from '@vue/runtime-vapor' +import { + VaporTeleport, + createTemplateRefSetter, + setInsertionState, + template, +} from '@vue/runtime-vapor' import { makeRender } from '../_utils' import { nextTick, onBeforeUnmount, onUnmounted, ref, shallowRef } from 'vue' @@ -192,50 +197,219 @@ function runSharedTests(deferMode: boolean): void { expect(target.innerHTML).toBe('') }) - test.todo( - 'descendent component should be unmounted when teleport is disabled and unmounted', - async () => { - const root = document.createElement('div') - const beforeUnmount = vi.fn() - const unmounted = vi.fn() - const { component: Comp } = define({ - setup() { - onBeforeUnmount(beforeUnmount) - onUnmounted(unmounted) - return [template('

')(), template('

')()] - }, - }) + test('descendent component should be unmounted when teleport is disabled and unmounted', async () => { + const root = document.createElement('div') + const beforeUnmount = vi.fn() + const unmounted = vi.fn() + const { component: Comp } = define({ + setup() { + onBeforeUnmount(beforeUnmount) + onUnmounted(unmounted) + return [template('

')(), template('

')()] + }, + }) - const { app } = define({ - setup() { - const n0 = createComponent( - VaporTeleport, - { - to: () => null, - disabled: () => true, - }, - { - default: () => createComponent(Comp), - }, - ) - return [n0] - }, - }).create() - app.mount(root) + const { app } = define({ + setup() { + const n0 = createComponent( + VaporTeleport, + { + to: () => null, + disabled: () => true, + }, + { + default: () => createComponent(Comp), + }, + ) + return [n0] + }, + }).create() + app.mount(root) - expect(beforeUnmount).toHaveBeenCalledTimes(0) - expect(unmounted).toHaveBeenCalledTimes(0) + expect(beforeUnmount).toHaveBeenCalledTimes(0) + expect(unmounted).toHaveBeenCalledTimes(0) - app.unmount() - expect(beforeUnmount).toHaveBeenCalledTimes(1) - expect(unmounted).toHaveBeenCalledTimes(1) - }, - ) + app.unmount() + await nextTick() + expect(beforeUnmount).toHaveBeenCalledTimes(1) + expect(unmounted).toHaveBeenCalledTimes(1) + }) + + test('multiple teleport with same target', async () => { + const target = document.createElement('div') + const root = document.createElement('div') + + const child1 = shallowRef(template('

one
')()) + const child2 = shallowRef(template('two')()) + + const { mount } = define({ + setup() { + const n0 = template('
')() + setInsertionState(n0 as any) + createComponent( + VaporTeleport, + { + to: () => target, + }, + { + default: () => child1.value, + }, + ) + createComponent( + VaporTeleport, + { + to: () => target, + }, + { + default: () => child2.value, + }, + ) + return [n0] + }, + }).create() + mount(root) + expect(root.innerHTML).toBe('
') + expect(target.innerHTML).toBe('
one
two') + + // update existing content + child1.value = [ + template('
one
')(), + template('
two
')(), + ] as any + child2.value = [template('three')()] as any + await nextTick() + expect(target.innerHTML).toBe('
one
two
three') + + // toggling + child1.value = [] as any + await nextTick() + expect(root.innerHTML).toBe('
') + expect(target.innerHTML).toBe('three') + + // toggle back + child1.value = [ + template('
one
')(), + template('
two
')(), + ] as any + child2.value = [template('three')()] as any + await nextTick() + expect(root.innerHTML).toBe('
') + // should append + expect(target.innerHTML).toBe('
one
two
three') + + // toggle the other teleport + child2.value = [] as any + await nextTick() + expect(root.innerHTML).toBe('
') + expect(target.innerHTML).toBe('
one
two
') + }) + + test('should work when using template ref as target', async () => { + const root = document.createElement('div') + const target = ref(null) + const disabled = ref(true) + + const { mount } = define({ + setup() { + const setTemplateRef = createTemplateRefSetter() + const n0 = template('
')() as any + setTemplateRef(n0, target) + + const n1 = createComponent( + VaporTeleport, + { + to: () => target.value, + disabled: () => disabled.value, + }, + { + default: () => template('
teleported
')(), + }, + ) + return [n0, n1] + }, + }).create() + mount(root) + + expect(root.innerHTML).toBe( + '
teleported
', + ) + disabled.value = false + await nextTick() + expect(root.innerHTML).toBe( + '
teleported
', + ) + }) + + test('disabled', async () => { + const target = document.createElement('div') + const root = document.createElement('div') + + const disabled = ref(false) + const { mount } = define({ + setup() { + const n0 = createComponent( + VaporTeleport, + { + to: () => target, + disabled: () => disabled.value, + }, + { + default: () => template('
teleported
')(), + }, + ) + const n1 = template('
root
')() + return [n0, n1] + }, + }).create() + mount(root) + + expect(root.innerHTML).toBe('
root
') + expect(target.innerHTML).toBe('
teleported
') + + disabled.value = true + await nextTick() + expect(root.innerHTML).toBe( + '
teleported
root
', + ) + expect(target.innerHTML).toBe('') + + // toggle back + disabled.value = false + await nextTick() + expect(root.innerHTML).toBe( + '
root
', + ) + expect(target.innerHTML).toBe('
teleported
') + }) + + test.todo('moving teleport while enabled', async () => { + const target = document.createElement('div') + const root = document.createElement('div') + + const child1 = createComponent( + VaporTeleport, + { to: () => target }, + { default: () => template('
teleported
')() }, + ) + const child2 = template('
root
')() + + const children = shallowRef([child1, child2]) + const { mount } = define({ + setup() { + return children.value + }, + }).create() + mount(root) + + expect(root.innerHTML).toBe('
root
') + expect(target.innerHTML).toBe('
teleported
') + + children.value = [child2, child1] + await nextTick() + expect(root.innerHTML).toBe('
root
') + expect(target.innerHTML).toBe('
teleported
') + }) - test.todo('multiple teleport with same target', async () => {}) - test.todo('should work when using template ref as target', async () => {}) - test.todo('disabled', async () => {}) - test.todo('moving teleport while enabled', async () => {}) test.todo('moving teleport while disabled', async () => {}) test.todo('should work with block tree', async () => {}) test.todo( diff --git a/packages/runtime-vapor/src/components/Teleport.ts b/packages/runtime-vapor/src/components/Teleport.ts index e12ad4d87e..b0da345403 100644 --- a/packages/runtime-vapor/src/components/Teleport.ts +++ b/packages/runtime-vapor/src/components/Teleport.ts @@ -59,14 +59,14 @@ export class TeleportFragment extends VaporFragment { } update(props: TeleportProps, children: Block): void { + const parent = this.anchor.parentNode // teardown previous - if (this.currentParent && this.nodes) { - remove(this.nodes, this.currentParent) + if (this.nodes && (parent || this.currentParent)) { + remove(this.nodes, this.currentParent! || parent) } this.nodes = children const disabled = isTeleportDisabled(props) - const parent = this.anchor.parentNode const mount = (parent: ParentNode, anchor: Node | null) => { insert(this.nodes, (this.currentParent = parent), anchor) -- 2.47.3