@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
1414limitations under the License.
1515*/
1616
17- import { render , RenderResult } from "@testing-library/react" ;
17+ import { render , RenderResult , waitFor , screen } from "@testing-library/react" ;
1818// eslint-disable-next-line deprecate/import
1919import { mount , ReactWrapper } from "enzyme" ;
2020import { MessageEvent } from "matrix-events-sdk" ;
@@ -27,6 +27,8 @@ import {
2727 PendingEventOrdering ,
2828 Room ,
2929 RoomEvent ,
30+ RoomMember ,
31+ RoomState ,
3032 TimelineWindow ,
3133} from "matrix-js-sdk/src/matrix" ;
3234import { EventTimeline } from "matrix-js-sdk/src/models/event-timeline" ;
@@ -44,7 +46,8 @@ import MatrixClientContext from "../../../src/contexts/MatrixClientContext";
4446import { MatrixClientPeg } from "../../../src/MatrixClientPeg" ;
4547import SettingsStore from "../../../src/settings/SettingsStore" ;
4648import { 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" ;
4851
4952const newReceipt = ( eventId : string , userId : string , readTs : number , fullyReadTs : number ) : MatrixEvent => {
5053 const receiptContent = {
@@ -60,7 +63,7 @@ const newReceipt = (eventId: string, userId: string, readTs: number, fullyReadTs
6063const getProps = ( room : Room , events : MatrixEvent [ ] ) : TimelinePanel [ "props" ] => {
6164 const timelineSet = { room : room as Room } as EventTimelineSet ;
6265 const timeline = new EventTimeline ( timelineSet ) ;
63- events . forEach ( ( event ) => timeline . addEvent ( event , true ) ) ;
66+ events . forEach ( ( event ) => timeline . addEvent ( event , { toStartOfTimeline : true } ) ) ;
6467 timelineSet . getLiveTimeline = ( ) => timeline ;
6568 timelineSet . getTimelineForEvent = ( ) => timeline ;
6669 timelineSet . getPendingEvents = ( ) => events ;
@@ -430,7 +433,7 @@ describe("TimelinePanel", () => {
430433 // @ts -ignore
431434 thread . fetchEditsWhereNeeded = ( ) => Promise . resolve ( ) ;
432435 await thread . addEvent ( reply1 , true ) ;
433- await allThreads . getLiveTimeline ( ) . addEvent ( thread . rootEvent ! , true ) ;
436+ await allThreads . getLiveTimeline ( ) . addEvent ( thread . rootEvent ! , { toStartOfTimeline : true } ) ;
434437 const replyToEvent = jest . spyOn ( thread , "replyToEvent" , "get" ) ;
435438
436439 const dom = render (
@@ -476,7 +479,7 @@ describe("TimelinePanel", () => {
476479 // @ts -ignore
477480 realThread . fetchEditsWhereNeeded = ( ) => Promise . resolve ( ) ;
478481 await realThread . addEvent ( reply1 , true ) ;
479- await allThreads . getLiveTimeline ( ) . addEvent ( realThread . rootEvent ! , true ) ;
482+ await allThreads . getLiveTimeline ( ) . addEvent ( realThread . rootEvent ! , { toStartOfTimeline : true } ) ;
480483 const replyToEvent = jest . spyOn ( realThread , "replyToEvent" , "get" ) ;
481484
482485 // @ts -ignore
@@ -513,4 +516,66 @@ describe("TimelinePanel", () => {
513516 replyToEvent . mockClear ( ) ;
514517 } ) ;
515518 } ) ;
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+ } ) ;
516581} ) ;
0 commit comments