guard-rspec 2.1.0 → 4.7.3
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.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.hound.yml +3 -0
- data/.rspec +2 -0
- data/.rubocop.yml +5 -0
- data/.rubocop_todo.yml +40 -0
- data/.travis.yml +14 -0
- data/CONTRIBUTING.md +38 -0
- data/Gemfile +25 -0
- data/Guardfile +28 -0
- data/{LICENSE → LICENSE.txt} +4 -2
- data/README.md +99 -114
- data/Rakefile +38 -0
- data/gemfiles/Gemfile.rspec-2.99 +6 -0
- data/gemfiles/Gemfile.rspec-3.4 +6 -0
- data/gemfiles/common +9 -0
- data/guard-rspec.gemspec +25 -0
- data/lib/guard/rspec/command.rb +71 -0
- data/lib/guard/rspec/deprecator.rb +86 -0
- data/lib/guard/rspec/dsl.rb +72 -0
- data/lib/guard/rspec/inspectors/base_inspector.rb +73 -0
- data/lib/guard/rspec/inspectors/factory.rb +23 -0
- data/lib/guard/rspec/inspectors/focused_inspector.rb +39 -0
- data/lib/guard/rspec/inspectors/keeping_inspector.rb +97 -0
- data/lib/guard/rspec/inspectors/simple_inspector.rb +21 -0
- data/lib/guard/rspec/notifier.rb +55 -0
- data/lib/guard/rspec/options.rb +37 -0
- data/lib/guard/rspec/results.rb +23 -0
- data/lib/guard/rspec/rspec_process.rb +93 -0
- data/lib/guard/rspec/runner.rb +71 -174
- data/lib/guard/rspec/templates/Guardfile +49 -17
- data/lib/guard/rspec/version.rb +1 -1
- data/lib/guard/rspec.rb +30 -59
- data/lib/guard/rspec_defaults.rb +5 -0
- data/lib/guard/rspec_formatter.rb +147 -0
- data/lib/guard/rspec_formatter_results_path.rb +29 -0
- data/spec/acceptance/fixtures/succeeding_spec.rb +4 -0
- data/spec/acceptance/formatter_spec.rb +46 -0
- data/spec/lib/guard/rspec/command_spec.rb +95 -0
- data/spec/lib/guard/rspec/deprecator_spec.rb +101 -0
- data/spec/lib/guard/rspec/inspectors/base_inspector_spec.rb +144 -0
- data/spec/lib/guard/rspec/inspectors/factory_spec.rb +45 -0
- data/spec/lib/guard/rspec/inspectors/focused_inspector_spec.rb +140 -0
- data/spec/lib/guard/rspec/inspectors/keeping_inspector_spec.rb +200 -0
- data/spec/lib/guard/rspec/inspectors/shared_examples.rb +121 -0
- data/spec/lib/guard/rspec/inspectors/simple_inspector_spec.rb +59 -0
- data/spec/lib/guard/rspec/notifier_spec.rb +90 -0
- data/spec/lib/guard/rspec/results_spec.rb +66 -0
- data/spec/lib/guard/rspec/rspec_process_spec.rb +152 -0
- data/spec/lib/guard/rspec/runner_spec.rb +372 -0
- data/spec/lib/guard/rspec/template_spec.rb +78 -0
- data/spec/lib/guard/rspec_formatter_spec.rb +277 -0
- data/spec/lib/guard/rspec_spec.rb +91 -0
- data/spec/spec_helper.rb +145 -0
- metadata +103 -42
- data/lib/guard/rspec/formatter.rb +0 -56
- data/lib/guard/rspec/inspector.rb +0 -72
@@ -0,0 +1,71 @@
|
|
1
|
+
require "rspec/core"
|
2
|
+
require "pathname"
|
3
|
+
|
4
|
+
require "guard/rspec"
|
5
|
+
|
6
|
+
module Guard
|
7
|
+
class RSpec < Plugin
|
8
|
+
class Command < String
|
9
|
+
FAILURE_EXIT_CODE = 2
|
10
|
+
|
11
|
+
attr_accessor :paths, :options
|
12
|
+
|
13
|
+
def initialize(paths, options = {})
|
14
|
+
@paths = paths
|
15
|
+
@options = options
|
16
|
+
super(_parts.join(" "))
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def _parts
|
22
|
+
parts = [options[:cmd]]
|
23
|
+
parts << _visual_formatter
|
24
|
+
parts << _guard_formatter
|
25
|
+
parts << "--failure-exit-code #{FAILURE_EXIT_CODE}"
|
26
|
+
parts << options[:cmd_additional_args] || ""
|
27
|
+
|
28
|
+
parts << _paths(options).join(" ")
|
29
|
+
end
|
30
|
+
|
31
|
+
def _paths(options)
|
32
|
+
chdir = options[:chdir]
|
33
|
+
return paths unless chdir
|
34
|
+
paths.map { |path| path.sub(File.join(chdir, "/"), "") }
|
35
|
+
end
|
36
|
+
|
37
|
+
def _visual_formatter
|
38
|
+
return if _cmd_include_formatter?
|
39
|
+
_rspec_formatters || "-f progress"
|
40
|
+
end
|
41
|
+
|
42
|
+
def _rspec_formatters
|
43
|
+
# RSpec::Core::ConfigurationOptions#parse_options method was renamed to
|
44
|
+
# #options in rspec-core v3.0.0.beta2 so call the first one if
|
45
|
+
# available. Fixes #249
|
46
|
+
config = ::RSpec::Core::ConfigurationOptions.new([])
|
47
|
+
config.parse_options if config.respond_to?(:parse_options)
|
48
|
+
formatters = config.options[:formatters] || nil
|
49
|
+
|
50
|
+
# RSpec's parser returns an array in the format
|
51
|
+
#
|
52
|
+
# [[formatter, output], ...],
|
53
|
+
#
|
54
|
+
# so match their format Construct a matching command line option,
|
55
|
+
# including output target
|
56
|
+
|
57
|
+
return formatters unless formatters
|
58
|
+
formatters.map { |entries| "-f #{entries.join ' -o '}" }.join(" ")
|
59
|
+
end
|
60
|
+
|
61
|
+
def _cmd_include_formatter?
|
62
|
+
options[:cmd] =~ /(?:^|\s)(?:-f\s*|--format(?:=|\s+))([\w:]+)/
|
63
|
+
end
|
64
|
+
|
65
|
+
def _guard_formatter
|
66
|
+
dir = Pathname.new(__FILE__).dirname.dirname
|
67
|
+
"-r #{dir + 'rspec_formatter.rb'} -f Guard::RSpecFormatter"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Guard
|
2
|
+
class RSpec < Plugin
|
3
|
+
class Deprecator
|
4
|
+
attr_accessor :options
|
5
|
+
|
6
|
+
def self.warns_about_deprecated_options(options = {})
|
7
|
+
new(options).warns_about_deprecated_options
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(options = {})
|
11
|
+
@options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
def warns_about_deprecated_options
|
15
|
+
_spec_opts_env
|
16
|
+
_version_option
|
17
|
+
_exclude_option
|
18
|
+
_use_cmd_option
|
19
|
+
_keep_failed_option
|
20
|
+
_focus_on_failed_option
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def _spec_opts_env
|
26
|
+
return if ENV["SPEC_OPTS"].nil?
|
27
|
+
Compat::UI.warning(
|
28
|
+
"The SPEC_OPTS environment variable is present." \
|
29
|
+
" This can conflict with guard-rspec."
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
def _version_option
|
34
|
+
return unless options.key?(:version)
|
35
|
+
_deprecated(
|
36
|
+
"The :version option is deprecated." \
|
37
|
+
" Only RSpec ~> 2.14 is now supported."
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
def _exclude_option
|
42
|
+
return unless options.key?(:exclude)
|
43
|
+
_deprecated(
|
44
|
+
"The :exclude option is deprecated." \
|
45
|
+
" Please Guard ignore method instead." \
|
46
|
+
" https://github.com/guard/guard#ignore"
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
def _use_cmd_option
|
51
|
+
%w(color drb fail_fast formatter env bundler
|
52
|
+
binstubs rvm cli spring turnip zeus foreman).each do |option|
|
53
|
+
next unless options.key?(option.to_sym)
|
54
|
+
_deprecated(
|
55
|
+
"The :#{option} option is deprecated." \
|
56
|
+
" Please customize the new :cmd option to fit your need."
|
57
|
+
)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def _keep_failed_option
|
62
|
+
return unless options.key?(:keep_failed)
|
63
|
+
_deprecated(
|
64
|
+
"The :keep_failed option is deprecated." \
|
65
|
+
" Please set new :failed_mode option value to" \
|
66
|
+
" :keep instead." \
|
67
|
+
" https://github.com/guard/guard-rspec#list-of-available-options"
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
def _focus_on_failed_option
|
72
|
+
return unless options.key?(:focus_on_failed)
|
73
|
+
_deprecated(
|
74
|
+
"The :focus_on_failed option is deprecated." \
|
75
|
+
" Please set new :failed_mode option value to" \
|
76
|
+
" :focus instead." \
|
77
|
+
" https://github.com/guard/guard-rspec#list-of-available-options"
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
def _deprecated(message)
|
82
|
+
Compat::UI.warning %(Guard::RSpec DEPRECATION WARNING: #{message})
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require "ostruct"
|
2
|
+
|
3
|
+
require "guard/rspec"
|
4
|
+
|
5
|
+
module Guard
|
6
|
+
class RSpec < Plugin
|
7
|
+
class Dsl
|
8
|
+
def initialize(dsl)
|
9
|
+
@dsl = dsl
|
10
|
+
end
|
11
|
+
|
12
|
+
def watch_spec_files_for(expr)
|
13
|
+
@dsl.send(:watch, expr) { |m| rspec.spec.call(m[1]) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.detect_spec_file_for(rspec, file)
|
17
|
+
# TODO: when spec not found ... run specs in topmost found path?
|
18
|
+
# Or show warning?
|
19
|
+
|
20
|
+
path = "#{rspec.spec_dir}/#{file}_spec.rb"
|
21
|
+
return path unless file.start_with?("lib/")
|
22
|
+
return path if Dir.exist?("#{rspec.spec_dir}/lib")
|
23
|
+
|
24
|
+
without_lib = file.sub(%r{^lib/}, "")
|
25
|
+
"#{rspec.spec_dir}/#{without_lib}_spec.rb"
|
26
|
+
end
|
27
|
+
|
28
|
+
def rspec
|
29
|
+
@rspec ||= OpenStruct.new(to_s: "spec").tap do |rspec|
|
30
|
+
rspec.spec_dir = "spec"
|
31
|
+
rspec.spec = ->(m) { Dsl.detect_spec_file_for(rspec, m) }
|
32
|
+
rspec.spec_helper = "#{rspec.spec_dir}/spec_helper.rb"
|
33
|
+
rspec.spec_files = %r{^#{rspec.spec_dir}/.+_spec\.rb$}
|
34
|
+
rspec.spec_support = %r{^#{rspec.spec_dir}/support/(.+)\.rb$}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def ruby
|
39
|
+
# Ruby apps
|
40
|
+
@ruby ||= OpenStruct.new.tap do |ruby|
|
41
|
+
ruby.lib_files = %r{^(lib/.+)\.rb$}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def rails(options = {})
|
46
|
+
# Rails example
|
47
|
+
@rails ||= _build_rails_rules(_view_extensions(options) * "|")
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def _view_extensions(options)
|
53
|
+
options.dup.delete(:view_extensions) || %w(erb haml slim)
|
54
|
+
end
|
55
|
+
|
56
|
+
def _build_rails_rules(exts)
|
57
|
+
OpenStruct.new.tap do |rails|
|
58
|
+
rails.app_files = %r{^app/(.+)\.rb$}
|
59
|
+
|
60
|
+
rails.views = %r{^app/(views/.+/[^/]*\.(?:#{exts}))$}
|
61
|
+
rails.view_dirs = %r{^app/views/(.+)/[^/]*\.(?:#{exts})$}
|
62
|
+
rails.layouts = %r{^app/layouts/(.+)/[^/]*\.(?:#{exts})$}
|
63
|
+
|
64
|
+
rails.controllers = %r{^app/controllers/(.+)_controller\.rb$}
|
65
|
+
rails.routes = "config/routes.rb"
|
66
|
+
rails.app_controller = "app/controllers/application_controller.rb"
|
67
|
+
rails.spec_helper = "#{rspec.spec_dir}/rails_helper.rb"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module Guard
|
2
|
+
class RSpec < Plugin
|
3
|
+
module Inspectors
|
4
|
+
class BaseInspector
|
5
|
+
attr_accessor :options, :spec_paths
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
@options = options
|
9
|
+
@spec_paths = @options[:spec_paths]
|
10
|
+
@chdir = @options[:chdir]
|
11
|
+
end
|
12
|
+
|
13
|
+
def paths(_paths)
|
14
|
+
raise NotImplementedError
|
15
|
+
end
|
16
|
+
|
17
|
+
def failed(_locations)
|
18
|
+
raise NotImplementedError
|
19
|
+
end
|
20
|
+
|
21
|
+
def reload
|
22
|
+
raise NotImplementedError
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
# Leave only spec/feature files from spec_paths, remove others
|
28
|
+
def _clean(paths)
|
29
|
+
paths.uniq!
|
30
|
+
paths.compact!
|
31
|
+
spec_dirs = _select_only_spec_dirs(paths)
|
32
|
+
spec_files = _select_only_spec_files(paths)
|
33
|
+
(spec_dirs + spec_files).uniq
|
34
|
+
end
|
35
|
+
|
36
|
+
def _select_only_spec_dirs(paths)
|
37
|
+
chdir_paths = _spec_paths_with_chdir
|
38
|
+
paths.select do |path|
|
39
|
+
File.directory?(path) || chdir_paths.include?(path)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def _select_only_spec_files(paths)
|
44
|
+
spec_files = _collect_files("*[_.]spec.rb")
|
45
|
+
feature_files = _collect_files("*.feature")
|
46
|
+
files = (spec_files + feature_files).flatten
|
47
|
+
|
48
|
+
paths.select do |path|
|
49
|
+
(files & [@chdir ? File.join(@chdir, path) : path]).any?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def _spec_paths_with_chdir
|
54
|
+
_paths_with_chdir(spec_paths, @chdir)
|
55
|
+
end
|
56
|
+
|
57
|
+
def _collect_files(pattern)
|
58
|
+
base_paths = _spec_paths_with_chdir
|
59
|
+
base_paths.map do |path|
|
60
|
+
# TODO: not tested properly
|
61
|
+
Dir[File.join(path, "**{,/*/**}", pattern)]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def _paths_with_chdir(paths, chdir)
|
66
|
+
paths.map do |path|
|
67
|
+
chdir ? File.join(chdir, path) : path
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "guard/rspec/inspectors/simple_inspector.rb"
|
2
|
+
require "guard/rspec/inspectors/keeping_inspector.rb"
|
3
|
+
require "guard/rspec/inspectors/focused_inspector.rb"
|
4
|
+
|
5
|
+
module Guard
|
6
|
+
class RSpec < Plugin
|
7
|
+
module Inspectors
|
8
|
+
class Factory
|
9
|
+
class << self
|
10
|
+
def create(options = {})
|
11
|
+
case options[:failed_mode]
|
12
|
+
when :focus then FocusedInspector.new(options)
|
13
|
+
when :keep then KeepingInspector.new(options)
|
14
|
+
else; SimpleInspector.new(options)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
private :new
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "guard/rspec/inspectors/base_inspector.rb"
|
2
|
+
|
3
|
+
module Guard
|
4
|
+
class RSpec < Plugin
|
5
|
+
module Inspectors
|
6
|
+
# Inspector that focuses on set of paths if any of them is failing.
|
7
|
+
# Returns only that set of paths on all future calls to #paths
|
8
|
+
# until they all pass
|
9
|
+
class FocusedInspector < BaseInspector
|
10
|
+
attr_accessor :focused_locations
|
11
|
+
|
12
|
+
def initialize(options = {})
|
13
|
+
super
|
14
|
+
@focused_locations = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def paths(paths)
|
18
|
+
if focused_locations.any?
|
19
|
+
focused_locations
|
20
|
+
else
|
21
|
+
_clean(paths)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def failed(locations)
|
26
|
+
if locations.empty?
|
27
|
+
@focused_locations = []
|
28
|
+
elsif focused_locations.empty?
|
29
|
+
@focused_locations = locations
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def reload
|
34
|
+
@focused_locations = []
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require "guard/rspec/inspectors/base_inspector.rb"
|
2
|
+
|
3
|
+
module Guard
|
4
|
+
class RSpec < Plugin
|
5
|
+
module Inspectors
|
6
|
+
# Inspector that remembers all failed paths and
|
7
|
+
# returns that paths in future calls to #paths method
|
8
|
+
# along with any new paths passed as parameter to #paths
|
9
|
+
class KeepingInspector < BaseInspector
|
10
|
+
attr_accessor :failed_locations
|
11
|
+
|
12
|
+
def initialize(options = {})
|
13
|
+
super
|
14
|
+
@failed_locations = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def paths(paths)
|
18
|
+
_with_failed_locations(_clean(paths))
|
19
|
+
end
|
20
|
+
|
21
|
+
def failed(locations)
|
22
|
+
@failed_locations = locations
|
23
|
+
end
|
24
|
+
|
25
|
+
def reload
|
26
|
+
@failed_locations = []
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# Return paths + failed locations.
|
32
|
+
# Do not include location in result if its path is already included.
|
33
|
+
def _with_failed_locations(paths)
|
34
|
+
failed_paths = failed_locations.map { |l| _location_path(l) }
|
35
|
+
(paths | failed_paths).uniq
|
36
|
+
end
|
37
|
+
|
38
|
+
# Extract file path from location
|
39
|
+
def _location_path(location)
|
40
|
+
location.match(%r{^(\./)?(.*?)(:\d+)?$})[2]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# FIXME uncomment when RSpec #952 will be resolved
|
49
|
+
#
|
50
|
+
# This is correct version of KeepingInspector class,
|
51
|
+
# bit it doesn't work because of bug with RSpec
|
52
|
+
# https://github.com/rspec/rspec-core/issues/952
|
53
|
+
#
|
54
|
+
# module Guard
|
55
|
+
# class RSpec < Plugin
|
56
|
+
# module Inspectors
|
57
|
+
# # Inspector that remembers all failed paths and
|
58
|
+
# # returns that paths in future calls to #paths method
|
59
|
+
# # along with any new paths passed as parameter to #paths
|
60
|
+
# class KeepingInspector < BaseInspector
|
61
|
+
# attr_accessor :failed_locations
|
62
|
+
#
|
63
|
+
# def initialize(options = {})
|
64
|
+
# super
|
65
|
+
# @failed_locations = []
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
# def paths(paths)
|
69
|
+
# _with_failed_locations(_clean(paths))
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
# def failed(locations)
|
73
|
+
# @failed_locations = locations
|
74
|
+
# end
|
75
|
+
#
|
76
|
+
# def reload
|
77
|
+
# @failed_locations = []
|
78
|
+
# end
|
79
|
+
#
|
80
|
+
# private
|
81
|
+
#
|
82
|
+
# # Return paths + failed locations.
|
83
|
+
# # Do not include location in result if its path is already included.
|
84
|
+
# def _with_failed_locations(paths)
|
85
|
+
# locations = failed_locations.select { |l|
|
86
|
+
# !paths.include?(_location_path(l)) }
|
87
|
+
# paths | locations
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
# # Extract file path from location
|
91
|
+
# def _location_path(location)
|
92
|
+
# location.match(/^(\.\/)?(.*?)(:\d+)?$/)[2]
|
93
|
+
# end
|
94
|
+
# end
|
95
|
+
# end
|
96
|
+
# end
|
97
|
+
# end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "guard/rspec/inspectors/base_inspector"
|
2
|
+
|
3
|
+
module Guard
|
4
|
+
class RSpec < Plugin
|
5
|
+
module Inspectors
|
6
|
+
class SimpleInspector < BaseInspector
|
7
|
+
def paths(paths)
|
8
|
+
_clean(paths)
|
9
|
+
end
|
10
|
+
|
11
|
+
def failed(_locations)
|
12
|
+
# Don't care
|
13
|
+
end
|
14
|
+
|
15
|
+
def reload
|
16
|
+
# Nothing to reload
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Guard
|
2
|
+
class RSpec < Plugin
|
3
|
+
class Notifier
|
4
|
+
attr_accessor :options
|
5
|
+
|
6
|
+
def initialize(options = {})
|
7
|
+
@options = options
|
8
|
+
end
|
9
|
+
|
10
|
+
def notify(summary)
|
11
|
+
return unless options[:notification]
|
12
|
+
failure_count, pending_count = _parse_summary(summary)
|
13
|
+
image = _image(failure_count, pending_count)
|
14
|
+
priority = _priority(image)
|
15
|
+
Guard::Compat::UI.notify(summary,
|
16
|
+
title: @options[:title],
|
17
|
+
image: image,
|
18
|
+
priority: priority)
|
19
|
+
end
|
20
|
+
|
21
|
+
def notify_failure
|
22
|
+
return unless options[:notification]
|
23
|
+
Guard::Compat::UI.notify("Failed",
|
24
|
+
title: @options[:title],
|
25
|
+
image: :failed,
|
26
|
+
priority: 2)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def _parse_summary(summary)
|
32
|
+
summary.match(/(\d+) failures( \((\d+) pending\))?/) do |m|
|
33
|
+
return m[1].to_i, m[3].to_i
|
34
|
+
end
|
35
|
+
[0, 0]
|
36
|
+
end
|
37
|
+
|
38
|
+
def _image(failure_count, pending_count)
|
39
|
+
if failure_count > 0
|
40
|
+
:failed
|
41
|
+
elsif pending_count > 0
|
42
|
+
:pending
|
43
|
+
else
|
44
|
+
:success
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def _priority(image)
|
49
|
+
{ failed: 2,
|
50
|
+
pending: -1,
|
51
|
+
success: -2 }[image]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Guard
|
2
|
+
class RSpec < Plugin
|
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: :none, # :keep and :focus are other posibilities
|
9
|
+
spec_paths: %w(spec),
|
10
|
+
cmd: nil,
|
11
|
+
cmd_additional_args: nil,
|
12
|
+
launchy: nil,
|
13
|
+
notification: true,
|
14
|
+
title: "RSpec results",
|
15
|
+
bundler_env: :original_env
|
16
|
+
}.freeze
|
17
|
+
|
18
|
+
class << self
|
19
|
+
def with_defaults(options = {})
|
20
|
+
_deep_merge(DEFAULTS, options)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def _deep_merge(hash1, hash2)
|
26
|
+
hash1.merge(hash2) do |_key, oldval, newval|
|
27
|
+
if oldval.instance_of?(Hash) && newval.instance_of?(Hash)
|
28
|
+
_deep_merge(oldval, newval)
|
29
|
+
else
|
30
|
+
newval
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Guard
|
2
|
+
class RSpec < Plugin
|
3
|
+
class Results
|
4
|
+
class InvalidData < RuntimeError
|
5
|
+
end
|
6
|
+
|
7
|
+
attr_reader :summary
|
8
|
+
attr_reader :failed_paths
|
9
|
+
|
10
|
+
def initialize(filename)
|
11
|
+
lines = File.readlines(filename)
|
12
|
+
if lines.empty? || lines.first.empty?
|
13
|
+
dump = lines.inspect
|
14
|
+
raise InvalidData, "Invalid results in: #{filename},"\
|
15
|
+
" lines:\n#{dump}\n"
|
16
|
+
end
|
17
|
+
|
18
|
+
@summary = lines.first.chomp
|
19
|
+
@failed_paths = lines[1..11].map(&:chomp).compact
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require "guard/rspec/command"
|
2
|
+
|
3
|
+
module Guard
|
4
|
+
class RSpec < Plugin
|
5
|
+
class RSpecProcess
|
6
|
+
class Failure < RuntimeError
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :results, :options
|
10
|
+
|
11
|
+
def initialize(command, formatter_tmp_file, options = {})
|
12
|
+
@command = command
|
13
|
+
@formatter_tmp_file = formatter_tmp_file
|
14
|
+
@results = nil
|
15
|
+
@options = options
|
16
|
+
|
17
|
+
@exit_code = _run
|
18
|
+
@results = _read_results
|
19
|
+
end
|
20
|
+
|
21
|
+
def all_green?
|
22
|
+
exit_code.zero?
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def _run
|
28
|
+
_with_desired_bundler_env do
|
29
|
+
exit_code = _really_run
|
30
|
+
|
31
|
+
msg = "Guard::RSpec: RSpec command %s exited with: %s"
|
32
|
+
Compat::UI.debug(format(msg, command, exit_code.inspect))
|
33
|
+
|
34
|
+
unless [0, Command::FAILURE_EXIT_CODE].include?(exit_code)
|
35
|
+
msg = "Failed: %s (exit code: %s)"
|
36
|
+
raise Failure, format(msg, command.inspect, exit_code.inspect)
|
37
|
+
end
|
38
|
+
exit_code
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def _really_run
|
43
|
+
env = { "GUARD_RSPEC_RESULTS_FILE" => formatter_tmp_file }
|
44
|
+
|
45
|
+
_warn_unless_absolute_path(formatter_tmp_file)
|
46
|
+
|
47
|
+
Compat::UI.debug("Guard::RSpec: results file: #{formatter_tmp_file}")
|
48
|
+
|
49
|
+
pid = Kernel.spawn(env, command) # use spawn to stub in JRuby
|
50
|
+
result = ::Process.wait2(pid)
|
51
|
+
result.last.exitstatus
|
52
|
+
rescue Errno::ENOENT => ex
|
53
|
+
raise Failure, "Failed: #{command.inspect} (#{ex})"
|
54
|
+
end
|
55
|
+
|
56
|
+
def _read_results
|
57
|
+
Results.new(formatter_tmp_file)
|
58
|
+
rescue Errno::ENOENT
|
59
|
+
msg = "Guard::RSpec cannot open results file: %s. This is likely a bug"\
|
60
|
+
"so please report this at"\
|
61
|
+
" http://github.com/guard/guard-rspec/issues/new along with as much"\
|
62
|
+
"information as possible to reproduce this issue."
|
63
|
+
Compat::UI.error(format(msg, formatter_tmp_file.inspect))
|
64
|
+
raise
|
65
|
+
ensure
|
66
|
+
File.delete(formatter_tmp_file) if File.exist?(formatter_tmp_file)
|
67
|
+
end
|
68
|
+
|
69
|
+
def _with_desired_bundler_env
|
70
|
+
desired_bundler_env = options[:bundler_env]
|
71
|
+
if !defined?(::Bundler) || desired_bundler_env == :inherit
|
72
|
+
yield
|
73
|
+
elsif desired_bundler_env == :clean_env
|
74
|
+
::Bundler.with_clean_env { yield }
|
75
|
+
else
|
76
|
+
::Bundler.with_original_env { yield }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def _warn_unless_absolute_path(formatter_tmp_file)
|
81
|
+
return if Pathname(formatter_tmp_file).absolute?
|
82
|
+
|
83
|
+
msg = "Guard::RSpec: The results file %s is not an absolute path."\
|
84
|
+
" Please provide an absolute path to avoid issues."
|
85
|
+
Compat::UI.warning(format(msg, formatter_tmp_file.inspect))
|
86
|
+
end
|
87
|
+
|
88
|
+
attr_reader :command
|
89
|
+
attr_reader :exit_code
|
90
|
+
attr_reader :formatter_tmp_file
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|