Merge tag '3.8.0'

This commit is contained in:
Douglas Christopher Wilson 2014-05-21 02:08:04 -04:00
commit db4a061ed6
19 changed files with 612 additions and 157 deletions

View File

@ -7,3 +7,4 @@ matrix:
- node_js: "0.11"
fast_finish: true
script: "npm run-script test-travis"
after_script: "npm install coveralls@2.10.0 && cat ./coverage/lcov.info | coveralls"

View File

@ -13,6 +13,7 @@ unreleased
- `app.set('trust proxy', '10.0.0.1, 10.0.0.2')` trust list
- `app.set('trust proxy', false)` turn off
- `app.set('trust proxy', true)` trust everything
* set proper `charset` in `Content-Type` for `res.send`
* update type-is to 1.2.0
- support suffix matching
@ -112,6 +113,17 @@ unreleased
- `app.route()` - Proxy to the app's `Router#route()` method to create a new route
- Router & Route - public API
3.8.0 / 2014-05-21
==================
* keep previous `Content-Type` for `res.jsonp`
* set proper `charset` in `Content-Type` for `res.send`
* update connect to 2.17.1
- fix `res.charset` appending charset when `content-type` has one
- deps: express-session@1.2.0
- deps: morgan@1.1.1
- deps: serve-index@1.0.3
3.7.0 / 2014-05-18
==================

View File

@ -9,6 +9,7 @@ var escapeHtml = require('escape-html');
var sign = require('cookie-signature').sign;
var normalizeType = require('./utils').normalizeType;
var normalizeTypes = require('./utils').normalizeTypes;
var setCharset = require('./utils').setCharset;
var contentDisposition = require('./utils').contentDisposition;
var deprecate = require('./utils').deprecate;
var etag = require('./utils').etag;
@ -83,6 +84,8 @@ res.links = function(links){
res.send = function(body){
var req = this.req;
var head = 'HEAD' == req.method;
var type;
var encoding;
var len;
// settings
@ -137,6 +140,17 @@ res.send = function(body){
}
}
// write strings in utf-8
if ('string' === typeof body) {
encoding = 'utf8';
type = this.get('Content-Type');
// reflect this in content-type
if ('string' === typeof type) {
this.set('Content-Type', setCharset(type, 'utf-8'));
}
}
// freshness
if (req.fresh) this.statusCode = 304;
@ -149,7 +163,8 @@ res.send = function(body){
}
// respond
this.end(head ? null : body);
this.end((head ? null : body), encoding);
return this;
};

View File

@ -8,6 +8,11 @@ var basename = require('path').basename;
var deprecate = require('util').deprecate;
var proxyaddr = require('proxy-addr');
/**
* Simple detection of charset parameter in content-type
*/
var charsetRegExp = /;\s*charset\s*=/;
/**
* Deprecate function, like core `util.deprecate`,
* but with NODE_ENV and color support.
@ -187,3 +192,34 @@ exports.compileTrust = function(val) {
return proxyaddr.compile(val || []);
}
/**
* Set the charset in a given Content-Type string.
*
* @param {String} type
* @param {String} charset
* @return {String}
* @api private
*/
exports.setCharset = function(type, charset){
if (!type || !charset) return type;
var exists = charsetRegExp.test(type);
// removing existing charset
if (exists) {
var parts = type.split(';');
for (var i = 1; i < parts.length; i++) {
if (charsetRegExp.test(';' + parts[i])) {
parts.splice(i, 1);
break;
}
}
type = parts.join(';');
}
return type + '; charset=' + charset;
};

View File

@ -68,9 +68,8 @@
},
"devDependencies": {
"after": "0.8.1",
"coveralls": "2.10.0",
"istanbul": "0.2.10",
"mocha": "~1.18.2",
"mocha": "~1.19.0",
"should": "~3.3.1",
"supertest": "~0.12.0",
"connect-redis": "~2.0.0",
@ -93,6 +92,6 @@
"prepublish": "npm prune",
"test": "mocha --require test/support/env --reporter dot --check-leaks test/ test/acceptance/",
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require test/support/env --reporter dot --check-leaks test/ test/acceptance/",
"test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --require test/support/env --reporter spec --check-leaks test/ test/acceptance/ && (cat ./coverage/lcov.info | coveralls || true)"
"test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --require test/support/env --reporter spec --check-leaks test/ test/acceptance/"
}
}

