@@ -146,6 +146,200 @@ describe('renderer: teleport', () => {
146146 )
147147 } )
148148
149+ test ( 'should keep the mounted vnode as the patch base across deferred updates' , async ( ) => {
150+ const root = document . createElement ( 'div' )
151+ document . body . appendChild ( root )
152+
153+ const show = ref ( false )
154+ const disabled = ref ( false )
155+ const text = ref ( 'A' )
156+ const phase = ref ( 0 )
157+
158+ const Step1 = {
159+ setup ( ) {
160+ disabled . value = true
161+ text . value = 'B'
162+ phase . value = 1
163+ return ( ) => h ( 'div' , 'step1' )
164+ } ,
165+ }
166+
167+ const Step2 = {
168+ setup ( ) {
169+ disabled . value = false
170+ text . value = 'C'
171+ return ( ) => h ( 'div' , 'step2' )
172+ } ,
173+ }
174+
175+ createDOMApp ( {
176+ render ( ) {
177+ return show . value
178+ ? [
179+ h (
180+ Teleport ,
181+ { to : '#targetId2' , defer : true , disabled : disabled . value } ,
182+ h ( 'div' , text . value ) ,
183+ ) ,
184+ phase . value === 0 ? h ( Step1 ) : h ( Step2 ) ,
185+ h ( 'div' , { id : 'targetId2' } ) ,
186+ ]
187+ : [ h ( 'div' ) ]
188+ } ,
189+ } ) . mount ( root )
190+
191+ show . value = true
192+ await nextTick ( )
193+
194+ expect ( root . innerHTML ) . toMatchInlineSnapshot (
195+ `"<!--teleport start--><!--teleport end--><div>step2</div><div id="targetId2"><div>C</div></div>"` ,
196+ )
197+ } )
198+
199+ test ( 'should handle disabled teleport updates before deferred mount' , async ( ) => {
200+ const root = document . createElement ( 'div' )
201+ const target = document . createElement ( 'div' )
202+ target . id = 'targetId3'
203+ document . body . appendChild ( root )
204+ document . body . appendChild ( target )
205+
206+ const showTeleport = ref ( false )
207+ const disabled = ref ( false )
208+
209+ const Step = {
210+ setup ( ) {
211+ disabled . value = true
212+ return ( ) => h ( 'div' , 'step' )
213+ } ,
214+ }
215+
216+ createDOMApp ( {
217+ render ( ) {
218+ return showTeleport . value
219+ ? [
220+ h (
221+ Teleport ,
222+ { to : '#targetId3' , defer : true , disabled : disabled . value } ,
223+ h ( 'div' , 'teleported' ) ,
224+ ) ,
225+ h ( Step ) ,
226+ ]
227+ : [ h ( 'div' ) ]
228+ } ,
229+ } ) . mount ( root )
230+
231+ expect ( root . innerHTML ) . toMatchInlineSnapshot ( `"<div></div>"` )
232+ expect ( target . innerHTML ) . toBe ( `` )
233+
234+ showTeleport . value = true
235+ await nextTick ( )
236+
237+ expect ( root . innerHTML ) . toMatchInlineSnapshot (
238+ `"<!--teleport start--><div>teleported</div><!--teleport end--><div>step</div>"` ,
239+ )
240+ expect ( target . innerHTML ) . toBe ( `` )
241+ } )
242+
243+ test ( 'should not mount discarded teleport after deferred updates' , async ( ) => {
244+ const root = document . createElement ( 'div' )
245+ const target = document . createElement ( 'div' )
246+ target . id = 'targetId4'
247+ document . body . appendChild ( root )
248+ document . body . appendChild ( target )
249+
250+ const showTeleport = ref ( false )
251+ const phase = ref ( 0 )
252+
253+ const Step1 = {
254+ setup ( ) {
255+ phase . value = 1
256+ return ( ) => h ( 'div' , 'step1' )
257+ } ,
258+ }
259+
260+ const Step2 = {
261+ setup ( ) {
262+ showTeleport . value = false
263+ return ( ) => h ( 'div' , 'step2' )
264+ } ,
265+ }
266+
267+ createDOMApp ( {
268+ render ( ) {
269+ return showTeleport . value
270+ ? [
271+ h (
272+ Teleport ,
273+ { to : '#targetId4' , defer : true } ,
274+ h ( 'div' , 'teleported' ) ,
275+ ) ,
276+ phase . value === 0 ? h ( Step1 ) : h ( Step2 ) ,
277+ ]
278+ : [ h ( 'div' , 'done' ) ]
279+ } ,
280+ } ) . mount ( root )
281+
282+ expect ( root . innerHTML ) . toMatchInlineSnapshot ( `"<div>done</div>"` )
283+ expect ( target . innerHTML ) . toBe ( `` )
284+
285+ showTeleport . value = true
286+ await nextTick ( )
287+
288+ expect ( root . innerHTML ) . toMatchInlineSnapshot ( `"<div>done</div>"` )
289+ expect ( target . innerHTML ) . toBe ( `` )
290+ } )
291+
292+ test ( 'should not mount discarded disabled teleport after deferred updates' , async ( ) => {
293+ const root = document . createElement ( 'div' )
294+ const target = document . createElement ( 'div' )
295+ target . id = 'targetId5'
296+ document . body . appendChild ( root )
297+ document . body . appendChild ( target )
298+
299+ const showTeleport = ref ( false )
300+ const disabled = ref ( false )
301+ const phase = ref ( 0 )
302+
303+ const Step1 = {
304+ setup ( ) {
305+ disabled . value = true
306+ phase . value = 1
307+ return ( ) => h ( 'div' , 'step1' )
308+ } ,
309+ }
310+
311+ const Step2 = {
312+ setup ( ) {
313+ showTeleport . value = false
314+ return ( ) => h ( 'div' , 'step2' )
315+ } ,
316+ }
317+
318+ createDOMApp ( {
319+ render ( ) {
320+ return showTeleport . value
321+ ? [
322+ h (
323+ Teleport ,
324+ { to : '#targetId5' , defer : true , disabled : disabled . value } ,
325+ h ( 'div' , 'teleported' ) ,
326+ ) ,
327+ phase . value === 0 ? h ( Step1 ) : h ( Step2 ) ,
328+ ]
329+ : [ h ( 'div' , 'done' ) ]
330+ } ,
331+ } ) . mount ( root )
332+
333+ expect ( root . innerHTML ) . toMatchInlineSnapshot ( `"<div>done</div>"` )
334+ expect ( target . innerHTML ) . toBe ( `` )
335+
336+ showTeleport . value = true
337+ await nextTick ( )
338+
339+ expect ( root . innerHTML ) . toMatchInlineSnapshot ( `"<div>done</div>"` )
340+ expect ( target . innerHTML ) . toBe ( `` )
341+ } )
342+
149343 // #13349
150344 test ( 'handle deferred teleport updates before and after mount' , async ( ) => {
151345 const root = document . createElement ( 'div' )
0 commit comments