Merge pull request #3345 from consul/backport-polls-officer-for-voter

Add officing booths
This commit is contained in:
Javier Martín
2019-04-03 18:26:05 +02:00
committed by GitHub
36 changed files with 505 additions and 67 deletions

View File

@@ -1,12 +1,46 @@
class Officing::BaseController < ApplicationController class Officing::BaseController < ApplicationController
layout "admin" layout "admin"
helper_method :current_booth
before_action :authenticate_user! before_action :authenticate_user!
before_action :verify_officer before_action :verify_officer
skip_authorization_check skip_authorization_check
private
def verify_officer def verify_officer
raise CanCan::AccessDenied unless current_user.try(:poll_officer?) raise CanCan::AccessDenied unless current_user.try(:poll_officer?)
end end
def load_officer_assignment
@officer_assignments ||= current_user.poll_officer.
officer_assignments.
voting_days.
where(date: Time.current.to_date)
end
def verify_officer_assignment
if @officer_assignments.blank?
redirect_to officing_root_path, notice: t("officing.residence.flash.not_allowed")
end
end
def verify_booth
return unless current_booth.blank?
booths = current_user.poll_officer.todays_booths
case booths.count
when 0
redirect_to officing_root_path
when 1
session[:booth_id] = booths.first.id
else
redirect_to new_officing_booth_path
end
end
def current_booth
Poll::Booth.where(id: session[:booth_id]).first
end
end end

View File

@@ -0,0 +1,24 @@
class Officing::BoothController < Officing::BaseController
before_action :load_officer_assignment
before_action :verify_officer_assignment
def new
@booths = current_user.poll_officer.todays_booths
end
def create
set_booth(Poll::Booth.find(booth_params[:id]))
redirect_to officing_root_path
end
private
def booth_params
params.require(:booth).permit(:id)
end
def set_booth(booth)
session[:booth_id] = booth.id
end
end

View File

@@ -1,4 +1,5 @@
class Officing::PollsController < Officing::BaseController class Officing::PollsController < Officing::BaseController
before_action :verify_booth
def index def index
@polls = current_user.poll_officer? ? current_user.poll_officer.voting_days_assigned_polls : [] @polls = current_user.poll_officer? ? current_user.poll_officer.voting_days_assigned_polls : []

View File

@@ -1,7 +1,8 @@
class Officing::ResidenceController < Officing::BaseController class Officing::ResidenceController < Officing::BaseController
before_action :load_officer_assignment before_action :load_officer_assignment
before_action :validate_officer_assignment, only: :create before_action :verify_officer_assignment
before_action :verify_booth
def new def new
@residence = Officing::Residence.new @residence = Officing::Residence.new
@@ -22,16 +23,4 @@ class Officing::ResidenceController < Officing::BaseController
params.require(:residence).permit(:document_number, :document_type, :year_of_birth) params.require(:residence).permit(:document_number, :document_type, :year_of_birth)
end end
def load_officer_assignment
@officer_assignments = current_user.poll_officer.
officer_assignments.
voting_days.
where(date: Date.current)
end
def validate_officer_assignment
if @officer_assignments.blank?
redirect_to officing_root_path, notice: t("officing.residence.flash.not_allowed")
end
end
end end

View File

@@ -7,6 +7,7 @@ class Officing::ResultsController < Officing::BaseController
before_action :load_officer_assignment, only: :create before_action :load_officer_assignment, only: :create
before_action :check_officer_assignment, only: :create before_action :check_officer_assignment, only: :create
before_action :build_results, only: :create before_action :build_results, only: :create
before_action :verify_booth
def new def new
end end

View File

@@ -1,6 +1,10 @@
class Officing::VotersController < Officing::BaseController class Officing::VotersController < Officing::BaseController
respond_to :html, :js respond_to :html, :js
before_action :load_officer_assignment
before_action :verify_officer_assignment
before_action :verify_booth
def new def new
@user = User.find(params[:id]) @user = User.find(params[:id])
booths = current_user.poll_officer.shifts.current.vote_collection.pluck(:booth_id).uniq booths = current_user.poll_officer.shifts.current.vote_collection.pluck(:booth_id).uniq
@@ -15,7 +19,9 @@ class Officing::VotersController < Officing::BaseController
user: @user, user: @user,
poll: @poll, poll: @poll,
origin: "booth", origin: "booth",
officer: current_user.poll_officer) officer: current_user.poll_officer,
booth_assignment: Poll::BoothAssignment.where(poll: @poll, booth: current_booth).first,
officer_assignment: officer_assignment(@poll))
@voter.save! @voter.save!
end end
@@ -25,4 +31,13 @@ class Officing::VotersController < Officing::BaseController
params.require(:voter).permit(:poll_id, :user_id) params.require(:voter).permit(:poll_id, :user_id)
end end
def officer_assignment(poll)
Poll::OfficerAssignment.by_officer(current_user.poll_officer)
.by_poll(poll)
.by_booth(current_booth)
.by_date(Date.current)
.where(final: false)
.first
end
end end

