rspec-interactive 0.2.0 → 0.6.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: 065d25cbce32ef3f5639e793c9f5db6d99c31a0c536d717ee49f05457251e4f3
4
+ data.tar.gz: 8d981a7cf504ec21bb9f0fdd12606ee78e53d6461909b1d0f4ceb06084c616b6
5
5
  SHA512:
6
- metadata.gz: 5af0bad059e46cd7933b3d83f48e2ebac13f6005817e5f7aee96ab3c27a153d89b136ba305881a76fd883c70b5f157534327c74608da4eeb28ed1158259f49c3
7
- data.tar.gz: c2059760cf7053a80f3465f937bd8a69bff7e49740617c82f2663abe474454d7fee8317996b6ed07ea63618ab2b972b79527fab7bfa9f40e191825e3d79481e8
6
+ metadata.gz: f5fd3e74737908f45f9eef622e977a71c8adfac52fb1141548f1e3960ee9839a234a1d4afc60ee0859cdeca6eb23e57a60ab9e77d4ed5475ea1f2e6429f74e5a
7
+ data.tar.gz: 734f4c31df3dde4da275c7680364348a549997496abdcd8acaab8ab755fd922de1993b154d94e3646e8e723bc6057ad1766b5add826ac45e5307d25b440b3665
data/Gemfile.lock CHANGED
@@ -1,17 +1,17 @@
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.5.0)
5
+ listen
6
+ pry
7
+ rspec-core
8
8
 
9
9
  GEM
10
10
  remote: https://rubygems.org/
11
11
  specs:
12
12
  coderay (1.1.3)
13
13
  diff-lcs (1.4.4)
14
- ffi (1.15.0)
14
+ ffi (1.15.3)
15
15
  listen (3.5.1)
16
16
  rb-fsevent (~> 0.10, >= 0.10.3)
17
17
  rb-inotify (~> 0.9, >= 0.9.10)
@@ -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
@@ -1,7 +1,5 @@
1
1
  # RSpec Interactive
2
2
 
3
- **WARNING: New (or maybe old by now), experimental, untested and poorly documented. Use at your own risk.**
4
-
5
3
  An Pry console capable of running specs.
6
4
 
7
5
  ## Installation & Configuration
@@ -12,23 +10,23 @@ Install:
12
10
  gem 'rspec-interactive'
13
11
  ```
14
12
 
15
- Add a config file named `.rspec_interactive_config`:
16
-
17
- ```json
18
- {
19
- "configs": [
20
- {
21
- "name": "spec",
22
- "watch_dirs": ["app"],
23
- "init_script": "spec/spec_helper.rb"
24
- },
25
- {
26
- "name": "spec_integration",
27
- "watch_dirs": ["app"],
28
- "init_script": "spec_integration/integration_helper.rb"
29
- }
30
- ]
31
- }
13
+ Add a config file which configures RSpec and RSpec::Interactive, for example `spec/rspec_interactive.rb`:
14
+
15
+ ```ruby
16
+ RSpec::Interactive.configure do |config|
17
+ # Directories to watch for file changes. When a file changes, it will be reloaded like `load 'path/to/file'`.
18
+ config.watch_dirs += ["app", "lib", "config"]
19
+
20
+ # This block is invoked on startup. RSpec configuration must happen here so that it can be reloaded before each test run.
21
+ config.configure_rspec do
22
+ require './spec/spec_helper.rb'
23
+ end
24
+
25
+ # Invoked whenever a class is loaded due to a file change in one of the watch_dirs.
26
+ config.on_class_load do |clazz|
27
+ clazz.clear_validators! if clazz < ApplicationRecord
28
+ end
29
+ end
32
30
  ```
33
31
 
34
32
  Update `.gitignore`
@@ -39,10 +37,10 @@ echo '.rspec_interactive_history' >> .gitignore
39
37
 
40
38
  ## Usage
41
39
 
42
- See more examples below.
40
+ Optionally, specify a config file with `--config <config-file>`. Optionally, specify arguments to an initial RSpec invocation with `--initial-rspec-args <initial-rspec-args>`.
43
41
 
44
42
  ```shell
45
- bundle exec rspec-interactive [spec name]
43
+ bundle exec rspec-interactive [--config <config-file>] [--initial-rspec-args <initial-rspec-args>]
46
44
  ```
47
45
 
48
46
  ## Example Usage In This Repo
@@ -53,16 +51,16 @@ Start:
53
51
  bundle exec rspec-interactive
54
52
  ```
55
53
 
56
- Run a passing spec:
54
+ Start with an initial RSpec invocation:
57
55
 
58
56
  ```shell
59
- [1] pry(main)> rspec examples/passing_spec.rb
57
+ bundle exec rspec-interactive --initial-rspec-args examples/passing_spec.rb
60
58
  ```
61
59
 
62
- Inspect the result:
60
+ Run a passing spec:
63
61
 
64
62
  ```shell
65
- [2] pry(main)> result
63
+ [1] pry(main)> rspec examples/passing_spec.rb
66
64
  ```
67
65
 
68
66
  Run a failing spec:
@@ -71,12 +69,6 @@ Run a failing spec:
71
69
  [3] pry(main)> rspec examples/failing_spec.rb
72
70
  ```
