use Poll model with relations API in poll rendering (#9877)

* wip

* remove dupe

* use poll model relations in all cases

* update mpollbody tests to use poll instance

* update poll fetching login in pinned messages card

* add pinned polls to room polls state

* add spinner while relations are still loading

* handle no poll in end poll dialog

* strict errors

* strict fix

* more strict fix
This commit is contained in:
Kerry 2023-02-03 09:22:26 +13:00 committed by GitHub
parent b45b933a65
commit 544baa30ed
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 350 additions and 670 deletions

File diff suppressed because it is too large Load diff

View file

@ -456,6 +456,8 @@ exports[`MPollBody renders a finished poll with no votes 1`] = `
</div>
`;
exports[`MPollBody renders a loader while responses are still loading 1`] = `"Based on 4 votes<div class="mx_Spinner"><div class="mx_Spinner_icon" style="width: 16px; height: 16px;" aria-label="Loading..." role="progressbar" data-testid="spinner"></div></div>"`;
exports[`MPollBody renders a poll that I have not voted in 1`] = `
<div>
<div
@ -769,7 +771,6 @@ exports[`MPollBody renders a poll with local, non-local and invalid votes 1`] =
class="mx_StyledRadioButton mx_MPollBody_live-option mx_StyledRadioButton_enabled mx_StyledRadioButton_checked"
>
<input
checked=""
name="poll_answer_select-$mypoll"
type="radio"
value="italian"
@ -1224,7 +1225,6 @@ exports[`MPollBody renders a poll with only non-local votes 1`] = `
class="mx_StyledRadioButton mx_MPollBody_live-option mx_StyledRadioButton_enabled mx_StyledRadioButton_checked"
>
<input
checked=""
name="poll_answer_select-$mypoll"
type="radio"
value="wings"

View file

@ -23,12 +23,12 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { EventType, RelationType, MsgType } from "matrix-js-sdk/src/@types/event";
import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
import { IEvent, Room, EventTimelineSet, IMinimalEvent } from "matrix-js-sdk/src/matrix";
import { M_POLL_RESPONSE, M_POLL_END, M_POLL_KIND_DISCLOSED } from "matrix-js-sdk/src/@types/polls";
import { M_POLL_KIND_DISCLOSED } from "matrix-js-sdk/src/@types/polls";
import { PollStartEvent } from "matrix-js-sdk/src/extensible_events_v1/PollStartEvent";
import { PollResponseEvent } from "matrix-js-sdk/src/extensible_events_v1/PollResponseEvent";
import { PollEndEvent } from "matrix-js-sdk/src/extensible_events_v1/PollEndEvent";
import { stubClient, mkStubRoom, mkEvent, mkMessage } from "../../../test-utils";
import { stubClient, mkEvent, mkMessage, flushPromises } from "../../../test-utils";
import { MatrixClientPeg } from "../../../../src/MatrixClientPeg";
import PinnedMessagesCard from "../../../../src/components/views/right_panel/PinnedMessagesCard";
import PinnedEventTile from "../../../../src/components/views/rooms/PinnedEventTile";
@ -40,16 +40,16 @@ describe("<PinnedMessagesCard />", () => {
stubClient();
const cli = mocked(MatrixClientPeg.get());
cli.getUserId.mockReturnValue("@alice:example.org");
cli.setRoomAccountData.mockReturnValue(undefined);
cli.setRoomAccountData.mockResolvedValue({});
cli.relations.mockResolvedValue({ originalEvent: {} as unknown as MatrixEvent, events: [] });
const mkRoom = (localPins: MatrixEvent[], nonLocalPins: MatrixEvent[]): Room => {
const room = mkStubRoom("!room:example.org", "room", cli);
const room = new Room("!room:example.org", cli, "@me:example.org");
// Deferred since we may be adding or removing pins later
const pins = () => [...localPins, ...nonLocalPins];
// Insert pin IDs into room state
mocked(room.currentState).getStateEvents.mockImplementation((): any =>
jest.spyOn(room.currentState, "getStateEvents").mockImplementation((): any =>
mkEvent({
event: true,
type: EventType.RoomPinnedEvents,
@ -61,6 +61,8 @@ describe("<PinnedMessagesCard />", () => {
}),
);
jest.spyOn(room.currentState, "on");
// Insert local pins into local timeline set
room.getUnfilteredTimelineSet = () =>
({
@ -75,6 +77,8 @@ describe("<PinnedMessagesCard />", () => {
return Promise.resolve(event as IMinimalEvent);
});
cli.getRoom.mockReturnValue(room);
return room;
};
@ -131,8 +135,8 @@ describe("<PinnedMessagesCard />", () => {
it("updates when messages are pinned", async () => {
// Start with nothing pinned
const localPins = [];
const nonLocalPins = [];
const localPins: MatrixEvent[] = [];
const nonLocalPins: MatrixEvent[] = [];
const pins = await mountPins(mkRoom(localPins, nonLocalPins));
expect(pins.find(PinnedEventTile).length).toBe(0);
@ -240,31 +244,27 @@ describe("<PinnedMessagesCard />", () => {
["@eve:example.org", 1],
].map(([user, option], i) =>
mkEvent({
...PollResponseEvent.from([answers[option].id], poll.getId()).serialize(),
...PollResponseEvent.from([answers[option as number].id], poll.getId()!).serialize(),
event: true,
room: "!room:example.org",
user: user as string,
}),
);
const end = mkEvent({
...PollEndEvent.from(poll.getId(), "Closing the poll").serialize(),
...PollEndEvent.from(poll.getId()!, "Closing the poll").serialize(),
event: true,
room: "!room:example.org",
user: "@alice:example.org",
});
// Make the responses available
cli.relations.mockImplementation(async (roomId, eventId, relationType, eventType, { from }) => {
cli.relations.mockImplementation(async (roomId, eventId, relationType, eventType, opts) => {
if (eventId === poll.getId() && relationType === RelationType.Reference) {
switch (eventType) {
case M_POLL_RESPONSE.name:
// Paginate the results, for added challenge
return from === "page2"
? { originalEvent: poll, events: responses.slice(2) }
: { originalEvent: poll, events: responses.slice(0, 2), nextBatch: "page2" };
case M_POLL_END.name:
return { originalEvent: null, events: [end] };
}
// Paginate the results, for added challenge
return opts?.from === "page2"
? { originalEvent: poll, events: responses.slice(2) }
: { originalEvent: poll, events: [...responses.slice(0, 2), end], nextBatch: "page2" };
}
// type does not allow originalEvent to be falsy
// but code seems to
@ -272,8 +272,20 @@ describe("<PinnedMessagesCard />", () => {
return { originalEvent: undefined as unknown as MatrixEvent, events: [] };
});
const pins = await mountPins(mkRoom([], [poll]));
const room = mkRoom([], [poll]);
// poll end event validates against this
jest.spyOn(room.currentState, "maySendRedactionForEvent").mockReturnValue(true);
const pins = await mountPins(room);
// two pages of results
await flushPromises();
await flushPromises();
const pollInstance = room.polls.get(poll.getId()!);
expect(pollInstance).toBeTruthy();
const pinTile = pins.find(MPollBody);
expect(pinTile.length).toEqual(1);
expect(pinTile.find(".mx_MPollBody_option_ended").length).toEqual(2);
expect(pinTile.find(".mx_MPollBody_optionVoteCount").first().text()).toEqual("2 votes");