diff --git a/lib/cli/etcher.js b/lib/cli/etcher.js index 09582580..735ace29 100644 --- a/lib/cli/etcher.js +++ b/lib/cli/etcher.js @@ -26,11 +26,12 @@ const writer = require('./writer'); const errors = require('./errors'); const options = require('./cli'); const robot = require('../shared/robot'); +const messages = require('../shared/messages'); const EXIT_CODES = require('../shared/exit-codes'); isElevated().then((elevated) => { if (!elevated) { - throw new Error('This should should be run with root/administrator permissions'); + throw new Error(messages.error.elevationRequired()); } return form.run([ @@ -102,7 +103,7 @@ isElevated().then((elevated) => { }); } - console.log('Your flash is complete!'); + console.log(messages.info.flashComplete()); if (results.sourceChecksum) { console.log(`Checksum: ${results.sourceChecksum}`); diff --git a/lib/gui/app.js b/lib/gui/app.js index e0cd15f2..5dd097d7 100644 --- a/lib/gui/app.js +++ b/lib/gui/app.js @@ -25,6 +25,7 @@ var angular = require('angular'); const electron = require('electron'); const EXIT_CODES = require('../shared/exit-codes'); +const messages = require('../shared/messages'); /* eslint-enable no-var */ @@ -168,11 +169,7 @@ app.run(($window, WarningModalService, ErrorService, FlashStateModel) => { WarningModalService.display({ confirmationLabel: 'Yes, quit', rejectionLabel: 'Cancel', - description: [ - 'You are currently flashing a drive. Closing Etcher may leave', - 'your drive in an unusable state.\n\nAre you sure you want to', - 'close Etcher?' - ].join(' ') + description: messages.warning.exitWhileFlashing() }).then((confirmed) => { if (confirmed) { diff --git a/lib/gui/components/drive-selector/controllers/drive-selector.js b/lib/gui/components/drive-selector/controllers/drive-selector.js index 604f4fe4..ab70727a 100644 --- a/lib/gui/components/drive-selector/controllers/drive-selector.js +++ b/lib/gui/components/drive-selector/controllers/drive-selector.js @@ -17,6 +17,7 @@ 'use strict'; const _ = require('lodash'); +const messages = require('../../../../shared/messages'); module.exports = function( $q, @@ -79,8 +80,10 @@ module.exports = function( return WarningModalService.display({ confirmationLabel: 'Yes, continue', description: [ - `This image recommends a ${SelectionStateModel.getImageRecommendedDriveSize()}`, - `bytes drive, however ${drive.device} is only ${drive.size} bytes.`, + messages.warning.unrecommendedDriveSize({ + image: SelectionStateModel.getImage(), + drive: drive + }), 'Are you sure you want to continue?' ].join(' ') }); diff --git a/lib/gui/pages/main/controllers/flash.js b/lib/gui/pages/main/controllers/flash.js index 9e33dad2..b3198f48 100644 --- a/lib/gui/pages/main/controllers/flash.js +++ b/lib/gui/pages/main/controllers/flash.js @@ -16,6 +16,8 @@ 'use strict'; +const messages = require('../../../../shared/messages'); + module.exports = function( $state, FlashStateModel, @@ -65,20 +67,20 @@ module.exports = function( return; } - OSNotificationService.send('Success!', 'Your flash is complete'); + OSNotificationService.send('Success!', messages.info.flashComplete()); AnalyticsService.logEvent('Done'); $state.go('success'); }) .catch((error) => { - OSNotificationService.send('Oops!', 'Looks like your flash has failed'); + OSNotificationService.send('Oops!', messages.error.flashFailure()); if (error.code === 'EVALIDATION') { - FlashErrorModalService.show('Your removable drive may be corrupted. Try inserting a different one and try again.'); + FlashErrorModalService.show(messages.error.validation()); AnalyticsService.logEvent('Validation error'); } else if (error.code === 'ENOSPC') { - FlashErrorModalService.show('Not enough space on the drive. Please insert larger one and try again.'); + FlashErrorModalService.show(messages.error.notEnoughSpaceInDrive()); } else { - FlashErrorModalService.show('Oops, seems something went wrong.'); + FlashErrorModalService.show(messages.error.genericFlashError()); ErrorService.reportException(error); AnalyticsService.logEvent('Flash error'); } diff --git a/lib/gui/pages/main/controllers/image-selection.js b/lib/gui/pages/main/controllers/image-selection.js index cd3bdba8..de63e8e3 100644 --- a/lib/gui/pages/main/controllers/image-selection.js +++ b/lib/gui/pages/main/controllers/image-selection.js @@ -17,6 +17,7 @@ 'use strict'; const _ = require('lodash'); +const messages = require('../../../../shared/messages'); module.exports = function(SupportedFormatsModel, SelectionStateModel, AnalyticsService, ErrorService, OSDialogService) { @@ -56,7 +57,10 @@ module.exports = function(SupportedFormatsModel, SelectionStateModel, AnalyticsS */ this.selectImage = (image) => { if (!SupportedFormatsModel.isSupportedImage(image.path)) { - OSDialogService.showError('Invalid image', `${image.path} is not a supported image type.`); + OSDialogService.showError('Invalid image', messages.error.invalidImage({ + image: image + })); + AnalyticsService.logEvent('Invalid image', image); return; } diff --git a/lib/shared/messages.js b/lib/shared/messages.js new file mode 100644 index 00000000..778c4e4a --- /dev/null +++ b/lib/shared/messages.js @@ -0,0 +1,86 @@ +/* + * Copyright 2016 resin.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +const _ = require('lodash'); + +/** + * @summary Application messages + * @namespace messages + * @public + */ +module.exports = { + + /** + * @summary Informational messages + * @namespace info + * @memberof messages + */ + info: { + + flashComplete: _.template('Your flash is complete!') + + }, + + /** + * @summary Warning messages + * @namespace warning + * @memberof messages + */ + warning: { + + unrecommendedDriveSize: _.template([ + 'This image recommends a <%= image.recommendedDriveSize %>', + 'bytes drive, however <%= drive.device %> is only <%= drive.size %> bytes.' + ].join(' ')), + + exitWhileFlashing: _.template([ + 'You are currently flashing a drive. Closing Etcher may leave', + 'your drive in an unusable state.\n\n', + 'Are you sure you want to close Etcher?' + ].join(' ')) + + }, + + /** + * @summary Error messages + * @namespace error + * @memberof messages + */ + error: { + + notEnoughSpaceInDrive: _.template([ + 'Not enough space on the drive.', + 'Please insert larger one and try again.' + ].join(' ')), + + genericFlashError: _.template('Oops, seems something went wrong.'), + + validation: _.template([ + 'Your removable drive may be corrupted.', + 'Try inserting a different one and try again.' + ].join(' ')), + + invalidImage: _.template('<%= image.path %> is not a supported image type.'), + + elevationRequired: _.template('This should should be run with root/administrator permissions.'), + + flashFailure: _.template('Looks like your flash has failed!') + + } + +}; diff --git a/tests/shared/messages.spec.js b/tests/shared/messages.spec.js new file mode 100644 index 00000000..8b254a28 --- /dev/null +++ b/tests/shared/messages.spec.js @@ -0,0 +1,35 @@ +/* + * Copyright 2016 resin.io + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +const m = require('mochainon'); +const _ = require('lodash'); +const messages = require('../../lib/shared/messages'); + +describe('Shared: Messages', function() { + + it('should contain object properties', function() { + m.chai.expect(_.every(_.map(messages, _.isPlainObject))).to.be.true; + }); + + it('should contain function properties in each category', function() { + _.each(messages, (category) => { + m.chai.expect(_.every(_.map(category, _.isFunction))).to.be.true; + }); + }); + +});