guard-rspec 4.0.4 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
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