pretender-rails 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +33 -0
- data/Rakefile +2 -0
- data/lib/pretender-rails.rb +10 -0
- data/lib/pretender/rails/cucumber.rb +18 -0
- data/lib/pretender/rails/engine.rb +6 -0
- data/lib/pretender/rails/middleware.rb +20 -0
- data/lib/pretender/rails/railtie.rb +9 -0
- data/lib/pretender/rails/server.rb +75 -0
- data/lib/pretender/rails/version.rb +5 -0
- data/pretender-rails.gemspec +23 -0
- data/vendor/assets/javascripts/fake_xml_http_request.js +497 -0
- data/vendor/assets/javascripts/pretender.js +367 -0
- data/vendor/assets/javascripts/route-recognizer.js +653 -0
- metadata +88 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1203f9df04b877d6a7ad3cc32daca9b01a1fd844
|
4
|
+
data.tar.gz: 723f9bc1e3792b40e644dcb043ae20e2b7826939
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0f946042676557f164c3f72d722cb0bff9fb30c134adb2b23557785dbd414199dc803332d729318d32a26bb1ee7faf1cffae22bc679d993a544986619517d687
|
7
|
+
data.tar.gz: 3a6a9d11965883cc5eb568f466c0cfb528c60846b3dabfa4c95f3bbd3e48254f482af6420a4fa093442ad9b2b19126acf3bed354f87f78b610e06ddf79334308
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Chase McCarthy
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# Pretender Rails
|
2
|
+
|
3
|
+
Stub clientside calls to external services.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'pretender-rails'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install pretender-rails
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
Pretender.server.stub('get', 'https://api.github.com/repos/code0100fun/pretender-rails', [200, {}, {name: "mock-repo"}.to_json])
|
25
|
+
```
|
26
|
+
|
27
|
+
## Contributing
|
28
|
+
|
29
|
+
1. Fork it ( https://github.com/[my-github-username]/pretender-rails/fork )
|
30
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
31
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
32
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
33
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'cucumber'
|
2
|
+
require 'capybara/cucumber'
|
3
|
+
|
4
|
+
module Pretender
|
5
|
+
module Rails
|
6
|
+
module CucumberHelper
|
7
|
+
def stub(method, route, response)
|
8
|
+
Pretender.server.stub(method, route, response)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
World(Pretender::Rails::CucumberHelper)
|
15
|
+
|
16
|
+
After do
|
17
|
+
Pretender.server.shutdown(page)
|
18
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Pretender
|
2
|
+
module Rails
|
3
|
+
class Middleware
|
4
|
+
def initialize(app)
|
5
|
+
@app = app
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(env)
|
9
|
+
status, headers, env = @app.call(env)
|
10
|
+
if headers['Content-Type'].to_s.include?("text/html")
|
11
|
+
body = Pretender.server.inject(env.body)
|
12
|
+
headers['Content-Length'] = Rack::Utils.bytesize(body.to_s).to_s
|
13
|
+
return [status, headers, [body]]
|
14
|
+
end
|
15
|
+
|
16
|
+
[status, headers, env]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Pretender
|
2
|
+
def self.server
|
3
|
+
@_server ||= ::Pretender::Rails::Server.new
|
4
|
+
end
|
5
|
+
|
6
|
+
module Rails
|
7
|
+
class Server
|
8
|
+
Stub = Struct.new(:method, :route, :response)
|
9
|
+
attr_accessor :session
|
10
|
+
|
11
|
+
def inject(body)
|
12
|
+
unless stubs.empty?
|
13
|
+
head_pos = body.index('</head>')
|
14
|
+
body.insert(head_pos, scripts) if head_pos
|
15
|
+
end
|
16
|
+
body
|
17
|
+
end
|
18
|
+
|
19
|
+
def shutdown(page)
|
20
|
+
stubs.clear
|
21
|
+
page.execute_script('typeof(server) !== "undefined" && server.shutdown();')
|
22
|
+
end
|
23
|
+
|
24
|
+
def stub(method, route, response)
|
25
|
+
stub = Stub.new(method, route, response)
|
26
|
+
stubs.unshift stub
|
27
|
+
stub
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def stubs
|
33
|
+
@_stubs ||= []
|
34
|
+
end
|
35
|
+
|
36
|
+
def routes
|
37
|
+
stubs.map do |stub|
|
38
|
+
"this.#{stub.method}('#{stub.route}', function(request) { return #{stub.response.to_json}; });"
|
39
|
+
end.join("\n")
|
40
|
+
end
|
41
|
+
|
42
|
+
def load_script(path)
|
43
|
+
open(path).read
|
44
|
+
end
|
45
|
+
|
46
|
+
def scripts
|
47
|
+
[dependencies_script, server_script].join("\n")
|
48
|
+
end
|
49
|
+
|
50
|
+
def dependencies_script
|
51
|
+
[javascript_include_tag('fake_xml_http_request.js'),
|
52
|
+
javascript_include_tag('route-recognizer.js'),
|
53
|
+
javascript_include_tag('pretender.js')].join("\n")
|
54
|
+
end
|
55
|
+
|
56
|
+
def javascript_include_tag(url)
|
57
|
+
"<script src=\"/assets/#{url}\"></script>"
|
58
|
+
end
|
59
|
+
|
60
|
+
def server_script
|
61
|
+
<<-JS
|
62
|
+
<script>
|
63
|
+
var server = new Pretender(function(){
|
64
|
+
#{routes}
|
65
|
+
this.get('*any', this.passthrough);
|
66
|
+
this.post('*any', this.passthrough);
|
67
|
+
this.put('*any', this.passthrough);
|
68
|
+
this.delete('*any', this.passthrough);
|
69
|
+
});
|
70
|
+
</script>
|
71
|
+
JS
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'pretender/rails/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "pretender-rails"
|
8
|
+
spec.version = Pretender::Rails::VERSION
|
9
|
+
spec.authors = ["Chase McCarthy"]
|
10
|
+
spec.email = ["chase@code0100fun.com"]
|
11
|
+
spec.summary = %q{Stub clientside ajax requests using pretender}
|
12
|
+
spec.description = %q{Injects pretender into all HTML requests when a route has been stubbed}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
end
|
@@ -0,0 +1,497 @@
|
|
1
|
+
(function (global, factory) {
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
3
|
+
typeof define === 'function' && define.amd ? define(factory) :
|
4
|
+
global.FakeXMLHttpRequest = factory()
|
5
|
+
}(this, function () { 'use strict';
|
6
|
+
|
7
|
+
/**
|
8
|
+
* Minimal Event interface implementation
|
9
|
+
*
|
10
|
+
* Original implementation by Sven Fuchs: https://gist.github.com/995028
|
11
|
+
* Modifications and tests by Christian Johansen.
|
12
|
+
*
|
13
|
+
* @author Sven Fuchs (svenfuchs@artweb-design.de)
|
14
|
+
* @author Christian Johansen (christian@cjohansen.no)
|
15
|
+
* @license BSD
|
16
|
+
*
|
17
|
+
* Copyright (c) 2011 Sven Fuchs, Christian Johansen
|
18
|
+
*/
|
19
|
+
|
20
|
+
var _Event = function Event(type, bubbles, cancelable, target) {
|
21
|
+
this.type = type;
|
22
|
+
this.bubbles = bubbles;
|
23
|
+
this.cancelable = cancelable;
|
24
|
+
this.target = target;
|
25
|
+
};
|
26
|
+
|
27
|
+
_Event.prototype = {
|
28
|
+
stopPropagation: function () {},
|
29
|
+
preventDefault: function () {
|
30
|
+
this.defaultPrevented = true;
|
31
|
+
}
|
32
|
+
};
|
33
|
+
|
34
|
+
/*
|
35
|
+
Used to set the statusText property of an xhr object
|
36
|
+
*/
|
37
|
+
var httpStatusCodes = {
|
38
|
+
100: "Continue",
|
39
|
+
101: "Switching Protocols",
|
40
|
+
200: "OK",
|
41
|
+
201: "Created",
|
42
|
+
202: "Accepted",
|
43
|
+
203: "Non-Authoritative Information",
|
44
|
+
204: "No Content",
|
45
|
+
205: "Reset Content",
|
46
|
+
206: "Partial Content",
|
47
|
+
300: "Multiple Choice",
|
48
|
+
301: "Moved Permanently",
|
49
|
+
302: "Found",
|
50
|
+
303: "See Other",
|
51
|
+
304: "Not Modified",
|
52
|
+
305: "Use Proxy",
|
53
|
+
307: "Temporary Redirect",
|
54
|
+
400: "Bad Request",
|
55
|
+
401: "Unauthorized",
|
56
|
+
402: "Payment Required",
|
57
|
+
403: "Forbidden",
|
58
|
+
404: "Not Found",
|
59
|
+
405: "Method Not Allowed",
|
60
|
+
406: "Not Acceptable",
|
61
|
+
407: "Proxy Authentication Required",
|
62
|
+
408: "Request Timeout",
|
63
|
+
409: "Conflict",
|
64
|
+
410: "Gone",
|
65
|
+
411: "Length Required",
|
66
|
+
412: "Precondition Failed",
|
67
|
+
413: "Request Entity Too Large",
|
68
|
+
414: "Request-URI Too Long",
|
69
|
+
415: "Unsupported Media Type",
|
70
|
+
416: "Requested Range Not Satisfiable",
|
71
|
+
417: "Expectation Failed",
|
72
|
+
422: "Unprocessable Entity",
|
73
|
+
500: "Internal Server Error",
|
74
|
+
501: "Not Implemented",
|
75
|
+
502: "Bad Gateway",
|
76
|
+
503: "Service Unavailable",
|
77
|
+
504: "Gateway Timeout",
|
78
|
+
505: "HTTP Version Not Supported"
|
79
|
+
};
|
80
|
+
|
81
|
+
|
82
|
+
/*
|
83
|
+
Cross-browser XML parsing. Used to turn
|
84
|
+
XML responses into Document objects
|
85
|
+
Borrowed from JSpec
|
86
|
+
*/
|
87
|
+
function parseXML(text) {
|
88
|
+
var xmlDoc;
|
89
|
+
|
90
|
+
if (typeof DOMParser != "undefined") {
|
91
|
+
var parser = new DOMParser();
|
92
|
+
xmlDoc = parser.parseFromString(text, "text/xml");
|
93
|
+
} else {
|
94
|
+
xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
|
95
|
+
xmlDoc.async = "false";
|
96
|
+
xmlDoc.loadXML(text);
|
97
|
+
}
|
98
|
+
|
99
|
+
return xmlDoc;
|
100
|
+
}
|
101
|
+
|
102
|
+
/*
|
103
|
+
Without mocking, the native XMLHttpRequest object will throw
|
104
|
+
an error when attempting to set these headers. We match this behavior.
|
105
|
+
*/
|
106
|
+
var unsafeHeaders = {
|
107
|
+
"Accept-Charset": true,
|
108
|
+
"Accept-Encoding": true,
|
109
|
+
"Connection": true,
|
110
|
+
"Content-Length": true,
|
111
|
+
"Cookie": true,
|
112
|
+
"Cookie2": true,
|
113
|
+
"Content-Transfer-Encoding": true,
|
114
|
+
"Date": true,
|
115
|
+
"Expect": true,
|
116
|
+
"Host": true,
|
117
|
+
"Keep-Alive": true,
|
118
|
+
"Referer": true,
|
119
|
+
"TE": true,
|
120
|
+
"Trailer": true,
|
121
|
+
"Transfer-Encoding": true,
|
122
|
+
"Upgrade": true,
|
123
|
+
"User-Agent": true,
|
124
|
+
"Via": true
|
125
|
+
};
|
126
|
+
|
127
|
+
/*
|
128
|
+
Adds an "event" onto the fake xhr object
|
129
|
+
that just calls the same-named method. This is
|
130
|
+
in case a library adds callbacks for these events.
|
131
|
+
*/
|
132
|
+
function _addEventListener(eventName, xhr){
|
133
|
+
xhr.addEventListener(eventName, function (event) {
|
134
|
+
var listener = xhr["on" + eventName];
|
135
|
+
|
136
|
+
if (listener && typeof listener == "function") {
|
137
|
+
listener(event);
|
138
|
+
}
|
139
|
+
});
|
140
|
+
}
|
141
|
+
|
142
|
+
function EventedObject() {
|
143
|
+
this._eventListeners = {};
|
144
|
+
var events = ["loadstart", "progress", "load", "abort", "loadend"];
|
145
|
+
for (var i = events.length - 1; i >= 0; i--) {
|
146
|
+
_addEventListener(events[i], this);
|
147
|
+
}
|
148
|
+
};
|
149
|
+
|
150
|
+
EventedObject.prototype = {
|
151
|
+
/*
|
152
|
+
Duplicates the behavior of native XMLHttpRequest's addEventListener function
|
153
|
+
*/
|
154
|
+
addEventListener: function addEventListener(event, listener) {
|
155
|
+
this._eventListeners[event] = this._eventListeners[event] || [];
|
156
|
+
this._eventListeners[event].push(listener);
|
157
|
+
},
|
158
|
+
|
159
|
+
/*
|
160
|
+
Duplicates the behavior of native XMLHttpRequest's removeEventListener function
|
161
|
+
*/
|
162
|
+
removeEventListener: function removeEventListener(event, listener) {
|
163
|
+
var listeners = this._eventListeners[event] || [];
|
164
|
+
|
165
|
+
for (var i = 0, l = listeners.length; i < l; ++i) {
|
166
|
+
if (listeners[i] == listener) {
|
167
|
+
return listeners.splice(i, 1);
|
168
|
+
}
|
169
|
+
}
|
170
|
+
},
|
171
|
+
|
172
|
+
/*
|
173
|
+
Duplicates the behavior of native XMLHttpRequest's dispatchEvent function
|
174
|
+
*/
|
175
|
+
dispatchEvent: function dispatchEvent(event) {
|
176
|
+
var type = event.type;
|
177
|
+
var listeners = this._eventListeners[type] || [];
|
178
|
+
|
179
|
+
for (var i = 0; i < listeners.length; i++) {
|
180
|
+
if (typeof listeners[i] == "function") {
|
181
|
+
listeners[i].call(this, event);
|
182
|
+
} else {
|
183
|
+
listeners[i].handleEvent(event);
|
184
|
+
}
|
185
|
+
}
|
186
|
+
|
187
|
+
return !!event.defaultPrevented;
|
188
|
+
},
|
189
|
+
|
190
|
+
/*
|
191
|
+
Triggers an `onprogress` event with the given parameters.
|
192
|
+
*/
|
193
|
+
_progress: function _progress(lengthComputable, loaded, total) {
|
194
|
+
var event = new _Event('progress');
|
195
|
+
event.target = this;
|
196
|
+
event.lengthComputable = lengthComputable;
|
197
|
+
event.loaded = loaded;
|
198
|
+
event.total = total;
|
199
|
+
this.dispatchEvent(event);
|
200
|
+
}
|
201
|
+
}
|
202
|
+
|
203
|
+
/*
|
204
|
+
Constructor for a fake window.XMLHttpRequest
|
205
|
+
*/
|
206
|
+
function FakeXMLHttpRequest() {
|
207
|
+
EventedObject.call(this);
|
208
|
+
this.readyState = FakeXMLHttpRequest.UNSENT;
|
209
|
+
this.requestHeaders = {};
|
210
|
+
this.requestBody = null;
|
211
|
+
this.status = 0;
|
212
|
+
this.statusText = "";
|
213
|
+
this.upload = new EventedObject();
|
214
|
+
}
|
215
|
+
|
216
|
+
FakeXMLHttpRequest.prototype = new EventedObject();
|
217
|
+
|
218
|
+
// These status codes are available on the native XMLHttpRequest
|
219
|
+
// object, so we match that here in case a library is relying on them.
|
220
|
+
FakeXMLHttpRequest.UNSENT = 0;
|
221
|
+
FakeXMLHttpRequest.OPENED = 1;
|
222
|
+
FakeXMLHttpRequest.HEADERS_RECEIVED = 2;
|
223
|
+
FakeXMLHttpRequest.LOADING = 3;
|
224
|
+
FakeXMLHttpRequest.DONE = 4;
|
225
|
+
|
226
|
+
var FakeXMLHttpRequestProto = {
|
227
|
+
UNSENT: 0,
|
228
|
+
OPENED: 1,
|
229
|
+
HEADERS_RECEIVED: 2,
|
230
|
+
LOADING: 3,
|
231
|
+
DONE: 4,
|
232
|
+
async: true,
|
233
|
+
|
234
|
+
/*
|
235
|
+
Duplicates the behavior of native XMLHttpRequest's open function
|
236
|
+
*/
|
237
|
+
open: function open(method, url, async, username, password) {
|
238
|
+
this.method = method;
|
239
|
+
this.url = url;
|
240
|
+
this.async = typeof async == "boolean" ? async : true;
|
241
|
+
this.username = username;
|
242
|
+
this.password = password;
|
243
|
+
this.responseText = null;
|
244
|
+
this.responseXML = null;
|
245
|
+
this.requestHeaders = {};
|
246
|
+
this.sendFlag = false;
|
247
|
+
this._readyStateChange(FakeXMLHttpRequest.OPENED);
|
248
|
+
},
|
249
|
+
|
250
|
+
/*
|
251
|
+
Duplicates the behavior of native XMLHttpRequest's setRequestHeader function
|
252
|
+
*/
|
253
|
+
setRequestHeader: function setRequestHeader(header, value) {
|
254
|
+
verifyState(this);
|
255
|
+
|
256
|
+
if (unsafeHeaders[header] || /^(Sec-|Proxy-)/.test(header)) {
|
257
|
+
throw new Error("Refused to set unsafe header \"" + header + "\"");
|
258
|
+
}
|
259
|
+
|
260
|
+
if (this.requestHeaders[header]) {
|
261
|
+
this.requestHeaders[header] += "," + value;
|
262
|
+
} else {
|
263
|
+
this.requestHeaders[header] = value;
|
264
|
+
}
|
265
|
+
},
|
266
|
+
|
267
|
+
/*
|
268
|
+
Duplicates the behavior of native XMLHttpRequest's send function
|
269
|
+
*/
|
270
|
+
send: function send(data) {
|
271
|
+
verifyState(this);
|
272
|
+
|
273
|
+
if (!/^(get|head)$/i.test(this.method)) {
|
274
|
+
if (this.requestHeaders["Content-Type"]) {
|
275
|
+
var value = this.requestHeaders["Content-Type"].split(";");
|
276
|
+
this.requestHeaders["Content-Type"] = value[0] + ";charset=utf-8";
|
277
|
+
} else {
|
278
|
+
this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8";
|
279
|
+
}
|
280
|
+
|
281
|
+
this.requestBody = data;
|
282
|
+
}
|
283
|
+
|
284
|
+
this.errorFlag = false;
|
285
|
+
this.sendFlag = this.async;
|
286
|
+
this._readyStateChange(FakeXMLHttpRequest.OPENED);
|
287
|
+
|
288
|
+
if (typeof this.onSend == "function") {
|
289
|
+
this.onSend(this);
|
290
|
+
}
|
291
|
+
|
292
|
+
this.dispatchEvent(new _Event("loadstart", false, false, this));
|
293
|
+
},
|
294
|
+
|
295
|
+
/*
|
296
|
+
Duplicates the behavior of native XMLHttpRequest's abort function
|
297
|
+
*/
|
298
|
+
abort: function abort() {
|
299
|
+
this.aborted = true;
|
300
|
+
this.responseText = null;
|
301
|
+
this.errorFlag = true;
|
302
|
+
this.requestHeaders = {};
|
303
|
+
|
304
|
+
if (this.readyState > FakeXMLHttpRequest.UNSENT && this.sendFlag) {
|
305
|
+
this._readyStateChange(FakeXMLHttpRequest.DONE);
|
306
|
+
this.sendFlag = false;
|
307
|
+
}
|
308
|
+
|
309
|
+
this.readyState = FakeXMLHttpRequest.UNSENT;
|
310
|
+
|
311
|
+
this.dispatchEvent(new _Event("abort", false, false, this));
|
312
|
+
if (typeof this.onerror === "function") {
|
313
|
+
this.onerror();
|
314
|
+
}
|
315
|
+
},
|
316
|
+
|
317
|
+
/*
|
318
|
+
Duplicates the behavior of native XMLHttpRequest's getResponseHeader function
|
319
|
+
*/
|
320
|
+
getResponseHeader: function getResponseHeader(header) {
|
321
|
+
if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) {
|
322
|
+
return null;
|
323
|
+
}
|
324
|
+
|
325
|
+
if (/^Set-Cookie2?$/i.test(header)) {
|
326
|
+
return null;
|
327
|
+
}
|
328
|
+
|
329
|
+
header = header.toLowerCase();
|
330
|
+
|
331
|
+
for (var h in this.responseHeaders) {
|
332
|
+
if (h.toLowerCase() == header) {
|
333
|
+
return this.responseHeaders[h];
|
334
|
+
}
|
335
|
+
}
|
336
|
+
|
337
|
+
return null;
|
338
|
+
},
|
339
|
+
|
340
|
+
/*
|
341
|
+
Duplicates the behavior of native XMLHttpRequest's getAllResponseHeaders function
|
342
|
+
*/
|
343
|
+
getAllResponseHeaders: function getAllResponseHeaders() {
|
344
|
+
if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) {
|
345
|
+
return "";
|
346
|
+
}
|
347
|
+
|
348
|
+
var headers = "";
|
349
|
+
|
350
|
+
for (var header in this.responseHeaders) {
|
351
|
+
if (this.responseHeaders.hasOwnProperty(header) && !/^Set-Cookie2?$/i.test(header)) {
|
352
|
+
headers += header + ": " + this.responseHeaders[header] + "\r\n";
|
353
|
+
}
|
354
|
+
}
|
355
|
+
|
356
|
+
return headers;
|
357
|
+
},
|
358
|
+
|
359
|
+
/*
|
360
|
+
Places a FakeXMLHttpRequest object into the passed
|
361
|
+
state.
|
362
|
+
*/
|
363
|
+
_readyStateChange: function _readyStateChange(state) {
|
364
|
+
this.readyState = state;
|
365
|
+
|
366
|
+
if (typeof this.onreadystatechange == "function") {
|
367
|
+
this.onreadystatechange();
|
368
|
+
}
|
369
|
+
|
370
|
+
this.dispatchEvent(new _Event("readystatechange"));
|
371
|
+
|
372
|
+
if (this.readyState == FakeXMLHttpRequest.DONE) {
|
373
|
+
this.dispatchEvent(new _Event("load", false, false, this));
|
374
|
+
this.dispatchEvent(new _Event("loadend", false, false, this));
|
375
|
+
}
|
376
|
+
},
|
377
|
+
|
378
|
+
|
379
|
+
/*
|
380
|
+
Sets the FakeXMLHttpRequest object's response headers and
|
381
|
+
places the object into readyState 2
|
382
|
+
*/
|
383
|
+
_setResponseHeaders: function _setResponseHeaders(headers) {
|
384
|
+
this.responseHeaders = {};
|
385
|
+
|
386
|
+
for (var header in headers) {
|
387
|
+
if (headers.hasOwnProperty(header)) {
|
388
|
+
this.responseHeaders[header] = headers[header];
|
389
|
+
}
|
390
|
+
}
|
391
|
+
|
392
|
+
if (this.async) {
|
393
|
+
this._readyStateChange(FakeXMLHttpRequest.HEADERS_RECEIVED);
|
394
|
+
} else {
|
395
|
+
this.readyState = FakeXMLHttpRequest.HEADERS_RECEIVED;
|
396
|
+
}
|
397
|
+
},
|
398
|
+
|
399
|
+
/*
|
400
|
+
Sets the FakeXMLHttpRequest object's response body and
|
401
|
+
if body text is XML, sets responseXML to parsed document
|
402
|
+
object
|
403
|
+
*/
|
404
|
+
_setResponseBody: function _setResponseBody(body) {
|
405
|
+
verifyRequestSent(this);
|
406
|
+
verifyHeadersReceived(this);
|
407
|
+
verifyResponseBodyType(body);
|
408
|
+
|
409
|
+
var chunkSize = this.chunkSize || 10;
|
410
|
+
var index = 0;
|
411
|
+
this.responseText = "";
|
412
|
+
|
413
|
+
do {
|
414
|
+
if (this.async) {
|
415
|
+
this._readyStateChange(FakeXMLHttpRequest.LOADING);
|
416
|
+
}
|
417
|
+
|
418
|
+
this.responseText += body.substring(index, index + chunkSize);
|
419
|
+
index += chunkSize;
|
420
|
+
} while (index < body.length);
|
421
|
+
|
422
|
+
var type = this.getResponseHeader("Content-Type");
|
423
|
+
|
424
|
+
if (this.responseText && (!type || /(text\/xml)|(application\/xml)|(\+xml)/.test(type))) {
|
425
|
+
try {
|
426
|
+
this.responseXML = parseXML(this.responseText);
|
427
|
+
} catch (e) {
|
428
|
+
// Unable to parse XML - no biggie
|
429
|
+
}
|
430
|
+
}
|
431
|
+
|
432
|
+
if (this.async) {
|
433
|
+
this._readyStateChange(FakeXMLHttpRequest.DONE);
|
434
|
+
} else {
|
435
|
+
this.readyState = FakeXMLHttpRequest.DONE;
|
436
|
+
}
|
437
|
+
},
|
438
|
+
|
439
|
+
/*
|
440
|
+
Forces a response on to the FakeXMLHttpRequest object.
|
441
|
+
|
442
|
+
This is the public API for faking responses. This function
|
443
|
+
takes a number status, headers object, and string body:
|
444
|
+
|
445
|
+
```
|
446
|
+
xhr.respond(404, {Content-Type: 'text/plain'}, "Sorry. This object was not found.")
|
447
|
+
|
448
|
+
```
|
449
|
+
*/
|
450
|
+
respond: function respond(status, headers, body) {
|
451
|
+
this._setResponseHeaders(headers || {});
|
452
|
+
this.status = typeof status == "number" ? status : 200;
|
453
|
+
this.statusText = httpStatusCodes[this.status];
|
454
|
+
this._setResponseBody(body || "");
|
455
|
+
}
|
456
|
+
};
|
457
|
+
|
458
|
+
for (var property in FakeXMLHttpRequestProto) {
|
459
|
+
FakeXMLHttpRequest.prototype[property] = FakeXMLHttpRequestProto[property];
|
460
|
+
}
|
461
|
+
|
462
|
+
function verifyState(xhr) {
|
463
|
+
if (xhr.readyState !== FakeXMLHttpRequest.OPENED) {
|
464
|
+
throw new Error("INVALID_STATE_ERR");
|
465
|
+
}
|
466
|
+
|
467
|
+
if (xhr.sendFlag) {
|
468
|
+
throw new Error("INVALID_STATE_ERR");
|
469
|
+
}
|
470
|
+
}
|
471
|
+
|
472
|
+
|
473
|
+
function verifyRequestSent(xhr) {
|
474
|
+
if (xhr.readyState == FakeXMLHttpRequest.DONE) {
|
475
|
+
throw new Error("Request done");
|
476
|
+
}
|
477
|
+
}
|
478
|
+
|
479
|
+
function verifyHeadersReceived(xhr) {
|
480
|
+
if (xhr.async && xhr.readyState != FakeXMLHttpRequest.HEADERS_RECEIVED) {
|
481
|
+
throw new Error("No headers received");
|
482
|
+
}
|
483
|
+
}
|
484
|
+
|
485
|
+
function verifyResponseBodyType(body) {
|
486
|
+
if (typeof body != "string") {
|
487
|
+
var error = new Error("Attempted to respond to fake XMLHttpRequest with " +
|
488
|
+
body + ", which is not a string.");
|
489
|
+
error.name = "InvalidBodyException";
|
490
|
+
throw error;
|
491
|
+
}
|
492
|
+
}
|
493
|
+
var fake_xml_http_request = FakeXMLHttpRequest;
|
494
|
+
|
495
|
+
return fake_xml_http_request;
|
496
|
+
|
497
|
+
}));
|