Merge pull request #525 from targos/eslint

eslint
This commit is contained in:
jongleberry 2015-10-13 10:18:05 -07:00
commit e6d76da1e5
72 changed files with 679 additions and 656 deletions

16
.editorconfig Normal file
View File

@ -0,0 +1,16 @@
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[Makefile]
indent_style = tab

10
.eslintrc Normal file
View File

@ -0,0 +1,10 @@
# Support ES2016 features
parser: babel-eslint
extends: standard
rules:
eqeqeq: 0
semi: [2, always]
space-before-function-paren: [2, never]
yoda: 0

View File

@ -1,7 +1,5 @@
SRC = lib/*.js SRC = lib/*.js
include node_modules/make-lint/index.mk
REQUIRED = --require should --require should-http REQUIRED = --require should --require should-http
TESTS = test/application/* \ TESTS = test/application/* \
@ -10,6 +8,9 @@ TESTS = test/application/* \
test/response/* \ test/response/* \
test/experimental/index.js test/experimental/index.js
lint:
@./node_modules/.bin/eslint lib test
test: test:
@NODE_ENV=test node \ @NODE_ENV=test node \
./node_modules/.bin/_mocha \ ./node_modules/.bin/_mocha \
@ -26,7 +27,7 @@ test-cov:
$(TESTS) \ $(TESTS) \
--bail --bail
test-travis: test-travis: lint
@NODE_ENV=test node \ @NODE_ENV=test node \
./node_modules/.bin/istanbul cover \ ./node_modules/.bin/istanbul cover \
./node_modules/.bin/_mocha \ ./node_modules/.bin/_mocha \
@ -39,4 +40,4 @@ test-travis:
bench: bench:
@$(MAKE) -C benchmarks @$(MAKE) -C benchmarks
.PHONY: test bench .PHONY: lint test bench

View File

@ -119,10 +119,10 @@ module.exports = class Application extends Emitter {
res.statusCode = 404; res.statusCode = 404;
const ctx = this.createContext(req, res); const ctx = this.createContext(req, res);
onFinished(res, ctx.onerror); onFinished(res, ctx.onerror);
fn.call(ctx).then(function () { fn.call(ctx).then(function() {
respond.call(ctx); respond.call(ctx);
}).catch(ctx.onerror); }).catch(ctx.onerror);
} };
} }
/** /**
@ -170,7 +170,7 @@ module.exports = class Application extends Emitter {
console.error(); console.error();
} }
} };
/** /**
* Response helper. * Response helper.

View File

@ -48,8 +48,8 @@ const proto = module.exports = {
originalUrl: this.originalUrl, originalUrl: this.originalUrl,
req: '<original node req>', req: '<original node req>',
res: '<original node res>', res: '<original node res>',
socket: '<original node socket>', socket: '<original node socket>'
} };
}, },
/** /**

View File

@ -44,8 +44,11 @@
}, },
"devDependencies": { "devDependencies": {
"babel": "^5.0.0", "babel": "^5.0.0",
"babel-eslint": "^4.1.3",
"eslint": "^1.6.0",
"eslint-config-standard": "^4.4.0",
"eslint-plugin-standard": "^1.3.1",
"istanbul": "^0.3.22", "istanbul": "^0.3.22",
"make-lint": "^1.0.1",
"mocha": "^2.0.1", "mocha": "^2.0.1",
"should": "^6.0.3", "should": "^6.0.3",
"should-http": "0.0.3", "should-http": "0.0.3",

5
test/.eslintrc Normal file
View File

@ -0,0 +1,5 @@
env:
mocha: true
rules:
space-before-blocks: [2, {functions: never, keywords: always}]

View File

@ -12,23 +12,23 @@ describe('app.context', function(){
it('should merge properties', function(done){ it('should merge properties', function(done){
app1.use(function *(next){ app1.use(function *(next){
assert.equal(this.msg, 'hello') assert.equal(this.msg, 'hello');
this.status = 204 this.status = 204;
}); });
request(app1.listen()) request(app1.listen())
.get('/') .get('/')
.expect(204, done); .expect(204, done);
}) });
it('should not affect the original prototype', function(done){ it('should not affect the original prototype', function(done){
app2.use(function *(next){ app2.use(function *(next){
assert.equal(this.msg, undefined) assert.equal(this.msg, undefined);
this.status = 204; this.status = 204;
}); });
request(app2.listen()) request(app2.listen())
.get('/') .get('/')
.expect(204, done); .expect(204, done);
}) });
}) });

View File

@ -22,7 +22,7 @@ describe('app', function(){
request(app.listen()) request(app.listen())
.get('/') .get('/')
.end(function(){}); .end(function(){});
}) });
it('should not .writeHead when !socket.writable', function(done){ it('should not .writeHead when !socket.writable', function(done){
const app = new Koa(); const app = new Koa();
@ -36,7 +36,7 @@ describe('app', function(){
this.res.end = function(){ this.res.end = function(){
throw new Error('response sent'); throw new Error('response sent');
}; };
}) });
// hackish, but the response should occur in a single tick // hackish, but the response should occur in a single tick
setImmediate(done); setImmediate(done);
@ -44,7 +44,7 @@ describe('app', function(){
request(app.listen()) request(app.listen())
.get('/') .get('/')
.end(function(){}); .end(function(){});
}) });
it('should set development env when NODE_ENV missing', function(){ it('should set development env when NODE_ENV missing', function(){
const NODE_ENV = process.env.NODE_ENV; const NODE_ENV = process.env.NODE_ENV;
@ -52,5 +52,5 @@ describe('app', function(){
const app = new Koa(); const app = new Koa();
process.env.NODE_ENV = NODE_ENV; process.env.NODE_ENV = NODE_ENV;
assert.equal(app.env, 'development'); assert.equal(app.env, 'development');
}) });
}) });

View File

@ -7,6 +7,6 @@ describe('app.inspect()', function(){
it('should work', function(){ it('should work', function(){
const app = new Koa(); const app = new Koa();
const util = require('util'); const util = require('util');
const str = util.inspect(app); util.inspect(app);
}) });
}) });

View File

@ -9,17 +9,12 @@ describe('app.onerror(err)', function(){
it('should throw an error if a non-error is given', function(done){ it('should throw an error if a non-error is given', function(done){
const app = new Koa(); const app = new Koa();
try { (function(){
app.onerror('foo'); app.onerror('foo');
}).should.throw(AssertionError, {message: 'non-error thrown: foo'});
should.fail();
} catch (err) {
err.should.be.instanceOf(AssertionError);
err.message.should.equal('non-error thrown: foo');
}
done(); done();
}) });
it('should do nothing if status is 404', function(done){ it('should do nothing if status is 404', function(done){
const app = new Koa(); const app = new Koa();
@ -27,28 +22,28 @@ describe('app.onerror(err)', function(){
err.status = 404; err.status = 404;
const output = stderr.inspectSync(function() { const output = stderr.inspectSync(function(){
app.onerror(err); app.onerror(err);
}); });
output.should.eql([]); output.should.eql([]);
done(); done();
}) });
it('should do nothing if .silent', function(done){ it('should do nothing if .silent', function(done){
const app = new Koa(); const app = new Koa();
app.silent = true; app.silent = true;
const err = new Error(); const err = new Error();
const output = stderr.inspectSync(function() { const output = stderr.inspectSync(function(){
app.onerror(err); app.onerror(err);
}); });
output.should.eql([]); output.should.eql([]);
done(); done();
}) });
it('should log the error to stderr', function(done){ it('should log the error to stderr', function(done){
const app = new Koa(); const app = new Koa();
@ -57,14 +52,14 @@ describe('app.onerror(err)', function(){
const err = new Error(); const err = new Error();
err.stack = 'Foo'; err.stack = 'Foo';
const output = stderr.inspectSync(function() { const output = stderr.inspectSync(function(){
app.onerror(err); app.onerror(err);
}); });
output.should.eql(["\n", " Foo\n", "\n"]); output.should.eql(['\n', ' Foo\n', '\n']);
done(); done();
}) });
it('should use err.toString() instad of err.stack', function(done){ it('should use err.toString() instad of err.stack', function(done){
const app = new Koa(); const app = new Koa();
@ -73,12 +68,12 @@ describe('app.onerror(err)', function(){
const err = new Error('mock stack null'); const err = new Error('mock stack null');
err.stack = null; err.stack = null;
const output = stderr.inspectSync(function() { const output = stderr.inspectSync(function(){
app.onerror(err); app.onerror(err);
}); });
output.should.eql(["\n", " Error: mock stack null\n", "\n"]); output.should.eql(['\n', ' Error: mock stack null\n', '\n']);
done(); done();
}) });
}) });

View File

@ -12,23 +12,23 @@ describe('app.request', function(){
it('should merge properties', function(done){ it('should merge properties', function(done){
app1.use(function *(next){ app1.use(function *(next){
assert.equal(this.request.message, 'hello') assert.equal(this.request.message, 'hello');
this.status = 204 this.status = 204;
}); });
request(app1.listen()) request(app1.listen())
.get('/') .get('/')
.expect(204, done); .expect(204, done);
}) });
it('should not affect the original prototype', function(done){ it('should not affect the original prototype', function(done){
app2.use(function *(next){ app2.use(function *(next){
assert.equal(this.request.message, undefined) assert.equal(this.request.message, undefined);
this.status = 204; this.status = 204;
}); });
request(app2.listen()) request(app2.listen())
.get('/') .get('/')
.expect(204, done); .expect(204, done);
}) });
}) });

View File

@ -21,8 +21,8 @@ describe('app.respond', function(){
setImmediate(function(){ setImmediate(function(){
res.setHeader('Content-Type', 'text/plain'); res.setHeader('Content-Type', 'text/plain');
res.end('lol'); res.end('lol');
}) });
}) });
const server = app.listen(); const server = app.listen();
@ -31,8 +31,8 @@ describe('app.respond', function(){
.expect(200) .expect(200)
.expect('lol') .expect('lol')
.end(done); .end(done);
}) });
}) });
describe('when HEAD is used', function(){ describe('when HEAD is used', function(){
it('should not respond with the body', function(done){ it('should not respond with the body', function(done){
@ -54,7 +54,7 @@ describe('app.respond', function(){
assert(0 == res.text.length); assert(0 == res.text.length);
done(); done();
}); });
}) });
it('should keep json headers', function(done){ it('should keep json headers', function(done){
const app = new Koa(); const app = new Koa();
@ -75,7 +75,7 @@ describe('app.respond', function(){
assert(0 == res.text.length); assert(0 == res.text.length);
done(); done();
}); });
}) });
it('should keep string headers', function(done){ it('should keep string headers', function(done){
const app = new Koa(); const app = new Koa();
@ -96,7 +96,7 @@ describe('app.respond', function(){
assert(0 == res.text.length); assert(0 == res.text.length);
done(); done();
}); });
}) });
it('should keep buffer headers', function(done){ it('should keep buffer headers', function(done){
const app = new Koa(); const app = new Koa();
@ -117,35 +117,35 @@ describe('app.respond', function(){
assert(0 == res.text.length); assert(0 == res.text.length);
done(); done();
}); });
}) });
it('should respond with a 404 if no body was set', function(done){ it('should respond with a 404 if no body was set', function(done){
const app = new Koa(); const app = new Koa();
app.use(function *(){ app.use(function *(){
}) });
const server = app.listen(); const server = app.listen();
request(server) request(server)
.head('/') .head('/')
.expect(404, done); .expect(404, done);
}) });
it('should respond with a 200 if body = ""', function(done){ it('should respond with a 200 if body = ""', function(done){
const app = new Koa(); const app = new Koa();
app.use(function *(){ app.use(function *(){
this.body = ''; this.body = '';
}) });
const server = app.listen(); const server = app.listen();
request(server) request(server)
.head('/') .head('/')
.expect(200, done); .expect(200, done);
}) });
it('should not overwrite the content-type', function(done){ it('should not overwrite the content-type', function(done){
const app = new Koa(); const app = new Koa();
@ -153,7 +153,7 @@ describe('app.respond', function(){
app.use(function *(){ app.use(function *(){
this.status = 200; this.status = 200;
this.type = 'application/javascript'; this.type = 'application/javascript';
}) });
const server = app.listen(); const server = app.listen();
@ -161,8 +161,8 @@ describe('app.respond', function(){
.head('/') .head('/')
.expect('content-type', /application\/javascript/) .expect('content-type', /application\/javascript/)
.expect(200, done); .expect(200, done);
}) });
}) });
describe('when no middleware are present', function(){ describe('when no middleware are present', function(){
it('should 404', function(done){ it('should 404', function(done){
@ -173,8 +173,8 @@ describe('app.respond', function(){
request(server) request(server)
.get('/') .get('/')
.expect(404, done); .expect(404, done);
}) });
}) });
describe('when res has already been written to', function(){ describe('when res has already been written to', function(){
it('should not cause an app error', function(done){ it('should not cause an app error', function(done){
@ -183,14 +183,14 @@ describe('app.respond', function(){
app.use(function *(next){ app.use(function *(next){
const res = this.res; const res = this.res;
this.status = 200; this.status = 200;
res.setHeader("Content-Type", "text/html") res.setHeader('Content-Type', 'text/html');
res.write('Hello'); res.write('Hello');
setTimeout(function(){ setTimeout(function(){
res.end("Goodbye") res.end('Goodbye');
}, 0); }, 0);
}); });
const errorCaught = false; let errorCaught = false;
app.on('error', function(err){ app.on('error', function(err){
errorCaught = err; errorCaught = err;
@ -206,7 +206,7 @@ describe('app.respond', function(){
if (errorCaught) return done(errorCaught); if (errorCaught) return done(errorCaught);
done(); done();
}); });
}) });
it('should send the right body', function(done){ it('should send the right body', function(done){
const app = new Koa(); const app = new Koa();
@ -214,10 +214,10 @@ describe('app.respond', function(){
app.use(function *(next){ app.use(function *(next){
const res = this.res; const res = this.res;
this.status = 200; this.status = 200;
res.setHeader("Content-Type", "text/html") res.setHeader('Content-Type', 'text/html');
res.write('Hello'); res.write('Hello');
setTimeout(function(){ setTimeout(function(){
res.end("Goodbye"); res.end('Goodbye');
}, 0); }, 0);
}); });
@ -227,8 +227,8 @@ describe('app.respond', function(){
.get('/') .get('/')
.expect(200) .expect(200)
.expect('HelloGoodbye', done); .expect('HelloGoodbye', done);
}) });
}) });
describe('when .body is missing', function(){ describe('when .body is missing', function(){
describe('with status=400', function(){ describe('with status=400', function(){
@ -246,8 +246,8 @@ describe('app.respond', function(){
.expect(400) .expect(400)
.expect('Content-Length', 11) .expect('Content-Length', 11)
.expect('Bad Request', done); .expect('Bad Request', done);
}) });
}) });
describe('with status=204', function(){ describe('with status=204', function(){
it('should respond without a body', function(done){ it('should respond without a body', function(done){
@ -255,7 +255,7 @@ describe('app.respond', function(){
app.use(function *(){ app.use(function *(){
this.status = 204; this.status = 204;
}) });
const server = app.listen(); const server = app.listen();
@ -268,9 +268,9 @@ describe('app.respond', function(){
res.header.should.not.have.property('content-type'); res.header.should.not.have.property('content-type');
done(); done();
}) });
}) });
}) });
describe('with status=205', function(){ describe('with status=205', function(){
it('should respond without a body', function(done){ it('should respond without a body', function(done){
@ -278,7 +278,7 @@ describe('app.respond', function(){
app.use(function *(){ app.use(function *(){
this.status = 205; this.status = 205;
}) });
const server = app.listen(); const server = app.listen();
@ -291,9 +291,9 @@ describe('app.respond', function(){
res.header.should.not.have.property('content-type'); res.header.should.not.have.property('content-type');
done(); done();
}) });
}) });
}) });
describe('with status=304', function(){ describe('with status=304', function(){
it('should respond without a body', function(done){ it('should respond without a body', function(done){
@ -301,7 +301,7 @@ describe('app.respond', function(){
app.use(function *(){ app.use(function *(){
this.status = 304; this.status = 304;
}) });
const server = app.listen(); const server = app.listen();
@ -314,18 +314,18 @@ describe('app.respond', function(){
res.header.should.not.have.property('content-type'); res.header.should.not.have.property('content-type');
done(); done();
}) });
}) });
}) });
describe('with custom status=700', function(){ describe('with custom status=700', function(){
it('should respond with the associated status message', function (done){ it('should respond with the associated status message', function(done){
const app = new Koa(); const app = new Koa();
statuses['700'] = 'custom status'; statuses['700'] = 'custom status';
app.use(function *(){ app.use(function *(){
this.status = 700; this.status = 700;
}) });
const server = app.listen(); const server = app.listen();
@ -337,18 +337,18 @@ describe('app.respond', function(){
if (err) return done(err); if (err) return done(err);
res.res.statusMessage.should.equal('custom status'); res.res.statusMessage.should.equal('custom status');
done(); done();
}) });
}) });
}) });
describe('with custom statusMessage=ok', function(){ describe('with custom statusMessage=ok', function(){
it('should respond with the custom status message', function (done){ it('should respond with the custom status message', function(done){
const app = new Koa(); const app = new Koa();
app.use(function *(){ app.use(function *(){
this.status = 200; this.status = 200;
this.message = 'ok'; this.message = 'ok';
}) });
const server = app.listen(); const server = app.listen();
@ -360,17 +360,17 @@ describe('app.respond', function(){
if (err) return done(err); if (err) return done(err);
res.res.statusMessage.should.equal('ok'); res.res.statusMessage.should.equal('ok');
done(); done();
}) });
}) });
}) });
describe('with custom status without message', function (){ describe('with custom status without message', function(){
it('should respond with the status code number', function (done){ it('should respond with the status code number', function(done){
const app = new Koa(); const app = new Koa();
app.use(function *(){ app.use(function *(){
this.res.statusCode = 701; this.res.statusCode = 701;
}) });
const server = app.listen(); const server = app.listen();
@ -378,9 +378,9 @@ describe('app.respond', function(){
.get('/') .get('/')
.expect(701) .expect(701)
.expect('701', done); .expect('701', done);
}) });
}) });
}) });
describe('when .body is a null', function(){ describe('when .body is a null', function(){
it('should respond 204 by default', function(done){ it('should respond 204 by default', function(done){
@ -388,7 +388,7 @@ describe('app.respond', function(){
app.use(function *(){ app.use(function *(){
this.body = null; this.body = null;
}) });
const server = app.listen(); const server = app.listen();
@ -401,8 +401,8 @@ describe('app.respond', function(){
res.header.should.not.have.property('content-type'); res.header.should.not.have.property('content-type');
done(); done();
}) });
}) });
it('should respond 204 with status=200', function(done){ it('should respond 204 with status=200', function(done){
const app = new Koa(); const app = new Koa();
@ -410,7 +410,7 @@ describe('app.respond', function(){
app.use(function *(){ app.use(function *(){
this.status = 200; this.status = 200;
this.body = null; this.body = null;
}) });
const server = app.listen(); const server = app.listen();
@ -423,8 +423,8 @@ describe('app.respond', function(){
res.header.should.not.have.property('content-type'); res.header.should.not.have.property('content-type');
done(); done();
}) });
}) });
it('should respond 205 with status=205', function(done){ it('should respond 205 with status=205', function(done){
const app = new Koa(); const app = new Koa();
@ -432,7 +432,7 @@ describe('app.respond', function(){
app.use(function *(){ app.use(function *(){
this.status = 205; this.status = 205;
this.body = null; this.body = null;
}) });
const server = app.listen(); const server = app.listen();
@ -445,8 +445,8 @@ describe('app.respond', function(){
res.header.should.not.have.property('content-type'); res.header.should.not.have.property('content-type');
done(); done();
}) });
}) });
it('should respond 304 with status=304', function(done){ it('should respond 304 with status=304', function(done){
const app = new Koa(); const app = new Koa();
@ -454,7 +454,7 @@ describe('app.respond', function(){
app.use(function *(){ app.use(function *(){
this.status = 304; this.status = 304;
this.body = null; this.body = null;
}) });
const server = app.listen(); const server = app.listen();
@ -467,9 +467,9 @@ describe('app.respond', function(){
res.header.should.not.have.property('content-type'); res.header.should.not.have.property('content-type');
done(); done();
}) });
}) });
}) });
describe('when .body is a string', function(){ describe('when .body is a string', function(){
it('should respond', function(done){ it('should respond', function(done){
@ -484,8 +484,8 @@ describe('app.respond', function(){
request(server) request(server)
.get('/') .get('/')
.expect('Hello', done); .expect('Hello', done);
}) });
}) });
describe('when .body is a Buffer', function(){ describe('when .body is a Buffer', function(){
it('should respond', function(done){ it('should respond', function(done){
@ -500,8 +500,8 @@ describe('app.respond', function(){
request(server) request(server)
.get('/') .get('/')
.expect('Hello', done); .expect('Hello', done);
}) });
}) });
describe('when .body is a Stream', function(){ describe('when .body is a Stream', function(){
it('should respond', function(done){ it('should respond', function(done){
@ -524,7 +524,7 @@ describe('app.respond', function(){
res.body.should.eql(pkg); res.body.should.eql(pkg);
done(); done();
}); });
}) });
it('should strip content-length when overwriting', function(done){ it('should strip content-length when overwriting', function(done){
const app = new Koa(); const app = new Koa();
@ -546,8 +546,8 @@ describe('app.respond', function(){
res.should.not.have.header('Content-Length'); res.should.not.have.header('Content-Length');
res.body.should.eql(pkg); res.body.should.eql(pkg);
done(); done();
}) });
}) });
it('should keep content-length if not overwritten', function(done){ it('should keep content-length if not overwritten', function(done){
const app = new Koa(); const app = new Koa();
@ -569,8 +569,8 @@ describe('app.respond', function(){
res.should.have.header('Content-Length'); res.should.have.header('Content-Length');
res.body.should.eql(pkg); res.body.should.eql(pkg);
done(); done();
}) });
}) });
it('should keep content-length if overwritten with the same stream', function(done){ it('should keep content-length if overwritten with the same stream', function(done){
const app = new Koa(); const app = new Koa();
@ -594,8 +594,8 @@ describe('app.respond', function(){
res.should.have.header('Content-Length'); res.should.have.header('Content-Length');
res.body.should.eql(pkg); res.body.should.eql(pkg);
done(); done();
}) });
}) });
it('should handle errors', function(done){ it('should handle errors', function(done){
const app = new Koa(); const app = new Koa();
@ -612,7 +612,7 @@ describe('app.respond', function(){
.expect('Content-Type', 'text/plain; charset=utf-8') .expect('Content-Type', 'text/plain; charset=utf-8')
.expect(404) .expect(404)
.end(done); .end(done);
}) });
it('should handle errors when no content status', function(done){ it('should handle errors when no content status', function(done){
const app = new Koa(); const app = new Koa();
@ -627,8 +627,7 @@ describe('app.respond', function(){
request(server) request(server)
.get('/') .get('/')
.expect(204, done); .expect(204, done);
}) });
it('should handle all intermediate stream body errors', function(done){ it('should handle all intermediate stream body errors', function(done){
const app = new Koa(); const app = new Koa();
@ -644,8 +643,8 @@ describe('app.respond', function(){
request(server) request(server)
.get('/') .get('/')
.expect(404, done); .expect(404, done);
}) });
}) });
describe('when .body is an Object', function(){ describe('when .body is an Object', function(){
it('should respond with json', function(done){ it('should respond with json', function(done){
@ -661,8 +660,8 @@ describe('app.respond', function(){
.get('/') .get('/')
.expect('Content-Type', 'application/json; charset=utf-8') .expect('Content-Type', 'application/json; charset=utf-8')
.expect('{"hello":"world"}', done); .expect('{"hello":"world"}', done);
}) });
}) });
describe('when an error occurs', function(){ describe('when an error occurs', function(){
it('should emit "error" on the app', function(done){ it('should emit "error" on the app', function(done){
@ -680,7 +679,7 @@ describe('app.respond', function(){
request(app.listen()) request(app.listen())
.get('/') .get('/')
.end(function(){}); .end(function(){});
}) });
describe('with an .expose property', function(){ describe('with an .expose property', function(){
it('should expose the message', function(done){ it('should expose the message', function(done){
@ -697,8 +696,8 @@ describe('app.respond', function(){
.get('/') .get('/')
.expect(403, 'sorry!') .expect(403, 'sorry!')
.end(done); .end(done);
}) });
}) });
describe('with a .status property', function(){ describe('with a .status property', function(){
it('should respond with .status', function(done){ it('should respond with .status', function(done){
@ -714,8 +713,8 @@ describe('app.respond', function(){
.get('/') .get('/')
.expect(403, 'Forbidden') .expect(403, 'Forbidden')
.end(done); .end(done);
}) });
}) });
it('should respond with 500', function(done){ it('should respond with 500', function(done){
const app = new Koa(); const app = new Koa();
@ -730,7 +729,7 @@ describe('app.respond', function(){
.get('/') .get('/')
.expect(500, 'Internal Server Error') .expect(500, 'Internal Server Error')
.end(done); .end(done);
}) });
it('should be catchable', function(done){ it('should be catchable', function(done){
const app = new Koa(); const app = new Koa();
@ -746,7 +745,6 @@ describe('app.respond', function(){
app.use(function *(next){ app.use(function *(next){
throw new Error('boom!'); throw new Error('boom!');
this.body = 'Oh no';
}); });
const server = app.listen(); const server = app.listen();
@ -755,8 +753,8 @@ describe('app.respond', function(){
.get('/') .get('/')
.expect(200, 'Got error') .expect(200, 'Got error')
.end(done); .end(done);
}) });
}) });
describe('when status and body property', function(){ describe('when status and body property', function(){
it('should 200', function(done){ it('should 200', function(done){
@ -774,9 +772,9 @@ describe('app.respond', function(){
.get('/') .get('/')
.expect(200) .expect(200)
.expect('hello', done); .expect('hello', done);
}) });
it('should 204', function(done) { it('should 204', function(done){
const app = new Koa(); const app = new Koa();
app.use(function *(){ app.use(function *(){
@ -791,10 +789,10 @@ describe('app.respond', function(){
request(server) request(server)
.get('/') .get('/')
.expect(204) .expect(204)
.end(function (err, res) { .end(function(err, res){
res.should.not.have.header('content-type'); res.should.not.have.header('content-type');
done(err); done(err);
}); });
}); });
}) });
}) });

View File

@ -12,23 +12,23 @@ describe('app.response', function(){
it('should merge properties', function(done){ it('should merge properties', function(done){
app1.use(function *(next){ app1.use(function *(next){
assert.equal(this.response.msg, 'hello') assert.equal(this.response.msg, 'hello');
this.status = 204 this.status = 204;
}); });
request(app1.listen()) request(app1.listen())
.get('/') .get('/')
.expect(204, done); .expect(204, done);
}) });
it('should not affect the original prototype', function(done){ it('should not affect the original prototype', function(done){
app2.use(function *(next){ app2.use(function *(next){
assert.equal(this.response.msg, undefined) assert.equal(this.response.msg, undefined);
this.status = 204; this.status = 204;
}); });
request(app2.listen()) request(app2.listen())
.get('/') .get('/')
.expect(204, done); .expect(204, done);
}) });
}) });

View File

@ -12,5 +12,5 @@ describe('app.toJSON()', function(){
subdomainOffset: 2, subdomainOffset: 2,
env: 'test' env: 'test'
}); });
}) });
}) });

View File

@ -34,10 +34,10 @@ describe('app.use(fn)', function(){
.expect(404) .expect(404)
.end(function(err){ .end(function(err){
if (err) return done(err); if (err) return done(err);
calls.should.eql([1,2,3,4,5,6]); calls.should.eql([1, 2, 3, 4, 5, 6]);
done(); done();
}); });
}) });
it('should error when a non-generator function is passed', function(){ it('should error when a non-generator function is passed', function(){
const app = new Koa(); const app = new Koa();
@ -47,11 +47,11 @@ describe('app.use(fn)', function(){
} catch (err) { } catch (err) {
err.message.should.equal('app.use() requires a generator function'); err.message.should.equal('app.use() requires a generator function');
} }
}) });
it('should not error when a non-generator function is passed when .experimental=true', function(){ it('should not error when a non-generator function is passed when .experimental=true', function(){
const app = new Koa(); const app = new Koa();
app.experimental = true; app.experimental = true;
app.use(function(){}); app.use(function(){});
}) });
}) });

View File

@ -15,5 +15,5 @@ describe('ctx.assert(value, status)', function(){
assert(404 == err.status); assert(404 == err.status);
assert(err.expose); assert(err.expose);
} }
}) });
}) });

View File

@ -11,7 +11,7 @@ describe('ctx.cookies.set()', function(){
app.use(function *(next){ app.use(function *(next){
this.cookies.set('name', 'jon'); this.cookies.set('name', 'jon');
this.status = 204; this.status = 204;
}) });
const server = app.listen(); const server = app.listen();
@ -26,8 +26,8 @@ describe('ctx.cookies.set()', function(){
}).should.be.ok; }).should.be.ok;
done(); done();
}) });
}) });
describe('with .signed', function(){ describe('with .signed', function(){
describe('when no .keys are set', function(){ describe('when no .keys are set', function(){
@ -45,8 +45,8 @@ describe('ctx.cookies.set()', function(){
request(app.listen()) request(app.listen())
.get('/') .get('/')
.expect('.keys required for signed cookies', done); .expect('.keys required for signed cookies', done);
}) });
}) });
it('should send a signed cookie', function(done){ it('should send a signed cookie', function(done){
const app = new Koa(); const app = new Koa();
@ -56,7 +56,7 @@ describe('ctx.cookies.set()', function(){
app.use(function *(next){ app.use(function *(next){
this.cookies.set('name', 'jon', { signed: true }); this.cookies.set('name', 'jon', { signed: true });
this.status = 204; this.status = 204;
}) });
const server = app.listen(); const server = app.listen();
@ -77,7 +77,7 @@ describe('ctx.cookies.set()', function(){
}).should.be.ok; }).should.be.ok;
done(); done();
}) });
}) });
}) });
}) });

View File

@ -9,5 +9,5 @@ describe('ctx.inspect()', function(){
const toJSON = ctx.toJSON(ctx); const toJSON = ctx.toJSON(ctx);
toJSON.should.eql(ctx.inspect()); toJSON.should.eql(ctx.inspect());
}) });
}) });

View File

@ -12,7 +12,7 @@ describe('ctx.onerror(err)', function(){
this.body = 'something else'; this.body = 'something else';
this.throw(418, 'boom'); this.throw(418, 'boom');
}) });
const server = app.listen(); const server = app.listen();
@ -22,7 +22,7 @@ describe('ctx.onerror(err)', function(){
.expect('Content-Type', 'text/plain; charset=utf-8') .expect('Content-Type', 'text/plain; charset=utf-8')
.expect('Content-Length', '4') .expect('Content-Length', '4')
.end(done); .end(done);
}) });
it('should unset all headers', function(done){ it('should unset all headers', function(done){
const app = new Koa(); const app = new Koa();
@ -33,7 +33,7 @@ describe('ctx.onerror(err)', function(){
this.body = 'response'; this.body = 'response';
this.throw(418, 'boom'); this.throw(418, 'boom');
}) });
const server = app.listen(); const server = app.listen();
@ -49,8 +49,8 @@ describe('ctx.onerror(err)', function(){
res.headers.should.not.have.property('x-csrf-token'); res.headers.should.not.have.property('x-csrf-token');
done(); done();
}) });
}) });
describe('when invalid err.status', function(){ describe('when invalid err.status', function(){
describe('not number', function(){ describe('not number', function(){
@ -62,7 +62,7 @@ describe('ctx.onerror(err)', function(){
const err = new Error('some error'); const err = new Error('some error');
err.status = 'notnumber'; err.status = 'notnumber';
throw err; throw err;
}) });
const server = app.listen(); const server = app.listen();
@ -71,8 +71,8 @@ describe('ctx.onerror(err)', function(){
.expect(500) .expect(500)
.expect('Content-Type', 'text/plain; charset=utf-8') .expect('Content-Type', 'text/plain; charset=utf-8')
.expect('Internal Server Error', done); .expect('Internal Server Error', done);
}) });
}) });
describe('not http status code', function(){ describe('not http status code', function(){
it('should respond 500', function(done){ it('should respond 500', function(done){
@ -83,7 +83,7 @@ describe('ctx.onerror(err)', function(){
const err = new Error('some error'); const err = new Error('some error');
err.status = 9999; err.status = 9999;
throw err; throw err;
}) });
const server = app.listen(); const server = app.listen();
@ -92,17 +92,17 @@ describe('ctx.onerror(err)', function(){
.expect(500) .expect(500)
.expect('Content-Type', 'text/plain; charset=utf-8') .expect('Content-Type', 'text/plain; charset=utf-8')
.expect('Internal Server Error', done); .expect('Internal Server Error', done);
}) });
}) });
}) });
describe('when non-error thrown', function(){ describe('when non-error thrown', function(){
it('should response non-error thrown message', function(done){ it('should response non-error thrown message', function(done){
const app = new Koa(); const app = new Koa();
app.use(function *(next){ app.use(function *(next){
throw 'string error'; throw 'string error'; // eslint-disable-line no-throw-literal
}) });
const server = app.listen(); const server = app.listen();
@ -111,6 +111,6 @@ describe('ctx.onerror(err)', function(){
.expect(500) .expect(500)
.expect('Content-Type', 'text/plain; charset=utf-8') .expect('Content-Type', 'text/plain; charset=utf-8')
.expect('Internal Server Error', done); .expect('Internal Server Error', done);
}) });
}) });
}) });

View File

@ -5,11 +5,11 @@ const request = require('supertest');
const assert = require('assert'); const assert = require('assert');
const Koa = require('../..'); const Koa = require('../..');
describe('ctx.state', function() { describe('ctx.state', function(){
it('should provide a ctx.state namespace', function(done) { it('should provide a ctx.state namespace', function(done){
const app = new Koa(); const app = new Koa();
app.use(function *() { app.use(function *(){
assert.deepEqual(this.state, {}); assert.deepEqual(this.state, {});
}); });
@ -19,5 +19,5 @@ describe('ctx.state', function() {
.get('/') .get('/')
.expect(404) .expect(404)
.end(done); .end(done);
}) });
}) });

View File

@ -15,8 +15,8 @@ describe('ctx.throw(msg)', function(){
assert(!err.expose); assert(!err.expose);
done(); done();
} }
}) });
}) });
describe('ctx.throw(err)', function(){ describe('ctx.throw(err)', function(){
it('should set .status to 500', function(done){ it('should set .status to 500', function(done){
@ -31,8 +31,8 @@ describe('ctx.throw(err)', function(){
assert(!err.expose); assert(!err.expose);
done(); done();
} }
}) });
}) });
describe('ctx.throw(err, status)', function(){ describe('ctx.throw(err, status)', function(){
it('should throw the error and set .status', function(done){ it('should throw the error and set .status', function(done){
@ -47,8 +47,8 @@ describe('ctx.throw(err, status)', function(){
assert(true === err.expose); assert(true === err.expose);
done(); done();
} }
}) });
}) });
describe('ctx.throw(status, err)', function(){ describe('ctx.throw(status, err)', function(){
it('should throw the error and set .status', function(done){ it('should throw the error and set .status', function(done){
@ -63,8 +63,8 @@ describe('ctx.throw(status, err)', function(){
assert(true === err.expose); assert(true === err.expose);
done(); done();
} }
}) });
}) });
describe('ctx.throw(msg, status)', function(){ describe('ctx.throw(msg, status)', function(){
it('should throw an error', function(done){ it('should throw an error', function(done){
@ -78,8 +78,8 @@ describe('ctx.throw(msg, status)', function(){
assert(true === err.expose); assert(true === err.expose);
done(); done();
} }
}) });
}) });
describe('ctx.throw(status, msg)', function(){ describe('ctx.throw(status, msg)', function(){
it('should throw an error', function(done){ it('should throw an error', function(done){
@ -93,8 +93,8 @@ describe('ctx.throw(status, msg)', function(){
assert(true === err.expose); assert(true === err.expose);
done(); done();
} }
}) });
}) });
describe('ctx.throw(status)', function(){ describe('ctx.throw(status)', function(){
it('should throw an error', function(done){ it('should throw an error', function(done){
@ -108,7 +108,7 @@ describe('ctx.throw(status)', function(){
assert(true === err.expose); assert(true === err.expose);
done(); done();
} }
}) });
describe('when not valid status', function(){ describe('when not valid status', function(){
it('should not expose', function(done){ it('should not expose', function(done){
@ -118,14 +118,14 @@ describe('ctx.throw(status)', function(){
const err = new Error('some error'); const err = new Error('some error');
err.status = -1; err.status = -1;
ctx.throw(err); ctx.throw(err);
} catch(err) { } catch (err) {
assert('some error' == err.message); assert('some error' == err.message);
assert(!err.expose); assert(!err.expose);
done(); done();
} }
}) });
}) });
}) });
describe('ctx.throw(status, msg, props)', function(){ describe('ctx.throw(status, msg, props)', function(){
it('should mixin props', function(done){ it('should mixin props', function(done){
@ -140,7 +140,7 @@ describe('ctx.throw(status, msg, props)', function(){
assert(true === err.prop); assert(true === err.prop);
done(); done();
} }
}) });
describe('when props include status', function(){ describe('when props include status', function(){
it('should be ignored', function(done){ it('should be ignored', function(done){
@ -158,9 +158,9 @@ describe('ctx.throw(status, msg, props)', function(){
assert(true === err.prop); assert(true === err.prop);
done(); done();
} }
}) });
}) });
}) });
describe('ctx.throw(msg, props)', function(){ describe('ctx.throw(msg, props)', function(){
it('should mixin props', function(done){ it('should mixin props', function(done){
@ -175,8 +175,8 @@ describe('ctx.throw(msg, props)', function(){
assert(true === err.prop); assert(true === err.prop);
done(); done();
} }
}) });
}) });
describe('ctx.throw(status, props)', function(){ describe('ctx.throw(status, props)', function(){
it('should mixin props', function(done){ it('should mixin props', function(done){
@ -191,8 +191,8 @@ describe('ctx.throw(status, props)', function(){
assert(true === err.prop); assert(true === err.prop);
done(); done();
} }
}) });
}) });
describe('ctx.throw(err, props)', function(){ describe('ctx.throw(err, props)', function(){
it('should mixin props', function(done){ it('should mixin props', function(done){
@ -207,5 +207,5 @@ describe('ctx.throw(err, props)', function(){
assert(true === err.prop); assert(true === err.prop);
done(); done();
} }
}) });
}) });

View File

@ -33,5 +33,5 @@ describe('ctx.toJSON()', function(){
'content-length': '10' 'content-length': '10'
} }
}); });
}) });
}) });

View File

@ -8,11 +8,11 @@
const request = require('supertest'); const request = require('supertest');
const Koa = require('../..'); const Koa = require('../..');
describe('.experimental=true', function () { describe('.experimental=true', function(){
it('should support async functions', function (done) { it('should support async functions', function(done){
const app = new Koa(); const app = new Koa();
app.experimental = true; app.experimental = true;
app.use(async function (next) { app.use(async function (next){
const string = await Promise.resolve('asdf'); const string = await Promise.resolve('asdf');
this.body = string; this.body = string;
}); });
@ -21,5 +21,5 @@ describe('.experimental=true', function () {
.get('/') .get('/')
.expect('asdf') .expect('asdf')
.expect(200, done); .expect(200, done);
}) });
}) });

View File

@ -8,16 +8,16 @@ module.exports = function(req, res){
const socket = new Stream.Duplex(); const socket = new Stream.Duplex();
req = req || { headers: {}, socket: socket, __proto__: Stream.Readable.prototype }; req = req || { headers: {}, socket: socket, __proto__: Stream.Readable.prototype };
res = res || { _headers: {}, socket: socket, __proto__: Stream.Writable.prototype }; res = res || { _headers: {}, socket: socket, __proto__: Stream.Writable.prototype };
res.getHeader = function(k){ return res._headers[k.toLowerCase()] }; res.getHeader = function(k){ return res._headers[k.toLowerCase()]; };
res.setHeader = function(k, v){ res._headers[k.toLowerCase()] = v }; res.setHeader = function(k, v){ res._headers[k.toLowerCase()] = v; };
res.removeHeader = function(k, v){ delete res._headers[k.toLowerCase()] }; res.removeHeader = function(k, v){ delete res._headers[k.toLowerCase()]; };
return (new Koa()).createContext(req, res); return (new Koa()).createContext(req, res);
} };
module.exports.request = function(req, res){ module.exports.request = function(req, res){
return module.exports(req, res).request; return module.exports(req, res).request;
} };
module.exports.response = function(req, res){ module.exports.response = function(req, res){
return module.exports(req, res).response; return module.exports(req, res).response;
} };

View File

@ -10,9 +10,9 @@ describe('ctx.accepts(types)', function(){
const ctx = context(); const ctx = context();
ctx.req.headers.accept = 'application/*;q=0.2, image/jpeg;q=0.8, text/html, text/plain'; ctx.req.headers.accept = 'application/*;q=0.2, image/jpeg;q=0.8, text/html, text/plain';
ctx.accepts().should.eql(['text/html', 'text/plain', 'image/jpeg', 'application/*']); ctx.accepts().should.eql(['text/html', 'text/plain', 'image/jpeg', 'application/*']);
}) });
}) });
}) });
describe('with no valid types', function(){ describe('with no valid types', function(){
describe('when Accept is populated', function(){ describe('when Accept is populated', function(){
@ -20,16 +20,16 @@ describe('ctx.accepts(types)', function(){
const ctx = context(); const ctx = context();
ctx.req.headers.accept = 'application/*;q=0.2, image/jpeg;q=0.8, text/html, text/plain'; ctx.req.headers.accept = 'application/*;q=0.2, image/jpeg;q=0.8, text/html, text/plain';
ctx.accepts('image/png', 'image/tiff').should.be.false; ctx.accepts('image/png', 'image/tiff').should.be.false;
}) });
}) });
describe('when Accept is not populated', function(){ describe('when Accept is not populated', function(){
it('should return the first type', function(){ it('should return the first type', function(){
const ctx = context(); const ctx = context();
ctx.accepts('text/html', 'text/plain', 'image/jpeg', 'application/*').should.equal('text/html'); ctx.accepts('text/html', 'text/plain', 'image/jpeg', 'application/*').should.equal('text/html');
}) });
}) });
}) });
describe('when extensions are given', function(){ describe('when extensions are given', function(){
it('should convert to mime types', function(){ it('should convert to mime types', function(){
@ -40,8 +40,8 @@ describe('ctx.accepts(types)', function(){
ctx.accepts('txt').should.equal('txt'); ctx.accepts('txt').should.equal('txt');
ctx.accepts('.txt').should.equal('.txt'); ctx.accepts('.txt').should.equal('.txt');
ctx.accepts('png').should.be.false; ctx.accepts('png').should.be.false;
}) });
}) });
describe('when an array is given', function(){ describe('when an array is given', function(){
it('should return the first match', function(){ it('should return the first match', function(){
@ -49,8 +49,8 @@ describe('ctx.accepts(types)', function(){
ctx.req.headers.accept = 'text/plain, text/html'; ctx.req.headers.accept = 'text/plain, text/html';
ctx.accepts(['png', 'text', 'html']).should.equal('text'); ctx.accepts(['png', 'text', 'html']).should.equal('text');
ctx.accepts(['png', 'html']).should.equal('html'); ctx.accepts(['png', 'html']).should.equal('html');
}) });
}) });
describe('when multiple arguments are given', function(){ describe('when multiple arguments are given', function(){
it('should return the first match', function(){ it('should return the first match', function(){
@ -58,8 +58,8 @@ describe('ctx.accepts(types)', function(){
ctx.req.headers.accept = 'text/plain, text/html'; ctx.req.headers.accept = 'text/plain, text/html';
ctx.accepts('png', 'text', 'html').should.equal('text'); ctx.accepts('png', 'text', 'html').should.equal('text');
ctx.accepts('png', 'html').should.equal('html'); ctx.accepts('png', 'html').should.equal('html');
}) });
}) });
describe('when present in Accept as an exact match', function(){ describe('when present in Accept as an exact match', function(){
it('should return the type', function(){ it('should return the type', function(){
@ -67,8 +67,8 @@ describe('ctx.accepts(types)', function(){
ctx.req.headers.accept = 'text/plain, text/html'; ctx.req.headers.accept = 'text/plain, text/html';
ctx.accepts('text/html').should.equal('text/html'); ctx.accepts('text/html').should.equal('text/html');
ctx.accepts('text/plain').should.equal('text/plain'); ctx.accepts('text/plain').should.equal('text/plain');
}) });
}) });
describe('when present in Accept as a type match', function(){ describe('when present in Accept as a type match', function(){
it('should return the type', function(){ it('should return the type', function(){
@ -77,8 +77,8 @@ describe('ctx.accepts(types)', function(){
ctx.accepts('text/html').should.equal('text/html'); ctx.accepts('text/html').should.equal('text/html');
ctx.accepts('text/plain').should.equal('text/plain'); ctx.accepts('text/plain').should.equal('text/plain');
ctx.accepts('image/png').should.equal('image/png'); ctx.accepts('image/png').should.equal('image/png');
}) });
}) });
describe('when present in Accept as a subtype match', function(){ describe('when present in Accept as a subtype match', function(){
it('should return the type', function(){ it('should return the type', function(){
@ -87,6 +87,6 @@ describe('ctx.accepts(types)', function(){
ctx.accepts('text/html').should.equal('text/html'); ctx.accepts('text/html').should.equal('text/html');
ctx.accepts('text/plain').should.equal('text/plain'); ctx.accepts('text/plain').should.equal('text/plain');
ctx.accepts('image/png').should.be.false; ctx.accepts('image/png').should.be.false;
}) });
}) });
}) });

View File

@ -10,9 +10,9 @@ describe('ctx.acceptsCharsets()', function(){
const ctx = context(); const ctx = context();
ctx.req.headers['accept-charset'] = 'utf-8, iso-8859-1;q=0.2, utf-7;q=0.5'; ctx.req.headers['accept-charset'] = 'utf-8, iso-8859-1;q=0.2, utf-7;q=0.5';
ctx.acceptsCharsets().should.eql(['utf-8', 'utf-7', 'iso-8859-1']); ctx.acceptsCharsets().should.eql(['utf-8', 'utf-7', 'iso-8859-1']);
}) });
}) });
}) });
describe('with multiple arguments', function(){ describe('with multiple arguments', function(){
describe('when Accept-Charset is populated', function(){ describe('when Accept-Charset is populated', function(){
@ -21,31 +21,31 @@ describe('ctx.acceptsCharsets()', function(){
const ctx = context(); const ctx = context();
ctx.req.headers['accept-charset'] = 'utf-8, iso-8859-1;q=0.2, utf-7;q=0.5'; ctx.req.headers['accept-charset'] = 'utf-8, iso-8859-1;q=0.2, utf-7;q=0.5';
ctx.acceptsCharsets('utf-7', 'utf-8').should.equal('utf-8'); ctx.acceptsCharsets('utf-7', 'utf-8').should.equal('utf-8');
}) });
}) });
describe('if no types match', function(){ describe('if no types match', function(){
it('should return false', function(){ it('should return false', function(){
const ctx = context(); const ctx = context();
ctx.req.headers['accept-charset'] = 'utf-8, iso-8859-1;q=0.2, utf-7;q=0.5'; ctx.req.headers['accept-charset'] = 'utf-8, iso-8859-1;q=0.2, utf-7;q=0.5';
ctx.acceptsCharsets('utf-16').should.be.false; ctx.acceptsCharsets('utf-16').should.be.false;
}) });
}) });
}) });
describe('when Accept-Charset is not populated', function(){ describe('when Accept-Charset is not populated', function(){
it('should return the first type', function(){ it('should return the first type', function(){
const ctx = context(); const ctx = context();
ctx.acceptsCharsets('utf-7', 'utf-8').should.equal('utf-7'); ctx.acceptsCharsets('utf-7', 'utf-8').should.equal('utf-7');
}) });
}) });
}) });
describe('with an array', function(){ describe('with an array', function(){
it('should return the best fit', function(){ it('should return the best fit', function(){
const ctx = context(); const ctx = context();
ctx.req.headers['accept-charset'] = 'utf-8, iso-8859-1;q=0.2, utf-7;q=0.5'; ctx.req.headers['accept-charset'] = 'utf-8, iso-8859-1;q=0.2, utf-7;q=0.5';
ctx.acceptsCharsets(['utf-7', 'utf-8']).should.equal('utf-8'); ctx.acceptsCharsets(['utf-7', 'utf-8']).should.equal('utf-8');
}) });
}) });
}) });

View File

@ -11,17 +11,17 @@ describe('ctx.acceptsEncodings()', function(){
ctx.req.headers['accept-encoding'] = 'gzip, compress;q=0.2'; ctx.req.headers['accept-encoding'] = 'gzip, compress;q=0.2';
ctx.acceptsEncodings().should.eql(['gzip', 'compress', 'identity']); ctx.acceptsEncodings().should.eql(['gzip', 'compress', 'identity']);
ctx.acceptsEncodings('gzip', 'compress').should.equal('gzip'); ctx.acceptsEncodings('gzip', 'compress').should.equal('gzip');
}) });
}) });
describe('when Accept-Encoding is not populated', function(){ describe('when Accept-Encoding is not populated', function(){
it('should return identity', function(){ it('should return identity', function(){
const ctx = context(); const ctx = context();
ctx.acceptsEncodings().should.eql(['identity']); ctx.acceptsEncodings().should.eql(['identity']);
ctx.acceptsEncodings('gzip', 'deflate', 'identity').should.equal('identity'); ctx.acceptsEncodings('gzip', 'deflate', 'identity').should.equal('identity');
}) });
}) });
}) });
describe('with multiple arguments', function(){ describe('with multiple arguments', function(){
it('should return the best fit', function(){ it('should return the best fit', function(){
@ -29,14 +29,14 @@ describe('ctx.acceptsEncodings()', function(){
ctx.req.headers['accept-encoding'] = 'gzip, compress;q=0.2'; ctx.req.headers['accept-encoding'] = 'gzip, compress;q=0.2';
ctx.acceptsEncodings('compress', 'gzip').should.eql('gzip'); ctx.acceptsEncodings('compress', 'gzip').should.eql('gzip');
ctx.acceptsEncodings('gzip', 'compress').should.eql('gzip'); ctx.acceptsEncodings('gzip', 'compress').should.eql('gzip');
}) });
}) });
describe('with an array', function(){ describe('with an array', function(){
it('should return the best fit', function(){ it('should return the best fit', function(){
const ctx = context(); const ctx = context();
ctx.req.headers['accept-encoding'] = 'gzip, compress;q=0.2'; ctx.req.headers['accept-encoding'] = 'gzip, compress;q=0.2';
ctx.acceptsEncodings(['compress', 'gzip']).should.eql('gzip'); ctx.acceptsEncodings(['compress', 'gzip']).should.eql('gzip');
}) });
}) });
}) });

View File

@ -10,9 +10,9 @@ describe('ctx.acceptsLanguages(langs)', function(){
const ctx = context(); const ctx = context();
ctx.req.headers['accept-language'] = 'en;q=0.8, es, pt'; ctx.req.headers['accept-language'] = 'en;q=0.8, es, pt';
ctx.acceptsLanguages().should.eql(['es', 'pt', 'en']); ctx.acceptsLanguages().should.eql(['es', 'pt', 'en']);
}) });
}) });
}) });
describe('with multiple arguments', function(){ describe('with multiple arguments', function(){
describe('when Accept-Language is populated', function(){ describe('when Accept-Language is populated', function(){
@ -21,31 +21,31 @@ describe('ctx.acceptsLanguages(langs)', function(){
const ctx = context(); const ctx = context();
ctx.req.headers['accept-language'] = 'en;q=0.8, es, pt'; ctx.req.headers['accept-language'] = 'en;q=0.8, es, pt';
ctx.acceptsLanguages('es', 'en').should.equal('es'); ctx.acceptsLanguages('es', 'en').should.equal('es');
}) });
}) });
describe('if no types match', function(){ describe('if no types match', function(){
it('should return false', function(){ it('should return false', function(){
const ctx = context(); const ctx = context();
ctx.req.headers['accept-language'] = 'en;q=0.8, es, pt'; ctx.req.headers['accept-language'] = 'en;q=0.8, es, pt';
ctx.acceptsLanguages('fr', 'au').should.be.false; ctx.acceptsLanguages('fr', 'au').should.be.false;
}) });
}) });
}) });
describe('when Accept-Language is not populated', function(){ describe('when Accept-Language is not populated', function(){
it('should return the first type', function(){ it('should return the first type', function(){
const ctx = context(); const ctx = context();
ctx.acceptsLanguages('es', 'en').should.equal('es'); ctx.acceptsLanguages('es', 'en').should.equal('es');
}) });
}) });
}) });
describe('with an array', function(){ describe('with an array', function(){
it('should return the best fit', function(){ it('should return the best fit', function(){
const ctx = context(); const ctx = context();
ctx.req.headers['accept-language'] = 'en;q=0.8, es, pt'; ctx.req.headers['accept-language'] = 'en;q=0.8, es, pt';
ctx.acceptsLanguages(['es', 'en']).should.equal('es'); ctx.acceptsLanguages(['es', 'en']).should.equal('es');
}) });
}) });
}) });

View File

@ -9,22 +9,22 @@ describe('req.charset', function(){
it('should return ""', function(){ it('should return ""', function(){
const req = request(); const req = request();
assert('' === req.charset); assert('' === req.charset);
}) });
}) });
describe('with charset present', function(){ describe('with charset present', function(){
it('should return ""', function(){ it('should return ""', function(){
const req = request(); const req = request();
req.header['content-type'] = 'text/plain'; req.header['content-type'] = 'text/plain';
assert('' === req.charset); assert('' === req.charset);
}) });
}) });
describe('with a charset', function(){ describe('with a charset', function(){
it('should return the charset', function(){ it('should return the charset', function(){
const req = request(); const req = request();
req.header['content-type'] = 'text/plain; charset=utf-8'; req.header['content-type'] = 'text/plain; charset=utf-8';
req.charset.should.equal('utf-8'); req.charset.should.equal('utf-8');
}) });
}) });
}) });

View File

@ -4,13 +4,13 @@
const context = require('../helpers/context'); const context = require('../helpers/context');
describe('ctx.fresh', function(){ describe('ctx.fresh', function(){
describe('the request method is not GET and HEAD', function (){ describe('the request method is not GET and HEAD', function(){
it('should return false', function (){ it('should return false', function(){
const ctx = context(); const ctx = context();
ctx.req.method = 'POST'; ctx.req.method = 'POST';
ctx.fresh.should.be.false; ctx.fresh.should.be.false;
}) });
}) });
describe('the response is non-2xx', function(){ describe('the response is non-2xx', function(){
it('should return false', function(){ it('should return false', function(){
@ -20,7 +20,7 @@ describe('ctx.fresh', function(){
ctx.req.headers['if-none-match'] = '123'; ctx.req.headers['if-none-match'] = '123';
ctx.set('ETag', '123'); ctx.set('ETag', '123');
ctx.fresh.should.be.false; ctx.fresh.should.be.false;
}) });
}); });
describe('the response is 2xx', function(){ describe('the response is 2xx', function(){
@ -32,8 +32,8 @@ describe('ctx.fresh', function(){
ctx.req.headers['if-none-match'] = '123'; ctx.req.headers['if-none-match'] = '123';
ctx.set('ETag', '123'); ctx.set('ETag', '123');
ctx.fresh.should.be.true; ctx.fresh.should.be.true;
}) });
}) });
describe('and etag do not match', function(){ describe('and etag do not match', function(){
it('should return false', function(){ it('should return false', function(){
@ -43,7 +43,7 @@ describe('ctx.fresh', function(){
ctx.req.headers['if-none-match'] = '123'; ctx.req.headers['if-none-match'] = '123';
ctx.set('ETag', 'hey'); ctx.set('ETag', 'hey');
ctx.fresh.should.be.false; ctx.fresh.should.be.false;
}) });
}) });
}) });
}) });

View File

@ -13,5 +13,5 @@ describe('ctx.get(name)', function(){
ctx.get('host').should.equal('http://google.com'); ctx.get('host').should.equal('http://google.com');
ctx.get('referer').should.equal('http://google.com'); ctx.get('referer').should.equal('http://google.com');
ctx.get('referrer').should.equal('http://google.com'); ctx.get('referrer').should.equal('http://google.com');
}) });
}) });

View File

@ -7,5 +7,5 @@ describe('req.header', function(){
it('should return the request header object', function(){ it('should return the request header object', function(){
const req = request(); const req = request();
req.header.should.equal(req.req.headers); req.header.should.equal(req.req.headers);
}) });
}) });

View File

@ -7,5 +7,5 @@ describe('req.headers', function(){
it('should return the request header object', function(){ it('should return the request header object', function(){
const req = request(); const req = request();
req.headers.should.equal(req.req.headers); req.headers.should.equal(req.req.headers);
}) });
}) });

View File

@ -9,33 +9,33 @@ describe('req.host', function(){
const req = request(); const req = request();
req.header.host = 'foo.com:3000'; req.header.host = 'foo.com:3000';
req.host.should.equal('foo.com:3000'); req.host.should.equal('foo.com:3000');
}) });
describe('with no host present', function(){ describe('with no host present', function(){
it('should return ""', function(){ it('should return ""', function(){
const req = request(); const req = request();
assert.equal(req.host, ''); assert.equal(req.host, '');
}) });
}) });
describe('when X-Forwarded-Host is present', function(){ describe('when X-Forwarded-Host is present', function(){
describe('and proxy is not trusted', function(){ describe('and proxy is not trusted', function(){
it('should be ignored', function(){ it('should be ignored', function(){
const req = request(); const req = request();
req.header['x-forwarded-host'] = 'bar.com'; req.header['x-forwarded-host'] = 'bar.com';
req.header['host'] = 'foo.com'; req.header.host = 'foo.com';
req.host.should.equal('foo.com'); req.host.should.equal('foo.com');
}) });
}) });
describe('and proxy is trusted', function(){ describe('and proxy is trusted', function(){
it('should be used', function(){ it('should be used', function(){
const req = request(); const req = request();
req.app.proxy = true; req.app.proxy = true;
req.header['x-forwarded-host'] = 'bar.com, baz.com'; req.header['x-forwarded-host'] = 'bar.com, baz.com';
req.header['host'] = 'foo.com'; req.header.host = 'foo.com';
req.host.should.equal('bar.com'); req.host.should.equal('bar.com');
}) });
}) });
}) });
}) });

View File

@ -9,33 +9,33 @@ describe('req.hostname', function(){
const req = request(); const req = request();
req.header.host = 'foo.com:3000'; req.header.host = 'foo.com:3000';
req.hostname.should.equal('foo.com'); req.hostname.should.equal('foo.com');
}) });
describe('with no host present', function(){ describe('with no host present', function(){
it('should return ""', function(){ it('should return ""', function(){
const req = request(); const req = request();
assert.equal(req.hostname, ''); assert.equal(req.hostname, '');
}) });
}) });
describe('when X-Forwarded-Host is present', function(){ describe('when X-Forwarded-Host is present', function(){
describe('and proxy is not trusted', function(){ describe('and proxy is not trusted', function(){
it('should be ignored', function(){ it('should be ignored', function(){
const req = request(); const req = request();
req.header['x-forwarded-host'] = 'bar.com'; req.header['x-forwarded-host'] = 'bar.com';
req.header['host'] = 'foo.com'; req.header.host = 'foo.com';
req.hostname.should.equal('foo.com') req.hostname.should.equal('foo.com');
}) });
}) });
describe('and proxy is trusted', function(){ describe('and proxy is trusted', function(){
it('should be used', function(){ it('should be used', function(){
const req = request(); const req = request();
req.app.proxy = true; req.app.proxy = true;
req.header['x-forwarded-host'] = 'bar.com, baz.com'; req.header['x-forwarded-host'] = 'bar.com, baz.com';
req.header['host'] = 'foo.com'; req.header.host = 'foo.com';
req.hostname.should.equal('bar.com') req.hostname.should.equal('bar.com');
}) });
}) });
}) });
}) });

View File

@ -22,29 +22,29 @@ describe('ctx.href', function(){
// change it also work // change it also work
ctx.url = '/foo/users/1?next=/dashboard'; ctx.url = '/foo/users/1?next=/dashboard';
ctx.href.should.equal('http://localhost/users/1?next=/dashboard'); ctx.href.should.equal('http://localhost/users/1?next=/dashboard');
}) });
it('should work with `GET http://example.com/foo`', function(done){ it('should work with `GET http://example.com/foo`', function(done){
const app = new Koa() const app = new Koa();
app.use(function* (){ app.use(function *(){
this.body = this.href this.body = this.href;
}) });
app.listen(function(){ app.listen(function(){
const address = this.address() const address = this.address();
http.get({ http.get({
host: 'localhost', host: 'localhost',
path: 'http://example.com/foo', path: 'http://example.com/foo',
port: address.port port: address.port
}, function(res){ }, function(res){
res.statusCode.should.equal(200) res.statusCode.should.equal(200);
var buf = '' var buf = '';
res.setEncoding('utf8') res.setEncoding('utf8');
res.on('data', function(s){ buf += s }) res.on('data', function(s){ buf += s; });
res.on('end', function(){ res.on('end', function(){
buf.should.equal('http://example.com/foo') buf.should.equal('http://example.com/foo');
done() done();
}) });
}) });
}) });
}) });
}) });

View File

@ -4,22 +4,22 @@
const request = require('../helpers/context').request; const request = require('../helpers/context').request;
describe('ctx.idempotent', function(){ describe('ctx.idempotent', function(){
describe('when the request method is idempotent', function (){ describe('when the request method is idempotent', function(){
it('should return true', function (){ it('should return true', function(){
['GET', 'HEAD', 'PUT', 'DELETE', 'OPTIONS', 'TRACE'].forEach(check); ['GET', 'HEAD', 'PUT', 'DELETE', 'OPTIONS', 'TRACE'].forEach(check);
function check(method) { function check(method){
const req = request(); const req = request();
req.method = method; req.method = method;
req.idempotent.should.equal(true); req.idempotent.should.equal(true);
} }
}) });
}) });
describe('when the request method is not idempotent', function(){ describe('when the request method is not idempotent', function(){
it('should return false', function (){ it('should return false', function(){
const req = request(); const req = request();
req.method = 'POST'; req.method = 'POST';
req.idempotent.should.equal(false); req.idempotent.should.equal(false);
}) });
}) });
}) });

View File

@ -11,8 +11,8 @@ describe('req.inspect()', function(){
req.method = 'GET'; req.method = 'GET';
delete req.req; delete req.req;
assert(null == req.inspect()); assert(null == req.inspect());
}) });
}) });
it('should return a json representation', function(){ it('should return a json representation', function(){
const req = request(); const req = request();
@ -27,5 +27,5 @@ describe('req.inspect()', function(){
host: 'example.com' host: 'example.com'
} }
}); });
}) });
}) });

View File

@ -11,22 +11,22 @@ describe('req.ip', function(){
req.header['x-forwarded-for'] = '127.0.0.1'; req.header['x-forwarded-for'] = '127.0.0.1';
req.socket.remoteAddress = '127.0.0.2'; req.socket.remoteAddress = '127.0.0.2';
req.ip.should.equal('127.0.0.1'); req.ip.should.equal('127.0.0.1');
}) });
}) });
describe('with no req.ips present', function(){ describe('with no req.ips present', function(){
it('should return req.socket.remoteAddress', function(){ it('should return req.socket.remoteAddress', function(){
const req = request(); const req = request();
req.socket.remoteAddress = '127.0.0.2'; req.socket.remoteAddress = '127.0.0.2';
req.ip.should.equal('127.0.0.2'); req.ip.should.equal('127.0.0.2');
}) });
describe('with req.socket.remoteAddress not present', function(){ describe('with req.socket.remoteAddress not present', function(){
it('should return an empty string', function(){ it('should return an empty string', function(){
const req = request(); const req = request();
req.socket.remoteAddress = null; req.socket.remoteAddress = null;
req.ip.should.equal(''); req.ip.should.equal('');
}) });
}) });
}) });
}) });

View File

@ -11,8 +11,8 @@ describe('req.ips', function(){
req.app.proxy = false; req.app.proxy = false;
req.header['x-forwarded-for'] = '127.0.0.1,127.0.0.2'; req.header['x-forwarded-for'] = '127.0.0.1,127.0.0.2';
req.ips.should.eql([]); req.ips.should.eql([]);
}) });
}) });
describe('and proxy is trusted', function(){ describe('and proxy is trusted', function(){
it('should be used', function(){ it('should be used', function(){
@ -20,7 +20,7 @@ describe('req.ips', function(){
req.app.proxy = true; req.app.proxy = true;
req.header['x-forwarded-for'] = '127.0.0.1,127.0.0.2'; req.header['x-forwarded-for'] = '127.0.0.1,127.0.0.2';
req.ips.should.eql(['127.0.0.1', '127.0.0.2']); req.ips.should.eql(['127.0.0.1', '127.0.0.2']);
}) });
}) });
}) });
}) });

View File

@ -2,7 +2,6 @@
'use strict'; 'use strict';
const context = require('../helpers/context'); const context = require('../helpers/context');
const should = require('should');
const assert = require('assert'); const assert = require('assert');
describe('ctx.is(type)', function(){ describe('ctx.is(type)', function(){
@ -12,7 +11,7 @@ describe('ctx.is(type)', function(){
ctx.header['transfer-encoding'] = 'chunked'; ctx.header['transfer-encoding'] = 'chunked';
ctx.is('text/*').should.equal('text/html'); ctx.is('text/*').should.equal('text/html');
}) });
describe('when no body is given', function(){ describe('when no body is given', function(){
it('should return null', function(){ it('should return null', function(){
@ -21,8 +20,8 @@ describe('ctx.is(type)', function(){
assert(null == ctx.is()); assert(null == ctx.is());
assert(null == ctx.is('image/*')); assert(null == ctx.is('image/*'));
assert(null == ctx.is('image/*', 'text/*')); assert(null == ctx.is('image/*', 'text/*'));
}) });
}) });
describe('when no content type is given', function(){ describe('when no content type is given', function(){
it('should return false', function(){ it('should return false', function(){
@ -32,8 +31,8 @@ describe('ctx.is(type)', function(){
ctx.is().should.be.false; ctx.is().should.be.false;
ctx.is('image/*').should.be.false; ctx.is('image/*').should.be.false;
ctx.is('text/*', 'image/*').should.be.false; ctx.is('text/*', 'image/*').should.be.false;
}) });
}) });
describe('give no types', function(){ describe('give no types', function(){
it('should return the mime type', function(){ it('should return the mime type', function(){
@ -42,8 +41,8 @@ describe('ctx.is(type)', function(){
ctx.header['transfer-encoding'] = 'chunked'; ctx.header['transfer-encoding'] = 'chunked';
ctx.is().should.equal('image/png'); ctx.is().should.equal('image/png');
}) });
}) });
describe('given one type', function(){ describe('given one type', function(){
it('should return the type or false', function(){ it('should return the type or false', function(){
@ -62,8 +61,8 @@ describe('ctx.is(type)', function(){
ctx.is('image/jpeg').should.be.false; ctx.is('image/jpeg').should.be.false;
ctx.is('text/*').should.be.false; ctx.is('text/*').should.be.false;
ctx.is('*/jpeg').should.be.false; ctx.is('*/jpeg').should.be.false;
}) });
}) });
describe('given multiple types', function(){ describe('given multiple types', function(){
it('should return the first match or false', function(){ it('should return the first match or false', function(){
@ -87,8 +86,8 @@ describe('ctx.is(type)', function(){
ctx.is('.jpeg').should.be.false; ctx.is('.jpeg').should.be.false;
ctx.is('text/*', 'application/*').should.be.false; ctx.is('text/*', 'application/*').should.be.false;
ctx.is('text/html', 'text/plain', 'application/json; charset=utf-8').should.be.false; ctx.is('text/html', 'text/plain', 'application/json; charset=utf-8').should.be.false;
}) });
}) });
describe('when Content-Type: application/x-www-form-urlencoded', function(){ describe('when Content-Type: application/x-www-form-urlencoded', function(){
it('should match "urlencoded"', function(){ it('should match "urlencoded"', function(){
@ -99,6 +98,6 @@ describe('ctx.is(type)', function(){
ctx.is('urlencoded').should.equal('urlencoded'); ctx.is('urlencoded').should.equal('urlencoded');
ctx.is('json', 'urlencoded').should.equal('urlencoded'); ctx.is('json', 'urlencoded').should.equal('urlencoded');
ctx.is('urlencoded', 'json').should.equal('urlencoded'); ctx.is('urlencoded', 'json').should.equal('urlencoded');
}) });
}) });
}) });

