diff --git a/.eslintrc.yml b/.eslintrc.yml deleted file mode 100644 index 58539da..0000000 --- a/.eslintrc.yml +++ /dev/null @@ -1,14 +0,0 @@ -extends: standard - -rules: - arrow-parens: [2, as-needed] - eqeqeq: 0 - no-return-assign: 0 - no-var: 2 - semi: [2, always] - space-before-function-paren: [2, never] - yoda: 0 - arrow-spacing: 2 - dot-location: [2, property] - prefer-arrow-callback: 2 - prefer-const: 2 diff --git a/.travis.yml b/.travis.yml index a51c800..55c0f5f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,10 +2,8 @@ language: node_js node_js: - 6 - 8 -sudo: false -before_install: "npm install --global npm" -script: "npm run validate" -after_script: "npm install coveralls && cat ./coverage/lcov.info | coveralls" +before_install: npm install --global npm +after_script: coveralls < coverage/lcov.info notifications: webhooks: urls: diff --git a/lib/__tests__/app.js b/lib/__tests__/app.js new file mode 100644 index 0000000..5cebed1 --- /dev/null +++ b/lib/__tests__/app.js @@ -0,0 +1,15 @@ +const koala = require('..'); +const request = require('supertest'); + +describe('app', () => { + test('listen()', () => { + const app = koala(); + app.use(function * () { + this.body = 'Hello World'; + }); + return request(app.listen().close()) + .get('/') + .expect(200) + .expect('Hello World'); + }); +}); diff --git a/lib/__tests__/index.js b/lib/__tests__/index.js new file mode 100644 index 0000000..88d838b --- /dev/null +++ b/lib/__tests__/index.js @@ -0,0 +1,252 @@ +const koala = require('..'); +const request = require('supertest'); + +describe('Body Parsing', () => { + describe('.request.json()', () => { + test('should parse a json body', () => { + const app = koala(); + app.use(function * () { + this.body = yield * this.request.json(); + }); + return request(app.callback()) + .post('/') + .send({ + message: 'lol' + }) + .expect(200) + .expect(/"message"/) + .expect(/"lol"/); + }); + + test('should throw on non-objects in strict mode', () => { + const app = koala(); + app.use(function * () { + this.body = yield * this.request.json(); + }); + return request(app.callback()) + .post('/') + .type('json') + .send('"lol"') + .expect(400); + }); + + test('should not throw on non-objects in non-strict mode', () => { + const app = koala(); + app.jsonStrict = false; + app.use(function * () { + this.body = yield * this.request.json(); + }); + return request(app.callback()) + .post('/') + .type('json') + .send('"lol"') + .expect(200) + .expect('lol'); + }); + }); + + describe('.request.urlencoded()', () => { + test('should parse a urlencoded body', () => { + const app = koala(); + app.use(function * () { + this.body = yield * this.request.urlencoded(); + }); + return request(app.callback()) + .post('/') + .send('message=lol') + .expect(200) + .expect(/"message"/) + .expect(/"lol"/); + }); + + test('should not support nested query strings by default', () => { + const app = koala(); + app.use(function * () { + this.body = yield * this.request.urlencoded(); + }); + return request(app.callback()) + .post('/') + .type('form') + .send({ + something: { + nested: true + } + }) + .expect(200) + .expect(/something\[nested\]/); + }); + + test('should support nested query strings with options.qs=true', () => { + const app = koala({ + qs: true + }); + app.use(function * () { + this.body = yield * this.request.urlencoded(); + }); + return request(app.callback()) + .post('/') + .type('form') + .send({ + something: { + nested: true + } + }) + .expect(200) + .expect({ + something: { + nested: 'true' + } + }); + }); + }); + + describe('.request.text()', () => { + test('should get the raw text body', () => { + const app = koala(); + app.use(function * () { + this.body = yield * this.request.text(); + expect(typeof this.body).toBe('string'); + }); + return request(app.callback()) + .post('/') + .send('message=lol') + .expect(200) + .expect('message=lol'); + }); + + test('should throw if the body is too large', () => { + const app = koala(); + app.use(function * () { + yield * this.request.text('1kb'); + this.body = 204; + }); + return request(app.callback()) + .post('/') + .send(Buffer.alloc(2048)) + .expect(413); + }); + }); + + describe('.request.buffer()', () => { + test('should get the raw buffer body', () => { + const app = koala(); + app.use(function * () { + this.body = yield * this.request.buffer(); + expect(Buffer.isBuffer(this.body)).toBeTruthy(); + }); + return request(app.callback()) + .post('/') + .send('message=lol') + .expect(200) + .expect('message=lol'); + }); + + test('should throw if the body is too large', () => { + const app = koala(); + app.use(function * () { + yield * this.request.buffer('1kb'); + this.body = 204; + }); + return request(app.callback()) + .post('/') + .send(Buffer.alloc(2048)) + .expect(413); + }); + }); + + describe('Expect: 100-continue', () => { + test('should send 100-continue', () => { + const app = koala(); + app.use(function * () { + this.body = yield * this.request.json(); + }); + return request(app.callback()) + .get('/') + .set('expect', '100-continue') + .set('content-type', 'application/json') + .send('message=lol'); + }); + }); + + describe('Nested Query Strings', () => { + describe('when options.qs = false', () => { + test('should not support nested query strings', () => { + const app = koala(); + app.use(function * (next) { + this.response.body = this.request.query; + }); + + return request(app.callback()) + .get('/') + .query({ + something: { + nested: true + } + }) + .expect(200) + .expect({ + 'something[nested]': 'true' + }); + }); + }); + + describe('when options.qs = true', () => { + test('should support nested query strings', () => { + const app = koala({ + qs: true + }); + app.use(function * (next) { + this.response.body = this.request.query; + }); + + return request(app.callback()) + .get('/') + .query({ + something: { + nested: true + } + }) + .expect(200) + .expect({ + something: { + nested: 'true' + } + }); + }); + }); + }); + + describe('jsonp', () => { + test('should return jsonp response', () => { + const app = koala({ + jsonp: { + callback: '_callback' + } + }); + app.use(function * (next) { + this.jsonp = {foo: 'bar'}; + }); + + return request(app.callback()) + .get('/user.json?_callback=fn') + .expect(200) + .expect('/**/ typeof fn === \'function\' && fn({"foo":"bar"});'); + }); + + test('should return json response', () => { + const app = koala({ + jsonp: { + callback: '_callback' + } + }); + app.use(function * (next) { + this.jsonp = {foo: 'bar'}; + }); + + return request(app.callback()) + .get('/user.json') + .expect(200) + .expect({'foo': 'bar'}); + }); + }); +}); diff --git a/test/basic-auth.test.js b/lib/__tests__/request.js similarity index 73% rename from test/basic-auth.test.js rename to lib/__tests__/request.js index 854353c..9ef542f 100644 --- a/test/basic-auth.test.js +++ b/lib/__tests__/request.js @@ -1,20 +1,20 @@ -const koala = require('../lib'); +const koala = require('..'); const request = require('supertest'); describe('Basic Auth', () => { - test('should return the value', done => { + test('should return the value', () => { const app = koala(); app.use(function * (next) { this.body = this.request.basicAuth; }); - request(app.listen()) + return request(app.callback()) .get('/') .auth('username', 'password') .expect(200) .expect({ name: 'username', pass: 'password' - }, done); + }); }); }); diff --git a/test/cache-control.test.js b/lib/__tests__/response.js similarity index 62% rename from test/cache-control.test.js rename to lib/__tests__/response.js index 4a892fe..babdf7c 100644 --- a/test/cache-control.test.js +++ b/lib/__tests__/response.js @@ -1,126 +1,119 @@ -const koala = require('../lib'); +const koala = require('..'); const request = require('supertest'); describe('Cache-Control', () => { describe('should be available as', () => { - test('this.cc()', done => { + test('this.cc()', () => { const app = koala(); app.use(function * (next) { this.cc(1000); this.status = 204; }); - request(app.listen()) + return request(app.callback()) .get('/') .expect(204) - .expect('Cache-Control', 'public, max-age=1') - .end(done); + .expect('Cache-Control', 'public, max-age=1'); }); - test('this.cacheControl()', done => { + test('this.cacheControl()', () => { const app = koala(); app.use(function * (next) { this.cacheControl(1000); this.status = 204; }); - request(app.listen()) + return request(app.callback()) .get('/') .expect(204) - .expect('Cache-Control', 'public, max-age=1') - .end(done); + .expect('Cache-Control', 'public, max-age=1'); }); - test('this.response.cc()', done => { + test('this.response.cc()', () => { const app = koala(); app.use(function * (next) { this.response.cc(1000); this.status = 204; }); - request(app.listen()) + return request(app.callback()) .get('/') .expect(204) - .expect('Cache-Control', 'public, max-age=1') - .end(done); + .expect('Cache-Control', 'public, max-age=1'); }); }); describe('when the value is a number', () => { - test('should set "public, max-age="', done => { + test('should set "public, max-age="', () => { const app = koala(); app.use(function * (next) { this.response.cacheControl(1000000); this.status = 204; }); - request(app.listen()) + return request(app.callback()) .get('/') .expect(204) - .expect('Cache-Control', 'public, max-age=1000') - .end(done); + .expect('Cache-Control', 'public, max-age=1000'); }); }); describe('when the value is a time string', () => { - test('should set "public, max-age="', done => { + test('should set "public, max-age="', () => { const app = koala(); app.use(function * (next) { this.response.cacheControl('1 hour'); this.status = 204; }); - request(app.listen()) + return request(app.callback()) .get('/') .expect(204) - .expect('Cache-Control', 'public, max-age=3600') - .end(done); + .expect('Cache-Control', 'public, max-age=3600'); }); }); describe('when the value is "false"', () => { - test('should set "private, no-cache"', done => { + test('should set "private, no-cache"', () => { const app = koala(); app.use(function * (next) { this.response.cacheControl(false); this.status = 204; }); - request(app.listen()) + return request(app.callback()) .get('/') .expect(204) - .expect('Cache-Control', 'private, no-cache') - .end(done); + .expect('Cache-Control', 'private, no-cache'); }); }); describe('when the value is a string', () => { - test('should just set it', done => { + test('should just set it', () => { const app = koala(); app.use(function * (next) { this.response.cacheControl('foo'); this.status = 204; }); - request(app.listen()) + return request(app.callback()) .get('/') .expect(204) - .expect('Cache-Control', 'foo') - .end(done); + .expect('Cache-Control', 'foo'); }); }); describe('when the value is anything else', () => { - test('should throw', done => { + test('should throw', () => { const app = koala(); app.use(function * (next) { this.response.cacheControl(true); this.status = 204; }); - request(app.listen()) + return request(app.callback()) .get('/') - .expect(500, done); + .expect(500); }); }); }); diff --git a/test/conditional-get.test.js b/lib/middleware/__tests__/conditional-get.js similarity index 53% rename from test/conditional-get.test.js rename to lib/middleware/__tests__/conditional-get.js index deae8a1..c5652fe 100644 --- a/test/conditional-get.test.js +++ b/lib/middleware/__tests__/conditional-get.js @@ -1,4 +1,4 @@ -const koala = require('../lib'); +const koala = require('../..'); const request = require('supertest'); describe('Conditional-Get', () => { @@ -10,24 +10,22 @@ describe('Conditional-Get', () => { this.body = 'hello'; }); - const server = app.listen(); + const server = app.listen().close(); - test('should set an etag', done => { - request(server) + test('should set an etag', () => { + return request(server) .get('/') - .expect(200, (err, res) => { - if (err) return done(err); - + .expect(200) + .expect(res => { etag = res.headers.etag.slice(1, -1); - done(); }); }); - test('should response 304 w/ if-none-match header', done => { - request(server) + test('should response 304 w/ if-none-match header', () => { + return request(server) .get('/') .set('If-None-Match', '"' + etag + '"') - .expect(304, done); + .expect(304); }); }); }); diff --git a/test/headers.test.js b/lib/middleware/__tests__/headers.js similarity index 50% rename from test/headers.test.js rename to lib/middleware/__tests__/headers.js index a725769..87ab515 100644 --- a/test/headers.test.js +++ b/lib/middleware/__tests__/headers.js @@ -1,61 +1,31 @@ -const koala = require('../lib'); +const koala = require('../..'); const request = require('supertest'); describe('Set headers', () => { - describe('X-Response-Time', () => { - test('should get X-Response-Time correctly by default', done => { - const app = koala(); - - request(app.listen()) - .get('/') - .expect(404) - .expect('X-Response-Time', /s$/) - .end(done); - }); - test( - 'should not get X-Response-Time by options.responseTime = false', - done => { - const app = koala({ - responseTime: false - }); - - request(app.listen()) - .get('/') - .expect(404) - .end((err, res) => { - expect(res.headers['X-Response-Time']).toBe(undefined); - done(err); - }); - } - ); - }); - describe('Strict-Transport-Security', () => { - test('should not set Strict-Transport-Security by default', done => { + test('should not set Strict-Transport-Security by default', () => { const app = koala(); - request(app.listen()) + return request(app.callback()) .get('/') .expect(404) - .end((err, res) => { + .expect(res => { expect(res.headers['Strict-Transport-Security']).toBe(undefined); - done(err); }); }); - test('should set Strict-Transport-Security if `hsts` is a number', done => { + test('should set Strict-Transport-Security if `hsts` is a number', () => { const app = koala({ security: { hsts: 1000 } }); - request(app.listen()) + return request(app.callback()) .get('/') .expect(404) - .expect('Strict-Transport-Security', 'max-age=1') - .end(done); + .expect('Strict-Transport-Security', 'max-age=1'); }); - test('should set Strict-Transport-Security if `hsts.maxAge` is present', done => { + test('should set Strict-Transport-Security if `hsts.maxAge` is present', () => { const app = koala({ security: { hsts: { @@ -64,13 +34,12 @@ describe('Set headers', () => { } }); - request(app.listen()) + return request(app.callback()) .get('/') .expect(404) - .expect('Strict-Transport-Security', 'max-age=1') - .end(done); + .expect('Strict-Transport-Security', 'max-age=1'); }); - test('should set Strict-Transport-Security with `includeSubDomains`', done => { + test('should set Strict-Transport-Security with `includeSubDomains`', () => { const app = koala({ security: { hsts: 1000, @@ -78,64 +47,59 @@ describe('Set headers', () => { } }); - request(app.listen()) + return request(app.callback()) .get('/') .expect(404) - .expect('Strict-Transport-Security', 'max-age=1; includeSubDomains') - .end(done); + .expect('Strict-Transport-Security', 'max-age=1; includeSubDomains'); }); }); describe('X-Frame-Options', () => { - test('should get X-Frame-Options DENY by default', done => { + test('should get X-Frame-Options DENY by default', () => { const app = koala(); - request(app.listen()) + return request(app.callback()) .get('/') .expect(404) - .expect('X-Frame-Options', 'DENY') - .end(done); + .expect('X-Frame-Options', 'DENY'); }); - test('should not get X-Frame-Options by xframe = false', done => { + test('should not get X-Frame-Options by xframe = false', () => { const app = koala({ security: { xframe: false } }); - request(app.listen()) + return request(app.callback()) .get('/') .expect(404) - .end((err, res) => { + .expect(res => { expect(res.headers['X-Frame-Options']).toBe(undefined); - done(err); }); }); - test('should get X-Frame-Options DENY by xframe = true', done => { + test('should get X-Frame-Options DENY by xframe = true', () => { const app = koala({ security: { xframe: true } }); - request(app.listen()) + return request(app.callback()) .get('/') .expect(404) - .expect('X-Frame-Options', 'DENY') - .end(done); + .expect('X-Frame-Options', 'DENY'); }); - test('should get X-Frame-Options SAMEORIGIN by xframe = same', done => { + test('should get X-Frame-Options SAMEORIGIN by xframe = same', () => { const app = koala({ security: { xframe: 'same' } }); - request(app.listen()) + return request(app.callback()) .get('/') .expect(404) - .expect('X-Frame-Options', 'SAMEORIGIN') - .end(done); + .expect('X-Frame-Options', 'SAMEORIGIN'); }); }); }); diff --git a/lib/middleware/__tests__/index.js b/lib/middleware/__tests__/index.js new file mode 100644 index 0000000..272a50b --- /dev/null +++ b/lib/middleware/__tests__/index.js @@ -0,0 +1,88 @@ +const koala = require('../..'); +const request = require('supertest'); +const PassThrough = require('stream').PassThrough; + +describe('Middlewares', () => { + describe('Response Time', () => { + test('should get X-Response-Time correctly by default', () => { + const app = koala(); + + return request(app.callback()) + .get('/') + .expect(404) + .expect('X-Response-Time', /s$/); + }); + test( + 'should not get X-Response-Time by options.responseTime = false', + () => { + const app = koala({ + responseTime: false + }); + + return request(app.callback()) + .get('/') + .expect(404) + .expect(res => { + expect(res.headers['X-Response-Time']).toBe(undefined); + }); + } + ); + }); + + describe('Object Streams', () => { + test('should be supported', () => { + const app = koala(); + app.use(function * (next) { + const body = this.body = new PassThrough({ + objectMode: true + }); + + body.write({ + message: 'a' + }); + + body.write({ + message: 'b' + }); + + body.end(); + }); + + return request(app.callback()) + .get('/') + .expect(200) + .expect([{ + message: 'a' + }, { + message: 'b' + }]); + }); + }); + + describe('Session', () => { + test('should has this.session by default', () => { + const app = koala(); + + app.use(function * () { + this.body = this.session; + }); + + return request(app.callback()) + .get('/') + .expect('{}'); + }); + test('should has no this.session by options.session = false', () => { + const app = koala({ + session: false + }); + + app.use(function * () { + this.body = this.session === undefined; + }); + + return request(app.callback()) + .get('/') + .expect('true'); + }); + }); +}); diff --git a/package-lock.json b/package-lock.json index d5e923b..86f8f9a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1044,6 +1044,112 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "coveralls": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/coveralls/-/coveralls-2.13.1.tgz", + "integrity": "sha1-1wu5rMGDXsTwY/+drFQjwXsR8Xg=", + "dev": true, + "requires": { + "js-yaml": "3.6.1", + "lcov-parse": "0.0.10", + "log-driver": "1.2.5", + "minimist": "1.2.0", + "request": "2.79.0" + }, + "dependencies": { + "caseless": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", + "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=", + "dev": true + }, + "esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", + "dev": true + }, + "form-data": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", + "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", + "dev": true, + "requires": { + "asynckit": "0.4.0", + "combined-stream": "1.0.5", + "mime-types": "2.1.17" + } + }, + "har-validator": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", + "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "commander": "2.11.0", + "is-my-json-valid": "2.16.1", + "pinkie-promise": "2.0.1" + } + }, + "js-yaml": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.6.1.tgz", + "integrity": "sha1-bl/mfYsgXOTSL60Ft3geja3MSzA=", + "dev": true, + "requires": { + "argparse": "1.0.9", + "esprima": "2.7.3" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "qs": { + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.3.2.tgz", + "integrity": "sha1-51vV9uJoEioqDgvaYwslUMFmUCw=", + "dev": true + }, + "request": { + "version": "2.79.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.79.0.tgz", + "integrity": "sha1-Tf5b9r6LjNw3/Pk+BLZVd3InEN4=", + "dev": true, + "requires": { + "aws-sign2": "0.6.0", + "aws4": "1.6.0", + "caseless": "0.11.0", + "combined-stream": "1.0.5", + "extend": "3.0.0", + "forever-agent": "0.6.1", + "form-data": "2.1.4", + "har-validator": "2.0.6", + "hawk": "3.1.3", + "http-signature": "1.1.1", + "is-typedarray": "1.0.0", + "isstream": "0.1.2", + "json-stringify-safe": "5.0.1", + "mime-types": "2.1.17", + "oauth-sign": "0.8.2", + "qs": "6.3.2", + "stringstream": "0.0.5", + "tough-cookie": "2.3.2", + "tunnel-agent": "0.4.3", + "uuid": "3.1.0" + } + }, + "tunnel-agent": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", + "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", + "dev": true + } + } + }, "crc": { "version": "3.4.4", "resolved": "https://registry.npmjs.org/crc/-/crc-3.4.4.tgz", @@ -3192,6 +3298,21 @@ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", "dev": true }, + "generate-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", + "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", + "dev": true + }, + "generate-object-property": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", + "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", + "dev": true, + "requires": { + "is-property": "1.0.2" + } + }, "get-caller-file": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.2.tgz", @@ -3708,6 +3829,18 @@ "is-extglob": "1.0.0" } }, + "is-my-json-valid": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz", + "integrity": "sha512-ochPsqWS1WXj8ZnMIV0vnNXooaMhp7cyL4FMSIPKTtnV0Ha/T19G2b9kkhcNsabV9bxYkze7/aLZJb/bYuFduQ==", + "dev": true, + "requires": { + "generate-function": "2.0.0", + "generate-object-property": "1.2.0", + "jsonpointer": "4.0.1", + "xtend": "4.0.1" + } + }, "is-npm": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", @@ -3780,6 +3913,12 @@ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", "dev": true }, + "is-property": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", + "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", + "dev": true + }, "is-redirect": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", @@ -4792,6 +4931,12 @@ "resolved": "https://registry.npmjs.org/jsonp-body/-/jsonp-body-1.0.0.tgz", "integrity": "sha1-5hD7b86nnPDMnye6p7Vjd9SwuzY=" }, + "jsonpointer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", + "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", + "dev": true + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -5065,6 +5210,12 @@ "invert-kv": "1.0.0" } }, + "lcov-parse": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-0.0.10.tgz", + "integrity": "sha1-GwuP+ayceIklBYK3C3ExXZ2m2aM=", + "dev": true + }, "leven": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-2.1.0.tgz", @@ -5153,6 +5304,12 @@ "integrity": "sha1-FQzwoWeR9ZA7iJHqsVRgknS96lU=", "dev": true }, + "log-driver": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/log-driver/-/log-driver-1.2.5.tgz", + "integrity": "sha1-euTsJXMC/XkNVXyxDJcQDYV7AFY=", + "dev": true + }, "longest": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", diff --git a/package.json b/package.json index 23563b9..bf538f9 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "qs": ">= 2" }, "devDependencies": { + "coveralls": "*", "eslint": "^4.6.1", "eslint-config-standard": "^10.2.1", "eslint-plugin-import": "^2.7.0", @@ -56,9 +57,8 @@ "template" ], "scripts": { - "test": "jest --forceExit", - "coverage": "npm run test -- --coverage", - "validate": "npm-run-all lint coverage", + "tests": "jest", + "test": "npm-run-all lint tests", "snyk-protect": "snyk protect", "prepare": "npm run snyk-protect", "lint": "eslint . bin/koala" @@ -75,5 +75,51 @@ "promises", "push" ], + "eslintConfig": { + "extends": [ + "standard", + "plugin:jest/recommended" + ], + "plugins": [ + "jest" + ], + "rules": { + "arrow-parens": [ + 2, + "as-needed" + ], + "eqeqeq": 0, + "no-return-assign": 0, + "no-var": 2, + "semi": [ + 2, + "always" + ], + "space-before-function-paren": [ + 2, + "never" + ], + "yoda": 0, + "arrow-spacing": 2, + "dot-location": [ + 2, + "property" + ], + "prefer-arrow-callback": 2, + "prefer-const": 2 + }, + "overrides": { + "files": "**/__tests__/*", + "env": { + "jest/globals": true + } + } + }, + "eslintIgnore": [ + "coverage" + ], + "jest": { + "collectCoverage": true + }, "snyk": true } diff --git a/test/.eslintrc.yml b/test/.eslintrc.yml deleted file mode 100644 index 57cff84..0000000 --- a/test/.eslintrc.yml +++ /dev/null @@ -1,6 +0,0 @@ -extends: [ plugin:jest/recommended ] - -env: - jest/globals: true - -plugins: [ jest ] diff --git a/test/body-parsing.test.js b/test/body-parsing.test.js deleted file mode 100644 index 5dbf909..0000000 --- a/test/body-parsing.test.js +++ /dev/null @@ -1,185 +0,0 @@ -const koala = require('../lib'); -const request = require('supertest'); -const http = require('http'); - -describe('Body Parsing', () => { - describe('.request.json()', () => { - test('should parse a json body', done => { - const app = koala(); - app.use(function * () { - this.body = yield * this.request.json(); - }); - request(app.listen()) - .post('/') - .send({ - message: 'lol' - }) - .expect(200) - .expect(/"message"/) - .expect(/"lol"/, done); - }); - - test('should throw on non-objects in strict mode', done => { - const app = koala(); - app.use(function * () { - this.body = yield * this.request.json(); - }); - request(app.listen()) - .post('/') - .type('json') - .send('"lol"') - .expect(400, done); - }); - - test('should not throw on non-objects in non-strict mode', done => { - const app = koala(); - app.jsonStrict = false; - app.use(function * () { - this.body = yield * this.request.json(); - }); - request(app.listen()) - .post('/') - .type('json') - .send('"lol"') - .expect(200) - .expect('lol', done); - }); - }); - - describe('.request.urlencoded()', () => { - test('should parse a urlencoded body', done => { - const app = koala(); - app.use(function * () { - this.body = yield * this.request.urlencoded(); - }); - request(app.listen()) - .post('/') - .send('message=lol') - .expect(200) - .expect(/"message"/) - .expect(/"lol"/, done); - }); - - test('should not support nested query strings by default', done => { - const app = koala(); - app.use(function * () { - this.body = yield * this.request.urlencoded(); - }); - request(app.listen()) - .post('/') - .type('form') - .send({ - something: { - nested: true - } - }) - .expect(200) - .expect(/something\[nested\]/, done); - }); - - test('should support nested query strings with options.qs=true', done => { - const app = koala({ - qs: true - }); - app.use(function * () { - this.body = yield * this.request.urlencoded(); - }); - request(app.listen()) - .post('/') - .type('form') - .send({ - something: { - nested: true - } - }) - .expect(200) - .expect({ - something: { - nested: 'true' - } - }, done); - }); - }); - - describe('.request.text()', () => { - test('should get the raw text body', done => { - const app = koala(); - app.use(function * () { - this.body = yield * this.request.text(); - expect(typeof this.body).toBe('string'); - }); - request(app.listen()) - .post('/') - .send('message=lol') - .expect(200) - .expect('message=lol', done); - }); - - test('should throw if the body is too large', done => { - const app = koala(); - app.use(function * () { - yield * this.request.text('1kb'); - this.body = 204; - }); - request(app.listen()) - .post('/') - .send(Buffer.alloc(2048)) - .expect(413, done); - }); - }); - - describe('.request.buffer()', () => { - test('should get the raw buffer body', done => { - const app = koala(); - app.use(function * () { - this.body = yield * this.request.buffer(); - expect(Buffer.isBuffer(this.body)).toBeTruthy(); - }); - request(app.listen()) - .post('/') - .send('message=lol') - .expect(200) - .expect('message=lol', done); - }); - - test('should throw if the body is too large', done => { - const app = koala(); - app.use(function * () { - yield * this.request.buffer('1kb'); - this.body = 204; - }); - request(app.listen()) - .post('/') - .send(Buffer.alloc(2048)) - .expect(413, done); - }); - }); - - describe('Expect: 100-continue', () => { - test('should send 100-continue', done => { - const app = koala(); - app.use(function * () { - this.body = yield * this.request.json(); - }); - app.listen(function() { - http.request({ - port: this.address().port, - path: '/', - headers: { - 'expect': '100-continue', - 'content-type': 'application/json' - } - }) - .once('continue', function() { - this.end(JSON.stringify({ - message: 'lol' - })); - }) - .once('response', res => { - done(); - }) - .once('error', done); - }); - }); - }); -}); diff --git a/test/jsonp.test.js b/test/jsonp.test.js deleted file mode 100644 index 34f420e..0000000 --- a/test/jsonp.test.js +++ /dev/null @@ -1,36 +0,0 @@ -const koala = require('../lib'); -const request = require('supertest'); - -describe('jsonp', () => { - test('should return jsonp response', done => { - const app = koala({ - jsonp: { - callback: '_callback' - } - }); - app.use(function * (next) { - this.jsonp = {foo: 'bar'}; - }); - - request(app.listen()) - .get('/user.json?_callback=fn') - .expect(200) - .expect('/**/ typeof fn === \'function\' && fn({"foo":"bar"});', done); - }); - - test('should return json response', done => { - const app = koala({ - jsonp: { - callback: '_callback' - } - }); - app.use(function * (next) { - this.jsonp = {foo: 'bar'}; - }); - - request(app.listen()) - .get('/user.json') - .expect(200) - .expect({'foo': 'bar'}, done); - }); -}); diff --git a/test/middleware.test.js b/test/middleware.test.js deleted file mode 100644 index 1a0b600..0000000 --- a/test/middleware.test.js +++ /dev/null @@ -1,33 +0,0 @@ -const koala = require('../lib'); -const request = require('supertest'); - -describe('Middlewares', () => { - describe('Session', () => { - test('should has this.session by default', done => { - const app = koala(); - - app.use(function * () { - this.body = this.session; - }); - - request(app.listen()) - .get('/') - .expect('{}') - .end(done); - }); - test('should has no this.session by options.session = false', done => { - const app = koala({ - session: false - }); - - app.use(function * () { - this.body = this.session === undefined; - }); - - request(app.listen()) - .get('/') - .expect('true') - .end(done); - }); - }); -}); diff --git a/test/object-streams.test.js b/test/object-streams.test.js deleted file mode 100644 index ab55202..0000000 --- a/test/object-streams.test.js +++ /dev/null @@ -1,33 +0,0 @@ -const koala = require('../lib'); -const request = require('supertest'); -const PassThrough = require('stream').PassThrough; - -describe('Object Streams', () => { - test('should be supported', done => { - const app = koala(); - app.use(function * (next) { - const body = this.body = new PassThrough({ - objectMode: true - }); - - body.write({ - message: 'a' - }); - - body.write({ - message: 'b' - }); - - body.end(); - }); - - request(app.listen()) - .get('/') - .expect(200) - .expect([{ - message: 'a' - }, { - message: 'b' - }], done); - }); -}); diff --git a/test/query-string.test.js b/test/query-string.test.js deleted file mode 100644 index f38c5fc..0000000 --- a/test/query-string.test.js +++ /dev/null @@ -1,50 +0,0 @@ -const koala = require('../lib'); -const request = require('supertest'); - -describe('Nested Query Strings', () => { - describe('when options.qs = false', () => { - test('should not support nested query strings', done => { - const app = koala(); - app.use(function * (next) { - this.response.body = this.request.query; - }); - - request(app.listen()) - .get('/') - .query({ - something: { - nested: true - } - }) - .expect(200) - .expect({ - 'something[nested]': 'true' - }, done); - }); - }); - - describe('when options.qs = true', () => { - test('should support nested query strings', done => { - const app = koala({ - qs: true - }); - app.use(function * (next) { - this.response.body = this.request.query; - }); - - request(app.listen()) - .get('/') - .query({ - something: { - nested: true - } - }) - .expect(200) - .expect({ - something: { - nested: 'true' - } - }, done); - }); - }); -});