spec_forge 0.6.0 → 0.7.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.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +112 -2
  3. data/README.md +133 -8
  4. data/flake.lock +3 -3
  5. data/flake.nix +3 -3
  6. data/lib/spec_forge/attribute/factory.rb +1 -1
  7. data/lib/spec_forge/callbacks.rb +9 -0
  8. data/lib/spec_forge/cli/docs/generate.rb +72 -0
  9. data/lib/spec_forge/cli/docs.rb +92 -0
  10. data/lib/spec_forge/cli/init.rb +39 -7
  11. data/lib/spec_forge/cli/new.rb +13 -3
  12. data/lib/spec_forge/cli/run.rb +12 -4
  13. data/lib/spec_forge/cli/serve.rb +155 -0
  14. data/lib/spec_forge/cli.rb +14 -6
  15. data/lib/spec_forge/configuration.rb +2 -2
  16. data/lib/spec_forge/context/store.rb +23 -40
  17. data/lib/spec_forge/core_ext/array.rb +27 -0
  18. data/lib/spec_forge/documentation/builder.rb +383 -0
  19. data/lib/spec_forge/documentation/document/operation.rb +47 -0
  20. data/lib/spec_forge/documentation/document/parameter.rb +22 -0
  21. data/lib/spec_forge/documentation/document/request_body.rb +24 -0
  22. data/lib/spec_forge/documentation/document/response.rb +39 -0
  23. data/lib/spec_forge/documentation/document/response_body.rb +27 -0
  24. data/lib/spec_forge/documentation/document.rb +48 -0
  25. data/lib/spec_forge/documentation/generators/base.rb +81 -0
  26. data/lib/spec_forge/documentation/generators/openapi/base.rb +100 -0
  27. data/lib/spec_forge/documentation/generators/openapi/error_formatter.rb +149 -0
  28. data/lib/spec_forge/documentation/generators/openapi/v3_0.rb +65 -0
  29. data/lib/spec_forge/documentation/generators/openapi.rb +59 -0
  30. data/lib/spec_forge/documentation/generators.rb +17 -0
  31. data/lib/spec_forge/documentation/loader/cache.rb +138 -0
  32. data/lib/spec_forge/documentation/loader.rb +159 -0
  33. data/lib/spec_forge/documentation/openapi/base.rb +33 -0
  34. data/lib/spec_forge/documentation/openapi/v3_0/example.rb +44 -0
  35. data/lib/spec_forge/documentation/openapi/v3_0/media_type.rb +42 -0
  36. data/lib/spec_forge/documentation/openapi/v3_0/operation.rb +175 -0
  37. data/lib/spec_forge/documentation/openapi/v3_0/response.rb +65 -0
  38. data/lib/spec_forge/documentation/openapi/v3_0/schema.rb +80 -0
  39. data/lib/spec_forge/documentation/openapi/v3_0/tag.rb +71 -0
  40. data/lib/spec_forge/documentation/openapi.rb +23 -0
  41. data/lib/spec_forge/documentation.rb +27 -0
  42. data/lib/spec_forge/error.rb +17 -0
  43. data/lib/spec_forge/factory.rb +2 -2
  44. data/lib/spec_forge/filter.rb +3 -4
  45. data/lib/spec_forge/forge.rb +5 -4
  46. data/lib/spec_forge/http/backend.rb +2 -0
  47. data/lib/spec_forge/http/request.rb +14 -3
  48. data/lib/spec_forge/loader.rb +14 -24
  49. data/lib/spec_forge/normalizer/default.rb +51 -0
  50. data/lib/spec_forge/normalizer/definition.rb +248 -0
  51. data/lib/spec_forge/normalizer/validators.rb +99 -0
  52. data/lib/spec_forge/normalizer.rb +356 -199
  53. data/lib/spec_forge/normalizers/_shared.yml +74 -0
  54. data/lib/spec_forge/normalizers/configuration.yml +23 -0
  55. data/lib/spec_forge/normalizers/constraint.yml +8 -0
  56. data/lib/spec_forge/normalizers/expectation.yml +47 -0
  57. data/lib/spec_forge/normalizers/factory.yml +12 -0
  58. data/lib/spec_forge/normalizers/factory_reference.yml +15 -0
  59. data/lib/spec_forge/normalizers/global_context.yml +28 -0
  60. data/lib/spec_forge/normalizers/spec.yml +50 -0
  61. data/lib/spec_forge/runner/adapter.rb +183 -0
  62. data/lib/spec_forge/runner/debug_proxy.rb +3 -3
  63. data/lib/spec_forge/runner/state.rb +4 -5
  64. data/lib/spec_forge/runner.rb +40 -124
  65. data/lib/spec_forge/spec/expectation/constraint.rb +13 -5
  66. data/lib/spec_forge/spec/expectation.rb +7 -3
  67. data/lib/spec_forge/spec.rb +13 -58
  68. data/lib/spec_forge/version.rb +1 -1
  69. data/lib/spec_forge.rb +30 -23
  70. data/lib/templates/openapi.yml.tt +22 -0
  71. data/lib/templates/redoc.html.tt +28 -0
  72. data/lib/templates/swagger.html.tt +59 -0
  73. metadata +92 -14
  74. data/lib/spec_forge/normalizer/configuration.rb +0 -90
  75. data/lib/spec_forge/normalizer/constraint.rb +0 -60
  76. data/lib/spec_forge/normalizer/expectation.rb +0 -105
  77. data/lib/spec_forge/normalizer/factory.rb +0 -78
  78. data/lib/spec_forge/normalizer/factory_reference.rb +0 -85
  79. data/lib/spec_forge/normalizer/global_context.rb +0 -88
  80. data/lib/spec_forge/normalizer/spec.rb +0 -97
  81. /data/lib/templates/{forge_helper.tt → forge_helper.rb.tt} +0 -0
  82. /data/lib/templates/{new_factory.tt → new_factory.yml.tt} +0 -0
  83. /data/lib/templates/{new_spec.tt → new_spec.yml.tt} +0 -0
