rspec-interactive 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6034dc7e5c24528799a1fd53510f0eba5f0d2d599b9e9736371a8ee210319d68
4
- data.tar.gz: 343e1fcaa3d42dbcfbb5b12dbfb8b6c33e7c44e80a2bfafdf22b9934ba63e986
3
+ metadata.gz: e3ff2b23a6cf52444bf173292d0e161006bbcf764f153f31ea9052d0f0c0f912
4
+ data.tar.gz: ddb6567801ceb90ff76054d554d3d63ecc951a92584395d809f691c10520555f
5
5
  SHA512:
6
- metadata.gz: 5af0bad059e46cd7933b3d83f48e2ebac13f6005817e5f7aee96ab3c27a153d89b136ba305881a76fd883c70b5f157534327c74608da4eeb28ed1158259f49c3
7
- data.tar.gz: c2059760cf7053a80f3465f937bd8a69bff7e49740617c82f2663abe474454d7fee8317996b6ed07ea63618ab2b972b79527fab7bfa9f40e191825e3d79481e8
6
+ metadata.gz: 2aa35c7ed2108213d90e97d9b8d30208f28993ab2e8111127024dec1ddf7404226a00a5e7c2a99dd47a026efc18f043558e8db3d87373e3c486bf4de8fac32a8
7
+ data.tar.gz: 35147d8a59a432d163dfd309db75c92100c781e5e1d6fa925df431ec70cc849b6886f1bd0cac9bb27e53ee9b6094b7332012520f69b29ed30e8178af5dbe6125
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
1
  rspec-interactive-*.gem
2
2
  .rspec_interactive_history
3
3
  .rspec_interactive_config
4
+ .rspec_interactive_results
data/Gemfile.lock CHANGED
@@ -1,10 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rspec-interactive (0.1.0)
5
- listen (~> 3.5, >= 3.5.1)
6
- pry (~> 0.14.1)
7
- rspec-core (~> 3.10, >= 3.10.1)
4
+ rspec-interactive (0.2.0)
5
+ listen
6
+ pry
7
+ rspec-core
8
8
 
9
9
  GEM
10
10
  remote: https://rubygems.org/
@@ -22,12 +22,12 @@ GEM
22
22
  rb-fsevent (0.11.0)
23
23
  rb-inotify (0.10.1)
24
24
  ffi (~> 1.0)
25
- rspec-core (3.10.1)
26
- rspec-support (~> 3.10.0)
27
- rspec-expectations (3.10.1)
25
+ rspec-core (3.9.3)
26
+ rspec-support (~> 3.9.3)
27
+ rspec-expectations (3.9.4)
28
28
  diff-lcs (>= 1.2.0, < 2.0)
29
- rspec-support (~> 3.10.0)
30
- rspec-support (3.10.2)
29
+ rspec-support (~> 3.9.0)
30
+ rspec-support (3.9.4)
31
31
 
32
32
  PLATFORMS
33
33
  x86_64-darwin-20
data/README.md CHANGED
@@ -35,6 +35,7 @@ Update `.gitignore`
35
35
 
36
36
  ```shell
37
37
  echo '.rspec_interactive_history' >> .gitignore
38
+ echo '.rspec_interactive_results' >> .gitignore
38
39
  ```
39
40
 
40
41
  ## Usage
@@ -59,24 +60,12 @@ Run a passing spec:
59
60
  [1] pry(main)> rspec examples/passing_spec.rb
60
61
  ```
61
62
 
62
- Inspect the result:
63
-
64
- ```shell
65
- [2] pry(main)> result
66
- ```
67
-
68
63
  Run a failing spec:
69
64
 
70
65
  ```shell
71
66
  [3] pry(main)> rspec examples/failing_spec.rb
72
67
  ```
73
68
 
74
- Inspect result history:
75
-
76
- ```shell
77
- [4] pry(main)> results
78
- ```
79
-
80
69
  Run an example group:
81
70
 
82
71
  ```shell