View File

@ -9,10 +9,10 @@ describe('ctx.length', function(){
const req = request(); const req = request();
req.header['content-length'] = '10'; req.header['content-length'] = '10';
req.length.should.equal(10); req.length.should.equal(10);
}) });
describe('with no content-length present', function(){ describe('with no content-length present', function(){
const req = request(); const req = request();
assert(null == req.length); assert(null == req.length);
}) });
}) });

View File

@ -2,8 +2,6 @@
'use strict'; 'use strict';
const Stream = require('stream'); const Stream = require('stream');
const http = require('http');
const Koa = require('../../');
const context = require('../helpers/context'); const context = require('../helpers/context');
describe('ctx.origin', function(){ describe('ctx.origin', function(){
@ -22,5 +20,5 @@ describe('ctx.origin', function(){
// change it also work // change it also work
ctx.url = '/foo/users/1?next=/dashboard'; ctx.url = '/foo/users/1?next=/dashboard';
ctx.origin.should.equal('http://localhost'); ctx.origin.should.equal('http://localhost');
}) });
}) });

View File

@ -8,8 +8,8 @@ describe('ctx.path', function(){
const ctx = context(); const ctx = context();
ctx.url = '/login?next=/dashboard'; ctx.url = '/login?next=/dashboard';
ctx.path.should.equal('/login'); ctx.path.should.equal('/login');
}) });
}) });
describe('ctx.path=', function(){ describe('ctx.path=', function(){
it('should set the pathname', function(){ it('should set the pathname', function(){
@ -19,7 +19,7 @@ describe('ctx.path=', function(){
ctx.path = '/logout'; ctx.path = '/logout';
ctx.path.should.equal('/logout'); ctx.path.should.equal('/logout');
ctx.url.should.equal('/logout?next=/dashboard'); ctx.url.should.equal('/logout?next=/dashboard');
}) });
it('should change .url but not .originalUrl', function(){ it('should change .url but not .originalUrl', function(){
const ctx = context({ url: '/login' }); const ctx = context({ url: '/login' });
@ -27,5 +27,5 @@ describe('ctx.path=', function(){
ctx.url.should.equal('/logout'); ctx.url.should.equal('/logout');
ctx.originalUrl.should.equal('/login'); ctx.originalUrl.should.equal('/login');
ctx.request.originalUrl.should.equal('/login'); ctx.request.originalUrl.should.equal('/login');
}) });
}) });

View File

@ -9,16 +9,16 @@ describe('req.protocol', function(){
const req = request(); const req = request();
req.req.socket = { encrypted: true }; req.req.socket = { encrypted: true };
req.protocol.should.equal('https'); req.protocol.should.equal('https');
}) });
}) });
describe('when unencrypted', function(){ describe('when unencrypted', function(){
it('should return "http"', function(){ it('should return "http"', function(){
const req = request(); const req = request();
req.req.socket = {}; req.req.socket = {};
req.protocol.should.equal('http'); req.protocol.should.equal('http');
}) });
}) });
describe('when X-Forwarded-Proto is set', function(){ describe('when X-Forwarded-Proto is set', function(){
describe('and proxy is trusted', function(){ describe('and proxy is trusted', function(){
@ -28,7 +28,7 @@ describe('req.protocol', function(){
req.req.socket = {}; req.req.socket = {};
req.header['x-forwarded-proto'] = 'https, http'; req.header['x-forwarded-proto'] = 'https, http';
req.protocol.should.equal('https'); req.protocol.should.equal('https');
}) });
describe('and X-Forwarded-Proto is empty', function(){ describe('and X-Forwarded-Proto is empty', function(){
it('should return "http"', function(){ it('should return "http"', function(){
@ -37,9 +37,9 @@ describe('req.protocol', function(){
req.req.socket = {}; req.req.socket = {};
req.header['x-forwarded-proto'] = ''; req.header['x-forwarded-proto'] = '';
req.protocol.should.equal('http'); req.protocol.should.equal('http');
}) });
}) });
}) });
describe('and proxy is not trusted', function(){ describe('and proxy is not trusted', function(){
it('should not be used', function(){ it('should not be used', function(){
@ -47,7 +47,7 @@ describe('req.protocol', function(){
req.req.socket = {}; req.req.socket = {};
req.header['x-forwarded-proto'] = 'https, http'; req.header['x-forwarded-proto'] = 'https, http';
req.protocol.should.equal('http'); req.protocol.should.equal('http');
}) });
}) });
}) });
}) });

