Skip to content

Commit

Permalink
Add no sleep cop
Browse files Browse the repository at this point in the history
It checks that feature specs do not use `sleep` greater than 1 second.
  • Loading branch information
oliverguenther authored and cbliard committed Oct 18, 2024
1 parent 0311949 commit 6d55c22
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 0 deletions.
5 changes: 5 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ OpenProject/NoDoEndBlockWithRSpecCapybaraMatcherInExpect:
Enabled: true
VersionAdded: '0.1.0'

OpenProject/NoSleepInFeatureSpecs:
Description: 'Avoid using `sleep` greater than 1 second in feature specs.'
Enabled: true
VersionAdded: '0.1.0'

OpenProject/UseServiceResultFactoryMethods:
Description: 'Use ServiceResult factory methods instead of ServiceResult.new.'
Enabled: true
Expand Down
65 changes: 65 additions & 0 deletions lib/rubocop/cop/open_project/no_sleep_in_feature_specs.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# frozen_string_literal: true

module RuboCop
module Cop
module OpenProject
# Checks that feature specs do not use `sleep` greater than 1 second.
#
# Relying on `sleep` for synchronization reduces overall performance of
# the test suite. Consider using Capybara `have_*` matchers or
# rspec-wait `wait_for` method instead.
#
# @example
#
# # bad
# sleep 20
#
# # bad
# sleep 1.5
#
# # bad
# delay = 15
# sleep delay
#
# # good (use sparingly)
# sleep 1
#
# # good
# expect(page).not_to have_text("please wait")
#
# # good
# expect(page).to have_text("success")
#
# good
# wait_for { work_package.reload.subject }.to eq("Updated name")
class NoSleepInFeatureSpecs < Base
MSG = "Avoid using `sleep` greater than 1 second in feature specs. " \
"It will reduce overall performance of the test suite. " \
"Consider using Capybara `have_*` matchers or rspec-wait " \
"`wait_for` method instead."

def_node_matcher :sleep_call?, "(send nil? :sleep $...)"

def on_send(node)
return unless feature_spec?(processed_source)

sleep_call?(node) do |args|
add_offense(node, message: MSG) if sleeping_too_much?(args[0])
end
end

private

def sleeping_too_much?(arg)
return false if arg&.numeric_type? && arg.value.between?(0, 1)

true
end

def feature_spec?(source)
source.file_path.include?("_spec.rb") && source.file_path.include?("features/")
end
end
end
end
end
1 change: 1 addition & 0 deletions lib/rubocop/cop/open_project_cops.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
require_relative "open_project/add_preview_for_view_component"
require_relative "open_project/no_do_end_block_with_rspec_capybara_matcher_in_expect"
require_relative "open_project/use_service_result_factory_methods"
require_relative "open_project/no_sleep_in_feature_specs"
34 changes: 34 additions & 0 deletions spec/rubocop/cop/open_project/no_sleep_in_feature_specs_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::OpenProject::NoSleepInFeatureSpecs, :config do
let(:config) { RuboCop::Config.new }

it "registers an offense for sleeping more than 1 second in a feature spec" do
expect_offense(<<~RUBY, "spec/features/some_spec.rb")
sleep 1.5
^^^^^^^^^ OpenProject/NoSleepInFeatureSpecs: #{described_class::MSG}
sleep 20
^^^^^^^^ OpenProject/NoSleepInFeatureSpecs: #{described_class::MSG}
sleep
^^^^^ OpenProject/NoSleepInFeatureSpecs: #{described_class::MSG}
RUBY

expect_no_corrections
end

it "does not register an offense in non-feature specs" do
expect_no_offenses(<<~RUBY, "spec/some_spec.rb")
sleep 1.5
RUBY
end

it "registers an offense sleep is called with a non-numeric argument" do
expect_offense(<<~RUBY, "spec/features/some_spec.rb")
delay = 15
sleep delay
^^^^^^^^^^^ OpenProject/NoSleepInFeatureSpecs: #{described_class::MSG}
RUBY

expect_no_corrections
end
end

0 comments on commit 6d55c22

Please sign in to comment.