timecop-rspec 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data/.devcontainer/devcontainer.json +26 -0
- data/.env.local.example +3 -0
- data/.envrc +41 -0
- data/.github/FUNDING.yml +13 -0
- data/.github/dependabot.yml +12 -0
- data/.github/workflows/ancient.yml +77 -0
- data/.github/workflows/auto-assign.yml +21 -0
- data/.github/workflows/codeql-analysis.yml +70 -0
- data/.github/workflows/coverage.yml +126 -0
- data/.github/workflows/current.yml +85 -0
- data/.github/workflows/dependency-review.yml +20 -0
- data/.github/workflows/deps_locked.yml +75 -0
- data/.github/workflows/deps_unlocked.yml +84 -0
- data/.github/workflows/discord-notifier.yml +38 -0
- data/.github/workflows/heads.yml +84 -0
- data/.github/workflows/jruby.yml +75 -0
- data/.github/workflows/legacy.yml +67 -0
- data/.github/workflows/style.yml +64 -0
- data/.github/workflows/supported.yml +82 -0
- data/.github/workflows/truffle.yml +75 -0
- data/.github/workflows/unsupported.yml +75 -0
- data/.gitignore +45 -0
- data/.gitlab-ci.yml +44 -0
- data/.idea/.gitignore +8 -0
- data/.idea/GitLink.xml +6 -0
- data/.idea/misc.xml +4 -0
- data/.idea/modules.xml +8 -0
- data/.idea/timecop-rspec.iml +120 -0
- data/.idea/vcs.xml +6 -0
- data/.junie/TASK_NOTE.md +1 -0
- data/.junie/guidelines.md +139 -0
- data/.qlty/qlty.toml +79 -0
- data/.rspec +8 -0
- data/.rubocop.yml +15 -0
- data/.rubocop_gradual.lock +76 -0
- data/.simplecov +11 -0
- data/.tool-versions +1 -0
- data/.yard_gfm_support.rb +22 -0
- data/.yardopts +11 -0
- data/Appraisal.root.gemfile +12 -0
- data/Appraisals +104 -0
- data/CHANGELOG.md +27 -0
- data/CITATION.cff +20 -0
- data/CNAME +1 -0
- data/CODE_OF_CONDUCT.md +134 -0
- data/CONTRIBUTING.md +145 -0
- data/Gemfile +38 -0
- data/Gemfile.lock +315 -0
- data/LICENSE.txt +22 -0
- data/README.md +563 -0
- data/REEK +0 -0
- data/RUBOCOP.md +71 -0
- data/Rakefile +287 -0
- data/SECURITY.md +21 -0
- data/bin/appraisal +16 -0
- data/bin/bundle-audit +16 -0
- data/bin/bundler-audit +16 -0
- data/bin/code_climate_reek +16 -0
- data/bin/coderay +16 -0
- data/bin/console +38 -0
- data/bin/erb +16 -0
- data/bin/gem_checksums +16 -0
- data/bin/htmldiff +16 -0
- data/bin/irb +16 -0
- data/bin/kramdown +16 -0
- data/bin/ldiff +16 -0
- data/bin/nokogiri +16 -0
- data/bin/pry +16 -0
- data/bin/racc +16 -0
- data/bin/rake +16 -0
- data/bin/rdbg +16 -0
- data/bin/rdoc +16 -0
- data/bin/reek +16 -0
- data/bin/ri +16 -0
- data/bin/rspec +16 -0
- data/bin/rubocop +16 -0
- data/bin/rubocop-gradual +16 -0
- data/bin/ruby-parse +16 -0
- data/bin/ruby-rewrite +16 -0
- data/bin/setup +33 -0
- data/bin/standardrb +16 -0
- data/bin/thor +16 -0
- data/bin/yard +16 -0
- data/bin/yard-junk +16 -0
- data/bin/yardoc +16 -0
- data/bin/yri +16 -0
- data/certs/pboling.pem +27 -0
- data/checksums/timecop-rspec-1.0.0.gem.sha256 +1 -0
- data/checksums/timecop-rspec-1.0.0.gem.sha512 +1 -0
- data/docs/Timecop/Rspec/ExampleDecorator.html +626 -0
- data/docs/Timecop/Rspec/SequentialTimeMachine.html +338 -0
- data/docs/Timecop/Rspec/TimeMachine.html +357 -0
- data/docs/Timecop/Rspec/TravelLog.html +440 -0
- data/docs/Timecop/Rspec/Traveler.html +353 -0
- data/docs/Timecop/Rspec/Version.html +154 -0
- data/docs/Timecop/Rspec.html +474 -0
- data/docs/Timecop.html +149 -0
- data/docs/_index.html +218 -0
- data/docs/class_list.html +54 -0
- data/docs/css/common.css +1 -0
- data/docs/css/full_list.css +58 -0
- data/docs/css/style.css +503 -0
- data/docs/file.CHANGELOG.html +99 -0
- data/docs/file.CITATION.html +92 -0
- data/docs/file.CODE_OF_CONDUCT.html +201 -0
- data/docs/file.CONTRIBUTING.html +220 -0
- data/docs/file.LICENSE.html +70 -0
- data/docs/file.README.html +523 -0
- data/docs/file.REEK.html +71 -0
- data/docs/file.RUBOCOP.html +171 -0
- data/docs/file.SECURITY.html +101 -0
- data/docs/file.rspec.html +109 -0
- data/docs/file.timecop.html +72 -0
- data/docs/file_list.html +109 -0
- data/docs/frames.html +22 -0
- data/docs/index.html +523 -0
- data/docs/js/app.js +344 -0
- data/docs/js/full_list.js +242 -0
- data/docs/js/jquery.js +4 -0
- data/docs/method_list.html +198 -0
- data/docs/top-level-namespace.html +110 -0
- data/gemfiles/audit.gemfile +10 -0
- data/gemfiles/coverage.gemfile +10 -0
- data/gemfiles/current.gemfile +8 -0
- data/gemfiles/deps_unlocked.gemfile +13 -0
- data/gemfiles/head.gemfile +9 -0
- data/gemfiles/modular/audit.gemfile +5 -0
- data/gemfiles/modular/coverage.gemfile +6 -0
- data/gemfiles/modular/documentation.gemfile +11 -0
- data/gemfiles/modular/style.gemfile +14 -0
- data/gemfiles/ruby_2_3.gemfile +5 -0
- data/gemfiles/ruby_2_4.gemfile +5 -0
- data/gemfiles/ruby_2_5.gemfile +5 -0
- data/gemfiles/ruby_2_6.gemfile +8 -0
- data/gemfiles/ruby_2_7.gemfile +8 -0
- data/gemfiles/ruby_3_0.gemfile +8 -0
- data/gemfiles/ruby_3_1.gemfile +8 -0
- data/gemfiles/ruby_3_2.gemfile +8 -0
- data/gemfiles/ruby_3_3.gemfile +8 -0
- data/gemfiles/style.gemfile +10 -0
- data/lib/timecop/rspec/example_decorator.rb +100 -0
- data/lib/timecop/rspec/sequential_time_machine.rb +80 -0
- data/lib/timecop/rspec/time_machine.rb +58 -0
- data/lib/timecop/rspec/travel_log.rb +103 -0
- data/lib/timecop/rspec/traveler.rb +68 -0
- data/lib/timecop/rspec/version.rb +37 -0
- data/lib/timecop/rspec.rb +78 -0
- data/sig/timecop/rspec.rbs +40 -0
- data/sig/timecop.rbs +2 -0
- data/timecop-rspec.gemspec +146 -0
- data.tar.gz.sig +2 -0
- metadata +434 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Documentation
|
4
|
+
gem "kramdown", "~> 2.5", ">= 2.5.1" # Ruby >= 2.5
|
5
|
+
gem "kramdown-parser-gfm", "~> 1.1" # Ruby >= 2.3
|
6
|
+
gem "yard", "~> 0.9", ">= 0.9.37", :require => false
|
7
|
+
gem "yard-junk", "~> 0.0", ">= 0.0.10", :github => "pboling/yard-junk", :branch => "next", :require => false
|
8
|
+
gem "yard-relative_markdown_links", "~> 0.5.0"
|
9
|
+
|
10
|
+
# Std Lib extractions
|
11
|
+
gem "rdoc", "~> 6.11"
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# We run rubocop on the latest version of Ruby,
|
4
|
+
# but in support of the oldest supported version of Ruby
|
5
|
+
|
6
|
+
gem "reek", "~> 6.4"
|
7
|
+
gem "rubocop", "~> 1.73", ">= 1.73.2"
|
8
|
+
gem "rubocop-lts", "~> 0.1", ">= 0.1.1" # Linting for Ruby >= 1.8
|
9
|
+
gem "rubocop-packaging", "~> 0.6", ">= 0.6.0"
|
10
|
+
gem "rubocop-rspec", "~> 3.2"
|
11
|
+
gem "standard", ">= 1.50"
|
12
|
+
|
13
|
+
# Std Lib extractions
|
14
|
+
gem "benchmark", "~> 0.4", ">= 0.4.1" # Removed from Std Lib in Ruby 3.5
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# The MIT License (MIT)
|
2
|
+
|
3
|
+
# Copyright (c) 2014-2017 Avant
|
4
|
+
|
5
|
+
# Author Zach Taylor
|
6
|
+
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
# of this software and associated documentation files (the "Software"), to deal
|
9
|
+
# in the Software without restriction, including without limitation the rights
|
10
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
# copies of the Software, and to permit persons to whom the Software is
|
12
|
+
# furnished to do so, subject to the following conditions:
|
13
|
+
|
14
|
+
# The above copyright notice and this permission notice shall be included in
|
15
|
+
# all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23
|
+
# THE SOFTWARE.
|
24
|
+
|
25
|
+
class Timecop
|
26
|
+
module Rspec
|
27
|
+
# Decorates an RSpec example to interpret timecop metadata.
|
28
|
+
#
|
29
|
+
# Recognized metadata keys:
|
30
|
+
# - :freeze => Time/Date/DateTime/String/Proc to freeze time to
|
31
|
+
# - :travel => Time/Date/DateTime/String/Proc to travel to (and optionally continue)
|
32
|
+
# - :skip_global_timecop => when present, disables global time for this example
|
33
|
+
class ExampleDecorator < SimpleDelegator
|
34
|
+
# @return [Boolean] whether any timecop behavior applies to the example
|
35
|
+
def timecop?
|
36
|
+
local_timecop? || global_timecop?
|
37
|
+
end
|
38
|
+
|
39
|
+
# Determines the timecop method to invoke.
|
40
|
+
# @return [Symbol, nil] :freeze, :travel, or nil
|
41
|
+
def timecop_method
|
42
|
+
local_timecop_method || global_timecop_method
|
43
|
+
end
|
44
|
+
|
45
|
+
# Determines the time to use for the chosen timecop method.
|
46
|
+
# @return [Object, nil] a time-like object or nil when not applicable
|
47
|
+
def timecop_time
|
48
|
+
local_timecop_time || global_timecop_time
|
49
|
+
end
|
50
|
+
|
51
|
+
# Whether the example has local timecop metadata.
|
52
|
+
# @return [Boolean]
|
53
|
+
def local_timecop?
|
54
|
+
local_timecop_method.present?
|
55
|
+
end
|
56
|
+
|
57
|
+
# Whether a global time is configured and not skipped by the example.
|
58
|
+
# @return [Boolean]
|
59
|
+
def global_timecop?
|
60
|
+
Rspec.global_time_configured? && !skip_global_timecop?
|
61
|
+
end
|
62
|
+
|
63
|
+
# Whether the example requested skipping global timecop behavior.
|
64
|
+
# @return [Boolean]
|
65
|
+
def skip_global_timecop?
|
66
|
+
metadata.key?(:skip_global_timecop)
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
# Reads the timecop method from example metadata.
|
72
|
+
# @return [Symbol, nil]
|
73
|
+
def local_timecop_method
|
74
|
+
metadata.keys.find do |key|
|
75
|
+
key == :freeze || key == :travel
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Evaluates local timecop time, supporting Proc values evaluated in the example context.
|
80
|
+
# @return [Object, nil]
|
81
|
+
def local_timecop_time
|
82
|
+
time = metadata[timecop_method]
|
83
|
+
return if time.nil?
|
84
|
+
time.respond_to?(:call) ? example.instance_exec(&time) : time
|
85
|
+
end
|
86
|
+
|
87
|
+
# Global time always uses :travel.
|
88
|
+
# @return [Symbol, nil]
|
89
|
+
def global_timecop_method
|
90
|
+
:travel if global_timecop?
|
91
|
+
end
|
92
|
+
|
93
|
+
# Global time value when applicable.
|
94
|
+
# @return [Time, nil]
|
95
|
+
def global_timecop_time
|
96
|
+
Rspec.global_time if global_timecop?
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# The MIT License (MIT)
|
2
|
+
|
3
|
+
# Copyright (c) 2014-2017 Avant
|
4
|
+
|
5
|
+
# Author Zach Taylor
|
6
|
+
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
# of this software and associated documentation files (the "Software"), to deal
|
9
|
+
# in the Software without restriction, including without limitation the rights
|
10
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
# copies of the Software, and to permit persons to whom the Software is
|
12
|
+
# furnished to do so, subject to the following conditions:
|
13
|
+
|
14
|
+
# The above copyright notice and this permission notice shall be included in
|
15
|
+
# all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23
|
+
# THE SOFTWARE.
|
24
|
+
|
25
|
+
require_relative "example_decorator"
|
26
|
+
require_relative "traveler"
|
27
|
+
require_relative "travel_log"
|
28
|
+
|
29
|
+
class Timecop
|
30
|
+
module Rspec
|
31
|
+
# Executes examples while allowing time travel to continue across examples.
|
32
|
+
#
|
33
|
+
# Uses TravelLog to coalesce sequential :travel operations when the same
|
34
|
+
# method and start time are reused, enabling long-running time journeys
|
35
|
+
# across multiple examples.
|
36
|
+
class SequentialTimeMachine
|
37
|
+
# Singleton instance accessor.
|
38
|
+
# @return [Timecop::Rspec::SequentialTimeMachine]
|
39
|
+
def self.instance
|
40
|
+
@instance ||= new
|
41
|
+
end
|
42
|
+
|
43
|
+
# Run an example with either local or global travel, or without any timecop.
|
44
|
+
# @param example [#run,#metadata]
|
45
|
+
# @return [Object]
|
46
|
+
def run(example)
|
47
|
+
example = ExampleDecorator.new(example)
|
48
|
+
|
49
|
+
runner_for(example).run
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
# Selects the appropriate runner for the example.
|
55
|
+
# @param example [ExampleDecorator]
|
56
|
+
# @return [#run]
|
57
|
+
def runner_for(example)
|
58
|
+
if example.local_timecop?
|
59
|
+
Traveler.new(example, local_travel_log)
|
60
|
+
elsif example.global_timecop?
|
61
|
+
Traveler.new(example, global_travel_log)
|
62
|
+
else
|
63
|
+
example
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Local travel log used for examples that specify local timecop metadata.
|
68
|
+
# @return [TravelLog]
|
69
|
+
def local_travel_log
|
70
|
+
@local_travel_log ||= TravelLog.new
|
71
|
+
end
|
72
|
+
|
73
|
+
# Global travel log used when a global time is configured.
|
74
|
+
# @return [TravelLog]
|
75
|
+
def global_travel_log
|
76
|
+
@global_travel_log ||= TravelLog.new(:travel, Rspec.global_time)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# The MIT License (MIT)
|
2
|
+
|
3
|
+
# Copyright (c) 2014-2017 Avant
|
4
|
+
|
5
|
+
# Author Zach Taylor
|
6
|
+
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
# of this software and associated documentation files (the "Software"), to deal
|
9
|
+
# in the Software without restriction, including without limitation the rights
|
10
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
# copies of the Software, and to permit persons to whom the Software is
|
12
|
+
# furnished to do so, subject to the following conditions:
|
13
|
+
|
14
|
+
# The above copyright notice and this permission notice shall be included in
|
15
|
+
# all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23
|
+
# THE SOFTWARE.
|
24
|
+
|
25
|
+
require "timecop"
|
26
|
+
|
27
|
+
require_relative "example_decorator"
|
28
|
+
|
29
|
+
class Timecop
|
30
|
+
module Rspec
|
31
|
+
# Executes an example using Timecop for a single, isolated time operation.
|
32
|
+
class TimeMachine
|
33
|
+
# Singleton instance accessor.
|
34
|
+
# @return [Timecop::Rspec::TimeMachine]
|
35
|
+
def self.instance
|
36
|
+
@instance ||= new
|
37
|
+
end
|
38
|
+
|
39
|
+
# Run an RSpec example, applying local timecop metadata if present.
|
40
|
+
#
|
41
|
+
# @param example [#run,#metadata] An RSpec example or object responding to
|
42
|
+
# the example protocol that will be wrapped by ExampleDecorator.
|
43
|
+
# @return [Object] the result of running the example
|
44
|
+
def run(example)
|
45
|
+
example = ExampleDecorator.new(example)
|
46
|
+
|
47
|
+
return example.run unless example.timecop?
|
48
|
+
|
49
|
+
method = example.timecop_method
|
50
|
+
time = example.timecop_time
|
51
|
+
|
52
|
+
Timecop.public_send(method, time) do
|
53
|
+
example.run
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# The MIT License (MIT)
|
2
|
+
|
3
|
+
# Copyright (c) 2014-2017 Avant
|
4
|
+
|
5
|
+
# Author Zach Taylor
|
6
|
+
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
# of this software and associated documentation files (the "Software"), to deal
|
9
|
+
# in the Software without restriction, including without limitation the rights
|
10
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
# copies of the Software, and to permit persons to whom the Software is
|
12
|
+
# furnished to do so, subject to the following conditions:
|
13
|
+
|
14
|
+
# The above copyright notice and this permission notice shall be included in
|
15
|
+
# all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23
|
+
# THE SOFTWARE.
|
24
|
+
|
25
|
+
class Timecop
|
26
|
+
module Rspec
|
27
|
+
# Tracks details about a time travel operation that can be resumed later.
|
28
|
+
class TravelLog
|
29
|
+
# @param travel_method [Symbol, nil] :travel or :freeze (or nil)
|
30
|
+
# @param start_time [Object, nil] a time-like object or string
|
31
|
+
def initialize(travel_method = nil, start_time = nil)
|
32
|
+
new_trip(travel_method, start_time)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Either resumes a previous trip or starts a new one.
|
36
|
+
# @param travel_method [Symbol] :travel or :freeze
|
37
|
+
# @param start_time [Object] a time-like object or string
|
38
|
+
# @return [Object] the computed starting time for the trip
|
39
|
+
def resume_or_new_trip(travel_method, start_time)
|
40
|
+
if resume_trip?(travel_method, start_time)
|
41
|
+
resume_trip
|
42
|
+
else
|
43
|
+
new_trip(travel_method, start_time)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Pauses the current trip, recording its duration.
|
48
|
+
# @return [void]
|
49
|
+
def pause_trip
|
50
|
+
self.trip_duration = Time.current - coalesced_start_time
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
attr_accessor :travel_method, :start_time, :trip_duration
|
56
|
+
|
57
|
+
# Starts a new trip tracking session.
|
58
|
+
# @param travel_method [Symbol, nil]
|
59
|
+
# @param start_time [Object, nil]
|
60
|
+
# @return [void]
|
61
|
+
def new_trip(travel_method, start_time)
|
62
|
+
reset_duration
|
63
|
+
self.travel_method = travel_method
|
64
|
+
self.start_time = start_time
|
65
|
+
end
|
66
|
+
|
67
|
+
# Determines whether the provided method/time match the current trip.
|
68
|
+
# @return [Boolean]
|
69
|
+
def resume_trip?(other_travel_method, other_start_time)
|
70
|
+
travel_method == other_travel_method &&
|
71
|
+
start_time == other_start_time &&
|
72
|
+
start_time.class == other_start_time.class
|
73
|
+
end
|
74
|
+
|
75
|
+
# The start time for resuming a trip including the elapsed duration.
|
76
|
+
# @return [Object]
|
77
|
+
def resume_trip
|
78
|
+
coalesced_start_time + trip_duration.seconds
|
79
|
+
end
|
80
|
+
|
81
|
+
# Coerces various time-like inputs into a Time-like baseline.
|
82
|
+
# @return [Object]
|
83
|
+
def coalesced_start_time
|
84
|
+
case start_time
|
85
|
+
when DateTime
|
86
|
+
start_time
|
87
|
+
when Date
|
88
|
+
start_time.at_beginning_of_day
|
89
|
+
when String
|
90
|
+
Time.parse(start_time)
|
91
|
+
else
|
92
|
+
start_time
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Resets the recorded duration.
|
97
|
+
# @return [void]
|
98
|
+
def reset_duration
|
99
|
+
self.trip_duration = 0
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# The MIT License (MIT)
|
2
|
+
|
3
|
+
# Copyright (c) 2014-2017 Avant
|
4
|
+
|
5
|
+
# Author Zach Taylor
|
6
|
+
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
# of this software and associated documentation files (the "Software"), to deal
|
9
|
+
# in the Software without restriction, including without limitation the rights
|
10
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
# copies of the Software, and to permit persons to whom the Software is
|
12
|
+
# furnished to do so, subject to the following conditions:
|
13
|
+
|
14
|
+
# The above copyright notice and this permission notice shall be included in
|
15
|
+
# all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23
|
+
# THE SOFTWARE.
|
24
|
+
|
25
|
+
class Timecop
|
26
|
+
module Rspec
|
27
|
+
# Runs an example, optionally continuing a travel across invocations.
|
28
|
+
class Traveler
|
29
|
+
# @param example [ExampleDecorator]
|
30
|
+
# @param travel_log [TravelLog]
|
31
|
+
def initialize(example, travel_log)
|
32
|
+
@example = example
|
33
|
+
@travel_log = travel_log
|
34
|
+
end
|
35
|
+
|
36
|
+
# Executes the example within the appropriate Timecop context.
|
37
|
+
# If the method is :travel, the starting time may be adjusted based on
|
38
|
+
# prior trips recorded in the travel log.
|
39
|
+
#
|
40
|
+
# @return [Object]
|
41
|
+
def run
|
42
|
+
method = example.timecop_method
|
43
|
+
time = example.timecop_time
|
44
|
+
|
45
|
+
if method == :travel
|
46
|
+
time = travel_log.resume_or_new_trip(
|
47
|
+
example.timecop_method, example.timecop_time
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
::Timecop.public_send(method, time) do
|
52
|
+
begin
|
53
|
+
example.run
|
54
|
+
ensure
|
55
|
+
travel_log.pause_trip if method == :travel
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
# @return [ExampleDecorator]
|
63
|
+
attr_reader :example
|
64
|
+
# @return [TravelLog]
|
65
|
+
attr_reader :travel_log
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# The MIT License (MIT)
|
2
|
+
|
3
|
+
# Copyright (c) 2014-2017 Avant
|
4
|
+
|
5
|
+
# Author Zach Taylor
|
6
|
+
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
# of this software and associated documentation files (the "Software"), to deal
|
9
|
+
# in the Software without restriction, including without limitation the rights
|
10
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
# copies of the Software, and to permit persons to whom the Software is
|
12
|
+
# furnished to do so, subject to the following conditions:
|
13
|
+
|
14
|
+
# The above copyright notice and this permission notice shall be included in
|
15
|
+
# all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23
|
+
# THE SOFTWARE.
|
24
|
+
|
25
|
+
class Timecop
|
26
|
+
module Rspec
|
27
|
+
# Version namespace for Timecop::Rspec.
|
28
|
+
module Version
|
29
|
+
# The gem version.
|
30
|
+
# @return [String]
|
31
|
+
VERSION = "1.0.0"
|
32
|
+
end
|
33
|
+
# Convenience constant aliasing Version::VERSION
|
34
|
+
# @return [String]
|
35
|
+
VERSION = Version::VERSION
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# The MIT License (MIT)
|
2
|
+
|
3
|
+
# Copyright (c) 2014-2017 Avant
|
4
|
+
|
5
|
+
# Author Zach Taylor
|
6
|
+
|
7
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
# of this software and associated documentation files (the "Software"), to deal
|
9
|
+
# in the Software without restriction, including without limitation the rights
|
10
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
# copies of the Software, and to permit persons to whom the Software is
|
12
|
+
# furnished to do so, subject to the following conditions:
|
13
|
+
|
14
|
+
# The above copyright notice and this permission notice shall be included in
|
15
|
+
# all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
23
|
+
# THE SOFTWARE.
|
24
|
+
|
25
|
+
require "timecop/rspec/version"
|
26
|
+
require "active_support/all"
|
27
|
+
|
28
|
+
Dir.glob(File.join(__dir__, "rspec", "**", "*.rb")).each { |file| require file }
|
29
|
+
|
30
|
+
# Top-level namespace for Timecop helpers.
|
31
|
+
class Timecop
|
32
|
+
# RSpec integration for the timecop gem.
|
33
|
+
#
|
34
|
+
# Provides helpers to run examples with frozen or traveling time, and an
|
35
|
+
# optional global time that applies across examples when configured via ENV.
|
36
|
+
#
|
37
|
+
# Environment variables:
|
38
|
+
# - GLOBAL_TIME_TRAVEL_TIME: String representation of a time (preferred)
|
39
|
+
# - GLOBAL_TIME_TRAVEL_DATE: String representation of a date (fallback)
|
40
|
+
module Rspec
|
41
|
+
class << self
|
42
|
+
# Selects a time machine strategy.
|
43
|
+
#
|
44
|
+
# @param sequential [Boolean] when true, uses a sequential strategy that
|
45
|
+
# can continue a travel across examples; when false, uses a simple per
|
46
|
+
# example strategy.
|
47
|
+
# @return [Timecop::Rspec::SequentialTimeMachine, Timecop::Rspec::TimeMachine]
|
48
|
+
def time_machine(sequential: false)
|
49
|
+
if sequential
|
50
|
+
SequentialTimeMachine.instance
|
51
|
+
else
|
52
|
+
TimeMachine.instance
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Whether a global time has been configured via ENV.
|
57
|
+
# @return [Boolean]
|
58
|
+
def global_time_configured?
|
59
|
+
global_time_travel_string.present?
|
60
|
+
end
|
61
|
+
|
62
|
+
# The globally configured time parsed from ENV.
|
63
|
+
# @return [Time] the parsed time
|
64
|
+
# @raise [ArgumentError] if ENV contains an unparsable time/date string
|
65
|
+
def global_time
|
66
|
+
@global_time ||= Time.parse(global_time_travel_string)
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
# The raw ENV value used to determine the global time.
|
72
|
+
# @return [String, nil]
|
73
|
+
def global_time_travel_string
|
74
|
+
ENV["GLOBAL_TIME_TRAVEL_TIME"] || ENV["GLOBAL_TIME_TRAVEL_DATE"]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|