@@ -0,0 +1,74 @@
1
+ id: string
2
+
3
+ name: string
4
+
5
+ line_number: integer
6
+
7
+ base_url:
8
+ type: string
9
+ default: null
10
+ required: false
11
+
12
+ url:
13
+ type: string
14
+ default: null
15
+ required: false
16
+ aliases:
17
+ - path
18
+
19
+ http_verb:
20
+ type: string
21
+ default: null # Do not default this to "GET". Leave it null. Seriously.
22
+ required: false
23
+ aliases:
24
+ - method
25
+ - http_method
26
+ validator: http_verb
27
+
28
+ headers:
29
+ type: hash
30
+ default: {}
31
+ required: false
32
+
33
+ query:
34
+ type:
35
+ - hash
36
+ - string
37
+ aliases:
38
+ - params
39
+ default: {}
40
+ required: false
41
+
42
+ body:
43
+ type:
44
+ - hash
45
+ - string
46
+ aliases:
47
+ - data
48
+ default: {}
49
+ required: false
50
+
51
+ variables:
52
+ type:
53
+ - hash
54
+ - string
55
+ default: {}
56
+ required: false
57
+
58
+ debug:
59
+ type: boolean
60
+ aliases:
61
+ - pry
62
+ - breakpoint
63
+ default: false
64
+ required: false
65
+
66
+ callback:
67
+ type: string
68
+ required: false
69
+ validator: callback
70
+
71
+ documentation:
72
+ type: boolean
73
+ required: false
74
+ default: true
@@ -0,0 +1,23 @@
1
+ base_url: string
2
+
3
+ headers:
4
+ reference: headers
5
+
6
+ query:
7
+ reference: query
8
+
9
+ factories:
10
+ type: hash
11
+ default: {}
12
+ structure:
13
+ ###########################################
14
+ auto_discover:
15
+ type: boolean
16
+ default: true
17
+
18
+ paths:
19
+ type: array
20
+ default: []
21
+
22
+ on_debug:
23
+ type: proc
@@ -0,0 +1,8 @@
1
+ status:
2
+ - integer
3
+ - string
4
+ json:
5
+ type:
6
+ - hash
7
+ - array
8
+ default: {}
@@ -0,0 +1,47 @@
1
+ # Internal
2
+ id:
3
+ reference: id
4
+
5
+ line_number:
6
+ reference: line_number
7
+
8
+ # User defined
9
+ name:
10
+ reference: name
11
+
12
+ base_url:
13
+ reference: base_url
14
+
15
+ url:
16
+ reference: url
17
+
18
+ http_verb:
19
+ reference: http_verb
20
+
21
+ headers:
22
+ reference: headers
23
+
24
+ query:
25
+ reference: query
26
+
27
+ body:
28
+ reference: body
29
+
30
+ variables:
31
+ reference: variables
32
+
33
+ debug:
34
+ reference: debug
35
+
36
+ store_as:
37
+ type: string
38
+ default: ""
39
+
40
+ documentation:
41
+ reference: documentation
42
+
43
+ expect:
44
+ type: hash
45
+ structure:
46
+ ###########################################
47
+ reference: constraint
@@ -0,0 +1,12 @@
1
+ model_class:
2
+ type: string
3
+ default: ""
4
+ aliases:
5
+ - class
6
+
7
+ variables:
8
+ reference: variables
9
+
10
+ attributes:
11
+ type: hash
12
+ default: {}
@@ -0,0 +1,15 @@
1
+ attributes:
2
+ type: hash
3
+ default: {}
4
+
5
+ build_strategy:
6
+ type: string
7
+ default: create
8
+ aliases:
9
+ - strategy
10
+
11
+ size:
12
+ type: integer
13
+ default: 0
14
+ aliases:
15
+ - count
@@ -0,0 +1,28 @@
1
+ variables:
2
+ reference: variables
3
+
4
+ callbacks:
5
+ type: array
6
+ default: []
7
+ structure:
8
+ ###########################################
9
+ type: hash
10
+ default: {}
11
+ structure:
12
+ ###########################################
13
+ before_file:
14
+ reference: callback
15
+ before_spec:
16
+ reference: callback
17
+ before_each:
18
+ reference: callback
19
+ aliases:
20
+ - before
21
+ after_each:
22
+ reference: callback
23
+ aliases:
24
+ - after
25
+ after_spec:
26
+ reference: callback
27
+ after_file:
28
+ reference: callback
@@ -0,0 +1,50 @@
1
+ # Internal
2
+ id:
3
+ reference: id
4
+
5
+ name:
6
+ reference: name
7
+
8
+ file_name: string
9
+
10
+ file_path: string
11
+
12
+ line_number:
13
+ reference: line_number
14
+
15
+ # User defined
16
+ base_url:
17
+ reference: base_url
18
+
19
+ url:
20
+ reference: url
21
+
22
+ http_verb:
23
+ reference: http_verb
24
+
25
+ headers:
26
+ reference: headers
27
+
28
+ query:
29
+ reference: query
30
+
31
+ body:
32
+ reference: body
33
+
34
+ variables:
35
+ reference: variables
36
+
37
+ debug:
38
+ reference: debug
39
+
40
+ documentation:
41
+ reference: documentation
42
+
43
+ expectations:
44
+ type: array
45
+ structure:
46
+ ###########################################
47
+ type: hash
48
+ structure:
49
+ ###########################################
50
+ reference: expectation
@@ -0,0 +1,183 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SpecForge
4
+ class Runner
5
+ #
6
+ # Bridges SpecForge specs with RSpec execution
7
+ #
8
+ # Converts SpecForge forge objects into RSpec test structures
9
+ # and manages the test execution lifecycle.
10
+ #
11
+ class Adapter
12
+ include Singleton
13
+
14
+ #
15
+ # Configures RSpec with forge definitions
16
+ #
17
+ # Sets up RSpec and prepares everything for running tests
18
+ #
19
+ # @param forges [Array<Forge>] The forges to set up for testing
20
+ #
21
+ def self.setup(forges)
22
+ # Defines the forges with RSpec
23
+ forges.each { |forge| instance.describe(forge) }
24
+
25
+ # Disable autorun because RSpec does it
26
+ RSpec::Core::Runner.disable_autorun!
27
+
28
+ # Allows modifying the error backtrace reporting within rspec
29
+ RSpec.configuration.instance_variable_set(:@backtrace_formatter, BacktraceFormatter)
30
+
31
+ # Listen for passed/failed events to trigger the "after_each" callback
32
+ RSpec.configuration.reporter.register_listener(
33
+ Listener.instance,
34
+ :example_passed, :example_failed
35
+ )
36
+ end
37
+
38
+ #
39
+ # Executes the configured RSpec tests
40
+ #
41
+ # Runs all configured tests through RSpec with optional exit behavior.
42
+ #
43
+ # @param exit_on_finish [Boolean] Whether to exit the process when done
44
+ # @param exit_on_failure [Boolean] Whether to exit the process if any test fails
45
+ #
46
+ # @return [Integer, nil] Exit status if exit_on_finish is false
47
+ #
48
+ def self.run(exit_on_finish: false, exit_on_failure: false)
49
+ status = RSpec::Core::Runner.run([]).to_i
50
+
51
+ exit(status) if exit_on_finish || (exit_on_failure && status != 0)
52
+
53
+ status
54
+ end
55
+
56
+ ##########################################################################
57
+
58
+ #
59
+ # Defines RSpec examples for a specific forge
60
+ # Creates the test structure for a single forge file
61
+ #
62
+ # @param forge [Forge] The forge to define
63
+ #
64
+ def describe(forge)
65
+ # This is just like writing a normal RSpec test, except with loops ;)
66
+ RSpec.describe(forge.name) do
67
+ # Callback for the file
68
+ before(:context) { Callbacks.before_file(forge) }
69
+ after(:context) { Callbacks.after_file(forge) }
70
+
71
+ # Specs
72
+ forge.specs.each do |spec|
73
+ # Describe the spec
74
+ describe(spec.name) do
75
+ # Request data is for the spec and contains the base and overlays
76
+ let!(:request_data) { forge.request[spec.id] }
77
+
78
+ # The HTTP client for the spec
79
+ let!(:http_client) { HTTP::Client.new(**request_data[:base]) }
80
+
81
+ # Callback for the spec
82
+ before(:context) { Callbacks.before_spec(forge, spec) }
83
+ after(:context) { Callbacks.after_spec(forge, spec) }
84
+
85
+ # Expectations
86
+ spec.expectations.each do |expectation|
87
+ # Onto the actual expectation itself
88
+ describe(expectation.name) do
89
+ # Set metadata for the example group for error reporting
90
+ Metadata.set_for_group(spec, expectation, self)
91
+
92
+ # Lazily load the constraints
93
+ let(:constraints) { expectation.constraints.as_matchers }
94
+
95
+ let(:match_status) { constraints[:status] }
96
+ let(:match_json) { constraints[:json] }
97
+ let(:match_json_class) { be_kind_of(match_json.class) }
98
+ let(:match_headers) { constraints[:headers] }
99
+
100
+ # The request for the test itself. Overlays the expectation's data if it exists
101
+ let(:request) do
102
+ request = request_data[:base]
103
+
104
+ if (overlay = request_data[:overlay][expectation.id])
105
+ request = request.deep_merge(overlay)
106
+ end
107
+
108
+ HTTP::Request.new(**request)
109
+ end
110
+
111
+ # The Faraday response
112
+ subject(:response) { http_client.call(request) }
113
+
114
+ # Callbacks for the expectation
115
+ before :each do
116
+ Callbacks.before_expectation(
117
+ forge, spec, expectation, self, RSpec.current_example
118
+ )
119
+ end
120
+
121
+ # The 'after_expectation' callback is handled by Listener due to RSpec not
122
+ # reporting the example's status until after the describe block has finished.
123
+ after :each do
124
+ # However, the downside about having the callback triggered later is that RSpec
125
+ # will have reset the memoized let variables back to nil.
126
+ # This causes an issue when an expectation goes to store the state, it will end
127
+ # up re-calling the various variables and triggering another HTTP request.
128
+ # Since the variables are still memoized in this hook, it is the perfect
129
+ # time to store the referenced to them.
130
+ State.set(response:)
131
+ end
132
+
133
+ # The test itself
134
+ it(expectation.constraints.description) do
135
+ # Debugging
136
+ if spec.debug? || expectation.debug?
137
+ Callbacks.on_debug(forge, spec, expectation, self)
138
+ end
139
+
140
+ ############################################################
141
+ # Status check
142
+ expect(response.status).to match_status
143
+
144
+ ############################################################
145
+ # Headers check
146
+ if match_headers.present?
147
+ match_headers.each do |key, matcher|
148
+ expect(response.headers).to include(key.downcase => matcher)
149
+ end
150
+ end
151
+
152
+ ############################################################
153
+ # JSON check
154
+ if match_json.present?
155
+ expect(response.body).to match_json_class
156
+
157
+ case match_json
158
+ when Hash
159
+ match_json.each do |key, matcher|
160
+ expect(response.body).to include(key)
161
+
162
+ begin
163
+ expect(response.body[key]).to matcher
164
+ rescue RSpec::Expectations::ExpectationNotMetError => e
165
+ # Add the key that failed to the front of the error message
166
+ e.message.insert(0, "Key: #{key.in_quotes}\n")
167
+ raise e
168
+ end
169
+ end
170
+ else
171
+ expect(response.body).to match_json
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
182
+ end
183
+ end
@@ -179,14 +179,14 @@ module SpecForge
179
179
  expectation_hash[:expect][:json] = matchers_to_description(expectation_hash[:expect][:json])
