diff --git a/app/assets/images/budget_execution_no_image.jpg b/app/assets/images/budget_execution_no_image.jpg
new file mode 100644
index 000000000..7de0e0a2c
Binary files /dev/null and b/app/assets/images/budget_execution_no_image.jpg differ
diff --git a/app/assets/stylesheets/participation.scss b/app/assets/stylesheets/participation.scss
index 112afe7d4..4bc64f9a5 100644
--- a/app/assets/stylesheets/participation.scss
+++ b/app/assets/stylesheets/participation.scss
@@ -1680,6 +1680,59 @@
}
}
+.budget-execution {
+ border: 1px solid $border;
+ overflow: hidden;
+ position: relative;
+
+ a {
+ color: $text;
+ display: block;
+
+ img {
+ height: $line-height * 9;
+ transition-duration: 0.3s;
+ transition-property: transform;
+ width: 100%;
+ }
+
+ &:hover {
+ text-decoration: none;
+
+ img {
+ transform: scale(1.05);
+ }
+ }
+ }
+
+ h5 {
+ font-size: $base-font-size;
+ margin-bottom: 0;
+ }
+
+ .budget-execution-info {
+ padding: $line-height / 2;
+ }
+
+ .author {
+ color: $text-medium;
+ font-size: $small-font-size;
+ }
+
+ .budget-execution-content {
+ min-height: $line-height * 3;
+ }
+
+ .price {
+ color: $budget;
+ font-size: rem-calc(24);
+ }
+
+ &:hover {
+ box-shadow: 0 0 12px 0 rgba(0, 0, 0, 0.2);
+ }
+}
+
// 07. Proposals successful
// -------------------------
diff --git a/app/controllers/budgets/executions_controller.rb b/app/controllers/budgets/executions_controller.rb
new file mode 100644
index 000000000..e33f43dcd
--- /dev/null
+++ b/app/controllers/budgets/executions_controller.rb
@@ -0,0 +1,46 @@
+module Budgets
+ class ExecutionsController < ApplicationController
+ before_action :load_budget
+
+ load_and_authorize_resource :budget
+
+ def show
+ authorize! :read_executions, @budget
+ @statuses = ::Budget::Investment::Status.all
+
+ if params[:status].present?
+ @investments_by_heading = @budget.investments.winners
+ .joins(:milestones).includes(:milestones)
+ .select { |i| i.milestones.published.with_status
+ .order_by_publication_date.last
+ .status_id == params[:status].to_i }
+ .uniq
+ .group_by(&:heading)
+ else
+ @investments_by_heading = @budget.investments.winners
+ .joins(:milestones).includes(:milestones)
+ .distinct.group_by(&:heading)
+ end
+
+ @investments_by_heading = reorder_alphabetically_with_city_heading_first.to_h
+ end
+
+ private
+
+ def load_budget
+ @budget = Budget.find_by(slug: params[:id]) || Budget.find_by(id: params[:id])
+ end
+
+ def reorder_alphabetically_with_city_heading_first
+ @investments_by_heading.sort do |a, b|
+ if a[0].name == 'Toda la ciudad'
+ -1
+ elsif b[0].name == 'Toda la ciudad'
+ 1
+ else
+ a[0].name <=> b[0].name
+ end
+ end
+ end
+ end
+end
diff --git a/app/helpers/budget_executions_helper.rb b/app/helpers/budget_executions_helper.rb
new file mode 100644
index 000000000..fd8376987
--- /dev/null
+++ b/app/helpers/budget_executions_helper.rb
@@ -0,0 +1,9 @@
+module BudgetExecutionsHelper
+
+ def filters_select_counts(status)
+ @budget.investments.winners.with_milestones.select { |i| i.milestones
+ .published.with_status.order_by_publication_date
+ .last.status_id == status rescue false }.count
+ end
+
+end
diff --git a/app/models/abilities/everyone.rb b/app/models/abilities/everyone.rb
index 23cfdc971..18927cc80 100644
--- a/app/models/abilities/everyone.rb
+++ b/app/models/abilities/everyone.rb
@@ -22,7 +22,7 @@ module Abilities
can [:read], Budget
can [:read], Budget::Group
can [:read, :print, :json_data], Budget::Investment
- can :read_results, Budget, phase: "finished"
+ can [:read_results, :read_executions], Budget, phase: "finished"
can :new, DirectMessage
can [:read, :debate, :draft_publication, :allegations, :result_publication, :proposals], Legislation::Process, published: true
can [:read, :changes, :go_to_version], Legislation::DraftVersion
diff --git a/app/models/budget/investment.rb b/app/models/budget/investment.rb
index e531612a8..486e65052 100644
--- a/app/models/budget/investment.rb
+++ b/app/models/budget/investment.rb
@@ -84,6 +84,7 @@ class Budget
scope :last_week, -> { where("created_at >= ?", 7.days.ago)}
scope :sort_by_flags, -> { order(flags_count: :desc, updated_at: :desc) }
scope :sort_by_created_at, -> { reorder(created_at: :desc) }
+ scope :with_milestones, -> { joins(:milestones).distinct }
scope :by_budget, ->(budget) { where(budget: budget) }
scope :by_group, ->(group_id) { where(group_id: group_id) }
diff --git a/app/models/budget/investment/milestone.rb b/app/models/budget/investment/milestone.rb
index c71516446..562be54b8 100644
--- a/app/models/budget/investment/milestone.rb
+++ b/app/models/budget/investment/milestone.rb
@@ -18,6 +18,8 @@ class Budget
validate :description_or_status_present?
scope :order_by_publication_date, -> { order(publication_date: :asc) }
+ scope :published, -> { where("publication_date <= ?", Date.current) }
+ scope :with_status, -> { where("status_id IS NOT NULL") }
def self.title_max_length
80
diff --git a/app/models/site_customization/image.rb b/app/models/site_customization/image.rb
index b10f3799f..f551d206f 100644
--- a/app/models/site_customization/image.rb
+++ b/app/models/site_customization/image.rb
@@ -4,7 +4,8 @@ class SiteCustomization::Image < ActiveRecord::Base
"logo_header" => [260, 80],
"social_media_icon" => [470, 246],
"social_media_icon_twitter" => [246, 246],
- "apple-touch-icon-200" => [200, 200]
+ "apple-touch-icon-200" => [200, 200],
+ "budget_execution_no_image" => [800, 600]
}
has_attached_file :image
diff --git a/app/views/budgets/executions/_investments.html.erb b/app/views/budgets/executions/_investments.html.erb
new file mode 100644
index 000000000..b329e1a7b
--- /dev/null
+++ b/app/views/budgets/executions/_investments.html.erb
@@ -0,0 +1,33 @@
+<% @investments_by_heading.each do |heading, investments| %>
+
+ <%= heading.name %> (<%= investments.count %>)
+
+
+ <% investments.each do |investment| %>
+
+
+ <%= link_to budget_investment_path(@budget, investment, anchor: "tab-milestones"), data: { 'equalizer-watch': true } do %>
+ <% investment.milestones.order(publication_date: :desc).limit(1).each do |milestone| %>
+ <% if milestone.image.present? %>
+ <%= image_tag milestone.image_url(:large), alt: milestone.image.title %>
+ <% elsif investment.image.present? %>
+ <%= image_tag investment.image_url(:thumb), alt: investment.image.title %>
+ <% else %>
+ <%= image_tag "budget_execution_no_image.jpg", alt: investment.title %>
+ <% end %>
+ <% end %>
+
+
+
<%= investment.title %>
+ <%= investment.author.name %>
+
+
+ <%= investment.formatted_price %>
+
+
+ <% end %>
+
+
+ <% end %>
+
+<% end %>
diff --git a/app/views/budgets/executions/show.html.erb b/app/views/budgets/executions/show.html.erb
new file mode 100644
index 000000000..c6844c955
--- /dev/null
+++ b/app/views/budgets/executions/show.html.erb
@@ -0,0 +1,77 @@
+<% provide :title, t("budgets.executions.page_title", budget: @budget.name) %>
+<% content_for :meta_description do %><%= @budget.description_for_phase('finished') %><% end %>
+<% provide :social_media_meta_tags do %>
+<%= render 'shared/social_media_meta_tags',
+ social_url: budget_executions_url(@budget),
+ social_title: @budget.name,
+ social_description: @budget.description_for_phase('finished') %>
+<% end %>
+
+<% content_for :canonical do %>
+ <%= render 'shared/canonical', href: budget_executions_url(@budget) %>
+<% end %>
+
+
+
+
+
+ <%= back_link_to budgets_path %>
+
+ <%= t("budgets.executions.heading") %>
+ <%= @budget.name %>
+
+
+
+
+
+
+
+
+
+ -
+ <%= link_to t("budgets.results.link"), budget_results_path(@budget) %>
+
+ -
+ <%= link_to t("budgets.executions.link"), budget_executions_path(@budget), class: 'is-active' %>
+
+
+
+
+
+
+
+
+ <%= t("budgets.executions.heading_selection_title") %>
+
+
+
+
+
+ <%= form_tag(budget_executions_path(@budget), method: :get) do %>
+
+ <%= label_tag t("budgets.executions.filters.label") %>
+ <%= select_tag :status,
+ options_from_collection_for_select(@statuses,
+ :id, lambda { |s| "#{s.name} (#{filters_select_counts(s.id)})" },
+ params[:status]),
+ class: "js-submit-on-change",
+ prompt: t("budgets.executions.filters.all",
+ count: @budget.investments.winners.with_milestones.count) %>
+
+ <% end %>
+
+ <% if @investments_by_heading.any? %>
+ <%= render 'budgets/executions/investments' %>
+ <% else %>
+
+ <%= t("budgets.executions.no_winner_investments") %>
+
+ <% end %>
+
+
diff --git a/app/views/budgets/index.html.erb b/app/views/budgets/index.html.erb
index 729975469..4f9184477 100644
--- a/app/views/budgets/index.html.erb
+++ b/app/views/budgets/index.html.erb
@@ -33,7 +33,7 @@
<% if current_user %>
<% if current_user.level_two_or_three_verified? %>
<%= link_to t("budgets.investments.index.sidebar.create"),
- new_budget_investment_path(@budget),
+ new_budget_investment_path(current_budget),
class: "button margin-top expanded" %>
<% else %>
diff --git a/app/views/budgets/results/show.html.erb b/app/views/budgets/results/show.html.erb
index 63308eaca..81c5e0904 100644
--- a/app/views/budgets/results/show.html.erb
+++ b/app/views/budgets/results/show.html.erb
@@ -29,10 +29,10 @@
-
<%= t("shared.you_are_in") %>
- <%= link_to t("budgets.results.link"), budget_results_path(@budget), class: "is-active" %>
+ <%= link_to t("budgets.results.link"), budget_results_path(@budget), class: "is-active" %>
-
- <%# link_to t("budgets.stats.link"), budget_stats_path(@budget)%>
+ <%= link_to t("budgets.executions.link"), budget_executions_path(@budget) %>
diff --git a/config/locales/en/budgets.yml b/config/locales/en/budgets.yml
index 094fcbe9e..e20c9f09f 100644
--- a/config/locales/en/budgets.yml
+++ b/config/locales/en/budgets.yml
@@ -174,6 +174,15 @@ en:
investment_proyects: List of all investment projects
unfeasible_investment_proyects: List of all unfeasible investment projects
not_selected_investment_proyects: List of all investment projects not selected for balloting
+ executions:
+ link: "Milestones"
+ page_title: "%{budget} - Milestones"
+ heading: "Participatory budget Milestones"
+ heading_selection_title: "By district"
+ no_winner_investments: "No winner investments in this state"
+ filters:
+ label: "Project's current state"
+ all: "All (%{count})"
phases:
errors:
dates_range_invalid: "Start date can't be equal or later than End date"
diff --git a/config/locales/es/budgets.yml b/config/locales/es/budgets.yml
index d493d6daa..531960330 100644
--- a/config/locales/es/budgets.yml
+++ b/config/locales/es/budgets.yml
@@ -174,6 +174,15 @@ es:
investment_proyects: Ver lista completa de proyectos de gasto
unfeasible_investment_proyects: Ver lista de proyectos de gasto inviables
not_selected_investment_proyects: Ver lista de proyectos de gasto no seleccionados para la votación final
+ executions:
+ link: "Seguimiento"
+ page_title: "%{budget} - Seguimiento de proyectos"
+ heading: "Seguimiento de proyectos"
+ heading_selection_title: "Ámbito de actuación"
+ no_winner_investments: "No hay proyectos de gasto ganadores en este estado"
+ filters:
+ label: "Estado actual del proyecto"
+ all: "Todos (%{count})"
phases:
errors:
dates_range_invalid: "La fecha de comienzo no puede ser igual o superior a la de finalización"
diff --git a/config/routes/budget.rb b/config/routes/budget.rb
index b3422744f..475d5496a 100644
--- a/config/routes/budget.rb
+++ b/config/routes/budget.rb
@@ -15,6 +15,7 @@ resources :budgets, only: [:show, :index] do
end
resource :results, only: :show, controller: "budgets/results"
+ resource :executions, only: :show, controller: 'budgets/executions'
end
scope '/participatory_budget' do
diff --git a/db/dev_seeds/budgets.rb b/db/dev_seeds/budgets.rb
index 04b5272ff..6a44b977c 100644
--- a/db/dev_seeds/budgets.rb
+++ b/db/dev_seeds/budgets.rb
@@ -139,6 +139,13 @@ section "Creating Valuation Assignments" do
end
end
+section "Creating default Investment Milestone Statuses" do
+ Budget::Investment::Status.create(name: I18n.t('seeds.budgets.statuses.studying_project'))
+ Budget::Investment::Status.create(name: I18n.t('seeds.budgets.statuses.bidding'))
+ Budget::Investment::Status.create(name: I18n.t('seeds.budgets.statuses.executing_project'))
+ Budget::Investment::Status.create(name: I18n.t('seeds.budgets.statuses.executed'))
+end
+
section "Creating investment milestones" do
Budget::Investment.find_each do |investment|
milestone = Budget::Investment::Milestone.new(investment_id: investment.id, publication_date: Date.tomorrow)
@@ -151,10 +158,3 @@ section "Creating investment milestones" do
end
end
end
-
-section "Creating default Investment Milestone Statuses" do
- Budget::Investment::Status.create(name: I18n.t('seeds.budgets.statuses.studying_project'))
- Budget::Investment::Status.create(name: I18n.t('seeds.budgets.statuses.bidding'))
- Budget::Investment::Status.create(name: I18n.t('seeds.budgets.statuses.executing_project'))
- Budget::Investment::Status.create(name: I18n.t('seeds.budgets.statuses.executed'))
-end
diff --git a/spec/features/budgets/budgets_spec.rb b/spec/features/budgets/budgets_spec.rb
index db0e6b806..a85df36cb 100644
--- a/spec/features/budgets/budgets_spec.rb
+++ b/spec/features/budgets/budgets_spec.rb
@@ -124,6 +124,15 @@ feature 'Budgets' do
expect(page).to have_content "There are no budgets"
end
+
+ scenario "Accepting" do
+ budget.update(phase: "accepting")
+ login_as(create(:user, :level_two))
+
+ visit budgets_path
+
+ expect(page).to have_link "Create a budget investment"
+ end
end
scenario 'Index shows only published phases' do
diff --git a/spec/features/budgets/executions_spec.rb b/spec/features/budgets/executions_spec.rb
new file mode 100644
index 000000000..5449c8c4b
--- /dev/null
+++ b/spec/features/budgets/executions_spec.rb
@@ -0,0 +1,252 @@
+require 'rails_helper'
+
+feature 'Executions' do
+
+ let(:budget) { create(:budget, phase: 'finished') }
+ let(:group) { create(:budget_group, budget: budget) }
+ let(:heading) { create(:budget_heading, group: group) }
+
+ let!(:investment1) { create(:budget_investment, :winner, heading: heading) }
+ let!(:investment2) { create(:budget_investment, :winner, heading: heading) }
+ let!(:investment4) { create(:budget_investment, :winner, heading: heading) }
+ let!(:investment3) { create(:budget_investment, :incompatible, heading: heading) }
+
+ scenario 'only displays investments with milestones' do
+ create(:budget_investment_milestone, investment: investment1)
+
+ visit budget_path(budget)
+ click_link 'See results'
+
+ expect(page).to have_link('Milestones')
+
+ click_link 'Milestones'
+
+ expect(page).to have_content(investment1.title)
+ expect(page).not_to have_content(investment2.title)
+ expect(page).not_to have_content(investment3.title)
+ expect(page).not_to have_content(investment4.title)
+ end
+
+ scenario "Do not display headings with no winning investments for selected status" do
+ create(:budget_investment_milestone, investment: investment1)
+
+ empty_group = create(:budget_group, budget: budget)
+ empty_heading = create(:budget_heading, group: empty_group, price: 1000)
+
+ visit budget_path(budget)
+ click_link 'See results'
+
+ expect(page).to have_content(heading.name)
+ expect(page).to have_content(empty_heading.name)
+
+ click_link 'Milestones'
+
+ expect(page).to have_content(heading.name)
+ expect(page).not_to have_content(empty_heading.name)
+ end
+
+ scenario "Show message when there are no winning investments with the selected status", :js do
+ create(:budget_investment_status, name: I18n.t('seeds.budgets.statuses.executed'))
+
+ visit budget_path(budget)
+
+ click_link 'See results'
+ click_link 'Milestones'
+
+ expect(page).not_to have_content('No winner investments in this state')
+
+ select 'Executed (0)', from: 'status'
+
+ expect(page).to have_content('No winner investments in this state')
+ end
+
+ context 'Images' do
+ scenario 'renders milestone image if available' do
+ milestone1 = create(:budget_investment_milestone, investment: investment1)
+ create(:image, imageable: milestone1)
+
+ visit budget_path(budget)
+
+ click_link 'See results'
+ click_link 'Milestones'
+
+ expect(page).to have_content(investment1.title)
+ expect(page).to have_css("img[alt='#{milestone1.image.title}']")
+ end
+
+ scenario 'renders investment image if no milestone image is available' do
+ create(:budget_investment_milestone, investment: investment2)
+ create(:image, imageable: investment2)
+
+ visit budget_path(budget)
+
+ click_link 'See results'
+ click_link 'Milestones'
+
+ expect(page).to have_content(investment2.title)
+ expect(page).to have_css("img[alt='#{investment2.image.title}']")
+ end
+
+ scenario 'renders default image if no milestone nor investment images are available' do
+ create(:budget_investment_milestone, investment: investment4)
+
+ visit budget_path(budget)
+
+ click_link 'See results'
+ click_link 'Milestones'
+
+ expect(page).to have_content(investment4.title)
+ expect(page).to have_css("img[alt='#{investment4.title}']")
+ end
+
+ scenario "renders last milestone's image if investment has multiple milestones with images associated" do
+ milestone1 = create(:budget_investment_milestone, investment: investment1,
+ publication_date: 2.weeks.ago)
+
+ milestone2 = create(:budget_investment_milestone, investment: investment1,
+ publication_date: Date.yesterday)
+
+ create(:image, imageable: milestone1, title: 'First milestone image')
+ create(:image, imageable: milestone2, title: 'Second milestone image')
+
+ visit budget_path(budget)
+
+ click_link 'See results'
+ click_link 'Milestones'
+
+ expect(page).to have_content(investment1.title)
+ expect(page).to have_css("img[alt='#{milestone2.image.title}']")
+ expect(page).not_to have_css("img[alt='#{milestone1.image.title}']")
+ end
+ end
+
+ context 'Filters' do
+
+ let!(:status1) { create(:budget_investment_status, name: I18n.t('seeds.budgets.statuses.studying_project')) }
+ let!(:status2) { create(:budget_investment_status, name: I18n.t('seeds.budgets.statuses.bidding')) }
+
+ scenario 'Filters select with counter are shown' do
+ create(:budget_investment_milestone, investment: investment1,
+ publication_date: Date.yesterday,
+ status: status1)
+
+ create(:budget_investment_milestone, investment: investment2,
+ publication_date: Date.yesterday,
+ status: status2)
+
+ visit budget_path(budget)
+
+ click_link 'See results'
+ click_link 'Milestones'
+
+ expect(page).to have_content("All (2)")
+ expect(page).to have_content("#{status1.name} (1)")
+ expect(page).to have_content("#{status2.name} (1)")
+ end
+
+ scenario 'by milestone status', :js do
+ create(:budget_investment_milestone, investment: investment1, status: status1)
+ create(:budget_investment_milestone, investment: investment2, status: status2)
+ create(:budget_investment_status, name: I18n.t('seeds.budgets.statuses.executing_project'))
+
+ visit budget_path(budget)
+
+ click_link 'See results'
+ click_link 'Milestones'
+
+ expect(page).to have_content(investment1.title)
+ expect(page).to have_content(investment2.title)
+
+ select 'Studying the project (1)', from: 'status'
+
+ expect(page).to have_content(investment1.title)
+ expect(page).not_to have_content(investment2.title)
+
+ select 'Bidding (1)', from: 'status'
+
+ expect(page).to have_content(investment2.title)
+ expect(page).not_to have_content(investment1.title)
+
+ select 'Executing the project (0)', from: 'status'
+
+ expect(page).not_to have_content(investment1.title)
+ expect(page).not_to have_content(investment2.title)
+ end
+
+ scenario 'are based on latest milestone status', :js do
+ create(:budget_investment_milestone, investment: investment1,
+ publication_date: 1.month.ago,
+ status: status1)
+
+ create(:budget_investment_milestone, investment: investment1,
+ publication_date: Date.yesterday,
+ status: status2)
+
+ visit budget_path(budget)
+ click_link 'See results'
+ click_link 'Milestones'
+
+ select 'Studying the project (0)', from: 'status'
+ expect(page).not_to have_content(investment1.title)
+
+ select 'Bidding (1)', from: 'status'
+ expect(page).to have_content(investment1.title)
+ end
+
+ scenario 'milestones with future dates are not shown', :js do
+ create(:budget_investment_milestone, investment: investment1,
+ publication_date: Date.yesterday,
+ status: status1)
+
+ create(:budget_investment_milestone, investment: investment1,
+ publication_date: Date.tomorrow,
+ status: status2)
+
+ visit budget_path(budget)
+ click_link 'See results'
+ click_link 'Milestones'
+
+ select 'Studying the project (1)', from: 'status'
+ expect(page).to have_content(investment1.title)
+
+ select 'Bidding (0)', from: 'status'
+ expect(page).not_to have_content(investment1.title)
+ end
+ end
+
+ context 'Heading Order' do
+
+ def create_heading_with_investment_with_milestone(group:, name:)
+ heading = create(:budget_heading, group: group, name: name)
+ investment = create(:budget_investment, :winner, heading: heading)
+ milestone = create(:budget_investment_milestone, investment: investment)
+ heading
+ end
+
+ scenario 'City heading is displayed first' do
+ heading.destroy!
+ other_heading1 = create_heading_with_investment_with_milestone(group: group, name: 'Other 1')
+ city_heading = create_heading_with_investment_with_milestone(group: group, name: 'Toda la ciudad')
+ other_heading2 = create_heading_with_investment_with_milestone(group: group, name: 'Other 2')
+
+ visit budget_executions_path(budget)
+
+ expect(page).to have_css('.budget-execution', count: 3)
+ expect(city_heading.name).to appear_before(other_heading1.name)
+ expect(city_heading.name).to appear_before(other_heading2.name)
+ end
+
+ scenario 'Non-city headings are displayed in alphabetical order' do
+ heading.destroy!
+ z_heading = create_heading_with_investment_with_milestone(group: group, name: 'Zzz')
+ a_heading = create_heading_with_investment_with_milestone(group: group, name: 'Aaa')
+ m_heading = create_heading_with_investment_with_milestone(group: group, name: 'Mmm')
+
+ visit budget_executions_path(budget)
+
+ expect(page).to have_css('.budget-execution', count: 3)
+ expect(a_heading.name).to appear_before(m_heading.name)
+ expect(m_heading.name).to appear_before(z_heading.name)
+ end
+ end
+end
diff --git a/spec/models/budget/investment/milestone_spec.rb b/spec/models/budget/investment/milestone_spec.rb
index 59cbe1a68..45d4e7c0b 100644
--- a/spec/models/budget/investment/milestone_spec.rb
+++ b/spec/models/budget/investment/milestone_spec.rb
@@ -69,4 +69,16 @@ describe Budget::Investment::Milestone do
end
end
+ describe ".published" do
+ it "uses the application's time zone date", :with_different_time_zone do
+ published_in_local_time_zone = create(:budget_investment_milestone,
+ publication_date: Date.today)
+
+ published_in_application_time_zone = create(:budget_investment_milestone,
+ publication_date: Date.current)
+
+ expect(Budget::Investment::Milestone.published).to include(published_in_application_time_zone)
+ expect(Budget::Investment::Milestone.published).not_to include(published_in_local_time_zone)
+ end
+ end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index f14ee4041..d845190ed 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -90,6 +90,17 @@ RSpec.configure do |config|
travel_back
end
+ config.before(:each, :with_different_time_zone) do
+ system_zone = ActiveSupport::TimeZone.new("UTC")
+ local_zone = ActiveSupport::TimeZone.new("Madrid")
+
+ # Make sure the date defined by `config.time_zone` and
+ # the local date are different.
+ allow(Time).to receive(:zone).and_return(system_zone)
+ allow(Time).to receive(:now).and_return(Date.current.at_end_of_day.in_time_zone(local_zone))
+ allow(Date).to receive(:today).and_return(Time.now.to_date)
+ end
+
# Allows RSpec to persist some state between runs in order to support
# the `--only-failures` and `--next-failure` CLI options.
config.example_status_persistence_file_path = "spec/examples.txt"