diff --git a/README.md b/README.md index 3c9bf8c..c586e3c 100644 --- a/README.md +++ b/README.md @@ -355,8 +355,11 @@ yarn add cypress-on-rails --dev ### for VCR This only works when you start the Rails server with a single worker and single thread +It can be used in two modes: +- with separate insert/eject calls (more general, recommended way) +- with use_cassette wrapper (supports only GraphQL integration) -#### setup +#### basic setup Add your VCR configuration to your `cypress_helper.rb` @@ -381,13 +384,16 @@ VCR.turn_off! WebMock.disable! if defined?(WebMock) ``` +#### insert/eject setup + Add to your `config/cypress_on_rails.rb`: ```ruby c.use_vcr_middleware = !Rails.env.production? && ENV['CYPRESS'].present? + # c.use_vcr_use_cassette_middleware = !Rails.env.production? && ENV['CYPRESS'].present? ``` -#### usage +#### insert/eject usage You have `vcr_insert_cassette` and `vcr_eject_cassette` available. https://www.rubydoc.info/github/vcr/vcr/VCR:insert_cassette @@ -414,6 +420,60 @@ describe('My First Test', () => { }) ``` +#### use_cassette setup + +Add to your `config/cypress_on_rails.rb`: + +```ruby + # c.use_vcr_middleware = !Rails.env.production? && ENV['CYPRESS'].present? + c.use_vcr_use_cassette_middleware = !Rails.env.production? && ENV['CYPRESS'].present? +``` + +Adjust record mode in `config/cypress_on_rails.rb` if needed: + +```ruby + c.vcr_record_mode = :once # Use to choose VCR record mode +``` + +Add to your `cypress/support/command.js`: + +```js + // Add proxy-like mock to add operation name into query string + Cypress.Commands.add('mockGraphQL', () => { + cy.on('window:before:load', (win) => { + const originalFetch = win.fetch; + const fetch = (path, options, ...rest) => { + if (options && options.body) { + try { + const body = JSON.parse(options.body); + if (body.operationName) { + return originalFetch(`${path}?operation=${body.operationName}`, options, ...rest); + } + } catch (e) { + return originalFetch(path, options, ...rest); + } + } + return originalFetch(path, options, ...rest); + }; + cy.stub(win, 'fetch', fetch); + }); + }); +``` + +Add to your `cypress/support/on-rails.js`, to `beforeEach`: + +```js + cy.mockGraphQL() // for GraphQL usage with use_cassette, see cypress/support/commands.rb +``` + +#### use_cassette usage + +There is nothing special to be called during the Cypress scenario. Each request is wrapped with `VCR.use_cassette`. +Consider VCR configuration in `cypress_helper.rb` to ignore hosts. + +All cassettes will be recorded and saved automatically, using the pattern `/graphql/` + + ## `before_request` configuration You may perform any custom action before running a CypressOnRails command, such as authentication, or sending metrics. Please set `before_request` as part of the CypressOnRails configuration. diff --git a/lib/cypress_on_rails/configuration.rb b/lib/cypress_on_rails/configuration.rb index b7d32e1..c7f42b4 100644 --- a/lib/cypress_on_rails/configuration.rb +++ b/lib/cypress_on_rails/configuration.rb @@ -6,8 +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 :vcr_record_mode # Attributes for backwards compatibility def cypress_folder @@ -25,14 +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.vcr_record_mode = :new_episodes end def tagged_logged diff --git a/lib/cypress_on_rails/railtie.rb b/lib/cypress_on_rails/railtie.rb index de6a582..924a81d 100644 --- a/lib/cypress_on_rails/railtie.rb +++ b/lib/cypress_on_rails/railtie.rb @@ -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_eject_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 diff --git a/lib/cypress_on_rails/vcr/insert_eject_middleware.rb b/lib/cypress_on_rails/vcr/insert_eject_middleware.rb new file mode 100644 index 0000000..c67520b --- /dev/null +++ b/lib/cypress_on_rails/vcr/insert_eject_middleware.rb @@ -0,0 +1,75 @@ +require_relative 'middleware_helpers' + +module CypressOnRails + module Vcr + # Middleware to handle vcr with insert/eject endpoints + class InsertEjectMiddleware + include MiddlewareHelpers + + 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! + body = parse_request_body(req) + logger.info "vcr insert cassette: #{body}" + cassette_name, options = extract_cassette_info(body) + vcr.insert_cassette(cassette_name, options) + [201, { 'Content-Type' => 'application/json' }, [{ 'message': 'OK' }.to_json]] + rescue JSON::ParserError => e + [400, { 'Content-Type' => 'application/json' }, [{ 'message': e.message }.to_json]] + rescue LoadError, ArgumentError => e + [500, { '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 + [500, { '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 diff --git a/lib/cypress_on_rails/vcr/middleware_helpers.rb b/lib/cypress_on_rails/vcr/middleware_helpers.rb new file mode 100644 index 0000000..2887c31 --- /dev/null +++ b/lib/cypress_on_rails/vcr/middleware_helpers.rb @@ -0,0 +1,28 @@ +require 'cypress_on_rails/middleware_config' + +module CypressOnRails + module Vcr + # Provides helper methods for VCR middlewares + module MiddlewareHelpers + include MiddlewareConfig + + def vcr + @vcr ||= configure_vcr + end + + def cassette_library_dir + "#{configuration.install_folder}/fixtures/vcr_cassettes" + end + + private + + def configure_vcr + require 'vcr' + VCR.configure do |config| + config.cassette_library_dir = cassette_library_dir + end + VCR + end + end + end +end diff --git a/lib/cypress_on_rails/vcr/use_cassette_middleware.rb b/lib/cypress_on_rails/vcr/use_cassette_middleware.rb new file mode 100644 index 0000000..0ec4e18 --- /dev/null +++ b/lib/cypress_on_rails/vcr/use_cassette_middleware.rb @@ -0,0 +1,56 @@ +require_relative 'middleware_helpers' + +module CypressOnRails + module Vcr + # Middleware to handle vcr with use_cassette + class UseCassetteMiddleware + include MiddlewareHelpers + + def initialize(app, vcr = nil) + @app = app + @vcr = vcr + end + + def call(env) + return @app.call(env) if should_not_use_vcr? + + # initialize_vcr + handle_request_with_vcr(env) + end + + private + + def vcr_defined? + defined?(VCR) != nil + end + + def should_not_use_vcr? + vcr_defined? && + VCR.configuration.cassette_library_dir.present? && + VCR.configuration.cassette_library_dir != cassette_library_dir + end + + def initialize_vcr + WebMock.enable! if defined?(WebMock) + vcr.turn_on! + end + + def handle_request_with_vcr(env) + request = Rack::Request.new(env) + cassette_name = fetch_request_cassette(request) + vcr.use_cassette(cassette_name, { record: configuration.vcr_record_mode }) do + logger.info "Handle request with cassette name: #{cassette_name}" + @app.call(env) + end + 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 diff --git a/lib/cypress_on_rails/vcr_middleware.rb b/lib/cypress_on_rails/vcr_middleware.rb deleted file mode 100644 index e675dbc..0000000 --- a/lib/cypress_on_rails/vcr_middleware.rb +++ /dev/null @@ -1,73 +0,0 @@ -require 'json' -require 'rack' -require 'cypress_on_rails/middleware_config' - -module CypressOnRails - # Middleware to handle vcr - class VCRMiddleware - include MiddlewareConfig - - 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! - body = JSON.parse(req.body.read) - logger.info "vcr insert cassette: #{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] - 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 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 vcr - return @vcr if @vcr - require 'vcr' - VCR.configure do |config| - config.cassette_library_dir = "#{configuration.install_folder}/fixtures/vcr_cassettes" - end - @vcr = VCR - end - - def do_first_call - @first_call = true - vcr.turn_off! - WebMock.disable! if defined?(WebMock) - rescue LoadError - # nop - end - end -end diff --git a/lib/generators/cypress_on_rails/templates/config/initializers/cypress_on_rails.rb.erb b/lib/generators/cypress_on_rails/templates/config/initializers/cypress_on_rails.rb.erb index 77bd389..2ec6534 100644 --- a/lib/generators/cypress_on_rails/templates/config/initializers/cypress_on_rails.rb.erb +++ b/lib/generators/cypress_on_rails/templates/config/initializers/cypress_on_rails.rb.erb @@ -6,6 +6,9 @@ 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_record_mode = :once # Use to choose VCR record mode c.logger = Rails.logger # If you want to enable a before_request logic, such as authentication, logging, sending metrics, etc. diff --git a/lib/generators/cypress_on_rails/templates/spec/cypress/support/commands.js b/lib/generators/cypress_on_rails/templates/spec/cypress/support/commands.js index c1f5a77..40caa52 100644 --- a/lib/generators/cypress_on_rails/templates/spec/cypress/support/commands.js +++ b/lib/generators/cypress_on_rails/templates/spec/cypress/support/commands.js @@ -23,3 +23,25 @@ // // -- This is will overwrite an existing command -- // Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) +// +// +// -- This is for Graphql usage. Add proxy-like mock to add operation name into query string -- +// Cypress.Commands.add('mockGraphQL', () => { +// cy.on('window:before:load', (win) => { +// const originalFetch = win.fetch; +// const fetch = (path, options, ...rest) => { +// if (options && options.body) { +// try { +// const body = JSON.parse(options.body); +// if (body.operationName) { +// return originalFetch(`${path}?operation=${body.operationName}`, options, ...rest); +// } +// } catch (e) { +// return originalFetch(path, options, ...rest); +// } +// } +// return originalFetch(path, options, ...rest); +// }; +// cy.stub(win, 'fetch', fetch); +// }); +// }); diff --git a/lib/generators/cypress_on_rails/templates/spec/cypress/support/on-rails.js b/lib/generators/cypress_on_rails/templates/spec/cypress/support/on-rails.js index 79e8eca..a3bbaef 100644 --- a/lib/generators/cypress_on_rails/templates/spec/cypress/support/on-rails.js +++ b/lib/generators/cypress_on_rails/templates/spec/cypress/support/on-rails.js @@ -47,6 +47,7 @@ Cypress.Commands.add('appFixtures', function (options) { // The next is optional // beforeEach(() => { // cy.app('clean') // have a look at cypress/app_commands/clean.rb +// cy.mockGraphQL() // for GraphQL usage with use_cassette, see cypress/support/commands.rb // }); // comment this out if you do not want to attempt to log additional info on test fail diff --git a/spec/cypress_on_rails/vcr/insert_eject_middleware_spec.rb b/spec/cypress_on_rails/vcr/insert_eject_middleware_spec.rb new file mode 100644 index 0000000..748f306 --- /dev/null +++ b/spec/cypress_on_rails/vcr/insert_eject_middleware_spec.rb @@ -0,0 +1,177 @@ +require 'cypress_on_rails/vcr/insert_eject_middleware' +require 'vcr' +require 'active_support/core_ext/hash' unless {}.respond_to?(:symbolize_keys) + +module CypressOnRails + module Vcr + RSpec.describe InsertEjectMiddleware do + let(:app) { ->(env) { [200, {}, ["app did #{env['PATH_INFO']}"]] } } + let(:vcr) { class_double(VCR, turn_on!: true, turn_off!: true, insert_cassette: true, eject_cassette: true) } + subject { described_class.new(app, vcr) } + + let(:env) { {} } + + let(:response) { subject.call(env) } + + def rack_input(json_value) + StringIO.new(JSON.generate(json_value)) + end + + describe '/__e2e__/vcr/insert' do + before do + env['PATH_INFO'] = '/__e2e__/vcr/insert' + end + + it do + env['rack.input'] = rack_input(['cas1']) + + aggregate_failures do + expect(response).to eq([201, + { 'Content-Type' => 'application/json' }, + ['{"message":"OK"}']]) + expect(vcr).to have_received(:turn_on!) + expect(vcr).to have_received(:insert_cassette).with('cas1', {}) + end + end + + it 'works with record' do + env['rack.input'] = rack_input(['cas1', { 'record' => 'new_episodes' }]) + + aggregate_failures do + expect(response).to eq([201, + { 'Content-Type' => 'application/json' }, + ['{"message":"OK"}']]) + expect(vcr).to have_received(:insert_cassette).with('cas1', record: :new_episodes) + end + end + + it 'works with match_requests_on' do + env['rack.input'] = rack_input(['cas1', { 'match_requests_on' => %w[method uri] }]) + + aggregate_failures do + expect(response).to eq([201, + { 'Content-Type' => 'application/json' }, + ['{"message":"OK"}']]) + expect(vcr).to have_received(:insert_cassette).with('cas1', match_requests_on: %i[method uri]) + end + end + + it 'works with serialize_with' do + env['rack.input'] = rack_input(['cas1', { 'serialize_with' => 'yaml' }]) + + aggregate_failures do + expect(response).to eq([201, + { 'Content-Type' => 'application/json' }, + ['{"message":"OK"}']]) + expect(vcr).to have_received(:insert_cassette).with('cas1', serialize_with: :yaml) + end + end + + it 'works with persist_with' do + env['rack.input'] = rack_input(['cas1', { 'persist_with' => 'file_system' }]) + + aggregate_failures do + expect(response).to eq([201, + { 'Content-Type' => 'application/json' }, + ['{"message":"OK"}']]) + expect(vcr).to have_received(:insert_cassette).with('cas1', persist_with: :file_system) + end + end + + context 'when an error occurs' do + it 'returns a 500 error with the error message' do + env['rack.input'] = rack_input(['cas1']) + allow(vcr).to receive(:insert_cassette).and_raise(ArgumentError.new('Invalid cassette name')) + + expect(response).to eq([ + 500, + { 'Content-Type' => 'application/json' }, + ['{"message":"Invalid cassette name"}'] + ]) + end + + it 'returns a 500 error when LoadError occurs' do + env['rack.input'] = rack_input(['cas1']) + allow(vcr).to receive(:insert_cassette).and_raise(LoadError.new('Cannot load VCR')) + + expect(response).to eq([ + 500, + { 'Content-Type' => 'application/json' }, + ['{"message":"Cannot load VCR"}'] + ]) + end + end + + it 'returns a 400 error when JSON parsing fails' do + env['rack.input'] = StringIO.new('invalid json') + + expect(response).to eq([ + 400, + { 'Content-Type' => 'application/json' }, + ['{"message":"unexpected token at \'invalid json\'"}'] + ]) + end + end + + describe '/__e2e__/vcr/eject' do + before do + env['PATH_INFO'] = '/__e2e__/vcr/eject' + end + + it do + aggregate_failures do + expect(response).to eq([201, + { 'Content-Type' => 'application/json' }, + ['{"message":"OK"}']]) + expect(vcr).to have_received(:turn_off!) + expect(vcr).to have_received(:eject_cassette) + end + end + + context 'when an error occurs' do + it 'returns a 500 error with the error message' do + allow(vcr).to receive(:eject_cassette).and_raise(ArgumentError.new('No cassette to eject')) + + expect(response).to eq([ + 500, + { 'Content-Type' => 'application/json' }, + ['{"message":"No cassette to eject"}'] + ]) + end + + it 'returns a 500 error when LoadError occurs' do + allow(vcr).to receive(:eject_cassette).and_raise(LoadError.new('Cannot load VCR')) + + expect(response).to eq([ + 500, + { 'Content-Type' => 'application/json' }, + ['{"message":"Cannot load VCR"}'] + ]) + end + end + end + + describe '"Other paths"' do + it 'calls vcr turn off the first time' do + env['PATH_INFO'] = '/test' + + expect(response).to eq([200, {}, ['app did /test']]) + expect(vcr).to have_received(:turn_off!) + end + + it 'runs app' do + aggregate_failures do + %w[/ /__e2e__/login command /e2e_command /].each do |path| + env['PATH_INFO'] = path + + response = subject.call(env) + + expect(response).to eq([200, {}, ["app did #{path}"]]) + expect(vcr).to have_received(:turn_off!) + end + end + end + end + end + end +end diff --git a/spec/cypress_on_rails/vcr/use_cassette_middleware_spec.rb b/spec/cypress_on_rails/vcr/use_cassette_middleware_spec.rb new file mode 100644 index 0000000..e18c8f9 --- /dev/null +++ b/spec/cypress_on_rails/vcr/use_cassette_middleware_spec.rb @@ -0,0 +1,72 @@ +require 'cypress_on_rails/vcr/use_cassette_middleware' +require 'vcr' +require 'active_support/core_ext/hash' unless {}.respond_to?(:symbolize_keys) + +module CypressOnRails + module Vcr + RSpec.describe UseCassetteMiddleware do + let(:app) { ->(env) { [200, {}, ["app did #{env['PATH_INFO']}"]] } } + let(:vcr) { VCR } + subject { described_class.new(app, vcr) } + + let(:env) { { 'rack.input' => rack_input([]) } } + + let(:response) { subject.call(env) } + + def rack_input(json_value) + StringIO.new(JSON.generate(json_value)) + end + + before do + allow(vcr).to receive(:use_cassette).and_yield + end + + it 'returns the application response using correct graphql cassette' do + env['PATH_INFO'] = '/graphql' + env['QUERY_STRING'] = 'operation=test' + + expect(response).to eq([200, {}, ['app did /graphql']]) + expect(vcr).to have_received(:use_cassette) + .with('/graphql/test', hash_including(record: :new_episodes)) + end + + it 'returns the application response using default request path cassette' do + allow(CypressOnRails).to receive(:configuration).and_return(double(vcr_record_mode: :once, + logger: Logger.new(nil))) + env['PATH_INFO'] = '/test/path' + + expect(response).to eq([200, {}, ['app did /test/path']]) + expect(vcr).to have_received(:use_cassette) + .with('/test/path', hash_including(record: :once)) + end + + context 'when VCR cassette library directory does not match' do + before do + allow(VCR.configuration).to receive(:cassette_library_dir).and_return('/different/path') + end + + it 'returns the application response without using VCR' do + env['PATH_INFO'] = '/test/path' + + expect(response).to eq([200, {}, ['app did /test/path']]) + expect(vcr).not_to have_received(:use_cassette) + end + end + + context 'when VCR is not defined' do + before do + allow(subject).to receive(:vcr_defined?).and_return(false) + end + + it 'returns the application response without error' do + env['PATH_INFO'] = '/graphql' + env['QUERY_STRING'] = 'operation=test' + + expect(response).to eq([200, {}, ['app did /graphql']]) + expect(vcr).to have_received(:use_cassette) + .with('/graphql/test', hash_including(record: :new_episodes)) + end + end + end + end +end diff --git a/spec/cypress_on_rails/vcr_middleware_spec.rb b/spec/cypress_on_rails/vcr_middleware_spec.rb deleted file mode 100644 index fe13616..0000000 --- a/spec/cypress_on_rails/vcr_middleware_spec.rb +++ /dev/null @@ -1,119 +0,0 @@ -require 'cypress_on_rails/vcr_middleware' -require 'vcr' -require 'active_support/core_ext/hash' unless Hash.new.respond_to?(:symbolize_keys) - -module CypressOnRails - RSpec.describe VCRMiddleware do - let(:app) { ->(env) { [200, {}, ["app did #{env['PATH_INFO']}"]] } } - let(:vcr) { class_double(VCR, turn_on!: true, turn_off!: true, insert_cassette: true, eject_cassette: true) } - subject { described_class.new(app, vcr) } - - let(:env) { {} } - - let(:response) { subject.call(env) } - - def rack_input(json_value) - StringIO.new(JSON.generate(json_value)) - end - - describe '/__e2e__/vcr/insert' do - before do - env['PATH_INFO'] = '/__e2e__/vcr/insert' - end - - it do - env['rack.input'] = rack_input(['cas1']) - - aggregate_failures do - expect(response).to eq([201, - {"Content-Type"=>"application/json"}, - ["{\"message\":\"OK\"}"]]) - expect(vcr).to have_received(:turn_on!) - expect(vcr).to have_received(:insert_cassette).with('cas1', {}) - end - end - - it 'works with record' do - env['rack.input'] = rack_input(['cas1', { "record" => "new_episodes" }]) - - aggregate_failures do - expect(response).to eq([201, - {"Content-Type"=>"application/json"}, - ["{\"message\":\"OK\"}"]]) - expect(vcr).to have_received(:insert_cassette).with('cas1', record: :new_episodes) - end - end - - it 'works with match_requests_on' do - env['rack.input'] = rack_input(['cas1', { "match_requests_on" => ["method", "uri"] }]) - - aggregate_failures do - expect(response).to eq([201, - {"Content-Type"=>"application/json"}, - ["{\"message\":\"OK\"}"]]) - expect(vcr).to have_received(:insert_cassette).with('cas1', match_requests_on: [:method, :uri]) - end - end - - it 'works with serialize_with' do - env['rack.input'] = rack_input(['cas1', { "serialize_with" => "yaml" }]) - - aggregate_failures do - expect(response).to eq([201, - {"Content-Type"=>"application/json"}, - ["{\"message\":\"OK\"}"]]) - expect(vcr).to have_received(:insert_cassette).with('cas1', serialize_with: :yaml) - end - end - - it 'works with persist_with' do - env['rack.input'] = rack_input(['cas1', { "persist_with" => "file_system" }]) - - aggregate_failures do - expect(response).to eq([201, - {"Content-Type"=>"application/json"}, - ["{\"message\":\"OK\"}"]]) - expect(vcr).to have_received(:insert_cassette).with('cas1', persist_with: :file_system) - end - end - end - - describe '/__e2e__/vcr/eject' do - before do - env['PATH_INFO'] = '/__e2e__/vcr/eject' - end - - it do - aggregate_failures do - expect(response).to eq([201, - {"Content-Type"=>"application/json"}, - ["{\"message\":\"OK\"}"]]) - expect(vcr).to have_received(:turn_off!) - expect(vcr).to have_received(:eject_cassette) - end - end - end - - describe '"Other paths"' do - it 'calls vcr turn off the first time' do - env['PATH_INFO'] = '/test' - - expect(response).to eq([200, {}, ["app did /test"]]) - expect(vcr).to have_received(:turn_off!) - end - - it 'runs app' do - aggregate_failures do - %w(/ /__e2e__/login command /e2e_command /).each do |path| - env['PATH_INFO'] = path - - response = subject.call(env) - - expect(response).to eq([200, {}, ["app did #{path}"]]) - expect(vcr).to have_received(:turn_off!) - end - end - end - end - end -end diff --git a/specs_e2e/rails_3_2/config/boot.rb b/specs_e2e/rails_3_2/config/boot.rb index 4489e58..f2830ae 100644 --- a/specs_e2e/rails_3_2/config/boot.rb +++ b/specs_e2e/rails_3_2/config/boot.rb @@ -3,4 +3,4 @@ # Set up gems listed in the Gemfile. ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) -require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE']) +require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) diff --git a/specs_e2e/rails_4_2/Gemfile b/specs_e2e/rails_4_2/Gemfile index 835f8b5..3ca9472 100644 --- a/specs_e2e/rails_4_2/Gemfile +++ b/specs_e2e/rails_4_2/Gemfile @@ -3,6 +3,9 @@ source 'https://rubygems.org' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' gem 'rails', '~> 4.2.10' gem 'sprockets', '~> 3.7.2' +# Solution for issue: NoMethodError: undefined method `new' for BigDecimal:Class +# https://github.com/ruby/bigdecimal?tab=readme-ov-file#which-version-should-you-select +gem 'bigdecimal', '1.3.5' group :development, :test do gem 'vcr'