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
data/lib/guard/rspec/runner.rb
CHANGED
@@ -1,202 +1,99 @@
|
|
1
|
-
require
|
2
|
-
|
1
|
+
require "guard/rspec_defaults"
|
2
|
+
|
3
|
+
require "guard/rspec/inspectors/factory"
|
4
|
+
require "guard/rspec/command"
|
5
|
+
require "guard/rspec/notifier"
|
6
|
+
require "guard/rspec/results"
|
7
|
+
require "guard/rspec/rspec_process"
|
3
8
|
|
4
9
|
module Guard
|
5
|
-
class RSpec
|
10
|
+
class RSpec < Plugin
|
6
11
|
class Runner
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
def initialize(options = {})
|
11
|
-
@options = {
|
12
|
-
:bundler => true,
|
13
|
-
:binstubs => false,
|
14
|
-
:rvm => nil,
|
15
|
-
:cli => nil,
|
16
|
-
:env => nil,
|
17
|
-
:notification => true,
|
18
|
-
:turnip => false,
|
19
|
-
:zeus => false
|
20
|
-
}.merge(options)
|
21
|
-
|
22
|
-
deprecations_warnings
|
23
|
-
end
|
24
|
-
|
25
|
-
def run(paths, options = {})
|
26
|
-
return false if paths.empty?
|
27
|
-
|
28
|
-
message = options[:message] || "Running: #{paths.join(' ')}"
|
29
|
-
UI.info(message, :reset => true)
|
30
|
-
|
31
|
-
options = @options.merge(options)
|
32
|
-
|
33
|
-
if drb_used?
|
34
|
-
run_via_drb(paths, options)
|
35
|
-
else
|
36
|
-
run_via_shell(paths, options)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def rspec_executable
|
41
|
-
@rspec_executable ||= binstubs? ? "#{binstubs}/rspec" : "rspec"
|
42
|
-
end
|
43
|
-
|
44
|
-
def failure_exit_code_supported?
|
45
|
-
@failure_exit_code_supported ||= begin
|
46
|
-
cmd_parts = []
|
47
|
-
cmd_parts << "bundle exec" if bundle_exec?
|
48
|
-
cmd_parts << rspec_executable
|
49
|
-
cmd_parts << "--help"
|
50
|
-
`#{cmd_parts.join(' ')}`.include? "--failure-exit-code"
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def parsed_or_default_formatter
|
55
|
-
@parsed_or_default_formatter ||= begin
|
56
|
-
# Use RSpec's parser to parse formatters
|
57
|
-
formatters = ::RSpec::Core::ConfigurationOptions.new([]).parse_options()[:formatters]
|
58
|
-
# Use a default formatter if none exists.
|
59
|
-
# RSpec's parser returns an array in the format [[formatter, output], ...], so match their format
|
60
|
-
formatters = [['progress']] if formatters.nil? || formatters.empty?
|
61
|
-
# Construct a matching command line option, including output target
|
62
|
-
formatters.map { |formatter| "-f #{formatter.join ' -o '}" }.join ' '
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
private
|
67
|
-
|
68
|
-
def environment_variables
|
69
|
-
return if @options[:env].nil?
|
70
|
-
"export " + @options[:env].map {|key, value| "#{key}=#{value}"}.join(' ') + ';'
|
71
|
-
end
|
72
|
-
|
73
|
-
def rspec_arguments(paths, options)
|
74
|
-
arg_parts = []
|
75
|
-
arg_parts << options[:cli]
|
76
|
-
if @options[:notification]
|
77
|
-
arg_parts << parsed_or_default_formatter unless options[:cli] =~ formatter_regex
|
78
|
-
arg_parts << "-r #{File.dirname(__FILE__)}/formatter.rb"
|
79
|
-
arg_parts << "-f Guard::RSpec::Formatter --out /dev/null"
|
12
|
+
class NoCmdOptionError < RuntimeError
|
13
|
+
def initialize
|
14
|
+
super "No cmd option specified, unable to run specs!"
|
80
15
|
end
|
81
|
-
arg_parts << "--failure-exit-code #{FAILURE_EXIT_CODE}" if failure_exit_code_supported?
|
82
|
-
arg_parts << "-r turnip/rspec" if @options[:turnip]
|
83
|
-
arg_parts << paths.join(' ')
|
84
|
-
|
85
|
-
arg_parts.compact.join(' ')
|
86
16
|
end
|
87
17
|
|
88
|
-
|
89
|
-
cmd_parts = []
|
90
|
-
cmd_parts << environment_variables
|
91
|
-
cmd_parts << "rvm #{@options[:rvm].join(',')} exec" if @options[:rvm].respond_to?(:join)
|
92
|
-
cmd_parts << "bundle exec" if bundle_exec?
|
93
|
-
cmd_parts << 'zeus' if zeus?
|
94
|
-
cmd_parts << rspec_executable
|
95
|
-
cmd_parts << rspec_arguments(paths, options)
|
96
|
-
cmd_parts.compact.join(' ')
|
97
|
-
end
|
98
|
-
|
99
|
-
def run_via_shell(paths, options)
|
100
|
-
success = system(rspec_command(paths, options))
|
101
|
-
|
102
|
-
if @options[:notification] && !drb_used? && !success && rspec_command_exited_with_an_exception?
|
103
|
-
Notifier.notify("Failed", :title => "RSpec results", :image => :failed, :priority => 2)
|
104
|
-
end
|
105
|
-
|
106
|
-
success
|
107
|
-
end
|
108
|
-
|
109
|
-
def rspec_command_exited_with_an_exception?
|
110
|
-
failure_exit_code_supported? && $?.exitstatus != FAILURE_EXIT_CODE
|
111
|
-
end
|
18
|
+
attr_accessor :options, :inspector, :notifier
|
112
19
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
20
|
+
def initialize(options = {})
|
21
|
+
@options = options
|
22
|
+
@inspector = Inspectors::Factory.create(@options)
|
23
|
+
@notifier = Notifier.new(@options)
|
24
|
+
end
|
25
|
+
|
26
|
+
def run_all
|
27
|
+
paths = options[:spec_paths]
|
28
|
+
options = @options.merge(@options[:run_all])
|
29
|
+
return true if paths.empty?
|
30
|
+
Compat::UI.info(options[:message], reset: true)
|
31
|
+
_run(paths, options)
|
32
|
+
end
|
33
|
+
|
34
|
+
def run(paths)
|
35
|
+
paths = inspector.paths(paths)
|
36
|
+
return true if paths.empty?
|
37
|
+
Compat::UI.info("Running: #{paths.join(' ')}", reset: true)
|
38
|
+
_run(paths, options) do |all_green|
|
39
|
+
next false unless all_green
|
40
|
+
next true unless options[:all_after_pass]
|
41
|
+
run_all
|
122
42
|
end
|
123
|
-
port = ENV["RSPEC_DRB"] || 8989 unless port && port > 0
|
124
|
-
ret = drb_service(port.to_i).run(argv, $stderr, $stdout)
|
125
|
-
|
126
|
-
[0, true].include?(ret)
|
127
|
-
rescue DRb::DRbConnError
|
128
|
-
# Fall back to the shell runner; we don't want to mangle the environment!
|
129
|
-
run_via_shell(paths, options)
|
130
43
|
end
|
131
44
|
|
132
|
-
def
|
133
|
-
|
45
|
+
def reload
|
46
|
+
inspector.reload
|
134
47
|
end
|
135
48
|
|
136
|
-
|
137
|
-
# just to let DRb know what to do.
|
138
|
-
#
|
139
|
-
# For reference:
|
140
|
-
#
|
141
|
-
# * RSpec: https://github.com/rspec/rspec-core/blob/master/lib/rspec/core/drb_command_line.rb
|
142
|
-
def drb_service(port)
|
143
|
-
require "drb/drb"
|
144
|
-
|
145
|
-
# Make sure we have a listener running
|
146
|
-
unless @drb_listener_running
|
147
|
-
begin
|
148
|
-
DRb.start_service("druby://localhost:0")
|
149
|
-
rescue SocketError, Errno::EADDRNOTAVAIL
|
150
|
-
DRb.start_service("druby://:0")
|
151
|
-
end
|
152
|
-
|
153
|
-
@drb_listener_running = true
|
154
|
-
end
|
49
|
+
private
|
155
50
|
|
156
|
-
|
157
|
-
|
51
|
+
def _run(paths, options, &block)
|
52
|
+
raise NoCmdOptionError unless options[:cmd]
|
53
|
+
command = Command.new(paths, options)
|
54
|
+
_really_run(command, options, &block)
|
55
|
+
rescue RSpecProcess::Failure, NoCmdOptionError => ex
|
56
|
+
Compat::UI.error(ex.to_s)
|
57
|
+
notifier.notify_failure
|
58
|
+
false
|
158
59
|
end
|
159
60
|
|
160
|
-
def
|
161
|
-
|
162
|
-
|
61
|
+
def _really_run(cmd, options)
|
62
|
+
# TODO: add option to specify the file
|
63
|
+
file = _results_file(options[:results_file], options[:chdir])
|
163
64
|
|
164
|
-
|
165
|
-
|
166
|
-
end
|
65
|
+
process = RSpecProcess.new(cmd, file, options)
|
66
|
+
results = process.results
|
167
67
|
|
168
|
-
|
169
|
-
|
170
|
-
|
68
|
+
inspector.failed(results.failed_paths)
|
69
|
+
notifier.notify(results.summary)
|
70
|
+
_open_launchy
|
171
71
|
|
172
|
-
|
173
|
-
|
72
|
+
all_green = process.all_green?
|
73
|
+
return yield all_green if block_given?
|
74
|
+
all_green
|
174
75
|
end
|
175
76
|
|
176
|
-
def
|
177
|
-
|
77
|
+
def _open_launchy
|
78
|
+
return unless options[:launchy]
|
79
|
+
require "launchy"
|
80
|
+
pn = Pathname.new(options[:launchy])
|
81
|
+
::Launchy.open(options[:launchy]) if pn.exist?
|
178
82
|
end
|
179
83
|
|
180
|
-
def
|
181
|
-
|
182
|
-
|
84
|
+
def _results_file(results_file, chdir)
|
85
|
+
results_file ||= File.expand_path(RSpecDefaults::TEMPORARY_FILE_PATH)
|
86
|
+
return results_file unless Pathname(results_file).relative?
|
87
|
+
results_file = File.join(chdir, results_file) if chdir
|
88
|
+
return results_file unless Pathname(results_file).relative?
|
183
89
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
@options.delete(key)
|
189
|
-
UI.info %{DEPRECATION WARNING: The :#{key} option is deprecated. Pass standard command line argument "--#{value}" to RSpec with the :cli option.}
|
190
|
-
end
|
90
|
+
unless Pathname(results_file).absolute?
|
91
|
+
msg = "Guard::RSpec: The results file %s is not an absolute path."\
|
92
|
+
" Please provide an absolute path to avoid issues."
|
93
|
+
Compat::UI.warning(format(msg, results_file.inspect))
|
191
94
|
end
|
192
|
-
if @options.key?(:version)
|
193
|
-
@options.delete(:version)
|
194
|
-
UI.info %{DEPRECATION WARNING: The :version option is deprecated. Only RSpec 2 is now supported.}
|
195
|
-
end
|
196
|
-
end
|
197
95
|
|
198
|
-
|
199
|
-
@formatter_regex ||= /(?:^|\s)(?:-f\s*|--format(?:=|\s+))([\w:]+)/
|
96
|
+
File.expand_path(results_file)
|
200
97
|
end
|
201
98
|
end
|
202
99
|
end
|
@@ -1,21 +1,53 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
#
|
15
|
-
|
1
|
+
# Note: The cmd option is now required due to the increasing number of ways
|
2
|
+
# rspec may be run, below are examples of the most common uses.
|
3
|
+
# * bundler: 'bundle exec rspec'
|
4
|
+
# * bundler binstubs: 'bin/rspec'
|
5
|
+
# * spring: 'bin/rspec' (This will use spring if running and you have
|
6
|
+
# installed the spring binstubs per the docs)
|
7
|
+
# * zeus: 'zeus rspec' (requires the server to be started separately)
|
8
|
+
# * 'just' rspec: 'rspec'
|
9
|
+
|
10
|
+
guard :rspec, cmd: "bundle exec rspec" do
|
11
|
+
require "guard/rspec/dsl"
|
12
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
13
|
+
|
14
|
+
# Feel free to open issues for suggestions and improvements
|
15
|
+
|
16
|
+
# RSpec files
|
17
|
+
rspec = dsl.rspec
|
18
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
19
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
20
|
+
watch(rspec.spec_files)
|
21
|
+
|
22
|
+
# Ruby files
|
23
|
+
ruby = dsl.ruby
|
24
|
+
dsl.watch_spec_files_for(ruby.lib_files)
|
25
|
+
|
26
|
+
# Rails files
|
27
|
+
rails = dsl.rails(view_extensions: %w(erb haml slim))
|
28
|
+
dsl.watch_spec_files_for(rails.app_files)
|
29
|
+
dsl.watch_spec_files_for(rails.views)
|
30
|
+
|
31
|
+
watch(rails.controllers) do |m|
|
32
|
+
[
|
33
|
+
rspec.spec.call("routing/#{m[1]}_routing"),
|
34
|
+
rspec.spec.call("controllers/#{m[1]}_controller"),
|
35
|
+
rspec.spec.call("acceptance/#{m[1]}")
|
36
|
+
]
|
37
|
+
end
|
38
|
+
|
39
|
+
# Rails config changes
|
40
|
+
watch(rails.spec_helper) { rspec.spec_dir }
|
41
|
+
watch(rails.routes) { "#{rspec.spec_dir}/routing" }
|
42
|
+
watch(rails.app_controller) { "#{rspec.spec_dir}/controllers" }
|
43
|
+
|
44
|
+
# Capybara features specs
|
45
|
+
watch(rails.view_dirs) { |m| rspec.spec.call("features/#{m[1]}") }
|
46
|
+
watch(rails.layouts) { |m| rspec.spec.call("features/#{m[1]}") }
|
16
47
|
|
17
48
|
# Turnip features and steps
|
18
49
|
watch(%r{^spec/acceptance/(.+)\.feature$})
|
19
|
-
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$})
|
50
|
+
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) do |m|
|
51
|
+
Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance"
|
52
|
+
end
|
20
53
|
end
|
21
|
-
|
data/lib/guard/rspec/version.rb
CHANGED
data/lib/guard/rspec.rb
CHANGED
@@ -1,80 +1,51 @@
|
|
1
|
-
require
|
2
|
-
|
1
|
+
require "guard/compat/plugin"
|
2
|
+
|
3
|
+
require "guard/rspec/options"
|
4
|
+
require "guard/rspec/deprecator"
|
5
|
+
require "guard/rspec/runner"
|
6
|
+
|
7
|
+
# NOTE: To avoid 'superclass mismatch for class RSpec' errors,
|
8
|
+
# every file has to have
|
9
|
+
#
|
10
|
+
# class RSpec < Plugin
|
11
|
+
#
|
12
|
+
# and not just
|
13
|
+
#
|
14
|
+
# class RSpec
|
3
15
|
|
4
16
|
module Guard
|
5
|
-
class RSpec <
|
6
|
-
|
7
|
-
autoload :Inspector, 'guard/rspec/inspector'
|
17
|
+
class RSpec < Plugin
|
18
|
+
attr_accessor :options, :runner
|
8
19
|
|
9
|
-
def initialize(
|
20
|
+
def initialize(options = {})
|
10
21
|
super
|
11
|
-
@options =
|
12
|
-
|
13
|
-
|
14
|
-
:keep_failed => true,
|
15
|
-
:spec_paths => ["spec"],
|
16
|
-
:run_all => {}
|
17
|
-
}.merge(options)
|
18
|
-
@last_failed = false
|
19
|
-
@failed_paths = []
|
20
|
-
|
21
|
-
@inspector = Inspector.new(@options)
|
22
|
-
@runner = Runner.new(@options)
|
22
|
+
@options = Options.with_defaults(options)
|
23
|
+
Deprecator.warns_about_deprecated_options(@options)
|
24
|
+
@runner = Runner.new(@options)
|
23
25
|
end
|
24
26
|
|
25
|
-
# Call once when guard starts
|
26
27
|
def start
|
27
|
-
UI.info "Guard::RSpec is running"
|
28
|
-
run_all if
|
28
|
+
Compat::UI.info "Guard::RSpec is running"
|
29
|
+
run_all if options[:all_on_start]
|
29
30
|
end
|
30
31
|
|
31
32
|
def run_all
|
32
|
-
|
33
|
-
|
34
|
-
unless @last_failed = !passed
|
35
|
-
@failed_paths = []
|
36
|
-
else
|
37
|
-
throw :task_has_failed
|
38
|
-
end
|
33
|
+
_throw_if_failed { runner.run_all }
|
39
34
|
end
|
40
35
|
|
41
36
|
def reload
|
42
|
-
|
37
|
+
runner.reload
|
43
38
|
end
|
44
39
|
|
45
|
-
def
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
if passed = @runner.run(paths)
|
50
|
-
remove_failed(paths)
|
51
|
-
|
52
|
-
# run all the specs if the run before this one failed
|
53
|
-
if @last_failed && @options[:all_after_pass]
|
54
|
-
@last_failed = false
|
55
|
-
run_all
|
56
|
-
end
|
57
|
-
else
|
58
|
-
@last_failed = true
|
59
|
-
add_failed(paths)
|
60
|
-
|
61
|
-
throw :task_has_failed
|
62
|
-
end
|
40
|
+
def run_on_modifications(paths)
|
41
|
+
return false if paths.empty?
|
42
|
+
_throw_if_failed { runner.run(paths) }
|
63
43
|
end
|
64
44
|
|
65
|
-
|
45
|
+
private
|
66
46
|
|
67
|
-
def
|
47
|
+
def _throw_if_failed
|
48
|
+
throw :task_has_failed unless yield
|
68
49
|
end
|
69
|
-
|
70
|
-
def remove_failed(paths)
|
71
|
-
@failed_paths -= paths if @options[:keep_failed]
|
72
|
-
end
|
73
|
-
|
74
|
-
def add_failed(paths)
|
75
|
-
@failed_paths += paths if @options[:keep_failed]
|
76
|
-
end
|
77
|
-
|
78
50
|
end
|
79
51
|
end
|
80
|
-
|
@@ -0,0 +1,147 @@
|
|
1
|
+
# NOTE: This class only exists for RSpec and should not be used by
|
2
|
+
# other classes in this project!
|
3
|
+
|
4
|
+
require "pathname"
|
5
|
+
require "fileutils"
|
6
|
+
|
7
|
+
require "rspec"
|
8
|
+
require "rspec/core/formatters/base_formatter"
|
9
|
+
|
10
|
+
require_relative "rspec_formatter_results_path"
|
11
|
+
|
12
|
+
module Guard
|
13
|
+
class RSpecFormatter < ::RSpec::Core::Formatters::BaseFormatter
|
14
|
+
UNSUPPORTED_PATTERN =
|
15
|
+
"Your RSpec.configuration.pattern uses characters "\
|
16
|
+
"unsupported by your Ruby version (File::FNM_EXTGLOB is undefined)".freeze
|
17
|
+
|
18
|
+
class Error < RuntimeError
|
19
|
+
class UnsupportedPattern < Error
|
20
|
+
def initialize(msg = UNSUPPORTED_PATTERN)
|
21
|
+
super
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.rspec_3?
|
27
|
+
::RSpec::Core::Version::STRING.split(".").first == "3"
|
28
|
+
end
|
29
|
+
|
30
|
+
if rspec_3?
|
31
|
+
::RSpec::Core::Formatters.register self, :dump_summary, :example_failed
|
32
|
+
|
33
|
+
def example_failed(failure)
|
34
|
+
examples.push failure.example
|
35
|
+
end
|
36
|
+
|
37
|
+
def examples
|
38
|
+
@examples ||= []
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class << self
|
43
|
+
# rspec issue https://github.com/rspec/rspec-core/issues/793
|
44
|
+
def extract_spec_location(metadata)
|
45
|
+
root_metadata = metadata
|
46
|
+
location = metadata[:location]
|
47
|
+
|
48
|
+
until spec_path?(location)
|
49
|
+
unless (metadata = _extract_group(metadata))
|
50
|
+
STDERR.puts "no spec file location in #{root_metadata.inspect}"
|
51
|
+
return root_metadata[:location]
|
52
|
+
end
|
53
|
+
|
54
|
+
# rspec issue https://github.com/rspec/rspec-core/issues/1243
|
55
|
+
location = first_colon_separated_entry(metadata[:location])
|
56
|
+
end
|
57
|
+
|
58
|
+
location
|
59
|
+
end
|
60
|
+
|
61
|
+
def spec_path?(path)
|
62
|
+
pattern = ::RSpec.configuration.pattern
|
63
|
+
|
64
|
+
flags = supported_fnmatch_flags(pattern)
|
65
|
+
path ||= ""
|
66
|
+
path = path.sub(/:\d+\z/, "")
|
67
|
+
path = Pathname.new(path).cleanpath.to_s
|
68
|
+
stripped = "{#{pattern.gsub(/\s*,\s*/, ',')}}"
|
69
|
+
File.fnmatch(stripped, path, flags)
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def first_colon_separated_entry(entries)
|
75
|
+
(entries || "").split(":").first
|
76
|
+
end
|
77
|
+
|
78
|
+
def supported_fnmatch_flags(pattern)
|
79
|
+
flags = File::FNM_PATHNAME | File::FNM_DOTMATCH
|
80
|
+
|
81
|
+
# ruby >= 2
|
82
|
+
return flags |= File::FNM_EXTGLOB if File.const_defined?(:FNM_EXTGLOB)
|
83
|
+
|
84
|
+
raise Error::UnsupportedPattern if pattern =~ /[{}]/
|
85
|
+
|
86
|
+
flags
|
87
|
+
end
|
88
|
+
|
89
|
+
def _extract_group(metadata)
|
90
|
+
metadata[:parent_example_group] || metadata[:example_group]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def dump_summary(*args)
|
95
|
+
return write_summary(*args) unless self.class.rspec_3?
|
96
|
+
|
97
|
+
notification = args[0]
|
98
|
+
write_summary(
|
99
|
+
notification.duration,
|
100
|
+
notification.example_count,
|
101
|
+
notification.failure_count,
|
102
|
+
notification.pending_count
|
103
|
+
)
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
# Write summary to temporary file for runner
|
109
|
+
def write_summary(duration, total, failures, pending)
|
110
|
+
_write do |f|
|
111
|
+
f.puts _message(total, failures, pending, duration)
|
112
|
+
f.puts _failed_paths.join("\n") if failures > 0
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def _write(&block)
|
117
|
+
file = RSpecFormatterResultsPath.new.path
|
118
|
+
if ENV['GUARD_RSPEC_DEBUGGING'] == '1'
|
119
|
+
msg = "Guard::RSpec: using results file: #{file.inspect}"
|
120
|
+
STDERR.puts format(msg, file)
|
121
|
+
end
|
122
|
+
FileUtils.mkdir_p(File.dirname(file))
|
123
|
+
File.open(file, "w", &block)
|
124
|
+
end
|
125
|
+
|
126
|
+
def _failed_paths
|
127
|
+
klass = self.class
|
128
|
+
failed = examples.select { |example| _status_failed?(example) }
|
129
|
+
failed.map { |e| klass.extract_spec_location(e.metadata) }.sort.uniq
|
130
|
+
end
|
131
|
+
|
132
|
+
def _message(example_count, failure_count, pending_count, duration)
|
133
|
+
message = "#{example_count} examples, #{failure_count} failures"
|
134
|
+
message << " (#{pending_count} pending)" if pending_count > 0
|
135
|
+
message << " in #{duration.round(4)} seconds"
|
136
|
+
message
|
137
|
+
end
|
138
|
+
|
139
|
+
def _status_failed?(example)
|
140
|
+
if self.class.rspec_3?
|
141
|
+
example.execution_result.status.to_s == "failed"
|
142
|
+
else
|
143
|
+
example.execution_result[:status].to_s == "failed"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative "rspec_defaults"
|
2
|
+
|
3
|
+
module Guard
|
4
|
+
# Just a wrapper class for the results file filename
|
5
|
+
class RSpecFormatterResultsPath
|
6
|
+
WIKI_ENV_WARN_URL =
|
7
|
+
"https://github.com/guard/guard-rspec/wiki/Warning:-no-environment".
|
8
|
+
freeze
|
9
|
+
|
10
|
+
NO_ENV_WARNING_MSG =
|
11
|
+
"no environment passed - see #{WIKI_ENV_WARN_URL}".freeze
|
12
|
+
|
13
|
+
NO_RESULTS_VALUE_MSG =
|
14
|
+
":results_file value unknown (using defaults)".freeze
|
15
|
+
|
16
|
+
attr_reader :path
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
path = ENV["GUARD_RSPEC_RESULTS_FILE"]
|
20
|
+
if path.nil?
|
21
|
+
STDERR.puts("Guard::RSpec: Warning: #{NO_ENV_WARNING_MSG}\n" \
|
22
|
+
"Guard::RSpec: Warning: #{NO_RESULTS_VALUE_MSG}")
|
23
|
+
path = RSpecDefaults::TEMPORARY_FILE_PATH
|
24
|
+
end
|
25
|
+
|
26
|
+
@path = File.expand_path(path)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|