rspec-interactive 0.1.0 → 0.5.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/Gemfile +3 -1
- data/Gemfile.lock +18 -7
- data/README.md +102 -0
- data/bin/console +1 -1
- data/bin/rspec-interactive +11 -2
- data/bin/test +2 -0
- data/examples/debugged_spec.rb +9 -0
- data/examples/failing_spec.rb +7 -0
- data/examples/other_passing_spec.rb +11 -0
- data/examples/passing_spec.rb +11 -0
- data/examples/spec_with_syntax_error.rb +5 -0
- data/lib/rspec-interactive.rb +101 -154
- data/lib/rspec-interactive/config.rb +23 -0
- data/lib/rspec-interactive/input_completer.rb +24 -0
- data/lib/rspec-interactive/rspec_command.rb +24 -0
- data/lib/rspec-interactive/rspec_config_cache.rb +96 -0
- data/lib/rspec-interactive/runner.rb +52 -29
- data/lib/rspec-interactive/version.rb +4 -2
- data/rspec-interactive.gemspec +5 -3
- data/run-with-local-dep.sh +36 -0
- data/tests/debugged_spec_test.rb +75 -0
- data/tests/eof_test.rb +10 -0
- data/tests/example_file_test.rb +54 -0
- data/tests/failing_spec_test.rb +41 -0
- data/tests/glob_test.rb +18 -0
- data/tests/line_number_test.rb +19 -0
- data/tests/passing_spec_test.rb +18 -0
- data/tests/rerun_failed_specs_test.rb +90 -0
- data/tests/spec_with_syntax_error_test.rb +30 -0
- data/tests/support/ansi.rb +32 -0
- data/tests/support/test_helper.rb +286 -0
- metadata +54 -5
- data/lib/repo.rb +0 -8
- data/rspec-console-0.1.0.gem +0 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Interactive
|
3
|
+
class Configuration
|
4
|
+
attr_accessor :watch_dirs, :configure_rspec, :on_class_load
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@watch_dirs = []
|
8
|
+
@configure_rspec = proc {}
|
9
|
+
@on_class_load = proc {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def configure_rspec(&block)
|
13
|
+
return @configure_rspec unless block
|
14
|
+
@configure_rspec = block
|
15
|
+
end
|
16
|
+
|
17
|
+
def on_class_load(&block)
|
18
|
+
return @on_class_load unless block
|
19
|
+
@on_class_load = block
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module RSpec::Interactive
|
2
|
+
class InputCompleter < Pry::InputCompleter
|
3
|
+
|
4
|
+
def rspec_completions(string)
|
5
|
+
line = Readline.line_buffer
|
6
|
+
before_current = Readline.point == string.length ? '' : line[0..(Readline.point - string.length)]
|
7
|
+
before_cursor = line[0..(Readline.point - 1)]
|
8
|
+
|
9
|
+
if line.match(/^ *rspec +/)
|
10
|
+
Dir[string + '*'].map { |filename| File.directory?(filename) ? "#{filename}/" : filename }
|
11
|
+
elsif before_current.strip.empty? && "rspec".match(/^#{Regexp.escape(string)}/)
|
12
|
+
["rspec "]
|
13
|
+
else
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(str, options = {})
|
19
|
+
rspec_completions = rspec_completions(str)
|
20
|
+
return rspec_completions if rspec_completions
|
21
|
+
super
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpec::Interactive
|
4
|
+
class RSpecCommand < Pry::ClassCommand
|
5
|
+
match 'rspec'
|
6
|
+
description "Invoke RSpec."
|
7
|
+
|
8
|
+
banner <<-BANNER
|
9
|
+
Usage: rspec [arguments]
|
10
|
+
|
11
|
+
See https://relishapp.com/rspec/rspec-core/docs/command-line.
|
12
|
+
BANNER
|
13
|
+
|
14
|
+
command_options(
|
15
|
+
:keep_retval => false
|
16
|
+
)
|
17
|
+
|
18
|
+
def process
|
19
|
+
RSpec::Interactive.rspec(args)
|
20
|
+
end
|
21
|
+
|
22
|
+
Pry::Commands.add_command(::RSpec::Interactive::RSpecCommand)
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# Copied from https://github.com/nviennot/rspec-console/blob/master/lib/rspec-console/config_cache.rb
|
2
|
+
class RSpec::Interactive::ConfigCache
|
3
|
+
# We have to reset the RSpec.configuration, because it contains a lot of
|
4
|
+
# information related to the current test (what's running, what are the
|
5
|
+
# different test results, etc).
|
6
|
+
#
|
7
|
+
# RSpec.configuration gets also loaded with a bunch of stuff from the
|
8
|
+
# 'spec/spec_helper.rb' file. Often that instance is extended with other
|
9
|
+
# modules (FactoryGirl, Mocha,...) and we don't want to replace requires with
|
10
|
+
# load all around the place.
|
11
|
+
#
|
12
|
+
# Instead, we proxy and record whatever is done to RSpec.configuration during
|
13
|
+
# the first invocation of require('spec_helper'). This is done by interposing
|
14
|
+
# the RecordingProxy class on of RSpec.configuration.
|
15
|
+
attr_accessor :config_proxy, :root_shared_examples
|
16
|
+
|
17
|
+
class RecordingProxy < Struct.new(:target, :recorded_messages)
|
18
|
+
[:include, :extend].each do |method|
|
19
|
+
define_method(method) do |*args|
|
20
|
+
method_missing(method, *args)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_missing(method, *args, &block)
|
25
|
+
self.recorded_messages << [method, args, block]
|
26
|
+
self.target.send(method, *args, &block)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def record_configuration(&configuration_block)
|
31
|
+
ensure_configuration_setter!
|
32
|
+
|
33
|
+
original_config = ::RSpec.configuration
|
34
|
+
::RSpec.configuration = RecordingProxy.new(original_config, [])
|
35
|
+
|
36
|
+
configuration_block.call # spec helper is called during this yield, see #reset
|
37
|
+
|
38
|
+
self.config_proxy = ::RSpec.configuration
|
39
|
+
::RSpec.configuration = original_config
|
40
|
+
|
41
|
+
stash_shared_examples
|
42
|
+
|
43
|
+
forward_rspec_config_singleton_to(self.config_proxy)
|
44
|
+
end
|
45
|
+
|
46
|
+
def replay_configuration
|
47
|
+
::RSpec.configure do |config|
|
48
|
+
self.config_proxy.recorded_messages.each do |method, args, block|
|
49
|
+
# reporter caches config.output_stream which is not good as it
|
50
|
+
# prevents the runner to use a custom stdout.
|
51
|
+
next if method == :reporter
|
52
|
+
config.send(method, *args, &block)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
restore_shared_examples
|
57
|
+
|
58
|
+
forward_rspec_config_singleton_to(self.config_proxy)
|
59
|
+
end
|
60
|
+
|
61
|
+
def has_recorded_config?
|
62
|
+
!!self.config_proxy
|
63
|
+
end
|
64
|
+
|
65
|
+
def forward_rspec_config_singleton_to(config_proxy)
|
66
|
+
# an old version of rspec-rails/lib/rspec/rails/view_rendering.rb adds
|
67
|
+
# methods on the configuration singleton. This takes care of that.
|
68
|
+
::RSpec.configuration.singleton_class
|
69
|
+
.send(:define_method, :method_missing, &config_proxy.method(:send))
|
70
|
+
end
|
71
|
+
|
72
|
+
def stash_shared_examples
|
73
|
+
self.root_shared_examples = ::RSpec.world.shared_example_group_registry.send(:shared_example_groups).dup
|
74
|
+
end
|
75
|
+
|
76
|
+
def restore_shared_examples
|
77
|
+
shared_example_groups = ::RSpec.world.shared_example_group_registry.send(:shared_example_groups)
|
78
|
+
shared_example_groups.clear
|
79
|
+
|
80
|
+
self.root_shared_examples.each do |context, hash|
|
81
|
+
hash.each do |name, shared_module|
|
82
|
+
shared_example_groups[context][name] = shared_module
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def ensure_configuration_setter!
|
88
|
+
return if RSpec.respond_to?(:configuration=)
|
89
|
+
|
90
|
+
::RSpec.instance_eval do
|
91
|
+
def self.configuration=(value)
|
92
|
+
@configuration = value
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -1,46 +1,69 @@
|
|
1
1
|
require 'rspec/core'
|
2
2
|
|
3
|
-
module
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
module RSpec
|
4
|
+
module Interactive
|
5
|
+
class Runner
|
6
|
+
def initialize(args)
|
7
|
+
::RSpec.world.wants_to_quit = false
|
8
|
+
@options = ::RSpec::Core::ConfigurationOptions.new(args)
|
9
|
+
end
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
def run()
|
12
|
+
begin
|
13
|
+
@options.configure(::RSpec.configuration)
|
14
|
+
return if ::RSpec.world.wants_to_quit
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
::RSpec.configuration.load_spec_files
|
17
|
+
ensure
|
18
|
+
::RSpec.world.announce_filters
|
19
|
+
end
|
19
20
|
|
20
|
-
|
21
|
+
return ::RSpec.configuration.reporter.exit_early(::RSpec.configuration.failure_exit_code) if ::RSpec.world.wants_to_quit
|
21
22
|
|
22
|
-
|
23
|
-
|
23
|
+
example_groups = ::RSpec.world.ordered_example_groups
|
24
|
+
examples_count = ::RSpec.world.example_count(example_groups)
|
24
25
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
26
|
+
exit_code = ::RSpec.configuration.reporter.report(examples_count) do |reporter|
|
27
|
+
::RSpec.configuration.with_suite_hooks do
|
28
|
+
if examples_count == 0 && ::RSpec.configuration.fail_if_no_examples
|
29
|
+
return ::RSpec.configuration.failure_exit_code
|
30
|
+
end
|
31
|
+
|
32
|
+
group_results = example_groups.map do |example_group|
|
33
|
+
example_group.run(reporter)
|
34
|
+
end
|
30
35
|
|
31
|
-
|
32
|
-
|
36
|
+
success = group_results.all?
|
37
|
+
exit_code = success ? 0 : 1
|
38
|
+
if ::RSpec.world.non_example_failure
|
39
|
+
success = false
|
40
|
+
exit_code = ::RSpec.configuration.failure_exit_code
|
41
|
+
end
|
42
|
+
persist_example_statuses
|
43
|
+
exit_code
|
33
44
|
end
|
45
|
+
end
|
34
46
|
|
35
|
-
|
47
|
+
if exit_code != 0 && ::RSpec.configuration.example_status_persistence_file_path
|
48
|
+
::RSpec.configuration.output_stream.puts "Rerun failures by executing the previous command with --only-failures or --next-failure."
|
49
|
+
::RSpec.configuration.output_stream.puts
|
36
50
|
end
|
51
|
+
|
52
|
+
exit_code
|
37
53
|
end
|
38
54
|
|
39
|
-
|
40
|
-
|
55
|
+
def quit
|
56
|
+
::RSpec.world.wants_to_quit = true
|
57
|
+
end
|
58
|
+
|
59
|
+
def persist_example_statuses
|
60
|
+
return if ::RSpec.configuration.dry_run
|
61
|
+
return unless (path = ::RSpec.configuration.example_status_persistence_file_path)
|
41
62
|
|
42
|
-
|
43
|
-
|
63
|
+
::RSpec::Core::ExampleStatusPersister.persist(::RSpec.world.all_examples, path)
|
64
|
+
rescue SystemCallError => e
|
65
|
+
::RSpec.configuration.error_stream.puts "warning: failed to write results to #{path}"
|
66
|
+
end
|
44
67
|
end
|
45
68
|
end
|
46
69
|
end
|
data/rspec-interactive.gemspec
CHANGED
@@ -4,7 +4,7 @@ require_relative "lib/rspec-interactive/version"
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = "rspec-interactive"
|
7
|
-
spec.version =
|
7
|
+
spec.version = RSpec::Interactive::VERSION
|
8
8
|
spec.authors = ["Nick Dower"]
|
9
9
|
spec.email = ["nicholasdower@gmail.com"]
|
10
10
|
|
@@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
|
|
16
16
|
|
17
17
|
spec.metadata["homepage_uri"] = spec.homepage
|
18
18
|
spec.metadata["source_code_uri"] = "https://github.com/nicholasdower/rspec-interactive"
|
19
|
-
spec.metadata["changelog_uri"] = "https://github.com/nicholasdower/rspec-interactive"
|
19
|
+
spec.metadata["changelog_uri"] = "https://github.com/nicholasdower/rspec-interactive/releases"
|
20
20
|
|
21
21
|
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
22
22
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
@@ -25,5 +25,7 @@ Gem::Specification.new do |spec|
|
|
25
25
|
spec.executables << 'rspec-interactive'
|
26
26
|
spec.require_paths = ["lib"]
|
27
27
|
|
28
|
-
spec.add_dependency
|
28
|
+
spec.add_dependency 'rspec-core'
|
29
|
+
spec.add_dependency 'listen'
|
30
|
+
spec.add_dependency 'pry'
|
29
31
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
# Used to quickly test a development version of a gem in the current
|
4
|
+
# directory. Adds a local copy of the specified gem, at the specified
|
5
|
+
# path, to the current directory's Gemfile and executes the specified
|
6
|
+
# command. Upon completion, the Gemfile will be returned to its
|
7
|
+
# previous state.
|
8
|
+
|
9
|
+
if [ $# -ne 3 ]; then
|
10
|
+
echo "usage: $0 <gem-name> <path> <command>" >&2
|
11
|
+
exit 1
|
12
|
+
fi
|
13
|
+
|
14
|
+
GEM_NAME=$1
|
15
|
+
GEM_PATH=$2
|
16
|
+
COMMAND=$3
|
17
|
+
|
18
|
+
if [ -d $GEM_NAME ]; then
|
19
|
+
echo "directory exists: $GEM_NAME" >&2
|
20
|
+
exit 1
|
21
|
+
fi
|
22
|
+
|
23
|
+
OLD_DEP=$(grep "'$GEM_NAME'" Gemfile)
|
24
|
+
if [ $? -ne 0 ]; then
|
25
|
+
echo "$GEM_NAME not found in Gemfile" >&2
|
26
|
+
exit 1
|
27
|
+
fi
|
28
|
+
|
29
|
+
cp Gemfile Gemfile.save
|
30
|
+
cp Gemfile.lock Gemfile.lock.save
|
31
|
+
cp -R $GEM_PATH $GEM_NAME
|
32
|
+
sed -i '' -E "s/^( *gem '$GEM_NAME').*/\1, path: '$GEM_NAME'/g" Gemfile
|
33
|
+
$COMMAND
|
34
|
+
rm -rf $GEM_NAME
|
35
|
+
mv Gemfile.lock.save Gemfile.lock
|
36
|
+
mv Gemfile.save Gemfile
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require_relative 'support/test_helper'
|
2
|
+
|
3
|
+
Test.test "debugged spec" do
|
4
|
+
await_prompt
|
5
|
+
input "rspec examples/debugged_spec.rb"
|
6
|
+
await_prompt
|
7
|
+
input "exit"
|
8
|
+
await_prompt
|
9
|
+
input "exit"
|
10
|
+
await_termination
|
11
|
+
expect_output <<~EOF
|
12
|
+
[1] pry(main)> rspec examples/debugged_spec.rb
|
13
|
+
|
14
|
+
From: /Users/nickdower/Development/rspec-interactive/examples/debugged_spec.rb:6 :
|
15
|
+
|
16
|
+
1: require 'rspec/core'
|
17
|
+
2: require 'pry'
|
18
|
+
3:
|
19
|
+
4: describe "example spec" do
|
20
|
+
5: it "gets debugged" do
|
21
|
+
=> 6: binding.pry
|
22
|
+
7: expect(true).to eq(true)
|
23
|
+
8: end
|
24
|
+
9: end
|
25
|
+
|
26
|
+
[1] pry(#<RSpec::ExampleGroups::ExampleSpec>)> exit
|
27
|
+
.
|
28
|
+
|
29
|
+
Finished in 0 seconds (files took 0 seconds to load)
|
30
|
+
1 example, 0 failures
|
31
|
+
|
32
|
+
[2] pry(main)> exit
|
33
|
+
EOF
|
34
|
+
end
|
35
|
+
|
36
|
+
Test.test "debugger does not add to history" do
|
37
|
+
await_prompt
|
38
|
+
input "rspec examples/debugged_spec.rb"
|
39
|
+
await_prompt
|
40
|
+
input '"this should not show up in history"'
|
41
|
+
await_prompt
|
42
|
+
input "exit"
|
43
|
+
await_prompt
|
44
|
+
input "exit"
|
45
|
+
await_termination
|
46
|
+
expect_output <<~EOF
|
47
|
+
[1] pry(main)> rspec examples/debugged_spec.rb
|
48
|
+
|
49
|
+
From: /Users/nickdower/Development/rspec-interactive/examples/debugged_spec.rb:6 :
|
50
|
+
|
51
|
+
1: require 'rspec/core'
|
52
|
+
2: require 'pry'
|
53
|
+
3:
|
54
|
+
4: describe "example spec" do
|
55
|
+
5: it "gets debugged" do
|
56
|
+
=> 6: binding.pry
|
57
|
+
7: expect(true).to eq(true)
|
58
|
+
8: end
|
59
|
+
9: end
|
60
|
+
|
61
|
+
[1] pry(#<RSpec::ExampleGroups::ExampleSpec>)> "this should not show up in history"
|
62
|
+
=> "this should not show up in history"
|
63
|
+
[2] pry(#<RSpec::ExampleGroups::ExampleSpec>)> exit
|
64
|
+
.
|
65
|
+
|
66
|
+
Finished in 0 seconds (files took 0 seconds to load)
|
67
|
+
1 example, 0 failures
|
68
|
+
|
69
|
+
[2] pry(main)> exit
|
70
|
+
EOF
|
71
|
+
|
72
|
+
expect_history <<~EOF
|
73
|
+
rspec examples/debugged_spec.rb
|
74
|
+
EOF
|
75
|
+
end
|
data/tests/eof_test.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require_relative 'support/test_helper'
|
2
|
+
|
3
|
+
Test.test "exiting via ctrl-d" do
|
4
|
+
await_prompt
|
5
|
+
ctrl_d
|
6
|
+
await_termination
|
7
|
+
# No newlines in tests because we return false from tty? in test_helper.rb.
|
8
|
+
# In the real app, Pry will add a newline because tty? is true.
|
9
|
+
expect_output '[1] pry(main)> '
|
10
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require_relative 'support/test_helper'
|
2
|
+
|
3
|
+
RSpec.configuration.backtrace_exclusion_patterns = [ /.*/ ]
|
4
|
+
RSpec.configuration.backtrace_inclusion_patterns = [ /examples\/failing_spec.rb/ ]
|
5
|
+
|
6
|
+
examples = Tempfile.new('examples')
|
7
|
+
|
8
|
+
config = Tempfile.new('config')
|
9
|
+
config.write <<~EOF
|
10
|
+
RSpec.configuration.example_status_persistence_file_path = "#{examples.path}"
|
11
|
+
EOF
|
12
|
+
config.rewind
|
13
|
+
|
14
|
+
Test.test "failing spec with example file", config_path: config.path do
|
15
|
+
await_prompt
|
16
|
+
input "rspec examples/failing_spec.rb"
|
17
|
+
await_prompt
|
18
|
+
input "exit"
|
19
|
+
await_termination
|
20
|
+
expect_output <<~EOF
|
21
|
+
[1] pry(main)> rspec examples/failing_spec.rb
|
22
|
+
F
|
23
|
+
|
24
|
+
Failures:
|
25
|
+
|
26
|
+
1) example spec fails
|
27
|
+
Failure/Error: expect(true).to eq(false)
|
28
|
+
|
29
|
+
expected: false
|
30
|
+
got: true
|
31
|
+
|
32
|
+
(compared using ==)
|
33
|
+
|
34
|
+
Diff:
|
35
|
+
@@ -1 +1 @@
|
36
|
+
-false
|
37
|
+
+true
|
38
|
+
# ./examples/failing_spec.rb:5:in `block (2 levels) in <top (required)>'
|
39
|
+
|
40
|
+
Finished in 0 seconds (files took 0 seconds to load)
|
41
|
+
1 example, 1 failure
|
42
|
+
|
43
|
+
Failed examples:
|
44
|
+
|
45
|
+
rspec ./examples/failing_spec.rb:4 # example spec fails
|
46
|
+
|
47
|
+
Rerun failures by executing the previous command with --only-failures or --next-failure.
|
48
|
+
|
49
|
+
[2] pry(main)> exit
|
50
|
+
EOF
|
51
|
+
end
|
52
|
+
|
53
|
+
config.close
|
54
|
+
examples.close
|