@@ -106,3 +95,9 @@ Exit:
106
95
  ```shell
107
96
  [9] pry(main)> exit
108
97
  ```
98
+
99
+ ## Running Tests
100
+
101
+ ```shell
102
+ bundle exec bin/test
103
+ ```
data/bin/test ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env ruby
2
+ Dir["tests/*.rb"].each {|file| load file }
@@ -4,8 +4,4 @@ describe "example spec" do
4
4
  it "fails" do
5
5
  expect(true).to eq(false)
6
6
  end
7
-
8
- it "fails again" do
9
- expect(true).to eq(false)
10
- end
11
7
  end
@@ -1,10 +1,8 @@
1
- #!/usr/bin/env ruby
2
-
3
1
  require 'json'
4
2
  require 'listen'
3
+ require 'pry'
5
4
  require 'readline'
6
5
  require 'rspec/core'
7
- require 'pry'
8
6
 
9
7
  require 'rspec-interactive/runner'
10
8
  require 'rspec-interactive/config_cache'
@@ -18,12 +16,13 @@ module RSpec
18
16
  CONFIG_FILE = '.rspec_interactive_config'.freeze
19
17
 
20
18
  class <<self
19
+ attr_accessor :readline, :input_stream, :output_stream, :error_stream
21
20
  attr_accessor :config, :stty_save, :mutex, :config_cache, :runner, :results, :result, :updated_files
22
21
  end
23
22
 
24
- def self.start(args)
23
+ def self.start(args, input_stream: STDIN, output_stream: STDOUT, error_stream: STDERR)
25
24
  if args.size > 1
26
- STDERR.puts "expected 0 or 1 argument, got: #{args.join(', ')}"
25
+ @error_stream.puts "expected 0 or 1 argument, got: #{args.join(', ')}"
27
26
  exit!(1)
28
27
  end
29
28
 
@@ -32,6 +31,9 @@ module RSpec
32
31
  @config = get_config(args[0])
33
32
  @stty_save = %x`stty -g`.chomp
34
33
  @mutex = Mutex.new
34
+ @output_stream = output_stream
35
+ @input_stream = input_stream
36
+ @error_stream = error_stream
35
37
  @config_cache = RSpec::Interactive::ConfigCache.new
36
38
 
37
39
  load_rspec_config
@@ -46,7 +48,7 @@ module RSpec
46
48
  def self.check_rails
47
49
  if defined?(::Rails)
48
50
  if ::Rails.application.config.cache_classes
49
- STDERR.puts "warning: Rails.application.config.cache_classes enabled. Disable to ensure code is reloaded."
51
+ @error_stream.puts "warning: Rails.application.config.cache_classes enabled. Disable to ensure code is reloaded."
50
52
  end
51
53
  end
52
54
  end
@@ -55,6 +57,13 @@ module RSpec
55
57
  @config_cache.record_configuration(&rspec_configuration)
56
58
  end
57
59
 
60
+ def self.configure_rspec
61
+ RSpec.configure do |config|
62
+ config.error_stream = @error_stream
63
+ config.output_stream = @output_stream
64
+ end
65
+ end
66
+
58
67
  def self.rspec_configuration
59
68
  proc do
60
69
  if @config["init_script"]
@@ -66,13 +75,13 @@ module RSpec
66
75
 
67
76
  def self.get_config(name = nil)
68
77
  unless File.exists? CONFIG_FILE
69
- STDERR.puts "warning: #{CONFIG_FILE} not found, using default config"
78
+ @error_stream.puts "warning: #{CONFIG_FILE} not found, using default config"
70
79
  return {}
71
80
  end
72
81
 
73
82
  configs = JSON.parse(File.read(CONFIG_FILE))["configs"] || []
74
83
  if configs.empty?
75
- STDERR.puts "no configs found in: #{CONFIG_FILE}"
84
+ @error_stream.puts "no configs found in: #{CONFIG_FILE}"
76
85
  exit!(1)
77
86
  end
78
87
 
@@ -80,7 +89,7 @@ module RSpec
80
89
  if name
