rspec-interactive 0.3.0 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
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