Groups and headings CRUD from budget view

Before, users needed to navigate to the list of groups in order to
add, edit or delete a group.

Also, they need to navigate to the list of groups first, and then to
the list of headings for that group in order to add, edit or delete a
heading.

Now, it's possible to do all these actions for any group or heading
from the participatory budget view to bring simplicity and to reduce
the number of clicks from a user perspective.

Co-Authored-By: Javi Martín <javim@elretirao.net>
This commit is contained in:
Julian Herrero
2020-03-24 07:20:22 +01:00
committed by Javi Martín
parent c8827f5c7f
commit 2b709f1a36
33 changed files with 309 additions and 223 deletions

View File

@@ -0,0 +1,92 @@
.admin .groups-and-headings {
$gap: rem-calc(map-get($grid-column-gutter, medium));
> section + section {
margin-top: $line-height * 1.5;
}
> section {
display: flex;
flex-wrap: wrap;
}
h4 {
margin-#{$global-right}: $gap;
}
dl,
.callout {
width: 100%;
}
dt,
dd {
display: inline;
}
dt {
font-weight: normal;
}
dd {
font-weight: bold;
}
th:last-child {
text-align: $global-right;
}
.table-actions {
justify-content: flex-end;
}
.groups-actions {
@include flex-with-gap($gap);
align-items: flex-start;
flex: 1;
margin-bottom: $line-height / 2;
.edit-link,
.destroy-link {
@include icon-on-top;
}
.edit-link {
@include has-fa-icon(edit, regular);
}
.destroy-link {
@include has-fa-icon(trash-alt, regular);
color: darken($alert-color, 5%);
}
.new-link {
@include hollow-button;
flex-shrink: 0;
}
> :nth-last-child(2) {
margin-#{$global-right}: $gap;
}
> :last-child {
margin-#{$global-left}: auto;
}
button {
cursor: pointer;
}
}
.new-link {
@include has-fa-icon(plus-square, solid);
&::before {
margin-#{$global-right}: $font-icon-margin;
}
}
> .new-link {
@include regular-button;
}
}

View File

@@ -9,4 +9,8 @@
} }
} }
} }
h4 {
@include header-font-size(h3);
}
} }

View File