81
90
  config = configs.find { |e| e["name"] == name }
82
91
  return config if config
83
- STDERR.puts "invalid config: #{name}"
92
+ @error_stream.puts "invalid config: #{name}"
84
93
  exit!(1)
85
94
  end
86
95
 
@@ -94,13 +103,13 @@ module RSpec
94
103
  names = configs.map { |e| e["name"] }
95
104
  names[0] = "#{names[0]} (default)"
96
105
  print "Multiple simultaneous configs not yet supported. Please choose a config. #{names.join(', ')}: "
97
- answer = STDIN.gets.chomp
106
+ answer = @input_stream.gets.chomp
98
107
  if answer.strip.empty?
99
108
  return configs[0]
100
109
  end
101
110
  config = configs.find { |e| e["name"] == answer }
102
111
  return config if config
103
- STDERR.puts "invalid config: #{answer}"
112
+ @error_stream.puts "invalid config: #{answer}"
104
113
  end
105
114
  end
106
115
 
@@ -110,7 +119,7 @@ module RSpec
110
119
  # We are on a different thread. There is a race here. Ignore nil.
111
120
  @runner&.quit
112
121
  else
113
- puts
122
+ @output_stream.puts
114
123
  system "stty", @stty_save
115
124
  exit!(0)
116
125
  end
@@ -133,8 +142,11 @@ module RSpec
133
142
  # Prevent Pry from trapping too. It will break ctrl-c handling.
134
143
  Pry.config.should_trap_interrupts = false
135
144
 
136
- # Set Pry to use Readline. This is the default anyway.
145
+ # Set up IO.
137
146
  Pry.config.input = Readline
147
+ Pry.config.output = @output_stream
148
+ Readline.output = @output_stream
149
+ Readline.input = @input_stream
138
150
 
139
151
  # Use custom completer to get file completion.
140
152
  Pry.config.completer = RSpec::Interactive::InputCompleter
@@ -12,7 +12,7 @@ module RSpec::Interactive
12
12
  BANNER
13
13
 
14
14
  command_options(
15
- :keep_retval => true
15
+ :keep_retval => false
16
16
  )
17
17
 
18
18
  def process
@@ -37,6 +37,9 @@ module RSpec::Interactive
37
37
  # Stop saving history in case a new Pry session is started for debugging.
38
38
  Pry.config.history_save = false
39
39
 
40
+ # RSpec::Interactive-specific RSpec configuration
41
+ RSpec::Interactive.configure_rspec
42
+
40
43
  # Run.
41
44
  result = RSpec::Interactive.runner.run
42
45
  RSpec::Interactive.runner = nil
@@ -53,11 +56,10 @@ module RSpec::Interactive
53
56
  RSpec.reset
54
57
  RSpec::Interactive.config_cache.replay_configuration
55
58
 
56
- Object.define_method :results do RSpec::Interactive.results end
57
- Object.define_method :result do RSpec::Interactive.result end
58
-
59
- puts "Result available at `result`. Result history available at `results`."
60
- puts
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
61
63
 
62
64
  result
63
65
  end
@@ -12,65 +12,75 @@ module RSpec
12
12
  end
13
13
 
14
14
  class Result
15
- attr_accessor :groups, :success, :exit_code
15
+ attr_accessor :group_results, :success, :exit_code
16
16
 
17
- def initialize(groups, success, exit_code)
18
- @groups = groups
17
+ def initialize(group_results, success, exit_code)
18
+ @group_results = group_results
19
19
  @success = success
20
20
  @exit_code = exit_code
21
21
  end
22
22
 
23
23
  def inspect(original = false)
24
- original ? super() : "<RSpec::Interactive::Result @success=#{@success}, @groups=[...]>"
24
+ original ? super() : "<RSpec::Interactive::Result @success=#{@success}, @group_results=[...]>"
25
25
  end
26
26
  end
27
27
 
28
28
  class Runner
29
29
  def initialize(args)
