change CRUD for budget groups and headings

To make it more consistent with the rest of the Admin panel,
the CRUD for budget groups and headings has been changed
from the old "all-in-one" form to a separate form for each resource.
This commit is contained in:
Julian Herrero
2018-12-13 10:34:01 +01:00
parent 8fb8f70efd
commit 6439be44f1
23 changed files with 757 additions and 250 deletions

View File

@@ -2,20 +2,60 @@ class Admin::BudgetGroupsController < Admin::BaseController
include FeatureFlags include FeatureFlags
feature_flag :budgets feature_flag :budgets
before_action :load_budget
before_action :load_group, except: [:index, :new, :create]
def index
@groups = @budget.groups.order(:id)
end
def new
@group = @budget.groups.new
end
def edit
end
def create def create
@budget = Budget.find(params[:budget_id]) @group = @budget.groups.new(budget_group_params)
@budget.groups.create(budget_group_params) if @group.save
@groups = @budget.groups.includes(:headings) redirect_to groups_index, notice: t("admin.budget_groups.create.notice")
else
render :new
end
end end
def update def update
@budget = Budget.find(params[:budget_id]) if @group.update(budget_group_params)
@group = @budget.groups.find(params[:id]) redirect_to groups_index, notice: t("admin.budget_groups.update.notice")
@group.update(budget_group_params) else
render :edit
end
end
def destroy
if @group.headings.any?
redirect_to groups_index, alert: t("admin.budget_groups.destroy.unable_notice")
else
@group.destroy
redirect_to groups_index, notice: t("admin.budget_groups.destroy.success_notice")
end
end end
private private
def load_budget
@budget = Budget.includes(:groups).find(params[:budget_id])
end
def load_group
@group = @budget.groups.find(params[:id])
end
def groups_index
admin_budget_groups_path(@budget)
end
def budget_group_params def budget_group_params
params.require(:budget_group).permit(:name, :max_votable_headings) params.require(:budget_group).permit(:name, :max_votable_headings)
end end

View File

@@ -2,36 +2,65 @@ class Admin::BudgetHeadingsController < Admin::BaseController
include FeatureFlags include FeatureFlags
feature_flag :budgets feature_flag :budgets
def create before_action :load_budget
@budget = Budget.find(params[:budget_id]) before_action :load_group
@budget_group = @budget.groups.find(params[:budget_group_id]) before_action :load_heading, except: [:index, :new, :create]
@budget_group.headings.create(budget_heading_params)
@headings = @budget_group.headings def index
@headings = @group.headings.order(:id)
end
def new
@heading = @group.headings.new
end end
def edit def edit
@budget = Budget.find(params[:budget_id]) end
@budget_group = @budget.groups.find(params[:budget_group_id])
@heading = Budget::Heading.find(params[:id]) def create
@heading = @group.headings.new(budget_heading_params)
if @heading.save
redirect_to headings_index, notice: t('admin.budget_headings.create.notice')
else
render :new
end
end end
def update def update
@budget = Budget.find(params[:budget_id]) if @heading.update(budget_heading_params)
@budget_group = @budget.groups.find(params[:budget_group_id]) redirect_to headings_index, notice: t('admin.budget_headings.update.notice')
@heading = Budget::Heading.find(params[:id]) else
@heading.assign_attributes(budget_heading_params) render :edit
render :edit unless @heading.save end
end end
def destroy def destroy
@heading = Budget::Heading.find(params[:id]) if @heading.can_be_deleted?
@heading.destroy @heading.destroy
@budget = Budget.find(params[:budget_id]) redirect_to headings_index, notice: t('admin.budget_headings.destroy.success_notice')
redirect_to admin_budget_path(@budget) else
redirect_to headings_index, alert: t('admin.budget_headings.destroy.unable_notice')
end
end end
private private
def load_budget
@budget = Budget.includes(:groups).find(params[:budget_id])
end
def load_group
@group = @budget.groups.find(params[:group_id])
end
def load_heading
@heading = @group.headings.find(params[:id])
end
def headings_index
admin_budget_group_headings_path(@budget, @group)
end
def budget_heading_params def budget_heading_params
params.require(:budget_heading).permit(:name, :price, :population, :allow_custom_content, :latitude, :longitude) params.require(:budget_heading).permit(:name, :price, :population, :allow_custom_content, :latitude, :longitude)
end end

View File

@@ -11,12 +11,13 @@ class Admin::BudgetsController < Admin::BaseController
end end
def show def show
@budget = Budget.includes(groups: :headings).find(params[:id])
end end
def new; end def new
end
def edit; end def edit
end
def calculate_winners def calculate_winners
return unless @budget.balloting_process? return unless @budget.balloting_process?

View File