View File

@ -7,16 +7,43 @@ describe('content-negotiation', function(){
it('should default to text/html', function(done){
request(app)
.get('/')
.expect('<ul><li>Tobi</li><li>Loki</li><li>Jane</li></ul>')
.end(done);
.expect(200, '<ul><li>Tobi</li><li>Loki</li><li>Jane</li></ul>', done)
})
it('should accept to text/plain', function(done){
request(app)
.get('/')
.set('Accept', 'text/plain')
.expect(' - Tobi\n - Loki\n - Jane\n')
.end(done);
.expect(200, ' - Tobi\n - Loki\n - Jane\n', done)
})
it('should accept to application/json', function(done){
request(app)
.get('/')
.set('Accept', 'application/json')
.expect(200, '[{"name":"Tobi"},{"name":"Loki"},{"name":"Jane"}]', done)
})
})
})
describe('GET /users', function(){
it('should default to text/html', function(done){
request(app)
.get('/users')
.expect(200, '<ul><li>Tobi</li><li>Loki</li><li>Jane</li></ul>', done)
})
it('should accept to text/plain', function(done){
request(app)
.get('/users')
.set('Accept', 'text/plain')
.expect(200, ' - Tobi\n - Loki\n - Jane\n', done)
})
it('should accept to application/json', function(done){
request(app)
.get('/users')
.set('Accept', 'application/json')
.expect(200, '[{"name":"Tobi"},{"name":"Loki"},{"name":"Jane"}]', done)
})
})
})

View File

@ -18,6 +18,35 @@ describe('cookies', function(){
done()
})
})
it('should respond to cookie', function(done){
request(app)
.post('/')
.send({ remember: 1 })
.expect(302, function(err, res){
if (err) return done(err)
request(app)
.get('/')
.set('Cookie', res.headers['set-cookie'][0])
.expect(200, /Remembered/, done)
})
})
})
describe('GET /forget', function(){
it('should clear cookie', function(done){
request(app)
.post('/')
.send({ remember: 1 })
.expect(302, function(err, res){
if (err) return done(err)
request(app)
.get('/forget')
.set('Cookie', res.headers['set-cookie'][0])
.expect('Set-Cookie', /remember=;/)
.expect(302, done)
})
})
})
describe('POST /', function(){
@ -25,10 +54,20 @@ describe('cookies', function(){
request(app)
.post('/')
.send({ remember: 1 })
.end(function(err, res){
.expect(302, function(err, res){
res.headers.should.have.property('set-cookie')
done()
})
})
it('should no set cookie w/o reminder', function(done){
request(app)
.post('/')
.send({})
.expect(302, function(err, res){
res.headers.should.not.have.property('set-cookie')
done()
})
})
})
})
})

View File

@ -7,10 +7,38 @@ describe('mvc', function(){
it('should redirect to /users', function(done){
request(app)
.get('/')
.expect('Location', '/users')
.expect(302, done)
})
})
describe('GET /pet/0', function(){
it('should get pet', function(done){
request(app)
.get('/pet/0')
.expect(200, /Tobi/, done)
})
})
describe('GET /pet/0/edit', function(){
it('should get pet edit page', function(done){
request(app)
.get('/pet/0/edit')
.expect(/<form/)
.expect(200, /Tobi/, done)
})
})
describe('PUT /pet/2', function(){
it('should update the pet', function(done){
request(app)
.put('/pet/3')
.send({ pet: { name: 'Boots' } })
.end(function(err, res){
res.should.have.status(302);
res.headers.location.should.include('/users');
done();
if (err) return done(err);
request(app)
.get('/pet/3/edit')
.expect(200, /Boots/, done)
})
})
})
@ -65,11 +93,8 @@ describe('mvc', function(){
it('should display the edit form', function(done){
request(app)
.get('/user/1/edit')
.end(function(err, res){
res.text.should.include('<h1>Guillermo</h1>');
res.text.should.include('value="put"');
done();
})
.expect(/Guillermo/)
.expect(200, /<form/, done)
})
})
@ -79,13 +104,26 @@ describe('mvc', function(){
.put('/user/1')
.send({ user: { name: 'Tobo' }})
.end(function(err, res){
if (err) return done(err);
request(app)
.get('/user/1/edit')
.end(function(err, res){
res.text.should.include('<h1>Tobo</h1>');
done();
})
.expect(200, /Tobo/, done)
})
})
})
})
describe('POST /user/:id/pet', function(){
it('should create a pet for user', function(done){
request(app)
.post('/user/2/pet')
.send({ pet: { name: 'Snickers' }})
.expect('Location', '/user/2')
.expect(302, function(err, res){
if (err) return done(err)
request(app)
.get('/user/2')
.expect(200, /Snickers/, done)
})
})
})
})

