Skip to content

Commit

Permalink
Add a cop to check for consistent method usage in feature specs.
Browse files Browse the repository at this point in the history
  • Loading branch information
rspeicher authored and Darhazer committed Sep 8, 2017
1 parent c4b280c commit 7fe393a
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 8 deletions.
6 changes: 4 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Master (Unreleased)

* Add `RSpec/Capybara` namespace including the first cop for feature specs: `Capybara/FeatureMethods`. ([@rspeicher][])

## 1.16.0 (2017-09-06)

* Add `RSpec/FactoryGirl` namespace including the first cop for factories: `FactoryGirl/DynamicAttributeDefinedStatically`. ([@jonatas][])
Expand All @@ -26,7 +28,7 @@

## 1.15.0 (2017-03-24)

* Add `RSpec/DescribeSymbol` cop. ([@tsigo][])
* Add `RSpec/DescribeSymbol` cop. ([@rspeicher][])
* Fix error when `RSpec/OverwritingSetup` and `RSpec/ScatteredLet` analyzed empty example groups. ([@backus][])

## 1.14.0 (2017-03-24)
Expand Down Expand Up @@ -233,7 +235,7 @@
[@redross]: https://github.com/redross
[@cfabianski]: https://github.com/cfabianski
[@dgollahon]: https://github.com/dgollahon
[@tsigo]: https://github.com/tsigo
[@rspeicher]: https://github.com/rspeicher
[@jonatas]: https://github.com/jonatas
[@pocke]: https://github.com/pocke
[@bmorrall]: https:/github.com/bmorrall
2 changes: 1 addition & 1 deletion bin/build_config
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ require 'rubocop-rspec'
require 'rubocop/rspec/description_extractor'
require 'rubocop/rspec/config_formatter'

cops = '{*.rb,factory_girl/*.rb}'
cops = '{*.rb,capybara/*.rb,factory_girl/*.rb}'
glob = File.join(__dir__, '..', 'lib', 'rubocop', 'cop', 'rspec', cops)
YARD.parse(Dir[glob], [])

Expand Down
7 changes: 6 additions & 1 deletion config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,12 @@ RSpec/VoidExpect:
Enabled: true
StyleGuide: http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/VoidExpect

Capybara/FeatureMethods:
Description: Checks for consistent method usage in feature specs.
Enabled: true
StyleGuide: http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/Capybara/FeatureMethods

FactoryGirl/DynamicAttributeDefinedStatically:
Description: Prefer declaring dynamic attribute values in a block.
Enabled: true
StyleGuide: http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/FactoryGirl/DynamicAttributeDefinedStatically
StyleGuide: http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/FactoryGirl/DynamicAttributeDefinedStatically
2 changes: 2 additions & 0 deletions lib/rubocop-rspec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
require 'rubocop/rspec/hook'
require 'rubocop/cop/rspec/cop'
require 'rubocop/rspec/align_let_brace'
require 'rubocop/rspec/capybara'
require 'rubocop/rspec/factory_girl'

RuboCop::RSpec::Inject.defaults!
Expand All @@ -28,6 +29,7 @@
require 'rubocop/cop/rspec/around_block'
require 'rubocop/cop/rspec/be_eql'
require 'rubocop/cop/rspec/before_after_all'
require 'rubocop/cop/rspec/capybara/feature_methods'
require 'rubocop/cop/rspec/describe_class'
require 'rubocop/cop/rspec/describe_method'
require 'rubocop/cop/rspec/describe_symbol'
Expand Down
67 changes: 67 additions & 0 deletions lib/rubocop/cop/rspec/capybara/feature_methods.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# frozen_string_literal: true

module RuboCop
module Cop
module RSpec
module Capybara
# Checks for consistent method usage in feature specs.
#
# @example
# # bad
# feature 'User logs in' do
# given(:user) { User.new }
#
# background do
# visit new_session_path
# end
#
# scenario 'with OAuth' do
# # ...
# end
# end
#
# # good
# describe 'User logs in' do
# let(:user) { User.new }
#
# before do
# visit new_session_path
# end
#
# it 'with OAuth' do
# # ...
# end
# end
class FeatureMethods < Cop
MSG = 'Use `%s` instead of `%s`.'.freeze