View File

@@ -3,7 +3,9 @@ class Users::SessionsController < Devise::SessionsController
private private
def after_sign_in_path_for(resource) def after_sign_in_path_for(resource)
if !verifying_via_email? && resource.show_welcome_screen? if current_user.poll_officer?
new_officing_booth_path
elsif !verifying_via_email? && resource.show_welcome_screen?
welcome_path welcome_path
else else
super super

View File

@@ -5,11 +5,15 @@ module OfficersHelper
end end
def vote_collection_shift? def vote_collection_shift?
current_user.poll_officer.officer_assignments.where(date: Time.current.to_date).any? current_user.poll_officer.officer_assignments.voting_days.where(date: Time.current.to_date).any?
end end
def final_recount_shift? def final_recount_shift?
current_user.poll_officer.officer_assignments.final.where(date: Time.current.to_date).any? current_user.poll_officer.officer_assignments.final.where(date: Time.current.to_date).any?
end end
def no_shifts?
current_user.poll_officer.officer_assignments.where(date: Time.current.to_date).blank?
end
end end

View File

@@ -23,5 +23,9 @@ class Poll
sort {|x, y| y.ends_at <=> x.ends_at} sort {|x, y| y.ends_at <=> x.ends_at}
end end
def todays_booths
officer_assignments.by_date(Date.current).map(&:booth).uniq
end
end end
end end

View File

@@ -17,11 +17,20 @@ class Poll
scope :by_officer_and_poll, ->(officer_id, poll_id) do scope :by_officer_and_poll, ->(officer_id, poll_id) do
where("officer_id = ? AND poll_booth_assignments.poll_id = ?", officer_id, poll_id) where("officer_id = ? AND poll_booth_assignments.poll_id = ?", officer_id, poll_id)
end end
scope :by_officer, ->(officer){ where(officer_id: officer.id) }
scope :by_poll, ->(poll){ joins(:booth_assignment).where("poll_booth_assignments.poll_id" => poll.id) }
scope :by_booth, ->(booth){ joins(:booth_assignment).where("poll_booth_assignments.booth_id" => booth.id) }
scope :by_date, ->(date){ where(date: date) }
before_create :log_user_data before_create :log_user_data
def log_user_data def log_user_data
self.user_data_log = "#{officer.user_id} - #{officer.user.name_and_email}" self.user_data_log = "#{officer.user_id} - #{officer.user.name_and_email}"
end end
def booth
booth_assignment.booth
end
end end
end end

View File

@@ -12,11 +12,13 @@ class Poll
validates :poll_id, presence: true validates :poll_id, presence: true
validates :user_id, presence: true validates :user_id, presence: true
validates :booth_assignment_id, presence: true, if: ->(voter) { voter.origin == "booth" }
validates :officer_assignment_id, presence: true, if: ->(voter) { voter.origin == "booth" }
validates :document_number, presence: true, uniqueness: { scope: [:poll_id, :document_type], message: :has_voted } validates :document_number, presence: true, uniqueness: { scope: [:poll_id, :document_type], message: :has_voted }
validates :origin, inclusion: { in: VALID_ORIGINS } validates :origin, inclusion: { in: VALID_ORIGINS }
before_validation :set_demographic_info, :set_document_info before_validation :set_demographic_info, :set_document_info, :set_denormalized_booth_assignment_id
scope :web, -> { where(origin: "web") } scope :web, -> { where(origin: "web") }
scope :booth, -> { where(origin: "booth") } scope :booth, -> { where(origin: "booth") }
@@ -38,6 +40,10 @@ class Poll
private private
def set_denormalized_booth_assignment_id
self.booth_assignment_id ||= officer_assignment.try(:booth_assignment_id)
end
def in_census? def in_census?
census_api_response.valid? census_api_response.valid?
end end

View File

@@ -43,14 +43,14 @@
<%= link_to booth_assignment.booth.name, admin_poll_booth_assignment_path(@poll, booth_assignment, anchor: "tab-recounts") %> <%= link_to booth_assignment.booth.name, admin_poll_booth_assignment_path(@poll, booth_assignment, anchor: "tab-recounts") %>
</strong> </strong>
</td> </td>
<td class="text-center <%= "count-error" if total_recounts.to_i != system_count %>"> <td class="text-center <%= "count-error" if total_recounts.to_i != system_count %>" id="<%= dom_id(booth_assignment) %>_recount">
<% if total_recounts.present? %> <% if total_recounts.present? %>
<strong><%= total_recounts %></strong> <strong><%= total_recounts %></strong>
<% else %> <% else %>
<span>-</span> <span>-</span>
<% end %> <% end %>
</td> </td>
<td class="text-center"> <td class="text-center" id="<%= dom_id(booth_assignment) %>_system">
<% if system_count.present? %> <% if system_count.present? %>
<strong><%= system_count %></strong> <strong><%= system_count %></strong>
<% else %> <% else %>