View File

@ -24,11 +24,72 @@ describe('web-service', function(){
it('should respond users json', function(done){
request(app)
.get('/api/users?api-key=foo')
.end(function(err, res){
res.should.be.json;
res.text.should.equal('[{"name":"tobi"},{"name":"loki"},{"name":"jane"}]');
done();
});
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(200, '[{"name":"tobi"},{"name":"loki"},{"name":"jane"}]', done)
})
})
})
describe('GET /api/repos', function(){
describe('without an api key', function(){
it('should respond with 400 bad request', function(done){
request(app)
.get('/api/repos')
.expect(400, done);
})
})
describe('with an invalid api key', function(){
it('should respond with 401 unauthorized', function(done){
request(app)
.get('/api/repos?api-key=rawr')
.expect(401, done);
})
})
describe('with a valid api key', function(){
it('should respond repos json', function(done){
request(app)
.get('/api/repos?api-key=foo')
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(/"name":"express"/)
.expect(/"url":"http:\/\/github.com\/visionmedia\/express"/)
.expect(200, done)
})
})
})
describe('GET /api/user/:name/repos', function(){
describe('without an api key', function(){
it('should respond with 400 bad request', function(done){
request(app)
.get('/api/user/loki/repos')
.expect(400, done);
})
})
describe('with an invalid api key', function(){
it('should respond with 401 unauthorized', function(done){
request(app)
.get('/api/user/loki/repos?api-key=rawr')
.expect(401, done);
})
})
describe('with a valid api key', function(){
it('should respond user repos json', function(done){
request(app)
.get('/api/user/loki/repos?api-key=foo')
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(/"name":"stylus"/)
.expect(/"url":"http:\/\/github.com\/learnboost\/stylus"/)
.expect(200, done)
})
it('should 404 with unknown user', function(done){
request(app)
.get('/api/user/bob/repos?api-key=foo')
.expect(404, done)
})
})
})
@ -45,4 +106,4 @@ describe('web-service', function(){
});
})
})
})
})

View File

@ -84,3 +84,12 @@ describe('in production', function(){
process.env.NODE_ENV = 'test';
})
})
describe('without NODE_ENV', function(){
it('should default to development', function(){
process.env.NODE_ENV = '';
var app = express();
app.get('env').should.equal('development');
process.env.NODE_ENV = 'test';
})
})

View File

@ -37,6 +37,11 @@ describe('app', function(){
});
})
it('should fail if not given fn', function(){
var app = express();
app.param.bind(app, ':name', 'bob').should.throw();
})
})
describe('.param(names, fn)', function(){
@ -151,5 +156,43 @@ describe('app', function(){
.get('/foo/bob')
.expect('2 2 foo,bob', done);
})
it('should catch thrown error', function(done){
var app = express();
app.param('id', function(req, res, next, id){
throw new Error('err!');
});
app.get('/user/:id', function(req, res){
var id = req.params.id;
res.send('' + id);
});
request(app)
.get('/user/123')
.expect(500, done);
})
it('should defer to next route', function(done){
var app = express();
app.param('id', function(req, res, next, id){
next('route');
});
app.get('/user/:id', function(req, res){
var id = req.params.id;
res.send('' + id);
});
app.get('/:name/123', function(req, res){
res.send('name');
});
request(app)
.get('/user/123')
.expect('name', done);
})
})
})

