refactor(errors): createError and createUserError accept an object (#1322)

Currently, both of these functions accept two arguments: the error
title, and the error description. This function signature makes is hard
to keep adding options to these error creation functions, like an error
code, so this commit refactors them to take a single argument: an
options object containing `title` and `description` properties.

Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
This commit is contained in:
Juan Cruz Viotti 2017-04-20 12:23:32 -04:00 committed by GitHub
parent b93fd86092
commit fe91be11e8
15 changed files with 312 additions and 122 deletions

View File

@ -152,7 +152,9 @@ return permissions.isElevated().then((elevated) => {
name: packageJSON.displayName name: packageJSON.displayName
}).then((stdout, stderr) => { }).then((stdout, stderr) => {
if (!_.isEmpty(stderr)) { if (!_.isEmpty(stderr)) {
throw errors.createError(stderr); throw errors.createError({
title: stderr
});
} }
// We're hardcoding internal error messages declared by `sudo-prompt`. // We're hardcoding internal error messages declared by `sudo-prompt`.
@ -167,10 +169,10 @@ return permissions.isElevated().then((elevated) => {
}).catch({ }).catch({
message: 'No polkit authentication agent found.' message: 'No polkit authentication agent found.'
}, () => { }, () => {
throw errors.createUserError( throw errors.createUserError({
'No polkit authentication agent found', title: 'No polkit authentication agent found',
'Please install a polkit authentication agent for your desktop environment of choice to continue' description: 'Please install a polkit authentication agent for your desktop environment of choice to continue'
); });
}); });
} }

View File

