@@ -55,6 +55,7 @@ import {
5555 getChars ,
5656 getWpmHistory ,
5757 forceReleaseAllKeys ,
58+ getCorrectedWords ,
5859 __testing as statsTesting ,
5960} from "../../../src/ts/test/events/stats" ;
6061import type {
@@ -677,6 +678,284 @@ describe("stats.ts", () => {
677678 } ) ;
678679 } ) ;
679680
681+ describe ( "getCorrectedWords" , ( ) => {
682+ it ( "returns input as-is when no corrections made" , ( ) => {
683+ logTestEvent ( "timer" , 1000 , timer ( "start" , 0 ) ) ;
684+ logTestEvent (
685+ "input" ,
686+ 1100 ,
687+ input ( { charIndex : 0 , wordIndex : 0 , data : "t" } ) ,
688+ ) ;
689+ logTestEvent (
690+ "input" ,
691+ 1150 ,
692+ input ( { charIndex : 1 , wordIndex : 0 , data : "e" } ) ,
693+ ) ;
694+ logTestEvent (
695+ "input" ,
696+ 1200 ,
697+ input ( { charIndex : 2 , wordIndex : 0 , data : "s" } ) ,
698+ ) ;
699+ logTestEvent (
700+ "input" ,
701+ 1250 ,
702+ input ( { charIndex : 3 , wordIndex : 0 , data : "t" } ) ,
703+ ) ;
704+
705+ expect ( getCorrectedWords ( ) ) . toEqual ( [ "test" ] ) ;
706+ } ) ;
707+
708+ it ( "returns last deleted char per position (xact -> fact)" , ( ) => {
709+ logTestEvent ( "timer" , 1000 , timer ( "start" , 0 ) ) ;
710+ // type "xact"
711+ logTestEvent (
712+ "input" ,
713+ 1100 ,
714+ input ( { charIndex : 0 , wordIndex : 0 , data : "x" } ) ,
715+ ) ;
716+ logTestEvent (
717+ "input" ,
718+ 1150 ,
719+ input ( { charIndex : 1 , wordIndex : 0 , data : "a" } ) ,
720+ ) ;
721+ logTestEvent (
722+ "input" ,
723+ 1200 ,
724+ input ( { charIndex : 2 , wordIndex : 0 , data : "c" } ) ,
725+ ) ;
726+ logTestEvent (
727+ "input" ,
728+ 1250 ,
729+ input ( { charIndex : 3 , wordIndex : 0 , data : "t" } ) ,
730+ ) ;
731+ // delete all
732+ logTestEvent ( "input" , 1300 , {
733+ charIndex : 3 ,
734+ wordIndex : 0 ,
735+ inputType : "deleteContentBackward" ,
736+ } as InputEventData ) ;
737+ logTestEvent ( "input" , 1350 , {
738+ charIndex : 2 ,
739+ wordIndex : 0 ,
740+ inputType : "deleteContentBackward" ,
741+ } as InputEventData ) ;
742+ logTestEvent ( "input" , 1400 , {
743+ charIndex : 1 ,
744+ wordIndex : 0 ,
745+ inputType : "deleteContentBackward" ,
746+ } as InputEventData ) ;
747+ logTestEvent ( "input" , 1450 , {
748+ charIndex : 0 ,
749+ wordIndex : 0 ,
750+ inputType : "deleteContentBackward" ,
751+ } as InputEventData ) ;
752+ // type "fact"
753+ logTestEvent (
754+ "input" ,
755+ 1500 ,
756+ input ( { charIndex : 0 , wordIndex : 0 , data : "f" } ) ,
757+ ) ;
758+ logTestEvent (
759+ "input" ,
760+ 1550 ,
761+ input ( { charIndex : 1 , wordIndex : 0 , data : "a" } ) ,
762+ ) ;
763+ logTestEvent (
764+ "input" ,
765+ 1600 ,
766+ input ( { charIndex : 2 , wordIndex : 0 , data : "c" } ) ,
767+ ) ;
768+ logTestEvent (
769+ "input" ,
770+ 1650 ,
771+ input ( { charIndex : 3 , wordIndex : 0 , data : "t" } ) ,
772+ ) ;
773+
774+ expect ( getCorrectedWords ( ) ) . toEqual ( [ "xact" ] ) ;
775+ } ) ;
776+
777+ it ( "returns last deleted char per position across multiple corrections (xest -> west -> test)" , ( ) => {
778+ logTestEvent ( "timer" , 1000 , timer ( "start" , 0 ) ) ;
779+ // type "xest"
780+ logTestEvent (
781+ "input" ,
782+ 1100 ,
783+ input ( { charIndex : 0 , wordIndex : 0 , data : "x" } ) ,
784+ ) ;
785+ logTestEvent (
786+ "input" ,
787+ 1150 ,
788+ input ( { charIndex : 1 , wordIndex : 0 , data : "e" } ) ,
789+ ) ;
790+ logTestEvent (
791+ "input" ,
792+ 1200 ,
793+ input ( { charIndex : 2 , wordIndex : 0 , data : "s" } ) ,
794+ ) ;
795+ logTestEvent (
796+ "input" ,
797+ 1250 ,
798+ input ( { charIndex : 3 , wordIndex : 0 , data : "t" } ) ,
799+ ) ;
800+ // delete all
801+ logTestEvent ( "input" , 1300 , {
802+ charIndex : 3 ,
803+ wordIndex : 0 ,
804+ inputType : "deleteWordBackward" ,
805+ } as InputEventData ) ;
806+ // type "west"
807+ logTestEvent (
808+ "input" ,
809+ 1400 ,
810+ input ( { charIndex : 0 , wordIndex : 0 , data : "w" } ) ,
811+ ) ;
812+ logTestEvent (
813+ "input" ,
814+ 1450 ,
815+ input ( { charIndex : 1 , wordIndex : 0 , data : "e" } ) ,
816+ ) ;
817+ logTestEvent (
818+ "input" ,
819+ 1500 ,
820+ input ( { charIndex : 2 , wordIndex : 0 , data : "s" } ) ,
821+ ) ;
822+ logTestEvent (
823+ "input" ,
824+ 1550 ,
825+ input ( { charIndex : 3 , wordIndex : 0 , data : "t" } ) ,
826+ ) ;
827+ // delete all
828+ logTestEvent ( "input" , 1600 , {
829+ charIndex : 3 ,
830+ wordIndex : 0 ,
831+ inputType : "deleteWordBackward" ,
832+ } as InputEventData ) ;
833+ // type "test"
834+ logTestEvent (
835+ "input" ,
836+ 1700 ,
837+ input ( { charIndex : 0 , wordIndex : 0 , data : "t" } ) ,
838+ ) ;
839+ logTestEvent (
840+ "input" ,
841+ 1750 ,
842+ input ( { charIndex : 1 , wordIndex : 0 , data : "e" } ) ,
843+ ) ;
844+ logTestEvent (
845+ "input" ,
846+ 1800 ,
847+ input ( { charIndex : 2 , wordIndex : 0 , data : "s" } ) ,
848+ ) ;
849+ logTestEvent (
850+ "input" ,
851+ 1850 ,
852+ input ( { charIndex : 3 , wordIndex : 0 , data : "t" } ) ,
853+ ) ;
854+
855+ expect ( getCorrectedWords ( ) ) . toEqual ( [ "west" ] ) ;
856+ } ) ;
857+
858+ it ( "handles partial correction (tset -> delete last 2 -> st)" , ( ) => {
859+ logTestEvent ( "timer" , 1000 , timer ( "start" , 0 ) ) ;
860+ // type "tset"
861+ logTestEvent (
862+ "input" ,
863+ 1100 ,
864+ input ( { charIndex : 0 , wordIndex : 0 , data : "t" } ) ,
865+ ) ;
866+ logTestEvent (
867+ "input" ,
868+ 1150 ,
869+ input ( { charIndex : 1 , wordIndex : 0 , data : "s" } ) ,
870+ ) ;
871+ logTestEvent (
872+ "input" ,
873+ 1200 ,
874+ input ( { charIndex : 2 , wordIndex : 0 , data : "e" } ) ,
875+ ) ;
876+ logTestEvent (
877+ "input" ,
878+ 1250 ,
879+ input ( { charIndex : 3 , wordIndex : 0 , data : "t" } ) ,
880+ ) ;
881+ // delete last 2
882+ logTestEvent ( "input" , 1300 , {
883+ charIndex : 3 ,
884+ wordIndex : 0 ,
885+ inputType : "deleteContentBackward" ,
886+ } as InputEventData ) ;
887+ logTestEvent ( "input" , 1350 , {
888+ charIndex : 2 ,
889+ wordIndex : 0 ,
890+ inputType : "deleteContentBackward" ,
891+ } as InputEventData ) ;
892+ // type "st"
893+ logTestEvent (
894+ "input" ,
895+ 1400 ,
896+ input ( { charIndex : 2 , wordIndex : 0 , data : "s" } ) ,
897+ ) ;
898+ logTestEvent (
899+ "input" ,
900+ 1450 ,
901+ input ( { charIndex : 3 , wordIndex : 0 , data : "t" } ) ,
902+ ) ;
903+
904+ // pos 0: "t" never deleted, pos 1: "s" never deleted, pos 2: "e" deleted, pos 3: "t" deleted
905+ expect ( getCorrectedWords ( ) ) . toEqual ( [ "tset" ] ) ;
906+ } ) ;
907+
908+ it ( "handles multiple words" , ( ) => {
909+ logTestEvent ( "timer" , 1000 , timer ( "start" , 0 ) ) ;
910+ // word 0: type "ab" correctly
911+ logTestEvent (
912+ "input" ,
913+ 1100 ,
914+ input ( { charIndex : 0 , wordIndex : 0 , data : "a" } ) ,
915+ ) ;
916+ logTestEvent (
917+ "input" ,
918+ 1150 ,
919+ input ( { charIndex : 1 , wordIndex : 0 , data : "b" } ) ,
920+ ) ;
921+ // word 1: type "xy", delete both, type "zw"
922+ logTestEvent (
923+ "input" ,
924+ 1200 ,
925+ input ( { charIndex : 0 , wordIndex : 1 , data : "x" } ) ,
926+ ) ;
927+ logTestEvent (
928+ "input" ,
929+ 1250 ,
930+ input ( { charIndex : 1 , wordIndex : 1 , data : "y" } ) ,
931+ ) ;
932+ logTestEvent ( "input" , 1300 , {
933+ charIndex : 1 ,
934+ wordIndex : 1 ,
935+ inputType : "deleteContentBackward" ,
936+ } as InputEventData ) ;
937+ logTestEvent ( "input" , 1350 , {
938+ charIndex : 1 ,
939+ wordIndex : 1 ,
940+ inputType : "deleteContentBackward" ,
941+ } as InputEventData ) ;
942+ logTestEvent (
943+ "input" ,
944+ 1400 ,
945+ input ( { charIndex : 0 , wordIndex : 1 , data : "z" } ) ,
946+ ) ;
947+ logTestEvent (
948+ "input" ,
949+ 1450 ,
950+ input ( { charIndex : 1 , wordIndex : 1 , data : "w" } ) ,
951+ ) ;
952+
953+ const result = getCorrectedWords ( ) ;
954+ expect ( result [ 0 ] ) . toEqual ( "ab" ) ;
955+ expect ( result [ 1 ] ) . toEqual ( "xy" ) ;
956+ } ) ;
957+ } ) ;
958+
680959 describe ( "forceReleaseAllKeys" , ( ) => {
681960 it ( "creates synthetic keyup events for pressed keys" , ( ) => {
682961 logTestEvent ( "timer" , 1000 , timer ( "start" , 0 ) ) ;
0 commit comments