@@ -20,7 +20,10 @@ import {
2020 ChatMessage ,
2121 Create ,
2222 Delete ,
23+ Emoji as CustomEmoji ,
24+ EmojiReact ,
2325 Hashtag ,
26+ Image ,
2427 Like as RawLike ,
2528 Mention ,
2629 Note ,
@@ -36,6 +39,7 @@ import { assertEquals } from "@std/assert/equals";
3639import { assertInstanceOf } from "@std/assert/instance-of" ;
3740import { assertRejects } from "@std/assert/rejects" ;
3841import { BotImpl } from "./bot-impl.ts" ;
42+ import { type DeferredCustomEmoji , isEmoji } from "./emoji.ts" ;
3943import {
4044 createMessage ,
4145 getMessageClass ,
@@ -578,3 +582,141 @@ Deno.test("getMessageVisibility()", () => {
578582 ) ;
579583 assertEquals ( getMessageVisibility ( [ ] , [ ] , new Person ( { } ) ) , "unknown" ) ;
580584} ) ;
585+
586+ Deno . test ( "MessageImpl.react()" , async ( t ) => {
587+ const bot = new BotImpl < void > ( {
588+ kv : new MemoryKvStore ( ) ,
589+ username : "bot" ,
590+ } ) ;
591+ const ctx = createMockContext ( bot , "https://example.com" ) ;
592+ const session = new SessionImpl ( bot , ctx ) ;
593+ const originalPost = new Note ( {
594+ id : new URL (
595+ "https://example.com/ap/note/react-test-note" ,
596+ ) ,
597+ content : "<p>React to this!</p>" ,
598+ attribution : new Person ( {
599+ id : new URL ( "https://example.com/ap/actor/john" ) ,
600+ preferredUsername : "john" ,
601+ } ) ,
602+ to : PUBLIC_COLLECTION ,
603+ } ) ;
604+ const message = await createMessage < Note , void > (
605+ originalPost ,
606+ session ,
607+ { } ,
608+ ) ;
609+
610+ await t . step ( "react() with string emoji" , async ( ) => {
611+ ctx . sentActivities = [ ] ; // Clear previous activities
612+ const emoji = "👍" ;
613+ assert ( isEmoji ( emoji ) ) ;
614+ const reaction = await message . react ( emoji ) ;
615+ assertEquals ( ctx . sentActivities . length , 2 ) ;
616+ const { recipients, activity } = ctx . sentActivities [ 0 ] ;
617+ assertEquals ( recipients , "followers" ) ;
618+ assertInstanceOf ( activity , EmojiReact ) ;
619+ assertEquals ( activity . actorId , ctx . getActorUri ( bot . identifier ) ) ;
620+ assertEquals ( activity . objectId , message . id ) ;
621+ assertEquals ( activity . name , "👍" ) ;
622+ assertEquals ( await Array . fromAsync ( activity . getTags ( ) ) , [ ] ) ;
623+ const { recipients : recipients2 , activity : activity2 } =
624+ ctx . sentActivities [ 1 ] ;
625+ assertEquals ( recipients2 , [ message . actor ] ) ;
626+ assertInstanceOf ( activity2 , EmojiReact ) ;
627+ assertEquals ( activity2 , activity ) ;
628+ assertEquals ( reaction . actor , await session . getActor ( ) ) ;
629+ assertEquals ( reaction . raw , activity ) ;
630+ assertEquals ( reaction . id , activity . id ) ;
631+ assertEquals ( reaction . message , message ) ;
632+ assertEquals ( reaction . emoji , emoji ) ;
633+
634+ // Test unreact
635+ ctx . sentActivities = [ ] ;
636+ await reaction . unreact ( ) ;
637+ assertEquals ( ctx . sentActivities . length , 2 ) ;
638+ const { recipients : urRecipients , activity : urActivity } =
639+ ctx . sentActivities [ 0 ] ;
640+ assertEquals ( urRecipients , "followers" ) ;
641+ assertInstanceOf ( urActivity , Undo ) ;
642+ assertEquals ( urActivity . actorId , ctx . getActorUri ( bot . identifier ) ) ;
643+ const urObject = await urActivity . getObject ( ) ;
644+ assertInstanceOf ( urObject , EmojiReact ) ;
645+ assertEquals ( urObject . id , reaction . id ) ;
646+ const { recipients : urRecipients2 , activity : urActivity2 } =
647+ ctx . sentActivities [ 1 ] ;
648+ assertEquals ( urRecipients2 , [ message . actor ] ) ;
649+ assertInstanceOf ( urActivity2 , Undo ) ;
650+ assertEquals ( urActivity2 , urActivity ) ;
651+ } ) ;
652+
653+ await t . step ( "react() with CustomEmoji" , async ( ) => {
654+ ctx . sentActivities = [ ] ;
655+ const customEmoji = new CustomEmoji ( {
656+ id : new URL ( "https://example.com/emojis/custom" ) ,
657+ name : ":custom:" ,
658+ icon : new Image ( {
659+ url : new URL ( "https://example.com/emojis/custom.png" ) ,
660+ } ) ,
661+ } ) ;
662+ const reaction = await message . react ( customEmoji ) ;
663+ assertEquals ( ctx . sentActivities . length , 2 ) ;
664+ const { activity } = ctx . sentActivities [ 0 ] ;
665+ assertInstanceOf ( activity , EmojiReact ) ;
666+ assertEquals ( activity . name , ":custom:" ) ;
667+ const tags = await Array . fromAsync ( activity . getTags ( ) ) ;
668+ assertEquals ( tags . length , 1 ) ;
669+ assertEquals ( tags [ 0 ] , customEmoji ) ;
670+ assertEquals ( reaction . emoji , customEmoji ) ;
671+
672+ // Test unreact
673+ ctx . sentActivities = [ ] ;
674+ await reaction . unreact ( ) ;
675+ assertEquals ( ctx . sentActivities . length , 2 ) ;
676+ const { activity : urActivity } = ctx . sentActivities [ 0 ] ;
677+ assertInstanceOf ( urActivity , Undo ) ;
678+ const urObject = await urActivity . getObject ( ) ;
679+ assertInstanceOf ( urObject , EmojiReact ) ;
680+ assertEquals ( urObject . id , reaction . id ) ;
681+ const urTags = await Array . fromAsync ( urActivity . getTags ( ) ) ;
682+ assertEquals ( urTags . length , 1 ) ;
683+ assertEquals ( urTags [ 0 ] , customEmoji ) ;
684+ } ) ;
685+
686+ await t . step ( "react() with DeferredCustomEmoji" , async ( ) => {
687+ ctx . sentActivities = [ ] ;
688+ const deferredEmoji : DeferredCustomEmoji < void > = ( sessionParam ) => {
689+ assertEquals ( sessionParam , session ) ; // Ensure correct session is passed
690+ return new CustomEmoji ( {
691+ id : new URL ( "https://example.com/emojis/deferred" ) ,
692+ name : ":deferred:" ,
693+ icon : new Image ( {
694+ url : new URL ( "https://example.com/emojis/deferred.png" ) ,
695+ } ) ,
696+ } ) ;
697+ } ;
698+ const reaction = await message . react ( deferredEmoji ) ;
699+ assertEquals ( ctx . sentActivities . length , 2 ) ;
700+ const { activity } = ctx . sentActivities [ 0 ] ;
701+ assertInstanceOf ( activity , EmojiReact ) ;
702+ assertEquals ( activity . name , ":deferred:" ) ;
703+ const tags = await Array . fromAsync ( activity . getTags ( ) ) ;
704+ assertEquals ( tags . length , 1 ) ;
705+ assertInstanceOf ( tags [ 0 ] , CustomEmoji ) ;
706+ assertEquals ( tags [ 0 ] . id ?. href , "https://example.com/emojis/deferred" ) ;
707+ assertEquals ( reaction . emoji , tags [ 0 ] ) ;
708+
709+ // Test unreact
710+ ctx . sentActivities = [ ] ;
711+ await reaction . unreact ( ) ;
712+ assertEquals ( ctx . sentActivities . length , 2 ) ;
713+ const { activity : urActivity } = ctx . sentActivities [ 0 ] ;
714+ assertInstanceOf ( urActivity , Undo ) ;
715+ const urObject = await urActivity . getObject ( ) ;
716+ assertInstanceOf ( urObject , EmojiReact ) ;
717+ assertEquals ( urObject . id , reaction . id ) ;
718+ const urTags = await Array . fromAsync ( urActivity . getTags ( ) ) ;
719+ assertEquals ( urTags . length , 1 ) ;
720+ assertEquals ( urTags [ 0 ] , tags [ 0 ] ) ; // Should be the resolved CustomEmoji
721+ } ) ;
722+ } ) ;
0 commit comments