spec_forge 0.7.1 → 1.0.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 (133) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +75 -1
  3. data/README.md +124 -202
  4. data/bin/spec_forge +1 -1
  5. data/flake.lock +76 -4
  6. data/flake.nix +5 -4
  7. data/lib/spec_forge/attribute/chainable.rb +6 -6
  8. data/lib/spec_forge/attribute/environment.rb +45 -0
  9. data/lib/spec_forge/attribute/factory.rb +26 -17
  10. data/lib/spec_forge/attribute/faker.rb +6 -1
  11. data/lib/spec_forge/attribute/generate.rb +114 -0
  12. data/lib/spec_forge/attribute/literal.rb +1 -14
  13. data/lib/spec_forge/attribute/matcher.rb +6 -2
  14. data/lib/spec_forge/attribute/parameterized.rb +20 -22
  15. data/lib/spec_forge/attribute/resolvable_array.rb +16 -16
  16. data/lib/spec_forge/attribute/resolvable_hash.rb +17 -16
  17. data/lib/spec_forge/attribute/resolvable_struct.rb +67 -0
  18. data/lib/spec_forge/attribute/template.rb +118 -0
  19. data/lib/spec_forge/attribute/transform.rb +14 -19
  20. data/lib/spec_forge/attribute/variable.rb +31 -31
  21. data/lib/spec_forge/attribute.rb +54 -100
  22. data/lib/spec_forge/blueprint.rb +27 -0
  23. data/lib/spec_forge/cli/docs/generate.rb +28 -8
  24. data/lib/spec_forge/cli/docs.rb +5 -2
  25. data/lib/spec_forge/cli/init.rb +4 -4
  26. data/lib/spec_forge/cli/new.rb +78 -27
  27. data/lib/spec_forge/cli/run.rb +84 -52
  28. data/lib/spec_forge/cli/serve.rb +5 -0
  29. data/lib/spec_forge/cli.rb +6 -14
  30. data/lib/spec_forge/configuration.rb +209 -79
  31. data/lib/spec_forge/documentation/{loader → builder}/cache.rb +26 -23
  32. data/lib/spec_forge/documentation/builder/compiler.rb +373 -0
  33. data/lib/spec_forge/documentation/builder/extractor.rb +75 -0
  34. data/lib/spec_forge/documentation/builder.rb +77 -329
  35. data/lib/spec_forge/documentation/document/operation.rb +4 -4
  36. data/lib/spec_forge/documentation/document.rb +0 -6
  37. data/lib/spec_forge/documentation/generator.rb +88 -0
  38. data/lib/spec_forge/documentation/{generators/openapi → openapi/v3_0}/error_formatter.rb +2 -2
  39. data/lib/spec_forge/documentation/openapi/v3_0/example.rb +1 -1
  40. data/lib/spec_forge/documentation/openapi/v3_0/media_type.rb +1 -1
  41. data/lib/spec_forge/documentation/openapi/v3_0/operation.rb +21 -5
  42. data/lib/spec_forge/documentation/openapi/v3_0/response.rb +28 -6
  43. data/lib/spec_forge/documentation/openapi/v3_0/schema.rb +20 -2
  44. data/lib/spec_forge/documentation/openapi/v3_0/tag.rb +1 -1
  45. data/lib/spec_forge/documentation/openapi/v3_0.rb +116 -0
  46. data/lib/spec_forge/documentation/openapi.rb +40 -12
  47. data/lib/spec_forge/documentation.rb +1 -7
  48. data/lib/spec_forge/error.rb +215 -41
  49. data/lib/spec_forge/factory.rb +38 -18
  50. data/lib/spec_forge/forge/action.rb +41 -0
  51. data/lib/spec_forge/forge/actions/call.rb +33 -0
  52. data/lib/spec_forge/forge/actions/debug.rb +47 -0
  53. data/lib/spec_forge/forge/actions/expect.rb +44 -0
  54. data/lib/spec_forge/forge/actions/request.rb +65 -0
  55. data/lib/spec_forge/forge/actions/store.rb +31 -0
  56. data/lib/spec_forge/forge/callbacks.rb +80 -0
  57. data/lib/spec_forge/forge/context.rb +41 -0
  58. data/lib/spec_forge/forge/display.rb +503 -0
  59. data/lib/spec_forge/forge/hooks.rb +131 -0
  60. data/lib/spec_forge/forge/runner/array_io.rb +81 -0
  61. data/lib/spec_forge/forge/runner/content_validator.rb +92 -0
  62. data/lib/spec_forge/forge/runner/header_validator.rb +66 -0
  63. data/lib/spec_forge/forge/runner/reporter.rb +56 -0
  64. data/lib/spec_forge/forge/runner/schema_validator.rb +113 -0
  65. data/lib/spec_forge/forge/runner.rb +118 -0
  66. data/lib/spec_forge/forge/timer.rb +94 -0
  67. data/lib/spec_forge/forge/variables.rb +38 -0
  68. data/lib/spec_forge/forge.rb +207 -133
  69. data/lib/spec_forge/http/backend.rb +49 -146
  70. data/lib/spec_forge/http/client.rb +14 -17
  71. data/lib/spec_forge/http/request.rb +37 -84
  72. data/lib/spec_forge/http/verb.rb +4 -0
  73. data/lib/spec_forge/http.rb +0 -5
  74. data/lib/spec_forge/loader/filter.rb +85 -0
  75. data/lib/spec_forge/loader/step_processor.rb +282 -0
  76. data/lib/spec_forge/loader.rb +105 -220
  77. data/lib/spec_forge/normalizer/default.rb +1 -1
  78. data/lib/spec_forge/normalizer/structure.rb +140 -0
  79. data/lib/spec_forge/normalizer/transformers.rb +168 -0
  80. data/lib/spec_forge/normalizer/validators.rb +50 -8
  81. data/lib/spec_forge/normalizer.rb +76 -119
  82. data/lib/spec_forge/normalizers/callback.yml +38 -0
  83. data/lib/spec_forge/normalizers/configuration.yml +59 -9
  84. data/lib/spec_forge/normalizers/factory.yml +53 -2
  85. data/lib/spec_forge/normalizers/factory_reference.yml +63 -2
  86. data/lib/spec_forge/normalizers/json_schema.yml +79 -0
  87. data/lib/spec_forge/normalizers/step.yml +506 -0
  88. data/lib/spec_forge/step/call.rb +36 -0
  89. data/lib/spec_forge/step/expect.rb +110 -0
  90. data/lib/spec_forge/step/source.rb +22 -0
  91. data/lib/spec_forge/step.rb +129 -0
  92. data/lib/spec_forge/type.rb +115 -66
  93. data/lib/spec_forge/version.rb +1 -1
  94. data/lib/spec_forge.rb +44 -106
  95. data/lib/templates/forge_helper.rb.tt +43 -22
  96. data/lib/templates/new_blueprint.yml.tt +54 -0
  97. metadata +75 -44
  98. data/lib/spec_forge/attribute/global.rb +0 -96
  99. data/lib/spec_forge/attribute/store.rb +0 -65
  100. data/lib/spec_forge/backtrace_formatter.rb +0 -50
  101. data/lib/spec_forge/callbacks.rb +0 -88
  102. data/lib/spec_forge/context/callbacks.rb +0 -91
  103. data/lib/spec_forge/context/global.rb +0 -72
  104. data/lib/spec_forge/context/store.rb +0 -131
  105. data/lib/spec_forge/context/variables.rb +0 -91
  106. data/lib/spec_forge/context.rb +0 -36
  107. data/lib/spec_forge/core_ext/rspec.rb +0 -55
  108. data/lib/spec_forge/core_ext.rb +0 -5
  109. data/lib/spec_forge/documentation/generators/base.rb +0 -81
  110. data/lib/spec_forge/documentation/generators/openapi/base.rb +0 -100
  111. data/lib/spec_forge/documentation/generators/openapi/v3_0.rb +0 -65
  112. data/lib/spec_forge/documentation/generators/openapi.rb +0 -59
  113. data/lib/spec_forge/documentation/generators.rb +0 -17
  114. data/lib/spec_forge/documentation/loader.rb +0 -159
  115. data/lib/spec_forge/documentation/openapi/base.rb +0 -33
  116. data/lib/spec_forge/filter.rb +0 -86
  117. data/lib/spec_forge/normalizer/definition.rb +0 -248
  118. data/lib/spec_forge/normalizers/_shared.yml +0 -76
  119. data/lib/spec_forge/normalizers/constraint.yml +0 -8
  120. data/lib/spec_forge/normalizers/expectation.yml +0 -47
  121. data/lib/spec_forge/normalizers/global_context.yml +0 -28
  122. data/lib/spec_forge/normalizers/spec.yml +0 -50
  123. data/lib/spec_forge/runner/adapter.rb +0 -181
  124. data/lib/spec_forge/runner/callbacks.rb +0 -246
  125. data/lib/spec_forge/runner/debug_proxy.rb +0 -215
  126. data/lib/spec_forge/runner/listener.rb +0 -54
  127. data/lib/spec_forge/runner/metadata.rb +0 -58
  128. data/lib/spec_forge/runner/state.rb +0 -98
  129. data/lib/spec_forge/runner.rb +0 -75
  130. data/lib/spec_forge/spec/expectation/constraint.rb +0 -127
  131. data/lib/spec_forge/spec/expectation.rb +0 -68
  132. data/lib/spec_forge/spec.rb +0 -68
  133. data/lib/templates/new_spec.yml.tt +0 -43
