Class: KnapsackPro::Adapters::RSpecAdapter

Inherits:
BaseAdapter
  • Object
show all
Defined in:
lib/knapsack_pro/adapters/rspec_adapter.rb

Direct Known Subclasses

RspecAdapter

Constant Summary collapse

TEST_DIR_PATTERN =
'spec/**{,/*/**}/*_spec.rb'
REGEX =
/\A(.*?)(?:\[([\d\s:,]+)\])?\z/.freeze

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from BaseAdapter

adapter_bind_method_called_file, bind, #bind, #bind_queue_mode, verify_bind_method_called

Class Method Details

.calculate_slow_id_pathsObject



23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/knapsack_pro/adapters/rspec_adapter.rb', line 23

def self.calculate_slow_id_paths
  # Shell out not to pollute the RSpec state
  cmd = [
    'RACK_ENV=test',
    'RAILS_ENV=test',
    KnapsackPro::Config::Env.rspec_test_example_detector_prefix,
    'rake knapsack_pro:rspec_test_example_detector',
  ].join(' ')
  raise "Failed to calculate Split by Test Examples: #{cmd}" unless Kernel.system(cmd)

  KnapsackPro::TestCaseDetectors::RSpecTestExampleDetector.new.slow_id_paths!
end

.concat_paths(test_files, id_paths) ⇒ Object



89
90
91
92
93
# File 'lib/knapsack_pro/adapters/rspec_adapter.rb', line 89

def self.concat_paths(test_files, id_paths)
  paths = KnapsackPro::TestFilePresenter.paths(test_files)
  file_paths = id_paths.map { |id_path| parse_file_path(id_path) }
  paths + id_paths - file_paths
end

.concat_test_files(test_files, id_paths) ⇒ Object



84
85
86
87
# File 'lib/knapsack_pro/adapters/rspec_adapter.rb', line 84

def self.concat_test_files(test_files, id_paths)
  paths = concat_paths(test_files, id_paths)
  KnapsackPro::TestFilePresenter.test_files(paths)
end

.file_path_for(example) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/knapsack_pro/adapters/rspec_adapter.rb', line 59

def self.file_path_for(example)
  [
    -> { parse_file_path(example.id) },
    -> { example.[:file_path] },
    -> { example.[:example_group][:file_path] },
    -> { top_level_group(example)[:file_path] },
  ]
    .each do |path|
      p = path.call
      return p if p.include?('_spec.rb') || p.include?('.feature')
    end

  return ''
end

.has_format_option?(cli_args) ⇒ Boolean

Returns:

  • (Boolean)


36
37
38
# File 'lib/knapsack_pro/adapters/rspec_adapter.rb', line 36

def self.has_format_option?(cli_args)
  !!parsed_options(cli_args)&.[](:formatters)
end

.has_require_rails_helper_option?(cli_args) ⇒ Boolean

Returns:

  • (Boolean)


40
41
42
# File 'lib/knapsack_pro/adapters/rspec_adapter.rb', line 40

def self.has_require_rails_helper_option?(cli_args)
  (parsed_options(cli_args)&.[](:requires) || []).include?("rails_helper")
end

.id_path?(path) ⇒ Boolean

Returns:

  • (Boolean)


79
80
81
82
# File 'lib/knapsack_pro/adapters/rspec_adapter.rb', line 79

def self.id_path?(path)
  _file, id = path.match(REGEX).captures
  !id.nil?
end

.order_option(cli_args) ⇒ Object



44
45
46
# File 'lib/knapsack_pro/adapters/rspec_adapter.rb', line 44

def self.order_option(cli_args)
  parsed_options(cli_args)&.[](:order)
end

.parse_file_path(path) ⇒ Object



74
75
76
77
# File 'lib/knapsack_pro/adapters/rspec_adapter.rb', line 74

def self.parse_file_path(path)
  file, _id = path.match(REGEX).captures
  file
end

.parsed_options(cli_args) ⇒ Object



187
188
189
190
191
# File 'lib/knapsack_pro/adapters/rspec_adapter.rb', line 187

def self.parsed_options(cli_args)
  ::RSpec::Core::Parser.parse(cli_args)
rescue SystemExit
  nil
end

.rails_helper_exists?(test_dir) ⇒ Boolean

Returns:

  • (Boolean)


95
96
97
# File 'lib/knapsack_pro/adapters/rspec_adapter.rb', line 95

def self.rails_helper_exists?(test_dir)
  File.exist?("#{test_dir}/rails_helper.rb")
end

.remove_formatters(cli_args) ⇒ Object



48
49
50
51
52
53
54
55
56
57
# File 'lib/knapsack_pro/adapters/rspec_adapter.rb', line 48

def self.remove_formatters(cli_args)
  formatter_options = ['-f', '--format', '-o', '--out']
  cli_args.dup.each_with_index do |arg, index|
    if formatter_options.include?(arg)
      cli_args[index] = nil
      cli_args[index + 1] = nil
    end
  end
  cli_args.compact
