crusade_rails 0.8.0 → 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/crusade_rails/abstract_notification.js.coffee +38 -0
  3. data/app/assets/javascripts/crusade_rails/configuration.js.coffee.erb +2 -1
  4. data/app/assets/javascripts/crusade_rails/local_notification.js.coffee +23 -0
  5. data/app/assets/javascripts/crusade_rails/notification_permission.js.coffee +3 -1
  6. data/app/assets/javascripts/crusade_rails/push_notification.coffee +42 -0
  7. data/app/controllers/crusade_rails/apns/v1/push_package_controller.rb +34 -3
  8. data/app/controllers/crusade_rails/apns/v1/subscription_controller.rb +5 -1
  9. data/app/controllers/crusade_rails/application_controller.rb +2 -2
  10. data/app/services/crusade_rails/user_subscription_service.rb +41 -7
  11. data/config/routes.rb +2 -0
  12. data/db/migrate/20130917203302_create_crusade_rails_user_subscriptions.rb +1 -0
  13. data/lib/crusade_rails.rb +1 -0
  14. data/lib/crusade_rails/configuration.rb +45 -0
  15. data/lib/crusade_rails/version.rb +1 -1
  16. data/lib/generators/crusade/install/templates/initializer.rb +3 -3
  17. data/test/controllers/apns/v1/push_package_controller_test.rb +39 -7
  18. data/test/controllers/apns/v1/subscription_controller_test.rb +3 -1
  19. data/test/dummy/config/initializers/crusade.rb +3 -3
  20. data/test/dummy/db/development.sqlite3 +0 -0
  21. data/test/dummy/db/migrate/{20130922190000_create_crusade_rails_user_subscriptions.crusade_rails.rb → 20130925062705_create_crusade_rails_user_subscriptions.crusade_rails.rb} +1 -0
  22. data/test/dummy/db/schema.rb +2 -1
  23. data/test/dummy/db/test.sqlite3 +0 -0
  24. data/test/dummy/log/development.log +7 -470
  25. data/test/dummy/log/test.log +36862 -3832
  26. data/test/integration/configuration_test.rb +29 -0
  27. data/test/integration/push_package_integration_test.rb +9 -2
  28. data/test/javascripts/support/configuration.js.coffee +4 -4
  29. data/test/javascripts/support/disable_logger.coffee +1 -0
  30. data/test/javascripts/support/fake_notification.js.coffee +4 -0
  31. data/test/javascripts/support/jquery.mockajax.js +587 -0
  32. data/test/javascripts/support/safari.js.coffee +6 -1
  33. data/test/javascripts/unit/configuration_test.js.coffee +5 -4
  34. data/test/javascripts/unit/local_notification_test.js.coffee +130 -0
  35. data/test/javascripts/unit/notification_permission_test.js.coffee +5 -5
  36. data/test/javascripts/unit/push_notification_test.js.coffee +180 -0
  37. data/test/routes/apns_routes_test.rb +6 -0
  38. data/test/services/user_subscription_service_test.rb +126 -19
  39. metadata +22 -93
  40. data/app/assets/javascripts/crusade_rails/crusade.js.coffee +0 -46
  41. data/test/dummy/tmp/cache/assets/development/sprockets/07be86ef00416c18f2f7dd96f4603129 +0 -0
  42. data/test/dummy/tmp/cache/assets/development/sprockets/116d0ea1f1fccbc5f17329e738b48f32 +0 -0
  43. data/test/dummy/tmp/cache/assets/development/sprockets/1c6cab9972997baaf0f7fd1599e62590 +0 -0
  44. data/test/dummy/tmp/cache/assets/development/sprockets/2a4f82e222d708931c0b88831e1bb864 +0 -0
  45. data/test/dummy/tmp/cache/assets/development/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
  46. data/test/dummy/tmp/cache/assets/development/sprockets/2fe1114a6234d617f0bb5e464a840942 +0 -0
  47. data/test/dummy/tmp/cache/assets/development/sprockets/39b9141911bd7b575b7e782b8c405a0d +0 -0
  48. data/test/dummy/tmp/cache/assets/development/sprockets/3c9d7a4a4511e1418a3f5e9beaac8d0a +0 -0
  49. data/test/dummy/tmp/cache/assets/development/sprockets/45a685d3bc33555e456c32424fd838e8 +0 -0
  50. data/test/dummy/tmp/cache/assets/development/sprockets/576608e546507cd114398639d7f7bd5d +0 -0
  51. data/test/dummy/tmp/cache/assets/development/sprockets/57adfe7d0eeb4aca9491ffb75157956e +0 -0
  52. data/test/dummy/tmp/cache/assets/development/sprockets/5c7b888c11190fa68628e8fd3212e8fd +0 -0
  53. data/test/dummy/tmp/cache/assets/development/sprockets/5ded1a08ed08028fbe21471a88881d28 +0 -0
  54. data/test/dummy/tmp/cache/assets/development/sprockets/5e40cea3d1e28179f2a2ad998d1f26f7 +0 -0
  55. data/test/dummy/tmp/cache/assets/development/sprockets/618327f5ca738fb1d560cdf13c073bf3 +0 -0
  56. data/test/dummy/tmp/cache/assets/development/sprockets/6e6652fce2d9f8b871274ccb2af329c7 +0 -0
  57. data/test/dummy/tmp/cache/assets/development/sprockets/6f5e75e28ef2f140b52beb6f1b6609b9 +0 -0
  58. data/test/dummy/tmp/cache/assets/development/sprockets/7161340e0502d24611730dd49b15e115 +0 -0
  59. data/test/dummy/tmp/cache/assets/development/sprockets/7d4e507c01e7ef7a4dc9d7f9c5478ca4 +0 -0
  60. data/test/dummy/tmp/cache/assets/development/sprockets/803bc12cb782253e6b4191a7346991c5 +0 -0
  61. data/test/dummy/tmp/cache/assets/development/sprockets/8b3d3f72f349b54596b95558be73d55d +0 -0
  62. data/test/dummy/tmp/cache/assets/development/sprockets/97ba61a3e4a7ec32873e08c29a7a6f94 +0 -0
  63. data/test/dummy/tmp/cache/assets/development/sprockets/9ccfcc3c008514fb5c35aa7b3d7bbc78 +0 -0
  64. data/test/dummy/tmp/cache/assets/development/sprockets/a82b6a43a4be516353a2ac1d89f6b610 +0 -0
  65. data/test/dummy/tmp/cache/assets/development/sprockets/a92311b92483c1f9472df2622e658768 +0 -0
  66. data/test/dummy/tmp/cache/assets/development/sprockets/aba15ac42f92b66ff70b0a3e26184263 +0 -0
  67. data/test/dummy/tmp/cache/assets/development/sprockets/b75702adcae93ffe045a33824fc40f12 +0 -0
  68. data/test/dummy/tmp/cache/assets/development/sprockets/be89d70eb8f19e72ed6ad9481a7c1b9a +0 -0
  69. data/test/dummy/tmp/cache/assets/development/sprockets/c1d4b2d589a2dc7649e075ffea2d2566 +0 -0
  70. data/test/dummy/tmp/cache/assets/development/sprockets/cd0a1473787729a36454fd789dd86195 +0 -0
  71. data/test/dummy/tmp/cache/assets/development/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
  72. data/test/dummy/tmp/cache/assets/development/sprockets/d03fd0d3629c94a744a70d063d15eb6f +0 -0
  73. data/test/dummy/tmp/cache/assets/development/sprockets/d572f7956c1b132aba30f71dfb1334d9 +0 -0
  74. data/test/dummy/tmp/cache/assets/development/sprockets/dc8639471867248d3cfc41c6dad35a43 +0 -0
  75. data/test/dummy/tmp/cache/assets/development/sprockets/e113ed0ed8564e75067a161599ed7fc9 +0 -0
  76. data/test/dummy/tmp/cache/assets/development/sprockets/e2bd66166c81c25a6fea7aab23cf58cf +0 -0
  77. data/test/dummy/tmp/cache/assets/development/sprockets/e653b1d89bb66ba5ee551887655e02da +0 -0
  78. data/test/dummy/tmp/cache/assets/development/sprockets/eb5bc1e85d69a5ec7063b599c7f6acf1 +0 -0
  79. data/test/dummy/tmp/cache/assets/development/sprockets/f084f98622288fc6a4d8fdbc755bcb48 +0 -0
  80. data/test/dummy/tmp/cache/assets/development/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
  81. data/test/dummy/tmp/cache/assets/development/sprockets/f99eb680da1b65060a9388e0b41f19b0 +0 -0
  82. data/test/dummy/tmp/cache/assets/development/sprockets/fa3e415ce51e582faa842454baced5a3 +0 -0
  83. data/test/javascripts/unit/crusade_spec.js.coffee +0 -102