View File

@ -8,21 +8,21 @@ describe('ctx.query', function(){
it('should return an empty object', function(){ it('should return an empty object', function(){
const ctx = context({ url: '/' }); const ctx = context({ url: '/' });
ctx.query.should.eql({}); ctx.query.should.eql({});
}) });
it('should return the same object each time it\'s accessed', function(done) { it('should return the same object each time it\'s accessed', function(done){
const ctx = context({ url: '/' }); const ctx = context({ url: '/' });
ctx.query.a = '2'; ctx.query.a = '2';
ctx.query.a.should.equal('2'); ctx.query.a.should.equal('2');
done(); done();
}); });
}) });
it('should return a parsed query-string', function(){ it('should return a parsed query-string', function(){
const ctx = context({ url: '/?page=2' }); const ctx = context({ url: '/?page=2' });
ctx.query.page.should.equal('2'); ctx.query.page.should.equal('2');
}) });
}) });
describe('ctx.query=', function(){ describe('ctx.query=', function(){
it('should stringify and replace the querystring and search', function(){ it('should stringify and replace the querystring and search', function(){
@ -30,8 +30,8 @@ describe('ctx.query=', function(){
ctx.query = { page: 2, color: 'blue' }; ctx.query = { page: 2, color: 'blue' };
ctx.url.should.equal('/store/shoes?page=2&color=blue'); ctx.url.should.equal('/store/shoes?page=2&color=blue');
ctx.querystring.should.equal('page=2&color=blue'); ctx.querystring.should.equal('page=2&color=blue');
ctx.search.should.equal('?page=2&color=blue') ctx.search.should.equal('?page=2&color=blue');
}) });
it('should change .url but not .originalUrl', function(){ it('should change .url but not .originalUrl', function(){
const ctx = context({ url: '/store/shoes' }); const ctx = context({ url: '/store/shoes' });
@ -39,5 +39,5 @@ describe('ctx.query=', function(){
ctx.url.should.equal('/store/shoes?page=2'); ctx.url.should.equal('/store/shoes?page=2');
ctx.originalUrl.should.equal('/store/shoes'); ctx.originalUrl.should.equal('/store/shoes');
ctx.request.originalUrl.should.equal('/store/shoes'); ctx.request.originalUrl.should.equal('/store/shoes');
}) });
}) });

View File

@ -7,16 +7,16 @@ describe('ctx.querystring', function(){
it('should return the querystring', function(){ it('should return the querystring', function(){
const ctx = context({ url: '/store/shoes?page=2&color=blue' }); const ctx = context({ url: '/store/shoes?page=2&color=blue' });
ctx.querystring.should.equal('page=2&color=blue'); ctx.querystring.should.equal('page=2&color=blue');
}) });
describe('when ctx.req not present', function(){ describe('when ctx.req not present', function(){
it('should return an empty string', function(){ it('should return an empty string', function(){
const ctx = context(); const ctx = context();
ctx.request.req = null; ctx.request.req = null;
ctx.querystring.should.equal(''); ctx.querystring.should.equal('');
}) });
}) });
}) });
describe('ctx.querystring=', function(){ describe('ctx.querystring=', function(){
it('should replace the querystring', function(){ it('should replace the querystring', function(){
@ -24,7 +24,7 @@ describe('ctx.querystring=', function(){
ctx.querystring = 'page=2&color=blue'; ctx.querystring = 'page=2&color=blue';
ctx.url.should.equal('/store/shoes?page=2&color=blue'); ctx.url.should.equal('/store/shoes?page=2&color=blue');
ctx.querystring.should.equal('page=2&color=blue'); ctx.querystring.should.equal('page=2&color=blue');
}) });
it('should update ctx.search and ctx.query', function(){ it('should update ctx.search and ctx.query', function(){
const ctx = context({ url: '/store/shoes' }); const ctx = context({ url: '/store/shoes' });
@ -35,7 +35,7 @@ describe('ctx.querystring=', function(){
page: '2', page: '2',
color: 'blue' color: 'blue'
}); });
}) });
it('should change .url but not .originalUrl', function(){ it('should change .url but not .originalUrl', function(){
const ctx = context({ url: '/store/shoes' }); const ctx = context({ url: '/store/shoes' });
@ -43,5 +43,5 @@ describe('ctx.querystring=', function(){
ctx.url.should.equal('/store/shoes?page=2&color=blue'); ctx.url.should.equal('/store/shoes?page=2&color=blue');
ctx.originalUrl.should.equal('/store/shoes'); ctx.originalUrl.should.equal('/store/shoes');
ctx.request.originalUrl.should.equal('/store/shoes'); ctx.request.originalUrl.should.equal('/store/shoes');
}) });
}) });

View File

@ -9,7 +9,7 @@ describe('ctx.search=', function(){
ctx.search = '?page=2&color=blue'; ctx.search = '?page=2&color=blue';
ctx.url.should.equal('/store/shoes?page=2&color=blue'); ctx.url.should.equal('/store/shoes?page=2&color=blue');
ctx.search.should.equal('?page=2&color=blue'); ctx.search.should.equal('?page=2&color=blue');
}) });
it('should update ctx.querystring and ctx.query', function(){ it('should update ctx.querystring and ctx.query', function(){
const ctx = context({ url: '/store/shoes' }); const ctx = context({ url: '/store/shoes' });
@ -20,7 +20,7 @@ describe('ctx.search=', function(){
page: '2', page: '2',
color: 'blue' color: 'blue'
}); });
}) });
it('should change .url but not .originalUrl', function(){ it('should change .url but not .originalUrl', function(){
const ctx = context({ url: '/store/shoes' }); const ctx = context({ url: '/store/shoes' });
@ -28,12 +28,12 @@ describe('ctx.search=', function(){
ctx.url.should.equal('/store/shoes?page=2&color=blue'); ctx.url.should.equal('/store/shoes?page=2&color=blue');
ctx.originalUrl.should.equal('/store/shoes'); ctx.originalUrl.should.equal('/store/shoes');
ctx.request.originalUrl.should.equal('/store/shoes'); ctx.request.originalUrl.should.equal('/store/shoes');
}) });
describe('when missing', function(){ describe('when missing', function(){
it('should return ""', function(){ it('should return ""', function(){
const ctx = context({ url: '/store/shoes' }); const ctx = context({ url: '/store/shoes' });
ctx.search.should.equal(''); ctx.search.should.equal('');
}) });
}) });
}) });

