guard-rspec 4.0.4 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.travis.yml +5 -1
  4. data/README.md +12 -9
  5. data/lib/guard/rspec.rb +3 -6
  6. data/lib/guard/rspec/command.rb +6 -19
  7. data/lib/guard/rspec/deprecator.rb +13 -1
  8. data/lib/guard/rspec/formatter.rb +36 -0
  9. data/lib/guard/rspec/inspectors/base_inspector.rb +52 -0
  10. data/lib/guard/rspec/inspectors/factory.rb +24 -0
  11. data/lib/guard/rspec/inspectors/focused_inspector.rb +39 -0
  12. data/lib/guard/rspec/inspectors/keeping_inspector.rb +96 -0
  13. data/lib/guard/rspec/inspectors/simple_inspector.rb +21 -0
  14. data/lib/guard/rspec/notifier.rb +52 -0
  15. data/lib/guard/rspec/options.rb +34 -0
  16. data/lib/guard/rspec/runner.rb +40 -31
  17. data/lib/guard/rspec/version.rb +1 -1
  18. data/spec/lib/guard/rspec/command_spec.rb +2 -6
  19. data/spec/lib/guard/rspec/deprecator_spec.rb +21 -2
  20. data/spec/lib/guard/rspec/formatter_spec.rb +42 -0
  21. data/spec/lib/guard/rspec/inspectors/base_inspector_spec.rb +34 -0
  22. data/spec/lib/guard/rspec/inspectors/factory_spec.rb +40 -0
  23. data/spec/lib/guard/rspec/inspectors/focused_inspector_spec.rb +74 -0
  24. data/spec/lib/guard/rspec/inspectors/keeping_inspector_spec.rb +122 -0
  25. data/spec/lib/guard/rspec/inspectors/shared_examples.rb +59 -0
  26. data/spec/lib/guard/rspec/inspectors/simple_inspector_spec.rb +23 -0
  27. data/spec/lib/guard/rspec/notifier_spec.rb +69 -0
  28. data/spec/lib/guard/rspec/runner_spec.rb +75 -66
  29. data/spec/lib/guard/rspec_spec.rb +8 -3
  30. metadata +25 -12
  31. data/lib/guard/rspec/formatters/focuser.rb +0 -29
  32. data/lib/guard/rspec/formatters/notifier.rb +0 -48
  33. data/lib/guard/rspec/inspector.rb +0 -78
  34. data/spec/lib/guard/rspec/formatters/focuser_spec.rb +0 -25
  35. data/spec/lib/guard/rspec/formatters/notifier_spec.rb +0 -44
  36. data/spec/lib/guard/rspec/inspector_spec.rb +0 -107
@@ -0,0 +1,52 @@
1
+ module Guard
2
+ class RSpec
3
+ class Notifier
4
+ attr_accessor :options
5
+
6
+ def initialize(options = {})
7
+ @options = options
8
+ end
9
+
10
+ TITLE = 'RSpec results'
11
+
12
+ def notify(summary)
13
+ return unless options[:notification]
14
+ failure_count, pending_count = _parse_summary(summary)
15
+ image = _image(failure_count, pending_count)
16
+ priority = _priority(image)
17
+ ::Guard::Notifier.notify(summary, title: TITLE, image: image, priority: priority)
18
+ end
19
+
20
+ def notify_failure
21
+ return unless options[:notification]
22
+ ::Guard::Notifier.notify('Failed', title: TITLE, image: :failed, priority: 2)
23
+ end
24
+
25
+ private
26
+
27
+ def _parse_summary(summary)
28
+ summary.match(/(\d+) failures( \((\d+) pending\))?/) do |m|
29
+ return m[1].to_i, m[3].to_i
30
+ end
31
+ [0, 0]
32
+ end
33
+
34
+ def _image(failure_count, pending_count)
35
+ if failure_count > 0
36
+ :failed
37
+ elsif pending_count > 0
38
+ :pending
39
+ else
40
+ :success
41
+ end
42
+ end
43
+
44
+ def _priority(image)
45
+ { failed: 2,
46
+ pending: -1,
47
+ success: -2
48
+ }[image]
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,34 @@
1
+ module Guard
2
+ class RSpec
3
+ module Options
4
+ DEFAULTS = {
5
+ all_on_start: false,
6
+ all_after_pass: false,
7
+ run_all: { message: 'Running all specs' },
8
+ failed_mode: :focus, # :keep and :none are other posibilities
9
+ spec_paths: %w[spec],
10
+ cmd: 'rspec',
11
+ launchy: nil,
12
+ notification: true
13
+ }.freeze
14
+
15
+ class << self
16
+ def with_defaults(options = {})
17
+ _deep_merge(DEFAULTS, options).freeze
18
+ end
19
+
20
+ private
21
+
22
+ def _deep_merge(hash1, hash2)
23
+ hash1.merge(hash2) do |key, oldval, newval|
24
+ if oldval.instance_of?(Hash) && newval.instance_of?(Hash)
25
+ _deep_merge(oldval, newval)
26
+ else
27
+ newval
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -1,51 +1,56 @@
1
+ require 'guard/rspec/inspectors/factory'
1
2
  require 'guard/rspec/command'