View File

@@ -0,0 +1,5 @@
<% if current_user.poll_officer? %>
<div id="officing-booth" class="callout info">
<%= t("admin.officing_booth.title", booth: try(:current_booth).try(:location)) %>
</div>
<% end %>

View File

@@ -33,6 +33,7 @@
<div class="admin-content small-12 medium-9 column" data-equalizer-watch> <div class="admin-content small-12 medium-9 column" data-equalizer-watch>
<%= render "layouts/flash" %> <%= render "layouts/flash" %>
<%= render "layouts/officing_booth" %>
<%= yield %> <%= yield %>
</div> </div>
</div> </div>

View File

@@ -0,0 +1,25 @@
<div class="row margin-top">
<div class="small-12 medium-6 column small-centered">
<div class="panel margin-top">
<h1 class="text-center">
<%= t("officing.booth.new.title") %>
</h1>
<%= form_for Poll::Booth.new,
as: :booth,
url: officing_booth_path,
method: :post do |f| %>
<div class="row">
<div class="small-12 column">
<%= f.select :id,
@booths.collect { |booth| [booth.location, booth.id] },
selected: @booths.first,
label: false,
tabindex: "1" %>
<%= f.submit(t("devise_views.sessions.new.submit"), class: "button expanded") %>
</div>
</div>
<% end %>
</div>
</div>
</div>

View File

@@ -3,7 +3,7 @@
<p><%= t("officing.dashboard.index.info") %></p> <p><%= t("officing.dashboard.index.info") %></p>
<% unless final_recount_shift? && vote_collection_shift? %> <% if no_shifts? %>
<div class="callout warning"> <div class="callout warning">
<%= t("officing.dashboard.index.no_shifts") %> <%= t("officing.dashboard.index.no_shifts") %>
</div> </div>

View File

@@ -1,6 +1,5 @@
<h2><%= t("officing.residence.new.title") %></h2> <h2><%= t("officing.residence.new.title") %></h2>
<% if @officer_assignments.present? %>
<div class="row verification account"> <div class="row verification account">
<div class="small-12 medium-8 column"> <div class="small-12 medium-8 column">
<%= form_for @residence, as: "residence", url: officing_residence_path do |f| %> <%= form_for @residence, as: "residence", url: officing_residence_path do |f| %>
@@ -9,7 +8,6 @@
<div class="small-12 medium-6"> <div class="small-12 medium-6">
<%= f.select :document_type, document_types, prompt: "" %> <%= f.select :document_type, document_types, prompt: "" %>
<%= f.text_field :document_number, <%= f.text_field :document_number,
placeholder: t("officing.residence.new.document_number"), placeholder: t("officing.residence.new.document_number"),
autocomplete: "off" %> autocomplete: "off" %>
@@ -25,8 +23,3 @@
<% end %> <% end %>
</div> </div>
</div> </div>
<% else %>
<div class="callout primary">
<%= t("officing.residence.new.no_assignments") %>
</div>
<% end %>

View File

@@ -1,3 +1,4 @@
$("#<%= dom_id(@poll) %> #actions").html("<%= j render("voted") %>"); $("#<%= dom_id(@poll) %> #actions").html("<%= j render("voted") %>");
$("#<%= dom_id(@poll) %> #can_vote_callout").hide(); $("#<%= dom_id(@poll) %> #can_vote_callout").hide();
$("#not_voted").hide();
$(".js-vote-collection").removeClass("is-hidden"); $(".js-vote-collection").removeClass("is-hidden");

View File

@@ -28,4 +28,8 @@
</table> </table>
<% end %> <% end %>
<% if Poll.votable_by(@user).any? %>
<div id="not_voted">
<%= link_to t("officing.voters.new.not_to_vote"), namespaced_root_path, class: "button" %> <%= link_to t("officing.voters.new.not_to_vote"), namespaced_root_path, class: "button" %>
</div>
<% end %>

View File

@@ -14,6 +14,8 @@ en:
edit: Edit edit: Edit
configure: Configure configure: Configure
delete: Delete delete: Delete
officing_booth:
title: "You are officing the booth located at %{booth}. If this is not correct, do not continue and call the help phone number. Thank you."
banners: banners:
index: index:
title: Banners title: Banners

View File

@@ -16,6 +16,9 @@ en:
no_polls: You are not officing final recounts in any active poll no_polls: You are not officing final recounts in any active poll
select_poll: Select poll select_poll: Select poll
add_results: Add results add_results: Add results
booth:
new:
title: "Choose your booth"
results: results:
flash: flash:
create: "Results saved" create: "Results saved"
@@ -51,7 +54,6 @@ en:
submit: Validate document submit: Validate document
error_verifying_census: "The Census was unable to verify this document." error_verifying_census: "The Census was unable to verify this document."
form_errors: prevented the verification of this document form_errors: prevented the verification of this document
no_assignments: "You don't have officing shifts today"
voters: voters:
new: new:
title: Polls title: Polls