180
180
 
181
181
  {
182
+ global:,
183
+ variables:,
184
+ request: request.to_h,
182
185
  response: {
183
186
  status: response.status,
184
187
  body: response.body,
185
188
  headers: response.headers
186
189
  },
187
- global:,
188
- variables:,
189
- request: request.to_h,
190
190
  expectation: expectation_hash,
191
191
  spec: spec_hash
192
192
  }
@@ -87,11 +87,10 @@ module SpecForge
87
87
  scope:,
88
88
  request: request&.to_h,
89
89
  variables: SpecForge.context.variables.deep_dup,
90
- response: {
91
- headers: response&.headers,
92
- status: response&.status,
93
- body: response&.body
94
- }
90
+ response:,
91
+ headers: response&.headers,
92
+ status: response&.status,
93
+ body: response&.body
95
94
  )
96
95
  end
97
96
  end
@@ -8,150 +8,66 @@ module SpecForge
8
8
  class Runner
9
9
  class << self
10
10
  #
11
- # Defines RSpec examples for a collection of forges
12
- # Creates the test structure that will be executed
11
+ # Prepares forge objects for test execution
13
12
  #
14
- # @param forges [Array<Forge>] The forges to define as RSpec examples
13
+ # Loads the forge helper, registers factories, loads specs from files,
14
+ # applies filtering, and returns ready-to-run forge objects.
15
15
  #
