Comparing version 0.5.0 to 0.6.0
var foldHeaderLine = require('./foldHeaderLine'), | ||
formatHeaderName = require('./formatHeaderName'); | ||
formatHeaderName = require('./formatHeaderName'), | ||
isRegExp = require('./isRegExp'); | ||
function Headers(obj) { | ||
function Headers(obj, doNotStringify) { | ||
this.valuesByName = {}; | ||
this.populate(obj); | ||
this.populate(obj, doNotStringify); | ||
} | ||
Headers.prototype.populate = function (obj) { | ||
Headers.prototype.populate = function (obj, doNotStringify) { | ||
if (typeof obj === 'string') { | ||
this.populateFromString(obj); | ||
} else if (obj && typeof obj === 'object') { | ||
this.populateFromObject(obj); | ||
this.populateFromObject(obj, doNotStringify); | ||
} | ||
@@ -97,3 +98,3 @@ }; | ||
Headers.prototype.populateFromObject = function (valuesByName) { | ||
Headers.prototype.populateFromObject = function (valuesByName, doNotStringify) { | ||
Object.keys(valuesByName).forEach(function (headerName) { | ||
@@ -103,5 +104,5 @@ var value = valuesByName[headerName], | ||
if (Array.isArray(value)) { | ||
this.valuesByName[headerNameLowerCase] = value.map(String); | ||
this.valuesByName[headerNameLowerCase] = doNotStringify ? [].concat(value) : value.map(String); | ||
} else if (typeof value !== 'undefined') { | ||
this.valuesByName[headerNameLowerCase] = [String(value)]; | ||
this.valuesByName[headerNameLowerCase] = doNotStringify ? [value] : [String(value)]; | ||
} | ||
@@ -161,5 +162,5 @@ }, this); | ||
// has('Cookie', ['foo=bar', 'baz=quux']); | ||
Headers.prototype.has = function (headerName, stringOrArray) { | ||
Headers.prototype.has = function (headerName, stringOrArrayOrRegExp) { | ||
var values = this.valuesByName[headerName.toLowerCase()]; | ||
if (typeof stringOrArray === 'undefined') { | ||
if (typeof stringOrArrayOrRegExp === 'undefined') { | ||
return !!values; | ||
@@ -169,8 +170,14 @@ } else if (typeof values === 'undefined') { | ||
} else { | ||
if (Array.isArray(stringOrArray)) { | ||
return stringOrArray.every(function (expectedValue) { | ||
return values.indexOf(String(expectedValue)) !== -1; | ||
if (Array.isArray(stringOrArrayOrRegExp)) { | ||
return stringOrArrayOrRegExp.every(function (expectedValue) { | ||
if (isRegExp(expectedValue)) { | ||
return values.some(function (value) { | ||
return expectedValue.test(value); | ||
}); | ||
} else { | ||
return values.indexOf(String(expectedValue)) !== -1; | ||
} | ||
}); | ||
} else { | ||
return values.length === 1 && values[0] === String(stringOrArray); | ||
return values.length === 1 && values[0] === String(stringOrArrayOrRegExp); | ||
} | ||
@@ -217,5 +224,9 @@ } | ||
Headers.prototype.satisfy = function (spec, mustBeExhaustive) { | ||
if (isRegExp(spec)) { | ||
return spec.test(this.toString()); | ||
} | ||
if (spec && typeof spec === 'object' && !(spec instanceof Headers)) { | ||
var specPropertyNames = Object.keys(spec); | ||
// A value of 'undefined' means that the header must be absent: | ||
if (Object.keys(spec).some(function (headerName) { | ||
if (specPropertyNames.some(function (headerName) { | ||
return typeof spec[headerName] === 'undefined' && this.has(headerName); | ||
@@ -227,3 +238,3 @@ }, this)) { | ||
var expectedHeaders = spec instanceof Headers ? spec : new Headers(spec), | ||
var expectedHeaders = spec instanceof Headers ? spec : new Headers(spec, true), | ||
expectedHeaderNames = expectedHeaders.getNames(); | ||
@@ -234,3 +245,2 @@ | ||
} | ||
return expectedHeaderNames.every(function (expectedHeaderName) { | ||
@@ -248,3 +258,3 @@ var expectedValues = expectedHeaders.getAll(expectedHeaderName); | ||
if (!values.every(function (value, i) { | ||
return value === expectedValues[i]; | ||
return isRegExp(expectedValues[i]) ? expectedValues[i].test(value) : value === expectedValues[i]; | ||
})) { | ||
@@ -251,0 +261,0 @@ return false; |
var Message = require('./Message'), | ||
util = require('util'); | ||
util = require('util'), | ||
isRegExp = require('./isRegExp'); | ||
@@ -134,3 +135,5 @@ function HttpRequest(obj) { | ||
return (propertyName in spec) ? | ||
this[propertyName] === spec[propertyName] : | ||
(isRegExp(spec[propertyName]) ? | ||
spec[propertyName].test(this[propertyName]) : | ||
this[propertyName] === spec[propertyName]) : | ||
!mustBeExhaustive || typeof this[propertyName] === 'undefined'; | ||
@@ -137,0 +140,0 @@ }, this); |
var Message = require('./Message'), | ||
util = require('util'); | ||
util = require('util'), | ||
isRegExp = require('./isRegExp'); | ||
@@ -107,3 +108,5 @@ function HttpResponse(obj) { | ||
return (propertyName in spec) ? | ||
this[propertyName] === spec[propertyName] : | ||
(isRegExp(spec[propertyName]) ? | ||
spec[propertyName].test(this[propertyName]) : | ||
this[propertyName] === spec[propertyName]) : | ||
!mustBeExhaustive || typeof this[propertyName] === 'undefined'; | ||
@@ -110,0 +113,0 @@ }, this); |
/*global unescape*/ | ||
var Headers = require('./Headers'); | ||
var Headers = require('./Headers'), | ||
isRegExp = require('./isRegExp'); | ||
@@ -70,31 +71,31 @@ function Message(obj) { | ||
function isNonBufferObject(obj) { | ||
return obj && typeof obj === 'object' && !Buffer.isBuffer(obj); | ||
function isNonBufferNonRegExpObject(obj) { | ||
return obj && typeof obj === 'object' && !Buffer.isBuffer(obj) && !isRegExp(obj); | ||
} | ||
function canonicalizeObject(obj, stack) { | ||
stack = stack || []; | ||
stack = stack || []; | ||
if (stack.indexOf(obj) !== -1) return '[Circular]'; | ||
if (stack.indexOf(obj) !== -1) return '[Circular]'; | ||
var canonicalizedObject; | ||
var canonicalizedObject; | ||
if ({}.toString.call(obj) === '[object Array]') { | ||
stack.push(obj); | ||
canonicalizedObject = exports.map(obj, function (item) { | ||
return canonicalizeObject(item, stack); | ||
}); | ||
stack.pop(); | ||
} else if (typeof obj === 'object' && obj !== null) { | ||
stack.push(obj); | ||
canonicalizedObject = {}; | ||
Object.keys(obj).sort().forEach(function (key) { | ||
canonicalizedObject[key] = canonicalizeObject(obj[key], stack); | ||
}); | ||
stack.pop(); | ||
} else { | ||
canonicalizedObject = obj; | ||
} | ||
if ({}.toString.call(obj) === '[object Array]') { | ||
stack.push(obj); | ||
canonicalizedObject = exports.map(obj, function (item) { | ||
return canonicalizeObject(item, stack); | ||
}); | ||
stack.pop(); | ||
} else if (typeof obj === 'object' && obj !== null) { | ||
stack.push(obj); | ||
canonicalizedObject = {}; | ||
Object.keys(obj).sort().forEach(function (key) { | ||
canonicalizedObject[key] = canonicalizeObject(obj[key], stack); | ||
}); | ||
stack.pop(); | ||
} else { | ||
canonicalizedObject = obj; | ||
} | ||
return canonicalizedObject; | ||
return canonicalizedObject; | ||
} | ||
@@ -123,2 +124,28 @@ | ||
function isTextualContentType(contentType) { | ||
if (typeof contentType === 'string') { | ||
contentType = contentType.toLowerCase().trim().replace(/\s*;.*$/, ''); | ||
return ( | ||
/^text\//.test(contentType) || | ||
/^application\/(json|javascript)$/.test(contentType) || | ||
/^application\/xml/.test(contentType) || | ||
/\+xml$/.test(contentType) | ||
); | ||
} | ||
return false; | ||
} | ||
function bufferCanBeInterpretedAsUtf8(buffer) { | ||
// Hack: Since Buffer.prototype.toString('utf-8') is very forgiving, convert the buffer to a string | ||
// with percent-encoded octets, then see if decodeURIComponent accepts it. | ||
try { | ||
decodeURIComponent(Array.prototype.map.call(buffer, function (octet) { | ||
return '%' + (octet < 16 ? '0' : '') + octet.toString(16); | ||
}).join('')); | ||
} catch (e) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
// Exploratory work wrt. https://github.com/sunesimonsen/unexpected/issues/40 | ||
@@ -136,23 +163,69 @@ Message.prototype.satisfies = function (spec, mustBeExhaustive) { | ||
} | ||
var thisBody = this.body; | ||
if ('body' in spec) { | ||
if (Buffer.isBuffer(spec.body) && Buffer.isBuffer(this.body)) { | ||
if (!buffersEqual(spec.body, this.body)) { | ||
var specBody = spec.body; | ||
if (typeof thisBody !== 'undefined') { | ||
if (isNonBufferNonRegExpObject(thisBody) && /^application\/json\b/i.test(this.headers.get('Content-Type'))) { | ||
if (typeof specBody === 'string' || Buffer.isBuffer(specBody)) { | ||
var parsedSpecBody; | ||
try { | ||
parsedSpecBody = JSON.parse(specBody); | ||
} catch (e) {} | ||
if (typeof parsedSpecBody !== 'undefined') { | ||
specBody = JSON.stringify(canonicalizeObject(parsedSpecBody), undefined, ' '); | ||
thisBody = JSON.stringify(thisBody, undefined, ' '); | ||
} | ||
} else if (isRegExp(specBody)) { | ||
thisBody = JSON.stringify(thisBody, undefined, ' '); | ||
} | ||
} | ||
if (Buffer.isBuffer(thisBody) && (typeof specBody === 'string' || isRegExp(specBody) || isNonBufferNonRegExpObject(specBody)) || (typeof specBody === 'undefined' && bufferCanBeInterpretedAsUtf8(thisBody) && isTextualContentType(this.headers.get('Content-Type')))) { | ||
try { | ||
thisBody = thisBody.toString('utf-8'); | ||
} catch (e) { | ||
// The body cannot be intepreted as utf-8, keep it as a Buffer instance | ||
} | ||
} | ||
if (/^application\/json\b/i.test(this.headers.get('Content-Type')) && typeof thisBody === 'string' && (typeof specBody === 'undefined' || isNonBufferNonRegExpObject(specBody))) { | ||
try { | ||
thisBody = JSON.parse(thisBody); | ||
} catch (e) { | ||
// The body cannot be parsed as JSON, kepe as a string instance | ||
} | ||
} else if (Buffer.isBuffer(specBody) && (!thisBody || typeof thisBody === 'string')) { | ||
thisBody = new Buffer(thisBody, 'utf-8'); | ||
} | ||
} else if (Buffer.isBuffer(specBody) && specBody.length === 0) { | ||
thisBody = new Buffer([]); | ||
} else if (specBody === '') { | ||
thisBody = ''; | ||
} | ||
if (Buffer.isBuffer(specBody) && Buffer.isBuffer(thisBody)) { | ||
if (!buffersEqual(specBody, thisBody)) { | ||
return false; | ||
} | ||
} else if (typeof spec.body === 'string' && typeof this.body === 'string') { | ||
if (spec.body !== this.body) { | ||
} else if (isRegExp(specBody) && typeof thisBody === 'string') { | ||
if (!specBody.test(thisBody)) { | ||
return false; | ||
} | ||
} else if (isNonBufferObject(spec.body) && isNonBufferObject(this.body)) { | ||
if (JSON.stringify(canonicalizeObject(spec.body)) !== JSON.stringify(canonicalizeObject(this.body))) { | ||
} else if (typeof specBody === 'string' && typeof thisBody === 'string') { | ||
if (specBody !== thisBody) { | ||
return false; | ||
} | ||
} else if (typeof spec.body === 'undefined') { | ||
if (typeof this.body !== 'undefined') { | ||
} else if (typeof specBody === 'undefined') { | ||
if (typeof thisBody !== 'undefined') { | ||
return false; | ||
} | ||
} else if (isNonBufferNonRegExpObject(specBody) && isNonBufferNonRegExpObject(thisBody)) { | ||
if (JSON.stringify(canonicalizeObject(specBody)) !== JSON.stringify(canonicalizeObject(thisBody))) { | ||
return false; | ||
} | ||
} else { | ||
throw new Error('Unsupported comparison between different types of bodies'); | ||
return false; | ||
} | ||
} else if (mustBeExhaustive && typeof this.body !== 'undefined') { | ||
} else if (mustBeExhaustive && typeof thisBody !== 'undefined') { | ||
return false; | ||
@@ -159,0 +232,0 @@ } |
{ | ||
"name": "messy", | ||
"version": "0.5.0", | ||
"version": "0.6.0", | ||
"description": "Object model for HTTP and RFC822 messages", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
@@ -135,2 +135,8 @@ /*global describe, it*/ | ||
it('should support regexp matching', function () { | ||
expect(new HttpRequest('GET /foo HTTP/1.1\r\nContent-Type: text/html').satisfies({ | ||
protocolName: /ttp/i | ||
}), 'to be true'); | ||
}); | ||
it('should fail when matching on properties defined by Message', function () { | ||
@@ -137,0 +143,0 @@ expect(new HttpRequest('GET /foo HTTP/1.1\r\nContent-Type: text/html').satisfies({ |
@@ -130,2 +130,8 @@ /*global describe, it*/ | ||
it('should support regexp matching', function () { | ||
expect(new HttpResponse('HTTP/1.1 200 OK\r\nContent-Type: text/html').satisfies({ | ||
protocolName: /ttp/i | ||
}), 'to be true'); | ||
}); | ||
it('should fail when matching on properties defined by Message', function () { | ||
@@ -132,0 +138,0 @@ expect(new HttpResponse('HTTP/1.1 200 OK\r\nContent-Type: text/html').satisfies({ |
@@ -186,2 +186,10 @@ /*global describe, it*/ | ||
it('should support matching the serialized headers with a regular expression', function () { | ||
expect(new Message({headers: {foo: 'a', bar: 'b'}}).satisfies({headers: /a\r\nBar/}), 'to be true'); | ||
}); | ||
it('should support matching individual headers with a regular expression', function () { | ||
expect(new Message({headers: {foo: 'abc'}}).satisfies({headers: {foo: /bc$/}}), 'to be true'); | ||
}); | ||
it('should support passing the expected properties as a string', function () { | ||
@@ -201,2 +209,6 @@ expect(new Message({headers: {foo: 'a'}}).satisfies('foo: a'), 'to be true'); | ||
it('should support matching a string body with a regular expression', function () { | ||
expect(new Message('foo: bar\n\nthe body').satisfies({body: /he b/}), 'to be true'); | ||
}); | ||
it('should support matching a Buffer body with a Buffer', function () { | ||
@@ -206,3 +218,67 @@ expect(new Message(new Buffer('foo: bar\n\nthe body', 'utf-8')).satisfies({body: new Buffer('the body', 'utf-8')}), 'to be true'); | ||
it('should support matching a object body (JSON) with an object', function () { | ||
it('should support matching a Buffer body with a string', function () { | ||
expect(new Message(new Buffer('foo: bar\n\nthe body', 'utf-8')).satisfies({body: 'the body'}), 'to be true'); | ||
}); | ||
it('should support matching a Buffer body with a regular expression', function () { | ||
expect(new Message(new Buffer('foo: bar\n\nthe body', 'utf-8')).satisfies({body: /he b/}), 'to be true'); | ||
}); | ||
it('should support matching a string body with a Buffer', function () { | ||
expect(new Message('foo: bar\n\nthe body').satisfies({body: new Buffer('the body', 'utf-8')}), 'to be true'); | ||
}); | ||
it('should support matching a Buffer body with an object when the Content-Type is application/json', function () { | ||
expect(new Message(new Buffer('Content-Type: application/json\n\n{"the": "body"}', 'utf-8')).satisfies({body: {the: 'body'}}), 'to be true'); | ||
}); | ||
it('should not support matching a Buffer body with an object when the Content-Type is not application/json', function () { | ||
expect(new Message(new Buffer('Content-Type: text/plain\n\n{"the": "body"}', 'utf-8')).satisfies({body: {the: 'body'}}), 'to be false'); | ||
}); | ||
it('should support matching a Buffer body containing invalid JSON with an object when the Content-Type is application/json', function () { | ||
expect(new Message(new Buffer('Content-Type: application/json\n\n{"the": "body', 'utf-8')).satisfies({body: {the: 'body'}}), 'to be false'); | ||
}); | ||
it('should support matching a string body with an object when the Content-Type is application/json', function () { | ||
expect(new Message('Content-Type: application/json\n\n{"the": "body"}').satisfies({body: {the: 'body'}}), 'to be true'); | ||
}); | ||
it('should not support matching a string body with an object when the Content-Type is not application/json', function () { | ||
expect(new Message('Content-Type: text/plain\n\n{"the": "body"}').satisfies({body: {the: 'body'}}), 'to be false'); | ||
}); | ||
it('should support matching a string body containing invalid JSON with an object when the Content-Type is application/json', function () { | ||
expect(new Message('Content-Type: application/json\n\n{"the": "body').satisfies({body: {the: 'body'}}), 'to be false'); | ||
}); | ||
it('should support matching an object body with a string when the Content-Type is application/json', function () { | ||
expect(new Message({headers: 'Content-Type: application/json', body: {the: 'body'}}).satisfies({body: '{"the": "body"}'}), 'to be true'); | ||
}); | ||
it('should not support matching an object body with a string when the Content-Type is not application/json', function () { | ||
expect(new Message({headers: 'Content-Type: text/plain', body: {the: 'body'}}).satisfies({body: '{"the": "body"}'}), 'to be false'); | ||
}); | ||
it('should support matching an object body with a regular expression when the Content-Type is application/json', function () { | ||
expect(new Message({headers: 'Content-Type: application/json', body: {the: 'body'}}).satisfies({body: /he": "bod/}), 'to be true'); | ||
}); | ||
it('should support matching an object body with a string containing invalid JSON when the Content-Type is application/json', function () { | ||
expect(new Message({headers: 'Content-Type: application/json', body: {the: 'body'}}).satisfies({body: '{"the": "body'}), 'to be false'); | ||
}); | ||
it('should support matching an object body with a Buffer when the Content-Type is application/json', function () { | ||
expect(new Message({headers: 'Content-Type: application/json', body: {the: 'body'}}).satisfies({body: new Buffer('{"the": "body"}', 'utf-8')}), 'to be true'); | ||
}); | ||
it('should not support matching an object body with a Buffer when the Content-Type is not application/json', function () { | ||
expect(new Message({headers: 'Content-Type: text/plain', body: {the: 'body'}}).satisfies({body: new Buffer('{"the": "body"}', 'utf-8')}), 'to be false'); | ||
}); | ||
it('should support matching an object body with a Buffer containing invalid JSON when the Content-Type is application/json', function () { | ||
expect(new Message({headers: 'Content-Type: application/json', body: {the: 'body'}}).satisfies({body: new Buffer('{"the": "body', 'utf-8')}), 'to be false'); | ||
}); | ||
it('should support matching an object body (JSON) with an object', function () { | ||
expect(new Message({body: {foo: 'bar', bar: 'baz'}}).satisfies({body: {bar: 'baz', foo: 'bar'}}), 'to be true'); | ||
@@ -209,0 +285,0 @@ }); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
75437
19
1502