Skip to content

Commit

Permalink
Refactor use_cassette middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
MUTOgen committed Jul 7, 2024
1 parent 111630a commit 308eddd
Show file tree
Hide file tree
Showing 12 changed files with 330 additions and 253 deletions.
9 changes: 5 additions & 4 deletions lib/cypress_on_rails/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ class Configuration
attr_accessor :install_folder
attr_accessor :use_middleware
attr_accessor :use_vcr_middleware
attr_accessor :use_vcr_use_cassette_middleware
attr_accessor :before_request
attr_accessor :logger
attr_accessor :use_vcr
attr_accessor :vcr_record_mode
attr_accessor :vcr_use_cassette_mode

# Attributes for backwards compatibility
def cypress_folder
Expand All @@ -27,16 +27,17 @@ def initialize

alias :use_middleware? :use_middleware
alias :use_vcr_middleware? :use_vcr_middleware
alias :use_vcr_use_cassette_middleware? :use_vcr_use_cassette_middleware

def reset
self.api_prefix = ''
self.install_folder = 'spec/e2e'
self.use_middleware = true
self.use_vcr_middleware = false
self.use_vcr_use_cassette_middleware = false
self.before_request = -> (request) {}
self.logger = Logger.new(STDOUT)
self.use_vcr = false
self.vcr_record_mode = :new_episodes
self.vcr_use_cassette_mode = :new_episodes
end

def tagged_logged
Expand Down
3 changes: 0 additions & 3 deletions lib/cypress_on_rails/middleware.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
require 'rack'
require 'cypress_on_rails/middleware_config'
require 'cypress_on_rails/command_executor'
require 'cypress_on_rails/vcr_wrapper'

module CypressOnRails
# Middleware to handle testing framework commands and eval
Expand All @@ -22,8 +21,6 @@ def call(env)
elsif request.path.start_with?("#{configuration.api_prefix}/__cypress__/command")
warn "/__cypress__/command is deprecated. Please use the install generator to use /__e2e__/command instead."
configuration.tagged_logged { handle_command(request) }
elsif defined?(VCR) && configuration.use_vcr
VCRWrapper.new(app: @app, env: env).run_with_cassette
else
@app.call(env)
end
Expand Down
8 changes: 6 additions & 2 deletions lib/cypress_on_rails/railtie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ class Railtie < Rails::Railtie
app.middleware.use Middleware
end
if CypressOnRails.configuration.use_vcr_middleware?
require 'cypress_on_rails/vcr_middleware'
app.middleware.use VCRMiddleware
require 'cypress_on_rails/vcr/insert_inject_middleware'
app.middleware.use Vcr::InsertEjectMiddleware
end
if CypressOnRails.configuration.use_vcr_use_cassette_middleware?
require 'cypress_on_rails/vcr/use_cassette_middleware'
app.middleware.use Vcr::UseCassetteMiddleware
end
end
end
Expand Down
39 changes: 39 additions & 0 deletions lib/cypress_on_rails/vcr/base_middleware.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
require 'json'
require 'rack'
require 'cypress_on_rails/middleware_config'

module CypressOnRails
module Vcr
# Base abstract Middleware
class BaseMiddleware
include MiddlewareConfig

def initialize(_app, _vcr = nil)
raise_not_implemented
end

def call(_env)
raise_not_implemented
end

def vcr
@vcr ||= configure_vcr
end

private

def configure_vcr
require 'vcr'
VCR.configure do |config|
config.cassette_library_dir = "#{configuration.install_folder}/fixtures/vcr_cassettes"
end
VCR
end

def raise_not_implemented
raise NotImplementedError,
'BaseMiddleware can not be initialized directly, use InsertEjectMiddleware or UseCassetteMiddleware'
end
end
end
end
71 changes: 71 additions & 0 deletions lib/cypress_on_rails/vcr/insert_eject_middleware.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
require_relative 'base_middleware'

module CypressOnRails
module Vcr
# Middleware to handle vcr with insert/eject endpoints
class InsertEjectMiddleware < BaseMiddleware
def initialize(app, vcr = nil)
@app = app
@vcr = vcr
@first_call = false
end

def call(env)
request = Rack::Request.new(env)
if request.path.start_with?('/__e2e__/vcr/insert')
configuration.tagged_logged { handle_insert(request) }
elsif request.path.start_with?('/__e2e__/vcr/eject')
configuration.tagged_logged { handle_eject }
else
do_first_call unless @first_call
@app.call(env)
end
end

