Merge pull request #1305 from consul/budgets-user

Budgets user
This commit is contained in:
Raimond Garcia
2016-12-26 11:28:14 +01:00
committed by GitHub
54 changed files with 659 additions and 739 deletions

View File

@@ -522,6 +522,3 @@ DEPENDENCIES
unicorn (~> 5.1.0)
web-console (= 3.3.0)
whenever
BUNDLED WITH
1.13.6

View File

@@ -0,0 +1,12 @@
App.AllowParticipation =
initialize: ->
$(document).on {
'mouseenter focus': ->
$(this).find(".js-participation-not-allowed").show();
$(this).find(".js-participation-allowed").hide();
mouseleave: ->
$(this).find(".js-participation-not-allowed").hide();
$(this).find(".js-participation-allowed").show();
}, ".js-participation"
false

View File

@@ -36,6 +36,7 @@
//= require tags
//= require users
//= require votes
//= require allow_participation
//= require annotatable
//= require advanced_search
//= require registration_form
@@ -53,6 +54,7 @@ var initialize_modules = function() {
App.Comments.initialize();
App.Users.initialize();
App.Votes.initialize();
App.AllowParticipation.initialize();
App.Tags.initialize();
App.Dropdown.initialize();
App.LocationChanger.initialize();

View File

@@ -4,11 +4,14 @@ class Admin::BudgetInvestmentsController < Admin::BaseController
before_action :load_budget
before_action :load_investment, only: [:show, :edit, :update]
before_action :load_ballot, only: [:show, :index]
has_filters %w{valuation_open without_admin managed valuating valuation_finished all}, only: :index
def index
@investments = Budget::Investment.scoped_filter(params, @current_filter).order(cached_votes_up: :desc, created_at: :desc).page(params[:page])
@investments = Budget::Investment.scoped_filter(params, @current_filter)
.order(cached_votes_up: :desc, created_at: :desc)
.page(params[:page])
end
def show
@@ -20,7 +23,7 @@ class Admin::BudgetInvestmentsController < Admin::BaseController
load_tags
end
def update
def update
if @investment.update(budget_investment_params)
redirect_to admin_budget_budget_investment_path(@budget, @investment, Budget::Investment.filter_params(params)),
notice: t("flash.actions.update.budget_investment")
@@ -35,7 +38,8 @@ class Admin::BudgetInvestmentsController < Admin::BaseController
private
def budget_investment_params
params.require(:budget_investment).permit(:title, :description, :external_url, :heading_id, :administrator_id, :tag_list, valuator_ids: [])
params.require(:budget_investment)
.permit(:title, :description, :external_url, :heading_id, :administrator_id, :tag_list, valuator_ids: [])
end
def load_budget
@@ -57,4 +61,9 @@ class Admin::BudgetInvestmentsController < Admin::BaseController
def load_tags
@tags = ActsAsTaggableOn::Tag.budget_investment_tags
end
end
def load_ballot
query = Budget::Ballot.where(user: current_user, budget: @budget)
@ballot = @budget.balloting? ? query.first_or_create : query.first_or_initialize
end
end

View File

@@ -80,10 +80,6 @@ class ApplicationController < ActionController::Base
@spending_proposal_votes = current_user ? current_user.spending_proposal_votes(spending_proposals) : {}
end
def set_budget_investment_votes(budget_investments)
@budget_investment_votes = current_user ? current_user.budget_investment_votes(budget_investments) : {}
end
def set_comment_flags(comments)
@comment_flags = current_user ? current_user.comment_flags(comments) : {}
end

View File

@@ -9,25 +9,15 @@ module Budgets
before_action :load_investments
load_and_authorize_resource :budget
load_and_authorize_resource :ballot, class: "Budget::Ballot"
load_and_authorize_resource :ballot, class: "Budget::Ballot", through: :budget
load_and_authorize_resource :line, through: :ballot, find_by: :investment_id, class: "Budget::Ballot::Line"
def create
load_investment
load_heading
if @ballot.add_investment(@investment)
#@ballot.set_geozone(@geozone)
#@current_user.update(representative_id: nil)
if request.get?
redirect_to @investment, notice: t('budget_investments.notice.voted')
end
else
if request.get?
redirect_to @investment, notice: t('budget_investments.notice.could_not_vote')
else
render :new
end
unless @ballot.add_investment(@investment)
head :bad_request
end
end
@@ -47,7 +37,7 @@ module Budgets
end
def line_params
params.permit(:investment_id)
params.permit(:investment_id, :budget_id)
end
def load_budget
@@ -64,6 +54,7 @@ module Budgets
def load_investments
if params[:investments_ids].present?
@investment_ids = params[:investment_ids]
@investments = Budget::Investment.where(id: params[:investments_ids])
end
end

View File

@@ -19,13 +19,14 @@ module Budgets
has_orders %w{most_voted newest oldest}, only: :show
has_orders ->(c){ c.instance_variable_get(:@budget).balloting? ? %w{random price} : %w{random confidence_score} }, only: :index
invisible_captcha only: [:create, :update], honeypot: :subtitle
invisible_captcha only: [:create, :update], honeypot: :subtitle, scope: :budget_investment
respond_to :html, :js
def index
@investments = apply_filters_and_search(@investments).send("sort_by_#{@current_order}").page(params[:page]).per(10).for_render
set_budget_investment_votes(@investments)
@investments = @investments.apply_filters_and_search(@budget, params).send("sort_by_#{@current_order}").page(params[:page]).per(10).for_render
@investment_ids = @investments.pluck(:id)
load_investment_votes(@investments)
end
def new
@@ -35,15 +36,19 @@ module Budgets
@commentable = @investment
@comment_tree = CommentTree.new(@commentable, params[:page], @current_order)
set_comment_flags(@comment_tree.comments)
set_budget_investment_votes(@investment)
load_investment_votes(@investment)
@investment_ids = [@investment.id]
end
def create
@investment.author = current_user
if @investment.save
notice = t('flash.actions.create.budget_investment', activity: "<a href='#{user_path(current_user, filter: :budget_investments)}'>#{t('layouts.header.my_activity_link')}</a>")
redirect_to @investment, notice: notice, flash: { html_safe: true }
activity_link = view_context.link_to(t('layouts.header.my_activity_link'),
user_path(current_user, filter: :budget_investments))
redirect_to budget_investment_path(@budget, @investment),
flash: { html_safe: true },
notice: t('flash.actions.create.budget_investment', activity: activity_link)
else
render :new
end
@@ -56,11 +61,15 @@ module Budgets
def vote
@investment.register_selection(current_user)
set_budget_investment_votes(@investment)
load_investment_votes(@investment)
end
private
def load_investment_votes(investments)
@investment_votes = current_user ? current_user.budget_investment_votes(investments) : {}
end
def set_random_seed
if params[:order] == 'random' || params[:order].blank?
params[:random_seed] ||= rand(99)/100.0
@@ -71,35 +80,19 @@ module Budgets
end
def investment_params
params.require(:investment).permit(:title, :description, :external_url, :heading_id, :terms_of_service)
end
def apply_filters_and_search(investments)
if params[:heading_id].blank?
@filter_heading_name = t('geozones.none')
else
@filter_heading = @budget.headings.find(params[:heading_id])
@filter_heading_name = @filter_heading.name
end
investments = investments.by_heading(params[:heading_id].presence || @budget.headings.first)
if params[:unfeasible].present?
investments = investments.unfeasible
else
investments = @budget.balloting? ? investments.feasible.valuation_finished : investments.not_unfeasible
end
investments = investments.search(params[:search]) if params[:search].present?
investments
params.require(:budget_investment).permit(:title, :description, :external_url, :heading_id, :terms_of_service)
end
def load_ballot
@ballot = Budget::Ballot.where(user: current_user, budget: @budget).first_or_create
query = Budget::Ballot.where(user: current_user, budget: @budget)
@ballot = @budget.balloting? ? query.first_or_create : query.first_or_initialize
end
def load_heading
@heading = @budget.headings.find(params[:heading_id]) if params[:heading_id].present?
if params[:heading_id].present?
@heading = @budget.headings.find(params[:heading_id])
@assigned_heading = @ballot.try(:heading_for_group, @heading.try(:group))
end
end
end

View File

@@ -4,8 +4,8 @@ module HasOrders
class_methods do
def has_orders(valid_orders, *args)
before_action(*args) do |c|
valid_orders = valid_orders.call(c) if valid_orders.respond_to?(:call)
@valid_orders = valid_orders
@valid_orders = valid_orders.respond_to?(:call) ? valid_orders.call(c) : valid_orders.dup
@valid_orders.delete('relevance') if params[:search].blank?
@current_order = @valid_orders.include?(params[:order]) ? params[:order] : @valid_orders.first
end
end

View File

@@ -4,10 +4,11 @@ class Management::Budgets::InvestmentsController < Management::BaseController
load_resource :investment, through: :budget, class: 'Budget::Investment'
before_action :only_verified_users, except: :print
before_action :load_heading, only: [:index, :show, :print]
def index
@investments = apply_filters_and_search(@investments).page(params[:page])
set_investment_votes(@investments)
@investments = @investments.apply_filters_and_search(@budget, params).page(params[:page])
load_investment_votes(@investments)
end
def new
@@ -26,22 +27,22 @@ class Management::Budgets::InvestmentsController < Management::BaseController
end
def show
set_investment_votes(@investment)
load_investment_votes(@investment)
end
def vote
@investment.register_selection(managed_user)
set_investment_votes(@investment)
load_investment_votes(@investment)
end
def print
@investments = apply_filters_and_search(@investments).order(cached_votes_up: :desc).for_render.limit(15)
set_investment_votes(@investments)
@investments = @investments.apply_filters_and_search(@budget, params).order(cached_votes_up: :desc).for_render.limit(15)
load_investment_votes(@investments)
end
private
def set_investment_votes(investments)
def load_investment_votes(investments)
@investment_votes = managed_user ? managed_user.budget_investment_votes(investments) : {}
end
@@ -53,14 +54,8 @@ class Management::Budgets::InvestmentsController < Management::BaseController
check_verified_user t("management.budget_investments.alert.unverified_user")
end
def apply_filters_and_search(investments)
investments = params[:unfeasible].present? ? investments.unfeasible : investments.not_unfeasible
if params[:heading_id].present?
investments = investments.by_heading(params[:heading_id])
@heading = Budget::Heading.find(params[:heading_id])
end
investments = investments.search(params[:search]) if params[:search].present?
investments
def load_heading
@heading = @budget.headings.find(params[:heading_id]) if params[:heading_id].present?
end
end

View File

@@ -17,7 +17,7 @@ module BudgetsHelper
when "management::budgets"
management_budget_investment_path(investment.budget, investment, options)
else
budget_investment_path(investment.budget, investment, options.merge(budget_id: investment.budget_id))
budget_investment_path(investment.budget, investment, options)
end
end

View File

@@ -1,7 +0,0 @@
module OrdersHelper
def valid_orders
@valid_orders.reject { |order| order =='relevance' && params[:search].blank? }
end
end

View File

@@ -48,7 +48,7 @@ module Abilities
can :create, Budget::Investment, budget: { phase: "accepting" }
can :vote, Budget::Investment, budget: { phase: "selecting" }
can [:show, :create], Budget::Ballot, budget: { phase: "balloting" }
can [:create, :destroy], Budget::Ballot::Line#, budget: { phase: "balloting" }
can [:create, :destroy], Budget::Ballot::Line, budget: { phase: "balloting" }
can :create, DirectMessage
can :show, DirectMessage, sender_id: user.id

View File

@@ -43,6 +43,10 @@ class Budget < ActiveRecord::Base
phase == "finished"
end
def current?
!finished?
end
def heading_price(heading)
heading_ids.include?(heading.id) ? heading.price : -1
end
@@ -59,7 +63,11 @@ class Budget < ActiveRecord::Base
end
def formatted_heading_price(heading)
formatted_ammount(heading_price(heading))
formatted_amount(heading_price(heading))
end
def formatted_heading_amount_spent(heading)
formatted_amount(amount_spent(heading))
end
end

View File

@@ -9,19 +9,27 @@ class Budget
has_many :headings, -> { uniq }, through: :groups
def add_investment(investment)
lines.create!(budget: budget, investment: investment, heading: investment.heading, group_id: investment.heading.group_id)
lines.create!(investment: investment)
end
def total_amount_spent
investments.sum(:price).to_i
end
def amount_spent(heading_id)
investments.by_heading(heading_id).sum(:price).to_i
def amount_spent(heading)
investments.by_heading(heading.id).sum(:price).to_i
end
def formatted_amount_spent(heading)
budget.formatted_amount(amount_spent(heading))
end
def amount_available(heading)
budget.heading_price(heading) - amount_spent(heading.id)
budget.heading_price(heading) - amount_spent(heading)
end
def formatted_amount_available(heading)
budget.formatted_amount(amount_available(heading))
end
def has_lines_in_group?(group)

View File

@@ -2,15 +2,18 @@ class Budget
class Ballot
class Line < ActiveRecord::Base
belongs_to :ballot
belongs_to :budget
belongs_to :group
belongs_to :heading
belongs_to :investment
belongs_to :heading
belongs_to :group
belongs_to :budget
validates :ballot_id, :investment_id, :heading_id, :group_id, :budget_id, presence: true
validate :insufficient_funds
#needed? validate :different_geozone, :if => :district_proposal?
validate :unfeasible
#needed? validates :ballot_id, :budget_id, :group_id, :heading_id, :investment_id, presence: true
validate :unselected
before_validation :set_denormalized_ids
def insufficient_funds
errors.add(:money, "insufficient funds") if ballot.amount_available(investment.heading) < investment.price.to_i
@@ -20,13 +23,21 @@ class Budget
errors.add(:heading, "different heading assigned") if (ballot.heading.present? && investment.heading != ballot.heading)
end
def unfeasible
errors.add(:unfeasible, "unfeasible investment") unless investment.feasible?
def unselected
errors.add(:investment, "unselected investment") unless investment.selected?
end
def heading_proposal?
investment.heading_id.present?
end
private
def set_denormalized_ids
self.heading_id ||= self.investment.try(:heading_id)
self.group_id ||= self.investment.try(:group_id)
self.budget_id ||= self.investment.try(:budget_id)
end
end
end
end

View File

@@ -44,6 +44,7 @@ class Budget
scope :not_unfeasible, -> { where.not(feasibility: "unfeasible") }
scope :undecided, -> { where(feasibility: "undecided") }
scope :with_supports, -> { where('cached_votes_up > 0') }
scope :selected, -> { where(selected: true) }
scope :by_group, -> (group_id) { where(group_id: group_id) }
scope :by_heading, -> (heading_id) { where(heading_id: heading_id) }
@@ -148,6 +149,7 @@ class Budget
def reason_for_not_being_ballotable_by(user, ballot)
return permission_problem(user) if permission_problem?(user)
return :not_selected unless selected?
return :no_ballots_allowed unless budget.balloting?
return :different_heading_assigned unless ballot.valid_heading?(heading)
return :not_enough_money if ballot.present? && !enough_money?(ballot)
@@ -205,11 +207,23 @@ class Budget
budget.formatted_amount(price)
end
def self.apply_filters_and_search(budget, params)
investments = all
if budget.balloting?
investments = investments.selected
else
investments = params[:unfeasible].present? ? investments.unfeasible : investments.not_unfeasible
end
investments = investments.by_heading(params[:heading_id]) if params[:heading_id].present?
investments = investments.search(params[:search]) if params[:search].present?
investments
end
private
def set_denormalized_ids
self.group_id ||= self.heading.group_id
self.budget_id ||= self.heading.group.budget_id
self.group_id ||= self.heading.try(:group_id)
self.budget_id ||= self.heading.try(:group).try(:budget_id)
end
end
end

View File

@@ -28,7 +28,7 @@
<h4 class="amount-spent text-right">
<%= t("budgets.ballots.show.amount_spent") %>
<span>
<%= @budget.formatted_amount(@ballot.amount_spent(@ballot.heading_for_group(group).id))) %>
<%= @ballot.formatted_amount_spent(@ballot.heading_for_group(group)) %>
</span>
</h4>
<% else %>
@@ -47,7 +47,7 @@
<h4>
<%= t("budgets.ballots.show.remaining",
amount_city: @budget.formatted_amount(@ballot.amount_available(@ballot.heading_for_group(group))))).html_safe %>
amount: @ballot.formatted_amount_available(@ballot.heading_for_group(group))).html_safe %>
</h4>
</div>
<% end %>