View File

@ -54,6 +54,27 @@ describe('app', function(){
})
})
it('should handle render error throws', function(done){
var app = express();
function View(name, options){
this.name = name;
this.path = 'fale';
}
View.prototype.render = function(options, fn){
throw new Error('err!');
};
app.set('view', View);
app.render('something', function(err, str){
err.should.be.ok;
err.message.should.equal('err!');
done();
})
})
describe('when the file does not exist', function(){
it('should provide a helpful error', function(done){
var app = express();
@ -132,6 +153,68 @@ describe('app', function(){
})
})
})
describe('caching', function(){
it('should always lookup view without cache', function(done){
var app = express();
var count = 0;
function View(name, options){
this.name = name;
this.path = 'fake';
count++;
}
View.prototype.render = function(options, fn){
fn(null, 'abstract engine');
};
app.set('view cache', false);
app.set('view', View);
app.render('something', function(err, str){
if (err) return done(err);
count.should.equal(1);
str.should.equal('abstract engine');
app.render('something', function(err, str){
if (err) return done(err);
count.should.equal(2);
str.should.equal('abstract engine');
done();
})
})
})
it('should cache with "view cache" setting', function(done){
var app = express();
var count = 0;
function View(name, options){
this.name = name;
this.path = 'fake';
count++;
}
View.prototype.render = function(options, fn){
fn(null, 'abstract engine');
};
app.set('view cache', true);
app.set('view', View);
app.render('something', function(err, str){
if (err) return done(err);
count.should.equal(1);
str.should.equal('abstract engine');
app.render('something', function(err, str){
if (err) return done(err);
count.should.equal(1);
str.should.equal('abstract engine');
done();
})
})
})
})
})
describe('.render(name, options, fn)', function(){
@ -175,5 +258,37 @@ describe('app', function(){
done();
})
})
describe('caching', function(){
it('should cache with cache option', function(done){
var app = express();
var count = 0;
function View(name, options){
this.name = name;
this.path = 'fake';
count++;
}
View.prototype.render = function(options, fn){
fn(null, 'abstract engine');
};
app.set('view cache', false);
app.set('view', View);
app.render('something', {cache: true}, function(err, str){
if (err) return done(err);
count.should.equal(1);
str.should.equal('abstract engine');
app.render('something', {cache: true}, function(err, str){
if (err) return done(err);
count.should.equal(1);
str.should.equal('abstract engine');
done();
})
})
})
})
})
})

View File

@ -0,0 +1,36 @@
var express = require('../')
, request = require('supertest');
describe('req', function(){
describe('.acceptsEncodings', function(){
it('should be true if encoding accpeted', function(done){
var app = express();
app.use(function(req, res){
req.acceptsEncoding('gzip').should.be.ok;
req.acceptsEncoding('deflate').should.be.ok;
res.end();
});
request(app)
.get('/')
.set('Accept-Encoding', ' gzip, deflate')
.expect(200, done);
})
it('should be false if encoding not accpeted', function(done){
var app = express();
app.use(function(req, res){
req.acceptsEncoding('bogus').should.not.be.ok;
res.end();
});
request(app)
.get('/')
.set('Accept-Encoding', ' gzip, deflate')
.expect(200, done);
})
})
})

View File

@ -0,0 +1,53 @@
var express = require('../')
, request = require('supertest');
describe('req', function(){
describe('.acceptsLanguage', function(){
it('should be true if language accpeted', function(done){
var app = express();
app.use(function(req, res){
req.acceptsLanguage('en-us').should.be.ok;
req.acceptsLanguage('en').should.be.ok;
res.end();
});
request(app)
.get('/')
.set('Accept-Language', 'en;q=.5, en-us')
.expect(200, done);
})
it('should be false if language not accpeted', function(done){
var app = express();
app.use(function(req, res){
req.acceptsLanguage('es').should.not.be.ok;
res.end();
});
request(app)
.get('/')
.set('Accept-Language', 'en;q=.5, en-us')
.expect(200, done);
})
describe('when Accept-Language is not present', function(){
it('should always return true', function(done){
var app = express();
app.use(function(req, res){
req.acceptsLanguage('en').should.be.ok;
req.acceptsLanguage('es').should.be.ok;
req.acceptsLanguage('jp').should.be.ok;
res.end();
});
request(app)
.get('/')
.expect(200, done);
})
})
})
})

