rspec-interactive 0.2.0 → 0.6.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: 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