2
- require 'guard/rspec/inspector'
3
+ require 'guard/rspec/formatter'
4
+ require 'guard/rspec/notifier'
3
5
 
4
6
  module Guard
5
7
  class RSpec
6
8
  class Runner
7
- attr_accessor :options, :inspector
9
+ attr_accessor :options, :inspector, :notifier
8
10
 
9
11
  def initialize(options = {})
10
- @options = {
11
- all_after_pass: false,
12
- notification: true,
13
- run_all: { message: 'Running all specs' },
14
- launchy: nil
15
- }.merge(options)
16
-
17
- @inspector = Inspector.new(@options)
12
+ @options = options
13
+ @inspector = Inspectors::Factory.create(@options)
14
+ @notifier = Notifier.new(@options)
18
15
  end
19
16
 
20
17
  def run_all
21
- options = @options.merge(@options[:run_all])
18
+ paths = options[:spec_paths]
19
+ options = @options.merge(@options[:run_all]).freeze
20
+ return if paths.empty?
22
21
  ::Guard::UI.info(options[:message], reset: true)
23
-
24
- _run(inspector.paths, [], options)
22
+ _run(true, paths, options)
25
23
  end
26
24
 
27
25
  def run(paths)
28
- failed_paths = inspector.failed_paths
29
26
  paths = inspector.paths(paths)
30
27
  return if paths.empty?
31
-
32
28
  ::Guard::UI.info("Running: #{paths.join(' ')}", reset: true)
33
-
34
- _run(paths, failed_paths, options)
29
+ _run(false, paths, options)
35
30
  end
36
31
 
37
32
  def reload
38
- inspector.clear_paths
33
+ inspector.reload
39
34
  end
40
35
 
41
36
  private
42
37
 
43
- def _run(paths, failed_paths, options)
38
+ def _run(all, paths, options)
44
39
  command = Command.new(paths, options)
45
40
  _without_bundler_env { Kernel.system(command) }.tap do |success|
46
- success ? inspector.clear_paths(paths) : _notify_failure
47
- _open_launchy
48
- _run_all_after_pass(success, failed_paths)
41
+ if _command_success?(success)
42
+ summary, failed_paths = _command_output
43
+ if summary && failed_paths
44
+ inspector.failed(failed_paths)
45
+ notifier.notify(summary)
46
+ _open_launchy
47
+ _run_all_after_pass if !all && success
48
+ else
49
+ notifier.notify_failure
50
+ end
51
+ else
52
+ notifier.notify_failure
53
+ end
49
54
  end
50
55
  end
51
56
 
@@ -57,14 +62,19 @@ module Guard
57
62
  end
58
63
  end
59
64
 
60
- def _notify_failure
61
- return unless options[:notification]
62
- return unless command_exception?
63
- ::Guard::Notifier.notify('Failed', title: 'RSpec results', image: :failed, priority: 2)
65
+ def _command_success?(success)
66
+ return false if success.nil?
67
+ [Command::FAILURE_EXIT_CODE, 0].include?($?.exitstatus)
64
68
  end