View File

@ -1,5 +1,6 @@
var express = require('../');
var assert = require('assert');
var express = require('..');
function req(ret) {
return {
@ -27,5 +28,11 @@ describe('req', function(){
ret.type = 'users';
req('users=0-').range(Infinity).should.eql(ret);
})
it('should return undefined if no range', function(){
var ret = [{ start: 0, end: 50 }, { start: 60, end: 100 }];
ret.type = 'bytes';
assert(req('').range(120) === undefined);
})
})
})

View File

@ -36,7 +36,7 @@ describe('res', function(){
app.use(function(req, res){
res.attachment('/path/to/image.png');
res.send('foo');
res.send(new Buffer(4));
});
request(app)

View File

@ -27,7 +27,7 @@ describe('res', function(){
request(app)
.get('/')
.expect('Content-Type', 'application/vnd.example+json')
.expect('Content-Type', 'application/vnd.example+json; charset=utf-8')
.expect(200, '{"hello":"world"}', done);
})
@ -41,11 +41,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(err, res){
res.headers.should.have.property('content-type', 'application/json');
res.text.should.equal('null');
done();
})
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(200, 'null', done)
})
it('should respond with json for Number', function(done){
@ -57,12 +54,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(err, res){
res.statusCode.should.equal(200);
res.headers.should.have.property('content-type', 'application/json');
res.text.should.equal('300');
done();
})
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(200, '300', done)
})
it('should respond with json for String', function(done){
@ -74,12 +67,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(err, res){
res.statusCode.should.equal(200);
res.headers.should.have.property('content-type', 'application/json');
res.text.should.equal('"str"');
done();
})
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(200, '"str"', done)
})
})
@ -93,11 +82,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(err, res){
res.headers.should.have.property('content-type', 'application/json');
res.text.should.equal('["foo","bar","baz"]');
done();
})
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(200, '["foo","bar","baz"]', done)
})
})
@ -111,11 +97,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(err, res){
res.headers.should.have.property('content-type', 'application/json');
res.text.should.equal('{"name":"tobi"}');
done();
})
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(200, '{"name":"tobi"}', done)
})
})
@ -135,10 +118,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(err, res){
res.text.should.equal('{"name":"tobi"}');
done();
});
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(200, '{"name":"tobi"}', done)
})
})
@ -159,10 +140,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(err, res){
res.text.should.equal('{\n "name": "tobi",\n "age": 2\n}');
done();
});
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(200, '{\n "name": "tobi",\n "age": 2\n}', done)
})
})
})
@ -177,12 +156,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(err, res){
res.statusCode.should.equal(201);
res.headers.should.have.property('content-type', 'application/json');
res.text.should.equal('{"id":1}');
done();
})
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(201, '{"id":1}', done)
})
})
@ -196,12 +171,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(err, res){
res.statusCode.should.equal(201);
res.headers.should.have.property('content-type', 'application/json');
res.text.should.equal('{"id":1}');
done();
})
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(201, '{"id":1}', done)
})
it('should use status as second number for backwards compat', function(done){
@ -213,12 +184,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(err, res){
res.statusCode.should.equal(201);
res.headers.should.have.property('content-type', 'application/json');
res.text.should.equal('200');
done();
})
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(201, '200', done)
})
})
})

View File

