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.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/.travis.yml +5 -1
- data/README.md +12 -9
- data/lib/guard/rspec.rb +3 -6
- data/lib/guard/rspec/command.rb +6 -19
- data/lib/guard/rspec/deprecator.rb +13 -1
- data/lib/guard/rspec/formatter.rb +36 -0
- data/lib/guard/rspec/inspectors/base_inspector.rb +52 -0
- data/lib/guard/rspec/inspectors/factory.rb +24 -0
- data/lib/guard/rspec/inspectors/focused_inspector.rb +39 -0
- data/lib/guard/rspec/inspectors/keeping_inspector.rb +96 -0
- data/lib/guard/rspec/inspectors/simple_inspector.rb +21 -0
- data/lib/guard/rspec/notifier.rb +52 -0
- data/lib/guard/rspec/options.rb +34 -0
- data/lib/guard/rspec/runner.rb +40 -31
- data/lib/guard/rspec/version.rb +1 -1
- data/spec/lib/guard/rspec/command_spec.rb +2 -6
- data/spec/lib/guard/rspec/deprecator_spec.rb +21 -2
- data/spec/lib/guard/rspec/formatter_spec.rb +42 -0
- data/spec/lib/guard/rspec/inspectors/base_inspector_spec.rb +34 -0
- data/spec/lib/guard/rspec/inspectors/factory_spec.rb +40 -0
- data/spec/lib/guard/rspec/inspectors/focused_inspector_spec.rb +74 -0
- data/spec/lib/guard/rspec/inspectors/keeping_inspector_spec.rb +122 -0
- data/spec/lib/guard/rspec/inspectors/shared_examples.rb +59 -0
- data/spec/lib/guard/rspec/inspectors/simple_inspector_spec.rb +23 -0
- data/spec/lib/guard/rspec/notifier_spec.rb +69 -0
- data/spec/lib/guard/rspec/runner_spec.rb +75 -66
- data/spec/lib/guard/rspec_spec.rb +8 -3
- metadata +25 -12
- data/lib/guard/rspec/formatters/focuser.rb +0 -29
- data/lib/guard/rspec/formatters/notifier.rb +0 -48
- data/lib/guard/rspec/inspector.rb +0 -78
- data/spec/lib/guard/rspec/formatters/focuser_spec.rb +0 -25
- data/spec/lib/guard/rspec/formatters/notifier_spec.rb +0 -44
- 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
|
data/lib/guard/rspec/runner.rb
CHANGED
@@ -1,51 +1,56 @@
|
|
1
|
+
require 'guard/rspec/inspectors/factory'
|
1
2
|
require 'guard/rspec/command'
|
2
|
-
require 'guard/rspec/
|
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
|
-
|
12
|
-
|
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
|
-
|
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.
|
33
|
+
inspector.reload
|
39
34
|
end
|
40
35
|
|
41
36
|
private
|
42
37
|
|
43
|
-
def _run(
|
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
|
-
|
47
|
-
|
48
|
-
|
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
|
61
|
-
return
|
62
|
-
|
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
|
67
|
-
|
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
|
87
|
+
def _run_all_after_pass
|
78
88
|
return unless options[:all_after_pass]
|
79
|
-
run_all
|
89
|
+
run_all
|
80
90
|
end
|
81
|
-
|
82
91
|
end
|
83
92
|
end
|
84
93
|
end
|
data/lib/guard/rspec/version.rb
CHANGED
@@ -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
|
20
|
-
expect(command).to match %r{-r .*/lib/guard/rspec/
|
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
|
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
|
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
|