View File

@@ -7,7 +7,7 @@
<div class="progress-meter"
style="width:
<%= progress_bar_width(@budget.heading_price(@heading),
@ballot.amount_spent(@heading.id)) %>">
@ballot.amount_spent(@heading)) %>">
</div>
</div>
@@ -16,12 +16,12 @@
<span class="progress-meter spent-amount-meter"
style="width:
<%= progress_bar_width(@budget.heading_price(@heading),
@ballot.amount_spent(@heading.id)) %>">
@ballot.amount_spent(@heading)) %>">
<p id="amount-spent" class="progress-meter-text spent-amount-text">
<%= @budget.format_amount(@ballot.amount_spent(@heading.id)) %>
<%= @ballot.formatted_amount_spent(@heading) %>
<span id="amount-available" class="amount-available">
<%= t("budget.progress_bar.available") %>
<span><%= @budget.format_amount(@ballot.amount_available(@heading)) %></span>
<span><%= @ballot.formatted_amount_available(@heading) %></span>
</span>
</p>
</span>

View File

@@ -1,5 +1,8 @@
<% if @investments.present? %>
<% @investments.each do |investment| %>
$("#<%= dom_id(investment) %>_ballot").html('<%= j render("budgets/investments/ballot", investment: investment) %>');
$("#<%= dom_id(investment) %>_ballot").html('<%= j render("/budgets/investments/ballot",
investment: investment,
investment_ids: investment_ids,
ballot: ballot) %>');
<% end %>
<% end %>
<% end %>

View File

@@ -1,5 +1,11 @@
$("#progress_bar").html('<%= j render("budgets/ballot/progress_bar", ballot: @ballot) %>');
$("#sidebar").html('<%= j render("budgets/investments/sidebar") %>');
$("#<%= dom_id(@investment) %>_ballot").html('<%= j render("budgets/investments/ballot", investment: @investment) %>');
$("#progress_bar").html('<%= j render("/budgets/ballot/progress_bar", ballot: @ballot) %>');
$("#sidebar").html('<%= j render("/budgets/investments/sidebar") %>');
$("#<%= dom_id(@investment) %>_ballot").html('<%= j render("/budgets/investments/ballot",
investment: @investment,
investment_ids: @investment_ids,
ballot: @ballot) %>');
<%= render 'refresh_ballots' %>
<%= render 'refresh_ballots',
investment: @investment,
investment_ids: @investment_ids,
ballot: @ballot %>

View File

@@ -1,6 +1,12 @@
$("#progress_bar").html('<%= j render("budgets/ballot/progress_bar", ballot: @ballot) %>');
$("#sidebar").html('<%= j render("budgets/investments/sidebar") %>');
$("#ballot").html('<%= j render("budgets/ballot/ballot") %>')
$("#<%= dom_id(@investment) %>_ballot").html('<%= j render("budgets/investments/ballot", investment: @investment) %>');
<%= render 'refresh_ballots' %>
$("#<%= dom_id(@investment) %>_ballot").html('<%= j render("/budgets/investments/ballot",
investment: @investment,
investment_ids: @investment_ids,
ballot: @ballot) %>');
<%= render 'refresh_ballots',
investment: @investment,
investment_ids: @investment_ids,
ballot: @ballot %>

View File

@@ -1,6 +1,6 @@
<% reason = investment.reason_for_not_being_ballotable_by(current_user, investment_ballot) %>
<div class="supports ballot">
<% if investment_ballot.has_investment?(investment) %>
<% reason = investment.reason_for_not_being_ballotable_by(current_user, ballot) %>
<div class="js-participation supports ballot">
<% if ballot.has_investment?(investment) %>
<div class="remove supported inline-block">
<span class="icon-check-circle bounceIn animated"
@@ -40,11 +40,11 @@
<% end %>
<% if reason.present? && !investment_ballot.has_investment?(investment) %>
<% if reason.present? && !ballot.has_investment?(investment) %>
<div class="no-supports-allowed" style='display:none'>
<div class="js-participation-not-allowed" style='display:none'>
<p>
<%= t("votes.budget_investments.#{reason}",
<%= t("budgets.ballots.reasons_for_not_balloting.#{reason}",
verify_account: link_to(t("votes.verify_account"), verification_path),
signin: link_to(t("votes.signin"), new_user_session_path),
signup: link_to(t("votes.signup"), new_user_registration_path),

View File

@@ -1,7 +1,12 @@
<%= form_for(@investment, url: form_url) do |f| %>
<%= form_for(@investment, url: form_url, method: :post) do |f| %>
<%= render 'shared/errors', resource: @investment %>
<div class="row">
<div class="small-12 column">
<%= f.label :heading_id, t("budget.investments.form.heading") %>
<%= f.select :heading_id, budget_heading_select_options(@budget), {include_blank: true, label: false} %>
</div>
<div class="small-12 column">
<%= f.label :title, t("budget.investments.form.title") %>
<%= f.text_field :title, maxlength: SpendingProposal.title_max_length, placeholder: t("budget.investments.form.title"), label: false %>
@@ -19,18 +24,9 @@
<%= f.text_field :external_url, placeholder: t("budget.investments.form.external_url"), label: false %>
</div>
<div class="small-12 column">
<%= f.label :heading_id, t("budget.investments.form.heading") %>
<%= f.select :heading_id, budget_heading_select_options(@budget), {include_blank: t("budget.headings.none"), label: false} %>
</div>
<% unless current_user.manager? %>
<div class="small-12 column">
<%= f.label :association_name, t("budget.investments.form.association_name_label") %>
<%= f.text_field :association_name, placeholder: t("budget.investments.form.association_name"), label: false %>
</div>
<div class="small-12 column">
<% if @investment.new_record? %>
<div class="small-12 column">
<%= f.label :terms_of_service do %>
<%= f.check_box :terms_of_service, title: t('form.accept_terms_title'), label: false %>
<span class="checkbox">
@@ -39,8 +35,9 @@
conditions: link_to(t("form.conditions"), "/conditions", target: "blank")).html_safe %>
</span>
<% end %>
<% end %>
</div>
</div>
<% end %>
<div class="actions small-12 column">
<%= f.submit(class: "button", value: t("budget.investments.form.submit_buttons.#{action_name}")) %>

View File

@@ -1,4 +1,4 @@
<% if @filter_heading_name.present? %>
<% if @heading.present? %>
<section class="no-margin-top margin-bottom">
<div class="expanded jumbo-budget budget-heading padding">
@@ -11,7 +11,7 @@
<% if can? :show, @ballot %>
<%= link_to t("budget.investments.header.check_ballot"),
budget_ballot_path(@budget, @ballot),
budget_ballot_path(@budget),
class: "button float-right" %>
<% end %>
</div>
@@ -27,20 +27,19 @@
budget_ballot_path(@budget) %>
<% end %>
</div>
<% if false
#@heading.present? &&
#@ballot.heading.present? &&
#@ballot.heading != @heading
%>
<br>
<p class="callout warning inline-block">
<%= t("budget.investments.header.different_heading_active") %>
<%= link_to @ballot.heading.name, budget_investments_path(budget_id: budget.id, heading_id: @ballot.heading_id) %>
</p>
<% if @ballot.valid_heading?(@heading) %>
<div id="progress_bar" class="no-margin-top">
<%= render 'budgets/ballot/progress_bar' %>
</div>
<% else %>
<div id="progress_bar" class="no-margin-top">
<%= render 'budgets/ballot/progress_bar' %>
</div>
<br>
<p class="callout warning inline-block">
<%= t("budget.investments.header.different_heading_assigned_html",
heading_link: link_to(
@assigned_heading.name,
budget_investments_path(@budget, heading: @assigned_heading))
) %>
</p>
<% end %>
</div>
</div>

View File

@@ -62,7 +62,7 @@
<%= render partial: '/budgets/investments/ballot', locals: {
investment: investment,
investment_ids: investment_ids,
investment_ballot: investment_ballots[investment.budget]
ballot: ballot
} %>
</div>

View File

@@ -6,7 +6,7 @@
<li><%= investment.heading.name %></a></li>
</ul>
<div id="<%= dom_id(investment) %>" class="row">
<div class="row">
<div class="small-12 medium-9 column">
<%= link_to :back, class: "back" do %>
<span class="icon-angle-left"></span>
@@ -65,7 +65,11 @@
</div>
<% elsif investment.should_show_ballots? %>
<div id="<%= dom_id(investment) %>_ballot">
<%= render 'ballot', investment: investment %>
<%= render partial: 'ballot', locals: {
investment: investment,
investment_ids: investment_ids,
ballot: ballot,
} %>
</div>
<% end %>
</div>

View File

@@ -8,12 +8,12 @@
<div class="sidebar-divider"></div>
<h3 class="sidebar-title"><%= t("spending_proposals.index.sidebar.my_ballot") %></h3>
<% if @ballot.investments.by_heading(@heading).count > 0 %>
<% if @heading && @ballot.investments.by_heading(@heading.id).count > 0 %>
<p>
<em>
<%= t("budget.investments.index.sidebar.voted_html",
count: @ballot.investments.by_heading(@heading.id).count,
amount_spent: @budget.format_amount(@ballot.amount_spent(@heading))) %>
amount_spent: @ballot.formatted_amount_spent(@heading)) %>
</em>
</p>
<% else %>
@@ -21,10 +21,12 @@
<% end %>
<ul class="ballot-list">
<% @ballot.investments.by_heading(@heading).each do |investment| %>
<%= render 'budgets/ballot/investment_for_sidebar',
investment: investment,
investment_ids: @investments %>
<% if @heading %>
<% @ballot.investments.by_heading(@heading.id).each do |investment| %>
<%= render 'budgets/ballot/investment_for_sidebar',
investment: investment,
investment_ids: @investment_ids %>
<% end %>
<% end %>
</ul>