View File

@@ -14,6 +14,8 @@ es:
edit: Editar edit: Editar
configure: Configurar configure: Configurar
delete: Borrar delete: Borrar
officing_booth:
title: "Estás ahora mismo en la mesa ubicada en %{booth}. Si esto no es correcto no sigas adelante y llama al teléfono de incidencias. Gracias."
banners: banners:
index: index:
title: Banners title: Banners

View File

@@ -16,6 +16,9 @@ es:
no_polls: No tienes permiso para recuento final en ninguna votación reciente no_polls: No tienes permiso para recuento final en ninguna votación reciente
select_poll: Selecciona votación select_poll: Selecciona votación
add_results: Añadir resultados add_results: Añadir resultados
booth:
new:
title: "Escoge tu urna"
results: results:
flash: flash:
create: "Datos guardados" create: "Datos guardados"
@@ -51,7 +54,6 @@ es:
submit: Validar documento submit: Validar documento
error_verifying_census: "El Padrón no pudo verificar este documento." error_verifying_census: "El Padrón no pudo verificar este documento."
form_errors: evitaron verificar este documento form_errors: evitaron verificar este documento
no_assignments: "Hoy no tienes turno de presidente de mesa"
voters: voters:
new: new:
title: Votaciones title: Votaciones

View File

@@ -4,6 +4,7 @@ namespace :officing do
resources :results, only: [:new, :create, :index] resources :results, only: [:new, :create, :index]
end end
resource :booth, controller: "booth", only: [:new, :create]
resource :residence, controller: "residence", only: [:new, :create] resource :residence, controller: "residence", only: [:new, :create]
resources :voters, only: [:new, :create] resources :voters, only: [:new, :create]
root to: "dashboard#index" root to: "dashboard#index"

View File

@@ -42,24 +42,24 @@ end
section "Creating Poll Questions & Answers" do section "Creating Poll Questions & Answers" do
Poll.find_each do |poll| Poll.find_each do |poll|
(1..4).to_a.sample.times do (1..4).to_a.sample.times do
title = Faker::Lorem.sentence(3).truncate(60) + "?" question_title = Faker::Lorem.sentence(3).truncate(60) + "?"
question = Poll::Question.new(author: User.all.sample, question = Poll::Question.new(author: User.all.sample,
title: title, title: question_title,
poll: poll) poll: poll)
I18n.available_locales.map do |locale| I18n.available_locales.map do |locale|
Globalize.with_locale(locale) do Globalize.with_locale(locale) do
question.title = "#{title} (#{locale})" question.title = "#{question_title} (#{locale})"
end end
end end
question.save! question.save!
Faker::Lorem.words((2..4).to_a.sample).each do |title| Faker::Lorem.words((2..4).to_a.sample).each do |answer_title|
description = "<p>#{Faker::Lorem.paragraphs.join("</p><p>")}</p>" description = "<p>#{Faker::Lorem.paragraphs.join("</p><p>")}</p>"
answer = Poll::Question::Answer.new(question: question, answer = Poll::Question::Answer.new(question: question,
title: title.capitalize, title: answer_title.capitalize,
description: description) description: description)
I18n.available_locales.map do |locale| I18n.available_locales.map do |locale|
Globalize.with_locale(locale) do Globalize.with_locale(locale) do
answer.title = "#{title} (#{locale})" answer.title = "#{answer_title} (#{locale})"
answer.description = "#{description} (#{locale})" answer.description = "#{description} (#{locale})"
end end
end end
@@ -114,12 +114,16 @@ end
section "Creating Poll Voters" do section "Creating Poll Voters" do
def vote_poll_on_booth(user, poll) def vote_poll_on_booth(user, poll)
officer = Poll::Officer.all.sample
Poll::Voter.create!(document_type: user.document_type, Poll::Voter.create!(document_type: user.document_type,
document_number: user.document_number, document_number: user.document_number,
user: user, user: user,
poll: poll, poll: poll,
origin: "booth", origin: "booth",
officer: Poll::Officer.all.sample) officer: officer,
officer_assignment: officer.officer_assignments.sample,
booth_assignment: poll.booth_assignments.sample)
end end
def vote_poll_on_web(user, poll) def vote_poll_on_web(user, poll)
@@ -142,11 +146,11 @@ section "Creating Poll Voters" do
end end
(Poll.expired + Poll.current + Poll.recounting).uniq.each do |poll| (Poll.expired + Poll.current + Poll.recounting).uniq.each do |poll|
level_two_verified_users = User.level_two_verified verified_users = User.level_two_or_three_verified
if poll.geozone_restricted? if poll.geozone_restricted?
level_two_verified_users = level_two_verified_users.where(geozone_id: poll.geozone_ids) verified_users = verified_users.where(geozone_id: poll.geozone_ids)
end end
user_groups = level_two_verified_users.in_groups(2) user_groups = verified_users.in_groups(2)
user_groups.first.each { |user| vote_poll_on_booth(user, poll) } user_groups.first.each { |user| vote_poll_on_booth(user, poll) }
user_groups.second.compact.each { |user| vote_poll_on_web(user, poll) } user_groups.second.compact.each { |user| vote_poll_on_web(user, poll) }
end end
@@ -158,13 +162,23 @@ section "Creating Poll Recounts" do
officer_assignment = poll.officer_assignments.first officer_assignment = poll.officer_assignments.first
author = Poll::Officer.first.user author = Poll::Officer.first.user
total_amount = white_amount = null_amount = 0
booth_assignment.voters.count.times do
case rand
when 0...0.1 then null_amount += 1
when 0.1...0.2 then white_amount += 1
else total_amount += 1
end
end
Poll::Recount.create!(officer_assignment: officer_assignment, Poll::Recount.create!(officer_assignment: officer_assignment,
booth_assignment: booth_assignment, booth_assignment: booth_assignment,
author: author, author: author,
date: poll.ends_at, date: poll.ends_at,
white_amount: rand(0..10), white_amount: white_amount,
null_amount: rand(0..10), null_amount: null_amount,
total_amount: rand(100..9999), total_amount: total_amount,
origin: "booth") origin: "booth")
end end
end end

