guard-rspec 4.3.1 → 4.4.1
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 +4 -4
- data/.hound.yml +262 -0
- data/.rspec +2 -0
- data/.rubocop.yml +2 -0
- data/.rubocop_todo.yml +10 -0
- data/.travis.yml +2 -3
- data/CONTRIBUTING.md +1 -1
- data/Gemfile +9 -2
- data/Guardfile +11 -4
- data/README.md +8 -4
- data/Rakefile +23 -2
- data/gemfiles/Gemfile.rspec-2.14 +1 -0
- data/gemfiles/Gemfile.rspec-2.99 +14 -0
- data/gemfiles/Gemfile.rspec-3.0 +1 -0
- data/guard-rspec.gemspec +18 -16
- data/lib/guard/rspec.rb +13 -8
- data/lib/guard/rspec/command.rb +30 -12
- data/lib/guard/rspec/deprecator.rb +32 -11
- data/lib/guard/rspec/inspectors/base_inspector.rb +36 -15
- data/lib/guard/rspec/inspectors/factory.rb +7 -8
- data/lib/guard/rspec/inspectors/focused_inspector.rb +2 -2
- data/lib/guard/rspec/inspectors/keeping_inspector.rb +7 -6
- data/lib/guard/rspec/inspectors/simple_inspector.rb +3 -3
- data/lib/guard/rspec/notifier.rb +9 -5
- data/lib/guard/rspec/options.rb +12 -10
- data/lib/guard/rspec/runner.rb +41 -25
- data/lib/guard/rspec/templates/Guardfile +43 -15
- data/lib/guard/rspec/version.rb +1 -1
- data/lib/guard/rspec_formatter.rb +111 -0
- data/spec/lib/guard/rspec/command_spec.rb +48 -12
- data/spec/lib/guard/rspec/deprecator_spec.rb +37 -18
- data/spec/lib/guard/rspec/inspectors/base_inspector_spec.rb +129 -18
- data/spec/lib/guard/rspec/inspectors/factory_spec.rb +20 -15
- data/spec/lib/guard/rspec/inspectors/focused_inspector_spec.rb +79 -13
- data/spec/lib/guard/rspec/inspectors/keeping_inspector_spec.rb +103 -33
- data/spec/lib/guard/rspec/inspectors/shared_examples.rb +93 -31
- data/spec/lib/guard/rspec/inspectors/simple_inspector_spec.rb +52 -16
- data/spec/lib/guard/rspec/notifier_spec.rb +49 -27
- data/spec/lib/guard/rspec/runner_spec.rb +120 -77
- data/spec/lib/guard/rspec_formatter_spec.rb +144 -0
- data/spec/lib/guard/rspec_spec.rb +23 -18
- data/spec/spec_helper.rb +99 -14
- metadata +12 -7
- data/lib/guard/rspec/formatter.rb +0 -99
- data/spec/lib/guard/rspec/formatter_spec.rb +0 -122
data/lib/guard/rspec/notifier.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module Guard
|
2
|
-
class RSpec
|
2
|
+
class RSpec < Plugin
|
3
3
|
class Notifier
|
4
4
|
attr_accessor :options
|
5
5
|
|
@@ -7,19 +7,23 @@ module Guard
|
|
7
7
|
@options = options
|
8
8
|
end
|
9
9
|
|
10
|
-
TITLE = 'RSpec results'
|
11
|
-
|
12
10
|
def notify(summary)
|
13
11
|
return unless options[:notification]
|
14
12
|
failure_count, pending_count = _parse_summary(summary)
|
15
13
|
image = _image(failure_count, pending_count)
|
16
14
|
priority = _priority(image)
|
17
|
-
::Guard::Notifier.notify(summary,
|
15
|
+
::Guard::Notifier.notify(summary,
|
16
|
+
title: @options[:title],
|
17
|
+
image: image,
|
18
|
+
priority: priority)
|
18
19
|
end
|
19
20
|
|
20
21
|
def notify_failure
|
21
22
|
return unless options[:notification]
|
22
|
-
::Guard::Notifier.notify(
|
23
|
+
::Guard::Notifier.notify("Failed",
|
24
|
+
title: @options[:title],
|
25
|
+
image: :failed,
|
26
|
+
priority: 2)
|
23
27
|
end
|
24
28
|
|
25
29
|
private
|
data/lib/guard/rspec/options.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
1
|
module Guard
|
2
|
-
class RSpec
|
2
|
+
class RSpec < Plugin
|
3
3
|
module Options
|
4
4
|
DEFAULTS = {
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
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"
|
13
15
|
}
|
14
16
|
|
15
17
|
class << self
|
@@ -20,7 +22,7 @@ module Guard
|
|
20
22
|
private
|
21
23
|
|
22
24
|
def _deep_merge(hash1, hash2)
|
23
|
-
hash1.merge(hash2) do |
|
25
|
+
hash1.merge(hash2) do |_key, oldval, newval|
|
24
26
|
if oldval.instance_of?(Hash) && newval.instance_of?(Hash)
|
25
27
|
_deep_merge(oldval, newval)
|
26
28
|
else
|
data/lib/guard/rspec/runner.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require 'guard/rspec/notifier'
|
1
|
+
require "guard/rspec/inspectors/factory"
|
2
|
+
require "guard/rspec/command"
|
3
|
+
require "guard/rspec/notifier"
|
5
4
|
|
6
5
|
module Guard
|
7
|
-
class RSpec
|
6
|
+
class RSpec < Plugin
|
8
7
|
class Runner
|
8
|
+
# NOTE: must match with const in RspecFormatter!
|
9
|
+
TEMPORARY_FILE_PATH ||= "tmp/rspec_guard_result"
|
10
|
+
|
9
11
|
attr_accessor :options, :inspector, :notifier
|
10
12
|
|
11
13
|
def initialize(options = {})
|
@@ -25,7 +27,7 @@ module Guard
|
|
25
27
|
def run(paths)
|
26
28
|
paths = inspector.paths(paths)
|
27
29
|
return true if paths.empty?
|
28
|
-
::Guard::UI.info("Running: #{paths.join(
|
30
|
+
::Guard::UI.info("Running: #{paths.join(" ")}", reset: true)
|
29
31
|
_run(false, paths, options)
|
30
32
|
end
|
31
33
|
|
@@ -38,20 +40,9 @@ module Guard
|
|
38
40
|
def _run(all, paths, options)
|
39
41
|
return unless _cmd_option_present(options)
|
40
42
|
command = Command.new(paths, options)
|
43
|
+
|
41
44
|
_without_bundler_env { Kernel.system(command) }.tap do |success|
|
42
|
-
|
43
|
-
summary, failed_paths = _command_output
|
44
|
-
if summary && failed_paths
|
45
|
-
inspector.failed(failed_paths)
|
46
|
-
notifier.notify(summary)
|
47
|
-
_open_launchy
|
48
|
-
_run_all_after_pass if !all && success
|
49
|
-
else
|
50
|
-
notifier.notify_failure
|
51
|
-
end
|
52
|
-
else
|
53
|
-
notifier.notify_failure
|
54
|
-
end
|
45
|
+
_process_run_result(success, all)
|
55
46
|
end
|
56
47
|
end
|
57
48
|
|
@@ -65,29 +56,32 @@ module Guard
|
|
65
56
|
|
66
57
|
def _cmd_option_present(options)
|
67
58
|
return true if options[:cmd]
|
68
|
-
::Guard::UI.error(
|
59
|
+
::Guard::UI.error("No cmd option specified, unable to run specs!")
|
69
60
|
notifier.notify_failure
|
70
61
|
false
|
71
62
|
end
|
72
63
|
|
73
64
|
def _command_success?(success)
|
74
65
|
return false if success.nil?
|
75
|
-
[Command::FAILURE_EXIT_CODE, 0].include?(
|
66
|
+
[Command::FAILURE_EXIT_CODE, 0].include?($CHILD_STATUS.exitstatus)
|
76
67
|
end
|
77
68
|
|
78
69
|
def _command_output
|
79
|
-
formatter_tmp_file =
|
70
|
+
formatter_tmp_file = _tmp_file(options[:chdir])
|
80
71
|
lines = File.readlines(formatter_tmp_file)
|
81
|
-
|
72
|
+
summary = lines.first.strip
|
73
|
+
failed_paths = lines[1..11].map(&:strip).compact
|
74
|
+
|
75
|
+
[summary, failed_paths]
|
82
76
|
rescue
|
83
77
|
[nil, nil]
|
84
78
|
ensure
|
85
|
-
File.
|
79
|
+
File.delete(formatter_tmp_file) if File.exists?(formatter_tmp_file)
|
86
80
|
end
|
87
81
|
|
88
82
|
def _open_launchy
|
89
83
|
return unless options[:launchy]
|
90
|
-
require
|
84
|
+
require "launchy"
|
91
85
|
pn = Pathname.new(options[:launchy])
|
92
86
|
::Launchy.open(options[:launchy]) if pn.exist?
|
93
87
|
end
|
@@ -96,6 +90,28 @@ module Guard
|
|
96
90
|
return unless options[:all_after_pass]
|
97
91
|
run_all
|
98
92
|
end
|
93
|
+
|
94
|
+
def _process_run_result(result, all)
|
95
|
+
unless _command_success?(result)
|
96
|
+
notifier.notify_failure
|
97
|
+
return
|
98
|
+
end
|
99
|
+
|
100
|
+
summary, failed_paths = _command_output
|
101
|
+
unless summary && failed_paths
|
102
|
+
notifier.notify_failure
|
103
|
+
end
|
104
|
+
|
105
|
+
inspector.failed(failed_paths)
|
106
|
+
notifier.notify(summary)
|
107
|
+
_open_launchy
|
108
|
+
|
109
|
+
_run_all_after_pass if !all && result
|
110
|
+
end
|
111
|
+
|
112
|
+
def _tmp_file(chdir)
|
113
|
+
chdir ? File.join(chdir, TEMPORARY_FILE_PATH) : TEMPORARY_FILE_PATH
|
114
|
+
end
|
99
115
|
end
|
100
116
|
end
|
101
117
|
end
|
@@ -2,29 +2,57 @@
|
|
2
2
|
# rspec may be run, below are examples of the most common uses.
|
3
3
|
# * bundler: 'bundle exec rspec'
|
4
4
|
# * bundler binstubs: 'bin/rspec'
|
5
|
-
# * spring: 'bin/
|
5
|
+
# * spring: 'bin/rspec' (This will use spring if running and you have
|
6
6
|
# installed the spring binstubs per the docs)
|
7
|
-
# * zeus: 'zeus rspec' (requires the server to be started
|
7
|
+
# * zeus: 'zeus rspec' (requires the server to be started separately)
|
8
8
|
# * 'just' rspec: 'rspec'
|
9
|
-
|
9
|
+
|
10
|
+
guard :rspec, cmd: "bundle exec rspec" do
|
11
|
+
require "ostruct"
|
12
|
+
|
13
|
+
# Generic Ruby apps
|
14
|
+
rspec = OpenStruct.new
|
15
|
+
rspec.spec = ->(m) { "spec/#{m}_spec.rb" }
|
16
|
+
rspec.spec_dir = "spec"
|
17
|
+
rspec.spec_helper = "spec/spec_helper.rb"
|
18
|
+
|
10
19
|
watch(%r{^spec/.+_spec\.rb$})
|
11
|
-
watch(%r{^lib/(.+)\.rb$}) { |m| "
|
12
|
-
watch(
|
20
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| rspec.spec.("lib/#{m[1]}") }
|
21
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
13
22
|
|
14
23
|
# Rails example
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
24
|
+
rails = OpenStruct.new
|
25
|
+
rails.app = %r{^app/(.+)\.rb$}
|
26
|
+
rails.views_n_layouts = %r{^app/(.*)(\.erb|\.haml|\.slim)$}
|
27
|
+
rails.controllers = %r{^app/controllers/(.+)_controller\.rb$}
|
28
|
+
rails.routes = "config/routes.rb"
|
29
|
+
rails.app_controller = "app/controllers/application_controller.rb"
|
30
|
+
rails.spec_helper = "spec/rails_helper.rb"
|
31
|
+
rails.spec_support = %r{^spec/support/(.+)\.rb$}
|
32
|
+
rails.views = %r{^app/views/(.+)/.*\.(erb|haml|slim)$}
|
33
|
+
|
34
|
+
watch(rails.app) { |m| rspec.spec.(m[1]) }
|
35
|
+
watch(rails.views_n_layouts) { |m| rspec.spec.("#{m[1]}#{m[2]}") }
|
36
|
+
watch(rails.controllers) do |m|
|
37
|
+
[
|
38
|
+
rspec.spec.("routing/#{m[1]}_routing"),
|
39
|
+
rspec.spec.("controllers/#{m[1]}_controller"),
|
40
|
+
rspec.spec.("acceptance/#{m[1]}")
|
41
|
+
]
|
42
|
+
end
|
43
|
+
|
44
|
+
watch(rails.spec_support) { rspec.spec_dir }
|
45
|
+
watch(rails.spec_helper) { rspec.spec_dir }
|
46
|
+
watch(rails.routes) { "spec/routing" }
|
47
|
+
watch(rails.app_controller) { "spec/controllers" }
|
22
48
|
|
23
49
|
# Capybara features specs
|
24
|
-
watch(
|
50
|
+
watch(rails.views) { |m| rspec.spec.("features/#{m[1]}") }
|
25
51
|
|
26
52
|
# Turnip features and steps
|
27
53
|
watch(%r{^spec/acceptance/(.+)\.feature$})
|
28
|
-
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$})
|
29
|
-
|
54
|
+
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) do |m|
|
55
|
+
Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance"
|
56
|
+
end
|
30
57
|
|
58
|
+
end
|
data/lib/guard/rspec/version.rb
CHANGED
@@ -0,0 +1,111 @@
|
|
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
|
+
|
6
|
+
require "rspec"
|
7
|
+
require "rspec/core/formatters/base_formatter"
|
8
|
+
|
9
|
+
module Guard
|
10
|
+
class RSpecFormatter < ::RSpec::Core::Formatters::BaseFormatter
|
11
|
+
TEMPORARY_FILE_PATH ||= "tmp/rspec_guard_result"
|
12
|
+
|
13
|
+
def self.rspec_3?
|
14
|
+
::RSpec::Core::Version::STRING.split(".").first == "3"
|
15
|
+
end
|
16
|
+
|
17
|
+
if rspec_3?
|
18
|
+
::RSpec::Core::Formatters.register self, :dump_summary, :example_failed
|
19
|
+
|
20
|
+
def example_failed(failure)
|
21
|
+
examples.push failure.example
|
22
|
+
end
|
23
|
+
|
24
|
+
def examples
|
25
|
+
@examples ||= []
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# rspec issue https://github.com/rspec/rspec-core/issues/793
|
30
|
+
def self.extract_spec_location(metadata)
|
31
|
+
root_metadata = metadata
|
32
|
+
location = metadata[:location]
|
33
|
+
|
34
|
+
until spec_path?(location)
|
35
|
+
metadata = metadata[:example_group]
|
36
|
+
|
37
|
+
unless metadata
|
38
|
+
STDERR.puts "no spec file found for #{root_metadata[:location]}"
|
39
|
+
return root_metadata[:location]
|
40
|
+
end
|
41
|
+
|
42
|
+
# rspec issue https://github.com/rspec/rspec-core/issues/1243
|
43
|
+
location = (metadata[:location] || "").split(":").first
|
44
|
+
end
|
45
|
+
|
46
|
+
location
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.spec_path?(path)
|
50
|
+
path ||= ""
|
51
|
+
flags = File::FNM_PATHNAME | File::FNM_DOTMATCH
|
52
|
+
if File.const_defined?(:FNM_EXTGLOB) # ruby >= 2
|
53
|
+
flags |= File::FNM_EXTGLOB
|
54
|
+
end
|
55
|
+
pattern = ::RSpec.configuration.pattern
|
56
|
+
path = path.sub(/:\d+\z/, "")
|
57
|
+
path = Pathname.new(path).cleanpath.to_s
|
58
|
+
File.fnmatch(pattern, path, flags)
|
59
|
+
end
|
60
|
+
|
61
|
+
def dump_summary(*args)
|
62
|
+
if self.class.rspec_3?
|
63
|
+
notification = args[0]
|
64
|
+
write_summary(
|
65
|
+
notification.duration,
|
66
|
+
notification.example_count,
|
67
|
+
notification.failure_count,
|
68
|
+
notification.pending_count
|
69
|
+
)
|
70
|
+
else
|
71
|
+
write_summary(*args)
|
72
|
+
end
|
73
|
+
rescue
|
74
|
+
# nothing really we can do, at least don"t kill the test runner
|
75
|
+
end
|
76
|
+
|
77
|
+
# Write summary to temporary file for runner
|
78
|
+
def write_summary(duration, total, failures, pending)
|
79
|
+
_write do |f|
|
80
|
+
f.puts _message(total, failures, pending, duration)
|
81
|
+
f.puts _failed_paths.join("\n") if failures > 0
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
|
87
|
+
def _write(&block)
|
88
|
+
file = File.expand_path(TEMPORARY_FILE_PATH)
|
89
|
+
FileUtils.mkdir_p(File.dirname(file))
|
90
|
+
File.open(file, "w", &block)
|
91
|
+
end
|
92
|
+
|
93
|
+
def _failed_paths
|
94
|
+
failed = examples.select do |e|
|
95
|
+
e.execution_result[:status].to_s == "failed"
|
96
|
+
end
|
97
|
+
|
98
|
+
klass = self.class
|
99
|
+
failed.map { |e| klass.extract_spec_location(e.metadata) }.sort.uniq
|
100
|
+
end
|
101
|
+
|
102
|
+
def _message(example_count, failure_count, pending_count, duration)
|
103
|
+
message = "#{example_count} examples, #{failure_count} failures"
|
104
|
+
if pending_count > 0
|
105
|
+
message << " (#{pending_count} pending)"
|
106
|
+
end
|
107
|
+
message << " in #{duration.round(4)} seconds"
|
108
|
+
message
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -1,12 +1,14 @@
|
|
1
|
-
require
|
2
|
-
require 'launchy'
|
1
|
+
require "launchy"
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
require "guard/compat/test/helper"
|
4
|
+
require "guard/rspec/command"
|
5
|
+
|
6
|
+
RSpec.describe Guard::RSpec::Command do
|
7
|
+
let(:options) { {} }
|
8
|
+
let(:paths) { %w(path1 path2) }
|
7
9
|
let(:command) { Guard::RSpec::Command.new(paths, options) }
|
8
10
|
|
9
|
-
describe
|
11
|
+
describe ".initialize" do
|
10
12
|
|
11
13
|
it "sets paths at the end" do
|
12
14
|
expect(command).to match /path1 path2$/
|
@@ -17,20 +19,26 @@ describe Guard::RSpec::Command do
|
|
17
19
|
end
|
18
20
|
|
19
21
|
it "sets formatter" do
|
20
|
-
|
22
|
+
regexp = %r{-r .*/lib/guard/rspec_formatter.rb -f Guard::RSpecFormatter}
|
23
|
+
expect(command).to match(regexp)
|
21
24
|
end
|
22
25
|
|
23
26
|
context "with custom cmd" do
|
24
|
-
let(:options) { { cmd:
|
27
|
+
let(:options) { { cmd: "rspec -t ~slow" } }
|
25
28
|
|
26
29
|
it "uses custom cmd" do
|
27
|
-
expect(command).to match
|
30
|
+
expect(command).to match /^rspec -t ~slow/
|
28
31
|
end
|
29
32
|
end
|
30
33
|
|
31
34
|
context "with RSpec defined formatter" do
|
32
|
-
let(:formatters) { [
|
33
|
-
|
35
|
+
let(:formatters) { [%w(doc output)] }
|
36
|
+
|
37
|
+
before do
|
38
|
+
allow(RSpec::Core::ConfigurationOptions).to receive(:new) do
|
39
|
+
double(options: { formatters: formatters })
|
40
|
+
end
|
41
|
+
end
|
34
42
|
|
35
43
|
it "uses them" do
|
36
44
|
expect(command).to match %r{-f doc -o output}
|
@@ -44,12 +52,40 @@ describe Guard::RSpec::Command do
|
|
44
52
|
end
|
45
53
|
|
46
54
|
context "with formatter in cmd" do
|
47
|
-
let(:options) { { cmd:
|
55
|
+
let(:options) { { cmd: "rspec -f doc" } }
|
48
56
|
|
49
57
|
it "sets no other formatters" do
|
50
58
|
expect(command).to match %r{-f doc}
|
51
59
|
end
|
52
60
|
end
|
61
|
+
|
62
|
+
context "with cmd_additional_args" do
|
63
|
+
let(:options) { { cmd: "rspec", cmd_additional_args: "-f progress" } }
|
64
|
+
|
65
|
+
it "uses them" do
|
66
|
+
expect(command).to match %r{-f progress}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context ":chdir option present" do
|
71
|
+
let(:chdir) { "moduleA" }
|
72
|
+
let(:paths) do
|
73
|
+
%w[path1 path2].map { |p| "#{chdir}#{File::Separator}#{p}" }
|
74
|
+
end
|
75
|
+
|
76
|
+
let(:options) do
|
77
|
+
{
|
78
|
+
cmd: "cd #{chdir} && rspec",
|
79
|
+
chdir: chdir
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
it "removes chdir part from the path
|
84
|
+
as it should be present in the cmd" do
|
85
|
+
|
86
|
+
expect(command).to match %r{path1 path2}
|
87
|
+
end
|
88
|
+
end
|
53
89
|
end
|
54
90
|
|
55
91
|
end
|