@@ -17,6 +17,7 @@ module Budgets
def create def create
load_investment load_investment
load_heading load_heading
load_map
@ballot.add_investment(@investment) @ballot.add_investment(@investment)
end end
@@ -24,6 +25,7 @@ module Budgets
def destroy def destroy
@investment = @line.investment @investment = @line.investment
load_heading load_heading
load_map
@line.destroy @line.destroy
load_investments load_investments
@@ -74,6 +76,12 @@ module Budgets
@ballot_referer = session[:ballot_referer] @ballot_referer = session[:ballot_referer]
end end
def load_map
@investments ||= []
@investments_map_coordinates = MapLocation.where(investment: @investments).map(&:json_data)
@map_location = MapLocation.load_from_heading(@heading)
end
end end
end end
end end

View File

@@ -0,0 +1,18 @@
<div class="small-12 medium-6">
<%= form_for [:admin, @budget, @group], url: path do |f| %>
<%= f.text_field :name,
label: t("admin.budget_groups.form.name"),
maxlength: 50,
placeholder: t("admin.budget_groups.form.name") %>
<% if @group.persisted? %>
<%= f.select :max_votable_headings,
(1..@group.headings.count),
label: t("admin.budget_groups.max_votable_headings"),
placeholder: t("admin.budget_groups.max_votable_headings") %>
<% end %>
<%= f.submit t("admin.budget_groups.form.#{action}"), class: "button success" %>
<% end %>
</div>

View File

@@ -0,0 +1,5 @@
<%= back_link_to admin_budget_groups_path(@budget) %>
<h2><%= @budget.name %></h2>
<h3><%= t("admin.budget_groups.form.#{action}") %></h3>

View File

@@ -0,0 +1,3 @@
<%= render "header", action: "edit" %>
<%= render "form", path: admin_budget_group_path(@budget, @group), action: "edit" %>

View File

@@ -0,0 +1,44 @@
<h2 class="inline-block"><%= @budget.name %></h2>
<%= link_to t("admin.budget_groups.form.create"),
new_admin_budget_group_path,
class: "button float-right" %>
<% if @groups.any? %>
<h3><%= t("admin.budget_groups.amount", count: @groups.count) %></h3>
<table>
<thead>
<tr id="<%= dom_id(@budget) %>">
<th><%= t("admin.budget_groups.name") %></th>
<th><%= t("admin.budget_groups.max_votable_headings") %></th>
<th><%= t("admin.budget_groups.headings_name") %></th>
<th><%= t("admin.budget_groups.headings_edit") %></th>
<th><%= t("admin.actions.actions") %></th>
</tr>
</thead>
<tbody>
<% @groups.each do |group| %>
<tr id="<%= dom_id(group) %>">
<td><%= link_to group.name, edit_admin_budget_group_path(@budget, group) %></td>
<td><%= group.max_votable_headings %></td>
<td><%= group.headings.count %></td>
<td><%= link_to t("admin.budget_groups.headings_manage"),
admin_budget_group_headings_path(@budget, group) %></td>
<td>
<%= link_to t("admin.actions.edit"),
edit_admin_budget_group_path(@budget, group),
class: "button hollow" %>
<%= link_to t("admin.actions.delete"),
admin_budget_group_path(@budget, group),
method: :delete,
class: "button hollow alert" %>
</td>
</tr>
<% end %>
</tbody>
</table>
<% else %>
<div class="callout primary clear">
<%= t("admin.budget_groups.no_groups") %>
</div>
<% end %>

View File

@@ -0,0 +1,3 @@
<%= render "header", action: "create" %>
<%= render "form", path: admin_budget_groups_path(@budget), action: "create" %>

View File

@@ -0,0 +1,40 @@
<div class="small-12 medium-6">
<%= form_for [:admin, @budget, @group, @heading], url: path do |f| %>
<%= f.text_field :name,
label: t("admin.budget_headings.form.name"),
maxlength: 50,
placeholder: t("admin.budget_headings.form.name") %>
<%= f.text_field :price,
label: t("admin.budget_headings.form.amount"),
maxlength: 8,
placeholder: t("admin.budget_headings.form.amount") %>
<%= f.label :population, t("admin.budget_headings.form.population") %>
<p class="help-text" id="budgets-population-help-text">
<%= t("admin.budget_headings.form.population_info") %>
</p>
<%= f.text_field :population,
label: false,
maxlength: 8,
placeholder: t("admin.budget_headings.form.population"),
data: {toggle_focus: "population-info"},
aria: {describedby: "budgets-population-help-text"} %>
<%= f.text_field :latitude,
label: t("admin.budget_headings.form.latitude"),
maxlength: 22,
placeholder: "latitude" %>
<%= f.text_field :longitude,
label: t("admin.budget_headings.form.longitude"),
maxlength: 22,
placeholder: "longitude" %>
<%= f.check_box :allow_custom_content, label: t("admin.budget_headings.form.allow_content_block") %>
<%= f.submit t("admin.budget_headings.form.#{action}"), class: "button success" %>
<% end %>
</div>

