Cypress technique to bypass OAuth2 in Rails
Use cypress-on-rails gem to mock and bypass authentication via App Command.
Problem
- Your application based on Rails using Devise with OmniAuth gem.
Adding both gems allow user to login to your website using Google Account or another 3rd party account, depending on you implementation. For example: Rails app with Devise and OmniAuth - Your application using Cypress as test tool
- Sign in step using Cypress on GUI is slow and anti-pattern
- Using cypress-cucumber-preprocessor to write test script as feature file
(Example code on this article is implemented using TypeScript) - This Rails using OAuth2 authentication matched above criteria
Solution
Install cypress-on-rails as a ready-to-use package that allow test script to access app command for mocking OAuth2. Then bypass authentication system provided by OmniAuth and Devise sign-in with mocked data. This process bypass without accessing GUI, which reduce execution time significantly
1) Pre-requisite
Clone repository from railsfriend:cy_on_rails_initial
2) Install cypress-on-rails Gem
Add this gem into Gemfile
group :development do
# Add to development Gem
gem 'cypress-on-rails'
end
Then execute bundle installation command
bundle install
Setup cypress-on-rails by execute this command
rails g cypress_on_rails:install
3) Add TypeScript configuration
Create tsconfig.json as TypeScript configuration file.
Warning: The tsconfig.json need to be in project root or it always failed to load some method.
More detail can be read from TypeScript official Document
{
"compilerOptions": {
"target": "es5",
"lib": ["es5", "dom"],
"types": ["node", "cypress"]
},
"include": ["**/*.ts"]
}
Create cypress/support/index.d.ts as as main TS file. These declaration would allow to use command generated from cypress_on_rails
/// <reference types="cypress" />
declare namespace Cypress {
interface Chainable<Subject = any> {
app(route: string, options: any): void;
appCommands(params: { name: string; options: any }): Promise<any>;
appScenario(name: any, options?: object): Promise<any>;
appEval(code: any): Promise<any>;
appFactories(option: any): Promise<any>;
appFixtures(option: any): Promise<any>;
}
}
4) Add app command to mock OAuth2
Create app command file in Test Helper module at cypress/app_commands/google_oauth2_mock.rb
module TestHelpers
class GoogleOAuth2Mock
def self.run(auth)
new.run(auth)
end
def run(auth)
OmniAuth.config.mock_auth[:google_oauth2] =
OmniAuth::AuthHash.new(auth) # From OmniAuth
end
end
end
TestHelpers::GoogleOAuth2Mock.run(command_options)
The the file name can be called as method as the following format
cy.app(‘google_oauth2_mock’, <mocked_auth_json_object>)
Ensure the configure are uncomment and enabled
config/environments/development.rb
Note: File name depending on using environment
OmniAuth.config.test_mode = true
5) Create auth mock data
Create cypress/fixtures/correct_user.json as test data
{
"provider": "google_oauth2",
"uid": "123456789012345678901",
"name": "mocked_name",
"info": {
"email": "mocked.name@gmail.com"
}
}
6) Implement Keyword file to call created app commands
Create cypress/integration/Bypass.feature as feature file
Feature: Bypass
Scenario: TC_00002 Bypass login to Railsfriends
Given bypass login
Then sign out displayed on menu bar
Create cypress/integration/Bypass/Bypass.ts as keyword file.
This file will call the commaned created sin Step 4
/// <reference types="cypress" />
// Above line needed as indicator for Cypress
// Import Cucumber prefix
import {Given, Then} from 'cypress-cucumber-preprocessor/steps';
import Common from "../Common";
// Keyword need to be called by Feature files
Given('bypass login', () => {
cy.fixture('correct_user').then((user_json)=>{
cy.app('google_oauth2_mock', user_json)
})
cy.visit('/')
cy.get('a').contains('Sign in with GoogleOauth2').click()
});
Then('sign out displayed on menu bar', () => {
Common.signOutDisplayed()
});
The follow code is custom command in cypress
cy.app('<command_name>', <params>)
7) Execute the test with GUI
Start railsfriend app
rails s
Start Cypress GUI
cypress open
Click on Bypass.feature then Cypress will bypass Sign in step.
Result in when script Sign in with GoogleOauth2, page will be redirected as authntication success immediatly
Summary
Using cypress-on-rails extend Cypress to access and interact with the app with app_command. Combine this with a built-in mock feature in omniauth. These allow the test script to mock the authentication instantly with only a one-time setup for each strategy.