diff --git a/.autod.conf.js b/.autod.conf.js new file mode 100644 index 0000000..48564fc --- /dev/null +++ b/.autod.conf.js @@ -0,0 +1,18 @@ +'use strict'; + +module.exports = { + write: true, + prefix: '^', + devprefix: '^', + exclude: [], + devdep: [ + 'autod', + 'eslint', + 'eslint-config-egg', + 'egg-bin', + ], + dep: [], + semver: [ + 'koa@1', + ], +}; diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..dc6da34 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,7 @@ +{ + "extends": "eslint-config-egg", + "rules": { + "no-bitwise": "off", + "no-control-regex": "off" + } +} diff --git a/.travis.yml b/.travis.yml index 6065018..c523a43 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,7 @@ sudo: false language: node_js node_js: - - "0.12" - - "4" - - "5" - - "6" -script: "npm run test-cov" + - "8" + - "10" +script: "npm run cov" after_script: "npm install coveralls@2 && cat ./coverage/lcov.info | coveralls" diff --git a/Makefile b/Makefile deleted file mode 100644 index 869b509..0000000 --- a/Makefile +++ /dev/null @@ -1,29 +0,0 @@ - -test: - @NODE_ENV=test ./node_modules/.bin/mocha \ - --harmony \ - --require should \ - --reporter spec \ - --bail - -test-cov: - @NODE_ENV=test node --harmony \ - ./node_modules/.bin/istanbul cover \ - ./node_modules/.bin/_mocha \ - -- -u exports \ - --require should \ - --reporter spec \ - --bail - -test-travis: - @NODE_ENV=test node --harmony \ - ./node_modules/.bin/istanbul cover \ - --harmony \ - ./node_modules/.bin/_mocha \ - --report lcovonly \ - -- -u exports \ - --require should \ - --reporter spec \ - --bail - -.PHONY: test diff --git a/Readme.md b/Readme.md index cfeee2a..66731ce 100644 --- a/Readme.md +++ b/Readme.md @@ -1,4 +1,3 @@ - # co-body [![NPM version][npm-image]][npm-url] @@ -42,22 +41,22 @@ more options available via [raw-body](https://github.com/stream-utils/raw-body#g ```js // application/json -var body = yield parse.json(req); +var body = await parse.json(req); // explicit limit -var body = yield parse.json(req, { limit: '10kb' }); +var body = await parse.json(req, { limit: '10kb' }); // application/x-www-form-urlencoded -var body = yield parse.form(req); +var body = await parse.form(req); // text/plain -var body = yield parse.text(req); +var body = await parse.text(req); // either -var body = yield parse(req); +var body = await parse(req); // custom type -var body = yield parse(req, { textTypes: ['text', 'html'] }); +var body = await parse(req, { textTypes: ['text', 'html'] }); ``` ## Koa @@ -67,16 +66,16 @@ var body = yield parse(req, { textTypes: ['text', 'html'] }); ```js // application/json -var body = yield parse.json(this); +var body = await parse.json(this); // application/x-www-form-urlencoded -var body = yield parse.form(this); +var body = await parse.form(this); // text/plain -var body = yield parse.text(this); +var body = await parse.text(this); // either -var body = yield parse(this); +var body = await parse(this); ``` # License diff --git a/index.js b/index.js index 1fa0d5d..fa9f07e 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,6 @@ +'use strict'; exports = module.exports = require('./lib/any'); exports.json = require('./lib/json'); exports.form = require('./lib/form'); -exports.text = require('./lib/text'); \ No newline at end of file +exports.text = require('./lib/text'); diff --git a/lib/any.js b/lib/any.js index 4798897..32f595c 100644 --- a/lib/any.js +++ b/lib/any.js @@ -1,16 +1,17 @@ +'use strict'; /** * Module dependencies. */ -var typeis = require('type-is'); -var json = require('./json'); -var form = require('./form'); -var text = require('./text'); +const typeis = require('type-is'); +const json = require('./json'); +const form = require('./form'); +const text = require('./text'); -var jsonTypes = ['json', 'application/*+json', 'application/csp-report']; -var formTypes = ['urlencoded']; -var textTypes = ['text']; +const jsonTypes = [ 'json', 'application/*+json', 'application/csp-report' ]; +const formTypes = [ 'urlencoded' ]; +const textTypes = [ 'text' ]; /** * Return a Promise which parses form and json requests @@ -25,26 +26,26 @@ var textTypes = ['text']; * @api public */ -module.exports = function(req, opts){ +module.exports = async function(req, opts) { req = req.req || req; opts = opts || {}; // json - var jsonType = opts.jsonTypes || jsonTypes; + const jsonType = opts.jsonTypes || jsonTypes; if (typeis(req, jsonType)) return json(req, opts); // form - var formType = opts.formTypes || formTypes; + const formType = opts.formTypes || formTypes; if (typeis(req, formType)) return form(req, opts); // text - var textType = opts.textTypes || textTypes; + const textType = opts.textTypes || textTypes; if (typeis(req, textType)) return text(req, opts); // invalid - var type = req.headers['content-type'] || ''; - var message = type ? 'Unsupported content-type: ' + type : 'Missing content-type'; - var err = new Error(message); + const type = req.headers['content-type'] || ''; + const message = type ? 'Unsupported content-type: ' + type : 'Missing content-type'; + const err = new Error(message); err.status = 415; - return Promise.reject(err); + throw err; }; diff --git a/lib/form.js b/lib/form.js index 1d85c4e..1b135bc 100644 --- a/lib/form.js +++ b/lib/form.js @@ -1,12 +1,13 @@ +'use strict'; /** * Module dependencies. */ -var raw = require('raw-body'); -var inflate = require('inflation'); -var qs = require('qs'); -var utils = require('./utils'); +const raw = require('raw-body'); +const inflate = require('inflation'); +const qs = require('qs'); +const utils = require('./utils'); /** * Return a Promise which parses x-www-form-urlencoded requests. @@ -20,35 +21,29 @@ var utils = require('./utils'); * @api public */ -module.exports = function(req, opts){ +module.exports = async function(req, opts) { req = req.req || req; opts = utils.clone(opts); - var queryString = opts.queryString || {}; + const queryString = opts.queryString || {}; // keep compatibility with qs@4 if (queryString.allowDots === undefined) queryString.allowDots = true; // defaults - var len = req.headers['content-length']; - var encoding = req.headers['content-encoding'] || 'identity'; + const len = req.headers['content-length']; + const encoding = req.headers['content-encoding'] || 'identity'; if (len && encoding === 'identity') opts.length = ~~len; opts.encoding = opts.encoding || 'utf8'; opts.limit = opts.limit || '56kb'; opts.qs = opts.qs || qs; - // raw-body returns a Promise when no callback is specified - return Promise.resolve() - .then(function() { - return raw(inflate(req), opts); - }) - .then(function(str){ - try { - var parsed = opts.qs.parse(str, queryString); - return opts.returnRawBody ? { parsed: parsed, raw: str } : parsed; - } catch (err) { - err.status = 400; - err.body = str; - throw err; - } - }); + const str = await raw(inflate(req), opts); + try { + const parsed = opts.qs.parse(str, queryString); + return opts.returnRawBody ? { parsed, raw: str } : parsed; + } catch (err) { + err.status = 400; + err.body = str; + throw err; + } }; diff --git a/lib/json.js b/lib/json.js index 7bc8f12..18e2ac0 100644 --- a/lib/json.js +++ b/lib/json.js @@ -1,15 +1,16 @@ +'use strict'; /** * Module dependencies. */ -var raw = require('raw-body'); -var inflate = require('inflation'); -var utils = require('./utils'); +const raw = require('raw-body'); +const inflate = require('inflation'); +const utils = require('./utils'); // Allowed whitespace is defined in RFC 7159 // http://www.rfc-editor.org/rfc/rfc7159.txt -var strictJSONReg = /^[\x20\x09\x0a\x0d]*(\[|\{)/; +const strictJSONReg = /^[\x20\x09\x0a\x0d]*(\[|\{)/; /** * Return a Promise which parses json requests. @@ -23,35 +24,29 @@ var strictJSONReg = /^[\x20\x09\x0a\x0d]*(\[|\{)/; * @api public */ -module.exports = function(req, opts){ +module.exports = async function(req, opts) { req = req.req || req; opts = utils.clone(opts); // defaults - var len = req.headers['content-length']; - var encoding = req.headers['content-encoding'] || 'identity'; + let len = req.headers['content-length']; + const encoding = req.headers['content-encoding'] || 'identity'; if (len && encoding === 'identity') opts.length = len = ~~len; opts.encoding = opts.encoding || 'utf8'; opts.limit = opts.limit || '1mb'; - var strict = opts.strict !== false; - - // raw-body returns a promise when no callback is specified - return Promise.resolve() - .then(function() { - return raw(inflate(req), opts); - }) - .then(function(str) { - try { - var parsed = parse(str); - return opts.returnRawBody ? { parsed: parsed, raw: str } : parsed; - } catch (err) { - err.status = 400; - err.body = str; - throw err; - } - }); + const strict = opts.strict !== false; + + const str = await raw(inflate(req), opts); + try { + const parsed = parse(str); + return opts.returnRawBody ? { parsed, raw: str } : parsed; + } catch (err) { + err.status = 400; + err.body = str; + throw err; + } - function parse(str){ + function parse(str) { if (!strict) return str ? JSON.parse(str) : str; // strict mode always return object if (!str) return {}; diff --git a/lib/text.js b/lib/text.js index 1e46cd6..6860ff0 100644 --- a/lib/text.js +++ b/lib/text.js @@ -1,10 +1,12 @@ +'use strict'; + /** * Module dependencies. */ -var raw = require('raw-body'); -var inflate = require('inflation'); -var utils = require('./utils'); +const raw = require('raw-body'); +const inflate = require('inflation'); +const utils = require('./utils'); /** * Return a Promise which parses text/plain requests. @@ -18,24 +20,18 @@ var utils = require('./utils'); * @api public */ -module.exports = function(req, opts){ +module.exports = async function(req, opts) { req = req.req || req; opts = utils.clone(opts); // defaults - var len = req.headers['content-length']; - var encoding = req.headers['content-encoding'] || 'identity'; + const len = req.headers['content-length']; + const encoding = req.headers['content-encoding'] || 'identity'; if (len && encoding === 'identity') opts.length = ~~len; - opts.encoding = opts.encoding === undefined ? 'utf8': opts.encoding; + opts.encoding = opts.encoding === undefined ? 'utf8' : opts.encoding; opts.limit = opts.limit || '1mb'; - // raw-body returns a Promise when no callback is specified - return Promise.resolve() - .then(function() { - return raw(inflate(req), opts); - }) - .then(str => { - // ensure return the same format with json / form - return opts.returnRawBody ? { parsed: str, raw: str } : str; - }); + const str = await raw(inflate(req), opts); + // ensure return the same format with json / form + return opts.returnRawBody ? { parsed: str, raw: str } : str; }; diff --git a/lib/utils.js b/lib/utils.js index 96f2d9c..ea6afaa 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,13 +1,14 @@ +'use strict'; /** * Module dependencies. */ -exports.clone = function (opts) { - var options = {}; +exports.clone = function(opts) { + const options = {}; opts = opts || {}; - for (var key in opts) { + for (const key in opts) { options[key] = opts[key]; } return options; -} +}; diff --git a/package.json b/package.json index 26326be..ba74438 100644 --- a/package.json +++ b/package.json @@ -14,22 +14,26 @@ ], "dependencies": { "inflation": "^2.0.0", - "qs": "^6.4.0", - "raw-body": "^2.2.0", - "type-is": "^1.6.14" + "qs": "^6.5.2", + "raw-body": "^2.3.3", + "type-is": "^1.6.16" }, "devDependencies": { - "istanbul": "^0.4.5", - "koa": "^1.2.5", - "mocha": "^3.2.0", + "autod": "^3.0.1", + "egg-bin": "^4.7.0", + "eslint": "^4.19.1", + "eslint-config-egg": "^7.0.0", + "koa": "^1.6.0", "safe-qs": "^6.0.1", "should": "^11.2.0", - "supertest": "^1.0.1" + "supertest": "^3.1.0" }, "license": "MIT", "scripts": { - "test": "make test", - "test-cov": "make test-cov" + "lint": "eslint .", + "autod": "autod", + "test": "egg-bin test -r should", + "cov": "eslint . && egg-bin cov -r should" }, "files": [ "index.js", diff --git a/test/any.js b/test/any.js deleted file mode 100644 index d70dbf5..0000000 --- a/test/any.js +++ /dev/null @@ -1,231 +0,0 @@ - -var request = require('supertest'); -var parse = require('..'); -var koa = require('koa'); -var zlib = require('zlib'); - -describe('parse(req, opts)', function(){ - describe('with valid form body', function(){ - it('should parse', function(done){ - var app = koa(); - - app.use(function *(){ - var body = yield parse(this); - body.foo.bar.should.equal('baz'); - done(); - }); - - request(app.listen()) - .post('/') - .type('form') - .send({ foo: { bar: 'baz' }}) - .end(function(){}); - }) - }) - - describe('with valid json', function(){ - it('should parse', function(done){ - var app = koa(); - - app.use(function *(){ - var body = yield parse(this); - body.should.eql({ foo: 'bar' }); - done(); - }); - - request(app.listen()) - .post('/') - .send({ foo: 'bar' }) - .end(function(){}); - }) - }) - - describe('with valid text', function(){ - it('should parse', function(done){ - var app = koa(); - - app.use(function *(){ - this.body = yield parse(this); - }); - - request(app.listen()) - .post('/') - .set('content-type', 'text/plain') - .send('plain text') - .expect(200) - .expect('plain text', done); - }) - }) - - describe('with know json content-type', function(){ - var app = koa(); - - app.use(function *(){ - this.body = yield parse(this); - }); - - it('should parse application/json-patch+json', function(done){ - request(app.listen()) - .post('/') - .type('application/json-patch+json') - .send(JSON.stringify([{op: 'replace', path: '/foo', value:'bar'}])) - .expect(200) - .expect([{op: 'replace', path: '/foo', value:'bar'}], done); - }); - - it('should parse application/vnd.api+json', function(done){ - request(app.listen()) - .post('/') - .type('application/vnd.api+json') - .send(JSON.stringify({posts: '1'})) - .expect(200) - .expect({posts: '1'}, done); - }); - - it('should parse application/csp-report', function(done){ - request(app.listen()) - .post('/') - .type('application/csp-report') - .send(JSON.stringify({posts: '1'})) - .expect(200) - .expect({posts: '1'}, done); - }); - - it('should parse application/ld+json', function(done){ - request(app.listen()) - .post('/') - .type('application/ld+json') - .send(JSON.stringify({posts: '1'})) - .expect(200) - .expect({posts: '1'}, done); - }); - }); - - describe('with custom types', function(){ - it('should parse html as text', function(done){ - var app = koa(); - - app.use(function *(){ - var body = yield parse(this, { textTypes: 'text/html' }); - this.body = body - }); - - request(app.listen()) - .post('/') - .set('Content-Type', 'text/html') - .send('