View File

@@ -0,0 +1,5 @@
<%= back_link_to admin_budget_group_headings_path(@budget, @group) %>
<h2><%= "#{@budget.name} / #{@group.name}" %></h2>
<h3><%= t("admin.budget_headings.form.#{action}") %></h3>

View File

@@ -0,0 +1,3 @@
<%= render "header", action: "edit" %>
<%= render "form", path: admin_budget_group_heading_path(@budget, @group, @heading), action: "edit" %>

View File

@@ -0,0 +1,47 @@
<%= back_link_to admin_budget_groups_path(@budget) %>
<div class="clear"></div>
<h2 class="inline-block"><%= "#{@budget.name} / #{@group.name}" %></h2>
<%= link_to t("admin.budget_headings.form.create"),
new_admin_budget_group_heading_path,
class: "button float-right" %>
<% if @headings.any? %>
<h3><%= t("admin.budget_headings.amount", count: @headings.count) %></h3>
<table>
<thead>
<tr id="<%= dom_id(@group) %>">
<th><%= t("admin.budget_headings.name") %></th>
<th><%= t("admin.budget_headings.form.amount") %></th>
<th><%= t("admin.budget_headings.form.population") %></th>
<th><%= t("admin.budget_headings.form.allow_content_block") %></th>
<th><%= t("admin.actions.actions") %></th>
</tr>
</thead>
<tbody>
<% @headings.each do |heading| %>
<tr id="<%= dom_id(heading) %>">
<td><%= link_to heading.name, edit_admin_budget_group_heading_path(@budget, @group, heading) %></td>
<td><%= @budget.formatted_heading_price(heading) %></td>
<td><%= heading.population %></td>
<td>
<%= heading.allow_custom_content ? t("admin.shared.true_value") : t("admin.shared.false_value") %>
</td>
<td>
<%= link_to t("admin.actions.edit"),
edit_admin_budget_group_heading_path(@budget, @group, heading),
class: "button hollow" %>
<%= link_to t("admin.actions.delete"),
admin_budget_group_heading_path(@budget, @group, heading),
method: :delete,
class: "button hollow alert" %>
</td>
</tr>
<% end %>
</tbody>
</table>
<% else %>
<div class="callout primary clear">
<%= t("admin.budget_headings.no_headings") %>
</div>
<% end %>

View File

@@ -0,0 +1,3 @@
<%= render "header", action: "create" %>
<%= render "form", path: admin_budget_group_headings_path(@budget, @group), action: "create" %>

View File

@@ -23,7 +23,7 @@
<% @budgets.each do |budget| %> <% @budgets.each do |budget| %>
<tr id="<%= dom_id(budget) %>" class="budget"> <tr id="<%= dom_id(budget) %>" class="budget">
<td> <td>
<%= budget.name %> <%= link_to budget.name, admin_budget_path(budget) %>
</td> </td>
<td class="small"> <td class="small">
<%= t("budgets.phase.#{budget.phase}") %> <%= t("budgets.phase.#{budget.phase}") %>
@@ -34,7 +34,7 @@
class: "button hollow medium" %> class: "button hollow medium" %>
</td> </td>
<td class="small"> <td class="small">
<%= link_to t("admin.budgets.index.edit_groups"), admin_budget_path(budget) %> <%= link_to t("admin.budgets.index.edit_groups"), admin_budget_groups_path(budget) %>
</td> </td>
<td class="small"> <td class="small">
<%= link_to t("admin.budgets.index.edit_budget"), edit_admin_budget_path(budget) %> <%= link_to t("admin.budgets.index.edit_budget"), edit_admin_budget_path(budget) %>

View File

@@ -2,6 +2,4 @@
<h2><%= @budget.name %></h2> <h2><%= @budget.name %></h2>
<div id="<%= dom_id @budget %>_groups"> <%= render "form" %>
<%= render "groups", groups: @budget.groups.order(:id) %>
</div>

View File

