@@ -324,4 +324,226 @@ describe('SuggestionMention', () => {
324324 expect ( mentions . map ( ( mention ) => mention . handle ) ) . toEqual ( [ 'ua@example.com' , 'ub@example.com' , 'uc@example.com' ] ) ;
325325 } ) ;
326326 } ) ;
327+
328+ it ( 'preserves the first mention when a second mention is inserted before it' , async ( ) => {
329+ mockPersonalDetails = { } ;
330+ mockPersonalDetails [ 2 ] = {
331+ accountID : 2 ,
332+ login : 'adam@example.com' ,
333+ firstName : 'Adam' ,
334+ lastName : 'Tester' ,
335+ } ;
336+ mockPersonalDetails [ 3 ] = {
337+ accountID : 3 ,
338+ login : 'bob@example.com' ,
339+ firstName : 'Bob' ,
340+ lastName : 'Tester' ,
341+ } ;
342+
343+ const updateComment = jest . fn ( ) ;
344+ const { setSelection} = renderSuggestionMention ( '@b@adam@example.com ' , updateComment , { start : 2 , end : 2 } ) ;
345+
346+ await waitFor ( ( ) => expect ( mockMentionSuggestionsSpy ) . toHaveBeenCalled ( ) ) ;
347+ const { onSelect} = getLastMentionSuggestionsProps ( ) ;
348+
349+ act ( ( ) => onSelect ( 0 ) ) ;
350+
351+ expect ( updateComment ) . toHaveBeenCalledWith ( '@bob@example.com @adam@example.com ' , true ) ;
352+ expect ( setSelection ) . toHaveBeenCalledWith ( { start : 17 , end : 17 } ) ;
353+ } ) ;
354+
355+ it ( 'preserves a trailing @here mention when inserting a new mention before it' , async ( ) => {
356+ mockPersonalDetails = { } ;
357+ mockPersonalDetails [ 2 ] = {
358+ accountID : 2 ,
359+ login : 'adam@example.com' ,
360+ firstName : 'Adam' ,
361+ lastName : 'Tester' ,
362+ } ;
363+
364+ const updateComment = jest . fn ( ) ;
365+ const { setSelection} = renderSuggestionMention ( '@adam@here' , updateComment , { start : 5 , end : 5 } ) ;
366+
367+ await waitFor ( ( ) => expect ( mockMentionSuggestionsSpy ) . toHaveBeenCalled ( ) ) ;
368+ const { onSelect} = getLastMentionSuggestionsProps ( ) ;
369+
370+ act ( ( ) => onSelect ( 0 ) ) ;
371+
372+ expect ( updateComment ) . toHaveBeenCalledWith ( '@adam@example.com @here' , true ) ;
373+ expect ( setSelection ) . toHaveBeenCalledWith ( { start : 18 , end : 18 } ) ;
374+ } ) ;
375+
376+ it ( 'preserves a trailing phone number mention when inserting a new mention before it' , async ( ) => {
377+ mockPersonalDetails = { } ;
378+ mockPersonalDetails [ 2 ] = {
379+ accountID : 2 ,
380+ login : 'adam@example.com' ,
381+ firstName : 'Adam' ,
382+ lastName : 'Tester' ,
383+ } ;
384+
385+ const updateComment = jest . fn ( ) ;
386+ const { setSelection} = renderSuggestionMention ( '@adam@+14404589784' , updateComment , { start : 5 , end : 5 } ) ;
387+
388+ await waitFor ( ( ) => expect ( mockMentionSuggestionsSpy ) . toHaveBeenCalled ( ) ) ;
389+ const { onSelect} = getLastMentionSuggestionsProps ( ) ;
390+
391+ act ( ( ) => onSelect ( 0 ) ) ;
392+
393+ expect ( updateComment ) . toHaveBeenCalledWith ( '@adam@example.com @+14404589784' , true ) ;
394+ expect ( setSelection ) . toHaveBeenCalledWith ( { start : 18 , end : 18 } ) ;
395+ } ) ;
396+
397+ it ( 'preserves a trailing private domain short mention when inserting a new mention before it' , async ( ) => {
398+ mockPersonalDetails = { } ;
399+ mockPersonalDetails [ 2 ] = {
400+ accountID : 2 ,
401+ login : 'adam@example.com' ,
402+ firstName : 'Adam' ,
403+ lastName : 'Tester' ,
404+ } ;
405+ mockPersonalDetails [ 3 ] = {
406+ accountID : 3 ,
407+ login : 'charlie@company.com' ,
408+ firstName : 'Charlie' ,
409+ lastName : 'Tester' ,
410+ } ;
411+
412+ // Override to a private domain so charlie@company .com shows as @charlie in the composer
413+ mockUseCurrentUserPersonalDetails . mockReturnValue ( { accountID : 1 , login : 'current@company.com' } ) ;
414+
415+ const updateComment = jest . fn ( ) ;
416+ const { setSelection} = renderSuggestionMention ( '@adam@charlie' , updateComment , { start : 5 , end : 5 } ) ;
417+
418+ await waitFor ( ( ) => expect ( mockMentionSuggestionsSpy ) . toHaveBeenCalled ( ) ) ;
419+ const { onSelect} = getLastMentionSuggestionsProps ( ) ;
420+
421+ act ( ( ) => onSelect ( 0 ) ) ;
422+
423+ expect ( updateComment ) . toHaveBeenCalledWith ( '@adam@example.com @charlie' , true ) ;
424+ expect ( setSelection ) . toHaveBeenCalledWith ( { start : 18 , end : 18 } ) ;
425+ } ) ;
426+
427+ it ( 'preserves a trailing #room mention when inserting a new mention before it' , async ( ) => {
428+ mockPersonalDetails = { } ;
429+ mockPersonalDetails [ 2 ] = {
430+ accountID : 2 ,
431+ login : 'adam@example.com' ,
432+ firstName : 'Adam' ,
433+ lastName : 'Tester' ,
434+ } ;
435+
436+ const updateComment = jest . fn ( ) ;
437+ const { setSelection} = renderSuggestionMention ( '@adam#admins' , updateComment , { start : 5 , end : 5 } ) ;
438+
439+ await waitFor ( ( ) => expect ( mockMentionSuggestionsSpy ) . toHaveBeenCalled ( ) ) ;
440+ const { onSelect} = getLastMentionSuggestionsProps ( ) ;
441+
442+ act ( ( ) => onSelect ( 0 ) ) ;
443+
444+ expect ( updateComment ) . toHaveBeenCalledWith ( '@adam@example.com #admins' , true ) ;
445+ expect ( setSelection ) . toHaveBeenCalledWith ( { start : 18 , end : 18 } ) ;
446+ } ) ;
447+
448+ it ( 'removes junk between the new mention and a trailing complete mention' , async ( ) => {
449+ mockPersonalDetails = { } ;
450+ mockPersonalDetails [ 2 ] = {
451+ accountID : 2 ,
452+ login : 'alice@example.com' ,
453+ firstName : 'Alice' ,
454+ lastName : 'Tester' ,
455+ } ;
456+
457+ const updateComment = jest . fn ( ) ;
458+ // "@alice@exam@alice@example.com" — cursor at index 6 (after "@alice").
459+ // The user partially typed "alice" before a stale/duplicate mention.
460+ // Inserting the full mention should drop the "@exam" junk and keep the trailing mention.
461+ const { setSelection} = renderSuggestionMention ( '@alice@exam@alice@example.com' , updateComment , { start : 6 , end : 6 } ) ;
462+
463+ await waitFor ( ( ) => expect ( mockMentionSuggestionsSpy ) . toHaveBeenCalled ( ) ) ;
464+ const { onSelect} = getLastMentionSuggestionsProps ( ) ;
465+
466+ act ( ( ) => onSelect ( 0 ) ) ;
467+
468+ expect ( updateComment ) . toHaveBeenCalledWith ( '@alice@example.com @alice@example.com' , true ) ;
469+ expect ( setSelection ) . toHaveBeenCalledWith ( { start : 19 , end : 19 } ) ;
470+ } ) ;
471+
472+ it ( 'removes junk between the new mention and a trailing private domain short mention' , async ( ) => {
473+ mockPersonalDetails = { } ;
474+ mockPersonalDetails [ 2 ] = {
475+ accountID : 2 ,
476+ login : 'bob@company.com' ,
477+ firstName : 'Bob' ,
478+ lastName : 'Tester' ,
479+ } ;
480+ mockPersonalDetails [ 3 ] = {
481+ accountID : 3 ,
482+ login : 'charlie@company.com' ,
483+ firstName : 'Charlie' ,
484+ lastName : 'Tester' ,
485+ } ;
486+
487+ // Override to a private domain so charlie@company .com shows as @charlie in the composer
488+ mockUseCurrentUserPersonalDetails . mockReturnValue ( { accountID : 1 , login : 'current@company.com' } ) ;
489+
490+ const updateComment = jest . fn ( ) ;
491+ // "@b@comp@charlie" — cursor at index 2 (after "@b").
492+ // Inserting the full @bob mention should drop the "@comp" junk and keep the trailing @charlie short mention.
493+ const { setSelection} = renderSuggestionMention ( '@b@comp@charlie' , updateComment , { start : 2 , end : 2 } ) ;
494+
495+ await waitFor ( ( ) => expect ( mockMentionSuggestionsSpy ) . toHaveBeenCalled ( ) ) ;
496+ const { onSelect} = getLastMentionSuggestionsProps ( ) ;
497+
498+ act ( ( ) => onSelect ( 0 ) ) ;
499+
500+ expect ( updateComment ) . toHaveBeenCalledWith ( '@bob @charlie' , true ) ;
501+ expect ( setSelection ) . toHaveBeenCalledWith ( { start : 5 , end : 5 } ) ;
502+ } ) ;
503+
504+ it ( 'removes junk between the new mention and a trailing phone number mention' , async ( ) => {
505+ mockPersonalDetails = { } ;
506+ mockPersonalDetails [ 2 ] = {
507+ accountID : 2 ,
508+ login : 'adam@example.com' ,
509+ firstName : 'Adam' ,
510+ lastName : 'Tester' ,
511+ } ;
512+
513+ const updateComment = jest . fn ( ) ;
514+ // "@adam@exam@+14404589784" — cursor at index 5 (after "@adam").
515+ // Inserting the full mention should drop the "@exam" junk and keep the trailing phone number mention.
516+ const { setSelection} = renderSuggestionMention ( '@adam@exam@+14404589784' , updateComment , { start : 5 , end : 5 } ) ;
517+
518+ await waitFor ( ( ) => expect ( mockMentionSuggestionsSpy ) . toHaveBeenCalled ( ) ) ;
519+ const { onSelect} = getLastMentionSuggestionsProps ( ) ;
520+
521+ act ( ( ) => onSelect ( 0 ) ) ;
522+
523+ expect ( updateComment ) . toHaveBeenCalledWith ( '@adam@example.com @+14404589784' , true ) ;
524+ expect ( setSelection ) . toHaveBeenCalledWith ( { start : 18 , end : 18 } ) ;
525+ } ) ;
526+
527+ it ( 'removes junk between the new mention and a trailing room mention' , async ( ) => {
528+ mockPersonalDetails = { } ;
529+ mockPersonalDetails [ 2 ] = {
530+ accountID : 2 ,
531+ login : 'alice@example.com' ,
532+ firstName : 'Alice' ,
533+ lastName : 'Tester' ,
534+ } ;
535+
536+ const updateComment = jest . fn ( ) ;
537+ // "@alice@exam#admins" — cursor at index 6 (after "@alice").
538+ // Inserting the full mention should drop the "@exam" junk and keep the trailing #admins room mention.
539+ const { setSelection} = renderSuggestionMention ( '@alice@exam#admins' , updateComment , { start : 6 , end : 6 } ) ;
540+
541+ await waitFor ( ( ) => expect ( mockMentionSuggestionsSpy ) . toHaveBeenCalled ( ) ) ;
542+ const { onSelect} = getLastMentionSuggestionsProps ( ) ;
543+
544+ act ( ( ) => onSelect ( 0 ) ) ;
545+
546+ expect ( updateComment ) . toHaveBeenCalledWith ( '@alice@example.com #admins' , true ) ;
547+ expect ( setSelection ) . toHaveBeenCalledWith ( { start : 19 , end : 19 } ) ;
548+ } ) ;
327549} ) ;
0 commit comments