30
- RSpec.world.wants_to_quit = false
31
- @options = RSpec::Core::ConfigurationOptions.new(args)
30
+ ::RSpec.world.wants_to_quit = false
31
+ @options = ::RSpec::Core::ConfigurationOptions.new(args)
32
32
  end
33
33
 
34
34
  def run()
35
35
  begin
36
- @options.configure(RSpec.configuration)
37
- return if RSpec.world.wants_to_quit
36
+ @options.configure(::RSpec.configuration)
37
+ return if ::RSpec.world.wants_to_quit
38
38
 
39
- RSpec.configuration.load_spec_files
39
+ ::RSpec.configuration.load_spec_files
40
40
  ensure
41
- RSpec.world.announce_filters
41
+ ::RSpec.world.announce_filters
42
42
  end
43
43
 
44
- return RSpec.configuration.reporter.exit_early(RSpec.configuration.failure_exit_code) if RSpec.world.wants_to_quit
44
+ return ::RSpec.configuration.reporter.exit_early(::RSpec.configuration.failure_exit_code) if ::RSpec.world.wants_to_quit
45
45
 
46
- example_groups = RSpec.world.ordered_example_groups
47
- examples_count = RSpec.world.example_count(example_groups)
46
+ example_groups = ::RSpec.world.ordered_example_groups
47
+ examples_count = ::RSpec.world.example_count(example_groups)
48
48
 
49
- result = RSpec.configuration.reporter.report(examples_count) do |reporter|
50
- RSpec.configuration.with_suite_hooks do
51
- if examples_count == 0 && RSpec.configuration.fail_if_no_examples
52
- return RSpec.configuration.failure_exit_code
49
+ result = ::RSpec.configuration.reporter.report(examples_count) do |reporter|
50
+ ::RSpec.configuration.with_suite_hooks do
51
+ if examples_count == 0 && ::RSpec.configuration.fail_if_no_examples
52
+ return ::RSpec.configuration.failure_exit_code
53
53
  end
54
54
 
55
- results = example_groups.map do |example_group|
55
+ group_results = example_groups.map do |example_group|
56
56
  group_success = example_group.run(reporter)
57
57
  ExampleGroupResult.new(example_group, group_success)
58
58
  end
59
59
 
60
- success = results.all?(&:success)
60
+ success = group_results.all?(&:success)
61
61
  exit_code = success ? 0 : 1
62
- if RSpec.world.non_example_failure
62
+ if ::RSpec.world.non_example_failure
63
63
  success = false
64
- exit_code = RSpec.configuration.failure_exit_code
64
+ exit_code = ::RSpec.configuration.failure_exit_code
65
65
  end
66
- Result.new(results, success, exit_code)
66
+ persist_example_statuses
67
+ Result.new(group_results, success, exit_code)
67
68
  end
68
69
  end
69
70
  result
70
71
  end
71
72
 
72
73
  def quit
73
- RSpec.world.wants_to_quit = true
74
+ ::RSpec.world.wants_to_quit = true
75
+ end
76
+
77
+ def persist_example_statuses
78
+ return if ::RSpec.configuration.dry_run
79
+ return unless (path = ::RSpec.configuration.example_status_persistence_file_path)
80
+
81
+ ::RSpec::Core::ExampleStatusPersister.persist(::RSpec.world.all_examples, path)
82
+ rescue SystemCallError => e
83
+ RSpec::Interactive.error_stream.puts "warning: failed to write results to #{path}"
74
84
  end
75
85
  end
76
86
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RSpec
4
4
  module Interactive
5
- VERSION = "0.2.0"
5
+ VERSION = "0.3.0"
6
6
  end
7
7
  end
@@ -25,7 +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 'rspec-core', '~> 3.9.2', '>= 3.9.2'
29
- spec.add_dependency 'listen', '~> 3.5', '>= 3.5.1'
30
- spec.add_dependency 'pry', '~> 0.14.1'
28
+ spec.add_dependency 'rspec-core'
29
+ spec.add_dependency 'listen'
30
+ spec.add_dependency 'pry'
31
31
  end