@@ -107,38 +107,56 @@ en:
unable_notice: You cannot destroy a Budget that has associated investments unable_notice: You cannot destroy a Budget that has associated investments
new: new:
title: New participatory budget title: New participatory budget
show:
groups:
one: 1 Group of budget headings
other: "%{count} Groups of budget headings"
form:
group: Group name
no_groups: No groups created yet. Each user will be able to vote in only one heading per group.
add_group: Add new group
create_group: Create group
edit_group: Edit group
submit: Save group
heading: Heading name
add_heading: Add heading
amount: Amount
population: "Population (optional)"
population_help_text: "This data is used exclusively to calculate the participation statistics"
save_heading: Save heading
no_heading: This group has no assigned heading.
table_heading: Heading
table_amount: Amount
table_population: Population
table_allow_custom_contents: Custom content allowed
population_info: "Budget Heading population field is used for Statistic purposes at the end of the Budget to show for each Heading that represents an area with population what percentage voted. The field is optional so you can leave it empty if it doesn't apply."
max_votable_headings: "Maximum number of headings in which a user can vote"
current_of_max_headings: "%{current} of %{max}"
allow_content_block: Allow content block
latitude: Latitude
longitude: Longitude
winners: winners:
calculate: Calculate Winner Investments calculate: Calculate Winner Investments
calculated: Winners being calculated, it may take a minute. calculated: Winners being calculated, it may take a minute.
recalculate: Recalculate Winner Investments recalculate: Recalculate Winner Investments
budget_groups:
name: "Name"
headings_name: "Headings"
headings_edit: "Edit Headings"
headings_manage: "Manage headings"
max_votable_headings: "Maximum number of headings in which a user can vote"
no_groups: "No groups created yet. Each user will be able to vote in only one heading per group."
amount:
one: "There is 1 group"
other: "There are %{count} groups"
create:
notice: "Group created successfully!"
update:
notice: "Group updated successfully"
destroy:
success_notice: "Group deleted successfully"
unable_notice: "You cannot destroy a Group that has associated headings"
form:
create: "Create new group"
edit: "Edit group"
name: "Group name"
submit: "Save group"
budget_headings:
name: "Name"
no_headings: "No headings created yet. Each user will be able to vote in only one heading per group."
amount:
one: "There is 1 heading"
other: "There are %{count} headings"
create:
notice: "Heading created successfully!"
update:
notice: "Heading updated successfully"
destroy:
success_notice: "Heading deleted successfully"
unable_notice: "You cannot destroy a Heading that has associated investments"
form:
name: "Heading name"
amount: "Amount"
population: "Population (optional)"
population_info: "Budget Heading population field is used for Statistic purposes at the end of the Budget to show for each Heading that represents an area with population what percentage voted. The field is optional so you can leave it empty if it doesn't apply."
latitude: "Latitude"
longitude: "Longitude"
allow_content_block: "Allow content block"
create: "Create new heading"
edit: "Edit heading"
submit: "Save heading"
budget_phases: budget_phases:
edit: edit:
start_date: Start date start_date: Start date

View File

@@ -107,35 +107,56 @@ es:
unable_notice: No se puede eliminar un presupuesto con proyectos asociados unable_notice: No se puede eliminar un presupuesto con proyectos asociados
new: new:
title: Nuevo presupuesto ciudadano title: Nuevo presupuesto ciudadano
show:
groups:
one: 1 Grupo de partidas presupuestarias
other: "%{count} Grupos de partidas presupuestarias"
form:
group: Nombre del grupo
no_groups: No hay grupos creados todavía. Cada usuario podrá votar en una sola partida de cada grupo.
add_group: Añadir nuevo grupo
create_group: Crear grupo
edit_group: Editar grupo
submit: Guardar grupo
heading: Nombre de la partida
add_heading: Añadir partida
amount: Cantidad
population: "Población (opcional)"
population_help_text: "Este dato se utiliza exclusivamente para calcular las estadísticas de participación"
save_heading: Guardar partida
no_heading: Este grupo no tiene ninguna partida asignada.
table_heading: Partida
table_amount: Cantidad
table_population: Población
population_info: "El campo población de las partidas presupuestarias se usa con fines estadísticos únicamente, con el objetivo de mostrar el porcentaje de votos habidos en cada partida que represente un área con población. Es un campo opcional, así que puedes dejarlo en blanco si no aplica."
max_votable_headings: "Máximo número de partidas en que un usuario puede votar"
current_of_max_headings: "%{current} de %{max}"
allow_content_block: Permite bloque de contenidos
winners: winners:
calculate: Calcular proyectos ganadores calculate: Calcular proyectos ganadores
calculated: Calculando ganadores, puede tardar un minuto. calculated: Calculando ganadores, puede tardar un minuto.
recalculate: Recalcular propuestas ganadoras recalculate: Recalcular propuestas ganadoras
budget_groups:
name: "Nombre"
headings_name: "Partidas"
headings_edit: "Editar Partidas"
headings_manage: "Gestionar partidas"
max_votable_headings: "Máximo número de partidas en que un usuario puede votar"
no_groups: "No hay grupos creados todavía. Cada usuario podrá votar en una sola partida de cada grupo."
amount:
one: "Hay 1 grupo de partidas presupuestarias"
other: "Hay %{count} grupos de partidas presupuestarias"
create:
notice: "¡Grupo creado con éxito!"
update:
notice: "Grupo actualizado"
destroy:
success_notice: "Grupo eliminado correctamente"
unable_notice: "No se puede eliminar un grupo con partidas asociadas"
form:
create: "Crear nuevo grupo"
edit: "Editar grupo"
name: "Nombre del grupo"
submit: "Guardar grupo"
budget_headings:
name: "Nombre"
no_headings: "No hay partidas creadas todavía. Cada usuario podrá votar en una sola partida de cada grupo."
amount:
one: "Hay 1 partida presupuestarias"
other: "Hay %{count} partidas presupuestarias"
create:
notice: "¡Partida presupuestaria creada con éxito!"
update:
notice: "Partida presupuestaria actualizada"
destroy:
success_notice: "Partida presupuestaria eliminada correctamente"
unable_notice: "No se puede eliminar una partida presupuestaria con proyectos asociados"
form:
name: "Nombre de la partida"
amount: "Cantidad"
population: "Población (opcional)"
population_info: "El campo población de las partidas presupuestarias se usa con fines estadísticos únicamente, con el objetivo de mostrar el porcentaje de votos habidos en cada partida que represente un área con población. Es un campo opcional, así que puedes dejarlo en blanco si no aplica."
latitude: "Latitud"
longitude: "Longitud"
allow_content_block: "Permitir bloque de contenidos"
create: "Crear nueva partida"
edit: "Editar partida"
submit: "Guardar partida"
budget_phases: budget_phases:
edit: edit:
start_date: Fecha de Inicio start_date: Fecha de Inicio

