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 +4 -4
- data/.gitignore +1 -0
- data/Gemfile.lock +9 -9
- data/README.md +7 -12
- data/bin/test +2 -0
- data/examples/failing_spec.rb +0 -4
- data/lib/rspec-interactive.rb +25 -13
- data/lib/rspec-interactive/rspec_command.rb +8 -6
- data/lib/rspec-interactive/runner.rb +33 -23
- data/lib/rspec-interactive/version.rb +1 -1
- data/rspec-interactive.gemspec +3 -3
- data/tests/debugged_spec_test.rb +35 -0
- data/tests/failing_spec_test.rb +44 -0
- data/tests/passing_spec_test.rb +19 -0
- data/tests/support/ansi.rb +32 -0
- data/tests/support/test_helper.rb +221 -0
- metadata +16 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e3ff2b23a6cf52444bf173292d0e161006bbcf764f153f31ea9052d0f0c0f912
|
4
|
+
data.tar.gz: ddb6567801ceb90ff76054d554d3d63ecc951a92584395d809f691c10520555f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2aa35c7ed2108213d90e97d9b8d30208f28993ab2e8111127024dec1ddf7404226a00a5e7c2a99dd47a026efc18f043558e8db3d87373e3c486bf4de8fac32a8
|
7
|
+
data.tar.gz: 35147d8a59a432d163dfd309db75c92100c781e5e1d6fa925df431ec70cc849b6886f1bd0cac9bb27e53ee9b6094b7332012520f69b29ed30e8178af5dbe6125
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rspec-interactive (0.
|
5
|
-
listen
|
6
|
-
pry
|
7
|
-
rspec-core
|
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.
|
26
|
-
rspec-support (~> 3.
|
27
|
-
rspec-expectations (3.
|
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.
|
30
|
-
rspec-support (3.
|
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
data/examples/failing_spec.rb
CHANGED
data/lib/rspec-interactive.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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 =
|
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
|
-
|
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
|
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 =>
|
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
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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 :
|
15
|
+
attr_accessor :group_results, :success, :exit_code
|
16
16
|
|
17
|
-
def initialize(
|
18
|
-
@
|
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}, @
|
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
|
-
|
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 =
|
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
|
-
|
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
|
data/rspec-interactive.gemspec
CHANGED
@@ -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'
|
29
|
-
spec.add_dependency 'listen'
|
30
|
-
spec.add_dependency 'pry'
|
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.
|
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-
|
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:
|
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:
|
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:
|
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:
|
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
|
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
|
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
|