Skip to content
This repository has been archived by the owner on Nov 4, 2020. It is now read-only.

Commit

Permalink
Extract code specific for key-value and collection traversing to sepa…
Browse files Browse the repository at this point in the history
…rate place.

This should allow to easy extend should.js with any container types like immutable.js etc
  • Loading branch information
btd committed Aug 8, 2016
1 parent 3addf38 commit 73fc861
Show file tree
Hide file tree
Showing 17 changed files with 748 additions and 397 deletions.
1 change: 1 addition & 0 deletions .eslintrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ rules:
curly: 2
no-case-declarations: 0
no-console: 0
no-shadow: 1

global-require: 2
no-path-concat: 2
3 changes: 2 additions & 1 deletion lib/assertion-error.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
* Copyright(c) 2013-2016 Denis Bardadym <bardadymchik@gmail.com>
* MIT Licensed
*/
import { merge, format, functionName } from './util';
import { merge, functionName } from './util';
import { format } from './format';

/**
* should AssertionError
Expand Down
3 changes: 3 additions & 0 deletions lib/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@
*/

import format from 'should-format';
import { defaultTypeAdaptorStorage } from './iterator';

var config = {
typeAdaptors: defaultTypeAdaptorStorage,

getFormatter: function(opts) {
return new format.Formatter(opts || config);
}
Expand Down
4 changes: 2 additions & 2 deletions lib/ext/assert.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
* MIT Licensed
*/

import { merge, format } from '../util';
import { merge } from '../util';
import assert from './_assert';
import AssertionError from '../assertion-error';

export default function(should) {
var i = format;
var i = should.format;

/*
* Expose assert to should
Expand Down
42 changes: 25 additions & 17 deletions lib/ext/contain.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
* MIT Licensed
*/

import { format, isIndexable, forEach, some, isEmptyObject, length } from '../util';
import { isIterable, some, isEmpty, forEach, iterator } from '../iterator';

import eql from 'should-equal';

export default function(should, Assertion) {
var i = format;
var i = should.format;

/**
* Assert that given object contain something that equal to `other`. It uses `should-equal` for equality checks.
Expand All @@ -36,20 +37,22 @@ export default function(should, Assertion) {
* // expected { a: 10, c: { d: 10 } } to have property b
*/
Assertion.add('containEql', function(other) {
this.params = {operator: 'to contain ' + i(other)};
this.params = { operator: 'to contain ' + i(other) };

this.is.not.null().and.not.undefined();

var obj = this.obj;

if (typeof obj == 'string') {
this.assert(obj.indexOf(String(other)) >= 0);
} else if (isIndexable(obj)) {
} else if (isIterable(obj)) {
this.assert(some(obj, function(v) {
return eql(v, other).length === 0;
}));
} else {
this.have.properties(other);
forEach(other, function(value, key) {
should(obj).have.value(key, value);
}, this);
}
});

Expand Down Expand Up @@ -78,27 +81,32 @@ export default function(should, Assertion) {
var obj = this.obj;
if (typeof obj == 'string') {// expect other to be string
this.is.equal(String(other));
} else if (isIndexable(obj) && isIndexable(other)) {
for (var objIdx = 0, otherIdx = 0, objLength = length(obj), otherLength = length(other); objIdx < objLength && otherIdx < otherLength; objIdx++) {
} else if (isIterable(obj) && isIterable(other)) {
var objIterator = iterator(obj);
var otherIterator = iterator(other);

var nextObj = objIterator.next();
var nextOther = otherIterator.next();
while (!nextObj.done && !nextOther.done) {
try {
should(obj[objIdx]).containDeepOrdered(other[otherIdx]);
otherIdx++;
should(nextObj.value[1]).containDeepOrdered(nextOther.value[1]);
nextOther = otherIterator.next();
} catch (e) {
if (e instanceof should.AssertionError) {
continue;
if (!(e instanceof should.AssertionError)) {
throw e;
}
throw e;
}
nextObj = objIterator.next();
}

this.assert(otherIdx === otherLength);
} else if (obj != null && other != null && typeof obj == 'object' && typeof other == 'object') {// object contains object case
this.assert(nextOther.done);
} else if (obj != null && other != null && typeof obj == 'object' && typeof other == 'object') {//TODO compare types object contains object case
forEach(other, function(value, key) {
should(obj[key]).containDeepOrdered(value);
});

// if both objects is empty means we finish traversing - and we need to compare for hidden values
if (isEmptyObject(other)) {
if (isEmpty(other)) {
this.eql(other);
}
} else {
Expand All @@ -124,7 +132,7 @@ export default function(should, Assertion) {
var obj = this.obj;
if (typeof obj == 'string') {// expect other to be string
this.is.equal(String(other));
} else if (isIndexable(obj) && isIndexable(other)) {
} else if (isIterable(obj) && isIterable(other)) {
var usedKeys = {};
forEach(other, function(otherItem) {
this.assert(some(obj, function(item, index) {
Expand All @@ -150,7 +158,7 @@ export default function(should, Assertion) {
});

// if both objects is empty means we finish traversing - and we need to compare for hidden values
if (isEmptyObject(other)) {
if (isEmpty(other)) {
this.eql(other);
}
} else {
Expand Down
4 changes: 3 additions & 1 deletion lib/ext/eql.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@

import eql from 'should-equal';
import getType from 'should-type';
import { formatProp, format, forEach } from '../util';
import { formatProp, format } from '../format';
import { forEach } from '../iterator';

function formatEqlResult(r, a, b) {
return ((r.path.length > 0 ? 'at ' + r.path.map(formatProp).join(' -> ') : '') +
Expand All @@ -18,6 +19,7 @@ function formatEqlResult(r, a, b) {

export default function(should, Assertion) {


/**
* Deep object equality comparison. For full spec see [`should-equal tests`](https://github.com/shouldjs/equal/blob/master/test.js).
*
Expand Down
6 changes: 3 additions & 3 deletions lib/ext/error.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
* Copyright(c) 2013-2016 Denis Bardadym <bardadymchik@gmail.com>
* MIT Licensed
*/
import { format, functionName, isGeneratorObject, isGeneratorFunction } from '../util';
import { functionName, isIterator, isGeneratorFunction } from '../util';

export default function(should, Assertion) {
var i = format;
var i = should.format;

/**
* Assert given function throws error with such message.
Expand Down Expand Up @@ -41,7 +41,7 @@ export default function(should, Assertion) {

if (isGeneratorFunction(fn)) {
return should(fn()).throw(message, properties);
} else if (isGeneratorObject(fn)) {
} else if (isIterator(fn)) {
return should(fn.next.bind(fn)).throw(message, properties);
}

Expand Down
9 changes: 3 additions & 6 deletions lib/ext/match.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@
* MIT Licensed
*/

import { format, forEach, formatProp, some } from '../util';
import { formatProp } from '../format';
import { some, forEach } from '../iterator';
import eql from 'should-equal';

export default function(should, Assertion) {
var i = format;
var i = should.format;

/**
* Asserts if given object match `other` object, using some assumptions:
Expand Down Expand Up @@ -101,10 +102,6 @@ export default function(should, Assertion) {

res = other(this.obj);

//if(res instanceof Assertion) {
// this.params.operator += '\n ' + res.getMessage();
//}

//if we throw exception ok - it is used .should inside
if (typeof res == 'boolean') {
this.assert(res); // if it is just boolean function assert on it
Expand Down
73 changes: 26 additions & 47 deletions lib/ext/property.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
* MIT Licensed
*/

import { format, formatProp, convertPropertyName, length } from '../util';
import { convertPropertyName, hasOwnProperty } from '../util';
import { formatProp } from '../format';
import { isEmpty, has as hasKey, get as getValue, size } from '../iterator';
import eql from 'should-equal';

var aSlice = Array.prototype.slice;

export default function(should, Assertion) {
var i = format;
var i = should.format;
/**
* Asserts given object has some descriptor. **On success it change given object to be value of property**.
*
Expand Down Expand Up @@ -227,8 +229,6 @@ export default function(should, Assertion) {

Assertion.alias('length', 'lengthOf');

var hasOwnProperty = Object.prototype.hasOwnProperty;

/**
* Asserts given object has own property. **On success it change given object to be value of property**.
*
Expand All @@ -250,7 +250,7 @@ export default function(should, Assertion) {
message: description
};

this.assert(hasOwnProperty.call(this.obj, name));
this.assert(hasOwnProperty(this.obj, name));

this.obj = this.obj[name];
});
Expand All @@ -271,15 +271,7 @@ export default function(should, Assertion) {
*/
Assertion.add('empty', function() {
this.params = {operator: 'to be empty'};

if (length(this.obj) !== void 0) {
should(this.obj).have.property('length', 0);
} else {
var obj = Object(this.obj); // wrap to reference for booleans and numbers
for (var prop in obj) {
should(this.obj).not.have.ownProperty(prop);
}
}
this.assert(isEmpty(this.obj));
}, true);

/**
Expand All @@ -289,60 +281,47 @@ export default function(should, Assertion) {
* @alias Assertion#key
* @memberOf Assertion
* @category assertion property
* @param {Array|...string} [keys] Keys to check
* @param {...*} [keys] Keys to check
* @example
*
* ({ a: 10 }).should.have.keys('a');
* ({ a: 10, b: 20 }).should.have.keys('a', 'b');
* ({ a: 10, b: 20 }).should.have.keys([ 'a', 'b' ]);
* ({}).should.have.keys();
* (new Map([[1, 2]])).should.have.key(1);
*/
Assertion.add('keys', function(keys) {
if (arguments.length > 1) {
keys = aSlice.call(arguments);
} else if (arguments.length === 1 && typeof keys === 'string') {
keys = [keys];
} else if (arguments.length === 0) {
keys = [];
}

keys = keys.map(String);
keys = aSlice.call(arguments);

var obj = Object(this.obj);

// first check if some keys are missing
var missingKeys = [];
keys.forEach(function(key) {
if (!hasOwnProperty.call(this.obj, key)) {
missingKeys.push(formatProp(key));
}
}, this);

// second check for extra keys
var extraKeys = [];
Object.keys(obj).forEach(function(key) {
if (keys.indexOf(key) < 0) {
extraKeys.push(formatProp(key));
}
var missingKeys = keys.filter(function(key) {
return !hasKey(obj, key);
});

var verb = keys.length === 0 ? 'to be empty' :
'to have ' + (keys.length === 1 ? 'key ' : 'keys ');
var verb = 'to have ' + (keys.length === 1 ? 'key ' : 'keys ');

this.params = {operator: verb + keys.map(formatProp).join(', ')};
this.params = {operator: verb + keys.join(', ')};

if (missingKeys.length > 0) {
this.params.operator += '\n\tmissing keys: ' + missingKeys.join(', ');
}

if (extraKeys.length > 0) {
this.params.operator += '\n\textra keys: ' + extraKeys.join(', ');
}
this.assert(missingKeys.length === 0);
});

Assertion.add('key', function(key) {
this.have.keys(key);
this.obj = getValue(this.obj, key);
});

this.assert(missingKeys.length === 0 && extraKeys.length === 0);
Assertion.add('value', function(key, value) {
this.have.key(key).which.is.eql(value);
});

Assertion.alias("keys", "key");
Assertion.add('size', function(s) {
this.params = { operator: 'to have size ' + s };
size(this.obj).should.be.exactly(s);
});

/**
* Asserts given object has nested property in depth by path. **On success it change given object to be value of final property**.
Expand Down
10 changes: 10 additions & 0 deletions lib/format.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

import config from './config';

export function format(value, opts) {
return config.getFormatter(opts).format(value);
}

export function formatProp(value) {
return config.getFormatter().formatPropertyName(String(value));
}
Loading

0 comments on commit 73fc861

Please sign in to comment.