View File

@@ -57,8 +57,8 @@ namespace :admin do
put :calculate_winners put :calculate_winners
end end
resources :budget_groups do resources :groups, except: [:show], controller: "budget_groups" do
resources :budget_headings resources :headings, except: [:show], controller: "budget_headings"
end end
resources :budget_investments, only: [:index, :show, :edit, :update] do resources :budget_investments, only: [:index, :show, :edit, :update] do

View File

@@ -1,89 +1,177 @@
require 'rails_helper' require 'rails_helper'
feature 'Admin can change the groups name' do feature "Admin budget groups" do
let(:budget) { create(:budget, phase: 'drafting') } let(:budget) { create(:budget, phase: "drafting") }
let(:group) { create(:budget_group, budget: budget) }
background do background do
admin = create(:administrator) admin = create(:administrator)
login_as(admin.user) login_as(admin.user)
end end
scenario "Show button" do context "Feature flag" do
visit admin_budget_path(group.budget)
within("#budget_group_#{group.id}") do
expect(page).to have_content('Edit group')
end
end
scenario "Change name" do
group.update(name: 'Google')
expect(group.name).to eq('Google')
end
scenario "Can change name when the budget isn't drafting, but the slug remains" do
old_slug = group.slug
budget.update(phase: 'reviewing')
group.update(name: 'Google')
expect(group.name).to eq('Google')
expect(group.slug).to eq old_slug
end
scenario "Can't repeat names" do
budget.groups << create(:budget_group, name: 'group_name')
group.name = 'group_name'
expect(group).not_to be_valid
expect(group.errors.size).to eq(1)
end
context "Maximum votable headings" do
background do background do
3.times { create(:budget_heading, group: group) } Setting["feature.budgets"] = nil
end end
scenario "Defaults to 1 heading per group", :js do after do
visit admin_budget_path(group.budget) Setting["feature.budgets"] = true
end
expect(page).to have_content('Maximum number of headings in which a user can vote 1 of 3') scenario "Disabled with a feature flag" do
expect { visit admin_budget_groups_path(budget) }.to raise_exception(FeatureFlags::FeatureDisabled)
end
within("#budget_group_#{group.id}") do end
click_link 'Edit group'
expect(page).to have_select('budget_group_max_votable_headings', selected: '1') context "Index" do
scenario "Displaying no groups for budget" do
visit admin_budget_groups_path(budget)
expect(page).to have_content "No groups created yet. Each user will be able to vote in only one heading per group."
end
scenario "Displaying groups" do
group1 = create(:budget_group, budget: budget)
group2 = create(:budget_group, budget: budget)
create(:budget_heading, group: group2)
group3 = create(:budget_group, budget: budget, max_votable_headings: 2)
3.times { create(:budget_heading, group: group3) }
visit admin_budget_groups_path(budget)
expect(page).to have_content "There are 3 groups"
within "#budget_group_#{group1.id}" do
expect(page).to have_content(group1.name)
expect(page).to have_content(group1.max_votable_headings)
expect(page).to have_content(group1.headings.count)
expect(page).to have_link "Manage headings", href: admin_budget_group_headings_path(budget, group1)
end
within "#budget_group_#{group2.id}" do
expect(page).to have_content(group2.name)
expect(page).to have_content(group2.max_votable_headings)
expect(page).to have_content(group2.headings.count)
expect(page).to have_link "Manage headings", href: admin_budget_group_headings_path(budget, group2)
end
within "#budget_group_#{group3.id}" do
expect(page).to have_content(group3.name)
expect(page).to have_content(group3.max_votable_headings)
expect(page).to have_content(group3.headings.count)
expect(page).to have_link "Manage headings", href: admin_budget_group_headings_path(budget, group3)
end end
end end
scenario "Update", :js do scenario "Delete a group without headings" do
visit admin_budget_path(group.budget) group = create(:budget_group, budget: budget)
within("#budget_group_#{group.id}") do visit admin_budget_groups_path(budget)
click_link 'Edit group' within("#budget_group_#{group.id}") { click_link "Delete" }
select '2', from: 'budget_group_max_votable_headings' expect(page).to have_content "Group deleted successfully"
click_button 'Save group' expect(page).not_to have_selector "#budget_group_#{group.id}"
end
expect(page).to have_content('Maximum number of headings in which a user can vote 2 of 3')
within("#budget_group_#{group.id}") do
click_link 'Edit group'
expect(page).to have_select('budget_group_max_votable_headings', selected: '2')
end
end end
scenario "Do not display maximum votable headings' select in new form", :js do scenario "Try to delete a group with headings" do
visit admin_budget_path(group.budget) group = create(:budget_group, budget: budget)
create(:budget_heading, group: group)
click_link 'Add new group' visit admin_budget_groups_path(budget)
within("#budget_group_#{group.id}") { click_link "Delete" }
expect(page).to have_field('budget_group_name') expect(page).to have_content "You cannot destroy a Group that has associated headings"
expect(page).not_to have_field('budget_group_max_votable_headings') expect(page).to have_selector "#budget_group_#{group.id}"
end
end
context "New" do
scenario "Create group" do
visit admin_budget_groups_path(budget)
click_link "Create new group"
fill_in "Group name", with: "All City"
click_button "Create new group"
expect(page).to have_content "Group created successfully!"
expect(page).to have_link "All City"
end
scenario "Maximum number of headings in which a user can vote is set to 1 by default" do
visit new_admin_budget_group_path(budget)
fill_in "Group name", with: "All City"
click_button "Create new group"
expect(page).to have_content "Group created successfully!"
expect(Budget::Group.first.max_votable_headings).to be 1
end
scenario "Group name is mandatory" do
visit new_admin_budget_group_path(budget)
click_button "Create new group"
expect(page).not_to have_content "Group created successfully!"
expect(page).to have_css("label.error", text: "Group name")
expect(page).to have_content "can't be blank"
end
end
context "Edit" do
scenario "Show group information" do
group = create(:budget_group, budget: budget, max_votable_headings: 2)
2.times { create(:budget_heading, group: group) }
visit admin_budget_groups_path(budget)
within("#budget_group_#{group.id}") { click_link "Edit" }
expect(page).to have_field "Group name", with: group.name
expect(page).to have_field "Maximum number of headings in which a user can vote", with: "2"
end
end
context "Update" do
let!(:group) { create(:budget_group, budget: budget, name: "All City") }
scenario "Updates group" do
2.times { create(:budget_heading, group: group) }
visit edit_admin_budget_group_path(budget, group)
expect(page).to have_field "Group name", with: "All City"
fill_in "Group name", with: "Districts"
select "2", from: "Maximum number of headings in which a user can vote"
click_button "Edit group"
expect(page).to have_content "Group updated successfully"
visit edit_admin_budget_group_path(budget, group)
expect(page).to have_field "Group name", with: "Districts"
expect(page).to have_field "Maximum number of headings in which a user can vote", with: "2"
end
scenario "Group name is already used" do
create(:budget_group, budget: budget, name: "Districts")
visit edit_admin_budget_group_path(budget, group)
expect(page).to have_field "Group name", with: "All City"
fill_in "Group name", with: "Districts"
click_button "Edit group"
expect(page).not_to have_content "Group updated successfully"
expect(page).to have_css("label.error", text: "Group name")
expect(page).to have_content "has already been taken"
end end
end end

