diff --git a/Gemfile b/Gemfile index b04ae88b1fb..d92563cd145 100644 --- a/Gemfile +++ b/Gemfile @@ -262,7 +262,8 @@ group :development, :test do gem 'cucumber-rails', '1.4.2', :require => false # Jasmine (client side application tests (JS)) - gem 'jasmine', '2.2.0' - gem 'jasmine-jquery-rails', '2.0.3' - gem 'sinon-rails', '1.10.3' + gem 'jasmine', '2.2.0' + gem 'jasmine-jquery-rails', '2.0.3' + gem 'rails-assets-jasmine-ajax', '3.0.0' + gem 'sinon-rails', '1.10.3' end diff --git a/Gemfile.lock b/Gemfile.lock index c828e38c1f0..5c0dd6db84f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -429,6 +429,9 @@ GEM rails-assets-jquery-fullscreen (~> 1.1.4) rails-assets-jquery-ui (~> 1.10.4) rails-assets-jquery.slimscroll (~> 1.3.3) + rails-assets-jasmine (2.2.1) + rails-assets-jasmine-ajax (3.0.0) + rails-assets-jasmine (~> 2.0) rails-assets-jeresig--jquery.hotkeys (0.2.0) rails-assets-jquery (>= 1.4.2) rails-assets-jquery (1.11.1) @@ -693,6 +696,7 @@ DEPENDENCIES rack-ssl (= 1.4.1) rails (= 4.1.8) rails-assets-diaspora_jsxc (~> 0.0.9) + rails-assets-jasmine-ajax (= 3.0.0) rails-assets-jeresig--jquery.hotkeys (= 0.2.0) rails-assets-jquery (= 1.11.1) rails-assets-jquery-idletimer (= 1.0.1) diff --git a/app/assets/javascripts/jasmine-load-all.js b/app/assets/javascripts/jasmine-load-all.js index d6d42ecf1c6..e13420cc951 100644 --- a/app/assets/javascripts/jasmine-load-all.js +++ b/app/assets/javascripts/jasmine-load-all.js @@ -8,3 +8,4 @@ //= require profile //= require contact-list //= require sinon +//= require jasmine-ajax diff --git a/spec/controllers/jasmine_fixtures/photos_spec.rb b/spec/controllers/jasmine_fixtures/photos_spec.rb new file mode 100644 index 00000000000..79e6868ee54 --- /dev/null +++ b/spec/controllers/jasmine_fixtures/photos_spec.rb @@ -0,0 +1,20 @@ +# Copyright (c) 2010-2011, Diaspora Inc. This file is +# licensed under the Affero General Public License version 3 or later. See +# the COPYRIGHT file. + +require 'spec_helper' + +describe PhotosController, :type => :controller do + before do + @alices_photo = alice.post(:photo, :user_file => uploaded_photo, :to => alice.aspects.first.id, :public => false) + sign_in :user, alice + end + + describe '#index' do + it "generates a jasmine fixture", :fixture => true do + request.env['HTTP_ACCEPT'] = 'application/json' + get :index, :person_id => alice.person.guid.to_s + save_fixture(response.body, "photos_json") + end + end +end diff --git a/spec/controllers/photos_controller_spec.rb b/spec/controllers/photos_controller_spec.rb index a2316b1b593..24bdf5354cb 100644 --- a/spec/controllers/photos_controller_spec.rb +++ b/spec/controllers/photos_controller_spec.rb @@ -99,7 +99,6 @@ get :index, :person_id => alice.person.guid.to_s expect(response.headers['Content-Type']).to match 'application/json.*' - save_fixture(response.body, "photos_json") end it 'displays by date of creation' do diff --git a/spec/javascripts/app/views/aspect_membership_blueprint_view_spec.js b/spec/javascripts/app/views/aspect_membership_blueprint_view_spec.js index 1fdeed070b5..d48612324d4 100644 --- a/spec/javascripts/app/views/aspect_membership_blueprint_view_spec.js +++ b/spec/javascripts/app/views/aspect_membership_blueprint_view_spec.js @@ -26,7 +26,7 @@ describe("app.views.AspectMembershipBlueprint", function(){ it('marks the aspect as selected', function() { this.newAspect.trigger('click'); - jasmine.Ajax.requests.mostRecent().response(resp_success); + jasmine.Ajax.requests.mostRecent().respondWith(resp_success); expect(this.newAspect.attr('class')).toContain('selected'); }); @@ -34,7 +34,7 @@ describe("app.views.AspectMembershipBlueprint", function(){ it('displays flash message when added to first aspect', function() { spec.content().find('li').removeClass('selected'); this.newAspect.trigger('click'); - jasmine.Ajax.requests.mostRecent().response(resp_success); + jasmine.Ajax.requests.mostRecent().respondWith(resp_success); expect($('[id^="flash"]')).toBeSuccessFlashMessage( Diaspora.I18n.t('aspect_dropdown.started_sharing_with', {name: this.person_name}) @@ -43,7 +43,7 @@ describe("app.views.AspectMembershipBlueprint", function(){ it('displays an error when it fails', function() { this.newAspect.trigger('click'); - jasmine.Ajax.requests.mostRecent().response(resp_fail); + jasmine.Ajax.requests.mostRecent().respondWith(resp_fail); expect($('[id^="flash"]')).toBeErrorFlashMessage( Diaspora.I18n.t('aspect_dropdown.error', {name: this.person_name}) @@ -59,7 +59,7 @@ describe("app.views.AspectMembershipBlueprint", function(){ it('marks the aspect as unselected', function(){ this.oldAspect.trigger('click'); - jasmine.Ajax.requests.mostRecent().response(resp_success); + jasmine.Ajax.requests.mostRecent().respondWith(resp_success); expect(this.oldAspect.attr('class')).not.toContain('selected'); }); @@ -67,7 +67,7 @@ describe("app.views.AspectMembershipBlueprint", function(){ it('displays a flash message when removed from last aspect', function() { spec.content().find('li.selected:last').removeClass('selected'); this.oldAspect.trigger('click'); - jasmine.Ajax.requests.mostRecent().response(resp_success); + jasmine.Ajax.requests.mostRecent().respondWith(resp_success); expect($('[id^="flash"]')).toBeSuccessFlashMessage( Diaspora.I18n.t('aspect_dropdown.stopped_sharing_with', {name: this.person_name}) @@ -76,7 +76,7 @@ describe("app.views.AspectMembershipBlueprint", function(){ it('displays an error when it fails', function() { this.oldAspect.trigger('click'); - jasmine.Ajax.requests.mostRecent().response(resp_fail); + jasmine.Ajax.requests.mostRecent().respondWith(resp_fail); expect($('[id^="flash"]')).toBeErrorFlashMessage( Diaspora.I18n.t('aspect_dropdown.error_remove', {name: this.person_name}) diff --git a/spec/javascripts/app/views/aspect_membership_view_spec.js b/spec/javascripts/app/views/aspect_membership_view_spec.js index 42769cf15d8..18c5b3bec7b 100644 --- a/spec/javascripts/app/views/aspect_membership_view_spec.js +++ b/spec/javascripts/app/views/aspect_membership_view_spec.js @@ -26,7 +26,7 @@ describe("app.views.AspectMembership", function(){ it('marks the aspect as selected', function() { this.newAspect.trigger('click'); - jasmine.Ajax.requests.mostRecent().response(resp_success); + jasmine.Ajax.requests.mostRecent().respondWith(resp_success); expect(this.newAspect.attr('class')).toContain('selected'); }); @@ -34,7 +34,7 @@ describe("app.views.AspectMembership", function(){ it('displays flash message when added to first aspect', function() { spec.content().find('li').removeClass('selected'); this.newAspect.trigger('click'); - jasmine.Ajax.requests.mostRecent().response(resp_success); + jasmine.Ajax.requests.mostRecent().respondWith(resp_success); expect($('[id^="flash"]')).toBeSuccessFlashMessage( Diaspora.I18n.t('aspect_dropdown.started_sharing_with', {name: this.person_name}) @@ -43,7 +43,7 @@ describe("app.views.AspectMembership", function(){ it('displays an error when it fails', function() { this.newAspect.trigger('click'); - jasmine.Ajax.requests.mostRecent().response(resp_fail); + jasmine.Ajax.requests.mostRecent().respondWith(resp_fail); expect($('[id^="flash"]')).toBeErrorFlashMessage( Diaspora.I18n.t('aspect_dropdown.error', {name: this.person_name}) @@ -59,7 +59,7 @@ describe("app.views.AspectMembership", function(){ it('marks the aspect as unselected', function(){ this.oldAspect.trigger('click'); - jasmine.Ajax.requests.mostRecent().response(resp_success); + jasmine.Ajax.requests.mostRecent().respondWith(resp_success); expect(this.oldAspect.attr('class')).not.toContain('selected'); }); @@ -67,7 +67,7 @@ describe("app.views.AspectMembership", function(){ it('displays a flash message when removed from last aspect', function() { spec.content().find('li.selected:last').removeClass('selected'); this.oldAspect.trigger('click'); - jasmine.Ajax.requests.mostRecent().response(resp_success); + jasmine.Ajax.requests.mostRecent().respondWith(resp_success); expect($('[id^="flash"]')).toBeSuccessFlashMessage( Diaspora.I18n.t('aspect_dropdown.stopped_sharing_with', {name: this.person_name}) @@ -76,7 +76,7 @@ describe("app.views.AspectMembership", function(){ it('displays an error when it fails', function() { this.oldAspect.trigger('click'); - jasmine.Ajax.requests.mostRecent().response(resp_fail); + jasmine.Ajax.requests.mostRecent().respondWith(resp_fail); expect($('[id^="flash"]')).toBeErrorFlashMessage( Diaspora.I18n.t('aspect_dropdown.error_remove', {name: this.person_name}) diff --git a/spec/javascripts/app/views/bookmarklet_view_spec.js b/spec/javascripts/app/views/bookmarklet_view_spec.js index 42c36340763..57296ad3b69 100644 --- a/spec/javascripts/app/views/bookmarklet_view_spec.js +++ b/spec/javascripts/app/views/bookmarklet_view_spec.js @@ -54,7 +54,7 @@ describe('app.views.Bookmarklet', function() { init_bookmarklet(test_data); spec.content().find('form').submit(); - jasmine.Ajax.requests.mostRecent().response({ + jasmine.Ajax.requests.mostRecent().respondWith({ status: 200, // success! responseText: "{}" }); diff --git a/spec/javascripts/app/views/comment_stream_view_spec.js b/spec/javascripts/app/views/comment_stream_view_spec.js index c5cf4cce68a..c9abb16f93d 100644 --- a/spec/javascripts/app/views/comment_stream_view_spec.js +++ b/spec/javascripts/app/views/comment_stream_view_spec.js @@ -44,21 +44,19 @@ describe("app.views.CommentStream", function(){ }); it("fires an AJAX request", function() { - var params = JSON.parse(this.request.params); - // TODO: use this, once jasmine-ajax is updated to latest version - //params = this.request.data(); + var params = this.request.data(); expect(params.text).toEqual("a new comment"); }); it("adds the comment to the view", function() { - this.request.response({status: 200, responseText: '[]'}); + this.request.respondWith({status: 200, responseText: '[]'}); expect(this.view.$(".comment-content p").text()).toEqual("a new comment"); }); it("doesn't add the comment to the view, when the request fails", function(){ Diaspora.I18n.load({failed_to_post_message: "posting failed!"}); - this.request.response({status: 500}); + this.request.respondWith({status: 500}); expect(this.view.$(".comment-content p").text()).not.toEqual("a new comment"); expect($('*[id^="flash"]')).toBeErrorFlashMessage("posting failed!"); @@ -93,7 +91,7 @@ describe("app.views.CommentStream", function(){ this.view.$("textarea").val("great post!"); this.view.expandComments(); - jasmine.Ajax.requests.mostRecent().response({ comments : [] }); + jasmine.Ajax.requests.mostRecent().respondWith({ comments : [] }); expect(this.view.$("textarea").val()).toEqual("great post!"); }); diff --git a/spec/javascripts/app/views/contact_view_spec.js b/spec/javascripts/app/views/contact_view_spec.js index 0dee502e938..b1d400ed16a 100644 --- a/spec/javascripts/app/views/contact_view_spec.js +++ b/spec/javascripts/app/views/contact_view_spec.js @@ -50,7 +50,7 @@ describe("app.views.Contact", function(){ it('adds a aspect_membership to the contact', function() { expect(this.model.aspect_memberships.length).toBe(1); $('.contact_add-to-aspect',this.contact).trigger('click'); - jasmine.Ajax.requests.mostRecent().response({ + jasmine.Ajax.requests.mostRecent().respondWith({ status: 200, // success responseText: this.response }); @@ -60,7 +60,7 @@ describe("app.views.Contact", function(){ it('calls render', function() { spyOn(this.view, 'render'); $('.contact_add-to-aspect',this.contact).trigger('click'); - jasmine.Ajax.requests.mostRecent().response({ + jasmine.Ajax.requests.mostRecent().respondWith({ status: 200, // success responseText: this.response }); @@ -70,7 +70,7 @@ describe("app.views.Contact", function(){ it('displays a flash message on errors', function(){ $('.contact_add-to-aspect',this.contact).trigger('click'); - jasmine.Ajax.requests.mostRecent().response({ + jasmine.Ajax.requests.mostRecent().respondWith({ status: 400, // fail }); expect($('[id^="flash"]')).toBeErrorFlashMessage( @@ -102,7 +102,7 @@ describe("app.views.Contact", function(){ it('removes the aspect_membership from the contact', function() { expect(this.model.aspect_memberships.length).toBe(1); $('.contact_remove-from-aspect',this.contact).trigger('click'); - jasmine.Ajax.requests.mostRecent().response({ + jasmine.Ajax.requests.mostRecent().respondWith({ status: 200, // success responseText: this.response }); @@ -112,7 +112,7 @@ describe("app.views.Contact", function(){ it('calls render', function() { spyOn(this.view, 'render'); $('.contact_remove-from-aspect',this.contact).trigger('click'); - jasmine.Ajax.requests.mostRecent().response({ + jasmine.Ajax.requests.mostRecent().respondWith({ status: 200, // success responseText: this.response, }); @@ -121,7 +121,7 @@ describe("app.views.Contact", function(){ it('displays a flash message on errors', function(){ $('.contact_remove-from-aspect',this.contact).trigger('click'); - jasmine.Ajax.requests.mostRecent().response({ + jasmine.Ajax.requests.mostRecent().respondWith({ status: 400, // fail }); expect($('[id^="flash"]')).toBeErrorFlashMessage( diff --git a/spec/javascripts/helpers/mock-ajax.js b/spec/javascripts/helpers/mock-ajax.js deleted file mode 100644 index 640287f8ebb..00000000000 --- a/spec/javascripts/helpers/mock-ajax.js +++ /dev/null @@ -1,285 +0,0 @@ -/* - -Jasmine-Ajax : a set of helpers for testing AJAX requests under the Jasmine -BDD framework for JavaScript. - -http://github.com/pivotal/jasmine-ajax - -Jasmine Home page: http://pivotal.github.com/jasmine - -Copyright (c) 2008-2013 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. - -*/ - -(function() { - function extend(destination, source) { - for (var property in source) { - if(source.hasOwnProperty(property)) { - destination[property] = source[property]; - } - } - return destination; - } - - function MockAjax(global) { - var requestTracker = new RequestTracker(), - stubTracker = new StubTracker(), - realAjaxFunction = global.XMLHttpRequest, - mockAjaxFunction = fakeRequest(requestTracker, stubTracker); - - this.install = function() { - global.XMLHttpRequest = mockAjaxFunction; - }; - - this.uninstall = function() { - global.XMLHttpRequest = realAjaxFunction; - - this.stubs.reset(); - this.requests.reset(); - }; - - this.stubRequest = function(url, data) { - var stub = new RequestStub(url, data); - stubTracker.addStub(stub); - return stub; - }; - - this.withMock = function(closure) { - this.install(); - try { - closure(); - } finally { - this.uninstall(); - } - }; - - this.requests = requestTracker; - this.stubs = stubTracker; - } - - function StubTracker() { - var stubs = []; - - this.addStub = function(stub) { - stubs.push(stub); - }; - - this.reset = function() { - stubs = []; - }; - - this.findStub = function(url, data) { - for (var i = stubs.length - 1; i >= 0; i--) { - var stub = stubs[i]; - if (stub.matches(url, data)) { - return stub; - } - } - }; - } - - function fakeRequest(requestTracker, stubTracker) { - function FakeXMLHttpRequest() { - requestTracker.track(this); - this.requestHeaders = {}; - } - - extend(FakeXMLHttpRequest.prototype, window.XMLHttpRequest); - extend(FakeXMLHttpRequest.prototype, { - open: function() { - this.method = arguments[0]; - this.url = arguments[1]; - this.username = arguments[3]; - this.password = arguments[4]; - this.readyState = 1; - this.onreadystatechange(); - }, - - setRequestHeader: function(header, value) { - this.requestHeaders[header] = value; - }, - - abort: function() { - this.readyState = 0; - this.status = 0; - this.statusText = "abort"; - this.onreadystatechange(); - }, - - readyState: 0, - - onload: function() { - }, - - onreadystatechange: function() { - }, - - status: null, - - send: function(data) { - this.params = data; - this.readyState = 2; - this.onreadystatechange(); - - var stub = stubTracker.findStub(this.url, data); - if (stub) { - this.response(stub); - } - }, - - data: function() { - var data = {}; - if (typeof this.params !== 'string') { return data; } - var params = this.params.split('&'); - - for (var i = 0; i < params.length; ++i) { - var kv = params[i].replace(/\+/g, ' ').split('='); - var key = decodeURIComponent(kv[0]); - data[key] = data[key] || []; - data[key].push(decodeURIComponent(kv[1])); - data[key].sort(); - } - return data; - }, - - 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.statusText = response.statusText || ""; - this.responseText = response.responseText || ""; - this.readyState = 4; - this.responseHeaders = response.responseHeaders || - {"Content-type": response.contentType || "application/json" }; - - this.onload(); - this.onreadystatechange(); - }, - - responseTimeout: function() { - this.readyState = 4; - jasmine.clock().tick(30000); - this.onreadystatechange('timeout'); - } - }); - - return FakeXMLHttpRequest; - } - - function RequestTracker() { - var requests = []; - - this.track = function(request) { - requests.push(request); - }; - - this.first = function() { - return requests[0]; - }; - - this.count = function() { - return requests.length; - }; - - this.reset = function() { - requests = []; - }; - - this.mostRecent = function() { - return requests[requests.length - 1]; - }; - - this.at = function(index) { - return requests[index]; - }; - - this.filter = function(url_to_match) { - if (requests.length === 0) return []; - var matching_requests = []; - - for (var i = 0; i < requests.length; i++) { - if (url_to_match instanceof RegExp && - url_to_match.test(requests[i].url)) { - matching_requests.push(requests[i]); - } else if (url_to_match instanceof Function && - url_to_match(requests[i])) { - matching_requests.push(requests[i]); - } else { - if (requests[i].url === url_to_match) { - matching_requests.push(requests[i]); - } - } - } - - return matching_requests; - }; - } - - function RequestStub(url, stubData) { - var split = url.split('?'); - this.url = split[0]; - - var normalizeQuery = function(query) { - return query ? query.split('&').sort().join('&') : undefined; - }; - - this.query = normalizeQuery(split[1]); - this.data = normalizeQuery(stubData); - - this.andReturn = function(options) { - this.status = options.status || 200; - - this.contentType = options.contentType; - this.responseText = options.responseText; - }; - - this.matches = function(fullUrl, data) { - var urlSplit = fullUrl.split('?'), - url = urlSplit[0], - query = urlSplit[1]; - return this.url === url && this.query === normalizeQuery(query) && (!this.data || this.data === normalizeQuery(data)); - }; - } - - if (typeof window === "undefined" && typeof exports === "object") { - exports.MockAjax = MockAjax; - jasmine.Ajax = new MockAjax(exports); - } else { - window.MockAjax = MockAjax; - jasmine.Ajax = new MockAjax(window); - } -}()); -