html text') - .expect('

html text', done); - }) - - it('should parse graphql as text', function(done){ - var app = koa(); - - app.use(function *(){ - var body = yield parse(this, { textTypes: ['application/graphql', 'text/html'] }); - this.body = body - }); - - var graphql = '{\n user(id: 4) {\n name\n }\n}' - - request(app.listen()) - .post('/') - .set('Content-Type', 'application/graphql') - .send(graphql) - .expect(graphql, done); - }) - }) - - describe('with missing content-type', function(){ - it('should fail with 415', function(done){ - var app = koa(); - - app.use(function *(){ - yield parse(this); - }); - - request(app.listen()) - .post('/') - .expect(415, 'Unsupported Media Type', done); - }) - }) - - describe('with content-encoding', function(){ - it('should inflate gzip', function(done){ - var app = koa(); - var json = JSON.stringify({ foo: 'bar' }); - - app.use(function *(){ - var body = yield parse(this); - body.should.eql({ foo: 'bar' }); - done(); - }); - - var req = request(app.listen()) - .post('/') - .type('json') - .set('Content-Encoding', 'gzip'); - req.write(zlib.gzipSync(json)); - req.end(function(){}); - }) - it('should inflate deflate', function(done){ - var app = koa(); - var json = JSON.stringify({ foo: 'bar' }); - - app.use(function *(){ - var body = yield parse(this); - body.should.eql({ foo: 'bar' }); - done(); - }); - - var req = request(app.listen()) - .post('/') - .type('json') - .set('Content-Encoding', 'deflate'); - req.write(zlib.deflateSync(json)); - req.end(function(){}); - }) - - describe('after indentity and with shared options', function () { - var app = koa(); - var options = {}; - app.use(function *(){ - this.body = yield parse(this, options); - }); - - before(function(done) { - request(app.listen()) - .post('/') - .set('Content-Encoding', 'identity') - .send({ foo: 'bar', and: 'something extra' }) - .expect(200, done); - }); - - it('should inflate deflate', function(done){ - var json = JSON.stringify({ foo: 'bar' }); - var req = request(app.listen()) - .post('/') - .type('json') - .set('Content-Encoding', 'deflate'); - req.write(zlib.deflateSync(json)); - req.expect(200, done); - }); - }) - - it('should pass-through identity', function(done){ - var app = koa(); - - app.use(function *(){ - var body = yield parse(this); - body.should.eql({ foo: 'bar' }); - done(); - }); - - request(app.listen()) - .post('/') - .set('Content-Encoding', 'identity') - .send({ foo: 'bar' }) - .end(function(){}); - }) - }) - -}) diff --git a/test/any.test.js b/test/any.test.js new file mode 100644 index 0000000..36ebb41 --- /dev/null +++ b/test/any.test.js @@ -0,0 +1,232 @@ +'use strict'; + +const request = require('supertest'); +const parse = require('..'); +const koa = require('koa'); +const zlib = require('zlib'); + +describe('parse(req, opts)', function() { + describe('with valid form body', function() { + it('should parse', function(done) { + const app = koa(); + + app.use(function* () { + const body = yield parse(this); + body.foo.bar.should.equal('baz'); + done(); + }); + + request(app.callback()) + .post('/') + .type('form') + .send({ foo: { bar: 'baz' } }) + .end(function() {}); + }); + }); + + describe('with valid json', function() { + it('should parse', function(done) { + const app = koa(); + + app.use(function* () { + const body = yield parse(this); + body.should.eql({ foo: 'bar' }); + done(); + }); + + request(app.callback()) + .post('/') + .send({ foo: 'bar' }) + .end(function() {}); + }); + }); + + describe('with valid text', function() { + it('should parse', function(done) { + const app = koa(); + + app.use(function* () { + this.body = yield parse(this); + }); + + request(app.callback()) + .post('/') + .set('content-type', 'text/plain') + .send('plain text') + .expect(200) + .expect('plain text', done); + }); + }); + + describe('with know json content-type', function() { + const app = koa(); + + app.use(function* () { + this.body = yield parse(this); + }); + + it('should parse application/json-patch+json', function(done) { + request(app.callback()) + .post('/') + .type('application/json-patch+json') + .send(JSON.stringify([{ op: 'replace', path: '/foo', value: 'bar' }])) + .expect(200) + .expect([{ op: 'replace', path: '/foo', value: 'bar' }], done); + }); + + it('should parse application/vnd.api+json', function(done) { + request(app.callback()) + .post('/') + .type('application/vnd.api+json') + .send(JSON.stringify({ posts: '1' })) + .expect(200) + .expect({ posts: '1' }, done); + }); + + it('should parse application/csp-report', function(done) { + request(app.callback()) + .post('/') + .type('application/csp-report') + .send(JSON.stringify({ posts: '1' })) + .expect(200) + .expect({ posts: '1' }, done); + }); + + it('should parse application/ld+json', function(done) { + request(app.callback()) + .post('/') + .type('application/ld+json') + .send(JSON.stringify({ posts: '1' })) + .expect(200) + .expect({ posts: '1' }, done); + }); + }); + + describe('with custom types', function() { + it('should parse html as text', function(done) { + const app = koa(); + + app.use(function* () { + const body = yield parse(this, { textTypes: 'text/html' }); + this.body = body; + }); + + request(app.callback()) + .post('/') + .set('Content-Type', 'text/html') + .send('

