@@ -14,18 +14,23 @@ See the License for the specific language governing permissions and
14
14
limitations under the License.
15
15
*/
16
16
17
- import React from "react" ;
18
- import { render , screen , fireEvent } from "@testing-library/react" ;
19
- import { mocked } from "jest-mock" ;
17
+ import { fireEvent , render , screen , waitFor } from "@testing-library/react" ;
20
18
import "focus-visible" ; // to fix context menus
19
+ import { mocked } from "jest-mock" ;
20
+ import React from "react" ;
21
+ import { MatrixClient , MatrixEvent , PendingEventOrdering , Room } from "matrix-js-sdk/src/matrix" ;
22
+ import { FeatureSupport , Thread } from "matrix-js-sdk/src/models/thread" ;
21
23
22
24
import ThreadPanel , { ThreadFilterType , ThreadPanelHeader } from "../../../src/components/structures/ThreadPanel" ;
25
+ import MatrixClientContext from "../../../src/contexts/MatrixClientContext" ;
26
+ import RoomContext from "../../../src/contexts/RoomContext" ;
23
27
import { _t } from "../../../src/languageHandler" ;
24
- import ResizeNotifier from "../../../src/utils/ResizeNotifier" ;
25
- import { RoomPermalinkCreator } from "../../../src/utils/permalinks/Permalinks" ;
26
- import { createTestClient , mkStubRoom } from "../../test-utils" ;
28
+ import { MatrixClientPeg } from "../../../src/MatrixClientPeg" ;
27
29
import { shouldShowFeedback } from "../../../src/utils/Feedback" ;
28
- import MatrixClientContext from "../../../src/contexts/MatrixClientContext" ;
30
+ import { RoomPermalinkCreator } from "../../../src/utils/permalinks/Permalinks" ;
31
+ import ResizeNotifier from "../../../src/utils/ResizeNotifier" ;
32
+ import { createTestClient , getRoomContext , mkStubRoom , mockPlatformPeg , stubClient } from "../../test-utils" ;
33
+ import { mkThread } from "../../test-utils/threads" ;
29
34
30
35
jest . mock ( "../../../src/utils/Feedback" ) ;
31
36
@@ -122,4 +127,184 @@ describe("ThreadPanel", () => {
122
127
expect ( foundButton ) . toMatchSnapshot ( ) ;
123
128
} ) ;
124
129
} ) ;
130
+
131
+ describe ( "Filtering" , ( ) => {
132
+ const ROOM_ID = "!roomId:example.org" ;
133
+ const SENDER = "@alice:example.org" ;
134
+
135
+ let mockClient : MatrixClient ;
136
+ let room : Room ;
137
+
138
+ const TestThreadPanel = ( ) => (
139
+ < MatrixClientContext . Provider value = { mockClient } >
140
+ < RoomContext . Provider
141
+ value = { getRoomContext ( room , {
142
+ canSendMessages : true ,
143
+ } ) }
144
+ >
145
+ < ThreadPanel
146
+ roomId = { ROOM_ID }
147
+ onClose = { jest . fn ( ) }
148
+ resizeNotifier = { new ResizeNotifier ( ) }
149
+ permalinkCreator = { new RoomPermalinkCreator ( room ) }
150
+ />
151
+ </ RoomContext . Provider >
152
+ </ MatrixClientContext . Provider >
153
+ ) ;
154
+
155
+ beforeEach ( async ( ) => {
156
+ jest . clearAllMocks ( ) ;
157
+
158
+ stubClient ( ) ;
159
+ mockPlatformPeg ( ) ;
160
+ mockClient = mocked ( MatrixClientPeg . get ( ) ) ;
161
+ Thread . setServerSideSupport ( FeatureSupport . Stable ) ;
162
+ Thread . setServerSideListSupport ( FeatureSupport . Stable ) ;
163
+ Thread . setServerSideFwdPaginationSupport ( FeatureSupport . Stable ) ;
164
+ jest . spyOn ( mockClient , "supportsExperimentalThreads" ) . mockReturnValue ( true ) ;
165
+
166
+ room = new Room ( ROOM_ID , mockClient , mockClient . getUserId ( ) ?? "" , {
167
+ pendingEventOrdering : PendingEventOrdering . Detached ,
168
+ } ) ;
169
+ jest . spyOn ( room , "fetchRoomThreads" ) . mockReturnValue ( Promise . resolve ( ) ) ;
170
+ jest . spyOn ( mockClient , "getRoom" ) . mockReturnValue ( room ) ;
171
+ await room . createThreadsTimelineSets ( ) ;
172
+ const [ allThreads , myThreads ] = room . threadsTimelineSets ;
173
+ jest . spyOn ( room , "createThreadsTimelineSets" )
174
+ . mockReturnValue ( Promise . resolve ( [ allThreads , myThreads ] ) ) ;
175
+ } ) ;
176
+
177
+ function toggleThreadFilter ( container : HTMLElement , newFilter : ThreadFilterType ) {
178
+ fireEvent . click ( container . querySelector ( ".mx_ThreadPanel_dropdown" ) ) ;
179
+ const found = screen . queryAllByRole ( "menuitemradio" ) ;
180
+ expect ( found ) . toHaveLength ( 2 ) ;
181
+
182
+ const allThreadsContent = `${ _t ( "All threads" ) } ${ _t ( "Shows all threads from current room" ) } ` ;
183
+ const myThreadsContent = `${ _t ( "My threads" ) } ${ _t ( "Shows all threads you've participated in" ) } ` ;
184
+
185
+ const allThreadsOption = found . find ( it => it . textContent === allThreadsContent ) ;
186
+ const myThreadsOption = found . find ( it => it . textContent === myThreadsContent ) ;
187
+ expect ( allThreadsOption ) . toBeTruthy ( ) ;
188
+ expect ( myThreadsOption ) . toBeTruthy ( ) ;
189
+
190
+ const toSelect = newFilter === ThreadFilterType . My ? myThreadsOption : allThreadsOption ;
191
+ fireEvent . click ( toSelect ) ;
192
+ }
193
+
194
+ type EventData = { sender : string , content : string } ;
195
+
196
+ function findEvents ( container : HTMLElement ) : EventData [ ] {
197
+ return Array . from ( container . querySelectorAll ( ".mx_EventTile" ) ) . map ( el => {
198
+ const sender = el . querySelector ( ".mx_DisambiguatedProfile_displayName" ) . textContent ;
199
+ const content = el . querySelector ( ".mx_EventTile_body" ) . textContent ;
200
+ return { sender, content } ;
201
+ } ) ;
202
+ }
203
+
204
+ function toEventData ( event : MatrixEvent ) : EventData {
205
+ return { sender : event . event . sender , content : event . event . content . body } ;
206
+ }
207
+
208
+ it ( "correctly filters Thread List with multiple threads" , async ( ) => {
209
+ const otherThread = mkThread ( {
210
+ room,
211
+ client : mockClient ,
212
+ authorId : SENDER ,
213
+ participantUserIds : [ mockClient . getUserId ( ) ] ,
214
+ } ) ;
215
+
216
+ const mixedThread = mkThread ( {
217
+ room,
218
+ client : mockClient ,
219
+ authorId : SENDER ,
220
+ participantUserIds : [ SENDER , mockClient . getUserId ( ) ] ,
221
+ } ) ;
222
+
223
+ const ownThread = mkThread ( {
224
+ room,
225
+ client : mockClient ,
226
+ authorId : mockClient . getUserId ( ) ,
227
+ participantUserIds : [ mockClient . getUserId ( ) ] ,
228
+ } ) ;
229
+
230
+ const threadRoots = [ otherThread . rootEvent , mixedThread . rootEvent , ownThread . rootEvent ] ;
231
+ jest . spyOn ( mockClient , "fetchRoomEvent" ) . mockImplementation (
232
+ ( _ , eventId ) => Promise . resolve ( threadRoots . find ( it => it . getId ( ) === eventId ) . event ) ,
233
+ ) ;
234
+ const [ allThreads , myThreads ] = room . threadsTimelineSets ;
235
+ allThreads . addLiveEvent ( otherThread . rootEvent ) ;
236
+ allThreads . addLiveEvent ( mixedThread . rootEvent ) ;
237
+ allThreads . addLiveEvent ( ownThread . rootEvent ) ;
238
+ myThreads . addLiveEvent ( mixedThread . rootEvent ) ;
239
+ myThreads . addLiveEvent ( ownThread . rootEvent ) ;
240
+
241
+ let events : EventData [ ] = [ ] ;
242
+ const renderResult = render ( < TestThreadPanel /> ) ;
243
+ await waitFor ( ( ) => expect ( renderResult . container . querySelector ( ".mx_AutoHideScrollbar" ) ) . toBeFalsy ( ) ) ;
244
+ await waitFor ( ( ) => {
245
+ events = findEvents ( renderResult . container ) ;
246
+ expect ( findEvents ( renderResult . container ) ) . toHaveLength ( 3 ) ;
247
+ } ) ;
248
+ expect ( events [ 0 ] ) . toEqual ( toEventData ( otherThread . rootEvent ) ) ;
249
+ expect ( events [ 1 ] ) . toEqual ( toEventData ( mixedThread . rootEvent ) ) ;
250
+ expect ( events [ 2 ] ) . toEqual ( toEventData ( ownThread . rootEvent ) ) ;
251
+ await waitFor ( ( ) => expect ( renderResult . container . querySelector ( ".mx_ThreadPanel_dropdown" ) ) . toBeTruthy ( ) ) ;
252
+ toggleThreadFilter ( renderResult . container , ThreadFilterType . My ) ;
253
+ await waitFor ( ( ) => expect ( renderResult . container . querySelector ( ".mx_AutoHideScrollbar" ) ) . toBeFalsy ( ) ) ;
254
+ await waitFor ( ( ) => {
255
+ events = findEvents ( renderResult . container ) ;
256
+ expect ( findEvents ( renderResult . container ) ) . toHaveLength ( 2 ) ;
257
+ } ) ;
258
+ expect ( events [ 0 ] ) . toEqual ( toEventData ( mixedThread . rootEvent ) ) ;
259
+ expect ( events [ 1 ] ) . toEqual ( toEventData ( ownThread . rootEvent ) ) ;
260
+ toggleThreadFilter ( renderResult . container , ThreadFilterType . All ) ;
261
+ await waitFor ( ( ) => expect ( renderResult . container . querySelector ( ".mx_AutoHideScrollbar" ) ) . toBeFalsy ( ) ) ;
262
+ await waitFor ( ( ) => {
263
+ events = findEvents ( renderResult . container ) ;
264
+ expect ( findEvents ( renderResult . container ) ) . toHaveLength ( 3 ) ;
265
+ } ) ;
266
+ expect ( events [ 0 ] ) . toEqual ( toEventData ( otherThread . rootEvent ) ) ;
267
+ expect ( events [ 1 ] ) . toEqual ( toEventData ( mixedThread . rootEvent ) ) ;
268
+ expect ( events [ 2 ] ) . toEqual ( toEventData ( ownThread . rootEvent ) ) ;
269
+ } ) ;
270
+
271
+ it ( "correctly filters Thread List with a single, unparticipated thread" , async ( ) => {
272
+ const otherThread = mkThread ( {
273
+ room,
274
+ client : mockClient ,
275
+ authorId : SENDER ,
276
+ participantUserIds : [ mockClient . getUserId ( ) ] ,
277
+ } ) ;
278
+
279
+ const threadRoots = [ otherThread . rootEvent ] ;
280
+ jest . spyOn ( mockClient , "fetchRoomEvent" ) . mockImplementation (
281
+ ( _ , eventId ) => Promise . resolve ( threadRoots . find ( it => it . getId ( ) === eventId ) . event ) ,
282
+ ) ;
283
+ const [ allThreads ] = room . threadsTimelineSets ;
284
+ allThreads . addLiveEvent ( otherThread . rootEvent ) ;
285
+
286
+ let events : EventData [ ] = [ ] ;
287
+ const renderResult = render ( < TestThreadPanel /> ) ;
288
+ await waitFor ( ( ) => expect ( renderResult . container . querySelector ( ".mx_AutoHideScrollbar" ) ) . toBeFalsy ( ) ) ;
289
+ await waitFor ( ( ) => {
290
+ events = findEvents ( renderResult . container ) ;
291
+ expect ( findEvents ( renderResult . container ) ) . toHaveLength ( 1 ) ;
292
+ } ) ;
293
+ expect ( events [ 0 ] ) . toEqual ( toEventData ( otherThread . rootEvent ) ) ;
294
+ await waitFor ( ( ) => expect ( renderResult . container . querySelector ( ".mx_ThreadPanel_dropdown" ) ) . toBeTruthy ( ) ) ;
295
+ toggleThreadFilter ( renderResult . container , ThreadFilterType . My ) ;
296
+ await waitFor ( ( ) => expect ( renderResult . container . querySelector ( ".mx_AutoHideScrollbar" ) ) . toBeFalsy ( ) ) ;
297
+ await waitFor ( ( ) => {
298
+ events = findEvents ( renderResult . container ) ;
299
+ expect ( findEvents ( renderResult . container ) ) . toHaveLength ( 0 ) ;
300
+ } ) ;
301
+ toggleThreadFilter ( renderResult . container , ThreadFilterType . All ) ;
302
+ await waitFor ( ( ) => expect ( renderResult . container . querySelector ( ".mx_AutoHideScrollbar" ) ) . toBeFalsy ( ) ) ;
303
+ await waitFor ( ( ) => {
304
+ events = findEvents ( renderResult . container ) ;
305
+ expect ( findEvents ( renderResult . container ) ) . toHaveLength ( 1 ) ;
306
+ } ) ;
307
+ expect ( events [ 0 ] ) . toEqual ( toEventData ( otherThread . rootEvent ) ) ;
308
+ } ) ;
309
+ } )
125
310
} ) ;
0 commit comments