@@ -0,0 +1,29 @@
1
+ require 'test_helper'
2
+
3
+ describe 'Configuration initialization' do
4
+ describe 'config' do
5
+ subject { Rails.application.config.crusade }
6
+
7
+ it 'builds a configutation wrapper' do
8
+ subject.is_a?(CrusadeRails::ConfigurationWrapper).must_be_true
9
+ end
10
+
11
+ it 'sets a configuration in the rails config' do
12
+ Rails.application.config.crusade.wont_be_nil
13
+ end
14
+
15
+ it 'delegates some values the crusade configuration' do
16
+ subject.apns.webservice_url.must_equal 'https://localhost/push'
17
+ end
18
+
19
+ it 'registers the plugin by name' do
20
+ subject.plugins.must_equal apns: "#{Rails.root}/config/crusade-apns.yml"
21
+ end
22
+
23
+ it 'sets the user_id_block' do
24
+ user_id = subject.user_id_block.call(self)
25
+
26
+ user_id.must_equal '123'
27
+ end
28
+ end
29
+ end
@@ -2,13 +2,20 @@ require 'test_helper'
2
2
 
3
3
  describe 'Push Package Integration Test' do
4
4
  let(:user_id) { 1234 }
5
+ let(:authenticity_token) { 'aaaa' }
6
+
7
+ before do
8
+ CrusadeRails::UserSubscription.create!(user_id: user_id, authenticity_token: authenticity_token)
9
+ end
10
+
11
+ after { CrusadeRails::UserSubscription.delete_all }
5
12
 
