Skip to content

Commit

Permalink
create CustomError to refactor other Error classes
Browse files Browse the repository at this point in the history
  • Loading branch information
Rémi Becheras committed Jan 2, 2017
1 parent aab7947 commit d8b2e66
Show file tree
Hide file tree
Showing 2 changed files with 210 additions and 0 deletions.
98 changes: 98 additions & 0 deletions lib/errors/CustomError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
'use strict'

var path = require('path')
var PrivateMethodError = require('./PrivateMethodError')

module.exports = CustomError

/**
* CustomError
* @class
* @mixin
* @property {String} message The error message
* @property {String} stack The error stack trace
*
* @constructor
* @param {String} [msg] The error message
*/
function CustomError (msg) {
// Error.call(this, msg)
if (msg && typeof msg === 'string') {
this.message = msg
} else if (this.constructor.name !== 'CustomError') {
this.message = global[this.constructor.name].DEFAULT_ERROR_MESSAGE
} else {
this.message = undefined
}
this.createStackTrace()
}

CustomError.prototype = Object.create(Error.prototype)
CustomError.prototype.constructor = CustomError
CustomError.prototype.createStackTrace = createStackTrace
CustomError.prototype.protect = protect
CustomError.prototype.privatize = privatize

/**
* Set the the correct stack trace for this error
* @method
* @returns {undefined}
* @throws {PrivateMethodError}
*/
function createStackTrace () {
privatize()
var stack = new Error().stack
var splited = stack.split('\n')
var modifiedStack = splited[0].concat('\n', splited.splice(3).join('\n'))
this.stack = modifiedStack
}

/**
* Throws a PrivateMethodError if the caller is not CustomError
* @function
* @inner
* @returns {undefined}
* @throws {PrivateMethodError}
*/
function privatize () {
var trace = new Error().stack
var here = getFileCall(trace, 1)
var caller = getFileCall(trace, 3)
if (here !== caller) {
throw new PrivateMethodError()
}
}

/**
* Throws a ProtectedMethodError if the caller is not a child class of CustomError
* @method
* @returns {undefined}
* @throws {ProtectedMethodError}
*/
function protect (sourcePath) {
var trace = new Error().stack
var caller = getFileCall(trace, 3)
if (sourcePath !== caller) {
// throw new ProtectedMethodError()
throw new Error()
}
}

/**
* Get the file path of the caller at a given stack level
* @function
* @inner
* @param {String} trace A given stack trace
* @param {Number} level A given stack level to get the file path at this level
* @returns {String} The file path for this level
*/
function getFileCall (trace, level) {
var firstLineOfStack = trace.split('\n')[level]
var splitted = firstLineOfStack.split('(')
firstLineOfStack = splitted.splice(1).join('')
splitted = firstLineOfStack.split(')')
firstLineOfStack = splitted.join('')
splitted = firstLineOfStack.split(':')
firstLineOfStack = splitted.slice(0, -2).join('')
return firstLineOfStack
}
112 changes: 112 additions & 0 deletions tests/errors/CustomError.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
'use strict'

var path = require('path')
var chai = require('chai')
var CustomError = require(path.resolve('./lib/errors/CustomError'))

var describe = global.describe
var it = global.it
var expect = chai.expect

describe('CustomError', function () {
it('should be a function', function () {
expect(CustomError).to.be.a('function')
})

it("should not have a 'DEFAULT_ERROR_MESSAGE' static constant property (child classes should have one)", function () {
expect(CustomError).to.not.have.a.property('DEFAULT_ERROR_MESSAGE')
})

describe('.prototype', function () {
it('should be a function', function () {
expect(CustomError.prototype).to.be.a('OBJECT')
})

it('should be an instance of Error', function () {
expect(CustomError.prototype).to.be.an.instanceof(Error)
})

describe('.constructor()', function () {
it('should be an instance of CustomError', function () {
var err = new CustomError()
expect(CustomError.prototype).to.be.an.instanceof(Error)
expect(err).to.be.an.instanceof(CustomError)
})

it("should have a 'name' property of type 'String'", function () {
var msg = "'arg' is missing"
var err = new CustomError(msg)
expect(err).to.be.a.property('name')
expect(err.message).to.be.a('String')
})

it("should have a 'message' property of type 'String'", function () {
var msg = "'arg' is missing"
var err = new CustomError(msg)
expect(err).to.be.a.property('message')
expect(err.message).to.be.a('String')
expect(err.message).to.equal(msg)
})

it("should have a 'stack' property of type 'String'", function () {
var msg = "'arg' is missing"
var err = new CustomError(msg)
expect(err).to.be.a.property('stack')
expect(err.stack).to.be.a('String')
})

it("should have a 'createStackTrace' property of type 'function'", function () {
var err = new CustomError('yo')
expect(err).to.have.a.property('createStackTrace')
expect(err.createStackTrace).to.be.a('function')
})

it("should have a 'privatize' property of type 'function'", function () {
var err = new CustomError()
expect(err).to.be.a.property('privatize')
expect(err.privatize).to.be.a('function')
})

it("should have a 'protect' property of type 'function'", function () {
var err = new CustomError()
expect(err).to.be.a.property('protect')
expect(err.protect).to.be.a('function')
})

it('should not fail if an bad type is given instead of a string as first argument', function () {
var err
var func = function () { err = new CustomError(func) }
var obj = function () { err = new CustomError({ foo: 'bar' }) }
var arr = function () { err = new CustomError([ 'foo', 'baz' ]) }

expect(func).to.not.throw('TypeError')
expect(obj).to.not.throw('TypeError')
expect(arr).to.not.throw('TypeError')
})

it('should fails if we call the private createStackTrace method', function () {
var func = function () {
var err = new CustomError(func)
var t = err.createStackTrace()
console.log(t)
}
expect(func).to.throw(Error)
})

it('should fails if a child class call the protected createStackTrace method', function () {
var func = function () {
var err = new CustomError(func)
var t = err.createStackTrace()
console.log(t)
}
expect(func).to.throw(Error)
})

it('should not have a default error message if no argument is given (child class should have)', function () {
var err = new CustomError()
expect(err.message).to.not.be.a('String')
expect(err.message).to.be.undefined
})
})
})
})

0 comments on commit d8b2e66

Please sign in to comment.