@@ -4,13 +4,13 @@
4
4
  # Framework Integration
5
5
  ##########################################
6
6
 
7
- # Rails Integration
7
+ ## Rails Integration
8
8
  # require_relative "../config/environment"
9
9
 
10
- # RSpec Integration (includes your existing configurations)
10
+ ## RSpec Integration (includes your existing configurations)
11
11
  # require_relative "../spec/spec_helper"
12
12
 
13
- # Custom requires (models, libraries, etc)
13
+ ## Custom requires (models, libraries, etc)
14
14
  # Dir[File.join(__dir__, "..", "lib", "**", "*.rb")].sort.each { |f| require f }
15
15
 
16
16
  ##########################################
@@ -18,31 +18,52 @@
18
18
  ##########################################
19
19
 
20
20
  SpecForge.configure do |config|
21
- # Base configuration
21
+ # Base URL for all requests
22
22
  config.base_url = "http://localhost:3000"
23
23
 
24
- # Default request headers
25
- config.headers = {
26
- "Authorization" => "Bearer #{ENV.fetch("API_TOKEN", "")}"
27
- }
24
+ ## Global variables (available in all blueprints via {{ variable_name }})
25
+ ## Use for shared values like auth tokens, API versions, etc.
26
+ # config.global_variables = {
27
+ # api_version: "v1",
28
+ # auth_header: "Bearer #{ENV['API_TOKEN']}"
29
+ # }
28
30
 
