From c55249448060e357d4fa8e03b8186b2ef45a1244 Mon Sep 17 00:00:00 2001 From: Juan Cruz Viotti Date: Fri, 13 Jan 2017 11:03:46 -0400 Subject: [PATCH] refactor: extract application messages to `lib/shared/messages.js` (#1022) There are certain application messages that should be re-used between the CLI and the GUI. In order to allow such re-usability, we extract out the application messages used in JavaScript into `lib/shared/messages.js` as a collection of Lodash `_.template` templates. Notice this file doesn't include application messages included in Angular templates directly since it'd be hard to refactor all of them. We plan to move to React soon, which will allow moving the remaining messages very easily. Signed-off-by: Juan Cruz Viotti --- lib/cli/etcher.js | 5 +- lib/gui/app.js | 7 +- .../controllers/drive-selector.js | 7 +- lib/gui/pages/main/controllers/flash.js | 12 +-- .../pages/main/controllers/image-selection.js | 6 +- lib/shared/messages.js | 86 +++++++++++++++++++ tests/shared/messages.spec.js | 35 ++++++++ 7 files changed, 143 insertions(+), 15 deletions(-) create mode 100644 lib/shared/messages.js create mode 100644 tests/shared/messages.spec.js 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; + }); + }); + +});