Feature: The directory structure

  Specs are usually placed in a canonical directory structure that describes
  their purpose:

  - [Model specs](./model-specs) reside in the `spec/models` directory

  - [Controller specs](./controller-specs) reside in the `spec/controllers` directory

  - [Request specs](./request-specs) reside in the `spec/requests` directory. The directory can also be named `integration` or `api`.

  - [Feature specs](./feature-specs) reside in the `spec/features` directory

  - [View specs](./view-specs) reside in the `spec/views` directory

  - [Helper specs](./helper-specs) reside in the `spec/helpers` directory

  - [Mailer specs](./mailer-specs) reside in the `spec/mailers` directory

  - [Routing specs](./routing-specs) reside in the `spec/routing` directory

  - [Job specs](./job-specs) reside in the `spec/jobs` directory

  - [System specs](./system-specs) reside in the `spec/system` directory

  Application developers are free to use a different directory structure. In
  order to include the correct `rspec-rails` support functions, the specs need
  to have the appropriate corresponding metadata `:type` value:

  - Model specs: `type: :model`
  - Controller specs: `type: :controller`
  - Request specs: `type: :request`
  - Feature specs: `type: :feature`
  - View specs: `type: :view`
  - Helper specs: `type: :helper`
  - Mailer specs: `type: :mailer`
  - Routing specs: `type: :routing`
  - Job specs: `type: :job`
  - System specs: `type: :system`

  For example, say the spec for the `ThingsController` is located in
  `spec/legacy/things_controller_spec.rb`. Simply tag the spec's
  `RSpec.describe` block with the `type: :controller` metadata:

  ```ruby
  ​# spec/legacy/things_controller_spec.rb
  RSpec.describe ThingsController, type: :controller do
    describe "GET index" do
      ​# Examples
    end
  end
  ```

  **Note:** Standard RSpec specs do not require any additional metadata by
  default.

  Check out the [`rspec-core`](../../3-12/rspec-core) documentation on [using metadata](../../3-12/rspec-core/metadata) for more details.

  **Automatically Adding Metadata**

  RSpec versions before 3.0.0 automatically added metadata to specs based on
  their location on the filesystem. This was both confusing to new users and not
  desirable for some veteran users.

  This behaviour must be explicitly enabled:

  ```ruby
  ​# spec/rails_helper.rb
  RSpec.configure do |config|
    config.infer_spec_type_from_file_location!
  end
  ```

  Since this assumed behavior is so prevalent in tutorials, the default
  configuration generated by `rails generate rspec:install` enables this.

  If you follow the above listed canonical directory structure and have
  configured `infer_spec_type_from_file_location!`, RSpec will automatically
  include the correct support functions for each type.

  If you want to set metadata for a custom directory that doesn't follow fit the canonical structure above, you can do the following:

  ```ruby
  ​# set `:type` for serializers directory
  RSpec.configure do |config|
    config.define_derived_metadata(:file_path => Regexp.new('/spec/serializers/')) do |metadata|
      metadata[:type] = :serializer
    end
  end
  ```

  **Tips on Spec Location**

  It is suggested that the `spec/` directory structure generally mirror both
  `app/` and `lib/`. This makes it easy to locate corresponding code and spec
  files.

  **Example:**

      app
      ├── controllers
      │   ├── application_controller.rb
      │   └── books_controller.rb
      ├── helpers
      │   ├── application_helper.rb
      │   └── books_helper.rb
      ├── models
      │   ├── author.rb
      │   └── book.rb
      └── views
          ├── books
          └── layouts
      lib
      ├── country_map.rb
      ├── development_mail_interceptor.rb
      ├── environment_mail_interceptor.rb
      └── tasks
          └── irc.rake
      spec
      ├── controllers
      │   └── books_controller_spec.rb
      ├── country_map_spec.rb
      ├── features
      │   └── tracking_book_delivery_spec.rb
      ├── helpers
      │   └── books_helper_spec.rb
      ├── models
      │   ├── author_spec.rb
      │   └── book_spec.rb
      ├── rails_helper.rb
      ├── requests
      │   └── books_spec.rb
      ├── routing
      │   └── books_routing_spec.rb
      ├── spec_helper.rb
      ├── tasks
      │   └── irc_spec.rb
      └── views
          └── books

  Scenario: Standard Rails specs must specify the `:type` metadata
    Given a file named "spec/functional/widgets_controller_spec.rb" with:
      """ruby
      require "rails_helper"

      RSpec.describe WidgetsController, type: :controller do
        it "responds successfully" do
          get :index
          expect(response.status).to eq(200)
        end
      end
      """
    When I run `rspec spec`
    Then the example should pass

  Scenario: Non-rails related specs do not require `:type` metadata by default
    Given a file named "spec/ledger/entry_spec.rb" with:
      """ruby
      require "spec_helper"

      Entry = Struct.new(:description, :us_cents)

      RSpec.describe Entry do
        it "has a description" do
          is_expected.to respond_to(:description)
        end
      end
      """
    When I run `rspec spec`
    Then the example should pass

  Scenario: Inferring spec type from the file location adds the appropriate metadata
    Given a file named "spec/controllers/widgets_controller_spec.rb" with:
      """ruby
      require "rails_helper"

      RSpec.configure do |config|
        config.infer_spec_type_from_file_location!
      end

      RSpec.describe WidgetsController do
        it "responds successfully" do
          get :index
          expect(response.status).to eq(200)
        end
      end
      """
    When I run `rspec spec`
    Then the example should pass

  Scenario: Specs in canonical directories can override their inferred types
    Given a file named "spec/routing/duckduck_routing_spec.rb" with:
      """ruby
      require "rails_helper"

      Rails.application.routes.draw do
        get "/example" => redirect("http://example.com")
      end

      RSpec.configure do |config|
        config.infer_spec_type_from_file_location!
      end

      # Due to limitations in the Rails routing test framework, routes that
      # perform redirects must actually be tested via request specs
      RSpec.describe "/example", type: :request do
        it "redirects to example.com" do
          get "/example"
          expect(response).to redirect_to("http://example.com")
        end
      end
      """
    When I run `rspec spec`
    Then the example should pass