29
- # Optional: Default query parameters
30
- # config.query = {api_key: ENV['API_KEY']}
31
-
32
- # Factory configuration
31
+ ## Factory configuration
33
32
  # config.factories.auto_discover = false # Default: true
34
33
  # config.factories.paths += ["lib/factories"] # Adds to default paths
35
34
 
36
- # Debug configuration
37
- # Available in specs with debug: true (aliases: breakpoint, pry)
38
- # Defaults to printing state overview (-> { puts inspect })
39
- # Available context: expectation, variables, request, response,
40
- # expected_status, expected_json
35
+ ## Debug configuration
36
+ # Triggers when debug: true is set on a step
41
37
  # config.on_debug { binding.pry }
42
38
 
43
- # Test Framework Configuration
44
- # Useful for database cleaners, test data setup, etc
45
- # config.specs.before(:suite) { }
46
- # config.specs.around { |example| example.run }
47
- # config.specs.formatter = :documentation
39
+ ## Callbacks and Hooks
40
+ # Register reusable callbacks for use with `call:` in blueprints or lifecycle hooks
41
+
42
+ ## Basic callback registration
43
+ # config.register_callback(:seed_database) do |context|
44
+ # User.create!(email: "test@test.com")
45
+ # end
46
+
47
+ ## With arguments
48
+ # config.register_callback(:create_records) do |context, count:, type:|
49
+ # count.times { type.constantize.create! }
50
+ # end
51
+
52
+ ## Complete example: Database cleanup
53
+ # config.register_callback(:cleanup_db) do |context|
54
+ # DatabaseCleaner.clean
55
+ # end
56
+ #
57
+ # config.after(:forge, :cleanup_db) # Runs once after all tests
58
+
59
+ ## Hooks can also be defined directly (useful for simple one-off hooks)
60
+ # config.after(:step) do |context|
61
+ # next unless context.step.request?
62
+ #
63
+ # request = context.variables[:request]
64
+ # puts "→ #{request[:http_verb]} #{request[:url]}"
65
+ # end
66
+
67
+ ## Test Framework Configuration
68
+ # config.rspec.formatter = :documentation
48
69
  end
