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