lib/rubocop/cop/rspec/implicit_subject.rb in rubocop-rspec-2.13.2 vs lib/rubocop/cop/rspec/implicit_subject.rb in rubocop-rspec-2.14.0

- old
+ new

@@ -40,59 +40,126 @@ # it { is_expected.to be_truthy } # # # good # it { expect(subject).to be_truthy } # + # @example `EnforcedStyle: require_implicit` + # # bad + # it { expect(subject).to be_truthy } + # + # # good + # it { is_expected.to be_truthy } + # + # # bad + # it do + # expect(subject).to be_truthy + # end + # + # # good + # it do + # is_expected.to be_truthy + # end + # + # # good + # it { expect(named_subject).to be_truthy } + # class ImplicitSubject < Base extend AutoCorrector include ConfigurableEnforcedStyle - MSG = "Don't use implicit subject." - RESTRICT_ON_SEND = %i[is_expected should should_not].freeze + MSG_REQUIRE_EXPLICIT = "Don't use implicit subject." + MSG_REQUIRE_IMPLICIT = "Don't use explicit subject." + + RESTRICT_ON_SEND = %i[ + expect + is_expected + should + should_not + ].freeze + + # @!method explicit_unnamed_subject?(node) + def_node_matcher :explicit_unnamed_subject?, <<-PATTERN + (send nil? :expect (send nil? :subject)) + PATTERN + # @!method implicit_subject?(node) def_node_matcher :implicit_subject?, <<-PATTERN (send nil? {:should :should_not :is_expected} ...) PATTERN def on_send(node) - return unless implicit_subject?(node) - return if valid_usage?(node) + return unless invalid?(node) add_offense(node) do |corrector| autocorrect(corrector, node) end end private def autocorrect(corrector, node) - replacement = 'expect(subject)' case node.method_name + when :expect + corrector.replace(node, 'is_expected') + when :is_expected + corrector.replace(node.location.selector, 'expect(subject)') when :should - replacement += '.to' + corrector.replace(node.location.selector, 'expect(subject).to') when :should_not - replacement += '.not_to' + corrector.replace(node.location.selector, 'expect(subject).not_to') end - - corrector.replace(node.loc.selector, replacement) end - def valid_usage?(node) - example = node.ancestors.find { |parent| example?(parent) } - return false if example.nil? - - example.method?(:its) || allowed_by_style?(example) + def message(_node) + case style + when :require_implicit + MSG_REQUIRE_IMPLICIT + else + MSG_REQUIRE_EXPLICIT + end end - def allowed_by_style?(example) + def invalid?(node) case style + when :require_implicit + explicit_unnamed_subject?(node) + when :disallow + implicit_subject_in_non_its?(node) when :single_line_only - example.single_line? + implicit_subject_in_non_its_and_non_single_line?(node) when :single_statement_only - !example.body.begin_type? - else - false + implicit_subject_in_non_its_and_non_single_statement?(node) + end + end + + def implicit_subject_in_non_its?(node) + implicit_subject?(node) && !its?(node) + end + + def implicit_subject_in_non_its_and_non_single_line?(node) + implicit_subject_in_non_its?(node) && !single_line?(node) + end + + def implicit_subject_in_non_its_and_non_single_statement?(node) + implicit_subject_in_non_its?(node) && !single_statement?(node) + end + + def its?(node) + example_of(node)&.method?(:its) + end + + def single_line?(node) + example_of(node)&.single_line? + end + + def single_statement?(node) + !example_of(node)&.body&.begin_type? + end + + def example_of(node) + node.each_ancestor.find do |ancestor| + example?(ancestor) end end end end end