Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 77ed1e3

Browse files
authored
More pinned messages tests (#7988)
* Test basic pinned message functionality Signed-off-by: Robin Townsend <[email protected]> * Test pinned polls Signed-off-by: Robin Townsend <[email protected]> * Fix test Signed-off-by: Robin Townsend <[email protected]>
1 parent b981a96 commit 77ed1e3

File tree

2 files changed

+148
-40
lines changed

2 files changed

+148
-40
lines changed

test/components/views/right_panel/PinnedMessagesCard-test.tsx

Lines changed: 147 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@ import { mount } from "enzyme";
1919
import { act } from "react-dom/test-utils";
2020
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
2121
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";
2231

2332
import "../../../skinned-sdk";
2433
import {
@@ -31,27 +40,30 @@ import {
3140
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
3241
import _PinnedMessagesCard from "../../../../src/components/views/right_panel/PinnedMessagesCard";
3342
import PinnedEventTile from "../../../../src/components/views/rooms/PinnedEventTile";
43+
import MPollBody from "../../../../src/components/views/messages/MPollBody.tsx";
44+
45+
const PinnedMessagesCard = wrapInMatrixClientContext(_PinnedMessagesCard);
3446

3547
describe("<PinnedMessagesCard />", () => {
3648
stubClient();
3749
const cli = MatrixClientPeg.get();
50+
cli.getUserId.mockReturnValue("@alice:example.org");
3851
cli.setRoomAccountData = () => {};
3952
cli.relations = jest.fn().mockResolvedValue({ events: [] });
40-
const PinnedMessagesCard = wrapInMatrixClientContext(_PinnedMessagesCard);
4153

42-
const mkRoom = (localPins: MatrixEvent[], nonLocalPins: MatrixEvent[]) => {
43-
const pins = [...localPins, ...nonLocalPins];
54+
const mkRoom = (localPins: MatrixEvent[], nonLocalPins: MatrixEvent[]): Room => {
4455
const room = mkStubRoom("!room:example.org");
56+
// Deferred since we may be adding or removing pins later
57+
const pins = () => [...localPins, ...nonLocalPins];
4558

4659
// Insert pin IDs into room state
47-
const pinState = mkEvent({
60+
room.currentState.getStateEvents.mockImplementation(() => mkEvent({
4861
event: true,
4962
type: EventType.RoomPinnedEvents,
5063
content: {
51-
pinned: pins.map(e => e.getId()),
64+
pinned: pins().map(e => e.getId()),
5265
},
53-
});
54-
room.currentState.getStateEvents.mockReturnValue(pinState);
66+
}));
5567

5668
// Insert local pins into local timeline set
5769
room.getUnfilteredTimelineSet = () => ({
@@ -61,11 +73,86 @@ describe("<PinnedMessagesCard />", () => {
6173
});
6274

6375
// 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;
6577

6678
return room;
6779
};
6880

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+
69156
it("hides unpinnable events found in local timeline", async () => {
70157
// Redacted messages are unpinnable
71158
const pin = mkEvent({
@@ -75,13 +162,7 @@ describe("<PinnedMessagesCard />", () => {
75162
unsigned: { redacted_because: {} },
76163
});
77164

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], []));
85166
expect(pins.find(PinnedEventTile).length).toBe(0);
86167
});
87168

@@ -94,23 +175,11 @@ describe("<PinnedMessagesCard />", () => {
94175
unsigned: { redacted_because: {} },
95176
});
96177

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]));
104179
expect(pins.find(PinnedEventTile).length).toBe(0);
105180
});
106181

107182
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-
});
114183
cli.relations.mockResolvedValue({
115184
events: [mkEvent({
116185
event: true,
@@ -119,29 +188,67 @@ describe("<PinnedMessagesCard />", () => {
119188
user: "@alice:example.org",
120189
content: {
121190
"msgtype": MsgType.Text,
122-
"body": " * Hello again!",
191+
"body": " * First pinned message, edited",
123192
"m.new_content": {
124193
msgtype: MsgType.Text,
125-
body: "Hello again!",
194+
body: "First pinned message, edited",
126195
},
127196
"m.relates_to": {
128197
rel_type: RelationType.Replace,
129-
event_id: pin.getId(),
198+
event_id: pin1.getId(),
130199
},
131200
},
132201
})],
133202
});
134203

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]));
143205
const pinTile = pins.find(PinnedEventTile);
144206
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");
146253
});
147254
});

test/test-utils/test-utils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,7 @@ export function mkStubRoom(roomId = null, name: string, client: MatrixClient): R
334334
getMember: jest.fn(),
335335
mayClientSendStateEvent: jest.fn().mockReturnValue(true),
336336
maySendStateEvent: jest.fn().mockReturnValue(true),
337+
maySendRedactionForEvent: jest.fn().mockReturnValue(true),
337338
maySendEvent: jest.fn().mockReturnValue(true),
338339
members: {},
339340
getJoinRule: jest.fn().mockReturnValue(JoinRule.Invite),

0 commit comments

Comments
 (0)