rspec-interactive 0.3.0 → 0.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e3ff2b23a6cf52444bf173292d0e161006bbcf764f153f31ea9052d0f0c0f912
4
- data.tar.gz: ddb6567801ceb90ff76054d554d3d63ecc951a92584395d809f691c10520555f
3
+ metadata.gz: 858197f05a88cbac9b4acf36c6220a79c15bc008945fa87cef7c008e20ca172d
4
+ data.tar.gz: b9a34852edc5bc544e9117ebfef1c6190e7ecae73cb7a29e2929f82743e72ed5
5
5
  SHA512:
6
- metadata.gz: 2aa35c7ed2108213d90e97d9b8d30208f28993ab2e8111127024dec1ddf7404226a00a5e7c2a99dd47a026efc18f043558e8db3d87373e3c486bf4de8fac32a8
7
- data.tar.gz: 35147d8a59a432d163dfd309db75c92100c781e5e1d6fa925df431ec70cc849b6886f1bd0cac9bb27e53ee9b6094b7332012520f69b29ed30e8178af5dbe6125
6
+ metadata.gz: ec6835e7baeb24d4126edb542629529e308ab9ecd79a28697c03f8290a8c6b5d15bbe6fd645338e870df124a073638c45bab0f790f8bb09becfd4a613d995cc0
7
+ data.tar.gz: cbf961a6c0a60cfc3cced32778f34ebbf7ae3e97399c017148cbe88e1229f5d96b2ab6f374ad4bc68e6eaaf9e7f18013e5510cab76521f4d70c0e90cb485b126
data/.gitignore CHANGED
@@ -1,4 +1,3 @@
1
1
  rspec-interactive-*.gem
2
2
  .rspec_interactive_history
3
3
  .rspec_interactive_config
4
- .rspec_interactive_results
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rspec-interactive (0.2.0)
4
+ rspec-interactive (0.6.0)
5
5
  listen
6
6
  pry
7
7
  rspec-core
@@ -11,7 +11,7 @@ GEM
11
11
  specs:
12
12
  coderay (1.1.3)
13
13
  diff-lcs (1.4.4)
14
- ffi (1.15.0)
14
+ ffi (1.15.3)
15
15
  listen (3.5.1)
16
16
  rb-fsevent (~> 0.10, >= 0.10.3)
17
17
  rb-inotify (~> 0.9, >= 0.9.10)
data/README.md CHANGED
@@ -1,7 +1,5 @@
1
1
  # RSpec Interactive
2
2
 
3
- **WARNING: New (or maybe old by now), experimental, untested and poorly documented. Use at your own risk.**
4
-
5
3
  An Pry console capable of running specs.
6
4
 
7
5
  ## Installation & Configuration
@@ -12,38 +10,37 @@ Install:
12
10
  gem 'rspec-interactive'
13
11
  ```
14
12
 
15
- Add a config file named `.rspec_interactive_config`:
16
-
17
- ```json
18
- {
19
- "configs": [
20
- {
21
- "name": "spec",
22
- "watch_dirs": ["app"],
23
- "init_script": "spec/spec_helper.rb"
24
- },
25
- {
26
- "name": "spec_integration",
27
- "watch_dirs": ["app"],
28
- "init_script": "spec_integration/integration_helper.rb"
29
- }
30
- ]
31
- }
13
+ Add a config file which configures RSpec and RSpec::Interactive, for example `spec/rspec_interactive.rb`:
14
+
15
+ ```ruby
16
+ RSpec::Interactive.configure do |config|
17
+ # Directories to watch for file changes. When a file changes, it will be reloaded like `load 'path/to/file'`.
18
+ config.watch_dirs += ["app", "lib", "config"]
19
+
20
+ # This block is invoked on startup. RSpec configuration must happen here so that it can be reloaded before each test run.
21
+ config.configure_rspec do
22
+ require './spec/spec_helper.rb'
23
+ end
24
+
25
+ # Invoked whenever a class is loaded due to a file change in one of the watch_dirs.
26
+ config.on_class_load do |clazz|
27
+ clazz.clear_validators! if clazz < ApplicationRecord
28
+ end
29
+ end
32
30
  ```
33
31
 
34
32
  Update `.gitignore`
35
33
 
