more tests

This commit is contained in:
David Frank 2015-01-27 23:33:06 +08:00
parent b1e70e149e
commit 53a763beab
4 changed files with 314 additions and 18 deletions

View File

@ -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();

View File

@ -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"

View File

@ -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
}));
});
}
}

View File

@ -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');
});
});