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

Commit 73fc861

Browse files
committed
Extract code specific for key-value and collection traversing to separate place.
This should allow to easy extend should.js with any container types like immutable.js etc
1 parent 3addf38 commit 73fc861

File tree

17 files changed

+748
-397
lines changed

17 files changed

+748
-397
lines changed

.eslintrc.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ rules:
3737
curly: 2
3838
no-case-declarations: 0
3939
no-console: 0
40+
no-shadow: 1
4041

4142
global-require: 2
4243
no-path-concat: 2

lib/assertion-error.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
* Copyright(c) 2013-2016 Denis Bardadym <[email protected]>
55
* MIT Licensed
66
*/
7-
import { merge, format, functionName } from './util';
7+
import { merge, functionName } from './util';
8+
import { format } from './format';
89

910
/**
1011
* should AssertionError

lib/config.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66
*/
77

88
import format from 'should-format';
9+
import { defaultTypeAdaptorStorage } from './iterator';
910

1011
var config = {
12+
typeAdaptors: defaultTypeAdaptorStorage,
13+
1114
getFormatter: function(opts) {
1215
return new format.Formatter(opts || config);
1316
}

lib/ext/assert.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55
* MIT Licensed
66
*/
77

8-
import { merge, format } from '../util';
8+
import { merge } from '../util';
99
import assert from './_assert';
1010
import AssertionError from '../assertion-error';
1111

1212
export default function(should) {
13-
var i = format;
13+
var i = should.format;
1414

1515
/*
1616
* Expose assert to should

lib/ext/contain.js

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55
* MIT Licensed
66
*/
77

8-
import { format, isIndexable, forEach, some, isEmptyObject, length } from '../util';
8+
import { isIterable, some, isEmpty, forEach, iterator } from '../iterator';
9+
910
import eql from 'should-equal';
1011

1112
export default function(should, Assertion) {
12-
var i = format;
13+
var i = should.format;
1314

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

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

4344
var obj = this.obj;
4445

4546
if (typeof obj == 'string') {
4647
this.assert(obj.indexOf(String(other)) >= 0);
47-
} else if (isIndexable(obj)) {
48+
} else if (isIterable(obj)) {
4849
this.assert(some(obj, function(v) {
4950
return eql(v, other).length === 0;
5051
}));
5152
} else {
52-
this.have.properties(other);
53+
forEach(other, function(value, key) {
54+
should(obj).have.value(key, value);
55+
}, this);
5356
}
5457
});
5558

@@ -78,27 +81,32 @@ export default function(should, Assertion) {
7881
var obj = this.obj;
7982
if (typeof obj == 'string') {// expect other to be string
8083
this.is.equal(String(other));
81-
} else if (isIndexable(obj) && isIndexable(other)) {
82-
for (var objIdx = 0, otherIdx = 0, objLength = length(obj), otherLength = length(other); objIdx < objLength && otherIdx < otherLength; objIdx++) {
84+
} else if (isIterable(obj) && isIterable(other)) {
85+
var objIterator = iterator(obj);
86+
var otherIterator = iterator(other);
87+
88+
var nextObj = objIterator.next();
89+
var nextOther = otherIterator.next();
90+
while (!nextObj.done && !nextOther.done) {
8391
try {
84-
should(obj[objIdx]).containDeepOrdered(other[otherIdx]);
85-
otherIdx++;
92+
should(nextObj.value[1]).containDeepOrdered(nextOther.value[1]);
93+
nextOther = otherIterator.next();
8694
} catch (e) {
87-
if (e instanceof should.AssertionError) {
88-
continue;
95+
if (!(e instanceof should.AssertionError)) {
96+
throw e;
8997
}
90-
throw e;
9198
}
99+
nextObj = objIterator.next();
92100
}
93101

94-
this.assert(otherIdx === otherLength);
95-
} else if (obj != null && other != null && typeof obj == 'object' && typeof other == 'object') {// object contains object case
102+
this.assert(nextOther.done);
103+
} else if (obj != null && other != null && typeof obj == 'object' && typeof other == 'object') {//TODO compare types object contains object case
96104
forEach(other, function(value, key) {
97105
should(obj[key]).containDeepOrdered(value);
98106
});
99107

100108
// if both objects is empty means we finish traversing - and we need to compare for hidden values
101-
if (isEmptyObject(other)) {
109+
if (isEmpty(other)) {
102110
this.eql(other);
103111
}
104112
} else {
@@ -124,7 +132,7 @@ export default function(should, Assertion) {
124132
var obj = this.obj;
125133
if (typeof obj == 'string') {// expect other to be string
126134
this.is.equal(String(other));
127-
} else if (isIndexable(obj) && isIndexable(other)) {
135+
} else if (isIterable(obj) && isIterable(other)) {
128136
var usedKeys = {};
129137
forEach(other, function(otherItem) {
130138
this.assert(some(obj, function(item, index) {
@@ -150,7 +158,7 @@ export default function(should, Assertion) {
150158
});
151159

152160
// if both objects is empty means we finish traversing - and we need to compare for hidden values
153-
if (isEmptyObject(other)) {
161+
if (isEmpty(other)) {
154162
this.eql(other);
155163
}
156164
} else {

lib/ext/eql.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77

88
import eql from 'should-equal';
99
import getType from 'should-type';
10-
import { formatProp, format, forEach } from '../util';
10+
import { formatProp, format } from '../format';
11+
import { forEach } from '../iterator';
1112

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

1920
export default function(should, Assertion) {
2021

22+
2123
/**
2224
* Deep object equality comparison. For full spec see [`should-equal tests`](https://github.com/shouldjs/equal/blob/master/test.js).
2325
*

lib/ext/error.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
44
* Copyright(c) 2013-2016 Denis Bardadym <[email protected]>
55
* MIT Licensed
66
*/
7-
import { format, functionName, isGeneratorObject, isGeneratorFunction } from '../util';
7+
import { functionName, isIterator, isGeneratorFunction } from '../util';
88

99
export default function(should, Assertion) {
10-
var i = format;
10+
var i = should.format;
1111

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

4242
if (isGeneratorFunction(fn)) {
4343
return should(fn()).throw(message, properties);
44-
} else if (isGeneratorObject(fn)) {
44+
} else if (isIterator(fn)) {
4545
return should(fn.next.bind(fn)).throw(message, properties);
4646
}
4747

lib/ext/match.js

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55
* MIT Licensed
66
*/
77

8-
import { format, forEach, formatProp, some } from '../util';
8+
import { formatProp } from '../format';
9+
import { some, forEach } from '../iterator';
910
import eql from 'should-equal';
1011

1112
export default function(should, Assertion) {
12-
var i = format;
13+
var i = should.format;
1314

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

102103
res = other(this.obj);
103104

104-
//if(res instanceof Assertion) {
105-
// this.params.operator += '\n ' + res.getMessage();
106-
//}
107-
108105
//if we throw exception ok - it is used .should inside
109106
if (typeof res == 'boolean') {
110107
this.assert(res); // if it is just boolean function assert on it

lib/ext/property.js

Lines changed: 26 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
* MIT Licensed
66
*/
77

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

1113
var aSlice = Array.prototype.slice;
1214

1315
export default function(should, Assertion) {
14-
var i = format;
16+
var i = should.format;
1517
/**
1618
* Asserts given object has some descriptor. **On success it change given object to be value of property**.
1719
*
@@ -227,8 +229,6 @@ export default function(should, Assertion) {
227229

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

230-
var hasOwnProperty = Object.prototype.hasOwnProperty;
231-
232232
/**
233233
* Asserts given object has own property. **On success it change given object to be value of property**.
234234
*
@@ -250,7 +250,7 @@ export default function(should, Assertion) {
250250
message: description
251251
};
252252

253-
this.assert(hasOwnProperty.call(this.obj, name));
253+
this.assert(hasOwnProperty(this.obj, name));
254254

255255
this.obj = this.obj[name];
256256
});
@@ -271,15 +271,7 @@ export default function(should, Assertion) {
271271
*/
272272
Assertion.add('empty', function() {
273273
this.params = {operator: 'to be empty'};
274-
275-
if (length(this.obj) !== void 0) {
276-
should(this.obj).have.property('length', 0);
277-
} else {
278-
var obj = Object(this.obj); // wrap to reference for booleans and numbers
279-
for (var prop in obj) {
280-
should(this.obj).not.have.ownProperty(prop);
281-
}
282-
}
274+
this.assert(isEmpty(this.obj));
283275
}, true);
284276

285277
/**
@@ -289,60 +281,47 @@ export default function(should, Assertion) {
289281
* @alias Assertion#key
290282
* @memberOf Assertion
291283
* @category assertion property
292-
* @param {Array|...string} [keys] Keys to check
284+
* @param {...*} [keys] Keys to check
293285
* @example
294286
*
295287
* ({ a: 10 }).should.have.keys('a');
296288
* ({ a: 10, b: 20 }).should.have.keys('a', 'b');
297-
* ({ a: 10, b: 20 }).should.have.keys([ 'a', 'b' ]);
298-
* ({}).should.have.keys();
289+
* (new Map([[1, 2]])).should.have.key(1);
299290
*/
300291
Assertion.add('keys', function(keys) {
301-
if (arguments.length > 1) {
302-
keys = aSlice.call(arguments);
303-
} else if (arguments.length === 1 && typeof keys === 'string') {
304-
keys = [keys];
305-
} else if (arguments.length === 0) {
306-
keys = [];
307-
}
308-
309-
keys = keys.map(String);
292+
keys = aSlice.call(arguments);
310293

311294
var obj = Object(this.obj);
312295

313296
// first check if some keys are missing
314-
var missingKeys = [];
315-
keys.forEach(function(key) {
316-
if (!hasOwnProperty.call(this.obj, key)) {
317-
missingKeys.push(formatProp(key));
318-
}
319-
}, this);
320-
321-
// second check for extra keys
322-
var extraKeys = [];
323-
Object.keys(obj).forEach(function(key) {
324-
if (keys.indexOf(key) < 0) {
325-
extraKeys.push(formatProp(key));
326-
}
297+
var missingKeys = keys.filter(function(key) {
298+
return !hasKey(obj, key);
327299
});
328300

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

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

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

338-
if (extraKeys.length > 0) {
339-
this.params.operator += '\n\textra keys: ' + extraKeys.join(', ');
340-
}
309+
this.assert(missingKeys.length === 0);
310+
});
311+
312+
Assertion.add('key', function(key) {
313+
this.have.keys(key);
314+
this.obj = getValue(this.obj, key);
315+
});
341316

342-
this.assert(missingKeys.length === 0 && extraKeys.length === 0);
317+
Assertion.add('value', function(key, value) {
318+
this.have.key(key).which.is.eql(value);
343319
});
344320

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

347326
/**
348327
* Asserts given object has nested property in depth by path. **On success it change given object to be value of final property**.

lib/format.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
import config from './config';
3+
4+
export function format(value, opts) {
5+
return config.getFormatter(opts).format(value);
6+
}
7+
8+
export function formatProp(value) {
9+
return config.getFormatter().formatPropertyName(String(value));
10+
}

0 commit comments

Comments
 (0)