rubocop-gusto 10.2.0 → 10.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ad5b8322bb818e1f765532f2757ae7bcce5bf7275ef22ced8700f87883715972
4
- data.tar.gz: aff19b02288f2b23afb2df259a2a63b441189527d9d91f2e115ab75c7a2f1746
3
+ metadata.gz: 5b80d8e2199d3f26a7c573b61905ea30b866eb0a9f5ca2bf6eb0f6ef9891ecd4
4
+ data.tar.gz: 88a6fac9e262c3c40caa6ce939b194badf71a84cd05e63165b11a3f36ed4b54d
5
5
  SHA512:
6
- metadata.gz: ed0bc51d62ead56c64464e36bd9142d6600d2a05430fc78c0510b50d577ccdb10d376de287079bec788abca083e40804eb59e6056f45743b9f8ac80576df7067
7
- data.tar.gz: 39af6427e9a5b54b167c26679c314272c26c178b645bcb141b373da48c6829ee1d61d515518398bdf64b9279e8f005ba618674cda144fd80d873988198c2066e
6
+ metadata.gz: 830e8793b87b42c2a727dcbbda20a93de35f5a4887009455e8ff65bc8002dc9499d5fdfadd6971e2e5ce73660e47ddbd4cdf53eeae67c5c26d4840dadda0f092
7
+ data.tar.gz: 1d6b49aa37716ac6095ae0e261f8673e5c2389bff7432ba5775d39b64a86f8f17725b2f35c4c1ffbea1e32cac36751c1ee7924d6251af30b5ea7bed60466bc1c
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  ## Pending
2
2
 
3
+ ## 10.3.0
4
+
5
+ - Add Gusto/RspecDateTimeMock cop
6
+
3
7
  ## 10.2.0
4
8
 
5
9
  - Fix Sorbet sigil exclusions, exclude specs from strict, and add rubocop-sorbet dependency
data/config/default.yml CHANGED
@@ -113,6 +113,12 @@ Gusto/RegexpBypass:
113
113
  - '**/spec/**/*'
114
114
  Safe: false
115
115
 
116
+ Gusto/RspecDateTimeMock:
117
+ Description: "Don't mock Date/Time/DateTime directly. Use Rails Testing Time Helpers (eg `freeze_time` and `travel_to`) instead."
118
+ Include:
119
+ - '**/spec/**/*'
120
+ Safe: false
121
+
116
122
  Gusto/SidekiqParams:
117
123
  Description: 'Sidekiq perform methods cannot take keyword arguments.'
118
124
 
@@ -134,6 +140,7 @@ Gusto/ToplevelConstants:
134
140
 
135
141
  Gusto/UsePaintNotColorize:
136
142
  Description: 'Use Paint instead of colorize for terminal colors.'
143
+ SafeAutoCorrect: false
137
144
 
138
145
  Gusto/VcrRecordings:
139
146
  Description: 'VCR should be set to not record in tests. Use vcr: {record: :none}.'
@@ -340,9 +347,6 @@ RSpec/DescribeMethod:
340
347
  RSpec/DescribedClass:
341
348
  Enabled: false
342
349
 
343
- RSpec/DescribedClassModuleWrapping:
344
- Enabled: true
345
-
346
350
  RSpec/ExampleLength:
347
351
  Enabled: false
348
352
 
@@ -466,7 +470,6 @@ Sorbet/ValidSigil:
466
470
  # We do suggest that the user type their file as `typed: strict`
467
471
  SuggestedStrictness: strict
468
472
  Exclude:
469
- - '**/spec/**/*'
470
473
  - '**/db/migrate/**/*'
471
474
  - '**/*.{rake,arb,erb,rabl}'
472
475
  - '**/{Gemfile,Rakefile}'
@@ -748,7 +751,7 @@ Style/SymbolProc:
748
751
  Enabled: true
749
752
 
750
753
  Style/TernaryParentheses:
751
- EnforcedStyle: require_parentheses_when_complex
754
+ Enabled: false
752
755
 
753
756
  Style/TrailingCommaInArguments:
754
757
  Enabled: true