@@ -0,0 +1,54 @@
1
+ # <%= original_name %>.yml
2
+
3
+ # Store some test data
4
+ - store:
5
+ test_email: "test@example.com"
6
+ test_name: "Test <%= singular_name.titleize %>"
7
+
8
+ # Create a <%= singular_name %>
9
+ - name: "Create <%= singular_name %>"
10
+ request:
11
+ path: /<%= plural_name %>
12
+ method: POST
13
+ json:
14
+ name: "{{ test_name }}"
15
+ email: "{{ test_email }}"
16
+ expect:
17
+ - status: 201
18
+ json:
19
+ shape:
20
+ id: integer
21
+ name: string
22
+ email: string
23
+ store:
24
+ <%= singular_name %>_id: "{{ response.body.id }}"
25
+
26
+ # Get the <%= singular_name %> we just created
27
+ - name: "Show <%= singular_name %>"
28
+ request:
29
+ path: "/<%= plural_name %>/{{ <%= singular_name %>_id }}"
30
+ method: GET
31
+ expect:
32
+ - status: 200
33
+ json:
34
+ content:
35
+ id: "{{ <%= singular_name %>_id }}"
36
+ name: "{{ test_name }}"
37
+
38
+ # Update the <%= singular_name %>
39
+ - name: "Update <%= singular_name %>"
40
+ request:
41
+ path: "/<%= plural_name %>/{{ <%= singular_name %>_id }}"
42
+ method: PATCH
43
+ json:
44
+ name: "Updated <%= singular_name.titleize %>"
45
+ expect:
46
+ - status: 200
47
+
48
+ # Delete the <%= singular_name %>
49
+ - name: "Delete <%= singular_name %>"
50
+ request:
51
+ path: "/<%= plural_name %>/{{ <%= singular_name %>_id }}"
52
+ method: DELETE
53
+ expect:
54
+ - status: 200
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spec_forge
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bryan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-10-09 00:00:00.000000000 Z
11
+ date: 2026-01-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '7.1'
19
+ version: '6.1'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '7.1'
26
+ version: '6.1'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: commander
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0.8'
47
+ version: '0.9'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0.8'
54
+ version: '0.9'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: factory_bot
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -122,6 +122,20 @@ dependencies:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
124
  version: 0.10.1
125
+ - !ruby/object:Gem::Dependency
126
+ name: pastel
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '0.8'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '0.8'
125
139
  - !ruby/object:Gem::Dependency
126
140
  name: rspec
127
141
  requirement: !ruby/object:Gem::Requirement
@@ -178,6 +192,20 @@ dependencies:
178
192
  - - "~>"
179
193
  - !ruby/object:Gem::Version
180
194
  version: '1.9'
195
+ - !ruby/object:Gem::Dependency
196
+ name: zeitwerk
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - "~>"
200
+ - !ruby/object:Gem::Version
201
+ version: '2.7'
202
+ type: :runtime
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - "~>"
207
+ - !ruby/object:Gem::Version
208
+ version: '2.7'
181
209
  description: Write API tests in YAML without sacrificing power. SpecForge combines
182
210
  RSpec's matcher system, Faker's data generation, and factory patterns into a clean,
183
211
  declarative syntax that eliminates boilerplate while preserving control over your
@@ -203,9 +231,10 @@ files:
203
231
  - lib/spec_forge.rb
204
232
  - lib/spec_forge/attribute.rb
205
233
  - lib/spec_forge/attribute/chainable.rb
234
+ - lib/spec_forge/attribute/environment.rb
206
235
  - lib/spec_forge/attribute/factory.rb
207
236
  - lib/spec_forge/attribute/faker.rb