# https://git.io/v7Kwr
MAP = {
background: :before,
scenario: :it,
xscenario: :xit,
given: :let,
given!: :let!,
feature: :describe
}.freeze

def_node_matcher :feature_method?, <<-PATTERN
(send {(const nil :RSpec) nil} ${:#{MAP.keys.join(' :')}} ...)
PATTERN

def on_send(node)
feature_method?(node) do |match|
add_offense(node, :selector, format(MSG, MAP[match], match))
end
end

def autocorrect(node)
lambda do |corrector|
corrector.replace(node.loc.selector, MAP[node.method_name].to_s)
end
end
end
end
end
end
end
7 changes: 7 additions & 0 deletions lib/rubocop/rspec/capybara.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module RuboCop
module RSpec
# RuboCop Capybara project namespace
module Capybara
end
end
end
6 changes: 3 additions & 3 deletions lib/rubocop/rspec/config_formatter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ module RuboCop
module RSpec
# Builds a YAML config file from two config hashes
class ConfigFormatter
NAMESPACES = /^(#{Regexp.union('RSpec', 'FactoryGirl')})/
STYLE_GUIDE_BASE_URL = 'http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/'.freeze
NAMESPACES = /^(#{Regexp.union('RSpec', 'Capybara', 'FactoryGirl')})/
STYLE_GUIDE_BASE_URL = 'http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/'.freeze

def initialize(config, descriptions)
@config = config
Expand All @@ -22,7 +22,7 @@ def unified_config
cops.each_with_object(config.dup) do |cop, unified|
unified[cop] = config.fetch(cop)
.merge(descriptions.fetch(cop))
.merge('StyleGuide' => STYLE_GUIDE_BASE_URL + cop)
.merge('StyleGuide' => STYLE_GUIDE_BASE_URL + cop.sub('RSpec/', ''))
end
end

Expand Down
3 changes: 2 additions & 1 deletion spec/project/default_config_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
let(:cop_names) do
namespaces = {
'rspec' => 'RSpec',
'capybara' => 'Capybara',
'factory_girl' => 'FactoryGirl'
}
glob = SpecHelper::ROOT.join('lib', 'rubocop', 'cop',
'rspec', '{,factory_girl/}*.rb')
'rspec', '{capybara/,,factory_girl/}*.rb')
cop_names =
Pathname.glob(glob).map do |file|
file_name = file.basename('.rb').to_s
Expand Down
52 changes: 52 additions & 0 deletions spec/rubocop/cop/rspec/capybara/feature_methods_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
RSpec.describe RuboCop::Cop::RSpec::Capybara::FeatureMethods do
subject(:cop) { described_class.new }

it 'flags violations for `background`' do
expect_offense(<<-RUBY)
background do; end
^^^^^^^^^^ Use `before` instead of `background`.
RUBY
end

it 'flags violations for `scenario`' do
expect_offense(<<-RUBY)
scenario 'Foo' do; end
^^^^^^^^ Use `it` instead of `scenario`.
RUBY
end

it 'flags violations for `xscenario`' do
expect_offense(<<-RUBY)
RSpec.xscenario 'Foo' do; end
^^^^^^^^^ Use `xit` instead of `xscenario`.
RUBY
end

it 'flags violations for `given`' do
expect_offense(<<-RUBY)
given(:foo) { :foo }
^^^^^ Use `let` instead of `given`.
RUBY
end

it 'flags violations for `given!`' do
expect_offense(<<-RUBY)
given!(:foo) { :foo }
^^^^^^ Use `let!` instead of `given!`.
RUBY
end

it 'flags violations for `feature`' do
expect_offense(<<-RUBY)
RSpec.feature 'Foo' do; end
^^^^^^^ Use `describe` instead of `feature`.
RUBY
end

include_examples 'autocorrect', 'background { }', 'before { }'
include_examples 'autocorrect', 'scenario { }', 'it { }'
include_examples 'autocorrect', 'xscenario { }', 'xit { }'
include_examples 'autocorrect', 'given(:foo) { }', 'let(:foo) { }'
include_examples 'autocorrect', 'given!(:foo) { }', 'let!(:foo) { }'
include_examples 'autocorrect', 'RSpec.feature { }', 'RSpec.describe { }'
end

0 comments on commit 7fe393a

Please sign in to comment.