end

.rspec_configurationObject

Hide RSpec configuration so that we could mock it in the spec. Mocking existing RSpec configuration could impact test’s runtime.



179
180
181
# File 'lib/knapsack_pro/adapters/rspec_adapter.rb', line 179

def self.rspec_configuration
  ::RSpec.configuration
end

.scheduled_pathsObject



183
184
185
# File 'lib/knapsack_pro/adapters/rspec_adapter.rb', line 183

def self.scheduled_paths
  rspec_configuration.instance_variable_get(:@files_or_directories_to_run) || []
end

.split_by_test_cases_enabled?Boolean

Returns:

  • (Boolean)


12
13
14
15
16
17
18
19
20
21
# File 'lib/knapsack_pro/adapters/rspec_adapter.rb', line 12

def self.split_by_test_cases_enabled?
  return false unless KnapsackPro::Config::Env.rspec_split_by_test_examples?

  require 'rspec/core/version'
  unless Gem::Version.new(::RSpec::Core::Version::STRING) >= Gem::Version.new('3.3.0')
    raise "RSpec >= 3.3.0 is required to split test files by test examples. Learn more: #{KnapsackPro::Urls::SPLIT_BY_TEST_EXAMPLES}"
  end

  true
end

.top_level_group(example) ⇒ Object

private



100
101
102
103
104
105
106
# File 'lib/knapsack_pro/adapters/rspec_adapter.rb', line 100

def self.top_level_group(example)
  group = example.[:example_group]
  until group[:parent_example_group].nil?
    group = group[:parent_example_group]
  end
  group
end

Instance Method Details

#bind_after_queue_hookObject



167
168
169
170
171
172
173
# File 'lib/knapsack_pro/adapters/rspec_adapter.rb', line 167

def bind_after_queue_hook
  ::RSpec.configure do |config|
    config.after(:suite) do
      KnapsackPro::Hooks::Queue.call_after_queue
    end
  end
end

#bind_before_queue_hookObject



159
160
161
162
163
164
165
# File 'lib/knapsack_pro/adapters/rspec_adapter.rb', line 159

def bind_before_queue_hook
  ::RSpec.configure do |config|
    config.before(:suite) do
      KnapsackPro::Hooks::Queue.call_before_queue
    end
  end
end

#bind_regular_mode_time_trackerObject



139
140
141
142
143
144
145
146
147
148
# File 'lib/knapsack_pro/adapters/rspec_adapter.rb', line 139

def bind_regular_mode_time_tracker
  return unless KnapsackPro::Config::Env.regular_mode?

  ::RSpec.configure do |config|
    config.append_before(:suite) do
      time_tracker = KnapsackPro::Formatters::TimeTrackerFetcher.call
      time_tracker.schedule(KnapsackPro::Adapters::RSpecAdapter.scheduled_paths)
    end
  end
end

#bind_save_reportObject



150
151
152
153
154
155
156
157
# File 'lib/knapsack_pro/adapters/rspec_adapter.rb', line 150

def bind_save_report
  ::RSpec.configure do |config|
    config.after(:suite) do
      time_tracker = KnapsackPro::Formatters::TimeTrackerFetcher.call
      KnapsackPro::Report.save(time_tracker.batch)
    end
  end
end

#bind_time_trackerObject



108
109
110
111
112
# File 'lib/knapsack_pro/adapters/rspec_adapter.rb', line 108

def bind_time_tracker
  ensure_no_focus!
  bind_regular_mode_time_tracker
  log_tests_duration
end

#ensure_no_focus!Object



114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/knapsack_pro/adapters/rspec_adapter.rb', line 114

def ensure_no_focus!
  ::RSpec.configure do |config|
    config.around(:each) do |example|
      if example.[:focus] && KnapsackPro::Adapters::RSpecAdapter.rspec_configuration.filter.rules[:focus]
        file_path = KnapsackPro::Adapters::RSpecAdapter.file_path_for(example)
        file_path = KnapsackPro::TestFileCleaner.clean(file_path)

        raise "Knapsack Pro found an example tagged with focus in #{file_path}, please remove it. See more: #{KnapsackPro::Urls::RSPEC__SKIPS_TESTS}"
      end

      example.run
    end
  end
end

#log_tests_durationObject



129
130
131
132
133
134
135
136
137
# File 'lib/knapsack_pro/adapters/rspec_adapter.rb', line 129

def log_tests_duration
  ::RSpec.configure do |config|
    config.append_after(:suite) do
      time_tracker = KnapsackPro::Formatters::TimeTrackerFetcher.call
      formatted = KnapsackPro::Presenter.global_time(time_tracker.duration)
      KnapsackPro.logger.debug(formatted)
    end
  end
end