View File

@@ -8,7 +8,7 @@ section "Creating Users" do
password_confirmation: password, password_confirmation: password,
confirmed_at: Time.current, confirmed_at: Time.current,
terms_of_service: "1", terms_of_service: "1",
gender: ["Male", "Female"].sample, gender: %w[male female].sample,
date_of_birth: rand((Time.current - 80.years)..(Time.current - 16.years)), date_of_birth: rand((Time.current - 80.years)..(Time.current - 16.years)),
public_activity: (rand(1..100) > 30) public_activity: (rand(1..100) > 30)
) )

View File

@@ -2,7 +2,7 @@ require "rails_helper"
feature "Budget Poll Officing" do feature "Budget Poll Officing" do
scenario "Show sidebar menu if officer has shifts assigned" do scenario "Show sidebar menus if officer has shifts assigned" do
poll = create(:poll) poll = create(:poll)
booth = create(:poll_booth) booth = create(:poll_booth)
booth_assignment = create(:poll_booth_assignment, poll: poll, booth: booth) booth_assignment = create(:poll_booth_assignment, poll: poll, booth: booth)
@@ -10,13 +10,21 @@ feature "Budget Poll Officing" do
user = create(:user) user = create(:user)
officer = create(:poll_officer, user: user) officer = create(:poll_officer, user: user)
create(:poll_shift, officer: officer, booth: booth, date: Date.current, task: :vote_collection)
login_as user
visit officing_root_path
expect(page).not_to have_content("You don't have officing shifts today")
expect(page).to have_content("Validate document")
expect(page).not_to have_content("Total recounts and results")
create(:poll_shift, officer: officer, booth: booth, date: Date.current, task: :recount_scrutiny) create(:poll_shift, officer: officer, booth: booth, date: Date.current, task: :recount_scrutiny)
officer_assignment = create(:poll_officer_assignment, officer_assignment = create(:poll_officer_assignment,
booth_assignment: booth_assignment, booth_assignment: booth_assignment,
officer: officer) officer: officer)
login_as user
visit officing_root_path visit officing_root_path
expect(page).not_to have_content("You don't have officing shifts today") expect(page).not_to have_content("You don't have officing shifts today")
@@ -24,7 +32,7 @@ feature "Budget Poll Officing" do
expect(page).to have_content("Total recounts and results") expect(page).to have_content("Total recounts and results")
end end
scenario "Do not show sidebar menu if officer has no shifts assigned" do scenario "Do not show sidebar menus if officer has no shifts assigned" do
user = create(:user) user = create(:user)
officer = create(:poll_officer, user: user) officer = create(:poll_officer, user: user)

View File

