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.
@@ -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 RSpecInteractive
4
- class Runner
5
- def initialize(args)
6
- RSpec.world.wants_to_quit = false
7
- @options = RSpec::Core::ConfigurationOptions.new(args)
8
- end
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
- def run()
11
- begin
12
- @options.configure(RSpec.configuration)
13
- return if RSpec.world.wants_to_quit
11
+ def run()
12
+ begin
13
+ @options.configure(::RSpec.configuration)
14
+ return if ::RSpec.world.wants_to_quit
14
15
 
15
- RSpec.configuration.load_spec_files
16
- ensure
17
- RSpec.world.announce_filters
18
- end
16
+ ::RSpec.configuration.load_spec_files
17
+ ensure
18
+ ::RSpec.world.announce_filters
19
+ end
19
20
 
20
- return RSpec.configuration.reporter.exit_early(RSpec.configuration.failure_exit_code) if RSpec.world.wants_to_quit
21
+ return ::RSpec.configuration.reporter.exit_early(::RSpec.configuration.failure_exit_code) if ::RSpec.world.wants_to_quit
21
22
 
22
- example_groups = RSpec.world.ordered_example_groups
23
- examples_count = RSpec.world.example_count(example_groups)
23
+ example_groups = ::RSpec.world.ordered_example_groups
24
+ examples_count = ::RSpec.world.example_count(example_groups)
24
25
 
25
- success = RSpec.configuration.reporter.report(examples_count) do |reporter|
26
- RSpec.configuration.with_suite_hooks do
27
- if examples_count == 0 && RSpec.configuration.fail_if_no_examples
28
- return RSpec.configuration.failure_exit_code
29
- end
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
- result = example_groups.map do |example_group|
32
- example_group.run(reporter)
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
- result.all?
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
- success && !RSpec.world.non_example_failure ? 0 : RSpec.configuration.failure_exit_code
40
- end
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
- def quit
43
- RSpec.world.wants_to_quit = true
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
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module RSpecInteractive
4
- VERSION = "0.1.0"
3
+ module RSpec
4
+ module Interactive
5
+ VERSION = "0.5.0"
6
+ end
5
7
  end
@@ -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 = RSpecInteractive::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 "listen"
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