View File

@ -8,5 +8,5 @@ describe('req.secure', function(){
const req = request(); const req = request();
req.req.socket = { encrypted: true }; req.req.socket = { encrypted: true };
req.secure.should.be.true; req.secure.should.be.true;
}) });
}) });

View File

@ -12,5 +12,5 @@ describe('req.stale', function(){
ctx.set('ETag', '"123"'); ctx.set('ETag', '"123"');
ctx.fresh.should.be.true; ctx.fresh.should.be.true;
ctx.stale.should.be.false; ctx.stale.should.be.false;
}) });
}) });

View File

@ -12,10 +12,10 @@ describe('req.subdomains', function(){
req.app.subdomainOffset = 3; req.app.subdomainOffset = 3;
req.subdomains.should.eql(['tobi']); req.subdomains.should.eql(['tobi']);
}) });
describe('with no host present', function(){ describe('with no host present', function(){
const req = request(); const req = request();
req.subdomains.should.eql([]); req.subdomains.should.eql([]);
}) });
}) });

View File

@ -9,10 +9,10 @@ describe('req.type', function(){
const req = request(); const req = request();
req.header['content-type'] = 'text/html; charset=utf-8'; req.header['content-type'] = 'text/html; charset=utf-8';
req.type.should.equal('text/html'); req.type.should.equal('text/html');
}) });
describe('with no host present', function(){ describe('with no host present', function(){
const req = request(); const req = request();
assert('' === req.type); assert('' === req.type);
}) });
}) });

