more tests
This commit is contained in:
parent
b1e70e149e
commit
53a763beab
26
index.js
26
index.js
|
@ -5,8 +5,8 @@
|
|||
* a request API compatible with window.fetch
|
||||
*/
|
||||
|
||||
var parse = require('url').parse;
|
||||
var resolve = require('url').resolve;
|
||||
var parse_url = require('url').parse;
|
||||
var resolve_url = require('url').resolve;
|
||||
var http = require('http');
|
||||
var https = require('https');
|
||||
var zlib = require('zlib');
|
||||
|
@ -41,15 +41,15 @@ function Fetch(url, opts) {
|
|||
|
||||
// wrap http.request into fetch
|
||||
return new Fetch.Promise(function(resolve, reject) {
|
||||
var uri = parse(url);
|
||||
var uri = parse_url(url);
|
||||
|
||||
if (!uri.protocol || !uri.hostname) {
|
||||
reject(Error('only absolute urls are supported'));
|
||||
reject(new Error('only absolute urls are supported'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (uri.protocol !== 'http:' && uri.protocol !== 'https:') {
|
||||
reject(Error('only http(s) protocols are supported'));
|
||||
reject(new Error('only http(s) protocols are supported'));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -73,7 +73,7 @@ function Fetch(url, opts) {
|
|||
, follow: opts.follow || 20
|
||||
, counter: opts.counter || 0
|
||||
, timeout: opts.timeout || 0
|
||||
, compress: opts.compress || true
|
||||
, compress: opts.compress !== false
|
||||
, size: opts.size || 0
|
||||
, body: opts.body
|
||||
, agent: opts.agent
|
||||
|
@ -122,14 +122,18 @@ function Fetch(url, opts) {
|
|||
// handle redirect
|
||||
if (self.isRedirect(res.statusCode)) {
|
||||
if (options.counter >= options.follow) {
|
||||
reject(Error('maximum redirect reached at: ' + uri.href));
|
||||
reject(new Error('maximum redirect reached at: ' + uri.href));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!res.headers.location) {
|
||||
reject(Error('redirect location header missing at: ' + uri.href));
|
||||
reject(new Error('redirect location header missing at: ' + uri.href));
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(Fetch(resolve(uri.href, res.headers.location), options));
|
||||
options.counter++;
|
||||
|
||||
resolve(Fetch(resolve_url(uri.href, res.headers.location), options));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -137,7 +141,7 @@ function Fetch(url, opts) {
|
|||
var body = res.pipe(new stream.PassThrough());
|
||||
var headers = new Headers(res.headers);
|
||||
|
||||
if (headers.has('content-encoding')) {
|
||||
if (options.compress && headers.has('content-encoding')) {
|
||||
var name = headers.get('content-encoding');
|
||||
|
||||
if (name == 'gzip' || name == 'x-gzip') {
|
||||
|
@ -161,7 +165,7 @@ function Fetch(url, opts) {
|
|||
if (typeof options.body === 'string') {
|
||||
req.write(options.body);
|
||||
req.end();
|
||||
} else if (options.body instanceof stream.Readable) {
|
||||
} else if (typeof options.body === 'object' && options.body.pipe) {
|
||||
options.body.pipe(req);
|
||||
} else {
|
||||
req.end();
|
||||
|
|
|
@ -26,7 +26,8 @@
|
|||
"chai": "^1.10.0",
|
||||
"chai-as-promised": "^4.1.1",
|
||||
"mocha": "^2.1.0",
|
||||
"promise": "^6.1.0"
|
||||
"promise": "^6.1.0",
|
||||
"resumer": "0.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"encoding": "^0.1.11"
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
|
||||
var http = require('http');
|
||||
var parse = require('url').parse;
|
||||
var zlib = require('zlib');
|
||||
var stream = require('stream');
|
||||
|
||||
module.exports = TestServer;
|
||||
|
||||
|
@ -31,4 +33,101 @@ TestServer.prototype.router = function(req, res) {
|
|||
res.end('world');
|
||||
}
|
||||
|
||||
if (p === '/plain') {
|
||||
res.statusCode = 200;
|
||||
res.setHeader('Content-Type', 'text/plain');
|
||||
res.end('text');
|
||||
}
|
||||
|
||||
if (p === '/html') {
|
||||
res.statusCode = 200;
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end('<html></html>');
|
||||
}
|
||||
|
||||
if (p === '/json') {
|
||||
res.statusCode = 200;
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.end(JSON.stringify({
|
||||
name: 'value'
|
||||
}));
|
||||
}
|
||||
|
||||
if (p === '/gzip') {
|
||||
res.statusCode = 200;
|
||||
res.setHeader('Content-Type', 'text/plain');
|
||||
res.setHeader('Content-Encoding', 'gzip');
|
||||
zlib.gzip('hello world', function(err, buffer) {
|
||||
res.end(buffer);
|
||||
});
|
||||
}
|
||||
|
||||
if (p === '/deflate') {
|
||||
res.statusCode = 200;
|
||||
res.setHeader('Content-Type', 'text/plain');
|
||||
res.setHeader('Content-Encoding', 'deflate');
|
||||
zlib.deflate('hello world', function(err, buffer) {
|
||||
res.end(buffer);
|
||||
});
|
||||
}
|
||||
|
||||
if (p === '/timeout') {
|
||||
setTimeout(function() {
|
||||
res.statusCode = 200;
|
||||
res.setHeader('Content-Type', 'text/plain');
|
||||
res.end('text');
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
if (p === '/redirect/301') {
|
||||
res.statusCode = 301;
|
||||
res.setHeader('Location', '/inspect');
|
||||
res.end();
|
||||
}
|
||||
|
||||
if (p === '/redirect/302') {
|
||||
res.statusCode = 302;
|
||||
res.setHeader('Location', '/inspect');
|
||||
res.end();
|
||||
}
|
||||
|
||||
if (p === '/redirect/303') {
|
||||
res.statusCode = 303;
|
||||
res.setHeader('Location', '/inspect');
|
||||
res.end();
|
||||
}
|
||||
|
||||
if (p === '/redirect/307') {
|
||||
res.statusCode = 307;
|
||||
res.setHeader('Location', '/inspect');
|
||||
res.end();
|
||||
}
|
||||
|
||||
if (p === '/redirect/308') {
|
||||
res.statusCode = 308;
|
||||
res.setHeader('Location', '/inspect');
|
||||
res.end();
|
||||
}
|
||||
|
||||
if (p === '/redirect/chain') {
|
||||
res.statusCode = 301;
|
||||
res.setHeader('Location', '/redirect/301');
|
||||
res.end();
|
||||
}
|
||||
|
||||
if (p === '/inspect') {
|
||||
res.statusCode = 200;
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
var body = '';
|
||||
req.on('data', function(c) { body += c });
|
||||
req.on('end', function() {
|
||||
res.end(JSON.stringify({
|
||||
method: req.method,
|
||||
url: req.url,
|
||||
headers: req.headers,
|
||||
body: body
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
204
test/test.js
204
test/test.js
|
@ -7,6 +7,8 @@ var expect = chai.expect;
|
|||
var bluebird = require('bluebird');
|
||||
var then = require('promise');
|
||||
var stream = require('stream');
|
||||
var resumer = require('resumer');
|
||||
|
||||
var TestServer = require('./server');
|
||||
|
||||
// test subjects
|
||||
|
@ -18,7 +20,7 @@ fetch.Promise = fetch.Promise || bluebird;
|
|||
|
||||
var url, opts, local, base;
|
||||
|
||||
describe('Fetch', function() {
|
||||
describe('node-fetch', function() {
|
||||
|
||||
before(function(done) {
|
||||
local = new TestServer();
|
||||
|
@ -42,10 +44,11 @@ describe('Fetch', function() {
|
|||
var old = fetch.Promise;
|
||||
fetch.Promise = then;
|
||||
expect(fetch(url)).to.be.an.instanceof(then);
|
||||
expect(fetch(url)).to.not.be.an.instanceof(bluebird);
|
||||
fetch.Promise = old;
|
||||
});
|
||||
|
||||
it('should throw error when no promise implementation found', function() {
|
||||
it('should throw error when no promise implementation are found', function() {
|
||||
url = 'http://example.com/';
|
||||
var old = fetch.Promise;
|
||||
fetch.Promise = undefined;
|
||||
|
@ -80,12 +83,201 @@ describe('Fetch', function() {
|
|||
return fetch(url).then(function(res) {
|
||||
expect(res).to.be.an.instanceof(Response);
|
||||
expect(res.headers).to.be.an.instanceof(Headers);
|
||||
expect(res.headers.get('content-type')).to.equal('text/plain');
|
||||
expect(res.status).to.equal(200);
|
||||
expect(res.statusText).to.equal('OK');
|
||||
expect(res.url).to.equal(url);
|
||||
expect(res.body).to.be.an.instanceof(stream.Transform);
|
||||
expect(res.bodyUsed).to.be.false;
|
||||
|
||||
expect(res.url).to.equal(url);
|
||||
expect(res.status).to.equal(200);
|
||||
expect(res.statusText).to.equal('OK');
|
||||
});
|
||||
});
|
||||
|
||||
it('should accept plain text response', function() {
|
||||
url = base + '/plain';
|
||||
return fetch(url).then(function(res) {
|
||||
expect(res.headers.get('content-type')).to.equal('text/plain');
|
||||
return res.text().then(function(result) {
|
||||
expect(res.bodyUsed).to.be.true;
|
||||
expect(result).to.be.a('string');
|
||||
expect(result).to.equal('text');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should accept html response (like plain text)', function() {
|
||||
url = base + '/html';
|
||||
return fetch(url).then(function(res) {
|
||||
expect(res.headers.get('content-type')).to.equal('text/html');
|
||||
return res.text().then(function(result) {
|
||||
expect(res.bodyUsed).to.be.true;
|
||||
expect(result).to.be.a('string');
|
||||
expect(result).to.equal('<html></html>');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should accept json response', function() {
|
||||
url = base + '/json';
|
||||
return fetch(url).then(function(res) {
|
||||
expect(res.headers.get('content-type')).to.equal('application/json');
|
||||
return res.json().then(function(result) {
|
||||
expect(res.bodyUsed).to.be.true;
|
||||
expect(result).to.be.an('object');
|
||||
expect(result).to.deep.equal({ name: 'value' });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should send request with custom headers', function() {
|
||||
url = base + '/inspect';
|
||||
opts = {
|
||||
headers: { 'x-custom-header': 'abc' }
|
||||
};
|
||||
return fetch(url, opts).then(function(res) {
|
||||
return res.json();
|
||||
}).then(function(res) {
|
||||
expect(res.headers['x-custom-header']).to.equal('abc');
|
||||
});
|
||||
});
|
||||
|
||||
it('should follow redirect code 301', function() {
|
||||
url = base + '/redirect/301';
|
||||
return fetch(url).then(function(res) {
|
||||
expect(res.url).to.equal(base + '/inspect');
|
||||
expect(res.status).to.equal(200);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should follow redirect code 302', function() {
|
||||
url = base + '/redirect/302';
|
||||
return fetch(url).then(function(res) {
|
||||
expect(res.url).to.equal(base + '/inspect');
|
||||
expect(res.status).to.equal(200);
|
||||
});
|
||||
});
|
||||
|
||||
it('should follow redirect code 303', function() {
|
||||
url = base + '/redirect/303';
|
||||
return fetch(url).then(function(res) {
|
||||
expect(res.url).to.equal(base + '/inspect');
|
||||
expect(res.status).to.equal(200);
|
||||
});
|
||||
});
|
||||
|
||||
it('should follow redirect code 307', function() {
|
||||
url = base + '/redirect/307';
|
||||
return fetch(url).then(function(res) {
|
||||
expect(res.url).to.equal(base + '/inspect');
|
||||
expect(res.status).to.equal(200);
|
||||
});
|
||||
});
|
||||
|
||||
it('should follow redirect code 308', function() {
|
||||
url = base + '/redirect/308';
|
||||
return fetch(url).then(function(res) {
|
||||
expect(res.url).to.equal(base + '/inspect');
|
||||
expect(res.status).to.equal(200);
|
||||
});
|
||||
});
|
||||
|
||||
it('should follow redirect chain', function() {
|
||||
url = base + '/redirect/chain';
|
||||
return fetch(url).then(function(res) {
|
||||
expect(res.url).to.equal(base + '/inspect');
|
||||
expect(res.status).to.equal(200);
|
||||
});
|
||||
});
|
||||
|
||||
it('should obey maximum redirect limit', function() {
|
||||
url = base + '/redirect/chain';
|
||||
opts = {
|
||||
follow: 1
|
||||
};
|
||||
return expect(fetch(url, opts)).to.eventually.be.rejectedWith(Error);
|
||||
});
|
||||
|
||||
it('should decompress gzip response', function() {
|
||||
url = base + '/gzip';
|
||||
return fetch(url).then(function(res) {
|
||||
expect(res.headers.get('content-type')).to.equal('text/plain');
|
||||
return res.text().then(function(result) {
|
||||
expect(result).to.be.a('string');
|
||||
expect(result).to.equal('hello world');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should decompress deflate response', function() {
|
||||
url = base + '/deflate';
|
||||
return fetch(url).then(function(res) {
|
||||
expect(res.headers.get('content-type')).to.equal('text/plain');
|
||||
return res.text().then(function(result) {
|
||||
expect(result).to.be.a('string');
|
||||
expect(result).to.equal('hello world');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow disabling auto decompression', function() {
|
||||
url = base + '/gzip';
|
||||
opts = {
|
||||
compress: false
|
||||
};
|
||||
return fetch(url, opts).then(function(res) {
|
||||
expect(res.headers.get('content-type')).to.equal('text/plain');
|
||||
return res.text().then(function(result) {
|
||||
expect(result).to.be.a('string');
|
||||
expect(result).to.not.equal('hello world');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow custom timeout', function() {
|
||||
url = base + '/timeout';
|
||||
opts = {
|
||||
timeout: 100
|
||||
};
|
||||
return expect(fetch(url, opts)).to.eventually.be.rejectedWith(Error);
|
||||
});
|
||||
|
||||
it('should allow POST request', function() {
|
||||
url = base + '/inspect';
|
||||
opts = {
|
||||
method: 'POST'
|
||||
};
|
||||
return fetch(url, opts).then(function(res) {
|
||||
return res.json();
|
||||
}).then(function(res) {
|
||||
expect(res.method).to.equal('POST');
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow POST request with string body', function() {
|
||||
url = base + '/inspect';
|
||||
opts = {
|
||||
method: 'POST'
|
||||
, body: 'a=1'
|
||||
};
|
||||
return fetch(url, opts).then(function(res) {
|
||||
return res.json();
|
||||
}).then(function(res) {
|
||||
expect(res.method).to.equal('POST');
|
||||
expect(res.body).to.equal('a=1');
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow POST request with readable stream as body', function() {
|
||||
url = base + '/inspect';
|
||||
opts = {
|
||||
method: 'POST'
|
||||
, body: resumer().queue('a=1').end()
|
||||
};
|
||||
return fetch(url, opts).then(function(res) {
|
||||
return res.json();
|
||||
}).then(function(res) {
|
||||
expect(res.method).to.equal('POST');
|
||||
expect(res.body).to.equal('a=1');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue