DEV: Remove widget wrapper from poll plugin (#28666)

- Uses `helper.renderGlimmer` with GJS to render the `<Poll` component without any widgets

- Moves some logic into component, so that only `@post`, `@poll` and `@titleHTML` need to be passed into the component (no more 'attrs')

- Updates `modifyClass` calls to modern syntax

- Replaces observer in `Post` model with a native setter & tracked property

- Replaced Poll EmberObject instances with TrackedObject

- Updated component tests with new arguments

- Updated some tests to qunit-dom

- Fixed up core `repliesBelow` and `repliesAbove` logic to create post models properly. Previously it was passing 'transformed' versions of posts into the model, which doesn't make sense.
This commit is contained in:
David Taylor 2024-09-04 09:38:22 +01:00 committed by GitHub
parent a2f625a0ef
commit e6edd52047
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 393 additions and 503 deletions

View File

@ -618,16 +618,8 @@ createWidget("post-contents", {
this.state.repliesBelow = posts.map((p) => {
let result = transformWithCallbacks(p);
// these would conflict with computed properties with identical names
// in the post model if we kept them.
delete result.new_user;
delete result.deleted;
delete result.shareUrl;
delete result.firstPost;
delete result.usernameUrl;
result.customShare = `${topicUrl}/${p.post_number}`;
result.asPost = this.store.createRecord("post", result);
result.asPost = this.store.createRecord("post", p);
return result;
});
});
@ -882,18 +874,8 @@ createWidget("post-article", {
this.state.repliesAbove = posts.map((p) => {
let result = transformWithCallbacks(p);
// We don't want to overwrite CPs - we are doing something a bit weird
// here by creating a post object from a transformed post. They aren't
// 100% the same.
delete result.new_user;
delete result.deleted;
delete result.shareUrl;
delete result.firstPost;
delete result.usernameUrl;
delete result.topicNotificationLevel;
result.customShare = `${topicUrl}/${p.post_number}`;
result.asPost = this.store.createRecord("post", result);
result.asPost = this.store.createRecord("post", p);
return result;
});
});

View File

@ -11,6 +11,7 @@ import I18n from "discourse-i18n";
export default class PollBreakdownModal extends Component {
@service dialog;
@service siteSettings;
model = null;
charts = null;
@ -19,7 +20,7 @@ export default class PollBreakdownModal extends Component {
displayMode = "percentage";
init() {
this.set("groupedBy", this.model.groupableUserFields[0]);
this.set("groupedBy", this.groupableUserFields[0]?.id);
loadScript("/javascripts/Chart.min.js")
.then(() => loadScript("/javascripts/chartjs-plugin-datalabels.min.js"))
.then(() => {
@ -33,17 +34,19 @@ export default class PollBreakdownModal extends Component {
return pollTitle ? htmlSafe(pollTitle) : topicTitle;
}
@discourseComputed("model.groupableUserFields")
groupableUserFields(fields) {
return fields.map((field) => {
const transformed = field.split("_").filter(Boolean);
get groupableUserFields() {
return this.siteSettings.poll_groupable_user_fields
.split("|")
.filter(Boolean)
.map((field) => {
const transformed = field.split("_").filter(Boolean);
if (transformed.length > 1) {
transformed[0] = classify(transformed[0]);
}
if (transformed.length > 1) {
transformed[0] = classify(transformed[0]);
}
return { id: field, label: transformed.join(" ") };
});
return { id: field, label: transformed.join(" ") };
});
}
@discourseComputed("model.poll.options")

View File

@ -42,11 +42,10 @@ export default class PollComponent extends Component {
@service dialog;
@service modal;
@tracked vote = this.args.attrs.vote || [];
@tracked poll = this.args.attrs.poll;
@tracked vote = this.args.post.polls_votes?.[this.args.poll.name] || [];
@tracked preloadedVoters = this.defaultPreloadedVoters();
@tracked voterListExpanded = false;
@tracked hasSavedVote = this.args.attrs.hasSavedVote;
@tracked hasSavedVote = this.vote.length > 0;
@tracked
showResults =
@ -123,13 +122,17 @@ export default class PollComponent extends Component {
this.vote = [...this.vote];
};
get poll() {
return this.args.poll;
}
defaultPreloadedVoters() {
const preloadedVoters = {};
if (this.poll.public && this.args.preloadedVoters) {
Object.keys(this.args.preloadedVoters).forEach((key) => {
if (this.poll.public && this.poll.preloaded_voters) {
Object.keys(this.poll.preloaded_voters).forEach((key) => {
preloadedVoters[key] = {
voters: this.args.preloadedVoters[key],
voters: this.poll.preloaded_voters[key],
loading: false,
};
});
@ -148,15 +151,17 @@ export default class PollComponent extends Component {
}
get id() {
return this.args.attrs.id;
return `${this.args.poll.name}-${this.args.post.id}`;
}
get post() {
return this.args.attrs.post;
return this.args.post;
}
get groupableUserFields() {
return this.args.attrs.groupableUserFields;
return this.siteSettings.poll_groupable_user_fields
.split("|")
.filter(Boolean);
}
get isStaff() {
@ -164,7 +169,7 @@ export default class PollComponent extends Component {
}
get titleHTML() {
return htmlSafe(this.args.attrs.titleHTML);
return htmlSafe(this.args.titleHTML);
}
get topicArchived() {
@ -192,7 +197,7 @@ export default class PollComponent extends Component {
}
get status() {
return this.poll.get("status");
return this.poll.status;
}
@action
@ -206,7 +211,7 @@ export default class PollComponent extends Component {
}
try {
const poll = await ajax("/polls/vote", {
const { poll } = await ajax("/polls/vote", {
type: "PUT",
data: {
post_id: this.post.id,
@ -216,7 +221,8 @@ export default class PollComponent extends Component {
});
this.hasSavedVote = true;
this.poll.setProperties(poll);
Object.assign(this.poll, poll);
this.appEvents.trigger("poll:voted", poll, this.post, this.vote);
if (this.poll.results !== ON_CLOSE) {
@ -243,7 +249,7 @@ export default class PollComponent extends Component {
}
get options() {
let enrichedOptions = this.poll.get("options");
let enrichedOptions = this.poll.options;
if (this.isRankedChoice) {
enrichedOptions.forEach((candidate) => {
@ -262,11 +268,11 @@ export default class PollComponent extends Component {
}
get voters() {
return this.poll.get("voters");
return this.poll.voters;
}
get rankedChoiceOutcome() {
return this.poll.get("ranked_choice_outcome") || [];
return this.poll.ranked_choice_outcome || [];
}
get min() {
@ -574,7 +580,7 @@ export default class PollComponent extends Component {
},
})
.then(() => {
this.poll.set("status", status);
this.poll.status = status;
if (
this.poll.results === ON_CLOSE ||
@ -606,7 +612,10 @@ export default class PollComponent extends Component {
@action
showBreakdown() {
this.modal.show(PollBreakdownModal, {
model: this.args.attrs,
model: {
poll: this.poll,
post: this.post,
},
});
}
@ -660,121 +669,129 @@ export default class PollComponent extends Component {
}
<template>
<div
{{didUpdate this.updatedVoters @preloadedVoters}}
class="poll-container"
>
{{this.titleHTML}}
{{#if this.notInVotingGroup}}
<div class="alert alert-danger">{{this.pollGroups}}</div>
{{/if}}
{{#if this.showResults}}
<div class={{this.resultsWidgetTypeClass}}>
{{#if this.isNumber}}
<span>{{this.averageRating}}</span>
{{else}}
{{#if this.resultsPie}}
<PollResultsPie @id={{this.id}} @options={{this.options}} />
<div class="poll">
<div
{{didUpdate this.updatedVoters this.poll.preloaded_voters}}
class="poll-container"
>
{{this.titleHTML}}
{{#if this.notInVotingGroup}}
<div class="alert alert-danger">{{this.pollGroups}}</div>
{{/if}}
{{#if this.showResults}}
<div class={{this.resultsWidgetTypeClass}}>
{{#if this.isNumber}}
<span>{{this.averageRating}}</span>
{{else}}
<PollResultsTabs
@options={{this.options}}
@pollName={{this.poll.name}}
@pollType={{this.poll.type}}
@isRankedChoice={{this.isRankedChoice}}
@isPublic={{this.poll.public}}
@postId={{this.post.id}}
@vote={{this.vote}}
@voters={{this.preloadedVoters}}
@votersCount={{this.poll.voters}}
@fetchVoters={{this.fetchVoters}}
@rankedChoiceOutcome={{this.rankedChoiceOutcome}}
@showTally={{this.showTally}}
/>
{{#if this.resultsPie}}
<PollResultsPie @id={{this.id}} @options={{this.options}} />
{{else}}
<PollResultsTabs
@options={{this.options}}
@pollName={{this.poll.name}}
@pollType={{this.poll.type}}
@isRankedChoice={{this.isRankedChoice}}
@isPublic={{this.poll.public}}
@postId={{this.post.id}}
@vote={{this.vote}}
@voters={{this.preloadedVoters}}
@votersCount={{this.poll.voters}}
@fetchVoters={{this.fetchVoters}}
@rankedChoiceOutcome={{this.rankedChoiceOutcome}}
@showTally={{this.showTally}}
/>
{{/if}}
{{/if}}
{{/if}}
</div>
{{else}}
<PollOptions
@isCheckbox={{this.isCheckbox}}
@isRankedChoice={{this.isRankedChoice}}
@options={{this.options}}
@votes={{this.vote}}
@sendOptionSelect={{this.toggleOption}}
/>
{{/if}}
</div>
<PollInfo
@options={{this.options}}
@min={{this.min}}
@max={{this.max}}
@isMultiple={{this.isMultiple}}
@close={{this.close}}
@closed={{this.closed}}
@results={{this.poll.results}}
@showResults={{this.showResults}}
@postUserId={{this.poll.post.user_id}}
@isPublic={{this.poll.public}}
@hasVoted={{this.hasVoted}}
@voters={{this.voters}}
/>
<div class="poll-buttons">
{{#if this.showCastVotesButton}}
<button
class={{this.castVotesButtonClass}}
title="poll.cast-votes.title"
disabled={{this.castVotesDisabled}}
{{on "click" this.castVotes}}
>
{{icon this.castVotesButtonIcon}}
<span class="d-button-label">{{i18n "poll.cast-votes.label"}}</span>
</button>
{{/if}}
{{#if this.showHideResultsButton}}
<button
class="btn btn-default toggle-results"
title="poll.hide-results.title"
{{on "click" this.toggleResults}}
>
{{icon "chevron-left"}}
<span class="d-button-label">{{i18n "poll.hide-results.label"}}</span>
</button>
{{/if}}
{{#if this.showShowResultsButton}}
<button
class="btn btn-default toggle-results"
title="poll.show-results.title"
{{on "click" this.toggleResults}}
>
{{icon "chart-bar"}}
<span class="d-button-label">{{i18n "poll.show-results.label"}}</span>
</button>
{{/if}}
{{#if this.showRemoveVoteButton}}
<button
class="btn btn-default remove-vote"
title="poll.remove-vote.title"
{{on "click" this.removeVote}}
>
{{icon "undo"}}
<span class="d-button-label">{{i18n "poll.remove-vote.label"}}</span>
</button>
{{/if}}
<PollButtonsDropdown
</div>
{{else}}
<PollOptions
@isCheckbox={{this.isCheckbox}}
@isRankedChoice={{this.isRankedChoice}}
@options={{this.options}}
@votes={{this.vote}}
@sendOptionSelect={{this.toggleOption}}
/>
{{/if}}
</div>
<PollInfo
@options={{this.options}}
@min={{this.min}}
@max={{this.max}}
@isMultiple={{this.isMultiple}}
@close={{this.close}}
@closed={{this.closed}}
@results={{this.poll.results}}
@showResults={{this.showResults}}
@postUserId={{this.poll.post.user_id}}
@isPublic={{this.poll.public}}
@hasVoted={{this.hasVoted}}
@voters={{this.voters}}
@isStaff={{this.isStaff}}
@isMe={{this.isMe}}
@isRankedChoice={{this.isRankedChoice}}
@topicArchived={{this.topicArchived}}
@groupableUserFields={{this.groupableUserFields}}
@isAutomaticallyClosed={{this.isAutomaticallyClosed}}
@dropDownClick={{this.dropDownClick}}
@availableDisplayMode={{this.availableDisplayMode}}
/>
<div class="poll-buttons">
{{#if this.showCastVotesButton}}
<button
class={{this.castVotesButtonClass}}
title="poll.cast-votes.title"
disabled={{this.castVotesDisabled}}
{{on "click" this.castVotes}}
>
{{icon this.castVotesButtonIcon}}
<span class="d-button-label">{{i18n "poll.cast-votes.label"}}</span>
</button>
{{/if}}
{{#if this.showHideResultsButton}}
<button
class="btn btn-default toggle-results"
title="poll.hide-results.title"
{{on "click" this.toggleResults}}
>
{{icon "chevron-left"}}
<span class="d-button-label">{{i18n
"poll.hide-results.label"
}}</span>
</button>
{{/if}}
{{#if this.showShowResultsButton}}
<button
class="btn btn-default toggle-results"
title="poll.show-results.title"
{{on "click" this.toggleResults}}
>
{{icon "chart-bar"}}
<span class="d-button-label">{{i18n
"poll.show-results.label"
}}</span>
</button>
{{/if}}
{{#if this.showRemoveVoteButton}}
<button
class="btn btn-default remove-vote"
title="poll.remove-vote.title"
{{on "click" this.removeVote}}
>
{{icon "undo"}}
<span class="d-button-label">{{i18n
"poll.remove-vote.label"
}}</span>
</button>
{{/if}}
<PollButtonsDropdown
@closed={{this.closed}}
@voters={{this.voters}}
@isStaff={{this.isStaff}}
@isMe={{this.isMe}}
@isRankedChoice={{this.isRankedChoice}}
@topicArchived={{this.topicArchived}}
@groupableUserFields={{this.groupableUserFields}}
@isAutomaticallyClosed={{this.isAutomaticallyClosed}}
@dropDownClick={{this.dropDownClick}}
@availableDisplayMode={{this.availableDisplayMode}}
/>
</div>
</div>
</template>
}

View File

@ -0,0 +1,121 @@
import { tracked } from "@glimmer/tracking";
import EmberObject from "@ember/object";
import { TrackedObject } from "@ember-compat/tracked-built-ins";
import { withPluginApi } from "discourse/lib/plugin-api";
import { bind } from "discourse-common/utils/decorators";
import { PIE_CHART_TYPE } from "../components/modal/poll-ui-builder";
import Poll from "../components/poll";
function attachPolls(elem, helper) {
let pollNodes = [...elem.querySelectorAll(".poll")];
pollNodes = pollNodes.filter(
(node) => node.parentNode.tagName !== "BLOCKQUOTE"
);
if (!pollNodes.length || !helper) {
return;
}
const post = helper.getModel();
const polls = post.pollsObject;
pollNodes.forEach((pollNode) => {
const pollName = pollNode.dataset.pollName;
let poll = polls[pollName];
let pollPost = post;
const quotedId = pollNode.closest(".expanded-quote")?.dataset.postId;
if (quotedId && post.quoted[quotedId]) {
pollPost = EmberObject.create(post.quoted[quotedId]);
poll = new TrackedObject(pollPost.polls.find((p) => p.name === pollName));
}
if (poll) {
const titleHTML = pollNode.querySelector(".poll-title")?.outerHTML;
const newPollNode = document.createElement("div");
Object.assign(newPollNode.dataset, {
pollName: poll.name,
pollType: poll.type,
});
newPollNode.classList.add("poll-outer");
if (poll.chart_type === PIE_CHART_TYPE) {
newPollNode.classList.add("pie");
}
pollNode.replaceWith(newPollNode);
helper.renderGlimmer(newPollNode, <template>
<Poll @poll={{poll}} @post={{post}} @titleHTML={{titleHTML}} />
</template>);
}
});
}
function initializePolls(api) {
api.modifyClass(
"controller:topic",
(Superclass) =>
class extends Superclass {
subscribe() {
super.subscribe(...arguments);
this.messageBus.subscribe(
`/polls/${this.model.id}`,
this._onPollMessage
);
}
unsubscribe() {
this.messageBus.unsubscribe("/polls/*", this._onPollMessage);
super.unsubscribe(...arguments);
}
@bind
_onPollMessage(msg) {
const post = this.get("model.postStream").findLoadedPost(msg.post_id);
if (post) {
post.polls = msg.polls;
}
}
}
);
api.modifyClass(
"model:post",
(Superclass) =>
class extends Superclass {
@tracked pollsObject = new TrackedObject();
@tracked _polls;
get polls() {
return this._polls;
}
set polls(value) {
this._polls = value;
this._refreshPollsObject();
}
_refreshPollsObject() {
for (const rawPoll of this.polls) {
const name = rawPoll.name;
this.pollsObject[name] ||= new TrackedObject();
Object.assign(this.pollsObject[name], rawPoll);
}
}
}
);
api.decorateCookedElement(attachPolls, { onlyStream: true });
const siteSettings = api.container.lookup("service:site-settings");
if (siteSettings.poll_enabled) {
api.addSearchSuggestion("in:polls");
}
}
export default {
name: "extend-for-poll",
initialize() {
withPluginApi("0.8.7", initializePolls);
},
};

View File

@ -1,150 +0,0 @@
import EmberObject from "@ember/object";
import { withPluginApi } from "discourse/lib/plugin-api";
import WidgetGlue from "discourse/widgets/glue";
import { getRegister } from "discourse-common/lib/get-owner";
import { bind, observes } from "discourse-common/utils/decorators";
const PLUGIN_ID = "discourse-poll";
let _glued = [];
let _interval = null;
function rerender() {
_glued.forEach((g) => g.queueRerender());
}
function cleanUpPolls() {
if (_interval) {
clearInterval(_interval);
_interval = null;
}
_glued.forEach((g) => g.cleanUp());
_glued = [];
}
function initializePolls(api) {
const register = getRegister(api),
pollGroupableUserFields = api.container.lookup(
"service:site-settings"
).poll_groupable_user_fields;
cleanUpPolls();
api.modifyClass("controller:topic", {
pluginId: PLUGIN_ID,
subscribe() {
this._super(...arguments);
this.messageBus.subscribe(`/polls/${this.model.id}`, this._onPollMessage);
},
unsubscribe() {
this.messageBus.unsubscribe("/polls/*", this._onPollMessage);
this._super(...arguments);
},
@bind
_onPollMessage(msg) {
const post = this.get("model.postStream").findLoadedPost(msg.post_id);
post?.set("polls", msg.polls);
},
});
api.modifyClass("model:post", {
pluginId: PLUGIN_ID,
_polls: null,
pollsObject: null,
// we need a proper ember object so it is bindable
@observes("polls")
pollsChanged() {
const polls = this.polls;
if (polls) {
this._polls = this._polls || {};
polls.forEach((p) => {
const existing = this._polls[p.name];
if (existing) {
this._polls[p.name].setProperties(p);
} else {
this._polls[p.name] = EmberObject.create(p);
}
});
this.set("pollsObject", this._polls);
rerender();
}
},
});
function attachPolls(elem, helper) {
let pollNodes = [...elem.querySelectorAll(".poll")];
pollNodes = pollNodes.filter(
(node) => node.parentNode.tagName !== "BLOCKQUOTE"
);
if (!pollNodes.length || !helper) {
return;
}
const post = helper.getModel();
api.preventCloak(post.id);
post.pollsChanged();
const polls = post.pollsObject || {};
const votes = post.polls_votes || {};
_interval = _interval || setInterval(rerender, 30000);
pollNodes.forEach((pollNode) => {
const pollName = pollNode.dataset.pollName;
let poll = polls[pollName];
let pollPost = post;
let vote = votes[pollName] || [];
const quotedId = pollNode.closest(".expanded-quote")?.dataset.postId;
if (quotedId && post.quoted[quotedId]) {
pollPost = post.quoted[quotedId];
pollPost = EmberObject.create(pollPost);
poll = EmberObject.create(pollPost.polls.findBy("name", pollName));
vote = pollPost.polls_votes || {};
vote = vote[pollName] || [];
}
if (poll) {
const titleElement = pollNode.querySelector(".poll-title");
const attrs = {
id: `${pollName}-${pollPost.id}`,
post: pollPost,
poll,
vote,
hasSavedVote: vote.length > 0,
titleHTML: titleElement?.outerHTML,
groupableUserFields: (pollGroupableUserFields || "")
.split("|")
.filter(Boolean),
_postCookedWidget: helper.widget,
};
const glue = new WidgetGlue("discourse-poll", register, attrs);
glue.appendTo(pollNode);
_glued.push(glue);
}
});
}
api.includePostAttributes("polls", "polls_votes");
api.decorateCookedElement(attachPolls, {
onlyStream: true,
});
api.cleanupStream(cleanUpPolls);
const siteSettings = api.container.lookup("service:site-settings");
if (siteSettings.poll_enabled) {
api.addSearchSuggestion("in:polls");
}
}
export default {
name: "extend-for-poll",
initialize() {
withPluginApi("0.8.7", initializePolls);
},
};

View File

@ -1,36 +0,0 @@
import { hbs } from "ember-cli-htmlbars";
import RenderGlimmer from "discourse/widgets/render-glimmer";
import { createWidget } from "discourse/widgets/widget";
import { PIE_CHART_TYPE } from "../components/modal/poll-ui-builder";
export default createWidget("discourse-poll", {
tagName: "div",
buildKey: (attrs) => `poll-${attrs.id}`,
services: ["dialog"],
buildAttributes(attrs) {
let cssClasses = "poll-outer";
if (attrs.poll.chart_type === PIE_CHART_TYPE) {
cssClasses += " pie";
}
return {
class: cssClasses,
"data-poll-name": attrs.poll.name,
"data-poll-type": attrs.poll.type,
};
},
html(attrs) {
return [
new RenderGlimmer(
this,
"div.poll",
hbs`<Poll @attrs={{@data.attrs}} @preloadedVoters={{@data.preloadedVoters}} />`,
{
attrs,
preloadedVoters: attrs.poll.preloaded_voters,
}
),
];
},
});

View File

@ -1,15 +1,10 @@
import EmberObject from "@ember/object";
import { click, render } from "@ember/test-helpers";
import { TrackedObject } from "@ember-compat/tracked-built-ins";
import hbs from "htmlbars-inline-precompile";
import { module, test } from "qunit";
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import pretender, { response } from "discourse/tests/helpers/create-pretender";
import {
count,
exists,
query,
queryAll,
} from "discourse/tests/helpers/qunit-helpers";
import I18n from "discourse-i18n";
let requests = 0;
@ -48,112 +43,86 @@ module("Poll | Component | poll", function (hooks) {
test("shows vote", async function (assert) {
this.setProperties({
attributes: EmberObject.create({
post: EmberObject.create({
id: 42,
topic: {
archived: false,
},
user_id: 29,
}),
poll: EmberObject.create({
name: "poll",
type: "regular",
status: "closed",
results: "always",
options: [
{ id: "1f972d1df351de3ce35a787c89faad29", html: "yes", votes: 1 },
{ id: "d7ebc3a9beea2e680815a1e4f57d6db6", html: "no", votes: 0 },
],
voters: 1,
chart_type: "bar",
}),
vote: [],
groupableUserFields: [],
post: EmberObject.create({
id: 42,
topic: {
archived: false,
},
user_id: 29,
}),
poll: new TrackedObject({
name: "poll",
type: "regular",
status: "closed",
results: "always",
options: [
{ id: "1f972d1df351de3ce35a787c89faad29", html: "yes", votes: 1 },
{ id: "d7ebc3a9beea2e680815a1e4f57d6db6", html: "no", votes: 0 },
],
voters: 1,
chart_type: "bar",
}),
preloadedVoters: [],
});
await render(
hbs`<Poll @attrs={{this.attributes}} @preloadedVoters={{this.preloadedVoters}} />`
);
await render(hbs`<Poll @post={{this.post}} @poll={{this.poll}} />`);
assert.deepEqual(
Array.from(queryAll(".results li .option p")).map(
(span) => span.innerText
),
["100% yes", "0% no"]
);
assert.dom(".results li:nth-of-type(1) .option p").hasText("100% yes");
assert.dom(".results li:nth-of-type(2) .option p").hasText("0% no");
});
test("does not show results after voting when results are to be shown only on closed", async function (assert) {
this.setProperties({
attributes: EmberObject.create({
post: EmberObject.create({
id: 42,
topic: {
archived: false,
},
user_id: 29,
}),
hasSavedVote: true,
poll: EmberObject.create({
name: "poll",
type: "regular",
status: "open",
results: "on_close",
options: [
{ id: "1f972d1df351de3ce35a787c89faad29", html: "yes" },
{ id: "d7ebc3a9beea2e680815a1e4f57d6db6", html: "no" },
],
voters: 1,
chart_type: "bar",
}),
vote: ["1f972d1df351de3ce35a787c89faad29"],
groupableUserFields: [],
post: EmberObject.create({
id: 42,
topic: {
archived: false,
},
user_id: 29,
}),
poll: new TrackedObject({
name: "poll",
type: "regular",
status: "open",
results: "on_close",
options: [
{ id: "1f972d1df351de3ce35a787c89faad29", html: "yes" },
{ id: "d7ebc3a9beea2e680815a1e4f57d6db6", html: "no" },
],
voters: 1,
chart_type: "bar",
}),
preloadedVoters: [],
});
await render(
hbs`<Poll @attrs={{this.attributes}} @preloadedVoters={{this.preloadedVoters}} />`
);
await render(hbs`<Poll @post={{this.post}} @poll={{this.poll}} />`);
assert.ok(exists("ul.options"), "options are shown");
assert.ok(!exists("ul.results"), "results are not shown");
assert.dom("ul.options").exists("options are shown");
assert.dom("ul.results").doesNotExist("results are not shown");
});
test("can vote", async function (assert) {
this.setProperties({
attributes: EmberObject.create({
post: EmberObject.create({
id: 42,
topic: {
archived: false,
},
user_id: 29,
}),
poll: EmberObject.create({
name: "poll",
type: "regular",
status: "open",
results: "always",
options: [
{ id: "1f972d1df351de3ce35a787c89faad29", html: "yes", votes: 0 },
{ id: "d7ebc3a9beea2e680815a1e4f57d6db6", html: "no", votes: 0 },
],
voters: 0,
chart_type: "bar",
}),
vote: [],
groupableUserFields: [],
post: EmberObject.create({
id: 42,
topic: {
archived: false,
},
user_id: 29,
}),
poll: new TrackedObject({
name: "poll",
type: "regular",
status: "open",
results: "always",
options: [
{ id: "1f972d1df351de3ce35a787c89faad29", html: "yes", votes: 0 },
{ id: "d7ebc3a9beea2e680815a1e4f57d6db6", html: "no", votes: 0 },
],
voters: 0,
chart_type: "bar",
}),
preloadedVoters: [],
});
await render(
hbs`<Poll @attrs={{this.attributes}} @preloadedVoters={{this.preloadedVoters}} />`
);
await render(hbs`<Poll @post={{this.post}} @poll={{this.poll}} />`);
requests = 0;
@ -161,100 +130,84 @@ module("Poll | Component | poll", function (hooks) {
"li[data-poll-option-id='1f972d1df351de3ce35a787c89faad29'] button"
);
assert.strictEqual(requests, 1);
assert.strictEqual(count(".chosen"), 1);
assert.dom(".chosen").exists({ count: 1 });
await click(".toggle-results");
assert.strictEqual(
count("li[data-poll-option-id='1f972d1df351de3ce35a787c89faad29']"),
1
);
assert
.dom("li[data-poll-option-id='1f972d1df351de3ce35a787c89faad29']")
.exists({ count: 1 });
});
test("cannot vote if not member of the right group", async function (assert) {
this.setProperties({
attributes: EmberObject.create({
post: EmberObject.create({
id: 42,
topic: {
archived: false,
},
user_id: 29,
}),
poll: EmberObject.create({
name: "poll",
type: "regular",
status: "open",
results: "always",
options: [
{ id: "1f972d1df351de3ce35a787c89faad29", html: "yes", votes: 0 },
{ id: "d7ebc3a9beea2e680815a1e4f57d6db6", html: "no", votes: 0 },
],
voters: 0,
chart_type: "bar",
groups: "foo",
}),
vote: [],
groupableUserFields: [],
post: EmberObject.create({
id: 42,
topic: {
archived: false,
},
user_id: 29,
}),
poll: new TrackedObject({
name: "poll",
type: "regular",
status: "open",
results: "always",
options: [
{ id: "1f972d1df351de3ce35a787c89faad29", html: "yes", votes: 0 },
{ id: "d7ebc3a9beea2e680815a1e4f57d6db6", html: "no", votes: 0 },
],
voters: 0,
chart_type: "bar",
groups: "foo",
}),
preloadedVoters: [],
});
await render(
hbs`<Poll @attrs={{this.attributes}} @preloadedVoters={{this.preloadedVoters}} />`
);
await render(hbs`<Poll @post={{this.post}} @poll={{this.poll}} />`);
requests = 0;
await click(
"li[data-poll-option-id='1f972d1df351de3ce35a787c89faad29'] button"
);
assert.strictEqual(
query(".poll-container .alert").innerText,
I18n.t("poll.results.groups.title", { groups: "foo" })
);
assert
.dom(".poll-container .alert")
.hasText(I18n.t("poll.results.groups.title", { groups: "foo" }));
assert.strictEqual(requests, 0);
assert.ok(!exists(".chosen"));
assert.dom(".chosen").doesNotExist();
});
test("voting on a multiple poll with no min attribute", async function (assert) {
this.setProperties({
attributes: EmberObject.create({
post: EmberObject.create({
id: 42,
topic: {
archived: false,
},
user_id: 29,
}),
poll: EmberObject.create({
name: "poll",
type: "multiple",
status: "open",
results: "always",
max: 2,
options: [
{ id: "1f972d1df351de3ce35a787c89faad29", html: "yes", votes: 0 },
{ id: "d7ebc3a9beea2e680815a1e4f57d6db6", html: "no", votes: 0 },
],
voters: 0,
chart_type: "bar",
}),
vote: [],
groupableUserFields: [],
post: EmberObject.create({
id: 42,
topic: {
archived: false,
},
user_id: 29,
}),
poll: EmberObject.create({
name: "poll",
type: "multiple",
status: "open",
results: "always",
max: 2,
options: [
{ id: "1f972d1df351de3ce35a787c89faad29", html: "yes", votes: 0 },
{ id: "d7ebc3a9beea2e680815a1e4f57d6db6", html: "no", votes: 0 },
],
voters: 0,
chart_type: "bar",
}),
preloadedVoters: [],
});
await render(
hbs`<Poll @attrs={{this.attributes}} @preloadedVoters={{this.preloadedVoters}} />`
);
await render(hbs`<Poll @post={{this.post}} @poll={{this.poll}} />`);
assert.ok(exists(".poll-buttons .cast-votes:disabled"));
assert.dom(".poll-buttons .cast-votes:disabled").exists();
await click(
"li[data-poll-option-id='1f972d1df351de3ce35a787c89faad29'] button"
);
await click(".poll-buttons .cast-votes");
assert.ok(exists(".chosen"));
assert.dom(".chosen").exists();
});
});