@@ -0,0 +1,100 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ module RuboCop
5
+ module Cop
6
+ module Gusto
7
+ # Never mock time in specs. Use Rails Testing Time Helpers (eg `freeze_time` and `travel_to`) instead.
8
+ # .and_call_original is allowed.
9
+ #
10
+ # @example
11
+ # # bad
12
+ # context 'time specific tests' do
13
+ # before { allow(Time).to receive(:now).and_return(current_time) }
14
+ # it 'does stuff in a fixed time' do
15
+ # subject
16
+ # end
17
+ # end
18
+ #
19
+ # # good
20
+ # context 'time specific tests' do
21
+ # it 'does stuff in a fixed time' do
22
+ # freeze_time do
23
+ # subject
24
+ # end
25
+ # end
26
+ # end
27
+ class RspecDateTimeMock < Base
28
+ CLASSES = Set[
29
+ :Date,
30
+ :Time,
31
+ :DateTime
32
+ ].freeze
33
+ MSG = "Don't mock #{CLASSES.join('/')} directly. Use Rails Testing Time Helpers (eg `freeze_time` and `travel_to`) instead.".freeze
34
+ RESTRICT_ON_SEND = %i(to).freeze
35
+
36
+ # Matches allow/expect with a time class (or chain) receiver and a `receive` or `receive_message_chain`
37
+ # Examples matched:
38
+ # allow(Time).to receive(:now)
39
+ # allow(Time.zone).to receive(:now)
40
+ # allow(Time).to receive_message_chain(:zone, :now)
41
+ # expect(Date.today).to receive(:wday)
42
+ # @!method time_mock?(node)
43
+ def_node_matcher :time_mock?, <<~PATTERN
44
+ (send
45
+ (send nil? {:allow :expect} #rooted_in_time_class?)
46
+ :to
47
+ {
48
+ (send (send nil? :receive _) ...)
49
+ (send (send nil? :receive_message_chain ...) ...)
50
+ }
51
+ )
52
+ PATTERN
53
+
54
+ def on_send(node)
55
+ time_mock?(node) do
56
+ # Allow usages that explicitly call `.and_call_original` after `receive`
57
+ # Example: allow(Time).to receive(:parse).and_call_original
58
+ receive_chain = node.first_argument
59
+ return if and_call_original_in_chain?(receive_chain)
60
+
61
+ add_offense(node)
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ # Returns true if the given node is a const or a call chain whose root receiver
68
+ # is one of the protected time classes (Date/Time/DateTime)
69
+ def rooted_in_time_class?(node)
70
+ return false if node.nil?
71
+
72
+ current = node
73
+ while current.respond_to?(:send_type?) && current.send_type?
74
+ current = current.receiver
75
+ end
76
+
77
+ if current.nil?
78
+ return false
79
+ end
80
+
81
+ return false unless current.const_type?
82
+
83
+ # Accept both `Time` and `::Time` as root-level constants
84
+ namespace = current.namespace
85
+ is_root_level = namespace.nil? || namespace.cbase_type?
86
+ is_root_level && CLASSES.include?(current.children[1])
87
+ end
88
+
89
+ def and_call_original_in_chain?(node)
90
+ return false if node.nil?
91
+ return false unless node.send_type?
92
+
93
+ return true if node.method?(:and_call_original)
94
+
95
+ node.each_descendant(:send).any? { |send_node| send_node.method?(:and_call_original) }
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -5,6 +5,12 @@ module RuboCop
5
5
  module Gusto
6
6
  # Requires the use of the `paint` gem for terminal color methods on strings
7
7
  #
8
+ # @safety
9
+ # This cop's autocorrection is unsafe because it replaces colorize gem
10
+ # methods with Paint gem methods. If the Paint gem is not included in the
11
+ # project's dependencies, the corrected code will fail at runtime with a
12
+ # NameError (uninitialized constant Paint).
13
+ #
8
14
  # @example
9
15
  #
10
16
  # # bad
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RuboCop
4
4
  module Gusto
5
- VERSION = "10.2.0"
5
+ VERSION = "10.3.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-gusto
3
3
  version: !ruby/object:Gem::Version
4
- version: 10.2.0
4
+ version: 10.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gusto Engineering
@@ -140,6 +140,7 @@ files:
140
140
  - lib/rubocop/cop/gusto/rails_env.rb
141
141
  - lib/rubocop/cop/gusto/rake_constants.rb
142
142
  - lib/rubocop/cop/gusto/regexp_bypass.rb
143
+ - lib/rubocop/cop/gusto/rspec_date_time_mock.rb
143
144
  - lib/rubocop/cop/gusto/sidekiq_params.rb
144
145
  - lib/rubocop/cop/gusto/toplevel_constants.rb
145
146
  - lib/rubocop/cop/gusto/use_paint_not_colorize.rb