@@ -0,0 +1,87 @@
require "rails_helper"
feature "Booth", :with_frozen_time do
scenario "Officer with no booth assignments today" do
officer = create(:poll_officer)
login_through_form_as_officer(officer.user)
expect(page).to have_content "You don't have officing shifts today"
end
scenario "Officer with booth assignments another day" do
officer = create(:poll_officer)
create(:poll_officer_assignment, officer: officer, date: Date.current + 1.day)
login_through_form_as_officer(officer.user)
expect(page).to have_content "You don't have officing shifts today"
end
scenario "Officer with single booth assignment today" do
officer = create(:poll_officer)
poll = create(:poll)
booth = create(:poll_booth)
booth_assignment = create(:poll_booth_assignment, poll: poll, booth: booth)
create(:poll_officer_assignment, officer: officer, booth_assignment: booth_assignment, date: Date.current)
login_through_form_as_officer(officer.user)
within("#officing-booth") do
expect(page).to have_content "You are officing the booth located at #{booth.location}."
end
end
scenario "Officer with multiple booth assignments today" do
officer = create(:poll_officer)
poll = create(:poll)
booth1 = create(:poll_booth)
booth2 = create(:poll_booth)
ba1 = create(:poll_booth_assignment, poll: poll, booth: booth1)
ba2 = create(:poll_booth_assignment, poll: poll, booth: booth2)
create(:poll_officer_assignment, officer: officer, booth_assignment: ba1, date: Date.current)
create(:poll_officer_assignment, officer: officer, booth_assignment: ba2, date: Date.current)
login_through_form_as_officer(officer.user)
expect(page).to have_content "Choose your booth"
select booth2.location, from: "booth_id"
click_button "Enter"
within("#officing-booth") do
expect(page).to have_content "You are officing the booth located at #{booth2.location}."
end
end
scenario "Display single booth for any number of polls" do
officer = create(:poll_officer)
booth1 = create(:poll_booth)
booth2 = create(:poll_booth)
poll1 = create(:poll)
poll2 = create(:poll)
ba1 = create(:poll_booth_assignment, poll: poll1, booth: booth1)
ba2 = create(:poll_booth_assignment, poll: poll2, booth: booth2)
ba3 = create(:poll_booth_assignment, poll: poll2, booth: booth2)
create(:poll_officer_assignment, officer: officer, booth_assignment: ba1, date: Date.current)
create(:poll_officer_assignment, officer: officer, booth_assignment: ba2, date: Date.current)
create(:poll_officer_assignment, officer: officer, booth_assignment: ba3, date: Date.current)
login_through_form_as_officer(officer.user)
expect(page).to have_content "Choose your booth"
expect(page).to have_select("booth_id", options: [booth1.location, booth2.location])
end
end

View File

@@ -25,7 +25,7 @@ feature "Residence", :with_frozen_time do
background do background do
create(:poll_officer_assignment, officer: officer) create(:poll_officer_assignment, officer: officer)
login_as(officer.user) login_through_form_as_officer(officer.user)
visit officing_root_path visit officing_root_path
end end
@@ -93,4 +93,27 @@ feature "Residence", :with_frozen_time do
end end
scenario "Verify booth", :js do
booth = create(:poll_booth)
poll = create(:poll)
ba = create(:poll_booth_assignment, poll: poll, booth: booth)
create(:poll_officer_assignment, officer: officer, booth_assignment: ba)
create(:poll_shift, officer: officer, booth: booth, date: Date.current)
login_as(officer.user)
visit new_officing_residence_path
within("#officing-booth") do
expect(page).to have_content "You are officing the booth located at #{booth.location}."
end
officing_verify_residence
expect(page).to have_content poll.name
click_button "Confirm vote"
expect(page).to have_content "Vote introduced!"
end
end end

View File

@@ -5,6 +5,7 @@ feature "Officing Results", :with_frozen_time do
background do background do
@poll_officer = create(:poll_officer) @poll_officer = create(:poll_officer)
@officer_assignment = create(:poll_officer_assignment, :final, officer: @poll_officer) @officer_assignment = create(:poll_officer_assignment, :final, officer: @poll_officer)
create(:poll_shift, officer: @poll_officer, booth: @officer_assignment.booth, date: Date.current)
@poll = @officer_assignment.booth_assignment.poll @poll = @officer_assignment.booth_assignment.poll
@poll.update(ends_at: 1.day.ago) @poll.update(ends_at: 1.day.ago)
@question_1 = create(:poll_question, poll: @poll) @question_1 = create(:poll_question, poll: @poll)
@@ -15,6 +16,7 @@ feature "Officing Results", :with_frozen_time do
create(:poll_question_answer, title: "Tomorrow", question: @question_2) create(:poll_question_answer, title: "Tomorrow", question: @question_2)
login_as(@poll_officer.user) login_as(@poll_officer.user)
set_officing_booth(@officer_assignment.booth)
end end
scenario "Only polls where user is officer for results are accessible" do scenario "Only polls where user is officer for results are accessible" do
@@ -30,6 +32,7 @@ feature "Officing Results", :with_frozen_time do
click_link "Polling officers" click_link "Polling officers"
expect(page).to have_content("Poll officing") expect(page).to have_content("Poll officing")
within("#side_menu") do within("#side_menu") do
click_link "Total recounts and results" click_link "Total recounts and results"
end end

View File

