From 94b75540e454d4c51e393ca80f1cafb2496154b5 Mon Sep 17 00:00:00 2001 From: Maikel Linke Date: Thu, 14 Aug 2025 14:57:32 +1000 Subject: [PATCH] Replace Timecop with Rails' time helpers Rails 4.1 added time helpers but we never bothered using them. But now I'm getting rid of the Timecop dependency and use standard helpers. Beware though that the new helpers always freeze time. When you travel to a certain date then the clock stops ticking while Timecop maintained the passing of time. The freezing of time could cause problems if you are trying to enforce a timeout. But all current specs don't seem affected. In most cases, the freezing will make it easier to avoid flaky specs. --- Gemfile | 1 - Gemfile.lock | 2 -- .../spec/services/affiliate_sales_query_spec.rb | 4 ++-- spec/base_spec_helper.rb | 1 + spec/jobs/heartbeat_job_spec.rb | 4 ++-- spec/jobs/open_order_cycle_job_spec.rb | 4 ++-- spec/jobs/webhook_delivery_job_spec.rb | 2 +- .../renderers/csv_renderer_spec.rb | 2 +- spec/lib/reports/xero_invoices_report_spec.rb | 2 +- spec/models/enterprise_caching_spec.rb | 2 +- spec/models/order_cycle_spec.rb | 4 ++-- spec/models/proxy_order_spec.rb | 10 +++------- spec/models/terms_of_service_file_spec.rb | 2 +- spec/services/vine/jwt_service_spec.rb | 4 ++-- spec/support/timecop.rb | 3 --- .../admin/enterprises/terms_and_conditions_spec.rb | 2 +- .../admin/reports/orders_and_distributors_spec.rb | 8 ++++---- .../admin/reports/orders_and_fulfillment_spec.rb | 5 +++-- spec/system/admin/reports/packing_report_spec.rb | 4 ++-- spec/system/admin/reports_spec.rb | 12 +++++------- spec/system/consumer/authentication_spec.rb | 2 +- spec/system/consumer/caching/shops_caching_spec.rb | 4 ++-- 22 files changed, 37 insertions(+), 47 deletions(-) delete mode 100644 spec/support/timecop.rb diff --git a/Gemfile b/Gemfile index bec0c273b9..cb4c78a11b 100644 --- a/Gemfile +++ b/Gemfile @@ -167,7 +167,6 @@ group :test, :development do gem 'rswag' gem 'shoulda-matchers' gem 'stimulus_reflex_testing', github: "podia/stimulus_reflex_testing", branch: :main - gem 'timecop' end group :test do diff --git a/Gemfile.lock b/Gemfile.lock index 94074d6d25..c59e016277 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -842,7 +842,6 @@ GEM thor (1.4.0) thread-local (1.1.0) tilt (2.3.0) - timecop (0.9.10) timeout (0.4.3) ttfunk (1.8.0) bigdecimal (~> 3.1) @@ -1049,7 +1048,6 @@ DEPENDENCIES stimulus_reflex_testing! stringex (~> 2.8.5) stripe - timecop turbo-rails turbo_power undercover diff --git a/engines/dfc_provider/spec/services/affiliate_sales_query_spec.rb b/engines/dfc_provider/spec/services/affiliate_sales_query_spec.rb index 7bb431cb06..74607e2550 100644 --- a/engines/dfc_provider/spec/services/affiliate_sales_query_spec.rb +++ b/engines/dfc_provider/spec/services/affiliate_sales_query_spec.rb @@ -11,11 +11,11 @@ RSpec.describe AffiliateSalesQuery do let(:yesterday) { Time.zone.yesterday } let(:tomorrow) { Time.zone.tomorrow } - around do |example| + before do # Query dates are interpreted as UTC while the spec runs in # Melbourne time. At noon in Melbourne, the date is the same. # That simplifies the spec. - Timecop.travel(Time.zone.today.noon, &example) + travel_to(Time.zone.today.noon) end it "returns data" do diff --git a/spec/base_spec_helper.rb b/spec/base_spec_helper.rb index 29431ee0ee..830ea0c21d 100644 --- a/spec/base_spec_helper.rb +++ b/spec/base_spec_helper.rb @@ -257,6 +257,7 @@ RSpec.configure do |config| config.include OpenFoodNetwork::FiltersHelper config.include OpenFoodNetwork::EnterpriseGroupsHelper config.include OpenFoodNetwork::HtmlHelper + config.include ActiveSupport::Testing::TimeHelpers config.include ActionView::Helpers::DateHelper config.include OpenFoodNetwork::PerformanceHelper config.include ActiveJob::TestHelper diff --git a/spec/jobs/heartbeat_job_spec.rb b/spec/jobs/heartbeat_job_spec.rb index 933e9cd456..76fa8cbd29 100644 --- a/spec/jobs/heartbeat_job_spec.rb +++ b/spec/jobs/heartbeat_job_spec.rb @@ -8,8 +8,8 @@ RSpec.describe HeartbeatJob do before { Spree::Config.last_job_queue_heartbeat_at = nil } - around do |example| - Timecop.freeze(run_time) { example.run } + before do + travel_to(run_time) end it "updates the last_job_queue_heartbeat_at config var" do diff --git a/spec/jobs/open_order_cycle_job_spec.rb b/spec/jobs/open_order_cycle_job_spec.rb index c6d1d1d27e..254887bd55 100644 --- a/spec/jobs/open_order_cycle_job_spec.rb +++ b/spec/jobs/open_order_cycle_job_spec.rb @@ -7,8 +7,8 @@ RSpec.describe OpenOrderCycleJob do let(:order_cycle) { create(:simple_order_cycle, orders_open_at: now) } subject { OpenOrderCycleJob.perform_now(order_cycle.id) } - around do |example| - Timecop.freeze(now) { example.run } + before do + freeze_time end it "marks as open" do diff --git a/spec/jobs/webhook_delivery_job_spec.rb b/spec/jobs/webhook_delivery_job_spec.rb index 247c3b2f13..45c2978c4b 100644 --- a/spec/jobs/webhook_delivery_job_spec.rb +++ b/spec/jobs/webhook_delivery_job_spec.rb @@ -23,7 +23,7 @@ RSpec.describe WebhookDeliveryJob do end it "delivers a payload" do - Timecop.freeze do + freeze_time do expected_body = { id: /.+/, at: at.to_s, diff --git a/spec/lib/reports/enterprise_fee_summary/renderers/csv_renderer_spec.rb b/spec/lib/reports/enterprise_fee_summary/renderers/csv_renderer_spec.rb index 91046372fd..bf58f26b4b 100644 --- a/spec/lib/reports/enterprise_fee_summary/renderers/csv_renderer_spec.rb +++ b/spec/lib/reports/enterprise_fee_summary/renderers/csv_renderer_spec.rb @@ -80,7 +80,7 @@ require "spec_helper" # end # it "generates filename correctly" do -# Timecop.freeze(Time.zone.local(2018, 10, 9, 7, 30, 0)) do +# travel_to(Time.zone.local(2018, 10, 9, 7, 30, 0)) do # filename = renderer.__send__(:filename) # expect(filename).to eq("enterprise_fee_summary_20181009.csv") # end diff --git a/spec/lib/reports/xero_invoices_report_spec.rb b/spec/lib/reports/xero_invoices_report_spec.rb index cc98f5c60e..cddc30a7dd 100644 --- a/spec/lib/reports/xero_invoices_report_spec.rb +++ b/spec/lib/reports/xero_invoices_report_spec.rb @@ -13,7 +13,7 @@ module Reporting describe "option defaults" do let(:report) { Base.new user } - around { |example| Timecop.travel(Time.zone.local(2015, 5, 5, 14, 0, 0)) { example.run } } + before { travel_to(Time.zone.local(2015, 5, 5, 14, 0, 0)) } it "uses defaults when blank params are passed" do expect(report.params).to eq(invoice_date: Date.civil(2015, 5, 5), diff --git a/spec/models/enterprise_caching_spec.rb b/spec/models/enterprise_caching_spec.rb index d2c8321690..38c1d8409a 100644 --- a/spec/models/enterprise_caching_spec.rb +++ b/spec/models/enterprise_caching_spec.rb @@ -149,6 +149,6 @@ RSpec.describe Enterprise do end def later(&) - Timecop.travel(1.day.from_now, &) + travel(1.day, &) end end diff --git a/spec/models/order_cycle_spec.rb b/spec/models/order_cycle_spec.rb index 2977affb32..1613389527 100644 --- a/spec/models/order_cycle_spec.rb +++ b/spec/models/order_cycle_spec.rb @@ -304,7 +304,7 @@ RSpec.describe OrderCycle do let(:oc) { build_stubbed(:simple_order_cycle) } it "reports status when an order cycle is upcoming" do - Timecop.freeze(oc.orders_open_at - 1.second) do + travel_to(oc.orders_open_at - 1.second) do expect(oc).not_to be_undated expect(oc).to be_dated expect(oc).to be_upcoming @@ -322,7 +322,7 @@ RSpec.describe OrderCycle do end it "reports status when an order cycle has closed" do - Timecop.freeze(oc.orders_close_at + 1.second) do + travel_to(oc.orders_close_at + 1.second) do expect(oc).not_to be_undated expect(oc).to be_dated expect(oc).not_to be_upcoming diff --git a/spec/models/proxy_order_spec.rb b/spec/models/proxy_order_spec.rb index 0da3098b00..6c7f5666be 100644 --- a/spec/models/proxy_order_spec.rb +++ b/spec/models/proxy_order_spec.rb @@ -7,10 +7,8 @@ RSpec.describe ProxyOrder do let(:order_cycle) { create(:simple_order_cycle) } let(:subscription) { create(:subscription) } - around do |example| - # We are testing if database columns have been set to "now". - Timecop.freeze(Time.zone.now) { example.run } - end + # We are testing if database columns have been set to "now". + before { freeze_time } context "when the order cycle is not yet closed" do let(:proxy_order) { @@ -94,9 +92,7 @@ RSpec.describe ProxyOrder do let(:proxy_order) { create(:proxy_order, order:, canceled_at: Time.zone.now) } let(:order_cycle) { proxy_order.order_cycle } - around do |example| - Timecop.freeze(Time.zone.now) { example.run } - end + before { freeze_time } context "when the order cycle is not yet closed" do before { order_cycle.update(orders_open_at: 1.day.ago, orders_close_at: 3.days.from_now) } diff --git a/spec/models/terms_of_service_file_spec.rb b/spec/models/terms_of_service_file_spec.rb index f25c0adf2a..41cb567136 100644 --- a/spec/models/terms_of_service_file_spec.rb +++ b/spec/models/terms_of_service_file_spec.rb @@ -40,7 +40,7 @@ RSpec.describe TermsOfServiceFile do let(:subject) { TermsOfServiceFile.updated_at } it "gives the most conservative time if not known" do - Timecop.freeze do + freeze_time do expect(subject).to eq Time.zone.now end end diff --git a/spec/services/vine/jwt_service_spec.rb b/spec/services/vine/jwt_service_spec.rb index 1a964b3666..1c192cfeaf 100644 --- a/spec/services/vine/jwt_service_spec.rb +++ b/spec/services/vine/jwt_service_spec.rb @@ -21,7 +21,7 @@ RSpec.describe Vine::JwtService do it "includes issuing time" do generate_time = Time.zone.now - Timecop.freeze(generate_time) do + travel_to(generate_time) do token = subject.generate_token payload = decode(token, vine_secret) @@ -32,7 +32,7 @@ RSpec.describe Vine::JwtService do it "includes expirations time" do generate_time = Time.zone.now - Timecop.freeze(generate_time) do + travel_to(generate_time) do token = subject.generate_token payload = decode(token, vine_secret) diff --git a/spec/support/timecop.rb b/spec/support/timecop.rb deleted file mode 100644 index 07ec0de98f..0000000000 --- a/spec/support/timecop.rb +++ /dev/null @@ -1,3 +0,0 @@ -# frozen_string_literal: true - -Timecop.safe_mode = true diff --git a/spec/system/admin/enterprises/terms_and_conditions_spec.rb b/spec/system/admin/enterprises/terms_and_conditions_spec.rb index 15301c3760..53b0031cb5 100644 --- a/spec/system/admin/enterprises/terms_and_conditions_spec.rb +++ b/spec/system/admin/enterprises/terms_and_conditions_spec.rb @@ -34,7 +34,7 @@ RSpec.describe "Uploading Terms and Conditions PDF" do attach_file "enterprise[terms_and_conditions]", original_terms, make_visible: true time = Time.zone.local(2002, 4, 13, 0, 0, 0) - Timecop.freeze(run_time = time) do + travel_to(run_time = time) do click_button "Update" expect(distributor.reload.terms_and_conditions_blob.created_at).to eq run_time end diff --git a/spec/system/admin/reports/orders_and_distributors_spec.rb b/spec/system/admin/reports/orders_and_distributors_spec.rb index 027338e09d..d3f2c4dd01 100644 --- a/spec/system/admin/reports/orders_and_distributors_spec.rb +++ b/spec/system/admin/reports/orders_and_distributors_spec.rb @@ -13,10 +13,6 @@ RSpec.describe "Orders And Distributors" do let!(:distributor2) { create(:distributor_enterprise, name: "By Moto") } let!(:completed_at) { Time.zone.now.to_fs(:db) } - around do |example| - Timecop.travel(completed_at) { example.run } - end - let!(:order) { create(:order_ready_to_ship, distributor_id: distributor.id, completed_at:) } @@ -25,6 +21,10 @@ RSpec.describe "Orders And Distributors" do } let(:variant) { order.variants.first } + before do + travel_to(completed_at) + end + context "as an enterprise user" do let(:header) { ["Order date", "Order Id", "Customer Name", "Customer Email", "Customer Phone", diff --git a/spec/system/admin/reports/orders_and_fulfillment_spec.rb b/spec/system/admin/reports/orders_and_fulfillment_spec.rb index 49fd56071d..5f42a0f2c3 100644 --- a/spec/system/admin/reports/orders_and_fulfillment_spec.rb +++ b/spec/system/admin/reports/orders_and_fulfillment_spec.rb @@ -116,9 +116,10 @@ RSpec.describe "Orders And Fulfillment" do let(:datetime_start1) { 1600.hours.ago } # 1600 hours in the past let(:datetime_start2) { 1800.hours.ago } # 1600 hours in the past let(:datetime_end) { 1400.hours.ago } # 1400 hours in the past + before do - Timecop.travel(completed_at1) { order1.finalize! } - Timecop.travel(completed_at2) { order2.finalize! } + travel_to(completed_at1) { order1.finalize! } + travel_to(completed_at2) { order2.finalize! } end it "is precise to time of day, not just date" do diff --git a/spec/system/admin/reports/packing_report_spec.rb b/spec/system/admin/reports/packing_report_spec.rb index 75f6e5c5b1..686162e389 100644 --- a/spec/system/admin/reports/packing_report_spec.rb +++ b/spec/system/admin/reports/packing_report_spec.rb @@ -6,8 +6,8 @@ RSpec.describe "Packing Reports" do include AuthenticationHelper include WebHelper - around do |example| - Timecop.freeze(Time.zone.now.strftime("%Y-%m-%d 00:00")) { example.run } + before do + travel_to(Time.zone.now.strftime("%Y-%m-%d 00:00")) end let!(:open_datetime) { 1.month.ago.strftime("%Y-%m-%d 00:00") } diff --git a/spec/system/admin/reports_spec.rb b/spec/system/admin/reports_spec.rb index d441b97ccd..3cf45c5678 100644 --- a/spec/system/admin/reports_spec.rb +++ b/spec/system/admin/reports_spec.rb @@ -102,13 +102,13 @@ RSpec.describe ' expect(content).to match "\nFirst Name\n" # Let's also check the expiry of the emailed link: - Timecop.travel(3.days.from_now) do + travel(3.days) do content = URI.parse(report_link).read expect(content).to match "\nFirst Name\n" end # The link should still expire though: - Timecop.travel(3.months.from_now) do + travel(3.months) do expect { URI.parse(report_link).read } .to raise_error OpenURI::HTTPError, "404 Not Found" end @@ -625,15 +625,13 @@ RSpec.describe ' order1.update_order! order1.update!(email: 'customer@email.com') order1.shipment.update(included_tax_total: 10.06) - Timecop.travel(Time.zone.local(2021, 4, 25, 14, 0, 0)) { order1.finalize! } + travel_to(Time.zone.local(2021, 4, 25, 14, 0, 0)) { order1.finalize! } order1.reload order1.create_tax_charge! end - around do |example| - Timecop.travel(Time.zone.local(2021, 4, 26, 14, 0, 0)) do - example.run - end + before do + travel_to(Time.zone.local(2021, 4, 26, 14, 0, 0)) end context "summary report" do diff --git a/spec/system/consumer/authentication_spec.rb b/spec/system/consumer/authentication_spec.rb index 196425e9d5..83cc79fbf0 100644 --- a/spec/system/consumer/authentication_spec.rb +++ b/spec/system/consumer/authentication_spec.rb @@ -139,7 +139,7 @@ RSpec.describe "Authentication" do end it "succeeding after time threshold" do - Timecop.travel(30.seconds.from_now) do + travel(30.seconds) do fill_in "Your email", with: "test@foo.com" fill_in "Choose a password", with: "test12345" fill_in "Confirm password", with: "test12345" diff --git a/spec/system/consumer/caching/shops_caching_spec.rb b/spec/system/consumer/caching/shops_caching_spec.rb index 341fdfe143..dc505551b7 100644 --- a/spec/system/consumer/caching/shops_caching_spec.rb +++ b/spec/system/consumer/caching/shops_caching_spec.rb @@ -29,7 +29,7 @@ RSpec.describe "Shops caching", caching: true do it "keeps data cached for a short time on subsequent requests" do # Ensure sufficient time for requests to load and timed caches to expire - Timecop.travel(10.minutes.ago) do + travel(-10.minutes) do visit shops_path expect(page).to have_content distributor.name @@ -95,7 +95,7 @@ RSpec.describe "Shops caching", caching: true do it "keeps data cached for a short time on subsequent requests" do # Ensure sufficient time for requests to load and timed caches to expire - Timecop.travel(10.minutes.ago) do + travel(-10.minutes) do visit enterprise_shop_path(distributor) # The page HTML contains the cached text but we need to test for the