diff --git a/assets/javascripts/discourse/components/vote-box.gjs b/assets/javascripts/discourse/components/vote-box.gjs
new file mode 100644
index 0000000..0125846
--- /dev/null
+++ b/assets/javascripts/discourse/components/vote-box.gjs
@@ -0,0 +1,125 @@
+import Component from "@glimmer/component";
+import { tracked } from "@glimmer/tracking";
+import { hash } from "@ember/helper";
+import { action } from "@ember/object";
+import { service } from "@ember/service";
+import { htmlSafe } from "@ember/template";
+import concatClass from "discourse/helpers/concat-class";
+import routeAction from "discourse/helpers/route-action";
+import { ajax } from "discourse/lib/ajax";
+import { popupAjaxError } from "discourse/lib/ajax-error";
+import closeOnClickOutside from "discourse/modifiers/close-on-click-outside";
+import { i18n } from "discourse-i18n";
+import VoteButton from "./vote-button";
+import VoteCount from "./vote-count";
+import VoteOptions from "./vote-options";
+
+export default class VoteBox extends Component {
+ @service siteSettings;
+ @service currentUser;
+
+ @tracked votesAlert;
+ @tracked allowClick = true;
+ @tracked initialVote = false;
+ @tracked showOptions = false;
+
+ @action
+ addVote() {
+ let topic = this.args.topic;
+ return ajax("/voting/vote", {
+ type: "POST",
+ data: {
+ topic_id: topic.id,
+ },
+ })
+ .then((result) => {
+ topic.vote_count = result.vote_count;
+ topic.user_voted = true;
+ this.currentUser.votes_exceeded = !result.can_vote;
+ this.currentUser.votes_left = result.votes_left;
+ if (result.alert) {
+ this.votesAlert = result.votes_left;
+ }
+ this.allowClick = true;
+ this.showOptions = false;
+ })
+ .catch(popupAjaxError);
+ }
+
+ @action
+ removeVote() {
+ const topic = this.args.topic;
+
+ return ajax("/voting/unvote", {
+ type: "POST",
+ data: {
+ topic_id: topic.id,
+ },
+ })
+ .then((result) => {
+ topic.vote_count = result.vote_count;
+ topic.user_voted = false;
+ this.currentUser.votes_exceeded = !result.can_vote;
+ this.currentUser.votes_left = result.votes_left;
+ this.allowClick = true;
+ this.showOptions = false;
+ })
+ .catch(popupAjaxError);
+ }
+
+ @action
+ showVoteOptions() {
+ this.showOptions = true;
+ }
+
+ @action
+ closeVoteOptions() {
+ this.showOptions = false;
+ }
+
+ @action
+ closeVotesAlert() {
+ this.votesAlert = null;
+ }
+
+
+
+
+
+
+ {{#if this.showOptions}}
+
+ {{/if}}
+
+ {{#if this.votesAlert}}
+
+ {{/if}}
+
+
+}
diff --git a/assets/javascripts/discourse/components/vote-button.gjs b/assets/javascripts/discourse/components/vote-button.gjs
new file mode 100644
index 0000000..ad60e56
--- /dev/null
+++ b/assets/javascripts/discourse/components/vote-button.gjs
@@ -0,0 +1,104 @@
+import Component from "@glimmer/component";
+import { action } from "@ember/object";
+import { service } from "@ember/service";
+import DButton from "discourse/components/d-button";
+import cookie from "discourse/lib/cookie";
+import { applyBehaviorTransformer } from "discourse/lib/transformer";
+import { i18n } from "discourse-i18n";
+
+export default class VoteBox extends Component {
+ @service siteSettings;
+ @service currentUser;
+
+ get wrapperClasses() {
+ const classes = [];
+ const { topic } = this.args;
+ if (topic.closed) {
+ classes.push("voting-closed");
+ } else {
+ if (!topic.user_voted) {
+ classes.push("nonvote");
+ } else {
+ if (this.currentUser && this.currentUser.votes_exceeded) {
+ classes.push("vote-limited nonvote");
+ } else {
+ classes.push("vote");
+ }
+ }
+ }
+ if (this.siteSettings.topic_voting_show_who_voted) {
+ classes.push("show-pointer");
+ }
+ return classes.join(" ");
+ }
+
+ get buttonContent() {
+ const { topic } = this.args;
+ if (this.currentUser) {
+ if (topic.closed) {
+ return i18n("topic_voting.voting_closed_title");
+ }
+
+ if (topic.user_voted) {
+ return i18n("topic_voting.voted_title");
+ }
+
+ if (this.currentUser.votes_exceeded) {
+ return i18n("topic_voting.voting_limit");
+ }
+
+ return i18n("topic_voting.vote_title");
+ }
+
+ if (topic.vote_count) {
+ return i18n("topic_voting.anonymous_button", {
+ count: topic.vote_count,
+ });
+ }
+
+ return i18n("topic_voting.anonymous_button", { count: 1 });
+ }
+
+ @action
+ click() {
+ applyBehaviorTransformer("topic-vote-button-click", () => {
+ if (!this.currentUser) {
+ cookie("destination_url", window.location.href, { path: "/" });
+ this.args.showLogin();
+ return;
+ }
+
+ const { topic } = this.args;
+
+ if (
+ !topic.closed &&
+ !topic.user_voted &&
+ !this.currentUser.votes_exceeded
+ ) {
+ this.args.addVote();
+ }
+
+ if (topic.user_voted || this.currentUser.votes_exceeded) {
+ this.args.showVoteOptions();
+ }
+ });
+ }
+
+
+
+
+
+
+}
diff --git a/assets/javascripts/discourse/components/vote-count.gjs b/assets/javascripts/discourse/components/vote-count.gjs
new file mode 100644
index 0000000..20d9a8a
--- /dev/null
+++ b/assets/javascripts/discourse/components/vote-count.gjs
@@ -0,0 +1,95 @@
+import Component from "@glimmer/component";
+import { tracked } from "@glimmer/tracking";
+import { hash } from "@ember/helper";
+import { on } from "@ember/modifier";
+import { action } from "@ember/object";
+import { service } from "@ember/service";
+import { eq } from "truth-helpers";
+import AsyncContent from "discourse/components/async-content";
+import SmallUserList from "discourse/components/small-user-list";
+import concatClass from "discourse/helpers/concat-class";
+import { ajax } from "discourse/lib/ajax";
+import cookie from "discourse/lib/cookie";
+import { bind } from "discourse/lib/decorators";
+import getURL from "discourse/lib/get-url";
+import closeOnClickOutside from "discourse/modifiers/close-on-click-outside";
+
+export default class VoteBox extends Component {
+ @service siteSettings;
+ @service currentUser;
+
+ @tracked showWhoVoted = false;
+
+ @bind
+ async loadWhoVoted() {
+ return ajax("/voting/who", {
+ type: "GET",
+ data: {
+ topic_id: this.args.topic.id,
+ },
+ }).then((users) =>
+ users.map((user) => {
+ return {
+ template: user.avatar_template,
+ username: user.username,
+ post_url: user.post_url,
+ url: getURL("/u/") + user.username.toLowerCase(),
+ };
+ })
+ );
+ }
+
+ @action
+ click(event) {
+ event.preventDefault();
+ event.stopPropagation();
+
+ if (!this.currentUser) {
+ cookie("destination_url", window.location.href, { path: "/" });
+ this.args.showLogin();
+ return;
+ }
+
+ if (this.showWhoVoted) {
+ this.showWhoVoted = false;
+ } else if (this.siteSettings.topic_voting_show_who_voted) {
+ this.showWhoVoted = true;
+ }
+ }
+
+ @action
+ clickOutside() {
+ this.showWhoVoted = false;
+ }
+
+
+
+
+ {{@topic.vote_count}}
+
+
+
+ {{#if this.showWhoVoted}}
+
+ {{/if}}
+
+}
diff --git a/assets/javascripts/discourse/components/vote-options.gjs b/assets/javascripts/discourse/components/vote-options.gjs
new file mode 100644
index 0000000..d634c16
--- /dev/null
+++ b/assets/javascripts/discourse/components/vote-options.gjs
@@ -0,0 +1,29 @@
+import Component from "@glimmer/component";
+import { on } from "@ember/modifier";
+import { service } from "@ember/service";
+import icon from "discourse/helpers/d-icon";
+import { i18n } from "discourse-i18n";
+
+export default class VoteBox extends Component {
+ @service currentUser;
+
+
+
+
+}
diff --git a/assets/javascripts/discourse/connectors/topic-above-post-stream/topic-title-voting.gjs b/assets/javascripts/discourse/connectors/topic-above-post-stream/topic-title-voting.gjs
index 5e4088c..d5919e8 100644
--- a/assets/javascripts/discourse/connectors/topic-above-post-stream/topic-title-voting.gjs
+++ b/assets/javascripts/discourse/connectors/topic-above-post-stream/topic-title-voting.gjs
@@ -1,7 +1,7 @@
import Component from "@ember/component";
import { classNames, tagName } from "@ember-decorators/component";
-import MountWidget from "discourse/components/mount-widget";
import routeAction from "discourse/helpers/route-action";
+import VoteBox from "../../components/vote-box";
@tagName("div")
@classNames("topic-above-post-stream-outlet", "topic-title-voting")
@@ -11,10 +11,8 @@ export default class TopicTitleVoting extends Component {
{{#if this.model.postStream.loaded}}
{{#if this.model.postStream.firstPostPresent}}
- {{! template-lint-disable no-capital-arguments }}
-
diff --git a/assets/javascripts/discourse/connectors/user-activity-bottom/user-voted-topics.gjs b/assets/javascripts/discourse/connectors/user-activity-bottom/user-voted-topics.gjs
index f0413ef..48ec76d 100644
--- a/assets/javascripts/discourse/connectors/user-activity-bottom/user-voted-topics.gjs
+++ b/assets/javascripts/discourse/connectors/user-activity-bottom/user-voted-topics.gjs
@@ -1,18 +1,21 @@
-import Component from "@ember/component";
+import Component from "@glimmer/component";
import { LinkTo } from "@ember/routing";
-import { classNames, tagName } from "@ember-decorators/component";
+import { service } from "@ember/service";
import icon from "discourse/helpers/d-icon";
import { i18n } from "discourse-i18n";
-@tagName("")
-@classNames("user-activity-bottom-outlet", "user-voted-topics")
export default class UserVotedTopics extends Component {
+ @service siteSettings;
+
{{#if this.siteSettings.topic_voting_show_votes_on_profile}}
-
- {{icon "heart"}}
- {{i18n "topic_voting.vote_title_plural"}}
-
+
+
+
+ {{icon "heart"}}
+ {{i18n "topic_voting.vote_title_plural"}}
+
+
{{/if}}
}
diff --git a/assets/javascripts/discourse/initializers/discourse-topic-voting.js b/assets/javascripts/discourse/initializers/discourse-topic-voting.js
index 62f2128..1a0a92a 100644
--- a/assets/javascripts/discourse/initializers/discourse-topic-voting.js
+++ b/assets/javascripts/discourse/initializers/discourse-topic-voting.js
@@ -6,7 +6,7 @@ export default {
name: "discourse-topic-voting",
initialize() {
- withPluginApi("0.8.32", (api) => {
+ withPluginApi((api) => {
const siteSettings = api.container.lookup("service:site-settings");
if (siteSettings.topic_voting_enabled) {
const pageSearchController = api.container.lookup(
@@ -57,16 +57,11 @@ export default {
},
});
}
- });
- withPluginApi("0.11.7", (api) => {
- const siteSettings = api.container.lookup("service:site-settings");
if (siteSettings.topic_voting_enabled) {
api.addSearchSuggestion("order:votes");
}
- });
- withPluginApi("2.1.0", (api) => {
api.registerValueTransformer(
"category-available-views",
({ value, context }) => {
diff --git a/assets/javascripts/discourse/pre-initializers/extend-category-for-voting.js b/assets/javascripts/discourse/pre-initializers/extend-category-for-voting.js
index b773dfa..9476cac 100644
--- a/assets/javascripts/discourse/pre-initializers/extend-category-for-voting.js
+++ b/assets/javascripts/discourse/pre-initializers/extend-category-for-voting.js
@@ -1,3 +1,4 @@
+import { tracked } from "@glimmer/tracking";
import { withPluginApi } from "discourse/lib/plugin-api";
import { i18n } from "discourse-i18n";
@@ -35,6 +36,23 @@ function initialize(api) {
},
{ priority: -100 }
);
+
+ api.modifyClass(
+ "model:topic",
+ (Superclass) =>
+ class extends Superclass {
+ @tracked vote_count;
+ @tracked user_voted;
+ }
+ );
+ api.modifyClass(
+ "model:user",
+ (Superclass) =>
+ class extends Superclass {
+ @tracked votes_exceeded;
+ @tracked votes_left;
+ }
+ );
}
export default {
diff --git a/assets/javascripts/discourse/widgets/remove-vote.js b/assets/javascripts/discourse/widgets/remove-vote.js
deleted file mode 100644
index e6a1b69..0000000
--- a/assets/javascripts/discourse/widgets/remove-vote.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import { iconNode } from "discourse/lib/icon-library";
-import { createWidget } from "discourse/widgets/widget";
-import { i18n } from "discourse-i18n";
-
-export default createWidget("remove-vote", {
- tagName: "div.remove-vote",
-
- buildClasses() {
- return "vote-option";
- },
-
- html() {
- return [iconNode("xmark"), i18n("topic_voting.remove_vote")];
- },
-
- click() {
- this.sendWidgetAction("removeVote");
- },
-});
diff --git a/assets/javascripts/discourse/widgets/vote-box.js b/assets/javascripts/discourse/widgets/vote-box.js
deleted file mode 100644
index 829a199..0000000
--- a/assets/javascripts/discourse/widgets/vote-box.js
+++ /dev/null
@@ -1,104 +0,0 @@
-import { ajax } from "discourse/lib/ajax";
-import { popupAjaxError } from "discourse/lib/ajax-error";
-import RawHtml from "discourse/widgets/raw-html";
-import { createWidget } from "discourse/widgets/widget";
-import { i18n } from "discourse-i18n";
-
-export default createWidget("vote-box", {
- tagName: "div.voting-wrapper",
- buildKey: () => "vote-box",
-
- buildClasses() {
- if (this.siteSettings.topic_voting_show_who_voted) {
- return "show-pointer";
- }
- },
-
- defaultState() {
- return { allowClick: true, initialVote: false };
- },
-
- html(attrs, state) {
- let voteCount = this.attach("vote-count", attrs);
- let voteButton = this.attach("vote-button", attrs);
- let voteOptions = this.attach("vote-options", attrs);
- let contents = [voteCount, voteButton, voteOptions];
-
- if (state.votesAlert > 0) {
- const html =
- "";
- contents.push(new RawHtml({ html }));
- }
-
- return contents;
- },
-
- hideVotesAlert() {
- if (this.state.votesAlert) {
- this.state.votesAlert = null;
- this.scheduleRerender();
- }
- },
-
- click() {
- this.hideVotesAlert();
- },
-
- clickOutside() {
- this.hideVotesAlert();
- },
-
- addVote() {
- let topic = this.attrs;
- let state = this.state;
- return ajax("/voting/vote", {
- type: "POST",
- data: {
- topic_id: topic.id,
- },
- })
- .then((result) => {
- topic.set("vote_count", result.vote_count);
- topic.set("user_voted", true);
- this.currentUser.setProperties({
- votes_exceeded: !result.can_vote,
- votes_left: result.votes_left,
- });
- if (result.alert) {
- state.votesAlert = result.votes_left;
- }
- topic.set("who_voted", result.who_voted);
- state.allowClick = true;
- this.scheduleRerender();
- })
- .catch(popupAjaxError);
- },
-
- removeVote() {
- let topic = this.attrs;
- let state = this.state;
- return ajax("/voting/unvote", {
- type: "POST",
- data: {
- topic_id: topic.id,
- },
- })
- .then((result) => {
- topic.set("vote_count", result.vote_count);
- topic.set("user_voted", false);
- this.currentUser.setProperties({
- votes_exceeded: !result.can_vote,
- votes_left: result.votes_left,
- });
- topic.set("who_voted", result.who_voted);
- state.allowClick = true;
- this.scheduleRerender();
- })
- .catch(popupAjaxError);
- },
-});
diff --git a/assets/javascripts/discourse/widgets/vote-button.js b/assets/javascripts/discourse/widgets/vote-button.js
deleted file mode 100644
index ce108ae..0000000
--- a/assets/javascripts/discourse/widgets/vote-button.js
+++ /dev/null
@@ -1,101 +0,0 @@
-import { h } from "virtual-dom";
-import cookie from "discourse/lib/cookie";
-import { applyBehaviorTransformer } from "discourse/lib/transformer";
-import { createWidget } from "discourse/widgets/widget";
-import { i18n } from "discourse-i18n";
-
-export default createWidget("vote-button", {
- tagName: "div",
-
- buildClasses(attrs) {
- let buttonClass = "";
- if (attrs.closed) {
- buttonClass = "voting-closed";
- } else {
- if (!attrs.user_voted) {
- buttonClass = "nonvote";
- } else {
- if (this.currentUser && this.currentUser.votes_exceeded) {
- buttonClass = "vote-limited nonvote";
- } else {
- buttonClass = "vote";
- }
- }
- }
- if (this.siteSettings.topic_voting_show_who_voted) {
- buttonClass += " show-pointer";
- }
- return buttonClass;
- },
-
- buildButtonTitle(attrs) {
- if (this.currentUser) {
- if (attrs.closed) {
- return i18n("topic_voting.voting_closed_title");
- }
-
- if (attrs.user_voted) {
- return i18n("topic_voting.voted_title");
- }
-
- if (this.currentUser.votes_exceeded) {
- return i18n("topic_voting.voting_limit");
- }
-
- return i18n("topic_voting.vote_title");
- }
-
- if (attrs.vote_count) {
- return i18n("topic_voting.anonymous_button", {
- count: attrs.vote_count,
- });
- }
-
- return i18n("topic_voting.anonymous_button", { count: 1 });
- },
-
- html(attrs) {
- return h(
- "button",
- {
- attributes: {
- title: this.currentUser
- ? i18n("topic_voting.votes_left_button_title", {
- count: this.currentUser.votes_left,
- })
- : "",
- },
- className: "btn btn-primary vote-button",
- },
- this.buildButtonTitle(attrs)
- );
- },
-
- click() {
- applyBehaviorTransformer("topic-vote-button-click", () => {
- if (!this.currentUser) {
- this.sendWidgetAction("showLogin");
- cookie("destination_url", window.location.href, { path: "/" });
- return;
- }
- if (
- !this.attrs.closed &&
- this.parentWidget.state.allowClick &&
- !this.attrs.user_voted &&
- !this.currentUser.votes_exceeded
- ) {
- this.parentWidget.state.allowClick = false;
- this.parentWidget.state.initialVote = true;
- this.sendWidgetAction("addVote");
- }
- if (this.attrs.user_voted || this.currentUser.votes_exceeded) {
- document.querySelector(".vote-options").classList.toggle("hidden");
- }
- });
- },
-
- clickOutside() {
- document.querySelector(".vote-options").classList.add("hidden");
- this.parentWidget.state.initialVote = false;
- },
-});
diff --git a/assets/javascripts/discourse/widgets/vote-count.js b/assets/javascripts/discourse/widgets/vote-count.js
deleted file mode 100644
index f09de06..0000000
--- a/assets/javascripts/discourse/widgets/vote-count.js
+++ /dev/null
@@ -1,90 +0,0 @@
-import { h } from "virtual-dom";
-import { ajax } from "discourse/lib/ajax";
-import cookie from "discourse/lib/cookie";
-import getURL from "discourse/lib/get-url";
-import { createWidget } from "discourse/widgets/widget";
-
-export default createWidget("vote-count", {
- tagName: "div.vote-count-wrapper",
- buildKey: () => "vote-count",
-
- buildClasses() {
- if (this.attrs.vote_count === 0) {
- return "no-votes";
- }
- },
-
- defaultState() {
- return { whoVotedUsers: null };
- },
-
- html(attrs) {
- let voteCount = h("div.vote-count", attrs.vote_count.toString());
- let whoVoted = null;
- if (
- this.siteSettings.topic_voting_show_who_voted &&
- this.state.whoVotedUsers &&
- this.state.whoVotedUsers.length > 0
- ) {
- whoVoted = this.attach("small-user-list", {
- users: this.state.whoVotedUsers,
- addSelf: attrs.liked,
- listClassName: "regular-votes",
- });
- }
-
- let buffer = [voteCount];
- if (whoVoted) {
- buffer.push(h("div.who-voted.popup-menu.voting-popup-menu", [whoVoted]));
- }
- return buffer;
- },
-
- click() {
- if (!this.currentUser) {
- this.sendWidgetAction("showLogin");
- cookie("destination_url", window.location.href, { path: "/" });
- return;
- }
-
- if (
- this.siteSettings.topic_voting_show_who_voted &&
- this.attrs.vote_count > 0
- ) {
- if (this.state.whoVotedUsers === null) {
- return this.getWhoVoted();
- } else {
- const whoVotedElement = document.querySelector(".who-voted");
- whoVotedElement.style.display =
- whoVotedElement.style.display === "none" ? "block" : "none";
- }
- }
- },
-
- clickOutside() {
- const whoVotedElement = document.querySelector(".who-voted");
- if (whoVotedElement) {
- whoVotedElement.style.display = "none";
- }
- },
-
- getWhoVoted() {
- return ajax("/voting/who", {
- type: "GET",
- data: {
- topic_id: this.attrs.id,
- },
- }).then((users) => {
- this.state.whoVotedUsers = users.map(whoVotedAvatars);
- });
- },
-});
-
-function whoVotedAvatars(user) {
- return {
- template: user.avatar_template,
- username: user.username,
- post_url: user.post_url,
- url: getURL("/u/") + user.username.toLowerCase(),
- };
-}
diff --git a/assets/javascripts/discourse/widgets/vote-options.js b/assets/javascripts/discourse/widgets/vote-options.js
deleted file mode 100644
index da95ace..0000000
--- a/assets/javascripts/discourse/widgets/vote-options.js
+++ /dev/null
@@ -1,36 +0,0 @@
-import { h } from "virtual-dom";
-import { createWidget } from "discourse/widgets/widget";
-import { i18n } from "discourse-i18n";
-
-export default createWidget("vote-options", {
- tagName: "div.vote-options",
-
- buildClasses() {
- return "voting-popup-menu popup-menu hidden";
- },
-
- html(attrs) {
- let contents = [];
-
- if (attrs.user_voted) {
- contents.push(this.attach("remove-vote", attrs));
- } else if (
- this.currentUser &&
- this.currentUser.votes_exceeded &&
- !attrs.user_voted
- ) {
- contents.push([
- h("div", i18n("topic_voting.reached_limit")),
- h(
- "p",
- h(
- "a",
- { href: this.currentUser.get("path") + "/activity/votes" },
- i18n("topic_voting.list_votes")
- )
- ),
- ]);
- }
- return contents;
- },
-});
diff --git a/spec/system/voting_spec.rb b/spec/system/voting_spec.rb
index 59ad246..4236511 100644
--- a/spec/system/voting_spec.rb
+++ b/spec/system/voting_spec.rb
@@ -16,31 +16,23 @@
fab!(:admin_page) { PageObjects::Pages::AdminSiteSettings.new }
before do
- SiteSetting.topic_voting_enabled = false
-
- admin.activate
- user.activate
-
+ SiteSetting.topic_voting_enabled = true
sign_in(admin)
end
- skip "enables voting in category topics and votes" do
+ it "enables voting in category topics and votes" do
category_page.visit(category1)
expect(category_page).to have_no_css(category_page.votes)
- # enables voting
- admin_page.visit_filtered_plugin_setting("topic%20voting%20enabled").toggle_setting(
- "topic_voting_enabled",
- "Allow users to vote on topics?",
- )
-
+ # enable voting in category
category_page
.visit_settings(category1)
.toggle_setting("enable-topic-voting", "Allow users to vote on topics in this category")
.save_settings
- .back_to_category
- # voting
+ try_until_success { expect(Category.can_vote?(category1.id)).to eq(true) }
+
+ # make a vote
category_page.visit(category1)
expect(category_page).to have_css(category_page.votes)
expect(category_page).to have_css(category_page.topic_with_vote_count(0), count: 2)
@@ -50,14 +42,12 @@
topic_page.vote
expect(topic_page.vote_popup).to have_text("You have 9 votes left, see your votes")
expect(topic_page.vote_count).to have_text("1")
- topic_page.click_vote_popup_activity
+ # visit user activity page
+ topic_page.click_vote_popup_activity
expect(user_page.active_user_primary_navigation).to have_text("Activity")
expect(user_page.active_user_secondary_navigation).to have_text("Votes")
- expect(page).to have_css(".topic-list-body tr[data-topic-id=\"#{topic1.id}\"]")
- expect(find(".topic-list-body tr[data-topic-id=\"#{topic1.id}\"] a.voted")).to have_text(
- "1 vote",
- )
+ expect(page).to have_css(".topic-list-body tr[data-topic-id=\"#{topic1.id}\"]", text: "1 vote")
find(".topic-list-body tr[data-topic-id=\"#{topic1.id}\"] a.raw-link").click
# unvoting