@@ -0,0 +1,35 @@
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
+ => <RSpec::Interactive::Result @success=true, @group_results=[...]>
33
+ [2] pry(main)> exit
34
+ EOF
35
+ end
@@ -0,0 +1,44 @@
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
+ Test.test "failing spec" do
7
+ await_prompt
8
+ input "rspec examples/failing_spec.rb"
9
+ await_prompt
10
+ input "exit"
11
+ await_termination
12
+ expect_output <<~EOF
13
+ [1] pry(main)> rspec examples/failing_spec.rb
14
+ F
15
+
16
+ Failures:
17
+
18
+ 1) example spec fails
19
+ Failure/Error: expect(true).to eq(false)
20
+
21
+ expected: false
22
+ got: true
23
+
24
+ (compared using ==)
25
+
26
+ Diff:
27
+ @@ -1 +1 @@
28
+ -false
29
+ +true
30
+ # ./examples/failing_spec.rb:5:in `block (2 levels) in <top (required)>'
31
+
32
+ Finished in 0 seconds (files took 0 seconds to load)
33
+ 1 example, 1 failure
34
+
35
+ Failed examples:
36
+
37
+ rspec ./examples/failing_spec.rb:4 # example spec fails
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
+ [2] pry(main)> exit
43
+ EOF
44
+ end
@@ -0,0 +1,19 @@
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
+ 2 examples, 0 failures
15
+
16
+ => <RSpec::Interactive::Result @success=true, @group_results=[...]>
17
+ [2] pry(main)> exit
18
+ EOF
19
+ end
@@ -0,0 +1,32 @@
1
+ module Ansi
2
+ @ansi_colors = {
3
+ :black => '0;30',
4
+ :red => '0;31',
5
+ :green => '0;32',
6
+ :orange => '0;33',
7
+ :blue => '0;34',
8
+ :purple => '0;35',
9
+ :cyan => '0;36',
10
+ :light_gray => '0;37',
11
+ :dark_gray => '1;30',
12
+ :light_red => '1;31',
13
+ :light_green => '1;32',
14
+ :yellow => '1;33',
15
+ :light_blue => '1;34',
16
+ :light_purple => '1;35',
17
+ :light_cyan => '1;36',
18
+ :white => '1;37'
19
+ }
20
+
21
+ def self.puts(color, string, output = STDOUT)
22
+ raise "invalid color: #{color}" unless @ansi_colors[color]
23
+ string = "" if string == nil
24
+ output.puts "\033[#{@ansi_colors[color]}m#{string}\033[0m"
25
+ end
26
+
27
+ def self.print(color, string, output = STDOUT)
28
+ raise "invalid color: #{color}" unless @ansi_colors[color]
29
+ return unless string
30
+ output.print "\033[#{@ansi_colors[color]}m#{string}\033[0m"
31
+ end
32
+ end
@@ -0,0 +1,221 @@
1
+ ENV['TERM'] = 'dumb'
2
+
3
+ require 'pry'
4
+ require 'readline'
5
+ require 'rspec/core'
6
+ require 'rspec-interactive'
7
+ require 'timeout'
8
+ require 'time'
9
+ require_relative 'ansi'
10
+
11
+ class Time
12
+ @start = Time.parse('1982-08-05 07:21:00 -0500')
13
+
14
+ def self.now
15
+ @start
16
+ end
17
+ end
18
+
19
+ module RSpec::Core
20
+ class Time
21
+ def self.now
22
+ ::Time.now
23
+ end
24
+ end
25
+ end
26
+
27
+ class Output
28
+ def initialize
29
+ @output = ""
30
+ @pos = 0
31
+ end
32
+
33
+ def print(string)
34
+ return if string == nil || string == "\e[0G"
35
+ @output += string
36
+ end
37
+
38
+ def puts(string = "")
39
+ string = "" if string == nil
40
+ print(string + "\n")
41
+ end
42
+
43
+ def closed?
44
+ false
45
+ end
46
+
47
+ def tty?
48
+ false
49
+ end
50
+
51
+ def flush
52
+ end
53
+
54
+ def next_string
55
+ output = @output[@pos..-1]
56
+ @pos = @output.size
57
+ output
58
+ end
59
+
60
+ def string
61
+ @output
62
+ end
63
+ end
64
+
65
+ module Readline
66
+ class << self
67
+ attr_accessor :error
68
+ end
69
+
70
+ @original_readline = method(:readline)
71
+
72
+ def self.reset
73
+ @next_response = []
74
+ @signal = ConditionVariable.new
75
+ @mutex = Mutex.new
76
+ @state = :waiting_for_prompt
77
+ @error = nil
78
+ end
79
+
80
+ reset
81
+
82
+ def self.readline(prompt = nil, save_history = nil)
83
+ temp = nil
84
+ input_read = nil
85
+ @mutex.synchronize do
86
+ Thread.current.kill if @error
87
+
88
+ if @state != :waiting_for_prompt
89
+ @error = "prompted in invalid state: #{@state}"
90
+ Thread.current.kill
91
+ end
92
+
93
+ @state = :prompted
94
+ @signal.signal
95
+ @signal.wait(@mutex, 1)
96
+
97
+ if @state != :response_available
98
+ @error = "sending response in invalid state: #{@state}"
99
+ Thread.current.kill
100
+ end
101
+ @state = :waiting_for_prompt
102
+
103
+ if @next_response.empty?
104
+ @error = "readline response signaled with nothing to respond with"
105
+ Thread.current.kill
106
+ end
107
+ if @next_response.size > 1
108
+ @error = "readline response signaled with too much to respond with"
109
+ Thread.current.kill
110
+ end
111
+
112
+ temp = Tempfile.new('input')
113
+ temp.write("#{@next_response[0]}\n")
114
+ temp.rewind
115
+ input_read = File.new(temp.path, 'r')
116
+ Readline.input = input_read
117
+
118
+ @next_response.clear
119
+
120
+ @original_readline.call(prompt, save_history)
121
+ end
122
+ ensure
123
+ temp&.close
124
+ input_read&.close
125
+ end
126
+
127
+ def self.await_readline
128
+ @mutex.synchronize do
129
+ raise @error if @error
130
+ @signal.wait(@mutex, 1)
131
+ raise @error if @error
132
+ if @state != :prompted
133
+ @error = "timed out waiting for prompt"
134
+ raise @error
135
+ end
136
+ end
137
+ end
138
+
139
+ def self.puts(string)
140
+ @mutex.synchronize do
141
+ raise @error if @error
142
+ if @state != :prompted
143
+ @error = "puts called in invalid state: #{@state}"
144
+ raise @error
145
+ end
146
+ @next_response << string
147
+ @state = :response_available
148
+ @signal.signal
149
+ end
150
+ end
151
+
152
+ def self.ctrl_d
153
+ puts(nil)
154
+ end
155
+ end
156
+
157
+ class Test
158
+
159
+ def self.test(name, &block)
160
+ Test.new.run(name, &block)
161
+ end
162
+
163
+ def run(name, &block)
164
+ @output_temp_file = Tempfile.new('output')
165
+ @output_write = File.open(@output_temp_file.path, 'w')
166
+
167
+ @interactive_thread = Thread.start do
168
+ RSpec::Interactive.start(ARGV, input_stream: STDIN, output_stream: @output_write, error_stream: @output_write)
169
+ end
170
+
171
+ begin
172
+ instance_eval &block
173
+ rescue Exception => e
174
+ failed = true
175
+ Ansi.puts :red, "failed: #{name}\n#{e.message}"
176
+ end
177
+
178
+ await_termination
179
+
180
+ if Readline.error
181
+ failed = true
182
+ Ansi.puts :red, "failed: #{name}\n#{Readline.error}"
183
+ end
184
+
185
+ if !failed
186
+ Ansi.puts :green, "passed: #{name}"
187
+ end
188
+ ensure
189
+ @output_write.close
190
+ @output_temp_file.close
191
+ Readline.reset
192
+ end
193
+
194
+ def await_termination
195
+ Timeout.timeout(5) do
196
+ @interactive_thread.join
197
+ end
198
+ rescue Timeout::Error => e
199
+ @interactive_thread.kill
200
+ raise "timed out waiting for interactive session to terminate"
201
+ end
202
+
203
+ def await_prompt
204
+ Readline.await_readline
205
+ end
206
+
207
+ def input(string)
208
+ Readline.puts(string)
209
+ end
210
+
211
+ def output
212
+ @output_temp_file.rewind
213
+ File.read(@output_temp_file.path).gsub("\e[0G", "")
214
+ end
215
+
216
+ def expect_output(expected)
217
+ if expected != output
218
+ raise "unexpected output:\n expected: #{expected.inspect}\n actual: #{output.inspect}"
219
+ end
220
+ end
221
+ end
metadata CHANGED
@@ -1,69 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-interactive
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
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-15 00:00:00.000000000 Z
11
+ date: 2021-05-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec-core
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: 3.9.2
20
17
  - - ">="
