diff --git a/app/controllers/budgets/ballot/lines_controller.rb b/app/controllers/budgets/ballot/lines_controller.rb new file mode 100644 index 000000000..36d0f5a40 --- /dev/null +++ b/app/controllers/budgets/ballot/lines_controller.rb @@ -0,0 +1,80 @@ +module Budgets + module Ballot + class LinesController < ApplicationController + before_action :authenticate_user! + #before_action :ensure_final_voting_allowed + before_action :load_budget + before_action :load_ballot + + before_action :load_investments + + load_and_authorize_resource :budget + load_and_authorize_resource :ballot, class: "Budget::Ballot" + load_and_authorize_resource :line, through: :ballot, find_by: :investment_id, class: "Budget::Ballot::Line" + + def create + load_investment + load_heading + + if @line.save + #@ballot.set_geozone(@geozone) + #@current_user.update(representative_id: nil) + if request.get? + redirect_to @spending_proposal, notice: t('spending_proposals.notice.voted') + end + else + if request.get? + redirect_to @spending_proposal, notice: t('spending_proposals.notice.could_not_vote') + else + render :new + end + end + end + + def destroy + load_spending_proposal + load_spending_proposals + load_geozone + + @ballot_line.destroy + @ballot.reset_geozone + end + + private + + def ensure_final_voting_allowed + return head(:forbidden) unless @budget.balloting? + end + + def line_params + params.permit(:investment_id) + end + + def load_budget + @budget = Budget.find(params[:budget_id]) + end + + def load_ballot + @ballot = Budget::Ballot.where(user: current_user, budget: @budget).first_or_create + end + + def load_investment + @investment = Budget::Investment.find(params[:investment_id]) + end + + def load_investments + if params[:investments_ids].present? + @investments = Budget::Investment.where(id: params[:investments_ids]) + end + end + + def load_heading + @heading = @investment.heading + end + + def load_geozone + @geozone = @line.investment.geozone + end + end + end +end \ No newline at end of file diff --git a/app/controllers/budgets/ballots_controller.rb b/app/controllers/budgets/ballots_controller.rb new file mode 100644 index 000000000..1f435dc69 --- /dev/null +++ b/app/controllers/budgets/ballots_controller.rb @@ -0,0 +1,17 @@ +module Budgets + class BallotsController < ApplicationController + before_action :authenticate_user! + before_action :load_ballot + load_and_authorize_resource + + def show + end + + private + + def load_ballot + @ballot = Ballot.where(user: current_user).first_or_create + end + + end +end \ No newline at end of file diff --git a/app/helpers/ballots_helper.rb b/app/helpers/ballots_helper.rb new file mode 100644 index 000000000..ece1be9fa --- /dev/null +++ b/app/helpers/ballots_helper.rb @@ -0,0 +1,7 @@ +module BallotsHelper + + def progress_bar_width(amount_available, amount_spent) + (amount_spent/amount_available.to_f * 100).to_s + "%" + end + +end \ No newline at end of file diff --git a/app/helpers/budget_helper.rb b/app/helpers/budget_helper.rb index 8c19516e8..745ea88f6 100644 --- a/app/helpers/budget_helper.rb +++ b/app/helpers/budget_helper.rb @@ -20,4 +20,8 @@ module BudgetHelper budget_investment_path(investment, options) end end + + def display_budget_countdown?(budget) + budget.balloting? + end end diff --git a/app/models/abilities/common.rb b/app/models/abilities/common.rb index fb03cbea2..fddf4918b 100644 --- a/app/models/abilities/common.rb +++ b/app/models/abilities/common.rb @@ -45,9 +45,10 @@ module Abilities can :vote, SpendingProposal can :create, SpendingProposal - can :create, Budget::Investment, budget: { phase: "accepting" } - can :vote, Budget::Investment, budget: { phase: "selecting" } - can :create, Budget::Ballot, budget: { phase: "balloting" } + 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, DirectMessage can :show, DirectMessage, sender_id: user.id diff --git a/app/models/abilities/everyone.rb b/app/models/abilities/everyone.rb index 561ac5994..30c1c3b55 100644 --- a/app/models/abilities/everyone.rb +++ b/app/models/abilities/everyone.rb @@ -6,7 +6,7 @@ module Abilities can [:read, :map], Debate can [:read, :map, :summary], Proposal can :read, Comment - can :read, Budget + can [:read, :welcome], Budget can :read, Budget::Investment can :read, SpendingProposal can :read, Legislation diff --git a/app/views/budgets/ballots/_add.html.erb b/app/views/budgets/ballot/_add.html.erb similarity index 100% rename from app/views/budgets/ballots/_add.html.erb rename to app/views/budgets/ballot/_add.html.erb diff --git a/app/views/budgets/ballots/_ballot.html.erb b/app/views/budgets/ballot/_ballot.html.erb similarity index 100% rename from app/views/budgets/ballots/_ballot.html.erb rename to app/views/budgets/ballot/_ballot.html.erb diff --git a/app/views/budgets/ballots/_investment.html.erb b/app/views/budgets/ballot/_investment.html.erb similarity index 100% rename from app/views/budgets/ballots/_investment.html.erb rename to app/views/budgets/ballot/_investment.html.erb diff --git a/app/views/budgets/ballots/_investment_for_sidebar.html.erb b/app/views/budgets/ballot/_investment_for_sidebar.html.erb similarity index 64% rename from app/views/budgets/ballots/_investment_for_sidebar.html.erb rename to app/views/budgets/ballot/_investment_for_sidebar.html.erb index c229cc93b..d9b3d9e69 100644 --- a/app/views/budgets/ballots/_investment_for_sidebar.html.erb +++ b/app/views/budgets/ballot/_investment_for_sidebar.html.erb @@ -1,10 +1,10 @@
  • <%= investment.title %> - <%= format_price(investment.price) %> + <%= format_price(@budget, investment.price) %> <% if @budget.balloting? %> - <%= link_to ballot_line_path(id: investment.id, - investments_ids: investment_ids), + <%= link_to budget_ballot_line_url(id: investment.id, + investments_ids: investment_ids), title: t('budgets.ballots.show.remove'), class: "remove-investment-project", method: :delete, diff --git a/app/views/budgets/ballot/_progress_bar.html.erb b/app/views/budgets/ballot/_progress_bar.html.erb new file mode 100644 index 000000000..58214bbb3 --- /dev/null +++ b/app/views/budgets/ballot/_progress_bar.html.erb @@ -0,0 +1,28 @@ + + <%= format_price(@budget, @budget.heading_price(@heading)) %> + + +
    +
    +
    +
    + +
    + +

    + <%= format_price(@budget, @ballot.amount_spent(@heading.id)) %> + + <%= t("spending_proposals.index.available") %> + <%= format_price(@budget, @ballot.amount_available(@heading)) %> + +

    +
    +
    diff --git a/app/views/budgets/ballots/_remove.html.erb b/app/views/budgets/ballot/_remove.html.erb similarity index 82% rename from app/views/budgets/ballots/_remove.html.erb rename to app/views/budgets/ballot/_remove.html.erb index 5511e60c7..e789b0ae6 100644 --- a/app/views/budgets/ballots/_remove.html.erb +++ b/app/views/budgets/ballot/_remove.html.erb @@ -4,12 +4,12 @@

    - <%= format_price(investment.price) %> + <%= format_price(@budget, investment.price) %>

    <% if @budget.balloting? %> <%= link_to t('budgets.ballots.show.remove'), - ballot_line_path(id: investment.id, + budget_ballot_line_path(id: investment.id, investments_ids: investment_ids), class: "delete small expanded", method: :delete, diff --git a/app/views/budgets/ballot/lines/_refresh_ballots.js.erb b/app/views/budgets/ballot/lines/_refresh_ballots.js.erb new file mode 100644 index 000000000..74e05639b --- /dev/null +++ b/app/views/budgets/ballot/lines/_refresh_ballots.js.erb @@ -0,0 +1,5 @@ +<% if @spending_proposals.present? %> + <% @spending_proposals.each do |spending_proposal| %> + $("#<%= dom_id(spending_proposal) %>_ballot").html('<%= j render("spending_proposals/ballot", spending_proposal: spending_proposal) %>'); + <% end %> +<% end %> \ No newline at end of file diff --git a/app/views/budgets/ballot/lines/create.js.erb b/app/views/budgets/ballot/lines/create.js.erb new file mode 100644 index 000000000..5680fb063 --- /dev/null +++ b/app/views/budgets/ballot/lines/create.js.erb @@ -0,0 +1,5 @@ +$("#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) %>'); + +<%= render 'refresh_ballots' %> diff --git a/app/views/budgets/ballot/lines/destroy.js.erb b/app/views/budgets/ballot/lines/destroy.js.erb new file mode 100644 index 000000000..454011207 --- /dev/null +++ b/app/views/budgets/ballot/lines/destroy.js.erb @@ -0,0 +1,6 @@ +$("#progress_bar").html('<%= j render("spending_proposals/progress_bar", ballot: @ballot) %>'); +$("#sidebar").html('<%= j render("spending_proposals/sidebar") %>'); +$("#ballot").html('<%= j render("ballots/ballot") %>') +$("#<%= dom_id(@spending_proposal) %>_ballot").html('<%= j render("spending_proposals/ballot", spending_proposal: @spending_proposal) %>'); + +<%= render 'refresh_ballots' %> diff --git a/app/views/budgets/ballot/lines/new.js.erb b/app/views/budgets/ballot/lines/new.js.erb new file mode 100644 index 000000000..2e8254866 --- /dev/null +++ b/app/views/budgets/ballot/lines/new.js.erb @@ -0,0 +1,2 @@ +$("#<%= dom_id(@spending_proposal) %>_ballot").html('<%= j render("spending_proposals/ballot", spending_proposal: @spending_proposal) %>'); +$(".no-supports-allowed").show(); \ No newline at end of file diff --git a/app/views/budgets/ballots/show.html.erb b/app/views/budgets/ballot/show.html.erb similarity index 100% rename from app/views/budgets/ballots/show.html.erb rename to app/views/budgets/ballot/show.html.erb diff --git a/app/views/budgets/investments/_ballot.html.erb b/app/views/budgets/investments/_ballot.html.erb index 6634ae1fa..b4aa8af97 100644 --- a/app/views/budgets/investments/_ballot.html.erb +++ b/app/views/budgets/investments/_ballot.html.erb @@ -1,9 +1,13 @@ <% reason = investment.reason_for_not_being_ballotable_by(current_user, @ballot) %>
    <% if @ballot.has_investment?(investment) %> - <%= render 'budgets/ballots/remove', investment: investment %> + <%= render 'budgets/ballot/remove', + investment: investment, + investment_ids: @investments %> <% else %> - <%= render 'budgets/ballots/add', investment: investment %> + <%= render 'budgets/ballot/add', + investment: investment, + investment_ids: @investments %> <% end %> <% if reason.present? && !@ballot.has_investment?(investment) %> @@ -14,7 +18,7 @@ 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), - my_heading: link_to(@ballot.heading.try(:name), budget_investments_path(budget_id: @budget.id, heading_id: @ballot.heading_id)) + my_heading: link_to(@ballot.try(:heading).try(:name), budget_investments_path(budget_id: @budget.id, heading_id: @ballot.try(:heading_id))) ).html_safe %>

    diff --git a/app/views/budgets/investments/_header.html.erb b/app/views/budgets/investments/_header.html.erb index 4aa0b35f4..58a503128 100644 --- a/app/views/budgets/investments/_header.html.erb +++ b/app/views/budgets/investments/_header.html.erb @@ -10,7 +10,9 @@ <% end %> <% if can? :show, @ballot %> - <%= link_to t("budget.investments.header.check_ballot"), budget_ballot_path(@budget, @ballot), class: "button float-right" %> + <%= link_to t("budget.investments.header.check_ballot"), + budget_ballot_path(@budget, @ballot), + class: "button float-right" %> <% end %> @@ -21,7 +23,8 @@

    <%= @filter_geozone_name %>

    <% if @heading.present? && @ballot.heading.present? && @ballot.heading != @heading %> diff --git a/app/views/budgets/investments/_investment.html.erb b/app/views/budgets/investments/_investment.html.erb index fd51ac532..773f11929 100644 --- a/app/views/budgets/investments/_investment.html.erb +++ b/app/views/budgets/investments/_investment.html.erb @@ -53,7 +53,7 @@ vote_url: vote_budget_investment_path(@budget, investment, value: 'yes') } %> - <% elsif feature?("investment_features.phase3") %> + <% elsif @budget.balloting? %>
    <%= render 'ballot', investment: investment %> diff --git a/app/views/budgets/investments/_sidebar.html.erb b/app/views/budgets/investments/_sidebar.html.erb index 53c5e98bc..d9981d6c7 100644 --- a/app/views/budgets/investments/_sidebar.html.erb +++ b/app/views/budgets/investments/_sidebar.html.erb @@ -13,7 +13,7 @@ <%= t("budget.investments.index.sidebar.voted_html", count: @ballot.investments.by_heading(@heading.id).count, - amount_spent: format_price(@ballot.amount_spent(@heading))) %> + amount_spent: format_price(@budget, @ballot.amount_spent(@heading))) %>

    <% else %> @@ -22,7 +22,9 @@ diff --git a/spec/features/budgets/ballots_spec.rb b/spec/features/budgets/ballots_spec.rb new file mode 100644 index 000000000..42a4ed1b5 --- /dev/null +++ b/spec/features/budgets/ballots_spec.rb @@ -0,0 +1,700 @@ +require 'rails_helper' + +feature 'Ballots' do + + let(:budget) { create(:budget, phase: "balloting") } + let(:group) { create(:budget_group, budget: budget) } + let(:heading) { create(:budget_heading, group: group) } + + context "Voting" do + let!(:user) { create(:user, :level_two) } + + background do + login_as(user) + visit budget_path(budget) + end + + context "City" do + + scenario "Add a proposal", :js, :focus do + investment1 = create(:budget_investment, :feasible, :finished, budget: budget, heading: heading, price: 10000) + investment2 = create(:budget_investment, :feasible, :finished, budget: budget, heading: heading, price: 20000) + + click_link "No Heading" + + within("#budget_investment_#{investment1.id}") do + find('.add a').trigger('click') + end + + 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 "€10,000" + end + + within("#budget_investment_#{investment2.id}") do + find('.add a').trigger('click') + end + + expect(page).to have_css("#amount-spent", text: "€30,000") + expect(page).to have_css("#amount-available", text: "€970,000") + + within("#sidebar") do + expect(page).to have_content investment2.title + expect(page).to have_content "€20,000" + end + end + + scenario "Remove a proposal", :js do + sp1 = create(:spending_proposal, :feasible, :finished, price: 10000) + ballot = create(:ballot, user: user, spending_proposals: [sp1]) + + click_link "Vote city proposals" + + expect(page).to have_css("#amount-spent", text: "€10,000") + expect(page).to have_css("#amount-available", text: "€23,990,000") + + within("#sidebar") do + expect(page).to have_content sp1.title + expect(page).to have_content "€10,000" + end + + within("#spending_proposal_#{sp1.id}") do + find('.remove a').trigger('click') + end + + expect(page).to have_css("#amount-spent", text: "€0") + expect(page).to have_css("#amount-available", text: "€24,000,000") + + within("#sidebar") do + expect(page).to_not have_content sp1.title + expect(page).to_not have_content "€10,000" + end + end + + end + + context 'District' do + + scenario "Add a proposal", :js do + carabanchel = create(:geozone, name: "Carabanchel") + + sp1 = create(:spending_proposal, :feasible, :finished, geozone: carabanchel, price: 10000) + sp2 = create(:spending_proposal, :feasible, :finished, geozone: carabanchel, price: 20000) + + click_link "Vote district proposals" + click_link carabanchel.name + + within("#spending_proposal_#{sp1.id}") do + find('.add a').trigger('click') + expect(page).to have_content "Remove" + end + + visit spending_proposals_path(geozone: carabanchel) + + expect(page).to have_css("#amount-spent", text: "€10,000") + expect(page).to have_css("#amount-available", text: "€3,237,830") + + within("#sidebar") do + expect(page).to have_content sp1.title + expect(page).to have_content "€10,000" + end + + within("#spending_proposal_#{sp2.id}") do + find('.add a').trigger('click') + end + + expect(page).to have_css("#amount-spent", text: "€30,000") + expect(page).to have_css("#amount-available", text: "€3,217,830") + + within("#sidebar") do + expect(page).to have_content sp2.title + expect(page).to have_content "€20,000" + end + end + + scenario "Remove a proposal", :js do + carabanchel = create(:geozone, name: "Carabanchel") + + sp1 = create(:spending_proposal, :feasible, :finished, geozone: carabanchel, price: 10000) + ballot = create(:ballot, user: user, geozone: carabanchel, spending_proposals: [sp1]) + + click_link "Vote district proposals" + click_link carabanchel.name + + expect(page).to have_css("#amount-spent", text: "€10,000") + expect(page).to have_css("#amount-available", text: "€3,237,830") + + within("#sidebar") do + expect(page).to have_content sp1.title + expect(page).to have_content "€10,000" + end + + within("#spending_proposal_#{sp1.id}") do + find('.remove a').trigger('click') + end + + expect(page).to have_css("#amount-spent", text: "€0") + expect(page).to have_css("#amount-available", text: "€3,247,830") + + within("#sidebar") do + expect(page).to_not have_content sp1.title + expect(page).to_not have_content "€10,000" + end + end + + end + + context "City and District" do + + scenario "Independent progress bar for city and district proposals", :js do + carabanchel = create(:geozone, name: "Carabanchel") + + sp1 = create(:spending_proposal, :feasible, :finished, geozone: nil, price: 10000) + sp2 = create(:spending_proposal, :feasible, :finished, geozone: carabanchel, price: 20000) + + click_link "Vote city proposals" + + within("#spending_proposal_#{sp1.id}") do + find('.add a').trigger('click') + expect(page).to have_content "Remove" + end + + expect(page).to have_css("#amount-spent", text: "€10,000") + expect(page).to have_css("#amount-available", text: "€23,990,000") + + within("#sidebar") do + expect(page).to have_content sp1.title + expect(page).to have_content "€10,000" + end + + visit spending_proposals_path(geozone: carabanchel) + + expect(page).to have_css("#amount-spent", text: "€0") + expect(page).to have_css("#amount-spent", text: "€3,247,830") + + within("#spending_proposal_#{sp2.id}") do + find('.add a').trigger('click') + expect(page).to have_content "Remove" + end + + visit spending_proposals_path(geozone: carabanchel) + + expect(page).to have_css("#amount-spent", text: "€20,000") + expect(page).to have_css("#amount-available", text: "€3,227,830") + + within("#sidebar") do + expect(page).to have_content sp2.title + expect(page).to have_content "€20,000" + + expect(page).to_not have_content sp1.title + expect(page).to_not have_content "€10,000" + end + + click_link "Participatory budgeting" + click_link "Vote city proposals" + + expect(page).to have_css("#amount-spent", text: "€10,000") + expect(page).to have_css("#amount-available", text: "€23,990,000") + + within("#sidebar") do + expect(page).to have_content sp1.title + expect(page).to have_content "€10,000" + + expect(page).to_not have_content sp2.title + expect(page).to_not have_content "€20,000" + end + end + end + + scenario "Display progress bar after first district vote", :js do + carabanchel = create(:geozone, name: "Carabanchel") + + sp1 = create(:spending_proposal, :feasible, :finished, geozone: carabanchel, price: 10000) + + click_link "Vote district proposals" + click_link carabanchel.name + + within("#spending_proposal_#{sp1.id}") do + find('.add a').trigger('click') + expect(page).to have_content "Remove" + end + + within("#progress_bar") do + expect(page).to have_css("#amount-spent", text: "€10,000") + end + end + end + + context "Choosing my district" do + let!(:user) { create(:user, :level_two) } + + background do + login_as(user) + visit welcome_spending_proposals_path + end + + scenario 'Select my district', :js do + california = create(:geozone) + new_york = create(:geozone) + sp1 = create(:spending_proposal, :feasible, :finished, geozone: california) + + click_link "Vote district proposals" + click_link california.name + + within("#spending_proposal_#{sp1.id}") do + find('.add a').trigger('click') + end + + visit select_district_path + expect(page).to have_css("#geozone_#{california.id}.active") + end + + scenario 'Change my district', :js do + california = create(:geozone) + new_york = create(:geozone) + + 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]) + + visit spending_proposals_path(geozone: california) + + within("#spending_proposal_#{sp1.id}") do + find('.remove a').trigger('click') + end + + visit spending_proposals_path(geozone: new_york) + + within("#spending_proposal_#{sp2.id}") do + find('.add a').trigger('click') + end + + visit select_district_path + expect(page).to have_css("#geozone_#{new_york.id}.active") + expect(page).to_not have_css("#geozone_#{california.id}.active") + end + + scenario 'View another district' do + california = create(:geozone) + new_york = create(:geozone) + + 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]) + + visit spending_proposals_path(geozone: new_york) + + 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.name, href: spending_proposals_path(geozone: california) + end + + end + + context 'Showing the ballot' do + + scenario 'Displaying the correct count & amount' do + user = create(:user, :level_two) + geozone = create(:geozone, name: "Carabanchel") + ballot = create(:ballot, user: user, geozone: geozone) + + ballot.spending_proposals = + create_list(:spending_proposal, 2, :feasible, :finished, price: 10) + + create_list(:spending_proposal, 3, :feasible, :finished, price: 5, geozone: geozone) + + login_as(user) + visit ballot_path + + expect(page).to have_content("You voted 5 proposals") + within("#city_wide") { expect(page).to have_content "€20" } + within("#district_wide") { expect(page).to have_content "€15" } + end + + end + + scenario 'Removing spending proposals from ballot', :js do + user = create(:user, :level_two) + ballot = create(:ballot, user: user) + sp = create(:spending_proposal, :feasible, :finished, price: 10) + ballot.spending_proposals = [sp] + + login_as(user) + visit ballot_path + + expect(page).to have_content("You voted one proposal") + + within("#spending_proposal_#{sp.id}") do + find(".remove-investment-project").trigger('click') + end + + expect(current_path).to eq(ballot_path) + expect(page).to have_content("You voted 0 proposals") + end + + scenario 'Removing spending proposals from ballot (sidebar)', :js do + user = create(:user, :level_two) + sp1 = create(:spending_proposal, :feasible, :finished, price: 10000) + sp2 = create(:spending_proposal, :feasible, :finished, price: 20000) + + ballot = create(:ballot, user: user, spending_proposals: [sp1, sp2]) + + login_as(user) + visit spending_proposals_path(geozone: 'all') + + expect(page).to have_css("#amount-spent", text: "€30,000") + expect(page).to have_css("#amount-available", text: "€23,970,000") + + within("#sidebar") do + expect(page).to have_content sp1.title + expect(page).to have_content "€10,000" + + expect(page).to have_content sp2.title + expect(page).to have_content "€20,000" + end + + within("#sidebar #spending_proposal_#{sp1.id}_sidebar") do + find(".remove-investment-project").trigger('click') + end + + expect(page).to have_css("#amount-spent", text: "€20,000") + expect(page).to have_css("#amount-available", text: "€23,980,000") + + within("#sidebar") do + expect(page).to_not have_content sp1.title + expect(page).to_not have_content "€10,000" + + expect(page).to have_content sp2.title + expect(page).to have_content "€20,000" + end + end + + scenario 'Removing spending proposals from ballot (sidebar)', :js do + user = create(:user, :level_two) + sp1 = create(:spending_proposal, :feasible, :finished, price: 10000) + sp2 = create(:spending_proposal, :feasible, :finished, price: 20000) + + ballot = create(:ballot, user: user, spending_proposals: [sp1, sp2]) + + login_as(user) + visit spending_proposals_path(geozone: 'all') + + expect(page).to have_css("#amount-spent", text: "€30,000") + expect(page).to have_css("#amount-available", text: "€23,970,000") + + within("#sidebar") do + expect(page).to have_content sp1.title + expect(page).to have_content "€10,000" + + expect(page).to have_content sp2.title + expect(page).to have_content "€20,000" + end + + within("#sidebar #spending_proposal_#{sp1.id}_sidebar") do + find(".remove-investment-project").trigger('click') + end + + expect(page).to have_css("#amount-spent", text: "€20,000") + expect(page).to have_css("#amount-available", text: "€23,980,000") + + within("#sidebar") do + expect(page).to_not have_content sp1.title + expect(page).to_not have_content "€10,000" + + expect(page).to have_content sp2.title + expect(page).to have_content "€20,000" + end + end + + context 'Permissions' do + + scenario 'User not logged in', :js do + spending_proposal = create(:spending_proposal, :feasible, :finished) + + visit spending_proposals_path + + within("#spending_proposal_#{spending_proposal.id}") do + find("div.ballot").hover + expect_message_you_need_to_sign_in + end + end + + scenario 'User not verified', :js do + user = create(:user) + spending_proposal = create(:spending_proposal, :feasible, :finished) + + login_as(user) + visit spending_proposals_path + + within("#spending_proposal_#{spending_proposal.id}") do + find("div.ballot").hover + expect_message_only_verified_can_vote_proposals + end + end + + scenario 'User is organization', :js do + org = create(:organization) + spending_proposal = create(:spending_proposal, :feasible, :finished) + + login_as(org.user) + visit spending_proposals_path + + within("#spending_proposal_#{spending_proposal.id}") do + find("div.ballot").hover + expect_message_organizations_cannot_vote + end + end + + scenario 'Spending proposal unfeasible' do + user = create(:user, :level_two) + spending_proposal = create(:spending_proposal, :finished, feasible: false) + + login_as(user) + visit spending_proposals_path(unfeasible: 1) + + within("#spending_proposal_#{spending_proposal.id}") do + expect(page).to_not have_css("div.ballot") + end + end + + scenario 'Spending proposal with feasibility undecided are not shown' do + user = create(:user, :level_two) + spending_proposal = create(:spending_proposal, :finished, feasible: nil) + + login_as(user) + visit spending_proposals_path + + within("#investment-projects") do + expect(page).to_not have_css("div.ballot") + expect(page).to_not have_css("#spending_proposal_#{spending_proposal.id}") + end + end + + scenario 'Different district', :js do + user = create(:user, :level_two) + california = create(:geozone) + new_york = create(:geozone) + + 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]) + + login_as(user) + visit spending_proposals_path(geozone: new_york) + + within("#spending_proposal_#{sp2.id}") do + find("div.ballot").hover + expect_message_already_voted_in_another_geozone(california) + end + end + + scenario 'Insufficient funds', :js do + user = create(:user, :level_two) + california = create(:geozone) + + sp1 = create(:spending_proposal, :feasible, :finished, price: 25000000) + + login_as(user) + visit spending_proposals_path(geozone: 'all') + + within("#spending_proposal_#{sp1.id}") do + find('.add a').trigger('click') + expect_message_insufficient_funds + 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) + + 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").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_selector('.in-favor a', visible: false) + end + + within("#spending_proposal_#{sp1.id}") do + find('.remove a').trigger('click') + end + + within("#spending_proposal_#{sp2.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 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) + + 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_selector('.in-favor a', visible: false) + end + + within("#spending_proposal_#{sp1.id}_sidebar") do + find('.remove-investment-project').trigger('click') + end + + expect(page).to_not have_css "#spending_proposal_#{sp1.id}_sidebar" + + within("#spending_proposal_#{sp2.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 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) + + sp1 = create(:spending_proposal, :feasible, :finished, price: 20000000) + sp2 = create(:spending_proposal, :feasible, :finished, price: 5000000) + + 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 + end + end + + scenario "Voting proposals when delegating", :js do + forum = create(:forum, name: 'hydra') + user = create(:user, :level_two, representative_id: forum.id) + sp = create(:spending_proposal, :feasible, :finished) + + login_as(user) + visit forums_path + expect(page).to have_content("You are delegating your votes on hydra") + + visit spending_proposals_path(geozone: 'all') + + within("#spending_proposal_#{sp.id}") do + find('.add a').trigger('click') + expect(page).to have_content "Remove vote" + end + + visit forums_path + expect(page).to_not have_content("You are delegating your votes on hydra") + end + + end + + context "voting with GET" do + let!(:user) { create(:user, :level_two) } + let!(:sp_city) { create(:spending_proposal, :feasible, :finished, price: 10000) } + let!(:sp_district) { create(:spending_proposal, :feasible, :finished, price: 10000, geozone: create(:geozone, name: "Barajas")) } + + context "When logged in" do + background { login_as(user) } + + scenario "Vote a City proposal", :js do + visit create_ballot_line_path(spending_proposal_id: sp_city.id) + expect(page).to have_content("You have voted this proposal") + expect(page).to have_link "Remove vote" + end + + scenario "Vote a District proposal", :js do + visit create_ballot_line_path(spending_proposal_id: sp_district.id) + expect(page).to have_content("You have voted this proposal") + expect(page).to have_link "Remove vote" + end + end + + context "When not logged in" do + scenario "Vote a City proposal", :js do + visit create_ballot_line_path(spending_proposal_id: sp_city.id) + + expect(page).to have_content "You must sign in or register to continue" + fill_in 'user_email', with: user.email + fill_in 'user_password', with: user.password + click_button 'Enter' + + expect(page).to have_content("You have voted this proposal") + expect(page).to have_link "Remove vote" + end + + scenario "Vote a District proposal", :js do + visit create_ballot_line_path(spending_proposal_id: sp_district.id) + + expect(page).to have_content "You must sign in or register to continue" + fill_in 'user_email', with: user.email + fill_in 'user_password', with: user.password + click_button 'Enter' + + expect(page).to have_content("You have voted this proposal") + expect(page).to have_link "Remove vote" + 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 +