@@ -7137,3 +7137,109 @@ describe('paste multiline over full selection', () => {
71377137 expect ( range . startOffset ) . toBe ( 3 ) ;
71387138 } ) ;
71397139} ) ;
7140+
7141+ describe ( 'paste partial selection replacement' , ( ) => {
7142+ function pasteText ( element : HTMLElement , text : string ) {
7143+ fireEvent . paste ( element , {
7144+ clipboardData : {
7145+ getData : ( type : string ) => ( type === 'text/plain' ? text : '' ) ,
7146+ types : [ 'text/plain' ] ,
7147+ items : [ ] ,
7148+ files : [ ] ,
7149+ } ,
7150+ } ) ;
7151+ }
7152+
7153+ test ( 'paste replaces partial text selection within a single line' , ( ) => {
7154+ const ref = React . createRef < PromptInputProps . Ref > ( ) ;
7155+ const onChange = jest . fn ( ) ;
7156+ renderStatefulTokenMode ( {
7157+ props : { tokens : [ { type : 'text' , value : 'hello world' } ] , onChange } ,
7158+ ref,
7159+ } ) ;
7160+ act ( ( ) => {
7161+ ref . current ! . focus ( ) ;
7162+ } ) ;
7163+ act ( ( ) => {
7164+ ref . current ! . setSelectionRange ( 6 , 11 ) ;
7165+ } ) ;
7166+ const el = document . querySelector ( '[role="textbox"]' ) as HTMLElement ;
7167+ act ( ( ) => {
7168+ pasteText ( el , 'universe' ) ;
7169+ } ) ;
7170+ expect ( onChange ) . toHaveBeenCalled ( ) ;
7171+ const lastValue = onChange . mock . calls [ onChange . mock . calls . length - 1 ] [ 0 ] . detail . value ;
7172+ expect ( lastValue ) . toContain ( 'hello' ) ;
7173+ expect ( lastValue ) . toContain ( 'universe' ) ;
7174+ expect ( lastValue ) . not . toContain ( 'world' ) ;
7175+ } ) ;
7176+
7177+ test ( 'paste multiline over partial selection in multiline content' , ( ) => {
7178+ const ref = React . createRef < PromptInputProps . Ref > ( ) ;
7179+ const onChange = jest . fn ( ) ;
7180+ renderStatefulTokenMode ( {
7181+ props : {
7182+ tokens : [
7183+ { type : 'text' , value : 'aaa' } ,
7184+ { type : 'break' , value : '\n' } ,
7185+ { type : 'text' , value : 'bbb' } ,
7186+ { type : 'break' , value : '\n' } ,
7187+ { type : 'text' , value : 'ccc' } ,
7188+ ] ,
7189+ onChange,
7190+ } ,
7191+ ref,
7192+ } ) ;
7193+ act ( ( ) => {
7194+ ref . current ! . focus ( ) ;
7195+ } ) ;
7196+ // Select middle line "bbb" (positions 4-7)
7197+ act ( ( ) => {
7198+ ref . current ! . setSelectionRange ( 4 , 7 ) ;
7199+ } ) ;
7200+ const el = document . querySelector ( '[role="textbox"]' ) as HTMLElement ;
7201+ act ( ( ) => {
7202+ pasteText ( el , 'xxx\nyyy' ) ;
7203+ } ) ;
7204+ expect ( onChange ) . toHaveBeenCalled ( ) ;
7205+ const lastTokens = onChange . mock . calls [ onChange . mock . calls . length - 1 ] [ 0 ] . detail . tokens ;
7206+ const texts = lastTokens . filter ( ( t : any ) => t . type === 'text' ) . map ( ( t : any ) => t . value ) ;
7207+ // "aaa" should remain, "bbb" replaced by "xxx\nyyy", "ccc" should remain
7208+ expect ( texts . join ( ' ' ) ) . toContain ( 'aaa' ) ;
7209+ expect ( texts . join ( ' ' ) ) . toContain ( 'ccc' ) ;
7210+ expect ( texts . join ( ' ' ) ) . not . toContain ( 'bbb' ) ;
7211+ } ) ;
7212+
7213+ test ( 'paste is no-op when readOnly' , ( ) => {
7214+ const ref = React . createRef < PromptInputProps . Ref > ( ) ;
7215+ const onChange = jest . fn ( ) ;
7216+ renderStatefulTokenMode ( {
7217+ props : { tokens : [ { type : 'text' , value : 'original' } ] , onChange, readOnly : true } ,
7218+ ref,
7219+ } ) ;
7220+ const callsBefore = onChange . mock . calls . length ;
7221+ const el = document . querySelector ( '[role="textbox"]' ) as HTMLElement ;
7222+ act ( ( ) => {
7223+ pasteText ( el , 'injected' ) ;
7224+ } ) ;
7225+ expect ( onChange . mock . calls . length ) . toBe ( callsBefore ) ;
7226+ } ) ;
7227+
7228+ test ( 'paste empty string is no-op' , ( ) => {
7229+ const ref = React . createRef < PromptInputProps . Ref > ( ) ;
7230+ const onChange = jest . fn ( ) ;
7231+ renderStatefulTokenMode ( {
7232+ props : { tokens : [ { type : 'text' , value : 'original' } ] , onChange } ,
7233+ ref,
7234+ } ) ;
7235+ act ( ( ) => {
7236+ ref . current ! . focus ( ) ;
7237+ } ) ;
7238+ const callsBefore = onChange . mock . calls . length ;
7239+ const el = document . querySelector ( '[role="textbox"]' ) as HTMLElement ;
7240+ act ( ( ) => {
7241+ pasteText ( el , '' ) ;
7242+ } ) ;
7243+ expect ( onChange . mock . calls . length ) . toBe ( callsBefore ) ;
7244+ } ) ;
7245+ } ) ;
0 commit comments