A pragmatic guide for Cypress On Rails
Tags: rails, testing, ruby
Published: 2023-06-28 05:13:18

I have discovered a wonderful gem, Cypress On Rails, that eases a lot of the process of E2E with Rails App. This incredible gem deserves an article. 
 
Hence through this article, we will navigate through the installation and the basics of the uses of Cypress On Rails.

## Let's begin with Cypress On Rails

Let's start a new Rails app and test all this. This will be a simple app where we will store artists. 

You can use `-T ` to skip the installation of the default testing gem `minitest`. 

```shell
rails new artists_on_rails -T
```

We can install the FactoryBot, Rspec, and Cypress-On-Rails gems in the dev and test environments.

```ruby
group :development, :test do
   gem "factory_bot_rails"
   gem "rspec-rails"
	 gem "cypress-on-rails"
end
```

Then we need to install the gem and trigger the boilerplate code generation.

```ruby
bundle install
//After a while
rails g rspec:install
rails g cypress_on_rails:install 
```

This will create a folder name Cypress with the following structure : 

```
cypress/
├── app_commands
│   ├── activerecord_fixtures.rb
│   ├── clean.rb
│   ├── eval.rb
│   ├── factory_bot.rb
│   ├── log_fail.rb
│   └── scenarios
│       └── basic.rb
├── cypress_helper.rb
├── e2e
│   └── rails_examples
│       ├── advance_factory_bot.cy.js
│       ├── other.cy.js
│       ├── using_factory_bot.cy.js
│       ├── using_fixtures.cy.js
│       ├── using_scenarios.cy.js
│       └── using_vcr.cy.js
└── support
    ├── commands.js
    ├── index.js
    └── on-rails.js

Six directories, 17 files
```

## Injecting a middleware

When you run the installation of cypress-on-rails a new middleware is injected. 
Then every request to your app will pass through this middleware before going to your Application.

If this request comes from Cypress, the middleware will not forward it to your app, and the cypress-on-rails middleware will treat it directly. 

```shell
rake middleware
use ActionDispatch::HostAuthorization
... a bunch of stuff
use CypressOnRails::Middleware
run ArtOnRails::Application.routes
```
When you execute some Cypress commands, this will go to a particular endpoint with the prefix `/__cypress__/command`.

```javascript
Cypress.Commands.add('appCommands', function (body) {
  Object.keys(body).forEach(key => body[key] === undefined ? delete body[key] : {});
  const log = Cypress.log({ name: "APP", message: body, autoEnd: false })
  return cy.request({
    method: 'POST',
    url: "/__cypress__/command",
    body: JSON.stringify(body),
    log: false,
    failOnStatusCode: false
  }).then((response) => {
    log.end();
    if (response.status !== 201) {
      expect(response.body.message).to.equal('')
      expect(response.status).to.be.equal(201)
    }
    return response.body
  });
});
```

Then the middleware will intercept the request if it contains this url prefix. This will not be forwarded to the app source. 
You can see this in this sample of the code of the lib : 

```ruby
def call(env)
      request = Rack::Request.new(env)
      if request.path.start_with?("#{configuration.api_prefix}/__e2e__/command")
        configuration.tagged_logged { handle_command(request) }
      elsif request.path.start_with?("#{configuration.api_prefix}/__cypress__/command")
        configuration.tagged_logged { handle_command(request) }
        warn "/__cypress__/command is deprecated. Please use the install generator to use /__e2e__/command instead."
      else
        @app.call(env)
      end
end
```


## Seeding

Your E2E tests should not depend on your application's previous state. Then when creating E2E tests, you must populate your database for your specific test case. For example, you can create a rake task to populate your database.

But with Cypress on Rails, you can be much more efficient and develop seeds with Factory Bot. These seeds are called `scenarios` in CypressOnRails terminology. 


Let's generate the model for the artists. Since we have installed the factory bot gem, this will create the factory for our model. 

```shell
rails g model artists pseudo:string
```

Now we can modify the `basic.rb` scenario. And use our factory of artists.

```ruby
/cypress/appCommands/scenarios/basic.rb
FactoryBot.create(:artist)
```

### Cultural Aside 
[Jean-Michel Basquiat](https://en.wikipedia.org/wiki/Jean-Michel_Basquiat) is a famous artist. He began his career under the pseudo of SAMO with graffiti in the street. And then, he had an incredible career; he was friends with Warhol, even though he died young (at 27).

## Populate your database
You can now use the scenarios you have defined in your Cypress test. For example, with the basic seeds, we've modified the following: 

```javascript
describe('Rails using factory bot examples', function() {
  beforeEach(() => {
	  //This clean the database
    cy.app('clean') 
    cy.appScenario('basic')
  })
})
``` 

You use the new  `cy.appScenario` function, and that's it; when you run your test, the test database will be seeded with your scenario data.

This function is defined in `cypress/support/on-rails.js`: 

```javascript
Cypress.Commands.add('appScenario', function (name, options = {}) {
  return cy.app('scenarios/' + name, options)
});
```

This will be based on the previous URL we've seen, `/__cypress__/commands`. And add `/scenarios/basic` to it here in our case. This will trigger our seed, 

## Call the factory directly in your test

You can call a factory directly from your Cypress test when you need to create specific data for a test.
You will use the function `cy.appFactories`. It takes an array of an array with: 
-The method you want to call on the factory (`create`) 
-The factory of the model you want to call (`artist`)
-The value that we want to provide to the factory (`{pseudo:'Jonone'}`)

```javascript
it('using single factory bot', function() {
    cy.appFactories([
      ['create', 'artist', {pseudo: 'JonOne'} ]
    ])
  })
```

With this, you will call the factory directly. The database will be populated with the data you provided to the appFactories method.

### Cultural Aside 
[JonOne](https://en.wikipedia.org/wiki/JonOne) is a famous artist. He began his career with graffiti in New York and then came to Paris. Now he is mainstream; he even collaborates with one of the most prominent French retail brands for a marketing campaign.

## Conclusion

When you have a stack combining Cypress and Rails, Cypress On Rails is a valuable gem that can ease your E2E test seeding.

Thanks to this gem, you can create seeds called scenarios in Cypress On Rails terminology. These scenarios can use Factory Bot directly, making seeding easier when you have dependencies (not treated in this article).

(PS): TIL that plural of scenario, an Italian word, is scenarii :).