spec_forge 0.6.0 → 0.7.1
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/CHANGELOG.md +174 -8
- data/README.md +135 -10
- data/flake.lock +3 -3
- data/flake.nix +3 -3
- data/lib/spec_forge/attribute/factory.rb +1 -1
- data/lib/spec_forge/attribute/transform.rb +1 -1
- data/lib/spec_forge/callbacks.rb +9 -0
- data/lib/spec_forge/cli/docs/generate.rb +72 -0
- data/lib/spec_forge/cli/docs.rb +92 -0
- data/lib/spec_forge/cli/init.rb +39 -7
- data/lib/spec_forge/cli/new.rb +13 -3
- data/lib/spec_forge/cli/run.rb +12 -4
- data/lib/spec_forge/cli/serve.rb +156 -0
- data/lib/spec_forge/cli.rb +14 -6
- data/lib/spec_forge/configuration.rb +13 -9
- data/lib/spec_forge/context/store.rb +23 -40
- data/lib/spec_forge/core_ext/array.rb +27 -0
- data/lib/spec_forge/documentation/builder.rb +383 -0
- data/lib/spec_forge/documentation/document/operation.rb +47 -0
- data/lib/spec_forge/documentation/document/parameter.rb +22 -0
- data/lib/spec_forge/documentation/document/request_body.rb +24 -0
- data/lib/spec_forge/documentation/document/response.rb +39 -0
- data/lib/spec_forge/documentation/document/response_body.rb +27 -0
- data/lib/spec_forge/documentation/document.rb +48 -0
- data/lib/spec_forge/documentation/generators/base.rb +81 -0
- data/lib/spec_forge/documentation/generators/openapi/base.rb +100 -0
- data/lib/spec_forge/documentation/generators/openapi/error_formatter.rb +149 -0
- data/lib/spec_forge/documentation/generators/openapi/v3_0.rb +65 -0
- data/lib/spec_forge/documentation/generators/openapi.rb +59 -0
- data/lib/spec_forge/documentation/generators.rb +17 -0
- data/lib/spec_forge/documentation/loader/cache.rb +138 -0
- data/lib/spec_forge/documentation/loader.rb +159 -0
- data/lib/spec_forge/documentation/openapi/base.rb +33 -0
- data/lib/spec_forge/documentation/openapi/v3_0/example.rb +44 -0
- data/lib/spec_forge/documentation/openapi/v3_0/media_type.rb +42 -0
- data/lib/spec_forge/documentation/openapi/v3_0/operation.rb +175 -0
- data/lib/spec_forge/documentation/openapi/v3_0/response.rb +65 -0
- data/lib/spec_forge/documentation/openapi/v3_0/schema.rb +80 -0
- data/lib/spec_forge/documentation/openapi/v3_0/tag.rb +71 -0
- data/lib/spec_forge/documentation/openapi.rb +23 -0
- data/lib/spec_forge/documentation.rb +27 -0
- data/lib/spec_forge/error.rb +17 -0
- data/lib/spec_forge/factory.rb +2 -2
- data/lib/spec_forge/filter.rb +3 -4
- data/lib/spec_forge/forge.rb +5 -4
- data/lib/spec_forge/http/backend.rb +5 -0
- data/lib/spec_forge/http/request.rb +14 -3
- data/lib/spec_forge/loader.rb +14 -24
- data/lib/spec_forge/normalizer/default.rb +51 -0
- data/lib/spec_forge/normalizer/definition.rb +248 -0
- data/lib/spec_forge/normalizer/validators.rb +99 -0
- data/lib/spec_forge/normalizer.rb +356 -199
- data/lib/spec_forge/normalizers/_shared.yml +76 -0
- data/lib/spec_forge/normalizers/configuration.yml +23 -0
- data/lib/spec_forge/normalizers/constraint.yml +8 -0
- data/lib/spec_forge/normalizers/expectation.yml +47 -0
- data/lib/spec_forge/normalizers/factory.yml +12 -0
- data/lib/spec_forge/normalizers/factory_reference.yml +15 -0
- data/lib/spec_forge/normalizers/global_context.yml +28 -0
- data/lib/spec_forge/normalizers/spec.yml +50 -0
- data/lib/spec_forge/runner/adapter.rb +181 -0
- data/lib/spec_forge/runner/debug_proxy.rb +44 -42
- data/lib/spec_forge/runner/state.rb +4 -5
- data/lib/spec_forge/runner.rb +40 -124
- data/lib/spec_forge/spec/expectation/constraint.rb +13 -5
- data/lib/spec_forge/spec/expectation.rb +7 -3
- data/lib/spec_forge/spec.rb +13 -58
- data/lib/spec_forge/version.rb +1 -1
- data/lib/spec_forge.rb +30 -23
- data/lib/templates/openapi.yml.tt +22 -0
- data/lib/templates/redoc.html.tt +28 -0
- data/lib/templates/swagger.html.tt +59 -0
- metadata +92 -14
- data/lib/spec_forge/normalizer/configuration.rb +0 -90
- data/lib/spec_forge/normalizer/constraint.rb +0 -60
- data/lib/spec_forge/normalizer/expectation.rb +0 -105
- data/lib/spec_forge/normalizer/factory.rb +0 -78
- data/lib/spec_forge/normalizer/factory_reference.rb +0 -85
- data/lib/spec_forge/normalizer/global_context.rb +0 -88
- data/lib/spec_forge/normalizer/spec.rb +0 -97
- /data/lib/templates/{forge_helper.tt → forge_helper.rb.tt} +0 -0
- /data/lib/templates/{new_factory.tt → new_factory.yml.tt} +0 -0
- /data/lib/templates/{new_spec.tt → new_spec.yml.tt} +0 -0
data/lib/spec_forge/runner.rb
CHANGED
@@ -8,150 +8,66 @@ module SpecForge
|
|
8
8
|
class Runner
|
9
9
|
class << self
|
10
10
|
#
|
11
|
-
#
|
12
|
-
# Creates the test structure that will be executed
|
11
|
+
# Prepares forge objects for test execution
|
13
12
|
#
|
14
|
-
#
|
13
|
+
# Loads the forge helper, registers factories, loads specs from files,
|
14
|
+
# applies filtering, and returns ready-to-run forge objects.
|
15
15
|
#
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
# @
|
20
|
+
# @return [Array<Forge>] Array of prepared forge objects
|
38
21
|
#
|
39
|
-
def
|
40
|
-
|
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
|
-
|
68
|
-
|
25
|
+
# Load factories
|
26
|
+
Factory.load_and_register
|
69
27
|
|
70
|
-
|
71
|
-
|
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
|
-
|
75
|
-
|
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
|
-
|
79
|
-
|
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
|
-
|
83
|
-
|
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
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
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
|
142
|
-
|
143
|
-
|
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
|
-
#
|
146
|
-
|
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"
|
@@ -12,21 +12,24 @@ module SpecForge
|
|
12
12
|
# @example In code
|
13
13
|
# constraint = Constraint.new(
|
14
14
|
# status: 200,
|
15
|
-
#
|
15
|
+
# headers: {response_header: "kind_of.string"},
|
16
|
+
# json: {name: {"matcher.eq" => "John"}}
|
16
17
|
# )
|
17
18
|
#
|
18
|
-
class Constraint < Data.define(:status, :json) # :xml, :html
|
19
|
+
class Constraint < Data.define(:status, :headers, :json) # :xml, :html
|
19
20
|
#
|
20
21
|
# Creates a new constraint
|
21
22
|
#
|
22
23
|
# @param status [Integer, String] The expected HTTP status code, or reference to one
|
24
|
+
# @param headers [Hash] The expected headers with matchers
|
23
25
|
# @param json [Hash, Array] The expected JSON with matchers
|
24
26
|
#
|
25
27
|
# @return [Constraint] A new constraint instance
|
26
28
|
#
|
27
|
-
def initialize(status:, json: {})
|
29
|
+
def initialize(status:, headers: {}, json: {})
|
28
30
|
super(
|
29
31
|
status: Attribute.from(status),
|
32
|
+
headers: Attribute.from(headers),
|
30
33
|
json: Attribute.from(json)
|
31
34
|
)
|
32
35
|
end
|
@@ -58,7 +61,8 @@ module SpecForge
|
|
58
61
|
def as_matchers
|
59
62
|
{
|
60
63
|
status: status.resolve_as_matcher,
|
61
|
-
json: resolve_json_matcher
|
64
|
+
json: resolve_json_matcher,
|
65
|
+
headers: resolve_hash_matcher(headers)
|
62
66
|
}
|
63
67
|
end
|
64
68
|
|
@@ -108,11 +112,15 @@ module SpecForge
|
|
108
112
|
def resolve_json_matcher
|
109
113
|
case json
|
110
114
|
when HashLike
|
111
|
-
json
|
115
|
+
resolve_hash_matcher(json)
|
112
116
|
else
|
113
117
|
json.resolve_as_matcher
|
114
118
|
end
|
115
119
|
end
|
120
|
+
|
121
|
+
def resolve_hash_matcher(hash)
|
122
|
+
hash.transform_values(&:resolve_as_matcher).stringify_keys
|
123
|
+
end
|
116
124
|
end
|
117
125
|
end
|
118
126
|
end
|
@@ -15,7 +15,10 @@ module SpecForge
|
|
15
15
|
# json:
|
16
16
|
# name: kind_of.string
|
17
17
|
#
|
18
|
-
class Expectation < Data.define(
|
18
|
+
class Expectation < Data.define(
|
19
|
+
:id, :name, :line_number,
|
20
|
+
:debug, :store_as, :documentation, :constraints
|
21
|
+
)
|
19
22
|
#
|
20
23
|
# @return [Boolean] True if debugging is enabled
|
21
24
|
#
|
@@ -34,14 +37,15 @@ module SpecForge
|
|
34
37
|
# @param line_number [Integer] Line number in source
|
35
38
|
# @param debug [Boolean] Whether to enable debugging
|
36
39
|
# @param store_as [String] Unique Context::Store identifier
|
40
|
+
# @param documentation [Boolean] Whether to include in documentation generation
|
37
41
|
# @param expect [Hash] Expected constraints
|
38
42
|
#
|
39
43
|
# @return [Expectation] A new expectation instance
|
40
44
|
#
|
41
|
-
def initialize(id:, name:, line_number:, debug:, store_as:, expect:)
|
45
|
+
def initialize(id:, name:, line_number:, debug:, store_as:, expect:, documentation:)
|
42
46
|
constraints = Constraint.new(**expect)
|
43
47
|
|
44
|
-
super(id:, name:, line_number:, debug:, store_as:, constraints:)
|
48
|
+
super(id:, name:, line_number:, debug:, store_as:, documentation:, constraints:)
|
45
49
|
end
|
46
50
|
|
47
51
|
#
|
data/lib/spec_forge/spec.rb
CHANGED
@@ -14,61 +14,15 @@ module SpecForge
|
|
14
14
|
# - expect:
|
15
15
|
# status: 200
|
16
16
|
#
|
17
|
-
class Spec
|
17
|
+
class Spec < Data.define(
|
18
|
+
:id, :name, :file_path, :file_name, :line_number,
|
19
|
+
:debug, :documentation, :expectations
|
20
|
+
)
|
18
21
|
#
|
19
22
|
# @return [Boolean] True if debugging is enabled
|
20
23
|
#
|
21
24
|
attr_predicate :debug
|
22
25
|
|
23
|
-
#
|
24
|
-
# Unique identifier for this spec
|
25
|
-
#
|
26
|
-
# @return [String] The spec ID
|
27
|
-
#
|
28
|
-
attr_reader :id
|
29
|
-
|
30
|
-
#
|
31
|
-
# Human-readable name for this spec
|
32
|
-
#
|
33
|
-
# @return [String] The spec name
|
34
|
-
#
|
35
|
-
attr_reader :name
|
36
|
-
|
37
|
-
#
|
38
|
-
# Absolute path to the file containing this spec
|
39
|
-
#
|
40
|
-
# @return [String] The file path
|
41
|
-
#
|
42
|
-
attr_reader :file_path
|
43
|
-
|
44
|
-
#
|
45
|
-
# Base name of the file without path or extension
|
46
|
-
#
|
47
|
-
# @return [String] The file name
|
48
|
-
#
|
49
|
-
attr_reader :file_name
|
50
|
-
|
51
|
-
#
|
52
|
-
# Whether to enable debugging for this spec
|
53
|
-
#
|
54
|
-
# @return [Boolean] Debug flag
|
55
|
-
#
|
56
|
-
attr_reader :debug
|
57
|
-
|
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
|
64
|
-
|
65
|
-
#
|
66
|
-
# The expectations to test for this spec
|
67
|
-
#
|
68
|
-
# @return [Array<Expectation>] The expectations
|
69
|
-
#
|
70
|
-
attr_accessor :expectations
|
71
|
-
|
72
26
|
#
|
73
27
|
# Creates a new spec instance
|
74
28
|
#
|
@@ -78,18 +32,18 @@ module SpecForge
|
|
78
32
|
# @param file_name [String] Base name of file
|
79
33
|
# @param debug [Boolean] Whether to enable debugging
|
80
34
|
# @param line_number [Integer] Line number in source
|
35
|
+
# @param documentation [Boolean] Whether to include in documentation generation
|
81
36
|
# @param expectations [Array<Hash>] Expectation configurations
|
82
37
|
#
|
83
38
|
# @return [Spec] A new spec instance
|
84
39
|
#
|
85
|
-
def initialize(
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
@expectations = expectations.map { |e| Expectation.new(**e) }
|
40
|
+
def initialize(
|
41
|
+
id:, name:, file_path:, file_name:, line_number:,
|
42
|
+
debug:, documentation:, expectations:
|
43
|
+
)
|
44
|
+
expectations = expectations.map { |e| Expectation.new(**e) }
|
45
|
+
|
46
|
+
super
|
93
47
|
end
|
94
48
|
|
95
49
|
#
|
@@ -104,6 +58,7 @@ module SpecForge
|
|
104
58
|
file_name:,
|
105
59
|
debug:,
|
106
60
|
line_number:,
|
61
|
+
documentation:,
|
107
62
|
expectations: expectations.map(&:to_h)
|
108
63
|
}
|
109
64
|
end
|
data/lib/spec_forge/version.rb
CHANGED
data/lib/spec_forge.rb
CHANGED
@@ -5,15 +5,19 @@ require "logger"
|
|
5
5
|
require "active_support"
|
6
6
|
require "active_support/core_ext"
|
7
7
|
require "commander"
|
8
|
-
require "everythingrb"
|
8
|
+
require "everythingrb/prelude"
|
9
|
+
require "everythingrb/all"
|
9
10
|
require "factory_bot"
|
10
11
|
require "faker"
|
11
12
|
require "faraday"
|
12
13
|
require "mime/types"
|
14
|
+
require "openapi3_parser"
|
13
15
|
require "pathname"
|
14
16
|
require "rspec"
|
17
|
+
require "sem_version"
|
15
18
|
require "singleton"
|
16
19
|
require "thor"
|
20
|
+
require "webrick"
|
17
21
|
require "yaml"
|
18
22
|
|
19
23
|
#
|
@@ -57,28 +61,8 @@ module SpecForge
|
|
57
61
|
# @param expectation_name [String, nil] Optional name of expectation to run
|
58
62
|
#
|
59
63
|
def run(file_name: nil, spec_name: nil, expectation_name: nil)
|
60
|
-
|
61
|
-
|
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
|
64
|
+
forges = Runner.prepare(file_name:, spec_name:, expectation_name:)
|
65
|
+
Runner.run(forges, exit_on_finish: true)
|
82
66
|
end
|
83
67
|
|
84
68
|
#
|
@@ -99,6 +83,15 @@ module SpecForge
|
|
99
83
|
@forge_path ||= root.join("spec_forge")
|
100
84
|
end
|
101
85
|
|
86
|
+
#
|
87
|
+
# Returns SpecForge's openapi directory
|
88
|
+
#
|
89
|
+
# @return [Pathname] The spec_forge openapi directory path
|
90
|
+
#
|
91
|
+
def openapi_path
|
92
|
+
@openapi_path ||= forge_path.join("openapi")
|
93
|
+
end
|
94
|
+
|
102
95
|
#
|
103
96
|
# Returns SpecForge's configuration
|
104
97
|
#
|
@@ -168,6 +161,19 @@ module SpecForge
|
|
168
161
|
def register_callback(name, &)
|
169
162
|
Callbacks.register(name, &)
|
170
163
|
end
|
164
|
+
|
165
|
+
#
|
166
|
+
# Generates a unique ID for an object based on hash and object_id
|
167
|
+
#
|
168
|
+
# @param object [Object] The object to generate an ID for
|
169
|
+
#
|
170
|
+
# @return [String] A unique ID string
|
171
|
+
#
|
172
|
+
# @private
|
173
|
+
#
|
174
|
+
def generate_id(object)
|
175
|
+
"#{object.hash.abs.to_s(36)}_#{object.object_id.to_s(36)}"
|
176
|
+
end
|
171
177
|
end
|
172
178
|
end
|
173
179
|
|
@@ -178,6 +184,7 @@ require_relative "spec_forge/cli"
|
|
178
184
|
require_relative "spec_forge/configuration"
|
179
185
|
require_relative "spec_forge/context"
|
180
186
|
require_relative "spec_forge/core_ext"
|
187
|
+
require_relative "spec_forge/documentation"
|
181
188
|
require_relative "spec_forge/error"
|
182
189
|
require_relative "spec_forge/factory"
|
183
190
|
require_relative "spec_forge/filter"
|
@@ -0,0 +1,22 @@
|
|
1
|
+
info:
|
2
|
+
title: My API
|
3
|
+
version: 1.0.0
|
4
|
+
description: A description of My API
|
5
|
+
contact:
|
6
|
+
name: My API Team
|
7
|
+
email: api@example.com
|
8
|
+
license:
|
9
|
+
name: MIT
|
10
|
+
url: https://opensource.org/licenses/MIT
|
11
|
+
|
12
|
+
# servers:
|
13
|
+
# - url: https://api.example.com/v1
|
14
|
+
# description: Production
|
15
|
+
# - url: https://staging-api.example.com/v1
|
16
|
+
# description: Staging
|
17
|
+
#
|
18
|
+
# tags:
|
19
|
+
# - name: users
|
20
|
+
# description: User account management
|
21
|
+
# - name: posts
|
22
|
+
# description: Blog post management
|
@@ -0,0 +1,28 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
|
4
|
+
<head>
|
5
|
+
<title>Redoc</title>
|
6
|
+
<!-- needed for adaptive design -->
|
7
|
+
<meta charset="utf-8" />
|
8
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
9
|
+
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
|
10
|
+
|
11
|
+
<!--
|
12
|
+
Redoc doesn't change outer page styles
|
13
|
+
-->
|
14
|
+
<style>
|
15
|
+
body {
|
16
|
+
margin: 0;
|
17
|
+
padding: 0;
|
18
|
+
}
|
19
|
+
</style>
|
20
|
+
</head>
|
21
|
+
|
22
|
+
<body>
|
23
|
+
<redoc spec-url="<%= spec_url %>"></redoc>
|
24
|
+
<script src=" https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js">
|
25
|
+
</script>
|
26
|
+
</body>
|
27
|
+
|
28
|
+
</html>
|
@@ -0,0 +1,59 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
|
4
|
+
<head>
|
5
|
+
<meta charset="UTF-8">
|
6
|
+
<title>Swagger UI</title>
|
7
|
+
<style>
|
8
|
+
html {
|
9
|
+
box-sizing: border-box;
|
10
|
+
overflow: -moz-scrollbars-vertical;
|
11
|
+
overflow-y: scroll;
|
12
|
+
}
|
13
|
+
|
14
|
+
*,
|
15
|
+
*:before,
|
16
|
+
*:after {
|
17
|
+
box-sizing: inherit;
|
18
|
+
}
|
19
|
+
|
20
|
+
body {
|
21
|
+
margin: 0;
|
22
|
+
background: #fafafa;
|
23
|
+
}
|
24
|
+
</style>
|
25
|
+
|
26
|
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.24.0/swagger-ui.min.css"
|
27
|
+
integrity="sha512-wWpxfn2bFvPwxuqDyiJbVB0WR3ffSqJNMMryNP07frPJ1h5Xg9HIDMV1wRr1rpxT5E+KTxDrKTuWfGb1RcV8SA=="
|
28
|
+
crossorigin="anonymous" referrerpolicy="no-referrer" />
|
29
|
+
</head>
|
30
|
+
|
31
|
+
<body>
|
32
|
+
<div id="swagger-ui"></div>
|
33
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.24.0/swagger-ui-bundle.min.js"
|
34
|
+
integrity="sha512-nEy/zRjIvuFMSr5ljsQUaUW4l7DoSHz8+SRybclmCjCh3MeF9UaooWYdr/SqjGCiyi4RIvBvn9DxCCV0ZDhiNA=="
|
35
|
+
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
36
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/5.24.0/swagger-ui-standalone-preset.min.js"
|
37
|
+
integrity="sha512-yJlD9FXQ7YaxAKXhviHSt/0KqWDCkLFdCnk0Ti23HXDMEQtHLAAWMHZ+POglC1mx/MOUB//h8kci3U1JYrywpQ=="
|
38
|
+
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
39
|
+
|
40
|
+
<script>
|
41
|
+
window.onload = function () {
|
42
|
+
const ui = SwaggerUIBundle({
|
43
|
+
url: "<%= spec_url %>",
|
44
|
+
dom_id: "#swagger-ui",
|
45
|
+
deepLinking: true,
|
46
|
+
presets: [
|
47
|
+
SwaggerUIBundle.presets.apis,
|
48
|
+
SwaggerUIStandalonePreset
|
49
|
+
],
|
50
|
+
plugins: [
|
51
|
+
SwaggerUIBundle.plugins.DownloadUrl
|
52
|
+
],
|
53
|
+
layout: "StandaloneLayout"
|
54
|
+
});
|
55
|
+
};
|
56
|
+
</script>
|
57
|
+
</body>
|
58
|
+
|
59
|
+
</html>
|