createerror
Version:
Helper for creating easily extensible and subclassable JavaScript Error classes.
168 lines (139 loc) • 7.11 kB
JavaScript
var expect = require('unexpected'),
createError = require('../lib/createError');
describe('createError', function () {
// https://github.com/joyent/node/issues/3212
it('should return "[object Error]" when Object.prototype.toString is called on it', function () {
var Err = createError({foo: 'bar'});
expect(Object.prototype.toString.call(new Err()), 'to equal', '[object Error]');
});
it('should default to the message provided in the options object when creating the class', function () {
var Err = createError({message: 'theDefaultMessage'});
expect(new Err().message, 'to equal', 'theDefaultMessage');
});
it('should include the default message in the stringification of the Error', function () {
var Err = createError({message: 'theDefaultMessage'});
expect(new Err().toString(), 'to equal', 'Error: theDefaultMessage');
});
it('should result a constructor that produces instances of itself and Error', function () {
var Err = createError({foo: 'bar'});
expect(new Err(), 'to be an', Err);
});
it('should result a constructor that produces instances whose constructor property point at it', function () {
var Err = createError({foo: 'bar'});
expect(new Err().constructor, 'to equal', Err);
});
it('should return a constructor that prefers a message passed as to first argument to the one provided in the options object', function () {
var Err = createError({message: 'default message'});
expect(new Err('overridden message').message, 'to equal', 'overridden message');
});
it('should return a constructor that produces instances of its SuperConstructor and Error', function () {
var SuperErr = createError({quux: 'baz'}),
Err = createError({foo: 'bar'}, SuperErr);
expect(new Err(), 'to be an', Error);
expect(new Err(), 'to be a', SuperErr);
});
it('should return a constructor that produces instances whose toString method return the expected value', function () {
var Err = createError({foo: 'bar', name: 'TheErrorName'});
expect(new Err('error message').toString(), 'to equal', 'TheErrorName: error message');
expect(new Err().toString(), 'to equal', 'TheErrorName');
});
it('should subclass TypeError (and thus other built-in Error subclasses) correctly', function () {
var Err = createError({foo: 'bar', name: 'TheErrorName'}, TypeError),
err = new Err('the error message');
expect(Object.prototype.toString.call(err), 'to equal', '[object Error]');
expect(err, 'to be a', TypeError);
expect(err, 'to be an', Error);
expect(err, 'to be an', Err);
});
it('should adopt arbitrary properties passed to a generated constructor', function () {
var Err = createError({foo: 'bar'}),
err = new Err();
expect(err.foo, 'to equal', 'bar');
var err2 = new Err({foo: 'quux'});
expect(err2.foo, 'to equal', 'quux');
// Make sure that top stack frame in err.stack points to this file rather than createError.js:
expect(err2.stack, 'to match', /test\/createError\.js:/);
});
it('should preserve the original stack trace and gain the properties of the Error class and its superclass when an existing Error instance is passed to a generated constructor', function () {
function Foo() {
return new Error('the original error');
}
var SuperError = createError({isSuper: true, name: 'Super'}),
Err = createError({name: 'SomethingMoreSpecific'}, SuperError),
err = new Err(Foo('blabla'));
expect(err.name, 'to equal', 'SomethingMoreSpecific');
expect(err.isSuper, 'to equal', true);
expect(err.SomethingMoreSpecific, 'to equal', true);
expect(err.stack, 'to match', /the original error/);
expect(err.stack, 'to match', /Foo/);
});
it('should produce a correct instance when a generated constructor is invoked without the new operator', function () {
var Err = createError(),
err = Err('message');
expect(err, 'to be an', Err);
// Make sure that top stack frame in err.stack points to this file rather than createError.js:
expect(err.stack.split("\n")[1], 'to match', /test\/createError\.js:/);
});
it('should mix the data object of the class with those of the instance', function () {
var Err = createError({data: {upstream: 'foo'}}),
err = Err({message: 'blah', data: {quux: 123}});
expect(err, 'to have properties', {
message: 'blah',
data: {
upstream: 'foo',
quux: 123
}
});
});
it('should not share the data object between instances', function () {
var classData = {hey: 'there'};
var Err = createError({data: classData}),
err1 = new Err();
expect(err1.data, 'to equal', {hey: 'there'});
expect(err1.data, 'not to be', classData);
err1.data.foo = 'bar';
var err2 = new Err();
expect(err2.data.foo, 'to be undefined');
});
it('should not alter an existing error', function () {
var classData = {hey: 'there'};
var LocalError = createError({name: 'LocalError', data: classData}),
SpecificLocalError = createError({name: 'SpecificLocalError'}, LocalError),
err = new Error('standard error');
// create a new local error passing in the original error
var localError = new SpecificLocalError(err);
// assert we added no keys to the orignal
expect(err, 'to only have keys', ['message']);
// assert the error was correctly extended
expect(localError, 'to have message', 'standard error');
expect(localError.name, 'to equal', 'SpecificLocalError');
expect(localError.data, 'to equal', classData);
});
it('should not break when the constructor options object has a data property with a value of undefined', function () {
var Err = createError();
new Err({data: undefined});
});
describe('with a preprocess parameter', function () {
it('should pass the constructor parameters to the preprocessor', function () {
var Err = createError({
preprocess: function (messageOrOptionsOrError) {
messageOrOptionsOrError.foo = 123;
return messageOrOptionsOrError;
}
});
expect(new Err({}), 'to satisfy', {
foo: 123
});
});
it('should keep the original options object if the preprocessor returns undefined', function () {
var Err = createError({
preprocess: function (messageOrOptionsOrError) {
return;
}
});
expect(new Err({ foo: 123 }), 'to satisfy', {
foo: 123
});
});
});
});