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.
@@ -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