diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 93077e3a..fc31f1e3 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -13,6 +13,7 @@ jobs:
matrix:
include:
# Recent Rubies and Rails
+ - ruby-version: '3.3'
- ruby-version: '3.2'
- ruby-version: '3.1'
- ruby-version: '3.0'
diff --git a/lib/rspec_api_documentation/api_formatter.rb b/lib/rspec_api_documentation/api_formatter.rb
index 7a9f97f3..117df936 100644
--- a/lib/rspec_api_documentation/api_formatter.rb
+++ b/lib/rspec_api_documentation/api_formatter.rb
@@ -2,7 +2,7 @@
module RspecApiDocumentation
class ApiFormatter < RSpec::Core::Formatters::BaseTextFormatter
- RSpec::Core::Formatters.register self, :example_passed, :example_failed, :stop
+ RSpec::Core::Formatters.register self, :example_passed, :example_failed, :stop, :example_group_started
def initialize(output)
super
@@ -19,7 +19,7 @@ def start(notification)
def example_group_started(notification)
super
- output.puts " #{@example_group.description}"
+ output.puts " #{notification.group.description}"
end
def example_passed(example_notification)
diff --git a/lib/rspec_api_documentation/client_base.rb b/lib/rspec_api_documentation/client_base.rb
index db0560a3..34ccbdb1 100644
--- a/lib/rspec_api_documentation/client_base.rb
+++ b/lib/rspec_api_documentation/client_base.rb
@@ -45,7 +45,8 @@ def process(method, path, params = {}, headers ={})
def read_request_body
input = last_request.env["rack.input"]
- input.rewind
+ return "" unless input
+ input.rewind if input.respond_to?(:rewind)
input.read
end
@@ -89,6 +90,12 @@ def record_response_body(response_content_type, response_body)
return nil if response_body.empty?
formatter = RspecApiDocumentation.configuration.response_body_formatter
+ # Only force UTF-8 for text-based content types
+ if response_body.respond_to?(:encoding) && response_body.encoding == Encoding::ASCII_8BIT
+ if response_content_type && (response_content_type.include?('json') || response_content_type.include?('text'))
+ response_body = response_body.force_encoding(Encoding::UTF_8)
+ end
+ end
formatter.call(response_content_type, response_body)
end
diff --git a/lib/rspec_api_documentation/headers.rb b/lib/rspec_api_documentation/headers.rb
index d3041cde..465fe5cf 100644
--- a/lib/rspec_api_documentation/headers.rb
+++ b/lib/rspec_api_documentation/headers.rb
@@ -6,7 +6,7 @@ def env_to_headers(env)
headers = {}
env.each do |key, value|
# HTTP_ACCEPT_CHARSET => Accept-Charset
- if key =~ /^(HTTP_|CONTENT_TYPE)/
+ if key =~ /^(HTTP_|CONTENT_TYPE)/ && key != "HTTP_VERSION"
header = key.gsub(/^HTTP_/, '').split('_').map{|s| s.titleize}.join("-")
headers[header] = value
end
diff --git a/lib/rspec_api_documentation/open_api/node.rb b/lib/rspec_api_documentation/open_api/node.rb
index 2f102c88..71b2179a 100644
--- a/lib/rspec_api_documentation/open_api/node.rb
+++ b/lib/rspec_api_documentation/open_api/node.rb
@@ -102,9 +102,10 @@ def as_json
end
end
+ def settings; @settings ||= {} end
+
private
- def settings; @settings ||= {} end
def instance_settings; @instance_settings ||= [] end
def self.class_settings; @class_settings ||= [] end
end
diff --git a/rspec_api_documentation.gemspec b/rspec_api_documentation.gemspec
index 978b3bf0..b96fcdf7 100644
--- a/rspec_api_documentation.gemspec
+++ b/rspec_api_documentation.gemspec
@@ -15,28 +15,52 @@ Gem::Specification.new do |s|
s.required_rubygems_version = ">= 1.3.6"
s.add_runtime_dependency "rspec", "~> 3.0"
+ s.add_development_dependency "rspec", "~> 3.0"
s.add_runtime_dependency "activesupport", ">= 3.0.0"
s.add_runtime_dependency "mustache", "~> 1.0", ">= 0.99.4"
- s.add_development_dependency "bundler", ">= 1.16"
- s.add_development_dependency "fakefs", "~> 0.6.0"
- s.add_development_dependency "sinatra", "~> 2.0.8"
- s.add_development_dependency "aruba", "~> 0.14.14"
- s.add_development_dependency "capybara", "~> 3.39.2"
- s.add_development_dependency "rake", "~> 13.2.1"
- s.add_development_dependency "rack-test", "~> 0.6.3"
- s.add_development_dependency "rack-oauth2", "~> 1.12.0"
- s.add_development_dependency "webmock", "~> 3.23.0"
- s.add_development_dependency "rspec-its", "~> 1.3.0"
- s.add_development_dependency "faraday", "~> 1.0.0"
- s.add_development_dependency "nokogiri", "~> 1.8.4"
- s.add_development_dependency "yard", "~> 0.9.15"
- s.add_development_dependency "inch", "~> 0.8.0"
- s.add_development_dependency "minitest", "~> 5.8.4"
- s.add_development_dependency "contracts", "~> 0.17"
- s.add_development_dependency "gherkin", "~> 9.0.0"
- s.add_development_dependency "multi_json", "~> 1.15.0"
- s.add_development_dependency "rspec", "~> 3.0"
+ if RUBY_VERSION < '2.7'
+ s.add_development_dependency "bundler", ">= 1.16"
+ s.add_development_dependency "fakefs", "~> 0.6.0"
+ s.add_development_dependency "sinatra", "~> 1.4.7"
+ s.add_development_dependency "aruba", "~> 0.13.0"
+ s.add_development_dependency "capybara", "~> 2.6.2"
+ s.add_development_dependency "rake", "~> 10.5.0"
+ s.add_development_dependency "rack-test", "~> 0.6.3"
+ s.add_development_dependency "rack-oauth2", "~> 1.2.2"
+ s.add_development_dependency "webmock", "~> 3.8.3"
+ s.add_development_dependency "rspec-its", "~> 1.2.0"
+ s.add_development_dependency "faraday", "~> 1.0.0"
+ s.add_development_dependency "nokogiri", "~> 1.8.4"
+ s.add_development_dependency "yard", "~> 0.9.15"
+ s.add_development_dependency "inch", "~> 0.8.0"
+ s.add_development_dependency "minitest", "~> 5.8.4"
+ s.add_development_dependency "contracts", "~> 0.13.0"
+ s.add_development_dependency "gherkin", "~> 3.2.0"
+ s.add_development_dependency "multi_json", "~> 1.11.2"
+ else
+ s.add_development_dependency "bundler", ">= 1.16"
+ s.add_development_dependency "fakefs"
+ s.add_development_dependency "sinatra", "~> 2.0"
+ s.add_development_dependency "aruba"
+ s.add_development_dependency "capybara"
+ s.add_development_dependency "rake"
+ s.add_development_dependency "rack", "~> 2.2"
+ s.add_development_dependency "rack-test"
+ s.add_development_dependency "rack-oauth2"
+ s.add_development_dependency "webmock"
+ s.add_development_dependency "rspec-its"
+ s.add_development_dependency "faraday"
+ s.add_development_dependency "nokogiri"
+ s.add_development_dependency "yard"
+ s.add_development_dependency "inch"
+ s.add_development_dependency "minitest"
+ s.add_development_dependency "contracts"
+ s.add_development_dependency "gherkin"
+ s.add_development_dependency "multi_json"
+ s.add_development_dependency "webrick"
+ s.add_development_dependency "rackup"
+ end
s.files = Dir.glob("lib/**/*") + Dir.glob("templates/**/*")
s.require_path = "lib"
diff --git a/spec/api_formatter_spec.rb b/spec/api_formatter_spec.rb
index 266cad8d..69d0ef18 100644
--- a/spec/api_formatter_spec.rb
+++ b/spec/api_formatter_spec.rb
@@ -2,7 +2,18 @@
describe RspecApiDocumentation::ApiFormatter do
let(:metadata) { {} }
- let(:group) { RSpec::Core::ExampleGroup.describe("Orders", metadata) }
+ let(:group) {
+ # Create an anonymous class that inherits from ExampleGroup but doesn't auto-register
+ Class.new(RSpec::Core::ExampleGroup) do
+ def self.description
+ "Orders"
+ end
+
+ def self.metadata
+ {}
+ end
+ end
+ }
let(:output) { StringIO.new }
let(:formatter) { RspecApiDocumentation::ApiFormatter.new(output) }
diff --git a/spec/example_spec.rb b/spec/example_spec.rb
index 1aa94610..3d78b885 100644
--- a/spec/example_spec.rb
+++ b/spec/example_spec.rb
@@ -64,7 +64,7 @@
end
context "when the example is pending" do
- let(:rspec_example) { rspec_example_group.pending(description, metadata) {} }
+ let(:rspec_example) { rspec_example_group.pending(description, metadata) { raise "Pending example" } }
it { should be_falsey }
end
diff --git a/spec/http_test_client_spec.rb b/spec/http_test_client_spec.rb
index fd77dc0f..a420cfbf 100644
--- a/spec/http_test_client_spec.rb
+++ b/spec/http_test_client_spec.rb
@@ -2,14 +2,14 @@
require 'rack/test'
require 'capybara'
require 'capybara/server'
-require 'sinatra/base'
require 'webmock/rspec'
require 'support/stub_app'
describe RspecApiDocumentation::HttpTestClient do
before(:all) do
WebMock.allow_net_connect!
- server = Capybara::Server.new(StubApp.new, 8888)
+ Capybara.server = :webrick
+ server = Capybara::Server.new(StubApp.new, port: 8888)
server.boot
end
diff --git a/spec/rack_test_client_spec.rb b/spec/rack_test_client_spec.rb
index e3a9b53c..f1b57b84 100644
--- a/spec/rack_test_client_spec.rb
+++ b/spec/rack_test_client_spec.rb
@@ -1,10 +1,9 @@
require 'spec_helper'
require 'rack/test'
-require 'sinatra/base'
require 'support/stub_app'
describe RspecApiDocumentation::RackTestClient do
- let(:context) { |example| double(:app => StubApp, :example => example) }
+ let(:context) { |example| double(:app => StubApp.new, :example => example) }
let(:test_client) { RspecApiDocumentation::RackTestClient.new(context, {}) }
subject { test_client }
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 918dd620..95fd852d 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -4,4 +4,16 @@
require 'pry'
RSpec.configure do |config|
+ config.before(:all) do
+ if self.class.metadata[:api_doc_dsl] || self.respond_to?(:app)
+ begin
+ require 'support/stub_app'
+ RspecApiDocumentation.configure do |config|
+ config.app = StubApp.new unless config.app
+ end
+ rescue LoadError
+ # StubApp not available, skip
+ end
+ end
+ end
end
diff --git a/spec/support/stub_app.rb b/spec/support/stub_app.rb
index 35226be2..f00787c0 100644
--- a/spec/support/stub_app.rb
+++ b/spec/support/stub_app.rb
@@ -1,31 +1,29 @@
-class StubApp < Sinatra::Base
- get "/" do
- content_type :json
-
- { :hello => "world" }.to_json
- end
-
- post "/greet" do
- content_type :json
-
- request.body.rewind
- begin
- data = JSON.parse request.body.read
- rescue JSON::ParserError
- request.body.rewind
- data = request.body.read
+class StubApp
+ def call(env)
+ req = Rack::Request.new(env)
+
+ case "#{req.request_method} #{req.path_info}"
+ when "GET /"
+ [200, {'Content-Type' => 'application/json'}, [{ :hello => "world" }.to_json]]
+ when "POST /greet"
+ body = req.body.read
+ req.body.rewind if req.body.respond_to?(:rewind)
+
+ begin
+ data = JSON.parse(body) if body && !body.empty?
+ rescue JSON::ParserError
+ data = nil
+ end
+
+ target = data.is_a?(Hash) ? data["target"] : "nurse"
+ [200, {'Content-Type' => 'application/json', 'Content-Length' => '17'}, [{ :hello => target }.to_json]]
+ when "GET /xml"
+ [200, {'Content-Type' => 'application/xml'}, ["World"]]
+ when "GET /binary"
+ [200, {'Content-Type' => 'application/octet-stream'}, ["\x01\x02\x03".force_encoding(Encoding::ASCII_8BIT)]]
+ else
+ [404, {'Content-Type' => 'text/plain'}, ["Not Found"]]
end
- { :hello => data["target"] }.to_json
- end
-
- get "/xml" do
- content_type :xml
-
- "World"
- end
-
- get '/binary' do
- content_type 'application/octet-stream'
- "\x01\x02\x03".force_encoding(Encoding::ASCII_8BIT)
end
end
+