Commit dcfaee50 authored by Rafael Reggiani Manzo's avatar Rafael Reggiani Manzo

Merge branch 'konacha_tests' into 'master'

Javascript tests

Includes javascript unit testing with teaspoon gem. __Note that for coverage reports one must install the system package `istanbul`.__

Also converts our js files into coffee script.

This must be accepted right after !6

See merge request !11

Accepting with some reserves into: !3 and !4
parents 9ba3fb4e 9dff14fe
Pipeline #3597309 passed with stage
in 13 minutes and 32 seconds
......@@ -41,3 +41,22 @@ cucumber:
- ./bin/setup
script:
- bundle exec rake cucumber
teaspoon:
stage: test
before_script:
- apt-get update -qq && apt-get install -y build-essential libpq-dev sqlite3 libsqlite3-dev nodejs npm
- ln -s /usr/bin/nodejs /usr/bin/node
- npm -g install npm
- npm -g install phantomjs-prebuilt
- npm -g install istanbul
- ruby -v
- which ruby
# FIXME: Apparently cucumber and newest version of rubygems have conflicts. Remember to remove this line when one of them updates.
- gem update --system 2.6.1
- gem install bundler --no-ri --no-rdoc
- mkdir -p tmp/gems
- bundle install --jobs $(nproc) "${FLAGS[@]}" --path=tmp/gems
- ./bin/setup
script:
- bundle exec rake teaspoon
......@@ -61,12 +61,8 @@ group :development, :test do
# This beta is tested against Rails 5. Once it gets released use the stable version.
gem 'rspec-rails', '>= 3.5.0.beta1'
#JavaScript testing
# Rails 5 requires a version higher than 4
gem 'konacha', '~> 4.0'
#Mocks and stubs for JavaScript tests
gem 'sinon-rails'
# Javascript test runner for Rails
gem "teaspoon-mocha"
end
group :development do
......
......@@ -64,7 +64,6 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.10.0)
colorize (0.7.7)
concurrent-ruby (1.0.2)
cucumber (2.3.3)
builder (>= 2.1.2)
......@@ -117,14 +116,6 @@ GEM
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
json (1.8.3)
konacha (4.0.0)
actionpack (>= 4.1, < 5)
capybara
colorize
railties (>= 4.1, < 5)
sprockets (>= 2, < 4)
sprockets-rails (>= 2, < 4)
tilt
libv8 (3.16.14.15)
listen (3.0.8)
rb-fsevent (~> 0.9, >= 0.9.4)
......@@ -221,8 +212,6 @@ GEM
json (~> 1.8)
simplecov-html (~> 0.10.0)
simplecov-html (0.10.0)
sinon-rails (1.15.0)
railties (>= 3.1)
spring (1.7.1)
spring-watcher-listen (2.0.0)
listen (>= 2.7, < 4.0)
......@@ -239,6 +228,10 @@ GEM
activesupport (>= 4.0)
sprockets (>= 3.0.0)
sqlite3 (1.3.11)
teaspoon (1.1.5)
railties (>= 3.2.5, < 6)
teaspoon-mocha (2.3.3)
teaspoon (>= 1.0.0)
therubyracer (0.12.2)
libv8 (~> 3.16.14.0)
ref
......@@ -280,7 +273,6 @@ DEPENDENCIES
http_accept_language
jbuilder (~> 2.0)
jquery-rails
konacha (~> 4.0)
listen (~> 3.0.5)
poltergeist
puma (~> 3.0)
......@@ -289,10 +281,10 @@ DEPENDENCIES
sass-rails (~> 5.0)
shoulda-matchers (~> 3.1)
simplecov
sinon-rails
spring
spring-watcher-listen (~> 2.0.0)
sqlite3
teaspoon-mocha
therubyracer
turbolinks (~> 5.x)
tzinfo-data
......
// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file. JavaScript code in this file should be added after the last require_* statement.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery
//= require jquery_ujs
//= require foundation
//= require turbolinks
$(function(){ $(document).foundation(); });
# This is a manifest file that'll be compiled into application.js, which will include all the files
# listed below.
#
# Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
# or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
#
# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
# compiled file. JavaScript code in this file should be added after the last require_* statement.
#
# Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
# about supported directives.
#
#= require jquery
#= require jquery_ujs
#= require foundation
#= require turbolinks
$(-> $(document).foundation())
// Action Cable provides the framework to deal with WebSockets in Rails.
// You can generate new channels where WebSocket features live using the rails generate channel command.
//
//= require action_cable
//= require_self
//= require_tree ./channels
(function() {
this.App || (this.App = {});
App.cable = ActionCable.createConsumer();
}).call(this);
# Action Cable provides the framework to deal with WebSockets in Rails.
# You can generate new channels where WebSocket features live using the rails generate channel command.
#
#= require action_cable
#= require_self
#= require_tree ./channels
(->
@App or (@App = {})
App.cable = ActionCable.createConsumer()
).call this
function responsible_fields(event){
var current_year = new Date().getFullYear();
if(current_year - $('#profile_year').val() < 18) {
$('#responsible_fields').css('display', 'block');
$("#user-responsible-name").prop('required', true);
$("#user-responsible-id-number").prop('required', true);
} else {
$('#responsible_fields').css('display', 'none');
$("#user-responsible-name").prop('required', false);
$("#user-responsible-id-number").prop('required', false);
}
}
$('#profile_year').on('change', responsible_fields);
@responsible_fields = (event)->
current_date = new Date()
year_difference = current_date.getFullYear() - $('#profile_year').val()
month_difference = current_date.getMonth() + 1 - $('#profile_month').val()
day_difference = current_date.getUTCDate() - $('#profile_day').val()
if year_difference <= 18
if month_difference <= 0
if day_difference < 0
$('#responsible_fields').css('display', 'block')
$('#user-responsible-name').prop('required', true)
$('#user-responsible-id-number').prop('required', true)
return
$('#responsible_fields').css('display', 'none')
$('#user-responsible-name').prop('required', false)
$('#user-responsible-id-number').prop('required', false)
$('#profile_year').on('change', @responsible_fields)
$('#profile_month').on('change', @responsible_fields)
$('#profile_day').on('change', @responsible_fields)
function school_fields(event){
if($('input[name=student]').first().is(':checked')) {
$('#school_fields').css('display', 'block');
$("#profile_school_type").prop('required', true);
$("#user-school-name").prop('required', true);
} else {
$('#school_fields').css('display', 'none');
$("#profile_school_type").prop('required', false);
$("#user-school-name").prop('required', false);
}
}
$('input[name=student]').on('change', school_fields);
@school_fields = (event) ->
if $('input[name=student]').first().is(':checked')
$('#school_fields').css('display', 'block')
$('#profile_school_type').prop('required', true)
$('#user-school-name').prop('required', true)
else
$('#school_fields').css('display', 'none')
$('#profile_school_type').prop('required', false)
$('#user-school-name').prop('required', false)
$('input[name=student]').on('change', @school_fields)
@state_select = (event) ->
$.get(
'/states',
{
country: $('select#profile_country :selected').text(),
locale: $('#locale').val()
},
(data) ->
$('div#state_select').html(data)
)
$(document).ready(@state_select)
$('select#profile_country').on('change', @state_select)
function state_select(event) {
$.get('/states',
{ country: $('select#profile_country :selected').text(), locale: $('#locale').val() },
function(data) { $('div#state_select').html(data); }
);
}
$(document).ready(state_select);
$('select#profile_country').on('change', state_select);
task :default => 'teaspoon'
#= require spec_helper
#= require profiles/responsible_fields
describe 'responsible_fields', ->
before ->
@profile_year_input = val: ->
@profile_month_input = val: ->
@profile_day_input = val: ->
@val = val: ->
@fields = css: ->
@responsible_name = prop: ->
@responsible_id = prop: ->
@clock = sinon.useFakeTimers(new Date(2016, 0, 1).getTime())
sinon.stub(window, '$')
$.withArgs('#profile_year').returns(@profile_year_input)
$.withArgs('#profile_month').returns(@profile_month_input)
$.withArgs('#profile_day').returns(@profile_day_input)
$.withArgs('#responsible_fields').returns(@fields)
$.withArgs('#user-responsible-name').returns(@responsible_name)
$.withArgs('#user-responsible-id-number').returns(@responsible_id)
@css_spy_mock = sinon.mock(@fields)
@name_prop_mock = sinon.mock(@responsible_name)
@id_prop_mock = sinon.mock(@responsible_id)
describe 'when the user is younger than 18', ->
before ->
@birth_year = 1999
@birth_month = 12
@birth_day = 31
@profile_year_stub = sinon.stub(@profile_year_input, 'val').returns(@birth_year)
@profile_month_stub = sinon.stub(@profile_month_input, 'val').returns(@birth_month)
@profile_day_stub = sinon.stub(@profile_day_input, 'val').returns(@birth_day)
it 'shows the responsible fields', ->
@css_spy_mock.expects('css').once().withArgs('display', 'block')
@name_prop_mock.expects('prop').once().withArgs('required', true)
@id_prop_mock.expects('prop').once().withArgs('required', true)
responsible_fields()
sinon.assert.called(@profile_year_stub)
sinon.assert.called(@profile_month_stub)
sinon.assert.called(@profile_day_stub)
after ->
@profile_year_input.val.restore()
@profile_month_input.val.restore()
@profile_day_input.val.restore()
describe 'when the user is older than 18', ->
before ->
@birth_year = 1998
@birth_month = 1
@birth_day = 1
@profile_year_stub = sinon.stub(@profile_year_input, 'val').returns(@birth_year)
@profile_month_stub = sinon.stub(@profile_month_input, 'val').returns(@birth_month)
@profile_day_stub = sinon.stub(@profile_day_input, 'val').returns(@birth_day)
it 'shows the responsible fields', ->
@css_spy_mock.expects('css').once().withArgs('display', 'none')
@name_prop_mock.expects('prop').once().withArgs('required', false)
@id_prop_mock.expects('prop').once().withArgs('required', false)
responsible_fields()
sinon.assert.called(@profile_year_stub)
sinon.assert.called(@profile_month_stub)
sinon.assert.called(@profile_day_stub)
after ->
@profile_year_input.val.restore()
@profile_month_input.val.restore()
@profile_day_input.val.restore()
after ->
@css_spy_mock.verify()
@name_prop_mock.verify()
@id_prop_mock.verify()
$.restore()
@clock.restore()
#= require spec_helper
#= require profiles/school_fields
describe 'school_fields', ->
before ->
@first_mock = is: ->
@fields = css: ->
@school_type = prop: ->
@school_name = prop: ->
@student_inputs = first: ->
sinon.stub(window, '$')
$.withArgs('input[name=student]').returns(@student_inputs)
$.withArgs('#school_fields').returns(@fields)
$.withArgs('#profile_school_type').returns(@school_type)
$.withArgs('#user-school-name').returns(@school_name)
@css_spy_mock = sinon.mock(@fields)
@type_prop_mock = sinon.mock(@school_type)
@name_prop_mock = sinon.mock(@school_name)
@first_stub = sinon.stub(@student_inputs, 'first').returns(@first_mock)
describe 'when the student input is checked', ->
before ->
@checked = true
@is_stub = sinon.stub(@first_mock, 'is').withArgs(':checked').returns(@checked)
it 'shows the school fields', ->
@css_spy_mock.expects('css').once().withArgs('display', 'block')
@type_prop_mock.expects('prop').once().withArgs('required', true)
@name_prop_mock.expects('prop').once().withArgs('required', true)
school_fields()
sinon.assert.called(@first_stub)
sinon.assert.called(@is_stub)
after ->
@first_mock.is.restore()
describe 'when the student input is not checked', ->
before ->
@checked = false
@is_stub = sinon.stub(@first_mock, 'is').withArgs(':checked').returns(@checked)
it 'hides the school fields', ->
@css_spy_mock.expects('css').once().withArgs('display', 'none')
@type_prop_mock.expects('prop').once().withArgs('required', false)
@name_prop_mock.expects('prop').once().withArgs('required', false)
school_fields()
sinon.assert.called(@first_stub)
sinon.assert.called(@is_stub)
after ->
@first_mock.is.restore()
after ->
@css_spy_mock.verify()
@type_prop_mock.verify()
@name_prop_mock.verify()
$.restore()
#= require spec_helper
#= require profiles/state_select
describe 'state_select', ->
before ->
sinon.stub(window, '$')
@get_stub = sinon.stub(window.$, 'get')
$.withArgs('select#profile_country :selected').returns(text: -> 'Brazil')
$.withArgs('#locale').returns(val: -> 'en')
$.withArgs('div#state_select').returns(html: (data) ->)
after ->
$.restore()
it 'is expected to access the state route', ->
@get_stub.withArgs('/states', { country: 'Brazil', locale: 'en' }, (data) -> )
state_select()
sinon.assert.calledOnce(@get_stub)
# Teaspoon includes some support files, but you can use anything from your own support path too.
# require support/expect
# require support/sinon
# require support/chai
# require support/chai-jq-0.0.7
# require support/your-support-file
#
# PhantomJS (Teaspoons default driver) doesn't have support for Function.prototype.bind, which has caused confusion.
# Use this polyfill to avoid the confusion.
#= require support/phantomjs-shims
#
# You can require your own javascript files here. By default this will include everything in application, however you
# may get better load performance if you require the specific files that are being used in the spec that tests them.
#= require application
#
# Deferring execution
# If you're using CommonJS, RequireJS or some other asynchronous library you can defer execution. Call
# Teaspoon.execute() after everything has been loaded. Simple example of a timeout:
#
# Teaspoon.defer = true
# setTimeout(Teaspoon.execute, 1000)
#
# Matching files
# By default Teaspoon will look for files that match _spec.{js,js.coffee,.coffee}. Add a filename_spec.js file in your
# spec path and it'll be included in the default suite automatically. If you want to customize suites, check out the
# configuration in teaspoon_env.rb
#
# Manifest
# If you'd rather require your spec files manually (to control order for instance) you can disable the suite matcher in
# the configuration and use this file as a manifest.
#
# For more information: http://github.com/modeset/teaspoon
#
# Chai
# If you're using Chai, you'll probably want to initialize your preferred assertion style. You can read more about Chai
# at: http://chaijs.com/guide/styles
#
# window.assert = chai.assert
# window.expect = chai.expect
# window.should = chai.should()
Teaspoon.configure do |config|
# Determines where the Teaspoon routes will be mounted. Changing this to "/jasmine" would allow you to browse to
# `http://localhost:3000/jasmine` to run your tests.
config.mount_at = "/teaspoon"
# Specifies the root where Teaspoon will look for files. If you're testing an engine using a dummy application it can
# be useful to set this to your engines root (e.g. `Teaspoon::Engine.root`).
# Note: Defaults to `Rails.root` if nil.
config.root = nil
# Paths that will be appended to the Rails assets paths
# Note: Relative to `config.root`.
config.asset_paths = ["spec/javascripts", "spec/javascripts/stylesheets"]
# Fixtures are rendered through a controller, which allows using HAML, RABL/JBuilder, etc. Files in these paths will
# be rendered as fixtures.
config.fixture_paths = ["spec/javascripts/fixtures"]
# SUITES
#
# You can modify the default suite configuration and create new suites here. Suites are isolated from one another.
#
# When defining a suite you can provide a name and a block. If the name is left blank, :default is assumed. You can
# omit various directives and the ones defined in the default suite will be used.
#
# To run a specific suite
# - in the browser: http://localhost/teaspoon/[suite_name]
# - with the rake task: rake teaspoon suite=[suite_name]
# - with the cli: teaspoon --suite=[suite_name]
config.suite do |suite|
# Specify the framework you would like to use. This allows you to select versions, and will do some basic setup for
# you -- which you can override with the directives below. This should be specified first, as it can override other
# directives.
# Note: If no version is specified, the latest is assumed.
#
# Versions: 1.10.0, 1.17.1, 1.18.2, 1.19.0, 2.0.1, 2.1.0, 2.2.4, 2.2.5, 2.3.3
suite.use_framework :mocha, "2.3.3"
# Specify a file matcher as a regular expression and all matching files will be loaded when the suite is run. These
# files need to be within an asset path. You can add asset paths using the `config.asset_paths`.
suite.matcher = "{spec/javascripts,app/assets}/**/*_spec.{js,js.coffee,coffee}"
# Load additional JS files, but requiring them in your spec helper is the preferred way to do this.
suite.javascripts += ["support/expect", "support/sinon"]
# You can include your own stylesheets if you want to change how Teaspoon looks.
# Note: Spec related CSS can and should be loaded using fixtures.
#suite.stylesheets = ["teaspoon"]
# This suites spec helper, which can require additional support files. This file is loaded before any of your test
# files are loaded.
suite.helper = "spec_helper"
# Partial to be rendered in the head tag of the runner. You can use the provided ones or define your own by creating
# a `_boot.html.erb` in your fixtures path, and adjust the config to `"/boot"` for instance.
#
# Available: boot, boot_require_js
suite.boot_partial = "boot"
# Partial to be rendered in the body tag of the runner. You can define your own to create a custom body structure.
suite.body_partial = "body"
# Hooks allow you to use `Teaspoon.hook("fixtures")` before, after, or during your spec run. This will make a
# synchronous Ajax request to the server that will call all of the blocks you've defined for that hook name.
#suite.hook :fixtures, &proc{}
# Determine whether specs loaded into the test harness should be embedded as individual script tags or concatenated
# into a single file. Similar to Rails' asset `debug: true` and `config.assets.debug = true` options. By default,
# Teaspoon expands all assets to provide more valuable stack traces that reference individual source files.
#suite.expand_assets = true
# Non-.js file extensions Teaspoon should consider JavaScript files
#suite.js_extensions = [/(\.js)?.coffee/, /(\.js)?.es6/, ".es6.js"]
end
# Example suite. Since we're just filtering to files already within the root test/javascripts, these files will also
# be run in the default suite -- but can be focused into a more specific suite.
#config.suite :targeted do |suite|
# suite.matcher = "spec/javascripts/targeted/*_spec.{js,js.coffee,coffee}"
#end
# CONSOLE RUNNER SPECIFIC
#
# These configuration directives are applicable only when running via the rake task or command line interface. These
# directives can be overridden using the command line interface arguments or with ENV variables when using the rake
# task.
#
# Command Line Interface:
# teaspoon --driver=phantomjs --server-port=31337 --fail-fast=true --format=junit --suite=my_suite /spec/file_spec.js
#
# Rake:
# teaspoon DRIVER=phantomjs SERVER_PORT=31337 FAIL_FAST=true FORMATTERS=junit suite=my_suite
# Specify which headless driver to use. Supports PhantomJS, Selenium Webdriver and BrowserStack Webdriver.
#
# Available: :phantomjs, :selenium, :browserstack, :capybara_webkit
# PhantomJS: https://github.com/modeset/teaspoon/wiki/Using-PhantomJS
# Selenium Webdriver: https://github.com/modeset/teaspoon/wiki/Using-Selenium-WebDriver
# BrowserStack Webdriver: https://github.com/modeset/teaspoon/wiki/Using-BrowserStack-WebDriver
# Capybara Webkit: https://github.com/modeset/teaspoon/wiki/Using-Capybara-Webkit
#config.driver = :phantomjs
# Specify additional options for the driver.
#
# PhantomJS: https://github.com/modeset/teaspoon/wiki/Using-PhantomJS
# Selenium Webdriver: https://github.com/modeset/teaspoon/wiki/Using-Selenium-WebDriver
# BrowserStack Webdriver: https://github.com/modeset/teaspoon/wiki/Using-BrowserStack-WebDriver
# Capybara Webkit: https://github.com/modeset/teaspoon/wiki/Using-Capybara-Webkit
#config.driver_options = nil
# Specify the timeout for the driver. Specs are expected to complete within this time frame or the run will be
# considered a failure. This is to avoid issues that can arise where tests stall.
#config.driver_timeout = 180
# Specify a server to use with Rack (e.g. thin, mongrel). If nil is provided Rack::Server is used.
#config.server = nil
# Specify a host to run on a specific host, otherwise Teaspoon will use 127.0.0.1.
#config.server_host = nil
# Specify a port to run on a specific port, otherwise Teaspoon will use a random available port.
#config.server_port = nil
# Timeout for starting the server in seconds. If your server is slow to start you may have to bump this, or you may
# want to lower this if you know it shouldn't take long to start.
#config.server_timeout = 20
# Force Teaspoon to fail immediately after a failing suite. Can be useful to make Teaspoon fail early if you have
# several suites, but in environments like CI this may not be desirable.
#config.fail_fast = true
# Specify the formatters to use when outputting the results.
# Note: Output files can be specified by using `"junit>/path/to/output.xml"`.
#
# Available: :dot, :clean, :documentation, :json, :junit, :pride, :rspec_html, :snowday, :swayze_or_oprah, :tap, :tap_y, :teamcity
#config.formatters = [:dot]
# Specify if you want color output from the formatters.
#config.color = true
# Teaspoon pipes all console[log/debug/error] to $stdout. This is useful to catch places where you've forgotten to
# remove them, but in verbose applications this may not be desirable.
#config.suppress_log = false
# COVERAGE REPORTS / THRESHOLD ASSERTIONS
#
# Coverage reports requires Istanbul (https://github.com/gotwarlost/istanbul) to add instrumentation to your code and
# display coverage statistics.
#
# Coverage configurations are similar to suites. You can define several, and use different ones under different
# conditions.
#
# To run with a specific coverage configuration
# - with the rake task: rake teaspoon USE_COVERAGE=[coverage_name]
# - with the cli: teaspoon --coverage=[coverage_name]
# Specify that you always want a coverage configuration to be used. Otherwise, specify that you want coverage
# on the CLI.
# Set this to "true" or the name of your coverage config.
config.use_coverage = true
# You can have multiple coverage configs by passing a name to config.coverage.
# e.g. config.coverage :ci do |coverage|
# The default coverage config name is :default.
config.coverage do |coverage|
# Which coverage reports Istanbul should generate. Correlates directly to what Istanbul supports.
#
# Available: text-summary, text, html, lcov, lcovonly, cobertura, teamcity
coverage.reports = ['text-summary', 'html', 'text']
# The path that the coverage should be written to - when there's an artifact to write to disk.
# Note: Relative to `config.root`.
coverage.output_path = 'coverage'
# Assets to be ignored when generating coverage reports. Accepts an array of filenames or regular expressions. The
# default excludes assets from vendor, gems and support libraries.
#coverage.ignore = [%r{/lib/ruby/gems/}, %r{/vendor/assets/}, %r{/support/}, %r{/(.+)_helper.}]
# Various thresholds requirements can be defined, and those thresholds will be checked at the end of a run. If any
# aren't met the run will fail with a message. Thresholds can be defined as a percentage (0-100), or nil.
#coverage.statements = nil
#coverage.functions = nil
#coverage.branches = nil
#coverage.lines = nil
end
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment