Skip to content

Commit

Permalink
Chore: Initialize Cypress tests (chatwoot#1078)
Browse files Browse the repository at this point in the history
Addresses: chatwoot#412

Co-authored-by: Pranav Raj S <[email protected]>
  • Loading branch information
sojan-official and Pranav Raj S authored Jul 21, 2020
1 parent fcb7625 commit d6f309c
Show file tree
Hide file tree
Showing 25 changed files with 654 additions and 38 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,7 @@ node_modules
package-lock.json

*.dump


# cypress
test/cypress/videos/*
7 changes: 7 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@ group :development do
gem 'json_refs', git: 'https://github.com/tzmfreedom/json_refs', ref: 'e32deb0'
end

group :test do
# Cypress in rails.
gem 'cypress-on-rails', '~> 1.0'
# fast cleaning of database
gem 'database_cleaner'
end

group :development, :test do
# locking until https://github.com/codeclimate/test-reporter/issues/418 is resolved
gem 'action-cable-testing'
Expand Down
5 changes: 5 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,9 @@ GEM
crack (0.4.3)
safe_yaml (~> 1.0.0)
crass (1.0.6)
cypress-on-rails (1.7.0)
rack
database_cleaner (1.8.5)
datetime_picker_rails (0.0.7)
momentjs-rails (>= 2.8.1)
declarative (0.0.10)
Expand Down Expand Up @@ -560,6 +563,8 @@ DEPENDENCIES
bullet
bundle-audit
byebug
cypress-on-rails (~> 1.0)
database_cleaner
devise
devise_token_auth
dotenv-rails
Expand Down
3 changes: 3 additions & 0 deletions Procfile.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
backend: RAILS_ENV=test bin/rails s -p 5050
frontend: bin/webpack-dev-server
worker: RAILS_ENV=test bundle exec sidekiq -C config/sidekiq.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<template>
<button
:type="type"
data-testid="submit_button"
:disabled="disabled"
:class="computedClass"
@click="onClick"
Expand Down
2 changes: 2 additions & 0 deletions app/javascript/dashboard/routes/login/Login.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
<input
v-model.trim="credentials.email"
type="text"
data-testid="email_input"
:placeholder="$t('LOGIN.EMAIL.PLACEHOLDER')"
@input="$v.credentials.email.$touch"
/>
Expand All @@ -30,6 +31,7 @@
<input
v-model.trim="credentials.password"
type="password"
data-testid="password_input"
:placeholder="$t('LOGIN.PASSWORD.PLACEHOLDER')"
@input="$v.credentials.password.$touch"
/>
Expand Down
9 changes: 9 additions & 0 deletions config/initializers/cypress_on_rails.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
if defined?(CypressOnRails)
CypressOnRails.configure do |c|
c.cypress_folder = File.expand_path("#{__dir__}/../../spec/cypress")
# WARNING!! CypressOnRails can execute arbitrary ruby code
# please use with extra caution if enabling on hosted servers or starting your local server on 0.0.0.0
c.use_middleware = Rails.env.test?
c.logger = Rails.logger
end
end
5 changes: 3 additions & 2 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ path: '/docs'

This guide will help you get started with Chatwoot!

### Environment Setup
### Environment Setup for local development

* [Mac](/docs/installation-guide-mac-os)
* [Ubuntu](/docs/installation-guide-ubuntu)
* [Windows](/docs/installation-guide-windows)
* [Docker](/docs/installation-guide-docker)
*

### Project Setup

Expand All @@ -19,7 +20,7 @@ This guide will help you get started with Chatwoot!
* [Conversation Continuity with Email](/docs/conversation-continuity)
* [Common Errors](/docs/common-errors)

### Deployment
### Deploying Chatwoot in Production

* [Architecture](/docs/deployment/architecture)
* [Heroku](/docs/deployment/deploy-chatwoot-with-heroku) (recommended)
Expand Down
16 changes: 15 additions & 1 deletion docs/development/project-setup/quick-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,21 @@ docker-compose run -rm server bundle exec rake db:reset

This command essentially runs postgres and redis containers and then run the rake command inside the chatwoot server container.

### Docker for production
### Running Cypress Tests

Refer the docs to learn how to write cypress specs
https://github.com/shakacode/cypress-on-rails
https://docs.cypress.io/guides/overview/why-cypress.html

```
# in terminal tab1
overmind start -f Procfile.test
# in terminal tab2
yarn cypress open --project ./test
```


### Debugging Docker for production

You can use our official Docker image from [https://hub.docker.com/r/chatwoot/chatwoot](https://hub.docker.com/r/chatwoot/chatwoot)

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"babel-jest": "^25.3.0",
"babel-loader": "^8.1.0",
"cypress": "^4.10.0",
"eslint": "^6.8.0",
"eslint-config-airbnb-base": "^14.1.0",
"eslint-config-prettier": "^4.0.0",
Expand Down
4 changes: 4 additions & 0 deletions spec/cypress.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"baseUrl": "http://localhost:5050",
"defaultCommandTimeout": 10000
}
22 changes: 22 additions & 0 deletions spec/cypress/app_commands/activerecord_fixtures.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# you can delete this file if you don't use Rails Test Fixtures

fixtures_dir = command_options.try(:[], 'fixtures_dir')
fixture_files = command_options.try(:[], 'fixtures')

if defined?(ActiveRecord)
require 'active_record/fixtures'

fixtures_dir ||= ActiveRecord::Tasks::DatabaseTasks.fixtures_path
fixture_files ||= Dir["#{fixtures_dir}/**/*.yml"].map { |f| f[(fixtures_dir.size + 1)..-5] }

logger.debug "loading fixtures: { dir: #{fixtures_dir}, files: #{fixture_files} }"
ActiveRecord::FixtureSet.reset_cache
ActiveRecord::FixtureSet.create_fixtures(fixtures_dir, fixture_files)
'Fixtures Done' # this gets returned
else # this else part can be removed
logger.error 'Looks like activerecord_fixtures has to be modified to suite your need'
Post.create(title: 'MyCypressFixtures')
Post.create(title: 'MyCypressFixtures2')
Post.create(title: 'MyRailsFixtures')
Post.create(title: 'MyRailsFixtures2')
end
10 changes: 10 additions & 0 deletions spec/cypress/app_commands/clean.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
if defined?(DatabaseCleaner)
# cleaning the database using database_cleaner
DatabaseCleaner.strategy = :truncation
DatabaseCleaner.clean
else
logger.warn 'add database_cleaner or update cypress/app_commands/clean.rb'
Post.delete_all if defined?(Post)
end

Rails.logger.info 'APPCLEANED' # used by log_fail.rb
1 change: 1 addition & 0 deletions spec/cypress/app_commands/eval.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Kernel.eval(command_options) unless command_options.nil?
12 changes: 12 additions & 0 deletions spec/cypress/app_commands/factory_bot.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
Array.wrap(command_options).map do |factory_options|
factory_method = factory_options.shift
begin
logger.debug "running #{factory_method}, #{factory_options}"
CypressOnRails::SmartFactoryWrapper.public_send(factory_method, *factory_options)
rescue StandardError => e
logger.error "#{e.class}: #{e.message}"
logger.error e.backtrace.join("\n")
logger.error e.record.inspect.to_s if e.is_a?(ActiveRecord::RecordInvalid)
raise e
end
end
1 change: 1 addition & 0 deletions spec/cypress/app_commands/load_seed.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Rails.application.load_seed
23 changes: 23 additions & 0 deletions spec/cypress/app_commands/log_fail.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# This file is called when a cypress spec fails and allows for extra logging to be captured
filename = command_options.fetch('runnable_full_title', 'no title').gsub(/[^[:print:]]/, '')

# grab last lines until "APPCLEANED" (Make sure in clean.rb to log the text "APPCLEANED")
system "tail -n 10000 -r log/#{Rails.env}.log | sed \"/APPCLEANED/ q\" | sed 'x;1!H;$!d;x' > 'log/#{filename}.log'"

# create a json debug file for server debugging
json_result = {}
json_result['error'] = command_options.fetch('error_message', 'no error message')

if defined?(ActiveRecord::Base)
json_result['records'] =
ActiveRecord::Base.descendants.each_with_object({}) do |record_class, records|
records[record_class.to_s] = record_class.limit(100).map(&:attributes)
rescue StandardError => e
Rails.logger.info e.message
end
end

filename = command_options.fetch('runnable_full_title', 'no title').gsub(/[^[:print:]]/, '')
File.open(Rails.root.join("log/#{filename}.json"), 'w+') do |file|
file << JSON.pretty_generate(json_result)
end
1 change: 1 addition & 0 deletions spec/cypress/app_commands/scenarios/default.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Rails.application.load_seed
33 changes: 33 additions & 0 deletions spec/cypress/cypress_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# This is loaded once before the first command is executed

begin
require 'database_cleaner'
rescue LoadError => e
puts e.message
end

begin
require 'factory_bot_rails'
rescue LoadError => e
puts e.message
begin
require 'factory_girl_rails'
rescue LoadError => e
puts e.message
end
end

require 'cypress_on_rails/smart_factory_wrapper'

factory = CypressOnRails::SimpleRailsFactory
factory = FactoryBot if defined?(FactoryBot)
factory = FactoryGirl if defined?(FactoryGirl)

CypressOnRails::SmartFactoryWrapper.configure(
always_reload: !Rails.configuration.cache_classes,
factory: factory,
files: [
Rails.root.join('spec/factories.rb'),
Rails.root.join('spec/factories/**/*.rb')
]
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
describe('AdminDashboardAuthentication', function() {
before(() => {
cy.app('clean');
cy.appScenario('default')
});

it('authenticates an admin ', function() {
cy.visit('/');

cy.get("[data-testid='email_input']")
.clear()
.type('[email protected]');
cy.get("[data-testid='password_input']")
.clear()
.type('123456');

cy.get("[data-testid='submit_button']").click();
cy.contains('Conversations');
});
});
21 changes: 21 additions & 0 deletions spec/cypress/plugins/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/// <reference types="cypress" />
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************

// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)

/**
* @type {Cypress.PluginConfig}
*/
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
}
25 changes: 25 additions & 0 deletions spec/cypress/support/commands.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// ***********************************************
// This example commands.js shows you how to
// create various custom commands and overwrite
// existing commands.
//
// For more comprehensive examples of custom
// commands please read more here:
// https://on.cypress.io/custom-commands
// ***********************************************
//
//
// -- This is a parent command --
// Cypress.Commands.add("login", (email, password) => { ... })
//
//
// -- This is a child command --
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
//
//
// -- This is a dual command --
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
//
//
// -- This is will overwrite an existing command --
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
21 changes: 21 additions & 0 deletions spec/cypress/support/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************

// Import commands.js using ES2015 syntax:
import './commands'
import './on-rails'

// Alternatively you can use CommonJS syntax:
// require('./commands')
54 changes: 54 additions & 0 deletions spec/cypress/support/on-rails.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// CypressOnRails: dont remove these command
Cypress.Commands.add('appCommands', function (body) {
cy.log("APP: " + JSON.stringify(body))
return cy.request({
method: 'POST',
url: "/__cypress__/command",
body: JSON.stringify(body),
log: true,
failOnStatusCode: true
}).then((response) => {
return response.body
});
});

Cypress.Commands.add('app', function (name, command_options) {
return cy.appCommands({name: name, options: command_options}).then((body) => {
return body[0]
});
});

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

Cypress.Commands.add('appEval', function (code) {
return cy.app('eval', code)
});

Cypress.Commands.add('appFactories', function (options) {
return cy.app('factory_bot', options)
});

Cypress.Commands.add('appFixtures', function (options) {
cy.app('activerecord_fixtures', options)
});
// CypressOnRails: end

// The next is optional
// beforeEach(() => {
// cy.app('clean') // have a look at cypress/app_commands/clean.rb
// });

// comment this out if you do not want to attempt to log additional info on test fail
Cypress.on('fail', (err, runnable) => {
// allow app to generate additional logging data
Cypress.$.ajax({
url: '/__cypress__/command',
data: JSON.stringify({name: 'log_fail', options: {error_message: err.message, runnable_full_title: runnable.fullTitle() }}),
async: false,
method: 'POST'
});

throw err;
});
Loading

0 comments on commit d6f309c

Please sign in to comment.