View File

@ -9,17 +9,17 @@ describe('ctx.append(name, val)', function(){
ctx.append('x-foo', 'bar1'); ctx.append('x-foo', 'bar1');
ctx.append('x-foo', 'bar2'); ctx.append('x-foo', 'bar2');
ctx.response.header['x-foo'].should.eql(['bar1', 'bar2']); ctx.response.header['x-foo'].should.eql(['bar1', 'bar2']);
}) });
it('should accept array of values', function (){ it('should accept array of values', function(){
const ctx = context(); const ctx = context();
ctx.append('Set-Cookie', ['foo=bar', 'fizz=buzz']); ctx.append('Set-Cookie', ['foo=bar', 'fizz=buzz']);
ctx.append('Set-Cookie', 'hi=again'); ctx.append('Set-Cookie', 'hi=again');
ctx.response.header['set-cookie'].should.eql(['foo=bar', 'fizz=buzz', 'hi=again']); ctx.response.header['set-cookie'].should.eql(['foo=bar', 'fizz=buzz', 'hi=again']);
}) });
it('should get reset by res.set(field, val)', function (){ it('should get reset by res.set(field, val)', function(){
const ctx = context(); const ctx = context();
ctx.append('Link', '<http://localhost/>'); ctx.append('Link', '<http://localhost/>');
@ -28,14 +28,14 @@ describe('ctx.append(name, val)', function(){
ctx.set('Link', '<http://127.0.0.1/>'); ctx.set('Link', '<http://127.0.0.1/>');
ctx.response.header.link.should.equal('<http://127.0.0.1/>'); ctx.response.header.link.should.equal('<http://127.0.0.1/>');
}) });
it('should work with res.set(field, val) first', function (){ it('should work with res.set(field, val) first', function(){
const ctx = context(); const ctx = context();
ctx.set('Link', '<http://localhost/>'); ctx.set('Link', '<http://localhost/>');
ctx.append('Link', '<http://localhost:80/>'); ctx.append('Link', '<http://localhost:80/>');
ctx.response.header.link.should.eql(['<http://localhost/>', '<http://localhost:80/>']); ctx.response.header.link.should.eql(['<http://localhost/>', '<http://localhost:80/>']);
}) });
}) });