@@ -12,9 +12,12 @@ feature "Voters" do
create(:poll_shift, officer: officer, booth: booth, date: Date.current, task: :vote_collection) create(:poll_shift, officer: officer, booth: booth, date: Date.current, task: :vote_collection)
booth_assignment = create(:poll_booth_assignment, poll: poll, booth: booth) booth_assignment = create(:poll_booth_assignment, poll: poll, booth: booth)
create(:poll_officer_assignment, officer: officer, booth_assignment: booth_assignment) create(:poll_officer_assignment, officer: officer, booth_assignment: booth_assignment)
set_officing_booth(booth)
end end
scenario "Can vote", :js do scenario "Can vote", :js do
create(:poll_officer_assignment, officer: officer)
visit new_officing_residence_path visit new_officing_residence_path
officing_verify_residence officing_verify_residence
@@ -57,6 +60,10 @@ feature "Voters" do
user = create(:user, residence_verified_at: Time.current, document_type: "1", document_number: "12345678Z") user = create(:user, residence_verified_at: Time.current, document_type: "1", document_number: "12345678Z")
expect(user).not_to be_level_two_verified expect(user).not_to be_level_two_verified
visit root_path
click_link "Sign out"
login_through_form_as_officer(officer.user)
visit new_officing_residence_path visit new_officing_residence_path
officing_verify_residence officing_verify_residence
@@ -83,6 +90,7 @@ feature "Voters" do
booth_assignment = create(:poll_booth_assignment, poll: poll_geozone_restricted_out, booth: booth) booth_assignment = create(:poll_booth_assignment, poll: poll_geozone_restricted_out, booth: booth)
create(:poll_officer_assignment, officer: officer, booth_assignment: booth_assignment) create(:poll_officer_assignment, officer: officer, booth_assignment: booth_assignment)
set_officing_booth(second_booth)
visit new_officing_residence_path visit new_officing_residence_path
officing_verify_residence officing_verify_residence
@@ -93,4 +101,44 @@ feature "Voters" do
expect(page).to have_content poll_geozone_restricted_in.name expect(page).to have_content poll_geozone_restricted_in.name
expect(page).not_to have_content poll_geozone_restricted_out.name expect(page).not_to have_content poll_geozone_restricted_out.name
end end
scenario "Store officer and booth information", :js do
create(:user, :in_census, id: rand(9999999))
poll1 = create(:poll, name: "¿Quieres que XYZ sea aprobado?")
poll2 = create(:poll, name: "Pregunta de votación de prueba")
second_booth = create(:poll_booth)
ba1 = create(:poll_booth_assignment, poll: poll1, booth: second_booth )
ba2 = create(:poll_booth_assignment, poll: poll2, booth: second_booth )
create(:poll_shift, officer: officer, booth: second_booth, date: Date.current, task: :vote_collection)
validate_officer
visit new_officing_residence_path
set_officing_booth(second_booth)
officing_verify_residence
within("#poll_#{poll1.id}") do
click_button "Confirm vote"
expect(page).to have_content "Vote introduced!"
end
within("#poll_#{poll2.id}") do
click_button "Confirm vote"
expect(page).to have_content "Vote introduced!"
end
expect(Poll::Voter.count).to eq(2)
voter1 = Poll::Voter.first
expect(voter1.booth_assignment).to eq(ba1)
expect(voter1.officer_assignment).to eq(ba1.officer_assignments.first)
voter2 = Poll::Voter.last
expect(voter2.booth_assignment).to eq(ba2)
expect(voter2.officer_assignment).to eq(ba2.officer_assignments.first)
end
end end

View File

@@ -97,6 +97,7 @@ feature "Poll Officing" do
end end
scenario "Poll officer access links" do scenario "Poll officer access links" do
create(:poll)
create(:poll_officer, user: user) create(:poll_officer, user: user)
login_as(user) login_as(user)
visit root_path visit root_path

View File