@@ -5,22 +5,7 @@
a, a,
button { button {
align-items: center; @include icon-on-top;
display: flex;
flex-direction: column;
font-size: 0.9em;
line-height: $global-lineheight;
text-align: center;
&:hover,
&:focus {
color: $link-hover;
text-decoration: none;
}
&::before {
font-size: 1.6em;
}
} }
button { button {
@@ -114,7 +99,6 @@
@include has-fa-icon(coins, solid); @include has-fa-icon(coins, solid);
} }
.groups-link,
.headings-link { .headings-link {
@include has-fa-icon(chart-pie, solid); @include has-fa-icon(chart-pie, solid);
color: $color-success; color: $color-success;

View File

@@ -1,4 +1,4 @@
.groups-and-headings { .public .groups-and-headings {
$spacing: $line-height / 2; $spacing: $line-height / 2;
.headings-list { .headings-list {

View File

@@ -1505,3 +1505,22 @@ $font-awesome-icons: (
mask-image: var(--fa-icon-#{$style}-#{$icon}); mask-image: var(--fa-icon-#{$style}-#{$icon});
} }
} }
@mixin icon-on-top {
align-items: center;
display: flex;
flex-direction: column;
font-size: 0.9em;
line-height: $global-lineheight;
text-align: center;
&:hover,
&:focus {
color: $link-hover;
text-decoration: none;
}
&::before {
font-size: 1.6em;
}
}

View File

@@ -15,7 +15,13 @@ class Admin::ActionComponent < ApplicationComponent
end end
def text def text
options[:text] || t("admin.actions.#{action}") action_key = if action == :destroy
:delete
else
action
end
options[:text] || t("admin.actions.#{action_key}")
end end
def path def path

View File

@@ -1,8 +1,7 @@
<% if headings.any? %> <% if headings.any? %>
<h3><%= t("admin.budget_headings.amount", count: headings.count) %></h3>
<table> <table>
<thead> <thead>
<tr id="<%= dom_id(group) %>"> <tr>
<th><%= Budget::Heading.human_attribute_name(:name) %></th> <th><%= Budget::Heading.human_attribute_name(:name) %></th>
<th><%= Budget::Heading.human_attribute_name(:price) %></th> <th><%= Budget::Heading.human_attribute_name(:price) %></th>
<% if budget.approval_voting? %> <% if budget.approval_voting? %>

View File

@@ -0,0 +1,26 @@
<% budget.groups.each do |group| %>
<section id="<%= dom_id(group) %>" aria-labelledby="<%= dom_id(group, :header) %>">
<h4 id="<%= dom_id(group, :header) %>"><%= group.name %></h4>
<div class="groups-actions">
<%= action(:edit, group, "aria-label": true) %>
<%= action(:destroy, group, method: :delete, confirm: true, "aria-label": true) %>
<%= action(:new,
group,
text: t("admin.budgets.show.add_heading"),
path: new_admin_budget_group_heading_path(budget, group),
"aria-label": true) %>
</div>
<% if group.multiple_headings? %>
<dl>
<dt><%= Budget::Group.human_attribute_name(:max_votable_headings) %></dt>
<dd><%= group.max_votable_headings %></dd>
</dl>
<% end %>
<%= render Admin::BudgetHeadings::HeadingsComponent.new(group.headings) %>
</section>
<% end %>
<%= action(:new, budget, text: t("admin.budgets.show.add_group"), path: new_admin_budget_group_path(budget)) %>

View File

@@ -0,0 +1,13 @@
class Admin::Budgets::GroupsAndHeadingsComponent < ApplicationComponent
attr_reader :budget
def initialize(budget)
@budget = budget
end
private
def action(...)
render Admin::ActionComponent.new(...)
end
end

View File

@@ -6,6 +6,11 @@
<%= render Admin::Budgets::DraftingComponent.new(budget) %> <%= render Admin::Budgets::DraftingComponent.new(budget) %>
<%= render Admin::Budgets::LinksComponent.new(budget) %> <%= render Admin::Budgets::LinksComponent.new(budget) %>
<section class="groups-and-headings" aria-labelledby="groups_and_headings_header">
<h3 id="groups_and_headings_header"><%= t("admin.budgets.show.groups_and_headings") %></h3>
<%= render Admin::Budgets::GroupsAndHeadingsComponent.new(budget) %>
</section>
<section aria-labelledby="phases_header"> <section aria-labelledby="phases_header">
<h3 id="phases_header"><%= t("admin.budgets.edit.phases_caption") %></h3> <h3 id="phases_header"><%= t("admin.budgets.edit.phases_caption") %></h3>
<%= render Admin::BudgetPhases::PhasesComponent.new(budget) %> <%= render Admin::BudgetPhases::PhasesComponent.new(budget) %>

View File

@@ -2,9 +2,6 @@
<%= actions.action(:investments, <%= actions.action(:investments,
text: t("admin.budgets.index.budget_investments"), text: t("admin.budgets.index.budget_investments"),
path: admin_budget_budget_investments_path(budget_id: budget.id)) %> path: admin_budget_budget_investments_path(budget_id: budget.id)) %>
<%= actions.action(:groups,
text: t("admin.budgets.index.edit_groups"),
path: admin_budget_groups_path(budget)) %>
<% if budget.poll.present? %> <% if budget.poll.present? %>
<%= actions.action(:ballots, <%= actions.action(:ballots,
text: t("admin.budgets.index.admin_ballots"), text: t("admin.budgets.index.admin_ballots"),

View File

@@ -6,4 +6,4 @@
<%= render Admin::BudgetsWizard::CreationTimelineComponent.new("phases") %> <%= render Admin::BudgetsWizard::CreationTimelineComponent.new("phases") %>
<%= render Admin::BudgetPhases::PhasesComponent.new(budget) %> <%= render Admin::BudgetPhases::PhasesComponent.new(budget) %>
<%= link_to t("admin.budgets_wizard.phases.continue"), admin_budgets_path, class: "button success" %> <%= link_to t("admin.budgets_wizard.phases.continue"), admin_budget_path(budget), class: "button success" %>

View File

@@ -29,7 +29,7 @@ class Admin::TableActionsComponent < ApplicationComponent
end end
def destroy_text def destroy_text
options[:destroy_text] || t("admin.actions.delete") options[:destroy_text]
end end
def destroy_path def destroy_path

View File

@@ -1,11 +1,6 @@
class Admin::BudgetGroupsController < Admin::BaseController class Admin::BudgetGroupsController < Admin::BaseController
include Admin::BudgetGroupsActions include Admin::BudgetGroupsActions
before_action :load_groups, only: :index
def index
end
def new def new
@group = @budget.groups.new @group = @budget.groups.new
end end
@@ -13,7 +8,7 @@ class Admin::BudgetGroupsController < Admin::BaseController
private private
def groups_index def groups_index
admin_budget_groups_path(@budget) admin_budget_path(@budget)
end end
def new_action def new_action

View File

@@ -1,9 +1,6 @@
class Admin::BudgetHeadingsController < Admin::BaseController class Admin::BudgetHeadingsController < Admin::BaseController
include Admin::BudgetHeadingsActions include Admin::BudgetHeadingsActions
def index
end
def new def new
@heading = @group.headings.new @heading = @group.headings.new
end end
@@ -11,7 +8,7 @@ class Admin::BudgetHeadingsController < Admin::BaseController
private private
def headings_index def headings_index
admin_budget_group_headings_path(@budget, @group) admin_budget_path(@budget)
end end
def new_action def new_action

View File

@@ -21,6 +21,10 @@ class Admin::BudgetsWizard::HeadingsController < Admin::BudgetsWizard::BaseContr
end end
end end
def load_headings
@headings = @group.headings.order(:id)
end
def new_action def new_action
:index :index
end end

View File

@@ -8,7 +8,6 @@ module Admin::BudgetHeadingsActions
before_action :load_budget before_action :load_budget
before_action :load_group before_action :load_group
before_action :load_headings, only: :index
before_action :load_heading, only: [:edit, :update, :destroy] before_action :load_heading, only: [:edit, :update, :destroy]
end end
@@ -51,10 +50,6 @@ module Admin::BudgetHeadingsActions
@group = @budget.groups.find_by_slug_or_id! params[:group_id] @group = @budget.groups.find_by_slug_or_id! params[:group_id]
end end
def load_headings
@headings = @group.headings.order(:id)
end
def load_heading def load_heading
@heading = @group.headings.find_by_slug_or_id! params[:id] @heading = @group.headings.find_by_slug_or_id! params[:id]
end end

View File

@@ -1,4 +1,4 @@
<%= back_link_to admin_budget_groups_path(@budget) %> <%= back_link_to admin_budget_path(@budget) %>
<h2><%= @budget.name %></h2> <h2><%= @budget.name %></h2>

View File

@@ -1,11 +0,0 @@
<%= back_link_to admin_budgets_path, t("admin.budget_groups.index.back") %>
<div class="clear"></div>
<header>
<h2><%= @budget.name %></h2>
<%= link_to t("admin.budget_groups.form.create"), new_admin_budget_group_path %>
</header>
<%= render Admin::Budgets::HelpComponent.new("groups") %>
<%= render Admin::BudgetGroups::GroupsComponent.new(@groups) %>

View File

@@ -1,4 +1,4 @@
<%= back_link_to admin_budget_group_headings_path(@budget, @group) %> <%= back_link_to admin_budget_path(@budget) %>
<h2><%= "#{@budget.name} / #{@group.name}" %></h2> <h2><%= "#{@budget.name} / #{@group.name}" %></h2>

View File

@@ -1,9 +0,0 @@
<%= back_link_to admin_budget_groups_path(@budget), t("admin.budget_headings.index.back") %>
<header>
<h2><%= t("admin.budget_headings.index.title", budget: @budget.name, group: @group.name) %></h2>
<%= link_to t("admin.budget_headings.form.create"), new_admin_budget_group_heading_path %>
</header>
<%= render Admin::Budgets::HelpComponent.new("headings") %>
<%= render Admin::BudgetHeadings::HeadingsComponent.new(@headings) %>

View File

@@ -90,7 +90,6 @@ en:
type_multiple: "Multiple headings" type_multiple: "Multiple headings"
type_pending: "Pending: No headings yet" type_pending: "Pending: No headings yet"
type_single: "Single heading" type_single: "Single heading"
edit_groups: Heading groups
admin_ballots: Ballots admin_ballots: Ballots
no_budgets: "There are no budgets." no_budgets: "There are no budgets."
create: create:
@@ -145,6 +144,10 @@ en:
multiple: "Headings are meant to divide the money of the participatory budget. Here you can add headings for this group and assign the amount of money that will be used for each heading." multiple: "Headings are meant to divide the money of the participatory budget. Here you can add headings for this group and assign the amount of money that will be used for each heading."
single: "Headings are meant to divide the money of the participatory budget. Since this budget will only contain one heading, this is the place where you stablish the money that will be spent in this participaroty budget." single: "Headings are meant to divide the money of the participatory budget. Since this budget will only contain one heading, this is the place where you stablish the money that will be spent in this participaroty budget."
phases: "Participatory budgets have different phases. Here you can enable or disable phases and also customize each individual phase." phases: "Participatory budgets have different phases. Here you can enable or disable phases and also customize each individual phase."
show:
add_group: "Add group"
add_heading: "Add heading"
groups_and_headings: "Groups and headings"
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.
@@ -170,13 +173,9 @@ en:
max_votable_headings_info: "Only applies to the selecting projects phase. During the voting projects phase users can only vote in one heading per group." max_votable_headings_info: "Only applies to the selecting projects phase. During the voting projects phase users can only vote in one heading per group."
submit: "Save group" submit: "Save group"
index: index:
back: "Go back to budgets"
new_button: "Add new group" new_button: "Add new group"
budget_headings: budget_headings:
no_headings: "There are no headings." no_headings: "There are no headings."
amount:
one: "There is 1 heading"
other: "There are %{count} headings"
create: create:
notice: "Heading created successfully!" notice: "Heading created successfully!"
update: update:

View File

@@ -90,7 +90,6 @@ es:
type_multiple: "Múltiples partidas" type_multiple: "Múltiples partidas"
type_pending: "Pendiente: Aún no hay partidas" type_pending: "Pendiente: Aún no hay partidas"
type_single: "Partida única" type_single: "Partida única"
edit_groups: Grupos de partidas
admin_ballots: Urnas admin_ballots: Urnas
no_budgets: "No hay presupuestos participativos." no_budgets: "No hay presupuestos participativos."
create: create:
@@ -145,6 +144,10 @@ es:
multiple: "Las partidas sirven para dividir el dinero del presupuesto participativo. Aquí puedes ir añadiendo partidas para cada grupo y establecer la cantidad de dinero que se gastará en cada partida." multiple: "Las partidas sirven para dividir el dinero del presupuesto participativo. Aquí puedes ir añadiendo partidas para cada grupo y establecer la cantidad de dinero que se gastará en cada partida."
single: "Las partidas sirven para dividir el dinero del presupuesto participativo. Como este presupuesto solo tendrá una partida aquí podrás establecer la cantidad de dinero que se gastará en este presupuesto participativo." single: "Las partidas sirven para dividir el dinero del presupuesto participativo. Como este presupuesto solo tendrá una partida aquí podrás establecer la cantidad de dinero que se gastará en este presupuesto participativo."
phases: "Los presupuestos participativos tienen distintas fases. Aquí puedes habilitar o deshabilitar fases y también personalizar cada una de las fases." phases: "Los presupuestos participativos tienen distintas fases. Aquí puedes habilitar o deshabilitar fases y también personalizar cada una de las fases."
show:
add_group: "Añadir grupo"
add_heading: "Añadir partida"
groups_and_headings: "Grupos y partidas"
winners: winners:
calculate: Calcular proyectos ganadores calculate: Calcular proyectos ganadores
calculated: Calculando ganadores, puede tardar un minuto. calculated: Calculando ganadores, puede tardar un minuto.
@@ -170,13 +173,9 @@ es:
max_votable_headings_info: "Solo se aplica en la fase de apoyos. Durante la votación final un usuario solo puede votar en una partida por grupo." max_votable_headings_info: "Solo se aplica en la fase de apoyos. Durante la votación final un usuario solo puede votar en una partida por grupo."
submit: "Guardar grupo" submit: "Guardar grupo"
index: index:
back: "Volver a presupuestos"
new_button: "Añadir un grupo nuevo" new_button: "Añadir un grupo nuevo"
budget_headings: budget_headings:
no_headings: "No hay partidas." no_headings: "No hay partidas."
amount:
one: "Hay 1 partida presupuestarias"
other: "Hay %{count} partidas presupuestarias"
create: create:
notice: "¡Partida presupuestaria creada con éxito!" notice: "¡Partida presupuestaria creada con éxito!"
update: update:

View File

@@ -57,8 +57,8 @@ namespace :admin do
put :calculate_winners put :calculate_winners
end end
resources :groups, except: [:show], controller: "budget_groups" do resources :groups, except: [:index, :show], controller: "budget_groups" do
resources :headings, except: [:show], controller: "budget_headings" resources :headings, except: [:index, :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

@@ -4,12 +4,11 @@ describe Admin::Budgets::TableActionsComponent, controller: Admin::BaseControlle
let(:budget) { create(:budget) } let(:budget) { create(:budget) }
let(:component) { Admin::Budgets::TableActionsComponent.new(budget) } let(:component) { Admin::Budgets::TableActionsComponent.new(budget) }
it "renders actions to edit and delete budget, manage investments and edit groups and manage ballots" do it "renders actions to edit and delete budget, manage investments and manage ballots" do
render_inline component render_inline component
expect(page).to have_link count: 4 expect(page).to have_link count: 3
expect(page).to have_link "Investment projects", href: /investments/ expect(page).to have_link "Investment projects", href: /investments/
expect(page).to have_link "Heading groups", href: /groups/
expect(page).to have_link "Edit", href: /#{budget.id}\Z/ expect(page).to have_link "Edit", href: /#{budget.id}\Z/
expect(page).to have_link "Preview", href: /budgets/ expect(page).to have_link "Preview", href: /budgets/

View File

@@ -1,12 +1,12 @@
require "rails_helper" require "rails_helper"
describe Admin::BudgetGroupsController, :admin do describe Admin::BudgetGroupsController, :admin do
describe "GET index" do describe "GET new" do
it "raises an exception when the feature is disabled" do it "raises an exception when the feature is disabled" do
Setting["process.budgets"] = false Setting["process.budgets"] = false
expect do expect do
get :index, params: { budget_id: create(:budget).id } get :new, params: { budget_id: create(:budget).id }
end.to raise_exception(FeatureFlags::FeatureDisabled) end.to raise_exception(FeatureFlags::FeatureDisabled)
end end
end end

View File

@@ -7,7 +7,7 @@ describe Admin::BudgetHeadingsController, :admin do
group = create(:budget_group) group = create(:budget_group)
expect do expect do
get :index, params: { budget_id: group.budget.id, group_id: group.id } get :new, params: { budget_id: group.budget.id, group_id: group.id }
end.to raise_exception(FeatureFlags::FeatureDisabled) end.to raise_exception(FeatureFlags::FeatureDisabled)
end end
end end

View File

@@ -14,96 +14,75 @@ describe "Admin budget groups", :admin do
end end
end end
context "Index" do context "List of groups from budget page" do
scenario "Displaying no groups for budget" do scenario "Displaying no groups for budget" do
visit admin_budget_groups_path(budget) visit admin_budget_path(budget)
expect(page).to have_content "There are no groups." within "section", text: "Groups and headings" do
expect(page.text).to eq "Groups and headings\nAdd group"
end
end end
scenario "Displaying groups" do scenario "Displaying groups" do
group1 = create(:budget_group, budget: budget) above = create(:budget_group, budget: budget, name: "Above ground")
below = create(:budget_group, budget: budget, name: "Below ground")
group2 = create(:budget_group, budget: budget) 1.times { create(:budget_heading, group: above) }
create(:budget_heading, group: group2) 2.times { create(:budget_heading, group: below) }
group3 = create(:budget_group, budget: budget, max_votable_headings: 2)
3.times { create(:budget_heading, group: group3) }
visit admin_budget_groups_path(budget) visit admin_budget_path(budget)
expect(page).to have_content "There are 3 groups"
within "#budget_group_#{group1.id}" do within "section", text: "Groups and headings" do
expect(page).to have_content(group1.name) within "section", text: "Above ground" do
expect(page).to have_content(group1.max_votable_headings) expect(page).to have_css "h4", exact_text: "Above ground"
expect(page).to have_content(group1.headings.count) expect(page).not_to have_content "Maximum number of headings"
expect(page).to have_link "Headings"
end end
within "#budget_group_#{group2.id}" do within "section", text: "Below ground" do
expect(page).to have_content(group2.name) expect(page).to have_css "h4", exact_text: "Below ground"
expect(page).to have_content(group2.max_votable_headings) expect(page).to have_content "Maximum number of headings in which a user can select projects 1"
expect(page).to have_content(group2.headings.count)
expect(page).to have_link "Headings"
end 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 "Headings"
end end
end end
scenario "Delete a group without headings" do scenario "Delete a group without headings" do
group = create(:budget_group, budget: budget) create(:budget_group, budget: budget, name: "Nowhere")
visit admin_budget_groups_path(budget) visit admin_budget_path(budget)
within("#budget_group_#{group.id}") { accept_confirm { click_button "Delete" } }
accept_confirm { click_button "Delete Nowhere" }
expect(page).to have_content "Group deleted successfully" expect(page).to have_content "Group deleted successfully"
expect(page).not_to have_selector "#budget_group_#{group.id}" expect(page).not_to have_content "Nowhere"
end end
scenario "Try to delete a group with headings" do scenario "Try to delete a group with headings" do
group = create(:budget_group, budget: budget) group = create(:budget_group, budget: budget, name: "Everywhere")
create(:budget_heading, group: group) create(:budget_heading, group: group, name: "Everything")
visit admin_budget_groups_path(budget) visit admin_budget_path(budget)
within("#budget_group_#{group.id}") { accept_confirm { click_button "Delete" } }
accept_confirm { click_button "Delete Everywhere" }
expect(page).to have_content "You cannot delete a Group that has associated headings" expect(page).to have_content "You cannot delete a Group that has associated headings"
expect(page).to have_selector "#budget_group_#{group.id}" expect(page).to have_content "Everywhere"
end end
end end
context "New" do context "New" do
scenario "Create group" do scenario "Create group" do
visit admin_budget_groups_path(budget) visit admin_budget_path(budget)
click_link "Create new group" click_link "Add group"
fill_in "Group name", with: "All City" fill_in "Group name", with: "All City"
click_button "Create new group" click_button "Create new group"
expect(page).to have_content "Group created successfully!" expect(page).to have_content "Group created successfully!"
expect(page).to have_content "All City"
end
scenario "Maximum number of headings in which a user can vote is set to 1 by default" do within "section", text: "Groups and headings" do
visit new_admin_budget_group_path(budget) expect(page).to have_css "h4", exact_text: "All City"
fill_in "Group name", with: "All City"
click_button "Create new group"
expect(page).to have_content "Group created successfully!"
within all("thead th")[1] do
expect(page).to have_content("Maximum number of headings in which a user can select projects")
end
within "tbody tr" do
within all("td")[1] { expect(page.text).to eq "1" }
end end
end end
@@ -119,13 +98,14 @@ describe "Admin budget groups", :admin do
context "Edit" do context "Edit" do
scenario "Show group information" do scenario "Show group information" do
group = create(:budget_group, budget: budget, max_votable_headings: 2) group = create(:budget_group, budget: budget, name: "Everywhere", max_votable_headings: 2)
2.times { create(:budget_heading, group: group) } 2.times { create(:budget_heading, group: group) }
visit admin_budget_groups_path(budget) visit admin_budget_path(budget)
within("#budget_group_#{group.id}") { click_link "Edit" }
expect(page).to have_field "Group name", with: group.name click_link "Edit Everywhere"
expect(page).to have_field "Group name", with: "Everywhere"
expect(page).to have_field "Maximum number of headings in which a user can select projects", with: "2" expect(page).to have_field "Maximum number of headings in which a user can select projects", with: "2"
end end

View File

@@ -2,7 +2,7 @@ require "rails_helper"
describe "Admin budget headings", :admin do describe "Admin budget headings", :admin do
let(:budget) { create(:budget, :drafting) } let(:budget) { create(:budget, :drafting) }
let(:group) { create(:budget_group, budget: budget) } let!(:group) { create(:budget_group, budget: budget) }
context "Load" do context "Load" do
let!(:budget) { create(:budget, slug: "budget_slug") } let!(:budget) { create(:budget, slug: "budget_slug") }
@@ -17,64 +17,63 @@ describe "Admin budget headings", :admin do
end end
end end
context "Index" do context "List of headings in the budget page" do
scenario "Displaying no headings for group" do scenario "Displaying no headings for group" do
visit admin_budget_group_headings_path(budget, group) visit admin_budget_path(budget)
within "section", text: "Groups and headings" do
expect(page).to have_content "There are no headings." expect(page).to have_content "There are no headings."
end end
end
scenario "Displaying headings" do scenario "Displaying headings" do
heading1 = create(:budget_heading, group: group, price: 1000, allow_custom_content: true) create(:budget_heading, name: "Laptops", group: group, price: 1000, allow_custom_content: true)
heading2 = create(:budget_heading, group: group, price: 2000, population: 10000) create(:budget_heading, name: "Tablets", group: group, price: 2000, population: 10000)
heading3 = create(:budget_heading, group: group, price: 3000, population: 10000) create(:budget_heading, name: "Phones", group: group, price: 3000, population: 20000)
visit admin_budget_group_headings_path(budget, group) visit admin_budget_path(budget)
expect(page).to have_content "There are 3 headings"
within "#budget_heading_#{heading1.id}" do within "section", text: "Groups and headings" do
expect(page).to have_content(heading1.name) within "tbody" do
expect(page).to have_selector "tr", count: 3
within "tr", text: "Laptops" do
expect(page).to have_content "€1,000" expect(page).to have_content "€1,000"
expect(page).not_to have_content "10000" expect(page).not_to have_content "10000"
expect(page).to have_content "Yes" expect(page).to have_content "Yes"
expect(page).to have_link "Edit"
expect(page).to have_button "Delete"
end end
within "#budget_heading_#{heading2.id}" do within "tr", text: "Tablets" do
expect(page).to have_content(heading2.name)
expect(page).to have_content "€2,000" expect(page).to have_content "€2,000"
expect(page).to have_content "10000" expect(page).to have_content "10000"
expect(page).to have_content "No" expect(page).to have_content "No"
expect(page).to have_link "Edit"
expect(page).to have_button "Delete"
end end
within "#budget_heading_#{heading3.id}" do within "tr", text: "Phones" do
expect(page).to have_content(heading3.name)
expect(page).to have_content "€3,000" expect(page).to have_content "€3,000"
expect(page).to have_content "10000" expect(page).to have_content "20000"
expect(page).to have_content "No" expect(page).to have_content "No"
expect(page).to have_link "Edit" end
expect(page).to have_button "Delete" end
end end
end end
scenario "Delete a heading without investments" do scenario "Delete a heading without investments" do
heading = create(:budget_heading, group: group) create(:budget_heading, group: group, name: "Lemuria")
visit admin_budget_group_headings_path(budget, group) visit admin_budget_path(budget)
within("#budget_heading_#{heading.id}") { accept_confirm { click_button "Delete" } }
within("tr", text: "Lemuria") { accept_confirm { click_button "Delete" } }
expect(page).to have_content "Heading deleted successfully" expect(page).to have_content "Heading deleted successfully"
expect(page).not_to have_selector "#budget_heading_#{heading.id}" expect(page).not_to have_content "Lemuria"
end end
scenario "Try to delete a heading with investments" do scenario "Try to delete a heading with investments" do
heading = create(:budget_heading, group: group, name: "Atlantis") heading = create(:budget_heading, group: group, name: "Atlantis")
create(:budget_investment, heading: heading) create(:budget_investment, heading: heading)
visit admin_budget_group_headings_path(budget, group) visit admin_budget_path(budget)
within(".heading", text: "Atlantis") { accept_confirm { click_button "Delete" } } within(".heading", text: "Atlantis") { accept_confirm { click_button "Delete" } }
expect(page).to have_content "You cannot delete a Heading that has associated investments" expect(page).to have_content "You cannot delete a Heading that has associated investments"
@@ -84,8 +83,11 @@ describe "Admin budget headings", :admin do
context "New" do context "New" do
scenario "Create heading" do scenario "Create heading" do
visit admin_budget_group_headings_path(budget, group) visit admin_budget_path(budget)
click_link "Create new heading"
within "section", text: "Groups and headings" do
within("section", text: group.name) { click_link "Add heading" }
end
fill_in "Heading name", with: "All City" fill_in "Heading name", with: "All City"
fill_in "Money amount", with: "1000" fill_in "Money amount", with: "1000"
@@ -95,11 +97,13 @@ describe "Admin budget headings", :admin do
click_button "Create new heading" click_button "Create new heading"
expect(page).to have_content "Heading created successfully!" expect(page).to have_content "Heading created successfully!"
expect(page).to have_content "All City"
within "tr", text: "All City" do
expect(page).to have_content "€1,000" expect(page).to have_content "€1,000"
expect(page).to have_content "10000" expect(page).to have_content "10000"
expect(page).to have_content "Yes" expect(page).to have_content "Yes"
end end
end
scenario "Heading name is mandatory" do scenario "Heading name is mandatory" do
visit new_admin_budget_group_heading_path(budget, group) visit new_admin_budget_group_heading_path(budget, group)
@@ -148,7 +152,7 @@ describe "Admin budget headings", :admin do
scenario "Show heading information" do scenario "Show heading information" do
heading = create(:budget_heading, group: group) heading = create(:budget_heading, group: group)
visit admin_budget_group_headings_path(budget, group) visit admin_budget_path(budget)
within("#budget_heading_#{heading.id}") { click_link "Edit" } within("#budget_heading_#{heading.id}") { click_link "Edit" }
expect(page).to have_field "Heading name", with: heading.name expect(page).to have_field "Heading name", with: heading.name

View File

@@ -112,10 +112,11 @@ describe "Budgets wizard, groups step", :admin do
expect(page).to have_content "Group updated successfully" expect(page).to have_content "Group updated successfully"
visit admin_budget_groups_path(budget) visit admin_budget_path(budget)
expect(page).to have_content "There is 1 group" within "section", text: "Groups and headings" do
within("tbody tr") { expect(page).to have_content "Group without typos" } expect(page).to have_css "h4", exact_text: "Group without typos"
end
end end
end end

View File

@@ -136,12 +136,13 @@ describe "Budgets wizard, headings step", :admin do
expect(page).to have_content "Heading updated successfully" expect(page).to have_content "Heading updated successfully"
visit admin_budget_group_headings_path(budget, group) visit admin_budget_path(budget)
expect(page).to have_content "There is 1 heading" within "section", text: "Groups and headings" do
within("tbody tr") { expect(page).to have_content "Heading without typos" } within("tbody tr") { expect(page).to have_content "Heading without typos" }
end end
end end
end
describe "Destroy" do describe "Destroy" do
scenario "delete a heading without investments" do scenario "delete a heading without investments" do

View File

@@ -43,7 +43,7 @@ describe "Budgets wizard, phases step", :admin do
click_link "Finish" click_link "Finish"
within("tr", text: budget.name) { click_link "Edit" } expect(page).to have_css "section h3", exact_text: "Phases"
within "tr", text: "Information" do within "tr", text: "Information" do
expect(page).to have_content "No" expect(page).to have_content "No"

View File

@@ -24,22 +24,14 @@ describe "Budgets creation wizard", :admin do
click_link "Finish" click_link "Finish"
within "tr", text: "Single heading budget" do within "section", text: "Groups and headings" do
click_link "Heading groups" expect(page).to have_content "Single heading budget"
end
expect(page).to have_content "There is 1 group"
within "tr", text: "Single heading budget" do
click_link "Headings"
end
expect(page).to have_content "There is 1 heading"
within "tbody" do within "tbody" do
expect(page).to have_content "One and only heading" expect(page).to have_content "One and only heading"
end end
end end
end
scenario "Creation of a multiple-headings budget by steps" do scenario "Creation of a multiple-headings budget by steps" do
visit admin_budgets_path visit admin_budgets_path
@@ -119,26 +111,21 @@ describe "Budgets creation wizard", :admin do
click_link "Finish" click_link "Finish"
within "tr", text: "Multiple headings budget" do within "section", text: "Groups and headings" do
click_link "Heading groups" within "section", text: "All city" do
end
expect(page).to have_content "There are 2 groups"
within "tbody" do within "tbody" do
expect(page).to have_css "tr", count: 1
expect(page).to have_content "All city" expect(page).to have_content "All city"
expect(page).to have_content "Districts" end
end end
within "tr", text: "Districts" do within "section", text: "Districts" do
click_link "Headings"
end
expect(page).to have_content "There are 2 headings"
within "tbody" do within "tbody" do
expect(page).to have_css "tr", count: 2
expect(page).to have_content "North" expect(page).to have_content "North"
expect(page).to have_content "South" expect(page).to have_content "South"
end end
end end
end end
end
end