View File

@ -12,16 +12,16 @@ describe('ctx.attachment([filename])', function(){
ctx.attachment('path/to/tobi.png'); ctx.attachment('path/to/tobi.png');
const str = 'attachment; filename="tobi.png"'; const str = 'attachment; filename="tobi.png"';
ctx.response.header['content-disposition'].should.equal(str); ctx.response.header['content-disposition'].should.equal(str);
}) });
}) });
describe('when omitting filename', function(){ describe('when omitting filename', function(){
it('should not set filename param', function(){ it('should not set filename param', function(){
const ctx = context(); const ctx = context();
ctx.attachment(); ctx.attachment();
ctx.response.header['content-disposition'].should.equal('attachment'); ctx.response.header['content-disposition'].should.equal('attachment');
}) });
}) });
describe('when given a no-ascii filename', function(){ describe('when given a no-ascii filename', function(){
it('should set the encodeURI filename param', function(){ it('should set the encodeURI filename param', function(){
@ -29,21 +29,21 @@ describe('ctx.attachment([filename])', function(){
ctx.attachment('path/to/include-no-ascii-char-中文名-ok.png'); ctx.attachment('path/to/include-no-ascii-char-中文名-ok.png');
const str = 'attachment; filename=\"include-no-ascii-char-???-ok.png\"; filename*=UTF-8\'\'include-no-ascii-char-%E4%B8%AD%E6%96%87%E5%90%8D-ok.png'; const str = 'attachment; filename=\"include-no-ascii-char-???-ok.png\"; filename*=UTF-8\'\'include-no-ascii-char-%E4%B8%AD%E6%96%87%E5%90%8D-ok.png';
ctx.response.header['content-disposition'].should.equal(str); ctx.response.header['content-disposition'].should.equal(str);
}) });
it('should work with http client', function(done){ it('should work with http client', function(done){
const app = new Koa(); const app = new Koa();
app.use(function* (next){ app.use(function *(next){
this.attachment('path/to/include-no-ascii-char-中文名-ok.json') this.attachment('path/to/include-no-ascii-char-中文名-ok.json');
this.body = {foo: 'bar'} this.body = {foo: 'bar'};
}) });
request(app.listen()) request(app.listen())
.get('/') .get('/')
.expect('content-disposition', 'attachment; filename="include-no-ascii-char-???-ok.json"; filename*=UTF-8\'\'include-no-ascii-char-%E4%B8%AD%E6%96%87%E5%90%8D-ok.json') .expect('content-disposition', 'attachment; filename="include-no-ascii-char-???-ok.json"; filename*=UTF-8\'\'include-no-ascii-char-%E4%B8%AD%E6%96%87%E5%90%8D-ok.json')
.expect({foo: 'bar'}) .expect({foo: 'bar'})
.expect(200, done) .expect(200, done);
}) });
}) });
}) });

View File