View File

@@ -0,0 +1,208 @@
require 'rails_helper'
feature "Admin budget headings" do
let(:budget) { create(:budget, phase: "drafting") }
let(:group) { create(:budget_group, budget: budget) }
background do
admin = create(:administrator)
login_as(admin.user)
end
context "Feature flag" do
background do
Setting["feature.budgets"] = nil
end
after do
Setting["feature.budgets"] = true
end
scenario "Disabled with a feature flag" do
expect { visit admin_budget_group_headings_path(budget, group) }.to raise_exception(FeatureFlags::FeatureDisabled)
end
end
context "Index" do
scenario "Displaying no headings for group" do
visit admin_budget_group_headings_path(budget, group)
expect(page).to have_content "No headings created yet. Each user will be able to vote in only one heading per group."
end
scenario "Displaying headings" do
heading1 = create(:budget_heading, group: group, price: 1000, allow_custom_content: true)
heading2 = create(:budget_heading, group: group, price: 2000, population: 10000)
heading3 = create(:budget_heading, group: group, price: 3000, population: 10000)
visit admin_budget_group_headings_path(budget, group)
expect(page).to have_content "There are 3 headings"
within "#budget_heading_#{heading1.id}" do
expect(page).to have_content(heading1.name)
expect(page).to have_content "€1,000"
expect(page).not_to have_content "10000"
expect(page).to have_content "Yes"
expect(page).to have_link "Edit", href: edit_admin_budget_group_heading_path(budget, group, heading1)
expect(page).to have_link "Delete", href: admin_budget_group_heading_path(budget, group, heading1)
end
within "#budget_heading_#{heading2.id}" do
expect(page).to have_content(heading2.name)
expect(page).to have_content "€2,000"
expect(page).to have_content "10000"
expect(page).to have_content "No"
expect(page).to have_link "Edit", href: edit_admin_budget_group_heading_path(budget, group, heading2)
expect(page).to have_link "Delete", href: admin_budget_group_heading_path(budget, group, heading2)
end
within "#budget_heading_#{heading3.id}" do
expect(page).to have_content(heading3.name)
expect(page).to have_content "€3,000"
expect(page).to have_content "10000"
expect(page).to have_content "No"
expect(page).to have_link "Edit", href: edit_admin_budget_group_heading_path(budget, group, heading3)
expect(page).to have_link "Delete", href: admin_budget_group_heading_path(budget, group, heading3)
end
end
scenario "Delete a heading without investments" do
heading = create(:budget_heading, group: group)
visit admin_budget_group_headings_path(budget, group)
within("#budget_heading_#{heading.id}") { click_link "Delete" }
expect(page).to have_content "Heading deleted successfully"
expect(page).not_to have_selector "#budget_heading_#{heading.id}"
end
scenario "Try to delete a heading with investments" do
heading = create(:budget_heading, group: group)
investment = create(:budget_investment, heading: heading)
visit admin_budget_group_headings_path(budget, group)
within("#budget_heading_#{heading.id}") { click_link "Delete" }
expect(page).to have_content "You cannot destroy a Heading that has associated investments"
expect(page).to have_selector "#budget_heading_#{heading.id}"
end
end
context "New" do
scenario "Create heading" do
visit admin_budget_group_headings_path(budget, group)
click_link "Create new heading"
fill_in "Heading name", with: "All City"
fill_in "Amount", with: "1000"
fill_in "Population (optional)", with: "10000"
check "Allow content block"
click_button "Create new heading"
expect(page).to have_content "Heading created successfully!"
expect(page).to have_link "All City"
expect(page).to have_content "€1,000"
expect(page).to have_content "10000"
expect(page).to have_content "Yes"
end
scenario "Heading name is mandatory" do
visit new_admin_budget_group_heading_path(budget, group)
click_button "Create new heading"
expect(page).not_to have_content "Heading created successfully!"
expect(page).to have_css("label.error", text: "Heading name")
expect(page).to have_content "can't be blank"
end
scenario "Heading amount is mandatory" do
visit new_admin_budget_group_heading_path(budget, group)
click_button "Create new heading"
expect(page).not_to have_content "Heading created successfully!"
expect(page).to have_css("label.error", text: "Amount")
expect(page).to have_content "can't be blank"
end
end
context "Edit" do
scenario "Show heading information" do
heading = create(:budget_heading, group: group)
visit admin_budget_group_headings_path(budget, group)
within("#budget_heading_#{heading.id}") { click_link "Edit" }
expect(page).to have_field "Heading name", with: heading.name
expect(page).to have_field "Amount", with: heading.price
expect(page).to have_field "Population (optional)", with: heading.population
expect(page).to have_field "Longitude", with: heading.longitude
expect(page).to have_field "Latitude", with: heading.latitude
expect(find_field("Allow content block")).not_to be_checked
end
end
context "Update" do
let(:heading) { create(:budget_heading,
group: group,
name: "All City",
price: 1000,
population: 10000,
longitude: 20.50,
latitude: -10.50,
allow_custom_content: true) }
scenario "Updates group" do
visit edit_admin_budget_group_heading_path(budget, group, heading)
expect(page).to have_field "Heading name", with: "All City"
expect(page).to have_field "Amount", with: 1000
expect(page).to have_field "Population (optional)", with: 10000
expect(page).to have_field "Longitude", with: 20.50
expect(page).to have_field "Latitude", with: -10.50
expect(find_field("Allow content block")).to be_checked
fill_in "Heading name", with: "Districts"
fill_in "Amount", with: "2000"
fill_in "Population (optional)", with: "20000"
fill_in "Longitude", with: "-40.47"
fill_in "Latitude", with: "25.25"
uncheck "Allow content block"
click_button "Edit heading"
expect(page).to have_content "Heading updated successfully"
visit edit_admin_budget_group_heading_path(budget, group, heading)
expect(page).to have_field "Heading name", with: "Districts"
expect(page).to have_field "Amount", with: 2000
expect(page).to have_field "Population (optional)", with: 20000
expect(page).to have_field "Longitude", with: -40.47
expect(page).to have_field "Latitude", with: 25.25
expect(find_field("Allow content block")).not_to be_checked
end
scenario "Heading name is already used" do
create(:budget_heading, group: group, name: "Districts")
visit edit_admin_budget_group_heading_path(budget, group, heading)
expect(page).to have_field "Heading name", with: "All City"
fill_in "Heading name", with: "Districts"
click_button "Edit heading"
expect(page).not_to have_content "Heading updated successfully"
expect(page).to have_css("label.error", text: "Heading name")
expect(page).to have_content "has already been taken"
end
end
end