@ -46,11 +46,8 @@ describe('res', function(){
request(app)
.get('/?callback[a]=something')
.end(function(err, res){
res.headers.should.have.property('content-type', 'application/json');
res.text.should.equal('{"count":1}');
done();
})
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(200, '{"count":1}', done)
})
it('should allow renaming callback', function(done){
@ -129,7 +126,7 @@ describe('res', function(){
request(app)
.get('/')
.expect('Content-Type', 'application/vnd.example+json')
.expect('Content-Type', 'application/vnd.example+json; charset=utf-8')
.expect(200, '{"hello":"world"}', done);
})
@ -157,11 +154,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(err, res){
res.headers.should.have.property('content-type', 'application/json');
res.text.should.equal('null');
done();
})
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(200, 'null', done)
})
})
@ -175,11 +169,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(err, res){
res.headers.should.have.property('content-type', 'application/json');
res.text.should.equal('["foo","bar","baz"]');
done();
})
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(200, '["foo","bar","baz"]', done)
})
})
@ -193,11 +184,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(err, res){
res.headers.should.have.property('content-type', 'application/json');
res.text.should.equal('{"name":"tobi"}');
done();
})
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(200, '{"name":"tobi"}', done)
})
})
@ -211,11 +199,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(err, res){
res.headers.should.have.property('content-type', 'application/json');
res.text.should.equal('null');
done();
})
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(200, 'null', done)
})
it('should respond with json for Number', function(done){
@ -227,12 +212,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(err, res){
res.statusCode.should.equal(200);
res.headers.should.have.property('content-type', 'application/json');
res.text.should.equal('300');
done();
})
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(200, '300', done)
})
it('should respond with json for String', function(done){
@ -244,12 +225,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(err, res){
res.statusCode.should.equal(200);
res.headers.should.have.property('content-type', 'application/json');
res.text.should.equal('"str"');
done();
})
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(200, '"str"', done)
})
})
@ -269,10 +246,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(err, res){
res.text.should.equal('{"name":"tobi"}');
done();
});
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(200, '{"name":"tobi"}', done)
})
})
@ -293,10 +268,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(err, res){
res.text.should.equal('{\n "name": "tobi",\n "age": 2\n}');
done();
});
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(200, '{\n "name": "tobi",\n "age": 2\n}', done)
})
})
})
@ -311,12 +284,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(err, res){
res.statusCode.should.equal(201);
res.headers.should.have.property('content-type', 'application/json');
res.text.should.equal('{"id":1}');
done();
})
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(201, '{"id":1}', done)
})
})
@ -330,12 +299,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(err, res){
res.statusCode.should.equal(201);
res.headers.should.have.property('content-type', 'application/json');
res.text.should.equal('{"id":1}');
done();
})
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(201, '{"id":1}', done)
})
it('should use status as second number for backwards compat', function(done){
@ -347,12 +312,22 @@ describe('res', function(){
request(app)
.get('/')
.end(function(err, res){
res.statusCode.should.equal(201);
res.headers.should.have.property('content-type', 'application/json');
res.text.should.equal('200');
done();
})
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(201, '200', done)
})
})
it('should not override previous Content-Types', function(done){
var app = express();
app.get('/', function(req, res){
res.type('application/vnd.example+json');
res.jsonp({ hello: 'world' });
});
request(app)
.get('/')
.expect('content-type', 'application/vnd.example+json; charset=utf-8')
.expect(200, '{"hello":"world"}', done)
})
})

View File

@ -140,8 +140,33 @@ describe('res', function(){
request(app)
.get('/')
.expect('Content-Type', 'text/plain; charset=utf-8')
.expect('hey')
.expect(200, done);
.expect(200, 'hey', done);
})
it('should override charset in Content-Type', function(done){
var app = express();
app.use(function(req, res){
res.set('Content-Type', 'text/plain; charset=iso-8859-1').send('hey');
});
request(app)
.get('/')
.expect('Content-Type', 'text/plain; charset=utf-8')
.expect(200, 'hey', done);
})
it('should keep charset in Content-Type for Buffers', function(done){
var app = express();
app.use(function(req, res){
res.set('Content-Type', 'text/plain; charset=iso-8859-1').send(new Buffer('hi'));
});
request(app)
.get('/')
.expect('Content-Type', 'text/plain; charset=iso-8859-1')
.expect(200, 'hi', done);
})
})
@ -205,11 +230,8 @@ describe('res', function(){
request(app)
.get('/')
.end(function(err, res){
res.headers.should.have.property('content-type', 'application/json');
res.text.should.equal('{"name":"tobi"}');
done();
})
.expect('Content-Type', 'application/json; charset=utf-8')
.expect(200, '{"name":"tobi"}', done)
})
})