36
34
  ```shell
37
35
  echo '.rspec_interactive_history' >> .gitignore
38
- echo '.rspec_interactive_results' >> .gitignore
39
36
  ```
40
37
 
41
38
  ## Usage
42
39
 
43
- See more examples below.
40
+ Optionally, specify a config file with `--config <config-file>`. Optionally, specify arguments to an initial RSpec invocation with `--initial-rspec-args <initial-rspec-args>`.
44
41
 
45
42
  ```shell
46
- bundle exec rspec-interactive [spec name]
43
+ bundle exec rspec-interactive [--config <config-file>] [--initial-rspec-args <initial-rspec-args>]
47
44
  ```
48
45
 
49
46
  ## Example Usage In This Repo
@@ -54,6 +51,12 @@ Start:
54
51
  bundle exec rspec-interactive
55
52
  ```
56
53
 
54
+ Start with an initial RSpec invocation:
55
+
56
+ ```shell
57
+ bundle exec rspec-interactive --initial-rspec-args examples/passing_spec.rb
58
+ ```
59
+
57
60
  Run a passing spec:
58
61
 
59
62
  ```shell
@@ -101,3 +104,9 @@ Exit:
101
104
  ```shell
102
105
  bundle exec bin/test
103
106
  ```
107
+
108
+ ## Releasing
109
+
110
+ ```shell
111
+ ./scripts/release.sh <version>
112
+ ```
@@ -1,5 +1,20 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ require 'optparse'
3
4
  require 'rspec-interactive'
4
5
 
5
- RSpec::Interactive.start(ARGV)
6
+ @options = {}
7
+ parser = OptionParser.new do |opts|
8
+ opts.banner = "Starts an interactive RSpec shell.\n\n"\
9
+ "Usage: bundle exec rspec-interactive [-c config-file] <initial-rspec-args>"
10
+
11
+ opts.on("-c", "--config <config-file>", String, "Optional. Path to the RSpec Interactive config file.") do |config_file|
12
+ @options[:config_file] = config_file
13
+ end
14
+
15
+ opts.on("-r", "--initial-rspec-args <initial-rspec-args>", String, "Optional. Arguments to pass to an initial invocation of RSpec.") do |initial_rspec_args|
16
+ @options[:initial_rspec_args] = initial_rspec_args
17
+ end
18
+ end.parse!
19
+
20
+ RSpec::Interactive.start(config_file: @options[:config_file], initial_rspec_args: @options[:initial_rspec_args])
@@ -0,0 +1,11 @@
1
+ require 'rspec/core'
2
+
3
+ describe "other example spec" do
4
+ it "succeeds" do
5
+ expect(true).to eq(true)
6
+ end
7
+
8
+ it "succeeds again" do
9
+ expect(true).to eq(true)
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ require 'rspec/core'
2
+
3
+ describe "spec with syntax error" do
4
+ it "succeeds
5
+ end
@@ -3,32 +3,30 @@ require 'listen'
3
3
  require 'pry'
4
4
  require 'readline'
5
5
  require 'rspec/core'
6
+ require 'shellwords'
6
7
 
7
8
  require 'rspec-interactive/runner'
8
- require 'rspec-interactive/config_cache'
9
+ require 'rspec-interactive/config'
10
+ require 'rspec-interactive/rspec_config_cache'
9
11
  require 'rspec-interactive/input_completer'
10
12
  require 'rspec-interactive/rspec_command'
11
13
 
12
14
  module RSpec
13
15
  module Interactive
14
16
 
15
- HISTORY_FILE = '.rspec_interactive_history'.freeze
16
- CONFIG_FILE = '.rspec_interactive_config'.freeze
17
+ DEFAULT_HISTORY_FILE = '.rspec_interactive_history'.freeze
17
18
 
18
- class <<self
19
- attr_accessor :readline, :input_stream, :output_stream, :error_stream
20
- attr_accessor :config, :stty_save, :mutex, :config_cache, :runner, :results, :result, :updated_files
19
+ class << self
20
+ attr_accessor :configuration
21
21
  end
22
22
 
23
- def self.start(args, input_stream: STDIN, output_stream: STDOUT, error_stream: STDERR)
24
- if args.size > 1
25
- @error_stream.puts "expected 0 or 1 argument, got: #{args.join(', ')}"
26
- exit!(1)
27
- end
23
+ def self.configure(&block)
24
+ block.call(@configuration)
25
+ end
28
26
 