208
- - lib/spec_forge/attribute/global.rb
237
+ - lib/spec_forge/attribute/generate.rb
209
238
  - lib/spec_forge/attribute/literal.rb
210
239
  - lib/spec_forge/attribute/matcher.rb
211
240
  - lib/spec_forge/attribute/parameterized.rb
@@ -213,11 +242,11 @@ files:
213
242
  - lib/spec_forge/attribute/resolvable.rb
214
243
  - lib/spec_forge/attribute/resolvable_array.rb
215
244
  - lib/spec_forge/attribute/resolvable_hash.rb
216
- - lib/spec_forge/attribute/store.rb
245
+ - lib/spec_forge/attribute/resolvable_struct.rb
246
+ - lib/spec_forge/attribute/template.rb
217
247
  - lib/spec_forge/attribute/transform.rb
218
248
  - lib/spec_forge/attribute/variable.rb
219
- - lib/spec_forge/backtrace_formatter.rb
220
- - lib/spec_forge/callbacks.rb
249
+ - lib/spec_forge/blueprint.rb
221
250
  - lib/spec_forge/cli.rb
222
251
  - lib/spec_forge/cli/actions.rb
223
252
  - lib/spec_forge/cli/command.rb
@@ -228,32 +257,22 @@ files:
228
257
  - lib/spec_forge/cli/run.rb
229
258
  - lib/spec_forge/cli/serve.rb
230
259
  - lib/spec_forge/configuration.rb
231
- - lib/spec_forge/context.rb
232
- - lib/spec_forge/context/callbacks.rb
233
- - lib/spec_forge/context/global.rb
234
- - lib/spec_forge/context/store.rb
235
- - lib/spec_forge/context/variables.rb
236
- - lib/spec_forge/core_ext.rb
237
260
  - lib/spec_forge/core_ext/array.rb
238
- - lib/spec_forge/core_ext/rspec.rb
239
261
  - lib/spec_forge/documentation.rb
240
262
  - lib/spec_forge/documentation/builder.rb
263
+ - lib/spec_forge/documentation/builder/cache.rb
264
+ - lib/spec_forge/documentation/builder/compiler.rb
265
+ - lib/spec_forge/documentation/builder/extractor.rb
241
266
  - lib/spec_forge/documentation/document.rb
242
267
  - lib/spec_forge/documentation/document/operation.rb
243
268
  - lib/spec_forge/documentation/document/parameter.rb
244
269
  - lib/spec_forge/documentation/document/request_body.rb
245
270
  - lib/spec_forge/documentation/document/response.rb
246
271
  - lib/spec_forge/documentation/document/response_body.rb
247
- - lib/spec_forge/documentation/generators.rb
248
- - lib/spec_forge/documentation/generators/base.rb
249
- - lib/spec_forge/documentation/generators/openapi.rb
250
- - lib/spec_forge/documentation/generators/openapi/base.rb
251
- - lib/spec_forge/documentation/generators/openapi/error_formatter.rb
252
- - lib/spec_forge/documentation/generators/openapi/v3_0.rb
253
- - lib/spec_forge/documentation/loader.rb
254
- - lib/spec_forge/documentation/loader/cache.rb
272
+ - lib/spec_forge/documentation/generator.rb
255
273
  - lib/spec_forge/documentation/openapi.rb
256
- - lib/spec_forge/documentation/openapi/base.rb
274
+ - lib/spec_forge/documentation/openapi/v3_0.rb
275
+ - lib/spec_forge/documentation/openapi/v3_0/error_formatter.rb
257
276
  - lib/spec_forge/documentation/openapi/v3_0/example.rb
258
277
  - lib/spec_forge/documentation/openapi/v3_0/media_type.rb
259
278
  - lib/spec_forge/documentation/openapi/v3_0/operation.rb
@@ -262,42 +281,54 @@ files:
262
281
  - lib/spec_forge/documentation/openapi/v3_0/tag.rb
263
282
  - lib/spec_forge/error.rb
264
283
  - lib/spec_forge/factory.rb
265
- - lib/spec_forge/filter.rb
266
284
  - lib/spec_forge/forge.rb
