11/*
2- Copyright 2022 The Matrix.org Foundation C.I.C.
2+ Copyright 2022 - 2023 The Matrix.org Foundation C.I.C.
33
44Licensed under the Apache License, Version 2.0 (the "License");
55you may not use this file except in compliance with the License.
@@ -19,8 +19,12 @@ import { Room } from "../../../src/models/room";
1919import { Thread , THREAD_RELATION_TYPE , ThreadEvent } from "../../../src/models/thread" ;
2020import { mkThread } from "../../test-utils/thread" ;
2121import { TestClient } from "../../TestClient" ;
22- import { emitPromise , mkMessage } from "../../test-utils/test-utils" ;
23- import { EventStatus } from "../../../src" ;
22+ import { emitPromise , mkMessage , mock } from "../../test-utils/test-utils" ;
23+ import { EventStatus , MatrixEvent } from "../../../src" ;
24+ import { ReceiptType } from "../../../src/@types/read_receipts" ;
25+ import { getMockClientWithEventEmitter , mockClientMethodsUser } from "../../test-utils/client" ;
26+ import { ReEmitter } from "../../../src/ReEmitter" ;
27+ import { Feature , ServerSupport } from "../../../src/feature" ;
2428
2529describe ( "Thread" , ( ) => {
2630 describe ( "constructor" , ( ) => {
@@ -71,17 +75,54 @@ describe("Thread", () => {
7175 } ) ;
7276
7377 describe ( "hasUserReadEvent" , ( ) => {
74- const myUserId = "@bob:example.org" ;
78+ let myUserId : string ;
7579 let client : MatrixClient ;
7680 let room : Room ;
7781
7882 beforeEach ( ( ) => {
79- const testClient = new TestClient ( myUserId , "DEVICE" , "ACCESS_TOKEN" , undefined , {
80- timelineSupport : false ,
83+ client = getMockClientWithEventEmitter ( {
84+ ...mockClientMethodsUser ( ) ,
85+ getRoom : jest . fn ( ) . mockImplementation ( ( ) => room ) ,
86+ decryptEventIfNeeded : jest . fn ( ) . mockResolvedValue ( void 0 ) ,
87+ supportsExperimentalThreads : jest . fn ( ) . mockReturnValue ( true ) ,
88+ } ) ;
89+ client . reEmitter = mock ( ReEmitter , "ReEmitter" ) ;
90+ client . canSupport = new Map ( ) ;
91+ Object . keys ( Feature ) . forEach ( ( feature ) => {
92+ client . canSupport . set ( feature as Feature , ServerSupport . Stable ) ;
8193 } ) ;
82- client = testClient . client ;
94+
95+ myUserId = client . getUserId ( ) ! ;
96+
8397 room = new Room ( "123" , client , myUserId ) ;
8498
99+ const receipt = new MatrixEvent ( {
100+ type : "m.receipt" ,
101+ room_id : "!foo:bar" ,
102+ content : {
103+ // first threaded receipt
104+ "$event0:localhost" : {
105+ [ ReceiptType . Read ] : {
106+ [ client . getUserId ( ) ! ] : { ts : 100 , thread_id : "$threadId:localhost" } ,
107+ } ,
108+ } ,
109+ // last unthreaded receipt
110+ "$event1:localhost" : {
111+ [ ReceiptType . Read ] : {
112+ [ client . getUserId ( ) ! ] : { ts : 200 } ,
113+ [ "@alice:example.org" ] : { ts : 200 } ,
114+ } ,
115+ } ,
116+ // last threaded receipt
117+ "$event2:localhost" : {
118+ [ ReceiptType . Read ] : {
119+ [ client . getUserId ( ) ! ] : { ts : 300 , thread_id : "$threadId" } ,
120+ } ,
121+ } ,
122+ } ,
123+ } ) ;
124+ room . addReceipt ( receipt ) ;
125+
85126 jest . spyOn ( client , "getRoom" ) . mockReturnValue ( room ) ;
86127 } ) ;
87128
@@ -98,19 +139,148 @@ describe("Thread", () => {
98139 length : 2 ,
99140 } ) ;
100141
142+ // The event is automatically considered read as the current user is the sender
101143 expect ( thread . hasUserReadEvent ( myUserId , events . at ( - 1 ) ! . getId ( ) ?? "" ) ) . toBeTruthy ( ) ;
102144 } ) ;
103145
104146 it ( "considers other events with no RR as unread" , ( ) => {
147+ const { thread, events } = mkThread ( {
148+ room,
149+ client,
150+ authorId : myUserId ,
151+ participantUserIds : [ myUserId ] ,
152+ length : 25 ,
153+ ts : 190 ,
154+ } ) ;
155+
156+ // Before alice's last unthreaded receipt
157+ expect ( thread . hasUserReadEvent ( "@alice:example.org" , events . at ( 1 ) ! . getId ( ) ?? "" ) ) . toBeTruthy ( ) ;
158+
159+ // After alice's last unthreaded receipt
160+ expect ( thread . hasUserReadEvent ( "@alice:example.org" , events . at ( - 1 ) ! . getId ( ) ?? "" ) ) . toBeFalsy ( ) ;
161+ } ) ;
162+
163+ it ( "considers event as read if there's a more recent unthreaded receipt" , ( ) => {
105164 const { thread, events } = mkThread ( {
106165 room,
107166 client,
108167 authorId : myUserId ,
109168 participantUserIds : [ "@alice:example.org" ] ,
110169 length : 2 ,
170+ ts : 150 , // before the latest unthreaded receipt
111171 } ) ;
172+ expect ( thread . hasUserReadEvent ( client . getUserId ( ) ! , events . at ( - 1 ) ! . getId ( ) ?? "" ) ) . toBe ( true ) ;
173+ } ) ;
112174
113- expect ( thread . hasUserReadEvent ( "@alice:example.org" , events . at ( - 1 ) ! . getId ( ) ?? "" ) ) . toBeFalsy ( ) ;
175+ it ( "considers event as unread if there's no more recent unthreaded receipt" , ( ) => {
176+ const { thread, events } = mkThread ( {
177+ room,
178+ client,
179+ authorId : myUserId ,
180+ participantUserIds : [ "@alice:example.org" ] ,
181+ length : 2 ,
182+ ts : 1000 ,
183+ } ) ;
184+ expect ( thread . hasUserReadEvent ( client . getUserId ( ) ! , events . at ( - 1 ) ! . getId ( ) ?? "" ) ) . toBe ( false ) ;
185+ } ) ;
186+ } ) ;
187+
188+ describe ( "getEventReadUpTo" , ( ) => {
189+ let myUserId : string ;
190+ let client : MatrixClient ;
191+ let room : Room ;
192+
193+ beforeEach ( ( ) => {
194+ client = getMockClientWithEventEmitter ( {
195+ ...mockClientMethodsUser ( ) ,
196+ getRoom : jest . fn ( ) . mockImplementation ( ( ) => room ) ,
197+ decryptEventIfNeeded : jest . fn ( ) . mockResolvedValue ( void 0 ) ,
198+ supportsExperimentalThreads : jest . fn ( ) . mockReturnValue ( true ) ,
199+ } ) ;
200+ client . reEmitter = mock ( ReEmitter , "ReEmitter" ) ;
201+ client . canSupport = new Map ( ) ;
202+ Object . keys ( Feature ) . forEach ( ( feature ) => {
203+ client . canSupport . set ( feature as Feature , ServerSupport . Stable ) ;
204+ } ) ;
205+
206+ myUserId = client . getUserId ( ) ! ;
207+
208+ room = new Room ( "123" , client , myUserId ) ;
209+
210+ jest . spyOn ( client , "getRoom" ) . mockReturnValue ( room ) ;
211+ } ) ;
212+
213+ afterAll ( ( ) => {
214+ jest . resetAllMocks ( ) ;
215+ } ) ;
216+
217+ it ( "uses unthreaded receipt to figure out read up to" , ( ) => {
218+ const receipt = new MatrixEvent ( {
219+ type : "m.receipt" ,
220+ room_id : "!foo:bar" ,
221+ content : {
222+ // last unthreaded receipt
223+ "$event1:localhost" : {
224+ [ ReceiptType . Read ] : {
225+ [ "@alice:example.org" ] : { ts : 200 } ,
226+ } ,
227+ } ,
228+ } ,
229+ } ) ;
230+ room . addReceipt ( receipt ) ;
231+
232+ const { thread, events } = mkThread ( {
233+ room,
234+ client,
235+ authorId : myUserId ,
236+ participantUserIds : [ myUserId ] ,
237+ length : 25 ,
238+ ts : 190 ,
239+ } ) ;
240+
241+ // The 10th event has been read, as alice's last unthreaded receipt is at ts 200
242+ // and `mkThread` increment every thread response by 1ms.
243+ expect ( thread . getEventReadUpTo ( "@alice:example.org" ) ) . toBe ( events . at ( 9 ) ! . getId ( ) ) ;
244+ } ) ;
245+
246+ it ( "considers thread created before the first threaded receipt to be read" , ( ) => {
247+ const receipt = new MatrixEvent ( {
248+ type : "m.receipt" ,
249+ room_id : "!foo:bar" ,
250+ content : {
251+ // last unthreaded receipt
252+ "$event1:localhost" : {
253+ [ ReceiptType . Read ] : {
254+ [ myUserId ] : { ts : 200 , thread_id : "$threadId" } ,
255+ } ,
256+ } ,
257+ } ,
258+ } ) ;
259+ room . addReceipt ( receipt ) ;
260+
261+ const { thread, events } = mkThread ( {
262+ room,
263+ client,
264+ authorId : "@alice:example.org" ,
265+ participantUserIds : [ "@alice:example.org" ] ,
266+ length : 2 ,
267+ ts : 10 ,
268+ } ) ;
269+
270+ // This is marked as read as it is before alice's first threaded receipt...
271+ expect ( thread . getEventReadUpTo ( myUserId ) ) . toBe ( events . at ( - 1 ) ! . getId ( ) ) ;
272+
273+ const { thread : thread2 } = mkThread ( {
274+ room,
275+ client,
276+ authorId : "@alice:example.org" ,
277+ participantUserIds : [ "@alice:example.org" ] ,
278+ length : 2 ,
279+ ts : 1000 ,
280+ } ) ;
281+
282+ // Nothing has been read, this thread is after the first threaded receipt...
283+ expect ( thread2 . getEventReadUpTo ( myUserId ) ) . toBe ( null ) ;
114284 } ) ;
115285 } ) ;
116286} ) ;
0 commit comments