65
69
 
66
- def command_exception?
67
- $?.exitstatus != Command::FAILURE_EXIT_CODE
70
+ def _command_output
71
+ formatter_tmp_file = Formatter::TEMPORARY_FILE_PATH
72
+ lines = File.readlines(formatter_tmp_file)
73
+ [lines.first.strip, lines[1..11].map(&:strip).compact]
74
+ rescue
75
+ [nil, nil]
76
+ ensure
77
+ File.exist?(formatter_tmp_file) && File.delete(formatter_tmp_file)
68
78
  end
69
79
 
70
80
  def _open_launchy
@@ -74,11 +84,10 @@ module Guard
74
84
  ::Launchy.open(options[:launchy]) if pn.exist?
75
85
  end
76
86
 
77
- def _run_all_after_pass(success, failed_paths)
87
+ def _run_all_after_pass
78
88
  return unless options[:all_after_pass]
79
- run_all if success && !failed_paths.empty?
89
+ run_all
80
90
  end
81
-
82
91
  end
83
92
  end
84
93
  end
@@ -1,5 +1,5 @@
1
1
  module Guard
2
2
  module RSpecVersion
3
- VERSION = '4.0.4'
3
+ VERSION = '4.1.0'
4
4
  end
5
5
  end
@@ -16,12 +16,8 @@ describe Guard::RSpec::Command do
16
16
  expect(command).to match /--failure-exit-code 2/
17
17
  end
18
18
 
19
- it "sets notifier formatter" do
20
- expect(command).to match %r{-r .*/lib/guard/rspec/formatters/notifier.rb -f Guard::RSpec::Formatters::Notifier}
21
- end
22
-
23
- it "sets focuser formatter" do
24
- expect(command).to match %r{-r .*/lib/guard/rspec/formatters/focuser.rb -f Guard::RSpec::Formatters::Focuser}
19
+ it "sets formatter" do
20
+ expect(command).to match %r{-r .*/lib/guard/rspec/formatter.rb -f Guard::RSpec::Formatter}
25
21
  end
26
22
 
27
23
  context "with custom cmd" do
@@ -10,13 +10,13 @@ describe Guard::RSpec::Deprecator do
10
10
  it "shows warning if SPEC_OPTS is set" do
11
11
  ENV['SPEC_OPTS'] = '-f p'
12
12
  expect(Guard::UI).to receive(:warning).with(
13
- 'The SPEC_OPTS environment variable is present. This can conflict with guard-rspec, particularly notifications.')
13
+ 'The SPEC_OPTS environment variable is present. This can conflict with guard-rspec.')
14
14
  deprecator.warns_about_deprecated_options
15
15
  ENV['SPEC_OPTS'] = nil # otherwise other specs pick it up and fail
16
16
  end
17
17
  it "does not show warning if SPEC_OPTS is unset" do
18
18
  expect(Guard::UI).to_not receive(:warning).with(
19
- 'The SPEC_OPTS environment variable is present. This can conflict with guard-rspec, particularly notifications.')
19
+ 'The SPEC_OPTS environment variable is present. This can conflict with guard-rspec.')
20
20
  deprecator.warns_about_deprecated_options
21
21
  end
22
22
  end
@@ -53,5 +53,24 @@ describe Guard::RSpec::Deprecator do
53
53
  end
54
54
  end
55
55
 
56
+ describe 'with keep_failed option' do
57
+ let(:options) { { keep_failed: true } }
58
+
59
+ it 'shows deprecation warning' do
60
+ expect(Guard::UI).to receive(:warning).with(
61
+ 'Guard::RSpec DEPRECATION WARNING: The :keep_failed option is deprecated. Please set new :failed_mode option value to :keep instead. https://github.com/guard/guard-rspec#list-of-available-options')
62
+ deprecator.warns_about_deprecated_options
63
+ end
64
+ end
65
+
66
+ describe 'with focus_on_failed option' do
67
+ let(:options) { { focus_on_failed: true } }
68
+
69
+ it 'shows deprecation warning' do
70
+ expect(Guard::UI).to receive(:warning).with(
71
+ 'Guard::RSpec DEPRECATION WARNING: The :focus_on_failed option is deprecated. Focus mode is the default and can be changed using new :failed_mode option. https://github.com/guard/guard-rspec#list-of-available-options')
72
+ deprecator.warns_about_deprecated_options
73
+ end
74
+ end
56
75
  end
