Enable voting for open-ended questions in public section
This commit is contained in:
@@ -1,14 +1,16 @@
|
||||
.poll-form {
|
||||
fieldset {
|
||||
fieldset,
|
||||
.poll-question-open-ended {
|
||||
border: 1px solid $border;
|
||||
border-radius: $global-radius;
|
||||
padding: $line-height;
|
||||
}
|
||||
|
||||
fieldset + fieldset {
|
||||
+ * {
|
||||
margin-top: calc($line-height / 2);
|
||||
}
|
||||
}
|
||||
|
||||
fieldset {
|
||||
legend {
|
||||
@include header-font-size(h3);
|
||||
float: $global-left;
|
||||
@@ -19,7 +21,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
label {
|
||||
@include radio-or-checkbox-and-label-alignment;
|
||||
font-weight: normal;
|
||||
@@ -30,6 +31,14 @@
|
||||
white-space: pre;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.poll-question-open-ended {
|
||||
label {
|
||||
@include header-font-size(h3);
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
.help-text {
|
||||
display: block;
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
<fieldset <%= fieldset_attributes %>>
|
||||
<% if question.open? %>
|
||||
<div class="poll-question-open-ended">
|
||||
<%= fields_for "web_vote[#{question.id}]" do |f| %>
|
||||
<%= f.text_area :answer, label: question.title, value: existing_answer, rows: 3 %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% else %>
|
||||
<fieldset <%= fieldset_attributes %>>
|
||||
<legend><%= question.title %></legend>
|
||||
|
||||
<% if multiple_choice? %>
|
||||
@@ -20,4 +27,5 @@
|
||||
</div>
|
||||
<% end %>
|
||||
<%= form.error_for(:"question_#{question.id}") %>
|
||||
</fieldset>
|
||||
</fieldset>
|
||||
<% end %>
|
||||
|
||||
@@ -33,6 +33,10 @@ class Polls::Questions::QuestionComponent < ApplicationComponent
|
||||
end, ", ")
|
||||
end
|
||||
|
||||
def existing_answer
|
||||
form.object.answers[question.id]&.first&.answer
|
||||
end
|
||||
|
||||
def multiple_choice?
|
||||
question.multiple?
|
||||
end
|
||||
|
||||
@@ -73,23 +73,27 @@ class Poll::Question < ApplicationRecord
|
||||
end
|
||||
end
|
||||
|
||||
def find_or_initialize_user_answer(user, option_id)
|
||||
option = question_options.find(option_id)
|
||||
def find_or_initialize_user_answer(user, option_id: nil, answer_text: nil)
|
||||
answer = answers.find_or_initialize_by(find_by_attributes(user, option_id))
|
||||
|
||||
answer = answers.find_or_initialize_by(find_by_attributes(user, option))
|
||||
if accepts_options?
|
||||
option = question_options.find(option_id)
|
||||
answer.option = option
|
||||
answer.answer = option.title
|
||||
else
|
||||
answer.answer = answer_text
|
||||
end
|
||||
|
||||
answer
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def find_by_attributes(user, option)
|
||||
case vote_type
|
||||
when "unique", nil
|
||||
def find_by_attributes(user, option_id)
|
||||
if multiple?
|
||||
{ author: user, option_id: option_id }
|
||||
else
|
||||
{ author: user }
|
||||
when "multiple"
|
||||
{ author: user, option: option }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -63,8 +63,17 @@ class Poll::WebVote
|
||||
def answers_for_question(question, question_params)
|
||||
return [] unless question_params
|
||||
|
||||
if question.open?
|
||||
answer_text = question_params[:answer].to_s.strip
|
||||
if answer_text.present?
|
||||
[question.find_or_initialize_user_answer(user, answer_text: answer_text)]
|
||||
else
|
||||
[]
|
||||
end
|
||||
else
|
||||
Array(question_params[:option_id]).map do |option_id|
|
||||
question.find_or_initialize_user_answer(user, option_id)
|
||||
question.find_or_initialize_user_answer(user, option_id: option_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -69,5 +69,26 @@ describe Polls::Questions::QuestionComponent do
|
||||
expect(page).to have_field "Yes", type: :radio, checked: true
|
||||
expect(page).to have_field "No", type: :radio, checked: false
|
||||
end
|
||||
|
||||
context "Open-ended question" do
|
||||
let(:question) { create(:poll_question_open, poll: poll, title: "What do you want?") }
|
||||
before { create(:poll_answer, author: user, question: question, answer: "I don't know") }
|
||||
|
||||
it "renders text area with persisted answer" do
|
||||
render_inline Polls::Questions::QuestionComponent.new(question, form: form)
|
||||
|
||||
expect(page).to have_field "What do you want?", type: :textarea, with: "I don't know"
|
||||
end
|
||||
|
||||
it "renders unsaved form text over the persisted value" do
|
||||
web_vote.answers[question.id] = [
|
||||
build(:poll_answer, question: question, author: user, answer: "Typed (unsaved)")
|
||||
]
|
||||
|
||||
render_inline Polls::Questions::QuestionComponent.new(question, form: form)
|
||||
|
||||
expect(page).to have_field "What do you want?", type: :textarea, with: "Typed (unsaved)"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -100,7 +100,7 @@ RSpec.describe Poll::Question do
|
||||
existing_answer = create(:poll_answer, question: question, author: user, option: answer_a)
|
||||
create(:poll_answer, question: question, author: other_user, option: answer_b)
|
||||
|
||||
answer = question.find_or_initialize_user_answer(user, answer_b.id)
|
||||
answer = question.find_or_initialize_user_answer(user, option_id: answer_b.id)
|
||||
|
||||
expect(answer).to eq existing_answer
|
||||
expect(answer.author).to eq user
|
||||
@@ -111,13 +111,25 @@ RSpec.describe Poll::Question do
|
||||
it "initializes a new answer when only another user has answered" do
|
||||
create(:poll_answer, question: question, author: other_user, option: answer_a)
|
||||
|
||||
answer = question.find_or_initialize_user_answer(user, answer_a.id)
|
||||
answer = question.find_or_initialize_user_answer(user, option_id: answer_a.id)
|
||||
|
||||
expect(answer).to be_new_record
|
||||
expect(answer.author).to eq user
|
||||
expect(answer.option).to eq answer_a
|
||||
expect(answer.answer).to eq "Answer A"
|
||||
end
|
||||
|
||||
it "raises when option_id is invalid" do
|
||||
expect do
|
||||
question.find_or_initialize_user_answer(user, option_id: 999999)
|
||||
end.to raise_error(ActiveRecord::RecordNotFound)
|
||||
end
|
||||
|
||||
it "raises when option_id is nil" do
|
||||
expect do
|
||||
question.find_or_initialize_user_answer(user, answer_text: "ignored")
|
||||
end.to raise_error(ActiveRecord::RecordNotFound)
|
||||
end
|
||||
end
|
||||
|
||||
context "multiple question" do
|
||||
@@ -129,7 +141,7 @@ RSpec.describe Poll::Question do
|
||||
existing_answer = create(:poll_answer, question: question, author: user, option: answer_a)
|
||||
create(:poll_answer, question: question, author: other_user, option: answer_a)
|
||||
|
||||
answer = question.find_or_initialize_user_answer(user, answer_a.id)
|
||||
answer = question.find_or_initialize_user_answer(user, option_id: answer_a.id)
|
||||
|
||||
expect(answer).to eq existing_answer
|
||||
expect(answer.author).to eq user
|
||||
@@ -141,7 +153,7 @@ RSpec.describe Poll::Question do
|
||||
create(:poll_answer, question: question, author: user, option: answer_a)
|
||||
create(:poll_answer, question: question, author: other_user, option: answer_b)
|
||||
|
||||
answer = question.find_or_initialize_user_answer(user, answer_b.id)
|
||||
answer = question.find_or_initialize_user_answer(user, option_id: answer_b.id)
|
||||
|
||||
expect(answer).to be_new_record
|
||||
expect(answer.author).to eq user
|
||||
@@ -149,5 +161,31 @@ RSpec.describe Poll::Question do
|
||||
expect(answer.answer).to eq "Answer B"
|
||||
end
|
||||
end
|
||||
|
||||
context "Open-ended question" do
|
||||
let(:question) { create(:poll_question_open) }
|
||||
|
||||
it "ignores invalid option_id and uses answer_text" do
|
||||
answer = question.find_or_initialize_user_answer(user, option_id: 999999, answer_text: "Hi")
|
||||
expect(answer.option).to be nil
|
||||
expect(answer.answer).to eq "Hi"
|
||||
end
|
||||
|
||||
it "ignores option_id when nil and assigns answer with option set to nil" do
|
||||
answer = question.find_or_initialize_user_answer(user, answer_text: "Hi")
|
||||
|
||||
expect(answer.option).to be nil
|
||||
expect(answer.answer).to eq "Hi"
|
||||
end
|
||||
|
||||
it "reuses the existing poll answer for the user and updates answer" do
|
||||
existing = create(:poll_answer, question: question, author: user, answer: "Before")
|
||||
|
||||
answer = question.find_or_initialize_user_answer(user, answer_text: "After")
|
||||
expect(answer).to eq existing
|
||||
expect(answer.author).to eq user
|
||||
expect(answer.answer).to eq "After"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -156,5 +156,47 @@ describe Poll::WebVote do
|
||||
expect(Poll::Answer.count).to be 2
|
||||
end
|
||||
end
|
||||
|
||||
context "Open-ended questions" do
|
||||
let!(:open_ended_question) { create(:poll_question_open, poll: poll) }
|
||||
|
||||
it "creates one answer when text is present" do
|
||||
web_vote.update(open_ended_question.id.to_s => { answer: " Hi " })
|
||||
|
||||
expect(poll.reload.voters.size).to eq 1
|
||||
open_answer = open_ended_question.reload.answers.find_by(author: user)
|
||||
|
||||
expect(open_answer.answer).to eq "Hi"
|
||||
expect(open_answer.option_id).to be nil
|
||||
end
|
||||
|
||||
it "does not create an answer but create voters when text is blank or only spaces" do
|
||||
web_vote.update(open_ended_question.id.to_s => { answer: " " })
|
||||
|
||||
expect(poll.reload.voters.size).to eq 1
|
||||
expect(open_ended_question.reload.answers.where(author: user)).to be_empty
|
||||
end
|
||||
|
||||
it "deletes existing answer but keeps voters when leaving open-ended blank" do
|
||||
create(:poll_answer, question: open_ended_question, author: user, answer: "Old answer")
|
||||
|
||||
web_vote.update(open_ended_question.id.to_s => { answer: " " })
|
||||
|
||||
expect(poll.reload.voters.size).to eq 1
|
||||
expect(open_ended_question.reload.answers.where(author: user)).to be_empty
|
||||
end
|
||||
|
||||
it "updates existing open answer without creating duplicates" do
|
||||
existing = create(:poll_answer, question: open_ended_question, author: user, answer: "Old text")
|
||||
|
||||
web_vote.update(open_ended_question.id.to_s => { answer: " New text " })
|
||||
|
||||
updated = open_ended_question.reload.answers.find_by(author: user)
|
||||
expect(updated.id).to eq existing.id
|
||||
expect(updated.answer).to eq "New text"
|
||||
expect(updated.option_id).to be nil
|
||||
expect(poll.reload.voters.size).to eq 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -8,9 +8,10 @@ describe "Poll Votation Type" do
|
||||
login_as(author)
|
||||
end
|
||||
|
||||
scenario "Unique and multiple answers" do
|
||||
scenario "Unique, multiple and open answers" do
|
||||
create(:poll_question_unique, :yes_no, poll: poll, title: "Is it that bad?")
|
||||
create(:poll_question_multiple, :abcde, poll: poll, max_votes: 3, title: "Which ones do you prefer?")
|
||||
create(:poll_question_open, poll: poll, title: "What do you think?")
|
||||
|
||||
visit poll_path(poll)
|
||||
|
||||
@@ -21,6 +22,10 @@ describe "Poll Votation Type" do
|
||||
check "Answer C"
|
||||
end
|
||||
|
||||
within(".poll-question-open-ended") do
|
||||
fill_in "What do you think?", with: "I believe it's great"
|
||||
end
|
||||
|
||||
click_button "Vote"
|
||||
|
||||
expect(page).to have_content "Thank you for voting!"
|
||||
@@ -39,6 +44,10 @@ describe "Poll Votation Type" do
|
||||
expect(page).to have_field "Answer E", type: :checkbox, checked: false
|
||||
end
|
||||
|
||||
within(".poll-question-open-ended") do
|
||||
expect(page).to have_field "What do you think?", with: "I believe it's great"
|
||||
end
|
||||
|
||||
expect(page).to have_button "Vote"
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user