@@ -1569,3 +1569,157 @@ describe('send reaction flow', () => {
1569
1569
} ) ;
1570
1570
} ) ;
1571
1571
} ) ;
1572
+
1573
+ describe ( 'delete reaction flow' , ( ) => {
1574
+ const messageId = 'msg-123' ;
1575
+ const reactionType = 'love' ;
1576
+ const user_id = 'user-abc' ;
1577
+
1578
+ let client ;
1579
+ let channel ;
1580
+ let loggerSpy ;
1581
+ let queueTaskSpy ;
1582
+ let deleteReactionSpy ;
1583
+ let deleteSpy ;
1584
+
1585
+ beforeEach ( async ( ) => {
1586
+ client = await getClientWithUser ( { id : user_id } ) ;
1587
+ const offlineDb = new MockOfflineDB ( { client } ) ;
1588
+
1589
+ client . setOfflineDBApi ( offlineDb ) ;
1590
+ await client . offlineDb . init ( client . userID ) ;
1591
+
1592
+ channel = client . channel ( 'messaging' , 'test' ) ;
1593
+ // trick the channel into being initialized
1594
+ channel . initialized = true ;
1595
+
1596
+ // Add a fake message to state for reaction deletion optimistic update in the db
1597
+ channel . state . messages . push ( { id : messageId } ) ;
1598
+
1599
+ loggerSpy = vi . spyOn ( client , 'logger' ) . mockImplementation ( vi . fn ( ) ) ;
1600
+ queueTaskSpy = vi . spyOn ( client . offlineDb , 'queueTask' ) . mockResolvedValue ( { } ) ;
1601
+ deleteReactionSpy = vi . spyOn ( client . offlineDb , 'deleteReaction' ) . mockResolvedValue ( ) ;
1602
+ deleteSpy = vi . spyOn ( client , 'delete' ) . mockResolvedValue ( { } ) ;
1603
+ } ) ;
1604
+
1605
+ afterEach ( ( ) => {
1606
+ vi . resetAllMocks ( ) ;
1607
+ } ) ;
1608
+
1609
+ describe ( 'deleteReaction' , ( ) => {
1610
+ beforeEach ( ( ) => {
1611
+ vi . spyOn ( channel , '_deleteReaction' ) . mockResolvedValue ( { } ) ;
1612
+ } ) ;
1613
+
1614
+ afterEach ( ( ) => {
1615
+ vi . resetAllMocks ( ) ;
1616
+ } ) ;
1617
+
1618
+ it ( 'throws if messageID or reactionType is missing' , async ( ) => {
1619
+ await expect ( channel . deleteReaction ( '' , reactionType ) ) . rejects . toThrow (
1620
+ 'Deleting a reaction requires specifying both the message and reaction type' ,
1621
+ ) ;
1622
+ await expect ( channel . deleteReaction ( messageId , '' ) ) . rejects . toThrow (
1623
+ 'Deleting a reaction requires specifying both the message and reaction type' ,
1624
+ ) ;
1625
+ } ) ;
1626
+
1627
+ it ( 'calls offlineDb.deleteReaction and queues task if offlineDb exists' , async ( ) => {
1628
+ await channel . deleteReaction ( messageId , reactionType ) ;
1629
+
1630
+ expect ( deleteReactionSpy ) . toHaveBeenCalledTimes ( 1 ) ;
1631
+ expect ( queueTaskSpy ) . toHaveBeenCalledTimes ( 1 ) ;
1632
+
1633
+ const expectedReaction = {
1634
+ created_at : '' ,
1635
+ updated_at : '' ,
1636
+ message_id : messageId ,
1637
+ type : reactionType ,
1638
+ user_id : user_id ,
1639
+ } ;
1640
+
1641
+ expect ( deleteReactionSpy ) . toHaveBeenCalledWith ( {
1642
+ message : { id : messageId } ,
1643
+ reaction : expectedReaction ,
1644
+ } ) ;
1645
+
1646
+ expect ( queueTaskSpy ) . toHaveBeenCalledWith ( {
1647
+ task : {
1648
+ channelId : 'test' ,
1649
+ channelType : 'messaging' ,
1650
+ messageId,
1651
+ payload : [ messageId , reactionType ] ,
1652
+ type : 'delete-reaction' ,
1653
+ } ,
1654
+ } ) ;
1655
+
1656
+ expect ( channel . _deleteReaction ) . not . toHaveBeenCalled ( ) ;
1657
+ } ) ;
1658
+
1659
+ it ( 'falls back to _deleteReaction if offlineDb throws' , async ( ) => {
1660
+ deleteReactionSpy . mockRejectedValue ( new Error ( 'Offline failure' ) ) ;
1661
+
1662
+ await channel . deleteReaction ( messageId , reactionType ) ;
1663
+
1664
+ expect ( loggerSpy ) . toHaveBeenCalledTimes ( 1 ) ;
1665
+ expect ( channel . _deleteReaction ) . toHaveBeenCalledTimes ( 1 ) ;
1666
+ expect ( channel . _deleteReaction ) . toHaveBeenCalledWith (
1667
+ messageId ,
1668
+ reactionType ,
1669
+ undefined ,
1670
+ ) ;
1671
+ } ) ;
1672
+
1673
+ it ( 'falls back to _deleteReaction if offlineDb is undefined' , async ( ) => {
1674
+ client . offlineDb = undefined ;
1675
+
1676
+ await channel . deleteReaction ( messageId , reactionType ) ;
1677
+
1678
+ expect ( channel . _deleteReaction ) . toHaveBeenCalledTimes ( 1 ) ;
1679
+ expect ( channel . _deleteReaction ) . toHaveBeenCalledWith (
1680
+ messageId ,
1681
+ reactionType ,
1682
+ undefined ,
1683
+ ) ;
1684
+ } ) ;
1685
+ } ) ;
1686
+
1687
+ describe ( '_deleteReaction' , ( ) => {
1688
+ it ( 'throws if messageID or reactionType is missing' , async ( ) => {
1689
+ await expect ( channel . _deleteReaction ( undefined , reactionType ) ) . rejects . toThrow (
1690
+ 'Deleting a reaction requires specifying both the message and reaction type' ,
1691
+ ) ;
1692
+ await expect ( channel . _deleteReaction ( messageId , undefined ) ) . rejects . toThrow (
1693
+ 'Deleting a reaction requires specifying both the message and reaction type' ,
1694
+ ) ;
1695
+ } ) ;
1696
+
1697
+ it ( 'calls delete with user_id when provided' , async ( ) => {
1698
+ await channel . _deleteReaction ( messageId , reactionType , user_id ) ;
1699
+
1700
+ expect ( deleteSpy ) . toHaveBeenCalledTimes ( 1 ) ;
1701
+ expect ( deleteSpy ) . toHaveBeenCalledWith (
1702
+ `${ client . baseURL } /messages/${ encodeURIComponent ( messageId ) } /reaction/${ encodeURIComponent ( reactionType ) } ` ,
1703
+ { user_id } ,
1704
+ ) ;
1705
+ } ) ;
1706
+
1707
+ it ( 'calls delete with empty body if user_id is not provided' , async ( ) => {
1708
+ await channel . _deleteReaction ( messageId , reactionType ) ;
1709
+
1710
+ expect ( deleteSpy ) . toHaveBeenCalledTimes ( 1 ) ;
1711
+ expect ( deleteSpy ) . toHaveBeenCalledWith (
1712
+ `${ client . baseURL } /messages/${ encodeURIComponent ( messageId ) } /reaction/${ encodeURIComponent ( reactionType ) } ` ,
1713
+ { } ,
1714
+ ) ;
1715
+ } ) ;
1716
+
1717
+ it ( 'returns the response from delete' , async ( ) => {
1718
+ deleteSpy . mockResolvedValue ( { success : true } ) ;
1719
+
1720
+ const result = await channel . _deleteReaction ( messageId , reactionType ) ;
1721
+
1722
+ expect ( result ) . toEqual ( { success : true } ) ;
1723
+ } ) ;
1724
+ } ) ;
1725
+ } ) ;
0 commit comments