rspec-interactive 0.2.0 → 0.3.0

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: 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