diff --git a/app/assets/stylesheets/admin.scss b/app/assets/stylesheets/admin.scss index 428390f91..c620a3751 100644 --- a/app/assets/stylesheets/admin.scss +++ b/app/assets/stylesheets/admin.scss @@ -657,6 +657,7 @@ code { .admin-content > header { align-items: flex-start; display: flex; + flex-wrap: wrap; a { @include regular-button; diff --git a/app/assets/stylesheets/admin/budgets/drafting.scss b/app/assets/stylesheets/admin/budgets/drafting.scss new file mode 100644 index 000000000..aa7cde254 --- /dev/null +++ b/app/assets/stylesheets/admin/budgets/drafting.scss @@ -0,0 +1,37 @@ +.admin .drafting { + margin-bottom: 2 * $line-height / 3; + margin-left: auto; + + @include breakpoint(large) { + align-items: flex-start; + display: flex; + + .callout { + flex: 1; + margin-bottom: 0; + } + } + + @include breakpoint(small medium only) { + text-align: right; + + .callout { + text-align: left; + } + } + + .preview-link { + @include has-fa-icon(eye, regular); + @include hollow-button; + } + + .publish-link { + @include regular-button; + margin-bottom: 0; + } + + .preview-link, + .publish-link { + margin-left: $line-height / 2; + } +} diff --git a/app/components/admin/budgets/drafting_component.html.erb b/app/components/admin/budgets/drafting_component.html.erb new file mode 100644 index 000000000..6c84a4972 --- /dev/null +++ b/app/components/admin/budgets/drafting_component.html.erb @@ -0,0 +1,16 @@ +
+ <% if can? :publish, budget %> +
+ <%= t("admin.budgets.edit.drafting") %> +
+ <% end %> + + <%= link_to t("admin.budgets.actions.preview"), budget_path(budget), class: "preview-link", target: "_blank" %> + + <% if can? :publish, budget %> + <%= link_to t("admin.budgets.edit.publish"), + publish_admin_budget_path(budget), + method: :patch, class: "publish-link", + data: { confirm: t("admin.actions.confirm") } %> + <% end %> +
diff --git a/app/components/admin/budgets/drafting_component.rb b/app/components/admin/budgets/drafting_component.rb new file mode 100644 index 000000000..18d37b93f --- /dev/null +++ b/app/components/admin/budgets/drafting_component.rb @@ -0,0 +1,8 @@ +class Admin::Budgets::DraftingComponent < ApplicationComponent + delegate :can?, to: :controller + attr_reader :budget + + def initialize(budget) + @budget = budget + end +end diff --git a/app/components/admin/budgets/table_actions_component.html.erb b/app/components/admin/budgets/table_actions_component.html.erb index 8bc788c34..e2d86ae80 100644 --- a/app/components/admin/budgets/table_actions_component.html.erb +++ b/app/components/admin/budgets/table_actions_component.html.erb @@ -12,4 +12,8 @@ <% else %> <%= link_to_create_budget_poll %> <% end %> + <%= link_to t("admin.budgets.actions.preview"), + budget_path(budget), + target: "_blank", + class: "preview-link" %> <% end %> diff --git a/app/controllers/admin/budgets_controller.rb b/app/controllers/admin/budgets_controller.rb index 1a953af9e..e3b74ca65 100644 --- a/app/controllers/admin/budgets_controller.rb +++ b/app/controllers/admin/budgets_controller.rb @@ -7,6 +7,7 @@ class Admin::BudgetsController < Admin::BaseController has_filters %w[open finished], only: :index before_action :load_budget, except: [:index, :new, :create] + before_action :load_staff, only: [:new, :create, :edit, :update, :show] load_and_authorize_resource def index @@ -14,14 +15,18 @@ class Admin::BudgetsController < Admin::BaseController end def show + render :edit end def new - load_staff end def edit - load_staff + end + + def publish + @budget.publish! + redirect_to edit_admin_budget_path(@budget), notice: t("admin.budgets.publish.notice") end def calculate_winners @@ -38,17 +43,15 @@ class Admin::BudgetsController < Admin::BaseController if @budget.update(budget_params) redirect_to admin_budgets_path, notice: t("admin.budgets.update.notice") else - load_staff render :edit end end def create - @budget = Budget.new(budget_params) + @budget = Budget.new(budget_params.merge(published: false)) if @budget.save - redirect_to admin_budget_path(@budget), notice: t("admin.budgets.create.notice") + redirect_to edit_admin_budget_path(@budget), notice: t("admin.budgets.create.notice") else - load_staff render :new end end diff --git a/app/helpers/budgets_helper.rb b/app/helpers/budgets_helper.rb index b3a01f7d5..d27fec415 100644 --- a/app/helpers/budgets_helper.rb +++ b/app/helpers/budgets_helper.rb @@ -64,7 +64,7 @@ module BudgetsHelper end def budget_published?(budget) - !budget.drafting? || current_user&.administrator? + budget.published? || current_user&.administrator? end def current_budget_map_locations diff --git a/app/models/abilities/administrator.rb b/app/models/abilities/administrator.rb index b70c7f4d0..e5eb20015 100644 --- a/app/models/abilities/administrator.rb +++ b/app/models/abilities/administrator.rb @@ -62,6 +62,7 @@ module Abilities can :manage, Dashboard::Action can [:index, :read, :new, :create, :update, :destroy, :calculate_winners], Budget + can :publish, Budget, id: Budget.drafting.ids can [:read, :create, :update, :destroy], Budget::Group can [:read, :create, :update, :destroy], Budget::Heading can [:hide, :admin_update, :toggle_selection], Budget::Investment diff --git a/app/models/budget.rb b/app/models/budget.rb index 269fd3d61..b36535278 100644 --- a/app/models/budget.rb +++ b/app/models/budget.rb @@ -43,7 +43,8 @@ class Budget < ApplicationRecord after_create :generate_phases - scope :drafting, -> { where(phase: "drafting") } + scope :published, -> { where(published: true) } + scope :drafting, -> { where.not(id: published) } scope :informing, -> { where(phase: "informing") } scope :accepting, -> { where(phase: "accepting") } scope :reviewing, -> { where(phase: "reviewing") } @@ -59,7 +60,7 @@ class Budget < ApplicationRecord scope :open, -> { where.not(phase: "finished") } def self.current - where.not(phase: "drafting").order(:created_at).last + published.order(:created_at).last end def current_phase @@ -86,8 +87,12 @@ class Budget < ApplicationRecord 80 end + def publish! + update!(published: true) + end + def drafting? - phase == "drafting" + !published? end def informing? diff --git a/app/models/budget/phase.rb b/app/models/budget/phase.rb index 8294b7c17..d8b59207f 100644 --- a/app/models/budget/phase.rb +++ b/app/models/budget/phase.rb @@ -1,6 +1,6 @@ class Budget class Phase < ApplicationRecord - PHASE_KINDS = %w[drafting informing accepting reviewing selecting valuating publishing_prices balloting + PHASE_KINDS = %w[informing accepting reviewing selecting valuating publishing_prices balloting reviewing_ballots finished].freeze PUBLISHED_PRICES_PHASES = %w[publishing_prices balloting reviewing_ballots finished].freeze SUMMARY_MAX_LENGTH = 1000 @@ -18,7 +18,7 @@ class Budget validates_translation :summary, length: { maximum: SUMMARY_MAX_LENGTH } validates_translation :description, length: { maximum: DESCRIPTION_MAX_LENGTH } validates :budget, presence: true - validates :kind, presence: true, uniqueness: { scope: :budget }, inclusion: { in: PHASE_KINDS } + validates :kind, presence: true, uniqueness: { scope: :budget }, inclusion: { in: ->(*) { PHASE_KINDS }} validate :invalid_dates_range? validate :prev_phase_dates_valid? validate :next_phase_dates_valid? diff --git a/app/views/admin/budgets/edit.html.erb b/app/views/admin/budgets/edit.html.erb index 455546803..70cdcfd69 100644 --- a/app/views/admin/budgets/edit.html.erb +++ b/app/views/admin/budgets/edit.html.erb @@ -1,5 +1,8 @@ <%= back_link_to admin_budgets_path %> -

<%= t("admin.budgets.edit.title") %>

+
+

<%= t("admin.budgets.edit.title") %>

+ <%= render Admin::Budgets::DraftingComponent.new(@budget) %> +
<%= render "/admin/budgets/form" %> diff --git a/app/views/admin/budgets/show.html.erb b/app/views/admin/budgets/show.html.erb deleted file mode 100644 index 2e46f79c8..000000000 --- a/app/views/admin/budgets/show.html.erb +++ /dev/null @@ -1,5 +0,0 @@ -<%= back_link_to admin_budgets_path %> - -

<%= @budget.name %>

- -<%= render "form" %> diff --git a/config/locales/en/admin.yml b/config/locales/en/admin.yml index 513ed25cf..ddaafb641 100644 --- a/config/locales/en/admin.yml +++ b/config/locales/en/admin.yml @@ -63,6 +63,8 @@ en: type: Type no_activity: There are no moderators activity. budgets: + actions: + preview: "Preview budget" index: title: Participatory budgets new_link: Create new budget @@ -82,8 +84,12 @@ en: notice: New participatory budget created successfully! update: notice: Participatory budget updated successfully + publish: + notice: "Participatory budget published successfully" edit: title: Edit Participatory budget + drafting: "This participatory budget is in draft mode, only administrators can see it in the public site. Once it's published it cannot be changed to draft mode again." + publish: "Publish budget" delete: Delete budget phase: Phase dates: Dates diff --git a/config/locales/es/admin.yml b/config/locales/es/admin.yml index de520b625..8afb17164 100644 --- a/config/locales/es/admin.yml +++ b/config/locales/es/admin.yml @@ -63,6 +63,8 @@ es: type: Tipo no_activity: No hay actividad de moderadores. budgets: + actions: + preview: "Previsualizar presupuesto" index: title: Presupuestos participativos new_link: Crear nuevo presupuesto @@ -82,8 +84,12 @@ es: notice: '¡Presupuestos participativos creados con éxito!' update: notice: Presupuestos participativos actualizados + publish: + notice: "Presupuestos participativos actualizados" edit: title: Editar presupuestos participativos + drafting: "Este presupuesto participativo está en modo borrador, solo los administradores pueden verlo desde la parte pública de la página. Una vez se haya publicado, no se podrá volver a poner en modo borrador otra vez." + publish: "Publicar presupuesto" delete: Eliminar presupuesto phase: Fase dates: Fechas diff --git a/config/routes/admin.rb b/config/routes/admin.rb index 13fdc5935..21712e04a 100644 --- a/config/routes/admin.rb +++ b/config/routes/admin.rb @@ -53,6 +53,7 @@ namespace :admin do resources :budgets do member do + patch :publish put :calculate_winners end diff --git a/db/migrate/20200211123827_add_published_status_to_budgets.rb b/db/migrate/20200211123827_add_published_status_to_budgets.rb new file mode 100644 index 000000000..8cf696ea4 --- /dev/null +++ b/db/migrate/20200211123827_add_published_status_to_budgets.rb @@ -0,0 +1,5 @@ +class AddPublishedStatusToBudgets < ActiveRecord::Migration[5.2] + def change + add_column :budgets, :published, :boolean + end +end diff --git a/db/schema.rb b/db/schema.rb index 25e23d4cb..27cc45b9d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -360,6 +360,7 @@ ActiveRecord::Schema.define(version: 2021_01_23_100638) do t.text "description_publishing_prices" t.text "description_informing" t.string "voting_style", default: "knapsack" + t.boolean "published" end create_table "campaigns", id: :serial, force: :cascade do |t| diff --git a/lib/tasks/budgets.rake b/lib/tasks/budgets.rake index 172944420..c5ffa9ef6 100644 --- a/lib/tasks/budgets.rake +++ b/lib/tasks/budgets.rake @@ -10,4 +10,23 @@ namespace :budgets do Budget.last.email_unselected end end + + desc "Set published attribute" + task set_published: :environment do + Budget.where(published: nil).each do |budget| + if budget.phase == "drafting" + if budget.phases.enabled.first.present? + next_enabled_phase = budget.phases.enabled.where.not(kind: "drafting").first.kind + else + next_enabled_phase = "informing" + budget.phases.informing.update!(enabled: true) + end + + budget.update!(phase: next_enabled_phase) + budget.update!(published: false) + else + budget.update!(published: true) + end + end + end end diff --git a/lib/tasks/consul.rake b/lib/tasks/consul.rake index 4f169c387..b4775c285 100644 --- a/lib/tasks/consul.rake +++ b/lib/tasks/consul.rake @@ -7,6 +7,7 @@ namespace :consul do desc "Runs tasks needed to upgrade from 1.2.0 to 1.3.0" task "execute_release_1.3.0_tasks": [ "db:load_sdg", - "db:calculate_tsv" + "db:calculate_tsv", + "budgets:set_published" ] end diff --git a/spec/components/admin/budgets/table_actions_component_spec.rb b/spec/components/admin/budgets/table_actions_component_spec.rb index 36aeea537..97f20083f 100644 --- a/spec/components/admin/budgets/table_actions_component_spec.rb +++ b/spec/components/admin/budgets/table_actions_component_spec.rb @@ -11,11 +11,12 @@ describe Admin::Budgets::TableActionsComponent, type: :component do it "renders links to edit budget, manage investments and edit groups and manage ballots" do render_inline component - expect(page).to have_css "a", count: 4 + expect(page).to have_css "a", count: 5 expect(page).to have_link "Manage projects", href: /investments/ expect(page).to have_link "Edit headings groups", href: /groups/ expect(page).to have_link "Edit budget", href: /edit/ expect(page).to have_link "Admin ballots" + expect(page).to have_link "Preview budget", href: /budgets/ end it "renders link to create new poll for budgets without polls" do diff --git a/spec/factories/budgets.rb b/spec/factories/budgets.rb index 871309dfe..db9f9c5f0 100644 --- a/spec/factories/budgets.rb +++ b/spec/factories/budgets.rb @@ -2,6 +2,7 @@ FactoryBot.define do factory :budget do sequence(:name) { |n| "#{Faker::Lorem.word} #{n}" } currency_symbol { "€" } + published { true } phase { "accepting" } description_drafting { "This budget is drafting" } description_informing { "This budget is informing" } @@ -15,7 +16,7 @@ FactoryBot.define do description_finished { "This budget is finished" } trait :drafting do - phase { "drafting" } + published { false } end trait :informing do diff --git a/spec/lib/tasks/budgets_spec.rb b/spec/lib/tasks/budgets_spec.rb new file mode 100644 index 000000000..609e218c7 --- /dev/null +++ b/spec/lib/tasks/budgets_spec.rb @@ -0,0 +1,70 @@ +require "rails_helper" + +describe Budget do + let(:run_rake_task) do + Rake::Task["budgets:set_published"].reenable + Rake.application.invoke_task("budgets:set_published") + end + + it "does not change anything if the published attribute is set" do + budget = create(:budget, published: false, phase: "accepting") + + run_rake_task + budget.reload + + expect(budget.phase).to eq "accepting" + expect(budget.published).to be false + end + + it "publishes budgets which are not in draft mode" do + budget = create(:budget, published: nil, phase: "accepting") + + run_rake_task + budget.reload + + expect(budget.phase).to eq "accepting" + expect(budget.published).to be true + end + + it "changes the published attribute to false on drafting budgets" do + stub_const("Budget::Phase::PHASE_KINDS", ["drafting"] + Budget::Phase::PHASE_KINDS) + budget = create(:budget, published: nil) + budget.update_column(:phase, "drafting") + stub_const("Budget::Phase::PHASE_KINDS", Budget::Phase::PHASE_KINDS - ["drafting"]) + + run_rake_task + budget.reload + + expect(budget.published).to be false + expect(budget.phase).to eq "informing" + end + + it "changes the phase to the first enabled phase" do + budget = create(:budget, published: nil) + budget.update_column(:phase, "drafting") + budget.phases.informing.update!(enabled: false) + + expect(budget.phase).to eq "drafting" + + run_rake_task + budget.reload + + expect(budget.phase).to eq "accepting" + expect(budget.published).to be false + end + + it "enables and select the informing phase if there are not any enabled phases" do + budget = create(:budget, published: nil) + budget.update_column(:phase, "drafting") + budget.phases.each { |phase| phase.update!(enabled: false) } + + expect(budget.phase).to eq "drafting" + + run_rake_task + budget.reload + + expect(budget.phase).to eq "informing" + expect(budget.phases.informing.enabled).to be true + expect(budget.published).to be false + end +end diff --git a/spec/models/budget/phase_spec.rb b/spec/models/budget/phase_spec.rb index e44a2e234..c5eccfaa2 100644 --- a/spec/models/budget/phase_spec.rb +++ b/spec/models/budget/phase_spec.rb @@ -1,12 +1,11 @@ require "rails_helper" describe Budget::Phase do - let(:budget) { create(:budget) } - let(:first_phase) { budget.phases.drafting } - let(:second_phase) { budget.phases.informing } - let(:third_phase) { budget.phases.accepting } - let(:fourth_phase) { budget.phases.reviewing } - let(:final_phase) { budget.phases.finished } + let(:budget) { create(:budget) } + let(:informing_phase) { budget.phases.informing } + let(:accepting_phase) { budget.phases.accepting } + let(:reviewing_phase) { budget.phases.reviewing } + let(:finished_phase) { budget.phases.finished } it_behaves_like "globalizable", :budget_phase @@ -31,54 +30,53 @@ describe Budget::Phase do describe "#dates_range_valid?" do it "is valid when start & end dates are different & consecutive" do - first_phase.assign_attributes(starts_at: Date.current, ends_at: Date.tomorrow) + informing_phase.assign_attributes(starts_at: Date.current, ends_at: Date.tomorrow) - expect(first_phase).to be_valid + expect(informing_phase).to be_valid end it "is not valid when dates are equal" do - first_phase.assign_attributes(starts_at: Date.current, ends_at: Date.current) + informing_phase.assign_attributes(starts_at: Date.current, ends_at: Date.current) - expect(first_phase).not_to be_valid + expect(informing_phase).not_to be_valid end it "is not valid when start date is later than end date" do - first_phase.assign_attributes(starts_at: Date.tomorrow, ends_at: Date.current) + informing_phase.assign_attributes(starts_at: Date.tomorrow, ends_at: Date.current) - expect(first_phase).not_to be_valid + expect(informing_phase).not_to be_valid end end describe "#prev_phase_dates_valid?" do let(:error) do - "Start date must be later than the start date of the previous enabled phase"\ - " (Draft (Not visible to the public))" + "Start date must be later than the start date of the previous enabled phase (Information)" end it "is invalid when start date is same as previous enabled phase start date" do - second_phase.assign_attributes(starts_at: second_phase.prev_enabled_phase.starts_at) + accepting_phase.assign_attributes(starts_at: accepting_phase.prev_enabled_phase.starts_at) - expect(second_phase).not_to be_valid - expect(second_phase.errors.messages[:starts_at]).to include(error) + expect(accepting_phase).not_to be_valid + expect(accepting_phase.errors.messages[:starts_at]).to include(error) end it "is invalid when start date is earlier than previous enabled phase start date" do - second_phase.assign_attributes(starts_at: second_phase.prev_enabled_phase.starts_at - 1.day) + accepting_phase.assign_attributes(starts_at: accepting_phase.prev_enabled_phase.starts_at - 1.day) - expect(second_phase).not_to be_valid - expect(second_phase.errors.messages[:starts_at]).to include(error) + expect(accepting_phase).not_to be_valid + expect(accepting_phase.errors.messages[:starts_at]).to include(error) end it "is valid when start date is in between previous enabled phase start & end dates" do - second_phase.assign_attributes(starts_at: second_phase.prev_enabled_phase.starts_at + 1.day) + accepting_phase.assign_attributes(starts_at: accepting_phase.prev_enabled_phase.starts_at + 1.day) - expect(second_phase).to be_valid + expect(accepting_phase).to be_valid end it "is valid when start date is later than previous enabled phase end date" do - second_phase.assign_attributes(starts_at: second_phase.prev_enabled_phase.ends_at + 1.day) + accepting_phase.assign_attributes(starts_at: accepting_phase.prev_enabled_phase.ends_at + 1.day) - expect(second_phase).to be_valid + expect(accepting_phase).to be_valid end end @@ -88,29 +86,29 @@ describe Budget::Phase do end it "is invalid when end date is same as next enabled phase end date" do - second_phase.assign_attributes(ends_at: second_phase.next_enabled_phase.ends_at) + informing_phase.assign_attributes(ends_at: informing_phase.next_enabled_phase.ends_at) - expect(second_phase).not_to be_valid - expect(second_phase.errors.messages[:ends_at]).to include(error) + expect(informing_phase).not_to be_valid + expect(informing_phase.errors.messages[:ends_at]).to include(error) end it "is invalid when end date is later than next enabled phase end date" do - second_phase.assign_attributes(ends_at: second_phase.next_enabled_phase.ends_at + 1.day) + informing_phase.assign_attributes(ends_at: informing_phase.next_enabled_phase.ends_at + 1.day) - expect(second_phase).not_to be_valid - expect(second_phase.errors.messages[:ends_at]).to include(error) + expect(informing_phase).not_to be_valid + expect(informing_phase.errors.messages[:ends_at]).to include(error) end it "is valid when end date is in between next enabled phase start & end dates" do - second_phase.assign_attributes(ends_at: second_phase.next_enabled_phase.ends_at - 1.day) + informing_phase.assign_attributes(ends_at: informing_phase.next_enabled_phase.ends_at - 1.day) - expect(second_phase).to be_valid + expect(informing_phase).to be_valid end it "is valid when end date is earlier than next enabled phase start date" do - second_phase.assign_attributes(ends_at: second_phase.next_enabled_phase.starts_at - 1.day) + informing_phase.assign_attributes(ends_at: informing_phase.next_enabled_phase.starts_at - 1.day) - expect(second_phase).to be_valid + expect(informing_phase).to be_valid end end end @@ -128,50 +126,50 @@ describe Budget::Phase do end describe "#adjust_date_ranges" do - let(:prev_enabled_phase) { second_phase.prev_enabled_phase } - let(:next_enabled_phase) { second_phase.next_enabled_phase } + let(:prev_enabled_phase) { accepting_phase.prev_enabled_phase } + let(:next_enabled_phase) { accepting_phase.next_enabled_phase } describe "when enabled" do it "adjusts previous enabled phase end date to its own start date" do - expect(prev_enabled_phase.ends_at).to eq(second_phase.starts_at) + expect(prev_enabled_phase.ends_at).to eq(accepting_phase.starts_at) end it "adjusts next enabled phase start date to its own end date" do - expect(next_enabled_phase.starts_at).to eq(second_phase.ends_at) + expect(next_enabled_phase.starts_at).to eq(accepting_phase.ends_at) end end describe "when being enabled" do before do - second_phase.update!(enabled: false, + accepting_phase.update!(enabled: false, starts_at: Date.current, ends_at: Date.current + 2.days) end it "adjusts previous enabled phase end date to its own start date" do - expect { second_phase.update(enabled: true) } + expect { accepting_phase.update(enabled: true) } .to change { prev_enabled_phase.ends_at.to_date }.to(Date.current) end it "adjusts next enabled phase start date to its own end date" do expect do - second_phase.update(enabled: true) + accepting_phase.update(enabled: true) end.to change { next_enabled_phase.starts_at.to_date }.to(Date.current + 2.days) end end describe "when disabled" do before do - second_phase.update!(enabled: false) + accepting_phase.update!(enabled: false) end it "doesn't change previous enabled phase end date" do - expect { second_phase.update(starts_at: Date.current, ends_at: Date.current + 2.days) } + expect { accepting_phase.update(starts_at: Date.current, ends_at: Date.current + 2.days) } .not_to change { prev_enabled_phase.ends_at } end it "doesn't change next enabled phase start date" do - expect { second_phase.update(starts_at: Date.current, ends_at: Date.current + 2.days) } + expect { accepting_phase.update(starts_at: Date.current, ends_at: Date.current + 2.days) } .not_to change { next_enabled_phase.starts_at } end end @@ -179,7 +177,7 @@ describe Budget::Phase do describe "when being disabled" do it "doesn't adjust previous enabled phase end date to its own start date" do expect do - second_phase.update(enabled: false, + accepting_phase.update(enabled: false, starts_at: Date.current, ends_at: Date.current + 2.days) end.not_to change { prev_enabled_phase.ends_at } @@ -187,7 +185,7 @@ describe Budget::Phase do it "adjusts next enabled phase start date to its own start date" do expect do - second_phase.update(enabled: false, + accepting_phase.update(enabled: false, starts_at: Date.current, ends_at: Date.current + 2.days) end.to change { next_enabled_phase.starts_at.to_date }.to(Date.current) @@ -197,22 +195,25 @@ describe Budget::Phase do describe "next & prev enabled phases" do before do - second_phase.update(enabled: false) + accepting_phase.update!(enabled: false) + %w[selecting reviewing_ballots balloting publishing_prices valuating].each do |phase| + budget.phases.send(phase).update(enabled: false) + end end describe "#next_enabled_phase" do it "returns the right next enabled phase" do - expect(first_phase.reload.next_enabled_phase).to eq(third_phase) - expect(third_phase.reload.next_enabled_phase).to eq(fourth_phase) - expect(final_phase.reload.next_enabled_phase).to eq(nil) + expect(informing_phase.reload.next_enabled_phase).to eq(reviewing_phase) + expect(reviewing_phase.reload.next_enabled_phase).to eq(finished_phase) + expect(finished_phase.reload.next_enabled_phase).to eq(nil) end end describe "#prev_enabled_phase" do it "returns the right previous enabled phase" do - expect(first_phase.reload.prev_enabled_phase).to eq(nil) - expect(third_phase.reload.prev_enabled_phase).to eq(first_phase) - expect(fourth_phase.reload.prev_enabled_phase).to eq(third_phase) + expect(informing_phase.reload.prev_enabled_phase).to eq(nil) + expect(reviewing_phase.reload.prev_enabled_phase).to eq(informing_phase) + expect(finished_phase.reload.prev_enabled_phase).to eq(reviewing_phase) end end end diff --git a/spec/models/budget_spec.rb b/spec/models/budget_spec.rb index 05b63c586..9f734f452 100644 --- a/spec/models/budget_spec.rb +++ b/spec/models/budget_spec.rb @@ -32,6 +32,36 @@ describe Budget do expect(Budget.valuating_or_later).to be_empty end end + + describe ".drafting" do + it "returns unpublished budgets" do + undefined = create(:budget, published: nil) + drafting = create(:budget, published: false) + + expect(Budget.drafting).to match_array([undefined, drafting]) + end + + it "does not return published budgets" do + create(:budget, published: true) + + expect(Budget.drafting).to be_empty + end + end + + describe ".published" do + it "does not return unpublished budgets" do + create(:budget, published: nil) + create(:budget, published: false) + + expect(Budget.published).to be_empty + end + + it "returns published budgets" do + published = create(:budget, published: true) + + expect(Budget.published).to eq [published] + end + end end describe "name" do @@ -96,9 +126,6 @@ describe Budget do end it "produces auxiliary methods" do - budget.phase = "drafting" - expect(budget).to be_drafting - budget.phase = "accepting" expect(budget).to be_accepting @@ -248,7 +275,6 @@ describe Budget do end describe "#generate_phases" do - let(:drafting_phase) { budget.phases.drafting } let(:informing_phase) { budget.phases.informing } let(:accepting_phase) { budget.phases.accepting } let(:reviewing_phase) { budget.phases.reviewing } @@ -262,7 +288,6 @@ describe Budget do it "generates all phases linked in correct order" do expect(budget.phases.count).to eq(Budget::Phase::PHASE_KINDS.count) - expect(drafting_phase.next_phase).to eq(informing_phase) expect(informing_phase.next_phase).to eq(accepting_phase) expect(accepting_phase.next_phase).to eq(reviewing_phase) expect(reviewing_phase.next_phase).to eq(selecting_phase) @@ -273,8 +298,7 @@ describe Budget do expect(reviewing_ballots_phase.next_phase).to eq(finished_phase) expect(finished_phase.next_phase).to eq(nil) - expect(drafting_phase.prev_phase).to eq(nil) - expect(informing_phase.prev_phase).to eq(drafting_phase) + expect(informing_phase.prev_phase).to eq(nil) expect(accepting_phase.prev_phase).to eq(informing_phase) expect(reviewing_phase.prev_phase).to eq(accepting_phase) expect(selecting_phase.prev_phase).to eq(reviewing_phase) diff --git a/spec/system/admin/budgets_spec.rb b/spec/system/admin/budgets_spec.rb index ad0bfe854..3303084aa 100644 --- a/spec/system/admin/budgets_spec.rb +++ b/spec/system/admin/budgets_spec.rb @@ -15,19 +15,20 @@ describe "Admin budgets", :admin do let!(:budget) { create(:budget, slug: "budget_slug") } scenario "finds budget by slug" do - visit admin_budget_path("budget_slug") - expect(page).to have_content(budget.name) + visit edit_admin_budget_path("budget_slug") + + expect(page).to have_content("Edit Participatory budget") end scenario "raises an error if budget slug is not found" do expect do - visit admin_budget_path("wrong_budget") + visit edit_admin_budget_path("wrong_budget") end.to raise_error ActiveRecord::RecordNotFound end scenario "raises an error if budget id is not found" do expect do - visit admin_budget_path(0) + visit edit_admin_budget_path(0) end.to raise_error ActiveRecord::RecordNotFound end end @@ -107,11 +108,13 @@ describe "Admin budgets", :admin do click_button "Create Budget" expect(page).to have_content "New participatory budget created successfully!" - expect(page).to have_content "M30 - Summer campaign" - expect(Budget.last.voting_style).to eq "knapsack" + expect(page).to have_field "Name", with: "M30 - Summer campaign" + expect(page).to have_select "Final voting style", selected: "Knapsack" end scenario "Create budget - Approval voting", :js do + admin = Administrator.first + visit admin_budgets_path click_link "Create new budget" @@ -121,8 +124,12 @@ describe "Admin budgets", :admin do click_button "Create Budget" expect(page).to have_content "New participatory budget created successfully!" - expect(page).to have_content "M30 - Summer campaign" - expect(Budget.last.voting_style).to eq "approval" + expect(page).to have_field "Name", with: "M30 - Summer campaign" + expect(page).to have_select "Final voting style", selected: "Approval" + + click_link "Select administrators" + + expect(page).to have_field admin.name end scenario "Name is mandatory" do @@ -155,6 +162,49 @@ describe "Admin budgets", :admin do end end + context "Create", :js do + scenario "A new budget is always created in draft mode" do + visit admin_budgets_path + click_link "Create new budget" + + fill_in "Name", with: "M30 - Summer campaign" + select "Accepting projects", from: "budget[phase]" + + click_button "Create Budget" + + expect(page).to have_content "New participatory budget created successfully!" + expect(page).to have_content "This participatory budget is in draft mode" + expect(page).to have_link "Preview budget" + expect(page).to have_link "Publish budget" + end + end + + context "Publish", :js do + let(:budget) { create(:budget, :drafting) } + + scenario "Can preview budget before it is published" do + visit edit_admin_budget_path(budget) + + within_window(window_opened_by { click_link "Preview budget" }) do + expect(page).to have_current_path budget_path(budget) + end + end + + scenario "Can preview a budget after it is published" do + visit edit_admin_budget_path(budget) + + accept_confirm { click_link "Publish budget" } + + expect(page).to have_content "Participatory budget published successfully" + expect(page).not_to have_content "This participatory budget is in draft mode" + expect(page).not_to have_link "Publish budget" + + within_window(window_opened_by { click_link "Preview budget" }) do + expect(page).to have_current_path budget_path(budget) + end + end + end + context "Destroy" do let!(:budget) { create(:budget) } let(:heading) { create(:budget_heading, budget: budget) } @@ -236,7 +286,7 @@ describe "Admin budgets", :admin do end scenario "Changing name for current locale will update the slug if budget is in draft phase", :js do - budget.update!(phase: "drafting") + budget.update!(published: false) old_slug = budget.slug visit edit_admin_budget_path(budget) diff --git a/spec/system/budgets/budgets_spec.rb b/spec/system/budgets/budgets_spec.rb index 66f1da827..7e9c31723 100644 --- a/spec/system/budgets/budgets_spec.rb +++ b/spec/system/budgets/budgets_spec.rb @@ -168,7 +168,7 @@ describe "Budgets" do scenario "Not show investment links earlier of balloting " do budget = create(:budget) create(:budget_heading, budget: budget) - phases_without_links = ["drafting", "informing"] + phases_without_links = ["informing"] not_allowed_phase_list = Budget::Phase::PHASE_KINDS - phases_without_links - allowed_phase_list @@ -205,9 +205,10 @@ describe "Budgets" do scenario "Index shows only published phases" do budget.update!(phase: :finished) phases = budget.phases - phases.drafting.update!(starts_at: "30-12-2017", ends_at: "31-12-2017", enabled: true, - description: "Description of drafting phase", - summary: "

This is the summary for drafting phase

") + + phases.informing.update!(starts_at: "30-12-2017", ends_at: "31-12-2017", enabled: true, + description: "Description of informing phase", + summary: "

This is the summary for informing phase

") phases.accepting.update!(starts_at: "01-01-2018", ends_at: "10-01-2018", enabled: true, description: "Description of accepting phase", @@ -243,8 +244,6 @@ describe "Budgets" do visit budgets_path - expect(page).not_to have_content "This is the summary for drafting phase" - expect(page).not_to have_content "December 30, 2017 - December 31, 2017" expect(page).not_to have_content "This is the summary for reviewing phase" expect(page).not_to have_content "January 11, 2018 - January 20, 2018" expect(page).not_to have_content "This is the summary for valuating phase" @@ -254,6 +253,8 @@ describe "Budgets" do expect(page).not_to have_content "This is the summary for reviewing_ballots phase" expect(page).not_to have_content "March 11, 2018 - March 20, 2018" + expect(page).to have_content "This is the summary for informing phase" + expect(page).to have_content "December 30, 2017 - December 31, 2017" expect(page).to have_content "This is the summary for accepting phase" expect(page).to have_content "January 01, 2018 - January 20, 2018" expect(page).to have_content "This is the summary for selecting phase" @@ -478,7 +479,7 @@ describe "Budgets" do before do logout - budget.update!(phase: "drafting") + budget.update!(published: false) create(:budget) end