All new
alle new
This commit is contained in:
1 parent
da64472251
commit
5b20adee48
1605 files changed
+396517
-1674
No files matched your search
+22
@@ -0,0 +1,22 @@
|
||||
Copyright (c) 2013, Michael Williamson
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
# duck.js -- rich matchers with helpful messages on match failure
|
||||
|
||||
duck.js allows you to perform assertions on complex objects.
|
||||
When those assertions fail, duck.js will try to produce helpful error messages.
|
||||
For instance, suppose you want to assert the same property on an array of objects:
|
||||
|
||||
```javascript
|
||||
var duck = require("duck");
|
||||
var isArray = duck.isArray;
|
||||
var hasProperties = duck.hasProperties;
|
||||
|
||||
var users = fetchUsers();
|
||||
duck.assertThat(users, isArray([
|
||||
hasProperties({name: "Bob"}),
|
||||
hasProperties({name: "Jim"}),
|
||||
]));
|
||||
```
|
||||
|
||||
which might produce an error message like:
|
||||
|
||||
```
|
||||
Expected [object with properties {
|
||||
name: 'Bob'
|
||||
}, object with properties {
|
||||
name: 'Jim'
|
||||
}]
|
||||
but element at index 0 didn't match:
|
||||
value of property "name" didn't match:
|
||||
was 'Jim'
|
||||
expected 'Bob'
|
||||
expected object with properties {
|
||||
name: 'Bob'
|
||||
}
|
||||
element at index 1 didn't match:
|
||||
value of property "name" didn't match:
|
||||
was 'Bob'
|
||||
expected 'Jim'
|
||||
expected object with properties {
|
||||
name: 'Jim'
|
||||
}
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
The below is a quick reference to the API.
|
||||
For more examples, take a look at the tests.
|
||||
|
||||
### duck.assertThat(value, matcher)
|
||||
|
||||
Assert that `value` satifies `matcher`.
|
||||
|
||||
If `value` satifies `matcher`, return normally, otherwise throw an
|
||||
AssertionError describing the mismatch.
|
||||
|
||||
### duck.is(value)
|
||||
|
||||
If `value` is a matcher, return that matcher,
|
||||
otherwise return `duck.equalTo(value)`.
|
||||
|
||||
### duck.equalTo(value)
|
||||
|
||||
Matcher for deep equality on `value`.
|
||||
|
||||
### duck.isObject(matcherObj)
|
||||
|
||||
An object `obj` matches `duck.isObject(matcherObj)` if:
|
||||
|
||||
* `obj` matches `duck.hasProperties(matcherObj)`, and
|
||||
* there is no key that is present in `obj` but not in `matcherObj`
|
||||
|
||||
Sample usage:
|
||||
|
||||
```javascript
|
||||
duck.isObject({
|
||||
name: "Bob",
|
||||
address: duck.isObject({
|
||||
city: "Cambridge",
|
||||
county: "UK"
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
`duck.is` is called on each value of the matcher object, meaning that the
|
||||
above is equivalent to:
|
||||
|
||||
```javascript
|
||||
duck.isObject({
|
||||
name: duck.is("Bob"),
|
||||
address: duck.isObject({
|
||||
city: duck.is("Cambridge"),
|
||||
county: duck.is("UK")
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### duck.hasProperties(matcherProperties)
|
||||
|
||||
An object `obj` matches `duck.hasProperties(matcherProperties)` if,
|
||||
for each `key` in `matcherProperties`, `matcherProperties[key].matches(obj[key])`
|
||||
|
||||
Sample usage:
|
||||
|
||||
```javascript
|
||||
duck.hasProperties({
|
||||
name: "Bob",
|
||||
address: duck.hasProperties({
|
||||
city: "Cambridge",
|
||||
county: "UK"
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
`duck.is` is called on each value of the matcher object, meaning that the
|
||||
above is equivalent to:
|
||||
|
||||
```javascript
|
||||
duck.hasProperties({
|
||||
name: duck.is("Bob"),
|
||||
address: duck.hasProperties({
|
||||
city: duck.is("Cambridge"),
|
||||
county: duck.is("UK")
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
### duck.isArray(matcherArray)
|
||||
|
||||
An array `blah` matches `duck.isArray(matcherArray)` if:
|
||||
|
||||
* `blah.length == matcherArray.length`, and
|
||||
* For `0 <= i < array.length`, `matcherArray[i].matches(blah[i])`
|
||||
|
||||
Sample usage:
|
||||
|
||||
```javascript
|
||||
duck.isArray([
|
||||
duck.hasProperties({name: "Bob"}),
|
||||
duck.hasProperties({name: "Jim"}),
|
||||
]))
|
||||
```
|
||||
|
||||
`duck.is` is called on each element of the matcher array, meaning that the
|
||||
following are equivalent:
|
||||
|
||||
```javascript
|
||||
duck.isArray(["apple", "banana"])
|
||||
|
||||
duck.isArray([duck.is("apple"), duck.is("banana")])
|
||||
```
|
||||
|
||||
### Matcher
|
||||
|
||||
Each matcher has the following methods:
|
||||
|
||||
#### matcher.matches(value)
|
||||
|
||||
Return `true` if `value` satifies this matcher, false otherwise.
|
||||
|
||||
#### matcher.describeMismatch(value)
|
||||
|
||||
Generate a string describing why `value` doesn't satisfy this matcher.
|
||||
Behaviour is undefined if `value` actually satisifies the matcher.
|
||||
|
||||
#### matcher.matchesWithDescription(value)
|
||||
|
||||
Equivalent to:
|
||||
|
||||
```javascript
|
||||
var isMatch = this.matches(value);
|
||||
return {
|
||||
matches: isMatch,
|
||||
description: isMatch ? "" : this.describeMismatch(value)
|
||||
};
|
||||
```
|
||||
|
||||
Useful if you're likely to want both the boolean and the mismatch description.
|
||||
|
||||
#### matcher.describeSelf()
|
||||
|
||||
Generate a string describing the matcher.
|
||||
|
||||
## Thanks
|
||||
|
||||
Thanks to [Hamcrest](http://hamcrest.org/) for inspiration.
|
||||
+267
@@ -0,0 +1,267 @@
|
||||
var util = require("util");
|
||||
var assert = require("assert");
|
||||
|
||||
var _ = require("underscore");
|
||||
|
||||
|
||||
var inspect = function(value) {
|
||||
return util.inspect(value, false, null);
|
||||
};
|
||||
|
||||
exports.assertThat = function(value, matcher) {
|
||||
var result = matcher.matchesWithDescription(value);
|
||||
var message = "Expected " + matcher.describeSelf() +
|
||||
"\nbut " + result.description;
|
||||
assert.ok(result.matches, message);
|
||||
};
|
||||
|
||||
exports.is = function(value) {
|
||||
if (value && value._isDuckMatcher) {
|
||||
return value;
|
||||
} else {
|
||||
return equalTo(value);
|
||||
}
|
||||
};
|
||||
|
||||
var equalTo = exports.equalTo = function(matchValue) {
|
||||
return new Matcher({
|
||||
matches: function(value) {
|
||||
return _.isEqual(value, matchValue);
|
||||
},
|
||||
describeMismatch: function(value) {
|
||||
return "was " + inspect(value);
|
||||
},
|
||||
describeSelf: function() {
|
||||
return inspect(matchValue);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.isObject = function(object) {
|
||||
var matchers = valuesToMatchers(object);
|
||||
|
||||
return new Matcher({
|
||||
matchesWithDescription: function(value) {
|
||||
var expectedKeys = ownKeys(object);
|
||||
var hasPropertiesResult = exports.hasProperties(matchers).matchesWithDescription(value);
|
||||
|
||||
var unexpectedPropertyMismatches = ownKeys(value).filter(function(key) {
|
||||
return expectedKeys.indexOf(key) === -1
|
||||
}).map(function(key) {
|
||||
return "unexpected property: \"" + key + "\"";
|
||||
});
|
||||
|
||||
var mismatchDescriptions =
|
||||
(hasPropertiesResult.matches ? [] : [hasPropertiesResult.description])
|
||||
.concat(unexpectedPropertyMismatches);
|
||||
|
||||
if (mismatchDescriptions.length === 0) {
|
||||
return {matches: true};
|
||||
} else {
|
||||
return {matches: false, description: mismatchDescriptions.join("\n")};
|
||||
}
|
||||
},
|
||||
describeSelf: function() {
|
||||
return formatObjectOfMatchers(matchers);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.hasProperties = function(object) {
|
||||
var matchers = valuesToMatchers(object);
|
||||
|
||||
return new Matcher({
|
||||
matchesWithDescription: function(value) {
|
||||
var expectedKeys = ownKeys(object);
|
||||
expectedKeys.sort(function(first, second) {
|
||||
if (first < second) {
|
||||
return -1;
|
||||
} else if (first > second) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
var propertyResults = expectedKeys.map(function(key) {
|
||||
var propertyMatcher = matchers[key];
|
||||
if (!objectHasOwnProperty(value, key)) {
|
||||
return {matches: false, description: util.format("missing property: \"%s\"", key)};
|
||||
} else if (!propertyMatcher.matches(value[key])) {
|
||||
var description = "value of property \"" + key + "\" didn't match:\n" +
|
||||
" " + indent(propertyMatcher.describeMismatch(value[key]), 1) + "\n" +
|
||||
" expected " + indent(propertyMatcher.describeSelf(), 1);
|
||||
return {matches: false, description: description};
|
||||
} else {
|
||||
return {matches: true};
|
||||
}
|
||||
});
|
||||
|
||||
return combineMatchResults(propertyResults);
|
||||
},
|
||||
describeSelf: function() {
|
||||
return "object with properties " + formatObjectOfMatchers(matchers);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.isArray = function(expectedArray) {
|
||||
var elementMatchers = expectedArray.map(exports.is);
|
||||
return new Matcher({
|
||||
matchesWithDescription: function(value) {
|
||||
if (value.length !== elementMatchers.length) {
|
||||
return {matches: false, description: "was of length " + value.length};
|
||||
} else {
|
||||
var elementResults = _.zip(elementMatchers, value).map(function(values, index) {
|
||||
var expectedMatcher = values[0];
|
||||
var actual = values[1];
|
||||
if (expectedMatcher.matches(actual)) {
|
||||
return {matches: true};
|
||||
} else {
|
||||
var description = "element at index " + index + " didn't match:\n " + indent(expectedMatcher.describeMismatch(actual), 1)
|
||||
+ "\n expected " + indent(expectedMatcher.describeSelf(), 1);
|
||||
return {matches: false, description: description};
|
||||
}
|
||||
});
|
||||
|
||||
return combineMatchResults(elementResults);
|
||||
}
|
||||
},
|
||||
describeSelf: function() {
|
||||
return util.format("[%s]", _.invoke(elementMatchers, "describeSelf").join(", "));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var Matcher = function(matcher) {
|
||||
this._matcher = matcher;
|
||||
this._isDuckMatcher = true;
|
||||
};
|
||||
|
||||
Matcher.prototype.matches = function(value) {
|
||||
if (this._matcher.matches) {
|
||||
return this._matcher.matches(value);
|
||||
} else {
|
||||
return this._matcher.matchesWithDescription(value).matches;
|
||||
}
|
||||
};
|
||||
|
||||
Matcher.prototype.describeMismatch = function(value) {
|
||||
if (this._matcher.describeMismatch) {
|
||||
return this._matcher.describeMismatch(value);
|
||||
} else {
|
||||
return this._matcher.matchesWithDescription(value).description;
|
||||
}
|
||||
};
|
||||
|
||||
Matcher.prototype.matchesWithDescription = function(value) {
|
||||
if (this._matcher.matchesWithDescription) {
|
||||
var result = this._matcher.matchesWithDescription(value);
|
||||
if (result.matches) {
|
||||
return {
|
||||
matches: true,
|
||||
description: ""
|
||||
};
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
var isMatch = this.matches(value);
|
||||
return {
|
||||
matches: isMatch,
|
||||
description: isMatch ? "" : this.describeMismatch(value)
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
Matcher.prototype.describeSelf = function() {
|
||||
return this._matcher.describeSelf();
|
||||
};
|
||||
|
||||
var combineMatchResults = function(results) {
|
||||
var mismatches = results.filter(function(result) {
|
||||
return !result.matches;
|
||||
});
|
||||
return combineMismatchs(mismatches);
|
||||
};
|
||||
|
||||
var combineMismatchs = function(mismatches) {
|
||||
if (mismatches.length === 0) {
|
||||
return {matches: true};
|
||||
} else {
|
||||
var mismatchDescriptions = mismatches.map(function(mismatch) {
|
||||
return mismatch.description;
|
||||
});
|
||||
return {matches: false, description: mismatchDescriptions.join("\n")};
|
||||
}
|
||||
};
|
||||
|
||||
var ownKeys = function(obj) {
|
||||
var keys = [];
|
||||
for (var key in obj) {
|
||||
if (objectHasOwnProperty(obj, key)) {
|
||||
keys.push(key);
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
};
|
||||
|
||||
var objectHasOwnProperty = function(obj, key) {
|
||||
return Object.prototype.hasOwnProperty.call(obj, key);
|
||||
};
|
||||
|
||||
var objectMap = function(obj, func) {
|
||||
var matchers = {};
|
||||
_.forEach(obj, function(value, key) {
|
||||
if (_.has(obj, key)) {
|
||||
matchers[key] = func(value, key);
|
||||
}
|
||||
});
|
||||
return matchers;
|
||||
|
||||
};
|
||||
|
||||
var valuesToMatchers = function(obj) {
|
||||
return objectMap(obj, exports.is);
|
||||
};
|
||||
|
||||
var formatObject = function(obj) {
|
||||
if (_.size(obj) === 0) {
|
||||
return "{}";
|
||||
} else {
|
||||
return util.format("{%s\n}", formatProperties(obj));
|
||||
}
|
||||
};
|
||||
|
||||
var formatProperties = function(obj) {
|
||||
var properties = _.map(obj, function(value, key) {
|
||||
return {key: key, value: value};
|
||||
});
|
||||
var sortedProperties = _.sortBy(properties, function(property) {
|
||||
return property.key;
|
||||
});
|
||||
return "\n " + sortedProperties.map(function(property) {
|
||||
return indent(property.key + ": " + property.value, 1);
|
||||
}).join(",\n ");
|
||||
};
|
||||
|
||||
var formatObjectOfMatchers = function(matchers) {
|
||||
return formatObject(objectMap(matchers, function(matcher) {
|
||||
return matcher.describeSelf();
|
||||
}));
|
||||
};
|
||||
|
||||
var indent = function(str, indentationLevel) {
|
||||
var indentation = _.range(indentationLevel).map(function() {
|
||||
return " ";
|
||||
}).join("");
|
||||
return str.replace(/\n/g, "\n" + indentation);
|
||||
};
|
||||
|
||||
exports.any = new Matcher({
|
||||
matchesWithDescription: function() {
|
||||
return {matches: true};
|
||||
},
|
||||
describeSelf: function() {
|
||||
return "<any>";
|
||||
}
|
||||
});
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
.PHONY: test
|
||||
|
||||
test:
|
||||
node_modules/.bin/nodeunit `find test -name \*.test.js -type f`
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "duck",
|
||||
"version": "0.1.12",
|
||||
"author": "Michael Williamson <mike@zwobble.org>",
|
||||
"description": "Rich matchers inspired by Hamcrest. Useful for generating helpful assertion failure messages in tests.",
|
||||
"license": "BSD",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mwilliamson/duck.js.git"
|
||||
},
|
||||
"keywords": [
|
||||
"test",
|
||||
"assert",
|
||||
"matcher",
|
||||
"assertThat"
|
||||
],
|
||||
"dependencies": {
|
||||
"underscore": "^1.13.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodeunit": "~0.8.0"
|
||||
}
|
||||
}
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
var duck = require("../");
|
||||
|
||||
var testMatcher = function(options) {
|
||||
return function(test) {
|
||||
var matcher = options.matcher;
|
||||
var description = options.description;
|
||||
var positives = options.positives;
|
||||
var negatives = options.negatives;
|
||||
|
||||
test.equal(matcher.describeSelf(), description);
|
||||
|
||||
positives.forEach(function(positive) {
|
||||
test.same(true, matcher.matches(positive));
|
||||
var result = matcher.matchesWithDescription(positive);
|
||||
test.same(true, result.matches);
|
||||
test.same("", result.description);
|
||||
});
|
||||
negatives.forEach(function(negative) {
|
||||
test.same(false, matcher.matches(negative.value));
|
||||
test.same(negative.description, matcher.describeMismatch(negative.value));
|
||||
var result = matcher.matchesWithDescription(negative.value);
|
||||
test.same(false, result.matches);
|
||||
test.same(negative.description, result.description);
|
||||
});
|
||||
|
||||
test.done();
|
||||
};
|
||||
};
|
||||
|
||||
exports.isMatchesPrimitiveValues = testMatcher({
|
||||
matcher: duck.is(1),
|
||||
description: "1",
|
||||
positives: [1],
|
||||
negatives: [
|
||||
{value: 2, description: "was 2"},
|
||||
{value: "1", description: "was '1'"},
|
||||
{value: null, description: "was null"}
|
||||
]
|
||||
});
|
||||
|
||||
exports.isMatchesObjectUsingIsEqual = testMatcher({
|
||||
matcher: duck.is({}),
|
||||
description: "{}",
|
||||
positives: [{}],
|
||||
negatives: [
|
||||
{value: {hair: "none"}, description: "was { hair: 'none' }"}
|
||||
]
|
||||
});
|
||||
|
||||
exports.isDoesNothingToMatchers = testMatcher({
|
||||
matcher: duck.is(duck.is(1)),
|
||||
description: "1",
|
||||
positives: [1],
|
||||
negatives: [
|
||||
{value: 2, description: "was 2"},
|
||||
{value: "1", description: "was '1'"},
|
||||
{value: null, description: "was null"}
|
||||
]
|
||||
});
|
||||
|
||||
exports.isObjectMatchesValuesExactly = testMatcher({
|
||||
matcher: duck.isObject({
|
||||
name: "Bob",
|
||||
age: 24
|
||||
}),
|
||||
description: "{\n age: 24,\n name: 'Bob'\n}",
|
||||
positives: [{name: "Bob", age: 24}],
|
||||
negatives: [
|
||||
{value: {name: "Bob"}, description: "missing property: \"age\""},
|
||||
{value: {}, description: "missing property: \"age\"\nmissing property: \"name\""},
|
||||
{value: {name: "bob", age: 24}, description: "value of property \"name\" didn't match:\n was 'bob'\n expected 'Bob'"},
|
||||
{value: {name: "Bob", age: 24, hair: "none"}, description: "unexpected property: \"hair\""}
|
||||
]
|
||||
});
|
||||
|
||||
exports.isObjectSubDescriptionsAreIndented = testMatcher({
|
||||
matcher: duck.isObject({artist: duck.isObject({name: "Bob"})}),
|
||||
description: "{\n artist: {\n name: 'Bob'\n }\n}",
|
||||
positives: [],
|
||||
negatives: [
|
||||
{
|
||||
value: {artist: {name: "Jim"}},
|
||||
description:
|
||||
"value of property \"artist\" didn't match:\n" +
|
||||
" value of property \"name\" didn't match:\n" +
|
||||
" was 'Jim'\n" +
|
||||
" expected 'Bob'\n" +
|
||||
" expected {\n" +
|
||||
" name: 'Bob'\n" +
|
||||
" }"
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
exports.hasPropertiesBehavesAsIsObjectExceptIgnoresUnexpectedValues = testMatcher({
|
||||
matcher: duck.hasProperties({
|
||||
name: "Bob",
|
||||
age: 24
|
||||
}),
|
||||
description: "object with properties {\n age: 24,\n name: 'Bob'\n}",
|
||||
positives: [{name: "Bob", age: 24}, {name: "Bob", age: 24, hair: "none"}],
|
||||
negatives: [
|
||||
{value: {name: "Bob"}, description: "missing property: \"age\""},
|
||||
{value: {}, description: "missing property: \"age\"\nmissing property: \"name\""},
|
||||
{value: {name: "bob", age: 24}, description: "value of property \"name\" didn't match:\n was 'bob'\n expected 'Bob'"}
|
||||
]
|
||||
});
|
||||
|
||||
exports.isArrayMatchesLengthAndIndividualElements = testMatcher({
|
||||
matcher: duck.isArray(["apple", "banana"]),
|
||||
description: "['apple', 'banana']",
|
||||
positives: [["apple", "banana"]],
|
||||
negatives: [
|
||||
{value: [], description: "was of length 0"},
|
||||
{value: ["apple", "banana", "coconut"], description: "was of length 3"},
|
||||
{value: ["apple", "coconut",], description: "element at index 1 didn't match:\n was 'coconut'\n expected 'banana'"}
|
||||
]
|
||||
});
|
||||
|
||||
exports.isArraySubDescriptionsAreIndented = testMatcher({
|
||||
matcher: duck.isArray([duck.isObject({name: "Bob"})]),
|
||||
description: "[{\n name: 'Bob'\n}]",
|
||||
positives: [[{name: "Bob"}]],
|
||||
negatives: [
|
||||
{
|
||||
value: [{name: "Jim"}],
|
||||
description:
|
||||
"element at index 0 didn't match:\n" +
|
||||
" value of property \"name\" didn't match:\n" +
|
||||
" was 'Jim'\n" +
|
||||
" expected 'Bob'\n" +
|
||||
" expected {\n" +
|
||||
" name: 'Bob'\n" +
|
||||
" }"
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
exports.isArraySubDescriptionsAreIndented = testMatcher({
|
||||
matcher: duck.any,
|
||||
description: "<any>",
|
||||
positives: [{name: "Bob"}, null, undefined, 0, 1, "Bob", []],
|
||||
negatives: []
|
||||
});
|
||||
|
||||
exports.assertReturnsNormallyOnSuccess = function(test) {
|
||||
duck.assertThat(42, duck.is(42));
|
||||
test.done();
|
||||
};
|
||||
|
||||
exports.assertRaisesErrorIfAssertionFails = function(test) {
|
||||
try {
|
||||
duck.assertThat(42, duck.is(43));
|
||||
test.fail("Should throw error");
|
||||
} catch (error) {
|
||||
test.equal(error.name, "AssertionError");
|
||||
test.equal(error.message, "Expected 43\nbut was 42");
|
||||
}
|
||||
test.done();
|
||||
};
|
||||
Reference in new issue
Block a user