@@ -14,7 +14,7 @@ import {
14
14
type MockContainerRuntime ,
15
15
} from "@fluidframework/test-runtime-utils/internal" ;
16
16
17
- import { SharedStringFactory } from "../sequenceFactory.js" ;
17
+ import { SharedStringFactory , type SharedString } from "../sequenceFactory.js" ;
18
18
import { SharedStringClass } from "../sharedString.js" ;
19
19
20
20
function setupSharedStringRollbackTest ( ) {
@@ -65,41 +65,6 @@ function createAdditionalClient(
65
65
return { sharedString, dataStoreRuntime, containerRuntime } ;
66
66
}
67
67
68
- describe ( "SharedString rollback change events" , ( ) => {
69
- it ( "rollback insert emits remove" , ( ) => {
70
- const { sharedString, containerRuntimeFactory, containerRuntime } =
71
- setupSharedStringRollbackTest ( ) ;
72
-
73
- // Apply insert we will rollback
74
- sharedString . insertText ( 0 , "abc" ) ;
75
- containerRuntimeFactory . processAllMessages ( ) ;
76
- assert . equal ( sharedString . getText ( ) , "abc" , "text after insert" ) ;
77
-
78
- containerRuntime . rollback ?.( ) ;
79
-
80
- // Expect rollback event to be a remove
81
- assert . equal ( sharedString . getText ( ) , "" , "text reverted after rollback" ) ;
82
- } ) ;
83
-
84
- it ( "rollback remove emits insert" , ( ) => {
85
- const { sharedString, containerRuntimeFactory, containerRuntime } =
86
- setupSharedStringRollbackTest ( ) ;
87
- sharedString . insertText ( 0 , "hello" ) ;
88
- containerRuntimeFactory . processAllMessages ( ) ;
89
- containerRuntime . flush ( ) ;
90
- assert . equal ( sharedString . getText ( ) , "hello" ) ;
91
-
92
- // Apply remove we will rollback
93
- sharedString . removeText ( 0 , 5 ) ;
94
- assert . equal ( sharedString . getText ( ) , "" , "text after remove" ) ;
95
-
96
- containerRuntime . rollback ?.( ) ;
97
-
98
- // Expect rollback event to be an insert
99
- assert . equal ( sharedString . getText ( ) , "hello" , "rollback discards remove" ) ;
100
- } ) ;
101
- } ) ;
102
-
103
68
describe ( "SharedString rollback with multiple clients (insert/remove)" , ( ) => {
104
69
it ( "Client1 insert + Client2 insert + rollback on Client1" , ( ) => {
105
70
const {
@@ -555,12 +520,13 @@ describe("SharedString annotate with rollback", () => {
555
520
} ) ;
556
521
557
522
describe ( "SharedString rollback triggers correct sequenceDelta events with text" , ( ) => {
558
- it ( "insert, remove, annotate, and replaceText rollback trigger correct sequenceDelta events" , ( ) => {
559
- const { sharedString, containerRuntimeFactory, containerRuntime } =
560
- setupSharedStringRollbackTest ( ) ;
523
+ interface Event {
524
+ op : string ;
525
+ text : string ;
526
+ }
561
527
562
- const events : { op : string ; text : string } [ ] = [ ] ;
563
- sharedString . on ( "sequenceDelta" , ( { deltaOperation, ranges , isLocal } ) => {
528
+ function setupDeltaListener ( sharedString : SharedString , events : Event [ ] ) {
529
+ sharedString . on ( "sequenceDelta" , ( { deltaOperation, isLocal } ) => {
564
530
if ( ! isLocal ) return ;
565
531
switch ( deltaOperation ) {
566
532
case MergeTreeDeltaType . INSERT :
@@ -576,51 +542,168 @@ describe("SharedString rollback triggers correct sequenceDelta events with text"
576
542
throw new Error ( `Unexpected deltaOperation: ${ deltaOperation } ` ) ;
577
543
}
578
544
} ) ;
545
+ }
546
+
547
+ it ( "rollback of insert triggers remove" , ( ) => {
548
+ const { sharedString, containerRuntimeFactory, containerRuntime } =
549
+ setupSharedStringRollbackTest ( ) ;
550
+ const events : Event [ ] = [ ] ;
551
+ setupDeltaListener ( sharedString , events ) ;
579
552
580
- // --- Insert and rollback ---
581
553
sharedString . insertText ( 0 , "hello" ) ;
582
554
containerRuntimeFactory . processAllMessages ( ) ;
583
555
assert . equal ( sharedString . getText ( ) , "hello" ) ;
556
+
584
557
containerRuntime . rollback ?.( ) ;
558
+
585
559
assert (
586
560
events . some ( ( e ) => e . op === "remove" && e . text === "" ) ,
587
561
"Rollback of insert should trigger remove of correct text" ,
588
562
) ;
563
+ } ) ;
589
564
590
- events . length = 0 ;
565
+ it ( "rollback of remove triggers insert" , ( ) => {
566
+ const { sharedString, containerRuntimeFactory, containerRuntime } =
567
+ setupSharedStringRollbackTest ( ) ;
568
+ const events : Event [ ] = [ ] ;
569
+ setupDeltaListener ( sharedString , events ) ;
591
570
592
- // --- Remove and rollback ---
593
571
sharedString . insertText ( 0 , "world" ) ;
594
572
containerRuntimeFactory . processAllMessages ( ) ;
595
573
sharedString . removeText ( 0 , 5 ) ;
596
574
assert . equal ( sharedString . getText ( ) , "" ) ;
575
+
597
576
containerRuntime . rollback ?.( ) ;
577
+
598
578
assert (
599
579
events . some ( ( e ) => e . op === "insert" && e . text === "world" ) ,
600
580
"Rollback of remove should trigger insert of correct text" ,
601
581
) ;
582
+ } ) ;
602
583
603
- events . length = 0 ;
584
+ it ( "rollback of annotate clears properties" , ( ) => {
585
+ const { sharedString, containerRuntimeFactory, containerRuntime } =
586
+ setupSharedStringRollbackTest ( ) ;
587
+ const events : Event [ ] = [ ] ;
588
+ setupDeltaListener ( sharedString , events ) ;
604
589
605
- // --- Annotate and rollback ---
606
590
sharedString . insertText ( 0 , "abc" ) ;
607
591
containerRuntimeFactory . processAllMessages ( ) ;
592
+
608
593
const styleProps = { style : "bold" } ;
609
594
sharedString . annotateRange ( 0 , 3 , styleProps ) ;
610
- Array . from ( { length : 3 } ) . forEach ( ( _ , i ) =>
611
- assert . deepEqual ( { ...sharedString . getPropertiesAtPosition ( i ) } , { ...styleProps } ) ,
612
- ) ;
595
+ for ( let i = 0 ; i < 3 ; i ++ ) {
596
+ assert . deepEqual ( { ...sharedString . getPropertiesAtPosition ( i ) } , styleProps ) ;
597
+ }
598
+
613
599
containerRuntime . rollback ?.( ) ;
614
- Array . from ( { length : 3 } ) . forEach ( ( _ , i ) =>
600
+
601
+ for ( let i = 0 ; i < 3 ; i ++ ) {
615
602
assert . deepEqual (
616
603
{ ...sharedString . getPropertiesAtPosition ( i ) } ,
617
604
{ } ,
618
605
"Rollback of annotate should clear properties" ,
619
- ) ,
620
- ) ;
606
+ ) ;
607
+ }
608
+
621
609
assert (
622
610
events . some ( ( e ) => e . op === "annotate" && e . text === "abc" ) ,
623
611
"Rollback of annotate should trigger annotate event with correct text" ,
624
612
) ;
625
613
} ) ;
614
+
615
+ it ( "multi-client: rollback of insert triggers remove" , ( ) => {
616
+ const {
617
+ sharedString : client1 ,
618
+ containerRuntimeFactory,
619
+ containerRuntime : cr1 ,
620
+ } = setupSharedStringRollbackTest ( ) ;
621
+ const { sharedString : client2 } = createAdditionalClient ( containerRuntimeFactory , "2" ) ;
622
+
623
+ const eventsClient1 : Event [ ] = [ ] ;
624
+ setupDeltaListener ( client1 , eventsClient1 ) ;
625
+
626
+ client1 . insertText ( 0 , "hello" ) ;
627
+ cr1 . flush ( ) ;
628
+ containerRuntimeFactory . processAllMessages ( ) ;
629
+ assert . equal ( client1 . getText ( ) , "hello" ) ;
630
+ assert . equal ( client2 . getText ( ) , "hello" ) ;
631
+
632
+ client1 . insertText ( 5 , "world" ) ;
633
+ cr1 . rollback ?.( ) ;
634
+
635
+ assert (
636
+ eventsClient1 . some ( ( e ) => e . op === "remove" && e . text === "hello" ) ,
637
+ "Rollback of insert should trigger remove of correct text on client1" ,
638
+ ) ;
639
+ assert . equal ( client1 . getText ( ) , "hello" ) ;
640
+ assert . equal ( client2 . getText ( ) , "hello" ) ;
641
+ } ) ;
642
+
643
+ it ( "multi-client: rollback of remove triggers insert" , ( ) => {
644
+ const {
645
+ sharedString : client1 ,
646
+ containerRuntimeFactory,
647
+ containerRuntime : cr1 ,
648
+ } = setupSharedStringRollbackTest ( ) ;
649
+ const { sharedString : client2 } = createAdditionalClient ( containerRuntimeFactory , "2" ) ;
650
+
651
+ const eventsClient1 : Event [ ] = [ ] ;
652
+ setupDeltaListener ( client1 , eventsClient1 ) ;
653
+
654
+ client1 . insertText ( 0 , "world" ) ;
655
+ cr1 . flush ( ) ;
656
+ containerRuntimeFactory . processAllMessages ( ) ;
657
+
658
+ client1 . removeText ( 0 , 5 ) ;
659
+ assert . equal ( client1 . getText ( ) , "" ) ;
660
+ assert . equal ( client2 . getText ( ) , "world" ) ;
661
+
662
+ cr1 . rollback ?.( ) ;
663
+
664
+ assert (
665
+ eventsClient1 . some ( ( e ) => e . op === "insert" && e . text === "world" ) ,
666
+ "Rollback of remove should trigger insert of correct text on client1" ,
667
+ ) ;
668
+ assert . equal ( client1 . getText ( ) , "world" ) ;
669
+ assert . equal ( client2 . getText ( ) , "world" ) ;
670
+ } ) ;
671
+
672
+ it ( "multi-client: rollback of annotate clears properties" , ( ) => {
673
+ const {
674
+ sharedString : client1 ,
675
+ containerRuntimeFactory,
676
+ containerRuntime : cr1 ,
677
+ } = setupSharedStringRollbackTest ( ) ;
678
+ createAdditionalClient ( containerRuntimeFactory , "2" ) ;
679
+
680
+ const eventsClient1 : Event [ ] = [ ] ;
681
+ setupDeltaListener ( client1 , eventsClient1 ) ;
682
+
683
+ client1 . insertText ( 0 , "abc" ) ;
684
+ cr1 . flush ( ) ;
685
+ containerRuntimeFactory . processAllMessages ( ) ;
686
+
687
+ const styleProps = { style : "bold" } ;
688
+ client1 . annotateRange ( 0 , 3 , styleProps ) ;
689
+
690
+ for ( let i = 0 ; i < 3 ; i ++ ) {
691
+ assert . deepEqual ( { ...client1 . getPropertiesAtPosition ( i ) } , styleProps ) ;
692
+ }
693
+
694
+ cr1 . rollback ?.( ) ;
695
+
696
+ for ( let i = 0 ; i < 3 ; i ++ ) {
697
+ assert . deepEqual (
698
+ { ...client1 . getPropertiesAtPosition ( i ) } ,
699
+ { } ,
700
+ "Rollback of annotate should clear properties" ,
701
+ ) ;
702
+ }
703
+
704
+ assert (
705
+ eventsClient1 . some ( ( e ) => e . op === "annotate" && e . text === "abc" ) ,
706
+ "Rollback of annotate should trigger annotate event with correct text" ,
707
+ ) ;
708
+ } ) ;
626
709
} ) ;
0 commit comments