spec_forge 0.5.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 +4 -4
- data/.standard.yml +3 -3
- data/CHANGELOG.md +106 -1
- data/README.md +34 -22
- data/flake.lock +3 -3
- data/flake.nix +8 -2
- data/lib/spec_forge/attribute/chainable.rb +208 -20
- data/lib/spec_forge/attribute/factory.rb +91 -14
- data/lib/spec_forge/attribute/faker.rb +62 -13
- data/lib/spec_forge/attribute/global.rb +96 -0
- data/lib/spec_forge/attribute/literal.rb +15 -2
- data/lib/spec_forge/attribute/matcher.rb +186 -11
- data/lib/spec_forge/attribute/parameterized.rb +45 -12
- data/lib/spec_forge/attribute/regex.rb +55 -5
- data/lib/spec_forge/attribute/resolvable.rb +48 -5
- data/lib/spec_forge/attribute/resolvable_array.rb +62 -4
- data/lib/spec_forge/attribute/resolvable_hash.rb +62 -4
- data/lib/spec_forge/attribute/store.rb +65 -0
- data/lib/spec_forge/attribute/transform.rb +33 -5
- data/lib/spec_forge/attribute/variable.rb +37 -6
- data/lib/spec_forge/attribute.rb +166 -66
- data/lib/spec_forge/backtrace_formatter.rb +26 -3
- data/lib/spec_forge/callbacks.rb +79 -0
- data/lib/spec_forge/cli/actions.rb +27 -0
- data/lib/spec_forge/cli/command.rb +78 -24
- data/lib/spec_forge/cli/init.rb +11 -1
- data/lib/spec_forge/cli/new.rb +54 -3
- data/lib/spec_forge/cli/run.rb +20 -0
- data/lib/spec_forge/cli.rb +16 -5
- data/lib/spec_forge/configuration.rb +94 -22
- data/lib/spec_forge/context/callbacks.rb +91 -0
- data/lib/spec_forge/context/global.rb +72 -0
- data/lib/spec_forge/context/store.rb +148 -0
- data/lib/spec_forge/context/variables.rb +91 -0
- data/lib/spec_forge/context.rb +36 -0
- data/lib/spec_forge/core_ext/rspec.rb +22 -4
- data/lib/spec_forge/error.rb +267 -113
- data/lib/spec_forge/factory.rb +33 -14
- data/lib/spec_forge/filter.rb +87 -0
- data/lib/spec_forge/forge.rb +170 -0
- data/lib/spec_forge/http/backend.rb +99 -29
- data/lib/spec_forge/http/client.rb +23 -13
- data/lib/spec_forge/http/request.rb +74 -62
- data/lib/spec_forge/http/verb.rb +79 -0
- data/lib/spec_forge/http.rb +105 -0
- data/lib/spec_forge/loader.rb +254 -0
- data/lib/spec_forge/matchers.rb +130 -0
- data/lib/spec_forge/normalizer/configuration.rb +24 -11
- data/lib/spec_forge/normalizer/constraint.rb +21 -8
- data/lib/spec_forge/normalizer/expectation.rb +31 -12
- data/lib/spec_forge/normalizer/factory.rb +24 -11
- data/lib/spec_forge/normalizer/factory_reference.rb +27 -13
- data/lib/spec_forge/normalizer/global_context.rb +88 -0
- data/lib/spec_forge/normalizer/spec.rb +39 -16
- data/lib/spec_forge/normalizer.rb +255 -41
- data/lib/spec_forge/runner/callbacks.rb +246 -0
- data/lib/spec_forge/runner/debug_proxy.rb +213 -0
- data/lib/spec_forge/runner/listener.rb +54 -0
- data/lib/spec_forge/runner/metadata.rb +58 -0
- data/lib/spec_forge/runner/state.rb +99 -0
- data/lib/spec_forge/runner.rb +132 -123
- data/lib/spec_forge/spec/expectation/constraint.rb +91 -20
- data/lib/spec_forge/spec/expectation.rb +43 -51
- data/lib/spec_forge/spec.rb +83 -96
- data/lib/spec_forge/type.rb +36 -4
- data/lib/spec_forge/version.rb +4 -1
- data/lib/spec_forge.rb +161 -76
- metadata +20 -5
- data/spec_forge/factories/user.yml +0 -4
- data/spec_forge/forge_helper.rb +0 -48
- data/spec_forge/specs/users.yml +0 -65
data/lib/spec_forge/spec.rb
CHANGED
@@ -1,126 +1,113 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "spec/expectation"
|
4
|
-
|
5
3
|
module SpecForge
|
4
|
+
#
|
5
|
+
# Represents a test specification in SpecForge
|
6
|
+
#
|
7
|
+
# A Spec contains one or more Expectations and defines the base configuration
|
8
|
+
# for those expectations. It maps directly to a test defined in YAML.
|
9
|
+
#
|
10
|
+
# @example YAML representation
|
11
|
+
# get_users:
|
12
|
+
# path: /users
|
13
|
+
# expectations:
|
14
|
+
# - expect:
|
15
|
+
# status: 200
|
16
|
+
#
|
6
17
|
class Spec
|
7
18
|
#
|
8
|
-
#
|
19
|
+
# @return [Boolean] True if debugging is enabled
|
9
20
|
#
|
10
|
-
|
11
|
-
|
12
|
-
# @param expectation_name [String, nil] The name of the expectation for a spec.
|
21
|
+
attr_predicate :debug
|
22
|
+
|
13
23
|
#
|
14
|
-
#
|
24
|
+
# Unique identifier for this spec
|
15
25
|
#
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
filter_specs(specs, file_name:, spec_name:, expectation_name:)
|
20
|
-
|
21
|
-
# Announce if we're using a filter
|
22
|
-
if file_name
|
23
|
-
filter = {file_name:, spec_name:, expectation_name:}.delete_if { |k, v| v.blank? }
|
24
|
-
filter.stringify_keys!
|
25
|
-
puts "Using filter: #{filter}"
|
26
|
-
end
|
27
|
-
|
28
|
-
specs.each(&:define)
|
29
|
-
end
|
26
|
+
# @return [String] The spec ID
|
27
|
+
#
|
28
|
+
attr_reader :id
|
30
29
|
|
31
30
|
#
|
32
|
-
#
|
31
|
+
# Human-readable name for this spec
|
33
32
|
#
|
34
|
-
# @return [
|
33
|
+
# @return [String] The spec name
|
35
34
|
#
|
36
|
-
|
37
|
-
path = SpecForge.forge.join("specs")
|
38
|
-
specs = []
|
39
|
-
|
40
|
-
Dir[path.join("**/*.yml")].each do |file_path|
|
41
|
-
content = File.read(file_path)
|
42
|
-
hash = YAML.load(content).deep_symbolize_keys
|
43
|
-
|
44
|
-
hash.each do |spec_name, spec_hash|
|
45
|
-
line_number = content.lines.index { |line| line.start_with?("#{spec_name}:") }
|
46
|
-
|
47
|
-
spec_hash[:name] = spec_name.to_s
|
48
|
-
spec_hash[:file_path] = file_path
|
49
|
-
spec_hash[:file_name] = file_path.delete_prefix("#{path}/").delete_suffix(".yml")
|
50
|
-
spec_hash[:line_number] = line_number ? line_number + 1 : -1
|
35
|
+
attr_reader :name
|
51
36
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
# @private
|
60
|
-
def self.filter_specs(specs, file_name: nil, spec_name: nil, expectation_name: nil)
|
61
|
-
# Guard against invalid partial filters
|
62
|
-
if expectation_name && spec_name.blank?
|
63
|
-
raise ArgumentError, "The spec's name is required when filtering by an expectation's name"
|
64
|
-
end
|
65
|
-
|
66
|
-
if spec_name && file_name.blank?
|
67
|
-
raise ArgumentError, "The spec's filename is required when filtering by a spec's name"
|
68
|
-
end
|
69
|
-
|
70
|
-
specs.select! { |spec| spec.file_name == file_name } if file_name
|
71
|
-
specs.select! { |spec| spec.name == spec_name } if spec_name
|
72
|
-
|
73
|
-
if expectation_name
|
74
|
-
specs.each do |spec|
|
75
|
-
spec.expectations.select! { |expectation| expectation.name == expectation_name }
|
76
|
-
end
|
77
|
-
end
|
37
|
+
#
|
38
|
+
# Absolute path to the file containing this spec
|
39
|
+
#
|
40
|
+
# @return [String] The file path
|
41
|
+
#
|
42
|
+
attr_reader :file_path
|
78
43
|
|
79
|
-
|
80
|
-
|
44
|
+
#
|
45
|
+
# Base name of the file without path or extension
|
46
|
+
#
|
47
|
+
# @return [String] The file name
|
48
|
+
#
|
49
|
+
attr_reader :file_name
|
81
50
|
|
82
|
-
|
51
|
+
#
|
52
|
+
# Whether to enable debugging for this spec
|
53
|
+
#
|
54
|
+
# @return [Boolean] Debug flag
|
55
|
+
#
|
56
|
+
attr_reader :debug
|
83
57
|
|
84
|
-
|
58
|
+
#
|
59
|
+
# Line number in the source file where this spec is defined
|
60
|
+
#
|
61
|
+
# @return [Integer] The line number
|
62
|
+
#
|
63
|
+
attr_reader :line_number
|
85
64
|
|
86
|
-
|
65
|
+
#
|
66
|
+
# The expectations to test for this spec
|
67
|
+
#
|
68
|
+
# @return [Array<Expectation>] The expectations
|
69
|
+
#
|
70
|
+
attr_accessor :expectations
|
87
71
|
|
88
72
|
#
|
89
|
-
# Creates a
|
73
|
+
# Creates a new spec instance
|
74
|
+
#
|
75
|
+
# @param id [String] Unique identifier
|
76
|
+
# @param name [String] Human-readable name
|
77
|
+
# @param file_path [String] Absolute path to source file
|
78
|
+
# @param file_name [String] Base name of file
|
79
|
+
# @param debug [Boolean] Whether to enable debugging
|
80
|
+
# @param line_number [Integer] Line number in source
|
81
|
+
# @param expectations [Array<Hash>] Expectation configurations
|
90
82
|
#
|
91
|
-
# @
|
92
|
-
# @param file_path [String] The path where this spec is defined
|
93
|
-
# @param **input [Hash] Any attributes related to the spec, including expectations
|
94
|
-
# See Normalizer::Spec
|
83
|
+
# @return [Spec] A new spec instance
|
95
84
|
#
|
96
|
-
def initialize(name:, file_path:, file_name:, line_number:,
|
85
|
+
def initialize(id:, name:, file_path:, file_name:, debug:, line_number:, expectations:)
|
86
|
+
@id = id
|
97
87
|
@name = name
|
98
88
|
@file_path = file_path
|
99
89
|
@file_name = file_name
|
90
|
+
@debug = debug
|
100
91
|
@line_number = line_number
|
101
|
-
|
102
|
-
input = Normalizer.normalize_spec!(input)
|
103
|
-
|
104
|
-
# Don't pass this down to the expectations
|
105
|
-
@debug = input.delete(:debug) || false
|
106
|
-
|
107
|
-
global_options = normalize_global_options(input)
|
108
|
-
|
109
|
-
@expectations =
|
110
|
-
input[:expectations].map.with_index do |expectation_input, index|
|
111
|
-
Expectation.new(expectation_input, global_options:)
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
def define
|
116
|
-
Runner.define_spec(self)
|
92
|
+
@expectations = expectations.map { |e| Expectation.new(**e) }
|
117
93
|
end
|
118
94
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
95
|
+
#
|
96
|
+
# Converts the spec to a hash representation
|
97
|
+
#
|
98
|
+
# @return [Hash] Hash representation
|
99
|
+
#
|
100
|
+
def to_h
|
101
|
+
{
|
102
|
+
name:,
|
103
|
+
file_path:,
|
104
|
+
file_name:,
|
105
|
+
debug:,
|
106
|
+
line_number:,
|
107
|
+
expectations: expectations.map(&:to_h)
|
108
|
+
}
|
124
109
|
end
|
125
110
|
end
|
126
111
|
end
|
112
|
+
|
113
|
+
require_relative "spec/expectation"
|
data/lib/spec_forge/type.rb
CHANGED
@@ -1,24 +1,28 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module SpecForge
|
4
|
+
#
|
5
|
+
# Provides helper methods for checking types
|
6
|
+
# Useful for working with both regular objects and Attribute delegators
|
7
|
+
#
|
4
8
|
module Type
|
5
9
|
#
|
6
|
-
# Checks if the object is a Hash
|
10
|
+
# Checks if the object is a Hash or a ResolvableHash delegator
|
7
11
|
#
|
8
12
|
# @param object [Object] The object to check
|
9
13
|
#
|
10
|
-
# @return [Boolean]
|
14
|
+
# @return [Boolean] True if the object is a hash-like structure
|
11
15
|
#
|
12
16
|
def self.hash?(object)
|
13
17
|
object.is_a?(Hash) || object.is_a?(Attribute::ResolvableHash)
|
14
18
|
end
|
15
19
|
|
16
20
|
#
|
17
|
-
# Checks if the object is an Array
|
21
|
+
# Checks if the object is an Array or a ResolvableArray delegator
|
18
22
|
#
|
19
23
|
# @param object [Object] The object to check
|
20
24
|
#
|
21
|
-
# @return [Boolean]
|
25
|
+
# @return [Boolean] True if the object is an array-like structure
|
22
26
|
#
|
23
27
|
def self.array?(object)
|
24
28
|
object.is_a?(Array) || object.is_a?(Attribute::ResolvableArray)
|
@@ -28,8 +32,22 @@ end
|
|
28
32
|
|
29
33
|
#
|
30
34
|
# Represents Hash/ResolvableHash in a form that can be used in a case statement
|
35
|
+
# Allows for type switching on hash-like objects
|
36
|
+
#
|
37
|
+
# @example
|
38
|
+
# case value
|
39
|
+
# when HashLike
|
40
|
+
# # Handle hash-like objects
|
41
|
+
# end
|
31
42
|
#
|
32
43
|
class HashLike
|
44
|
+
#
|
45
|
+
# Provides custom type matching for use in case statements
|
46
|
+
#
|
47
|
+
# @param object [Object] The object to check against the type
|
48
|
+
#
|
49
|
+
# @return [Boolean] Whether the object matches the type
|
50
|
+
#
|
33
51
|
def self.===(object)
|
34
52
|
SpecForge::Type.hash?(object)
|
35
53
|
end
|
@@ -37,8 +55,22 @@ end
|
|
37
55
|
|
38
56
|
#
|
39
57
|
# Represents Array/ResolvableArray in a form that can be used in a case statement
|
58
|
+
# Allows for type switching on array-like objects
|
59
|
+
#
|
60
|
+
# @example
|
61
|
+
# case value
|
62
|
+
# when ArrayLike
|
63
|
+
# # Handle array-like objects
|
64
|
+
# end
|
40
65
|
#
|
41
66
|
class ArrayLike
|
67
|
+
#
|
68
|
+
# Provides custom type matching for use in case statements
|
69
|
+
#
|
70
|
+
# @param object [Object] The object to check against the type
|
71
|
+
#
|
72
|
+
# @return [Boolean] Whether the object matches the type
|
73
|
+
#
|
42
74
|
def self.===(object)
|
43
75
|
SpecForge::Type.array?(object)
|
44
76
|
end
|
data/lib/spec_forge/version.rb
CHANGED
data/lib/spec_forge.rb
CHANGED
@@ -16,92 +16,177 @@ require "singleton"
|
|
16
16
|
require "thor"
|
17
17
|
require "yaml"
|
18
18
|
|
19
|
+
#
|
20
|
+
# SpecForge: Write expressive API tests in YAML with the power of RSpec matchers
|
21
|
+
#
|
22
|
+
# SpecForge is a testing framework that allows writing API tests in a YAML format
|
23
|
+
# that reads like documentation. It combines the readability of YAML with the
|
24
|
+
# power of RSpec matchers, Faker data generation, and FactoryBot test objects.
|
25
|
+
#
|
26
|
+
# @example Basic spec in YAML
|
27
|
+
# get_user:
|
28
|
+
# path: /users/1
|
29
|
+
# expectations:
|
30
|
+
# - expect:
|
31
|
+
# status: 200
|
32
|
+
# json:
|
33
|
+
# name: kind_of.string
|
34
|
+
# email: /@/
|
35
|
+
#
|
36
|
+
# @example Running specs
|
37
|
+
# # Run all specs
|
38
|
+
# SpecForge.run
|
39
|
+
#
|
40
|
+
# # Run specific file
|
41
|
+
# SpecForge.run(file_name: "users")
|
42
|
+
#
|
43
|
+
# # Run specific spec
|
44
|
+
# SpecForge.run(file_name: "users", spec_name: "create_user")
|
45
|
+
#
|
46
|
+
module SpecForge
|
47
|
+
class << self
|
48
|
+
#
|
49
|
+
# Loads all factories and specs and runs the tests with optional filtering
|
50
|
+
#
|
51
|
+
# This is the main entry point for running SpecForge tests. It loads the
|
52
|
+
# forge_helper.rb file if it exists, configures the environment, loads
|
53
|
+
# factories and specs, and runs the tests through RSpec.
|
54
|
+
#
|
55
|
+
# @param file_name [String, nil] Optional name of spec file to run
|
56
|
+
# @param spec_name [String, nil] Optional name of spec to run
|
57
|
+
# @param expectation_name [String, nil] Optional name of expectation to run
|
58
|
+
#
|
59
|
+
def run(file_name: nil, spec_name: nil, expectation_name: nil)
|
60
|
+
# Load spec_helper.rb
|
61
|
+
forge_helper = SpecForge.forge_path.join("forge_helper.rb")
|
62
|
+
require_relative forge_helper if File.exist?(forge_helper)
|
63
|
+
|
64
|
+
# Validate in case anything was changed
|
65
|
+
configuration.validate
|
66
|
+
|
67
|
+
# Load factories
|
68
|
+
Factory.load_and_register
|
69
|
+
|
70
|
+
# Load the specs from their files and create forges from them
|
71
|
+
forges = Loader.load_from_files.map { |f| Forge.new(*f) }
|
72
|
+
|
73
|
+
# Filter out the specs and expectations
|
74
|
+
forges = Filter.apply(forges, file_name:, spec_name:, expectation_name:)
|
75
|
+
|
76
|
+
# Tell the user that we filtered if we did
|
77
|
+
Filter.announce(forges, file_name:, spec_name:, expectation_name:)
|
78
|
+
|
79
|
+
# Define and run everything
|
80
|
+
Runner.define(forges)
|
81
|
+
Runner.run
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
# Returns the directory root for the working directory
|
86
|
+
#
|
87
|
+
# @return [Pathname] The root directory path
|
88
|
+
#
|
89
|
+
def root
|
90
|
+
@root ||= Pathname.pwd
|
91
|
+
end
|
92
|
+
|
93
|
+
#
|
94
|
+
# Returns SpecForge's working directory
|
95
|
+
#
|
96
|
+
# @return [Pathname] The spec_forge directory path
|
97
|
+
#
|
98
|
+
def forge_path
|
99
|
+
@forge_path ||= root.join("spec_forge")
|
100
|
+
end
|
101
|
+
|
102
|
+
#
|
103
|
+
# Returns SpecForge's configuration
|
104
|
+
#
|
105
|
+
# @return [Configuration] The current configuration
|
106
|
+
#
|
107
|
+
def configuration
|
108
|
+
@configuration ||= Configuration.new
|
109
|
+
end
|
110
|
+
|
111
|
+
#
|
112
|
+
# Yields SpecForge's configuration to a block for modification
|
113
|
+
#
|
114
|
+
# @yield [config] Block that receives the configuration object
|
115
|
+
# @yieldparam config [Configuration] The configuration to modify
|
116
|
+
#
|
117
|
+
# @return [Configuration] The updated configuration
|
118
|
+
#
|
119
|
+
def configure(&block)
|
120
|
+
block&.call(configuration)
|
121
|
+
configuration
|
122
|
+
end
|
123
|
+
|
124
|
+
#
|
125
|
+
# Returns a backtrace cleaner configured for SpecForge
|
126
|
+
#
|
127
|
+
# Creates and configures an ActiveSupport::BacktraceCleaner to improve
|
128
|
+
# error messages by removing unnecessary lines and root paths.
|
129
|
+
#
|
130
|
+
# @return [ActiveSupport::BacktraceCleaner] The configured backtrace cleaner
|
131
|
+
#
|
132
|
+
def backtrace_cleaner
|
133
|
+
@backtrace_cleaner ||= begin
|
134
|
+
root = "#{SpecForge.root}/"
|
135
|
+
|
136
|
+
cleaner = ActiveSupport::BacktraceCleaner.new
|
137
|
+
cleaner.add_filter { |line| line.delete_prefix(root) }
|
138
|
+
cleaner.add_silencer { |line| /rubygems|backtrace_cleaner/.match?(line) }
|
139
|
+
cleaner
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
#
|
144
|
+
# Returns the current execution context
|
145
|
+
#
|
146
|
+
# @return [Context] The current context object
|
147
|
+
#
|
148
|
+
def context
|
149
|
+
@context ||= Context.new
|
150
|
+
end
|
151
|
+
|
152
|
+
#
|
153
|
+
# Registers a callback for a specific test lifecycle event
|
154
|
+
# Allows custom code execution at specific points during test execution
|
155
|
+
#
|
156
|
+
# @param name [Symbol, String] A unique identifier for this callback
|
157
|
+
# @yield A block to execute when the callback is triggered
|
158
|
+
# @yieldparam context [Object] An object containing context-specific state data, depending
|
159
|
+
# on which hook the callback is triggered from.
|
160
|
+
#
|
161
|
+
# @return [Proc] The registered callback
|
162
|
+
#
|
163
|
+
# @example Registering a custom debug handler
|
164
|
+
# SpecForge.register_callback(:clean_database) do |context|
|
165
|
+
# DatabaseCleaner.clean
|
166
|
+
# end
|
167
|
+
#
|
168
|
+
def register_callback(name, &)
|
169
|
+
Callbacks.register(name, &)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
19
174
|
require_relative "spec_forge/attribute"
|
20
175
|
require_relative "spec_forge/backtrace_formatter"
|
176
|
+
require_relative "spec_forge/callbacks"
|
21
177
|
require_relative "spec_forge/cli"
|
22
178
|
require_relative "spec_forge/configuration"
|
179
|
+
require_relative "spec_forge/context"
|
23
180
|
require_relative "spec_forge/core_ext"
|
24
181
|
require_relative "spec_forge/error"
|
25
182
|
require_relative "spec_forge/factory"
|
183
|
+
require_relative "spec_forge/filter"
|
184
|
+
require_relative "spec_forge/forge"
|
26
185
|
require_relative "spec_forge/http"
|
186
|
+
require_relative "spec_forge/loader"
|
187
|
+
require_relative "spec_forge/matchers"
|
27
188
|
require_relative "spec_forge/normalizer"
|
28
189
|
require_relative "spec_forge/runner"
|
29
190
|
require_relative "spec_forge/spec"
|
30
191
|
require_relative "spec_forge/type"
|
31
192
|
require_relative "spec_forge/version"
|
32
|
-
|
33
|
-
module SpecForge
|
34
|
-
#
|
35
|
-
# Loads all factories and specs located in "path", then runs all of the specs
|
36
|
-
#
|
37
|
-
# @param path [String] The file path that contains factories and specs
|
38
|
-
#
|
39
|
-
def self.run(file_name: nil, spec_name: nil, expectation_name: nil)
|
40
|
-
path = SpecForge.forge
|
41
|
-
|
42
|
-
# Initialize
|
43
|
-
forge_helper = path.join("forge_helper.rb")
|
44
|
-
require_relative forge_helper if File.exist?(forge_helper)
|
45
|
-
|
46
|
-
# Validate
|
47
|
-
configuration.validate
|
48
|
-
|
49
|
-
# Prepare
|
50
|
-
Factory.load_and_register
|
51
|
-
Spec.load_and_define(file_name:, spec_name:, expectation_name:)
|
52
|
-
|
53
|
-
# Run
|
54
|
-
Runner.run
|
55
|
-
end
|
56
|
-
|
57
|
-
#
|
58
|
-
# Returns the directory root for the working directory
|
59
|
-
#
|
60
|
-
# @return [Pathname]
|
61
|
-
#
|
62
|
-
def self.root
|
63
|
-
@root ||= Pathname.pwd
|
64
|
-
end
|
65
|
-
|
66
|
-
#
|
67
|
-
# Returns SpecForge's working directory
|
68
|
-
#
|
69
|
-
# @return [Pathname]
|
70
|
-
#
|
71
|
-
def self.forge
|
72
|
-
@forge ||= root.join("spec_forge")
|
73
|
-
end
|
74
|
-
|
75
|
-
#
|
76
|
-
# Returns SpecForge's configuration
|
77
|
-
#
|
78
|
-
# @return [Config]
|
79
|
-
#
|
80
|
-
def self.configuration
|
81
|
-
@configuration ||= Configuration.new
|
82
|
-
end
|
83
|
-
|
84
|
-
#
|
85
|
-
# Yields SpecForge's configuration to a block
|
86
|
-
#
|
87
|
-
def self.configure(&block)
|
88
|
-
block&.call(configuration)
|
89
|
-
configuration
|
90
|
-
end
|
91
|
-
|
92
|
-
#
|
93
|
-
# Returns a backtrace cleaner configured for SpecForge
|
94
|
-
#
|
95
|
-
# @return [ActiveSupport::BacktraceCleaner]
|
96
|
-
#
|
97
|
-
def self.backtrace_cleaner
|
98
|
-
@backtrace_cleaner ||= begin
|
99
|
-
root = "#{SpecForge.root}/"
|
100
|
-
|
101
|
-
cleaner = ActiveSupport::BacktraceCleaner.new
|
102
|
-
cleaner.add_filter { |line| line.delete_prefix(root) }
|
103
|
-
cleaner.add_silencer { |line| /rubygems|backtrace_cleaner/.match?(line) }
|
104
|
-
cleaner
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
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.
|
4
|
+
version: 0.6.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-
|
11
|
+
date: 2025-03-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -163,6 +163,7 @@ files:
|
|
163
163
|
- lib/spec_forge/attribute/chainable.rb
|
164
164
|
- lib/spec_forge/attribute/factory.rb
|
165
165
|
- lib/spec_forge/attribute/faker.rb
|
166
|
+
- lib/spec_forge/attribute/global.rb
|
166
167
|
- lib/spec_forge/attribute/literal.rb
|
167
168
|
- lib/spec_forge/attribute/matcher.rb
|
168
169
|
- lib/spec_forge/attribute/parameterized.rb
|
@@ -170,9 +171,11 @@ files:
|
|
170
171
|
- lib/spec_forge/attribute/resolvable.rb
|
171
172
|
- lib/spec_forge/attribute/resolvable_array.rb
|
172
173
|
- lib/spec_forge/attribute/resolvable_hash.rb
|
174
|
+
- lib/spec_forge/attribute/store.rb
|
173
175
|
- lib/spec_forge/attribute/transform.rb
|
174
176
|
- lib/spec_forge/attribute/variable.rb
|
175
177
|
- lib/spec_forge/backtrace_formatter.rb
|
178
|
+
- lib/spec_forge/callbacks.rb
|
176
179
|
- lib/spec_forge/cli.rb
|
177
180
|
- lib/spec_forge/cli/actions.rb
|
178
181
|
- lib/spec_forge/cli/command.rb
|
@@ -180,23 +183,38 @@ files:
|
|
180
183
|
- lib/spec_forge/cli/new.rb
|
181
184
|
- lib/spec_forge/cli/run.rb
|
182
185
|
- lib/spec_forge/configuration.rb
|
186
|
+
- lib/spec_forge/context.rb
|
187
|
+
- lib/spec_forge/context/callbacks.rb
|
188
|
+
- lib/spec_forge/context/global.rb
|
189
|
+
- lib/spec_forge/context/store.rb
|
190
|
+
- lib/spec_forge/context/variables.rb
|
183
191
|
- lib/spec_forge/core_ext.rb
|
184
192
|
- lib/spec_forge/core_ext/rspec.rb
|
185
193
|
- lib/spec_forge/error.rb
|
186
194
|
- lib/spec_forge/factory.rb
|
195
|
+
- lib/spec_forge/filter.rb
|
196
|
+
- lib/spec_forge/forge.rb
|
187
197
|
- lib/spec_forge/http.rb
|
188
198
|
- lib/spec_forge/http/backend.rb
|
189
199
|
- lib/spec_forge/http/client.rb
|
190
200
|
- lib/spec_forge/http/request.rb
|
191
201
|
- lib/spec_forge/http/verb.rb
|
202
|
+
- lib/spec_forge/loader.rb
|
203
|
+
- lib/spec_forge/matchers.rb
|
192
204
|
- lib/spec_forge/normalizer.rb
|
193
205
|
- lib/spec_forge/normalizer/configuration.rb
|
194
206
|
- lib/spec_forge/normalizer/constraint.rb
|
195
207
|
- lib/spec_forge/normalizer/expectation.rb
|
196
208
|
- lib/spec_forge/normalizer/factory.rb
|
197
209
|
- lib/spec_forge/normalizer/factory_reference.rb
|
210
|
+
- lib/spec_forge/normalizer/global_context.rb
|
198
211
|
- lib/spec_forge/normalizer/spec.rb
|
199
212
|
- lib/spec_forge/runner.rb
|
213
|
+
- lib/spec_forge/runner/callbacks.rb
|
214
|
+
- lib/spec_forge/runner/debug_proxy.rb
|
215
|
+
- lib/spec_forge/runner/listener.rb
|
216
|
+
- lib/spec_forge/runner/metadata.rb
|
217
|
+
- lib/spec_forge/runner/state.rb
|
200
218
|
- lib/spec_forge/spec.rb
|
201
219
|
- lib/spec_forge/spec/expectation.rb
|
202
220
|
- lib/spec_forge/spec/expectation/constraint.rb
|
@@ -205,9 +223,6 @@ files:
|
|
205
223
|
- lib/templates/forge_helper.tt
|
206
224
|
- lib/templates/new_factory.tt
|
207
225
|
- lib/templates/new_spec.tt
|
208
|
-
- spec_forge/factories/user.yml
|
209
|
-
- spec_forge/forge_helper.rb
|
210
|
-
- spec_forge/specs/users.yml
|
211
226
|
homepage: https://github.com/itsthedevman/spec_forge
|
212
227
|
licenses:
|
213
228
|
- MIT
|
data/spec_forge/forge_helper.rb
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
##########################################
|
4
|
-
# Framework Integration
|
5
|
-
##########################################
|
6
|
-
|
7
|
-
# Rails Integration
|
8
|
-
# require_relative "../config/environment"
|
9
|
-
|
10
|
-
# RSpec Integration (includes your existing configurations)
|
11
|
-
# require_relative "../spec/spec_helper"
|
12
|
-
|
13
|
-
# Custom requires (models, libraries, etc)
|
14
|
-
# Dir[File.join(__dir__, "..", "lib", "**", "*.rb")].sort.each { |f| require f }
|
15
|
-
|
16
|
-
##########################################
|
17
|
-
# Configuration
|
18
|
-
##########################################
|
19
|
-
|
20
|
-
SpecForge.configure do |config|
|
21
|
-
# Base configuration
|
22
|
-
config.base_url = "http://localhost:3000"
|
23
|
-
|
24
|
-
# Default request headers
|
25
|
-
config.headers = {
|
26
|
-
"Authorization" => "Bearer #{ENV.fetch("API_TOKEN", "")}"
|
27
|
-
}
|
28
|
-
|
29
|
-
# Optional: Default query parameters
|
30
|
-
# config.query = {api_key: ENV['API_KEY']}
|
31
|
-
|
32
|
-
# Factory configuration
|
33
|
-
# config.factories.auto_discover = false # Default: true
|
34
|
-
# config.factories.paths += ["lib/factories"] # Adds to default paths
|
35
|
-
|
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
|
41
|
-
# config.on_debug { binding.pry }
|
42
|
-
|
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
|
48
|
-
end
|