27
+ def self.start(config_file: nil, initial_rspec_args: nil, history_file: DEFAULT_HISTORY_FILE, input_stream: STDIN, output_stream: STDOUT, error_stream: STDERR)
28
+ @history_file = history_file
29
29
  @updated_files = []
30
- @results = []
31
- @config = get_config(args[0])
32
30
  @stty_save = %x`stty -g`.chomp
33
31
  @mutex = Mutex.new
34
32
  @output_stream = output_stream
@@ -36,13 +34,24 @@ module RSpec
36
34
  @error_stream = error_stream
37
35
  @config_cache = RSpec::Interactive::ConfigCache.new
38
36
 
39
- load_rspec_config
37
+ @configuration = Configuration.new
38
+ load config_file if config_file
39
+
40
+ @config_cache.record_configuration { @configuration.configure_rspec.call }
41
+
40
42
  check_rails
41
43
  start_file_watcher
42
44
  trap_interrupt
43
45
  configure_pry
44
46
 
47
+ if initial_rspec_args
48
+ open(@history_file, 'a') { |f| f.puts "rspec #{initial_rspec_args.strip}" }
49
+ rspec Shellwords.split(initial_rspec_args)
50
+ end
51
+
45
52
  Pry.start
53
+ @listener.stop if @listener
54
+ 0
46
55
  end
47
56
 
48
57
  def self.check_rails
@@ -53,10 +62,6 @@ module RSpec
53
62
  end
54
63
  end
55
64
 
56
- def self.load_rspec_config
57
- @config_cache.record_configuration(&rspec_configuration)
58
- end
59
-
60
65
  def self.configure_rspec
61
66
  RSpec.configure do |config|
62
67
  config.error_stream = @error_stream
@@ -64,55 +69,6 @@ module RSpec
64
69
  end
65
70
  end
66
71
 
67
- def self.rspec_configuration
68
- proc do
69
- if @config["init_script"]
70
- $LOAD_PATH << '.'
71
- require @config["init_script"]
72
- end
73
- end
74
- end
75
-
76
- def self.get_config(name = nil)
77
- unless File.exists? CONFIG_FILE
78
- @error_stream.puts "warning: #{CONFIG_FILE} not found, using default config"
79
- return {}
80
- end
81
-
82
- configs = JSON.parse(File.read(CONFIG_FILE))["configs"] || []
83
- if configs.empty?
84
- @error_stream.puts "no configs found in: #{CONFIG_FILE}"
85
- exit!(1)
86
- end
87
-
88
- # If a specific config was specified, use it.
89
- if name
90
- config = configs.find { |e| e["name"] == name }
91
- return config if config
92
- @error_stream.puts "invalid config: #{name}"
93
- exit!(1)
94
- end
95
-
96
- # If there is only one, use it.
97
- if configs.size == 1
98
- return configs[0]
99
- end
100
-
101
- # Ask the user which to use.
102
- loop do
103
- names = configs.map { |e| e["name"] }
104
- names[0] = "#{names[0]} (default)"
105
- print "Multiple simultaneous configs not yet supported. Please choose a config. #{names.join(', ')}: "
106
- answer = @input_stream.gets.chomp
107
- if answer.strip.empty?
108
- return configs[0]
109
- end
110
- config = configs.find { |e| e["name"] == answer }
111
- return config if config
112
- @error_stream.puts "invalid config: #{answer}"
113
- end
114
- end
115
-
116
72
  def self.trap_interrupt
117
73
  trap('INT') do
118
74
  if @runner
@@ -127,15 +83,15 @@ module RSpec
127
83
  end
128
84
 
129
85
  def self.start_file_watcher
130
- return unless @config["watch_dirs"]
86
+ return if @configuration.watch_dirs.empty?
131
87
 
132
88
  # Only polling seems to work in Docker.
133
- listener = Listen.to(*@config["watch_dirs"], only: /\.rb$/, force_polling: true) do |modified, added, removed|
89
+ @listener = Listen.to(*@configuration.watch_dirs, only: /\.rb$/, force_polling: true) do |modified, added, removed|
134
90
  @mutex.synchronize do
135
91
  @updated_files.concat(added + modified)
136
92
  end