@@ -8,6 +8,7 @@ feature "Voter" do
let(:question) { create(:poll_question, poll: poll) } let(:question) { create(:poll_question, poll: poll) }
let(:booth) { create(:poll_booth) } let(:booth) { create(:poll_booth) }
let(:officer) { create(:poll_officer) } let(:officer) { create(:poll_officer) }
let(:admin) { create(:administrator) }
let!(:answer_yes) { create(:poll_question_answer, question: question, title: "Yes") } let!(:answer_yes) { create(:poll_question_answer, question: question, title: "Yes") }
let!(:answer_no) { create(:poll_question_answer, question: question, title: "No") } let!(:answer_no) { create(:poll_question_answer, question: question, title: "No") }
@@ -71,6 +72,56 @@ feature "Voter" do
expect(Poll::Voter.count).to eq(1) expect(Poll::Voter.count).to eq(1)
expect(Poll::Voter.first.origin).to eq("booth") expect(Poll::Voter.first.origin).to eq("booth")
visit root_path
click_link "Sign out"
login_as(admin.user)
visit admin_poll_recounts_path(poll)
within("#total_system") do
expect(page).to have_content "1"
end
within("#poll_booth_assignment_#{Poll::BoothAssignment.where(poll: poll, booth: booth).first.id}_recounts") do
expect(page).to have_content "1"
end
end
context "The person has decided not to vote at this time" do
let!(:user) { create(:user, :in_census) }
scenario "Show not to vote at this time button" do
login_through_form_as_officer(officer.user)
visit new_officing_residence_path
officing_verify_residence
expect(page).to have_content poll.name
expect(page).to have_button "Confirm vote"
expect(page).to have_content "Can vote"
expect(page).to have_link "The person has decided not to vote at this time"
end
scenario "Hides not to vote at this time button if already voted", :js do
login_through_form_as_officer(officer.user)
visit new_officing_residence_path
officing_verify_residence
within("#poll_#{poll.id}") do
click_button("Confirm vote")
expect(page).not_to have_button("Confirm vote")
expect(page).to have_content "Vote introduced!"
expect(page).not_to have_content "The person has decided not to vote at this time"
end
visit new_officing_residence_path
officing_verify_residence
expect(page).to have_content "Has already participated in this poll"
expect(page).not_to have_content "The person has decided not to vote at this time"
end
end end
context "Trying to vote the same poll in booth and web" do context "Trying to vote the same poll in booth and web" do
@@ -107,6 +158,19 @@ feature "Voter" do
expect(page).not_to have_link(answer_yes.title) expect(page).not_to have_link(answer_yes.title)
expect(page).to have_content "You have already participated in a physical booth. You can not participate again." 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) expect(Poll::Voter.count).to eq(1)
visit root_path
click_link "Sign out"
login_as(admin.user)
visit admin_poll_recounts_path(poll)
within("#total_system") do
expect(page).to have_content "1"
end
within("#poll_booth_assignment_#{Poll::BoothAssignment.where(poll: poll, booth: booth).first.id}_recounts") do
expect(page).to have_content "1"
end
end end
scenario "Trying to vote in web again", :js do scenario "Trying to vote in web again", :js do
@@ -162,6 +226,19 @@ feature "Voter" do
expect(page).not_to have_link(answer_yes.title) expect(page).not_to have_link(answer_yes.title)
expect(page).to have_content "You have already participated in a physical booth. You can not participate again." 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) expect(Poll::Voter.count).to eq(1)
visit root_path
click_link "Sign out"
login_as(admin.user)
visit admin_poll_recounts_path(poll)
within("#total_system") do
expect(page).to have_content "1"
end
within("#poll_booth_assignment_#{Poll::BoothAssignment.where(poll: poll, booth: booth).first.id}_recounts") do
expect(page).to have_content "1"
end
end end
context "Side menu" do context "Side menu" do

View File

@@ -121,4 +121,21 @@ describe Poll::Officer do
expect(assigned_polls.last).to eq(poll_3) expect(assigned_polls.last).to eq(poll_3)
end end
end end
describe "todays_booths" do
let(:officer) { create(:poll_officer) }
it "returns booths for the application's time zone date", :with_different_time_zone do
assignment_with_local_time_zone = create(:poll_officer_assignment,
date: Date.today,
officer: officer)
assignment_with_application_time_zone = create(:poll_officer_assignment,
date: Date.current,
officer: officer)
expect(officer.todays_booths).to include(assignment_with_application_time_zone.booth)
expect(officer.todays_booths).not_to include(assignment_with_local_time_zone.booth)
end
end
end end

View File

@@ -6,6 +6,7 @@ describe Poll::Voter do
let(:booth) { create(:poll_booth) } let(:booth) { create(:poll_booth) }
let(:booth_assignment) { create(:poll_booth_assignment, poll: poll, booth: booth) } let(:booth_assignment) { create(:poll_booth_assignment, poll: poll, booth: booth) }
let(:voter) { create(:poll_voter) } let(:voter) { create(:poll_voter) }
let(:officer_assignment) { create(:poll_officer_assignment) }
describe "validations" do describe "validations" do
@@ -97,6 +98,7 @@ describe Poll::Voter do
it "is valid with a booth origin" do it "is valid with a booth origin" do
voter.origin = "booth" voter.origin = "booth"
voter.officer_assignment = officer_assignment
expect(voter).to be_valid expect(voter).to be_valid
end end
@@ -104,7 +106,27 @@ describe Poll::Voter do
voter.origin = "web" voter.origin = "web"
expect(voter).to be_valid expect(voter).to be_valid
end end
end
context "assignments" do
it "should not be valid without a booth_assignment_id when origin is booth" do
voter.origin = "booth"
voter.booth_assignment_id = nil
expect(voter).not_to be_valid
end
it "should not be valid without an officer_assignment_id when origin is booth" do
voter.origin = "booth"
voter.officer_assignment_id = nil
expect(voter).not_to be_valid
end
it "should be valid without assignments when origin is web" do
voter.origin = "web"
voter.booth_assignment_id = nil
voter.officer_assignment_id = nil
expect(voter).to be_valid
end
end end
end end

View File

@@ -21,4 +21,15 @@ module CommonActions
check "user_terms_of_service" check "user_terms_of_service"
end end
def validate_officer
allow_any_instance_of(Officing::BaseController).
to receive(:verify_officer_assignment).and_return(true)
end
def set_officing_booth(booth=nil)
booth = create(:poll_booth) if booth.blank?
allow_any_instance_of(Officing::BaseController).
to receive(:current_booth).and_return(booth)
end
end end