spec_forge 0.7.0 → 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 +139 -9
  3. data/README.md +125 -203
  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 +6 -0
  29. data/lib/spec_forge/cli.rb +6 -14
  30. data/lib/spec_forge/configuration.rb +212 -78
  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 +22 -6
  42. data/lib/spec_forge/documentation/openapi/v3_0/response.rb +29 -7
  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 -143
  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 -74
  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 -183
  124. data/lib/spec_forge/runner/callbacks.rb +0 -246
  125. data/lib/spec_forge/runner/debug_proxy.rb +0 -213
  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
@@ -0,0 +1,79 @@
1
+ # =============================================================================
2
+ # JSON Schema Structure Definition
3
+ # =============================================================================
4
+ # Defines the structure for advanced JSON validation using explicit schemas.
5
+ # Used when the simpler "shape" syntax isn't sufficient, particularly for:
6
+ # - Union types (field can be string OR integer)
7
+ # - Explicit pattern vs structure control on arrays
8
+ # - Verbose, self-documenting style preference
9
+ #
10
+ # Key concepts:
11
+ # - structure = positional (element N matches item N)
12
+ # - pattern = repeating (all elements match same schema)
13
+ #
14
+ # Example usage in blueprints:
15
+ # expect:
16
+ # - json:
17
+ # schema:
18
+ # type: array
19
+ # structure:
20
+ # - integer # First element must be integer
21
+ # - string # Second element must be string
22
+ # - hash # Third element must be hash
23
+ # =============================================================================
24
+
25
+ type:
26
+ type:
27
+ - array
28
+ - string
29
+ aliases: ["types"]
30
+ description: |-
31
+ The expected type of the JSON value. Can be a single type or an
32
+ array of allowed types for union types. Prefix with ? for nullable,
33
+ * for optional, or combine (*?).
34
+ examples:
35
+ - "array"
36
+ - "hash"
37
+ - "?string"
38
+ - "*?string"
39
+ - '["string", "integer"]'
40
+
41
+ structure:
42
+ type:
43
+ - hash
44
+ - array
45
+ default: null
46
+ description: |-
47
+ Defines the internal structure of the value. Behavior depends on type:
48
+ - For arrays: Positional/tuple (element N matches item N)
49
+ - For hashes: A hash mapping keys to their type definitions
50
+ Each element can be a type string or a nested schema definition.
51
+ examples:
52
+ - |-
53
+ structure:
54
+ - integer
55
+ - string
56
+ - hash
57
+ - |-
58
+ structure:
59
+ id: integer
60
+ name: string
61
+ bio:
62
+ type: string
63
+ optional: true
64
+ nullable: true
65
+
66
+ pattern:
67
+ type: hash
68
+ default: null
69
+ description: |-
70
+ Defines a repeating pattern for array elements. Use when all array
71
+ elements should match the same structure (alternative to positional
72
+ structure). Contains a nested schema definition.
73
+ examples:
74
+ - |-
75
+ pattern:
76
+ type: hash
77
+ structure:
78
+ id: integer
79
+ name: string
@@ -0,0 +1,506 @@
1
+ # =============================================================================
2
+ # Step Structure Definition
3
+ # =============================================================================
4
+ # Defines the structure for individual steps in a SpecForge blueprint.
5
+ # Steps are the fundamental building blocks of API test workflows.
6
+ #
7
+ # A step can perform various actions:
8
+ # - Send HTTP requests and validate responses
9
+ # - Store values for later use
10
+ # - Include other blueprint files
11
+ # - Execute callbacks
12
+ #
13
+ # Steps execute sequentially, with later steps able to reference values
14
+ # from earlier steps via the {{ variable }} syntax.
15
+ # =============================================================================
16
+
17
+ # -----------------------------------------------------------------------------
18
+ # Core Attributes
19
+ # -----------------------------------------------------------------------------
20
+
21
+ name:
22
+ type: string
23
+ default: ""
24
+ description: |-
25
+ Human-readable identifier for the step, displayed in test output.
26
+ Helps identify which step failed and documents the test's purpose.
27
+ examples:
28
+ - "Create new user"
29
+ - "Login as admin"
30
+ - "Verify user was deleted"
31
+
32
+ debug:
33
+ type: boolean
34
+ aliases:
35
+ - pry
36
+ - breakpoint
37
+ default: false
38
+ description: |-
39
+ When true, triggers a breakpoint before executing this step.
40
+ Useful for interactive debugging. Configure the debug handler
41
+ in forge_helper.rb with config.on_debug { binding.pry }.
42
+ Note: Does NOT inherit to child steps.
43
+ examples:
44
+ - true
45
+ - false
46
+
47
+ tags:
48
+ type: array
49
+ default: []
50
+ structure:
51
+ type: string
52
+ description: |-
53
+ Labels for organizing and filtering steps. Tags cascade down through
54
+ nesting and are applied to included files. Use CLI flags like
55
+ --tags smoke or --skip-tags slow to filter execution.
56
+ examples:
57
+ - '["smoke", "auth"]'
58
+ - '["crud", "users", "fast"]'
59
+ - '["integration", "slow"]'
60
+
61
+ documentation:
62
+ type: boolean
63
+ default: true
64
+ description: |-
65
+ Controls whether this step is included in OpenAPI documentation generation.
66
+ Set to false to exclude a step from the generated API docs while still
67
+ executing the step during test runs.
68
+ examples:
69
+ - true
70
+ - false
71
+
72
+ # -----------------------------------------------------------------------------
73
+ # Request Configuration
74
+ # -----------------------------------------------------------------------------
75
+
76
+ request: &request_structure
77
+ type: hash
78
+ default: {}
79
+ description: |-
80
+ Defines the HTTP request to send. When present, the step becomes a
81
+ request step that sends an HTTP request and optionally validates
82
+ the response. Child steps inherit and can override parent request config.
83
+ structure:
84
+ base_url:
85
+ type: string
86
+ default: null
87
+ aliases:
88
+ - base_path
89
+ description: |-
90
+ The base URL for the request (e.g., "https://api.example.com").
91
+ Overrides the global base_url from configuration.
92
+ examples:
93
+ - "https://api.example.com"
94
+ - "http://localhost:3000"
95
+
96
+ url:
97
+ type: string
98
+ default: null
99
+ aliases:
100
+ - path
101
+ description: |-
102
+ The URL path for the request. Combined with base_url to form
103
+ the full request URL. Supports variable interpolation.
104
+ examples:
105
+ - "/users"
106
+ - "/posts/{{ post_id }}"
107
+ - "/api/v1/users/{{ user_id }}/comments"
108
+
109
+ http_verb:
110
+ type: string
111
+ default: null
112
+ aliases:
113
+ - method
114
+ - http_method
115
+ validator: http_verb
116
+ description: |-
117
+ The HTTP method for the request. If not specified, defaults to GET.
118
+ examples:
119
+ - "GET"
120
+ - "POST"
121
+ - "PUT"
122
+ - "PATCH"
123
+ - "DELETE"
124
+
125
+ headers:
126
+ type: hash
127
+ default: null
128
+ description: |-
129
+ HTTP headers to include with the request. Child steps merge with
130
+ parent headers (child wins on conflicts). Supports variable interpolation.
131
+ examples:
132
+ - |-
133
+ headers:
134
+ Authorization: "Bearer {{ auth_token }}"
135
+ Content-Type: "application/json"
136
+ - |-
137
+ headers:
138
+ X-API-Key: "{{ env.API_KEY }}"
139
+
140
+ query:
141
+ type:
142
+ - hash
143
+ - string
144
+ default: null
145
+ aliases:
146
+ - params
147
+ description: |-
148
+ Query parameters appended to the URL. Can be a hash or a raw
149
+ query string. Supports variable interpolation.
150
+ examples:
151
+ - |-
152
+ query:
153
+ page: 1
154
+ per_page: 25
155
+ - |-
156
+ params:
157
+ filter: active
158
+ sort: name
159
+
160
+ raw:
161
+ type: string
162
+ default: null
163
+ aliases:
164
+ - body
165
+ - data
166
+ description: |-
167
+ Raw request body as a string. Use for non-JSON payloads or when
168
+ you need exact control over the request body format.
169
+ examples:
170
+ - "<xml><user><name>Test</name></user></xml>"
171
+ - "plain text body"
172
+
173
+ json:
174
+ type: hash
175
+ default: null
176
+ aliases:
177
+ - hash
178
+ - array
179
+ description: |-
180
+ JSON request body. Automatically serialized and sets Content-Type
181
+ to application/json. Supports variable interpolation and data generation.
182
+ examples:
183
+ - |-
184
+ json:
185
+ name: "{{ faker.name.first_name }}"
186
+ email: "{{ faker.internet.email }}"
187
+ - |-
188
+ json:
189
+ ids: ["id1", "id2", "id3"]
190
+
191
+ # -----------------------------------------------------------------------------
192
+ # Expectations (Response Validation)
193
+ # -----------------------------------------------------------------------------
194
+
195
+ expect:
196
+ type: array
197
+ default: []
198
+ aliases: [expects]
199
+ description: |-
200
+ Array of expectations to validate against the response. Each expectation
201
+ is a separate test that runs independently. Validates status codes,
202
+ headers, and response body content/structure.
203
+ structure:
204
+ type: hash
205
+ default: {}
206
+ structure:
207
+ status:
208
+ type:
209
+ - string
210
+ - integer
211
+ description: |-
212
+ Expected HTTP status code. Can be an exact value or a matcher.
213
+ examples:
214
+ - 200
215
+ - 201
216
+ - "{{ kind_of.integer }}"
217
+
218
+ headers:
219
+ type: hash
220
+ default: {}
221
+ description: |-
222
+ Expected response headers. Keys are header names, values are
223
+ expected values or matchers. Case insensitive.
224
+ examples:
225
+ - |-
226
+ headers:
227
+ Content-Type: "application/json"
228
+ Location: "/users/{{ user_id }}"
229
+
230
+ raw:
231
+ type: string
232
+ default: null
233
+ aliases:
234
+ - body
235
+ - data
236
+ description: |-
237
+ Expected raw response body as a string. Use for non-JSON
238
+ responses or exact string matching.
239
+
240
+ json:
241
+ type: hash
242
+ default: {}
243
+ aliases:
244
+ - array
245
+ - hash
246
+ validator: json_expectation
247
+ description: |-
248
+ JSON response validation. Supports structure validation (shape/schema),
249
+ size validation, and content matching. Use shape for simple cases,
250
+ schema for complex or positional array validation.
251
+ structure:
252
+ size:
253
+ type:
254
+ - string
255
+ - integer
256
+ description: |-
257
+ Validates the size of an array response. Can be an exact
258
+ number or a matcher for range validation.
259
+ examples:
260
+ - 5
261
+ - "be.greater_than: 3"
262
+
263
+ shape:
264
+ type:
265
+ - array
266
+ - hash
267
+ transformer: normalize_shape
268
+ description: |-
269
+ Type validation for JSON responses. Use for most API testing.
270
+
271
+ Type flags:
272
+ ? = nullable (value can be null)
273
+ * = optional (key can be missing)
274
+ *? or ?* = both (missing or null allowed)
275
+
276
+ Array behavior:
277
+ Single item = pattern (all elements must match)
278
+ Multiple items = positional (element N matches item N)
279
+
280
+ Available types: string, integer, float, number (numeric), boolean (bool), hash (object), array, nil (null)
281
+ examples:
282
+ - |-
283
+ shape:
284
+ id: integer
285
+ name: string
286
+ deleted_at: ?string
287
+ nickname: *string
288
+ bio: *?string
289
+ - |-
290
+ shape:
291
+ - id: integer
292
+ name: string
293
+ - |-
294
+ shape:
295
+ users:
296
+ - id: integer
297
+ profile:
298
+ name: string
299
+
300
+ schema:
301
+ type:
302
+ - hash
303
+ transformer: normalize_schema
304
+ validator: json_schema
305
+ description: |-
306
+ Explicit structure control for JSON validation. Use when shape
307
+ can't express what you need:
308
+ - Union types (field can be string OR integer)
309
+ - Explicit pattern vs structure control on arrays
310
+ - Verbose, self-documenting style preference
311
+
312
+ Key concepts:
313
+ structure = positional (element N matches item N)
314
+ pattern = repeating (all elements match same schema)
315
+
316
+ Supports optional: and nullable: keys, or type flags (?*).
317
+ examples:
318
+ - |-
319
+ schema:
320
+ type: array
321
+ structure:
322
+ - integer
323
+ - string
324
+ - hash
325
+ - |-
326
+ schema:
327
+ type: hash
328
+ structure:
329
+ id: integer
330
+ value:
331
+ type: [string, integer]
332
+ - |-
333
+ schema:
334
+ type: hash
335
+ structure:
336
+ bio:
337
+ type: string
338
+ optional: true
339
+ nullable: true
340
+
341
+ content:
342
+ type:
343
+ - array
344
+ - hash
345
+ description: |-
346
+ Validates specific values in the response. Unlike shape/schema,
347
+ supports expressions, matchers, and variables. Only checks fields
348
+ you specify, extra fields are ignored.
349
+ examples:
350
+ - |-
351
+ content:
352
+ status: "active"
353
+ id: "{{ created_user_id }}"
354
+ - |-
355
+ content:
356
+ email: /@example\.com$/
357
+ count:
358
+ be.greater_than: 0
359
+
360
+ # -----------------------------------------------------------------------------
361
+ # Variable Storage
362
+ # -----------------------------------------------------------------------------
363
+
364
+ store:
365
+ type: hash
366
+ default: {}
367
+ description: |-
368
+ Saves values as local variables available to subsequent steps.
369
+ - Without request: Sets variables directly
370
+ - With request: Can capture values from response (response and request
371
+ variables are temporarily available during store: evaluation)
372
+ Values are available to all subsequent steps via {{ variable }} syntax.
373
+ Note: request, response, and index are reserved variable names.
374
+ examples:
375
+ - |-
376
+ store:
377
+ base_email: "test@example.com"
378
+ default_role: "user"
379
+ - |-
380
+ store:
381
+ user_id: "{{ response.body.id }}"
382
+ location: "{{ response.headers.location }}"
383
+
384
+ # -----------------------------------------------------------------------------
385
+ # Callbacks
386
+ # -----------------------------------------------------------------------------
387
+
388
+ call:
389
+ type: [string, hash, array]
390
+ default: []
391
+ aliases: [calls]
392
+ transformer: normalize_callback
393
+ validator: callback
394
+ description: |-
395
+ Executes a named callback registered in forge_helper.rb. Use for
396
+ database seeding, cleanup, or other custom operations. Can pass
397
+ arguments as a hash (keyword args) or array (positional args).
398
+ examples:
399
+ - "call: seed_database"
400
+ - |-
401
+ call:
402
+ name: "create_records"
403
+ arguments:
404
+ count: 50
405
+ type: "User"
406
+ - |-
407
+ call:
408
+ name: "increment_counter"
409
+ arguments: [1]
410
+
411
+ hook: &hook_structure
412
+ type: hash
413
+ default: {}
414
+ aliases: [hooks]
415
+ description: |-
416
+ Lifecycle hooks that execute callbacks at specific points during test
417
+ execution. Hooks allow setup and teardown logic to run before/after
418
+ files or individual steps.
419
+
420
+ Execution order: before hooks run outside-in, after hooks run inside-out.
421
+ Hooks are deduplicated by callback name at each scope.
422
+ examples:
423
+ - |-
424
+ hook:
425
+ before_blueprint: seed_database
426
+ after_blueprint: cleanup
427
+ - |-
428
+ hook:
429
+ before_step:
430
+ name: create_user
431
+ arguments:
432
+ role: admin
433
+ after_step: delete_user
434
+ - |-
435
+ hook:
436
+ before_blueprint:
437
+ - seed_users
438
+ - seed_posts
439
+ structure:
440
+ before_forge:
441
+ type: [string, hash, array]
442
+ transformer: normalize_callback
443
+ validator: callback
444
+ description: |-
445
+ Executes once before all blueprints.
446
+ after_forge:
447
+ type: [string, hash, array]
448
+ transformer: normalize_callback
449
+ validator: callback
450
+ description: |-
451
+ Executes once after all blueprints complete.
452
+ before_blueprint:
453
+ type: [string, hash, array]
454
+ transformer: normalize_callback
455
+ validator: callback
456
+ description: |-
457
+ Executes once before all steps in a blueprint.
458
+ after_blueprint:
459
+ type: [string, hash, array]
460
+ transformer: normalize_callback
461
+ validator: callback
462
+ description: |-
463
+ Executes once after all steps in a blueprint complete.
464
+ before_step:
465
+ type: [string, hash, array]
466
+ transformer: normalize_callback
467
+ validator: callback
468
+ description: |-
469
+ Executes before each step in a blueprint.
470
+ after_step:
471
+ type: [string, hash, array]
472
+ transformer: normalize_callback
473
+ validator: callback
474
+ description: |-
475
+ Executes after each step in a blueprint completes.
476
+
477
+ # -----------------------------------------------------------------------------
478
+ # File Inclusion
479
+ # -----------------------------------------------------------------------------
480
+
481
+ include:
482
+ type: [string, array]
483
+ default: []
484
+ aliases: [includes]
485
+ transformer: normalize_includes
486
+ description: |-
487
+ Injects steps from other blueprint files at load-time. Included steps
488
+ become indistinguishable from locally defined ones after loading.
489
+ Cannot be combined with other step configuration (like request, store, hook).
490
+ Use shared: with steps: when you need included steps to inherit configuration.
491
+ examples:
492
+ - "include: auth_setup"
493
+ - |-
494
+ include:
495
+ - auth_setup
496
+ - seed_data
497
+
498
+ # -----------------------------------------------------------------------------
499
+ # Inheritance
500
+ # -----------------------------------------------------------------------------
501
+
502
+ shared:
503
+ type: hash
504
+ structure:
505
+ request: *request_structure
506
+ hook: *hook_structure
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SpecForge
4
+ class Step
5
+ #
6
+ # Represents a callback invocation within a step
7
+ #
8
+ # Holds the callback name and any arguments to pass when the
9
+ # callback is executed during step processing.
10
+ #
11
+ class Call < Data.define(:callback_name, :arguments)
12
+ #
13
+ # Transforms raw hook data into Call objects
14
+ #
15
+ # @param hooks [Hash, nil] Raw hooks hash with callback definitions
16
+ #
17
+ # @return [Hash{Symbol => Array<Call>}] Transformed hooks with Call objects
18
+ #
19
+ def self.wrap_hooks(hooks)
20
+ hooks = hooks&.compact_blank
21
+ return {} if hooks.blank?
22
+
23
+ hooks.transform_values do |call|
24
+ calls =
25
+ if call.is_a?(Set)
26
+ call.to_a
27
+ else
28
+ Array.wrap(call)
29
+ end
30
+
31
+ calls.map { |c| Call.new(callback_name: c[:name], arguments: c[:arguments]) }
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end