@@ -929,6 +929,90 @@ describe('AnimationGroup', () => {
929929 } ) ;
930930 } ) ;
931931
932+ describe ( 'onAbort()' , ( ) => {
933+ test ( 'should execute callback when a CSSAnimation is cancelled externally (not via AnimationGroup.cancel)' , async ( ) => {
934+ const callback = vi . fn ( ) ;
935+ let rejectFinished : ( error : any ) => void ;
936+
937+ const finishedPromise = new Promise < Animation > ( ( _resolve , reject ) => {
938+ rejectFinished = reject ;
939+ } ) ;
940+
941+ const mockAnimation = createMockAnimation ( {
942+ finished : finishedPromise ,
943+ cancel : vi . fn ( ( ) => {
944+ rejectFinished ( new DOMException ( 'The animation was aborted.' , 'AbortError' ) ) ;
945+ } ) ,
946+ } ) ;
947+
948+ const animationGroup = new AnimationGroup ( [ mockAnimation ] ) ;
949+ const abortPromise = animationGroup . onAbort ( callback ) ;
950+
951+ expect ( callback ) . not . toHaveBeenCalled ( ) ;
952+
953+ // Cancel the inner CSSAnimation directly, simulating the browser
954+ // removing the animation (e.g. a viewEnter effect going out of range)
955+ mockAnimation . cancel ( ) ;
956+
957+ await abortPromise ;
958+
959+ expect ( callback ) . toHaveBeenCalledTimes ( 1 ) ;
960+ } ) ;
961+
962+ test ( 'should execute callback when one of multiple animations is cancelled externally' , async ( ) => {
963+ const callback = vi . fn ( ) ;
964+ let rejectFinished : ( error : any ) => void ;
965+
966+ const cancelledAnimation = createMockAnimation ( {
967+ finished : new Promise < Animation > ( ( _resolve , reject ) => {
968+ rejectFinished = reject ;
969+ } ) ,
970+ cancel : vi . fn ( ( ) => {
971+ rejectFinished ( new DOMException ( 'The animation was aborted.' , 'AbortError' ) ) ;
972+ } ) ,
973+ } ) ;
974+
975+ const normalAnimation = createMockAnimation ( {
976+ finished : new Promise < Animation > ( ( ) => { } ) ,
977+ } ) ;
978+
979+ const animationGroup = new AnimationGroup ( [ normalAnimation , cancelledAnimation ] ) ;
980+ const abortPromise = animationGroup . onAbort ( callback ) ;
981+
982+ cancelledAnimation . cancel ( ) ;
983+
984+ await abortPromise ;
985+
986+ expect ( callback ) . toHaveBeenCalledTimes ( 1 ) ;
987+ } ) ;
988+
989+ test ( 'should not execute callback for non-AbortError rejections' , async ( ) => {
990+ const callback = vi . fn ( ) ;
991+
992+ const mockAnimation = createMockAnimation ( {
993+ finished : Promise . reject ( new Error ( 'Some other error' ) ) ,
994+ } ) ;
995+
996+ const animationGroup = new AnimationGroup ( [ mockAnimation ] ) ;
997+ await animationGroup . onAbort ( callback ) ;
998+
999+ expect ( callback ) . not . toHaveBeenCalled ( ) ;
1000+ } ) ;
1001+
1002+ test ( 'should not execute callback when animations finish successfully' , async ( ) => {
1003+ const callback = vi . fn ( ) ;
1004+
1005+ const mockAnimation = createMockAnimation ( {
1006+ finished : Promise . resolve ( undefined as any ) ,
1007+ } ) ;
1008+
1009+ const animationGroup = new AnimationGroup ( [ mockAnimation ] ) ;
1010+ await animationGroup . onAbort ( callback ) ;
1011+
1012+ expect ( callback ) . not . toHaveBeenCalled ( ) ;
1013+ } ) ;
1014+ } ) ;
1015+
9321016 describe ( 'playState getter' , ( ) => {
9331017 test ( 'should return playState from first animation' , ( ) => {
9341018 const mockAnimation1 = createMockAnimation ( {
0 commit comments