16
- def define(forges)
17
- forges.each do |forge|
18
- define_forge(forge)
19
- end
20
- end
21
-
22
- #
23
- # Runs the defined RSpec examples
24
- # Executes the tests after they've been defined
25
- #
26
- def run
27
- prepare_for_run
28
-
29
- ARGV.clear
30
- RSpec::Core::Runner.invoke
31
- end
32
-
33
- #
34
- # Defines RSpec examples for a specific forge
35
- # Creates the test structure for a single forge file
16
+ # @param file_name [String, nil] Optional file name filter
17
+ # @param spec_name [String, nil] Optional spec name filter
18
+ # @param expectation_name [String, nil] Optional expectation name filter
36
19
  #
37
- # @param forge [Forge] The forge to define
20
+ # @return [Array<Forge>] Array of prepared forge objects
38
21
  #
39
- def define_forge(forge)
40
- # This is just like writing a normal RSpec test, except with loops ;)
41
- RSpec.describe(forge.name) do
42
- # Callback for the file
43
- before(:context) { Callbacks.before_file(forge) }
44
- after(:context) { Callbacks.after_file(forge) }
45
-
46
- # Specs
47
- forge.specs.each do |spec|
48
- # Describe the spec
49
- describe(spec.name) do
50
- # Request data is for the spec and contains the base and overlays
51
- let!(:request_data) { forge.request[spec.id] }
52
-
53
- # The HTTP client for the spec
54
- let!(:http_client) { HTTP::Client.new(**request_data[:base]) }
55
-
56
- # Callback for the spec
57
- before(:context) { Callbacks.before_spec(forge, spec) }
58
- after(:context) { Callbacks.after_spec(forge, spec) }
59
-
60
- # Expectations
61
- spec.expectations.each do |expectation|
62
- # Onto the actual expectation itself
63
- describe(expectation.name) do
64
- # Set metadata for the example group for error reporting
65
- Metadata.set_for_group(spec, expectation, self)
22
+ def prepare(file_name: nil, spec_name: nil, expectation_name: nil)
23
+ load_forge_helper
66
24
 