21
18
  - !ruby/object:Gem::Version
22
- version: 3.9.2
19
+ version: '0'
23
20
  type: :runtime
24
21
  prerelease: false
25
22
  version_requirements: !ruby/object:Gem::Requirement
26
23
  requirements:
27
- - - "~>"
28
- - !ruby/object:Gem::Version
29
- version: 3.9.2
30
24
  - - ">="
31
25
  - !ruby/object:Gem::Version
32
- version: 3.9.2
26
+ version: '0'
33
27
  - !ruby/object:Gem::Dependency
34
28
  name: listen
35
29
  requirement: !ruby/object:Gem::Requirement
36
30
  requirements:
37
- - - "~>"
38
- - !ruby/object:Gem::Version
39
- version: '3.5'
40
31
  - - ">="
41
32
  - !ruby/object:Gem::Version
42
- version: 3.5.1
33
+ version: '0'
43
34
  type: :runtime
44
35
  prerelease: false
45
36
  version_requirements: !ruby/object:Gem::Requirement
46
37
  requirements:
47
- - - "~>"
48
- - !ruby/object:Gem::Version
49
- version: '3.5'
50
38
  - - ">="
51
39
  - !ruby/object:Gem::Version
52
- version: 3.5.1
40
+ version: '0'
53
41
  - !ruby/object:Gem::Dependency