View File

@@ -227,109 +227,6 @@ feature 'Admin budgets' do
end end
end end
context 'Manage groups and headings' do
scenario 'Create group', :js do
budget = create(:budget, name: 'Yearly budget')
visit admin_budgets_path
within("#budget_#{budget.id}") do
click_link 'Edit headings groups'
end
expect(page).to have_content '0 Groups of budget headings'
expect(page).to have_content 'No groups created yet.'
click_link 'Add new group'
fill_in 'budget_group_name', with: 'Health'
click_button 'Create group'
expect(page).to have_content '1 Group of budget headings'
expect(page).to have_content 'Health'
expect(page).to have_content 'Yearly budget'
expect(page).not_to have_content 'No groups created yet.'
visit admin_budgets_path
within("#budget_#{budget.id}") do
click_link 'Edit headings groups'
end
expect(page).to have_content '1 Group of budget headings'
expect(page).to have_content 'Health'
expect(page).to have_content 'Yearly budget'
expect(page).not_to have_content 'No groups created yet.'
end
scenario 'Create heading', :js do
budget = create(:budget, name: 'Yearly budget')
group = create(:budget_group, budget: budget, name: 'Districts improvments')
visit admin_budget_path(budget)
within("#budget_group_#{group.id}") do
expect(page).to have_content 'This group has no assigned heading.'
click_link 'Add heading'
fill_in 'budget_heading_name', with: 'District 9 reconstruction'
fill_in 'budget_heading_price', with: '6785'
fill_in 'budget_heading_population', with: '100500'
fill_in 'budget_heading_latitude', with: '40.416775'
fill_in 'budget_heading_longitude', with: '-3.703790'
click_button 'Save heading'
end
visit admin_budget_path(budget)
within("#budget_group_#{group.id}") do
expect(page).not_to have_content 'This group has no assigned heading.'
expect(page).to have_content 'District 9 reconstruction'
expect(page).to have_content '€6,785'
expect(page).to have_content '100500'
end
end
scenario 'Update heading', :js do
budget = create(:budget, name: 'Yearly budget')
group = create(:budget_group, budget: budget, name: 'Districts improvments')
heading = create(:budget_heading, group: group, name: "District 1")
heading = create(:budget_heading, group: group, name: "District 3")
visit admin_budget_path(budget)
within("#heading-#{heading.id}") do
click_link 'Edit'
fill_in 'budget_heading_name', with: 'District 2'
fill_in 'budget_heading_price', with: '10000'
fill_in 'budget_heading_population', with: '6000'
click_button 'Save heading'
end
expect(page).to have_content 'District 2'
expect(page).to have_content '€10,000'
expect(page).to have_content '6000'
end
scenario 'Delete heading', :js do
budget = create(:budget, name: 'Yearly budget')
group = create(:budget_group, budget: budget, name: 'Districts improvments')
heading = create(:budget_heading, group: group, name: "District 1")
visit admin_budget_path(budget)
expect(page).to have_content 'District 1'
within("#heading-#{heading.id}") do
click_link 'Delete'
end
expect(page).not_to have_content 'District 1'
end
end
end end
def translated_phase_name(phase_kind: kind) def translated_phase_name(phase_kind: kind)

View File

@@ -161,6 +161,34 @@ feature 'Ballots' do
end end
end end
scenario "the Map shoud be visible before and after", :js do
investment = create(:budget_investment, :selected, heading: new_york, price: 10000)
visit budget_path(budget)
click_link "States"
click_link "New York"
within("#sidebar") do
expect(page).to have_content "OpenStreetMap"
end
add_to_ballot(investment)
within("#sidebar") do
expect(page).to have_content investment.title
expect(page).to have_content "OpenStreetMap"
end
within("#budget_investment_#{investment.id}") do
click_link "Remove vote"
end
within("#sidebar") do
expect(page).not_to have_content investment.title
expect(page).to have_content "OpenStreetMap"
end
end
end end
#Break up or simplify with helpers #Break up or simplify with helpers