mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
FEATURE: Add Ranked Choice Voting
using Instant Run-off Voting algorithm to Poll Plugin (Part 2 add Ranked Choice) --------- Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com> Co-authored-by: Jarek Radosz <jradosz@gmail.com>
This commit is contained in:
@@ -31,12 +31,16 @@ module("Poll | Component | poll-options", function (hooks) {
|
||||
test("single, not selected", async function (assert) {
|
||||
this.setProperties({
|
||||
isCheckbox: false,
|
||||
isRankedChoice: false,
|
||||
rankedChoiceDropdownContent: [],
|
||||
options: OPTIONS,
|
||||
votes: [],
|
||||
});
|
||||
|
||||
await render(hbs`<PollOptions
|
||||
@isCheckbox={{this.isCheckbox}}
|
||||
@isRankedChoice={{this.isRankedChoice}}
|
||||
@ranked_choice_dropdown_content={{this.ranked_choice_dropdown_content}}
|
||||
@options={{this.options}}
|
||||
@votes={{this.votes}}
|
||||
@sendRadioClick={{this.toggleOption}}
|
||||
@@ -48,12 +52,16 @@ module("Poll | Component | poll-options", function (hooks) {
|
||||
test("single, selected", async function (assert) {
|
||||
this.setProperties({
|
||||
isCheckbox: false,
|
||||
isRankedChoice: false,
|
||||
rankedChoiceDropdownContent: [],
|
||||
options: OPTIONS,
|
||||
votes: ["6c986ebcde3d5822a6e91a695c388094"],
|
||||
});
|
||||
|
||||
await render(hbs`<PollOptions
|
||||
@isCheckbox={{this.isCheckbox}}
|
||||
@isRankedChoice={{this.isRankedChoice}}
|
||||
@ranked_choice_dropdown_content={{this.ranked_choice_dropdown_content}}
|
||||
@options={{this.options}}
|
||||
@votes={{this.votes}}
|
||||
@sendRadioClick={{this.toggleOption}}
|
||||
@@ -65,12 +73,16 @@ module("Poll | Component | poll-options", function (hooks) {
|
||||
test("multi, not selected", async function (assert) {
|
||||
this.setProperties({
|
||||
isCheckbox: true,
|
||||
isRankedChoice: false,
|
||||
rankedChoiceDropdownContent: [],
|
||||
options: OPTIONS,
|
||||
votes: [],
|
||||
});
|
||||
|
||||
await render(hbs`<PollOptions
|
||||
@isCheckbox={{this.isCheckbox}}
|
||||
@isRankedChoice={{this.isRankedChoice}}
|
||||
@ranked_choice_dropdown_content={{this.ranked_choice_dropdown_content}}
|
||||
@options={{this.options}}
|
||||
@votes={{this.votes}}
|
||||
@sendRadioClick={{this.toggleOption}}
|
||||
@@ -82,12 +94,16 @@ module("Poll | Component | poll-options", function (hooks) {
|
||||
test("multi, selected", async function (assert) {
|
||||
this.setProperties({
|
||||
isCheckbox: true,
|
||||
isRankedChoice: false,
|
||||
rankedChoiceDropdownContent: [],
|
||||
options: OPTIONS,
|
||||
votes: ["6c986ebcde3d5822a6e91a695c388094"],
|
||||
});
|
||||
|
||||
await render(hbs`<PollOptions
|
||||
@isCheckbox={{this.isCheckbox}}
|
||||
@isRankedChoice={{this.isRankedChoice}}
|
||||
@ranked_choice_dropdown_content={{this.ranked_choice_dropdown_content}}
|
||||
@options={{this.options}}
|
||||
@votes={{this.votes}}
|
||||
@sendRadioClick={{this.toggleOption}}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import { render } from "@ember/test-helpers";
|
||||
import hbs from "htmlbars-inline-precompile";
|
||||
import { module, test } from "qunit";
|
||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
import { count, query } from "discourse/tests/helpers/qunit-helpers";
|
||||
import I18n from "I18n";
|
||||
|
||||
const RANKED_CHOICE_OUTCOME = {
|
||||
tied: false,
|
||||
tied_candidates: null,
|
||||
winner: true,
|
||||
winning_candidate: {
|
||||
digest: "c8678f4ce846ad5415278ff7ecadf3a6",
|
||||
html: "Team Blue",
|
||||
},
|
||||
round_activity: [
|
||||
{
|
||||
round: 1,
|
||||
eliminated: [
|
||||
{ digest: "8bbb100d504298ad65a2604e99d5ba82", html: "Team Yellow" },
|
||||
],
|
||||
majority: null,
|
||||
},
|
||||
{
|
||||
round: 2,
|
||||
majority: [
|
||||
{ digest: "c8678f4ce846ad5415278ff7ecadf3a6", html: "Team Blue" },
|
||||
],
|
||||
eliminated: null,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
module("Poll | Component | poll-results-ranked-choice", function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test("Renders the ranked choice results component correctly", async function (assert) {
|
||||
this.setProperties({
|
||||
rankedChoiceOutcome: RANKED_CHOICE_OUTCOME,
|
||||
});
|
||||
|
||||
await render(
|
||||
hbs`<PollResultsRankedChoice @rankedChoiceOutcome={{this.rankedChoiceOutcome}} />`
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
count("table.poll-results-ranked-choice tr"),
|
||||
3,
|
||||
"there are two rounds of ranked choice"
|
||||
);
|
||||
|
||||
assert.strictEqual(
|
||||
query("span.poll-results-ranked-choice-info").textContent.trim(),
|
||||
I18n.t("poll.ranked_choice.winner", {
|
||||
count: this.rankedChoiceOutcome.round_activity.length,
|
||||
winner: this.rankedChoiceOutcome.winning_candidate.html,
|
||||
}),
|
||||
"displays the winner information"
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,155 @@
|
||||
import { render } from "@ember/test-helpers";
|
||||
import hbs from "htmlbars-inline-precompile";
|
||||
import { module, test } from "qunit";
|
||||
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
|
||||
import { count } from "discourse/tests/helpers/qunit-helpers";
|
||||
|
||||
const TWO_OPTIONS = [
|
||||
{
|
||||
id: "1ddc47be0d2315b9711ee8526ca9d83f",
|
||||
html: "Team Yellow",
|
||||
votes: 5,
|
||||
rank: 2,
|
||||
},
|
||||
{
|
||||
id: "70e743697dac09483d7b824eaadb91e1",
|
||||
html: "Team Blue",
|
||||
votes: 4,
|
||||
rank: 1,
|
||||
},
|
||||
];
|
||||
|
||||
const RANKED_CHOICE_OUTCOME = {
|
||||
tied: false,
|
||||
tied_candidates: null,
|
||||
winner: true,
|
||||
winning_candidate: {
|
||||
digest: "70e743697dac09483d7b824eaadb91e1",
|
||||
html: "Team Blue",
|
||||
},
|
||||
round_activity: [
|
||||
{
|
||||
round: 1,
|
||||
eliminated: [
|
||||
{ digest: "1ddc47be0d2315b9711ee8526ca9d83f", html: "Team Yellow" },
|
||||
],
|
||||
majority: null,
|
||||
},
|
||||
{
|
||||
round: 2,
|
||||
majority: [
|
||||
{ digest: "70e743697dac09483d7b824eaadb91e1", html: "Team Blue" },
|
||||
],
|
||||
eliminated: null,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const PRELOADEDVOTERS = {
|
||||
db753fe0bc4e72869ac1ad8765341764: [
|
||||
{
|
||||
id: 1,
|
||||
username: "bianca",
|
||||
name: null,
|
||||
avatar_template: "/letter_avatar_proxy/v4/letter/b/3be4f8/{size}.png",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
module("Poll | Component | poll-results-tabs", function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test("Renders one tab for non-ranked-choice poll", async function (assert) {
|
||||
this.setProperties({
|
||||
options: TWO_OPTIONS,
|
||||
pollName: "Two Choice Poll",
|
||||
pollType: "single",
|
||||
isPublic: true,
|
||||
isRankedChoice: false,
|
||||
postId: 123,
|
||||
vote: ["1ddc47be0d2315b9711ee8526ca9d83f"],
|
||||
voters: PRELOADEDVOTERS,
|
||||
votersCount: 9,
|
||||
fetchVoters: () => {},
|
||||
});
|
||||
|
||||
await render(hbs`<PollResultsTabs
|
||||
@options={{this.options}}
|
||||
@pollName={{this.pollName}}
|
||||
@pollType={{this.pollType}}
|
||||
@isPublic={{this.isPublic}}
|
||||
@isRankedChoice={{this.isRankedChoice}}
|
||||
@postId={{this.postId}}
|
||||
@vote={{this.vote}}
|
||||
@voters={{this.voters}}
|
||||
@votersCount={{this.votersCount}}
|
||||
@fetchVoters={{this.fetchVoters}}
|
||||
/>`);
|
||||
|
||||
assert.strictEqual(count("li.tab"), 1);
|
||||
});
|
||||
|
||||
test("Renders two tabs for public ranked choice poll", async function (assert) {
|
||||
this.setProperties({
|
||||
options: TWO_OPTIONS,
|
||||
pollName: "Two Choice Poll",
|
||||
pollType: "ranked_choice",
|
||||
isPublic: true,
|
||||
isRankedChoice: true,
|
||||
rankedChoiceOutcome: RANKED_CHOICE_OUTCOME,
|
||||
postId: 123,
|
||||
vote: ["1ddc47be0d2315b9711ee8526ca9d83f"],
|
||||
voters: PRELOADEDVOTERS,
|
||||
votersCount: 9,
|
||||
fetchVoters: () => {},
|
||||
});
|
||||
|
||||
await render(hbs`<PollResultsTabs
|
||||
@options={{this.options}}
|
||||
@pollName={{this.pollName}}
|
||||
@pollType={{this.pollType}}
|
||||
@isPublic={{this.isPublic}}
|
||||
@isRankedChoice={{this.isRankedChoice}}
|
||||
@rankedChoiceOutcome={{this.rankedChoiceOutcome}}
|
||||
@postId={{this.postId}}
|
||||
@vote={{this.vote}}
|
||||
@voters={{this.voters}}
|
||||
@votersCount={{this.votersCount}}
|
||||
@fetchVoters={{this.fetchVoters}}
|
||||
/>`);
|
||||
|
||||
assert.strictEqual(count("li.tab"), 2);
|
||||
});
|
||||
|
||||
test("Renders one tab for private ranked choice poll", async function (assert) {
|
||||
this.setProperties({
|
||||
options: TWO_OPTIONS,
|
||||
pollName: "Two Choice Poll",
|
||||
pollType: "ranked_choice",
|
||||
isPublic: false,
|
||||
isRankedChoice: true,
|
||||
rankedChoiceOutcome: RANKED_CHOICE_OUTCOME,
|
||||
postId: 123,
|
||||
vote: ["1ddc47be0d2315b9711ee8526ca9d83f"],
|
||||
voters: PRELOADEDVOTERS,
|
||||
votersCount: 9,
|
||||
fetchVoters: () => {},
|
||||
});
|
||||
|
||||
await render(hbs`<PollResultsTabs
|
||||
@options={{this.options}}
|
||||
@pollName={{this.pollName}}
|
||||
@pollType={{this.pollType}}
|
||||
@isPublic={{this.isPublic}}
|
||||
@isRankedChoice={{this.isRankedChoice}}
|
||||
@rankedChoiceOutcome={{this.rankedChoiceOutcome}}
|
||||
@postId={{this.postId}}
|
||||
@vote={{this.vote}}
|
||||
@voters={{this.voters}}
|
||||
@votersCount={{this.votersCount}}
|
||||
@fetchVoters={{this.fetchVoters}}
|
||||
/>`);
|
||||
|
||||
assert.strictEqual(count("li.tab"), 1);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user