View File

@@ -2,7 +2,7 @@
<% voting_allowed = true unless reason.presence == :not_voting_allowed %>
<% user_voted_for = voted_for?(investment_votes, investment) %>
<div class="supports">
<div class="supports js-participation">
<span class="total-supports <%= 'no-button' unless voting_allowed || user_voted_for %>">
<%= t("budget.investments.investment.supports", count: investment.total_votes) %>
@@ -27,7 +27,7 @@
</div>
<% if reason.present? && !user_voted_for %>
<div class="no-supports-allowed" style='display:none' aria-hidden="false">
<div class="js-participation-not-allowed" style='display:none' aria-hidden="false">
<p>
<%= t("votes.budget_investments.#{reason}",
verify_account: link_to(t("votes.verify_account"), verification_path),

View File

@@ -8,7 +8,7 @@
<main id="budget-investments-main">
<% unless params[:search].present? %>
<%= render 'header' %>
<%= render '/budgets/investments/header' %>
<% end %>
<div class="wrap row">
@@ -24,7 +24,7 @@
</p>
<% end %>
<%= content_tag(:h2, t("budget.investments.index.by_heading", heading: @filter_heading_name)) if @filter_heading_name.present? %>
<%= content_tag(:h2, t("budget.investments.index.by_heading", heading: @heading.name)) if @heading.present? %>
<% if params[:search].present? %>
<h2>
<%= page_entries_info @investments %>
@@ -35,14 +35,22 @@
<%= render('shared/order_links', i18n_namespace: "budget.investments.index") unless params[:unfeasible].present? %>
<%= render partial: 'investment', collection: @investments %>
<% @investments.each do |investment| %>
<%= render partial: '/budgets/investments/investment', locals: {
investment: investment,
investment_ids: @investment_ids,
investment_votes: @investment_votes,
ballot: @ballot
} %>
<% end %>
<%= paginate @investments %>
</div>
<div class="small-12 medium-3 column">
<aside class="margin-bottom">
<div id="sidebar">
<%= render 'sidebar' %>
<%= render '/budgets/investments/sidebar' %>
</div>
</aside>
</div>

View File

@@ -0,0 +1,8 @@
<div class="budget-investment-new">
<div class="small-12 medium-9 column end">
<h1 class=""><%= t("management.budget_investments.create") %></h1>
<%= render '/budgets/investments/form', form_url: budget_investments_path(@budget) %>
</div>
</div>

View File

@@ -1,5 +1,10 @@
<% provide :title do %><%= investment.title %><% end %>
<% provide :title do %><%= @investment.title %><% end %>
<%= render partial: '/budgets/investments/investment_show', locals: { investment: @investment, investment_votes: @investment_votes } %>
<%= render partial: '/budgets/investments/investment_show', locals: {
investment: @investment,
investment_ids: @investment_ids,
investment_votes: @investment_votes,
ballot: @ballot
} %>
<%= render partial: '/comments/comment_tree', locals: { comment_tree: @comment_tree, comment_flags: @comment_flags } %>

View File

@@ -1,4 +1,4 @@
$("#<%= dom_id(@investment) %>_votes").html('<%= j render("/budgets/investments/votes",
investment: @investment,
investment_votes: @budget_investment_votes,
investment_votes: @investment_votes,
vote_url: namespaced_budget_investment_vote_path(@investment, value: 'yes')) %>');

View File

@@ -22,7 +22,7 @@
investment: investment,
investment_ids: @investment_ids,
investment_votes: @investment_votes,
investment_ballots: @investment_ballots
ballot: @ballot
} %>
<% end %>

View File

@@ -7,50 +7,6 @@
<div class="small-12 medium-9 column end">
<h1 class=""><%= t("management.budget_investments.create") %></h1>
<%= form_for(@investment, url: management_budget_investments_path(@budget), method: :post) do |f| %>
<%= render 'shared/errors', resource: @investment %>
<div class="row">
<div class="small-12 column">
<%= f.label :heading_id, t("budget.investments.form.heading") %>
<%= f.select :heading_id, budget_heading_select_options(@budget), {include_blank: t("budget.headings.none"), label: false} %>
</div>
<div class="small-12 column">
<%= f.label :title, t("budget.investments.form.title") %>
<%= f.text_field :title, maxlength: SpendingProposal.title_max_length, placeholder: t("budget.investments.form.title"), label: false %>
</div>
<%= f.invisible_captcha :subtitle %>
<div class="ckeditor small-12 column">
<%= f.label :description, t("budget.investments.form.description") %>
<%= f.cktext_area :description, maxlength: SpendingProposal.description_max_length, ckeditor: { language: I18n.locale }, label: false %>
</div>
<div class="small-12 column">
<%= f.label :external_url, t("budget.investments.form.external_url") %>
<%= f.text_field :external_url, placeholder: t("budget.investments.form.external_url"), label: false %>
</div>
<div class="small-12 column">
<%= f.label :terms_of_service do %>
<%= f.check_box :terms_of_service, title: t('form.accept_terms_title'), label: false %>
<span class="checkbox">
<%= t("form.accept_terms",
policy: link_to(t("form.policy"), "/privacy", target: "blank"),
conditions: link_to(t("form.conditions"), "/conditions", target: "blank")).html_safe %>
</span>
<% end %>
</div>
<div class="actions small-12 column">
<%= f.submit(class: "button", value: t("budget.investments.form.submit_buttons.#{action_name}")) %>
</div>
</div>
<% end %>
<%= render '/budgets/investments/form', form_url: management_budget_investments_path(@budget) %>
</div>
</div>

View File

@@ -27,7 +27,7 @@
investment: investment,
investment_ids: @investment_ids,
investment_votes: @investment_votes,
investment_ballots: @investment_ballots
ballot: @ballot
} %>
<% end %>

View File

@@ -1,5 +1,5 @@
<div class="submenu">
<% valid_orders.each do |order| %>
<% @valid_orders.each do |order| %>
<%= link_to current_path_with_query_params(order: order, page: 1), class: order == @current_order ? 'active' : '' do %>
<%= content_tag(order == @current_order ? :h2 : :span) do %>
<%= t("#{i18n_namespace}.orders.#{order}") %>

View File

@@ -4,7 +4,7 @@
data-order="<%= @current_order %>"
name="order-selector"
id="order-selector-participation">
<% valid_orders.each do |order| %>
<% @valid_orders.each do |order| %>
<option <%= 'selected' if order == @current_order %>
value='<%= current_path_with_query_params(order: order, page: 1) %>'>
<%= t("#{i18n_namespace}.orders.#{order}") %>

View File

@@ -4,13 +4,21 @@ en:
show:
title: Your ballot
amount_spent: Amount spent
remaining: "You still have <span>%{amount_city}</span> to invest."
remaining: "You still have <span>%{amount}</span> to invest."
remove: Remove vote
voted_html:
one: "You have voted <span>one</span> proposal."
other: "You have voted <span>%{count}</span> proposals."
voted_info_html: "You can change your vote at any time until the close of this phase.<br> No need to invest all the money available."
zero: You have not voted any investment project.
reasons_for_not_balloting:
not_logged_in: You must %{signin} or %{signup} to continue.
not_verified: Only verified users can vote on proposals; %{verify_account}.
organization: Organizations are not permitted to vote
not_selected: Unselected investment projects can not be supported
not_enough_money: "Price is higher than the available amount left"
no_ballots_allowed: Selecting phase is closed
different_heading_assigned: You have already voted a different heading
groups:
show:
title: Select a heading
@@ -105,9 +113,9 @@ en:
vote: Vote
header:
check_ballot: Check my ballot
different_heading_active: You have active votes in another district.
different_heading_assigned_html: "You have active votes in another heading: %{heading_link}"
show:
group: Group
heading: Heading
price: Price
no_heading: No Heading
no_heading: No Heading

View File

@@ -4,13 +4,21 @@ es:
show:
title: Mis votos
amount_spent: Coste total
remaining: "Te quedan <span>%{amount_city}</span> para invertir"
remaining: "Te quedan <span>%{amount}</span> para invertir"
remove: Quitar voto
voted_html:
one: "Has votado <span>una</span> propuesta."
other: "Has votado <span>%{count}</span> propuestas."
voted_info_html: "Puedes cambiar tus votos en cualquier momento hasta el cierre de esta fase.<br> No hace falta que inviertas todo el dinero disponible."
zero: "Todavía no has votado ninguna propuesta de inversión."
reasons_for_not_balloting:
not_logged_in: Necesitas %{signin} o %{signup} para continuar.
not_verified: Las propuestas de inversión sólo pueden ser apoyadas por usuarios verificados, %{verify_account}.
organization: Las organizaciones no pueden votar.
not_selected: No se pueden votar propuestas inviables.
not_enough_money: No hay fondos suficientes
no_ballots_allowed: El periodo de votación está cerrado.
different_heading_assigned: Ya votaste en una sección distinta del presupuesto.
groups:
show:
title: Selecciona una partida
@@ -105,9 +113,9 @@ es:
vote: Votar
header:
check_ballot: Revisar mis votos
different_heading_active: Ya apoyaste propuestas de otro distrito.
different_heading_assigned_html: "Ya apoyaste propuestas de otra sección del presupuesto: %{heading_link}"
show:
group: Grupo
heading: Partida
price: Cantidad
no_heading: Sin línea
no_heading: Sin línea

View File

@@ -8,8 +8,8 @@ en:
erase_account_link: Erase my account
finish_verification: Complete verification
notifications: Notifications
organization_name_label: Name of organisation
organization_responsible_name_placeholder: Representative of the organisation/collective
organization_name_label: Name of organization
organization_responsible_name_placeholder: Representative of the organization/collective
personal: Personal details
phone_number_label: Phone number
public_activity_label: Keep my list of activities public
@@ -42,10 +42,6 @@ en:
selecting: Selecting investment projects
balloting: Voting investment projects
finished: Finished
budget_investments:
notice:
could_not_vote: Could not vote
voted: Vote stored successfully
comments:
comment:
admin: Administrator
@@ -216,7 +212,7 @@ en:
new_notifications:
one: You have a new notification
other: You have %{count} new notifications
no_notifications: You don't have new notifications
no_notifications: "You don't have new notifications"
open: open
open_city_slogan_html: There are cities that are governed directly by their inhabitants, who <b>discuss</b> the topics they are concerned about, <b>propose</b> ideas to improve their lives and <b>decide</b> among themselves which ones will be carried out.
open_city_title: Love the city, and it will become a city you love.
@@ -586,7 +582,7 @@ en:
anonymous: Too many anonymous votes to admit vote %{verify_account}.
comment_unauthenticated: You must %{signin} or %{signup} to vote.
disagree: I disagree
organizations: Organisations are not permitted to vote
organizations: Organizations are not permitted to vote
signin: Sign in
signup: Sign up
supports: Supports
@@ -596,7 +592,7 @@ en:
spending_proposals:
not_logged_in: You must %{signin} or %{signup} to continue.
not_verified: Only verified users can vote on proposals; %{verify_account}.
organization: Organisations are not permitted to vote
organization: Organizations are not permitted to vote
unfeasible: Unfeasible investment projects can not be supported
not_voting_allowed: Voting phase is closed
welcome:

