lib/rubocop/cop/rspec/pending_without_reason.rb
# frozen_string_literal: true
module RuboCop
module Cop
module RSpec
# Checks for pending or skipped examples without reason.
#
# @example
# # bad
# pending 'does something' do
# end
#
# # bad
# it 'does something', :pending do
# end
#
# # bad
# it 'does something' do
# pending
# end
#
# # bad
# xdescribe 'something' do
# end
#
# # bad
# skip 'does something' do
# end
#
# # bad
# it 'does something', :skip do
# end
#
# # bad
# it 'does something' do
# skip
# end
#
# # bad
# it 'does something'
#
# # good
# it 'does something' do
# pending 'reason'
# end
#
# # good
# it 'does something' do
# skip 'reason'
# end
#
# # good
# it 'does something', pending: 'reason' do
# end
#
# # good
# it 'does something', skip: 'reason' do
# end
class PendingWithoutReason < Base
MSG = 'Give the reason for pending or skip.'
def on_send(node)
on_pending_by_metadata(node)
return unless (parent = parent_node(node))
if spec_group?(parent) || block_node_example_group?(node)
on_skipped_by_example_method(node)
on_skipped_by_example_group_method(node)
elsif example?(parent)
on_skipped_by_in_example_method(node)
end
end
private
# @!method skipped_in_example?(node)
def_node_matcher :skipped_in_example?, <<~PATTERN
{
(send nil? ${#Examples.skipped #Examples.pending})
(block (send nil? ${#Examples.skipped}) ...)
(numblock (send nil? ${#Examples.skipped}) ...)
}
PATTERN
# @!method skipped_by_example_method?(node)
def_node_matcher :skipped_by_example_method?, <<~PATTERN
(send nil? ${#Examples.skipped #Examples.pending})
PATTERN
# @!method skipped_by_example_method_with_block?(node)
def_node_matcher :skipped_by_example_method_with_block?, <<~PATTERN
({block numblock} (send nil? ${#Examples.skipped #Examples.pending} ...) ...)
PATTERN
# @!method metadata_without_reason?(node)
def_node_matcher :metadata_without_reason?, <<~PATTERN
(send #rspec?
{#ExampleGroups.all #Examples.all} ...
{
<(sym ${:pending :skip}) ...>
(hash <(pair (sym ${:pending :skip}) true) ...>)
}
)
PATTERN
# @!method skipped_by_example_group_method?(node)
def_node_matcher :skipped_by_example_group_method?, <<~PATTERN
(send #rspec? ${#ExampleGroups.skipped} ...)
PATTERN
# @!method pending_step_without_reason?(node)
def_node_matcher :pending_step_without_reason?, <<~PATTERN
(send nil? {:skip :pending})
PATTERN
def parent_node(node)
node_or_block = node.block_node || node
return unless (parent = node_or_block.parent)
parent.begin_type? && parent.parent ? parent.parent : parent
end
def block_node_example_group?(node)
node.block_node &&
example_group?(node.block_node) &&
explicit_rspec?(node.receiver)
end
def on_skipped_by_in_example_method(node)
skipped_in_example?(node) do |pending|
add_offense(node, message: "Give the reason for #{pending}.")
end
end
def on_pending_by_metadata(node)
metadata_without_reason?(node) do |pending|
add_offense(node, message: "Give the reason for #{pending}.")
end
end
def on_skipped_by_example_method(node)
skipped_by_example_method?(node) do |pending|
add_offense(node, message: "Give the reason for #{pending}.")
end
skipped_by_example_method_with_block?(node.parent) do |pending|
add_offense(node, message: "Give the reason for #{pending}.")
end
end
def on_skipped_by_example_group_method(node)
skipped_by_example_group_method?(node) do
add_offense(node, message: 'Give the reason for skip.')
end
end
end
end
end
end