137
93
  end
138
- listener.start
94
+ @listener.start
139
95
  end
140
96
 
141
97
  def self.configure_pry
@@ -151,7 +107,52 @@ module RSpec
151
107
  # Use custom completer to get file completion.
152
108
  Pry.config.completer = RSpec::Interactive::InputCompleter
153
109
 
154
- Pry.config.history_file = HISTORY_FILE
110
+ Pry.config.history_file = @history_file
111
+ end
112
+
113
+ def self.rspec(args)
114
+ parsed_args = args.flat_map do |arg|
115
+ if arg.match(/[\*\?\[]/)
116
+ glob = Dir.glob(arg)
117
+ glob.empty? ? [arg] : glob
118
+ else
119
+ [arg]
120
+ end
121
+ end
122
+
123
+ @mutex.synchronize do
124
+ @updated_files.uniq.each do |filename|
125
+ @output_stream.puts "modified: #{filename}"
126
+ trace = TracePoint.new(:class) do |tp|
127
+ @configuration.on_class_load.call(tp.self)
128
+ end
129
+ trace.enable
130
+ load filename
131
+ trace.disable
132
+ @output_stream.puts
133
+ end
134
+ @updated_files.clear
135
+ end
136
+
137
+ @runner = RSpec::Interactive::Runner.new(parsed_args)
138
+
139
+ # Stop saving history in case a new Pry session is started for debugging.
140
+ Pry.config.history_save = false
141
+
142
+ # RSpec::Interactive-specific RSpec configuration
143
+ configure_rspec
144
+
145
+ # Run.
146
+ exit_code = @runner.run
147
+ @runner = nil
148
+
149
+ # Reenable history
150
+ Pry.config.history_save = true
151
+
152
+ # Reset
153
+ RSpec.clear_examples
154
+ RSpec.reset
155
+ @config_cache.replay_configuration
155
156
  end
156
157
  end
157
158
  end
@@ -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
@@ -16,52 +16,7 @@ module RSpec::Interactive
16
16
  )
17
17
 
18
18
  def process