private

def handle_insert(req)
WebMock.enable! if defined?(WebMock)
vcr.turn_on!
logger.info "vcr insert cassette: #{body}"
body = parse_request_body(req)
cassette_name, options = extract_cassette_info(body)
vcr.insert_cassette(cassette_name, options)
[201, { 'Content-Type' => 'application/json' }, [{ 'message': 'OK' }.to_json]]
rescue LoadError, ArgumentError => e
[501, { 'Content-Type' => 'application/json' }, [{ 'message': e.message }.to_json]]
end

def parse_request_body(req)
JSON.parse(req.body.read)
end

def extract_cassette_info(body)
cassette_name = body[0]
options = (body[1] || {}).symbolize_keys
options[:record] = options[:record].to_sym if options[:record]
options[:match_requests_on] = options[:match_requests_on].map(&:to_sym) if options[:match_requests_on]
options[:serialize_with] = options[:serialize_with].to_sym if options[:serialize_with]
options[:persist_with] = options[:persist_with].to_sym if options[:persist_with]
[cassette_name, options]
end

def handle_eject
logger.info 'vcr eject cassette'
vcr.eject_cassette
do_first_call
[201, { 'Content-Type' => 'application/json' }, [{ 'message': 'OK' }.to_json]]
rescue LoadError, ArgumentError => e
[501, { 'Content-Type' => 'application/json' }, [{ 'message': e.message }.to_json]]
end

def do_first_call
@first_call = true
vcr.turn_off!
WebMock.disable! if defined?(WebMock)
rescue LoadError
# nop
end
end
end
end
41 changes: 41 additions & 0 deletions lib/cypress_on_rails/vcr/use_cassette_middleware.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
require 'cypress_on_rails/configuration'
require_relative 'base_middleware'

module CypressOnRails
module Vcr
# Middleware to handle vcr with use_cassette
class UseCassetteMiddleware < BaseMiddleware
def initialize(app, vcr = nil)
@app = app
@vcr = vcr
end

def call(env)
request = Rack::Request.new(env)
cassette_name = fetch_request_cassette(request)
vcr.use_cassette(cassette_name, { record: configuration.vcr_use_cassette_mode }) do
logger.info "Handle request with cassette name: #{cassette_name}"
@app.call(env)
end
end

private

def configuration
CypressOnRails.configuration
end

def logger
configuration.logger
end

def fetch_request_cassette(request)
if request.path.start_with?('/graphql') && request.params.key?('operation')
"#{request.path}/#{request.params['operation']}"
else
request.path
end
end
end
end
end
73 changes: 0 additions & 73 deletions lib/cypress_on_rails/vcr_middleware.rb

This file was deleted.

38 changes: 0 additions & 38 deletions lib/cypress_on_rails/vcr_wrapper.rb

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,10 @@ if defined?(CypressOnRails)
# please use with extra caution if enabling on hosted servers or starting your local server on 0.0.0.0
c.use_middleware = !Rails.env.production?
<% unless options.experimental %># <% end %> c.use_vcr_middleware = !Rails.env.production?
# Use this if you want to use use_cassette wrapper instead of manual insert/eject
# c.use_vcr_use_cassette_middleware = !Rails.env.production?
# c.vcr_use_cassette_mode = :once # Use to choose VCR record mode (:new_episodes by default)
c.logger = Rails.logger
c.use_vcr = ENV['WITH_VCR'].present?

# # Setup VCR to mock external HTTP requests
# if ENV['WITH_VCR'].present?
# # c.vcr_record_mode = :once # Use to choose VCR record mode (:new_episodes by default)
#
# require 'vcr'
# VCR.configure do |config|
# config.cassette_library_dir = Rails.root.join('spec', 'cypress', 'fixtures', 'cassettes') # Update cassettes path as nedded
# config.hook_into :webmock
# config.ignore_localhost = true
# config.ignore_hosts('localhost', '127.0.0.1', '0.0.0.0')
# end
# end

# If you want to enable a before_request logic, such as authentication, logging, sending metrics, etc.
# Refer to https://www.rubydoc.info/gems/rack/Rack/Request for the `request` argument.
Expand Down
Loading

0 comments on commit 308eddd

Please sign in to comment.