285
+ - lib/spec_forge/forge/action.rb
286
+ - lib/spec_forge/forge/actions/call.rb
287
+ - lib/spec_forge/forge/actions/debug.rb
288
+ - lib/spec_forge/forge/actions/expect.rb
289
+ - lib/spec_forge/forge/actions/request.rb
290
+ - lib/spec_forge/forge/actions/store.rb
291
+ - lib/spec_forge/forge/callbacks.rb
292
+ - lib/spec_forge/forge/context.rb
293
+ - lib/spec_forge/forge/display.rb
294
+ - lib/spec_forge/forge/hooks.rb
295
+ - lib/spec_forge/forge/runner.rb
296
+ - lib/spec_forge/forge/runner/array_io.rb
297
+ - lib/spec_forge/forge/runner/content_validator.rb
298
+ - lib/spec_forge/forge/runner/header_validator.rb
299
+ - lib/spec_forge/forge/runner/reporter.rb
300
+ - lib/spec_forge/forge/runner/schema_validator.rb
301
+ - lib/spec_forge/forge/timer.rb
302
+ - lib/spec_forge/forge/variables.rb
267
303
  - lib/spec_forge/http.rb
268
304
  - lib/spec_forge/http/backend.rb
269
305
  - lib/spec_forge/http/client.rb
270
306
  - lib/spec_forge/http/request.rb
271
307
  - lib/spec_forge/http/verb.rb
272
308
  - lib/spec_forge/loader.rb
309
+ - lib/spec_forge/loader/filter.rb
310
+ - lib/spec_forge/loader/step_processor.rb
273
311
  - lib/spec_forge/matchers.rb
274
312
  - lib/spec_forge/normalizer.rb
275
313
  - lib/spec_forge/normalizer/default.rb
276
- - lib/spec_forge/normalizer/definition.rb
314
+ - lib/spec_forge/normalizer/structure.rb
315
+ - lib/spec_forge/normalizer/transformers.rb
277
316
  - lib/spec_forge/normalizer/validators.rb
278
- - lib/spec_forge/normalizers/_shared.yml
317
+ - lib/spec_forge/normalizers/callback.yml
279
318
  - lib/spec_forge/normalizers/configuration.yml
280
- - lib/spec_forge/normalizers/constraint.yml
281
- - lib/spec_forge/normalizers/expectation.yml
282
319
  - lib/spec_forge/normalizers/factory.yml
283
320
  - lib/spec_forge/normalizers/factory_reference.yml
284
- - lib/spec_forge/normalizers/global_context.yml
285
- - lib/spec_forge/normalizers/spec.yml
286
- - lib/spec_forge/runner.rb
287
- - lib/spec_forge/runner/adapter.rb
288
- - lib/spec_forge/runner/callbacks.rb
289
- - lib/spec_forge/runner/debug_proxy.rb
290
- - lib/spec_forge/runner/listener.rb
291
- - lib/spec_forge/runner/metadata.rb
292
- - lib/spec_forge/runner/state.rb
293
- - lib/spec_forge/spec.rb
294
- - lib/spec_forge/spec/expectation.rb
295
- - lib/spec_forge/spec/expectation/constraint.rb
321
+ - lib/spec_forge/normalizers/json_schema.yml
322
+ - lib/spec_forge/normalizers/step.yml
323
+ - lib/spec_forge/step.rb
324
+ - lib/spec_forge/step/call.rb
325
+ - lib/spec_forge/step/expect.rb
326
+ - lib/spec_forge/step/source.rb
296
327
  - lib/spec_forge/type.rb
297
328
  - lib/spec_forge/version.rb
298
329
  - lib/templates/forge_helper.rb.tt
330
+ - lib/templates/new_blueprint.yml.tt
299
331
  - lib/templates/new_factory.yml.tt
300
- - lib/templates/new_spec.yml.tt
301
332
  - lib/templates/openapi.yml.tt
302
333
  - lib/templates/redoc.html.tt
303
334
  - lib/templates/swagger.html.tt
