From 14071a2286d0bc16979b407994be8082c545d46e Mon Sep 17 00:00:00 2001 From: Sasha Koss Date: Thu, 11 Aug 2011 11:18:07 +0700 Subject: [PATCH 01/20] Bundle Jasmine gem --- Gemfile | 3 +++ Gemfile.lock | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 Gemfile create mode 100644 Gemfile.lock diff --git a/Gemfile b/Gemfile new file mode 100644 index 000000000..80af767d8 --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source :rubygems + +gem 'jasmine' diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 000000000..bfb8269fc --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,34 @@ +GEM + remote: http://rubygems.org/ + specs: + childprocess (0.2.0) + ffi (~> 1.0.6) + diff-lcs (1.1.2) + ffi (1.0.9) + jasmine (1.0.2.1) + json_pure (>= 1.4.3) + rack (>= 1.1) + rspec (>= 1.3.1) + selenium-webdriver (>= 0.1.3) + json_pure (1.5.3) + rack (1.3.2) + rspec (2.6.0) + rspec-core (~> 2.6.0) + rspec-expectations (~> 2.6.0) + rspec-mocks (~> 2.6.0) + rspec-core (2.6.4) + rspec-expectations (2.6.0) + diff-lcs (~> 1.1.2) + rspec-mocks (2.6.0) + rubyzip (0.9.4) + selenium-webdriver (2.3.2) + childprocess (>= 0.1.9) + ffi (>= 1.0.7) + json_pure + rubyzip + +PLATFORMS + ruby + +DEPENDENCIES + jasmine From 41c24eeddcf8d5671ee3214dbb55465b6c83122c Mon Sep 17 00:00:00 2001 From: Sasha Koss Date: Thu, 11 Aug 2011 11:20:58 +0700 Subject: [PATCH 02/20] Setup rake with bundler --- Rakefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Rakefile b/Rakefile index bec4a53f3..436674b9d 100644 --- a/Rakefile +++ b/Rakefile @@ -1,3 +1,6 @@ +require 'rubygems' +require 'bundler/setup' + require 'rake' require 'rake/packagetask' From ebafc9dd252915d46745f8bf528770cb35ae8260 Mon Sep 17 00:00:00 2001 From: Sasha Koss Date: Thu, 11 Aug 2011 11:21:10 +0700 Subject: [PATCH 03/20] Bundle rake --- Gemfile | 1 + Gemfile.lock | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Gemfile b/Gemfile index 80af767d8..9853f0fdc 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,4 @@ source :rubygems +gem 'rake' gem 'jasmine' diff --git a/Gemfile.lock b/Gemfile.lock index bfb8269fc..7ad85c730 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -12,6 +12,7 @@ GEM selenium-webdriver (>= 0.1.3) json_pure (1.5.3) rack (1.3.2) + rake (0.8.7) rspec (2.6.0) rspec-core (~> 2.6.0) rspec-expectations (~> 2.6.0) @@ -32,3 +33,4 @@ PLATFORMS DEPENDENCIES jasmine + rake From a50f962d567b4d7967f4e2f55c2751a2421bc950 Mon Sep 17 00:00:00 2001 From: Sasha Koss Date: Thu, 11 Aug 2011 11:29:08 +0700 Subject: [PATCH 04/20] Init Jasmine --- Rakefile | 9 +++ public/javascripts/Player.js | 22 +++++++ public/javascripts/Song.js | 7 +++ spec/javascripts/PlayerSpec.js | 58 +++++++++++++++++ spec/javascripts/helpers/SpecHelper.js | 9 +++ spec/javascripts/support/jasmine.yml | 73 ++++++++++++++++++++++ spec/javascripts/support/jasmine_config.rb | 23 +++++++ spec/javascripts/support/jasmine_runner.rb | 32 ++++++++++ 8 files changed, 233 insertions(+) create mode 100644 public/javascripts/Player.js create mode 100644 public/javascripts/Song.js create mode 100644 spec/javascripts/PlayerSpec.js create mode 100644 spec/javascripts/helpers/SpecHelper.js create mode 100644 spec/javascripts/support/jasmine.yml create mode 100644 spec/javascripts/support/jasmine_config.rb create mode 100644 spec/javascripts/support/jasmine_runner.rb diff --git a/Rakefile b/Rakefile index 436674b9d..e6a8ad4a9 100644 --- a/Rakefile +++ b/Rakefile @@ -135,3 +135,12 @@ Rake::PackageTask.new('zepto', ZEPTO_VERSION) do |package| 'examples/**/*' ) end + +begin + require 'jasmine' + load 'jasmine/tasks/jasmine.rake' +rescue LoadError + task :jasmine do + abort "Jasmine is not available. In order to run jasmine, you must: (sudo) gem install jasmine" + end +end diff --git a/public/javascripts/Player.js b/public/javascripts/Player.js new file mode 100644 index 000000000..fcce8268c --- /dev/null +++ b/public/javascripts/Player.js @@ -0,0 +1,22 @@ +function Player() { +} +Player.prototype.play = function(song) { + this.currentlyPlayingSong = song; + this.isPlaying = true; +}; + +Player.prototype.pause = function() { + this.isPlaying = false; +}; + +Player.prototype.resume = function() { + if (this.isPlaying) { + throw new Error("song is already playing"); + } + + this.isPlaying = true; +}; + +Player.prototype.makeFavorite = function() { + this.currentlyPlayingSong.persistFavoriteStatus(true); +}; \ No newline at end of file diff --git a/public/javascripts/Song.js b/public/javascripts/Song.js new file mode 100644 index 000000000..a8a3f2ddd --- /dev/null +++ b/public/javascripts/Song.js @@ -0,0 +1,7 @@ +function Song() { +} + +Song.prototype.persistFavoriteStatus = function(value) { + // something complicated + throw new Error("not yet implemented"); +}; \ No newline at end of file diff --git a/spec/javascripts/PlayerSpec.js b/spec/javascripts/PlayerSpec.js new file mode 100644 index 000000000..79f102217 --- /dev/null +++ b/spec/javascripts/PlayerSpec.js @@ -0,0 +1,58 @@ +describe("Player", function() { + var player; + var song; + + beforeEach(function() { + player = new Player(); + song = new Song(); + }); + + it("should be able to play a Song", function() { + player.play(song); + expect(player.currentlyPlayingSong).toEqual(song); + + //demonstrates use of custom matcher + expect(player).toBePlaying(song); + }); + + describe("when song has been paused", function() { + beforeEach(function() { + player.play(song); + player.pause(); + }); + + it("should indicate that the song is currently paused", function() { + expect(player.isPlaying).toBeFalsy(); + + // demonstrates use of 'not' with a custom matcher + expect(player).not.toBePlaying(song); + }); + + it("should be possible to resume", function() { + player.resume(); + expect(player.isPlaying).toBeTruthy(); + expect(player.currentlyPlayingSong).toEqual(song); + }); + }); + + // demonstrates use of spies to intercept and test method calls + it("tells the current song if the user has made it a favorite", function() { + spyOn(song, 'persistFavoriteStatus'); + + player.play(song); + player.makeFavorite(); + + expect(song.persistFavoriteStatus).toHaveBeenCalledWith(true); + }); + + //demonstrates use of expected exceptions + describe("#resume", function() { + it("should throw an exception if song is already playing", function() { + player.play(song); + + expect(function() { + player.resume(); + }).toThrow("song is already playing"); + }); + }); +}); \ No newline at end of file diff --git a/spec/javascripts/helpers/SpecHelper.js b/spec/javascripts/helpers/SpecHelper.js new file mode 100644 index 000000000..4919c87ac --- /dev/null +++ b/spec/javascripts/helpers/SpecHelper.js @@ -0,0 +1,9 @@ +beforeEach(function() { + this.addMatchers({ + toBePlaying: function(expectedSong) { + var player = this.actual; + return player.currentlyPlayingSong === expectedSong + && player.isPlaying; + } + }) +}); diff --git a/spec/javascripts/support/jasmine.yml b/spec/javascripts/support/jasmine.yml new file mode 100644 index 000000000..7cce692d7 --- /dev/null +++ b/spec/javascripts/support/jasmine.yml @@ -0,0 +1,73 @@ +# src_files +# +# Return an array of filepaths relative to src_dir to include before jasmine specs. +# Default: [] +# +# EXAMPLE: +# +# src_files: +# - lib/source1.js +# - lib/source2.js +# - dist/**/*.js +# +src_files: + - public/javascripts/**/*.js + +# stylesheets +# +# Return an array of stylesheet filepaths relative to src_dir to include before jasmine specs. +# Default: [] +# +# EXAMPLE: +# +# stylesheets: +# - css/style.css +# - stylesheets/*.css +# +stylesheets: + +# helpers +# +# Return an array of filepaths relative to spec_dir to include before jasmine specs. +# Default: ["helpers/**/*.js"] +# +# EXAMPLE: +# +# helpers: +# - helpers/**/*.js +# +helpers: + +# spec_files +# +# Return an array of filepaths relative to spec_dir to include. +# Default: ["**/*[sS]pec.js"] +# +# EXAMPLE: +# +# spec_files: +# - **/*[sS]pec.js +# +spec_files: + +# src_dir +# +# Source directory path. Your src_files must be returned relative to this path. Will use root if left blank. +# Default: project root +# +# EXAMPLE: +# +# src_dir: public +# +src_dir: + +# spec_dir +# +# Spec directory path. Your spec_files must be returned relative to this path. +# Default: spec/javascripts +# +# EXAMPLE: +# +# spec_dir: spec/javascripts +# +spec_dir: diff --git a/spec/javascripts/support/jasmine_config.rb b/spec/javascripts/support/jasmine_config.rb new file mode 100644 index 000000000..47286f230 --- /dev/null +++ b/spec/javascripts/support/jasmine_config.rb @@ -0,0 +1,23 @@ +module Jasmine + class Config + + # Add your overrides or custom config code here + + end +end + + +# Note - this is necessary for rspec2, which has removed the backtrace +module Jasmine + class SpecBuilder + def declare_spec(parent, spec) + me = self + example_name = spec["name"] + @spec_ids << spec["id"] + backtrace = @example_locations[parent.description + " " + example_name] + parent.it example_name, {} do + me.report_spec(spec["id"]) + end + end + end +end diff --git a/spec/javascripts/support/jasmine_runner.rb b/spec/javascripts/support/jasmine_runner.rb new file mode 100644 index 000000000..13ebce0cd --- /dev/null +++ b/spec/javascripts/support/jasmine_runner.rb @@ -0,0 +1,32 @@ +$:.unshift(ENV['JASMINE_GEM_PATH']) if ENV['JASMINE_GEM_PATH'] # for gem testing purposes + +require 'rubygems' +require 'jasmine' +jasmine_config_overrides = File.expand_path(File.join(File.dirname(__FILE__), 'jasmine_config.rb')) +require jasmine_config_overrides if File.exist?(jasmine_config_overrides) +if Jasmine::rspec2? + require 'rspec' +else + require 'spec' +end + +jasmine_config = Jasmine::Config.new +spec_builder = Jasmine::SpecBuilder.new(jasmine_config) + +should_stop = false + +if Jasmine::rspec2? + RSpec.configuration.after(:suite) do + spec_builder.stop if should_stop + end +else + Spec::Runner.configure do |config| + config.after(:suite) do + spec_builder.stop if should_stop + end + end +end + +spec_builder.start +should_stop = true +spec_builder.declare_suites \ No newline at end of file From b3f6cfed1de99410d917145ee89384c99880c671 Mon Sep 17 00:00:00 2001 From: Sasha Koss Date: Thu, 11 Aug 2011 11:42:16 +0700 Subject: [PATCH 05/20] Write example spec for ajax.js --- spec/javascripts/PlayerSpec.js | 58 -------------------------- spec/javascripts/ajax_spec.js | 7 ++++ spec/javascripts/helpers/SpecHelper.js | 9 ---- spec/javascripts/support/jasmine.yml | 5 ++- 4 files changed, 10 insertions(+), 69 deletions(-) delete mode 100644 spec/javascripts/PlayerSpec.js create mode 100644 spec/javascripts/ajax_spec.js delete mode 100644 spec/javascripts/helpers/SpecHelper.js diff --git a/spec/javascripts/PlayerSpec.js b/spec/javascripts/PlayerSpec.js deleted file mode 100644 index 79f102217..000000000 --- a/spec/javascripts/PlayerSpec.js +++ /dev/null @@ -1,58 +0,0 @@ -describe("Player", function() { - var player; - var song; - - beforeEach(function() { - player = new Player(); - song = new Song(); - }); - - it("should be able to play a Song", function() { - player.play(song); - expect(player.currentlyPlayingSong).toEqual(song); - - //demonstrates use of custom matcher - expect(player).toBePlaying(song); - }); - - describe("when song has been paused", function() { - beforeEach(function() { - player.play(song); - player.pause(); - }); - - it("should indicate that the song is currently paused", function() { - expect(player.isPlaying).toBeFalsy(); - - // demonstrates use of 'not' with a custom matcher - expect(player).not.toBePlaying(song); - }); - - it("should be possible to resume", function() { - player.resume(); - expect(player.isPlaying).toBeTruthy(); - expect(player.currentlyPlayingSong).toEqual(song); - }); - }); - - // demonstrates use of spies to intercept and test method calls - it("tells the current song if the user has made it a favorite", function() { - spyOn(song, 'persistFavoriteStatus'); - - player.play(song); - player.makeFavorite(); - - expect(song.persistFavoriteStatus).toHaveBeenCalledWith(true); - }); - - //demonstrates use of expected exceptions - describe("#resume", function() { - it("should throw an exception if song is already playing", function() { - player.play(song); - - expect(function() { - player.resume(); - }).toThrow("song is already playing"); - }); - }); -}); \ No newline at end of file diff --git a/spec/javascripts/ajax_spec.js b/spec/javascripts/ajax_spec.js new file mode 100644 index 000000000..66c716ed5 --- /dev/null +++ b/spec/javascripts/ajax_spec.js @@ -0,0 +1,7 @@ +describe('Ajax', function () { + describe('$.ajax', function () { + it('should be defined', function () { + expect($.ajax).toBeDefined(); + }); + }); +}); diff --git a/spec/javascripts/helpers/SpecHelper.js b/spec/javascripts/helpers/SpecHelper.js deleted file mode 100644 index 4919c87ac..000000000 --- a/spec/javascripts/helpers/SpecHelper.js +++ /dev/null @@ -1,9 +0,0 @@ -beforeEach(function() { - this.addMatchers({ - toBePlaying: function(expectedSong) { - var player = this.actual; - return player.currentlyPlayingSong === expectedSong - && player.isPlaying; - } - }) -}); diff --git a/spec/javascripts/support/jasmine.yml b/spec/javascripts/support/jasmine.yml index 7cce692d7..e72c3878b 100644 --- a/spec/javascripts/support/jasmine.yml +++ b/spec/javascripts/support/jasmine.yml @@ -11,7 +11,8 @@ # - dist/**/*.js # src_files: - - public/javascripts/**/*.js + - zepto.js + - *.js # stylesheets # @@ -59,7 +60,7 @@ spec_files: # # src_dir: public # -src_dir: +src_dir: src # spec_dir # From 197a07243c14c4c0e0da99071b0d2998a4f6a3e8 Mon Sep 17 00:00:00 2001 From: Sasha Koss Date: Thu, 11 Aug 2011 11:43:14 +0700 Subject: [PATCH 06/20] Remove example jasmine's files --- public/javascripts/Player.js | 22 ---------------------- public/javascripts/Song.js | 7 ------- 2 files changed, 29 deletions(-) delete mode 100644 public/javascripts/Player.js delete mode 100644 public/javascripts/Song.js diff --git a/public/javascripts/Player.js b/public/javascripts/Player.js deleted file mode 100644 index fcce8268c..000000000 --- a/public/javascripts/Player.js +++ /dev/null @@ -1,22 +0,0 @@ -function Player() { -} -Player.prototype.play = function(song) { - this.currentlyPlayingSong = song; - this.isPlaying = true; -}; - -Player.prototype.pause = function() { - this.isPlaying = false; -}; - -Player.prototype.resume = function() { - if (this.isPlaying) { - throw new Error("song is already playing"); - } - - this.isPlaying = true; -}; - -Player.prototype.makeFavorite = function() { - this.currentlyPlayingSong.persistFavoriteStatus(true); -}; \ No newline at end of file diff --git a/public/javascripts/Song.js b/public/javascripts/Song.js deleted file mode 100644 index a8a3f2ddd..000000000 --- a/public/javascripts/Song.js +++ /dev/null @@ -1,7 +0,0 @@ -function Song() { -} - -Song.prototype.persistFavoriteStatus = function(value) { - // something complicated - throw new Error("not yet implemented"); -}; \ No newline at end of file From 2e613141c55759a98eaadfaa865ceb24470207e6 Mon Sep 17 00:00:00 2001 From: Sasha Koss Date: Thu, 11 Aug 2011 12:07:47 +0700 Subject: [PATCH 07/20] Install mock-ajax.js --- spec/javascripts/helpers/mock-ajax.js | 207 ++++++++++++++++++++++++++ 1 file changed, 207 insertions(+) create mode 100644 spec/javascripts/helpers/mock-ajax.js diff --git a/spec/javascripts/helpers/mock-ajax.js b/spec/javascripts/helpers/mock-ajax.js new file mode 100644 index 000000000..5c99627ae --- /dev/null +++ b/spec/javascripts/helpers/mock-ajax.js @@ -0,0 +1,207 @@ +/* + Jasmine-Ajax : a set of helpers for testing AJAX requests under the Jasmine + BDD framework for JavaScript. + + Supports both Prototype.js and jQuery. + + http://github.com/pivotal/jasmine-ajax + + Jasmine Home page: http://pivotal.github.com/jasmine + + Copyright (c) 2008-2010 Pivotal Labs + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + */ + +// Jasmine-Ajax interface +var ajaxRequests = []; + +function mostRecentAjaxRequest() { + if (ajaxRequests.length > 0) { + return ajaxRequests[ajaxRequests.length - 1]; + } else { + return null; + } +} + +function clearAjaxRequests() { + ajaxRequests = []; +} + +// Fake XHR for mocking Ajax Requests & Responses +function FakeXMLHttpRequest() { + var extend = Object.extend || $.extend; + extend(this, { + requestHeaders: {}, + + open: function() { + this.method = arguments[0]; + this.url = arguments[1]; + this.readyState = 1; + }, + + setRequestHeader: function(header, value) { + this.requestHeaders[header] = value; + }, + + abort: function() { + this.readyState = 0; + }, + + readyState: 0, + + onreadystatechange: function(isTimeout) { + }, + + status: null, + + send: function(data) { + this.params = data; + this.readyState = 2; + }, + + getResponseHeader: function(name) { + return this.responseHeaders[name]; + }, + + getAllResponseHeaders: function() { + var responseHeaders = []; + for (var i in this.responseHeaders) { + if (this.responseHeaders.hasOwnProperty(i)) { + responseHeaders.push(i + ': ' + this.responseHeaders[i]); + } + } + return responseHeaders.join('\r\n'); + }, + + responseText: null, + + response: function(response) { + this.status = response.status; + this.responseText = response.responseText || ""; + this.readyState = 4; + this.responseHeaders = response.responseHeaders || + {"Content-type": response.contentType || "application/json" }; + // uncomment for jquery 1.3.x support + // jasmine.Clock.tick(20); + + this.onreadystatechange(); + }, + responseTimeout: function() { + this.readyState = 4; + jasmine.Clock.tick(jQuery.ajaxSettings.timeout || 30000); + this.onreadystatechange('timeout'); + } + }); + + return this; +} + + +jasmine.Ajax = { + + isInstalled: function() { + return jasmine.Ajax.installed == true; + }, + + assertInstalled: function() { + if (!jasmine.Ajax.isInstalled()) { + throw new Error("Mock ajax is not installed, use jasmine.Ajax.useMock()") + } + }, + + useMock: function() { + if (!jasmine.Ajax.isInstalled()) { + var spec = jasmine.getEnv().currentSpec; + spec.after(jasmine.Ajax.uninstallMock); + + jasmine.Ajax.installMock(); + } + }, + + installMock: function() { + if (typeof jQuery != 'undefined') { + jasmine.Ajax.installJquery(); + } else if (typeof Prototype != 'undefined') { + jasmine.Ajax.installPrototype(); + } else { + throw new Error("jasmine.Ajax currently only supports jQuery and Prototype"); + } + jasmine.Ajax.installed = true; + }, + + installJquery: function() { + jasmine.Ajax.mode = 'jQuery'; + jasmine.Ajax.real = jQuery.ajaxSettings.xhr; + jQuery.ajaxSettings.xhr = jasmine.Ajax.jQueryMock; + + }, + + installPrototype: function() { + jasmine.Ajax.mode = 'Prototype'; + jasmine.Ajax.real = Ajax.getTransport; + + Ajax.getTransport = jasmine.Ajax.prototypeMock; + }, + + uninstallMock: function() { + jasmine.Ajax.assertInstalled(); + if (jasmine.Ajax.mode == 'jQuery') { + jQuery.ajaxSettings.xhr = jasmine.Ajax.real; + } else if (jasmine.Ajax.mode == 'Prototype') { + Ajax.getTransport = jasmine.Ajax.real; + } + jasmine.Ajax.reset(); + }, + + reset: function() { + jasmine.Ajax.installed = false; + jasmine.Ajax.mode = null; + jasmine.Ajax.real = null; + }, + + jQueryMock: function() { + var newXhr = new FakeXMLHttpRequest(); + ajaxRequests.push(newXhr); + return newXhr; + }, + + prototypeMock: function() { + return new FakeXMLHttpRequest(); + }, + + installed: false, + mode: null +} + + +// Jasmine-Ajax Glue code for Prototype.js +if (typeof Prototype != 'undefined' && Ajax && Ajax.Request) { + Ajax.Request.prototype.originalRequest = Ajax.Request.prototype.request; + Ajax.Request.prototype.request = function(url) { + this.originalRequest(url); + ajaxRequests.push(this); + }; + + Ajax.Request.prototype.response = function(responseOptions) { + return this.transport.response(responseOptions); + }; +} From 47e7a7f4765db5c48da154087a622f0185c12abf Mon Sep 17 00:00:00 2001 From: Sasha Koss Date: Thu, 11 Aug 2011 12:26:36 +0700 Subject: [PATCH 08/20] Write adaptation for Zepto in mock-ajax.js --- spec/javascripts/helpers/mock-ajax.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/spec/javascripts/helpers/mock-ajax.js b/spec/javascripts/helpers/mock-ajax.js index 5c99627ae..15faebd97 100644 --- a/spec/javascripts/helpers/mock-ajax.js +++ b/spec/javascripts/helpers/mock-ajax.js @@ -2,7 +2,7 @@ Jasmine-Ajax : a set of helpers for testing AJAX requests under the Jasmine BDD framework for JavaScript. - Supports both Prototype.js and jQuery. + Supports both Prototype.js, jQuery and Zepto.js. http://github.com/pivotal/jasmine-ajax @@ -142,8 +142,10 @@ jasmine.Ajax = { jasmine.Ajax.installJquery(); } else if (typeof Prototype != 'undefined') { jasmine.Ajax.installPrototype(); + } else if (typeof Zepto != 'undefined') { + jasmine.Ajax.installZepto(); } else { - throw new Error("jasmine.Ajax currently only supports jQuery and Prototype"); + throw new Error("jasmine.Ajax currently only supports jQuery, Prototype and Zepto.js"); } jasmine.Ajax.installed = true; }, @@ -162,12 +164,20 @@ jasmine.Ajax = { Ajax.getTransport = jasmine.Ajax.prototypeMock; }, + installZepto: function() { + jasmine.Ajax.mode = 'Zepto.js'; + jasmine.Ajax.real = Zepto.ajaxSettings.xhr(); + Zepto.ajaxSettings.xhr = jasmine.Ajax.zeptoMock; + }, + uninstallMock: function() { jasmine.Ajax.assertInstalled(); if (jasmine.Ajax.mode == 'jQuery') { jQuery.ajaxSettings.xhr = jasmine.Ajax.real; } else if (jasmine.Ajax.mode == 'Prototype') { Ajax.getTransport = jasmine.Ajax.real; + } else if (jasmine.Ajax.mode == 'Zepto.js') { + Zepto.ajaxSettings.xhr = jasmine.Ajax.real; } jasmine.Ajax.reset(); }, @@ -188,6 +198,10 @@ jasmine.Ajax = { return new FakeXMLHttpRequest(); }, + zeptoMock: function() { + return new FakeXMLHttpRequest(); + }, + installed: false, mode: null } From fd81b811d9805a48b9f41e6cb064369661ad4589 Mon Sep 17 00:00:00 2001 From: Sasha Koss Date: Thu, 11 Aug 2011 12:27:21 +0700 Subject: [PATCH 09/20] Allow to specify xhr transport --- src/ajax.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ajax.js b/src/ajax.js index f0c96fcd8..accdb3cdc 100644 --- a/src/ajax.js +++ b/src/ajax.js @@ -59,6 +59,10 @@ error: empty, // Callback that is executed on request complete (both: error and success) complete: empty, + // Transport + xhr: function () { + return new window.XMLHttpRequest(); + }, // MIME types mapping accepts: { script: 'text/javascript, application/javascript', @@ -137,7 +141,7 @@ } var mime = settings.accepts[settings.dataType], - xhr = new XMLHttpRequest(); + xhr = $.ajaxSettings.xhr(); settings.headers = $.extend({'X-Requested-With': 'XMLHttpRequest'}, settings.headers || {}); if (mime) settings.headers['Accept'] = mime; From 0cee6aef46daa6f1f50696f4f89ed574f7b02542 Mon Sep 17 00:00:00 2001 From: Sasha Koss Date: Fri, 12 Aug 2011 00:53:53 +0700 Subject: [PATCH 10/20] Fix typo in mock-ajax helper --- spec/javascripts/helpers/mock-ajax.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/javascripts/helpers/mock-ajax.js b/spec/javascripts/helpers/mock-ajax.js index 15faebd97..51bc76b2c 100644 --- a/spec/javascripts/helpers/mock-ajax.js +++ b/spec/javascripts/helpers/mock-ajax.js @@ -166,7 +166,7 @@ jasmine.Ajax = { installZepto: function() { jasmine.Ajax.mode = 'Zepto.js'; - jasmine.Ajax.real = Zepto.ajaxSettings.xhr(); + jasmine.Ajax.real = Zepto.ajaxSettings.xhr; Zepto.ajaxSettings.xhr = jasmine.Ajax.zeptoMock; }, From 21976ef563f6ecbb768ac8015d981aa133f0085c Mon Sep 17 00:00:00 2001 From: Sasha Koss Date: Fri, 12 Aug 2011 01:10:46 +0700 Subject: [PATCH 11/20] Make mock-ajax for Zepto finally works --- spec/javascripts/helpers/mock-ajax.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/javascripts/helpers/mock-ajax.js b/spec/javascripts/helpers/mock-ajax.js index 51bc76b2c..1ba8c68b1 100644 --- a/spec/javascripts/helpers/mock-ajax.js +++ b/spec/javascripts/helpers/mock-ajax.js @@ -199,7 +199,9 @@ jasmine.Ajax = { }, zeptoMock: function() { - return new FakeXMLHttpRequest(); + var newXhr = new FakeXMLHttpRequest(); + ajaxRequests.push(newXhr); + return newXhr; }, installed: false, From 04af6bc5629e309e9ae0e32f5199b29ad974c8be Mon Sep 17 00:00:00 2001 From: Sasha Koss Date: Fri, 12 Aug 2011 08:50:53 +0700 Subject: [PATCH 12/20] Basic examples for all ajax functions --- spec/javascripts/ajax_spec.js | 46 +++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/spec/javascripts/ajax_spec.js b/spec/javascripts/ajax_spec.js index 66c716ed5..a3872775b 100644 --- a/spec/javascripts/ajax_spec.js +++ b/spec/javascripts/ajax_spec.js @@ -3,5 +3,51 @@ describe('Ajax', function () { it('should be defined', function () { expect($.ajax).toBeDefined(); }); + + it('should returns xhr object', function () { + expect($.ajax().constructor).toBe(XMLHttpRequest); + }); + }); + + describe('$.get', function () { + it('should be defined', function () { + expect($.get).toBeDefined(); + }); + }); + + describe('$.post', function () { + it('should be defined', function () { + expect($.post).toBeDefined(); + }); + }); + + describe('$.getJSON', function () { + it('should be defined', function () { + expect($.post).toBeDefined(); + }); + }); + + describe('$.load', function () { + it('should be defined', function () { + expect($.post).toBeDefined(); + }); + }); + + describe('$.param', function () { + it('should be defined', function () { + expect($.post).toBeDefined(); + }); + }); + + describe('$.ajaxSettings', function () { + it('should be defined', function () { + expect($.ajaxSettings).toBeDefined(); + }); + }); + + describe('$.ajaxJSONP', function () { + it('should be defined', function () { + expect($.ajaxJSONP).toBeDefined(); + }); }); }); From a51f8765e7f42b6cd20dab45febc0ebf16a6bb50 Mon Sep 17 00:00:00 2001 From: Sasha Koss Date: Sat, 3 Sep 2011 23:41:00 +0700 Subject: [PATCH 13/20] Added $.ajaxSettings specs --- spec/javascripts/ajax_spec.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/spec/javascripts/ajax_spec.js b/spec/javascripts/ajax_spec.js index a3872775b..c1f3183f9 100644 --- a/spec/javascripts/ajax_spec.js +++ b/spec/javascripts/ajax_spec.js @@ -43,6 +43,23 @@ describe('Ajax', function () { it('should be defined', function () { expect($.ajaxSettings).toBeDefined(); }); + + it('should contain default ajax settings', function () { + expect($.ajaxSettings.constructor).toBe(Object); + expect($.ajaxSettings.type).toBe('GET'); + expect($.ajaxSettings.beforeSend.constructor).toBe(Function); + expect($.ajaxSettings.success.constructor).toBe(Function); + expect($.ajaxSettings.error.constructor).toBe(Function); + expect($.ajaxSettings.complete.constructor).toBe(Function); + expect($.ajaxSettings.xhr().constructor).toBe(XMLHttpRequest); + expect($.ajaxSettings.accepts.constructor).toBe(Object); + expect($.ajaxSettings.accepts.script).toBe('text/javascript, application/javascript'); + expect($.ajaxSettings.accepts.json).toBe('application/json'); + expect($.ajaxSettings.accepts.xml).toBe('application/xml, text/xml'); + expect($.ajaxSettings.accepts.html).toBe('text/html'); + expect($.ajaxSettings.accepts.text).toBe('text/plain'); + expect($.ajaxSettings.timeout).toBe(0); + }); }); describe('$.ajaxJSONP', function () { From a8cc48fff0ad0aefa384928a080d3808ffef448a Mon Sep 17 00:00:00 2001 From: Sasha Koss Date: Sun, 4 Sep 2011 00:07:34 +0700 Subject: [PATCH 14/20] Added basic $.ajax specs --- spec/javascripts/ajax_spec.js | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/spec/javascripts/ajax_spec.js b/spec/javascripts/ajax_spec.js index c1f3183f9..751981e0e 100644 --- a/spec/javascripts/ajax_spec.js +++ b/spec/javascripts/ajax_spec.js @@ -1,4 +1,7 @@ describe('Ajax', function () { + + var onSuccess, onError, request; + describe('$.ajax', function () { it('should be defined', function () { expect($.ajax).toBeDefined(); @@ -7,8 +10,38 @@ describe('Ajax', function () { it('should returns xhr object', function () { expect($.ajax().constructor).toBe(XMLHttpRequest); }); + + describe('XHR request', function () { + + beforeEach(function () { + jasmine.Ajax.useMock(); + onSuccess = jasmine.createSpy('onSuccess'); + }); + + describe('on GET request', function () { + + beforeEach(function () { + $.ajax({ url: '/example/url', success: onSuccess }); + request = mostRecentAjaxRequest(); + request.response({ status: 200, responseText: 'Small is beautiful.' }); + }); + + it('should perform XHR request', function () { + expect(onSuccess).toHaveBeenCalled(); + }); + + it('should pass responseText, success string and xhr object', function () { + var args = onSuccess.mostRecentCall.args; + expect(args.length).toEqual(3); + expect(args[0]).toEqual('Small is beautiful.'); + expect(args[1]).toEqual('success'); + expect(args[2].constructor).toEqual(FakeXMLHttpRequest); + }); + }); + }); }); + describe('$.get', function () { it('should be defined', function () { expect($.get).toBeDefined(); From 777126b3ae50731d71c7105a0f39467974b09f7b Mon Sep 17 00:00:00 2001 From: Sasha Koss Date: Sun, 4 Sep 2011 00:44:20 +0700 Subject: [PATCH 15/20] Specs for $.ajax callbacks --- spec/javascripts/ajax_spec.js | 117 ++++++++++++++++++++++++++++++---- 1 file changed, 104 insertions(+), 13 deletions(-) diff --git a/spec/javascripts/ajax_spec.js b/spec/javascripts/ajax_spec.js index 751981e0e..d3be3b07b 100644 --- a/spec/javascripts/ajax_spec.js +++ b/spec/javascripts/ajax_spec.js @@ -1,6 +1,6 @@ describe('Ajax', function () { - var onSuccess, onError, request; + var beforeSend, onSuccess, onComplete, onError, settings, request, xhrResponse; describe('$.ajax', function () { it('should be defined', function () { @@ -13,35 +13,126 @@ describe('Ajax', function () { describe('XHR request', function () { + var performAjax = function () { + $.ajax(settings); + request = mostRecentAjaxRequest(); + request.response(xhrResponse); + }; + beforeEach(function () { jasmine.Ajax.useMock(); + beforeSend = jasmine.createSpy('beforeSend'); onSuccess = jasmine.createSpy('onSuccess'); + onError = jasmine.createSpy('onError'); + onComplete = jasmine.createSpy('onComplete'); }); describe('on GET request', function () { beforeEach(function () { - $.ajax({ url: '/example/url', success: onSuccess }); - request = mostRecentAjaxRequest(); - request.response({ status: 200, responseText: 'Small is beautiful.' }); + settings = { + url: '/example/url', + success: onSuccess, + error: onError, + complete: onComplete, + beforeSend: beforeSend + }; + + xhrResponse = { + status: 200, + responseText: 'Small is beautiful.' + }; }); - it('should perform XHR request', function () { - expect(onSuccess).toHaveBeenCalled(); + describe('beforeSend callback', function () { + + beforeEach(function () { + performAjax(); + }); + + it('should call beforeSend callback', function () { + expect(beforeSend).toHaveBeenCalled(); + }); + + it('should pass xhr and settings to beforeSend callback', function () { + var args = beforeSend.mostRecentCall.args; + expect(args.length).toEqual(2); + expect(args[0].constructor).toEqual(FakeXMLHttpRequest); + expect(args[1].constructor).toEqual(Object); + expect(args[1].beforeSend).toEqual(beforeSend); + }); }); - it('should pass responseText, success string and xhr object', function () { - var args = onSuccess.mostRecentCall.args; - expect(args.length).toEqual(3); - expect(args[0]).toEqual('Small is beautiful.'); - expect(args[1]).toEqual('success'); - expect(args[2].constructor).toEqual(FakeXMLHttpRequest); + describe('success callback', function () { + + beforeEach(function () { + performAjax(); + }); + + it('should call success callback', function () { + expect(onSuccess).toHaveBeenCalled(); + }); + + it('should pass responseText, success string and xhr object to success callback', function () { + var args = onSuccess.mostRecentCall.args; + expect(args.length).toEqual(3); + expect(args[0]).toEqual('Small is beautiful.'); + expect(args[1]).toEqual('success'); + expect(args[2].constructor).toEqual(FakeXMLHttpRequest); + }); + }); + + describe('error callback', function () { + + beforeEach(function () { + xhrResponse.status = 500; + performAjax(); + }); + + it('should call error callback', function () { + expect(onError).toHaveBeenCalled(); + }); + + it('should pass xhr and error string to error callback', function () { + var args = onError.mostRecentCall.args; + expect(args.length).toEqual(2); + expect(args[0].constructor).toEqual(FakeXMLHttpRequest); + expect(args[1]).toEqual('error'); + }); + }); + + describe('complete callback', function () { + + it('should call error callback on 200', function () { + performAjax(); + expect(onComplete).toHaveBeenCalled(); + }); + + it('should call error callback on 200', function () { + xhrResponse.status = 500; + performAjax(); + expect(onComplete).toHaveBeenCalled(); + }); + + it('should pass xhr and success string to complete callback', function () { + performAjax(); + var args = onComplete.mostRecentCall.args; + expect(args.length).toEqual(2); + expect(args[0].constructor).toEqual(FakeXMLHttpRequest); + expect(args[1]).toEqual('success'); + }); + + it('should pass xhr and error string to complete callback', function () { + xhrResponse.status = 500; + performAjax(); + var args = onComplete.mostRecentCall.args; + expect(args[1]).toEqual('error'); + }); }); }); }); }); - describe('$.get', function () { it('should be defined', function () { expect($.get).toBeDefined(); From aeb66809473181a2bb89bd97b94beb78ab58c496 Mon Sep 17 00:00:00 2001 From: Sasha Koss Date: Sun, 4 Sep 2011 00:54:22 +0700 Subject: [PATCH 16/20] Added specs for $.get --- spec/javascripts/ajax_spec.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/spec/javascripts/ajax_spec.js b/spec/javascripts/ajax_spec.js index d3be3b07b..42f86bca3 100644 --- a/spec/javascripts/ajax_spec.js +++ b/spec/javascripts/ajax_spec.js @@ -137,6 +137,23 @@ describe('Ajax', function () { it('should be defined', function () { expect($.get).toBeDefined(); }); + + it('should call $.ajax with url and success callback', function () { + var successCallback = function () {}; + + var zeptoAjax = $.ajax; + $.ajax = jasmine.createSpy('$.ajax'); + + $.get('/example/url', successCallback); + + var options = $.ajax.mostRecentCall.args[0]; + + expect(options.constructor).toBe(Object); + expect(options.url).toBe('/example/url'); + expect(options.success).toBe(successCallback); + + $.ajax = zeptoAjax; + }); }); describe('$.post', function () { From bbf0c2f78f01f97eed412c2b7347a35fcb0da931 Mon Sep 17 00:00:00 2001 From: Sasha Koss Date: Sun, 4 Sep 2011 01:10:01 +0700 Subject: [PATCH 17/20] Bundle jasmine-headless-webkit gem --- Gemfile | 1 + Gemfile.lock | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/Gemfile b/Gemfile index 9853f0fdc..46a90d061 100644 --- a/Gemfile +++ b/Gemfile @@ -2,3 +2,4 @@ source :rubygems gem 'rake' gem 'jasmine' +gem 'jasmine-headless-webkit' diff --git a/Gemfile.lock b/Gemfile.lock index 7ad85c730..e11ccd783 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,15 +3,29 @@ GEM specs: childprocess (0.2.0) ffi (~> 1.0.6) + coffee-script (2.2.0) + coffee-script-source + execjs + coffee-script-source (1.1.2) diff-lcs (1.1.2) + execjs (1.2.4) + multi_json (~> 1.0) ffi (1.0.9) jasmine (1.0.2.1) json_pure (>= 1.4.3) rack (>= 1.1) rspec (>= 1.3.1) selenium-webdriver (>= 0.1.3) + jasmine-core (1.1.0.rc4) + jasmine-headless-webkit (0.6.3) + coffee-script (>= 2.2) + jasmine-core (~> 1.1.beta) + multi_json + rainbow json_pure (1.5.3) + multi_json (1.0.3) rack (1.3.2) + rainbow (1.1.1) rake (0.8.7) rspec (2.6.0) rspec-core (~> 2.6.0) @@ -33,4 +47,5 @@ PLATFORMS DEPENDENCIES jasmine + jasmine-headless-webkit rake From a3305286d6743ebad7ff6fd5fd673c68b6320546 Mon Sep 17 00:00:00 2001 From: Sasha Koss Date: Sun, 4 Sep 2011 01:12:30 +0700 Subject: [PATCH 18/20] jasmine-headless-webkit won't work without setting options in jasmine.yml --- spec/javascripts/support/jasmine.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/javascripts/support/jasmine.yml b/spec/javascripts/support/jasmine.yml index e72c3878b..740e917b8 100644 --- a/spec/javascripts/support/jasmine.yml +++ b/spec/javascripts/support/jasmine.yml @@ -38,6 +38,7 @@ stylesheets: # - helpers/**/*.js # helpers: + - helpers/**/*.js # spec_files # @@ -50,6 +51,7 @@ helpers: # - **/*[sS]pec.js # spec_files: + - **/*[sS]pec.js # src_dir # @@ -71,4 +73,4 @@ src_dir: src # # spec_dir: spec/javascripts # -spec_dir: +spec_dir: spec/javascripts From 4edac6fa92b4021cf7102b272ce84ddbbbef2496 Mon Sep 17 00:00:00 2001 From: Sasha Koss Date: Sun, 4 Sep 2011 01:14:48 +0700 Subject: [PATCH 19/20] Clean up jasmine.yml file --- spec/javascripts/support/jasmine.yml | 71 ++-------------------------- 1 file changed, 5 insertions(+), 66 deletions(-) diff --git a/spec/javascripts/support/jasmine.yml b/spec/javascripts/support/jasmine.yml index 740e917b8..254cc29bb 100644 --- a/spec/javascripts/support/jasmine.yml +++ b/spec/javascripts/support/jasmine.yml @@ -1,76 +1,15 @@ -# src_files -# -# Return an array of filepaths relative to src_dir to include before jasmine specs. -# Default: [] -# -# EXAMPLE: -# -# src_files: -# - lib/source1.js -# - lib/source2.js -# - dist/**/*.js -# +src_dir: src + src_files: - zepto.js - *.js -# stylesheets -# -# Return an array of stylesheet filepaths relative to src_dir to include before jasmine specs. -# Default: [] -# -# EXAMPLE: -# -# stylesheets: -# - css/style.css -# - stylesheets/*.css -# -stylesheets: - -# helpers -# -# Return an array of filepaths relative to spec_dir to include before jasmine specs. -# Default: ["helpers/**/*.js"] -# -# EXAMPLE: -# -# helpers: -# - helpers/**/*.js -# helpers: - helpers/**/*.js -# spec_files -# -# Return an array of filepaths relative to spec_dir to include. -# Default: ["**/*[sS]pec.js"] -# -# EXAMPLE: -# -# spec_files: -# - **/*[sS]pec.js -# +spec_dir: spec/javascripts + spec_files: - **/*[sS]pec.js -# src_dir -# -# Source directory path. Your src_files must be returned relative to this path. Will use root if left blank. -# Default: project root -# -# EXAMPLE: -# -# src_dir: public -# -src_dir: src - -# spec_dir -# -# Spec directory path. Your spec_files must be returned relative to this path. -# Default: spec/javascripts -# -# EXAMPLE: -# -# spec_dir: spec/javascripts -# -spec_dir: spec/javascripts +stylesheets: From f47e446d3c54b044fe7df6ccf80a3c08279d257c Mon Sep 17 00:00:00 2001 From: Sasha Koss Date: Sun, 4 Sep 2011 03:09:03 +0700 Subject: [PATCH 20/20] Specs now works in browser and headless-webkit --- spec/javascripts/ajax_spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/javascripts/ajax_spec.js b/spec/javascripts/ajax_spec.js index 42f86bca3..73d02a0e5 100644 --- a/spec/javascripts/ajax_spec.js +++ b/spec/javascripts/ajax_spec.js @@ -8,7 +8,7 @@ describe('Ajax', function () { }); it('should returns xhr object', function () { - expect($.ajax().constructor).toBe(XMLHttpRequest); + expect($.ajax().onreadystatechange).toBeDefined(); }); describe('XHR request', function () { @@ -192,7 +192,7 @@ describe('Ajax', function () { expect($.ajaxSettings.success.constructor).toBe(Function); expect($.ajaxSettings.error.constructor).toBe(Function); expect($.ajaxSettings.complete.constructor).toBe(Function); - expect($.ajaxSettings.xhr().constructor).toBe(XMLHttpRequest); + expect($.ajaxSettings.xhr().onreadystatechange).toBeDefined(); expect($.ajaxSettings.accepts.constructor).toBe(Object); expect($.ajaxSettings.accepts.script).toBe('text/javascript, application/javascript'); expect($.ajaxSettings.accepts.json).toBe('application/json');