View File

@@ -42,10 +42,6 @@ es:
selecting: Selección de proyectos
balloting: Votación de proyectos
finished: Terminado
budget_investments:
notice:
could_not_vote: No se pudo votar
voted: Voto emitido con éxito
comments:
comment:
admin: Administrador
@@ -226,7 +222,6 @@ es:
proposal_ballot: Votaciones
see_all: Ver propuestas
budgets: Presupuestos ciudadanos
spending_proposals: Presupuestos participativos
legislation:
help:
alt: Selecciona el texto que quieres comentar y pulsa en el botón con el lápiz.

View File

@@ -362,7 +362,7 @@ tags = Faker::Lorem.words(10)
external_url: Faker::Internet.url,
description: "<p>#{Faker::Lorem.paragraphs.join('</p><p>')}</p>",
created_at: rand((Time.now - 1.week) .. Time.now),
feasibility: %w{undecided feasible unfeasible}.sample,
feasibility: %w{undecided unfeasible feasible feasible feasible feasible}.sample,
unfeasibility_explanation: "<p>#{Faker::Lorem.paragraphs.join('</p><p>')}</p>",
valuation_finished: [false, true].sample,
tag_list: tags.sample(3).join(','),
@@ -371,6 +371,11 @@ tags = Faker::Lorem.words(10)
puts " #{investment.title}"
end
puts "Selecting Investments"
Budget.balloting.reorder("RANDOM()").limit(3).each do |budget|
budget.investments.feasible.reorder("RANDOM()").limit(10).update_all(selected: true)
end
puts "Creating Valuation Assignments"
(1..17).to_a.sample.times do

View File

@@ -0,0 +1,5 @@
class AddSelectedToBudgetInvestment < ActiveRecord::Migration
def change
add_column :budget_investments, :selected, :bool, default: false, index: true
end
end

View File

@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20161102133838) do
ActiveRecord::Schema.define(version: 20161221172447) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -141,6 +141,7 @@ ActiveRecord::Schema.define(version: 20161102133838) do
t.string "responsible_name"
t.integer "budget_id"
t.integer "group_id"
t.boolean "selected", default: false
end
add_index "budget_investments", ["administrator_id"], name: "index_budget_investments_on_administrator_id", using: :btree

View File

@@ -1,13 +1,13 @@
require 'rails_helper'
xdescribe 'HasOrders' do
describe 'HasOrders' do
class FakeController < ActionController::Base; end
controller(FakeController) do
include HasOrders
has_orders ['created_at', 'votes_count', 'flags_count'], only: :index
has_orders -> { ['votes_count', 'flags_count'] }, only: :new
has_orders ['created_at', 'votes_count', 'flags_count', 'relevance'], only: :index
has_orders -> (c) { ['votes_count', 'flags_count'] }, only: :new
def index
render text: "#{@current_order} (#{@valid_orders.join(' ')})"
@@ -18,7 +18,7 @@ xdescribe 'HasOrders' do
end
end
it "has the valid orders set up" do
it "displays all the orders except relevance when not searching" do
get :index
expect(response.body).to eq('created_at (created_at votes_count flags_count)')
end
@@ -28,6 +28,19 @@ xdescribe 'HasOrders' do
expect(response.body).to eq('votes_count (votes_count flags_count)')
end
it "displays relevance when searching" do
get :index, search: 'ipsum'
expect(response.body).to eq('created_at (created_at votes_count flags_count relevance)')
end
it "does not overwrite the has_orders options when doing several requests" do
get :index
# Since has_orders did valid_options.delete, the first call to :index might remove 'relevance' from
# the list by mistake.
get :index, search: 'ipsum'
expect(response.body).to eq('created_at (created_at votes_count flags_count relevance)')
end
describe "the current order" do
it "defaults to the first one on the list" do
get :index

View File

@@ -227,9 +227,7 @@ FactoryGirl.define do
factory :budget_investment, class: 'Budget::Investment' do
sequence(:title) { |n| "Budget Investment #{n} title" }
budget
group { create :budget_group, budget: budget }
heading { create :budget_heading, group: group }
association :heading, factory: :budget_heading
association :author, factory: :user
description 'Spend money on this'
price 10
@@ -253,6 +251,12 @@ FactoryGirl.define do
trait :finished do
valuation_finished true
end
trait :selected do
selected true
feasibility "feasible"
valuation_finished true
end
end
factory :budget_ballot, class: 'Budget::Ballot' do
@@ -261,11 +265,8 @@ FactoryGirl.define do
end
factory :budget_ballot_line, class: 'Budget::Ballot::Line' do
budget
ballot { create :budget_ballot, budget: budget }
group { create :budget_group, budget: budget }
heading { create :budget_heading, group: group }
investment { create :budget_investment, :feasible, heading: heading }
association :ballot, factory: :budget_ballot
association :investment, factory: :budget_investment
end
factory :vote do

View File

