diff --git a/src/components/views/messages/MPollBody.tsx b/src/components/views/messages/MPollBody.tsx index 85500cfbb2..a9317957c6 100644 --- a/src/components/views/messages/MPollBody.tsx +++ b/src/components/views/messages/MPollBody.tsx @@ -448,7 +448,7 @@ export default class MPollBody extends React.Component { return (
-

+

{poll.question.text} {editedSpan}

@@ -471,7 +471,12 @@ export default class MPollBody extends React.Component { const answerPercent = totalVotes === 0 ? 0 : Math.round((100.0 * answerVotes) / totalVotes); return ( -
this.selectOption(answer.id)}> +
this.selectOption(answer.id)} + > {ended ? ( ) : ( @@ -493,7 +498,9 @@ export default class MPollBody extends React.Component { ); })}
-
{totalText}
+
+ {totalText} +
); } diff --git a/test/components/views/messages/MPollBody-test.tsx b/test/components/views/messages/MPollBody-test.tsx index 1be993f88e..a6f9b5e11c 100644 --- a/test/components/views/messages/MPollBody-test.tsx +++ b/test/components/views/messages/MPollBody-test.tsx @@ -15,8 +15,7 @@ limitations under the License. */ import React from "react"; -// eslint-disable-next-line deprecate/import -import { mount, ReactWrapper } from "enzyme"; +import { fireEvent, render, RenderResult } from "@testing-library/react"; import { MatrixClient, MatrixEvent, Room } from "matrix-js-sdk/src/matrix"; import { Relations } from "matrix-js-sdk/src/models/relations"; import { RelatedRelations } from "matrix-js-sdk/src/models/related-relations"; @@ -44,6 +43,8 @@ import { IBodyProps } from "../../../../src/components/views/messages/IBodyProps import { getMockClientWithEventEmitter } from "../../../test-utils"; import MatrixClientContext from "../../../../src/contexts/MatrixClientContext"; import MPollBody from "../../../../src/components/views/messages/MPollBody"; +import { RoomPermalinkCreator } from "../../../../src/utils/permalinks/Permalinks"; +import { MediaEventHelper } from "../../../../src/utils/MediaEventHelper"; const CHECKED = "mx_MPollBody_option_checked"; @@ -85,13 +86,13 @@ describe("MPollBody", () => { new RelatedRelations([newEndRelations([])]), ), ).toEqual([ - new UserVote(ev1.getTs(), ev1.getSender(), ev1.getContent()[M_POLL_RESPONSE.name].answers), + new UserVote(ev1.getTs(), ev1.getSender()!, ev1.getContent()[M_POLL_RESPONSE.name].answers), new UserVote( badEvent.getTs(), - badEvent.getSender(), + badEvent.getSender()!, [], // should be spoiled ), - new UserVote(ev2.getTs(), ev2.getSender(), ev2.getContent()[M_POLL_RESPONSE.name].answers), + new UserVote(ev2.getTs(), ev2.getSender()!, ev2.getContent()[M_POLL_RESPONSE.name].answers), ]); }); @@ -146,14 +147,14 @@ describe("MPollBody", () => { }); it("renders no votes if none were made", () => { - const votes = []; - const body = newMPollBody(votes); - expect(votesCount(body, "pizza")).toBe(""); - expect(votesCount(body, "poutine")).toBe(""); - expect(votesCount(body, "italian")).toBe(""); - expect(votesCount(body, "wings")).toBe(""); - expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("No votes cast"); - expect(body.find("h2").html()).toEqual("

What should we order for the party?

