diff --git a/spec/javascripts/helpers/SpecHelper.js b/spec/javascripts/helpers/SpecHelper.js index a58a1b6d7efaf20ac2a86025ad167725beb03cb8..92a22e0bbbc7bc94e90b05ff416ccf3f37a1b4f4 100644 --- a/spec/javascripts/helpers/SpecHelper.js +++ b/spec/javascripts/helpers/SpecHelper.js @@ -1,18 +1,10 @@ -// Add custom matchers here, in a beforeEach block. Example: -//beforeEach(function() { -// this.addMatchers({ -// toBePlaying: function(expectedSong) { -// var player = this.actual; -// return player.currentlyPlayingSong === expectedSong -// && player.isPlaying; -// } -// }) -//}); +// for docs, see http://jasmine.github.io beforeEach(function() { $('#jasmine_content').html(spec.readFixture("underscore_templates")); - jasmine.Clock.useMock(); + jasmine.clock().install(); + jasmine.Ajax.install(); Diaspora.Pages.TestPage = function() { var self = this; @@ -29,43 +21,56 @@ beforeEach(function() { Diaspora.page = new Page(); Diaspora.page.publish("page/ready", [$(document.body)]); - - // matches flash messages with success/error and contained text - var flashMatcher = function(flash, id, text) { - textContained = true; - if( text ) { - textContained = (flash.text().indexOf(text) !== -1); - } - - return flash.is(id) && - flash.hasClass('expose') && - textContained; - }; - // add custom matchers for flash messages - this.addMatchers({ - toBeSuccessFlashMessage: function(containedText) { - var flash = this.actual; - return flashMatcher(flash, '#flash_notice', containedText); - }, - - toBeErrorFlashMessage: function(containedText) { - var flash = this.actual; - return flashMatcher(flash, '#flash_error', containedText); - } - }); - + jasmine.addMatchers(customMatchers); }); afterEach(function() { //spec.clearLiveEventBindings(); + + jasmine.clock().uninstall(); + jasmine.Ajax.uninstall(); + $("#jasmine_content").empty() expect(spec.loadFixtureCount).toBeLessThan(2); spec.loadFixtureCount = 0; }); + +// matches flash messages with success/error and contained text +var flashMatcher = function(flash, id, text) { + textContained = true; + if( text ) { + textContained = (flash.text().indexOf(text) !== -1); + } + + return flash.is(id) && + flash.hasClass('expose') && + textContained; +}; + var context = describe; var spec = {}; +var customMatchers = { + toBeSuccessFlashMessage: function(util) { + return { + compare: function(actual, expected) { + var result = {}; + result.pass = flashMatcher(actual, '#flash_notice', expected); + return result; + } + }; + }, + toBeErrorFlashMessage: function(util) { + return { + compare: function(actual, expected) { + var result = {}; + result.pass = flashMatcher(actual, '#flash_error', expected); + return result; + } + }; + } +}; window.stubView = function stubView(text){ var stubClass = Backbone.View.extend({ @@ -159,7 +164,7 @@ spec.retrieveFixture = function(fixtureName) { // retrieve the fixture markup via xhr request to jasmine server try { - xhr = new jasmine.XmlHttpRequest(); + xhr = new XMLHttpRequest(); xhr.open("GET", path, false); xhr.send(null); } catch(e) { diff --git a/spec/javascripts/helpers/mock-ajax.js b/spec/javascripts/helpers/mock-ajax.js index 249efc9070877d14070aecd0717c2d4f9a25bbe9..9bd8c3f5449c8dbb7428c9ed8b113ddca70770be 100644 --- a/spec/javascripts/helpers/mock-ajax.js +++ b/spec/javascripts/helpers/mock-ajax.js @@ -1,207 +1,283 @@ /* - Jasmine-Ajax : a set of helpers for testing AJAX requests under the Jasmine - BDD framework for JavaScript. - Supports both Prototype.js and jQuery. +Jasmine-Ajax : a set of helpers for testing AJAX requests under the Jasmine +BDD framework for JavaScript. - http://github.com/pivotal/jasmine-ajax +http://github.com/pivotal/jasmine-ajax - Jasmine Home page: http://pivotal.github.com/jasmine +Jasmine Home page: http://pivotal.github.com/jasmine - Copyright (c) 2008-2010 Pivotal Labs +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: +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 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. +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() { + function extend(destination, source) { + for (var property in source) { + destination[property] = source[property]; + } + return destination; } -} - -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, + 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(); + } + }; - onreadystatechange: function(isTimeout) { - }, + this.requests = requestTracker; + this.stubs = stubTracker; + } - status: null, + function StubTracker() { + var stubs = []; - send: function(data) { - this.params = data; - this.readyState = 2; - }, + this.addStub = function(stub) { + stubs.push(stub); + }; - getResponseHeader: function(name) { - return this.responseHeaders[name]; - }, + this.reset = function() { + stubs = []; + }; - getAllResponseHeaders: function() { - var responseHeaders = []; - for (var i in this.responseHeaders) { - if (this.responseHeaders.hasOwnProperty(i)) { - responseHeaders.push(i + ': ' + this.responseHeaders[i]); + 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; } } - 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'); + }; + } + + function fakeRequest(requestTracker, stubTracker) { + function FakeXMLHttpRequest() { + requestTracker.track(this); + this.requestHeaders = {}; } - }); - return this; -} + 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(isTimeout) { + }, + + 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; + } -jasmine.Ajax = { + 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]); + } + } + } - isInstalled: function() { - return jasmine.Ajax.installed == true; - }, + return matching_requests; + }; + } - assertInstalled: function() { - if (!jasmine.Ajax.isInstalled()) { - throw new Error("Mock ajax is not installed, use jasmine.Ajax.useMock()") - } - }, + function RequestStub(url, stubData) { + var split = url.split('?'); + this.url = split[0]; - useMock: function() { - if (!jasmine.Ajax.isInstalled()) { - var spec = jasmine.getEnv().currentSpec; - spec.after(jasmine.Ajax.uninstallMock); + var normalizeQuery = function(query) { + return query ? query.split('&').sort().join('&') : undefined; + }; - 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; - }, + this.query = normalizeQuery(split[1]); + this.data = normalizeQuery(stubData); - installJquery: function() { - jasmine.Ajax.mode = 'jQuery'; - jasmine.Ajax.real = jQuery.ajaxSettings.xhr; - jQuery.ajaxSettings.xhr = jasmine.Ajax.jQueryMock; + this.andReturn = function(options) { + this.status = options.status || 200; - }, + this.contentType = options.contentType; + this.responseText = options.responseText; + }; - installPrototype: function() { - jasmine.Ajax.mode = 'Prototype'; - jasmine.Ajax.real = Ajax.getTransport; + 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)); + }; + } - Ajax.getTransport = jasmine.Ajax.prototypeMock; - }, + if (typeof window === "undefined" && typeof exports === "object") { + exports.MockAjax = MockAjax; + jasmine.Ajax = new MockAjax(exports); + } else { + window.MockAjax = MockAjax; + jasmine.Ajax = new MockAjax(window); + } +}()); - 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); - }; -} \ No newline at end of file