@@ -2,24 +2,25 @@ require 'rails_helper'
feature 'Ballots' do
let(:budget) { create(:budget, phase: "balloting") }
let(:group) { create(:budget_group, budget: budget, name: "Group 1") }
let(:heading) { create(:budget_heading, group: group, name: "Heading 1", price: 1000000) }
let!(:user) { create(:user, :level_two) }
let!(:budget) { create(:budget, phase: "balloting") }
let!(:states) { create(:budget_group, budget: budget, name: "States") }
let!(:california) { create(:budget_heading, group: states, name: "California", price: 1000) }
let!(:new_york) { create(:budget_heading, group: states, name: "New York", price: 1000000) }
context "Voting" do
let!(:user) { create(:user, :level_two) }
background do
login_as(user)
visit budget_path(budget)
end
let!(:city) { create(:budget_group, budget: budget, name: "City") }
let!(:districts) { create(:budget_group, budget: budget, name: "Districts") }
context "Group and Heading Navigation" do
scenario "Groups" do
city = create(:budget_group, budget: budget, name: "City")
districts = create(:budget_group, budget: budget, name: "Districts")
visit budget_path(budget)
expect(page).to have_link "City"
@@ -27,9 +28,6 @@ feature 'Ballots' do
end
scenario "Headings" do
city = create(:budget_group, budget: budget, name: "City")
districts = create(:budget_group, budget: budget, name: "Districts")
city_heading1 = create(:budget_heading, group: city, name: "Investments Type1")
city_heading2 = create(:budget_heading, group: city, name: "Investments Type2")
district_heading1 = create(:budget_heading, group: districts, name: "District 1")
@@ -41,7 +39,7 @@ feature 'Ballots' do
expect(page).to have_link "Investments Type1"
expect(page).to have_link "Investments Type2"
click_link "Go back"
visit budget_path(budget)
click_link "Districts"
expect(page).to have_link "District 1"
@@ -50,19 +48,16 @@ feature 'Ballots' do
end
scenario "Investments" do
city = create(:budget_group, budget: budget, name: "City")
districts = create(:budget_group, budget: budget, name: "Districts")
city_heading1 = create(:budget_heading, group: city, name: "Investments Type1")
city_heading2 = create(:budget_heading, group: city, name: "Investments Type2")
district_heading1 = create(:budget_heading, group: districts, name: "District 1")
district_heading2 = create(:budget_heading, group: districts, name: "District 2")
city_investment1 = create(:budget_investment, :feasible, :finished, heading: city_heading1)
city_investment2 = create(:budget_investment, :feasible, :finished, heading: city_heading1)
district1_investment1 = create(:budget_investment, :feasible, :finished, heading: district_heading1)
district1_investment2 = create(:budget_investment, :feasible, :finished, heading: district_heading1)
district2_investment1 = create(:budget_investment, :feasible, :finished, heading: district_heading2)
city_investment1 = create(:budget_investment, :selected, heading: city_heading1)
city_investment2 = create(:budget_investment, :selected, heading: city_heading1)
district1_investment1 = create(:budget_investment, :selected, heading: district_heading1)
district1_investment2 = create(:budget_investment, :selected, heading: district_heading1)
district2_investment1 = create(:budget_investment, :selected, heading: district_heading2)
visit budget_path(budget)
click_link "City"
@@ -72,8 +67,7 @@ feature 'Ballots' do
expect(page).to have_content city_investment1.title
expect(page).to have_content city_investment2.title
click_link "Go back"
click_link "Go back"
visit budget_path(budget)
click_link "Districts"
click_link "District 1"
@@ -82,7 +76,8 @@ feature 'Ballots' do
expect(page).to have_content district1_investment1.title
expect(page).to have_content district1_investment2.title
click_link "Go back"
visit budget_path(budget)
click_link "Districts"
click_link "District 2"
expect(page).to have_css(".budget-investment", count: 1)
@@ -90,14 +85,11 @@ feature 'Ballots' do
end
scenario "Redirect to first heading if there is only one" do
city = create(:budget_group, budget: budget, name: "City")
districts = create(:budget_group, budget: budget, name: "Districts")
city_heading = create(:budget_heading, group: city, name: "City")
district_heading1 = create(:budget_heading, group: districts, name: "District 1")
district_heading2 = create(:budget_heading, group: districts, name: "District 2")
city_investment = create(:budget_investment, :feasible, :finished, heading: city_heading)
city_investment = create(:budget_investment, :selected, heading: city_heading)
visit budget_path(budget)
click_link "City"
@@ -110,15 +102,14 @@ feature 'Ballots' do
context "Adding and Removing Investments" do
scenario "Add a proposal", :js do
investment1 = create(:budget_investment, :feasible, :finished, budget: budget, heading: heading, group: group, price: 10000)
investment2 = create(:budget_investment, :feasible, :finished, budget: budget, heading: heading, group: group, price: 20000)
investment1 = create(:budget_investment, :selected, heading: new_york, price: 10000)
investment2 = create(:budget_investment, :selected, heading: new_york, price: 20000)
visit budget_path(budget)
click_link "Group 1"
click_link "States"
click_link "New York"
within("#budget_investment_#{investment1.id}") do
find('.add a').trigger('click')
end
add_to_ballot(investment1)
expect(page).to have_css("#amount-spent", text: "€10,000")
expect(page).to have_css("#amount-available", text: "€990,000")
@@ -128,9 +119,7 @@ feature 'Ballots' do
expect(page).to have_content "€10,000"
end
within("#budget_investment_#{investment2.id}") do
find('.add a').trigger('click')
end
add_to_ballot(investment2)
expect(page).to have_css("#amount-spent", text: "€30,000")
expect(page).to have_css("#amount-available", text: "€970,000")
@@ -141,23 +130,25 @@ feature 'Ballots' do
end
end
scenario "Remove a proposal", :js do
investment1 = create(:budget_investment, :feasible, :finished, budget: budget, heading: heading, group: group, price: 10000)
ballot = create(:budget_ballot, user: user, budget: budget, investments: [investment1])
scenario "Removing a proposal", :js do
investment = create(:budget_investment, :selected, heading: new_york, price: 10000)
ballot = create(:budget_ballot, user: user, budget: budget)
ballot.investments << investment
visit budget_path(budget)
click_link "Group 1"
click_link "States"
click_link "New York"
expect(page).to have_content investment1.title
expect(page).to have_content investment.title
expect(page).to have_css("#amount-spent", text: "€10,000")
expect(page).to have_css("#amount-available", text: "€990,000")
within("#sidebar") do
expect(page).to have_content investment1.title
expect(page).to have_content investment.title
expect(page).to have_content "€10,000"
end
within("#budget_investment_#{investment1.id}") do
within("#budget_investment_#{investment.id}") do
find('.remove a').trigger('click')
end
@@ -165,7 +156,7 @@ feature 'Ballots' do
expect(page).to have_css("#amount-available", text: "€1,000,000")
within("#sidebar") do
expect(page).to_not have_content investment1.title
expect(page).to_not have_content investment.title
expect(page).to_not have_content "€10,000"
end
end
@@ -176,24 +167,18 @@ feature 'Ballots' do
context "Balloting in multiple headings" do
scenario "Independent progress bar for headings", :js do
city = create(:budget_group, budget: budget, name: "City")
districts = create(:budget_group, budget: budget, name: "Districts")
city_heading = create(:budget_heading, group: city, name: "All city", price: 10000000)
district_heading1 = create(:budget_heading, group: districts, name: "District 1", price: 1000000)
district_heading2 = create(:budget_heading, group: districts, name: "District 2", price: 2000000)
investment1 = create(:budget_investment, :feasible, :finished, heading: city_heading, price: 10000)
investment2 = create(:budget_investment, :feasible, :finished, heading: district_heading1, price: 20000)
investment3 = create(:budget_investment, :feasible, :finished, heading: district_heading2, price: 30000)
investment1 = create(:budget_investment, :selected, heading: city_heading, price: 10000)
investment2 = create(:budget_investment, :selected, heading: district_heading1, price: 20000)
investment3 = create(:budget_investment, :selected, heading: district_heading2, price: 30000)
visit budget_path(budget)
click_link "City"
within("#budget_investment_#{investment1.id}") do
find('.add a').trigger('click')
expect(page).to have_content "Remove"
end
add_to_ballot(investment1)
expect(page).to have_css("#amount-spent", text: "€10,000")
expect(page).to have_css("#amount-available", text: "€9,990,000")
@@ -210,14 +195,7 @@ feature 'Ballots' do
expect(page).to have_css("#amount-spent", text: "€0")
expect(page).to have_css("#amount-spent", text: "€1,000,000")
within("#budget_investment_#{investment2.id}") do
find('.add a').trigger('click')
expect(page).to have_content "Remove"
end
visit budget_path(budget)
click_link "Districts"
click_link "District 1"
add_to_ballot(investment2)
expect(page).to have_css("#amount-spent", text: "€20,000")
expect(page).to have_css("#amount-available", text: "€980,000")
@@ -248,21 +226,17 @@ feature 'Ballots' do
click_link "Districts"
click_link "District 2"
expect(page).to have_css("#amount-spent", text: "€0")
expect(page).to have_css("#amount-spent", text: "€2,000,000")
expect(page).to have_content("You have active votes in another heading")
end
end
scenario "Display progress bar after first vote", :js do
investment = create(:budget_investment, :feasible, :finished, heading: heading, price: 10000)
investment = create(:budget_investment, :selected, heading: new_york, price: 10000)
visit budget_investments_path(budget, heading_id: heading.id)
visit budget_investments_path(budget, heading_id: new_york.id)
expect(page).to have_content investment.title
within("#budget_investment_#{investment.id}") do
find('.add a').trigger('click')
expect(page).to have_content "Remove"
end
add_to_ballot(investment)
within("#progress_bar") do
expect(page).to have_css("#amount-spent", text: "€10,000")
@@ -271,98 +245,90 @@ feature 'Ballots' do
end
context "Groups" do
let!(:user) { create(:user, :level_two) }
let!(:districts_group) { create(:budget_group, budget: budget, name: "Districts") }
let!(:california_heading) { create(:budget_heading, group: districts_group, name: "California") }
let!(:new_york_heading) { create(:budget_heading, group: districts_group, name: "New York") }
let!(:investment) { create(:budget_investment, :feasible, :finished, heading: california_heading) }
background do
login_as(user)
end
let!(:investment) { create(:budget_investment, :selected, heading: california) }
background { login_as(user) }
scenario 'Select my heading', :js do
visit budget_path(budget)
click_link "Districts"
click_link "States"
click_link "California"
within("#budget_investment_#{investment.id}") do
find('.add a').trigger('click')
expect(page).to have_content "Remove"
end
add_to_ballot(investment)
visit budget_path(budget)
click_link "Districts"
click_link "States"
expect(page).to have_content "California"
expect(page).to have_css("#budget_heading_#{california_heading.id}.active")
expect(page).to have_css("#budget_heading_#{california.id}.active")
end
scenario 'Change my heading', :js do
investment1 = create(:budget_investment, :feasible, :finished, heading: california_heading)
investment2 = create(:budget_investment, :feasible, :finished, heading: new_york_heading)
investment1 = create(:budget_investment, :selected, heading: california)
investment2 = create(:budget_investment, :selected, heading: new_york)
create(:budget_ballot, user: user, budget: budget, investments: [investment1])
ballot = create(:budget_ballot, user: user, budget: budget)
ballot.investments << investment1
visit budget_investments_path(budget, heading_id: california_heading.id)
visit budget_investments_path(budget, heading_id: california.id)
within("#budget_investment_#{investment1.id}") do
find('.remove a').trigger('click')
end
visit budget_investments_path(budget, heading_id: new_york_heading.id)
visit budget_investments_path(budget, heading_id: new_york.id)
within("#budget_investment_#{investment2.id}") do
find('.add a').trigger('click')
end
add_to_ballot(investment2)
visit budget_path(budget)
click_link "Districts"
expect(page).to have_css("#budget_heading_#{new_york_heading.id}.active")
expect(page).to_not have_css("#budget_heading_#{california_heading.id}.active")
click_link "States"
expect(page).to have_css("#budget_heading_#{new_york.id}.active")
expect(page).to_not have_css("#budget_heading_#{california.id}.active")
end
scenario 'View another heading' do
investment = create(:budget_investment, :feasible, :finished, heading: california_heading)
investment = create(:budget_investment, :selected, heading: california)
create(:budget_ballot, user: user, budget: budget, investments: [investment])
ballot = create(:budget_ballot, user: user, budget: budget)
ballot.investments << investment
visit budget_investments_path(budget, heading_id: new_york_heading.id)
visit budget_investments_path(budget, heading_id: new_york.id)
expect(page).to_not have_css "#progressbar"
expect(page).to have_content "You have active votes in another district:"
expect(page).to have_link california_heading.name, href: budget_investments_path(budget, heading: california_heading)
expect(page).to have_content "You have active votes in another heading:"
expect(page).to have_link california.name, href: budget_investments_path(budget, heading: california)
end
end
context 'Showing the ballot' do
pending "Do not display heading name if there is only one heading in the group (example: group city)"
scenario "Do not display heading name if there is only one heading in the group (example: group city)" do
group = create(:budget_group, budget: budget)
heading = create(:budget_heading, group: group)
visit budget_path(budget)
click_link group.name
# No need to click on the heading name
expect(page).to have_content("Investment projects with scope: #{heading.name}")
expect(current_path).to eq(budget_investments_path(budget))
end
scenario 'Displaying the correct count & amount' do
user = create(:user, :level_two)
group1 = create(:budget_group, budget: budget)
group2 = create(:budget_group, budget: budget)
heading1 = create(:budget_heading, name: "District 1", group: group1, price: 100)
heading2 = create(:budget_heading, name: "District 2", group: group2, price: 50)
investment1 = create(:budget_investment, :selected, price: 10, heading: heading1)
investment2 = create(:budget_investment, :selected, price: 10, heading: heading1)
investment3 = create(:budget_investment, :selected, price: 5, heading: heading2)
investment4 = create(:budget_investment, :selected, price: 5, heading: heading2)
investment5 = create(:budget_investment, :selected, price: 5, heading: heading2)
ballot = create(:budget_ballot, user: user, budget: budget)
investment1 = create(:budget_investment, :feasible, price: 10, heading: heading1, group: group1)
investment2 = create(:budget_investment, :feasible, price: 10, heading: heading1, group: group1)
investment3 = create(:budget_investment, :feasible, price: 5, heading: heading2, group: group2)
investment4 = create(:budget_investment, :feasible, price: 5, heading: heading2, group: group2)
investment5 = create(:budget_investment, :feasible, price: 5, heading: heading2, group: group2)
create(:budget_ballot_line, ballot: ballot, investment: investment1, group: group1)
create(:budget_ballot_line, ballot: ballot, investment: investment2, group: group1)
create(:budget_ballot_line, ballot: ballot, investment: investment3, group: group2)
create(:budget_ballot_line, ballot: ballot, investment: investment4, group: group2)
create(:budget_ballot_line, ballot: ballot, investment: investment5, group: group2)
ballot.investments << investment1 << investment2 << investment3 << investment4 << investment5
login_as(user)
visit budget_ballot_path(budget)
@@ -385,10 +351,9 @@ feature 'Ballots' do
end
scenario 'Removing spending proposals from ballot', :js do
user = create(:user, :level_two)
investment = create(:budget_investment, :selected, price: 10, heading: new_york)
ballot = create(:budget_ballot, user: user, budget: budget)
investment = create(:budget_investment, :feasible, :finished, price: 10, heading: heading, group: group)
create(:budget_ballot_line, ballot: ballot, investment: investment, heading: heading, group: group)
ballot.investments << investment
login_as(user)
visit budget_ballot_path(budget)
@@ -404,14 +369,14 @@ feature 'Ballots' do
end
scenario 'Removing spending proposals from ballot (sidebar)', :js do
user = create(:user, :level_two)
investment1 = create(:budget_investment, :feasible, :finished, price: 10000, heading: heading)
investment2 = create(:budget_investment, :feasible, :finished, price: 20000, heading: heading)
investment1 = create(:budget_investment, :selected, price: 10000, heading: new_york)
investment2 = create(:budget_investment, :selected, price: 20000, heading: new_york)
ballot = create(:budget_ballot, budget: budget, user: user, investments: [investment1, investment2])
ballot = create(:budget_ballot, budget: budget, user: user)
ballot.investments << investment1 << investment2
login_as(user)
visit budget_investments_path(budget, heading_id: heading.id)
visit budget_investments_path(budget, heading_id: new_york.id)
expect(page).to have_css("#amount-spent", text: "€30,000")
expect(page).to have_css("#amount-available", text: "€970,000")
@@ -443,35 +408,37 @@ feature 'Ballots' do
context 'Permissions' do
scenario 'User not logged in', :js do
investment = create(:budget_investment, :feasible, :finished, heading: heading)
investment = create(:budget_investment, :selected, heading: new_york)
visit budget_investments_path(budget, heading_id: heading.id)
visit budget_investments_path(budget, heading_id: new_york.id)
within("#budget_investment_#{investment.id}") do
find("div.ballot").hover
expect_message_you_need_to_sign_in_to_ballot
expect(page).to have_content 'You must Sign in or Sign up to continue.'
expect(page).to have_selector('.in-favor a', visible: false)
end
end
scenario 'User not verified', :js do
user = create(:user)
investment = create(:budget_investment, :feasible, :finished, heading: heading)
unverified_user = create(:user)
investment = create(:budget_investment, :selected, heading: new_york)
login_as(user)
visit budget_investments_path(budget, heading_id: heading.id)
login_as(unverified_user)
visit budget_investments_path(budget, heading_id: new_york.id)
within("#budget_investment_#{investment.id}") do
find("div.ballot").hover
expect_message_only_verified_can_vote_investments
expect(page).to have_content 'Only verified users can vote on proposals'
expect(page).to have_selector('.in-favor a', visible: false)
end
end
scenario 'User is organization', :js do
org = create(:organization)
investment = create(:budget_investment, :feasible, :finished, heading: heading)
investment = create(:budget_investment, :selected, heading: new_york)
login_as(org.user)
visit budget_investments_path(budget, heading_id: heading.id)
visit budget_investments_path(budget, heading_id: new_york.id)
within("#budget_investment_#{investment.id}") do
find("div.ballot").hover
@@ -479,24 +446,20 @@ feature 'Ballots' do
end
end
scenario 'Unfeasible investments' do
user = create(:user, :level_two)
investment = create(:budget_investment, :finished, :unfeasible, heading: heading)
scenario 'Unselected investments' do
investment = create(:budget_investment, heading: new_york)
login_as(user)
visit budget_investments_path(budget, heading_id: heading.id, unfeasible: 1)
visit budget_investments_path(budget, heading_id: new_york.id, unfeasible: 1)
within("#budget_investment_#{investment.id}") do
expect(page).to_not have_css("div.ballot")
end
expect(page).to_not have_css("#budget_investment_#{investment.id}")
end
scenario 'Investments with feasibility undecided are not shown' do
user = create(:user, :level_two)
investment = create(:budget_investment, :finished, feasibility: "undecided", heading: heading)
investment = create(:budget_investment, feasibility: "undecided", heading: new_york)
login_as(user)
visit budget_investments_path(budget, heading_id: heading.id)
visit budget_investments_path(budget, heading_id: new_york.id)
within("#budget-investments") do
expect(page).to_not have_css("div.ballot")
@@ -505,157 +468,134 @@ feature 'Ballots' do
end
scenario 'Different district', :js do
user = create(:user, :level_two)
california = create(:geozone)
new_york = create(:geozone)
bi1 = create(:budget_investment, :selected, heading: california)
bi2 = create(:budget_investment, :selected, heading: new_york)
sp1 = create(:spending_proposal, :feasible, :finished, geozone: california)
sp2 = create(:spending_proposal, :feasible, :finished, geozone: new_york)
create(:ballot, user: user, geozone: california, spending_proposals: [sp1])
ballot = create(:budget_ballot, budget: budget, user: user)
ballot.investments << bi1
login_as(user)
visit spending_proposals_path(geozone: new_york)
visit budget_investments_path(budget, heading: new_york)
within("#spending_proposal_#{sp2.id}") do
within("#budget_investment_#{bi2.id}") do
find("div.ballot").hover
expect_message_already_voted_in_another_geozone(california)
expect(page).to have_content('already voted a different heading')
expect(page).to have_selector('.in-favor a', visible: false)
end
end
scenario 'Insufficient funds', :js do
user = create(:user, :level_two)
california = create(:geozone)
scenario 'Insufficient funds (on page load)', :js do
bi1 = create(:budget_investment, :selected, heading: california, price: 600)
bi2 = create(:budget_investment, :selected, heading: california, price: 500)
sp1 = create(:spending_proposal, :feasible, :finished, price: 25000000)
ballot = create(:budget_ballot, budget: budget, user: user)
ballot.investments << bi1
login_as(user)
visit spending_proposals_path(geozone: 'all')
visit budget_investments_path(budget, heading_id: california.id)
within("#spending_proposal_#{sp1.id}") do
find('.add a').trigger('click')
expect_message_insufficient_funds
within("#budget_investment_#{bi2.id}") do
find("div.ballot").hover
expect(page).to have_content('Price is higher than the available amount left')
expect(page).to have_selector('.in-favor a', visible: false)
end
end
scenario 'Displays error message for all proposals (on create)', :js do
user = create(:user, :level_two)
california = create(:geozone)
sp1 = create(:spending_proposal, :feasible, :finished, price: 20000000)
sp2 = create(:spending_proposal, :feasible, :finished, price: 5000000)
scenario 'Insufficient funds (added after create)', :js do
bi1 = create(:budget_investment, :selected, heading: california, price: 600)
bi2 = create(:budget_investment, :selected, heading: california, price: 500)
login_as(user)
visit spending_proposals_path(geozone: 'all')
visit budget_investments_path(budget, heading_id: california.id)
within("#spending_proposal_#{sp1.id}") do
find('.add a').trigger('click')
expect(page).to have_content "Remove vote"
within("#budget_investment_#{bi1.id}") do
find("div.ballot").hover
expect(page).to_not have_content('Price is higher than the available amount left')
expect(page).to have_selector('.in-favor a', visible: true)
end
within("#spending_proposal_#{sp2.id}") do
add_to_ballot(bi1)
within("#budget_investment_#{bi2.id}") do
find("div.ballot").hover
expect_message_insufficient_funds
end
end
scenario 'Displays error message for all proposals (on destroy)', :js do
user = create(:user, :level_two)
sp1 = create(:spending_proposal, :feasible, :finished, price: 24000000)
sp2 = create(:spending_proposal, :feasible, :finished, price: 5000000)
create(:ballot, user: user, spending_proposals: [sp1])
login_as(user)
visit spending_proposals_path(geozone: 'all')
within("#spending_proposal_#{sp2.id}") do
find("div.ballot").hover
expect(page).to have_content "This proposal's price is more than the available amount left"
expect(page).to have_content('Price is higher than the available amount left')
expect(page).to have_selector('.in-favor a', visible: false)
end
within("#spending_proposal_#{sp1.id}") do
find('.remove a').trigger('click')
end
scenario 'Insufficient funds (removed after destroy)', :js do
bi1 = create(:budget_investment, :selected, heading: california, price: 600)
bi2 = create(:budget_investment, :selected, heading: california, price: 500)
ballot = create(:budget_ballot, budget: budget, user: user)
ballot.investments << bi1
login_as(user)
visit budget_investments_path(budget, heading_id: california.id)
within("#budget_investment_#{bi2.id}") do
find("div.ballot").hover
expect(page).to have_content('Price is higher than the available amount left')
expect(page).to have_selector('.in-favor a', visible: false)
end
within("#spending_proposal_#{sp2.id}") do
within("#budget_investment_#{bi1.id}") do
find('.remove a').trigger('click')
expect(page).to have_css ".add a"
end
within("#budget_investment_#{bi2.id}") do
find("div.ballot").hover
expect(page).to_not have_content "This proposal's price is more than the available amount left"
expect(page).to_not have_content('Price is higher than the available amount left')
expect(page).to have_selector('.in-favor a', visible: true)
end
end
scenario 'Displays error message for all proposals (on destroy from sidebar)', :js do
user = create(:user, :level_two)
scenario 'Insufficient functs (removed after destroying from sidebar)', :js do
bi1 = create(:budget_investment, :selected, heading: california, price: 600)
bi2 = create(:budget_investment, :selected, heading: california, price: 500)
sp1 = create(:spending_proposal, :feasible, :finished, price: 24000000)
sp2 = create(:spending_proposal, :feasible, :finished, price: 5000000)
create(:ballot, user: user, spending_proposals: [sp1])
ballot = create(:budget_ballot, budget: budget, user: user)
ballot.investments << bi1
login_as(user)
visit spending_proposals_path(geozone: 'all')
visit budget_investments_path(budget, heading_id: california.id)
within("#spending_proposal_#{sp2.id}") do
within("#budget_investment_#{bi2.id}") do
find("div.ballot").hover
expect(page).to have_content "This proposal's price is more than the available amount left"
expect(page).to have_content('Price is higher than the available amount left')
expect(page).to have_selector('.in-favor a', visible: false)
end
within("#spending_proposal_#{sp1.id}_sidebar") do
within("#budget_investment_#{bi1.id}_sidebar") do
find('.remove-investment-project').trigger('click')
end
expect(page).to_not have_css "#spending_proposal_#{sp1.id}_sidebar"
expect(page).to_not have_css "#budget_investment_#{bi1.id}_sidebar"
within("#spending_proposal_#{sp2.id}") do
within("#budget_investment_#{bi2.id}") do
find("div.ballot").hover
expect(page).to_not have_content "This proposal's price is more than the available amount left"
expect(page).to_not have_content('Price is higher than the available amount left')
expect(page).to have_selector('.in-favor a', visible: true)
end
end
scenario "Display hover for ajax generated content", :js do
user = create(:user, :level_two)
california = create(:geozone)
scenario "Balloting is disabled when budget isn't in the balotting phase", :js do
budget.update(phase: 'on_hold')
sp1 = create(:spending_proposal, :feasible, :finished, price: 20000000)
sp2 = create(:spending_proposal, :feasible, :finished, price: 5000000)
bi1 = create(:budget_investment, :selected, heading: california, price: 600)
login_as(user)
visit spending_proposals_path(geozone: 'all')
within("#spending_proposal_#{sp1.id}") do
find('.add a').trigger('click')
expect(page).to have_content "Remove vote"
end
within("#spending_proposal_#{sp2.id}") do
find("div.ballot").trigger(:mouseover)
expect_message_insufficient_funds
visit budget_investments_path(budget, heading_id: california.id)
within("#budget_investment_#{bi1.id}") do
expect(page).to_not have_css("div.ballot")
end
end
end
end
feature "Ballots in the wrong phase" do
background { login_as(create(:user, :level_two)) }
let(:sp) { create(:spending_proposal, :feasible, :finished, price: 10000) }
scenario "When not on phase 3" do
Setting['feature.spending_proposal_features.phase3'] = nil
visit create_ballot_line_path(spending_proposal_id: sp.id)
expect(page.status_code).to eq(403)
end
scenario "When in phase 3 but voting disabled" do
Setting['feature.spending_proposal_features.phase3'] = true
Setting['feature.spending_proposal_features.final_voting_allowed'] = nil
expect{visit create_ballot_line_path(spending_proposal_id: sp.id)}.to raise_error(ActionController::RoutingError)
end
end