@@ -1,96 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SpecForge
4
- class Attribute
5
- #
6
- # Represents an attribute that references values from the global context
7
- #
8
- # This class allows accessing shared data defined at the global level through
9
- # namespaced references. It provides access to global variables that are shared
10
- # across all specs in a file, enabling consistent test data without repetition.
11
- #
12
- # Currently supports the "variables" namespace.
13
- #
14
- # @example Basic usage in YAML
15
- # # Reference a global variable in a spec
16
- # session_token: global.variables.session_token
17
- #
18
- # # Using within a request body
19
- # body:
20
- # api_version: global.variables.api_version
21
- # auth_token: global.variables.auth_token
22
- #
23
- class Global < Attribute
24
- #
25
- # Regular expression pattern that matches attribute keywords with this prefix
26
- # Used for identifying this attribute type during parsing
27
- #
28
- # @return [Regexp]
29
- #
30
- KEYWORD_REGEX = /^global\./i
31
-
32
- #
33
- # An array of valid namespaces that can be access on global
34
- #
35
- # @return [Array<String>]
36
- #
37
- VALID_NAMESPACES = %w[
38
- variables
39
- ].freeze
40
-
41
- #
42
- # Creates a new global attribute from the input string
43
- #
44
- # Parses the input string to extract the namespace to validate it
45
- # Conversion happens when `#value` is called
46
- #
47
- # @raise [Error::InvalidGlobalNamespaceError] If an unsupported namespace is referenced
48
- #
49
- def initialize(...)
50
- super
51
-
52
- # Check to make sure the namespace is valid
53
- namespace = input.split(".").second
54
-
55
- if !VALID_NAMESPACES.include?(namespace)
56
- raise Error::InvalidGlobalNamespaceError, namespace
57
- end
58
- end
59
-
60
- #
61
- # Converts the global reference into an underlying attribute
62
- #
63
- # Parses the input and returns the corresponding attribute based on the namespace.
64
- # Currently supports extracting variables from the global context.
65
- #
66
- # @return [Attribute] An attribute representing the referenced global value
67
- #
68
- def value
69
- # Skip the "global" prefix
70
- components = input.split(".")[1..]
71
- namespace = components.first
72
-
73
- global_context = SpecForge.context.global
74
-
75
- case namespace
76
- when "variables"
77
- variable_input = components.join(".")
78
- variable = Attribute::Variable.new(variable_input)
79
- variable.bind_variables(global_context.variables.to_h)
80
- variable
81
- end
82
- end
83
-
84
- #
85
- # Resolves the global reference to its actual value
86
- #
87
- # Delegates resolution to the underlying attribute and caches the result
88
- #
89
- # @return [Object] The fully resolved value from the global context
90
- #
91
- def resolved
92
- @resolved ||= value.resolved
93
- end
94
- end
95
- end
96
- end
@@ -1,65 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SpecForge
4
- class Attribute
5
- #
6
- # Represents an attribute that references values from stored test results
7
- #
8
- # This class allows accessing data from previous test executions that were
9
- # saved using the `store_as` directive. It provides access to response data
10
- # including status, headers, and body from previously run expectations.
11
- #
12
- # @example Basic usage in YAML
13
- # create_user:
14
- # path: /users
15
- # method: post
16
- # expectations:
17
- # - store_as: new_user
18
- # body:
19
- # name: faker.name.name
20
- # expect:
21
- # status: 201
22
- #
23
- # get_user:
24
- # path: /users/{id}
25
- # expectations:
26
- # - query:
27
- # id: store.new_user.body.id
28
- # expect:
29
- # status: 200
30
- #
31
- # @example Accessing specific response components
32
- # check_status:
33
- # path: /health
34
- # expectations:
35
- # - variables:
36
- # expected_status: store.new_user.status
37
- # auth_token: store.new_user.headers.authorization
38
- # user_name: store.new_user.body.user.name
39
- # expect:
40
- # status: 200
41
- #
42
- class Store < Attribute
43
- include Chainable
44
-
45
- #
46
- # Regular expression pattern that matches attribute keywords with this prefix
47
- # Used for identifying this attribute type during parsing
48
- #
49
- # @return [Regexp]
50
- #
51
- KEYWORD_REGEX = /^store\./i
52
-
53
- alias_method :stored_id, :header
54
-
55
- #
56
- # Returns the base object for the variable chain
57
- #
58
- # @return [Context::Store::Entry, nil] The stored entry or nil if not found
59
- #
60
- def base_object
61
- @base_object ||= SpecForge.context.store[stored_id.to_s]
62
- end
63
- end
64
- end
65
- end
@@ -1,50 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SpecForge
4
- #
5
- # Used internally by RSpec to format backtraces for test failures
6
- # Customizes error output to make it more readable and useful for SpecForge
7
- #
8
- module BacktraceFormatter
9
- #
10
- # Returns the RSpec backtrace formatter instance
11
- # Lazily initializes the formatter on first access
12
- #
13
- # @return [RSpec::Core::BacktraceFormatter] The backtrace formatter
14
- #
15
- def self.formatter
16
- @formatter ||= RSpec::Core::BacktraceFormatter.new
17
- end
18
-
19
- #
20
- # Formats a single backtrace line
21
- # Delegates to the RSpec formatter
22
- #
23
- # @param line [String] The backtrace line to format
24
- #
25
- # @return [String] The formatted backtrace line
26
- #
27
- def self.backtrace_line(line)
28
- formatter.backtrace_line(line)
29
- end
30
-
31
- #
32
- # Formats a complete backtrace for an example
33
- # Adds the YAML location to the front of the backtrace for better context
34
- #
35
- # @param backtrace [Array<String>] The raw backtrace lines
36
- # @param example_metadata [Hash] Metadata about the failing example
37
- #
38
- # @return [Array<String>] The formatted backtrace with YAML location first
39
- #
40
- def self.format_backtrace(backtrace, example_metadata)
41
- backtrace = SpecForge.backtrace_cleaner.clean(backtrace)
42
-
43
- location = example_metadata[:example_group][:location]
44
- line_number = example_metadata[:example_group][:line_number]
45
-
46
- # Add the yaml location to the front so it's the first thing people see
47
- ["#{location}:#{line_number}"] + backtrace[0..50]
48
- end
49
- end
50
- end
@@ -1,88 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SpecForge
4
- #
5
- # Manages user-defined callbacks for test lifecycle events
6
- #
7
- # This singleton class stores and executes callback functions that
8
- # users can register to run at specific points in the test lifecycle.
9
- # Each callback receives a context object containing relevant state
10
- # information for that point in execution.
11
- #
12
- # @example Registering and using a callback
13
- # SpecForge::Callbacks.register(:my_callback) do |context|
14
- # puts "Running test: #{context.expectation_name}"
15
- # end
16
- #
17
- class Callbacks < Hash
18
- include Singleton
19
-
20
- class << self
21
- #
22
- # Registers a new callback for a specific event
23
- #
24
- # @param name [String, Symbol] The name of the callback event
25
- # @param block [Proc] The callback function to execute
26
- #
27
- # @raise [ArgumentError] If no block is provided
28
- #
29
- def register(name, &block)
30
- raise ArgumentError, "A block must be provided" unless block.is_a?(Proc)
31
-
32
- if registered?(name)
33
- warn("Callback #{name.in_quotes} is already registered. It will be overwritten")
34
- end
35
-
36
- instance[name.to_s] = block
37
- end
38
-
39
- #
40
- # Deregisters a callback
41
- #
42
- # @param name [String, Symbol] The name of the callback
43
- #
44
- def deregister(name)
45
- instance.delete(name.to_s)
46
- end
47
-
48
- #
49
- # Checks if a callback is registered for the given event
50
- #
51
- # @param name [String, Symbol] The name of the callback event
52
- #
53
- # @return [Boolean] True if the callback exists
54
- #
55
- def registered?(name)
56
- instance.key?(name.to_s)
57
- end
58
-
59
- #
60
- # Returns all registered callback names
61
- #
62
- # @return [Array<String>] List of registered callback names
63
- #
64
- def registered_names
65
- instance.keys
66
- end
67
-
68
- #
69
- # Executes a named callback with the provided context
70
- #
71
- # @param name [String, Symbol] The name of the callback to run
72
- # @param context [Object] Context object containing state data
73
- #
74
- # @raise [ArgumentError] If the callback is not registered
75
- #
76
- def run(name, context)
77
- callback = instance[name.to_s]
78
- raise ArgumentError, "Callback #{name.in_quotes} is not defined" if callback.nil?
79
-
80
- if callback.arity == 0
81
- callback.call
82
- else
83
- callback.call(context)
84
- end
85
- end
86
- end
87
- end
88
- end