54
42
  name: pry
55
43
  requirement: !ruby/object:Gem::Requirement
56
44
  requirements:
57
- - - "~>"
45
+ - - ">="
58
46
  - !ruby/object:Gem::Version
59
- version: 0.14.1
47
+ version: '0'
60
48
  type: :runtime
61
49
  prerelease: false
62
50
  version_requirements: !ruby/object:Gem::Requirement
63
51
  requirements:
64
- - - "~>"
52
+ - - ">="
65
53
  - !ruby/object:Gem::Version
66
- version: 0.14.1
54
+ version: '0'
67
55
  description: An interactive console for running specs.
68
56
  email:
69
57
  - nicholasdower@gmail.com
@@ -81,6 +69,7 @@ files:
81
69
  - bin/console
82
70
  - bin/rspec-interactive
83
71
  - bin/setup
72
+ - bin/test
84
73
  - examples/debugged_spec.rb
85
74
  - examples/failing_spec.rb
86
75
  - examples/passing_spec.rb
@@ -91,6 +80,11 @@ files:
91
80
  - lib/rspec-interactive/runner.rb
92
81
  - lib/rspec-interactive/version.rb
93
82
  - rspec-interactive.gemspec
83
+ - tests/debugged_spec_test.rb
84
+ - tests/failing_spec_test.rb
85
+ - tests/passing_spec_test.rb
86
+ - tests/support/ansi.rb
87
+ - tests/support/test_helper.rb
94
88
  homepage: https://github.com/nicholasdower/rspec-interactive
95
89
  licenses:
96
90
  - MIT