@@ -26,6 +26,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2626import { cleanup , fireEvent , render , screen } from "@testing-library/react" ;
2727import { useState } from "react" ;
2828import { animatedProps , installMatchMediaStub } from "../test/motionHarness" ;
29+ import { gsap } from "../motion/engine" ;
2930import { ANIMATABLE_PROPERTIES } from "../motion/shift" ;
3031import { DURATION_TOKENS } from "../ui/tokens.gen" ;
3132import { flipDurationSec , flipSelector , useColumnFlip , type ColumnFlip } from "./useColumnFlip" ;
@@ -206,4 +207,24 @@ describe("T33 · 列折叠 Flip(路径 B:`data-flip-id` 跨元素配对)",
206207 expect ( tiers . map ( ( t ) => flipDurationSec ( t ) ) ) . toEqual ( [ null , ms ( "card" ) / 1000 , ms ( "reveal" ) / 1000 ] ) ;
207208 expect ( tiers . map ( ( t ) => flipDurationSec ( t ) ) ) . toEqual ( [ null , 0.22 , 0.5 ] ) ;
208209 } ) ;
210+
211+ it ( "C6 中断(R8.2 **双断言**):第二次折叠接管后,句柄换成**新**时间线,且**旧时间线已从 globalTimeline 摘除**(收尸)" , async ( ) => {
212+ render ( < Harness /> ) ;
213+ await flush ( ) ;
214+ fireEvent . click ( screen . getByTestId ( "toggle" ) ) ; // 展开 → 折叠
215+ await flush ( ) ;
216+ const first = handle ?. current ?. timeline ?? null ;
217+ expect ( first , "第一次折叠没建时间线(本判据的前提)" ) . not . toBeNull ( ) ;
218+ // 前提自证(仪器看得见时间线):新时间线在场上时确实挂在 globalTimeline 上
219+ expect ( gsap . globalTimeline . getChildren ( ) . includes ( first as never ) , "仪器看不到时间线 ⇒ 下面的「已摘除」会假绿" ) . toBe ( true ) ;
220+
221+ fireEvent . click ( screen . getByTestId ( "toggle" ) ) ; // 折叠 → 展开:下一个输入接管,**不排队**
222+ await flush ( ) ;
223+ const second = handle ?. current ?. timeline ?? null ;
224+ expect ( second === first , "旧时间线仍是句柄里那条 ⇒ 没有接管" ) . toBe ( false ) ;
225+ expect (
226+ gsap . globalTimeline . getChildren ( ) . includes ( first as never ) ,
227+ "旧时间线仍挂在 globalTimeline ⇒ 没被收尸(下一个输入接管失败;M6 变异体正是这一条)" ,
228+ ) . toBe ( false ) ;
229+ } ) ;
209230} ) ;
0 commit comments