lib/rubocop/cop/rspec/subject_stub.rb in rubocop-rspec-1.39.0 vs lib/rubocop/cop/rspec/subject_stub.rb in rubocop-rspec-1.40.0

- old
+ new

@@ -1,7 +1,9 @@ # frozen_string_literal: true +require 'set' + module RuboCop module Cop module RSpec # Checks for stubbed test subjects. # @@ -73,73 +75,49 @@ } ...) PATTERN def on_block(node) return unless example_group?(node) + return if (processed_example_groups & node.ancestors).any? - find_subject_stub(node) do |stub| + processed_example_groups << node + @explicit_subjects = find_all_explicit_subjects(node) + + find_subject_expectations(node) do |stub| add_offense(stub) end end private - # Find subjects within tree and then find (send) nodes for that subject - # - # @param node [RuboCop::Node] example group - # - # @yield [RuboCop::Node] message expectations for subject - def find_subject_stub(node, &block) - find_subject(node) do |subject_name, context| - find_subject_expectation(context, subject_name, &block) - end + def processed_example_groups + @processed_example_groups ||= Set.new end - # Find a subject message expectation - # - # @param node [RuboCop::Node] - # @param subject_name [Symbol] name of subject - # - # @yield [RuboCop::Node] message expectation - def find_subject_expectation(node, subject_name, &block) - # Do not search node if it is an example group with its own subject. - return if example_group?(node) && redefines_subject?(node) + def find_all_explicit_subjects(node) + node.each_descendant(:block).with_object({}) do |child, h| + name = subject(child) + next unless name - # Yield the current node if it is a message expectation. - yield(node) if message_expectation?(node, subject_name) + outer_example_group = child.each_ancestor.find do |a| + example_group?(a) + end - # Recurse through node's children looking for a message expectation. - node.each_child_node do |child| - find_subject_expectation(child, subject_name, &block) + h[outer_example_group] ||= [] + h[outer_example_group] << name end end - # Check if node's children contain a subject definition - # - # @param node [RuboCop::Node] - # - # @return [Boolean] - def redefines_subject?(node) - node.each_child_node.any? do |child| - subject(child) || redefines_subject?(child) + def find_subject_expectations(node, subject_names = [], &block) + subject_names = @explicit_subjects[node] if @explicit_subjects[node] + + expectation_detected = (subject_names + [:subject]).any? do |name| + message_expectation?(node, name) end - end + return yield(node) if expectation_detected - # Find a subject definition - # - # @param node [RuboCop::Node] - # @param parent [RuboCop::Node,nil] - # - # @yieldparam subject_name [Symbol] name of subject being defined - # @yieldparam parent [RuboCop::Node] parent of subject definition - def find_subject(node, parent: nil, &block) - # An implicit subject is defined by RSpec when no subject is declared - subject_name = subject(node) || :subject - - yield(subject_name, parent) if parent - node.each_child_node do |child| - find_subject(child, parent: node, &block) + find_subject_expectations(child, subject_names, &block) end end end end end