73
71
 
74
- Inspect result history:
75
-
76
- ```shell
77
- [4] pry(main)> results
78
- ```
79
-
80
72
  Run an example group:
81
73
 
82
74
  ```shell
@@ -106,3 +98,15 @@ Exit:
106
98
  ```shell
107
99
  [9] pry(main)> exit
108
100
  ```
101
+
102
+ ## Running Tests
103
+
104
+ ```shell
105
+ bundle exec bin/test
106
+ ```
107
+
108
+ ## Releasing
109
+
110
+ ```shell
111
+ ./scripts/release.sh <version>
112
+ ```
@@ -1,5 +1,21 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
+ require 'optparse'
3
4
  require 'rspec-interactive'
5
+ require 'shellwords'
4
6
 
5
- RSpec::Interactive.start(ARGV)
7
+ @options = {}
8
+ parser = OptionParser.new do |opts|
9
+ opts.banner = "Starts an interactive RSpec shell.\n\n"\
10
+ "Usage: bundle exec rspec-interactive [-c config-file] <initial-rspec-args>"
11
+
12
+ opts.on("-c", "--config <config-file>", String, "Optional. Path to the RSpec Interactive config file.") do |config_file|
13
+ @options[:config_file] = config_file
14
+ end
15
+
16
+ opts.on("-r", "--initial-rspec-args <initial-rspec-args>", String, "Optional. Arguments to pass to an initial invocation of RSpec.") do |initial_rspec_args|
17
+ @options[:initial_rspec_args] = Shellwords.split(initial_rspec_args)
18
+ end
19
+ end.parse!
20
+
21
+ RSpec::Interactive.start(config_file: @options[:config_file], initial_rspec_args: @options[:initial_rspec_args])
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
@@ -0,0 +1,11 @@
1
+ require 'rspec/core'
2
+
3
+ describe "other example spec" do
4
+ it "succeeds" do
5
+ expect(true).to eq(true)
6
+ end
7
+
8
+ it "succeeds again" do
9
+ expect(true).to eq(true)
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ require 'rspec/core'
2
+
3
+ describe "spec with syntax error" do
4
+ it "succeeds
5
+ end
@@ -1,106 +1,69 @@
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
- require 'rspec-interactive/config_cache'
8
+ require 'rspec-interactive/config'
9
+ require 'rspec-interactive/rspec_config_cache'
11
10
  require 'rspec-interactive/input_completer'
12
11
  require 'rspec-interactive/rspec_command'
13
12
 
14
13
  module RSpec
15
14
  module Interactive
16
15
 
17
- HISTORY_FILE = '.rspec_interactive_history'.freeze
18
- CONFIG_FILE = '.rspec_interactive_config'.freeze
16
+ DEFAULT_HISTORY_FILE = '.rspec_interactive_history'.freeze
19
17
 
20
- class <<self
21
- attr_accessor :config, :stty_save, :mutex, :config_cache, :runner, :results, :result, :updated_files
18
+ class << self
19
+ attr_accessor :configuration
22
20
  end
23
21
 
24
- def self.start(args)
25
- if args.size > 1
26
- STDERR.puts "expected 0 or 1 argument, got: #{args.join(', ')}"
27
- exit!(1)
28
- end
22
+ def self.configure(&block)
23
+ block.call(@configuration)
24
+ end
29
25
 
26
+ def self.start(config_file: nil, initial_rspec_args: nil, history_file: DEFAULT_HISTORY_FILE, input_stream: STDIN, output_stream: STDOUT, error_stream: STDERR)
27
+ @history_file = history_file
30
28
  @updated_files = []
31
- @results = []
32
- @config = get_config(args[0])
33
29
  @stty_save = %x`stty -g`.chomp
34
30
  @mutex = Mutex.new
31
+ @output_stream = output_stream
32
+ @input_stream = input_stream
33
+ @error_stream = error_stream
35
34
  @config_cache = RSpec::Interactive::ConfigCache.new
36
35
 
37
- load_rspec_config
36
+ @configuration = Configuration.new
37
+ load config_file if config_file
38
+
39
+ @config_cache.record_configuration { @configuration.configure_rspec.call }
40
+
38
41
  check_rails
39
42
  start_file_watcher
40
43
  trap_interrupt
41
44
  configure_pry
42
45
 
46
+ if initial_rspec_args
47
+ rspec initial_rspec_args
48
+ end
49
+
43
50
  Pry.start
51
+ @listener.stop if @listener
52
+ 0
44
53
  end
45
54
 
46
55
  def self.check_rails
47
56
  if defined?(::Rails)
48
57
  if ::Rails.application.config.cache_classes
49
- STDERR.puts "warning: Rails.application.config.cache_classes enabled. Disable to ensure code is reloaded."
58
+ @error_stream.puts "warning: Rails.application.config.cache_classes enabled. Disable to ensure code is reloaded."
50
59
  end
51
60
  end
52
61
  end
53
62
 
54
- def self.load_rspec_config
55
- @config_cache.record_configuration(&rspec_configuration)
56
- end
57
-
58
- def self.rspec_configuration
59
- proc do
60
- if @config["init_script"]
61
- $LOAD_PATH << '.'
62
- require @config["init_script"]
63
- end
64
- end
65
- end
66
-
67
- def self.get_config(name = nil)
68
- unless File.exists? CONFIG_FILE
69
- STDERR.puts "warning: #{CONFIG_FILE} not found, using default config"
70
- return {}
71
- end
72
-
73
- configs = JSON.parse(File.read(CONFIG_FILE))["configs"] || []
74
- if configs.empty?
75
- STDERR.puts "no configs found in: #{CONFIG_FILE}"
76
- exit!(1)
77
- end
78
-
79
- # If a specific config was specified, use it.
80
- if name
81
- config = configs.find { |e| e["name"] == name }
82
- return config if config
83
- STDERR.puts "invalid config: #{name}"
84
- exit!(1)
85
- end
86
-
87
- # If there is only one, use it.
88
- if configs.size == 1
89
- return configs[0]
90
- end
91
-
92
- # Ask the user which to use.
93
- loop do
94
- names = configs.map { |e| e["name"] }
95
- names[0] = "#{names[0]} (default)"
96
- print "Multiple simultaneous configs not yet supported. Please choose a config. #{names.join(', ')}: "
97
- answer = STDIN.gets.chomp
98
- if answer.strip.empty?
99
- return configs[0]
100
- end
101
- config = configs.find { |e| e["name"] == answer }
102
- return config if config
103
- STDERR.puts "invalid config: #{answer}"
63
+ def self.configure_rspec
64
+ RSpec.configure do |config|
65
+ config.error_stream = @error_stream
66
+ config.output_stream = @output_stream
104
67
  end
105
68
  end
106
69
 
@@ -110,7 +73,7 @@ module RSpec
110
73
  # We are on a different thread. There is a race here. Ignore nil.
111
74
  @runner&.quit
112
75
  else
113
- puts
76
+ @output_stream.puts
114
77
  system "stty", @stty_save
115
78
  exit!(0)
116
79
  end
@@ -118,28 +81,76 @@ module RSpec
118
81
  end
119
82
 
120
83
  def self.start_file_watcher
121
- return unless @config["watch_dirs"]
84
+ return if @configuration.watch_dirs.empty?
122
85
 
123
86
  # Only polling seems to work in Docker.
124
- listener = Listen.to(*@config["watch_dirs"], only: /\.rb$/, force_polling: true) do |modified, added, removed|
87
+ @listener = Listen.to(*@configuration.watch_dirs, only: /\.rb$/, force_polling: true) do |modified, added, removed|
125
88
  @mutex.synchronize do
126
89
  @updated_files.concat(added + modified)
127
90
  end
128
91
  end
129
- listener.start
92
+ @listener.start
130
93
  end
131
94
 
132
95
  def self.configure_pry
133
96
  # Prevent Pry from trapping too. It will break ctrl-c handling.
134
97
  Pry.config.should_trap_interrupts = false
135
98
 
136
- # Set Pry to use Readline. This is the default anyway.
99
+ # Set up IO.
137
100
  Pry.config.input = Readline
101
+ Pry.config.output = @output_stream
102
+ Readline.output = @output_stream
103
+ Readline.input = @input_stream
138
104
 
139
105
  # Use custom completer to get file completion.
140
106
  Pry.config.completer = RSpec::Interactive::InputCompleter
141
107
 
142
- Pry.config.history_file = HISTORY_FILE
108
+ Pry.config.history_file = @history_file
109
+ end
110
+
111
+ def self.rspec(args)
112
+ parsed_args = args.flat_map do |arg|
113
+ if arg.match(/[\*\?\[]/)
114
+ glob = Dir.glob(arg)
115
+ glob.empty? ? [arg] : glob
116
+ else
117
+ [arg]
118
+ end
119
+ end
120
+
121
+ @mutex.synchronize do
122
+ @updated_files.uniq.each do |filename|
123
+ @output_stream.puts "modified: #{filename}"
124
+ trace = TracePoint.new(:class) do |tp|
125
+ @configuration.on_class_load.call(tp.self)
126
+ end
127
+ trace.enable
128
+ load filename
129
+ trace.disable
130
+ @output_stream.puts
131
+ end
132
+ @updated_files.clear
133
+ end
134
+
135
+ @runner = RSpec::Interactive::Runner.new(parsed_args)
136
+
137
+ # Stop saving history in case a new Pry session is started for debugging.
138
+ Pry.config.history_save = false
139
+
140
+ # RSpec::Interactive-specific RSpec configuration
141
+ configure_rspec
142
+
143
+ # Run.
144
+ exit_code = @runner.run
145
+ @runner = nil
146
+
147
+ # Reenable history
148
+ Pry.config.history_save = true
149
+
150
+ # Reset
151
+ RSpec.clear_examples
152
+ RSpec.reset
153
+ @config_cache.replay_configuration
143
154
  end
144
155
  end
145
156
  end