@@ -19,6 +19,15 @@ import { mount } from "enzyme";
19
19
import { act } from "react-dom/test-utils" ;
20
20
import { MatrixEvent } from "matrix-js-sdk/src/models/event" ;
21
21
import { EventType , RelationType , MsgType } from "matrix-js-sdk/src/@types/event" ;
22
+ import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state" ;
23
+ import {
24
+ M_POLL_RESPONSE ,
25
+ M_POLL_END ,
26
+ M_POLL_KIND_DISCLOSED ,
27
+ PollStartEvent ,
28
+ PollResponseEvent ,
29
+ PollEndEvent ,
30
+ } from "matrix-events-sdk" ;
22
31
23
32
import "../../../skinned-sdk" ;
24
33
import {
@@ -31,27 +40,30 @@ import {
31
40
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg" ;
32
41
import _PinnedMessagesCard from "../../../../src/components/views/right_panel/PinnedMessagesCard" ;
33
42
import PinnedEventTile from "../../../../src/components/views/rooms/PinnedEventTile" ;
43
+ import MPollBody from "../../../../src/components/views/messages/MPollBody.tsx" ;
44
+
45
+ const PinnedMessagesCard = wrapInMatrixClientContext ( _PinnedMessagesCard ) ;
34
46
35
47
describe ( "<PinnedMessagesCard />" , ( ) => {
36
48
stubClient ( ) ;
37
49
const cli = MatrixClientPeg . get ( ) ;
50
+ cli . getUserId . mockReturnValue ( "@alice:example.org" ) ;
38
51
cli . setRoomAccountData = ( ) => { } ;
39
52
cli . relations = jest . fn ( ) . mockResolvedValue ( { events : [ ] } ) ;
40
- const PinnedMessagesCard = wrapInMatrixClientContext ( _PinnedMessagesCard ) ;
41
53
42
- const mkRoom = ( localPins : MatrixEvent [ ] , nonLocalPins : MatrixEvent [ ] ) => {
43
- const pins = [ ...localPins , ...nonLocalPins ] ;
54
+ const mkRoom = ( localPins : MatrixEvent [ ] , nonLocalPins : MatrixEvent [ ] ) : Room => {
44
55
const room = mkStubRoom ( "!room:example.org" ) ;
56
+ // Deferred since we may be adding or removing pins later
57
+ const pins = ( ) => [ ...localPins , ...nonLocalPins ] ;
45
58
46
59
// Insert pin IDs into room state
47
- const pinState = mkEvent ( {
60
+ room . currentState . getStateEvents . mockImplementation ( ( ) => mkEvent ( {
48
61
event : true ,
49
62
type : EventType . RoomPinnedEvents ,
50
63
content : {
51
- pinned : pins . map ( e => e . getId ( ) ) ,
64
+ pinned : pins ( ) . map ( e => e . getId ( ) ) ,
52
65
} ,
53
- } ) ;
54
- room . currentState . getStateEvents . mockReturnValue ( pinState ) ;
66
+ } ) ) ;
55
67
56
68
// Insert local pins into local timeline set
57
69
room . getUnfilteredTimelineSet = ( ) => ( {
@@ -61,11 +73,86 @@ describe("<PinnedMessagesCard />", () => {
61
73
} ) ;
62
74
63
75
// Return all pins over fetchRoomEvent
64
- cli . fetchRoomEvent = ( roomId , eventId ) => pins . find ( e => e . getId ( ) === eventId ) ?. event ;
76
+ cli . fetchRoomEvent = ( roomId , eventId ) => pins ( ) . find ( e => e . getId ( ) === eventId ) ?. event ;
65
77
66
78
return room ;
67
79
} ;
68
80
81
+ const mountPins = async ( room : Room ) : ReactWrapper => {
82
+ let pins ;
83
+ await act ( async ( ) => {
84
+ pins = mount ( < PinnedMessagesCard room = { room } onClose = { ( ) => { } } /> ) ;
85
+ // Wait a tick for state updates
86
+ await new Promise ( resolve => setImmediate ( resolve ) ) ;
87
+ } ) ;
88
+ pins . update ( ) ;
89
+
90
+ return pins ;
91
+ } ;
92
+
93
+ const emitPinUpdates = async ( pins : ReactWrapper ) => {
94
+ const room = pins . props ( ) . room ;
95
+ const pinListener = room . currentState . on . mock . calls
96
+ . find ( ( [ eventName , listener ] ) => eventName === RoomStateEvent . Events ) [ 1 ] ;
97
+
98
+ await act ( async ( ) => {
99
+ // Emit the update
100
+ pinListener ( room . currentState . getStateEvents ( ) ) ;
101
+ // Wait a tick for state updates
102
+ await new Promise ( resolve => setImmediate ( resolve ) ) ;
103
+ } ) ;
104
+ pins . update ( ) ;
105
+ } ;
106
+
107
+ const pin1 = mkMessage ( {
108
+ event : true ,
109
+ room : "!room:example.org" ,
110
+ user : "@alice:example.org" ,
111
+ msg : "First pinned message" ,
112
+ } ) ;
113
+ const pin2 = mkMessage ( {
114
+ event : true ,
115
+ room : "!room:example.org" ,
116
+ user : "@alice:example.org" ,
117
+ msg : "The second one" ,
118
+ } ) ;
119
+
120
+ it ( "updates when messages are pinned" , async ( ) => {
121
+ // Start with nothing pinned
122
+ const localPins = [ ] ;
123
+ const nonLocalPins = [ ] ;
124
+ const pins = await mountPins ( mkRoom ( localPins , nonLocalPins ) ) ;
125
+ expect ( pins . find ( PinnedEventTile ) . length ) . toBe ( 0 ) ;
126
+
127
+ // Pin the first message
128
+ localPins . push ( pin1 ) ;
129
+ await emitPinUpdates ( pins ) ;
130
+ expect ( pins . find ( PinnedEventTile ) . length ) . toBe ( 1 ) ;
131
+
132
+ // Pin the second message
133
+ nonLocalPins . push ( pin2 ) ;
134
+ await emitPinUpdates ( pins ) ;
135
+ expect ( pins . find ( PinnedEventTile ) . length ) . toBe ( 2 ) ;
136
+ } ) ;
137
+
138
+ it ( "updates when messages are unpinned" , async ( ) => {
139
+ // Start with two pins
140
+ const localPins = [ pin1 ] ;
141
+ const nonLocalPins = [ pin2 ] ;
142
+ const pins = await mountPins ( mkRoom ( localPins , nonLocalPins ) ) ;
143
+ expect ( pins . find ( PinnedEventTile ) . length ) . toBe ( 2 ) ;
144
+
145
+ // Unpin the first message
146
+ localPins . pop ( ) ;
147
+ await emitPinUpdates ( pins ) ;
148
+ expect ( pins . find ( PinnedEventTile ) . length ) . toBe ( 1 ) ;
149
+
150
+ // Unpin the second message
151
+ nonLocalPins . pop ( ) ;
152
+ await emitPinUpdates ( pins ) ;
153
+ expect ( pins . find ( PinnedEventTile ) . length ) . toBe ( 0 ) ;
154
+ } ) ;
155
+
69
156
it ( "hides unpinnable events found in local timeline" , async ( ) => {
70
157
// Redacted messages are unpinnable
71
158
const pin = mkEvent ( {
@@ -75,13 +162,7 @@ describe("<PinnedMessagesCard />", () => {
75
162
unsigned : { redacted_because : { } } ,
76
163
} ) ;
77
164
78
- let pins ;
79
- await act ( async ( ) => {
80
- pins = mount ( < PinnedMessagesCard room = { mkRoom ( [ pin ] , [ ] ) } onClose = { ( ) => { } } /> ) ;
81
- // Wait a tick for state updates
82
- await new Promise ( resolve => setImmediate ( resolve ) ) ;
83
- } ) ;
84
- pins . update ( ) ;
165
+ const pins = await mountPins ( mkRoom ( [ pin ] , [ ] ) ) ;
85
166
expect ( pins . find ( PinnedEventTile ) . length ) . toBe ( 0 ) ;
86
167
} ) ;
87
168
@@ -94,23 +175,11 @@ describe("<PinnedMessagesCard />", () => {
94
175
unsigned : { redacted_because : { } } ,
95
176
} ) ;
96
177
97
- let pins ;
98
- await act ( async ( ) => {
99
- pins = mount ( < PinnedMessagesCard room = { mkRoom ( [ ] , [ pin ] ) } onClose = { ( ) => { } } /> ) ;
100
- // Wait a tick for state updates
101
- await new Promise ( resolve => setImmediate ( resolve ) ) ;
102
- } ) ;
103
- pins . update ( ) ;
178
+ const pins = await mountPins ( mkRoom ( [ ] , [ pin ] ) ) ;
104
179
expect ( pins . find ( PinnedEventTile ) . length ) . toBe ( 0 ) ;
105
180
} ) ;
106
181
107
182
it ( "accounts for edits" , async ( ) => {
108
- const pin = mkMessage ( {
109
- event : true ,
110
- room : "!room:example.org" ,
111
- user : "@alice:example.org" ,
112
- msg : "Hello!" ,
113
- } ) ;
114
183
cli . relations . mockResolvedValue ( {
115
184
events : [ mkEvent ( {
116
185
event : true ,
@@ -119,29 +188,67 @@ describe("<PinnedMessagesCard />", () => {
119
188
user : "@alice:example.org" ,
120
189
content : {
121
190
"msgtype" : MsgType . Text ,
122
- "body" : " * Hello again! " ,
191
+ "body" : " * First pinned message, edited " ,
123
192
"m.new_content" : {
124
193
msgtype : MsgType . Text ,
125
- body : "Hello again! " ,
194
+ body : "First pinned message, edited " ,
126
195
} ,
127
196
"m.relates_to" : {
128
197
rel_type : RelationType . Replace ,
129
- event_id : pin . getId ( ) ,
198
+ event_id : pin1 . getId ( ) ,
130
199
} ,
131
200
} ,
132
201
} ) ] ,
133
202
} ) ;
134
203
135
- let pins ;
136
- await act ( async ( ) => {
137
- pins = mount ( < PinnedMessagesCard room = { mkRoom ( [ ] , [ pin ] ) } onClose = { ( ) => { } } /> ) ;
138
- // Wait a tick for state updates
139
- await new Promise ( resolve => setImmediate ( resolve ) ) ;
140
- } ) ;
141
- pins . update ( ) ;
142
-
204
+ const pins = await mountPins ( mkRoom ( [ ] , [ pin1 ] ) ) ;
143
205
const pinTile = pins . find ( PinnedEventTile ) ;
144
206
expect ( pinTile . length ) . toBe ( 1 ) ;
145
- expect ( pinTile . find ( ".mx_EventTile_body" ) . text ( ) ) . toEqual ( "Hello again!" ) ;
207
+ expect ( pinTile . find ( ".mx_EventTile_body" ) . text ( ) ) . toEqual ( "First pinned message, edited" ) ;
208
+ } ) ;
209
+
210
+ it ( "displays votes on polls not found in local timeline" , async ( ) => {
211
+ const poll = mkEvent ( {
212
+ ...PollStartEvent . from ( "A poll" , [ "Option 1" , "Option 2" ] , M_POLL_KIND_DISCLOSED ) . serialize ( ) ,
213
+ event : true ,
214
+ room : "!room:example.org" ,
215
+ user : "@alice:example.org" ,
216
+ } ) ;
217
+
218
+ const answers = ( poll . unstableExtensibleEvent as PollStartEvent ) . answers ;
219
+ const responses = [
220
+ [ "@alice:example.org" , 0 ] ,
221
+ [ "@bob:example.org" , 0 ] ,
222
+ [ "@eve:example.org" , 1 ] ,
223
+ ] . map ( ( [ user , option ] , i ) => mkEvent ( {
224
+ ...PollResponseEvent . from ( [ answers [ option ] . id ] , poll . getId ( ) ) . serialize ( ) ,
225
+ event : true ,
226
+ room : "!room:example.org" ,
227
+ user,
228
+ } ) ) ;
229
+ const end = mkEvent ( {
230
+ ...PollEndEvent . from ( poll . getId ( ) , "Closing the poll" ) . serialize ( ) ,
231
+ event : true ,
232
+ room : "!room:example.org" ,
233
+ user : "@alice:example.org" ,
234
+ } ) ;
235
+
236
+ // Make the responses available
237
+ cli . relations . mockImplementation ( ( roomId , eventId , relationType , eventType ) => {
238
+ if ( eventId === poll . getId ( ) && relationType === RelationType . Reference ) {
239
+ switch ( eventType ) {
240
+ case M_POLL_RESPONSE . name : return { events : responses } ;
241
+ case M_POLL_END . name : return { events : [ end ] } ;
242
+ }
243
+ }
244
+ return { events : [ ] } ;
245
+ } ) ;
246
+
247
+ const pins = await mountPins ( mkRoom ( [ ] , [ poll ] ) ) ;
248
+ const pinTile = pins . find ( MPollBody ) ;
249
+ expect ( pinTile . length ) . toEqual ( 1 ) ;
250
+ expect ( pinTile . find ( ".mx_MPollBody_option_ended" ) . length ) . toEqual ( 2 ) ;
251
+ expect ( pinTile . find ( ".mx_MPollBody_optionVoteCount" ) . first ( ) . text ( ) ) . toEqual ( "2 votes" ) ;
252
+ expect ( pinTile . find ( ".mx_MPollBody_optionVoteCount" ) . last ( ) . text ( ) ) . toEqual ( "1 vote" ) ;
146
253
} ) ;
147
254
} ) ;
0 commit comments