@@ -882,6 +882,82 @@ describe('D3 pre-handoff animation runtime', () => {
882882 expect ( rect . getFinalAttribute ( ) . width ) . toBe ( resized . width ) ;
883883 } ) ;
884884
885+ test ( 'superseded executor update cannot commit stale layout when it ends late' , ( ) => {
886+ const { group, ticker, graphicService } = createStageHarness ( 'executor-update-stale-end' ) ;
887+ const threeItemLayout = {
888+ x : 76.61875 ,
889+ y : 0 ,
890+ y1 : 320 ,
891+ width : 41.5125 ,
892+ visible : true
893+ } ;
894+ const fourItemLayout = {
895+ x : 73.54375 ,
896+ y : 0 ,
897+ y1 : 320 ,
898+ width : 32.2875 ,
899+ visible : true
900+ } ;
901+ const rect = createRect ( threeItemLayout ) ;
902+ bindGraphicService ( rect as any , graphicService ) ;
903+ rect . setFinalAttributes ( threeItemLayout ) ;
904+ group . appendChild ( rect ) ;
905+
906+ ( rect as any ) . context = {
907+ animationState : 'update' ,
908+ data : [ { value : 10 } ] ,
909+ diffAttrs : {
910+ x : fourItemLayout . x ,
911+ width : fourItemLayout . width
912+ } ,
913+ finalAttrs : fourItemLayout
914+ } ;
915+ rect . setFinalAttributes ( fourItemLayout ) ;
916+ new AnimateExecutor ( rect ) . execute ( {
917+ type : 'update' ,
918+ duration : 200 ,
919+ easing : 'linear'
920+ } ) ;
921+
922+ tick ( ticker , 50 ) ;
923+ expect ( rect . attribute . x ) . toBeLessThan ( threeItemLayout . x ) ;
924+ expect ( rect . attribute . x ) . toBeGreaterThan ( fourItemLayout . x ) ;
925+ expect ( rect . attribute . width ) . toBeLessThan ( threeItemLayout . width ) ;
926+ expect ( rect . attribute . width ) . toBeGreaterThan ( fourItemLayout . width ) ;
927+
928+ ( rect as any ) . context = {
929+ animationState : 'update' ,
930+ data : [ { value : 10 } ] ,
931+ diffAttrs : {
932+ x : threeItemLayout . x ,
933+ width : threeItemLayout . width
934+ } ,
935+ finalAttrs : threeItemLayout
936+ } ;
937+ rect . setFinalAttributes ( threeItemLayout ) ;
938+ new AnimateExecutor ( rect ) . execute ( {
939+ type : 'update' ,
940+ duration : 100 ,
941+ easing : 'linear'
942+ } ) ;
943+
944+ tick ( ticker , 100 ) ;
945+ expect ( rect . attribute . x ) . toBeCloseTo ( threeItemLayout . x , 5 ) ;
946+ expect ( rect . attribute . width ) . toBeCloseTo ( threeItemLayout . width , 5 ) ;
947+ expect ( ( rect as any ) . baseAttributes . x ) . toBeCloseTo ( threeItemLayout . x , 5 ) ;
948+ expect ( ( rect as any ) . baseAttributes . width ) . toBeCloseTo ( threeItemLayout . width , 5 ) ;
949+ expect ( rect . getFinalAttribute ( ) . x ) . toBeCloseTo ( threeItemLayout . x , 5 ) ;
950+ expect ( rect . getFinalAttribute ( ) . width ) . toBeCloseTo ( threeItemLayout . width , 5 ) ;
951+
952+ tick ( ticker , 50 ) ;
953+ expect ( rect . attribute . x ) . toBeCloseTo ( threeItemLayout . x , 5 ) ;
954+ expect ( rect . attribute . width ) . toBeCloseTo ( threeItemLayout . width , 5 ) ;
955+ expect ( ( rect as any ) . baseAttributes . x ) . toBeCloseTo ( threeItemLayout . x , 5 ) ;
956+ expect ( ( rect as any ) . baseAttributes . width ) . toBeCloseTo ( threeItemLayout . width , 5 ) ;
957+ expect ( rect . getFinalAttribute ( ) . x ) . toBeCloseTo ( threeItemLayout . x , 5 ) ;
958+ expect ( rect . getFinalAttribute ( ) . width ) . toBeCloseTo ( threeItemLayout . width , 5 ) ;
959+ } ) ;
960+
885961 test ( 'switching states mid-animation restores to the new static truth and blocks late writes' , ( ) => {
886962 const { group, ticker, graphicService } = createStageHarness ( 'state-conflict' ) ;
887963 const rect = createAnimatedRect ( graphicService ) ;
0 commit comments