@ -12,7 +12,7 @@ describe('res.body=', function(){
res.type = 'png'; res.type = 'png';
res.body = new Buffer('something'); res.body = new Buffer('something');
assert('image/png' == res.header['content-type']); assert('image/png' == res.header['content-type']);
}) });
describe('when body is an object', function(){ describe('when body is an object', function(){
it('should override as json', function(){ it('should override as json', function(){
@ -23,52 +23,52 @@ describe('res.body=', function(){
res.body = { foo: 'bar' }; res.body = { foo: 'bar' };
assert('application/json; charset=utf-8' == res.header['content-type']); assert('application/json; charset=utf-8' == res.header['content-type']);
}) });
}) });
it('should override length', function(){ it('should override length', function(){
const res = response(); const res = response();
res.type = 'html'; res.type = 'html';
res.body = 'something'; res.body = 'something';
res.length.should.equal(9); res.length.should.equal(9);
}) });
}) });
describe('when a string is given', function(){ describe('when a string is given', function(){
it('should default to text', function(){ it('should default to text', function(){
const res = response(); const res = response();
res.body = 'Tobi'; res.body = 'Tobi';
assert('text/plain; charset=utf-8' == res.header['content-type']); assert('text/plain; charset=utf-8' == res.header['content-type']);
}) });
it('should set length', function(){ it('should set length', function(){
const res = response(); const res = response();
res.body = 'Tobi'; res.body = 'Tobi';
assert('4' == res.header['content-length']); assert('4' == res.header['content-length']);
}) });
describe('and contains a non-leading <', function(){ describe('and contains a non-leading <', function(){
it('should default to text', function(){ it('should default to text', function(){
const res = response(); const res = response();
res.body = 'aklsdjf < klajsdlfjasd'; res.body = 'aklsdjf < klajsdlfjasd';
assert('text/plain; charset=utf-8' == res.header['content-type']); assert('text/plain; charset=utf-8' == res.header['content-type']);
}) });
}) });
}) });
describe('when an html string is given', function(){ describe('when an html string is given', function(){
it('should default to html', function(){ it('should default to html', function(){
const res = response(); const res = response();
res.body = '<h1>Tobi</h1>'; res.body = '<h1>Tobi</h1>';
assert('text/html; charset=utf-8' == res.header['content-type']); assert('text/html; charset=utf-8' == res.header['content-type']);
}) });
it('should set length', function(){ it('should set length', function(){
const string = '<h1>Tobi</h1>'; const string = '<h1>Tobi</h1>';
const res = response(); const res = response();
res.body = string; res.body = string;
assert.equal(res.length, Buffer.byteLength(string)); assert.equal(res.length, Buffer.byteLength(string));
}) });
it('should set length when body is overridden', function(){ it('should set length when body is overridden', function(){
const string = '<h1>Tobi</h1>'; const string = '<h1>Tobi</h1>';
@ -76,16 +76,16 @@ describe('res.body=', function(){
res.body = string; res.body = string;
res.body = string + string; res.body = string + string;
assert.equal(res.length, 2 * Buffer.byteLength(string)); assert.equal(res.length, 2 * Buffer.byteLength(string));
}) });
describe('when it contains leading whitespace', function(){ describe('when it contains leading whitespace', function(){
it('should default to html', function(){ it('should default to html', function(){
const res = response(); const res = response();
res.body = ' <h1>Tobi</h1>'; res.body = ' <h1>Tobi</h1>';
assert('text/html; charset=utf-8' == res.header['content-type']); assert('text/html; charset=utf-8' == res.header['content-type']);
}) });
}) });
}) });
describe('when an xml string is given', function(){ describe('when an xml string is given', function(){
it('should default to html', function(){ it('should default to html', function(){
@ -99,36 +99,36 @@ describe('res.body=', function(){
const res = response(); const res = response();
res.body = '<?xml version="1.0" encoding="UTF-8"?>\n<俄语>данные</俄语>'; res.body = '<?xml version="1.0" encoding="UTF-8"?>\n<俄语>данные</俄语>';
assert('text/html; charset=utf-8' == res.header['content-type']); assert('text/html; charset=utf-8' == res.header['content-type']);
}) });
}) });
describe('when a stream is given', function(){ describe('when a stream is given', function(){
it('should default to an octet stream', function(){ it('should default to an octet stream', function(){
const res = response(); const res = response();
res.body = fs.createReadStream('LICENSE'); res.body = fs.createReadStream('LICENSE');
assert('application/octet-stream' == res.header['content-type']); assert('application/octet-stream' == res.header['content-type']);
}) });
}) });
describe('when a buffer is given', function(){ describe('when a buffer is given', function(){
it('should default to an octet stream', function(){ it('should default to an octet stream', function(){
const res = response(); const res = response();
res.body = new Buffer('hey'); res.body = new Buffer('hey');
assert('application/octet-stream' == res.header['content-type']); assert('application/octet-stream' == res.header['content-type']);
}) });
it('should set length', function(){ it('should set length', function(){
const res = response(); const res = response();
res.body = new Buffer('Tobi'); res.body = new Buffer('Tobi');
assert('4' == res.header['content-length']); assert('4' == res.header['content-length']);
}) });
}) });
describe('when an object is given', function(){ describe('when an object is given', function(){
it('should default to json', function(){ it('should default to json', function(){
const res = response(); const res = response();
res.body = { foo: 'bar' }; res.body = { foo: 'bar' };
assert('application/json; charset=utf-8' == res.header['content-type']); assert('application/json; charset=utf-8' == res.header['content-type']);
}) });
}) });
}) });

View File

@ -8,25 +8,25 @@ describe('res.etag=', function(){
const res = response(); const res = response();
res.etag = '"asdf"'; res.etag = '"asdf"';
res.header.etag.should.equal('"asdf"'); res.header.etag.should.equal('"asdf"');
}) });
it('should not modify a weak etag', function(){ it('should not modify a weak etag', function(){
const res = response(); const res = response();
res.etag = 'W/"asdf"'; res.etag = 'W/"asdf"';
res.header.etag.should.equal('W/"asdf"'); res.header.etag.should.equal('W/"asdf"');
}) });
it('should add quotes around an etag if necessary', function(){ it('should add quotes around an etag if necessary', function(){
const res = response(); const res = response();
res.etag = 'asdf'; res.etag = 'asdf';
res.header.etag.should.equal('"asdf"'); res.header.etag.should.equal('"asdf"');
}) });
}) });
describe('res.etag', function(){ describe('res.etag', function(){
it('should return etag', function(){ it('should return etag', function(){
const res = response(); const res = response();
res.etag = '"asdf"'; res.etag = '"asdf"';
res.etag.should.equal('"asdf"'); res.etag.should.equal('"asdf"');
}) });
}) });

View File

@ -8,13 +8,13 @@ describe('res.header', function(){
const res = response(); const res = response();
res.set('X-Foo', 'bar'); res.set('X-Foo', 'bar');
res.header.should.eql({ 'x-foo': 'bar' }); res.header.should.eql({ 'x-foo': 'bar' });
}) });
describe('when res._headers not present', function (){ describe('when res._headers not present', function(){
it('should return empty object', function (){ it('should return empty object', function(){
const res = response(); const res = response();
res.res._headers = null; res.res._headers = null;
res.header.should.eql({}); res.header.should.eql({});
}) });
}) });
}) });

View File

@ -8,13 +8,13 @@ describe('res.header', function(){
const res = response(); const res = response();
res.set('X-Foo', 'bar'); res.set('X-Foo', 'bar');
res.headers.should.eql({ 'x-foo': 'bar' }); res.headers.should.eql({ 'x-foo': 'bar' });
}) });
describe('when res._headers not present', function (){ describe('when res._headers not present', function(){
it('should return empty object', function (){ it('should return empty object', function(){
const res = response(); const res = response();
res.res._headers = null; res.res._headers = null;
res.headers.should.eql({}); res.headers.should.eql({});
}) });
}) });
}) });

View File

@ -11,8 +11,8 @@ describe('res.inspect()', function(){
res.body = 'hello'; res.body = 'hello';
delete res.res; delete res.res;
assert(null == res.inspect()); assert(null == res.inspect());
}) });
}) });
it('should return a json representation', function(){ it('should return a json representation', function(){
const res = response(); const res = response();
@ -27,5 +27,5 @@ describe('res.inspect()', function(){
'content-type': 'text/plain; charset=utf-8' 'content-type': 'text/plain; charset=utf-8'
} }
}); });
}) });
}) });

View File

@ -2,7 +2,6 @@
'use strict'; 'use strict';
const context = require('../helpers/context'); const context = require('../helpers/context');
const should = require('should');
const assert = require('assert'); const assert = require('assert');
describe('response.is(type)', function(){ describe('response.is(type)', function(){
@ -11,7 +10,7 @@ describe('response.is(type)', function(){
res.type = 'text/html; charset=utf-8'; res.type = 'text/html; charset=utf-8';
res.is('text/*').should.equal('text/html'); res.is('text/*').should.equal('text/html');
}) });
describe('when no type is set', function(){ describe('when no type is set', function(){
it('should return false', function(){ it('should return false', function(){
@ -19,8 +18,8 @@ describe('response.is(type)', function(){
assert(false === res.is()); assert(false === res.is());
assert(false === res.is('html')); assert(false === res.is('html'));
}) });
}) });
describe('when given no types', function(){ describe('when given no types', function(){
it('should return the type', function(){ it('should return the type', function(){
@ -28,8 +27,8 @@ describe('response.is(type)', function(){
res.type = 'text/html; charset=utf-8'; res.type = 'text/html; charset=utf-8';
res.is().should.equal('text/html'); res.is().should.equal('text/html');
}) });
}) });
describe('given one type', function(){ describe('given one type', function(){
it('should return the type or false', function(){ it('should return the type or false', function(){
@ -47,8 +46,8 @@ describe('response.is(type)', function(){
res.is('image/jpeg').should.be.false; res.is('image/jpeg').should.be.false;
res.is('text/*').should.be.false; res.is('text/*').should.be.false;
res.is('*/jpeg').should.be.false; res.is('*/jpeg').should.be.false;
}) });
}) });
describe('given multiple types', function(){ describe('given multiple types', function(){
it('should return the first match or false', function(){ it('should return the first match or false', function(){
@ -71,8 +70,8 @@ describe('response.is(type)', function(){
res.is('.jpeg').should.be.false; res.is('.jpeg').should.be.false;
res.is('text/*', 'application/*').should.be.false; res.is('text/*', 'application/*').should.be.false;
res.is('text/html', 'text/plain', 'application/json; charset=utf-8').should.be.false; res.is('text/html', 'text/plain', 'application/json; charset=utf-8').should.be.false;
}) });
}) });
describe('when Content-Type: application/x-www-form-urlencoded', function(){ describe('when Content-Type: application/x-www-form-urlencoded', function(){
it('should match "urlencoded"', function(){ it('should match "urlencoded"', function(){
@ -82,6 +81,6 @@ describe('response.is(type)', function(){
res.is('urlencoded').should.equal('urlencoded'); res.is('urlencoded').should.equal('urlencoded');
res.is('json', 'urlencoded').should.equal('urlencoded'); res.is('json', 'urlencoded').should.equal('urlencoded');
res.is('urlencoded', 'json').should.equal('urlencoded'); res.is('urlencoded', 'json').should.equal('urlencoded');
}) });
}) });
}) });

View File

@ -9,14 +9,14 @@ describe('res.lastModified', function(){
const date = new Date(); const date = new Date();
res.lastModified = date; res.lastModified = date;
res.header['last-modified'].should.equal(date.toUTCString()); res.header['last-modified'].should.equal(date.toUTCString());
}) });
it('should work with date strings', function(){ it('should work with date strings', function(){
const res = response(); const res = response();
const date = new Date(); const date = new Date();
res.lastModified = date.toString(); res.lastModified = date.toString();
res.header['last-modified'].should.equal(date.toUTCString()); res.header['last-modified'].should.equal(date.toUTCString());
}) });
it('should get the header as a Date', function(){ it('should get the header as a Date', function(){
// Note: Date() removes milliseconds, but it's practically important. // Note: Date() removes milliseconds, but it's practically important.
@ -25,12 +25,12 @@ describe('res.lastModified', function(){
res.lastModified = date; res.lastModified = date;
(res.lastModified.getTime() / 1000) (res.lastModified.getTime() / 1000)
.should.equal(Math.floor(date.getTime() / 1000)); .should.equal(Math.floor(date.getTime() / 1000));
}) });
describe('when lastModified not set', function (){ describe('when lastModified not set', function(){
it('should get undefined', function(){ it('should get undefined', function(){
const res = response(); const res = response();
(res.lastModified === undefined).should.be.ok; (res.lastModified === undefined).should.be.ok;
}) });
}) });
}) });

View File

@ -12,9 +12,9 @@ describe('res.length', function(){
const res = response(); const res = response();
res.header['content-length'] = '120'; res.header['content-length'] = '120';
res.length.should.equal(120); res.length.should.equal(120);
}) });
}) });
}) });
describe('res.length', function(){ describe('res.length', function(){
describe('when Content-Length is defined', function(){ describe('when Content-Length is defined', function(){
@ -22,8 +22,8 @@ describe('res.length', function(){
const res = response(); const res = response();
res.set('Content-Length', '1024'); res.set('Content-Length', '1024');
res.length.should.equal(1024); res.length.should.equal(1024);
}) });
}) });
describe('when Content-Length is not defined', function(){ describe('when Content-Length is not defined', function(){
describe('and a .body is set', function(){ describe('and a .body is set', function(){
@ -56,14 +56,14 @@ describe('res.length', function(){
res.body = null; res.body = null;
should.not.exist(res.length); should.not.exist(res.length);
}) });
}) });
describe('and .body is not', function(){ describe('and .body is not', function(){
it('should return undefined', function(){ it('should return undefined', function(){
const res = response(); const res = response();
assert(null == res.length); assert(null == res.length);
}) });
}) });
}) });
}) });

View File