html text') + .expect('

html text', done); + }); + + it('should parse graphql as text', function(done) { + const app = koa(); + + app.use(function* () { + const body = yield parse(this, { textTypes: [ 'application/graphql', 'text/html' ] }); + this.body = body; + }); + + const graphql = '{\n user(id: 4) {\n name\n }\n}'; + + request(app.callback()) + .post('/') + .set('Content-Type', 'application/graphql') + .send(graphql) + .expect(graphql, done); + }); + }); + + describe('with missing content-type', function() { + it('should fail with 415', function(done) { + const app = koa(); + + app.use(function* () { + yield parse(this); + }); + + request(app.callback()) + .post('/') + .expect(415, 'Unsupported Media Type', done); + }); + }); + + describe('with content-encoding', function() { + it('should inflate gzip', function(done) { + const app = koa(); + const json = JSON.stringify({ foo: 'bar' }); + + app.use(function* () { + const body = yield parse(this); + body.should.eql({ foo: 'bar' }); + done(); + }); + + const req = request(app.callback()) + .post('/') + .type('json') + .set('Content-Encoding', 'gzip'); + req.write(zlib.gzipSync(json)); + req.end(function() {}); + }); + it('should inflate deflate', function(done) { + const app = koa(); + const json = JSON.stringify({ foo: 'bar' }); + + app.use(function* () { + const body = yield parse(this); + body.should.eql({ foo: 'bar' }); + done(); + }); + + const req = request(app.callback()) + .post('/') + .type('json') + .set('Content-Encoding', 'deflate'); + req.write(zlib.deflateSync(json)); + req.end(function() {}); + }); + + describe('after indentity and with shared options', function() { + const app = koa(); + const options = {}; + app.use(function* () { + this.body = yield parse(this, options); + }); + + before(function(done) { + request(app.callback()) + .post('/') + .set('Content-Encoding', 'identity') + .send({ foo: 'bar', and: 'something extra' }) + .expect(200, done); + }); + + it('should inflate deflate', function(done) { + const json = JSON.stringify({ foo: 'bar' }); + const req = request(app.callback()) + .post('/') + .type('json') + .set('Content-Encoding', 'deflate'); + req.write(zlib.deflateSync(json)); + req.expect(200, done); + }); + }); + + it('should pass-through identity', function(done) { + const app = koa(); + + app.use(function* () { + const body = yield parse(this); + body.should.eql({ foo: 'bar' }); + done(); + }); + + request(app.callback()) + .post('/') + .set('Content-Encoding', 'identity') + .send({ foo: 'bar' }) + .end(function() {}); + }); + }); + +}); diff --git a/test/form.js b/test/form.js deleted file mode 100644 index 65121bf..0000000 --- a/test/form.js +++ /dev/null @@ -1,154 +0,0 @@ - -var request = require('supertest'); -var parse = require('..'); -var koa = require('koa'); - -describe('parse.form(req, opts)', function(){ - describe('with valid form body', function(){ - it('should parse', function(done){ - var app = koa(); - - app.use(function *(){ - var body = yield parse.form(this); - body.foo.bar.should.equal('baz'); - this.status = 200; - }); - - request(app.listen()) - .post('/') - .type('form') - .send({ foo: { bar: 'baz' }}) - .end(function(err){ done(err); }); - }) - }) - - describe('with invalid content encoding', function() { - it('should throw 415', function(done) { - var app = koa(); - - app.use(function *(){ - var body = yield parse.form(this); - body.foo.bar.should.equal('baz'); - this.status = 200; - }); - - request(app.listen()) - .post('/') - .type('form') - .set('content-encoding', 'invalid') - .send({ foo: { bar: 'baz' }}) - .expect(415, done); - }) - }) - - describe('with qs settings', function(){ - var data = { level1: { level2: { level3: { level4: { level5: { level6: { level7: 'Hello' } } } } } } }; - - it('should not parse full depth', function(done){ - var app = koa(); - - app.use(function *(){ - var body = yield parse.form(this); // default to depth = 5 - body.level1.level2.level3.level4.level5.level6['[level7]'].should.equal('Hello'); - this.status = 200; - }); - - request(app.listen()) - .post('/') - .type('form') - .send({ level1: { level2: { level3: { level4: { level5: { level6: { level7: 'Hello' }}}}}}}) - .end(function(err){ done(err); }); - - }) - - it('should parse', function(done){ - var app = koa(); - - app.use(function *(){ - var body = yield parse.form(this, { queryString: { depth: 10 } }); - body.level1.level2.level3.level4.level5.level6.level7.should.equal('Hello'); - this.status = 200; - }); - - request(app.listen()) - .post('/') - .type('form') - .send(data) - .end(function(err){ done(err); }); - }) - }) - - describe('with custom qs module', function(){ - it('should parse with safe-qs', function(done){ - var app = koa(); - - app.use(function *(){ - try { - var body = yield parse.form(this, { - qs: require('safe-qs'), - }); - throw new Error('should not run this'); - } catch (err) { - this.status = err.status; - this.body = err.message; - } - }); - - request(app.listen()) - .post('/') - .type('form') - .send({ a: { '21': 'a' } }) - .expect('Index of array [21] is overstep limit: 20') - .expect(400, done); - }) - }) - - describe('allowDots', function(){ - it('should allowDots default to true', function(done){ - var app = koa(); - - app.use(function *(){ - this.body = yield parse.form(this); - }); - - request(app.listen()) - .post('/') - .type('form') - .send('a.b=1&a.c=2') - .expect({ a: { b: '1', c: '2' } }) - .expect(200, done); - }); - - it('allowDots can set to false', function(done){ - var app = koa(); - - app.use(function *(){ - this.body = yield parse.form(this, { queryString: { allowDots: false } }); - }); - - request(app.listen()) - .post('/') - .type('form') - .send('a.b=1&a.c=2') - .expect({ 'a.b': '1', 'a.c': '2' }) - .expect(200, done); - }); - }) - - describe('returnRawBody', function(){ - it('should return raw body when opts.returnRawBody = true', function(done){ - var app = koa(); - - app.use(function *(){ - this.body = yield parse.form(this, { returnRawBody: true }); - }); - - request(app.listen()) - .post('/') - .type('form') - .send('a[b]=1&a[c]=2') - .expect({ parsed: { a: { b: '1', c: '2' } }, raw: 'a[b]=1&a[c]=2' }) - .expect(200, done); - }); - }) -}) diff --git a/test/form.test.js b/test/form.test.js new file mode 100644 index 0000000..63873dc --- /dev/null +++ b/test/form.test.js @@ -0,0 +1,155 @@ +'use strict'; + +const request = require('supertest'); +const parse = require('..'); +const koa = require('koa'); + +describe('parse.form(req, opts)', function() { + describe('with valid form body', function() { + it('should parse', function(done) { + const app = koa(); + + app.use(function* () { + const body = yield parse.form(this); + body.foo.bar.should.equal('baz'); + this.status = 200; + }); + + request(app.callback()) + .post('/') + .type('form') + .send({ foo: { bar: 'baz' } }) + .end(function(err) { done(err); }); + }); + }); + + describe('with invalid content encoding', function() { + it('should throw 415', function(done) { + const app = koa(); + + app.use(function* () { + const body = yield parse.form(this); + body.foo.bar.should.equal('baz'); + this.status = 200; + }); + + request(app.callback()) + .post('/') + .type('form') + .set('content-encoding', 'invalid') + .send({ foo: { bar: 'baz' } }) + .expect(415, done); + }); + }); + + describe('with qs settings', function() { + const data = { level1: { level2: { level3: { level4: { level5: { level6: { level7: 'Hello' } } } } } } }; + + it('should not parse full depth', function(done) { + const app = koa(); + + app.use(function* () { + const body = yield parse.form(this); // default to depth = 5 + body.level1.level2.level3.level4.level5.level6['[level7]'].should.equal('Hello'); + this.status = 200; + }); + + request(app.callback()) + .post('/') + .type('form') + .send({ level1: { level2: { level3: { level4: { level5: { level6: { level7: 'Hello' } } } } } } }) + .end(function(err) { done(err); }); + + }); + + it('should parse', function(done) { + const app = koa(); + + app.use(function* () { + const body = yield parse.form(this, { queryString: { depth: 10 } }); + body.level1.level2.level3.level4.level5.level6.level7.should.equal('Hello'); + this.status = 200; + }); + + request(app.callback()) + .post('/') + .type('form') + .send(data) + .end(function(err) { done(err); }); + }); + }); + + describe('with custom qs module', function() { + it('should parse with safe-qs', function(done) { + const app = koa(); + + app.use(function* () { + try { + yield parse.form(this, { + qs: require('safe-qs'), + }); + throw new Error('should not run this'); + } catch (err) { + this.status = err.status; + this.body = err.message; + } + }); + + request(app.callback()) + .post('/') + .type('form') + .send({ a: { 21: 'a' } }) + .expect('Index of array [21] is overstep limit: 20') + .expect(400, done); + }); + }); + + describe('allowDots', function() { + it('should allowDots default to true', function(done) { + const app = koa(); + + app.use(function* () { + this.body = yield parse.form(this); + }); + + request(app.callback()) + .post('/') + .type('form') + .send('a.b=1&a.c=2') + .expect({ a: { b: '1', c: '2' } }) + .expect(200, done); + }); + + it('allowDots can set to false', function(done) { + const app = koa(); + + app.use(function* () { + this.body = yield parse.form(this, { queryString: { allowDots: false } }); + }); + + request(app.callback()) + .post('/') + .type('form') + .send('a.b=1&a.c=2') + .expect({ 'a.b': '1', 'a.c': '2' }) + .expect(200, done); + }); + }); + + describe('returnRawBody', function() { + it('should return raw body when opts.returnRawBody = true', function(done) { + const app = koa(); + + app.use(function* () { + this.body = yield parse.form(this, { returnRawBody: true }); + }); + + request(app.callback()) + .post('/') + .type('form') + .send('a[b]=1&a[c]=2') + .expect({ parsed: { a: { b: '1', c: '2' } }, raw: 'a[b]=1&a[c]=2' }) + .expect(200, done); + }); + }); +}); diff --git a/test/json.js b/test/json.js deleted file mode 100644 index 0583c89..0000000 --- a/test/json.js +++ /dev/null @@ -1,158 +0,0 @@ - -var request = require('supertest'); -var parse = require('..'); -var koa = require('koa'); - -describe('parse.json(req, opts)', function(){ - describe('with valid json', function(){ - it('should parse', function(done){ - var app = koa(); - - app.use(function *(){ - var body = yield parse.json(this); - body.should.eql({ foo: 'bar' }); - done(); - }); - - request(app.listen()) - .post('/') - .send({ foo: 'bar' }) - .end(function(){}); - }) - }) - - describe('with invalid content encoding', function() { - it('should throw 415', function(done) { - var app = koa(); - - app.use(function *(){ - var body = yield parse.json(this); - body.foo.bar.should.equal('baz'); - this.status = 200; - }); - - request(app.listen()) - .post('/') - .type('json') - .set('content-encoding', 'invalid') - .send({ foo: { bar: 'baz' }}) - .expect(415, done); - }) - }) - - describe('with content-length zero', function(){ - describe('and strict === false', function(){ - it('should return null', function(done) { - var app = koa(); - - app.use(function *() { - var body = yield parse.json(this, {strict: false}); - body.should.equal(''); - done(); - }); - request(app.listen()) - .post('/') - .set('content-length', 0) - .end(function(){}); - }) - }) - - describe('and strict === true', function(){ - it('should return null', function(done) { - var app = koa(); - - app.use(function *() { - var body = yield parse.json(this); - body.should.eql({}); - done(); - }); - request(app.listen()) - .post('/') - .set('content-length', 0) - .end(function(){}); - }) - }) - }) - - describe('with invalid json', function(){ - it('should parse error', function(done){ - var app = koa(); - - app.use(function *(){ - try { - yield parse.json(this); - } catch (err) { - err.status.should.equal(400); - err.body.should.equal('{"foo": "bar'); - done(); - } - }); - - request(app.listen()) - .post('/') - .set('content-type', 'application/json') - .send('{"foo": "bar') - .end(function(){}); - }) - }) - - describe('with non-object json', function(){ - describe('and strict === false', function(){ - it('should parse', function(done){ - var app = koa(); - - app.use(function *(){ - var body = yield parse.json(this, {strict: false}); - body.should.equal('foo'); - done(); - }); - - request(app.listen()) - .post('/') - .set('content-type', 'application/json') - .send('"foo"') - .end(function(){}); - }) - }) - - describe('and strict === true', function(){ - it('should parse', function(done){ - var app = koa(); - - app.use(function *(){ - try { - yield parse.json(this, {strict: true}); - } catch (err) { - err.status.should.equal(400); - err.body.should.equal('"foo"'); - err.message.should.equal('invalid JSON, only supports object and array'); - done(); - } - }); - - request(app.listen()) - .post('/') - .set('content-type', 'application/json') - .send('"foo"') - .end(function(){}); - }) - }) - }) - - describe('returnRawBody', function(){ - it('should return raw body when opts.returnRawBody = true', function(done){ - var app = koa(); - - app.use(function *(){ - this.body = yield parse.json(this, { returnRawBody: true }); - }); - - request(app.listen()) - .post('/') - .type('json') - .send({ foo: 'bar' }) - .expect({ parsed: { foo: 'bar' }, raw: '{"foo":"bar"}' }) - .expect(200, done); - }); - }) -}) diff --git a/test/json.test.js b/test/json.test.js new file mode 100644 index 0000000..97f86cf --- /dev/null +++ b/test/json.test.js @@ -0,0 +1,159 @@ +'use strict'; + +const request = require('supertest'); +const parse = require('..'); +const koa = require('koa'); + +describe('parse.json(req, opts)', function() { + describe('with valid json', function() { + it('should parse', function(done) { + const app = koa(); + + app.use(function* () { + const body = yield parse.json(this); + body.should.eql({ foo: 'bar' }); + done(); + }); + + request(app.callback()) + .post('/') + .send({ foo: 'bar' }) + .end(function() {}); + }); + }); + + describe('with invalid content encoding', function() { + it('should throw 415', function(done) { + const app = koa(); + + app.use(function* () { + const body = yield parse.json(this); + body.foo.bar.should.equal('baz'); + this.status = 200; + }); + + request(app.callback()) + .post('/') + .type('json') + .set('content-encoding', 'invalid') + .send({ foo: { bar: 'baz' } }) + .expect(415, done); + }); + }); + + describe('with content-length zero', function() { + describe('and strict === false', function() { + it('should return null', function(done) { + const app = koa(); + + app.use(function* () { + const body = yield parse.json(this, { strict: false }); + body.should.equal(''); + done(); + }); + request(app.callback()) + .post('/') + .set('content-length', 0) + .end(function() {}); + }); + }); + + describe('and strict === true', function() { + it('should return null', function(done) { + const app = koa(); + + app.use(function* () { + const body = yield parse.json(this); + body.should.eql({}); + done(); + }); + request(app.callback()) + .post('/') + .set('content-length', 0) + .end(function() {}); + }); + }); + }); + + describe('with invalid json', function() { + it('should parse error', function(done) { + const app = koa(); + + app.use(function* () { + try { + yield parse.json(this); + } catch (err) { + err.status.should.equal(400); + err.body.should.equal('{"foo": "bar'); + done(); + } + }); + + request(app.callback()) + .post('/') + .set('content-type', 'application/json') + .send('{"foo": "bar') + .end(function() {}); + }); + }); + + describe('with non-object json', function() { + describe('and strict === false', function() { + it('should parse', function(done) { + const app = koa(); + + app.use(function* () { + const body = yield parse.json(this, { strict: false }); + body.should.equal('foo'); + done(); + }); + + request(app.callback()) + .post('/') + .set('content-type', 'application/json') + .send('"foo"') + .end(function() {}); + }); + }); + + describe('and strict === true', function() { + it('should parse', function(done) { + const app = koa(); + + app.use(function* () { + try { + yield parse.json(this, { strict: true }); + } catch (err) { + err.status.should.equal(400); + err.body.should.equal('"foo"'); + err.message.should.equal('invalid JSON, only supports object and array'); + done(); + } + }); + + request(app.callback()) + .post('/') + .set('content-type', 'application/json') + .send('"foo"') + .end(function() {}); + }); + }); + }); + + describe('returnRawBody', function() { + it('should return raw body when opts.returnRawBody = true', function(done) { + const app = koa(); + + app.use(function* () { + this.body = yield parse.json(this, { returnRawBody: true }); + }); + + request(app.callback()) + .post('/') + .type('json') + .send({ foo: 'bar' }) + .expect({ parsed: { foo: 'bar' }, raw: '{"foo":"bar"}' }) + .expect(200, done); + }); + }); +}); diff --git a/test/text.js b/test/text.js deleted file mode 100644 index bdaffc0..0000000 --- a/test/text.js +++ /dev/null @@ -1,73 +0,0 @@ - -var request = require('supertest'); -var parse = require('..'); -var koa = require('koa'); -var Buffer = require('buffer').Buffer; - -describe('parse.text(req, opts)', function(){ - describe('with valid str', function(){ - it('should parse', function(done){ - var app = koa(); - - app.use(function *(){ - this.body = yield parse.text(this); - }); - - request(app.listen()) - .post('/') - .send('Hello World!') - .expect(200) - .expect('Hello World!', done); - }); - }); - - describe('with invalid content encoding', function() { - it('should throw 415', function(done) { - var app = koa(); - - app.use(function *(){ - var body = yield parse.text(this); - this.status = 200; - }); - - request(app.listen()) - .post('/') - .set('content-encoding', 'invalid') - .send('Hello World!') - .expect(415, done); - }) - }) - - describe('returnRawBody', function(){ - it('should return raw body when opts.returnRawBody = true', function(done){ - var app = koa(); - - app.use(function *(){ - this.body = yield parse.text(this, { returnRawBody: true }); - }); - - request(app.listen()) - .post('/') - .send('Hello World!') - .expect({ parsed: 'Hello World!', raw: 'Hello World!' }) - .expect(200, done); - }); - }) - - describe('use no encoding', function(){ - it('should return raw body when opts.returnRawBody = true', function(done){ - var app = koa(); - - app.use(function *(){ - const requestBody = yield parse.text(this, {encoding: false}); - this.body = { isBuffer: Buffer.isBuffer(requestBody) }; - }); - - request(app.listen()) - .post('/') - .send('Hello World!') - .expect({ isBuffer: true }) - .expect(200, done); - }); - }) -}); diff --git a/test/text.test.js b/test/text.test.js new file mode 100644 index 0000000..0b71309 --- /dev/null +++ b/test/text.test.js @@ -0,0 +1,74 @@ +'use strict'; + +const request = require('supertest'); +const parse = require('..'); +const koa = require('koa'); +const Buffer = require('buffer').Buffer; + +describe('parse.text(req, opts)', function() { + describe('with valid str', function() { + it('should parse', function(done) { + const app = koa(); + + app.use(function* () { + this.body = yield parse.text(this); + }); + + request(app.callback()) + .post('/') + .send('Hello World!') + .expect(200) + .expect('Hello World!', done); + }); + }); + + describe('with invalid content encoding', function() { + it('should throw 415', function(done) { + const app = koa(); + + app.use(function* () { + yield parse.text(this); + this.status = 200; + }); + + request(app.callback()) + .post('/') + .set('content-encoding', 'invalid') + .send('Hello World!') + .expect(415, done); + }); + }); + + describe('returnRawBody', function() { + it('should return raw body when opts.returnRawBody = true', function(done) { + const app = koa(); + + app.use(function* () { + this.body = yield parse.text(this, { returnRawBody: true }); + }); + + request(app.callback()) + .post('/') + .send('Hello World!') + .expect({ parsed: 'Hello World!', raw: 'Hello World!' }) + .expect(200, done); + }); + }); + + describe('use no encoding', function() { + it('should return raw body when opts.returnRawBody = true', function(done) { + const app = koa(); + + app.use(function* () { + const requestBody = yield parse.text(this, { encoding: false }); + this.body = { isBuffer: Buffer.isBuffer(requestBody) }; + }); + + request(app.callback()) + .post('/') + .send('Hello World!') + .expect({ isBuffer: true }) + .expect(200, done); + }); + }); +});