View File

@@ -3,13 +3,13 @@ require 'rails_helper'
feature 'Budget Investments' do
let(:author) { create(:user, :level_two, username: 'Isabel') }
let(:budget) { create(:budget) }
let(:group) { create(:budget_group, budget: budget) }
let(:heading) { create(:budget_heading, group: group) }
let(:budget) { create(:budget, name: "Big Budget") }
let(:group) { create(:budget_group, name: "Health", budget: budget) }
let!(:heading) { create(:budget_heading, name: "More hospitals", group: group) }
scenario 'Index' do
investments = [create(:budget_investment, budget: budget, group: group, heading: heading), create(:budget_investment, budget: budget, group: group, heading: heading), create(:budget_investment, :feasible, budget: budget, group: group, heading: heading)]
unfeasible_investment = create(:budget_investment, :unfeasible, budget: budget, group: group, heading: heading)
investments = [create(:budget_investment, heading: heading), create(:budget_investment, heading: heading), create(:budget_investment, :feasible, heading: heading)]
unfeasible_investment = create(:budget_investment, :unfeasible, heading: heading)
visit budget_investments_path(budget, heading_id: heading.id)
@@ -25,9 +25,9 @@ feature 'Budget Investments' do
context("Search") do
scenario 'Search by text' do
investment1 = create(:budget_investment, budget: budget, group: group, heading: heading, title: "Get Schwifty")
investment2 = create(:budget_investment, budget: budget, group: group, heading: heading, title: "Schwifty Hello")
investment3 = create(:budget_investment, budget: budget, group: group, heading: heading, title: "Do not show me")
investment1 = create(:budget_investment, heading: heading, title: "Get Schwifty")
investment2 = create(:budget_investment, heading: heading, title: "Schwifty Hello")
investment3 = create(:budget_investment, heading: heading, title: "Do not show me")
visit budget_investments_path(budget, heading_id: heading.id)
@@ -48,10 +48,10 @@ feature 'Budget Investments' do
context("Filters") do
scenario 'by unfeasibility' do
investment1 = create(:budget_investment, :unfeasible, budget: budget, group: group, heading: heading, valuation_finished: true)
investment2 = create(:budget_investment, :feasible, budget: budget, group: group, heading: heading)
investment3 = create(:budget_investment, budget: budget, group: group, heading: heading)
investment4 = create(:budget_investment, :feasible, budget: budget, group: group, heading: heading)
investment1 = create(:budget_investment, :unfeasible, heading: heading, valuation_finished: true)
investment2 = create(:budget_investment, :feasible, heading: heading)
investment3 = create(:budget_investment, heading: heading)
investment4 = create(:budget_investment, :feasible, heading: heading)
visit budget_investments_path(budget_id: budget.id, heading_id: heading.id, unfeasible: 1)
@@ -70,7 +70,7 @@ feature 'Budget Investments' do
scenario "Default order is random" do
per_page = Kaminari.config.default_per_page
(per_page + 2).times { create(:budget_investment) }
(per_page + 100).times { create(:budget_investment) }
visit budget_investments_path(budget, heading_id: heading.id)
order = all(".budget-investment h3").collect {|i| i.text }
@@ -99,7 +99,7 @@ feature 'Budget Investments' do
scenario 'Random order maintained with pagination', :js do
per_page = Kaminari.config.default_per_page
(per_page + 2).times { create(:budget_investment, budget: budget, group: group, heading: heading) }
(per_page + 2).times { create(:budget_investment, heading: heading) }
visit budget_investments_path(budget, heading_id: heading.id)
@@ -116,9 +116,9 @@ feature 'Budget Investments' do
end
scenario 'Proposals are ordered by confidence_score', :js do
create(:budget_investment, budget: budget, group: group, heading: heading, title: 'Best proposal').update_column(:confidence_score, 10)
create(:budget_investment, budget: budget, group: group, heading: heading, title: 'Worst proposal').update_column(:confidence_score, 2)
create(:budget_investment, budget: budget, group: group, heading: heading, title: 'Medium proposal').update_column(:confidence_score, 5)
create(:budget_investment, heading: heading, title: 'Best proposal').update_column(:confidence_score, 10)
create(:budget_investment, heading: heading, title: 'Worst proposal').update_column(:confidence_score, 2)
create(:budget_investment, heading: heading, title: 'Medium proposal').update_column(:confidence_score, 5)
visit budget_investments_path(budget, heading_id: heading.id)
click_link 'highest rated'
@@ -135,79 +135,82 @@ feature 'Budget Investments' do
end
xscenario 'Create with invisible_captcha honeypot field' do
login_as(author)
visit new_budget_investment_path(budget_id: budget.id)
context 'Phase I - Accepting' do
before(:each) { budget.update(phase: 'accepting') }
fill_in 'investment_title', with: 'I am a bot'
fill_in 'investment_subtitle', with: 'This is the honeypot'
fill_in 'investment_description', with: 'This is the description'
select 'All city', from: 'investment_heading_id'
check 'investment_terms_of_service'
scenario 'Create with invisible_captcha honeypot field' do
login_as(author)
visit new_budget_investment_path(budget_id: budget.id)
click_button 'Create'
select 'Health: More hospitals', from: 'budget_investment_heading_id'
fill_in 'budget_investment_title', with: 'I am a bot'
fill_in 'budget_investment_subtitle', with: 'This is the honeypot'
fill_in 'budget_investment_description', with: 'This is the description'
check 'budget_investment_terms_of_service'
expect(page.status_code).to eq(200)
expect(page.html).to be_empty
expect(current_path).to eq(budget_investments_path(budget_id: budget.id))
end
click_button 'Create'
xscenario 'Create spending proposal too fast' do
allow(InvisibleCaptcha).to receive(:timestamp_threshold).and_return(Float::INFINITY)
login_as(author)
visit new_budget_investments_path(budget_id: budget.id)
fill_in 'investment_title', with: 'I am a bot'
fill_in 'investment_description', with: 'This is the description'
select 'All city', from: 'investment_heading_id'
check 'investment_terms_of_service'
click_button 'Create'
expect(page).to have_content 'Sorry, that was too quick! Please resubmit'
expect(current_path).to eq(new_budget_investment_path(budget_id: budget.id))
end
xscenario 'Create notice' do
budget.update(phase: "accepting")
login_as(author)
visit new_budget_investment_path(budget_id: budget.id)
save_and_open_page
fill_in 'budget_investment_title', with: 'Build a skyscraper'
fill_in 'budget_investment_description', with: 'I want to live in a high tower over the clouds'
fill_in 'budget_investment_external_url', with: 'http://http://skyscraperpage.com/'
select 'All city', from: 'investment_heading_id'
check 'investment_terms_of_service'
click_button 'Create'
expect(page).to have_content 'Investment created successfully'
expect(page).to have_content 'You can access it from My activity'
within "#notice" do
click_link 'My activity'
expect(page.status_code).to eq(200)
expect(page.html).to be_empty
expect(current_path).to eq(budget_investments_path(budget_id: budget.id))
end
expect(current_url).to eq(user_url(author, filter: :budget_investments))
expect(page).to have_content "1 Investment"
expect(page).to have_content "Build a skyscraper"
end
scenario 'Create spending proposal too fast' do
allow(InvisibleCaptcha).to receive(:timestamp_threshold).and_return(Float::INFINITY)
xscenario 'Errors on create' do
login_as(author)
login_as(author)
visit new_budget_investment_path(budget_id: budget.id)
visit new_budget_investment_path(budget_id: budget.id)
click_button 'Create'
expect(page).to have_content error_message
select 'Health: More hospitals', from: 'budget_investment_heading_id'
fill_in 'budget_investment_title', with: 'I am a bot'
fill_in 'budget_investment_description', with: 'This is the description'
check 'budget_investment_terms_of_service'
click_button 'Create'
expect(page).to have_content 'Sorry, that was too quick! Please resubmit'
expect(current_path).to eq(new_budget_investment_path(budget_id: budget.id))
end
scenario 'Create notice' do
login_as(author)
visit new_budget_investment_path(budget_id: budget.id)
select 'Health: More hospitals', from: 'budget_investment_heading_id'
fill_in 'budget_investment_title', with: 'Build a skyscraper'
fill_in 'budget_investment_description', with: 'I want to live in a high tower over the clouds'
fill_in 'budget_investment_external_url', with: 'http://http://skyscraperpage.com/'
check 'budget_investment_terms_of_service'
click_button 'Create'
expect(page).to have_content 'Investment created successfully'
expect(page).to have_content 'You can access it from My activity'
within "#notice" do
click_link 'My activity'
end
expect(current_url).to eq(user_url(author, filter: :budget_investments))
expect(page).to have_content "1 Investment"
expect(page).to have_content "Build a skyscraper"
end
scenario 'Errors on create' do
login_as(author)
visit new_budget_investment_path(budget_id: budget.id)
click_button 'Create'
expect(page).to have_content error_message
end
end
scenario "Show" do
user = create(:user)
login_as(user)
investment = create(:budget_investment, budget: budget, group: group, heading: heading)
investment = create(:budget_investment, heading: heading)
visit budget_investment_path(budget_id: budget.id, id: investment.id)
@@ -259,15 +262,15 @@ feature 'Budget Investments' do
context "Destroy" do
xscenario "Admin cannot destroy spending proposals" do
scenario "Admin cannot destroy spending proposals" do
admin = create(:administrator)
user = create(:user, :level_two)
investment = create(:budget_investment, budget: budget, group: group, heading: heading, author: user)
investment = create(:budget_investment, heading: heading, author: user)
login_as(admin.user)
visit user_path(user)
within("#investment_#{investment.id}") do
within("#budget_investment_#{investment.id}") do
expect(page).to_not have_link "Delete"
end
end
@@ -280,17 +283,17 @@ feature 'Budget Investments' do
budget.update(phase: "balloting")
end
xscenario "Index" do
scenario "Index" do
user = create(:user, :level_two)
sp1 = create(:budget_investment, :feasible, :finished, budget: budget, group: group, heading: heading, price: 10000)
sp2 = create(:budget_investment, :feasible, :finished, budget: budget, group: group, heading: heading, price: 20000)
sp1 = create(:budget_investment, :selected, heading: heading, price: 10000)
sp2 = create(:budget_investment, :selected, heading: heading, price: 20000)
login_as(user)
visit root_path
first(:link, "Participatory budgeting").click
click_link budget.name
click_link "No Heading"
click_link "Health"
within("#budget_investment_#{sp1.id}") do
expect(page).to have_content sp1.title
@@ -303,10 +306,10 @@ feature 'Budget Investments' do
end
end
xscenario 'Order by cost (only in phase3)' do
create(:budget_investment, :feasible, :finished, budget: budget, group: group, heading: heading, title: 'Build a nice house', price: 1000).update_column(:confidence_score, 10)
create(:budget_investment, :feasible, :finished, budget: budget, group: group, heading: heading, title: 'Build an ugly house', price: 1000).update_column(:confidence_score, 5)
create(:budget_investment, :feasible, :finished, budget: budget, group: group, heading: heading, title: 'Build a skyscraper', price: 20000)
scenario 'Order by cost (only when balloting)' do
create(:budget_investment, :selected, heading: heading, title: 'Build a nice house', price: 1000).update_column(:confidence_score, 10)
create(:budget_investment, :selected, heading: heading, title: 'Build an ugly house', price: 1000).update_column(:confidence_score, 5)
create(:budget_investment, :selected, heading: heading, title: 'Build a skyscraper', price: 20000)
visit budget_investments_path(budget, heading_id: heading.id)
@@ -324,7 +327,7 @@ feature 'Budget Investments' do
scenario "Show" do
user = create(:user, :level_two)
sp1 = create(:budget_investment, :feasible, :finished, budget: budget, group: group, heading: heading, price: 10000)
sp1 = create(:budget_investment, :selected, heading: heading, price: 10000)
login_as(user)
visit budget_investments_path(budget, heading_id: heading.id)
@@ -334,45 +337,49 @@ feature 'Budget Investments' do
expect(page).to have_content "€10,000"
end
xscenario "Confirm", :js do
scenario "Confirm", :js do
budget.update(phase: 'balloting')
user = create(:user, :level_two)
carabanchel = create(:geozone, name: "Carabanchel")
new_york = create(:geozone, name: "New York")
global_group = create(:budget_group, budget: budget, name: 'Global Group')
global_heading = create(:budget_heading, group: global_group, name: 'Global Heading')
carabanchel_heading = create(:budget_heading, heading: heading, geozone: carabanchel, name: carabanchel.name)
new_york_heading = create(:budget_heading, heading: heading, geozone: new_york, name: new_york.name)
carabanchel = create(:geozone, name: "Carabanchel")
new_york = create(:geozone, name: "New York")
carabanchel_heading = create(:budget_heading, group: group, geozone: carabanchel, name: carabanchel.name)
new_york_heading = create(:budget_heading, group: group, geozone: new_york, name: new_york.name)
sp1 = create(:budget_investment, :feasible, :finished, price: 1, heading: nil)
sp2 = create(:budget_investment, :feasible, :finished, price: 10, heading: nil)
sp3 = create(:budget_investment, :feasible, :finished, price: 100, heading: nil)
sp4 = create(:budget_investment, :feasible, :finished, price: 1000, budget: budget, group: group, heading: carabanchel_heading)
sp5 = create(:budget_investment, :feasible, :finished, price: 10000, budget: budget, group: group, heading: carabanchel_heading)
sp6 = create(:budget_investment, :feasible, :finished, price: 100000, budget: budget, group: group, heading: new_york_heading)
sp1 = create(:budget_investment, :selected, price: 1, heading: global_heading)
sp2 = create(:budget_investment, :selected, price: 10, heading: global_heading)
sp3 = create(:budget_investment, :selected, price: 100, heading: global_heading)
sp4 = create(:budget_investment, :selected, price: 1000, heading: carabanchel_heading)
sp5 = create(:budget_investment, :selected, price: 10000, heading: carabanchel_heading)
sp6 = create(:budget_investment, :selected, price: 100000, heading: new_york_heading)
login_as(user)
visit root_path
visit budget_path(budget)
first(:link, "Participatory budgeting").click
click_link budget.name
click_link "No Heading"
click_link "Global Group"
# No need to click_link "Global Heading" because the link of a group with a single heading
# points to the list of investments directly
add_to_ballot(sp1)
add_to_ballot(sp2)
first(:link, "Participatory budgeting").click
visit budget_path(budget)
click_link budget.name
click_link carabanchel.name
click_link "Health"
click_link "Carabanchel"
add_to_ballot(sp4)
add_to_ballot(sp5)
click_link "Check my ballot"
visit budget_ballot_path(budget)
expect(page).to have_content "You can change your vote at any time until the close of this phase"
within("#city_wide") do
within("#budget_group_#{global_group.id}") do
expect(page).to have_content sp1.title
expect(page).to have_content sp1.price
@@ -383,15 +390,15 @@ feature 'Budget Investments' do
expect(page).to_not have_content sp3.price
end
within("#district_wide") do
within("#budget_group_#{group.id}") do
expect(page).to have_content sp4.title
expect(page).to have_content "$1,000"
expect(page).to have_content "1,000"
expect(page).to have_content sp5.title
expect(page).to have_content "$10,000"
expect(page).to have_content "10,000"
expect(page).to_not have_content sp6.title
expect(page).to_not have_content "$100,000"
expect(page).to_not have_content "100,000"
end
end