57
76
  end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper.rb'
2
+
3
+ require 'guard/rspec/formatter'
4
+
5
+ describe Guard::RSpec::Formatter do
6
+ let(:formatter) { Guard::RSpec::Formatter.new(StringIO.new) }
7
+
8
+ describe '#dump_summary' do
9
+ after { File.delete('./tmp/rspec_guard_result') }
10
+
11
+ context 'with failures' do
12
+ let(:example) { double(
13
+ execution_result: { status: 'failed' },
14
+ metadata: { location: 'failed_location' }
15
+ ) }
16
+
17
+ it 'writes summary line and failed location in tmp dir' do
18
+ formatter.stub(:examples) { [example] }
19
+ formatter.dump_summary(123, 3, 1, 0)
20
+ result = File.open('./tmp/rspec_guard_result').read
21
+ expect(result).to match /^3 examples, 1 failures in 123\.0 seconds\nfailed_location\n$/
22
+ end
23
+ end
24
+
25
+ context 'with only success' do
26
+ it 'notifies success' do
27
+ formatter.dump_summary(123, 3, 0, 0)
28
+ result = File.open('./tmp/rspec_guard_result').read
29
+ expect(result).to match /^3 examples, 0 failures in 123\.0 seconds\n$/
30
+ end
31
+ end
32
+
33
+ context 'with pending' do
34
+ it "notifies pending too" do
35
+ formatter.dump_summary(123, 3, 0, 1)
36
+ result = File.open('./tmp/rspec_guard_result').read
37
+ expect(result).to match /^3 examples, 0 failures \(1 pending\) in 123\.0 seconds\n$/
38
+ end
39
+ end
40
+ end
41
+
42
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe Guard::RSpec::Inspectors::BaseInspector do
4
+ let(:options) { { custom: 'value', spec_paths: %w[myspec] } }
5
+ let(:inspector) { Guard::RSpec::Inspectors::BaseInspector.new(options) }
6
+ let(:paths) { %w[spec/foo_spec.rb spec/bar_spec.rb] }
7
+ let(:abstract_error) { 'Must be implemented in subclass' }
8
+
9
+ describe '.initialize' do
10
+ it 'sets options and spec_paths' do
11
+ expect(inspector.options).to include(:custom, :spec_paths)
12
+ expect(inspector.options[:custom]).to eq('value')
13
+ expect(inspector.spec_paths).to eq(%w[myspec])
14
+ end
15
+ end
16
+
17
+ describe '#paths' do
18
+ it 'should not be emplemented here' do
19
+ expect { inspector.paths(paths) }.to raise_error(abstract_error)
20
+ end
21
+ end
22
+
23
+ describe '#failed' do
24
+ it 'should not be emplemented here' do
25
+ expect { inspector.failed(paths) }.to raise_error(abstract_error)
26
+ end
27
+ end
28
+
29
+ describe '#reload' do
30
+ it 'should not be emplemented here' do
31
+ expect { inspector.reload }.to raise_error(abstract_error)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ describe Guard::RSpec::Inspectors::Factory do
4
+ let(:factory) { Guard::RSpec::Inspectors::Factory }
5
+ let(:options) { {} }
6
+
7
+ it 'can not be instantiated' do
8
+ expect { factory.new(options) }.to raise_error(NoMethodError)
9
+ end
10
+
11
+ context 'with :focus failed mode and custom options' do
12
+ let(:options) { { failed_mode: :focus, custom: 'value' } }
13
+
14
+ it 'creates FocusedInspector instance with custom options' do
15
+ inspector = factory.create(options)
16
+ expect(inspector).to be_an_instance_of(Guard::RSpec::Inspectors::FocusedInspector)
17
+ expect(inspector.options).to eq(options)
18
+ end
19
+ end
20
+
21
+ context 'with :keep failed mode and custom options' do
22
+ let(:options) { { failed_mode: :keep, custom: 'value' } }
23
+
24
+ it 'creates KeepingInspector instance with custom options' do
25
+ inspector = factory.create(options)
26
+ expect(inspector).to be_an_instance_of(Guard::RSpec::Inspectors::KeepingInspector)
27
+ expect(inspector.options).to eq(options)
28
+ end
29
+ end
30
+
31
+ context 'with :none failed mode and custom options' do
32
+ let(:options) { { failed_mode: :none, custom: 'value' } }
33
+
34
+ it 'creates SimpleInspector instance with custom options' do
35
+ inspector = factory.create(options)
36
+ expect(inspector).to be_an_instance_of(Guard::RSpec::Inspectors::SimpleInspector)
37
+ expect(inspector.options).to eq(options)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+ require 'lib/guard/rspec/inspectors/shared_examples'
3
+
4
+ klass = Guard::RSpec::Inspectors::FocusedInspector
5
+
6
+ describe klass do
7
+ include_examples 'inspector', klass
8
+
9
+ # Use real paths because BaseInspector#_clean will be used to clean them
10
+ let(:other_paths) { [
11
+ 'spec/lib/guard/rspec/inspectors/simple_inspector_spec.rb',
12
+ 'spec/lib/guard/rspec/runner_spec.rb'
13
+ ] }
14
+ let(:other_failed_locations) { %w[./spec/lib/guard/rspec/deprecator_spec.rb:446] }
15
+
16
+ it 'remembers failed paths and returns them until they all pass' do
17
+ expect(inspector.paths(paths)).to match_array(paths)
18
+ inspector.failed(failed_locations)
19
+
20
+ # Return failed_locations until they pass
21
+ 3.times do
22
+ expect(inspector.paths(other_paths)).to match_array(failed_locations)
23
+ inspector.failed(other_failed_locations)
24
+
25
+ expect(inspector.paths(paths)).to match_array(failed_locations)
26
+ inspector.failed(other_failed_locations)
27
+
28
+ expect(inspector.paths([])).to match_array(failed_locations)
29
+ inspector.failed(failed_locations)
30
+ end
31
+
32
+ # Now all pass
33
+ expect(inspector.paths(paths)).to match_array(failed_locations)
34
+ inspector.failed([])
35
+
36
+ # And some fails again
37
+ expect(inspector.paths(other_paths)).to match_array(other_paths)
38
+ inspector.failed(other_failed_locations)
39
+
40
+ # Return other_failed_locations until they pass
41
+ 3.times do
42
+ expect(inspector.paths(other_paths)).to match_array(other_failed_locations)
43
+ inspector.failed(other_failed_locations)
44
+
45
+ expect(inspector.paths(paths)).to match_array(other_failed_locations)
46
+ inspector.failed(other_failed_locations)
47
+
48
+ expect(inspector.paths([])).to match_array(other_failed_locations)
49
+ inspector.failed(failed_locations)
50
+ end
51
+
52
+ # Now all pass
53
+ expect(inspector.paths(paths)).to match_array(other_failed_locations)
54
+ inspector.failed([])
55
+
56
+ expect(inspector.paths(paths)).to match_array(paths)
57
+ inspector.failed([])
58
+
59
+ expect(inspector.paths(other_paths)).to match_array(other_paths)
60
+ inspector.failed([])
61
+
62
+ expect(inspector.paths([])).to eq([])
63
+ end
64
+
65
+ describe '#reload' do
66
+ it 'force to forget about focused locations' do
67
+ expect(inspector.paths(paths)).to match_array(paths)
68
+ inspector.failed(failed_locations)
69
+
70
+ inspector.reload
71
+ expect(inspector.paths(other_paths)).to match_array(other_paths)
72
+ end
73
+ end
74
+ end