diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index b73c07474..ce9861547 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -70,6 +70,7 @@
//= require polls_admin
//= require leaflet
//= require map
+//= require polls
var initialize_modules = function() {
App.Comments.initialize();
@@ -108,6 +109,7 @@ var initialize_modules = function() {
App.TagAutocomplete.initialize();
App.PollsAdmin.initialize();
App.Map.initialize();
+ App.Polls.initialize();
};
$(function(){
diff --git a/app/assets/javascripts/polls.js.coffee b/app/assets/javascripts/polls.js.coffee
new file mode 100644
index 000000000..5cf792ce2
--- /dev/null
+++ b/app/assets/javascripts/polls.js.coffee
@@ -0,0 +1,28 @@
+App.Polls =
+ generateToken: ->
+ token = ''
+ rand = ''
+ for n in [0..5]
+ rand = Math.random().toString(36).substr(2) # remove `0.`
+ token = token + rand;
+
+ token = token.substring(0, 64)
+ return token
+
+ replaceToken: ->
+ for link in $('.js-question-answer')
+ token_param = link.search.slice(-6)
+ if token_param == "token="
+ link.href = link.href + @token
+
+ initialize: ->
+ @token = App.Polls.generateToken()
+ App.Polls.replaceToken()
+
+ $(".js-question-answer").on
+ click: =>
+ token_message = $(".js-token-message")
+ if !token_message.is(':visible')
+ token_message.html(token_message.html() + "
" + @token + "");
+ token_message.show()
+ false
diff --git a/app/assets/stylesheets/participation.scss b/app/assets/stylesheets/participation.scss
index 2df8292f6..7d6a0fff7 100644
--- a/app/assets/stylesheets/participation.scss
+++ b/app/assets/stylesheets/participation.scss
@@ -335,8 +335,16 @@
word-wrap: break-word;
}
- .callout.proposal-retired {
- font-size: $base-font-size;
+ .callout {
+ &.token-message {
+ background-color: #fff;
+ border-color: $info-border;
+ color: $color-info;
+ }
+
+ &.proposal-retired {
+ font-size: $base-font-size;
+ }
}
.social-share-full .social-share-button {
@@ -1574,7 +1582,23 @@
border-bottom: 1px solid #eee;
.column:nth-child(odd) {
- border-right: 1px solid #eee;
+ border-right: 2px solid $text;
+ }
+
+ .answer-divider {
+ border-bottom: 2px solid $text;
+ border-right: 0 !important;
+ margin-bottom: $line-height;
+ padding-bottom: $line-height;
+ }
+
+ .answer-description {
+ height: 100%;
+
+ &.short {
+ height: $line-height * 12;
+ overflow: hidden;
+ }
}
}
@@ -1599,6 +1623,10 @@
.orbit-slide {
max-height: none !important;
+
+ img {
+ image-rendering: pixelated;
+ }
}
.orbit-caption {
@@ -1606,6 +1634,11 @@
color: $text;
}
+.orbit-next,
+.orbit-previous {
+ background: rgba(34, 34, 34, 0.25);
+}
+
.zoom-link {
background: #fff;
border-radius: rem-calc(48);
@@ -1638,7 +1671,10 @@
.poll {
&.with-image {
- padding: 0 $line-height / 2 0 0;
+
+ @include breakpoint(medium) {
+ padding: 0 $line-height / 2 0 0;
+ }
img {
height: 100%;
@@ -1769,6 +1805,10 @@
margin-right: $line-height / 4;
min-width: rem-calc(168);
+ @include breakpoint(medium down) {
+ width: 100%;
+ }
+
&.answered {
background: #f4f8ec;
border: 2px solid #92ba48;
diff --git a/app/controllers/admin/poll/questions/answers/images_controller.rb b/app/controllers/admin/poll/questions/answers/images_controller.rb
index 4074eb371..1bf30907d 100644
--- a/app/controllers/admin/poll/questions/answers/images_controller.rb
+++ b/app/controllers/admin/poll/questions/answers/images_controller.rb
@@ -23,7 +23,8 @@ class Admin::Poll::Questions::Answers::ImagesController < Admin::Poll::BaseContr
private
def images_params
- params.permit(images_attributes: [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy])
+ params.require(:poll_question_answer).permit(:answer_id,
+ images_attributes: [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy])
end
def load_answer
diff --git a/app/controllers/admin/poll/questions/answers/videos_controller.rb b/app/controllers/admin/poll/questions/answers/videos_controller.rb
new file mode 100644
index 000000000..a231c1a20
--- /dev/null
+++ b/app/controllers/admin/poll/questions/answers/videos_controller.rb
@@ -0,0 +1,57 @@
+class Admin::Poll::Questions::Answers::VideosController < Admin::Poll::BaseController
+ before_action :load_answer, only: [:index, :new, :create]
+ before_action :load_video, only: [:edit, :update, :destroy]
+
+ def index
+ end
+
+ def new
+ @video = ::Poll::Question::Answer::Video.new
+ end
+
+ def create
+ @video = ::Poll::Question::Answer::Video.new(video_params)
+
+ if @video.save
+ redirect_to admin_answer_videos_path(@answer),
+ notice: t("flash.actions.create.poll_question_answer_video")
+ else
+ render :new
+ end
+ end
+
+ def edit
+ end
+
+ def update
+ if @video.update(video_params)
+ redirect_to admin_answer_videos_path(@video.answer_id),
+ notice: t("flash.actions.save_changes.notice")
+ else
+ render :edit
+ end
+ end
+
+ def destroy
+ if @video.destroy
+ notice = t("flash.actions.destroy.poll_question_answer_video")
+ else
+ notice = t("flash.actions.destroy.error")
+ end
+ redirect_to :back, notice: notice
+ end
+
+ private
+
+ def video_params
+ params.require(:poll_question_answer_video).permit(:title, :url, :answer_id)
+ end
+
+ def load_answer
+ @answer = ::Poll::Question::Answer.find(params[:answer_id])
+ end
+
+ def load_video
+ @video = ::Poll::Question::Answer::Video.find(params[:id])
+ end
+end
diff --git a/app/controllers/admin/poll/questions/answers_controller.rb b/app/controllers/admin/poll/questions/answers_controller.rb
index 660339844..51c44dd94 100644
--- a/app/controllers/admin/poll/questions/answers_controller.rb
+++ b/app/controllers/admin/poll/questions/answers_controller.rb
@@ -1,5 +1,5 @@
class Admin::Poll::Questions::AnswersController < Admin::Poll::BaseController
- before_action :load_question
+ before_action :load_answer, only: [:show, :edit, :update, :documents]
load_and_authorize_resource :question, class: "::Poll::Question"
@@ -11,20 +11,42 @@ class Admin::Poll::Questions::AnswersController < Admin::Poll::BaseController
@answer = ::Poll::Question::Answer.new(answer_params)
if @answer.save
- redirect_to admin_question_path(@question),
+ redirect_to admin_question_path(@answer.question),
notice: t("flash.actions.create.poll_question_answer")
else
render :new
end
end
+ def show
+ end
+
+ def edit
+ end
+
+ def update
+ if @answer.update(answer_params)
+ redirect_to admin_question_path(@answer.question),
+ notice: t("flash.actions.save_changes.notice")
+ else
+ redirect_to :back
+ end
+ end
+
+ def documents
+ @documents = @answer.documents
+
+ render 'admin/poll/questions/answers/documents'
+ end
+
private
def answer_params
- params.require(:poll_question_answer).permit(:title, :description, :question_id)
+ params.require(:poll_question_answer).permit(:title, :description, :question_id, documents_attributes: [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy])
end
- def load_question
- @question = ::Poll::Question.find(params[:question_id])
+ def load_answer
+ @answer = ::Poll::Question::Answer.find(params[:id] || params[:answer_id])
end
+
end
diff --git a/app/controllers/admin/poll/questions_controller.rb b/app/controllers/admin/poll/questions_controller.rb
index 107c06740..5cf587735 100644
--- a/app/controllers/admin/poll/questions_controller.rb
+++ b/app/controllers/admin/poll/questions_controller.rb
@@ -56,8 +56,7 @@ class Admin::Poll::QuestionsController < Admin::Poll::BaseController
private
def question_params
- params.require(:poll_question).permit(:poll_id, :title, :question, :proposal_id, :valid_answers, :video_url,
- documents_attributes: [:id, :title, :attachment, :cached_attachment, :user_id, :_destroy])
+ params.require(:poll_question).permit(:poll_id, :title, :question, :proposal_id, :valid_answers, :video_url)
end
def search_params
diff --git a/app/controllers/polls/questions_controller.rb b/app/controllers/polls/questions_controller.rb
index 490b32fbe..407e6d984 100644
--- a/app/controllers/polls/questions_controller.rb
+++ b/app/controllers/polls/questions_controller.rb
@@ -7,10 +7,12 @@ class Polls::QuestionsController < ApplicationController
def answer
answer = @question.answers.find_or_initialize_by(author: current_user)
+ token = params[:token]
answer.answer = params[:answer]
+ answer.touch if answer.persisted?
answer.save!
- answer.record_voter_participation
+ answer.record_voter_participation(token)
@answers_by_question_id = { @question.id => params[:answer] }
end
diff --git a/app/controllers/polls_controller.rb b/app/controllers/polls_controller.rb
index 289a33be3..6240d4809 100644
--- a/app/controllers/polls_controller.rb
+++ b/app/controllers/polls_controller.rb
@@ -1,6 +1,8 @@
class PollsController < ApplicationController
include CommentableActions
+ include PollsHelper
+
load_and_authorize_resource
has_filters %w{current expired incoming}
@@ -14,15 +16,16 @@ class PollsController < ApplicationController
def show
@questions = @poll.questions.for_render.sort_for_list
-
- @commentable = @poll
- @comment_tree = CommentTree.new(@commentable, params[:page], @current_order)
-
+ @token = poll_voter_token(@poll, current_user)
+
@answers_by_question_id = {}
poll_answers = ::Poll::Answer.by_question(@poll.question_ids).by_author(current_user.try(:id))
poll_answers.each do |answer|
@answers_by_question_id[answer.question_id] = answer.answer
end
+
+ @commentable = @poll
+ @comment_tree = CommentTree.new(@commentable, params[:page], @current_order)
end
end
diff --git a/app/helpers/polls_helper.rb b/app/helpers/polls_helper.rb
index 27d33ea04..dd4018be7 100644
--- a/app/helpers/polls_helper.rb
+++ b/app/helpers/polls_helper.rb
@@ -41,4 +41,12 @@ module PollsHelper
booth.name + location
end
+ def poll_voter_token(poll, user)
+ Poll::Voter.where(poll: poll, user: user, origin: "web").first&.token || ''
+ end
+
+ def voted_before_sign_in(question)
+ question.answers.where(author: current_user).any? { |vote| current_user.current_sign_in_at >= vote.updated_at }
+ end
+
end
diff --git a/app/models/direct_upload.rb b/app/models/direct_upload.rb
index 192f06d04..e597f9f12 100644
--- a/app/models/direct_upload.rb
+++ b/app/models/direct_upload.rb
@@ -19,7 +19,9 @@ class DirectUpload
if @resource_type.present? && @resource_relation.present? && (@attachment.present? || @cached_attachment.present?)
@resource = @resource_type.constantize.find_or_initialize_by(id: @resource_id)
- if @resource.respond_to?(:images)
+ #Refactor
+ if @resource.respond_to?(:images) &&
+ ((@attachment.present? && !@attachment.content_type.match(/pdf/)) || @cached_attachment.present?)
@relation = @resource.images.send("build", relation_attributtes)
elsif @resource.class.reflections[@resource_relation].macro == :has_one
@relation = @resource.send("build_#{resource_relation}", relation_attributtes)
diff --git a/app/models/poll/answer.rb b/app/models/poll/answer.rb
index 2378de6ba..4484060dd 100644
--- a/app/models/poll/answer.rb
+++ b/app/models/poll/answer.rb
@@ -16,7 +16,7 @@ class Poll::Answer < ActiveRecord::Base
scope :by_author, ->(author_id) { where(author_id: author_id) }
scope :by_question, ->(question_id) { where(question_id: question_id) }
- def record_voter_participation
- Poll::Voter.find_or_create_by!(user: author, poll: poll, origin: "web")
+ def record_voter_participation(token)
+ Poll::Voter.find_or_create_by(user: author, poll: poll, origin: "web", token: token)
end
-end
\ No newline at end of file
+end
diff --git a/app/models/poll/question.rb b/app/models/poll/question.rb
index 1e94e0180..e6ef482a9 100644
--- a/app/models/poll/question.rb
+++ b/app/models/poll/question.rb
@@ -1,11 +1,6 @@
class Poll::Question < ActiveRecord::Base
include Measurable
include Searchable
- include Documentable
- documentable max_documents_allowed: 1,
- max_file_size: 3.megabytes,
- accepted_content_types: [ "application/pdf" ]
- accepts_nested_attributes_for :documents, allow_destroy: true
acts_as_paranoid column: :hidden_at
include ActsAsParanoidAliases
diff --git a/app/models/poll/question/answer.rb b/app/models/poll/question/answer.rb
index 1746c480c..b3324e57f 100644
--- a/app/models/poll/question/answer.rb
+++ b/app/models/poll/question/answer.rb
@@ -1,7 +1,13 @@
class Poll::Question::Answer < ActiveRecord::Base
include Galleryable
+ include Documentable
+ documentable max_documents_allowed: 3,
+ max_file_size: 3.megabytes,
+ accepted_content_types: [ "application/pdf" ]
+ accepts_nested_attributes_for :documents, allow_destroy: true
belongs_to :question, class_name: 'Poll::Question', foreign_key: 'question_id'
+ has_many :videos, class_name: 'Poll::Question::Answer::Video'
validates :title, presence: true
diff --git a/app/models/poll/question/answer/video.rb b/app/models/poll/question/answer/video.rb
new file mode 100644
index 000000000..3d214af96
--- /dev/null
+++ b/app/models/poll/question/answer/video.rb
@@ -0,0 +1,16 @@
+class Poll::Question::Answer::Video < ActiveRecord::Base
+ belongs_to :answer, class_name: 'Poll::Question::Answer', foreign_key: 'answer_id'
+
+ VIMEO_REGEX = /vimeo.*(staffpicks\/|channels\/|videos\/|video\/|\/)([^#\&\?]*).*/
+ YOUTUBE_REGEX = /youtu.*(be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/
+
+ validates :title, presence: true
+ validate :valid_url?
+
+ def valid_url?
+ return if url.blank?
+ return if url.match(VIMEO_REGEX)
+ return if url.match(YOUTUBE_REGEX)
+ errors.add(:url, :invalid)
+ end
+end
diff --git a/app/views/admin/_menu.html.erb b/app/views/admin/_menu.html.erb
index 81e1ae9bd..21dc7cffb 100644
--- a/app/views/admin/_menu.html.erb
+++ b/app/views/admin/_menu.html.erb
@@ -53,7 +53,6 @@
<% end %>
-
<% if feature?(:polls) %>
<%= t("proposals.form.proposal_video_url_note") %>
- <%= f.text_field :video_url, placeholder: t("proposals.form.proposal_video_url"), label: false, - aria: {describedby: "video-url-help-text"} %> + ++ <%= t("proposals.form.proposal_video_url_note") %> +
+ + <%= f.text_field :video_url, + placeholder: t("proposals.form.proposal_video_url"), + label: false, + aria: {describedby: "video-url-help-text"} %>| <%= t("admin.questions.show.answers.document_title") %> | +<%= t("admin.questions.show.answers.document_actions") %> | +
|---|---|
| + <%= link_to document.title, document.attachment.url %> + | ++ <%= link_to t('documents.buttons.download_document'), + document.attachment.url, + target: "_blank", + rel: "nofollow", + class: 'button hollow' %> + | +
+ <%= t("admin.answers.show.title") %>
+
+ <%= @answer.title %>
+
+ <%= t("admin.answers.show.description") %> + <%= @answer.description %> +
+ +
+ <%= t("admin.answers.show.images") %>
+
+ <%= link_to t("admin.answers.show.images_list"), admin_answer_images_path(@answer) %>
+
| <%= t("admin.answers.videos.index.video_title") %> | +<%= t("admin.answers.videos.index.video_url") %> | ++ <%= t("admin.actions.actions") %> + | +
|---|---|---|
| <%= video.title %> | +<%= link_to "#{video.url}", video.url %> | ++ + <%= link_to t("shared.edit"), + edit_admin_video_path(video), + class: "button hollow" %> + + <%= link_to t("shared.delete"), + admin_video_path(video), + class: "button hollow alert", + method: :delete %> + | +
| + | <%= t('admin.questions.show.valid_answers') %> <%= link_to t("admin.questions.show.add_answer"), new_admin_question_answer_path(@question), @@ -41,19 +41,34 @@ | ||||||
|---|---|---|---|---|---|---|---|
| <%= t("admin.questions.show.answers.title") %> | -<%= t("admin.questions.show.answers.description") %> | -<%= t("admin.questions.show.answers.images") %> | +<%= t("admin.questions.show.answers.description") %> | +<%= t("admin.questions.show.answers.images") %> | +<%= t("admin.questions.show.answers.documents") %> | +<%= t("admin.questions.show.answers.videos") %> | |
| <%= answer.title %> | +<%= link_to answer.title, admin_answer_path(answer) %> | <%= answer.description %> |
- (<%= answer.images.count %>) + (<%= answer.images.count %>) + <%= link_to t("admin.questions.show.answers.images_list"), admin_answer_images_path(answer) %> |
+
+ (<%= answer.documents.count rescue 0 %>)
+ + <%= link_to t("admin.questions.show.answers.documents_list"), + admin_answer_documents_path(answer) %> + |
+
+ (<%= answer.videos.count %>)
+ + <%= link_to t("admin.questions.show.answers.video_list"), + admin_answer_videos_path(answer) %> + |
||
- <%= t("admin.questions.show.documents") %>
-
- <%= @question.documents.first.title %>
-
+ + <%= t("polls.show.documents") %> +
+ + <% answer.documents.each do |document| %> + <%= link_to document.title, + document.attachment.url, + target: "_blank", + rel: "nofollow" %>#{Faker::Lorem.paragraphs.join('
')}
" open_at = rand(2.months.ago..2.months.from_now) + answers = Faker::Lorem.words((2..4).to_a.sample).map { |answer| answer.capitalize } question = Poll::Question.create!(author: author, title: Faker::Lorem.sentence(3).truncate(60), - description: description, - valid_answers: Faker::Lorem.words((2..7).to_a.sample).join(', '), + valid_answers: answers.join(', '), poll: poll) + answers.each do |answer| + Poll::Question::Answer.create!(question: question, title: answer, description: Faker::ChuckNorris.fact) + end end puts " ✅" diff --git a/db/migrate/20171004210108_create_poll_question_answer_videos.rb b/db/migrate/20171004210108_create_poll_question_answer_videos.rb new file mode 100644 index 000000000..7cce33b29 --- /dev/null +++ b/db/migrate/20171004210108_create_poll_question_answer_videos.rb @@ -0,0 +1,11 @@ +class CreatePollQuestionAnswerVideos < ActiveRecord::Migration + def change + create_table :poll_question_answer_videos do |t| + t.string :title + t.string :url + t.integer :answer_id, index: true + end + + add_foreign_key :poll_question_answer_videos, :poll_question_answers, column: :answer_id + end +end diff --git a/db/migrate/20171006145053_add_token_to_poll_voters.rb b/db/migrate/20171006145053_add_token_to_poll_voters.rb new file mode 100644 index 000000000..5d07f5065 --- /dev/null +++ b/db/migrate/20171006145053_add_token_to_poll_voters.rb @@ -0,0 +1,5 @@ +class AddTokenToPollVoters < ActiveRecord::Migration + def change + add_column :poll_voters, :token, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index d393716e6..9adba12b1 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20171004151553) do +ActiveRecord::Schema.define(version: 20171006145053) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -668,6 +668,14 @@ ActiveRecord::Schema.define(version: 20171004151553) do add_index "poll_partial_results", ["origin"], name: "index_poll_partial_results_on_origin", using: :btree add_index "poll_partial_results", ["question_id"], name: "index_poll_partial_results_on_question_id", using: :btree + create_table "poll_question_answer_videos", force: :cascade do |t| + t.string "title" + t.string "url" + t.integer "answer_id" + end + + add_index "poll_question_answer_videos", ["answer_id"], name: "index_poll_question_answer_videos_on_answer_id", using: :btree + create_table "poll_question_answers", force: :cascade do |t| t.string "title" t.text "description" @@ -760,6 +768,7 @@ ActiveRecord::Schema.define(version: 20171004151553) do t.integer "user_id" t.string "origin" t.integer "officer_id" + t.string "token" end add_index "poll_voters", ["booth_assignment_id"], name: "index_poll_voters_on_booth_assignment_id", using: :btree @@ -1155,6 +1164,7 @@ ActiveRecord::Schema.define(version: 20171004151553) do add_foreign_key "poll_partial_results", "poll_officer_assignments", column: "officer_assignment_id" add_foreign_key "poll_partial_results", "poll_questions", column: "question_id" add_foreign_key "poll_partial_results", "users", column: "author_id" + add_foreign_key "poll_question_answer_videos", "poll_question_answers", column: "answer_id" add_foreign_key "poll_question_answers", "poll_questions", column: "question_id" add_foreign_key "poll_questions", "polls" add_foreign_key "poll_questions", "proposals" diff --git a/spec/features/admin/poll/questions/answers/answers_spec.rb b/spec/features/admin/poll/questions/answers/answers_spec.rb new file mode 100644 index 000000000..66918c4d8 --- /dev/null +++ b/spec/features/admin/poll/questions/answers/answers_spec.rb @@ -0,0 +1,51 @@ +require 'rails_helper' + +feature 'Answers' do + + background do + admin = create(:administrator) + login_as (admin.user) + end + + scenario 'Create' do + question = create(:poll_question) + title = 'Whatever the question may be, the answer is always 42' + description = "The Hitchhiker's Guide To The Universe" + + visit admin_question_path(question) + click_link 'Add answer' + + fill_in 'poll_question_answer_title', with: title + fill_in 'poll_question_answer_description', with: description + + click_button 'Save' + + expect(page).to have_content(title) + expect(page).to have_content(description) + end + + scenario 'Update' do + question = create(:poll_question) + answer = create(:poll_question_answer, question: question, title: "Answer title") + + visit admin_answer_path(answer) + + click_link 'Edit' + + old_title = answer.title + new_title = 'Ex Machina' + + fill_in 'poll_question_answer_title', with: new_title + + click_button 'Save' + + expect(page).to have_content('Changes saved') + expect(page).to have_content(new_title) + + visit admin_question_path(question) + + expect(page).to have_content(new_title) + expect(page).to_not have_content(old_title) + end + +end diff --git a/spec/features/admin/poll/questions/answers/videos/videos_spec.rb b/spec/features/admin/poll/questions/answers/videos/videos_spec.rb new file mode 100644 index 000000000..35d07f454 --- /dev/null +++ b/spec/features/admin/poll/questions/answers/videos/videos_spec.rb @@ -0,0 +1,33 @@ +require 'rails_helper' + +feature 'Videos' do + + background do + admin = create(:administrator) + login_as(admin.user) + end + + scenario "Create" do + question = create(:poll_question) + answer = create(:poll_question_answer, question: question) + video_title = "'Magical' by Junko Ohashi" + video_url = "https://www.youtube.com/watch?v=-JMf43st-1A" + + visit admin_question_path(question) + + within("#poll_question_answer_#{answer.id}") do + click_link "Video list" + end + + click_link "Add video" + + fill_in 'poll_question_answer_video_title', with: video_title + fill_in 'poll_question_answer_video_url', with: video_url + + click_button "Save" + + expect(page).to have_content(video_title) + expect(page).to have_content(video_url) + end + +end diff --git a/spec/features/admin/poll/questions_spec.rb b/spec/features/admin/poll/questions_spec.rb index 7e103c647..5c8cba9c1 100644 --- a/spec/features/admin/poll/questions_spec.rb +++ b/spec/features/admin/poll/questions_spec.rb @@ -111,22 +111,4 @@ feature 'Admin poll questions' do pending "Mark all city by default when creating a poll question from a successful proposal" - it_behaves_like "nested documentable", - "administrator", - "poll_question", - "new_admin_question_path", - { }, - "documentable_fill_new_valid_poll_question", - "Save", - "Star Wars: Episode IV - A New Hope" - - it_behaves_like "nested documentable", - "administrator", - "poll_question", - "edit_admin_question_path", - { "id": "id" }, - nil, - "Save", - "Changes saved" - end diff --git a/spec/features/polls/polls_spec.rb b/spec/features/polls/polls_spec.rb index 76932558b..9fdb07255 100644 --- a/spec/features/polls/polls_spec.rb +++ b/spec/features/polls/polls_spec.rb @@ -94,6 +94,9 @@ feature 'Polls' do end scenario 'Level 1 users' do + visit polls_path + expect(page).to_not have_selector('.already-answer') + poll.update(geozone_restricted: true) poll.geozones << geozone @@ -209,8 +212,7 @@ feature 'Polls' do visit poll_path(poll) expect(page).to have_link('Han Solo') - expect(page).to_not have_link('Chewbacca') - expect(page).to have_content('Chewbacca') + expect(page).to have_link('Chewbacca') end scenario 'Level 2 users answering', :js do @@ -256,82 +258,79 @@ feature 'Polls' do expect(page).to have_link('Han Solo') end - scenario 'User can write comments' do - create(:comment, commentable: poll) + scenario 'Level 2 votes, signs out, signs in, votes again', :js do + poll.update(geozone_restricted: true) + poll.geozones << geozone + question = create(:poll_question, poll: poll) + answer1 = create(:poll_question_answer, question: question, title: 'Han Solo') + answer2 = create(:poll_question_answer, question: question, title: 'Chewbacca') + + user = create(:user, :level_two, geozone: geozone) + + login_as user visit poll_path(poll) + click_link 'Han Solo' - expect(page).to have_css('.comment', count: 1) + expect(page).to_not have_link('Han Solo') + expect(page).to have_link('Chewbacca') - comment = Comment.last - within first('.comment') do - expect(page).to have_content comment.user.name - expect(page).to have_content I18n.l(comment.created_at, format: :datetime) - expect(page).to have_content comment.body - end - end - - scenario 'user b can access to comments from user a' do - oliver = create(:user, username: 'Oliver Atom') - benji = create(:user, username: 'Benji Prince') - create(:comment, commentable: poll, user: oliver) - - login_as(benji) + click_link "Sign out" + login_as user visit poll_path(poll) + click_link 'Han Solo' - expect(page).to have_content oliver.username - end + expect(page).to_not have_link('Han Solo') + expect(page).to have_link('Chewbacca') - scenario 'user b can reply to comments from user a', :js do - koji = create(:user, username: 'Koji Kabuto') - sayaka = create(:user, username: 'Sayaka') - comment = create(:comment, commentable: poll, user: koji) - - login_as(sayaka) + click_link "Sign out" + login_as user visit poll_path(poll) + click_link 'Chewbacca' - click_link "Reply" - - within "#js-comment-form-comment_#{comment.id}" do - fill_in "comment-body-comment_#{comment.id}", with: 'MAZINGER!!.' - click_button 'Publish reply' - end - - within "#comment_#{comment.id}" do - expect(page).to have_content 'MAZINGER!!.' - end - - expect(page).to_not have_selector("#js-comment-form-comment_#{comment.id}", visible: true) - end - - scenario 'user can upvote a comment', :js do - goku = create(:user, username: 'Goku') - vegeta = create(:user, username: 'Vegeta') - comment = create(:comment, commentable: poll, user: goku) - - login_as(vegeta) - visit poll_path(poll) - - within("#comment_#{comment.id}_votes") do - find('.in_favor a').click - - expect(page).to have_content "1 vote" - end - end - - scenario 'user can downvote a comment', :js do - doraemon = create(:user, username: 'Doraemon') - nobita = create(:user, username: 'Nobi Nobita') - comment = create(:comment, commentable: poll, user: doraemon) - - login_as(nobita) - visit poll_path(poll) - - within("#comment_#{comment.id}_votes") do - find('.against a').click - - expect(page).to have_content "1 vote" - end + expect(page).to_not have_link('Chewbacca') + expect(page).to have_link('Han Solo') end end + + context 'Booth & Website' do + + let(:poll) { create(:poll, summary: "Summary", description: "Description") } + let(:booth) { create(:poll_booth) } + let(:officer) { create(:poll_officer) } + + scenario 'Already voted on booth cannot vote on website', :js do + + create(:poll_shift, officer: officer, booth: booth, date: Date.current, task: :vote_collection) + booth_assignment = create(:poll_booth_assignment, poll: poll, booth: booth) + create(:poll_officer_assignment, officer: officer, booth_assignment: booth_assignment) + question = create(:poll_question, poll: poll) + create(:poll_question_answer, question: question, title: 'Han Solo') + create(:poll_question_answer, question: question, title: 'Chewbacca') + user = create(:user, :level_two, :in_census) + + login_as(officer.user) + visit new_officing_residence_path + officing_verify_residence + click_button "Confirm vote" + + expect(page).to have_content "Vote introduced!" + + visit new_officing_residence_path + click_link "Sign out" + login_as user + visit poll_path(poll) + + expect(page).to have_content "You have already participated in a physical booth. You can not participate again." + + within("#poll_question_#{question.id}_answers") do + expect(page).to have_content('Han Solo') + expect(page).to have_content('Chewbacca') + + expect(page).to_not have_link('Han Solo') + expect(page).to_not have_link('Chewbacca') + end + end + + end end diff --git a/spec/features/polls/voter_spec.rb b/spec/features/polls/voter_spec.rb index 453924674..256807928 100644 --- a/spec/features/polls/voter_spec.rb +++ b/spec/features/polls/voter_spec.rb @@ -32,6 +32,11 @@ feature "Voter" do expect(page).to_not have_link('Yes') end + find(:css, ".js-token-message").should be_visible + token = find(:css, ".js-question-answer")[:href].gsub(/.+?(?=token)/, '').gsub('token=', '') + + expect(page).to have_content "You can write down this vote identifier, to check your vote on the final results: #{token}" + expect(Poll::Voter.count).to eq(1) expect(Poll::Voter.first.origin).to eq("web") end @@ -91,9 +96,34 @@ feature "Voter" do visit poll_path(poll) expect(page).to_not have_link('Yes') - expect(page).to have_content "You have already participated in a booth for this poll." + expect(page).to have_content "You have already participated in a physical booth. You can not participate again." expect(Poll::Voter.count).to eq(1) end + + scenario "Trying to vote in web again", :js do + login_as user + vote_for_poll_via_web(poll, question) + + visit poll_path(poll) + + expect(page).to_not have_selector('.js-token-message') + + expect(page).to have_content "You have already participated in this poll. If you vote again it will be overwritten." + within("#poll_question_#{question.id}_answers") do + expect(page).to_not have_link('Yes') + end + + click_link "Sign out" + + login_as user + visit poll_path(poll) + + within("#poll_question_#{question.id}_answers") do + expect(page).to have_link('Yes') + expect(page).to have_link('No') + end + + end end end diff --git a/spec/models/poll/answer_spec.rb b/spec/models/poll/answer_spec.rb index 2c27bc060..8731445cc 100644 --- a/spec/models/poll/answer_spec.rb +++ b/spec/models/poll/answer_spec.rb @@ -46,7 +46,7 @@ describe Poll::Answer do answer = create(:poll_answer, question: question, author: author, answer: "Yes") expect(answer.poll.voters).to be_blank - answer.record_voter_participation + answer.record_voter_participation('token') expect(poll.reload.voters.size).to eq(1) voter = poll.voters.first @@ -57,12 +57,12 @@ describe Poll::Answer do it "updates a poll_voter with user and poll data" do answer = create(:poll_answer, question: question, author: author, answer: "Yes") - answer.record_voter_participation + answer.record_voter_participation('token') expect(poll.reload.voters.size).to eq(1) answer = create(:poll_answer, question: question, author: author, answer: "No") - answer.record_voter_participation + answer.record_voter_participation('token') expect(poll.reload.voters.size).to eq(1) diff --git a/spec/models/poll/voter_spec.rb b/spec/models/poll/voter_spec.rb index f306248dc..ae0f84a49 100644 --- a/spec/models/poll/voter_spec.rb +++ b/spec/models/poll/voter_spec.rb @@ -76,7 +76,7 @@ describe :voter do it "should not be valid if the user has voted via web" do answer = create(:poll_answer) - answer.record_voter_participation + answer.record_voter_participation('token') voter = build(:poll_voter, poll: answer.question.poll, user: answer.author) expect(voter).to_not be_valid @@ -162,11 +162,12 @@ describe :voter do it "sets user info" do user = create(:user, document_number: "1234A", document_type: "1") - voter = build(:poll_voter, user: user) + voter = build(:poll_voter, user: user, token: "1234abcd") voter.save expect(voter.document_number).to eq("1234A") expect(voter.document_type).to eq("1") + expect(voter.token).to eq("1234abcd") end end -end \ No newline at end of file +end diff --git a/spec/shared/features/nested_documentable.rb b/spec/shared/features/nested_documentable.rb index 8780936a7..24d6631a8 100644 --- a/spec/shared/features/nested_documentable.rb +++ b/spec/shared/features/nested_documentable.rb @@ -191,9 +191,17 @@ shared_examples "nested documentable" do |login_as_name, documentable_factory_na documentable_attach_new_file(documentable_factory_name, 0, "spec/fixtures/files/empty.pdf") click_on submit_button + documentable_redirected_to_resource_show_or_navigate_to - expect(page).to have_content "Documents (1)" + expect(page).to have_content "Documents" + + find("#tab-documents-label").click + expect(page).to have_content "empty.pdf" + + #Review + #Doble check why the file is stored with a name different to empty.pdf + expect(page).to have_css("a[href$='.pdf']") end scenario "Should show resource with new document after successful creation with maximum allowed uploaded files", :js do @@ -263,7 +271,6 @@ shared_examples "nested documentable" do |login_as_name, documentable_factory_na end - end end @@ -275,7 +282,7 @@ rescue return end -def documentable_attach_new_file(documentable_factory_name, index, path, success = true) +def documentable_attach_new_file(_documentable_factory_name, index, path, success = true) click_link "Add new document" document = all(".document")[index] document_input = document.find("input[type=file]", visible: false) @@ -318,8 +325,3 @@ def documentable_fill_new_valid_budget_investment fill_in_ckeditor "budget_investment_description", with: "Budget investment description" check :budget_investment_terms_of_service end - -def documentable_fill_new_valid_poll_question - page.select documentable.poll.name, from: 'poll_question_poll_id' - fill_in 'poll_question_title', with: "Star Wars: Episode IV - A New Hope" -end diff --git a/spec/shared/features/nested_imageable.rb b/spec/shared/features/nested_imageable.rb index bd519a5bb..1eef9a269 100644 --- a/spec/shared/features/nested_imageable.rb +++ b/spec/shared/features/nested_imageable.rb @@ -146,7 +146,11 @@ shared_examples "nested imageable" do |imageable_factory_name, path, imageable_p send(fill_resource_method_name) if fill_resource_method_name click_on submit_button - expect(page).to have_content imageable_success_notice + if has_many_images + skip "no need to test, there are no attributes for the parent resource" + else + expect(page).to have_content imageable_success_notice + end end scenario "Should show successful notice when resource filled correctly and after valid file uploads", :js do