View File

@@ -94,16 +94,16 @@ feature 'Votes' do
visit budget_investments_path(budget, heading_id: heading.id)
within("#budget_investment_#{investment.id}") do
find("div.supports").hover
expect_message_selecting_not_allowed
find("div.js-participation").hover
expect(page).to have_content 'No Selecting Allowed'
end
visit budget_investment_path(budget, investment)
within("#budget_investment_#{investment.id}") do
find("div.supports").hover
expect_message_selecting_not_allowed
find("div.js-participation").hover
expect(page).to have_content 'No Selecting Allowed'
end
end
end
end
end

View File

@@ -1,18 +0,0 @@
require 'rails_helper'
describe OrdersHelper do
describe '#valid_orders' do
it 'displays relevance when searching' do
params[:search] = 'ipsum'
assign(:valid_orders, %w(created_at random relevance))
expect(helper.valid_orders).to eq %w(created_at random relevance)
end
it 'does not display relevance when not searching' do
assign(:valid_orders, %w(created_at random relevance))
expect(helper.valid_orders).to eq %w(created_at random)
end
end
end

View File

@@ -1,104 +1,45 @@
require 'rails_helper'
xdescribe "Budget::Ballot::Line" do
let(:ballot_line) { build(:budget_ballot_line) }
describe "Budget::Ballot::Line" do
describe 'Validations' do
let(:budget){ create(:budget) }
let(:group){ create(:budget_group, budget: budget) }
let(:heading){ create(:budget_heading, group: group, price: 10000000) }
let(:investment){ create(:budget_investment, :selected, price: 5000000, heading: heading) }
let(:ballot) { create(:budget_ballot, budget: budget) }
let(:ballot_line) { build(:budget_ballot_line, ballot: ballot, investment: investment) }
it "should be valid" do
it "should be valid and automatically denormallyze budget, group and heading when validated" do
expect(ballot_line).to be_valid
end
it "should be invalid if missing id from ballot|budget|group|heading|investment" do
budget = create(:budget)
group = create(:budget_group, budget: budget)
heading = create(:budget_heading, group: group, price: 10000000)
investment = create(:budget_investment, :feasible, price: 5000000, heading: heading)
ballot = create(:budget_ballot, budget: budget)
ballot_line = build(:budget_ballot_line, ballot: ballot, budget: budget, group: group, heading: heading, investment: investment)
expect(ballot_line).to be_valid
ballot_line = build(:budget_ballot_line, ballot: nil, budget: budget, group: group, heading: heading, investment: investment)
expect(ballot_line).to_not be_valid
ballot_line = build(:budget_ballot_line, ballot: ballot, budget: nil, group: group, heading: heading, investment: investment)
expect(ballot_line).to_not be_valid
ballot_line = build(:budget_ballot_line, ballot: ballot, budget: budget, group: nil, heading: heading, investment: investment)
expect(ballot_line).to_not be_valid
ballot_line = build(:budget_ballot_line, ballot: ballot, budget: budget, group: group, heading: nil, investment: investment)
expect(ballot_line).to_not be_valid
ballot_line = build(:budget_ballot_line, ballot: ballot, budget: budget, group: group, heading: heading, investment: nil)
expect(ballot_line).to_not be_valid
expect(ballot_line.budget).to eq(budget)
expect(ballot_line.group).to eq(group)
expect(ballot_line.heading).to eq(heading)
end
describe 'Money' do
it "should not be valid if insufficient funds" do
budget = create(:budget)
group = create(:budget_group, budget: budget)
heading = create(:budget_heading, group: group, price: 10000000)
investment = create(:budget_investment, :feasible, price: heading.price + 1, heading: heading)
ballot = create(:budget_ballot, budget: budget)
ballot_line = build(:budget_ballot_line, ballot: ballot, budget: budget, group: group, heading: heading, investment: investment)
investment.update(price: heading.price + 1)
expect(ballot_line).to_not be_valid
end
it "should be valid if sufficient funds" do
budget = create(:budget)
group = create(:budget_group, budget: budget)
heading = create(:budget_heading, group: group, price: 10000000)
investment = create(:budget_investment, :feasible, price: heading.price - 1, heading: heading, )
ballot = create(:budget_ballot, budget: budget)
ballot_line = build(:budget_ballot_line, ballot: ballot, budget: budget, group: group, heading: heading, investment: investment)
investment.update(price: heading.price - 1)
expect(ballot_line).to be_valid
end
end
describe 'Feasibility' do
it "should not be valid if investment is unfeasible" do
budget = create(:budget)
group = create(:budget_group, budget: budget)
heading = create(:budget_heading, group: group, price: 10000000)
investment = create(:budget_investment, :feasible, price: 20000, feasibility: "unfeasible")
ballot = create(:budget_ballot, budget: budget)
ballot_line = build(:budget_ballot_line, ballot: ballot, budget: budget, group: group, heading: heading, investment: investment)
describe 'Selectibility' do
it "should not be valid if investment is unselected" do
investment.update(selected: false)
expect(ballot_line).to_not be_valid
end
it "should not be valid if investment feasibility is undecided" do
budget = create(:budget)
group = create(:budget_group, budget: budget)
heading = create(:budget_heading, group: group, price: 10000000)
investment = create(:budget_investment, price: 20000, feasibility: "undecided")
ballot = create(:budget_ballot, budget: budget)
ballot_line = build(:budget_ballot_line, ballot: ballot, budget: budget, group: group, heading: heading, investment: investment)
expect(ballot_line).to_not be_valid
end
xit "should be valid if investment is feasible" do
budget = create(:budget)
group = create(:budget_group, budget: budget)
heading = create(:budget_heading, group: group, price: 10000000)
investment = create(:budget_investment, price: 20000, feasibility: "feasible")
ballot = create(:budget_ballot, budget: budget)
ballot_line = build(:budget_ballot_line, ballot: ballot, budget: budget, group: group, heading: heading, investment: investment)
it "should be valid if investment is selected" do
investment.update(selected: true, price: 20000)
expect(ballot_line).to be_valid
end
end
end
end
end