"); + const votes: MatrixEvent[] = []; + const renderResult = newMPollBody(votes); + expect(votesCount(renderResult, "pizza")).toBe(""); + expect(votesCount(renderResult, "poutine")).toBe(""); + expect(votesCount(renderResult, "italian")).toBe(""); + expect(votesCount(renderResult, "wings")).toBe(""); + expect(renderResult.getByTestId("totalVotes").innerHTML).toBe("No votes cast"); + expect(renderResult.getByText("What should we order for the party?")).toBeTruthy(); }); it("finds votes from multiple people", () => { @@ -163,12 +164,12 @@ describe("MPollBody", () => { responseEvent("@catrd:example.com", "poutine"), responseEvent("@dune2:example.com", "wings"), ]; - const body = newMPollBody(votes); - expect(votesCount(body, "pizza")).toBe("2 votes"); - expect(votesCount(body, "poutine")).toBe("1 vote"); - expect(votesCount(body, "italian")).toBe("0 votes"); - expect(votesCount(body, "wings")).toBe("1 vote"); - expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Based on 4 votes"); + const renderResult = newMPollBody(votes); + expect(votesCount(renderResult, "pizza")).toBe("2 votes"); + expect(votesCount(renderResult, "poutine")).toBe("1 vote"); + expect(votesCount(renderResult, "italian")).toBe("0 votes"); + expect(votesCount(renderResult, "wings")).toBe("1 vote"); + expect(renderResult.getByTestId("totalVotes").innerHTML).toBe("Based on 4 votes"); }); it("ignores end poll events from unauthorised users", () => { @@ -179,15 +180,15 @@ describe("MPollBody", () => { responseEvent("@dune2:example.com", "wings"), ]; const ends = [endEvent("@notallowed:example.com", 12)]; - const body = newMPollBody(votes, ends); + const renderResult = newMPollBody(votes, ends); // Even though an end event was sent, we render the poll as unfinished // because this person is not allowed to send these events - expect(votesCount(body, "pizza")).toBe("2 votes"); - expect(votesCount(body, "poutine")).toBe("1 vote"); - expect(votesCount(body, "italian")).toBe("0 votes"); - expect(votesCount(body, "wings")).toBe("1 vote"); - expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Based on 4 votes"); + expect(votesCount(renderResult, "pizza")).toBe("2 votes"); + expect(votesCount(renderResult, "poutine")).toBe("1 vote"); + expect(votesCount(renderResult, "italian")).toBe("0 votes"); + expect(votesCount(renderResult, "wings")).toBe("1 vote"); + expect(renderResult.getByTestId("totalVotes").innerHTML).toBe("Based on 4 votes"); }); it("hides scores if I have not voted", () => { @@ -197,22 +198,22 @@ describe("MPollBody", () => { responseEvent("@catrd:example.com", "poutine"), responseEvent("@dune2:example.com", "wings"), ]; - const body = newMPollBody(votes); - expect(votesCount(body, "pizza")).toBe(""); - expect(votesCount(body, "poutine")).toBe(""); - expect(votesCount(body, "italian")).toBe(""); - expect(votesCount(body, "wings")).toBe(""); - expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("4 votes cast. Vote to see the results"); + const renderResult = newMPollBody(votes); + expect(votesCount(renderResult, "pizza")).toBe(""); + expect(votesCount(renderResult, "poutine")).toBe(""); + expect(votesCount(renderResult, "italian")).toBe(""); + expect(votesCount(renderResult, "wings")).toBe(""); + expect(renderResult.getByTestId("totalVotes").innerHTML).toBe("4 votes cast. Vote to see the results"); }); it("hides a single vote if I have not voted", () => { const votes = [responseEvent("@alice:example.com", "pizza")]; - const body = newMPollBody(votes); - expect(votesCount(body, "pizza")).toBe(""); - expect(votesCount(body, "poutine")).toBe(""); - expect(votesCount(body, "italian")).toBe(""); - expect(votesCount(body, "wings")).toBe(""); - expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("1 vote cast. Vote to see the results"); + const renderResult = newMPollBody(votes); + expect(votesCount(renderResult, "pizza")).toBe(""); + expect(votesCount(renderResult, "poutine")).toBe(""); + expect(votesCount(renderResult, "italian")).toBe(""); + expect(votesCount(renderResult, "wings")).toBe(""); + expect(renderResult.getByTestId("totalVotes").innerHTML).toBe("1 vote cast. Vote to see the results"); }); it("takes someone's most recent vote if they voted several times", () => { @@ -223,12 +224,12 @@ describe("MPollBody", () => { responseEvent("@qbert:example.com", "poutine", 16), // latest qbert responseEvent("@qbert:example.com", "wings", 15), ]; - const body = newMPollBody(votes); - expect(votesCount(body, "pizza")).toBe("0 votes"); - expect(votesCount(body, "poutine")).toBe("1 vote"); - expect(votesCount(body, "italian")).toBe("0 votes"); - expect(votesCount(body, "wings")).toBe("1 vote"); - expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Based on 2 votes"); + const renderResult = newMPollBody(votes); + expect(votesCount(renderResult, "pizza")).toBe("0 votes"); + expect(votesCount(renderResult, "poutine")).toBe("1 vote"); + expect(votesCount(renderResult, "italian")).toBe("0 votes"); + expect(votesCount(renderResult, "wings")).toBe("1 vote"); + expect(renderResult.getByTestId("totalVotes").innerHTML).toBe("Based on 2 votes"); }); it("uses my local vote", () => { @@ -238,18 +239,18 @@ describe("MPollBody", () => { responseEvent("@fg:example.com", "pizza", 15), responseEvent("@hi:example.com", "pizza", 15), ]; - const body = newMPollBody(votes); + const renderResult = newMPollBody(votes); // When I vote for Italian - clickRadio(body, "italian"); + clickOption(renderResult, "italian"); // My vote is counted - expect(votesCount(body, "pizza")).toBe("3 votes"); - expect(votesCount(body, "poutine")).toBe("0 votes"); - expect(votesCount(body, "italian")).toBe("1 vote"); - expect(votesCount(body, "wings")).toBe("0 votes"); + expect(votesCount(renderResult, "pizza")).toBe("3 votes"); + expect(votesCount(renderResult, "poutine")).toBe("0 votes"); + expect(votesCount(renderResult, "italian")).toBe("1 vote"); + expect(votesCount(renderResult, "wings")).toBe("0 votes"); - expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Based on 4 votes"); + expect(renderResult.getByTestId("totalVotes").innerHTML).toBe("Based on 4 votes"); }); it("overrides my other votes with my local vote", () => { @@ -260,53 +261,66 @@ describe("MPollBody", () => { responseEvent("@me:example.com", "italian", 14), responseEvent("@nf:example.com", "italian", 15), ]; - const body = newMPollBody(votes); + const renderResult = newMPollBody(votes); // When I click Wings - clickRadio(body, "wings"); + clickOption(renderResult, "wings"); // Then my vote is counted for Wings, and not for Italian - expect(votesCount(body, "pizza")).toBe("0 votes"); - expect(votesCount(body, "poutine")).toBe("0 votes"); - expect(votesCount(body, "italian")).toBe("1 vote"); - expect(votesCount(body, "wings")).toBe("1 vote"); + expect(votesCount(renderResult, "pizza")).toBe("0 votes"); + expect(votesCount(renderResult, "poutine")).toBe("0 votes"); + expect(votesCount(renderResult, "italian")).toBe("1 vote"); + expect(votesCount(renderResult, "wings")).toBe("1 vote"); - expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Based on 2 votes"); + expect(renderResult.getByTestId("totalVotes").innerHTML).toBe("Based on 2 votes"); // And my vote is highlighted - expect(voteButton(body, "wings").hasClass(CHECKED)).toBe(true); - expect(voteButton(body, "italian").hasClass(CHECKED)).toBe(false); + expect(voteButton(renderResult, "wings").className.includes(CHECKED)).toBe(true); + expect(voteButton(renderResult, "italian").className.includes(CHECKED)).toBe(false); }); it("cancels my local vote if another comes in", () => { // Given I voted locally const votes = [responseEvent("@me:example.com", "pizza", 100)]; - const body = newMPollBody(votes); - const props: IBodyProps = body.instance().props as IBodyProps; + const mxEvent = new MatrixEvent({ + type: M_POLL_START.name, + event_id: "$mypoll", + room_id: "#myroom:example.com", + content: newPollStart(undefined, undefined, true), + }); + const props = getMPollBodyPropsFromEvent(mxEvent, votes); + const renderResult = renderMPollBodyWithWrapper(props); const voteRelations = props!.getRelationsForEvent!("$mypoll", "m.reference", M_POLL_RESPONSE.name); expect(voteRelations).toBeDefined(); - clickRadio(body, "pizza"); + clickOption(renderResult, "pizza"); // When a new vote from me comes in voteRelations!.addEvent(responseEvent("@me:example.com", "wings", 101)); // Then the new vote is counted, not the old one - expect(votesCount(body, "pizza")).toBe("0 votes"); - expect(votesCount(body, "poutine")).toBe("0 votes"); - expect(votesCount(body, "italian")).toBe("0 votes"); - expect(votesCount(body, "wings")).toBe("1 vote"); + expect(votesCount(renderResult, "pizza")).toBe("0 votes"); + expect(votesCount(renderResult, "poutine")).toBe("0 votes"); + expect(votesCount(renderResult, "italian")).toBe("0 votes"); + expect(votesCount(renderResult, "wings")).toBe("1 vote"); - expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Based on 1 vote"); + expect(renderResult.getByTestId("totalVotes").innerHTML).toBe("Based on 1 vote"); }); it("doesn't cancel my local vote if someone else votes", () => { // Given I voted locally const votes = [responseEvent("@me:example.com", "pizza")]; - const body = newMPollBody(votes); - const props: IBodyProps = body.instance().props as IBodyProps; + const mxEvent = new MatrixEvent({ + type: M_POLL_START.name, + event_id: "$mypoll", + room_id: "#myroom:example.com", + content: newPollStart(undefined, undefined, true), + }); + const props = getMPollBodyPropsFromEvent(mxEvent, votes); + const renderResult = renderMPollBodyWithWrapper(props); + const voteRelations = props!.getRelationsForEvent!("$mypoll", "m.reference", M_POLL_RESPONSE.name); expect(voteRelations).toBeDefined(); - clickRadio(body, "pizza"); + clickOption(renderResult, "pizza"); // When a new vote from someone else comes in voteRelations!.addEvent(responseEvent("@xx:example.com", "wings", 101)); @@ -315,39 +329,39 @@ describe("MPollBody", () => { // NOTE: the new event does not affect the counts for other people - // that is handled through the Relations, not by listening to // these timeline events. - expect(votesCount(body, "pizza")).toBe("1 vote"); - expect(votesCount(body, "poutine")).toBe("0 votes"); - expect(votesCount(body, "italian")).toBe("0 votes"); - expect(votesCount(body, "wings")).toBe("1 vote"); + expect(votesCount(renderResult, "pizza")).toBe("1 vote"); + expect(votesCount(renderResult, "poutine")).toBe("0 votes"); + expect(votesCount(renderResult, "italian")).toBe("0 votes"); + expect(votesCount(renderResult, "wings")).toBe("1 vote"); - expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Based on 2 votes"); + expect(renderResult.getByTestId("totalVotes").innerHTML).toBe("Based on 2 votes"); // And my vote is highlighted - expect(voteButton(body, "pizza").hasClass(CHECKED)).toBe(true); - expect(voteButton(body, "wings").hasClass(CHECKED)).toBe(false); + expect(voteButton(renderResult, "pizza").className.includes(CHECKED)).toBe(true); + expect(voteButton(renderResult, "wings").className.includes(CHECKED)).toBe(false); }); it("highlights my vote even if I did it on another device", () => { // Given I voted italian const votes = [responseEvent("@me:example.com", "italian"), responseEvent("@nf:example.com", "wings")]; - const body = newMPollBody(votes); + const renderResult = newMPollBody(votes); // But I didn't click anything locally // Then my vote is highlighted, and others are not - expect(voteButton(body, "italian").hasClass(CHECKED)).toBe(true); - expect(voteButton(body, "wings").hasClass(CHECKED)).toBe(false); + expect(voteButton(renderResult, "italian").className.includes(CHECKED)).toBe(true); + expect(voteButton(renderResult, "wings").className.includes(CHECKED)).toBe(false); }); it("ignores extra answers", () => { // When cb votes for 2 things, we consider the first only const votes = [responseEvent("@cb:example.com", ["pizza", "wings"]), responseEvent("@me:example.com", "wings")]; - const body = newMPollBody(votes); - expect(votesCount(body, "pizza")).toBe("1 vote"); - expect(votesCount(body, "poutine")).toBe("0 votes"); - expect(votesCount(body, "italian")).toBe("0 votes"); - expect(votesCount(body, "wings")).toBe("1 vote"); - expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Based on 2 votes"); + const renderResult = newMPollBody(votes); + expect(votesCount(renderResult, "pizza")).toBe("1 vote"); + expect(votesCount(renderResult, "poutine")).toBe("0 votes"); + expect(votesCount(renderResult, "italian")).toBe("0 votes"); + expect(votesCount(renderResult, "wings")).toBe("1 vote"); + expect(renderResult.getByTestId("totalVotes").innerHTML).toBe("Based on 2 votes"); }); it("allows un-voting by passing an empty vote", () => { @@ -356,12 +370,12 @@ describe("MPollBody", () => { responseEvent("@nc:example.com", [], 13), responseEvent("@me:example.com", "italian"), ]; - const body = newMPollBody(votes); - expect(votesCount(body, "pizza")).toBe("0 votes"); - expect(votesCount(body, "poutine")).toBe("0 votes"); - expect(votesCount(body, "italian")).toBe("1 vote"); - expect(votesCount(body, "wings")).toBe("0 votes"); - expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Based on 1 vote"); + const renderResult = newMPollBody(votes); + expect(votesCount(renderResult, "pizza")).toBe("0 votes"); + expect(votesCount(renderResult, "poutine")).toBe("0 votes"); + expect(votesCount(renderResult, "italian")).toBe("1 vote"); + expect(votesCount(renderResult, "wings")).toBe("0 votes"); + expect(renderResult.getByTestId("totalVotes").innerHTML).toBe("Based on 1 vote"); }); it("allows re-voting after un-voting", () => { @@ -371,12 +385,12 @@ describe("MPollBody", () => { responseEvent("@op:example.com", "italian", 14), responseEvent("@me:example.com", "italian"), ]; - const body = newMPollBody(votes); - expect(votesCount(body, "pizza")).toBe("0 votes"); - expect(votesCount(body, "poutine")).toBe("0 votes"); - expect(votesCount(body, "italian")).toBe("2 votes"); - expect(votesCount(body, "wings")).toBe("0 votes"); - expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Based on 2 votes"); + const renderResult = newMPollBody(votes); + expect(votesCount(renderResult, "pizza")).toBe("0 votes"); + expect(votesCount(renderResult, "poutine")).toBe("0 votes"); + expect(votesCount(renderResult, "italian")).toBe("2 votes"); + expect(votesCount(renderResult, "wings")).toBe("0 votes"); + expect(renderResult.getByTestId("totalVotes").innerHTML).toBe("Based on 2 votes"); }); it("treats any invalid answer as a spoiled ballot", () => { @@ -389,12 +403,12 @@ describe("MPollBody", () => { responseEvent("@uy:example.com", "italian", 14), responseEvent("@uy:example.com", "doesntexist", 15), ]; - const body = newMPollBody(votes); - expect(votesCount(body, "pizza")).toBe("0 votes"); - expect(votesCount(body, "poutine")).toBe("0 votes"); - expect(votesCount(body, "italian")).toBe("0 votes"); - expect(votesCount(body, "wings")).toBe("0 votes"); - expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Based on 0 votes"); + const renderResult = newMPollBody(votes); + expect(votesCount(renderResult, "pizza")).toBe("0 votes"); + expect(votesCount(renderResult, "poutine")).toBe("0 votes"); + expect(votesCount(renderResult, "italian")).toBe("0 votes"); + expect(votesCount(renderResult, "wings")).toBe("0 votes"); + expect(renderResult.getByTestId("totalVotes").innerHTML).toBe("Based on 0 votes"); }); it("allows re-voting after a spoiled ballot", () => { @@ -405,31 +419,31 @@ describe("MPollBody", () => { responseEvent("@uy:example.com", "doesntexist", 15), responseEvent("@uy:example.com", "poutine", 16), ]; - const body = newMPollBody(votes); - expect(body.find('input[type="radio"]')).toHaveLength(4); - expect(votesCount(body, "pizza")).toBe("0 votes"); - expect(votesCount(body, "poutine")).toBe("1 vote"); - expect(votesCount(body, "italian")).toBe("0 votes"); - expect(votesCount(body, "wings")).toBe("0 votes"); - expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Based on 1 vote"); + const renderResult = newMPollBody(votes); + expect(renderResult.container.querySelectorAll('input[type="radio"]')).toHaveLength(4); + expect(votesCount(renderResult, "pizza")).toBe("0 votes"); + expect(votesCount(renderResult, "poutine")).toBe("1 vote"); + expect(votesCount(renderResult, "italian")).toBe("0 votes"); + expect(votesCount(renderResult, "wings")).toBe("0 votes"); + expect(renderResult.getByTestId("totalVotes").innerHTML).toBe("Based on 1 vote"); }); it("renders nothing if poll has no answers", () => { - const answers = []; - const votes = []; - const ends = []; - const body = newMPollBody(votes, ends, answers); - expect(body.html()).toBeNull(); + const answers: PollAnswer[] = []; + const votes: MatrixEvent[] = []; + const ends: MatrixEvent[] = []; + const { container } = newMPollBody(votes, ends, answers); + expect(container.childElementCount).toEqual(0); }); it("renders the first 20 answers if 21 were given", () => { const answers = Array.from(Array(21).keys()).map((i) => { return { id: `id${i}`, [M_TEXT.name]: `Name ${i}` }; }); - const votes = []; - const ends = []; - const body = newMPollBody(votes, ends, answers); - expect(body.find(".mx_MPollBody_option").length).toBe(20); + const votes: MatrixEvent[] = []; + const ends: MatrixEvent[] = []; + const { container } = newMPollBody(votes, ends, answers); + expect(container.querySelectorAll(".mx_MPollBody_option").length).toBe(20); }); it("hides scores if I voted but the poll is undisclosed", () => { @@ -440,12 +454,12 @@ describe("MPollBody", () => { responseEvent("@catrd:example.com", "poutine"), responseEvent("@dune2:example.com", "wings"), ]; - const body = newMPollBody(votes, [], undefined, false); - expect(votesCount(body, "pizza")).toBe(""); - expect(votesCount(body, "poutine")).toBe(""); - expect(votesCount(body, "italian")).toBe(""); - expect(votesCount(body, "wings")).toBe(""); - expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Results will be visible when the poll is ended"); + const renderResult = newMPollBody(votes, [], undefined, false); + expect(votesCount(renderResult, "pizza")).toBe(""); + expect(votesCount(renderResult, "poutine")).toBe(""); + expect(votesCount(renderResult, "italian")).toBe(""); + expect(votesCount(renderResult, "wings")).toBe(""); + expect(renderResult.getByTestId("totalVotes").innerHTML).toBe("Results will be visible when the poll is ended"); }); it("highlights my vote if the poll is undisclosed", () => { @@ -456,13 +470,13 @@ describe("MPollBody", () => { responseEvent("@catrd:example.com", "poutine"), responseEvent("@dune2:example.com", "wings"), ]; - const body = newMPollBody(votes, [], undefined, false); + const { container } = newMPollBody(votes, [], undefined, false); // My vote is marked - expect(body.find('input[value="pizza"]').prop("checked")).toBeTruthy(); + expect(container.querySelector('input[value="pizza"]')!).toBeChecked(); // Sanity: other items are not checked - expect(body.find('input[value="poutine"]').prop("checked")).toBeFalsy(); + expect(container.querySelector('input[value="poutine"]')!).not.toBeChecked(); }); it("shows scores if the poll is undisclosed but ended", () => { @@ -474,47 +488,47 @@ describe("MPollBody", () => { responseEvent("@dune2:example.com", "wings"), ]; const ends = [endEvent("@me:example.com", 12)]; - const body = newMPollBody(votes, ends, undefined, false); - expect(endedVotesCount(body, "pizza")).toBe("3 votes"); - expect(endedVotesCount(body, "poutine")).toBe("1 vote"); - expect(endedVotesCount(body, "italian")).toBe("0 votes"); - expect(endedVotesCount(body, "wings")).toBe("1 vote"); - expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Final result based on 5 votes"); + const renderResult = newMPollBody(votes, ends, undefined, false); + expect(endedVotesCount(renderResult, "pizza")).toBe("3 votes"); + expect(endedVotesCount(renderResult, "poutine")).toBe("1 vote"); + expect(endedVotesCount(renderResult, "italian")).toBe("0 votes"); + expect(endedVotesCount(renderResult, "wings")).toBe("1 vote"); + expect(renderResult.getByTestId("totalVotes").innerHTML).toBe("Final result based on 5 votes"); }); it("sends a vote event when I choose an option", () => { - const votes = []; - const body = newMPollBody(votes); - clickRadio(body, "wings"); + const votes: MatrixEvent[] = []; + const renderResult = newMPollBody(votes); + clickOption(renderResult, "wings"); expect(mockClient.sendEvent).toHaveBeenCalledWith(...expectedResponseEventCall("wings")); }); it("sends only one vote event when I click several times", () => { - const votes = []; - const body = newMPollBody(votes); - clickRadio(body, "wings"); - clickRadio(body, "wings"); - clickRadio(body, "wings"); - clickRadio(body, "wings"); + const votes: MatrixEvent[] = []; + const renderResult = newMPollBody(votes); + clickOption(renderResult, "wings"); + clickOption(renderResult, "wings"); + clickOption(renderResult, "wings"); + clickOption(renderResult, "wings"); expect(mockClient.sendEvent).toHaveBeenCalledWith(...expectedResponseEventCall("wings")); }); it("sends no vote event when I click what I already chose", () => { const votes = [responseEvent("@me:example.com", "wings")]; - const body = newMPollBody(votes); - clickRadio(body, "wings"); - clickRadio(body, "wings"); - clickRadio(body, "wings"); - clickRadio(body, "wings"); + const renderResult = newMPollBody(votes); + clickOption(renderResult, "wings"); + clickOption(renderResult, "wings"); + clickOption(renderResult, "wings"); + clickOption(renderResult, "wings"); expect(mockClient.sendEvent).not.toHaveBeenCalled(); }); it("sends several events when I click different options", () => { - const votes = []; - const body = newMPollBody(votes); - clickRadio(body, "wings"); - clickRadio(body, "italian"); - clickRadio(body, "poutine"); + const votes: MatrixEvent[] = []; + const renderResult = newMPollBody(votes); + clickOption(renderResult, "wings"); + clickOption(renderResult, "italian"); + clickOption(renderResult, "poutine"); expect(mockClient.sendEvent).toHaveBeenCalledTimes(3); expect(mockClient.sendEvent).toHaveBeenCalledWith(...expectedResponseEventCall("wings")); expect(mockClient.sendEvent).toHaveBeenCalledWith(...expectedResponseEventCall("italian")); @@ -524,10 +538,10 @@ describe("MPollBody", () => { it("sends no events when I click in an ended poll", () => { const ends = [endEvent("@me:example.com", 25)]; const votes = [responseEvent("@uy:example.com", "wings", 15), responseEvent("@uy:example.com", "poutine", 15)]; - const body = newMPollBody(votes, ends); - clickEndedOption(body, "wings"); - clickEndedOption(body, "italian"); - clickEndedOption(body, "poutine"); + const renderResult = newMPollBody(votes, ends); + clickOption(renderResult, "wings"); + clickOption(renderResult, "italian"); + clickOption(renderResult, "poutine"); expect(mockClient.sendEvent).not.toHaveBeenCalled(); }); @@ -577,9 +591,9 @@ describe("MPollBody", () => { it("shows non-radio buttons if the poll is ended", () => { const events = [endEvent()]; - const body = newMPollBody([], events); - expect(body.find(".mx_StyledRadioButton")).toHaveLength(0); - expect(body.find('input[type="radio"]')).toHaveLength(0); + const { container } = newMPollBody([], events); + expect(container.querySelector(".mx_StyledRadioButton")).not.toBeInTheDocument(); + expect(container.querySelector('input[type="radio"]')).not.toBeInTheDocument(); }); it("counts votes as normal if the poll is ended", () => { @@ -591,23 +605,23 @@ describe("MPollBody", () => { responseEvent("@qbert:example.com", "wings", 15), ]; const ends = [endEvent("@me:example.com", 25)]; - const body = newMPollBody(votes, ends); - expect(endedVotesCount(body, "pizza")).toBe("0 votes"); - expect(endedVotesCount(body, "poutine")).toBe("1 vote"); - expect(endedVotesCount(body, "italian")).toBe("0 votes"); - expect(endedVotesCount(body, "wings")).toBe("1 vote"); - expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Final result based on 2 votes"); + const renderResult = newMPollBody(votes, ends); + expect(endedVotesCount(renderResult, "pizza")).toBe("0 votes"); + expect(endedVotesCount(renderResult, "poutine")).toBe("1 vote"); + expect(endedVotesCount(renderResult, "italian")).toBe("0 votes"); + expect(endedVotesCount(renderResult, "wings")).toBe("1 vote"); + expect(renderResult.getByTestId("totalVotes").innerHTML).toBe("Final result based on 2 votes"); }); it("counts a single vote as normal if the poll is ended", () => { const votes = [responseEvent("@qbert:example.com", "poutine", 16)]; const ends = [endEvent("@me:example.com", 25)]; - const body = newMPollBody(votes, ends); - expect(endedVotesCount(body, "pizza")).toBe("0 votes"); - expect(endedVotesCount(body, "poutine")).toBe("1 vote"); - expect(endedVotesCount(body, "italian")).toBe("0 votes"); - expect(endedVotesCount(body, "wings")).toBe("0 votes"); - expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Final result based on 1 vote"); + const renderResult = newMPollBody(votes, ends); + expect(endedVotesCount(renderResult, "pizza")).toBe("0 votes"); + expect(endedVotesCount(renderResult, "poutine")).toBe("1 vote"); + expect(endedVotesCount(renderResult, "italian")).toBe("0 votes"); + expect(endedVotesCount(renderResult, "wings")).toBe("0 votes"); + expect(renderResult.getByTestId("totalVotes").innerHTML).toBe("Final result based on 1 vote"); }); it("shows ended vote counts of different numbers", () => { @@ -619,15 +633,15 @@ describe("MPollBody", () => { responseEvent("@hi:example.com", "pizza", 15), ]; const ends = [endEvent("@me:example.com", 25)]; - const body = newMPollBody(votes, ends); + const renderResult = newMPollBody(votes, ends); - expect(body.find(".mx_StyledRadioButton")).toHaveLength(0); - expect(body.find('input[type="radio"]')).toHaveLength(0); - expect(endedVotesCount(body, "pizza")).toBe("2 votes"); - expect(endedVotesCount(body, "poutine")).toBe("0 votes"); - expect(endedVotesCount(body, "italian")).toBe("0 votes"); - expect(endedVotesCount(body, "wings")).toBe("3 votes"); - expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Final result based on 5 votes"); + expect(renderResult.container.querySelectorAll(".mx_StyledRadioButton")).toHaveLength(0); + expect(renderResult.container.querySelectorAll('input[type="radio"]')).toHaveLength(0); + expect(endedVotesCount(renderResult, "pizza")).toBe("2 votes"); + expect(endedVotesCount(renderResult, "poutine")).toBe("0 votes"); + expect(endedVotesCount(renderResult, "italian")).toBe("0 votes"); + expect(endedVotesCount(renderResult, "wings")).toBe("3 votes"); + expect(renderResult.getByTestId("totalVotes").innerHTML).toBe("Final result based on 5 votes"); }); it("ignores votes that arrived after poll ended", () => { @@ -641,13 +655,13 @@ describe("MPollBody", () => { responseEvent("@ld:example.com", "pizza", 15), ]; const ends = [endEvent("@me:example.com", 25)]; - const body = newMPollBody(votes, ends); + const renderResult = newMPollBody(votes, ends); - expect(endedVotesCount(body, "pizza")).toBe("2 votes"); - expect(endedVotesCount(body, "poutine")).toBe("0 votes"); - expect(endedVotesCount(body, "italian")).toBe("0 votes"); - expect(endedVotesCount(body, "wings")).toBe("3 votes"); - expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Final result based on 5 votes"); + expect(endedVotesCount(renderResult, "pizza")).toBe("2 votes"); + expect(endedVotesCount(renderResult, "poutine")).toBe("0 votes"); + expect(endedVotesCount(renderResult, "italian")).toBe("0 votes"); + expect(endedVotesCount(renderResult, "wings")).toBe("3 votes"); + expect(renderResult.getByTestId("totalVotes").innerHTML).toBe("Final result based on 5 votes"); }); it("counts votes that arrived after an unauthorised poll end event", () => { @@ -664,13 +678,13 @@ describe("MPollBody", () => { endEvent("@unauthorised:example.com", 5), // Should be ignored endEvent("@me:example.com", 25), ]; - const body = newMPollBody(votes, ends); + const renderResult = newMPollBody(votes, ends); - expect(endedVotesCount(body, "pizza")).toBe("2 votes"); - expect(endedVotesCount(body, "poutine")).toBe("0 votes"); - expect(endedVotesCount(body, "italian")).toBe("0 votes"); - expect(endedVotesCount(body, "wings")).toBe("3 votes"); - expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Final result based on 5 votes"); + expect(endedVotesCount(renderResult, "pizza")).toBe("2 votes"); + expect(endedVotesCount(renderResult, "poutine")).toBe("0 votes"); + expect(endedVotesCount(renderResult, "italian")).toBe("0 votes"); + expect(endedVotesCount(renderResult, "wings")).toBe("3 votes"); + expect(renderResult.getByTestId("totalVotes").innerHTML).toBe("Final result based on 5 votes"); }); it("ignores votes that arrived after the first end poll event", () => { @@ -691,13 +705,13 @@ describe("MPollBody", () => { endEvent("@me:example.com", 25), endEvent("@me:example.com", 75), ]; - const body = newMPollBody(votes, ends); + const renderResult = newMPollBody(votes, ends); - expect(endedVotesCount(body, "pizza")).toBe("2 votes"); - expect(endedVotesCount(body, "poutine")).toBe("0 votes"); - expect(endedVotesCount(body, "italian")).toBe("0 votes"); - expect(endedVotesCount(body, "wings")).toBe("3 votes"); - expect(body.find(".mx_MPollBody_totalVotes").text()).toBe("Final result based on 5 votes"); + expect(endedVotesCount(renderResult, "pizza")).toBe("2 votes"); + expect(endedVotesCount(renderResult, "poutine")).toBe("0 votes"); + expect(endedVotesCount(renderResult, "italian")).toBe("0 votes"); + expect(endedVotesCount(renderResult, "wings")).toBe("3 votes"); + expect(renderResult.getByTestId("totalVotes").innerHTML).toBe("Final result based on 5 votes"); }); it("highlights the winning vote in an ended poll", () => { @@ -708,15 +722,15 @@ describe("MPollBody", () => { responseEvent("@xy:example.com", "wings", 15), ]; const ends = [endEvent("@me:example.com", 25)]; - const body = newMPollBody(votes, ends); + const renderResult = newMPollBody(votes, ends); // Then the winner is highlighted - expect(endedVoteChecked(body, "wings")).toBe(true); - expect(endedVoteChecked(body, "pizza")).toBe(false); + expect(endedVoteChecked(renderResult, "wings")).toBe(true); + expect(endedVoteChecked(renderResult, "pizza")).toBe(false); // Double-check by looking for the endedOptionWinner class - expect(endedVoteDiv(body, "wings").hasClass("mx_MPollBody_endedOptionWinner")).toBe(true); - expect(endedVoteDiv(body, "pizza").hasClass("mx_MPollBody_endedOptionWinner")).toBe(false); + expect(endedVoteDiv(renderResult, "wings").className.includes("mx_MPollBody_endedOptionWinner")).toBe(true); + expect(endedVoteDiv(renderResult, "pizza").className.includes("mx_MPollBody_endedOptionWinner")).toBe(false); }); it("highlights multiple winning votes", () => { @@ -726,23 +740,23 @@ describe("MPollBody", () => { responseEvent("@fg:example.com", "poutine", 15), ]; const ends = [endEvent("@me:example.com", 25)]; - const body = newMPollBody(votes, ends); + const renderResult = newMPollBody(votes, ends); - expect(endedVoteChecked(body, "pizza")).toBe(true); - expect(endedVoteChecked(body, "wings")).toBe(true); - expect(endedVoteChecked(body, "poutine")).toBe(true); - expect(endedVoteChecked(body, "italian")).toBe(false); - expect(body.find(".mx_MPollBody_option_checked")).toHaveLength(3); + expect(endedVoteChecked(renderResult, "pizza")).toBe(true); + expect(endedVoteChecked(renderResult, "wings")).toBe(true); + expect(endedVoteChecked(renderResult, "poutine")).toBe(true); + expect(endedVoteChecked(renderResult, "italian")).toBe(false); + expect(renderResult.container.getElementsByClassName("mx_MPollBody_option_checked")).toHaveLength(3); }); it("highlights nothing if poll has no votes", () => { const ends = [endEvent("@me:example.com", 25)]; - const body = newMPollBody([], ends); - expect(body.find(".mx_MPollBody_option_checked")).toHaveLength(0); + const renderResult = newMPollBody([], ends); + expect(renderResult.container.getElementsByClassName("mx_MPollBody_option_checked")).toHaveLength(0); }); it("says poll is not ended if there is no end event", () => { - const ends = []; + const ends: MatrixEvent[] = []; expect(runIsPollEnded(ends)).toBe(false); }); @@ -810,26 +824,26 @@ describe("MPollBody", () => { }, }); pollEvent.makeReplaced(replacingEvent); - const body = newMPollBodyFromEvent(pollEvent, []); - expect(body.find("h2").html()).toEqual( - "

new question" + ' (edited)' + "

", + const { getByTestId, container } = newMPollBodyFromEvent(pollEvent, []); + expect(getByTestId("pollQuestion").innerHTML).toEqual( + 'new question (edited)', ); - const inputs = body.find('input[type="radio"]'); + const inputs = container.querySelectorAll('input[type="radio"]'); expect(inputs).toHaveLength(3); - expect(inputs.at(0).prop("value")).toEqual("n1"); - expect(inputs.at(1).prop("value")).toEqual("n2"); - expect(inputs.at(2).prop("value")).toEqual("n3"); - const options = body.find(".mx_MPollBody_optionText"); + expect(inputs[0].getAttribute("value")).toEqual("n1"); + expect(inputs[1].getAttribute("value")).toEqual("n2"); + expect(inputs[2].getAttribute("value")).toEqual("n3"); + const options = container.querySelectorAll(".mx_MPollBody_optionText"); expect(options).toHaveLength(3); - expect(options.at(0).text()).toEqual("new answer 1"); - expect(options.at(1).text()).toEqual("new answer 2"); - expect(options.at(2).text()).toEqual("new answer 3"); + expect(options[0].innerHTML).toEqual("new answer 1"); + expect(options[1].innerHTML).toEqual("new answer 2"); + expect(options[2].innerHTML).toEqual("new answer 3"); }); it("renders a poll with no votes", () => { - const votes = []; - const body = newMPollBody(votes); - expect(body).toMatchSnapshot(); + const votes: MatrixEvent[] = []; + const { container } = newMPollBody(votes); + expect(container).toMatchSnapshot(); }); it("renders a poll with only non-local votes", () => { @@ -840,8 +854,8 @@ describe("MPollBody", () => { responseEvent("@me:example.com", "wings", 15), responseEvent("@qr:example.com", "italian", 16), ]; - const body = newMPollBody(votes); - expect(body).toMatchSnapshot(); + const { container } = newMPollBody(votes); + expect(container).toMatchSnapshot(); }); it("renders a poll with local, non-local and invalid votes", () => { @@ -853,9 +867,9 @@ describe("MPollBody", () => { responseEvent("@e:example.com", "wings", 15), responseEvent("@me:example.com", "italian", 16), ]; - const body = newMPollBody(votes); - clickRadio(body, "italian"); - expect(body).toMatchSnapshot(); + const renderResult = newMPollBody(votes); + clickOption(renderResult, "italian"); + expect(renderResult.container).toMatchSnapshot(); }); it("renders a poll that I have not voted in", () => { @@ -866,14 +880,14 @@ describe("MPollBody", () => { responseEvent("@yo:example.com", "wings", 15), responseEvent("@qr:example.com", "italian", 16), ]; - const body = newMPollBody(votes); - expect(body).toMatchSnapshot(); + const { container } = newMPollBody(votes); + expect(container).toMatchSnapshot(); }); it("renders a finished poll with no votes", () => { const ends = [endEvent("@me:example.com", 25)]; - const body = newMPollBody([], ends); - expect(body).toMatchSnapshot(); + const { container } = newMPollBody([], ends); + expect(container).toMatchSnapshot(); }); it("renders a finished poll", () => { @@ -885,8 +899,8 @@ describe("MPollBody", () => { responseEvent("@qr:example.com", "italian", 16), ]; const ends = [endEvent("@me:example.com", 25)]; - const body = newMPollBody(votes, ends); - expect(body).toMatchSnapshot(); + const { container } = newMPollBody(votes, ends); + expect(container).toMatchSnapshot(); }); it("renders a finished poll with multiple winners", () => { @@ -899,8 +913,8 @@ describe("MPollBody", () => { responseEvent("@yh:example.com", "poutine", 14), ]; const ends = [endEvent("@me:example.com", 25)]; - const body = newMPollBody(votes, ends); - expect(body).toMatchSnapshot(); + const { container } = newMPollBody(votes, ends); + expect(container).toMatchSnapshot(); }); it("renders an undisclosed, unfinished poll", () => { @@ -912,9 +926,9 @@ describe("MPollBody", () => { responseEvent("@th:example.com", "poutine", 13), responseEvent("@yh:example.com", "poutine", 14), ]; - const ends = []; - const body = newMPollBody(votes, ends, undefined, false); - expect(body.html()).toMatchSnapshot(); + const ends: MatrixEvent[] = []; + const { container } = newMPollBody(votes, ends, undefined, false); + expect(container).toMatchSnapshot(); }); it("renders an undisclosed, finished poll", () => { @@ -927,8 +941,8 @@ describe("MPollBody", () => { responseEvent("@yh:example.com", "poutine", 14), ]; const ends = [endEvent("@me:example.com", 25)]; - const body = newMPollBody(votes, ends, undefined, false); - expect(body.html()).toMatchSnapshot(); + const { container } = newMPollBody(votes, ends, undefined, false); + expect(container).toMatchSnapshot(); }); }); @@ -941,7 +955,7 @@ function newEndRelations(relationEvents: Array): Relations { } function newRelations(relationEvents: Array, eventType: string): Relations { - const voteRelations = new Relations("m.reference", eventType, null); + const voteRelations = new Relations("m.reference", eventType, mockClient); for (const ev of relationEvents) { voteRelations.addEvent(ev); } @@ -953,84 +967,88 @@ function newMPollBody( endEvents: Array = [], answers?: PollAnswer[], disclosed = true, -): ReactWrapper { +): RenderResult { const mxEvent = new MatrixEvent({ type: M_POLL_START.name, event_id: "$mypoll", room_id: "#myroom:example.com", - content: newPollStart(answers, null, disclosed), + content: newPollStart(answers, undefined, disclosed), }); return newMPollBodyFromEvent(mxEvent, relationEvents, endEvents); } +function getMPollBodyPropsFromEvent( + mxEvent: MatrixEvent, + relationEvents: Array, + endEvents: Array = [], +): IBodyProps { + const voteRelations = newVoteRelations(relationEvents); + const endRelations = newEndRelations(endEvents); + + const getRelationsForEvent = (eventId: string, relationType: string, eventType: string) => { + expect(eventId).toBe("$mypoll"); + expect(relationType).toBe("m.reference"); + if (M_POLL_RESPONSE.matches(eventType)) { + return voteRelations; + } else if (M_POLL_END.matches(eventType)) { + return endRelations; + } else { + fail("Unexpected eventType: " + eventType); + } + }; + + return { + mxEvent, + getRelationsForEvent, + // We don't use any of these props, but they're required. + highlightLink: "unused", + highlights: [], + mediaEventHelper: {} as unknown as MediaEventHelper, + onHeightChanged: () => {}, + onMessageAllowed: () => {}, + permalinkCreator: {} as unknown as RoomPermalinkCreator, + }; +} + +function renderMPollBodyWithWrapper(props: IBodyProps): RenderResult { + return render(, { + wrapper: ({ children }) => ( + {children} + ), + }); +} + function newMPollBodyFromEvent( mxEvent: MatrixEvent, relationEvents: Array, endEvents: Array = [], -): ReactWrapper { - const voteRelations = newVoteRelations(relationEvents); - const endRelations = newEndRelations(endEvents); - return mount( - { - expect(eventId).toBe("$mypoll"); - expect(relationType).toBe("m.reference"); - if (M_POLL_RESPONSE.matches(eventType)) { - return voteRelations; - } else if (M_POLL_END.matches(eventType)) { - return endRelations; - } else { - fail("Unexpected eventType: " + eventType); - } - }} - // We don't use any of these props, but they're required. - highlightLink="unused" - highlights={[]} - mediaEventHelper={null} - onHeightChanged={() => {}} - onMessageAllowed={() => {}} - permalinkCreator={null} - />, - { - wrappingComponent: MatrixClientContext.Provider, - wrappingComponentProps: { - value: mockClient, - }, - }, - ); +): RenderResult { + const props = getMPollBodyPropsFromEvent(mxEvent, relationEvents, endEvents); + return renderMPollBodyWithWrapper(props); } -function clickRadio(wrapper: ReactWrapper, value: string) { - const div = wrapper.find(`StyledRadioButton[value="${value}"]`); - expect(div).toHaveLength(1); - div.simulate("click"); +function clickOption({ getByTestId }: RenderResult, value: string) { + fireEvent.click(getByTestId(`pollOption-${value}`)); } -function clickEndedOption(wrapper: ReactWrapper, value: string) { - const div = wrapper.find(`div[data-value="${value}"]`); - expect(div).toHaveLength(1); - div.simulate("click"); +function voteButton({ getByTestId }: RenderResult, value: string): Element { + return getByTestId(`pollOption-${value}`); } -function voteButton(wrapper: ReactWrapper, value: string): ReactWrapper { - return wrapper.find(`div.mx_MPollBody_option`).findWhere((w) => w.key() === value); +function votesCount({ getByTestId }: RenderResult, value: string): string { + return getByTestId(`pollOption-${value}`).querySelector(".mx_MPollBody_optionVoteCount")!.innerHTML; } -function votesCount(wrapper: ReactWrapper, value: string): string { - return wrapper.find(`StyledRadioButton[value="${value}"] .mx_MPollBody_optionVoteCount`).text(); +function endedVoteChecked({ getByTestId }: RenderResult, value: string): boolean { + return getByTestId(`pollOption-${value}`).className.includes("mx_MPollBody_option_checked"); } -function endedVoteChecked(wrapper: ReactWrapper, value: string): boolean { - return endedVoteDiv(wrapper, value).closest(".mx_MPollBody_option").hasClass("mx_MPollBody_option_checked"); +function endedVoteDiv({ getByTestId }: RenderResult, value: string): Element { + return getByTestId(`pollOption-${value}`).firstElementChild!; } -function endedVoteDiv(wrapper: ReactWrapper, value: string): ReactWrapper { - return wrapper.find(`div[data-value="${value}"]`); -} - -function endedVotesCount(wrapper: ReactWrapper, value: string): string { - return wrapper.find(`div[data-value="${value}"] .mx_MPollBody_optionVoteCount`).text(); +function endedVotesCount(renderResult: RenderResult, value: string): string { + return votesCount(renderResult, value); } function newPollStart(answers?: PollAnswer[], question?: string, disclosed = true): PollStartEventContent { diff --git a/test/components/views/messages/__snapshots__/MPollBody-test.tsx.snap b/test/components/views/messages/__snapshots__/MPollBody-test.tsx.snap index e4039b82b6..2263527148 100644 --- a/test/components/views/messages/__snapshots__/MPollBody-test.tsx.snap +++ b/test/components/views/messages/__snapshots__/MPollBody-test.tsx.snap @@ -1,2626 +1,1627 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`MPollBody renders a finished poll 1`] = ` - +
-

+

What should we order for the party?

-
+ Pizza +
+
+ 0 votes +
+
+
+
+
+
+
+
+
+
+
+ Poutine +
+
+ 0 votes +
+
+
+
+
+
+
+
+
+
+
+ Italian +
+
+ 2 votes +
+
+
+
+
+
+
+
+
+
+
+ Wings +
+
+ 1 vote +
+
+
+
+
+
+
+
+
+ Final result based on 3 votes +
+
+
+`; + +exports[`MPollBody renders a finished poll with multiple winners 1`] = ` +
+
+

+ What should we order for the party? +

+
+
+
+
+
+ Pizza +
+
+ 2 votes +
+
+
+
+
+
+
+
+
+
+
+ Poutine +
+
+ 0 votes +
+
+
+
+
+
+
+
+
+
+
+ Italian +
+
+ 0 votes +
+
+
+
+
+
+
+
+
+
+
+ Wings +
+
+ 2 votes +
+
+
+
+
+
+
+
+
+ Final result based on 4 votes +
+
+
+`; + +exports[`MPollBody renders a finished poll with no votes 1`] = ` +
+
+

+ What should we order for the party? +

+
+
+
+
+
+ Pizza +
+
+ 0 votes +
+
+
+
+
+
+
+
+
+
+
+ Poutine +
+
+ 0 votes +
+
+
+
+
+
+
+
+
+
+
+ Italian +
+
+ 0 votes +
+
+
+
+
+
+
+
+
+
+
+ Wings +
+
+ 0 votes +
+
+
+
+
+
+
+
+
+ Final result based on 0 votes +
+
+
+`; + +exports[`MPollBody renders a poll that I have not voted in 1`] = ` +
+
+

+ What should we order for the party? +

+
+
+