6
13
  it 'generates and send the push package' do
7
14
  post '/push/apns/v1/pushPackages/web.com.example.push.id', {},
8
- 'RAW_POST_DATA' => "{\"user\": \"#{user_id}\"}"
15
+ 'RAW_POST_DATA' => "{\"user_id\": \"#{user_id}\",\"authenticity_token\": \"#{authenticity_token}\"}"
9
16
 
10
17
  response.status.must_equal 200
11
18
  response.content_type.must_equal 'application/zip'
12
- response.content_length.must_be_close_to 21217, 10
19
+ response.content_length.must_be_close_to 21217, 100
13
20
  end
14
21
  end
@@ -1,4 +1,4 @@
1
-
2
- window.crusade =
3
- pushId: 'web.com.example.push.id'
4
- webserviceUrl: 'https://www.example.com/push'
1
+ loadConfiguration = ->
2
+ window.Crusade.configuration =
3
+ pushId: 'web.com.example.push.id'
4
+ webserviceUrl: 'https://www.example.com/push'
@@ -0,0 +1 @@
1
+ console.log = ->
@@ -0,0 +1,4 @@
1
+ class FakeNotification
2
+ constructor: (title, args) ->
3
+ @title = title
4
+ @body = args.body
@@ -0,0 +1,587 @@
1
+ /*!
2
+ * MockJax - jQuery Plugin to Mock Ajax requests
3
+ *
4
+ * Version: 1.5.2
5
+ * Released:
6
+ * Home: http://github.com/appendto/jquery-mockjax
7
+ * Author: Jonathan Sharp (http://jdsharp.com)
8
+ * License: MIT,GPL
9
+ *
10
+ * Copyright (c) 2011 appendTo LLC.
11
+ * Dual licensed under the MIT or GPL licenses.
12
+ * http://appendto.com/open-source-licenses
13
+ */
14
+ (function($) {
15
+ var _ajax = $.ajax,
16
+ mockHandlers = [],
17
+ mockedAjaxCalls = [],
18
+ CALLBACK_REGEX = /=\?(&|$)/,
19
+ jsc = (new Date()).getTime();
20
+
21
+
22
+ // Parse the given XML string.
23
+ function parseXML(xml) {
24
+ if ( window.DOMParser == undefined && window.ActiveXObject ) {
25
+ DOMParser = function() { };
26
+ DOMParser.prototype.parseFromString = function( xmlString ) {
27
+ var doc = new ActiveXObject('Microsoft.XMLDOM');
28
+ doc.async = 'false';
29
+ doc.loadXML( xmlString );
30
+ return doc;
31
+ };
32
+ }
33
+
34
+ try {
35
+ var xmlDoc = ( new DOMParser() ).parseFromString( xml, 'text/xml' );
36
+ if ( $.isXMLDoc( xmlDoc ) ) {
37
+ var err = $('parsererror', xmlDoc);
38
+ if ( err.length == 1 ) {
39
+ throw('Error: ' + $(xmlDoc).text() );
40
+ }
41
+ } else {
42
+ throw('Unable to parse XML');
43
+ }
44
+ return xmlDoc;
45
+ } catch( e ) {
46
+ var msg = ( e.name == undefined ? e : e.name + ': ' + e.message );
47
+ $(document).trigger('xmlParseError', [ msg ]);
48
+ return undefined;
49
+ }
50
+ }
51
+
52
+ // Trigger a jQuery event
53
+ function trigger(s, type, args) {
54
+ (s.context ? $(s.context) : $.event).trigger(type, args);
55
+ }
56
+
57
+ // Check if the data field on the mock handler and the request match. This
58
+ // can be used to restrict a mock handler to being used only when a certain
59
+ // set of data is passed to it.
60
+ function isMockDataEqual( mock, live ) {
61
+ var identical = true;
62
+ // Test for situations where the data is a querystring (not an object)
63
+ if (typeof live === 'string') {
64
+ // Querystring may be a regex
65
+ return $.isFunction( mock.test ) ? mock.test(live) : mock == live;
66
+ }
67
+ $.each(mock, function(k) {
68
+ if ( live[k] === undefined ) {
69
+ identical = false;
70
+ return identical;
71
+ } else {
72
+ // This will allow to compare Arrays
73
+ if ( typeof live[k] === 'object' && live[k] !== null ) {
74
+ identical = identical && isMockDataEqual(mock[k], live[k]);
75
+ } else {
76
+ if ( mock[k] && $.isFunction( mock[k].test ) ) {
77
+ identical = identical && mock[k].test(live[k]);
78
+ } else {
79
+ identical = identical && ( mock[k] == live[k] );
80
+ }
81
+ }
82
+ }
83
+ });
84
+
85
+ return identical;
86
+ }
87
+
88
+ // Check the given handler should mock the given request
89
+ function getMockForRequest( handler, requestSettings ) {
90
+ // If the mock was registered with a function, let the function decide if we
91
+ // want to mock this request
92
+ if ( $.isFunction(handler) ) {
93
+ return handler( requestSettings );
94
+ }
95
+
96
+ // Inspect the URL of the request and check if the mock handler's url
97
+ // matches the url for this ajax request
98
+ if ( $.isFunction(handler.url.test) ) {
99
+ // The user provided a regex for the url, test it
100
+ if ( !handler.url.test( requestSettings.url ) ) {
101
+ return null;
102
+ }
103
+ } else {
104
+ // Look for a simple wildcard '*' or a direct URL match
105
+ var star = handler.url.indexOf('*');
106
+ if (handler.url !== requestSettings.url && star === -1 ||
107
+ !new RegExp(handler.url.replace(/[-[\]{}()+?.,\\^$|#\s]/g, "\\$&").replace(/\*/g, '.+')).test(requestSettings.url)) {
108
+ return null;
109
+ }
110
+ }
111
+
112
+ // Inspect the data submitted in the request (either POST body or GET query string)
113
+ if ( handler.data && requestSettings.data ) {
114
+ if ( !isMockDataEqual(handler.data, requestSettings.data) ) {
115
+ // They're not identical, do not mock this request
116
+ return null;
117
+ }
118
+ }
119
+ // Inspect the request type
120
+ if ( handler && handler.type &&
121
+ handler.type.toLowerCase() != requestSettings.type.toLowerCase() ) {
122
+ // The request type doesn't match (GET vs. POST)
123
+ return null;
124
+ }
125
+
126
+ return handler;
127
+ }
128
+
129
+ // Process the xhr objects send operation
130
+ function _xhrSend(mockHandler, requestSettings, origSettings) {
131
+
132
+ // This is a substitute for < 1.4 which lacks $.proxy
133
+ var process = (function(that) {
134
+ return function() {
135
+ return (function() {
136
+ var onReady;
137
+
138
+ // The request has returned
139
+ this.status = mockHandler.status;
140
+ this.statusText = mockHandler.statusText;
141
+ this.readyState = 4;
142
+
143
+ // We have an executable function, call it to give
144
+ // the mock handler a chance to update it's data
145
+ if ( $.isFunction(mockHandler.response) ) {
146
+ mockHandler.response(origSettings);
147
+ }
148
+ // Copy over our mock to our xhr object before passing control back to
149
+ // jQuery's onreadystatechange callback
150
+ if ( requestSettings.dataType == 'json' && ( typeof mockHandler.responseText == 'object' ) ) {
151
+ this.responseText = JSON.stringify(mockHandler.responseText);
152
+ } else if ( requestSettings.dataType == 'xml' ) {
153
+ if ( typeof mockHandler.responseXML == 'string' ) {
154
+ this.responseXML = parseXML(mockHandler.responseXML);
155
+ //in jQuery 1.9.1+, responseXML is processed differently and relies on responseText
156
+ this.responseText = mockHandler.responseXML;
157
+ } else {
158
+ this.responseXML = mockHandler.responseXML;
159
+ }
160
+ } else {
161
+ this.responseText = mockHandler.responseText;
162
+ }
163
+ if( typeof mockHandler.status == 'number' || typeof mockHandler.status == 'string' ) {
164
+ this.status = mockHandler.status;
165
+ }
166
+ if( typeof mockHandler.statusText === "string") {
167
+ this.statusText = mockHandler.statusText;
168
+ }
169
+ // jQuery 2.0 renamed onreadystatechange to onload
170
+ onReady = this.onreadystatechange || this.onload;
171
+
172
+ // jQuery < 1.4 doesn't have onreadystate change for xhr
173
+ if ( $.isFunction( onReady ) ) {
174
+ if( mockHandler.isTimeout) {
175
+ this.status = -1;
176
+ }
177
+ onReady.call( this, mockHandler.isTimeout ? 'timeout' : undefined );
178
+ } else if ( mockHandler.isTimeout ) {
179
+ // Fix for 1.3.2 timeout to keep success from firing.
180
+ this.status = -1;
181
+ }
182
+ }).apply(that);
183
+ };
184
+ })(this);
185
+
186
+ if ( mockHandler.proxy ) {
187
+ // We're proxying this request and loading in an external file instead
188
+ _ajax({
189
+ global: false,
190
+ url: mockHandler.proxy,
191
+ type: mockHandler.proxyType,
192
+ data: mockHandler.data,
193
+ dataType: requestSettings.dataType === "script" ? "text/plain" : requestSettings.dataType,
194
+ complete: function(xhr) {
195
+ mockHandler.responseXML = xhr.responseXML;
196
+ mockHandler.responseText = xhr.responseText;
197
+ mockHandler.status = xhr.status;
198
+ mockHandler.statusText = xhr.statusText;
199
+ this.responseTimer = setTimeout(process, mockHandler.responseTime || 0);
200
+ }
201
+ });
202
+ } else {
203
+ // type == 'POST' || 'GET' || 'DELETE'
204
+ if ( requestSettings.async === false ) {
205
+ // TODO: Blocking delay
206
+ process();
207
+ } else {
208
+ this.responseTimer = setTimeout(process, mockHandler.responseTime || 50);
209
+ }
210
+ }
211
+ }
212
+
213
+ // Construct a mocked XHR Object
214
+ function xhr(mockHandler, requestSettings, origSettings, origHandler) {
215
+ // Extend with our default mockjax settings
216
+ mockHandler = $.extend(true, {}, $.mockjaxSettings, mockHandler);
217
+
218
+ if (typeof mockHandler.headers === 'undefined') {
219
+ mockHandler.headers = {};
220
+ }
221
+ if ( mockHandler.contentType ) {
222
+ mockHandler.headers['content-type'] = mockHandler.contentType;
223
+ }
224
+
225
+ return {
226
+ status: mockHandler.status,
227
+ statusText: mockHandler.statusText,
228
+ readyState: 1,
229
+ open: function() { },
230
+ send: function() {
231
+ origHandler.fired = true;
232
+ _xhrSend.call(this, mockHandler, requestSettings, origSettings);
233
+ },
234
+ abort: function() {
235
+ clearTimeout(this.responseTimer);
236
+ },
237
+ setRequestHeader: function(header, value) {
238
+ mockHandler.headers[header] = value;
239
+ },
240
+ getResponseHeader: function(header) {
241
+ // 'Last-modified', 'Etag', 'content-type' are all checked by jQuery
242
+ if ( mockHandler.headers && mockHandler.headers[header] ) {
243
+ // Return arbitrary headers
244
+ return mockHandler.headers[header];
245
+ } else if ( header.toLowerCase() == 'last-modified' ) {
246
+ return mockHandler.lastModified || (new Date()).toString();
247
+ } else if ( header.toLowerCase() == 'etag' ) {
248
+ return mockHandler.etag || '';
249
+ } else if ( header.toLowerCase() == 'content-type' ) {
250
+ return mockHandler.contentType || 'text/plain';
251
+ }
252
+ },
253
+ getAllResponseHeaders: function() {
254
+ var headers = '';
255
+ $.each(mockHandler.headers, function(k, v) {
256
+ headers += k + ': ' + v + "\n";
257
+ });
258
+ return headers;
259
+ }
260
+ };
261
+ }
262
+
263
+ // Process a JSONP mock request.
264
+ function processJsonpMock( requestSettings, mockHandler, origSettings ) {
265
+ // Handle JSONP Parameter Callbacks, we need to replicate some of the jQuery core here
266
+ // because there isn't an easy hook for the cross domain script tag of jsonp
267
+
268
+ processJsonpUrl( requestSettings );
269
+
270
+ requestSettings.dataType = "json";
271
+ if(requestSettings.data && CALLBACK_REGEX.test(requestSettings.data) || CALLBACK_REGEX.test(requestSettings.url)) {
272
+ createJsonpCallback(requestSettings, mockHandler, origSettings);
273
+
274
+ // We need to make sure
275
+ // that a JSONP style response is executed properly
276
+
277
+ var rurl = /^(\w+:)?\/\/([^\/?#]+)/,
278
+ parts = rurl.exec( requestSettings.url ),
279
+ remote = parts && (parts[1] && parts[1] !== location.protocol || parts[2] !== location.host);
280
+
281
+ requestSettings.dataType = "script";
282
+ if(requestSettings.type.toUpperCase() === "GET" && remote ) {
283
+ var newMockReturn = processJsonpRequest( requestSettings, mockHandler, origSettings );
284
+
285
+ // Check if we are supposed to return a Deferred back to the mock call, or just
286
+ // signal success
287
+ if(newMockReturn) {
288
+ return newMockReturn;
289
+ } else {
290
+ return true;
291
+ }
292
+ }
293
+ }
294
+ return null;
295
+ }
296
+
297
+ // Append the required callback parameter to the end of the request URL, for a JSONP request
298
+ function processJsonpUrl( requestSettings ) {
299
+ if ( requestSettings.type.toUpperCase() === "GET" ) {
300
+ if ( !CALLBACK_REGEX.test( requestSettings.url ) ) {
301
+ requestSettings.url += (/\?/.test( requestSettings.url ) ? "&" : "?") +
302
+ (requestSettings.jsonp || "callback") + "=?";
303
+ }
304
+ } else if ( !requestSettings.data || !CALLBACK_REGEX.test(requestSettings.data) ) {
305
+ requestSettings.data = (requestSettings.data ? requestSettings.data + "&" : "") + (requestSettings.jsonp || "callback") + "=?";
306
+ }
307
+ }
308
+
309
+ // Process a JSONP request by evaluating the mocked response text
310
+ function processJsonpRequest( requestSettings, mockHandler, origSettings ) {
311
+ // Synthesize the mock request for adding a script tag
312
+ var callbackContext = origSettings && origSettings.context || requestSettings,
313
+ newMock = null;
314
+
315
+
316
+ // If the response handler on the moock is a function, call it
317
+ if ( mockHandler.response && $.isFunction(mockHandler.response) ) {
318
+ mockHandler.response(origSettings);
319
+ } else {
320
+
321
+ // Evaluate the responseText javascript in a global context
322
+ if( typeof mockHandler.responseText === 'object' ) {
323
+ $.globalEval( '(' + JSON.stringify( mockHandler.responseText ) + ')');
324
+ } else {
325
+ $.globalEval( '(' + mockHandler.responseText + ')');
326
+ }
327
+ }
328
+
329
+ // Successful response
330
+ jsonpSuccess( requestSettings, callbackContext, mockHandler );
331
+ jsonpComplete( requestSettings, callbackContext, mockHandler );
332
+
333
+ // If we are running under jQuery 1.5+, return a deferred object
334
+ if($.Deferred){
335
+ newMock = new $.Deferred();
336
+ if(typeof mockHandler.responseText == "object"){
337
+ newMock.resolveWith( callbackContext, [mockHandler.responseText] );
338
+ }
339
+ else{
340
+ newMock.resolveWith( callbackContext, [$.parseJSON( mockHandler.responseText )] );
341
+ }
342
+ }
343
+ return newMock;
344
+ }
345
+
346
+
347
+ // Create the required JSONP callback function for the request
348
+ function createJsonpCallback( requestSettings, mockHandler, origSettings ) {
349
+ var callbackContext = origSettings && origSettings.context || requestSettings;
350
+ var jsonp = requestSettings.jsonpCallback || ("jsonp" + jsc++);
351
+
352
+ // Replace the =? sequence both in the query string and the data
353
+ if ( requestSettings.data ) {
354
+ requestSettings.data = (requestSettings.data + "").replace(CALLBACK_REGEX, "=" + jsonp + "$1");
355
+ }
356
+
357
+ requestSettings.url = requestSettings.url.replace(CALLBACK_REGEX, "=" + jsonp + "$1");
358
+
359
+
360
+ // Handle JSONP-style loading
361
+ window[ jsonp ] = window[ jsonp ] || function( tmp ) {
362
+ data = tmp;
363
+ jsonpSuccess( requestSettings, callbackContext, mockHandler );
364
+ jsonpComplete( requestSettings, callbackContext, mockHandler );
365
+ // Garbage collect
366
+ window[ jsonp ] = undefined;
367
+
368
+ try {
369
+ delete window[ jsonp ];
370
+ } catch(e) {}
371
+
372
+ if ( head ) {
373
+ head.removeChild( script );
374
+ }
375
+ };
376
+ }
377
+
378
+ // The JSONP request was successful
379
+ function jsonpSuccess(requestSettings, callbackContext, mockHandler) {
380
+ // If a local callback was specified, fire it and pass it the data
381
+ if ( requestSettings.success ) {
382
+ requestSettings.success.call( callbackContext, mockHandler.responseText || "", status, {} );
383
+ }
384
+
385
+ // Fire the global callback
386
+ if ( requestSettings.global ) {
387
+ trigger(requestSettings, "ajaxSuccess", [{}, requestSettings] );
388
+ }
389
+ }
390
+
391
+ // The JSONP request was completed
392
+ function jsonpComplete(requestSettings, callbackContext) {
393
+ // Process result
394
+ if ( requestSettings.complete ) {
395
+ requestSettings.complete.call( callbackContext, {} , status );
396
+ }
397
+
398
+ // The request was completed
399
+ if ( requestSettings.global ) {
400
+ trigger( "ajaxComplete", [{}, requestSettings] );
401
+ }
402
+
403
+ // Handle the global AJAX counter
404
+ if ( requestSettings.global && ! --$.active ) {
405
+ $.event.trigger( "ajaxStop" );
406
+ }
407
+ }
408
+
409
+
410
+ // The core $.ajax replacement.
411
+ function handleAjax( url, origSettings ) {
412
+ var mockRequest, requestSettings, mockHandler;
413
+
414
+ // If url is an object, simulate pre-1.5 signature
415
+ if ( typeof url === "object" ) {
416
+ origSettings = url;
417
+ url = undefined;
418
+ } else {
419
+ // work around to support 1.5 signature
420
+ origSettings.url = url;
421
+ }
422
+
423
+ // Extend the original settings for the request
424
+ requestSettings = $.extend(true, {}, $.ajaxSettings, origSettings);
425
+
426
+ // Iterate over our mock handlers (in registration order) until we find
427
+ // one that is willing to intercept the request
428
+ for(var k = 0; k < mockHandlers.length; k++) {
429
+ if ( !mockHandlers[k] ) {
430
+ continue;
431
+ }
432
+
433
+ mockHandler = getMockForRequest( mockHandlers[k], requestSettings );
434
+ if(!mockHandler) {
435
+ // No valid mock found for this request
436
+ continue;
437
+ }
438
+
439
+ mockedAjaxCalls.push(requestSettings);
440
+
441
+ // If logging is enabled, log the mock to the console
442
+ $.mockjaxSettings.log( mockHandler, requestSettings );
443
+
444
+
445
+ if ( requestSettings.dataType === "jsonp" ) {
446
+ if ((mockRequest = processJsonpMock( requestSettings, mockHandler, origSettings ))) {
447
+ // This mock will handle the JSONP request
448
+ return mockRequest;
449
+ }
450
+ }
451
+
452
+
453
+ // Removed to fix #54 - keep the mocking data object intact
454
+ //mockHandler.data = requestSettings.data;
455
+
456
+ mockHandler.cache = requestSettings.cache;
457
+ mockHandler.timeout = requestSettings.timeout;
458
+ mockHandler.global = requestSettings.global;
459
+
460
+ copyUrlParameters(mockHandler, origSettings);
461
+
462
+ (function(mockHandler, requestSettings, origSettings, origHandler) {
463
+ mockRequest = _ajax.call($, $.extend(true, {}, origSettings, {
464
+ // Mock the XHR object
465
+ xhr: function() { return xhr( mockHandler, requestSettings, origSettings, origHandler ); }
466
+ }));
467
+ })(mockHandler, requestSettings, origSettings, mockHandlers[k]);
468
+
469
+ return mockRequest;
470
+ }
471
+
472
+ // We don't have a mock request
473
+ if($.mockjaxSettings.throwUnmocked === true) {
474
+ throw('AJAX not mocked: ' + origSettings.url);
475
+ }
476
+ else { // trigger a normal request
477
+ return _ajax.apply($, [origSettings]);
478
+ }
479
+ }
480
+
481
+ /**
482
+ * Copies URL parameter values if they were captured by a regular expression
483
+ * @param {Object} mockHandler
484
+ * @param {Object} origSettings
485
+ */
486
+ function copyUrlParameters(mockHandler, origSettings) {
487
+ //parameters aren't captured if the URL isn't a RegExp
488
+ if (!(mockHandler.url instanceof RegExp)) {
489
+ return;
490
+ }
491
+ //if no URL params were defined on the handler, don't attempt a capture
492
+ if (!mockHandler.hasOwnProperty('urlParams')) {
493
+ return;
494
+ }
495
+ var captures = mockHandler.url.exec(origSettings.url);
496
+ //the whole RegExp match is always the first value in the capture results
497
+ if (captures.length === 1) {
498
+ return;
499
+ }
500
+ captures.shift();
501
+ //use handler params as keys and capture resuts as values
502
+ var i = 0,
503
+ capturesLength = captures.length,
504
+ paramsLength = mockHandler.urlParams.length,
505
+ //in case the number of params specified is less than actual captures
506
+ maxIterations = Math.min(capturesLength, paramsLength),
507
+ paramValues = {};
508
+ for (i; i < maxIterations; i++) {
509
+ var key = mockHandler.urlParams[i];
510
+ paramValues[key] = captures[i];
511
+ }
512
+ origSettings.urlParams = paramValues;
513
+ }
514
+
515
+
516
+ // Public
517
+
518
+ $.extend({
519
+ ajax: handleAjax
520
+ });
521
+
522
+ $.mockjaxSettings = {
523
+ //url: null,
524
+ //type: 'GET',
525
+ log: function( mockHandler, requestSettings ) {
526
+ if ( mockHandler.logging === false ||
527
+ ( typeof mockHandler.logging === 'undefined' && $.mockjaxSettings.logging === false ) ) {
528
+ return;
529
+ }
530
+ if ( window.console && console.log ) {
531
+ var message = 'MOCK ' + requestSettings.type.toUpperCase() + ': ' + requestSettings.url;
532
+ var request = $.extend({}, requestSettings);
533
+
534
+ if (typeof console.log === 'function') {
535
+ console.log(message, request);
536
+ } else {
537
+ try {
538
+ console.log( message + ' ' + JSON.stringify(request) );
539
+ } catch (e) {
540
+ console.log(message);
541
+ }
542
+ }
543
+ }
544
+ },
545
+ logging: true,
546
+ status: 200,
547
+ statusText: "OK",
548
+ responseTime: 500,
549
+ isTimeout: false,
550
+ throwUnmocked: false,
551
+ contentType: 'text/plain',
552
+ response: '',
553
+ responseText: '',
554
+ responseXML: '',
555
+ proxy: '',
556
+ proxyType: 'GET',
557
+
558
+ lastModified: null,
559
+ etag: '',
560
+ headers: {
561
+ etag: 'IJF@H#@923uf8023hFO@I#H#',
562
+ 'content-type' : 'text/plain'
563
+ }
564
+ };
565
+
566
+ $.mockjax = function(settings) {
567
+ var i = mockHandlers.length;
568
+ mockHandlers[i] = settings;
569
+ return i;
570
+ };
571
+ $.mockjaxClear = function(i) {
572
+ if ( arguments.length == 1 ) {
573
+ mockHandlers[i] = null;
574
+ } else {
575
+ mockHandlers = [];
576
+ }
577
+ mockedAjaxCalls = [];
578
+ };
579
+ $.mockjax.handler = function(i) {
580
+ if ( arguments.length == 1 ) {
581
+ return mockHandlers[i];
582
+ }
583
+ };
584
+ $.mockjax.mockedAjaxCalls = function() {
585
+ return mockedAjaxCalls;
586
+ };
587
+ })(jQuery);