67
- # Lazily load the constraints
68
- let(:constraints) { expectation.constraints.as_matchers }
25
+ # Load factories
26
+ Factory.load_and_register
69
27
 
70
- let(:match_status) { constraints[:status] }
71
- let(:match_json) { constraints[:json] }
72
- let(:match_json_class) { be_kind_of(match_json.class) }
28
+ # Load the specs from their files and create forges from them
29
+ forges = Loader.load_from_files.map { |f| Forge.new(*f) }
73
30
 
74
- # The request for the test itself. Overlays the expectation's data if it exists
75
- let(:request) do
76
- request = request_data[:base]
31
+ # Filter out the specs and expectations
32
+ forges = Filter.apply(forges, file_name:, spec_name:, expectation_name:)
77
33
 
78
- if (overlay = request_data[:overlay][expectation.id])
79
- request = request.deep_merge(overlay)
80
- end
34
+ # Tell the user that we filtered if we did
35
+ Filter.announce(forges, file_name:, spec_name:, expectation_name:)
81
36
 
82
- HTTP::Request.new(**request)
83
- end
84
-
85
- # The Faraday response
86
- subject(:response) { http_client.call(request) }
87
-
88
- # Callbacks for the expectation
89
- before :each do
90
- Callbacks.before_expectation(
91
- forge, spec, expectation, self, RSpec.current_example
92
- )
93
- end
94
-
95
- # The 'after_expectation' callback is handled by Listener due to RSpec not
96
- # reporting the example's status until after the describe block has finished.
97
- after :each do
98
- # However, the downside about having the callback triggered later is that RSpec
99
- # will have reset the memoized let variables back to nil.
100
- # This causes an issue when an expectation goes to store the state, it will end
101
- # up re-calling the various variables and triggering another HTTP request.
102
- # Since the variables are still memoized in this hook, it is the perfect
103
- # time to store the referenced to them.
104
- State.set(response:)
105
- end
106
-
107
- # The test itself
108
- it(expectation.constraints.description) do
109
- if spec.debug? || expectation.debug?
110
- Callbacks.on_debug(forge, spec, expectation, self)
111
- end
112
-
113
- # Status check
114
- expect(response.status).to match_status
115
-
116
- # JSON check
117
- if match_json.present?
118
- expect(response.body).to match_json_class
37
+ forges
38
+ end
119
39
 