@ -35,10 +35,10 @@ const imagePath = options._[ARGV_IMAGE_PATH_INDEX];
permissions.isElevated().then((elevated) => { permissions.isElevated().then((elevated) => {
if (!elevated) { if (!elevated) {
throw errors.createUserError( throw errors.createUserError({
messages.error.elevationRequired(), title: messages.error.elevationRequired(),
'This tool requires special permissions to write to external drives' description: 'This tool requires special permissions to write to external drives'
); });
} }
return form.run([ return form.run([
@ -66,7 +66,10 @@ permissions.isElevated().then((elevated) => {
}); });
}).then((answers) => { }).then((answers) => {
if (!answers.yes) { if (!answers.yes) {
throw errors.createUserError('Aborted', 'We can\'t proceed without confirmation'); throw errors.createUserError({
title: 'Aborted',
description: 'We can\'t proceed without confirmation'
});
} }
const progressBars = { const progressBars = {
@ -80,10 +83,10 @@ permissions.isElevated().then((elevated) => {
}); });
if (!selectedDrive) { if (!selectedDrive) {
throw errors.createUserError( throw errors.createUserError({
'The selected drive was not found', title: 'The selected drive was not found',
`We can't find ${answers.drive} in your system. Did you unplug the drive?` description: `We can't find ${answers.drive} in your system. Did you unplug the drive?`
); });
} }
return writer.writeImage(imagePath, selectedDrive, { return writer.writeImage(imagePath, selectedDrive, {

View File

@ -93,7 +93,9 @@ module.exports = yargs
// Error reporting // Error reporting
.fail((message, error) => { .fail((message, error) => {
const errorObject = error || errors.createUserError(message); const errorObject = error || errors.createUserError({
title: message
});
if (robot.isEnabled(process.env)) { if (robot.isEnabled(process.env)) {
robot.printError(errorObject); robot.printError(errorObject);
@ -112,7 +114,10 @@ module.exports = yargs
try { try {
fs.accessSync(imagePath); fs.accessSync(imagePath);
} catch (error) { } catch (error) {
throw errors.createUserError('Unable to access file', `The image ${imagePath} is not accessible`); throw errors.createUserError({
title: 'Unable to access file',
description: `The image ${imagePath} is not accessible`
});
} }
return true; return true;
@ -120,10 +125,10 @@ module.exports = yargs
.check((argv) => { .check((argv) => {
if (robot.isEnabled(process.env) && !argv.drive) { if (robot.isEnabled(process.env) && !argv.drive) {
throw errors.createUserError( throw errors.createUserError({
'Missing drive', title: 'Missing drive',
'You need to explicitly pass a drive when enabling robot mode' description: 'You need to explicitly pass a drive when enabling robot mode'
); });
} }
return true; return true;

View File

@ -70,10 +70,10 @@ exports.writeImage = (imagePath, drive, options, onProgress) => {
}).then((driveFileDescriptor) => { }).then((driveFileDescriptor) => {
return imageStream.getFromFilePath(imagePath).then((image) => { return imageStream.getFromFilePath(imagePath).then((image) => {
if (!constraints.isDriveLargeEnough(drive, image)) { if (!constraints.isDriveLargeEnough(drive, image)) {
throw errors.createUserError( throw errors.createUserError({
'The image you selected is too big for this drive', title: 'The image you selected is too big for this drive',
'Please connect a bigger drive and try again' description: 'Please connect a bigger drive and try again'
); });
} }
return imageWrite.write({ return imageWrite.write({

View File

@ -119,11 +119,15 @@ const storeReducer = (state = DEFAULT_STATE, action) => {
case ACTIONS.SET_AVAILABLE_DRIVES: { case ACTIONS.SET_AVAILABLE_DRIVES: {
if (!action.data) { if (!action.data) {
throw errors.createError('Missing drives'); throw errors.createError({
title: 'Missing drives'
});
} }
if (!_.isArray(action.data) || !_.every(action.data, _.isPlainObject)) { if (!_.isArray(action.data) || !_.every(action.data, _.isPlainObject)) {
throw errors.createError(`Invalid drives: ${action.data}`); throw errors.createError({
title: `Invalid drives: ${action.data}`
});
} }
const newState = state.set('availableDrives', Immutable.fromJS(action.data)); const newState = state.set('availableDrives', Immutable.fromJS(action.data));
@ -170,35 +174,51 @@ const storeReducer = (state = DEFAULT_STATE, action) => {
case ACTIONS.SET_FLASH_STATE: { case ACTIONS.SET_FLASH_STATE: {
if (!state.get('isFlashing')) { if (!state.get('isFlashing')) {
throw errors.createError('Can\'t set the flashing state when not flashing'); throw errors.createError({
title: 'Can\'t set the flashing state when not flashing'
});
} }
if (!action.data.type) { if (!action.data.type) {
throw errors.createError('Missing state type'); throw errors.createError({
title: 'Missing state type'
});
} }
if (!_.isString(action.data.type)) { if (!_.isString(action.data.type)) {
throw errors.createError(`Invalid state type: ${action.data.type}`); throw errors.createError({
title: `Invalid state type: ${action.data.type}`
});
} }
if (_.isNil(action.data.percentage)) { if (_.isNil(action.data.percentage)) {
throw errors.createError('Missing state percentage'); throw errors.createError({
title: 'Missing state percentage'
});
} }
if (!_.isNumber(action.data.percentage)) { if (!_.isNumber(action.data.percentage)) {
throw errors.createError(`Invalid state percentage: ${action.data.percentage}`); throw errors.createError({
title: `Invalid state percentage: ${action.data.percentage}`
});
} }
if (_.isNil(action.data.eta)) { if (_.isNil(action.data.eta)) {
throw errors.createError('Missing state eta'); throw errors.createError({
title: 'Missing state eta'
});
} }
if (!_.isNumber(action.data.eta)) { if (!_.isNumber(action.data.eta)) {
throw errors.createError(`Invalid state eta: ${action.data.eta}`); throw errors.createError({
title: `Invalid state eta: ${action.data.eta}`
});
} }
if (_.isNil(action.data.speed)) { if (_.isNil(action.data.speed)) {
throw errors.createError('Missing state speed'); throw errors.createError({
title: 'Missing state speed'
});
} }
return state.set('flashState', Immutable.fromJS(action.data)); return state.set('flashState', Immutable.fromJS(action.data));
@ -218,7 +238,9 @@ const storeReducer = (state = DEFAULT_STATE, action) => {
case ACTIONS.UNSET_FLASHING_FLAG: { case ACTIONS.UNSET_FLASHING_FLAG: {
if (!action.data) { if (!action.data) {
throw errors.createError('Missing results'); throw errors.createError({
title: 'Missing results'
});
} }
_.defaults(action.data, { _.defaults(action.data, {
@ -226,19 +248,27 @@ const storeReducer = (state = DEFAULT_STATE, action) => {
}); });
if (!_.isBoolean(action.data.cancelled)) { if (!_.isBoolean(action.data.cancelled)) {
throw errors.createError(`Invalid results cancelled: ${action.data.cancelled}`); throw errors.createError({
title: `Invalid results cancelled: ${action.data.cancelled}`
});
} }
if (action.data.cancelled && action.data.sourceChecksum) { if (action.data.cancelled && action.data.sourceChecksum) {
throw errors.createError('The sourceChecksum value can\'t exist if the flashing was cancelled'); throw errors.createError({
title: 'The sourceChecksum value can\'t exist if the flashing was cancelled'
});
} }
if (action.data.sourceChecksum && !_.isString(action.data.sourceChecksum)) { if (action.data.sourceChecksum && !_.isString(action.data.sourceChecksum)) {
throw errors.createError(`Invalid results sourceChecksum: ${action.data.sourceChecksum}`); throw errors.createError({
title: `Invalid results sourceChecksum: ${action.data.sourceChecksum}`
});
} }
if (action.data.errorCode && !_.isString(action.data.errorCode) && !_.isNumber(action.data.errorCode)) { if (action.data.errorCode && !_.isString(action.data.errorCode) && !_.isNumber(action.data.errorCode)) {
throw errors.createError(`Invalid results errorCode: ${action.data.errorCode}`); throw errors.createError({
title: `Invalid results errorCode: ${action.data.errorCode}`
});
} }
return state return state
@ -249,26 +279,36 @@ const storeReducer = (state = DEFAULT_STATE, action) => {
case ACTIONS.SELECT_DRIVE: { case ACTIONS.SELECT_DRIVE: {
if (!action.data) { if (!action.data) {
throw errors.createError('Missing drive'); throw errors.createError({
title: 'Missing drive'
});
} }
if (!_.isString(action.data)) { if (!_.isString(action.data)) {
throw errors.createError(`Invalid drive: ${action.data}`); throw errors.createError({
title: `Invalid drive: ${action.data}`
});
} }
const selectedDrive = findDrive(state, action.data); const selectedDrive = findDrive(state, action.data);
if (!selectedDrive) { if (!selectedDrive) {
throw errors.createError(`The drive is not available: ${action.data}`); throw errors.createError({
title: `The drive is not available: ${action.data}`
});
} }
if (selectedDrive.get('protected')) { if (selectedDrive.get('protected')) {
throw errors.createError('The drive is write-protected'); throw errors.createError({
title: 'The drive is write-protected'
});
} }
const image = state.getIn([ 'selection', 'image' ]); const image = state.getIn([ 'selection', 'image' ]);
if (image && !constraints.isDriveLargeEnough(selectedDrive.toJS(), image.toJS())) { if (image && !constraints.isDriveLargeEnough(selectedDrive.toJS(), image.toJS())) {
throw errors.createError('The drive is not large enough'); throw errors.createError({
title: 'The drive is not large enough'
});
} }
return state.setIn([ 'selection', 'drive' ], Immutable.fromJS(action.data)); return state.setIn([ 'selection', 'drive' ], Immutable.fromJS(action.data));
@ -276,45 +316,65 @@ const storeReducer = (state = DEFAULT_STATE, action) => {
case ACTIONS.SELECT_IMAGE: { case ACTIONS.SELECT_IMAGE: {
if (!action.data.path) { if (!action.data.path) {
throw errors.createError('Missing image path'); throw errors.createError({
title: 'Missing image path'
});
} }
if (!_.isString(action.data.path)) { if (!_.isString(action.data.path)) {
throw errors.createError(`Invalid image path: ${action.data.path}`); throw errors.createError({
title: `Invalid image path: ${action.data.path}`
});
} }
if (!action.data.size) { if (!action.data.size) {
throw errors.createError('Missing image size'); throw errors.createError({
title: 'Missing image size'
});
} }
if (!_.isPlainObject(action.data.size)) { if (!_.isPlainObject(action.data.size)) {
throw errors.createError(`Invalid image size: ${action.data.size}`); throw errors.createError({
title: `Invalid image size: ${action.data.size}`
});
} }
const MINIMUM_IMAGE_SIZE = 0; const MINIMUM_IMAGE_SIZE = 0;
if (!_.isInteger(action.data.size.original) || action.data.size.original < MINIMUM_IMAGE_SIZE) { if (!_.isInteger(action.data.size.original) || action.data.size.original < MINIMUM_IMAGE_SIZE) {
throw errors.createError(`Invalid original image size: ${action.data.size.original}`); throw errors.createError({
title: `Invalid original image size: ${action.data.size.original}`
});
} }
if (!_.isInteger(action.data.size.final.value) || action.data.size.final.value < MINIMUM_IMAGE_SIZE) { if (!_.isInteger(action.data.size.final.value) || action.data.size.final.value < MINIMUM_IMAGE_SIZE) {
throw errors.createError(`Invalid final image size: ${action.data.size.final.value}`); throw errors.createError({
title: `Invalid final image size: ${action.data.size.final.value}`
});
} }
if (!_.isBoolean(action.data.size.final.estimation)) { if (!_.isBoolean(action.data.size.final.estimation)) {
throw errors.createError(`Invalid final image size estimation flag: ${action.data.size.final.estimation}`); throw errors.createError({
title: `Invalid final image size estimation flag: ${action.data.size.final.estimation}`
});
} }
if (action.data.url && !_.isString(action.data.url)) { if (action.data.url && !_.isString(action.data.url)) {
throw errors.createError(`Invalid image url: ${action.data.url}`); throw errors.createError({
title: `Invalid image url: ${action.data.url}`
});
} }
if (action.data.name && !_.isString(action.data.name)) { if (action.data.name && !_.isString(action.data.name)) {
throw errors.createError(`Invalid image name: ${action.data.name}`); throw errors.createError({
title: `Invalid image name: ${action.data.name}`
});
} }
if (action.data.logo && !_.isString(action.data.logo)) { if (action.data.logo && !_.isString(action.data.logo)) {
throw errors.createError(`Invalid image logo: ${action.data.logo}`); throw errors.createError({
title: `Invalid image logo: ${action.data.logo}`
});
} }
const selectedDrive = findDrive(state, state.getIn([ 'selection', 'drive' ])); const selectedDrive = findDrive(state, state.getIn([ 'selection', 'drive' ]));
@ -346,19 +406,27 @@ const storeReducer = (state = DEFAULT_STATE, action) => {
const value = action.data.value; const value = action.data.value;
if (!key) { if (!key) {
throw errors.createError('Missing setting key'); throw errors.createError({
title: 'Missing setting key'
});
} }
if (!_.isString(key)) { if (!_.isString(key)) {
throw errors.createError(`Invalid setting key: ${key}`); throw errors.createError({
title: `Invalid setting key: ${key}`
});
} }
if (!DEFAULT_STATE.get('settings').has(key)) { if (!DEFAULT_STATE.get('settings').has(key)) {
throw errors.createError(`Unsupported setting: ${key}`); throw errors.createError({
title: `Unsupported setting: ${key}`
});
} }
if (_.isObject(value)) { if (_.isObject(value)) {
throw errors.createError(`Invalid setting value: ${value}`); throw errors.createError({
title: `Invalid setting value: ${value}`
});
} }
return state.setIn([ 'settings', key ], value); return state.setIn([ 'settings', key ], value);

View File

@ -68,9 +68,12 @@ module.exports = function(
*/ */
this.selectImage = (image) => { this.selectImage = (image) => {
if (!supportedFormats.isSupportedImage(image.path)) { if (!supportedFormats.isSupportedImage(image.path)) {
const invalidImageError = errors.createUserError('Invalid image', messages.error.invalidImage({ const invalidImageError = errors.createUserError({
title: 'Invalid image',
description: messages.error.invalidImage({
image image
})); })
});
OSDialogService.showError(invalidImageError); OSDialogService.showError(invalidImageError);
AnalyticsService.logEvent('Invalid image', image); AnalyticsService.logEvent('Invalid image', image);

View File

@ -41,7 +41,9 @@ module.exports = (ManifestBindService) => {
const value = ManifestBindService.get(attributes.manifestBind); const value = ManifestBindService.get(attributes.manifestBind);
if (!value) { if (!value) {
throw errors.createError(`ManifestBind: Unknown property \`${attributes.manifestBind}\``); throw errors.createError({
title: `ManifestBind: Unknown property \`${attributes.manifestBind}\``
});
} }
element.html(value); element.html(value);

View File

@ -89,7 +89,9 @@ exports.extractFile = (archive, entries, file) => {
if (!_.find(entries, { if (!_.find(entries, {
name: file name: file
})) { })) {
throw errors.createError(`Invalid entry: ${file}`); throw errors.createError({
title: `Invalid entry: ${file}`
});
} }
yauzl.openAsync(archive, { yauzl.openAsync(archive, {

View File

@ -119,10 +119,10 @@ const extractArchiveMetadata = (archive, basePath, options) => {
try { try {
return JSON.parse(manifest); return JSON.parse(manifest);
} catch (parseError) { } catch (parseError) {
throw errors.createUserError( throw errors.createUserError({
'Invalid archive manifest.json', title: 'Invalid archive manifest.json',
'The archive manifest.json file is not valid JSON' description: 'The archive manifest.json file is not valid JSON'
); });
} }
}); });
}) })
@ -178,10 +178,10 @@ exports.extractImage = (archive, hooks) => {
const VALID_NUMBER_OF_IMAGE_ENTRIES = 1; const VALID_NUMBER_OF_IMAGE_ENTRIES = 1;
if (imageEntries.length !== VALID_NUMBER_OF_IMAGE_ENTRIES) { if (imageEntries.length !== VALID_NUMBER_OF_IMAGE_ENTRIES) {
throw errors.createUserError( throw errors.createUserError({
'Invalid archive image', title: 'Invalid archive image',
'The archive image should contain one and only one top image file' description: 'The archive image should contain one and only one top image file'
); });
} }
const imageEntry = _.first(imageEntries); const imageEntry = _.first(imageEntries);

View File

@ -69,12 +69,18 @@ const errors = require('../shared/errors');
exports.getFromFilePath = (file) => { exports.getFromFilePath = (file) => {
return fs.statAsync(file).then((fileStats) => { return fs.statAsync(file).then((fileStats) => {
if (!fileStats.isFile()) { if (!fileStats.isFile()) {
throw errors.createUserError('Invalid image', 'The image must be a file'); throw errors.createUserError({
title: 'Invalid image',
description: 'The image must be a file'
});
} }
return utils.getArchiveMimeType(file).then((type) => { return utils.getArchiveMimeType(file).then((type) => {
if (!_.has(handlers, type)) { if (!_.has(handlers, type)) {
throw errors.createUserError('Invalid image', `The ${type} format is not supported`); throw errors.createUserError({
title: 'Invalid image',
description: `The ${type} format is not supported`
});
} }
return _.invoke(handlers, type, file, { return _.invoke(handlers, type, file, {

View File

@ -231,23 +231,27 @@ exports.getDescription = (error, options = {}) => {
* @function * @function
* @public * @public
* *
* @param {String} title - error title * @param {Object} options - options
* @param {String} [description] - error description * @param {String} options.title - error title
* @param {Object} [options] - options * @param {String} [options.description] - error description
* @param {Boolean} [options.report] - report error * @param {Boolean} [options.report] - report error
* @returns {Error} error * @returns {Error} error
* *
* @example * @example
* const error = errors.createError('Foo', 'Bar'); * const error = errors.createError({
* title: 'Foo'
* description: 'Bar'
* });
*
* throw error; * throw error;
*/ */
exports.createError = (title, description, options = {}) => { exports.createError = (options) => {
if (isBlank(title)) { if (isBlank(options.title)) {
throw new Error(`Invalid error title: ${title}`); throw new Error(`Invalid error title: ${options.title}`);
} }
const error = new Error(title); const error = new Error(options.title);
error.description = description; error.description = options.description;
if (!_.isNil(options.report) && !options.report) { if (!_.isNil(options.report) && !options.report) {
error.report = false; error.report = false;
@ -267,16 +271,23 @@ exports.createError = (title, description, options = {}) => {
* Therefore, user errors don't get reported to analytics * Therefore, user errors don't get reported to analytics
* and error reporting services. * and error reporting services.
* *
* @param {String} title - error title * @param {Object} options - options
* @param {String} [description] - error description * @param {String} options.title - error title
* @param {String} [options.description] - error description
* @returns {Error} user error * @returns {Error} user error
* *
* @example * @example
* const error = errors.createUserError('Foo', 'Bar'); * const error = errors.createUserError({
* title: 'Foo',
* description: 'Bar'
* });
*
* throw error; * throw error;
*/ */
exports.createUserError = (title, description) => { exports.createUserError = (options) => {
return exports.createError(title, description, { return exports.createError({
title: options.title,
description: options.description,
report: false report: false
}); });
}; };

View File

@ -120,7 +120,11 @@ functions: `.printError()` and `.recomposeErrorMessage()`.
Here's an example of these functions in action: Here's an example of these functions in action:
```javascript ```javascript
const error = errors.createError('This is an error', 'My description'); const error = errors.createError({
title: 'This is an error',
description: 'My description'
});
robot.printError(error); robot.printError(error);
``` ```

View File

@ -56,7 +56,9 @@ exports.isEnabled = (environment) => {
*/ */
exports.buildMessage = (title, data = {}) => { exports.buildMessage = (title, data = {}) => {
if (!_.isPlainObject(data)) { if (!_.isPlainObject(data)) {
throw errors.createError(`Invalid data: ${data}`); throw errors.createError({
title: `Invalid data: ${data}`
});
} }
return JSON.stringify({ return JSON.stringify({
@ -117,11 +119,17 @@ exports.parseMessage = (string) => {
try { try {
output = JSON.parse(string); output = JSON.parse(string);
} catch (error) { } catch (error) {
throw errors.createError('Invalid message', `${string}, ${error.message}`); throw errors.createError({
title: 'Invalid message',
description: `${string}, ${error.message}`
});
} }
if (!output.command || !output.data) { if (!output.command || !output.data) {
throw errors.createError('Invalid message', `No command or data: ${string}`); throw errors.createError({
title: 'Invalid message',
description: `No command or data: ${string}`
});
} }
return output; return output;

View File

@ -71,7 +71,9 @@ exports.isValidPercentage = (percentage) => {
*/ */
exports.percentageToFloat = (percentage) => { exports.percentageToFloat = (percentage) => {
if (!exports.isValidPercentage(percentage)) { if (!exports.isValidPercentage(percentage)) {
throw errors.createError(`Invalid percentage: ${percentage}`); throw errors.createError({
title: `Invalid percentage: ${percentage}`
});
} }
return percentage / PERCENTAGE_MAXIMUM; return percentage / PERCENTAGE_MAXIMUM;

View File

@ -390,12 +390,18 @@ describe('Shared: Errors', function() {
describe('.createError()', function() { describe('.createError()', function() {
it('should not set `error.report` by default', function() { it('should not set `error.report` by default', function() {
const error = errors.createError('Foo', 'Something happened'); const error = errors.createError({
title: 'Foo',
description: 'Something happened'
});
m.chai.expect(error.report).to.be.undefined; m.chai.expect(error.report).to.be.undefined;
}); });
it('should set `error.report` to false if `options.report` is false', function() { it('should set `error.report` to false if `options.report` is false', function() {
const error = errors.createError('Foo', 'Something happened', { const error = errors.createError({
title: 'Foo',
description: 'Something happened',
report: false report: false
}); });
@ -403,7 +409,9 @@ describe('Shared: Errors', function() {
}); });
it('should set `error.report` to false if `options.report` evaluates to false', function() { it('should set `error.report` to false if `options.report` evaluates to false', function() {
const error = errors.createError('Foo', 'Something happened', { const error = errors.createError({
title: 'Foo',
description: 'Something happened',
report: 0 report: 0
}); });
@ -411,47 +419,78 @@ describe('Shared: Errors', function() {
}); });
it('should be an instance of Error', function() { it('should be an instance of Error', function() {
const error = errors.createError('Foo', 'Something happened'); const error = errors.createError({
title: 'Foo',
description: 'Something happened'
});
m.chai.expect(error).to.be.an.instanceof(Error); m.chai.expect(error).to.be.an.instanceof(Error);
}); });
it('should correctly add both a message and a description', function() { it('should correctly add both a title and a description', function() {
const error = errors.createError('Foo', 'Something happened'); const error = errors.createError({
title: 'Foo',
description: 'Something happened'
});
m.chai.expect(errors.getTitle(error)).to.equal('Foo'); m.chai.expect(errors.getTitle(error)).to.equal('Foo');
m.chai.expect(errors.getDescription(error)).to.equal('Something happened'); m.chai.expect(errors.getDescription(error)).to.equal('Something happened');
}); });
it('should correctly add only a message', function() { it('should correctly add only a title', function() {
const error = errors.createError('Foo'); const error = errors.createError({
title: 'Foo'
});
m.chai.expect(errors.getTitle(error)).to.equal('Foo'); m.chai.expect(errors.getTitle(error)).to.equal('Foo');
m.chai.expect(errors.getDescription(error)).to.equal(error.stack); m.chai.expect(errors.getDescription(error)).to.equal(error.stack);
}); });
it('should ignore an empty description', function() { it('should ignore an empty description', function() {
const error = errors.createError('Foo', ''); const error = errors.createError({
title: 'Foo',
description: ''
});
m.chai.expect(errors.getDescription(error)).to.equal(error.stack); m.chai.expect(errors.getDescription(error)).to.equal(error.stack);
}); });
it('should ignore a blank description', function() { it('should ignore a blank description', function() {
const error = errors.createError('Foo', ' '); const error = errors.createError({
title: 'Foo',
description: ' '
});
m.chai.expect(errors.getDescription(error)).to.equal(error.stack); m.chai.expect(errors.getDescription(error)).to.equal(error.stack);
}); });
it('should throw if no message', function() { it('should throw if no title', function() {
m.chai.expect(() => { m.chai.expect(() => {
errors.createError(); errors.createError({});
}).to.throw('Invalid error title: undefined'); }).to.throw('Invalid error title: undefined');
}); });
it('should throw if message is empty', function() { it('should throw if there is a description but no title', function() {
m.chai.expect(() => { m.chai.expect(() => {
errors.createError(''); errors.createError({
description: 'foo'
});
}).to.throw('Invalid error title: undefined');
});
it('should throw if title is empty', function() {
m.chai.expect(() => {
errors.createError({
title: ''
});
}).to.throw('Invalid error title: '); }).to.throw('Invalid error title: ');
}); });
it('should throw if message is blank', function() { it('should throw if title is blank', function() {
m.chai.expect(() => { m.chai.expect(() => {
errors.createError(' '); errors.createError({
title: ' '
});
}).to.throw('Invalid error title: '); }).to.throw('Invalid error title: ');
}); });
@ -460,52 +499,87 @@ describe('Shared: Errors', function() {
describe('.createUserError()', function() { describe('.createUserError()', function() {
it('should set the `report` flag to `false`', function() { it('should set the `report` flag to `false`', function() {
const error = errors.createUserError('Foo', 'Something happened'); const error = errors.createUserError({
title: 'Foo',
description: 'Something happened'
});
m.chai.expect(error.report).to.be.false; m.chai.expect(error.report).to.be.false;
}); });
it('should be an instance of Error', function() { it('should be an instance of Error', function() {
const error = errors.createUserError('Foo', 'Something happened'); const error = errors.createUserError({
title: 'Foo',
description: 'Something happened'
});
m.chai.expect(error).to.be.an.instanceof(Error); m.chai.expect(error).to.be.an.instanceof(Error);
}); });
it('should correctly add both a message and a description', function() { it('should correctly add both a title and a description', function() {
const error = errors.createUserError('Foo', 'Something happened'); const error = errors.createUserError({
title: 'Foo',
description: 'Something happened'
});
m.chai.expect(errors.getTitle(error)).to.equal('Foo'); m.chai.expect(errors.getTitle(error)).to.equal('Foo');
m.chai.expect(errors.getDescription(error)).to.equal('Something happened'); m.chai.expect(errors.getDescription(error)).to.equal('Something happened');
}); });
it('should correctly add only a message', function() { it('should correctly add only a title', function() {
const error = errors.createUserError('Foo'); const error = errors.createUserError({
title: 'Foo'
});
m.chai.expect(errors.getTitle(error)).to.equal('Foo'); m.chai.expect(errors.getTitle(error)).to.equal('Foo');
m.chai.expect(errors.getDescription(error)).to.equal(error.stack); m.chai.expect(errors.getDescription(error)).to.equal(error.stack);
}); });
it('should ignore an empty description', function() { it('should ignore an empty description', function() {
const error = errors.createUserError('Foo', ''); const error = errors.createUserError({
title: 'Foo',
description: ''
});
m.chai.expect(errors.getDescription(error)).to.equal(error.stack); m.chai.expect(errors.getDescription(error)).to.equal(error.stack);
}); });
it('should ignore a blank description', function() { it('should ignore a blank description', function() {
const error = errors.createUserError('Foo', ' '); const error = errors.createUserError({
title: 'Foo',
description: ' '
});
m.chai.expect(errors.getDescription(error)).to.equal(error.stack); m.chai.expect(errors.getDescription(error)).to.equal(error.stack);
}); });
it('should throw if no message', function() { it('should throw if no title', function() {
m.chai.expect(() => { m.chai.expect(() => {
errors.createUserError(); errors.createUserError({});
}).to.throw('Invalid error title: undefined'); }).to.throw('Invalid error title: undefined');
}); });
it('should throw if message is empty', function() { it('should throw if title is empty', function() {
m.chai.expect(() => { m.chai.expect(() => {
errors.createUserError(''); errors.createUserError({
title: ''
});
}).to.throw('Invalid error title: '); }).to.throw('Invalid error title: ');
}); });
it('should throw if message is blank', function() { it('should throw if there is a description but no title', function() {
m.chai.expect(() => { m.chai.expect(() => {
errors.createUserError(' '); errors.createUserError({
description: 'foo'
});
}).to.throw('Invalid error title: undefined');
});
it('should throw if title is blank', function() {
m.chai.expect(() => {
errors.createUserError({
title: ' '
});
}).to.throw('Invalid error title: '); }).to.throw('Invalid error title: ');
}); });