├── .document ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml └── workflows │ ├── ci.yml │ └── depsreview.yaml ├── .gitignore ├── .rspec ├── .rubocop.yml ├── .rubocop_rspec_base.yml ├── .rubocop_todo.yml ├── .yardopts ├── BUILD_DETAIL.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Capybara.md ├── Changelog.md ├── DEVELOPMENT.md ├── Gemfile ├── Gemfile-custom.sample ├── Gemfile-rails-dependencies ├── Gemfile-rspec-dependencies ├── LICENSE.md ├── README.md ├── README_DEV.md ├── Rakefile ├── SECURITY.md ├── Thorfile ├── appveyor.yml ├── benchmarks └── before_block_capture_block_vs_yield.rb ├── cucumber.yml ├── example_app_generator ├── app │ └── views │ │ ├── _example.html.erb │ │ ├── foo.html │ │ └── some_templates │ │ └── bar.html ├── ci_retry_bundle_install.sh ├── generate_action_mailer_specs.rb ├── generate_app.rb ├── generate_stuff.rb ├── log │ └── development.log ├── no_active_record │ ├── app │ │ └── models │ │ │ └── in_memory │ │ │ └── model.rb │ ├── config │ │ └── initializers │ │ │ └── zeitwerk.rb │ ├── lib │ │ └── rails │ │ │ └── generators │ │ │ └── in_memory │ │ │ └── model │ │ │ ├── model_generator.rb │ │ │ └── templates │ │ │ └── model.rb.erb │ └── spec │ │ ├── verify_fixture_file_upload_spec.rb │ │ ├── verify_no_active_record_spec.rb │ │ └── verify_no_fixture_setup_spec.rb ├── run_specs.rb └── spec │ ├── __verify_fixture_load_order_spec.rb │ ├── features │ └── model_mocks_integration_spec.rb │ ├── support │ └── default_preview_path │ ├── verify_active_record_spec.rb │ ├── verify_custom_renderers_spec.rb │ ├── verify_fixture_warning_spec.rb │ ├── verify_mailer_preview_path_spec.rb │ └── verify_view_path_stub_spec.rb ├── features ├── .nav ├── Generators.md ├── Getting_started.md ├── README.md ├── Transactions.md ├── backtrace_filtering.feature ├── channel_specs │ └── channel_spec.feature ├── controller_specs │ ├── README.md │ ├── anonymous_controller.feature │ ├── bypass_rescue.feature │ ├── controller_spec.feature │ ├── cookies.feature │ ├── engine_routes.feature │ ├── isolation_from_views.feature │ ├── render_views.feature │ └── setting_request_headers.feature ├── directory_structure.feature ├── feature_specs │ └── feature_spec.feature ├── file_fixture.feature ├── generator_specs │ ├── channel_specs.feature │ ├── controller_specs.feature │ ├── feature_specs.feature │ ├── generator_specs.feature │ ├── helper_specs.feature │ ├── job_specs.feature │ ├── mailbox_specs.feature │ ├── mailer_specs.feature │ ├── request_specs.feature │ ├── system_specs.feature │ └── view_specs.feature ├── helper_specs │ └── helper_spec.feature ├── job_specs │ └── job_spec.feature ├── mailbox_specs │ └── mailbox_spec.feature ├── mailer_specs │ ├── README.md │ ├── mailer_spec.feature │ └── url_helpers.feature ├── matchers │ ├── README.md │ ├── have_been_enqueued_matcher.feature │ ├── have_been_performed_matcher.feature │ ├── have_broadcasted_matcher.feature │ ├── have_enqueued_job_matcher.feature │ ├── have_enqueued_mail_matcher.feature │ ├── have_http_status_matcher.feature │ ├── have_performed_job_matcher.feature │ ├── have_stream_from_matcher.feature │ ├── new_record_matcher.feature │ ├── redirect_to_matcher.feature │ ├── relation_match_array.feature │ ├── render_template_matcher.feature │ └── send_email_matcher.feature ├── model_specs │ ├── README.md │ ├── transactional_examples.feature │ └── verified_doubles.feature ├── request_specs │ └── request_spec.feature ├── routing_specs │ ├── README.md │ ├── be_routable_matcher.feature │ ├── engine_routes.feature │ ├── named_routes.feature │ └── route_to_matcher.feature ├── step_definitions │ └── additional_cli_steps.rb ├── support │ ├── capybara.rb │ └── env.rb ├── system_specs │ └── system_specs.feature ├── upgrade │ └── README.md └── view_specs │ ├── inferred_controller_path.feature │ ├── stub_template.feature │ └── view_spec.feature ├── lib ├── generators │ ├── rspec.rb │ └── rspec │ │ ├── authentication │ │ ├── authentication_generator.rb │ │ └── templates │ │ │ ├── user_spec.rb │ │ │ └── users.yml │ │ ├── channel │ │ ├── channel_generator.rb │ │ └── templates │ │ │ └── channel_spec.rb.erb │ │ ├── controller │ │ ├── controller_generator.rb │ │ └── templates │ │ │ ├── controller_spec.rb │ │ │ ├── request_spec.rb │ │ │ ├── routing_spec.rb │ │ │ └── view_spec.rb │ │ ├── feature │ │ ├── feature_generator.rb │ │ └── templates │ │ │ ├── feature_singular_spec.rb │ │ │ └── feature_spec.rb │ │ ├── generator │ │ ├── generator_generator.rb │ │ └── templates │ │ │ └── generator_spec.rb │ │ ├── helper │ │ ├── helper_generator.rb │ │ └── templates │ │ │ └── helper_spec.rb │ │ ├── install │ │ ├── install_generator.rb │ │ └── templates │ │ │ └── spec │ │ │ └── rails_helper.rb │ │ ├── job │ │ ├── job_generator.rb │ │ └── templates │ │ │ └── job_spec.rb.erb │ │ ├── mailbox │ │ ├── mailbox_generator.rb │ │ └── templates │ │ │ └── mailbox_spec.rb.erb │ │ ├── mailer │ │ ├── mailer_generator.rb │ │ └── templates │ │ │ ├── fixture │ │ │ ├── mailer_spec.rb │ │ │ └── preview.rb │ │ ├── model │ │ ├── model_generator.rb │ │ └── templates │ │ │ ├── fixtures.yml │ │ │ └── model_spec.rb │ │ ├── request │ │ ├── request_generator.rb │ │ └── templates │ │ │ └── request_spec.rb │ │ ├── scaffold │ │ ├── scaffold_generator.rb │ │ └── templates │ │ │ ├── api_controller_spec.rb │ │ │ ├── api_request_spec.rb │ │ │ ├── controller_spec.rb │ │ │ ├── edit_spec.rb │ │ │ ├── index_spec.rb │ │ │ ├── new_spec.rb │ │ │ ├── request_spec.rb │ │ │ ├── routing_spec.rb │ │ │ └── show_spec.rb │ │ ├── system │ │ ├── system_generator.rb │ │ └── templates │ │ │ └── system_spec.rb │ │ └── view │ │ ├── templates │ │ └── view_spec.rb │ │ └── view_generator.rb ├── rspec-rails.rb └── rspec │ ├── rails.rb │ └── rails │ ├── active_record.rb │ ├── adapters.rb │ ├── configuration.rb │ ├── example.rb │ ├── example │ ├── channel_example_group.rb │ ├── controller_example_group.rb │ ├── feature_example_group.rb │ ├── helper_example_group.rb │ ├── job_example_group.rb │ ├── mailbox_example_group.rb │ ├── mailer_example_group.rb │ ├── model_example_group.rb │ ├── rails_example_group.rb │ ├── request_example_group.rb │ ├── routing_example_group.rb │ ├── system_example_group.rb │ └── view_example_group.rb │ ├── extensions.rb │ ├── extensions │ └── active_record │ │ └── proxy.rb │ ├── feature_check.rb │ ├── file_fixture_support.rb │ ├── fixture_file_upload_support.rb │ ├── fixture_support.rb │ ├── matchers.rb │ ├── matchers │ ├── action_cable.rb │ ├── action_cable │ │ ├── have_broadcasted_to.rb │ │ └── have_streams.rb │ ├── action_mailbox.rb │ ├── active_job.rb │ ├── base_matcher.rb │ ├── be_a_new.rb │ ├── be_new_record.rb │ ├── be_valid.rb │ ├── have_enqueued_mail.rb │ ├── have_http_status.rb │ ├── have_rendered.rb │ ├── redirect_to.rb │ ├── relation_match_array.rb │ ├── routing_matchers.rb │ └── send_email.rb │ ├── tasks │ └── rspec.rake │ ├── vendor │ └── capybara.rb │ ├── version.rb │ ├── view_assigns.rb │ ├── view_path_builder.rb │ ├── view_rendering.rb │ └── view_spec_methods.rb ├── maintenance-branch ├── rfcs └── versioning-strategy.md ├── rspec-rails.gemspec ├── script ├── clone_all_rspec_repos ├── functions.sh ├── run_build ├── run_rubocop ├── run_snippets.sh └── update_rubygems_and_install_bundler ├── snippets ├── avoid_fixture_name_collision.rb ├── include_activesupport_testing_tagged_logger.rb └── use_active_record_false.rb ├── spec ├── generators │ └── rspec │ │ ├── authentication │ │ └── authentication_generator_spec.rb │ │ ├── channel │ │ └── channel_generator_spec.rb │ │ ├── controller │ │ └── controller_generator_spec.rb │ │ ├── feature │ │ └── feature_generator_spec.rb │ │ ├── generator │ │ └── generator_generator_spec.rb │ │ ├── helper │ │ └── helper_generator_spec.rb │ │ ├── install │ │ └── install_generator_spec.rb │ │ ├── job │ │ └── job_generator_spec.rb │ │ ├── mailbox │ │ └── mailbox_generator_spec.rb │ │ ├── mailer │ │ └── mailer_generator_spec.rb │ │ ├── model │ │ └── model_generator_spec.rb │ │ ├── request │ │ └── request_generator_spec.rb │ │ ├── scaffold │ │ └── scaffold_generator_spec.rb │ │ ├── system │ │ └── system_generator_spec.rb │ │ └── view │ │ └── view_generator_spec.rb ├── rspec │ ├── rails │ │ ├── active_model_spec.rb │ │ ├── active_record_spec.rb │ │ ├── assertion_adapter_spec.rb │ │ ├── assertion_delegator_spec.rb │ │ ├── configuration_spec.rb │ │ ├── example │ │ │ ├── channel_example_group_spec.rb │ │ │ ├── controller_example_group_spec.rb │ │ │ ├── feature_example_group_spec.rb │ │ │ ├── helper_example_group_spec.rb │ │ │ ├── job_example_group_spec.rb │ │ │ ├── mailbox_example_group_spec.rb │ │ │ ├── mailer_example_group_spec.rb │ │ │ ├── model_example_group_spec.rb │ │ │ ├── rails_example_group_spec.rb │ │ │ ├── request_example_group_spec.rb │ │ │ ├── routing_example_group_spec.rb │ │ │ ├── system_example_group_spec.rb │ │ │ └── view_example_group_spec.rb │ │ ├── fixture_file_upload_support_spec.rb │ │ ├── fixture_support_spec.rb │ │ ├── matchers │ │ │ ├── action_cable │ │ │ │ ├── have_broadcasted_to_spec.rb │ │ │ │ └── have_stream_spec.rb │ │ │ ├── action_mailbox_spec.rb │ │ │ ├── active_job_spec.rb │ │ │ ├── be_a_new_spec.rb │ │ │ ├── be_new_record_spec.rb │ │ │ ├── be_routable_spec.rb │ │ │ ├── be_valid_spec.rb │ │ │ ├── has_spec.rb │ │ │ ├── have_enqueued_mail_spec.rb │ │ │ ├── have_http_status_spec.rb │ │ │ ├── have_rendered_spec.rb │ │ │ ├── redirect_to_spec.rb │ │ │ ├── relation_match_array_spec.rb │ │ │ ├── route_to_spec.rb │ │ │ └── send_email_spec.rb │ │ ├── minitest_lifecycle_adapter_spec.rb │ │ ├── setup_and_teardown_adapter_spec.rb │ │ ├── view_rendering_spec.rb │ │ └── view_spec_methods_spec.rb │ └── rails_spec.rb ├── sanity_check_spec.rb ├── spec_helper.rb └── support │ ├── ar_classes.rb │ ├── fixtures │ └── namespaced │ │ └── model.yml │ ├── generators.rb │ ├── group_failure_formatter.rb │ ├── null_object.rb │ └── shared_examples.rb └── yard └── template └── default ├── fulldoc └── html │ └── css │ └── rspec.css └── layout └── html └── setup.rb /.document: -------------------------------------------------------------------------------- 1 | lib/**/*.rb 2 | - 3 | README.md 4 | LICENSE.md 5 | Changelog.md 6 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # This file was generated on 2023-04-16T20:53:24+01:00 from the rspec-dev repo. 2 | # DO NOT modify it by hand as your changes will get lost the next time it is generated. 3 | 4 | github: [JonRowe, benoittgt] 5 | open_collective: rspec 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 17 | 18 | ## What Ruby, Rails and RSpec versions are you using? 19 | 20 | Ruby version: 21 | Rails version: 22 | RSpec version: 23 | 24 | 35 | 36 | ## Observed behaviour 37 | 38 | 42 | 43 | ## Expected behaviour 44 | 45 | 49 | 50 | ## Can you provide an example reproduction? 51 | 52 | 66 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | 7 | --- 8 | 9 | 16 | 17 | **Is your feature request related to a problem? Please describe.** 18 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 19 | 20 | **Describe the solution you'd like** 21 | A clear and concise description of what you want to happen. 22 | 23 | **Describe alternatives you've considered** 24 | A clear and concise description of any alternative solutions or features you've considered. 25 | 26 | **Additional context** 27 | Add any other context or screenshots about the feature request here. 28 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: RSpec CI 2 | on: 3 | push: 4 | branches: 5 | - 'main' 6 | - '*-maintenance' 7 | - '*-dev' 8 | - 'pr-*' 9 | pull_request: 10 | branches: 11 | - '*' 12 | permissions: 13 | contents: read 14 | concurrency: 15 | group: ${{ github.workflow }}-${{ github.github.head_ref || github.run_id }} 16 | cancel-in-progress: true 17 | 18 | jobs: 19 | rubocop: 20 | name: Rubocop 21 | runs-on: 'ubuntu-latest' 22 | steps: 23 | - uses: actions/checkout@v4 24 | - uses: ruby/setup-ruby@v1 25 | with: 26 | ruby-version: '3.3' 27 | - run: script/update_rubygems_and_install_bundler 28 | - run: bundle install --standalone 29 | - run: bundle binstubs --all 30 | - run: script/run_rubocop 31 | 32 | test: 33 | name: 'Ruby: ${{ matrix.ruby }}, Rails: ${{ matrix.env.RAILS_VERSION }}' 34 | runs-on: 'ubuntu-latest' 35 | strategy: 36 | fail-fast: false 37 | matrix: 38 | include: 39 | # Edge Rails (?) builds >= 3.1 40 | - ruby: 3.4 41 | env: 42 | RAILS_VERSION: 'main' 43 | - ruby: 3.3 44 | env: 45 | RAILS_VERSION: 'main' 46 | - ruby: 3.2 47 | env: 48 | RAILS_VERSION: 'main' 49 | 50 | # Rails 8.0 builds >= 3.2 51 | - ruby: 3.4 52 | env: 53 | RAILS_VERSION: '~> 8.0.0' 54 | - ruby: 3.3 55 | env: 56 | RAILS_VERSION: '~> 8.0.0' 57 | - ruby: 3.2 58 | env: 59 | RAILS_VERSION: '~> 8.0.0' 60 | 61 | # Rails 7.2 builds >= 3.1 62 | - ruby: 3.3 63 | env: 64 | RAILS_VERSION: '~> 7.2.0' 65 | - ruby: 3.2 66 | env: 67 | RAILS_VERSION: '~> 7.2.0' 68 | - ruby: 3.1 69 | env: 70 | RAILS_VERSION: '~> 7.2.0' 71 | 72 | env: ${{ matrix.env }} 73 | steps: 74 | - uses: actions/checkout@v4 75 | - uses: ruby/setup-ruby@v1 76 | with: 77 | ruby-version: ${{ matrix.ruby }} 78 | - run: script/update_rubygems_and_install_bundler 79 | - run: script/clone_all_rspec_repos 80 | - run: bundle install --binstubs 81 | - run: script/run_build 82 | continue-on-error: ${{ matrix.allow_failure || false }} 83 | -------------------------------------------------------------------------------- /.github/workflows/depsreview.yaml: -------------------------------------------------------------------------------- 1 | name: 'Dependency Review' 2 | on: [pull_request] 3 | 4 | permissions: 5 | contents: read 6 | 7 | jobs: 8 | dependency-review: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: 'Checkout Repository' 12 | uses: actions/checkout@v4 13 | - name: 'Dependency Review' 14 | uses: actions/dependency-review-action@v4 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | tmp 2 | doc 3 | pkg 4 | vendor 5 | !lib/rspec/rails/vendor 6 | *.gem 7 | Gemfile.lock 8 | bin 9 | .rvmrc 10 | *.rbc 11 | .yardoc 12 | .bundle 13 | .rails-version* 14 | .DS_Store 15 | Gemfile-custom 16 | .rbenv-version 17 | .rbx 18 | /.ruby-gemset 19 | /.ruby-version 20 | bundle 21 | .bundle 22 | specs.out 23 | spec/examples.txt 24 | specs.out 25 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --warnings 2 | --require spec_helper 3 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | inherit_from: 2 | - .rubocop_rspec_base.yml 3 | - .rubocop_todo.yml 4 | 5 | AllCops: 6 | TargetRubyVersion: 2.5 7 | NewCops: disable 8 | Exclude: 9 | # Templates are really ERB which Rubocop does not parse 10 | - bin/**/* 11 | - bundle/**/* 12 | - lib/generators/rspec/*/templates/**/* 13 | - tmp/**/* 14 | 15 | # Over time we'd like to get this down, but this is what we're at now. 16 | Metrics/CyclomaticComplexity: 17 | Max: 10 # default: 6 18 | 19 | # Over time we'd like to get this down, but this is what we're at now. 20 | Layout/LineLength: 21 | Max: 186 # default: 80 22 | 23 | # Over time we'd like to get this down, but this is what we're at now. 24 | Metrics/MethodLength: 25 | Max: 43 # default: 10 26 | 27 | Bundler/DuplicatedGem: 28 | Enabled: false 29 | 30 | Gemspec/RequiredRubyVersion: 31 | # Rubocop checks that the target ruby version matches the gemspec version 32 | # but doesn't have a 2.2 option 33 | Enabled: false 34 | 35 | Metrics/BlockLength: 36 | Exclude: 37 | - Rakefile 38 | - example_app_generator/generate_app.rb 39 | - example_app_generator/spec/**/* 40 | - lib/rspec/rails/configuration.rb 41 | - lib/rspec/rails/example/system_example_group.rb 42 | - lib/rspec/rails/tasks/rspec.rake 43 | - rspec-rails.gemspec 44 | - spec/**/* 45 | 46 | # Offense count: 3 47 | # Configuration parameters: CountComments, CountAsOne. 48 | Metrics/ModuleLength: 49 | Exclude: 50 | - spec/**/* 51 | 52 | # Override the shared base defaults that are in place for 1.8.7 support 53 | 54 | Layout/DotPosition: 55 | EnforcedStyle: leading 56 | 57 | Style/HashSyntax: 58 | EnforcedStyle: ruby19 59 | 60 | Style/Lambda: 61 | Enabled: true 62 | 63 | -------------------------------------------------------------------------------- /.yardopts: -------------------------------------------------------------------------------- 1 | --no-private 2 | --exclude features 3 | --exclude lib/generators/([^/]+/)*.*_spec.rb 4 | --exclude lib/generators/([^/]+/)*templates/([^/]+/)*.rb 5 | --exclude lib/generators/([^/]+/)*templates/.+\.rb 6 | --markup markdown 7 | --template-path yard/template/ 8 | - 9 | Changelog.md 10 | LICENSE.md 11 | Capybara.md 12 | -------------------------------------------------------------------------------- /Capybara.md: -------------------------------------------------------------------------------- 1 | rspec-rails supports integration with Capybara out of the box by adding 2 | its Capybara::DSL (visit/page) and Capybara::RSpecMatchers to the 3 | examples in the applicable directories. 4 | 5 | ## Capybara::DSL 6 | 7 | Adds the `visit` and `page` methods, which work together to simulate a 8 | GET request and provide access to the result (via `page`). 9 | 10 | Capybara::DSL is added to examples in: 11 | 12 | * spec/features 13 | 14 | ## Capybara::RSpecMatchers 15 | 16 | Exposes matchers used to specify expected HTML content (e.g. `should_not have_selector` will work correctly). 17 | 18 | Capybara::RSpecMatchers is added to examples in: 19 | 20 | * spec/features 21 | * spec/controllers 22 | * spec/views 23 | * spec/helpers 24 | * spec/mailers 25 | 26 | ## Upgrading to Capybara-3.x 27 | 28 | Consult the official [Upgrading from Capybara 2.x to 3.x](https://github.com/teamcapybara/capybara/blob/master/UPGRADING.md#upgrading-from-capybara-2x-to-3x) guide. 29 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gemspec 4 | 5 | eval_gemfile 'Gemfile-rspec-dependencies' 6 | 7 | gem 'yard', '~> 0.9.24', require: false 8 | 9 | group :documentation do 10 | gem 'github-markup', '~> 3.0.3' 11 | gem 'redcarpet', '~> 3.5.1', platforms: [:ruby] 12 | end 13 | 14 | gem 'capybara' 15 | gem 'ffi', '> 1.15.5' 16 | gem 'rake', '> 12' 17 | gem 'rubocop', '~> 1.28.2' 18 | 19 | custom_gemfile = File.expand_path('Gemfile-custom', __dir__) 20 | eval_gemfile custom_gemfile if File.exist?(custom_gemfile) 21 | 22 | eval_gemfile 'Gemfile-rails-dependencies' 23 | -------------------------------------------------------------------------------- /Gemfile-custom.sample: -------------------------------------------------------------------------------- 1 | group :development do 2 | gem 'interactive_rspec' 3 | gem 'relish', '~> 0.6.0' 4 | gem 'guard-rspec', '~> 1.2.1' 5 | gem 'growl', '1.0.3' 6 | gem 'spork', '0.9.0' 7 | 8 | platform :mri do 9 | gem 'rb-fsevent', '~> 0.9.0' 10 | gem 'ruby-prof', '~> 0.10.0' 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /Gemfile-rails-dependencies: -------------------------------------------------------------------------------- 1 | version_file = File.expand_path("../.rails-version", __FILE__) 2 | 3 | # This is required for Ruby 3.1, because in Ruby 3.1 these gems were moved to 4 | # bundled gems from default gems. This issue was fixed in Rails Rails 7.0.1. 5 | # Discussion can be found here - https://github.com/mikel/mail/pull/1439 6 | def add_net_gems_dependency 7 | if RUBY_VERSION >= '3.1' 8 | gem 'net-smtp', require: false 9 | gem 'net-imap', require: false 10 | gem 'net-pop', require: false 11 | end 12 | end 13 | 14 | def add_sqlite3_gem_dependency(rails_version) 15 | # sqlite3 is an optional, unspecified, dependency and Rails 8.0 only supports `~> 2.0` 16 | if RUBY_VERSION.to_f < 3 17 | # sqlite3 1.7.x doesn't work on all platforms for Ruby 2.x 18 | gem 'sqlite3', '~> 1.4', '< 1.7', platforms: [:ruby] 19 | elsif rails_version.to_f >= 8 20 | gem 'sqlite3', '~> 2.0', platforms: [:ruby] 21 | else 22 | gem 'sqlite3', '~> 1.7', platforms: [:ruby] 23 | end 24 | end 25 | 26 | gem 'puma' 27 | 28 | case version = ENV['RAILS_VERSION'] || (File.exist?(version_file) && File.read(version_file).chomp) || '' 29 | when /main/ 30 | gem "rails", :git => "https://github.com/rails/rails.git" 31 | gem 'activerecord-jdbcsqlite3-adapter', git: 'https://github.com/jruby/activerecord-jdbc-adapter', platforms: [:jruby] 32 | gem 'selenium-webdriver', require: false 33 | 34 | gem 'sqlite3', '~> 2.0', platforms: [:ruby] 35 | when nil, false, "" 36 | if RUBY_VERSION.to_f > 3.1 37 | gem "rails", "~> 8.0.0" 38 | gem 'sqlite3', '~> 2.0', platforms: [:ruby] 39 | else 40 | gem "rails", "~> 7.2.0" 41 | gem 'sqlite3', '~> 1.7', platforms: [:ruby] 42 | end 43 | 44 | gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby] 45 | gem 'selenium-webdriver', require: false 46 | else 47 | version_number = version.split(' ').last 48 | 49 | gem "rails", version 50 | gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby] 51 | 52 | gem 'selenium-webdriver', require: false 53 | add_sqlite3_gem_dependency(version_number) 54 | end 55 | -------------------------------------------------------------------------------- /Gemfile-rspec-dependencies: -------------------------------------------------------------------------------- 1 | branch = File.read(File.expand_path("../maintenance-branch", __FILE__)).chomp 2 | %w[rspec rspec-core rspec-expectations rspec-mocks rspec-support].each do |lib| 3 | library_path = File.expand_path("../../rspec/#{lib}", __FILE__) 4 | if File.exist?(library_path) && !ENV['USE_GIT_REPOS'] 5 | gem lib, path: library_path, require: false 6 | else 7 | gem lib, git: "https://github.com/rspec/rspec", glob: "#{lib}/#{lib}.gemspec" 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | * Copyright © 2015 David Chelimsky, Aaron Kromer 5 | * Copyright © 2012 David Chelimsky, Andy Lindeman 6 | * Copyright © 2006 David Chelimsky, The RSpec Development Team 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining 9 | a copy of this software and associated documentation files (the 10 | "Software"), to deal in the Software without restriction, including 11 | without limitation the rights to use, copy, modify, merge, publish, 12 | distribute, sublicense, and/or sell copies of the Software, and to 13 | permit persons to whom the Software is furnished to do so, subject to 14 | the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be 17 | included in all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 23 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README_DEV.md: -------------------------------------------------------------------------------- 1 | # rspec-rails development 2 | 3 | This documentation is meant for folks contributing the rspec-rails project 4 | itself. 5 | 6 | ## Background 7 | 8 | rspec-rails lives in a complicated ecosystem. We run our specs against multiple 9 | Rails and Ruby versions. 10 | 11 | ### Default 12 | 13 | By default, rspec-rails' test suite will run against the latest stable version 14 | of Rails. 15 | 16 | ### Running Tests 17 | 18 | ```bash 19 | bundle install --binstubs 20 | bin/rake 21 | ``` 22 | 23 | ### Errors 24 | 25 | If you receive an error from `bundler` where constraints cannot be satisfied 26 | for Rails, try removing `Gemfile.lock` (`rm Gemfile.lock`) and running `bundle 27 | install --binstubs` again. 28 | 29 | This can happen if the `Gemfile.lock` was generated for a different version of 30 | Rails than you are trying to use now. 31 | 32 | ### Changing Rails Version 33 | 34 | To run the specs against a different version of Rails, use the `thor` command: 35 | 36 | ```bash 37 | bin/thor version:use 7.0.3.1 38 | bin/rake 39 | ``` 40 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | ## Security contact information 2 | 3 | To report a security vulnerability, please use the 4 | [Tidelift security contact](https://tidelift.com/security). 5 | Tidelift will coordinate the fix and disclosure. 6 | -------------------------------------------------------------------------------- /Thorfile: -------------------------------------------------------------------------------- 1 | class Version < Thor 2 | include Thor::Actions 3 | 4 | desc "use VERSION", "installs the bundle the rails-VERSION" 5 | def use(version) 6 | remove_file "Gemfile.lock" 7 | run "echo '#{version}' > ./.rails-version" 8 | run "bundle install --binstubs" 9 | end 10 | 11 | desc "which", "print out the configured rails version" 12 | def which 13 | say `cat ./.rails-version` 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # This file was generated on 2019-12-18T14:01:39+00:00 from the rspec-dev repo. 2 | # DO NOT modify it by hand as your changes will get lost the next time it is generated. 3 | 4 | version: "{build}" 5 | 6 | # This will build all PRs targeting matching branches. 7 | # Without this, each PR builds twice -- once for the PR branch HEAD, 8 | # and once for the merge commit that github creates for each mergeable PR. 9 | branches: 10 | only: 11 | - main 12 | - /.*-maintenance$/ 13 | 14 | # Disable normal Windows builds in favor of our test script. 15 | build: off 16 | 17 | cache: 18 | - vendor/bundle 19 | 20 | install: 21 | - set PATH=C:\Ruby%RUBY_VERSION%\bin;%PATH% 22 | - bundle config --local path vendor/bundle 23 | - bundle install 24 | - cinst ansicon 25 | 26 | before_test: 27 | - ruby --version 28 | - gem --version 29 | - bundle --version 30 | 31 | test_script: 32 | - bundle exec rspec --backtrace 33 | 34 | environment: 35 | matrix: 36 | - ruby_version: 23-x64 37 | - ruby_version: 24-x64 38 | - ruby_version: 25-x64 39 | - ruby_version: 26-x64 40 | - ruby_version: 27-x64 41 | -------------------------------------------------------------------------------- /cucumber.yml: -------------------------------------------------------------------------------- 1 | default: --publish-quiet --require features --format progress --tags 'not @wip' 2 | pretty: --publish-quiet --require features --format pretty --tags 'not @wip' 3 | wip: --publish-quiet --require features --tags @wip 4 | -------------------------------------------------------------------------------- /example_app_generator/app/views/_example.html.erb: -------------------------------------------------------------------------------- 1 | TEMPLATE_HTML 2 | -------------------------------------------------------------------------------- /example_app_generator/app/views/foo.html: -------------------------------------------------------------------------------- 1 | Static template named 'foo.html' 2 | -------------------------------------------------------------------------------- /example_app_generator/app/views/some_templates/bar.html: -------------------------------------------------------------------------------- 1 | Static template named 'bar.html' 2 | -------------------------------------------------------------------------------- /example_app_generator/ci_retry_bundle_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | source FUNCTIONS_SCRIPT_FILE 5 | 6 | echo "Starting bundle install using shared bundle path" 7 | ci_retry eval "RUBYOPT=$RUBYOPT:' --enable rubygems' bundle install --gemfile ./Gemfile --path REPLACE_BUNDLE_PATH --retry=3 --jobs=3" 8 | -------------------------------------------------------------------------------- /example_app_generator/generate_action_mailer_specs.rb: -------------------------------------------------------------------------------- 1 | require 'active_support' 2 | require 'active_support/core_ext/module' 3 | 4 | using_source_path(File.expand_path(__dir__)) do 5 | # Comment out the default mailer stuff 6 | comment_lines 'config/environments/development.rb', /action_mailer/ 7 | comment_lines 'config/environments/test.rb', /action_mailer/ 8 | 9 | initializer 'action_mailer.rb', <<-CODE 10 | require "action_view/base" 11 | if ENV['DEFAULT_URL'] 12 | ExampleApp::Application.configure do 13 | config.action_mailer.default_url_options = { :host => ENV['DEFAULT_URL'] } 14 | end 15 | end 16 | CODE 17 | 18 | rails_parent = Rails.application.class.module_parent.to_s 19 | 20 | gsub_file 'config/initializers/action_mailer.rb', /ExampleApp/, rails_parent 21 | 22 | copy_file 'spec/support/default_preview_path' 23 | chmod 'spec/support/default_preview_path', 0755 24 | gsub_file 'spec/support/default_preview_path', /ExampleApp/, rails_parent 25 | 26 | if skip_active_record? 27 | comment_lines 'spec/support/default_preview_path', /active_record/ 28 | comment_lines 'spec/support/default_preview_path', /active_storage/ 29 | comment_lines 'spec/support/default_preview_path', /action_mailbox/ 30 | end 31 | copy_file 'spec/verify_mailer_preview_path_spec.rb' 32 | end 33 | -------------------------------------------------------------------------------- /example_app_generator/log/development.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rspec/rspec-rails/c858849352be22ea39473780ced45919bd2e9fd1/example_app_generator/log/development.log -------------------------------------------------------------------------------- /example_app_generator/no_active_record/app/models/in_memory/model.rb: -------------------------------------------------------------------------------- 1 | raise "ActiveRecord is defined but should not be!" if defined?(::ActiveRecord) 2 | 3 | require 'active_model' 4 | 5 | module InMemory 6 | module Persistence 7 | def all 8 | @all_records ||= [] 9 | end 10 | 11 | def count 12 | all.length 13 | end 14 | alias_method :size, :count 15 | alias_method :length, :count 16 | 17 | def last 18 | all.last 19 | end 20 | 21 | def find(id) 22 | id = id.to_i 23 | all.find { |record| record.id == id } || raise 24 | end 25 | 26 | def create!(attributes = {}) 27 | record = new(attributes) 28 | record.save 29 | record 30 | end 31 | 32 | def next_id 33 | @id_count ||= 0 34 | @id_count += 1 35 | end 36 | end 37 | 38 | class Model 39 | extend Persistence 40 | 41 | if defined?(::ActiveModel::Model) 42 | include ::ActiveModel::Model 43 | else 44 | extend ::ActiveModel::Naming 45 | include ::ActiveModel::Conversion 46 | include ::ActiveModel::Validations 47 | 48 | def initialize(attributes = {}) 49 | assign_attributes(attributes) 50 | end 51 | end 52 | 53 | attr_accessor :id, :persisted 54 | 55 | alias_method :persisted?, :persisted 56 | 57 | def update(attributes) 58 | assign_attributes(attributes) 59 | save 60 | end 61 | 62 | alias_method :update_attributes, :update 63 | 64 | def assign_attributes(attributes) 65 | attributes.each do |name, value| 66 | __send__("#{name}=", value) 67 | end 68 | end 69 | 70 | def save(*) 71 | self.id = self.class.next_id 72 | self.class.all << self 73 | true 74 | end 75 | alias :save! :save 76 | 77 | def destroy 78 | self.class.all.delete(self) 79 | true 80 | end 81 | alias :destroy! :destroy 82 | 83 | def reload(*) 84 | self 85 | end 86 | 87 | def ==(other) 88 | other.is_a?(self.class) && id == other.id 89 | end 90 | 91 | def persisted? 92 | !id.nil? 93 | end 94 | 95 | def new_record? 96 | !persisted? 97 | end 98 | 99 | def to_param 100 | id.to_s 101 | end 102 | end 103 | end 104 | -------------------------------------------------------------------------------- /example_app_generator/no_active_record/config/initializers/zeitwerk.rb: -------------------------------------------------------------------------------- 1 | if Rails.autoloaders.respond_to?(:main) && Rails.autoloaders.main.respond_to?(:ignore) 2 | Rails.autoloaders.main.ignore('lib/rails/generators/in_memory/model/model_generator.rb') 3 | end 4 | -------------------------------------------------------------------------------- /example_app_generator/no_active_record/lib/rails/generators/in_memory/model/model_generator.rb: -------------------------------------------------------------------------------- 1 | # Hook into the work already done to support older Rails 2 | require 'generators/rspec' 3 | 4 | module InMemory 5 | module Generators 6 | class ModelGenerator < ::Rspec::Generators::Base 7 | source_root File.expand_path('templates', __dir__) 8 | 9 | desc "Creates a Fake ActiveRecord acting model" 10 | argument :attributes, 11 | type: :array, 12 | default: [], 13 | banner: "field:type field:type" 14 | 15 | check_class_collision 16 | 17 | class_option :parent, 18 | type: :string, 19 | desc: "The parent class for the generated model" 20 | 21 | def create_model_file 22 | template "model.rb.erb", 23 | File.join("app/models", class_path, "#{file_name}.rb") 24 | end 25 | 26 | hook_for :test_framework 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /example_app_generator/no_active_record/lib/rails/generators/in_memory/model/templates/model.rb.erb: -------------------------------------------------------------------------------- 1 | <% module_namespacing do -%> 2 | class <%= class_name %> < <%= options[:parent].try(:classify) || 'InMemory::Model' %> 3 | <% attributes.each do |attribute| -%> 4 | attr_accessor :<%= attribute.name %> 5 | <% end -%> 6 | end 7 | <% end -%> 8 | -------------------------------------------------------------------------------- /example_app_generator/no_active_record/spec/verify_fixture_file_upload_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe 'Example App', :use_fixtures, type: :model do 4 | it 'supports fixture file upload' do 5 | file = fixture_file_upload(__FILE__) 6 | expect(file.read).to match(/RSpec\.describe 'Example App'/im) 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /example_app_generator/no_active_record/spec/verify_no_active_record_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe 'Example App' do 4 | it "does not have ActiveRecord defined" do 5 | expect(defined?(ActiveRecord)).not_to be 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /example_app_generator/no_active_record/spec/verify_no_fixture_setup_spec.rb: -------------------------------------------------------------------------------- 1 | # Pretend that ActiveRecord::Rails is defined and this doesn't blow up 2 | # with `config.use_active_record = false`. 3 | # Trick the other spec that checks that ActiveRecord is 4 | # *not* defined by wrapping it in RSpec::Rails namespace 5 | # so that it's reachable from RSpec::Rails::FixtureSupport. 6 | # NOTE: this has to be defined before requiring `rails_helper`. 7 | module RSpec 8 | module Rails 9 | module ActiveRecord 10 | module TestFixtures 11 | end 12 | end 13 | end 14 | end 15 | 16 | require 'rails_helper' 17 | 18 | RSpec.describe 'Example App', :use_fixtures, type: :model do 19 | it "does not set up fixtures" do 20 | expect(defined?(fixtures)).not_to be 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /example_app_generator/run_specs.rb: -------------------------------------------------------------------------------- 1 | run('bin/rspec spec -cfdoc') || abort 2 | # Ensure we test the issue in-case this isn't the first spec file loaded 3 | run( 4 | 'bin/rspec --backtrace -cfdoc spec/__verify_fixture_load_order_spec.rb' 5 | ) || abort 6 | run('bin/rake --backtrace spec') || abort 7 | run('bin/rake --backtrace spec:requests') || abort 8 | run('bin/rake --backtrace spec:models') || abort 9 | run('bin/rake --backtrace spec:views') || abort 10 | run('bin/rake --backtrace spec:controllers') || abort 11 | run('bin/rake --backtrace spec:helpers') || abort 12 | run('bin/rake --backtrace spec:mailers') || abort 13 | run("bin/rake --backtrace stats") || abort 14 | -------------------------------------------------------------------------------- /example_app_generator/spec/__verify_fixture_load_order_spec.rb: -------------------------------------------------------------------------------- 1 | # This spec needs to be run before `rails_helper` is loaded to check the issue 2 | RSpec.describe "Verify issue rspec/rspec-rails#1355" do 3 | it "passes" do 4 | expect(1).to eq 1 5 | end 6 | end 7 | require 'rails_helper' 8 | -------------------------------------------------------------------------------- /example_app_generator/spec/features/model_mocks_integration_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Using rspec-mocks with models" do 4 | it "supports stubbing class methods on models" do 5 | allow(Widget).to receive(:all).and_return(:any_stub) 6 | expect(Widget.all).to be :any_stub 7 | end 8 | 9 | it "supports stubbing attribute methods on models" do 10 | a_widget = Widget.new 11 | allow(a_widget).to receive(:name).and_return("Any Stub") 12 | 13 | expect(a_widget.name).to eq "Any Stub" 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /example_app_generator/spec/support/default_preview_path: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # Transparent helper to simply document code sections 3 | def require_file_stub(_name) 4 | yield 5 | end 6 | 7 | ENV['RAILS_ENV'] ||= 'development' 8 | # Pick the frameworks you want: 9 | begin 10 | require "openssl" 11 | require "active_storage" 12 | require "active_storage/engine" 13 | rescue LoadError 14 | end 15 | 16 | require_file_stub 'config/environment' do 17 | # Load the Rails application. 18 | require_file_stub 'config/application' do 19 | require_file_stub 'config/boot' do 20 | # Set up gems listed in the Gemfile. 21 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 22 | require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE']) 23 | end 24 | 25 | # Pick the frameworks you want: 26 | require "active_record/railtie" 27 | require "action_controller/railtie" 28 | require "action_mailer/railtie" unless ENV['NO_ACTION_MAILER'] 29 | require "action_view/railtie" 30 | require "action_cable/engine" 31 | require "active_job/railtie" 32 | require "action_mailbox/engine" 33 | 34 | # Require the gems listed in Gemfile, including any gems 35 | # you've limited to :test, :development, or :production. 36 | Bundler.require(*Rails.groups) 37 | 38 | module ExampleApp 39 | class Application < Rails::Application 40 | config.eager_load = false 41 | config.active_support.cache_format_version = 7.0 42 | 43 | # Don't care if the mailer can't send. 44 | config.action_mailer.raise_delivery_errors = false unless ENV['NO_ACTION_MAILER'] 45 | if ENV['CUSTOM_PREVIEW_PATH'] 46 | config.action_mailer.preview_paths = [ENV['CUSTOM_PREVIEW_PATH']] 47 | end 48 | if ENV['SHOW_PREVIEWS'] 49 | config.action_mailer.show_previews = (ENV['SHOW_PREVIEWS'] == 'true') 50 | end 51 | end 52 | end 53 | 54 | I18n.enforce_available_locales = true 55 | end 56 | 57 | # Initialize the Rails application. 58 | Rails.application.initialize! 59 | end 60 | 61 | exit(0) if ENV['NO_ACTION_MAILER'] 62 | if ENV['DEFAULT_URL'] 63 | puts ActionMailer::Base.default_url_options[:host] 64 | elsif defined?(::ActionMailer::Preview) 65 | puts Rails.application.config.action_mailer.preview_paths 66 | end 67 | 68 | # This will force the loading of ActionMailer settings to ensure we do not 69 | # accidentally set something we should not 70 | ActionMailer::Base.smtp_settings 71 | exit 0 72 | -------------------------------------------------------------------------------- /example_app_generator/spec/verify_active_record_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe 'Example App' do 4 | it "has ActiveRecord defined" do 5 | expect(defined?(ActiveRecord)).to be 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /example_app_generator/spec/verify_fixture_warning_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "Fixture warnings" do 4 | def generate_fixture_example_group(hook_type) 5 | RSpec.describe do 6 | include RSpec::Rails::RailsExampleGroup 7 | fixtures :things 8 | 9 | before(hook_type) do 10 | things :a 11 | end 12 | 13 | it "" do 14 | 15 | end 16 | end 17 | end 18 | 19 | it "Warns when a fixture call is made in a before :context call" do 20 | expect(RSpec).to receive(:warn_with).with(match(/Calling fixture method in before :context/)) 21 | 22 | generate_fixture_example_group(:context).run 23 | end 24 | 25 | it "Does not warn when a fixture call is made in a before :each call" do 26 | expect(RSpec).not_to receive(:warn_with) 27 | 28 | generate_fixture_example_group(:each).run 29 | end 30 | 31 | end 32 | 33 | RSpec.describe "Global fixture warnings" do 34 | def generate_fixture_example_group(hook_type) 35 | RSpec.describe do 36 | include RSpec::Rails::RailsExampleGroup 37 | 38 | before(hook_type) do 39 | things :a 40 | end 41 | 42 | it "" do 43 | 44 | end 45 | end 46 | end 47 | around do |ex| 48 | RSpec.configuration.global_fixtures = [:things] 49 | ex.call 50 | RSpec.configuration.global_fixtures = [] 51 | end 52 | 53 | it "warns when a global fixture call is made in a before :context call" do 54 | expect(RSpec).to receive(:warn_with).with(match(/Calling fixture method in before :context/)) 55 | 56 | generate_fixture_example_group(:context).run 57 | end 58 | 59 | it "does not warn when a global fixture call is made in a before :each call" do 60 | expect(RSpec).not_to receive(:warn_with) 61 | 62 | generate_fixture_example_group(:each).run 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /example_app_generator/spec/verify_view_path_stub_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "verify view path doesn't leak stubs between examples", type: :view, order: :defined do 4 | subject(:html) do 5 | render partial: "example" 6 | rendered 7 | end 8 | 9 | it "renders the stub template" do 10 | stub_template("_example.html.erb" => "STUB_HTML") 11 | expect(html).to include("STUB_HTML") 12 | end 13 | 14 | it "renders the file template" do 15 | expect(html).to include("TEMPLATE_HTML") 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /features/.nav: -------------------------------------------------------------------------------- 1 | - GettingStarted.md (Start from scratch) 2 | - Generators.md (Generators) 3 | - Transactions.md 4 | - directory_structure.feature 5 | - backtrace_filtering.feature 6 | - model_specs: 7 | - transactional_examples.feature 8 | - mocks: 9 | - mock_model.feature 10 | - stub_model.feature 11 | - controller_specs: 12 | - cookies.feature 13 | - controller_spec.feature 14 | - isolation_from_views.feature 15 | - render_views.feature 16 | - anonymous_controller.feature 17 | - bypass_rescue.feature 18 | - engine_routes.feature 19 | - matchers: 20 | - new_record_matcher.feature 21 | - render_template_matcher.feature 22 | - redirect_to_matcher.feature 23 | - request_specs: 24 | - request_spec.feature 25 | - feature_specs: 26 | - feature_spec.feature 27 | - view_specs: 28 | - view_spec.feature 29 | - stub_template.feature 30 | - inferred_controller_path.feature 31 | - helper_specs: 32 | - helper_spec.feature 33 | - mailer_specs: 34 | - url_helpers.feature 35 | - routing_specs: 36 | - route_to_matcher.feature 37 | - be_routable_matcher.feature 38 | - named_routes.feature 39 | - engine_routes.feature 40 | -------------------------------------------------------------------------------- /features/Generators.md: -------------------------------------------------------------------------------- 1 | # Using generators 2 | 3 | RSpec `_spec.rb` files are normally generated alongside other application components. 4 | For instance, `rails generate model` will also generate an RSpec `_spec.rb` file 5 | for the model. 6 | 7 | Note that the generators are there to help you get started, but they are no 8 | substitute for writing your own examples, and they are only guaranteed to work 9 | out of the box for with Rails' defaults. 10 | 11 | RSpec generators can also be run independently. For instance, 12 | 13 | ```console 14 | rails generate rspec:model widget 15 | ``` 16 | 17 | will create a new spec file in `spec/models/widget_spec.rb`. 18 | 19 | The same generator pattern is available for all specs: 20 | 21 | * channel 22 | * controller 23 | * feature 24 | * generator 25 | * helper 26 | * job 27 | * mailbox 28 | * mailer 29 | * model 30 | * request 31 | * scaffold 32 | * system 33 | * view 34 | -------------------------------------------------------------------------------- /features/README.md: -------------------------------------------------------------------------------- 1 | # RSpec Rails 2 | 3 | `rspec-rails` extends Rails' built-in testing framework to support rspec 4 | examples for requests, controllers, models, views, helpers, mailers and 5 | routing. It is a thin framework around Rails own helpers and you can 6 | check their documentation for help as well. 7 | 8 | ## Rails 9 | 10 | rspec-rails 7 supports Rails 7.0 to 7.2. For earlier versions of Rails, you 11 | should use [rspec-rails-6](https://github.com/rspec/rspec-rails/tree/5-1-maintenance) 12 | for Rails 6.1, [rspec-rails-5](https://github.com/rspec/rspec-rails/tree/5-1-maintenance) 13 | for Rails 5.2 and 6.0, [rspec-rails-4](https://github.com/rspec/rspec-rails/tree/4-1-maintenance) 14 | for Rails 5.x, and [rspec-rails 3](https://github.com/rspec/rspec-rails/tree/3-9-maintenance) 15 | for even older versions. 16 | 17 | ## Install 18 | 19 | ```console 20 | gem install rspec-rails 21 | ``` 22 | 23 | This installs the following gems: 24 | 25 | rspec 26 | rspec-core 27 | rspec-expectations 28 | rspec-mocks 29 | rspec-rails 30 | 31 | ## Configure 32 | 33 | Add rspec-rails to the :test and :development groups in the Gemfile: 34 | 35 | ```ruby 36 | group :test, :development do 37 | gem 'rspec-rails', '~> 7.0.0' 38 | end 39 | ``` 40 | 41 | It needs to be in the :development group to expose generators and rake tasks 42 | without having to type RAILS_ENV=test. 43 | 44 | Now you can run: 45 | 46 | ```console 47 | bundle exec rails generate rspec:install 48 | ``` 49 | 50 | This adds the spec directory and some skeleton files, including a .rspec 51 | file. 52 | 53 | You can also customize the default spec path with `--default-path` option: 54 | 55 | ```console 56 | bundle exec rails generate rspec:install --default-path behaviour 57 | ``` 58 | 59 | ## Issues 60 | 61 | The documentation for rspec-rails is a work in progress. We'll be adding 62 | Cucumber features over time, and clarifying existing ones. If you have 63 | specific features you'd like to see added, find the existing documentation 64 | incomplete or confusing, or, better yet, wish to write a missing Cucumber 65 | feature yourself, please [submit an 66 | issue](https://github.com/rspec/rspec-rails/issues) or a [pull 67 | request](https://github.com/rspec/rspec-rails). 68 | -------------------------------------------------------------------------------- /features/backtrace_filtering.feature: -------------------------------------------------------------------------------- 1 | Feature: Backtrace filtering 2 | 3 | The following configuration setting will filter out lines in backtraces 4 | that come from Rails gems in order to reduce the noise in test failure output: 5 | 6 | ```ruby 7 | RSpec.configure do |config| 8 | config.filter_rails_from_backtrace! 9 | end 10 | ``` 11 | 12 | `rspec` will always show the full backtrace output when run with 13 | the `--backtrace` commandline option. 14 | 15 | Background: Using `filter_rails_from_backtrace!` 16 | Given a file named "spec/failing_spec.rb" with: 17 | """ruby 18 | require "rails_helper" 19 | 20 | RSpec.configure do |config| 21 | config.filter_rails_from_backtrace! 22 | end 23 | 24 | RSpec.describe "Controller", type: :controller do 25 | controller do 26 | def index 27 | raise "Something went wrong." 28 | end 29 | end 30 | 31 | describe "GET index" do 32 | it "raises an error" do 33 | get :index 34 | end 35 | end 36 | end 37 | """ 38 | 39 | Scenario: Using the bare `rspec` command 40 | When I run `rspec` 41 | Then the output should contain "1 example, 1 failure" 42 | And the output should not contain "actionpack" 43 | 44 | Scenario: Using `rspec --backtrace` 45 | When I run `rspec --backtrace` 46 | Then the output should contain "1 example, 1 failure" 47 | And the output should contain "actionpack" 48 | -------------------------------------------------------------------------------- /features/controller_specs/bypass_rescue.feature: -------------------------------------------------------------------------------- 1 | Feature: Using `bypass_rescue` 2 | 3 | Use `bypass_rescue` to bypass both Rails' default handling of errors in 4 | controller actions, and any custom handling declared with a `rescue_from` 5 | statement. 6 | 7 | This lets you specify details of the exception being raised, regardless of 8 | how it might be handled upstream. 9 | 10 | Background: 11 | Given a file named "spec/controllers/gadgets_controller_spec_context.rb" with: 12 | """ruby 13 | class AccessDenied < StandardError; end 14 | 15 | class ApplicationController < ActionController::Base 16 | rescue_from AccessDenied, :with => :access_denied 17 | 18 | private 19 | 20 | def access_denied 21 | redirect_to "/401.html" 22 | end 23 | end 24 | """ 25 | 26 | Scenario: Standard exception handling using `rescue_from` 27 | Given a file named "spec/controllers/gadgets_controller_spec.rb" with: 28 | """ruby 29 | require "rails_helper" 30 | 31 | require 'controllers/gadgets_controller_spec_context' 32 | 33 | RSpec.describe GadgetsController, type: :controller do 34 | before do 35 | def controller.index 36 | raise AccessDenied 37 | end 38 | end 39 | 40 | describe "index" do 41 | it "redirects to the /401.html page" do 42 | get :index 43 | expect(response).to redirect_to("/401.html") 44 | end 45 | end 46 | end 47 | """ 48 | When I run `rspec spec/controllers/gadgets_controller_spec.rb` 49 | Then the examples should all pass 50 | 51 | Scenario: Bypass `rescue_from` handling with `bypass_rescue` 52 | Given a file named "spec/controllers/gadgets_controller_spec.rb" with: 53 | """ruby 54 | require "rails_helper" 55 | 56 | require 'controllers/gadgets_controller_spec_context' 57 | 58 | RSpec.describe GadgetsController, type: :controller do 59 | before do 60 | def controller.index 61 | raise AccessDenied 62 | end 63 | end 64 | 65 | describe "index" do 66 | it "raises AccessDenied" do 67 | bypass_rescue 68 | expect { get :index }.to raise_error(AccessDenied) 69 | end 70 | end 71 | end 72 | """ 73 | When I run `rspec spec/controllers/gadgets_controller_spec.rb` 74 | Then the examples should all pass 75 | -------------------------------------------------------------------------------- /features/controller_specs/cookies.feature: -------------------------------------------------------------------------------- 1 | Feature: Cookies 2 | 3 | There are different ways to make assertions on cookies from controller specs, 4 | but we recommend using the `cookies` method as set out below. 5 | 6 | You can use strings or symbols to fetch or set your cookies because the `cookies` 7 | method supports indifferent access. 8 | 9 | Scenario: Testing cookie's value cleared in controller 10 | Given a file named "spec/controllers/application_controller_spec.rb" with: 11 | """ruby 12 | require "rails_helper" 13 | 14 | RSpec.describe ApplicationController, type: :controller do 15 | controller do 16 | def clear_cookie 17 | cookies.delete(:user_name) 18 | head :ok 19 | end 20 | end 21 | 22 | before do 23 | routes.draw { get "clear_cookie" => "anonymous#clear_cookie" } 24 | end 25 | 26 | it "clear cookie's value 'user_name'" do 27 | cookies[:user_name] = "Sam" 28 | 29 | get :clear_cookie 30 | 31 | expect(cookies[:user_name]).to eq nil 32 | end 33 | end 34 | """ 35 | When I run `rspec spec` 36 | Then the example should pass 37 | -------------------------------------------------------------------------------- /features/controller_specs/engine_routes.feature: -------------------------------------------------------------------------------- 1 | Feature: Engine routes for controllers 2 | 3 | Controller specs can specify the routeset that will be used for the example 4 | group. This is most useful when testing Rails engines. 5 | 6 | Scenario: Specify engine routes 7 | Given a file named "spec/controllers/widgets_controller_spec.rb" with: 8 | """ruby 9 | require "rails_helper" 10 | 11 | # A very simple Rails engine 12 | module MyEngine 13 | class Engine < ::Rails::Engine 14 | isolate_namespace MyEngine 15 | end 16 | 17 | Engine.routes.draw do 18 | resources :widgets, :only => [:show] do 19 | get :random, :on => :collection 20 | end 21 | end 22 | 23 | class WidgetsController < ::ActionController::Base 24 | def random 25 | @random_widget = Widget.all.shuffle.first 26 | redirect_to widget_path(@random_widget) 27 | end 28 | 29 | def show 30 | @widget = Widget.find(params[:id]) 31 | render :text => @widget.name 32 | end 33 | end 34 | end 35 | 36 | RSpec.describe MyEngine::WidgetsController, type: :controller do 37 | routes { MyEngine::Engine.routes } 38 | 39 | it "redirects to a random widget" do 40 | widget1 = Widget.create!(:name => "Widget 1") 41 | widget2 = Widget.create!(:name => "Widget 2") 42 | 43 | get :random 44 | expect(response).to be_redirect 45 | expect(response).to redirect_to(assigns(:random_widget)) 46 | end 47 | end 48 | """ 49 | When I run `rspec spec` 50 | Then the examples should all pass 51 | -------------------------------------------------------------------------------- /features/controller_specs/setting_request_headers.feature: -------------------------------------------------------------------------------- 1 | Feature: Setting request headers 2 | 3 | We recommend you to switch to request specs instead of controller specs if you want to set 4 | headers in your call. If you still want to set headers in controller specs, you can use 5 | `request.headers` as mentioned below. 6 | 7 | Scenario: Setting a header value in a controller spec 8 | Given a file named "spec/controllers/application_controller_spec.rb" with: 9 | """ruby 10 | require "rails_helper" 11 | 12 | RSpec.describe ApplicationController, type: :controller do 13 | controller do 14 | def show 15 | if request.headers["Authorization"] == "foo" 16 | head :ok 17 | else 18 | head :forbidden 19 | end 20 | end 21 | end 22 | 23 | before do 24 | routes.draw { get "show" => "anonymous#show" } 25 | end 26 | 27 | context "valid Authorization header" do 28 | it "returns a 200" do 29 | request.headers["Authorization"] = "foo" 30 | 31 | get :show 32 | 33 | expect(response).to have_http_status(:ok) 34 | end 35 | end 36 | 37 | context "invalid Authorization header" do 38 | it "returns a 403" do 39 | request.headers["Authorization"] = "bar" 40 | 41 | get :show 42 | 43 | expect(response).to have_http_status(:forbidden) 44 | end 45 | end 46 | end 47 | """ 48 | When I run `rspec spec` 49 | Then the example should pass 50 | -------------------------------------------------------------------------------- /features/feature_specs/feature_spec.feature: -------------------------------------------------------------------------------- 1 | Feature: Feature specs 2 | 3 | Feature specs are high-level tests meant to exercise slices of functionality 4 | through an application. They should drive the application only via its external 5 | interface, usually web pages. 6 | 7 | Feature specs are marked by `type: :feature` or if you have set 8 | `config.infer_spec_type_from_file_location!` by placing them in 9 | `spec/features`. 10 | 11 | Feature specs require the [Capybara](https://github.com/teamcapybara/capybara) gem, version 2.13.0 or later. 12 | Refer to the [capybara API documentation](https://rubydoc.info/github/teamcapybara/capybara/master) for more information on the methods and matchers that can be 13 | used in feature specs. Capybara is intended to simulate browser requests with HTTP. It will primarily send HTML content. 14 | 15 | The `feature` and `scenario` DSL correspond to `describe` and `it`, respectively. 16 | These methods are simply aliases that allow feature specs to read more as 17 | [customer](http://c2.com/cgi/wiki?CustomerTest) and [acceptance](http://c2.com/cgi/wiki?AcceptanceTest) tests. When capybara is required it sets 18 | `type: :feature` automatically for you. 19 | 20 | @capybara 21 | Scenario: Specify creating a Widget by driving the application with capybara 22 | Given a file named "spec/features/widget_management_spec.rb" with: 23 | """ruby 24 | require "rails_helper" 25 | 26 | RSpec.feature "Widget management", type: :feature do 27 | scenario "User creates a new widget" do 28 | visit "/widgets/new" 29 | 30 | click_button "Create Widget" 31 | 32 | expect(page).to have_text("Widget was successfully created.") 33 | end 34 | end 35 | """ 36 | When I run `rspec spec/features/widget_management_spec.rb` 37 | Then the example should pass 38 | -------------------------------------------------------------------------------- /features/file_fixture.feature: -------------------------------------------------------------------------------- 1 | Feature: Using `file_fixture` 2 | 3 | Rails 5 adds simple access to sample files called file fixtures. 4 | File fixtures are normal files stored in spec/fixtures/files by default. 5 | 6 | File fixtures are represented as +Pathname+ objects. 7 | This makes it easy to extract specific information: 8 | 9 | ```ruby 10 | file_fixture("example.txt").read # get the file's content 11 | file_fixture("example.mp3").size # get the file size 12 | ``` 13 | 14 | You can customize files location by setting 15 | ```ruby 16 | RSpec.configure do |config| 17 | config.file_fixture_path = "spec/custom_directory" 18 | end 19 | ``` 20 | 21 | Scenario: Reading file content from fixtures directory 22 | And a file named "spec/fixtures/files/sample.txt" with: 23 | """ 24 | Hello 25 | """ 26 | 27 | And a file named "spec/lib/file_spec.rb" with: 28 | """ruby 29 | require "rails_helper" 30 | 31 | RSpec.describe "file" do 32 | it "reads sample file" do 33 | expect(file_fixture("sample.txt").read).to eq("Hello") 34 | end 35 | end 36 | """ 37 | When I run `rspec spec/lib/file_spec.rb` 38 | Then the examples should all pass 39 | 40 | Scenario: Creating a ActiveStorage::Blob from a file fixture 41 | Given a file named "spec/fixtures/files/sample.txt" with: 42 | """ 43 | Hello 44 | """ 45 | And a file named "spec/lib/fixture_set_blob.rb" with: 46 | """ruby 47 | require "rails_helper" 48 | 49 | RSpec.describe "blob" do 50 | it "creates a blob from a sample file" do 51 | expect(ActiveStorage::FixtureSet.blob(filename: "sample.txt")).to include("sample.txt") 52 | end 53 | end 54 | """ 55 | When I run `rspec spec/lib/fixture_set_blob.rb` 56 | Then the examples should all pass 57 | -------------------------------------------------------------------------------- /features/generator_specs/channel_specs.feature: -------------------------------------------------------------------------------- 1 | Feature: Channel generator spec 2 | 3 | Scenario: Channel generator 4 | When I run `bundle exec rails generate channel group` 5 | Then the features should pass 6 | Then the output should contain: 7 | """ 8 | invoke rspec 9 | create spec/channels/group_channel_spec.rb 10 | """ 11 | Then the output should contain: 12 | """ 13 | create app/channels/group_channel.rb 14 | """ 15 | 16 | Scenario: Channel generator with customized `default-path` 17 | Given a file named ".rspec" with: 18 | """ 19 | --default-path behaviour 20 | """ 21 | And I run `bundle exec rails generate channel group` 22 | Then the features should pass 23 | Then the output should contain: 24 | """ 25 | invoke rspec 26 | create behaviour/channels/group_channel_spec.rb 27 | """ 28 | Then the output should contain: 29 | """ 30 | create app/channels/group_channel.rb 31 | """ 32 | -------------------------------------------------------------------------------- /features/generator_specs/controller_specs.feature: -------------------------------------------------------------------------------- 1 | Feature: Controller generator spec 2 | 3 | Scenario: Controller generator 4 | When I run `bundle exec rails generate controller posts` 5 | Then the features should pass 6 | Then the output should contain: 7 | """ 8 | create app/controllers/posts_controller.rb 9 | invoke erb 10 | create app/views/posts 11 | invoke rspec 12 | create spec/requests/posts_spec.rb 13 | invoke helper 14 | create app/helpers/posts_helper.rb 15 | invoke rspec 16 | create spec/helpers/posts_helper_spec.rb 17 | """ 18 | 19 | Scenario: Controller generator with customized `default-path` 20 | Given a file named ".rspec" with: 21 | """ 22 | --default-path behaviour 23 | """ 24 | And I run `bundle exec rails generate controller posts` 25 | Then the features should pass 26 | Then the output should contain: 27 | """ 28 | create app/controllers/posts_controller.rb 29 | invoke erb 30 | create app/views/posts 31 | invoke rspec 32 | create behaviour/requests/posts_spec.rb 33 | invoke helper 34 | create app/helpers/posts_helper.rb 35 | invoke rspec 36 | create behaviour/helpers/posts_helper_spec.rb 37 | """ 38 | -------------------------------------------------------------------------------- /features/generator_specs/feature_specs.feature: -------------------------------------------------------------------------------- 1 | Feature: Feature generator spec 2 | 3 | Scenario: Feature generator 4 | When I run `bundle exec rails generate rspec:feature posts` 5 | Then the features should pass 6 | Then the output should contain: 7 | """ 8 | create spec/features/posts_spec.rb 9 | """ 10 | 11 | Scenario: Feature generator with customized `default-path` 12 | Given a file named ".rspec" with: 13 | """ 14 | --default-path behaviour 15 | """ 16 | And I run `bundle exec rails generate rspec:feature posts` 17 | Then the features should pass 18 | Then the output should contain: 19 | """ 20 | create behaviour/features/posts_spec.rb 21 | """ 22 | -------------------------------------------------------------------------------- /features/generator_specs/generator_specs.feature: -------------------------------------------------------------------------------- 1 | Feature: Generator spec 2 | 3 | RSpec spec(s) can be generated when generating application components. For instance, `rails generate model` will also generate an RSpec spec file for the model but you can also write your own generator. See [customizing your workflow](https://guides.rubyonrails.org/generators.html#customizing-your-workflow) 4 | 5 | Scenario: Use custom generator 6 | When I run `bundle exec rails generate generator my_generator` 7 | Then the features should pass 8 | Then the output should contain: 9 | """ 10 | create lib/generators/my_generator 11 | create lib/generators/my_generator/my_generator_generator.rb 12 | create lib/generators/my_generator/USAGE 13 | create lib/generators/my_generator/templates 14 | invoke rspec 15 | create spec/generator/my_generator_generator_spec.rb 16 | """ 17 | 18 | Scenario: Use custom generator with customized `default-path` 19 | Given a file named ".rspec" with: 20 | """ 21 | --default-path behaviour 22 | """ 23 | And I run `bundle exec rails generate generator my_generator` 24 | Then the features should pass 25 | Then the output should contain: 26 | """ 27 | create lib/generators/my_generator 28 | create lib/generators/my_generator/my_generator_generator.rb 29 | create lib/generators/my_generator/USAGE 30 | create lib/generators/my_generator/templates 31 | invoke rspec 32 | create behaviour/generator/my_generator_generator_spec.rb 33 | """ 34 | -------------------------------------------------------------------------------- /features/generator_specs/helper_specs.feature: -------------------------------------------------------------------------------- 1 | Feature: Helper generator spec 2 | 3 | Scenario: Helper generator 4 | When I run `bundle exec rails generate helper posts` 5 | Then the features should pass 6 | Then the output should contain: 7 | """ 8 | create app/helpers/posts_helper.rb 9 | invoke rspec 10 | create spec/helpers/posts_helper_spec.rb 11 | """ 12 | 13 | Scenario: Helper generator with customized `default-path` 14 | Given a file named ".rspec" with: 15 | """ 16 | --default-path behaviour 17 | """ 18 | And I run `bundle exec rails generate helper posts` 19 | Then the features should pass 20 | Then the output should contain: 21 | """ 22 | create app/helpers/posts_helper.rb 23 | invoke rspec 24 | create behaviour/helpers/posts_helper_spec.rb 25 | """ 26 | -------------------------------------------------------------------------------- /features/generator_specs/job_specs.feature: -------------------------------------------------------------------------------- 1 | Feature: Job generator spec 2 | 3 | Scenario: Job generator 4 | When I run `bundle exec rails generate job user` 5 | Then the features should pass 6 | Then the output should contain: 7 | """ 8 | invoke rspec 9 | create spec/jobs/user_job_spec.rb 10 | create app/jobs/user_job.rb 11 | """ 12 | 13 | Scenario: Job generator with customized `default-path` 14 | Given a file named ".rspec" with: 15 | """ 16 | --default-path behaviour 17 | """ 18 | And I run `bundle exec rails generate job user` 19 | Then the features should pass 20 | Then the output should contain: 21 | """ 22 | invoke rspec 23 | create behaviour/jobs/user_job_spec.rb 24 | create app/jobs/user_job.rb 25 | """ 26 | -------------------------------------------------------------------------------- /features/generator_specs/mailbox_specs.feature: -------------------------------------------------------------------------------- 1 | Feature: Mailbox generator spec 2 | 3 | Scenario: Mailbox generator 4 | When I run `bundle exec rails generate mailbox forwards` 5 | Then the features should pass 6 | Then the output should contain: 7 | """ 8 | create app/mailboxes/forwards_mailbox.rb 9 | invoke rspec 10 | create spec/mailboxes/forwards_mailbox_spec.rb 11 | """ 12 | 13 | Scenario: Mailbox generator with customized `default-path` 14 | Given a file named ".rspec" with: 15 | """ 16 | --default-path behaviour 17 | """ 18 | And I run `bundle exec rails generate mailbox forwards` 19 | Then the features should pass 20 | Then the output should contain: 21 | """ 22 | create app/mailboxes/forwards_mailbox.rb 23 | invoke rspec 24 | create behaviour/mailboxes/forwards_mailbox_spec.rb 25 | """ 26 | -------------------------------------------------------------------------------- /features/generator_specs/mailer_specs.feature: -------------------------------------------------------------------------------- 1 | Feature: Mailer generator spec 2 | 3 | Scenario: Mailer generator 4 | When I run `bundle exec rails generate mailer posts index show` 5 | Then the features should pass 6 | Then the output should contain: 7 | """ 8 | create app/mailers/posts_mailer.rb 9 | invoke erb 10 | create app/views/posts_mailer 11 | create app/views/posts_mailer/index.text.erb 12 | create app/views/posts_mailer/index.html.erb 13 | create app/views/posts_mailer/show.text.erb 14 | create app/views/posts_mailer/show.html.erb 15 | invoke rspec 16 | create spec/mailers/posts_mailer_spec.rb 17 | create spec/fixtures/posts/index 18 | create spec/fixtures/posts/show 19 | create spec/mailers/previews/posts_mailer_preview.rb 20 | """ 21 | 22 | Scenario: Mailer generator with customized `default-path` 23 | Given a file named ".rspec" with: 24 | """ 25 | --default-path behaviour 26 | """ 27 | And I run `bundle exec rails generate mailer posts index show` 28 | Then the features should pass 29 | Then the output should contain: 30 | """ 31 | create app/mailers/posts_mailer.rb 32 | invoke erb 33 | create app/views/posts_mailer 34 | create app/views/posts_mailer/index.text.erb 35 | create app/views/posts_mailer/index.html.erb 36 | create app/views/posts_mailer/show.text.erb 37 | create app/views/posts_mailer/show.html.erb 38 | invoke rspec 39 | create behaviour/mailers/posts_mailer_spec.rb 40 | create behaviour/fixtures/posts/index 41 | create behaviour/fixtures/posts/show 42 | create behaviour/mailers/previews/posts_mailer_preview.rb 43 | """ 44 | -------------------------------------------------------------------------------- /features/generator_specs/request_specs.feature: -------------------------------------------------------------------------------- 1 | Feature: Request generator spec 2 | 3 | Scenario: Request generator 4 | When I run `bundle exec rails generate rspec:request posts` 5 | Then the features should pass 6 | Then the output should contain: 7 | """ 8 | create spec/requests/posts_spec.rb 9 | """ 10 | 11 | Scenario: Request generator with customized `default-path` 12 | Given a file named ".rspec" with: 13 | """ 14 | --default-path behaviour 15 | """ 16 | And I run `bundle exec rails generate rspec:request posts` 17 | Then the features should pass 18 | Then the output should contain: 19 | """ 20 | create behaviour/requests/posts_spec.rb 21 | """ 22 | -------------------------------------------------------------------------------- /features/generator_specs/system_specs.feature: -------------------------------------------------------------------------------- 1 | Feature: System generator spec 2 | 3 | Scenario: System generator 4 | When I run `bundle exec rails generate rspec:system posts` 5 | Then the features should pass 6 | Then the output should contain: 7 | """ 8 | create spec/system/posts_spec.rb 9 | """ 10 | 11 | Scenario: System generator with customized `default-path` 12 | Given a file named ".rspec" with: 13 | """ 14 | --default-path behaviour 15 | """ 16 | And I run `bundle exec rails generate rspec:system posts` 17 | Then the features should pass 18 | Then the output should contain: 19 | """ 20 | create behaviour/system/posts_spec.rb 21 | """ 22 | -------------------------------------------------------------------------------- /features/generator_specs/view_specs.feature: -------------------------------------------------------------------------------- 1 | Feature: View generator spec 2 | 3 | Scenario: View generator 4 | When I run `bundle exec rails generate rspec:view posts index` 5 | Then the features should pass 6 | Then the output should contain: 7 | """ 8 | create spec/views/posts 9 | create spec/views/posts/index.html.erb_spec.rb 10 | """ 11 | 12 | Scenario: View generator with customized `default-path` 13 | Given a file named ".rspec" with: 14 | """ 15 | --default-path behaviour 16 | """ 17 | And I run `bundle exec rails generate rspec:view posts index` 18 | Then the features should pass 19 | Then the output should contain: 20 | """ 21 | create behaviour/views/posts 22 | create behaviour/views/posts/index.html.erb_spec.rb 23 | """ 24 | -------------------------------------------------------------------------------- /features/mailer_specs/README.md: -------------------------------------------------------------------------------- 1 | # Mailer specs 2 | 3 | By default Mailer specs reside in the `spec/mailers` folder. Adding the metadata 4 | `type: :mailer` to any context makes its examples be treated as mailer specs. 5 | 6 | A mailer spec is a thin wrapper for an ActionMailer::TestCase, and includes all 7 | of the behavior and assertions that it provides, in addition to RSpec's own 8 | behavior and expectations. 9 | 10 | ## Examples 11 | 12 | ```ruby 13 | require "rails_helper" 14 | 15 | RSpec.describe Notifications, type: :mailer do 16 | describe "notify" do 17 | let(:mail) { Notifications.signup } 18 | 19 | it "renders the headers" do 20 | expect(mail.subject).to eq("Signup") 21 | expect(mail.to).to eq(["to@example.org"]) 22 | expect(mail.from).to eq(["from@example.com"]) 23 | end 24 | 25 | it "renders the body" do 26 | expect(mail.body.encoded).to match("Hi") 27 | end 28 | end 29 | end 30 | ``` 31 | -------------------------------------------------------------------------------- /features/mailer_specs/mailer_spec.feature: -------------------------------------------------------------------------------- 1 | Feature: Mailer specs 2 | 3 | Scenario: A simple passing example 4 | Given a file named "spec/mailers/notifications_mailer_spec.rb" with: 5 | """ruby 6 | require "rails_helper" 7 | 8 | RSpec.describe NotificationsMailer, type: :mailer do 9 | describe "notify" do 10 | let(:mail) { NotificationsMailer.signup } 11 | 12 | it "renders the headers" do 13 | expect(mail.subject).to eq("Signup") 14 | expect(mail.to).to eq(["to@example.org"]) 15 | expect(mail.from).to eq(["from@example.com"]) 16 | end 17 | 18 | it "renders the body" do 19 | expect(mail.body.encoded).to match("Hi") 20 | end 21 | end 22 | end 23 | """ 24 | When I run `rspec spec` 25 | Then the example should pass 26 | -------------------------------------------------------------------------------- /features/mailer_specs/url_helpers.feature: -------------------------------------------------------------------------------- 1 | Feature: URL helpers in mailer examples 2 | 3 | Mailer specs are marked by `type: :mailer` or if you have set 4 | `config.infer_spec_type_from_file_location!` by placing them in `spec/mailers`. 5 | 6 | Scenario: Using URL helpers with default options 7 | Given a file named "config/initializers/mailer_defaults.rb" with: 8 | """ruby 9 | Rails.configuration.action_mailer.default_url_options = { :host => 'example.com' } 10 | """ 11 | And a file named "spec/mailers/notifications_spec.rb" with: 12 | """ruby 13 | require 'rails_helper' 14 | 15 | RSpec.describe NotificationsMailer, type: :mailer do 16 | it 'should have access to URL helpers' do 17 | expect { gadgets_url }.not_to raise_error 18 | end 19 | end 20 | """ 21 | When I run `rspec spec` 22 | Then the examples should all pass 23 | 24 | Scenario: Using URL helpers without default options 25 | Given a file named "config/initializers/mailer_defaults.rb" with: 26 | """ruby 27 | # no default options 28 | """ 29 | And a file named "spec/mailers/notifications_spec.rb" with: 30 | """ruby 31 | require 'rails_helper' 32 | 33 | RSpec.describe NotificationsMailer, type: :mailer do 34 | it 'should have access to URL helpers' do 35 | expect { gadgets_url :host => 'example.com' }.not_to raise_error 36 | expect { gadgets_url }.to raise_error 37 | end 38 | end 39 | """ 40 | When I run `rspec spec` 41 | Then the examples should all pass 42 | -------------------------------------------------------------------------------- /features/matchers/README.md: -------------------------------------------------------------------------------- 1 | # Matchers 2 | 3 | rspec-rails offers a number of custom matchers, most of which are 4 | rspec-compatible wrappers for Rails' assertions. 5 | 6 | ### redirects 7 | 8 | ```ruby 9 | # delegates to assert_redirected_to 10 | expect(response).to redirect_to(path) 11 | ``` 12 | 13 | ### templates 14 | 15 | ```ruby 16 | # delegates to assert_template 17 | expect(response).to render_template(template_name) 18 | ``` 19 | 20 | ### assigned objects 21 | 22 | ```ruby 23 | # passes if assigns(:widget) is an instance of Widget 24 | # and it is not persisted 25 | expect(assigns(:widget)).to be_a_new(Widget) 26 | ``` 27 | -------------------------------------------------------------------------------- /features/matchers/new_record_matcher.feature: -------------------------------------------------------------------------------- 1 | Feature: `be_a_new` matcher 2 | 3 | The `be_a_new` matcher accepts a class and passes if the subject is an 4 | instance of that class that returns false to persisted? 5 | 6 | You can also chain `with` on `be_a_new` with a hash of attributes to specify 7 | the subject has equal attributes. 8 | 9 | Scenario: An example spec with four be_a_new possibilities 10 | Given a file named "spec/models/widget_spec.rb" with: 11 | """ruby 12 | require "rails_helper" 13 | 14 | RSpec.describe Widget do 15 | context "when initialized" do 16 | subject(:widget) { Widget.new } 17 | 18 | it "is a new widget" do 19 | expect(widget).to be_a_new(Widget) 20 | end 21 | 22 | it "is not a new string" do 23 | expect(widget).not_to be_a_new(String) 24 | end 25 | end 26 | 27 | context "when saved" do 28 | subject(:widget) { Widget.create } 29 | 30 | it "is not a new widget" do 31 | expect(widget).not_to be_a_new(Widget) 32 | end 33 | 34 | it "is not a new string" do 35 | expect(widget).not_to be_a_new(String) 36 | end 37 | end 38 | end 39 | """ 40 | When I run `rspec spec/models/widget_spec.rb` 41 | Then the examples should all pass 42 | -------------------------------------------------------------------------------- /features/matchers/redirect_to_matcher.feature: -------------------------------------------------------------------------------- 1 | Feature: `redirect_to` matcher 2 | 3 | The `redirect_to` matcher is used to specify that a request redirects to a 4 | given template or action. It delegates to 5 | [`assert_redirected_to`](https://api.rubyonrails.org/classes/ActionDispatch/Assertions/ResponseAssertions.html#method-i-assert_redirected_to). 6 | 7 | It is available in controller specs (spec/controllers) and request 8 | specs (spec/requests). 9 | 10 | Scenario: Using `redirect_to` with four possible options 11 | Given a file named "spec/controllers/widgets_controller_spec.rb" with: 12 | """ruby 13 | require "rails_helper" 14 | 15 | RSpec.describe WidgetsController , type: :controller do 16 | 17 | describe "#create" do 18 | subject { post :create, :params => { :widget => { :name => "Foo" } } } 19 | 20 | it "redirects to widget_url(@widget)" do 21 | expect(subject).to redirect_to(widget_url(assigns(:widget))) 22 | end 23 | 24 | it "redirects_to :action => :show" do 25 | expect(subject).to redirect_to :action => :show, 26 | :id => assigns(:widget).id 27 | end 28 | 29 | it "redirects_to(@widget)" do 30 | expect(subject).to redirect_to(assigns(:widget)) 31 | end 32 | 33 | it "redirects_to /widgets/:id" do 34 | expect(subject).to redirect_to("/widgets/#{assigns(:widget).id}") 35 | end 36 | end 37 | end 38 | """ 39 | When I run `rspec spec/controllers/widgets_controller_spec.rb` 40 | Then the examples should all pass 41 | -------------------------------------------------------------------------------- /features/matchers/relation_match_array.feature: -------------------------------------------------------------------------------- 1 | Feature: ActiveRecord::Relation match array 2 | 3 | The `match_array` matcher can be used with an `ActiveRecord::Relation` 4 | (scope). The assertion will pass if the scope would return all of the 5 | elements specified in the array on the right hand side. 6 | 7 | Scenario: An example spec with relation match_array matcher 8 | Given a file named "spec/models/widget_spec.rb" with: 9 | """ruby 10 | require "rails_helper" 11 | 12 | RSpec.describe Widget do 13 | let!(:widgets) { Array.new(3) { Widget.create } } 14 | 15 | subject { Widget.all } 16 | 17 | it "returns all widgets in any order" do 18 | expect(subject).to match_array(widgets) 19 | end 20 | end 21 | """ 22 | When I run `rspec spec/models/widget_spec.rb` 23 | Then the examples should all pass 24 | -------------------------------------------------------------------------------- /features/model_specs/README.md: -------------------------------------------------------------------------------- 1 | # Model specs 2 | 3 | Model specs are marked by `type: :model` or if you have set 4 | `config.infer_spec_type_from_file_location!` by placing them in `spec/models`. 5 | 6 | A model spec is a thin wrapper for an ActiveSupport::TestCase, and includes all 7 | of the behavior and assertions that it provides, in addition to RSpec's own 8 | behavior and expectations. 9 | 10 | ## Examples 11 | 12 | ```ruby 13 | require "rails_helper" 14 | 15 | RSpec.describe Post, type: :model do 16 | context "with 2 or more comments" do 17 | it "orders them in reverse chronologically" do 18 | post = Post.create! 19 | comment1 = post.comments.create!(:body => "first comment") 20 | comment2 = post.comments.create!(:body => "second comment") 21 | expect(post.reload.comments).to eq([comment2, comment1]) 22 | end 23 | end 24 | end 25 | ``` 26 | -------------------------------------------------------------------------------- /features/model_specs/verified_doubles.feature: -------------------------------------------------------------------------------- 1 | Feature: Using verified doubles 2 | 3 | By default rspec verified doubles dont support dynamic methods on 4 | `instance_double`. `rspec-rails` enabled this support for column 5 | methods through an extension. 6 | 7 | Scenario: 8 | Given a file named "spec/models/widget_spec.rb" with: 9 | """ruby 10 | require "rails_helper" 11 | 12 | RSpec.describe Widget, type: :model do 13 | it "has one after adding one" do 14 | instance_double("Widget", :name => "my name") 15 | end 16 | end 17 | """ 18 | When I run `rspec spec/models/widget_spec.rb` 19 | Then the examples should all pass 20 | -------------------------------------------------------------------------------- /features/routing_specs/README.md: -------------------------------------------------------------------------------- 1 | # Routing specs 2 | 3 | Routing specs are marked by `type: :routing` or if you have set 4 | `config.infer_spec_type_from_file_location!` by placing them in `spec/routing`. 5 | 6 | Simple apps with nothing but standard RESTful routes won't get much value from 7 | routing specs, but they can provide significant value when used to specify 8 | customized routes, like vanity links, slugs, etc. 9 | 10 | ```ruby 11 | expect(:get => "/articles/2012/11/when-to-use-routing-specs").to route_to( 12 | :controller => "articles", 13 | :month => "2012-11", 14 | :slug => "when-to-use-routing-specs" 15 | ) 16 | ``` 17 | 18 | They are also valuable for routes that should not be available: 19 | 20 | ```ruby 21 | expect(:delete => "/accounts/37").not_to be_routable 22 | ``` 23 | -------------------------------------------------------------------------------- /features/routing_specs/engine_routes.feature: -------------------------------------------------------------------------------- 1 | Feature: Using engine routes 2 | 3 | Routing specs can specify the routeset that will be used for the example 4 | group. This is most useful when testing Rails engines. 5 | 6 | Scenario: Specify engine routes 7 | Given a file named "spec/routing/engine_routes_spec.rb" with: 8 | """ruby 9 | require "rails_helper" 10 | 11 | # A very simple Rails engine 12 | module MyEngine 13 | class Engine < ::Rails::Engine 14 | isolate_namespace MyEngine 15 | end 16 | 17 | Engine.routes.draw do 18 | resources :widgets, :only => [:index] 19 | end 20 | 21 | class WidgetsController < ::ActionController::Base 22 | def index 23 | end 24 | end 25 | end 26 | 27 | RSpec.describe MyEngine::WidgetsController, type: :routing do 28 | routes { MyEngine::Engine.routes } 29 | 30 | it "routes to the list of all widgets" do 31 | expect(:get => widgets_path). 32 | to route_to(:controller => "my_engine/widgets", :action => "index") 33 | end 34 | end 35 | """ 36 | When I run `rspec spec` 37 | Then the examples should all pass 38 | -------------------------------------------------------------------------------- /features/routing_specs/named_routes.feature: -------------------------------------------------------------------------------- 1 | Feature: Using named routes 2 | 3 | Routing specs have access to named routes. 4 | 5 | Scenario: Access named route 6 | Given a file named "spec/routing/widget_routes_spec.rb" with: 7 | """ruby 8 | require "rails_helper" 9 | 10 | RSpec.describe "routes to the widgets controller", type: :routing do 11 | it "routes a named route" do 12 | expect(:get => new_widget_path). 13 | to route_to(:controller => "widgets", :action => "new") 14 | end 15 | end 16 | """ 17 | When I run `rspec spec` 18 | Then the examples should all pass 19 | -------------------------------------------------------------------------------- /features/step_definitions/additional_cli_steps.rb: -------------------------------------------------------------------------------- 1 | begin 2 | require "active_job" 3 | rescue LoadError # rubocop:disable Lint/SuppressedException 4 | end 5 | begin 6 | require "action_cable" 7 | rescue LoadError # rubocop:disable Lint/SuppressedException 8 | end 9 | begin 10 | require "active_storage" 11 | require "action_mailbox" 12 | rescue LoadError # rubocop:disable Lint/SuppressedException 13 | end 14 | 15 | require "rails/version" 16 | 17 | require "rspec/rails/feature_check" 18 | 19 | Then /^the example(s)? should( all)? pass$/ do |_, _| 20 | step 'the output should contain "0 failures"' 21 | step 'the exit status should be 0' 22 | end 23 | 24 | Then /^the example(s)? should( all)? fail/ do |_, _| 25 | step 'the output should not contain "0 failures"' 26 | step 'the exit status should not be 0' 27 | end 28 | 29 | Given /active job is available/ do 30 | unless RSpec::Rails::FeatureCheck.has_active_job? 31 | pending "ActiveJob is not available" 32 | end 33 | end 34 | 35 | Given /action cable testing is available/ do 36 | unless RSpec::Rails::FeatureCheck.has_action_cable_testing? 37 | pending "Action Cable testing is not available" 38 | end 39 | end 40 | 41 | Given /action mailbox is available/ do 42 | unless RSpec::Rails::FeatureCheck.has_action_mailbox? 43 | pending "Action Mailbox is not available" 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /features/support/capybara.rb: -------------------------------------------------------------------------------- 1 | Around "@capybara" do |_scenario, block| 2 | require 'capybara' 3 | block.call 4 | end 5 | -------------------------------------------------------------------------------- /features/support/env.rb: -------------------------------------------------------------------------------- 1 | require 'aruba/cucumber' 2 | require 'fileutils' 3 | 4 | module ArubaExt 5 | def run_command(cmd, timeout = nil) 6 | exec_cmd = cmd =~ /^rspec/ ? "bin/#{cmd}" : cmd 7 | unset_bundler_env_vars 8 | # Ensure the correct Gemfile and binstubs are found 9 | in_current_directory do 10 | with_unbundled_env do 11 | super(exec_cmd, timeout) 12 | end 13 | end 14 | end 15 | 16 | def unset_bundler_env_vars 17 | empty_env = with_environment { with_unbundled_env { ENV.to_h } } 18 | aruba_env = aruba.environment.to_h 19 | (aruba_env.keys - empty_env.keys).each do |key| 20 | delete_environment_variable key 21 | end 22 | empty_env.each do |k, v| 23 | set_environment_variable k, v 24 | end 25 | end 26 | 27 | def with_unbundled_env 28 | if Bundler.respond_to?(:with_unbundled_env) 29 | Bundler.with_unbundled_env { yield } 30 | else 31 | Bundler.with_clean_env { yield } 32 | end 33 | end 34 | end 35 | 36 | World(ArubaExt) 37 | 38 | Aruba.configure do |config| 39 | if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'truffleruby' 40 | config.exit_timeout = 120 41 | else 42 | config.exit_timeout = 30 43 | end 44 | end 45 | 46 | unless File.directory?('./tmp/example_app') 47 | system "rake generate:app generate:stuff" 48 | end 49 | 50 | Before do 51 | example_app_dir = 'tmp/example_app' 52 | # Per default Aruba will create a directory tmp/aruba where it performs its file operations. 53 | # https://github.com/cucumber/aruba/blob/v0.6.1/README.md#use-a-different-working-directory 54 | aruba_dir = 'tmp/aruba' 55 | 56 | # Remove the previous aruba workspace. 57 | FileUtils.rm_rf(aruba_dir) if File.exist?(aruba_dir) 58 | 59 | # We want fresh `example_app` project with empty `spec` dir except helpers. 60 | # FileUtils.cp_r on Ruby 1.9.2 doesn't preserve permissions. 61 | system('cp', '-r', example_app_dir, aruba_dir) 62 | helpers = %w[spec/spec_helper.rb spec/rails_helper.rb] 63 | directories = [] 64 | 65 | Dir["#{aruba_dir}/spec/**/*"].each do |path| 66 | next if helpers.any? { |helper| path.end_with?(helper) } 67 | 68 | # Because we now check for things like spec/support we only want to delete empty directories 69 | if File.directory?(path) 70 | directories << path 71 | next 72 | end 73 | 74 | FileUtils.rm_rf(path) 75 | end 76 | 77 | directories.each { |dir| FileUtils.rm_rf(dir) if Dir.empty?(dir) } 78 | end 79 | -------------------------------------------------------------------------------- /features/view_specs/inferred_controller_path.feature: -------------------------------------------------------------------------------- 1 | Feature: View specs infer controller's path and action 2 | 3 | Scenario: Infer controller path 4 | Given a file named "spec/views/widgets/new.html.erb_spec.rb" with: 5 | """ruby 6 | require "rails_helper" 7 | 8 | RSpec.describe "widgets/new" , type: :view do 9 | it "infers the controller path" do 10 | expect(controller.request.path_parameters[:controller]).to eq("widgets") 11 | expect(controller.controller_path).to eq("widgets") 12 | end 13 | end 14 | """ 15 | When I run `rspec spec/views` 16 | Then the examples should all pass 17 | 18 | Scenario: Infer action 19 | Given a file named "spec/views/widgets/new.html.erb_spec.rb" with: 20 | """ruby 21 | require "rails_helper" 22 | 23 | RSpec.describe "widgets/new" , type: :view do 24 | it "infers the controller action" do 25 | expect(controller.request.path_parameters[:action]).to eq("new") 26 | end 27 | end 28 | """ 29 | When I run `rspec spec/views` 30 | Then the examples should all pass 31 | 32 | Scenario: Do not infer action in a partial 33 | Given a file named "spec/views/widgets/_form.html.erb_spec.rb" with: 34 | """ruby 35 | require "rails_helper" 36 | 37 | RSpec.describe "widgets/_form" , type: :view do 38 | it "includes a link to new" do 39 | expect(controller.request.path_parameters[:action]).to be_nil 40 | end 41 | end 42 | """ 43 | When I run `rspec spec/views` 44 | Then the examples should all pass 45 | -------------------------------------------------------------------------------- /features/view_specs/stub_template.feature: -------------------------------------------------------------------------------- 1 | Feature: Using `stub_template` 2 | 3 | In order to isolate view specs from the partials rendered by the primary 4 | view, rspec-rails (since 2.2) provides the stub_template method. 5 | 6 | Scenario: Stub a template that does not exist 7 | Given a file named "spec/views/gadgets/list.html.erb_spec.rb" with: 8 | """ruby 9 | require "rails_helper" 10 | 11 | RSpec.describe "gadgets/list" , type: :view do 12 | it "renders the gadget partial for each gadget" do 13 | assign(:gadgets, [ 14 | double(:name => "First"), 15 | double(:name => "Second") 16 | ]) 17 | stub_template "gadgets/_gadget.html.erb" => "<%= gadget.name %>
" 18 | render 19 | expect(rendered).to match /First/ 20 | expect(rendered).to match /Second/ 21 | end 22 | end 23 | """ 24 | 25 | And a file named "app/views/gadgets/list.html.erb" with: 26 | """ 27 | <%= render :partial => "gadget", :collection => @gadgets %> 28 | """ 29 | When I run `rspec spec/views/gadgets/list.html.erb_spec.rb` 30 | Then the examples should all pass 31 | 32 | Scenario: Stub a template that exists 33 | Given a file named "spec/views/gadgets/edit.html.erb_spec.rb" with: 34 | """ruby 35 | require "rails_helper" 36 | 37 | RSpec.describe "gadgets/edit" , type: :view do 38 | before(:each) do 39 | @gadget = assign(:gadget, Gadget.create!) 40 | end 41 | 42 | it "renders the form partial" do 43 | stub_template "gadgets/_form.html.erb" => "This content" 44 | render 45 | expect(rendered).to match /This content/ 46 | end 47 | end 48 | """ 49 | When I run `rspec spec/views/gadgets/edit.html.erb_spec.rb` 50 | Then the examples should all pass 51 | -------------------------------------------------------------------------------- /lib/generators/rspec.rb: -------------------------------------------------------------------------------- 1 | require 'rails/generators/named_base' 2 | require 'rspec/core' 3 | require 'rspec/rails/feature_check' 4 | 5 | # @private 6 | # Weirdly named generators namespace (should be `RSpec`) for compatibility with 7 | # rails loading. 8 | module Rspec 9 | # @private 10 | module Generators 11 | # @private 12 | class Base < ::Rails::Generators::NamedBase 13 | include RSpec::Rails::FeatureCheck 14 | 15 | def self.source_root(path = nil) 16 | if path 17 | @_rspec_source_root = path 18 | else 19 | @_rspec_source_root ||= File.expand_path(File.join(File.dirname(__FILE__), 'rspec', generator_name, 'templates')) 20 | end 21 | end 22 | 23 | # @private 24 | # Load configuration from RSpec to ensure `--default-path` is set 25 | def self.configuration 26 | @configuration ||= 27 | begin 28 | configuration = RSpec.configuration 29 | options = RSpec::Core::ConfigurationOptions.new({}) 30 | options.configure(configuration) 31 | configuration 32 | end 33 | end 34 | 35 | def target_path(*paths) 36 | File.join(self.class.configuration.default_path, *paths) 37 | end 38 | end 39 | end 40 | end 41 | 42 | # @private 43 | module Rails 44 | module Generators 45 | # @private 46 | class GeneratedAttribute 47 | def input_type 48 | @input_type ||= if type == :text 49 | "textarea" 50 | else 51 | "input" 52 | end 53 | end 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /lib/generators/rspec/authentication/authentication_generator.rb: -------------------------------------------------------------------------------- 1 | require 'generators/rspec' 2 | 3 | module Rspec 4 | module Generators 5 | # @private 6 | class AuthenticationGenerator < Base 7 | def initialize(args, *options) 8 | args.replace(['User']) 9 | super 10 | end 11 | 12 | def create_user_spec 13 | template 'user_spec.rb', target_path('models', 'user_spec.rb') 14 | end 15 | 16 | hook_for :fixture_replacement 17 | 18 | def create_fixture_file 19 | return if options[:fixture_replacement] 20 | 21 | template 'users.yml', target_path('fixtures', 'users.yml') 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/generators/rspec/authentication/templates/user_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe User, <%= type_metatag(:model) %> do 4 | pending "add some examples to (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /lib/generators/rspec/authentication/templates/users.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | <%% password_digest = BCrypt::Password.create("password") %> 4 | 5 | one: 6 | email_address: one@example.com 7 | password_digest: <%%= password_digest %> 8 | 9 | two: 10 | email_address: two@example.com 11 | password_digest: <%%= password_digest %> 12 | -------------------------------------------------------------------------------- /lib/generators/rspec/channel/channel_generator.rb: -------------------------------------------------------------------------------- 1 | require 'generators/rspec' 2 | 3 | module Rspec 4 | module Generators 5 | # @private 6 | class ChannelGenerator < Base 7 | def create_channel_spec 8 | template 'channel_spec.rb.erb', target_path('channels', class_path, "#{file_name}_channel_spec.rb") 9 | end 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/generators/rspec/channel/templates/channel_spec.rb.erb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | <% module_namespacing do -%> 4 | RSpec.describe <%= class_name %>Channel, <%= type_metatag(:channel) %> do 5 | pending "add some examples to (or delete) #{__FILE__}" 6 | end 7 | <% end -%> 8 | -------------------------------------------------------------------------------- /lib/generators/rspec/controller/controller_generator.rb: -------------------------------------------------------------------------------- 1 | require 'generators/rspec' 2 | 3 | module Rspec 4 | module Generators 5 | # @private 6 | class ControllerGenerator < Base 7 | argument :actions, type: :array, default: [], banner: "action action" 8 | 9 | class_option :template_engine, desc: "Template engine to generate view files" 10 | class_option :request_specs, type: :boolean, default: true, desc: "Generate request specs" 11 | class_option :controller_specs, type: :boolean, default: false, desc: "Generate controller specs" 12 | class_option :view_specs, type: :boolean, default: true, desc: "Generate view specs" 13 | class_option :routing_specs, type: :boolean, default: false, desc: "Generate routing specs" 14 | 15 | def generate_request_spec 16 | return unless options[:request_specs] 17 | 18 | template 'request_spec.rb', 19 | target_path('requests', class_path, "#{file_name}_spec.rb") 20 | end 21 | 22 | def generate_controller_spec 23 | return unless options[:controller_specs] 24 | 25 | template 'controller_spec.rb', 26 | target_path('controllers', class_path, "#{file_name}_controller_spec.rb") 27 | end 28 | 29 | def generate_view_specs 30 | return if actions.empty? && behavior == :invoke 31 | return unless options[:view_specs] && options[:template_engine] 32 | 33 | empty_directory File.join("spec", "views", file_path) 34 | 35 | actions.each do |action| 36 | @action = action 37 | template 'view_spec.rb', 38 | target_path('views', file_path, "#{@action}.html.#{options[:template_engine]}_spec.rb") 39 | end 40 | end 41 | 42 | def generate_routing_spec 43 | return if actions.empty? 44 | return unless options[:routing_specs] 45 | 46 | template 'routing_spec.rb', 47 | target_path('routing', class_path, "#{file_name}_routing_spec.rb") 48 | end 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /lib/generators/rspec/controller/templates/controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | <% module_namespacing do -%> 4 | RSpec.describe <%= class_name %>Controller, <%= type_metatag(:controller) %> do 5 | 6 | <% for action in actions -%> 7 | describe "GET #<%= action %>" do 8 | it "returns http success" do 9 | get :<%= action %> 10 | expect(response).to have_http_status(:success) 11 | end 12 | end 13 | 14 | <% end -%> 15 | end 16 | <% end -%> 17 | -------------------------------------------------------------------------------- /lib/generators/rspec/controller/templates/request_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "<%= class_name.pluralize %>", <%= type_metatag(:request) %> do 4 | <% namespaced_path = regular_class_path.join('/') -%> 5 | <% if actions.empty? -%> 6 | describe "GET /index" do 7 | pending "add some examples (or delete) #{__FILE__}" 8 | end 9 | <% end -%> 10 | <% for action in actions -%> 11 | describe "GET /<%= action %>" do 12 | it "returns http success" do 13 | get "<%= "/#{namespaced_path}" if namespaced_path != '' %>/<%= file_name %>/<%= action %>" 14 | expect(response).to have_http_status(:success) 15 | end 16 | end 17 | 18 | <% end -%> 19 | end 20 | -------------------------------------------------------------------------------- /lib/generators/rspec/controller/templates/routing_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | <% module_namespacing do -%> 4 | RSpec.describe '<%= class_name %>Controller', <%= type_metatag(:routing) %> do 5 | describe 'routing' do 6 | <% for action in actions -%> 7 | it 'routes to #<%= action %>' do 8 | expect(get: "/<%= class_name.underscore %>/<%= action %>").to route_to("<%= class_name.underscore %>#<%= action %>") 9 | end 10 | <% end -%> 11 | end 12 | end 13 | <% end -%> 14 | -------------------------------------------------------------------------------- /lib/generators/rspec/controller/templates/view_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "<%= file_name %>/<%= @action %>.html.<%= options[:template_engine] %>", <%= type_metatag(:view) %> do 4 | pending "add some examples to (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /lib/generators/rspec/feature/feature_generator.rb: -------------------------------------------------------------------------------- 1 | require 'generators/rspec' 2 | 3 | module Rspec 4 | module Generators 5 | # @private 6 | class FeatureGenerator < Base 7 | class_option :feature_specs, type: :boolean, default: true, desc: "Generate feature specs" 8 | class_option :singularize, type: :boolean, default: false, desc: "Singularize the generated feature" 9 | 10 | def generate_feature_spec 11 | return unless options[:feature_specs] 12 | 13 | template template_name, target_path('features', class_path, filename) 14 | end 15 | 16 | def template_name 17 | options[:singularize] ? 'feature_singular_spec.rb' : 'feature_spec.rb' 18 | end 19 | 20 | def filename 21 | if options[:singularize] 22 | "#{file_name.singularize}_spec.rb" 23 | else 24 | "#{file_name}_spec.rb" 25 | end 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/generators/rspec/feature/templates/feature_singular_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.feature "<%= class_name.singularize %>", <%= type_metatag(:feature) %> do 4 | pending "add some scenarios (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /lib/generators/rspec/feature/templates/feature_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.feature "<%= class_name.pluralize %>", <%= type_metatag(:feature) %> do 4 | pending "add some scenarios (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /lib/generators/rspec/generator/generator_generator.rb: -------------------------------------------------------------------------------- 1 | require 'generators/rspec' 2 | 3 | module Rspec 4 | module Generators 5 | # @private 6 | class GeneratorGenerator < Base 7 | class_option :generator_specs, type: :boolean, default: true, desc: 'Generate generator specs' 8 | 9 | def generate_generator_spec 10 | return unless options[:generator_specs] 11 | 12 | template template_name, target_path('generator', class_path, filename) 13 | end 14 | 15 | def template_name 16 | 'generator_spec.rb' 17 | end 18 | 19 | def filename 20 | "#{file_name}_generator_spec.rb" 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/generators/rspec/generator/templates/generator_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "<%= class_name %>Generator", <%= type_metatag(:generator) %> do 4 | pending "add some scenarios (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /lib/generators/rspec/helper/helper_generator.rb: -------------------------------------------------------------------------------- 1 | require 'generators/rspec' 2 | 3 | module Rspec 4 | module Generators 5 | # @private 6 | class HelperGenerator < Base 7 | class_option :helper_specs, type: :boolean, default: true 8 | 9 | def generate_helper_spec 10 | return unless options[:helper_specs] 11 | 12 | template 'helper_spec.rb', target_path('helpers', class_path, "#{file_name}_helper_spec.rb") 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/generators/rspec/helper/templates/helper_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | # Specs in this file have access to a helper object that includes 4 | # the <%= class_name %>Helper. For example: 5 | # 6 | # describe <%= class_name %>Helper do 7 | # describe "string concat" do 8 | # it "concats two strings with spaces" do 9 | # expect(helper.concat_strings("this","that")).to eq("this that") 10 | # end 11 | # end 12 | # end 13 | <% module_namespacing do -%> 14 | RSpec.describe <%= class_name %>Helper, <%= type_metatag(:helper) %> do 15 | pending "add some examples to (or delete) #{__FILE__}" 16 | end 17 | <% end -%> 18 | -------------------------------------------------------------------------------- /lib/generators/rspec/install/install_generator.rb: -------------------------------------------------------------------------------- 1 | require "rspec/support" 2 | require "rspec/core" 3 | RSpec::Support.require_rspec_core "project_initializer" 4 | require "rspec/rails/feature_check" 5 | 6 | module Rspec 7 | module Generators 8 | # @private 9 | class InstallGenerator < ::Rails::Generators::Base 10 | desc < 4 | RSpec.describe <%= class_name %><%= "Job" unless class_name.end_with?("Job")%>, <%= type_metatag(:job) %> do 5 | pending "add some examples to (or delete) #{__FILE__}" 6 | end 7 | <% end -%> 8 | -------------------------------------------------------------------------------- /lib/generators/rspec/mailbox/mailbox_generator.rb: -------------------------------------------------------------------------------- 1 | require 'generators/rspec' 2 | 3 | module Rspec 4 | module Generators 5 | # @private 6 | class MailboxGenerator < Base 7 | def create_mailbox_spec 8 | template('mailbox_spec.rb.erb', 9 | target_path('mailboxes', class_path, "#{file_name}_mailbox_spec.rb") 10 | ) 11 | end 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/generators/rspec/mailbox/templates/mailbox_spec.rb.erb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | <% module_namespacing do -%> 4 | RSpec.describe <%= class_name %>Mailbox, <%= type_metatag(:mailbox) %> do 5 | pending "add some examples to (or delete) #{__FILE__}" 6 | end 7 | <% end -%> 8 | -------------------------------------------------------------------------------- /lib/generators/rspec/mailer/mailer_generator.rb: -------------------------------------------------------------------------------- 1 | require 'generators/rspec' 2 | require "rspec/rails/feature_check" 3 | 4 | module Rspec 5 | module Generators 6 | # @private 7 | class MailerGenerator < Base 8 | argument :actions, type: :array, default: [], banner: "method method" 9 | 10 | def generate_mailer_spec 11 | file_suffix = file_name.end_with?('mailer') ? 'spec.rb' : 'mailer_spec.rb' 12 | template "mailer_spec.rb", target_path('mailers', class_path, [file_name, file_suffix].join('_')) 13 | end 14 | 15 | def generate_fixtures_files 16 | actions.each do |action| 17 | @action, @path = action, File.join(file_path, action) 18 | template "fixture", target_path("fixtures", @path) 19 | end 20 | end 21 | 22 | def generate_preview_files 23 | return unless RSpec::Rails::FeatureCheck.has_action_mailer_preview? 24 | 25 | file_suffix = file_name.end_with?('mailer') ? 'preview.rb' : 'mailer_preview.rb' 26 | template "preview.rb", target_path("mailers/previews", class_path, [file_name, file_suffix].join('_')) 27 | end 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/generators/rspec/mailer/templates/fixture: -------------------------------------------------------------------------------- 1 | <%= class_name %>#<%= @action %> 2 | 3 | Hi, find me in app/views/<%= @path %> 4 | -------------------------------------------------------------------------------- /lib/generators/rspec/mailer/templates/mailer_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | <% module_namespacing do -%> 4 | RSpec.describe <%= class_name.sub(/(Mailer)?$/, 'Mailer') %>, <%= type_metatag(:mailer) %> do 5 | <% for action in actions -%> 6 | describe "<%= action %>" do 7 | let(:mail) { <%= class_name.sub(/(Mailer)?$/, 'Mailer') %>.<%= action %> } 8 | 9 | it "renders the headers" do 10 | expect(mail.subject).to eq(<%= action.to_s.humanize.inspect %>) 11 | expect(mail.to).to eq(["to@example.org"]) 12 | expect(mail.from).to eq(["from@example.com"]) 13 | end 14 | 15 | it "renders the body" do 16 | expect(mail.body.encoded).to match("Hi") 17 | end 18 | end 19 | 20 | <% end -%> 21 | <% if actions.blank? -%> 22 | pending "add some examples to (or delete) #{__FILE__}" 23 | <% end -%> 24 | end 25 | <% end -%> 26 | -------------------------------------------------------------------------------- /lib/generators/rspec/mailer/templates/preview.rb: -------------------------------------------------------------------------------- 1 | <% module_namespacing do -%> 2 | # Preview all emails at http://localhost:3000/rails/mailers/<%= file_path %>_mailer 3 | class <%= class_name %><%= 'Mailer' unless class_name.end_with?('Mailer') %>Preview < ActionMailer::Preview 4 | <% actions.each do |action| -%> 5 | 6 | # Preview this email at http://localhost:3000/rails/mailers/<%= file_path %>_mailer/<%= action %> 7 | def <%= action %> 8 | <%= class_name.sub(/(Mailer)?$/, 'Mailer') %>.<%= action %> 9 | end 10 | <% end -%> 11 | 12 | end 13 | <% end -%> 14 | -------------------------------------------------------------------------------- /lib/generators/rspec/model/model_generator.rb: -------------------------------------------------------------------------------- 1 | require 'generators/rspec' 2 | 3 | module Rspec 4 | module Generators 5 | # @private 6 | class ModelGenerator < Base 7 | argument :attributes, 8 | type: :array, 9 | default: [], 10 | banner: "field:type field:type" 11 | class_option :fixture, type: :boolean 12 | 13 | def create_model_spec 14 | template_file = target_path( 15 | 'models', 16 | class_path, 17 | "#{file_name}_spec.rb" 18 | ) 19 | template 'model_spec.rb', template_file 20 | end 21 | 22 | hook_for :fixture_replacement 23 | 24 | def create_fixture_file 25 | return unless missing_fixture_replacement? 26 | 27 | template 'fixtures.yml', target_path('fixtures', class_path, "#{(pluralize_table_names? ? plural_file_name : file_name)}.yml") 28 | end 29 | 30 | private 31 | 32 | def missing_fixture_replacement? 33 | options[:fixture] && options[:fixture_replacement].nil? 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/generators/rspec/model/templates/fixtures.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | <% unless attributes.empty? -%> 4 | one: 5 | <% for attribute in attributes -%> 6 | <%= attribute.name %>: <%= attribute.default %> 7 | <% end -%> 8 | 9 | two: 10 | <% for attribute in attributes -%> 11 | <%= attribute.name %>: <%= attribute.default %> 12 | <% end -%> 13 | <% else -%> 14 | # one: 15 | # column: value 16 | # 17 | # two: 18 | # column: value 19 | <% end -%> 20 | -------------------------------------------------------------------------------- /lib/generators/rspec/model/templates/model_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | <% module_namespacing do -%> 4 | RSpec.describe <%= class_name %>, <%= type_metatag(:model) %> do 5 | pending "add some examples to (or delete) #{__FILE__}" 6 | end 7 | <% end -%> 8 | -------------------------------------------------------------------------------- /lib/generators/rspec/request/request_generator.rb: -------------------------------------------------------------------------------- 1 | require 'generators/rspec' 2 | 3 | module Rspec 4 | module Generators 5 | # @private 6 | class RequestGenerator < Base 7 | class_option :request_specs, type: :boolean, default: true, desc: 'Generate request specs' 8 | 9 | def generate_request_spec 10 | return unless options[:request_specs] 11 | 12 | template 'request_spec.rb', 13 | target_path('requests', "#{name.underscore.pluralize}_spec.rb") 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/generators/rspec/request/templates/request_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe "<%= class_name.pluralize %>", <%= type_metatag(:request) %> do 4 | describe "GET /<%= name.underscore.pluralize %>" do 5 | it "works! (now write some real specs)" do 6 | get <%= index_helper %>_path 7 | expect(response).to have_http_status(200) 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/generators/rspec/scaffold/templates/edit_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | <% output_attributes = attributes.reject{|attribute| [:datetime, :timestamp, :time, :date].index(attribute.type) } -%> 4 | RSpec.describe "<%= ns_table_name %>/edit", <%= type_metatag(:view) %> do 5 | let(:<%= singular_table_name %>) { 6 | <%= class_name %>.create!(<%= ')' if output_attributes.empty? %> 7 | <% output_attributes.each_with_index do |attribute, attribute_index| -%> 8 | <%= attribute.name %>: <%= attribute.default.inspect %><%= attribute_index == output_attributes.length - 1 ? '' : ','%> 9 | <% end -%> 10 | <%= " )\n" unless output_attributes.empty? -%> 11 | } 12 | 13 | before(:each) do 14 | assign(:<%= singular_table_name %>, <%= singular_table_name %>) 15 | end 16 | 17 | it "renders the edit <%= ns_file_name %> form" do 18 | render 19 | 20 | assert_select "form[action=?][method=?]", <%= ns_file_name %>_path(<%= singular_table_name %>), "post" do 21 | <% for attribute in output_attributes -%> 22 | <%- name = attribute.respond_to?(:column_name) ? attribute.column_name : attribute.name %> 23 | assert_select "<%= attribute.input_type -%>[name=?]", "<%= ns_file_name %>[<%= name %>]" 24 | <% end -%> 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/generators/rspec/scaffold/templates/index_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | <% output_attributes = attributes.reject{|attribute| [:datetime, :timestamp, :time, :date].index(attribute.type) } -%> 4 | RSpec.describe "<%= ns_table_name %>/index", <%= type_metatag(:view) %> do 5 | before(:each) do 6 | assign(:<%= table_name %>, [ 7 | <% [1,2].each_with_index do |id, model_index| -%> 8 | <%= class_name %>.create!(<%= output_attributes.empty? ? (model_index == 1 ? ')' : '),') : '' %> 9 | <% output_attributes.each_with_index do |attribute, attribute_index| -%> 10 | <%= attribute.name %>: <%= value_for(attribute) %><%= attribute_index == output_attributes.length - 1 ? '' : ','%> 11 | <% end -%> 12 | <% if !output_attributes.empty? -%> 13 | <%= model_index == 1 ? ')' : '),' %> 14 | <% end -%> 15 | <% end -%> 16 | ]) 17 | end 18 | 19 | it "renders a list of <%= ns_table_name %>" do 20 | render 21 | cell_selector = 'div>p' 22 | <% for attribute in output_attributes -%> 23 | assert_select cell_selector, text: Regexp.new(<%= value_for(attribute) %>.to_s), count: 2 24 | <% end -%> 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/generators/rspec/scaffold/templates/new_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | <% output_attributes = attributes.reject{|attribute| [:datetime, :timestamp, :time, :date].index(attribute.type) } -%> 4 | RSpec.describe "<%= ns_table_name %>/new", <%= type_metatag(:view) %> do 5 | before(:each) do 6 | assign(:<%= singular_table_name %>, <%= class_name %>.new(<%= '))' if output_attributes.empty? %> 7 | <% output_attributes.each_with_index do |attribute, attribute_index| -%> 8 | <%= attribute.name %>: <%= attribute.default.inspect %><%= attribute_index == output_attributes.length - 1 ? '' : ','%> 9 | <% end -%> 10 | <%= !output_attributes.empty? ? " ))\n end" : " end" %> 11 | 12 | it "renders new <%= ns_file_name %> form" do 13 | render 14 | 15 | assert_select "form[action=?][method=?]", <%= index_helper %>_path, "post" do 16 | <% for attribute in output_attributes -%> 17 | <%- name = attribute.respond_to?(:column_name) ? attribute.column_name : attribute.name %> 18 | assert_select "<%= attribute.input_type -%>[name=?]", "<%= ns_file_name %>[<%= name %>]" 19 | <% end -%> 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/generators/rspec/scaffold/templates/routing_spec.rb: -------------------------------------------------------------------------------- 1 | require "rails_helper" 2 | 3 | <% module_namespacing do -%> 4 | RSpec.describe <%= controller_class_name %>Controller, <%= type_metatag(:routing) %> do 5 | describe "routing" do 6 | <% unless options[:singleton] -%> 7 | it "routes to #index" do 8 | expect(get: "/<%= ns_table_name %>").to route_to("<%= ns_table_name %>#index") 9 | end 10 | 11 | <% end -%> 12 | <% unless options[:api] -%> 13 | it "routes to #new" do 14 | expect(get: "/<%= ns_table_name %>/new").to route_to("<%= ns_table_name %>#new") 15 | end 16 | 17 | <% end -%> 18 | it "routes to #show" do 19 | expect(get: "/<%= ns_table_name %>/1").to route_to("<%= ns_table_name %>#show", id: "1") 20 | end 21 | 22 | <% unless options[:api] -%> 23 | it "routes to #edit" do 24 | expect(get: "/<%= ns_table_name %>/1/edit").to route_to("<%= ns_table_name %>#edit", id: "1") 25 | end 26 | 27 | <% end -%> 28 | 29 | it "routes to #create" do 30 | expect(post: "/<%= ns_table_name %>").to route_to("<%= ns_table_name %>#create") 31 | end 32 | 33 | it "routes to #update via PUT" do 34 | expect(put: "/<%= ns_table_name %>/1").to route_to("<%= ns_table_name %>#update", id: "1") 35 | end 36 | 37 | it "routes to #update via PATCH" do 38 | expect(patch: "/<%= ns_table_name %>/1").to route_to("<%= ns_table_name %>#update", id: "1") 39 | end 40 | 41 | it "routes to #destroy" do 42 | expect(delete: "/<%= ns_table_name %>/1").to route_to("<%= ns_table_name %>#destroy", id: "1") 43 | end 44 | end 45 | end 46 | <% end -%> 47 | -------------------------------------------------------------------------------- /lib/generators/rspec/scaffold/templates/show_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | <% output_attributes = attributes.reject{|attribute| [:datetime, :timestamp, :time, :date].index(attribute.type) } -%> 4 | RSpec.describe "<%= ns_table_name %>/show", <%= type_metatag(:view) %> do 5 | before(:each) do 6 | assign(:<%= singular_table_name %>, <%= class_name %>.create!(<%= '))' if output_attributes.empty? %> 7 | <% output_attributes.each_with_index do |attribute, attribute_index| -%> 8 | <%= attribute.name %>: <%= value_for(attribute) %><%= attribute_index == output_attributes.length - 1 ? '' : ','%> 9 | <% end -%> 10 | <% if !output_attributes.empty? -%> 11 | )) 12 | <% end -%> 13 | end 14 | 15 | it "renders attributes in

" 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 | --------------------------------------------------------------------------------