120
- case match_json
121
- when Hash
122
- # Check per key for easier debugging
123
- match_json.each do |key, matcher|
124
- expect(response.body).to have_key(key)
125
- expect(response.body[key]).to matcher
126
- end
127
- else
128
- expect(response.body).to match_json
129
- end
130
- end
131
- end
132
- end
133
- end
134
- end
135
- end
136
- end
40
+ #
41
+ # Runs the prepared forges through RSpec
42
+ #
43
+ # Sets up the RSpec adapter and executes all tests, with optional
44
+ # exit behavior for CLI usage.
45
+ #
46
+ # @param forges [Array<Forge>] The forge objects to run
47
+ # @param exit_on_finish [Boolean] Whether to exit the process when complete
48
+ # @param exit_on_failure [Boolean] Whether to exit the process if any test fails
49
+ #
50
+ # @return [Integer, nil] Exit status if exit_on_finish is false
51
+ #
52
+ def run(forges, **)
53
+ Adapter.setup(forges)
54
+ Adapter.run(**)
137
55
  end
138
56
 
139
57
  private
140
58
 
141
- def prepare_for_run
142
- # Allows modifying the error backtrace reporting within rspec
143
- RSpec.configuration.instance_variable_set(:@backtrace_formatter, BacktraceFormatter)
59
+ def load_forge_helper
60
+ forge_helper = SpecForge.forge_path.join("forge_helper.rb")
61
+ require_relative forge_helper if File.exist?(forge_helper)
144
62
 
145
- # Listen for passed/failed events to trigger the "after_each" callback
146
- RSpec.configuration.reporter.register_listener(
147
- Listener.instance,
148
- :example_passed, :example_failed
149
- )
63
+ # Validate in case anything was changed
64
+ SpecForge.configuration.validate
150
65
  end
151
66
  end
152
67
  end
153
68
  end
154
69
 
70
+ require_relative "runner/adapter"
155
71
  require_relative "runner/callbacks"
156
72
  require_relative "runner/debug_proxy"
157
73
  require_relative "runner/listener"