@ -2,23 +2,22 @@
'use strict'; 'use strict';
const response = require('../helpers/context').response; const response = require('../helpers/context').response;
const Stream = require('stream');
describe('res.message', function(){ describe('res.message', function(){
it('should return the response status message', function(){ it('should return the response status message', function(){
const res = response(); const res = response();
res.status = 200; res.status = 200;
res.message.should.equal('OK'); res.message.should.equal('OK');
}) });
describe('when res.message not present', function(){ describe('when res.message not present', function(){
it('should look up in statuses', function(){ it('should look up in statuses', function(){
const res = response(); const res = response();
res.res.statusCode = 200; res.res.statusCode = 200;
res.message.should.equal('OK'); res.message.should.equal('OK');
}) });
}) });
}) });
describe('res.message=', function(){ describe('res.message=', function(){
it('should set response status message', function(){ it('should set response status message', function(){
@ -27,5 +26,5 @@ describe('res.message=', function(){
res.message = 'ok'; res.message = 'ok';
res.res.statusMessage.should.equal('ok'); res.res.statusMessage.should.equal('ok');
res.inspect().message.should.equal('ok'); res.inspect().message.should.equal('ok');
}) });
}) });

View File

@ -9,7 +9,7 @@ describe('ctx.redirect(url)', function(){
ctx.redirect('http://google.com'); ctx.redirect('http://google.com');
ctx.response.header.location.should.equal('http://google.com'); ctx.response.header.location.should.equal('http://google.com');
ctx.status.should.equal(302); ctx.status.should.equal(302);
}) });
describe('with "back"', function(){ describe('with "back"', function(){
it('should redirect to Referrer', function(){ it('should redirect to Referrer', function(){
@ -17,27 +17,27 @@ describe('ctx.redirect(url)', function(){
ctx.req.headers.referrer = '/login'; ctx.req.headers.referrer = '/login';
ctx.redirect('back'); ctx.redirect('back');
ctx.response.header.location.should.equal('/login'); ctx.response.header.location.should.equal('/login');
}) });
it('should redirect to Referer', function(){ it('should redirect to Referer', function(){
const ctx = context(); const ctx = context();
ctx.req.headers.referer = '/login'; ctx.req.headers.referer = '/login';
ctx.redirect('back'); ctx.redirect('back');
ctx.response.header.location.should.equal('/login'); ctx.response.header.location.should.equal('/login');
}) });
it('should default to alt', function(){ it('should default to alt', function(){
const ctx = context(); const ctx = context();
ctx.redirect('back', '/index.html'); ctx.redirect('back', '/index.html');
ctx.response.header.location.should.equal('/index.html'); ctx.response.header.location.should.equal('/index.html');
}) });
it('should default redirect to /', function(){ it('should default redirect to /', function(){
const ctx = context(); const ctx = context();
ctx.redirect('back'); ctx.redirect('back');
ctx.response.header.location.should.equal('/'); ctx.response.header.location.should.equal('/');
}) });
}) });
describe('when html is accepted', function(){ describe('when html is accepted', function(){
it('should respond with html', function(){ it('should respond with html', function(){
@ -47,7 +47,7 @@ describe('ctx.redirect(url)', function(){
ctx.redirect(url); ctx.redirect(url);
ctx.response.header['content-type'].should.equal('text/html; charset=utf-8'); ctx.response.header['content-type'].should.equal('text/html; charset=utf-8');
ctx.body.should.equal(`Redirecting to <a href="${url}">${url}</a>.`); ctx.body.should.equal(`Redirecting to <a href="${url}">${url}</a>.`);
}) });
it('should escape the url', function(){ it('should escape the url', function(){
const ctx = context(); const ctx = context();
@ -57,8 +57,8 @@ describe('ctx.redirect(url)', function(){
url = escape(url); url = escape(url);
ctx.response.header['content-type'].should.equal('text/html; charset=utf-8'); ctx.response.header['content-type'].should.equal('text/html; charset=utf-8');
ctx.body.should.equal(`Redirecting to <a href="${url}">${url}</a>.`); ctx.body.should.equal(`Redirecting to <a href="${url}">${url}</a>.`);
}) });
}) });
describe('when text is accepted', function(){ describe('when text is accepted', function(){
it('should respond with text', function(){ it('should respond with text', function(){
@ -67,8 +67,8 @@ describe('ctx.redirect(url)', function(){
ctx.header.accept = 'text/plain'; ctx.header.accept = 'text/plain';
ctx.redirect(url); ctx.redirect(url);
ctx.body.should.equal(`Redirecting to ${url}.`); ctx.body.should.equal(`Redirecting to ${url}.`);
}) });
}) });
describe('when status is 301', function(){ describe('when status is 301', function(){
it('should not change the status code', function(){ it('should not change the status code', function(){
@ -79,8 +79,8 @@ describe('ctx.redirect(url)', function(){
ctx.redirect('http://google.com'); ctx.redirect('http://google.com');
ctx.status.should.equal(301); ctx.status.should.equal(301);
ctx.body.should.equal(`Redirecting to ${url}.`); ctx.body.should.equal(`Redirecting to ${url}.`);
}) });
}) });
describe('when status is 304', function(){ describe('when status is 304', function(){
it('should change the status code', function(){ it('should change the status code', function(){
@ -91,11 +91,11 @@ describe('ctx.redirect(url)', function(){
ctx.redirect('http://google.com'); ctx.redirect('http://google.com');
ctx.status.should.equal(302); ctx.status.should.equal(302);
ctx.body.should.equal(`Redirecting to ${url}.`); ctx.body.should.equal(`Redirecting to ${url}.`);
}) });
}) });
describe('when content-type was present', function(){ describe('when content-type was present', function(){
it('should overwrite content-type', function() { it('should overwrite content-type', function(){
const ctx = context(); const ctx = context();
ctx.body = {}; ctx.body = {};
const url = 'http://google.com'; const url = 'http://google.com';
@ -104,11 +104,11 @@ describe('ctx.redirect(url)', function(){
ctx.status.should.equal(302); ctx.status.should.equal(302);
ctx.body.should.equal(`Redirecting to ${url}.`); ctx.body.should.equal(`Redirecting to ${url}.`);
ctx.type.should.equal('text/plain'); ctx.type.should.equal('text/plain');
}) });
}) });
}) });
function escape(html) { function escape(html){
return String(html) return String(html)
.replace(/&/g, '&amp;') .replace(/&/g, '&amp;')
.replace(/"/g, '&quot;') .replace(/"/g, '&quot;')

View File

@ -9,5 +9,5 @@ describe('ctx.remove(name)', function(){
ctx.set('x-foo', 'bar'); ctx.set('x-foo', 'bar');
ctx.remove('x-foo'); ctx.remove('x-foo');
ctx.response.header.should.eql({}); ctx.response.header.should.eql({});
}) });
}) });

View File

@ -8,20 +8,20 @@ describe('ctx.set(name, val)', function(){
const ctx = context(); const ctx = context();
ctx.set('x-foo', 'bar'); ctx.set('x-foo', 'bar');
ctx.response.header['x-foo'].should.equal('bar'); ctx.response.header['x-foo'].should.equal('bar');
}) });
it('should coerce to a string', function(){ it('should coerce to a string', function(){
const ctx = context(); const ctx = context();
ctx.set('x-foo', 5); ctx.set('x-foo', 5);
ctx.response.header['x-foo'].should.equal('5'); ctx.response.header['x-foo'].should.equal('5');
}) });
it('should set a field value of array', function(){ it('should set a field value of array', function(){
const ctx = context(); const ctx = context();
ctx.set('x-foo', ['foo', 'bar']); ctx.set('x-foo', ['foo', 'bar']);
ctx.response.header['x-foo'].should.eql([ 'foo', 'bar' ]); ctx.response.header['x-foo'].should.eql([ 'foo', 'bar' ]);
}) });
}) });
describe('ctx.set(object)', function(){ describe('ctx.set(object)', function(){
it('should set multiple fields', function(){ it('should set multiple fields', function(){
@ -34,5 +34,5 @@ describe('ctx.set(object)', function(){
ctx.response.header.foo.should.equal('1'); ctx.response.header.foo.should.equal('1');
ctx.response.header.bar.should.equal('2'); ctx.response.header.bar.should.equal('2');
}) });
}) });

View File

@ -8,5 +8,5 @@ describe('res.socket', function(){
it('should return the request socket object', function(){ it('should return the request socket object', function(){
const res = response(); const res = response();
res.socket.should.be.instanceOf(Stream); res.socket.should.be.instanceOf(Stream);
}) });
}) });

View File

@ -14,51 +14,51 @@ describe('res.status=', function(){
const res = response(); const res = response();
res.status = 403; res.status = 403;
res.status.should.equal(403); res.status.should.equal(403);
}) });
it('should not throw', function(){ it('should not throw', function(){
assert.doesNotThrow(function() { assert.doesNotThrow(function(){
response().status = 403; response().status = 403;
}); });
}) });
}) });
describe('and invalid', function(){ describe('and invalid', function(){
it('should throw', function(){ it('should throw', function(){
assert.throws(function() { assert.throws(function(){
response().status = 999; response().status = 999;
}, 'invalid status code: 999'); }, 'invalid status code: 999');
}) });
}) });
describe('and custom status', function (){ describe('and custom status', function(){
before(function () { before(function(){
statuses['700'] = 'custom status'; statuses['700'] = 'custom status';
}) });
it('should set the status', function (){ it('should set the status', function(){
const res = response(); const res = response();
res.status = 700; res.status = 700;
res.status.should.equal(700); res.status.should.equal(700);
}) });
it('should not throw', function(){ it('should not throw', function(){
assert.doesNotThrow(function() { assert.doesNotThrow(function(){
response().status = 700; response().status = 700;
}); });
}) });
}) });
}) });
describe('when a status string', function(){ describe('when a status string', function(){
it('should throw', function(){ it('should throw', function(){
assert.throws(function() { assert.throws(function(){
response().status = 'forbidden'; response().status = 'forbidden';
}, 'status code must be a number'); }, 'status code must be a number');
}) });
}) });
function strip(status) { function strip(status){
it('should strip content related header fields', function(done){ it('should strip content related header fields', function(done){
const app = new Koa(); const app = new Koa();
@ -76,16 +76,16 @@ describe('res.status=', function(){
request(app.listen()) request(app.listen())
.get('/') .get('/')
.expect(status) .expect(status)
.end(function(err, res) { .end(function(err, res){
res.should.not.have.header('content-type'); res.should.not.have.header('content-type');
res.should.not.have.header('content-length'); res.should.not.have.header('content-length');
res.should.not.have.header('content-encoding'); res.should.not.have.header('content-encoding');
res.text.should.have.length(0); res.text.should.have.length(0);
done(err); done(err);
}); });
}) });
it('should strip content releated header fields after status set', function(done) { it('should strip content releated header fields after status set', function(done){
const app = new Koa(); const app = new Koa();
app.use(function *(){ app.use(function *(){
@ -99,25 +99,25 @@ describe('res.status=', function(){
request(app.listen()) request(app.listen())
.get('/') .get('/')
.expect(status) .expect(status)
.end(function(err, res) { .end(function(err, res){
res.should.not.have.header('content-type'); res.should.not.have.header('content-type');
res.should.not.have.header('content-length'); res.should.not.have.header('content-length');
res.should.not.have.header('content-encoding'); res.should.not.have.header('content-encoding');
res.text.should.have.length(0); res.text.should.have.length(0);
done(err); done(err);
}); });
}) });
} }
describe('when 204', function(){ describe('when 204', function(){
strip(204); strip(204);
}) });
describe('when 205', function(){ describe('when 205', function(){
strip(205); strip(205);
}) });
describe('when 304', function(){ describe('when 304', function(){
strip(304); strip(304);
}) });
}) });

View File

@ -11,8 +11,8 @@ describe('ctx.type=', function(){
ctx.type = 'text/plain'; ctx.type = 'text/plain';
ctx.type.should.equal('text/plain'); ctx.type.should.equal('text/plain');
ctx.response.header['content-type'].should.equal('text/plain; charset=utf-8'); ctx.response.header['content-type'].should.equal('text/plain; charset=utf-8');
}) });
}) });
describe('with an extension', function(){ describe('with an extension', function(){
it('should lookup the mime', function(){ it('should lookup the mime', function(){
@ -20,8 +20,8 @@ describe('ctx.type=', function(){
ctx.type = 'json'; ctx.type = 'json';
ctx.type.should.equal('application/json'); ctx.type.should.equal('application/json');
ctx.response.header['content-type'].should.equal('application/json; charset=utf-8'); ctx.response.header['content-type'].should.equal('application/json; charset=utf-8');
}) });
}) });
describe('without a charset', function(){ describe('without a charset', function(){
it('should default the charset', function(){ it('should default the charset', function(){
@ -29,8 +29,8 @@ describe('ctx.type=', function(){
ctx.type = 'text/html'; ctx.type = 'text/html';
ctx.type.should.equal('text/html'); ctx.type.should.equal('text/html');
ctx.response.header['content-type'].should.equal('text/html; charset=utf-8'); ctx.response.header['content-type'].should.equal('text/html; charset=utf-8');
}) });
}) });
describe('with a charset', function(){ describe('with a charset', function(){
it('should not default the charset', function(){ it('should not default the charset', function(){
@ -38,18 +38,18 @@ describe('ctx.type=', function(){
ctx.type = 'text/html; charset=foo'; ctx.type = 'text/html; charset=foo';
ctx.type.should.equal('text/html'); ctx.type.should.equal('text/html');
ctx.response.header['content-type'].should.equal('text/html; charset=foo'); ctx.response.header['content-type'].should.equal('text/html; charset=foo');
}) });
}) });
describe('with an unknown extension', function(){ describe('with an unknown extension', function(){
it('should default to application/octet-stream',function(){ it('should default to application/octet-stream', function(){
const ctx = context(); const ctx = context();
ctx.type = 'asdf'; ctx.type = 'asdf';
ctx.type.should.equal('application/octet-stream'); ctx.type.should.equal('application/octet-stream');
ctx.response.header['content-type'].should.equal('application/octet-stream'); ctx.response.header['content-type'].should.equal('application/octet-stream');
}) });
}) });
}) });
describe('ctx.type', function(){ describe('ctx.type', function(){
describe('with no Content-Type', function(){ describe('with no Content-Type', function(){
@ -57,14 +57,14 @@ describe('ctx.type', function(){
const ctx = context(); const ctx = context();
// TODO: this is lame // TODO: this is lame
assert('' === ctx.type); assert('' === ctx.type);
}) });
}) });
describe('with a Content-Type', function(){ describe('with a Content-Type', function(){
it('should return the mime', function(){ it('should return the mime', function(){
const ctx = context(); const ctx = context();
ctx.type = 'json'; ctx.type = 'json';
ctx.type.should.equal('application/json'); ctx.type.should.equal('application/json');
}) });
}) });
}) });

View File

@ -9,8 +9,8 @@ describe('ctx.vary(field)', function(){
const ctx = context(); const ctx = context();
ctx.vary('Accept'); ctx.vary('Accept');
ctx.response.header.vary.should.equal('Accept'); ctx.response.header.vary.should.equal('Accept');
}) });
}) });
describe('when Vary is set', function(){ describe('when Vary is set', function(){
it('should append', function(){ it('should append', function(){
@ -18,8 +18,8 @@ describe('ctx.vary(field)', function(){
ctx.vary('Accept'); ctx.vary('Accept');
ctx.vary('Accept-Encoding'); ctx.vary('Accept-Encoding');
ctx.response.header.vary.should.equal('Accept, Accept-Encoding'); ctx.response.header.vary.should.equal('Accept, Accept-Encoding');
}) });
}) });
describe('when Vary already contains the value', function(){ describe('when Vary already contains the value', function(){
it('should not append', function(){ it('should not append', function(){
@ -29,6 +29,6 @@ describe('ctx.vary(field)', function(){
ctx.vary('Accept'); ctx.vary('Accept');
ctx.vary('Accept-Encoding'); ctx.vary('Accept-Encoding');
ctx.response.header.vary.should.equal('Accept, Accept-Encoding'); ctx.response.header.vary.should.equal('Accept, Accept-Encoding');
}) });
}) });
}) });

View File

@ -7,13 +7,13 @@ describe('res.writable', function(){
it('should return the request is writable', function(){ it('should return the request is writable', function(){
const res = response(); const res = response();
res.writable.should.be.ok; res.writable.should.be.ok;
}) });
describe('when res.socket not present', function (){ describe('when res.socket not present', function(){
it('should return the request is not writable', function (){ it('should return the request is not writable', function(){
const res = response(); const res = response();
res.res.socket = null; res.res.socket = null;
res.writable.should.not.be.ok; res.writable.should.not.be.ok;
}) });
}) });
}) });