" do 16 | render 17 | <% for attribute in output_attributes -%> 18 | expect(rendered).to match(/<%= raw_value_for(attribute) %>/) 19 | <% end -%> 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/generators/rspec/system/system_generator.rb: -------------------------------------------------------------------------------- 1 | require 'generators/rspec' 2 | 3 | module Rspec 4 | module Generators 5 | # @private 6 | class SystemGenerator < Base 7 | class_option :system_specs, type: :boolean, default: true, desc: "Generate system specs" 8 | 9 | def generate_system_spec 10 | return unless options[:system_specs] 11 | 12 | template template_name, target_path('system', class_path, filename) 13 | end 14 | 15 | def template_name 16 | 'system_spec.rb' 17 | end 18 | 19 | def filename 20 | "#{table_name}_spec.rb" 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/generators/rspec/system/templates/system_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "<%= class_name.pluralize %>", <%= type_metatag(:system) %> do 4 | before do 5 | driven_by(:rack_test) 6 | end 7 | 8 | pending "add some scenarios (or delete) #{__FILE__}" 9 | end 10 | -------------------------------------------------------------------------------- /lib/generators/rspec/view/templates/view_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "<%= file_path %>/<%= @action %>", <%= type_metatag(:view) %> do 4 | pending "add some examples to (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /lib/generators/rspec/view/view_generator.rb: -------------------------------------------------------------------------------- 1 | require 'generators/rspec' 2 | 3 | module Rspec 4 | module Generators 5 | # @private 6 | class ViewGenerator < Base 7 | argument :actions, type: :array, default: [], banner: "action action" 8 | 9 | class_option :template_engine, desc: "Template engine to generate view files" 10 | 11 | def create_view_specs 12 | empty_directory target_path("views", file_path) 13 | 14 | actions.each do |action| 15 | @action = action 16 | template 'view_spec.rb', 17 | target_path("views", file_path, "#{@action}.html.#{options[:template_engine]}_spec.rb") 18 | end 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/rspec/rails.rb: -------------------------------------------------------------------------------- 1 | require 'rspec/core' 2 | require 'rails/version' 3 | 4 | # Load any of our adapters and extensions early in the process 5 | require 'rspec/rails/adapters' 6 | require 'rspec/rails/extensions' 7 | 8 | # Load the rspec-rails parts 9 | require 'rspec/rails/view_rendering' 10 | require 'rspec/rails/matchers' 11 | require 'rspec/rails/fixture_support' 12 | require 'rspec/rails/file_fixture_support' 13 | require 'rspec/rails/fixture_file_upload_support' 14 | require 'rspec/rails/example' 15 | require 'rspec/rails/vendor/capybara' 16 | require 'rspec/rails/configuration' 17 | require 'rspec/rails/active_record' 18 | require 'rspec/rails/feature_check' 19 | -------------------------------------------------------------------------------- /lib/rspec/rails/active_record.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Rails 3 | # Fake class to document RSpec ActiveRecord configuration options. In practice, 4 | # these are dynamically added to the normal RSpec configuration object. 5 | class ActiveRecordConfiguration 6 | # @private 7 | def self.initialize_activerecord_configuration(config) 8 | config.before :suite do 9 | # This allows dynamic columns etc to be used on ActiveRecord models when creating instance_doubles 10 | if defined?(ActiveRecord) && defined?(ActiveRecord::Base) && defined?(::RSpec::Mocks) && (::RSpec::Mocks.respond_to?(:configuration)) 11 | ::RSpec::Mocks.configuration.when_declaring_verifying_double do |possible_model| 12 | target = possible_model.target 13 | 14 | if Class === target && ActiveRecord::Base > target && !target.abstract_class? 15 | target.define_attribute_methods 16 | end 17 | end 18 | end 19 | end 20 | end 21 | 22 | initialize_activerecord_configuration RSpec.configuration 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/rspec/rails/example.rb: -------------------------------------------------------------------------------- 1 | require 'rspec/rails/example/rails_example_group' 2 | require 'rspec/rails/example/controller_example_group' 3 | require 'rspec/rails/example/request_example_group' 4 | require 'rspec/rails/example/helper_example_group' 5 | require 'rspec/rails/example/view_example_group' 6 | require 'rspec/rails/example/mailer_example_group' 7 | require 'rspec/rails/example/routing_example_group' 8 | require 'rspec/rails/example/model_example_group' 9 | require 'rspec/rails/example/job_example_group' 10 | require 'rspec/rails/example/feature_example_group' 11 | require 'rspec/rails/example/system_example_group' 12 | require 'rspec/rails/example/channel_example_group' 13 | require 'rspec/rails/example/mailbox_example_group' 14 | -------------------------------------------------------------------------------- /lib/rspec/rails/example/feature_example_group.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Rails 3 | # @api public 4 | # Container module for routing spec functionality. 5 | module FeatureExampleGroup 6 | extend ActiveSupport::Concern 7 | include RSpec::Rails::RailsExampleGroup 8 | 9 | # Default host to be used in Rails route helpers if none is specified. 10 | DEFAULT_HOST = "www.example.com" 11 | 12 | included do 13 | app = ::Rails.application 14 | if app.respond_to?(:routes) 15 | include app.routes.url_helpers if app.routes.respond_to?(:url_helpers) 16 | include app.routes.mounted_helpers if app.routes.respond_to?(:mounted_helpers) 17 | 18 | if respond_to?(:default_url_options) 19 | default_url_options[:host] ||= ::RSpec::Rails::FeatureExampleGroup::DEFAULT_HOST 20 | end 21 | end 22 | end 23 | 24 | # Shim to check for presence of Capybara. Will delegate if present, raise 25 | # if not. We assume here that in most cases `visit` will be the first 26 | # Capybara method called in a spec. 27 | def visit(*) 28 | if defined?(super) 29 | super 30 | else 31 | raise "Capybara not loaded, please add it to your Gemfile:\n\ngem \"capybara\"" 32 | end 33 | end 34 | end 35 | end 36 | end 37 | 38 | unless RSpec.respond_to?(:feature) 39 | opts = { 40 | capybara_feature: true, 41 | type: :feature, 42 | skip: <<-EOT.squish 43 | Feature specs require the Capybara (https://github.com/teamcapybara/capybara) 44 | gem, version 2.13.0 or later. 45 | EOT 46 | } 47 | 48 | RSpec.configure do |c| 49 | c.alias_example_group_to :feature, opts 50 | c.alias_example_to :scenario 51 | c.alias_example_to :xscenario, skip: 'Temporarily skipped with xscenario' 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /lib/rspec/rails/example/helper_example_group.rb: -------------------------------------------------------------------------------- 1 | require 'rspec/rails/view_assigns' 2 | 3 | module RSpec 4 | module Rails 5 | # @api public 6 | # Container module for helper specs. 7 | module HelperExampleGroup 8 | extend ActiveSupport::Concern 9 | include RSpec::Rails::RailsExampleGroup 10 | include ActionView::TestCase::Behavior 11 | include RSpec::Rails::ViewAssigns 12 | 13 | # @private 14 | module ClassMethods 15 | def determine_constant_from_test_name(_ignore) 16 | described_class if yield(described_class) 17 | end 18 | end 19 | 20 | # Returns an instance of ActionView::Base with the helper being specified 21 | # mixed in, along with any of the built-in rails helpers. 22 | def helper 23 | _view.tap do |v| 24 | v.extend(ApplicationHelper) if defined?(ApplicationHelper) 25 | v.assign(view_assigns) 26 | end 27 | end 28 | 29 | private 30 | 31 | def _controller_path(example) 32 | example.example_group.described_class.to_s.sub(/Helper/, '').underscore 33 | end 34 | 35 | included do 36 | before do |example| 37 | controller.controller_path = _controller_path(example) 38 | end 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/rspec/rails/example/job_example_group.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Rails 3 | # @api public 4 | # Container module for job spec functionality. It is only available if 5 | # ActiveJob has been loaded before it. 6 | module JobExampleGroup 7 | # This blank module is only necessary for YARD processing. It doesn't 8 | # handle the conditional `defined?` check below very well. 9 | end 10 | end 11 | end 12 | 13 | if defined?(ActiveJob) 14 | module RSpec 15 | module Rails 16 | # Container module for job spec functionality. 17 | module JobExampleGroup 18 | extend ActiveSupport::Concern 19 | include RSpec::Rails::RailsExampleGroup 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/rspec/rails/example/mailbox_example_group.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Rails 3 | # @api public 4 | # Container module for mailbox spec functionality. 5 | module MailboxExampleGroup 6 | extend ActiveSupport::Concern 7 | 8 | if RSpec::Rails::FeatureCheck.has_action_mailbox? 9 | require 'action_mailbox/test_helper' 10 | extend ::ActionMailbox::TestHelper 11 | 12 | # @private 13 | def self.create_inbound_email(arg) 14 | case arg 15 | when Hash 16 | create_inbound_email_from_mail(**arg) 17 | else 18 | create_inbound_email_from_source(arg.to_s) 19 | end 20 | end 21 | else 22 | def self.create_inbound_email(_arg) 23 | raise "Could not load ActionMailer::TestHelper" 24 | end 25 | end 26 | 27 | class_methods do 28 | # @private 29 | def mailbox_class 30 | described_class 31 | end 32 | end 33 | 34 | included do 35 | subject { described_class } 36 | end 37 | 38 | # @api public 39 | # Passes if the inbound email was delivered 40 | # 41 | # @example 42 | # inbound_email = process(args) 43 | # expect(inbound_email).to have_been_delivered 44 | def have_been_delivered 45 | satisfy('have been delivered', &:delivered?) 46 | end 47 | 48 | # @api public 49 | # Passes if the inbound email bounced during processing 50 | # 51 | # @example 52 | # inbound_email = process(args) 53 | # expect(inbound_email).to have_bounced 54 | def have_bounced 55 | satisfy('have bounced', &:bounced?) 56 | end 57 | 58 | # @api public 59 | # Passes if the inbound email failed to process 60 | # 61 | # @example 62 | # inbound_email = process(args) 63 | # expect(inbound_email).to have_failed 64 | def have_failed 65 | satisfy('have failed', &:failed?) 66 | end 67 | 68 | # Process an inbound email message directly, bypassing routing. 69 | # 70 | # @param message [Hash, Mail::Message] a mail message or hash of 71 | # attributes used to build one 72 | # @return [ActionMailbox::InboundMessage] 73 | def process(message) 74 | MailboxExampleGroup.create_inbound_email(message).tap do |mail| 75 | self.class.mailbox_class.receive(mail) 76 | end 77 | end 78 | end 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /lib/rspec/rails/example/mailer_example_group.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Rails 3 | # @api public 4 | # Container module for mailer spec functionality. It is only available if 5 | # ActionMailer has been loaded before it. 6 | module MailerExampleGroup 7 | # This blank module is only necessary for YARD processing. It doesn't 8 | # handle the conditional `defined?` check below very well. 9 | end 10 | end 11 | end 12 | 13 | if defined?(ActionMailer) 14 | module RSpec 15 | module Rails 16 | # Container module for mailer spec functionality. 17 | module MailerExampleGroup 18 | extend ActiveSupport::Concern 19 | include RSpec::Rails::RailsExampleGroup 20 | include ActionMailer::TestCase::Behavior 21 | 22 | included do 23 | include ::Rails.application.routes.url_helpers 24 | options = ::Rails.configuration.action_mailer.default_url_options || {} 25 | options.each { |key, value| default_url_options[key] = value } 26 | end 27 | 28 | # Class-level DSL for mailer specs. 29 | module ClassMethods 30 | # Alias for `described_class`. 31 | def mailer_class 32 | described_class 33 | end 34 | end 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/rspec/rails/example/model_example_group.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Rails 3 | # @api public 4 | # Container class for model spec functionality. Does not provide anything 5 | # special over the common RailsExampleGroup currently. 6 | module ModelExampleGroup 7 | extend ActiveSupport::Concern 8 | include RSpec::Rails::RailsExampleGroup 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/rspec/rails/example/rails_example_group.rb: -------------------------------------------------------------------------------- 1 | # Temporary workaround to resolve circular dependency between rspec-rails' spec 2 | # suite and ammeter. 3 | require 'rspec/rails/matchers' 4 | 5 | require 'active_support/current_attributes/test_helper' 6 | require 'active_support/execution_context/test_helper' 7 | 8 | module RSpec 9 | module Rails 10 | # @api public 11 | # Common rails example functionality. 12 | module RailsExampleGroup 13 | extend ActiveSupport::Concern 14 | include RSpec::Rails::SetupAndTeardownAdapter 15 | include RSpec::Rails::MinitestLifecycleAdapter 16 | include RSpec::Rails::MinitestAssertionAdapter 17 | include RSpec::Rails::FixtureSupport 18 | include RSpec::Rails::TaggedLoggingAdapter 19 | include ActiveSupport::CurrentAttributes::TestHelper 20 | include ActiveSupport::ExecutionContext::TestHelper 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/rspec/rails/example/request_example_group.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Rails 3 | # @api public 4 | # Container class for request spec functionality. 5 | module RequestExampleGroup 6 | extend ActiveSupport::Concern 7 | include RSpec::Rails::RailsExampleGroup 8 | include ActionDispatch::Integration::Runner 9 | include ActionDispatch::Assertions 10 | include RSpec::Rails::Matchers::RedirectTo 11 | include RSpec::Rails::Matchers::RenderTemplate 12 | include ActionController::TemplateAssertions 13 | include ActionDispatch::IntegrationTest::Behavior 14 | 15 | # Delegates to `Rails.application`. 16 | def app 17 | ::Rails.application 18 | end 19 | 20 | included do 21 | before do 22 | @routes = ::Rails.application.routes 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/rspec/rails/example/routing_example_group.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Rails 3 | # @private 4 | RoutingAssertionDelegator = RSpec::Rails::AssertionDelegator.new( 5 | ActionDispatch::Assertions::RoutingAssertions 6 | ) 7 | 8 | # @api public 9 | # Container module for routing spec functionality. 10 | module RoutingExampleGroup 11 | extend ActiveSupport::Concern 12 | include RSpec::Rails::RailsExampleGroup 13 | include RSpec::Rails::Matchers::RoutingMatchers 14 | include RSpec::Rails::Matchers::RoutingMatchers::RouteHelpers 15 | include RSpec::Rails::RoutingAssertionDelegator 16 | 17 | # Class-level DSL for route specs. 18 | module ClassMethods 19 | # Specifies the routeset that will be used for the example group. This 20 | # is most useful when testing Rails engines. 21 | # 22 | # @example 23 | # describe MyEngine::PostsController do 24 | # routes { MyEngine::Engine.routes } 25 | # 26 | # it "routes posts#index" do 27 | # expect(:get => "/posts").to 28 | # route_to(:controller => "my_engine/posts", :action => "index") 29 | # end 30 | # end 31 | def routes 32 | before do 33 | self.routes = yield 34 | end 35 | end 36 | end 37 | 38 | included do 39 | before do 40 | self.routes = ::Rails.application.routes 41 | end 42 | end 43 | 44 | # @!attribute [r] 45 | # @private 46 | attr_reader :routes 47 | 48 | # @private 49 | def routes=(routes) 50 | @routes = routes 51 | assertion_instance.instance_variable_set(:@routes, routes) 52 | end 53 | 54 | private 55 | 56 | def method_missing(m, *args, &block) 57 | routes.url_helpers.respond_to?(m) ? routes.url_helpers.send(m, *args) : super 58 | end 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /lib/rspec/rails/extensions.rb: -------------------------------------------------------------------------------- 1 | require 'rspec/rails/extensions/active_record/proxy' 2 | -------------------------------------------------------------------------------- /lib/rspec/rails/extensions/active_record/proxy.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |rspec| 2 | # Delay this in order to give users a chance to configure `expect_with`... 3 | rspec.before(:suite) do 4 | if defined?(RSpec::Matchers) && 5 | RSpec::Matchers.configuration.respond_to?(:syntax) && # RSpec 4 dropped support for monkey-patching `should` syntax 6 | RSpec::Matchers.configuration.syntax.include?(:should) && 7 | defined?(ActiveRecord::Associations) 8 | RSpec::Matchers.configuration.add_should_and_should_not_to ActiveRecord::Associations::CollectionProxy 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/rspec/rails/feature_check.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Rails 3 | # @private 4 | module FeatureCheck 5 | module_function 6 | def has_active_job? 7 | defined?(::ActiveJob) 8 | end 9 | 10 | def has_active_record? 11 | defined?(::ActiveRecord) 12 | end 13 | 14 | def has_active_record_migration? 15 | has_active_record? && defined?(::ActiveRecord::Migration) 16 | end 17 | 18 | def has_action_mailer? 19 | defined?(::ActionMailer) 20 | end 21 | 22 | def has_action_mailer_preview? 23 | has_action_mailer? && defined?(::ActionMailer::Preview) 24 | end 25 | 26 | def has_action_cable_testing? 27 | defined?(::ActionCable) 28 | end 29 | 30 | def has_action_mailer_parameterized? 31 | has_action_mailer? && defined?(::ActionMailer::Parameterized::DeliveryJob) 32 | end 33 | 34 | def has_action_mailer_unified_delivery? 35 | has_action_mailer? && defined?(::ActionMailer::MailDeliveryJob) 36 | end 37 | 38 | def has_action_mailer_legacy_delivery_job? 39 | defined?(ActionMailer::DeliveryJob) 40 | end 41 | 42 | def has_action_mailbox? 43 | defined?(::ActionMailbox) 44 | end 45 | 46 | def type_metatag(type) 47 | "type: :#{type}" 48 | end 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /lib/rspec/rails/file_fixture_support.rb: -------------------------------------------------------------------------------- 1 | require 'active_support/testing/file_fixtures' 2 | 3 | module RSpec 4 | module Rails 5 | # @private 6 | module FileFixtureSupport 7 | extend ActiveSupport::Concern 8 | include ActiveSupport::Testing::FileFixtures 9 | 10 | included do 11 | self.file_fixture_path = RSpec.configuration.file_fixture_path 12 | if defined?(ActiveStorage::FixtureSet) 13 | ActiveStorage::FixtureSet.file_fixture_path = RSpec.configuration.file_fixture_path 14 | end 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/rspec/rails/fixture_file_upload_support.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Rails 3 | # @private 4 | module FixtureFileUploadSupport 5 | delegate :fixture_file_upload, to: :rails_fixture_file_wrapper 6 | 7 | private 8 | 9 | # In Rails 7.0 fixture file path needs to be relative to `file_fixture_path` instead, this change 10 | # was brought in with a deprecation warning on 6.1. In Rails 7.0 expect to rework this to remove 11 | # the old accessor. 12 | def rails_fixture_file_wrapper 13 | RailsFixtureFileWrapper.file_fixture_path = nil 14 | resolved_fixture_path = 15 | if respond_to?(:file_fixture_path) && !file_fixture_path.nil? 16 | file_fixture_path.to_s 17 | else 18 | (RSpec.configuration.fixture_paths&.first || '').to_s 19 | end 20 | RailsFixtureFileWrapper.file_fixture_path = File.join(resolved_fixture_path, '') unless resolved_fixture_path.strip.empty? 21 | RailsFixtureFileWrapper.instance 22 | end 23 | 24 | class RailsFixtureFileWrapper 25 | include ActionDispatch::TestProcess if defined?(ActionDispatch::TestProcess) 26 | include ActiveSupport::Testing::FileFixtures 27 | 28 | class << self 29 | attr_accessor :fixture_paths 30 | 31 | # Get instance of wrapper 32 | def instance 33 | @instance ||= new 34 | end 35 | end 36 | end 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/rspec/rails/fixture_support.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Rails 3 | # @private 4 | module FixtureSupport 5 | if defined?(ActiveRecord::TestFixtures) 6 | extend ActiveSupport::Concern 7 | include RSpec::Rails::SetupAndTeardownAdapter 8 | include RSpec::Rails::MinitestLifecycleAdapter 9 | include RSpec::Rails::MinitestAssertionAdapter 10 | include ActiveRecord::TestFixtures 11 | 12 | # @private prevent ActiveSupport::TestFixtures to start a DB transaction. 13 | # Monkey patched to avoid collisions with 'let(:name)' since Rails 6.1 14 | def run_in_transaction? 15 | current_example_name = (RSpec.current_example && RSpec.current_example.metadata[:description]) 16 | use_transactional_tests && !self.class.uses_transaction?(current_example_name) 17 | end 18 | 19 | included do 20 | if RSpec.configuration.use_active_record? 21 | include Fixtures 22 | 23 | self.fixture_paths = RSpec.configuration.fixture_paths 24 | 25 | self.use_transactional_tests = RSpec.configuration.use_transactional_fixtures 26 | self.use_instantiated_fixtures = RSpec.configuration.use_instantiated_fixtures 27 | 28 | fixtures RSpec.configuration.global_fixtures if RSpec.configuration.global_fixtures 29 | end 30 | end 31 | 32 | module Fixtures 33 | extend ActiveSupport::Concern 34 | 35 | class_methods do 36 | def fixtures(*args) 37 | super.tap do 38 | fixture_sets.each_pair do |method_name, fixture_name| 39 | proxy_method_warning_if_called_in_before_context_scope(method_name, fixture_name) 40 | end 41 | end 42 | end 43 | 44 | def proxy_method_warning_if_called_in_before_context_scope(method_name, fixture_name) 45 | define_method(method_name) do |*args, **kwargs, &blk| 46 | if RSpec.current_scope == :before_context_hook 47 | RSpec.warn_with("Calling fixture method in before :context ") 48 | else 49 | access_fixture(fixture_name, *args, **kwargs, &blk) 50 | end 51 | end 52 | end 53 | end 54 | end 55 | end 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/rspec/rails/matchers.rb: -------------------------------------------------------------------------------- 1 | require 'rspec/core/warnings' 2 | require 'rspec/expectations' 3 | require 'rspec/rails/feature_check' 4 | 5 | module RSpec 6 | module Rails 7 | # @api public 8 | # Container module for Rails specific matchers. 9 | module Matchers 10 | end 11 | end 12 | end 13 | 14 | require 'rspec/rails/matchers/base_matcher' 15 | require 'rspec/rails/matchers/have_rendered' 16 | require 'rspec/rails/matchers/redirect_to' 17 | require 'rspec/rails/matchers/routing_matchers' 18 | require 'rspec/rails/matchers/be_new_record' 19 | require 'rspec/rails/matchers/be_a_new' 20 | require 'rspec/rails/matchers/relation_match_array' 21 | require 'rspec/rails/matchers/be_valid' 22 | require 'rspec/rails/matchers/have_http_status' 23 | require 'rspec/rails/matchers/send_email' 24 | 25 | if RSpec::Rails::FeatureCheck.has_active_job? 26 | require 'rspec/rails/matchers/active_job' 27 | require 'rspec/rails/matchers/have_enqueued_mail' 28 | end 29 | 30 | if RSpec::Rails::FeatureCheck.has_action_cable_testing? 31 | require 'rspec/rails/matchers/action_cable' 32 | end 33 | 34 | if RSpec::Rails::FeatureCheck.has_action_mailbox? 35 | require 'rspec/rails/matchers/action_mailbox' 36 | end 37 | -------------------------------------------------------------------------------- /lib/rspec/rails/matchers/action_cable/have_streams.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Rails 3 | module Matchers 4 | module ActionCable 5 | # @api private 6 | # Provides the implementation for `have_stream`, `have_stream_for`, and `have_stream_from`. 7 | # Not intended to be instantiated directly. 8 | class HaveStream < RSpec::Matchers::BuiltIn::BaseMatcher 9 | # @api private 10 | # @return [String] 11 | def failure_message 12 | "expected to have #{base_message}" 13 | end 14 | 15 | # @api private 16 | # @return [String] 17 | def failure_message_when_negated 18 | "expected not to have #{base_message}" 19 | end 20 | 21 | # @api private 22 | # @return [Boolean] 23 | def matches?(subscription) 24 | raise(ArgumentError, "have_streams is used for negated expectations only") if no_expected? 25 | 26 | match(subscription) 27 | end 28 | 29 | # @api private 30 | # @return [Boolean] 31 | def does_not_match?(subscription) 32 | !match(subscription) 33 | end 34 | 35 | private 36 | 37 | def match(subscription) 38 | case subscription 39 | when ::ActionCable::Channel::Base 40 | @actual = subscription.streams 41 | no_expected? ? actual.any? : actual.any? { |i| expected === i } 42 | else 43 | raise ArgumentError, "have_stream, have_stream_from and have_stream_from support expectations on subscription only" 44 | end 45 | end 46 | 47 | def base_message 48 | no_expected? ? "any stream started" : "stream #{expected_formatted} started, but have #{actual_formatted}" 49 | end 50 | 51 | def no_expected? 52 | !defined?(@expected) 53 | end 54 | end 55 | end 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/rspec/rails/matchers/action_mailbox.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Rails 3 | module Matchers 4 | # Namespace for various implementations of ActionMailbox features 5 | # 6 | # @api private 7 | module ActionMailbox 8 | # @private 9 | class Base < RSpec::Rails::Matchers::BaseMatcher 10 | private 11 | 12 | def create_inbound_email(message) 13 | RSpec::Rails::MailboxExampleGroup.create_inbound_email(message) 14 | end 15 | end 16 | 17 | # @private 18 | class ReceiveInboundEmail < Base 19 | def initialize(message) 20 | super() 21 | 22 | @inbound_email = create_inbound_email(message) 23 | end 24 | 25 | if defined?(::ApplicationMailbox) && ::ApplicationMailbox.router.respond_to?(:mailbox_for) 26 | def matches?(mailbox) 27 | @mailbox = mailbox 28 | @receiver = ApplicationMailbox.router.mailbox_for(inbound_email) 29 | 30 | @receiver == @mailbox 31 | end 32 | else 33 | def matches?(mailbox) 34 | @mailbox = mailbox 35 | @receiver = ApplicationMailbox.router.send(:match_to_mailbox, inbound_email) 36 | 37 | @receiver == @mailbox 38 | end 39 | end 40 | 41 | def failure_message 42 | "expected #{describe_inbound_email} to route to #{mailbox}".tap do |msg| 43 | if receiver 44 | msg << ", but routed to #{receiver} instead" 45 | end 46 | end 47 | end 48 | 49 | def failure_message_when_negated 50 | "expected #{describe_inbound_email} not to route to #{mailbox}" 51 | end 52 | 53 | private 54 | 55 | attr_reader :inbound_email, :mailbox, :receiver 56 | 57 | def describe_inbound_email 58 | "mail to #{inbound_email.mail.to.to_sentence}" 59 | end 60 | end 61 | end 62 | 63 | # @api public 64 | # Passes if the given inbound email would be routed to the subject inbox. 65 | # 66 | # @param message [Hash, Mail::Message] a mail message or hash of 67 | # attributes used to build one 68 | def receive_inbound_email(message) 69 | ActionMailbox::ReceiveInboundEmail.new(message) 70 | end 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /lib/rspec/rails/matchers/be_new_record.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Rails 3 | module Matchers 4 | # @private 5 | class BeANewRecord < RSpec::Rails::Matchers::BaseMatcher 6 | def matches?(actual) 7 | actual.new_record? 8 | end 9 | 10 | def failure_message 11 | "expected #{actual.inspect} to be a new record, but was persisted" 12 | end 13 | 14 | def failure_message_when_negated 15 | "expected #{actual.inspect} to be persisted, but was a new record" 16 | end 17 | end 18 | 19 | # @api public 20 | # Passes if actual returns `true` for `new_record?`. 21 | # 22 | # @example 23 | # get :new 24 | # expect(assigns(:thing)).to be_new_record 25 | def be_new_record 26 | BeANewRecord.new 27 | end 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/rspec/rails/matchers/be_valid.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Rails 3 | module Matchers 4 | # @private 5 | class BeValid < RSpec::Matchers::BuiltIn::Be 6 | def initialize(*args) 7 | @args = args 8 | end 9 | 10 | def matches?(actual) 11 | @actual = actual 12 | actual.valid?(*@args) 13 | end 14 | 15 | def failure_message 16 | message = "expected #{actual.inspect} to be valid" 17 | 18 | if actual.respond_to?(:errors) && actual.method(:errors).arity < 1 19 | errors = if actual.errors.respond_to?(:full_messages) 20 | actual.errors.full_messages 21 | else 22 | actual.errors 23 | end 24 | 25 | message << ", but got errors: #{errors.map(&:to_s).join(', ')}" 26 | end 27 | 28 | message 29 | end 30 | 31 | def failure_message_when_negated 32 | "expected #{actual.inspect} not to be valid" 33 | end 34 | end 35 | 36 | # @api public 37 | # Passes if the given model instance's `valid?` method is true, meaning 38 | # all of the `ActiveModel::Validations` passed and no errors exist. If a 39 | # message is not given, a default message is shown listing each error. 40 | # 41 | # @example 42 | # thing = Thing.new 43 | # expect(thing).to be_valid 44 | def be_valid(*args) 45 | BeValid.new(*args) 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /lib/rspec/rails/matchers/have_rendered.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Rails 3 | module Matchers 4 | # Matcher for template rendering. 5 | module RenderTemplate 6 | # @private 7 | class RenderTemplateMatcher < RSpec::Rails::Matchers::BaseMatcher 8 | def initialize(scope, expected, message = nil) 9 | @expected = Symbol === expected ? expected.to_s : expected 10 | @message = message 11 | @scope = scope 12 | @redirect_is = nil 13 | end 14 | 15 | # @api private 16 | def matches?(*) 17 | match_check = match_unless_raises ActiveSupport::TestCase::Assertion do 18 | @scope.assert_template expected, @message 19 | end 20 | check_redirect unless match_check 21 | match_check 22 | end 23 | 24 | # Uses normalize_argument_to_redirection to find and format 25 | # the redirect location. normalize_argument_to_redirection is private 26 | # in ActionDispatch::Assertions::ResponseAssertions so we call it 27 | # here using #send. This will keep the error message format consistent 28 | # @api private 29 | def check_redirect 30 | response = @scope.response 31 | return unless response.respond_to?(:redirect?) && response.redirect? 32 | 33 | @redirect_is = @scope.send(:normalize_argument_to_redirection, response.location) 34 | end 35 | 36 | # @api private 37 | def failure_message 38 | if @redirect_is 39 | rescued_exception.message[/(.*?)( but|$)/, 1] + 40 | " but was a redirect to <#{@redirect_is}>" 41 | else 42 | rescued_exception.message 43 | end 44 | end 45 | 46 | # @api private 47 | def failure_message_when_negated 48 | "expected not to render #{expected.inspect}, but did" 49 | end 50 | end 51 | 52 | # Delegates to `assert_template`. 53 | # 54 | # @example 55 | # expect(response).to have_rendered("new") 56 | def have_rendered(options, message = nil) 57 | RenderTemplateMatcher.new(self, options, message) 58 | end 59 | 60 | alias_method :render_template, :have_rendered 61 | end 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /lib/rspec/rails/matchers/redirect_to.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Rails 3 | module Matchers 4 | # Matcher for redirects. 5 | module RedirectTo 6 | # @private 7 | class RedirectTo < RSpec::Rails::Matchers::BaseMatcher 8 | def initialize(scope, expected) 9 | @expected = expected 10 | @scope = scope 11 | end 12 | 13 | def matches?(_) 14 | match_unless_raises ActiveSupport::TestCase::Assertion do 15 | @scope.assert_redirected_to(@expected) 16 | end 17 | end 18 | 19 | def failure_message 20 | rescued_exception.message 21 | end 22 | 23 | def failure_message_when_negated 24 | "expected not to redirect to #{@expected.inspect}, but did" 25 | end 26 | end 27 | 28 | # Delegates to `assert_redirected_to`. 29 | # 30 | # @example 31 | # expect(response).to redirect_to(:action => "new") 32 | def redirect_to(target) 33 | RedirectTo.new(self, target) 34 | end 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/rspec/rails/matchers/relation_match_array.rb: -------------------------------------------------------------------------------- 1 | if defined?(ActiveRecord::Relation) && defined?(RSpec::Matchers::BuiltIn::OperatorMatcher) # RSpec 4 removed OperatorMatcher 2 | RSpec::Matchers::BuiltIn::OperatorMatcher.register(ActiveRecord::Relation, '=~', RSpec::Matchers::BuiltIn::ContainExactly) 3 | end 4 | -------------------------------------------------------------------------------- /lib/rspec/rails/tasks/rspec.rake: -------------------------------------------------------------------------------- 1 | require 'rspec/core/rake_task' 2 | if default = Rake.application.instance_variable_get('@tasks')['default'] 3 | default.prerequisites.delete('test') 4 | end 5 | 6 | task default: :spec 7 | 8 | if ::Rails::VERSION::STRING < "8.0.0" 9 | task stats: "spec:statsetup" 10 | end 11 | 12 | desc "Run all specs in spec directory (excluding plugin specs)" 13 | RSpec::Core::RakeTask.new(spec: "spec:prepare") 14 | 15 | namespace :spec do 16 | types = begin 17 | dirs = Dir['./spec/**/*_spec.rb'] 18 | .map { |f| f.sub(/^\.\/(spec\/\w+)\/.*/, '\\1') } 19 | .uniq 20 | .select { |f| File.directory?(f) } 21 | Hash[dirs.map { |d| [d.split('/').last, d] }] 22 | end 23 | 24 | task :prepare do 25 | ENV['RACK_ENV'] = ENV['RAILS_ENV'] = 'test' 26 | if Rails.configuration.generators.options[:rails][:orm] == :active_record 27 | if Rake::Task.task_defined?("test:prepare") 28 | Rake::Task["test:prepare"].invoke 29 | end 30 | end 31 | end 32 | 33 | types.each do |type, dir| 34 | desc "Run the code examples in #{dir}" 35 | RSpec::Core::RakeTask.new(type => "spec:prepare") do |t| 36 | t.pattern = "./#{dir}/**/*_spec.rb" 37 | end 38 | end 39 | 40 | task :statsetup do 41 | require 'rails/code_statistics' 42 | types.each do |type, dir| 43 | name = type.singularize.capitalize 44 | 45 | ::STATS_DIRECTORIES << ["#{name} specs", dir] 46 | ::CodeStatistics::TEST_TYPES << "#{name} specs" 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /lib/rspec/rails/vendor/capybara.rb: -------------------------------------------------------------------------------- 1 | begin 2 | require 'capybara/rspec' 3 | rescue LoadError 4 | end 5 | 6 | begin 7 | require 'capybara/rails' 8 | rescue LoadError 9 | end 10 | 11 | if defined?(Capybara) 12 | RSpec.configure do |c| 13 | if defined?(Capybara::DSL) 14 | c.include Capybara::DSL, type: :feature 15 | c.include Capybara::DSL, type: :system 16 | end 17 | 18 | if defined?(Capybara::RSpecMatchers) 19 | c.include Capybara::RSpecMatchers, type: :view 20 | c.include Capybara::RSpecMatchers, type: :helper 21 | c.include Capybara::RSpecMatchers, type: :mailer 22 | c.include Capybara::RSpecMatchers, type: :controller 23 | c.include Capybara::RSpecMatchers, type: :feature 24 | c.include Capybara::RSpecMatchers, type: :system 25 | end 26 | 27 | unless defined?(Capybara::RSpecMatchers) || defined?(Capybara::DSL) 28 | c.include Capybara, type: :request 29 | c.include Capybara, type: :controller 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /lib/rspec/rails/version.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Rails 3 | # Version information for RSpec Rails. 4 | module Version 5 | # Current version of RSpec Rails, in semantic versioning format. 6 | STRING = '8.1.0.pre' 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/rspec/rails/view_assigns.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Rails 3 | # Helpers for making instance variables available to views. 4 | module ViewAssigns 5 | # Assigns a value to an instance variable in the scope of the 6 | # view being rendered. 7 | # 8 | # @example 9 | # 10 | # assign(:widget, stub_model(Widget)) 11 | def assign(key, value) 12 | _encapsulated_assigns[key] = value 13 | end 14 | 15 | # Compat-shim for AbstractController::Rendering#view_assigns 16 | def view_assigns 17 | super.merge(_encapsulated_assigns) 18 | end 19 | 20 | private 21 | 22 | def _encapsulated_assigns 23 | @_encapsulated_assigns ||= {} 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/rspec/rails/view_path_builder.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Rails 3 | # Builds paths for view specs using a particular route set. 4 | class ViewPathBuilder 5 | def initialize(route_set) 6 | self.class.send(:include, route_set.url_helpers) 7 | end 8 | 9 | # Given a hash of parameters, build a view path, if possible. 10 | # Returns nil if no path can be built from the given params. 11 | # 12 | # @example 13 | # # path can be built because all required params are present in the hash 14 | # view_path_builder = ViewPathBuilder.new(::Rails.application.routes) 15 | # view_path_builder.path_for({ :controller => 'posts', :action => 'show', :id => '54' }) 16 | # # => "/post/54" 17 | # 18 | # @example 19 | # # path cannot be built because the params are missing a required element (:id) 20 | # view_path_builder.path_for({ :controller => 'posts', :action => 'delete' }) 21 | # # => ActionController::UrlGenerationError: No route matches {:action=>"delete", :controller=>"posts"} 22 | def path_for(path_params) 23 | url_for(path_params.merge(only_path: true)) 24 | rescue => e 25 | e.message 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/rspec/rails/view_spec_methods.rb: -------------------------------------------------------------------------------- 1 | module RSpec 2 | module Rails 3 | # Adds methods (generally to ActionView::TestCase::TestController). 4 | # Intended for use in view specs. 5 | module ViewSpecMethods 6 | module_function 7 | 8 | # Adds methods `extra_params=` and `extra_params` to the indicated class. 9 | # When class is `::ActionView::TestCase::TestController`, these methods 10 | # are exposed in view specs on the `controller` object. 11 | def add_to(klass) 12 | return if klass.method_defined?(:extra_params) && klass.method_defined?(:extra_params=) 13 | 14 | klass.module_exec do 15 | # Set any extra parameters that rendering a URL for this view 16 | # would require. 17 | # 18 | # @example 19 | # 20 | # # In "spec/views/widgets/show.html.erb_spec.rb": 21 | # before do 22 | # widget = Widget.create!(:name => "slicer") 23 | # controller.extra_params = { :id => widget.id } 24 | # end 25 | def extra_params=(hash) 26 | @extra_params = hash 27 | request.path = 28 | ViewPathBuilder.new(::Rails.application.routes).path_for( 29 | extra_params.merge(request.path_parameters) 30 | ) 31 | end 32 | 33 | # Use to read extra parameters that are set in the view spec. 34 | # 35 | # @example 36 | # 37 | # # After the before in the above example: 38 | # controller.extra_params 39 | # # => { :id => 4 } 40 | def extra_params 41 | @extra_params ||= {} 42 | @extra_params.dup.freeze 43 | end 44 | end 45 | end 46 | 47 | # Removes methods `extra_params=` and `extra_params` from the indicated class. 48 | def remove_from(klass) 49 | klass.module_exec do 50 | undef extra_params= if klass.method_defined?(:extra_params=) 51 | undef extra_params if klass.method_defined?(:extra_params) 52 | end 53 | end 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /maintenance-branch: -------------------------------------------------------------------------------- 1 | main 2 | -------------------------------------------------------------------------------- /script/clone_all_rspec_repos: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This file was generated on 2019-12-18T14:01:39+00:00 from the rspec-dev repo. 3 | # DO NOT modify it by hand as your changes will get lost the next time it is generated. 4 | 5 | set -e 6 | source script/functions.sh 7 | 8 | pushd .. 9 | 10 | clone_repo "rspec-metagem" "rspec" 11 | clone_repo "rspec-core" 12 | clone_repo "rspec-expectations" 13 | clone_repo "rspec-mocks" 14 | clone_repo "rspec-rails" 15 | clone_repo "rspec-support" 16 | 17 | popd 18 | -------------------------------------------------------------------------------- /script/run_build: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This file is manually managed unlike the rest of core rspec gems because it is independent. 3 | 4 | set -e 5 | source script/functions.sh 6 | 7 | fold "binstub check" check_binstubs 8 | 9 | fold "specs" run_specs_and_record_done 10 | 11 | fold "acceptance" bin/rake acceptance --trace 12 | 13 | fold "snippets" script/run_snippets.sh 14 | 15 | if documentation_enforced; then 16 | fold "doc check" check_documentation_coverage 17 | fi 18 | 19 | fold "one-by-one specs" run_specs_one_by_one 20 | -------------------------------------------------------------------------------- /script/run_rubocop: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This file is manually managed unlike the rest of core rspec gems because it is independent. 3 | 4 | set -e 5 | source script/functions.sh 6 | 7 | function check_style_and_lint { 8 | echo "bin/rubocop" 9 | eval "(unset RUBYOPT; rm -rf tmp/*; exec bin/rubocop)" 10 | } 11 | 12 | fold "rubocop" check_style_and_lint 13 | -------------------------------------------------------------------------------- /script/run_snippets.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | ( 5 | cd snippets 6 | # This is required to load `bundle/inline` 7 | unset RUBYOPT 8 | for snippet in *.rb; 9 | do 10 | echo Running $snippet 11 | ruby $snippet 12 | done 13 | ) 14 | -------------------------------------------------------------------------------- /script/update_rubygems_and_install_bundler: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This file is manually managed unlike the rest of core rspec gems because it is independent. 3 | 4 | set -e 5 | 6 | function is_ruby_3_plus { 7 | if ruby -e "exit(RUBY_VERSION.to_f >= 3.0)"; then 8 | return 0 9 | else 10 | return 1 11 | fi 12 | } 13 | 14 | function is_ruby_3_1_plus { 15 | if ruby -e "exit(RUBY_VERSION.to_f >= 3.1)"; then 16 | return 0 17 | else 18 | return 1 19 | fi 20 | } 21 | 22 | 23 | if is_ruby_3_1_plus; then 24 | gem update --no-document --system 25 | gem install --no-document bundler 26 | elif is_ruby_3_plus; then 27 | gem update --no-document --system '3.5.23' 28 | gem install --no-document bundler 29 | else 30 | gem update --no-document --system '3.4.22' 31 | gem install --no-document bundler 32 | fi 33 | -------------------------------------------------------------------------------- /snippets/avoid_fixture_name_collision.rb: -------------------------------------------------------------------------------- 1 | if __FILE__ =~ /^snippets/ 2 | fail "Snippets are supposed to be run from their own directory to avoid side " \ 3 | "effects as e.g. the root `Gemfile`, or `spec/spec_helpers.rb` to be " \ 4 | "loaded by the root `.rspec`." 5 | end 6 | 7 | # We opt-out from using RubyGems, but `bundler/inline` requires it 8 | require 'rubygems' 9 | 10 | require "bundler/inline" 11 | 12 | # We pass `false` to `gemfile` to skip the installation of gems, 13 | # because it may install versions that would conflict with versions 14 | # from the main `Gemfile.lock`. 15 | gemfile(false) do 16 | source "https://rubygems.org" 17 | 18 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 19 | 20 | # Those Gemfiles carefully pick the right versions depending on 21 | # settings in the ENV, `.rails-version` and `maintenance-branch`. 22 | Dir.chdir('..') do 23 | # This Gemfile expects `maintenance-branch` file to be present 24 | # in the current directory. 25 | eval_gemfile 'Gemfile-rspec-dependencies' 26 | # This Gemfile expects `.rails-version` file 27 | eval_gemfile 'Gemfile-rails-dependencies' 28 | end 29 | 30 | gem "rspec-rails", path: "../" 31 | end 32 | 33 | # Run specs at exit 34 | require "rspec/autorun" 35 | 36 | require "rails" 37 | require "active_record/railtie" 38 | require "rspec/rails" 39 | 40 | # This connection will do for database-independent bug reports 41 | ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") 42 | 43 | RSpec.configure do |config| 44 | config.use_transactional_fixtures = true 45 | end 46 | 47 | RSpec.describe 'Foo' do 48 | subject { true } 49 | 50 | let(:name) { raise "Should never raise" } 51 | 52 | it { is_expected.to be_truthy } 53 | end 54 | -------------------------------------------------------------------------------- /snippets/include_activesupport_testing_tagged_logger.rb: -------------------------------------------------------------------------------- 1 | if __FILE__ =~ /^snippets/ 2 | fail "Snippets are supposed to be run from their own directory to avoid side " \ 3 | "effects as e.g. the root `Gemfile`, or `spec/spec_helpers.rb` to be " \ 4 | "loaded by the root `.rspec`." 5 | end 6 | 7 | # We opt-out from using RubyGems, but `bundler/inline` requires it 8 | require 'rubygems' 9 | 10 | require "bundler/inline" 11 | 12 | # We pass `false` to `gemfile` to skip the installation of gems, 13 | # because it may install versions that would conflict with versions 14 | # from the main `Gemfile.lock`. 15 | gemfile(false) do 16 | source "https://rubygems.org" 17 | 18 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 19 | 20 | # Those Gemfiles carefully pick the right versions depending on 21 | # settings in the ENV, `.rails-version` and `maintenance-branch`. 22 | Dir.chdir('..') do 23 | # This Gemfile expects `maintenance-branch` file to be present 24 | # in the current directory. 25 | eval_gemfile 'Gemfile-rspec-dependencies' 26 | # This Gemfile expects `.rails-version` file 27 | eval_gemfile 'Gemfile-rails-dependencies' 28 | end 29 | 30 | gem "rspec-rails", path: "../" 31 | end 32 | 33 | # Run specs at exit 34 | require "rspec/autorun" 35 | 36 | require "rails" 37 | require "active_record/railtie" 38 | require "active_job/railtie" 39 | require "rspec/rails" 40 | 41 | ActiveJob::Base.queue_adapter = :test 42 | 43 | # This connection will do for database-independent bug reports 44 | ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") 45 | 46 | class TestError < StandardError; end 47 | 48 | class TestJob < ActiveJob::Base 49 | def perform 50 | raise TestError 51 | end 52 | end 53 | 54 | RSpec.describe 'Foo', type: :job do 55 | include ::ActiveJob::TestHelper 56 | 57 | describe 'error raised in perform_enqueued_jobs with block' do 58 | it 'raises the explicitly thrown error' do 59 | expected_error = Minitest::UnexpectedError.new(TestError) 60 | 61 | expect { perform_enqueued_jobs { TestJob.perform_later } } 62 | .to raise_error(expected_error) 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /snippets/use_active_record_false.rb: -------------------------------------------------------------------------------- 1 | if __FILE__ =~ /^snippets/ 2 | fail "Snippets are supposed to be run from their own directory to avoid side " \ 3 | "effects as e.g. the root `Gemfile`, or `spec/spec_helpers.rb` to be " \ 4 | "loaded by the root `.rspec`." 5 | end 6 | 7 | # We opt-out from using RubyGems, but `bundler/inline` requires it 8 | require 'rubygems' 9 | 10 | require "bundler/inline" 11 | 12 | # We pass `false` to `gemfile` to skip the installation of gems, 13 | # because it may install versions that would conflict with versions 14 | # from the main `Gemfile.lock`. 15 | gemfile(false) do 16 | source "https://rubygems.org" 17 | 18 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 19 | 20 | # Those Gemfiles carefully pick the right versions depending on 21 | # settings in the ENV, `.rails-version` and `maintenance-branch`. 22 | Dir.chdir('..') do 23 | # This Gemfile expects `maintenance-branch` file to be present 24 | # in the current directory. 25 | eval_gemfile 'Gemfile-rspec-dependencies' 26 | # This Gemfile expects `.rails-version` file 27 | eval_gemfile 'Gemfile-rails-dependencies' 28 | end 29 | 30 | gem "rspec-rails", path: "../" 31 | end 32 | 33 | # Run specs at exit 34 | require "rspec/autorun" 35 | 36 | # This snippet describes the case when ActiveRecord is loaded, but 37 | # `use_active_record` is set to `false` in RSpec configuration. 38 | 39 | # Initialization 40 | require "active_record/railtie" 41 | require "rspec/rails" 42 | 43 | # This connection will do for database-independent bug reports 44 | ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") 45 | 46 | # RSpec configuration 47 | RSpec.configure do |config| 48 | config.use_active_record = false 49 | end 50 | 51 | # Rails project code 52 | class Foo 53 | end 54 | 55 | # Rails project specs 56 | RSpec.describe Foo do 57 | it 'does not not break' do 58 | Foo 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /spec/generators/rspec/authentication/authentication_generator_spec.rb: -------------------------------------------------------------------------------- 1 | # Generators are not automatically loaded by Rails 2 | require 'generators/rspec/authentication/authentication_generator' 3 | require 'support/generators' 4 | 5 | RSpec.describe Rspec::Generators::AuthenticationGenerator, type: :generator do 6 | setup_default_destination 7 | 8 | it 'runs both the model and fixture tasks' do 9 | gen = generator 10 | expect(gen).to receive :create_user_spec 11 | expect(gen).to receive :create_fixture_file 12 | gen.invoke_all 13 | end 14 | 15 | describe 'the generated files' do 16 | it 'creates the user spec' do 17 | run_generator 18 | 19 | expect(File.exist?(file('spec/models/user_spec.rb'))).to be true 20 | end 21 | 22 | describe 'with fixture replacement' do 23 | before do 24 | run_generator ['--fixture-replacement=factory_bot'] 25 | end 26 | 27 | describe 'the fixtures' do 28 | it "will skip the file" do 29 | expect(File.exist?(file('spec/fixtures/users.yml'))).to be false 30 | end 31 | end 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /spec/generators/rspec/channel/channel_generator_spec.rb: -------------------------------------------------------------------------------- 1 | # Generators are not automatically loaded by Rails 2 | require "generators/rspec/channel/channel_generator" 3 | require 'support/generators' 4 | 5 | RSpec.describe Rspec::Generators::ChannelGenerator, type: :generator, skip: !RSpec::Rails::FeatureCheck.has_action_cable_testing? do 6 | setup_default_destination 7 | 8 | before { run_generator %w[chat] } 9 | 10 | subject(:channel_spec) { file("spec/channels/chat_channel_spec.rb") } 11 | 12 | it "generates a channel spec file" do 13 | expect(channel_spec).to contain(/require 'rails_helper'/).and(contain(/describe ChatChannel, #{type_metatag(:channel)}/)) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/generators/rspec/feature/feature_generator_spec.rb: -------------------------------------------------------------------------------- 1 | # Generators are not automatically loaded by rails 2 | require 'generators/rspec/feature/feature_generator' 3 | require 'support/generators' 4 | 5 | RSpec.describe Rspec::Generators::FeatureGenerator, type: :generator do 6 | setup_default_destination 7 | 8 | describe 'feature specs' do 9 | describe 'are generated independently from the command line' do 10 | before do 11 | run_generator %w[posts] 12 | end 13 | 14 | describe 'the spec' do 15 | subject(:feature_spec) { file('spec/features/posts_spec.rb') } 16 | 17 | it 'includes the standard boilerplate' do 18 | expect( 19 | feature_spec 20 | ).to contain(/require 'rails_helper'/).and(contain(/^RSpec.feature "Posts", #{type_metatag(:feature)}/)) 21 | end 22 | end 23 | end 24 | 25 | describe 'are generated with the correct namespace' do 26 | before do 27 | run_generator %w[folder/posts] 28 | end 29 | 30 | describe 'the spec' do 31 | subject(:feature_spec) { file('spec/features/folder/posts_spec.rb') } 32 | 33 | it 'includes the standard boilerplate' do 34 | expect(feature_spec).to contain(/^RSpec.feature "Folder::Posts", #{type_metatag(:feature)}/) 35 | end 36 | end 37 | end 38 | 39 | describe 'are singularized appropriately with the --singularize flag' do 40 | before do 41 | run_generator %w[posts --singularize] 42 | end 43 | 44 | describe 'the spec' do 45 | subject(:feature_spec) { file('spec/features/post_spec.rb') } 46 | 47 | it "contains the singularized feature" do 48 | expect(feature_spec).to contain(/^RSpec.feature "Post", #{type_metatag(:feature)}/) 49 | end 50 | end 51 | end 52 | 53 | describe "are not generated" do 54 | before do 55 | run_generator %w[posts --no-feature-specs] 56 | end 57 | 58 | describe "the spec" do 59 | subject(:feature_spec) { file('spec/features/posts_spec.rb') } 60 | 61 | it "does not exist" do 62 | expect(File.exist?(feature_spec)).to be false 63 | end 64 | end 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /spec/generators/rspec/generator/generator_generator_spec.rb: -------------------------------------------------------------------------------- 1 | require 'generators/rspec/generator/generator_generator' 2 | require 'support/generators' 3 | 4 | RSpec.describe Rspec::Generators::GeneratorGenerator, type: :generator do 5 | setup_default_destination 6 | 7 | describe "generator specs" do 8 | subject(:generator_spec) { file("spec/generator/posts_generator_spec.rb") } 9 | before do 10 | run_generator %w[posts] 11 | end 12 | 13 | it "include the standard boilerplate" do 14 | expect(generator_spec).to contain(/require 'rails_helper'/).and(contain(/^RSpec.describe "PostsGenerator", #{type_metatag(:generator)}/)) 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/generators/rspec/helper/helper_generator_spec.rb: -------------------------------------------------------------------------------- 1 | # Generators are not automatically loaded by Rails 2 | require 'generators/rspec/helper/helper_generator' 3 | require 'support/generators' 4 | 5 | RSpec.describe Rspec::Generators::HelperGenerator, type: :generator do 6 | setup_default_destination 7 | 8 | subject(:helper_spec) { file('spec/helpers/posts_helper_spec.rb') } 9 | 10 | describe 'generated by default' do 11 | before do 12 | run_generator %w[posts] 13 | end 14 | 15 | it 'includes the standard boilerplate' do 16 | expect(helper_spec).to contain(/require 'rails_helper'/).and(contain(/^RSpec.describe PostsHelper, #{type_metatag(:helper)}/)) 17 | end 18 | end 19 | 20 | describe 'skipped with a flag' do 21 | before do 22 | run_generator %w[posts --no-helper_specs] 23 | end 24 | 25 | it 'does not create the helper spec' do 26 | expect(File.exist?(helper_spec)).to be false 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /spec/generators/rspec/job/job_generator_spec.rb: -------------------------------------------------------------------------------- 1 | # Generators are not automatically loaded by Rails 2 | require 'generators/rspec/job/job_generator' 3 | require 'support/generators' 4 | 5 | RSpec.describe Rspec::Generators::JobGenerator, type: :generator, skip: !RSpec::Rails::FeatureCheck.has_active_job? do 6 | setup_default_destination 7 | 8 | describe 'the generated files' do 9 | before { run_generator [file_name] } 10 | 11 | subject(:job_spec) { file('spec/jobs/user_job_spec.rb') } 12 | 13 | context 'with file_name without job as suffix' do 14 | let(:file_name) { 'user' } 15 | 16 | it 'creates the standard boiler plate' do 17 | expect(job_spec).to contain(/require 'rails_helper'/).and(contain(/describe UserJob, #{type_metatag(:job)}/)) 18 | end 19 | end 20 | 21 | context 'with file_name with job as suffix' do 22 | let(:file_name) { 'user_job' } 23 | 24 | it 'creates the standard boiler plate' do 25 | expect(job_spec).to contain(/require 'rails_helper'/).and(contain(/describe UserJob, #{type_metatag(:job)}/)) 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /spec/generators/rspec/mailbox/mailbox_generator_spec.rb: -------------------------------------------------------------------------------- 1 | # Generators are not automatically loaded by Rails 2 | require 'generators/rspec/mailbox/mailbox_generator' 3 | require 'support/generators' 4 | 5 | RSpec.describe Rspec::Generators::MailboxGenerator, type: :generator, skip: !RSpec::Rails::FeatureCheck.has_action_mailbox? do 6 | setup_default_destination 7 | 8 | describe 'the generated files' do 9 | before { run_generator %w[forwards] } 10 | 11 | subject(:mailbox_spec) { file('spec/mailboxes/forwards_mailbox_spec.rb') } 12 | 13 | it 'generates the file' do 14 | expect( 15 | mailbox_spec 16 | ).to contain(/require 'rails_helper'/).and contain(/describe ForwardsMailbox, #{type_metatag(:mailbox)}/) 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /spec/generators/rspec/model/model_generator_spec.rb: -------------------------------------------------------------------------------- 1 | # Generators are not automatically loaded by Rails 2 | require 'generators/rspec/model/model_generator' 3 | require 'support/generators' 4 | 5 | RSpec.describe Rspec::Generators::ModelGenerator, type: :generator do 6 | setup_default_destination 7 | 8 | it 'runs both the model and fixture tasks' do 9 | gen = generator %w[posts] 10 | expect(gen).to receive :create_model_spec 11 | expect(gen).to receive :create_fixture_file 12 | gen.invoke_all 13 | end 14 | 15 | it_behaves_like 'a model generator with fixtures', 'admin/posts', 'Admin::Posts' 16 | it_behaves_like 'a model generator with fixtures', 'posts', 'Posts' 17 | 18 | describe 'the generated files' do 19 | describe 'without fixtures' do 20 | before do 21 | run_generator %w[posts] 22 | end 23 | 24 | describe 'the fixtures' do 25 | it "will skip the file" do 26 | expect(File.exist?(file('spec/fixtures/posts.yml'))).to be false 27 | end 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/generators/rspec/request/request_generator_spec.rb: -------------------------------------------------------------------------------- 1 | # Generators are not automatically loaded by Rails 2 | require 'generators/rspec/request/request_generator' 3 | require 'support/generators' 4 | 5 | RSpec.describe Rspec::Generators::RequestGenerator, type: :generator do 6 | setup_default_destination 7 | it_behaves_like "a request spec generator" 8 | end 9 | -------------------------------------------------------------------------------- /spec/generators/rspec/system/system_generator_spec.rb: -------------------------------------------------------------------------------- 1 | # Generators are not automatically loaded by rails 2 | require 'generators/rspec/system/system_generator' 3 | require 'support/generators' 4 | 5 | RSpec.describe Rspec::Generators::SystemGenerator, type: :generator do 6 | setup_default_destination 7 | 8 | describe "system specs" do 9 | subject(:system_spec) { file("spec/system/posts_spec.rb") } 10 | 11 | describe "are generated independently from the command line" do 12 | before do 13 | run_generator %w[posts] 14 | end 15 | 16 | describe "the spec" do 17 | it "contains the standard boilerplate" do 18 | expect(system_spec).to contain(/require 'rails_helper'/).and(contain(/^RSpec.describe "Posts", #{type_metatag(:system)}/)) 19 | end 20 | end 21 | end 22 | 23 | describe "are not generated" do 24 | before do 25 | run_generator %w[posts --no-system-specs] 26 | end 27 | 28 | describe "the spec" do 29 | it "does not exist" do 30 | expect(File.exist?(system_spec)).to be false 31 | end 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /spec/generators/rspec/view/view_generator_spec.rb: -------------------------------------------------------------------------------- 1 | # Generators are not automatically loaded by Rails 2 | require 'generators/rspec/view/view_generator' 3 | require 'support/generators' 4 | 5 | RSpec.describe Rspec::Generators::ViewGenerator, type: :generator do 6 | setup_default_destination 7 | 8 | describe 'with default template engine' do 9 | it 'generates a spec for the supplied action' do 10 | run_generator %w[posts index] 11 | file('spec/views/posts/index.html.erb_spec.rb').tap do |f| 12 | expect(f).to contain(/require 'rails_helper'/) 13 | expect(f).to contain(/^RSpec.describe "posts\/index", #{type_metatag(:view)}/) 14 | end 15 | end 16 | 17 | describe 'with a nested resource' do 18 | it 'generates a spec for the supplied action' do 19 | run_generator %w[admin/posts index] 20 | file('spec/views/admin/posts/index.html.erb_spec.rb').tap do |f| 21 | expect(f).to contain(/require 'rails_helper'/) 22 | expect(f).to contain(/^RSpec.describe "admin\/posts\/index", #{type_metatag(:view)}/) 23 | end 24 | end 25 | end 26 | end 27 | 28 | describe 'with a specified template engine' do 29 | it 'generates a spec for the supplied action' do 30 | run_generator %w[posts index --template_engine haml] 31 | file('spec/views/posts/index.html.haml_spec.rb').tap do |f| 32 | expect(f).to contain(/require 'rails_helper'/) 33 | expect(f).to contain(/^RSpec.describe "posts\/index", #{type_metatag(:view)}/) 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /spec/rspec/rails/active_model_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "ActiveModel support" do 2 | around do |ex| 3 | old_value = RSpec::Mocks.configuration.verify_partial_doubles? 4 | ex.run 5 | RSpec::Mocks.configuration.verify_partial_doubles = old_value 6 | end 7 | 8 | RSpec.shared_examples_for "stubbing ActiveModel" do 9 | before do 10 | stub_const 'ActiveRecord' unless defined?(ActiveRecord) 11 | end 12 | 13 | it "allows you to stub `ActiveModel`" do 14 | allow(ActiveModel).to receive(:inspect).and_return("stubbed inspect") 15 | expect(ActiveModel.inspect).to eq "stubbed inspect" 16 | end 17 | 18 | it 'allows you to stub instances of `ActiveModel`' do 19 | klass = Class.new do 20 | include ActiveModel::AttributeMethods 21 | attr_accessor :name 22 | end 23 | model = klass.new 24 | allow(model).to receive(:name) { 'stubbed name' } 25 | expect(model.name).to eq 'stubbed name' 26 | end 27 | end 28 | 29 | context "with partial double verification enabled" do 30 | before do 31 | RSpec::Mocks.configuration.verify_partial_doubles = true 32 | end 33 | 34 | include_examples "stubbing ActiveModel" 35 | end 36 | 37 | context "with partial double verification disabled" do 38 | before do 39 | RSpec::Mocks.configuration.verify_partial_doubles = false 40 | end 41 | 42 | include_examples "stubbing ActiveModel" 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /spec/rspec/rails/active_record_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "ActiveRecord support" do 2 | around do |ex| 3 | old_value = RSpec::Mocks.configuration.verify_partial_doubles? 4 | ex.run 5 | RSpec::Mocks.configuration.verify_partial_doubles = old_value 6 | end 7 | 8 | RSpec.shared_examples_for "stubbing ActiveRecord::Base" do 9 | it "allows you to stub `ActiveRecord::Base`" do 10 | allow(ActiveRecord::Base).to receive(:inspect).and_return("stubbed inspect") 11 | expect(ActiveRecord::Base.inspect).to eq "stubbed inspect" 12 | end 13 | end 14 | 15 | RSpec.shared_examples_for "stubbing abstract classes" do 16 | it "allows you to stub abstract classes" do 17 | klass = Class.new(ActiveRecord::Base) do 18 | self.abstract_class = true 19 | end 20 | allow(klass).to receive(:find).and_return("stubbed find") 21 | expect(klass.find(1)).to eq "stubbed find" 22 | end 23 | end 24 | 25 | context "with partial double verification enabled" do 26 | before do 27 | RSpec::Mocks.configuration.verify_partial_doubles = true 28 | end 29 | 30 | include_examples "stubbing ActiveRecord::Base" 31 | include_examples "stubbing abstract classes" 32 | end 33 | 34 | context "with partial double verification disabled" do 35 | before do 36 | RSpec::Mocks.configuration.verify_partial_doubles = false 37 | end 38 | 39 | include_examples "stubbing ActiveRecord::Base" 40 | include_examples "stubbing abstract classes" 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /spec/rspec/rails/assertion_adapter_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe RSpec::Rails::MinitestAssertionAdapter do 2 | include RSpec::Rails::MinitestAssertionAdapter 3 | 4 | RSpec::Rails::Assertions.public_instance_methods.select { |m| m.to_s =~ /^(assert|flunk|refute)/ }.each do |m| 5 | if m.to_s == "assert_equal" 6 | it "exposes #{m} to host examples" do 7 | assert_equal 3, 3 8 | expect do 9 | assert_equal 3, 4 10 | end.to raise_error(ActiveSupport::TestCase::Assertion) 11 | end 12 | else 13 | it "exposes #{m} to host examples" do 14 | expect(methods).to include(m) 15 | end 16 | end 17 | end 18 | 19 | it "does not expose internal methods of Minitest" do 20 | expect(methods).not_to include("_assertions") 21 | end 22 | 23 | it "does not expose Minitest's message method" do 24 | expect(methods).not_to include("message") 25 | end 26 | 27 | it 'does not leak TestUnit specific methods into the AssertionDelegator' do 28 | expect(methods).to_not include(:build_message) 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /spec/rspec/rails/assertion_delegator_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe RSpec::Rails::AssertionDelegator do 2 | it "provides a module that delegates assertion methods to an isolated class" do 3 | klass = Class.new { 4 | include RSpec::Rails::AssertionDelegator.new(RSpec::Rails::Assertions) 5 | } 6 | 7 | expect(klass.new).to respond_to(:assert) 8 | end 9 | 10 | it "delegates back to the including instance for methods the assertion module requires" do 11 | assertions = Module.new { 12 | def has_thing?(thing) 13 | things.include?(thing) 14 | end 15 | } 16 | 17 | klass = Class.new { 18 | include RSpec::Rails::AssertionDelegator.new(assertions) 19 | 20 | def things 21 | [:a] 22 | end 23 | } 24 | 25 | expect(klass.new).to have_thing(:a) 26 | expect(klass.new).not_to have_thing(:b) 27 | end 28 | 29 | it "does not delegate method_missing" do 30 | assertions = Module.new { 31 | def method_missing(method, *args) 32 | end 33 | } 34 | 35 | klass = Class.new { 36 | include RSpec::Rails::AssertionDelegator.new(assertions) 37 | } 38 | 39 | expect { klass.new.abc123 }.to raise_error(NoMethodError) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /spec/rspec/rails/example/channel_example_group_spec.rb: -------------------------------------------------------------------------------- 1 | require "rspec/rails/feature_check" 2 | 3 | module RSpec::Rails 4 | RSpec.describe ChannelExampleGroup do 5 | if RSpec::Rails::FeatureCheck.has_action_cable_testing? 6 | it_behaves_like "an rspec-rails example group mixin", :channel, 7 | './spec/channels/', '.\\spec\\channels\\' 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /spec/rspec/rails/example/feature_example_group_spec.rb: -------------------------------------------------------------------------------- 1 | module RSpec::Rails 2 | RSpec.describe FeatureExampleGroup do 3 | it_behaves_like "an rspec-rails example group mixin", :feature, 4 | './spec/features/', '.\\spec\\features\\' 5 | 6 | it "includes Rails route helpers" do 7 | with_isolated_stderr do 8 | Rails.application.routes.draw do 9 | get "/foo", as: :foo, to: "foo#bar" 10 | end 11 | end 12 | 13 | group = RSpec::Core::ExampleGroup.describe do 14 | include FeatureExampleGroup 15 | end 16 | 17 | expect(group.new.foo_path).to eq("/foo") 18 | expect(group.new.foo_url).to eq("http://www.example.com/foo") 19 | end 20 | 21 | context "when nested inside a request example group" do 22 | it "includes Rails route helpers" do 23 | Rails.application.routes.draw do 24 | get "/foo", as: :foo, to: "foo#bar" 25 | end 26 | 27 | outer_group = RSpec::Core::ExampleGroup.describe do 28 | include RequestExampleGroup 29 | end 30 | group = outer_group.describe do 31 | include FeatureExampleGroup 32 | end 33 | 34 | expect(group.new.foo_path).to eq("/foo") 35 | expect(group.new.foo_url).to eq("http://www.example.com/foo") 36 | end 37 | end 38 | 39 | describe "#visit" do 40 | it "raises an error informing about missing Capybara" do 41 | group = RSpec::Core::ExampleGroup.describe do 42 | include FeatureExampleGroup 43 | end 44 | 45 | expect { 46 | group.new.visit('/foobar') 47 | }.to raise_error(/Capybara not loaded/) 48 | end 49 | 50 | it "is resistant to load order errors" do 51 | capybara = Module.new do 52 | def visit(url) 53 | "success: #{url}" 54 | end 55 | end 56 | 57 | group = RSpec::Core::ExampleGroup.describe do 58 | include capybara 59 | include FeatureExampleGroup 60 | end 61 | 62 | expect(group.new.visit("/foo")).to eq("success: /foo") 63 | end 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /spec/rspec/rails/example/job_example_group_spec.rb: -------------------------------------------------------------------------------- 1 | module RSpec::Rails 2 | RSpec.describe JobExampleGroup do 3 | if defined?(ActiveJob) 4 | it_behaves_like "an rspec-rails example group mixin", :job, 5 | './spec/jobs/', '.\\spec\\jobs\\' 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/rspec/rails/example/mailer_example_group_spec.rb: -------------------------------------------------------------------------------- 1 | module RSpec::Rails 2 | RSpec.describe MailerExampleGroup do 3 | module ::Rails; end 4 | before do 5 | allow(Rails).to receive_message_chain(:application, :routes, :url_helpers).and_return(Rails) 6 | allow(Rails.application).to receive(:config).and_return(double("Rails.application.config").as_null_object) 7 | allow(Rails).to receive_message_chain(:configuration, :action_mailer, :default_url_options).and_return({}) 8 | end 9 | 10 | it_behaves_like "an rspec-rails example group mixin", :mailer, 11 | './spec/mailers/', '.\\spec\\mailers\\' 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/rspec/rails/example/model_example_group_spec.rb: -------------------------------------------------------------------------------- 1 | module RSpec::Rails 2 | RSpec.describe ModelExampleGroup do 3 | it_behaves_like "an rspec-rails example group mixin", :model, 4 | './spec/models/', '.\\spec\\models\\' 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /spec/rspec/rails/example/rails_example_group_spec.rb: -------------------------------------------------------------------------------- 1 | module RSpec::Rails 2 | RSpec.describe RailsExampleGroup do 3 | it 'supports tagged_logger' do 4 | expect(described_class.private_instance_methods).to include(:tagged_logger) 5 | end 6 | 7 | it 'does not leak context between example groups' do 8 | groups = 9 | [ 10 | RSpec::Core::ExampleGroup.describe("A group") do 11 | include RSpec::Rails::RailsExampleGroup 12 | specify { expect(ActiveSupport::ExecutionContext.to_h).to eq({}) } 13 | end, 14 | RSpec::Core::ExampleGroup.describe("A controller group", type: :controller) do 15 | specify do 16 | Rails.error.set_context(foo: "bar") 17 | expect(ActiveSupport::ExecutionContext.to_h).to eq(foo: "bar") 18 | end 19 | end, 20 | RSpec::Core::ExampleGroup.describe("Another group") do 21 | include RSpec::Rails::RailsExampleGroup 22 | specify { expect(ActiveSupport::ExecutionContext.to_h).to eq({}) } 23 | end 24 | ] 25 | 26 | results = 27 | groups.map do |group| 28 | group.run(failure_reporter) ? true : failure_reporter.exceptions 29 | end 30 | 31 | expect(results).to all be true 32 | end 33 | 34 | it 'will not leak ActiveSupport::CurrentAttributes between examples' do 35 | group = 36 | RSpec::Core::ExampleGroup.describe("A group", order: :defined) do 37 | include RSpec::Rails::RailsExampleGroup 38 | 39 | # rubocop:disable Lint/ConstantDefinitionInBlock 40 | class CurrentSample < ActiveSupport::CurrentAttributes 41 | attribute :request_id 42 | end 43 | # rubocop:enable Lint/ConstantDefinitionInBlock 44 | 45 | it 'sets a current attribute' do 46 | CurrentSample.request_id = '123' 47 | expect(CurrentSample.request_id).to eq('123') 48 | end 49 | 50 | it 'does not leak current attributes' do 51 | expect(CurrentSample.request_id).to eq(nil) 52 | end 53 | end 54 | 55 | expect( 56 | group.run(failure_reporter) ? true : failure_reporter.exceptions 57 | ).to be true 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /spec/rspec/rails/example/request_example_group_spec.rb: -------------------------------------------------------------------------------- 1 | module RSpec::Rails 2 | RSpec.describe RequestExampleGroup do 3 | it_behaves_like "an rspec-rails example group mixin", :request, 4 | './spec/requests/', '.\\spec\\requests\\', 5 | './spec/integration/', '.\\spec\\integration\\', 6 | './spec/api/', '.\\spec\\api\\' 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/rspec/rails/example/routing_example_group_spec.rb: -------------------------------------------------------------------------------- 1 | module RSpec::Rails 2 | RSpec.describe RoutingExampleGroup do 3 | it_behaves_like "an rspec-rails example group mixin", :routing, 4 | './spec/routing/', '.\\spec\\routing\\' 5 | 6 | describe "named routes" do 7 | it "delegates them to the route_set" do 8 | group = RSpec::Core::ExampleGroup.describe do 9 | include RoutingExampleGroup 10 | end 11 | 12 | example = group.new 13 | 14 | # Yes, this is quite invasive 15 | url_helpers = double('url_helpers', foo_path: "foo") 16 | routes = double('routes', url_helpers: url_helpers) 17 | allow(example).to receive_messages(routes: routes) 18 | 19 | expect(example.foo_path).to eq("foo") 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/rspec/rails/fixture_file_upload_support_spec.rb: -------------------------------------------------------------------------------- 1 | module RSpec::Rails 2 | RSpec.describe FixtureFileUploadSupport do 3 | context 'with fixture paths set in config' do 4 | it 'resolves fixture file' do 5 | RSpec.configuration.fixture_paths = [File.dirname(__FILE__)] 6 | expect_to_pass fixture_file_upload_resolved('fixture_file_upload_support_spec.rb') 7 | end 8 | 9 | it 'resolves supports `Pathname` objects' do 10 | RSpec.configuration.fixture_paths = [Pathname(File.dirname(__FILE__))] 11 | expect_to_pass fixture_file_upload_resolved('fixture_file_upload_support_spec.rb') 12 | end 13 | end 14 | 15 | context 'with fixture paths set in spec' do 16 | it 'resolves fixture file' do 17 | expect_to_pass fixture_file_upload_resolved('fixture_file_upload_support_spec.rb', File.dirname(__FILE__)) 18 | end 19 | end 20 | 21 | context 'with fixture paths not set' do 22 | it 'resolves fixture using relative path' do 23 | RSpec.configuration.fixture_paths = [] 24 | expect_to_pass fixture_file_upload_resolved('spec/rspec/rails/fixture_file_upload_support_spec.rb') 25 | end 26 | end 27 | 28 | def expect_to_pass(group) 29 | result = group.run(failure_reporter) 30 | failure_reporter.exceptions.map { |e| raise e } 31 | expect(result).to be true 32 | end 33 | 34 | def fixture_file_upload_resolved(fixture_name, file_fixture_path = nil) 35 | RSpec::Core::ExampleGroup.describe do 36 | include RSpec::Rails::FixtureFileUploadSupport 37 | 38 | self.file_fixture_path = file_fixture_path 39 | 40 | it 'supports fixture file upload' do 41 | file = fixture_file_upload(fixture_name) 42 | expect(file.read).to match(/describe FixtureFileUploadSupport/im) 43 | end 44 | end 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /spec/rspec/rails/fixture_support_spec.rb: -------------------------------------------------------------------------------- 1 | module RSpec::Rails 2 | RSpec.describe FixtureSupport do 3 | context "with use_transactional_fixtures set to false" do 4 | it "still supports fixture_path/fixture_paths" do 5 | allow(RSpec.configuration).to receive(:use_transactional_fixtures) { false } 6 | group = RSpec::Core::ExampleGroup.describe do 7 | include FixtureSupport 8 | end 9 | 10 | expect(group).to respond_to(:fixture_paths) 11 | expect(group).to respond_to(:fixture_paths=) 12 | end 13 | end 14 | 15 | context "with use_transactional_tests set to true" do 16 | it "works with #uses_transaction helper" do 17 | group = RSpec::Core::ExampleGroup.describe do 18 | include FixtureSupport 19 | self.use_transactional_tests = true 20 | 21 | uses_transaction "doesn't run in transaction" 22 | 23 | it "doesn't run in transaction" do 24 | expect(ActiveRecord::Base.connection.transaction_open?).to eq(false) 25 | end 26 | 27 | it "runs in transaction" do 28 | expect(ActiveRecord::Base.connection.transaction_open?).to eq(true) 29 | end 30 | end 31 | 32 | expect_to_pass(group) 33 | end 34 | end 35 | 36 | context "with use_transactional_tests set to false" do 37 | it "does not wrap the test in a transaction" do 38 | allow(RSpec.configuration).to receive(:use_transactional_fixtures) { true } 39 | group = RSpec::Core::ExampleGroup.describe do 40 | include FixtureSupport 41 | 42 | self.use_transactional_tests = false 43 | 44 | it "doesn't run in transaction" do 45 | expect(ActiveRecord::Base.connection.transaction_open?).to eq(false) 46 | end 47 | end 48 | 49 | expect_to_pass(group) 50 | end 51 | end 52 | 53 | it "handles namespaced fixtures" do 54 | group = RSpec::Core::ExampleGroup.describe do 55 | include FixtureSupport 56 | fixtures 'namespaced/model' 57 | 58 | it 'has the fixture' do 59 | namespaced_model(:one) 60 | end 61 | end 62 | group.fixture_paths = [File.expand_path('../../support/fixtures', __dir__)] 63 | 64 | expect_to_pass(group) 65 | end 66 | 67 | def expect_to_pass(group) 68 | result = group.run(failure_reporter) 69 | failure_reporter.exceptions.map { |e| raise e } 70 | expect(result).to be true 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /spec/rspec/rails/matchers/action_mailbox_spec.rb: -------------------------------------------------------------------------------- 1 | require "rspec/rails/feature_check" 2 | 3 | class ApplicationMailbox 4 | class Router 5 | def match_to_mailbox(*) 6 | Inbox 7 | end 8 | end 9 | 10 | def self.router 11 | Router.new 12 | end 13 | end 14 | 15 | class Inbox < ApplicationMailbox; end 16 | class Otherbox < ApplicationMailbox; end 17 | 18 | RSpec.describe "ActionMailbox matchers", skip: !RSpec::Rails::FeatureCheck.has_action_mailbox? do 19 | describe "receive_inbound_email" do 20 | let(:to) { ['to@example.com'] } 21 | 22 | before do 23 | allow(RSpec::Rails::MailboxExampleGroup).to receive(:create_inbound_email) do |attributes| 24 | mail = double('Mail::Message', attributes) 25 | double('InboundEmail', mail: mail) 26 | end 27 | end 28 | 29 | it "passes when it receives inbound email" do 30 | expect(Inbox).to receive_inbound_email(to: to) 31 | end 32 | 33 | it "passes when negated when it doesn't receive inbound email" do 34 | expect(Otherbox).not_to receive_inbound_email(to: to) 35 | end 36 | 37 | it "fails when it doesn't receive inbound email" do 38 | expect { 39 | expect(Otherbox).to receive_inbound_email(to: to) 40 | }.to raise_error(/expected mail to to@example.com to route to Otherbox, but routed to Inbox/) 41 | end 42 | 43 | it "fails when negated when it receives inbound email" do 44 | expect { 45 | expect(Inbox).not_to receive_inbound_email(to: to) 46 | }.to raise_error(/expected mail to to@example.com not to route to Inbox/) 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /spec/rspec/rails/matchers/be_new_record_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "be_new_record" do 2 | context "a new record" do 3 | let(:record) { double('record', new_record?: true) } 4 | 5 | it "passes" do 6 | expect(record).to be_new_record 7 | end 8 | 9 | it "fails with custom failure message" do 10 | expect { 11 | expect(record).not_to be_new_record 12 | }.to raise_exception(/expected .* to be persisted, but was a new record/) 13 | end 14 | end 15 | 16 | context "a persisted record" do 17 | let(:record) { double('record', new_record?: false) } 18 | 19 | it "fails" do 20 | expect(record).not_to be_new_record 21 | end 22 | 23 | it "fails with custom failure message" do 24 | expect { 25 | expect(record).to be_new_record 26 | }.to raise_exception(/expected .* to be a new record, but was persisted/) 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /spec/rspec/rails/matchers/be_routable_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "be_routable" do 2 | include RSpec::Rails::Matchers::RoutingMatchers 3 | attr_reader :routes 4 | 5 | before { @routes = double("routes") } 6 | 7 | it "provides a description" do 8 | expect(be_routable.description).to eq("be routable") 9 | end 10 | 11 | context "with should" do 12 | it "passes if routes recognize the path" do 13 | allow(routes).to receive(:recognize_path) { {} } 14 | expect do 15 | expect({ get: "/a/path" }).to be_routable 16 | end.to_not raise_error 17 | end 18 | 19 | it "fails if routes do not recognize the path" do 20 | allow(routes).to receive(:recognize_path) { raise ActionController::RoutingError, 'ignore' } 21 | 22 | message = 23 | if RUBY_VERSION >= '3.4' 24 | /expected \{get: "\/a\/path"\} to be routable/ 25 | else 26 | /expected \{:get=>"\/a\/path"\} to be routable/ 27 | end 28 | 29 | expect do 30 | expect({ get: "/a/path" }).to be_routable 31 | end.to raise_error(message) 32 | end 33 | end 34 | 35 | context "with should_not" do 36 | 37 | it "passes if routes do not recognize the path" do 38 | allow(routes).to receive(:recognize_path) { raise ActionController::RoutingError, 'ignore' } 39 | expect do 40 | expect({ get: "/a/path" }).not_to be_routable 41 | end.to_not raise_error 42 | end 43 | 44 | it "fails if routes recognize the path" do 45 | allow(routes).to receive(:recognize_path) { { controller: "foo" } } 46 | 47 | message = 48 | if RUBY_VERSION >= '3.4' 49 | /expected \{get: "\/a\/path"\} not to be routable, but it routes to \{controller: "foo"\}/ 50 | else 51 | /expected \{:get=>"\/a\/path"\} not to be routable, but it routes to \{:controller=>"foo"\}/ 52 | end 53 | 54 | expect do 55 | expect({ get: "/a/path" }).not_to be_routable 56 | end.to raise_error(message) 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /spec/rspec/rails/matchers/be_valid_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rspec/rails/matchers/be_valid' 2 | 3 | RSpec.describe "be_valid matcher" do 4 | class Post 5 | include ActiveModel::Validations 6 | attr_accessor :title 7 | validates_presence_of :title 8 | end 9 | 10 | class Book 11 | def valid? 12 | false 13 | end 14 | 15 | def errors 16 | ['the spine is broken', 'the pages are dog-eared'] 17 | end 18 | end 19 | 20 | class Boat 21 | def valid? 22 | false 23 | end 24 | end 25 | 26 | class Car 27 | def valid? 28 | false 29 | end 30 | 31 | def errors(_) 32 | end 33 | end 34 | 35 | let(:post) { Post.new } 36 | let(:book) { Book.new } 37 | let(:boat) { Boat.new } 38 | let(:car) { Car.new } 39 | 40 | it "includes the error messages in the failure message" do 41 | expect { 42 | expect(post).to be_valid 43 | }.to raise_exception(/Title can.t be blank/) 44 | end 45 | 46 | it "includes the error messages for simple implementations of error messages" do 47 | expect { 48 | expect(book).to be_valid 49 | }.to raise_exception(/the spine is broken/) 50 | end 51 | 52 | it "includes a brief error message for the simplest implementation of validity" do 53 | expect { 54 | expect(boat).to be_valid 55 | }.to raise_exception(/expected .+ to be valid\z/) 56 | end 57 | 58 | it "includes a brief error message when error message is wrong arity" do 59 | expect { 60 | expect(car).to be_valid 61 | }.to raise_exception(/expected .+ to be valid\z/) 62 | end 63 | 64 | it "includes a failure message for the negative case" do 65 | allow(post).to receive(:valid?) { true } 66 | expect { 67 | expect(post).not_to be_valid 68 | }.to raise_exception(/expected .* not to be valid/) 69 | end 70 | 71 | it "uses a custom failure message if provided" do 72 | expect { 73 | expect(post).to be_valid, "Post was not valid!" 74 | }.to raise_exception(/Post was not valid!/) 75 | end 76 | 77 | it "includes the validation context if provided" do 78 | expect(post).to receive(:valid?).with(:create) { true } 79 | expect(post).to be_valid(:create) 80 | end 81 | 82 | it "does not include the validation context if not provided" do 83 | expect(post).to receive(:valid?).with(no_args) { true } 84 | expect(post).to be_valid 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /spec/rspec/rails/matchers/has_spec.rb: -------------------------------------------------------------------------------- 1 | class CollectionOwner < ActiveRecord::Base 2 | connection.execute <<-SQL 3 | CREATE TABLE collection_owners ( 4 | id integer PRIMARY KEY AUTOINCREMENT 5 | ) 6 | SQL 7 | has_many :associated_items do 8 | def has_some_quality?; true end 9 | end 10 | end 11 | 12 | class AssociatedItem < ActiveRecord::Base 13 | connection.execute <<-SQL 14 | CREATE TABLE associated_items ( 15 | id integer PRIMARY KEY AUTOINCREMENT, 16 | collection_owner_id integer 17 | ) 18 | SQL 19 | belongs_to :collection_owner 20 | end 21 | 22 | RSpec.describe "should have_xxx" do 23 | it "works with ActiveRecord::Associations::CollectionProxy" do 24 | owner = CollectionOwner.new 25 | expect(owner.associated_items).to have_some_quality 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/rspec/rails/matchers/redirect_to_spec.rb: -------------------------------------------------------------------------------- 1 | require "active_support" 2 | require "active_support/test_case" 3 | 4 | RSpec.describe "redirect_to" do 5 | include RSpec::Rails::Matchers::RedirectTo 6 | 7 | let(:response) { ActionDispatch::TestResponse.new } 8 | 9 | context "with should" do 10 | context "when assert_redirected_to passes" do 11 | def assert_redirected_to(*); end 12 | 13 | it "passes" do 14 | expect do 15 | expect(response).to redirect_to("destination") 16 | end.to_not raise_exception 17 | end 18 | end 19 | 20 | context "when assert_redirected_to fails" do 21 | def assert_redirected_to(*) 22 | raise ActiveSupport::TestCase::Assertion, "this message" 23 | end 24 | 25 | it "uses failure message from assert_redirected_to" do 26 | expect do 27 | expect(response).to redirect_to("destination") 28 | end.to raise_exception("this message") 29 | end 30 | end 31 | 32 | context "when fails due to some other exception" do 33 | def assert_redirected_to(*) 34 | raise "oops" 35 | end 36 | 37 | it "raises that exception" do 38 | expect do 39 | expect(response).to redirect_to("destination") 40 | end.to raise_exception("oops") 41 | end 42 | end 43 | end 44 | 45 | context "with should_not" do 46 | context "when assert_redirected_to fails" do 47 | def assert_redirected_to(*) 48 | raise ActiveSupport::TestCase::Assertion, "this message" 49 | end 50 | 51 | it "passes" do 52 | expect do 53 | expect(response).not_to redirect_to("destination") 54 | end.to_not raise_exception 55 | end 56 | end 57 | 58 | context "when assert_redirected_to passes" do 59 | def assert_redirected_to(*); end 60 | 61 | it "fails with custom failure message" do 62 | expect do 63 | expect(response).not_to redirect_to("destination") 64 | end.to raise_exception(/expected not to redirect to "destination", but did/) 65 | end 66 | end 67 | 68 | context "when fails due to some other exception" do 69 | def assert_redirected_to(*) 70 | raise "oops" 71 | end 72 | 73 | it "raises that exception" do 74 | expect do 75 | expect(response).not_to redirect_to("destination") 76 | end.to raise_exception("oops") 77 | end 78 | end 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /spec/rspec/rails/matchers/relation_match_array_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe "ActiveSupport::Relation match_array matcher" do 2 | before { MockableModel.delete_all } 3 | 4 | let!(:models) { Array.new(3) { MockableModel.create } } 5 | 6 | it "verifies that the scope returns the records on the right hand side, regardless of order" do 7 | expect(MockableModel.all).to match_array(models.reverse) 8 | end 9 | 10 | it "fails if the scope encompasses more records than on the right hand side" do 11 | MockableModel.create 12 | expect(MockableModel.all).not_to match_array(models.reverse) 13 | end 14 | 15 | it "fails if the scope encompasses fewer records than on the right hand side" do 16 | expect(MockableModel.limit(models.length - 1)).not_to match_array(models.reverse) 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/rspec/rails/minitest_lifecycle_adapter_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe RSpec::Rails::MinitestLifecycleAdapter do 2 | it "invokes minitest lifecycle hooks at the appropriate times" do 3 | invocations = [] 4 | example_group = RSpec::Core::ExampleGroup.describe("MinitestHooks") do 5 | include RSpec::Rails::MinitestLifecycleAdapter 6 | 7 | define_method(:before_setup) { invocations << :before_setup } 8 | define_method(:after_setup) { invocations << :after_setup } 9 | define_method(:before_teardown) { invocations << :before_teardown } 10 | define_method(:after_teardown) { invocations << :after_teardown } 11 | end 12 | 13 | example_group.example("foo") { invocations << :example } 14 | example_group.run(NullObject.new) 15 | 16 | expect(invocations).to eq([ 17 | :before_setup, :after_setup, :example, :before_teardown, :after_teardown 18 | ]) 19 | end 20 | 21 | it "allows let variables named 'send'" do 22 | run_result = ::RSpec::Core::ExampleGroup.describe do 23 | let(:send) { "WHAT" } 24 | specify { expect(send).to eq "WHAT" } 25 | end.run NullObject.new 26 | 27 | expect(run_result).to be true 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /spec/rspec/rails/setup_and_teardown_adapter_spec.rb: -------------------------------------------------------------------------------- 1 | RSpec.describe RSpec::Rails::SetupAndTeardownAdapter do 2 | describe ".setup" do 3 | it "registers before hooks in the order setup is received" do 4 | group = RSpec::Core::ExampleGroup.describe do 5 | include RSpec::Rails::SetupAndTeardownAdapter 6 | def self.foo; "foo"; end 7 | def self.bar; "bar"; end 8 | end 9 | expect(group).to receive(:before).ordered { |&block| expect(block.call).to eq "foo" } 10 | expect(group).to receive(:before).ordered { |&block| expect(block.call).to eq "bar" } 11 | expect(group).to receive(:before).ordered { |&block| expect(block.call).to eq "baz" } 12 | 13 | group.setup :foo 14 | group.setup :bar 15 | group.setup { "baz" } 16 | end 17 | 18 | it "registers prepend_before hooks for the Rails' setup methods" do 19 | group = RSpec::Core::ExampleGroup.describe do 20 | include RSpec::Rails::SetupAndTeardownAdapter 21 | def self.setup_fixtures; "setup fixtures" end 22 | def self.setup_controller_request_and_response; "setup controller" end 23 | end 24 | 25 | expect(group).to receive(:prepend_before) { |&block| expect(block.call).to eq "setup fixtures" } 26 | expect(group).to receive(:prepend_before) { |&block| expect(block.call).to eq "setup controller" } 27 | 28 | group.setup :setup_fixtures 29 | group.setup :setup_controller_request_and_response 30 | end 31 | 32 | it "registers teardown hooks in the order setup is received" do 33 | group = RSpec::Core::ExampleGroup.describe do 34 | include RSpec::Rails::SetupAndTeardownAdapter 35 | def self.foo; "foo"; end 36 | def self.bar; "bar"; end 37 | end 38 | expect(group).to receive(:after).ordered { |&block| expect(block.call).to eq "foo" } 39 | expect(group).to receive(:after).ordered { |&block| expect(block.call).to eq "bar" } 40 | expect(group).to receive(:after).ordered { |&block| expect(block.call).to eq "baz" } 41 | 42 | group.teardown :foo 43 | group.teardown :bar 44 | group.teardown { "baz" } 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /spec/rspec/rails/view_spec_methods_spec.rb: -------------------------------------------------------------------------------- 1 | module RSpec::Rails 2 | RSpec.describe ViewSpecMethods do 3 | before do 4 | class ::VCSampleClass; end 5 | end 6 | 7 | after do 8 | Object.send(:remove_const, :VCSampleClass) 9 | end 10 | 11 | describe ".add_extra_params_accessors_to" do 12 | describe "when accessors are not yet defined" do 13 | it "adds them as instance methods" do 14 | ViewSpecMethods.add_to(VCSampleClass) 15 | 16 | expect(VCSampleClass.instance_methods.map(&:to_sym)).to(include(:extra_params=)) 17 | expect(VCSampleClass.instance_methods.map(&:to_sym)).to(include(:extra_params)) 18 | end 19 | 20 | describe "the added #extra_params reader" do 21 | it "raises an error when a user tries to mutate it" do 22 | ViewSpecMethods.add_to(VCSampleClass) 23 | 24 | expect { 25 | VCSampleClass.new.extra_params[:id] = 4 26 | }.to raise_error(/can't modify frozen/) 27 | end 28 | end 29 | end 30 | 31 | describe "when accessors are already defined" do 32 | before do 33 | class ::VCSampleClass 34 | def extra_params; end 35 | 36 | def extra_params=; end 37 | end 38 | end 39 | 40 | it "does not redefine them" do 41 | ViewSpecMethods.add_to(VCSampleClass) 42 | expect(VCSampleClass.new.extra_params).to be_nil 43 | end 44 | end 45 | end 46 | 47 | describe ".remove_extra_params_accessors_from" do 48 | describe "when accessors are defined" do 49 | before do 50 | ViewSpecMethods.add_to(VCSampleClass) 51 | end 52 | 53 | it "removes them" do 54 | ViewSpecMethods.remove_from(VCSampleClass) 55 | 56 | expect(VCSampleClass.instance_methods).to_not include("extra_params=") 57 | expect(VCSampleClass.instance_methods).to_not include(:extra_params=) 58 | expect(VCSampleClass.instance_methods).to_not include("extra_params") 59 | expect(VCSampleClass.instance_methods).to_not include(:extra_params) 60 | end 61 | end 62 | 63 | describe "when accessors are not defined" do 64 | it "does nothing" do 65 | expect { 66 | ViewSpecMethods.remove_from(VCSampleClass) 67 | }.to_not change { VCSampleClass.instance_methods } 68 | end 69 | end 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /spec/rspec/rails_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rspec/support/spec/library_wide_checks' 2 | 3 | RSpec.describe "RSpec::Rails" do 4 | include RSpec::Support::WhitespaceChecks 5 | 6 | # Pasted from rspec-support lib/rspec/support/spec/library_wide_checks.rb:134 7 | # Easier to do that here than to extract it out 8 | RSpec::Matchers.define :be_well_formed do 9 | match do |actual| 10 | actual.empty? 11 | end 12 | 13 | failure_message do |actual| 14 | actual.join("\n") 15 | end 16 | end 17 | 18 | it "has no malformed whitespace", :slow do 19 | error_messages = [] 20 | `git ls-files -z`.split("\x0").each do |filename| 21 | error_messages << check_for_tab_characters(filename) 22 | error_messages << check_for_extra_spaces(filename) 23 | end 24 | expect(error_messages.compact).to be_well_formed 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /spec/sanity_check_spec.rb: -------------------------------------------------------------------------------- 1 | require 'pathname' 2 | 3 | RSpec.describe "Verify required rspec dependencies" do 4 | 5 | tmp_root = Pathname.new(RSpec::Core::RubyProject.root).join("tmp") 6 | 7 | before { FileUtils.mkdir_p tmp_root } 8 | 9 | def with_clean_env 10 | if Bundler.respond_to?(:with_unbundled_env) 11 | Bundler.with_unbundled_env { yield } 12 | else 13 | Bundler.with_clean_env { yield } 14 | end 15 | end 16 | 17 | it "fails when libraries are not required" do 18 | script = tmp_root.join("fail_sanity_check") 19 | File.open(script, "w") do |f| 20 | f.write <<-EOF.gsub(/^\s+\|/, '') 21 | |#!/usr/bin/env ruby 22 | |RSpec::Support.require_rspec_core "project_initializer" 23 | EOF 24 | end 25 | FileUtils.chmod 0777, script 26 | 27 | with_clean_env do 28 | expect(`bundle exec #{script} 2>&1`) 29 | .to match(/uninitialized constant RSpec::Support/) 30 | .or match(/undefined method `require_rspec_core' for RSpec::Support:Module/) 31 | .or match(/undefined method `require_rspec_core' for module RSpec::Support/) 32 | .or match(/undefined method 'require_rspec_core' for module RSpec::Support/) 33 | 34 | expect($?.exitstatus).to eq(1) 35 | end 36 | end 37 | 38 | it "passes when libraries are required", skip: RSpec::Support::Ruby.jruby? do 39 | script = tmp_root.join("pass_sanity_check") 40 | File.open(script, "w") do |f| 41 | f.write <<-EOF.gsub(/^\s+\|/, '') 42 | |#!/usr/bin/env ruby 43 | |require 'rspec/core' 44 | |require 'rspec/support' 45 | |RSpec::Support.require_rspec_core "project_initializer" 46 | EOF 47 | end 48 | FileUtils.chmod 0777, script 49 | 50 | with_clean_env do 51 | expect(`bundle exec #{script} 2>&1`).to be_empty 52 | expect($?.exitstatus).to eq(0) 53 | end 54 | end 55 | 56 | end 57 | -------------------------------------------------------------------------------- /spec/support/ar_classes.rb: -------------------------------------------------------------------------------- 1 | ActiveRecord::Base.establish_connection( 2 | adapter: 'sqlite3', 3 | database: ':memory:' 4 | ) 5 | 6 | module Connections 7 | def self.extended(host) 8 | fields = 9 | { host.primary_key => "integer PRIMARY KEY AUTOINCREMENT" } 10 | 11 | fields.merge!(host.connection_fields) if host.respond_to?(:connection_fields) 12 | 13 | host.connection.execute <<-EOSQL 14 | CREATE TABLE #{host.table_name} ( #{fields.map { |column, type| "#{column} #{type}"}.join(", ") }) 15 | EOSQL 16 | 17 | host.reset_column_information 18 | end 19 | end 20 | 21 | class NonActiveRecordModel 22 | extend ActiveModel::Naming 23 | include ActiveModel::Conversion 24 | end 25 | 26 | class MockableModel < ActiveRecord::Base 27 | def self.connection_fields 28 | { associated_model_id: :integer } 29 | end 30 | extend Connections 31 | 32 | has_one :associated_model 33 | end 34 | 35 | class SubMockableModel < MockableModel 36 | end 37 | 38 | class AssociatedModel < ActiveRecord::Base 39 | def self.connection_fields 40 | { mockable_model_id: :integer, nonexistent_model_id: :integer } 41 | end 42 | extend Connections 43 | 44 | belongs_to :mockable_model 45 | belongs_to :nonexistent_model, class_name: "Other" 46 | end 47 | 48 | class AlternatePrimaryKeyModel < ActiveRecord::Base 49 | self.primary_key = :my_id 50 | extend Connections 51 | 52 | attr_accessor :my_id 53 | end 54 | 55 | module Namespaced 56 | class Model < ActiveRecord::Base 57 | def self.connection_fields 58 | { name: :string } 59 | end 60 | 61 | extend Connections 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /spec/support/fixtures/namespaced/model.yml: -------------------------------------------------------------------------------- 1 | one: 2 | name: "Model #1" 3 | -------------------------------------------------------------------------------- /spec/support/group_failure_formatter.rb: -------------------------------------------------------------------------------- 1 | module RSpec::Rails::TestSupport 2 | class FailureReporter 3 | def initialize 4 | @exceptions = [] 5 | end 6 | attr_reader :exceptions 7 | 8 | def example_failed(example) 9 | @exceptions << example.exception 10 | end 11 | 12 | def method_missing(name, *_args, &_block) 13 | end 14 | end 15 | 16 | def failure_reporter 17 | @failure_reporter ||= FailureReporter.new 18 | end 19 | end 20 | 21 | RSpec.configure do |config| 22 | config.include RSpec::Rails::TestSupport 23 | end 24 | -------------------------------------------------------------------------------- /spec/support/null_object.rb: -------------------------------------------------------------------------------- 1 | class NullObject 2 | private 3 | 4 | def method_missing(method, *args, &blk) 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /yard/template/default/fulldoc/html/css/rspec.css: -------------------------------------------------------------------------------- 1 | .notetag { 2 | background-color: #FFE5E5; 3 | } 4 | 5 | code { 6 | background-color: #F0F0F0; 7 | padding: 0 2px 0 2px; 8 | } 9 | 10 | .deprecated code { 11 | background-color: #FFA8A8; 12 | } 13 | 14 | .summary_desc code { 15 | background-color: #F0F0F0; 16 | } 17 | 18 | .notetag code { 19 | background-color: #FFA8A8; 20 | } 21 | -------------------------------------------------------------------------------- /yard/template/default/layout/html/setup.rb: -------------------------------------------------------------------------------- 1 | # Docs suggest I don't need this, but ... 2 | YARD::Server::Commands::StaticFileCommand::STATIC_PATHS << File.expand_path('../../fulldoc/html', __dir__) 3 | 4 | def stylesheets 5 | super + ['css/rspec.css'] 6 | end 7 | --------------------------------------------------------------------------------