rspec-interactive 0.1.0 → 0.5.0
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/.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
|