19
- parsed_args = args.flat_map do |arg|
20
- if arg.match(/[\*\?\[]/)
21
- glob = Dir.glob(arg)
22
- glob.empty? ? [arg] : glob
23
- else
24
- [arg]
25
- end
26
- end
27
-
28
- RSpec::Interactive.mutex.synchronize do
29
- RSpec::Interactive.updated_files.uniq.each do |filename|
30
- load filename
31
- end
32
- RSpec::Interactive.updated_files.clear
33
- end
34
-
35
- RSpec::Interactive.runner = RSpec::Interactive::Runner.new(parsed_args)
36
-
37
- # Stop saving history in case a new Pry session is started for debugging.
38
- Pry.config.history_save = false
39
-
40
- # RSpec::Interactive-specific RSpec configuration
41
- RSpec::Interactive.configure_rspec
42
-
43
- # Run.
44
- result = RSpec::Interactive.runner.run
45
- RSpec::Interactive.runner = nil
46
-
47
- # Save results
48
- RSpec::Interactive.results << result
49
- RSpec::Interactive.result = result
50
-
51
- # Reenable history
52
- Pry.config.history_save = true
53
-
54
- # Reset
55
- RSpec.clear_examples
56
- RSpec.reset
57
- RSpec::Interactive.config_cache.replay_configuration
58
-
59
- if !RSpec::Interactive.result.success && ::RSpec.configuration.example_status_persistence_file_path
60
- RSpec::Interactive.output_stream.puts "Rerun failures by executing the previous command with --only-failures or --next-failure."
61
- RSpec::Interactive.output_stream.puts
62
- end
63
-
64
- result
19
+ RSpec::Interactive.rspec(args)
65
20
  end
66
21
 
67
22
  Pry::Commands.add_command(::RSpec::Interactive::RSpecCommand)
@@ -2,29 +2,6 @@ require 'rspec/core'
2
2
 
3
3
  module RSpec
4
4
  module Interactive
5
- class ExampleGroupResult
6
- attr_accessor :group, :success
7
-
8
- def initialize(group, success)
9
- @group = group
10
- @success = success
11
- end
12
- end
13
-
14
- class Result
15
- attr_accessor :group_results, :success, :exit_code
16
-
17
- def initialize(group_results, success, exit_code)
18
- @group_results = group_results
19
- @success = success
20
- @exit_code = exit_code
21
- end
22
-
23
- def inspect(original = false)
24
- original ? super() : "<RSpec::Interactive::Result @success=#{@success}, @group_results=[...]>"
25
- end
26
- end
27
-
28
5
  class Runner
29
6
  def initialize(args)
30
7
  ::RSpec.world.wants_to_quit = false
@@ -46,28 +23,33 @@ module RSpec
46
23
  example_groups = ::RSpec.world.ordered_example_groups
47
24
  examples_count = ::RSpec.world.example_count(example_groups)
48
25
 
49
- result = ::RSpec.configuration.reporter.report(examples_count) do |reporter|
26
+ exit_code = ::RSpec.configuration.reporter.report(examples_count) do |reporter|
50
27
  ::RSpec.configuration.with_suite_hooks do
51
28
  if examples_count == 0 && ::RSpec.configuration.fail_if_no_examples
52
29
  return ::RSpec.configuration.failure_exit_code
53
30
  end
54
31
 
55
32
  group_results = example_groups.map do |example_group|
56
- group_success = example_group.run(reporter)
57
- ExampleGroupResult.new(example_group, group_success)
33
+ example_group.run(reporter)
58
34
  end
59
35
 
60
- success = group_results.all?(&:success)
36
+ success = group_results.all?
61
37
  exit_code = success ? 0 : 1
62
38
  if ::RSpec.world.non_example_failure
63
39
  success = false
64
40
  exit_code = ::RSpec.configuration.failure_exit_code
65
41
  end
66
42
  persist_example_statuses
67
- Result.new(group_results, success, exit_code)
43
+ exit_code
68
44
  end
69
45
  end
70
- result
46
+
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
50
+ end
51
+
52
+ exit_code
71
53
  end
72
54
 
73
55
  def quit
@@ -80,7 +62,7 @@ module RSpec
80
62
 
81
63
  ::RSpec::Core::ExampleStatusPersister.persist(::RSpec.world.all_examples, path)
82
64
  rescue SystemCallError => e
83
- RSpec::Interactive.error_stream.puts "warning: failed to write results to #{path}"
65
+ ::RSpec.configuration.error_stream.puts "warning: failed to write results to #{path}"
84
66
  end
85
67
  end
86
68
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RSpec
4
4
  module Interactive
5
- VERSION = "0.3.0"
5
+ VERSION = "0.6.1"
6
6
  end
7
7
  end
@@ -0,0 +1,25 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ if [ $# -ne 1 ]; then
6
+ echo "usage: $0 <version>" >&2
7
+ exit 1
8
+ fi
9
+
10
+ if [ -n "$(git status --porcelain)" ]; then
11
+ echo "error: stage or commit your changes." >&2
12
+ exit 1;
13
+ fi
14
+
15
+ NEW_VERSION=$1
16
+ CURRENT_VERSION=$(grep VERSION lib/rspec-interactive/version.rb | cut -d'"' -f 2)
17
+
18
+ echo "Updating from v$CURRENT_VERSION to v$NEW_VERSION. Press enter to continue."
19
+ read
20
+
21
+ sed -E -i '' "s/VERSION = \"[^\"]+\"/VERSION = \"$NEW_VERSION\"/g" lib/rspec-interactive/version.rb
22
+ gem build
23
+ gem push rspec-interactive-$NEW_VERSION.gem
24
+ bundle install
25
+ git commit -a -m "v$NEW_VERSION Release"
@@ -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
@@ -29,7 +29,47 @@ Test.test "debugged spec" do
29
29
  Finished in 0 seconds (files took 0 seconds to load)
30
30
  1 example, 0 failures
31
31
 
32
- => <RSpec::Interactive::Result @success=true, @group_results=[...]>
33
32
  [2] pry(main)> exit
34
33
  EOF
35
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
@@ -36,9 +36,6 @@ Test.test "failing spec" do
36
36
 
37
37
  rspec ./examples/failing_spec.rb:4 # example spec fails
38
38
 
39
- Rerun failures by executing the previous command with --only-failures or --next-failure.
40
-
41
- => <RSpec::Interactive::Result @success=false, @group_results=[...]>
42
39
  [2] pry(main)> exit
43
40
  EOF
44
41
  end
@@ -0,0 +1,18 @@
1
+ require_relative 'support/test_helper'
2
+
3
+ Test.test "passing spec" do
4
+ await_prompt
5
+ input "rspec examples/*passing*_spec.rb"
6
+ await_prompt
7
+ input "exit"
8
+ await_termination
9
+ expect_output <<~EOF
10
+ [1] pry(main)> rspec examples/*passing*_spec.rb
11
+ ....
12
+
13
+ Finished in 0 seconds (files took 0 seconds to load)
14
+ 4 examples, 0 failures
15
+
16
+ [2] pry(main)> exit
17
+ EOF
18
+ end
@@ -0,0 +1,19 @@
1
+ require_relative 'support/test_helper'
2
+
3
+ Test.test "running example group at line number" do
4
+ await_prompt
5
+ input "rspec examples/passing_spec.rb:8"
6
+ await_prompt
7
+ input "exit"
8
+ await_termination
9
+ expect_output <<~EOF
10
+ [1] pry(main)> rspec examples/passing_spec.rb:8
11
+ Run options: include {:locations=>{"./examples/passing_spec.rb"=>[8]}}
12
+ .
13
+
14
+ Finished in 0 seconds (files took 0 seconds to load)
15
+ 1 example, 0 failures
16
+
17
+ [2] pry(main)> exit
18
+ EOF
19
+ end
@@ -13,7 +13,6 @@ Test.test "passing spec" do
13
13
  Finished in 0 seconds (files took 0 seconds to load)
14
14
  2 examples, 0 failures
15
15
 
16
- => <RSpec::Interactive::Result @success=true, @group_results=[...]>
17
16
  [2] pry(main)> exit
18
17
  EOF
19
18
  end
@@ -0,0 +1,90 @@
1
+ require_relative 'support/test_helper'
2
+
3
+ examples = Tempfile.new('examples')
4
+
5
+ config = Tempfile.new('config')
6
+ config.write <<~EOF
7
+ RSpec.configuration.example_status_persistence_file_path = "#{examples.path}"
8
+ EOF
9
+ config.rewind
10
+
11
+ Test.test "failing spec with example file", config_path: config.path do
12
+ await_prompt
13
+
14
+ RSpec.configuration.backtrace_exclusion_patterns = [ /.*/ ]
15
+ RSpec.configuration.backtrace_inclusion_patterns = [ /examples\/failing_spec.rb/ ]
16
+
17
+ input "rspec examples/failing_spec.rb examples/passing_spec.rb"
18
+ await_prompt
19
+
20
+ RSpec.configuration.backtrace_exclusion_patterns = [ /.*/ ]
21
+ RSpec.configuration.backtrace_inclusion_patterns = [ /examples\/failing_spec.rb/ ]
22
+
23
+ input "rspec examples/failing_spec.rb examples/passing_spec.rb --only-failures"
24
+ await_prompt
25
+ input "exit"
26
+ await_termination
27
+ expect_output <<~EOF
28
+ [1] pry(main)> rspec examples/failing_spec.rb examples/passing_spec.rb
29
+ F..
30
+
31
+ Failures:
32
+
33
+ 1) example spec fails
34
+ Failure/Error: expect(true).to eq(false)
35
+
36
+ expected: false
37
+ got: true
38
+
39
+ (compared using ==)
40
+
41
+ Diff:
42
+ @@ -1 +1 @@
43
+ -false
44
+ +true
45
+ # ./examples/failing_spec.rb:5:in `block (2 levels) in <top (required)>'
46
+
47
+ Finished in 0 seconds (files took 0 seconds to load)
48
+ 3 examples, 1 failure
49
+
50
+ Failed examples:
51
+
52
+ rspec ./examples/failing_spec.rb:4 # example spec fails
53
+
54
+ Rerun failures by executing the previous command with --only-failures or --next-failure.
55
+
56
+ [2] pry(main)> rspec examples/failing_spec.rb examples/passing_spec.rb --only-failures
57
+ Run options: include {:last_run_status=>"failed"}
58
+ F
59
+
60
+ Failures:
61
+
62
+ 1) example spec fails
63
+ Failure/Error: expect(true).to eq(false)
64
+
65
+ expected: false
66
+ got: true
67
+
68
+ (compared using ==)
69
+
70
+ Diff:
71
+ @@ -1 +1 @@
72
+ -false
73
+ +true
74
+ # ./examples/failing_spec.rb:5:in `block (2 levels) in <top (required)>'
75
+
76
+ Finished in 0 seconds (files took 0 seconds to load)
77
+ 1 example, 1 failure
78
+
79
+ Failed examples:
80
+
81
+ rspec ./examples/failing_spec.rb:4 # example spec fails
82
+
83
+ Rerun failures by executing the previous command with --only-failures or --next-failure.
84
+
85
+ [3] pry(main)> exit
86
+ EOF
87
+ end
88
+
89
+ config.close
90
+ examples.close
@@ -0,0 +1,30 @@
1
+ require_relative 'support/test_helper'
2
+
3
+ RSpec.configuration.backtrace_exclusion_patterns = [ /.*/ ]
4
+ RSpec.configuration.backtrace_inclusion_patterns = [ /`load_spec_files'/ ]
5
+
6
+ Test.test "spec with syntax error" do
7
+ await_prompt
8
+ input "rspec examples/spec_with_syntax_error.rb"
9
+ await_prompt
10
+ input "exit"
11
+ await_termination
12
+ expect_equal "output", output.gsub(/.+(?=(\\|\/)[a-z_-]+[.]rb:[0-9]+:.*)/, ' [...]'), <<~EOF
13
+ [1] pry(main)> rspec examples/spec_with_syntax_error.rb
14
+
15
+ An error occurred while loading ./examples/spec_with_syntax_error.rb.
16
+ Failure/Error: ::RSpec.configuration.load_spec_files
17
+
18
+ SyntaxError:
19
+ [...]/spec_with_syntax_error.rb:5: unterminated string meets end of file
20
+ [...]/spec_with_syntax_error.rb:5: syntax error, unexpected end-of-input, expecting `end'
21
+ [...]/configuration.rb:1607:in `load_spec_files'
22
+ No examples found.
23
+
24
+
25
+ Finished in 0 seconds (files took 0 seconds to load)
26
+ 0 examples, 0 failures, 1 error occurred outside of examples
27
+
28
+ [2] pry(main)> exit
29
+ EOF
30
+ end
@@ -109,9 +109,16 @@ module Readline
109
109
  Thread.current.kill
110
110
  end
111
111
 
112
+ response = @next_response[0]
113
+ @next_response.clear
114
+
112
115
  temp = Tempfile.new('input')
113
- temp.write("#{@next_response[0]}\n")
114
- temp.rewind
116
+
117
+ unless response.nil?
118
+ temp.write("#{response}\n")
119
+ temp.rewind
120
+ end
121
+
115
122
  input_read = File.new(temp.path, 'r')
116
123
  Readline.input = input_read
117
124
 
@@ -156,39 +163,67 @@ end
156
163
 
157
164
  class Test
158
165
 
159
- def self.test(name, &block)
160
- Test.new.run(name, &block)
166
+ def self.test(name, config_path: nil, &block)
167
+ Test.new.run(name, config_path, &block)
161
168
  end
162
169
 
163
- def run(name, &block)
170
+ def run(name, config_path, &block)
171
+ puts "running: #{name}"
172
+
164
173
  @output_temp_file = Tempfile.new('output')
165
174
  @output_write = File.open(@output_temp_file.path, 'w')
166
175
 
176
+ @error_temp_file = Tempfile.new('error')
177
+ @error_write = File.open(@error_temp_file.path, 'w')
178
+
179
+ @history_temp_file = Tempfile.new('history')
180
+
167
181
  @interactive_thread = Thread.start do
168
- RSpec::Interactive.start(ARGV, input_stream: STDIN, output_stream: @output_write, error_stream: @output_write)
182
+ begin
183
+ @result = RSpec::Interactive.start(
184
+ config_file: config_path,
185
+ history_file: @history_temp_file.path,
186
+ input_stream: STDIN,
187
+ output_stream: @output_write,
188
+ error_stream: @error_write)
189
+ ensure
190
+ RSpec.clear_examples
191
+ RSpec.reset
192
+ end
169
193
  end
170
194
 
171
195
  begin
172
196
  instance_eval &block
173
197
  rescue Exception => e
174
198
  failed = true
175
- Ansi.puts :red, "failed: #{name}\n#{e.message}"
199
+ STDERR.puts e.message
200
+ e.backtrace[0..5].each { |line| STDERR.puts " #{line}" }
176
201
  end
177
202
 
178
203
  await_termination
179
204
 
180
205
  if Readline.error
181
206
  failed = true
182
- Ansi.puts :red, "failed: #{name}\n#{Readline.error}"
207
+ STDOUT.puts Readline.error
183
208
  end
184
209
 
185
- if !failed
210
+ if failed
211
+ Ansi.puts :red, "failed: #{name}"
212
+ else
186
213
  Ansi.puts :green, "passed: #{name}"
187
214
  end
215
+ puts
188
216
  ensure
189
217
  @output_write.close
190
218
  @output_temp_file.close
219
+
220
+ @error_write.close
221
+ @error_temp_file.close
222
+
223
+ @history_temp_file.close
224
+
191
225
  Readline.reset
226
+ Pry.reset_defaults
192
227
  end
193
228
 
194
229
  def await_termination
@@ -208,14 +243,44 @@ class Test
208
243
  Readline.puts(string)
209
244
  end
210
245
 
246
+ def ctrl_d
247
+ Readline.ctrl_d
248
+ end
249
+
211
250
  def output
212
251
  @output_temp_file.rewind
213
252
  File.read(@output_temp_file.path).gsub("\e[0G", "")
214
253
  end
215
254
 
255
+ def error_output
256
+ @error_write.flush
257
+ @error_temp_file.rewind
258
+ File.read(@error_temp_file.path)
259
+ end
260
+
261
+ def expect_history(expected)
262
+ @history_temp_file.rewind
263
+ history = File.read(@history_temp_file.path)
264
+ if expected != history
265
+ raise "unexpected history:\n expected: #{expected.inspect}\n actual: #{history.inspect}"
266
+ end
267
+ end
268
+
216
269
  def expect_output(expected)
217
- if expected != output
218
- raise "unexpected output:\n expected: #{expected.inspect}\n actual: #{output.inspect}"
270
+ expect_equal("output", output, expected)
271
+ end
272
+
273
+ def expect_error_output(expected)
274
+ expect_equal("error output", error_output, expected)
275
+ end
276
+
277
+ def expect_result(expected)
278
+ expect_equal("result", @result, expected)
279
+ end
280
+
281
+ def expect_equal(name, actual, expected)
282
+ if expected != actual
283
+ raise "unexpected #{name}:\n expected: #{expected.inspect}\n actual: #{actual.inspect}"
219
284
  end
220
285
  end
221
286
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-interactive
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Dower
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-18 00:00:00.000000000 Z
11
+ date: 2021-06-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec-core
@@ -72,17 +72,28 @@ files:
72
72
  - bin/test
73
73
  - examples/debugged_spec.rb
74
74
  - examples/failing_spec.rb
75
+ - examples/other_passing_spec.rb
75
76
  - examples/passing_spec.rb
77
+ - examples/spec_with_syntax_error.rb
76
78
  - lib/rspec-interactive.rb
77
- - lib/rspec-interactive/config_cache.rb
79
+ - lib/rspec-interactive/config.rb
78
80
  - lib/rspec-interactive/input_completer.rb
79
81
  - lib/rspec-interactive/rspec_command.rb
82
+ - lib/rspec-interactive/rspec_config_cache.rb
80
83
  - lib/rspec-interactive/runner.rb
81
84
  - lib/rspec-interactive/version.rb
82
85
  - rspec-interactive.gemspec
86
+ - scripts/release.sh
87
+ - scripts/run-with-local-dep.sh
83
88
  - tests/debugged_spec_test.rb
89
+ - tests/eof_test.rb
90
+ - tests/example_file_test.rb
84
91
  - tests/failing_spec_test.rb
92
+ - tests/glob_test.rb
93
+ - tests/line_number_test.rb
85
94
  - tests/passing_spec_test.rb
95
+ - tests/rerun_failed_specs_test.rb
96
+ - tests/spec_with_syntax_error_test.rb
86
97
  - tests/support/ansi.rb
87
98
  - tests/support/test_helper.rb
88
99
  homepage: https://github.com/nicholasdower/rspec-interactive