@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
14
14
limitations under the License.
15
15
*/
16
16
17
- import { render , RenderResult } from "@testing-library/react" ;
17
+ import { render , RenderResult , waitFor , screen } from "@testing-library/react" ;
18
18
// eslint-disable-next-line deprecate/import
19
19
import { mount , ReactWrapper } from "enzyme" ;
20
20
import { MessageEvent } from "matrix-events-sdk" ;
@@ -27,6 +27,8 @@ import {
27
27
PendingEventOrdering ,
28
28
Room ,
29
29
RoomEvent ,
30
+ RoomMember ,
31
+ RoomState ,
30
32
TimelineWindow ,
31
33
} from "matrix-js-sdk/src/matrix" ;
32
34
import { EventTimeline } from "matrix-js-sdk/src/models/event-timeline" ;
@@ -44,7 +46,8 @@ import MatrixClientContext from "../../../src/contexts/MatrixClientContext";
44
46
import { MatrixClientPeg } from "../../../src/MatrixClientPeg" ;
45
47
import SettingsStore from "../../../src/settings/SettingsStore" ;
46
48
import { isCallEvent } from "../../../src/components/structures/LegacyCallEventGrouper" ;
47
- import { flushPromises , mkRoom , stubClient } from "../../test-utils" ;
49
+ import { flushPromises , mkMembership , mkRoom , stubClient } from "../../test-utils" ;
50
+ import { mkThread } from "../../test-utils/threads" ;
48
51
49
52
const newReceipt = ( eventId : string , userId : string , readTs : number , fullyReadTs : number ) : MatrixEvent => {
50
53
const receiptContent = {
@@ -60,7 +63,7 @@ const newReceipt = (eventId: string, userId: string, readTs: number, fullyReadTs
60
63
const getProps = ( room : Room , events : MatrixEvent [ ] ) : TimelinePanel [ "props" ] => {
61
64
const timelineSet = { room : room as Room } as EventTimelineSet ;
62
65
const timeline = new EventTimeline ( timelineSet ) ;
63
- events . forEach ( ( event ) => timeline . addEvent ( event , true ) ) ;
66
+ events . forEach ( ( event ) => timeline . addEvent ( event , { toStartOfTimeline : true } ) ) ;
64
67
timelineSet . getLiveTimeline = ( ) => timeline ;
65
68
timelineSet . getTimelineForEvent = ( ) => timeline ;
66
69
timelineSet . getPendingEvents = ( ) => events ;
@@ -430,7 +433,7 @@ describe("TimelinePanel", () => {
430
433
// @ts -ignore
431
434
thread . fetchEditsWhereNeeded = ( ) => Promise . resolve ( ) ;
432
435
await thread . addEvent ( reply1 , true ) ;
433
- await allThreads . getLiveTimeline ( ) . addEvent ( thread . rootEvent ! , true ) ;
436
+ await allThreads . getLiveTimeline ( ) . addEvent ( thread . rootEvent ! , { toStartOfTimeline : true } ) ;
434
437
const replyToEvent = jest . spyOn ( thread , "replyToEvent" , "get" ) ;
435
438
436
439
const dom = render (
@@ -476,7 +479,7 @@ describe("TimelinePanel", () => {
476
479
// @ts -ignore
477
480
realThread . fetchEditsWhereNeeded = ( ) => Promise . resolve ( ) ;
478
481
await realThread . addEvent ( reply1 , true ) ;
479
- await allThreads . getLiveTimeline ( ) . addEvent ( realThread . rootEvent ! , true ) ;
482
+ await allThreads . getLiveTimeline ( ) . addEvent ( realThread . rootEvent ! , { toStartOfTimeline : true } ) ;
480
483
const replyToEvent = jest . spyOn ( realThread , "replyToEvent" , "get" ) ;
481
484
482
485
// @ts -ignore
@@ -513,4 +516,66 @@ describe("TimelinePanel", () => {
513
516
replyToEvent . mockClear ( ) ;
514
517
} ) ;
515
518
} ) ;
519
+
520
+ it ( "renders when the last message is an undecryptable thread root" , async ( ) => {
521
+ jest . spyOn ( SettingsStore , "getValue" ) . mockImplementation ( ( name ) => name === "feature_threadstable" ) ;
522
+
523
+ const client = MatrixClientPeg . get ( ) ;
524
+ client . isRoomEncrypted = ( ) => true ;
525
+ client . supportsExperimentalThreads = ( ) => true ;
526
+ client . decryptEventIfNeeded = ( ) => Promise . resolve ( ) ;
527
+ const authorId = client . getUserId ( ) ! ;
528
+ const room = new Room ( "roomId" , client , authorId , {
529
+ lazyLoadMembers : false ,
530
+ pendingEventOrdering : PendingEventOrdering . Detached ,
531
+ } ) ;
532
+
533
+ const events = mockEvents ( room ) ;
534
+ const timelineSet = room . getUnfilteredTimelineSet ( ) ;
535
+
536
+ const { rootEvent } = mkThread ( {
537
+ room,
538
+ client,
539
+ authorId,
540
+ participantUserIds : [ authorId ] ,
541
+ } ) ;
542
+
543
+ events . push ( rootEvent ) ;
544
+
545
+ events . forEach ( ( event ) => timelineSet . getLiveTimeline ( ) . addEvent ( event , { toStartOfTimeline : true } ) ) ;
546
+
547
+ const roomMembership = mkMembership ( {
548
+ mship : "join" ,
549
+ prevMship : "join" ,
550
+ user : authorId ,
551
+ room : room . roomId ,
552
+ event : true ,
553
+ skey : "123" ,
554
+ } ) ;
555
+
556
+ events . push ( roomMembership ) ;
557
+
558
+ const member = new RoomMember ( room . roomId , authorId ) ;
559
+ member . membership = "join" ;
560
+
561
+ const roomState = new RoomState ( room . roomId ) ;
562
+ jest . spyOn ( roomState , "getMember" ) . mockReturnValue ( member ) ;
563
+
564
+ jest . spyOn ( timelineSet . getLiveTimeline ( ) , "getState" ) . mockReturnValue ( roomState ) ;
565
+ timelineSet . addEventToTimeline ( roomMembership , timelineSet . getLiveTimeline ( ) , { toStartOfTimeline : false } ) ;
566
+
567
+ for ( const event of events ) {
568
+ jest . spyOn ( event , "isDecryptionFailure" ) . mockReturnValue ( true ) ;
569
+ jest . spyOn ( event , "shouldAttemptDecryption" ) . mockReturnValue ( false ) ;
570
+ }
571
+
572
+ const { container } = render (
573
+ < MatrixClientContext . Provider value = { client } >
574
+ < TimelinePanel timelineSet = { timelineSet } manageReadReceipts = { true } sendReadReceiptOnLoad = { true } />
575
+ </ MatrixClientContext . Provider > ,
576
+ ) ;
577
+
578
+ await waitFor ( ( ) => expect ( screen . queryByRole ( "progressbar" ) ) . toBeNull ( ) ) ;
579
+ await waitFor ( ( ) => expect ( container . querySelector ( ".mx_RoomView_MessageList" ) ) . not . toBeEmptyDOMElement ( ) ) ;
580
+ } ) ;
516
581
} ) ;
0 commit comments