View File

@@ -3,62 +3,60 @@ require 'rails_helper'
describe Budget::Ballot do
describe "#amount_spent" do
xit "returns the total amount spent in investments" do
it "returns the total amount spent in investments" do
budget = create(:budget)
group1 = create(:budget_group, budget: budget)
group2 = create(:budget_group, budget: budget)
heading1 = create(:budget_heading, group: group1, price: 100000)
heading2 = create(:budget_heading, group: group2, price: 200000)
inv1 = create(:budget_investment, :feasible, price: 10000, heading: heading1)
inv2 = create(:budget_investment, :feasible, price: 20000, heading: heading2)
inv1 = create(:budget_investment, :selected, price: 10000, heading: heading1)
inv2 = create(:budget_investment, :selected, price: 20000, heading: heading2)
ballot = create(:budget_ballot, budget: budget)
ballot.add_investment inv1
ballot.investments << inv1
expect(ballot.total_amount_spent).to eq 10000
ballot.add_investment inv2
ballot.investments << inv2
expect(ballot.total_amount_spent).to eq 30000
end
xit "returns the amount spent on all investments assigned to a specific heading" do
it "returns the amount spent on all investments assigned to a specific heading" do
heading = create(:budget_heading)
budget = heading.group.budget
inv1 = create(:budget_investment, :feasible, price: 10000, heading: heading)
inv2 = create(:budget_investment, :feasible, price: 20000, heading: create(:budget_heading, group: heading.group))
inv3 = create(:budget_investment, :feasible, price: 40000, heading: heading)
inv1 = create(:budget_investment, :selected, price: 10000, heading: heading)
inv2 = create(:budget_investment, :selected, price: 20000, heading: create(:budget_heading, group: heading.group))
inv3 = create(:budget_investment, :selected, price: 40000, heading: heading)
ballot = create(:budget_ballot, budget: budget)
ballot.add_investment inv1
ballot.add_investment inv2
ballot.investments << inv1 << inv2
expect(ballot.amount_spent(heading.id)).to eq 10000
expect(ballot.amount_spent(heading)).to eq 10000
ballot.add_investment inv3
ballot.investments << inv3
expect(ballot.amount_spent(heading.id)).to eq 50000
expect(ballot.amount_spent(heading)).to eq 50000
end
end
describe "#amount_available" do
xit "returns how much is left after taking some investments" do
it "returns how much is left after taking some investments" do
budget = create(:budget)
group = create(:budget_group, budget: budget)
heading1 = create(:budget_heading, group: group, price: 1000)
heading2 = create(:budget_heading, group: group, price: 300)
inv1 = create(:budget_investment, :feasible, price: 100, heading: heading1)
inv2 = create(:budget_investment, :feasible, price: 200, heading: heading2)
inv3 = create(:budget_investment, :feasible, price: 400, heading: heading1)
inv1 = create(:budget_investment, :selected, price: 100, heading: heading1)
inv2 = create(:budget_investment, :selected, price: 200, heading: heading2)
inv3 = create(:budget_investment, :selected, price: 400, heading: heading1)
ballot = create(:budget_ballot, budget: budget)
ballot.add_investment inv1
ballot.add_investment inv2
ballot.investments << inv1 << inv2
expect(ballot.amount_available(heading1)).to eq 900
expect(ballot.amount_available(heading2)).to eq 100
ballot.add_investment inv3
ballot.investments << inv3
expect(ballot.amount_available(heading1)).to eq 500
end

View File

@@ -301,7 +301,7 @@ describe Budget::Investment do
let(:user) { create(:user, :level_two) }
let(:luser) { create(:user) }
let(:ballot) { create(:budget_ballot, budget: budget) }
let(:investment) { create(:budget_investment, budget: budget, group: group, heading: heading) }
let(:investment) { create(:budget_investment, :selected, budget: budget, heading: heading) }
describe '#reason_for_not_being_ballotable_by' do
it "rejects not logged in users" do
@@ -322,6 +322,11 @@ describe Budget::Investment do
expect(investment.reason_for_not_being_ballotable_by(user, ballot)).to eq(:no_ballots_allowed)
end
it "rejects non-selected investments" do
investment.selected = false
expect(investment.reason_for_not_being_ballotable_by(user, ballot)).to eq(:not_selected)
end
it "accepts valid ballots when voting is allowed" do
budget.phase = "balloting"
expect(investment.reason_for_not_being_ballotable_by(user, ballot)).to be_nil
@@ -338,23 +343,23 @@ describe Budget::Investment do
california = create(:budget_heading, group: group)
new_york = create(:budget_heading, group: group)
inv1 = create(:budget_investment, :feasible, budget: budget, group: group, heading: california)
inv2 = create(:budget_investment, :feasible, budget: budget, group: group, heading: new_york)
b = create(:budget_ballot, user: user, budget: budget)
b.add_investment inv1
inv1 = create(:budget_investment, :selected, budget: budget, group: group, heading: california)
inv2 = create(:budget_investment, :selected, budget: budget, group: group, heading: new_york)
ballot = create(:budget_ballot, user: user, budget: budget)
ballot.investments << inv1
expect(inv2.reason_for_not_being_ballotable_by(user, b)).to eq(:different_heading_assigned)
expect(inv2.reason_for_not_being_ballotable_by(user, ballot)).to eq(:different_heading_assigned)
end
it "rejects proposals with price higher than current available money" do
budget.phase = "balloting"
districts = create(:budget_group, budget: budget)
carabanchel = create(:budget_heading, group: districts, price: 35)
inv1 = create(:budget_investment, :feasible, budget: budget, group: districts, heading: carabanchel, price: 30)
inv2 = create(:budget_investment, :feasible, budget: budget, group: districts, heading: carabanchel, price: 10)
inv1 = create(:budget_investment, :selected, budget: budget, group: districts, heading: carabanchel, price: 30)
inv2 = create(:budget_investment, :selected, budget: budget, group: districts, heading: carabanchel, price: 10)
ballot = create(:budget_ballot, user: user, budget: budget)
ballot.add_investment inv1
ballot.investments << inv1
expect(inv2.reason_for_not_being_ballotable_by(user, ballot)).to eq(:not_enough_money)
end

View File

@@ -159,11 +159,6 @@ module CommonActions
expect(page).to have_selector('.in-favor', visible: false)
end
def expect_message_you_need_to_sign_in_to_ballot
expect(page).to have_content 'Not Logged In'
expect(page).to have_selector('.in-favor a', visible: false)
end
def expect_message_you_need_to_sign_in_to_vote_comments
expect(page).to have_content 'You must Sign in or Sign up to vote'
expect(page).to have_selector('.participation-allowed', visible: false)
@@ -180,11 +175,6 @@ module CommonActions
expect(page).to have_selector('.in-favor', visible: false)
end
def expect_message_only_verified_can_vote_investments
expect(page).to have_content 'Not Verified'
expect(page).to have_selector('.in-favor a', visible: false)
end
def expect_message_voting_not_allowed
expect(page).to have_content 'Voting phase is closed'
expect(page).to_not have_selector('.in-favor a')
@@ -278,7 +268,9 @@ module CommonActions
def add_to_ballot(budget_investment)
within("#budget_investment_#{budget_investment.id}") do
click_link "Spend money on this"#find('.add a').trigger('click')
find('.add a').trigger('click')
expect(page).to have_content "Remove"
end
end
end