diff --git a/.gitignore b/.gitignore
index 6bc401aa72..1d613d8b2c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -53,3 +53,4 @@ test/reports/
node_modules
.byebug_history
vendor/bundle
+spec/TEST-Teaspoon-Result.xml
diff --git a/Gemfile b/Gemfile
index f3b74ff9e6..7ceb25e378 100644
--- a/Gemfile
+++ b/Gemfile
@@ -99,11 +99,13 @@ group :test, :development do
gem 'json_expressions'
gem 'minitest-reporters', '~> 1.1.19'
gem 'openssl', '~> 2.0.0.beta.1'
+ gem 'phantomjs'
gem 'rails-perftest'
gem 'rake', '~> 12.3.1'
gem 'rest-client'
gem 'rspec'
gem 'test-unit'
+ gem 'teaspoon-mocha'
gem 'timecop'
end
diff --git a/Gemfile.lock b/Gemfile.lock
index 08480b5366..ce461375be 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -408,6 +408,10 @@ GEM
activesupport (>= 4.0)
sprockets (>= 3.0.0)
sqlite3 (1.3.13)
+ teaspoon (1.1.5)
+ railties (>= 3.2.5, < 6)
+ teaspoon-mocha (2.3.3)
+ teaspoon (>= 1.0.0)
term-ansicolor (1.6.0)
tins (~> 1.0)
test-unit (3.2.8)
@@ -501,6 +505,7 @@ DEPENDENCIES
openssl (~> 2.0.0.beta.1)
paperclip (~> 5.2.0)
passenger
+ phantomjs
php-serialize
progress_bar
rack-cors
@@ -526,6 +531,7 @@ DEPENDENCIES
scrypt (~> 3)
sidekiq
sqlite3
+ teaspoon-mocha
test-unit
therubyracer
timecop
diff --git a/Rakefile b/Rakefile
index 02795c1784..6837e41217 100644
--- a/Rakefile
+++ b/Rakefile
@@ -18,7 +18,7 @@ namespace :test do
t.test_files = FileList['test/**/*_test.rb']
end
- desc "Run rails and jasmine tests"
+ desc "Run rails and teaspoon tests"
task :all => :environment do
require 'coveralls/rake/task'
Coveralls::RakeTask.new
@@ -26,15 +26,8 @@ namespace :test do
require 'ci/reporter/rake/test_unit'
Rake::Task["ci:setup:testunit"].execute
end
- puts "Running jasmine tests headlessly"
- Rake::Task["spec:javascript"].execute
+ puts "Running teaspoon tests headlessly"
+ Rake::Task["teaspoon"].execute
Rake::Task["coveralls:push"].execute
end
-
- desc "Run rails and jasmine tests"
- task :javascript do
- puts "Running jasmine tests headlessly"
- Rake::Task["spec:javascript"].execute
- end
-
end
diff --git a/spec/javascripts/fixtures/index.html b/spec/javascripts/fixtures/index.html
index b07c6c5363..701ba87716 100644
--- a/spec/javascripts/fixtures/index.html
+++ b/spec/javascripts/fixtures/index.html
@@ -1,24 +1,6 @@
-
diff --git a/spec/javascripts/inline_grids_spec.js b/spec/javascripts/inline_grids_spec.js
index 810f320f3c..1bcabf0c5e 100644
--- a/spec/javascripts/inline_grids_spec.js
+++ b/spec/javascripts/inline_grids_spec.js
@@ -1,39 +1,31 @@
-//= require application
-//= require jasmine-jquery
-//= require comment_expand
-
var editor;
+fixture.preload("inline_grid.html");
+
describe("Inline grids", function() {
beforeEach(function() {
- // for phantomjs running
- jasmine.getFixtures().fixturesPath="../../spec/javascripts/fixtures";
-
- // for in-browser running... still doesn't work
- //jasmine.getFixtures().fixturesPath = 'assets/fixtures';
-// preloadFixtures('inline_grids.html');
+ this.fixtures = fixture.load("inline_grid.html");
});
it("sends a like request when like button is clicked", function() {
- loadFixtures('inline_grid.html');
- expect($($('.notes-grid tr')[1]).find('td:first a').html()).toEqual('First post');
+ expect($($('.notes-grid tr')[1]).find('td:first a').html()).to.eql('First post');
$('table:first th:first a').trigger('click');
setTimeout(0, function() {
- expect($($('.notes-grid tr')[1]).find('td:first a').html()).toEqual('Third post');
+ expect($($('.notes-grid tr')[1]).find('td:first a').html()).to.eql('Third post');
setTimeout(0, function() {
$('table:first th:first a').trigger('click');
- expect($($('.notes-grid tr')[1]).find('td:first a').html()).toEqual('First post');
+ expect($($('.notes-grid tr')[1]).find('td:first a').html()).to.eql('First post');
});
diff --git a/spec/javascripts/plots2_spec.js b/spec/javascripts/plots2_spec.js
index 40bd32c3e2..c47722e6d2 100644
--- a/spec/javascripts/plots2_spec.js
+++ b/spec/javascripts/plots2_spec.js
@@ -1,105 +1,106 @@
-//= require application
-//= require jasmine-jquery
//= require comment_expand
-
+//= require like
var editor;
describe("Plots2", function() {
beforeEach(function() {
- // for phantomjs running
- jasmine.getFixtures().fixturesPath="../../spec/javascripts/fixtures";
-
- // for in-browser running... still doesn't work
- //jasmine.getFixtures().fixturesPath = 'assets/fixtures';
- preloadFixtures('index.html', 'unlike.html', 'comment_expand.html');
-
- jasmine.Ajax.install();
+ fixture.preload('index.html', 'unlike.html', 'comment_expand.html');
});
+ it("sends a like request when like button is clicked", function() {
- afterEach(function(){
-
- jasmine.Ajax.uninstall();
-
- });
+ fixture.load('index.html');
+ ajaxStub = sinon.stub($, 'ajax', function(object) {
- it("sends a like request when like button is clicked", function() {
+ if (object.url == '/likes/node/1/create') response = '4';
+ else response = 'none';
- loadFixtures('index.html');
+ // check this if you have trouble faking a server response:
+ if (response == '4'){
+ console.log('Faked response to:', object.url);
+ }
+ else console.log('Failed to fake response to:', object.url);
- ajaxSpy = spyOn($, "ajax").and.callFake(function(object) {
+ var d = $.Deferred();
+ if(response == '4'){
+ d.resolve(response);
+ }
+ else{
+ d.reject(response);
+ }
- if (object.url == '/likes/node/1/create') response = "4";
- else response = 'none';
+ return d.promise();
- // check this if you have trouble faking a server response:
- if (response != 'none'){
- console.log('Faked response to:', object.url);
- console.log(response);
- }
- else console.log('Failed to fake response to:', object.url);
-
- // http://stackoverflow.com/questions/13148356/how-to-properly-unit-test-jquerys-ajax-promises-using-jasmine-and-or-sinon
- var d = $.Deferred();
- d.resolve(response);
- d.reject(response);
- return d.promise();
+ });
+ $(document).ready(function(){
+ $("#like-button-1").on('click', clicknotliked);
});
- $('#like-button-1').trigger('click');
-
+ $("#like-button-1").click();
// should trigger the following and our ajaxSpy should return a fake response of "4":
- // jQuery.getJSON("/likes/node/1/create", {}, function() { ...
- // then triggering like.js code
+ /*var response;
+ jQuery.getJSON("/likes/node/1/create").done(function(data){
+ response = data;
+ });*/
- expect(response).toEqual('4');
- expect($('#like-count-1').html()).toEqual('4'); // passing
- expect($('#like-star-1')[0].className).toEqual('fa fa-star');
+ // then triggering like.js code
+ expect($('#like-count-1').html()).to.eql('4'); // passing
+ expect($('#like-star-1')[0].className).to.eql('fa fa-star');
+ ajaxStub.restore();
});
it("unlikes a request if already liked", function() {
- loadFixtures('unlike.html');
+ fixture.load('unlike.html');
- ajaxSpy = spyOn($, "ajax").and.callFake(function(object) {
+ ajaxStub = sinon.stub($, 'ajax', function(object) {
- if (object.url == '/likes/node/1/delete') response = "-1";
- else response = 'none';
+ if (object.url == '/likes/node/1/delete') response = '-1';
+ else response = 'none';
- var d = $.Deferred();
- d.resolve(response);
- d.reject(response);
- return d.promise();
+ // check this if you have trouble faking a server response:
- });
+ var d = $.Deferred();
+ if(response == '-1'){
+ d.resolve(response);
+ }
+ else{
+ d.reject(response);
+ }
+
+ return d.promise();
- $('#like-button-1').trigger('click');
+ });
- expect(response).toEqual('-1');
- expect($('#like-count-1').html()).toEqual('0');
- expect($('#like-star-1')[0].className).toEqual('fa fa-star-o');
+ $(document).ready(function(){
+ $("#like-button-1").on('click', clickliked);
+ });
+ $("#like-button-1").trigger('click');
+ expect($('#like-count-1').html()).to.eql('0');
+ expect($('#like-star-1')[0].className).to.eql('fa fa-star-o');
+ ajaxStub.restore();
});
it("shows expand comment button with remaining comment count", function(){
- loadFixtures('comment_expand.html');
+ fixture.load('comment_expand.html');
- $('#answer-0-expand').trigger('click');
- expect($('#answer-0-expand').html()).toEqual('View 2 previous comments');
+ expand_comments(0);
+ expect($('#answer-0-expand').html()).to.eql('View 2 previous comments');
- $('#answer-0-expand').trigger('click');
- expect($('#answer-0-expand').css('display')).toEqual('none');
+ expand_comments(0);
+ expect($('#answer-0-expand').css('display')).to.eql('none');
});
it("loads up i18n-js library properly", function() {
- expect(I18n.t('js.dashboard.selected_updates')).toBe('Selected updates')
+ expect(I18n.t('js.dashboard.selected_updates')).to.eql('Selected updates')
});
});
diff --git a/spec/javascripts/spec_helper.js b/spec/javascripts/spec_helper.js
new file mode 100644
index 0000000000..25f68cb09b
--- /dev/null
+++ b/spec/javascripts/spec_helper.js
@@ -0,0 +1,41 @@
+// 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 jquery
+// 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();
diff --git a/spec/javascripts/tagging_spec.js b/spec/javascripts/tagging_spec.js
index a0437230fe..23a1bb308e 100644
--- a/spec/javascripts/tagging_spec.js
+++ b/spec/javascripts/tagging_spec.js
@@ -1,6 +1,5 @@
/*
//= require application
-//= require jasmine-jquery
//= require tagging
var editor;
diff --git a/spec/javascripts/wikis_spec.js b/spec/javascripts/wikis_spec.js
index 2073e9bc8f..26a20b53a5 100644
--- a/spec/javascripts/wikis_spec.js
+++ b/spec/javascripts/wikis_spec.js
@@ -1,5 +1,4 @@
//= require application
-//= require jasmine-jquery
//= require wikis
var editor;
@@ -9,36 +8,27 @@ describe("Wikis", function() {
beforeEach(function() {
// for phantomjs running
- jasmine.getFixtures().fixturesPath="../../spec/javascripts/fixtures";
// for in-browser running... still doesn't work
//jasmine.getFixtures().fixturesPath = 'assets/fixtures';
- preloadFixtures('content.html');
+ fixture.preload('content.html');
- jasmine.Ajax.install();
-
- });
-
-
- afterEach(function(){
-
- jasmine.Ajax.uninstall();
});
it("adds deep links like example.com#Sub+section", function() {
- loadFixtures('content.html');
+ fixture.load('content.html');
addDeepLinks($('#content'));
- expect($('#content h2 i.fa').length).not.toBe(0);
+ expect($('#content h2 i.fa').length).to.not.equal(0);
});
it("adds table CSS", function() {
- loadFixtures('content.html');
+ fixture.load('content.html');
postProcessContent($('#content'));
- expect($('#content table.table').length).not.toBe(0);
+ expect($('#content table.table').length).to.not.equal(0);
});
diff --git a/spec/teaspoon_env.rb b/spec/teaspoon_env.rb
new file mode 100644
index 0000000000..49cb8e3eb0
--- /dev/null
+++ b/spec/teaspoon_env.rb
@@ -0,0 +1,185 @@
+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 = []
+
+ # 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", "junit>spec/TEST-Teaspoon-Result.xml"]
+ # 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 = nil
+
+ # 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"]
+
+ # 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