feat(GUI): rich image extensions (#597)

The following PRs add support for a custom `_info` directory containing
metadata such an image URL, display name, logo, etc in
`etcher-image-stream`:

- https://github.com/resin-io-modules/etcher-image-stream/pull/16
- https://github.com/resin-io-modules/etcher-image-stream/pull/14
- https://github.com/resin-io-modules/etcher-image-stream/pull/13

Now that this module supports such metadata, we make use of it in the
GUI as follows:

- The file name is replaced with the display name.
- The file name links to the image URL.
- The "Select Image" logo is replaced with the image logo.

Some miscellaneous changes introduces in this PR to support the changes
described above:

- Implement `SelectionStateModel.getImageUrl()`.
- Implement `SelectionStateModel.getImageLogo()`.
- Implement `SelectionStateModel.getImageName()`.
- Ignore the "logo" image property when displaying the "Select image"
  event, in order to not fill the console with SVG contents.
- Make `svg-icon` understand SVG strings as paths.
- Make `svg-icon` react to changes to the `path` attribute.
- Extract the core functionality of `openExternal` into
  `OSOpenExternalService`.
- Upgrade `etcher-image-stream` to v3.0.1.

Change-Type: minor
Changelog-Entry: Support rich image extensions.
Fixes: https://github.com/resin-io/etcher/issues/470
Signed-off-by: Juan Cruz Viotti <jviottidc@gmail.com>
This commit is contained in:
Juan Cruz Viotti 2016-07-26 08:47:20 -04:00 committed by GitHub
parent ce9093625b
commit 7e0f54e7d6
14 changed files with 494 additions and 50 deletions

View File

@ -146,7 +146,8 @@ app.controller('AppController', function(
TooltipModalService,
OSWindowProgressService,
OSNotificationService,
OSDialogService
OSDialogService,
OSOpenExternalService
) {
this.formats = SupportedFormatsModel;
this.selection = SelectionStateModel;
@ -205,7 +206,15 @@ app.controller('AppController', function(
}
this.selection.setImage(image);
AnalyticsService.logEvent('Select image', image);
AnalyticsService.logEvent('Select image', _.omit(image, 'logo'));
};
this.openImageUrl = () => {
const imageUrl = this.selection.getImageUrl();
if (imageUrl) {
OSOpenExternalService.open(imageUrl);
}
};
this.openImageSelector = () => {

View File

@ -16,6 +16,7 @@
'use strict';
const _ = require('lodash');
const path = require('path');
const fs = require('fs');
@ -45,21 +46,30 @@ module.exports = () => {
height: '@'
},
link: (scope, element) => {
// This means the path to the icon should be
// relative to *this directory*.
// TODO: There might be a way to compute the path
// relatively to the `index.html`.
const imagePath = path.join(__dirname, scope.path);
const contents = fs.readFileSync(imagePath, {
encoding: 'utf8'
});
element.html(contents);
element.css('width', scope.width || '40px');
element.css('height', scope.height || '40px');
scope.$watch('path', (value) => {
// The path contains SVG contents
if (_.first(value) === '<') {
element.html(value);
} else {
// This means the path to the icon should be
// relative to *this directory*.
// TODO: There might be a way to compute the path
// relatively to the `index.html`.
const imagePath = path.join(__dirname, value);
const contents = fs.readFileSync(imagePath, {
encoding: 'utf8'
});
element.html(contents);
}
});
}
};
};

View File

@ -220,6 +220,48 @@ SelectionStateModel.service('SelectionStateModel', function() {
return _.get(Store.getState().toJS(), 'selection.image.size');
};
/**
* @summary Get image url
* @function
* @public
*
* @returns {String} image url
*
* @example
* const imageUrl = SelectionStateModel.getImageUrl();
*/
this.getImageUrl = () => {
return _.get(Store.getState().toJS(), 'selection.image.url');
};
/**
* @summary Get image name
* @function
* @public
*
* @returns {String} image name
*
* @example
* const imageName = SelectionStateModel.getImageName();
*/
this.getImageName = () => {
return _.get(Store.getState().toJS(), 'selection.image.name');
};
/**
* @summary Get image logo
* @function
* @public
*
* @returns {String} image logo
*
* @example
* const imageLogo = SelectionStateModel.getImageLogo();
*/
this.getImageLogo = () => {
return _.get(Store.getState().toJS(), 'selection.image.logo');
};
/**
* @summary Check if there is a selected drive
* @function

View File

@ -274,6 +274,18 @@ const storeReducer = (state, action) => {
throw new Error(`Invalid image size: ${action.data.size}`);
}
if (action.data.url && !_.isString(action.data.url)) {
throw new Error(`Invalid image url: ${action.data.url}`);
}
if (action.data.name && !_.isString(action.data.name)) {
throw new Error(`Invalid image name: ${action.data.name}`);
}
if (action.data.logo && !_.isString(action.data.logo)) {
throw new Error(`Invalid image logo: ${action.data.logo}`);
}
return state.setIn([ 'selection', 'image' ], Immutable.fromJS(action.data));
}

View File

@ -71,10 +71,13 @@ module.exports = function($q, SupportedFormatsModel) {
return resolve();
}
imageStream.getEstimatedFinalSize(imagePath).then((estimatedSize) => {
imageStream.getImageMetadata(imagePath).then((metadata) => {
return resolve({
path: imagePath,
size: estimatedSize
size: metadata.estimatedSize,
name: metadata.name,
url: metadata.url,
logo: metadata.logo
});
}).catch(reject);
});

View File

@ -16,8 +16,6 @@
'use strict';
const electron = require('electron');
/**
* @summary OsOpenExternal directive
* @function
@ -27,12 +25,13 @@ const electron = require('electron');
* This directive provides an attribute to open an external
* resource with the default operating system action.
*
* @param {Object} OSOpenExternalService - OSOpenExternalService
* @returns {Object} directive
*
* @example
* <button os-open-external="https://resin.io">Resin.io</button>
*/
module.exports = () => {
module.exports = (OSOpenExternalService) => {
return {
restrict: 'A',
scope: false,
@ -43,7 +42,7 @@ module.exports = () => {
element.css('cursor', 'pointer');
element.on('click', () => {
electron.shell.openExternal(attributes.osOpenExternal);
OSOpenExternalService.open(attributes.osOpenExternal);
});
}
};

View File

@ -23,6 +23,7 @@
const angular = require('angular');
const MODULE_NAME = 'Etcher.OS.OpenExternal';
const OSOpenExternal = angular.module(MODULE_NAME, []);
OSOpenExternal.service('OSOpenExternalService', require('./services/open-external'));
OSOpenExternal.directive('osOpenExternal', require('./directives/open-external'));
module.exports = MODULE_NAME;

View File

@ -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 electron = require('electron');
module.exports = function() {
/**
* @summary Open an external resource
* @function
* @public
*
* @param {String} url - url
*
* @example
* OSOpenExternalService.open('https://www.google.com');
*/
this.open = electron.shell.openExternal;
};

View File

@ -1,7 +1,7 @@
<div class="row around-xs">
<div class="col-xs">
<div class="box text-center" os-dropzone="app.selectImage($file)">
<svg-icon class="center-block" path="../../../assets/image.svg"></svg-icon>
<svg-icon class="center-block" path="{{ app.selection.getImageLogo() || '../../../assets/image.svg' }}"></svg-icon>
<span class="icon-caption">SELECT IMAGE</span>
<span class="badge space-top-medium">1</span>
@ -16,7 +16,8 @@
</p>
</div>
<div ng-if="app.selection.hasImage()">
<div ng-bind="app.selection.getImagePath() | basename | middleEllipses:25"></div>
<div ng-click="app.openImageUrl()"
ng-bind="app.selection.getImageName() || app.selection.getImagePath() | basename | middleEllipses:25"></div>
<button class="btn btn-link step-tooltip"
ng-click="app.tooltipModal.show({

257
npm-shrinkwrap.json generated
View File

@ -123,6 +123,11 @@
}
}
},
"aproba": {
"version": "1.0.4",
"from": "aproba@>=1.0.3 <2.0.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.0.4.tgz"
},
"archive-type": {
"version": "3.2.0",
"from": "archive-type@>=3.2.0 <4.0.0",
@ -172,6 +177,23 @@
}
}
},
"are-we-there-yet": {
"version": "1.1.2",
"from": "are-we-there-yet@>=1.1.2 <1.2.0",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz",
"dependencies": {
"isarray": {
"version": "1.0.0",
"from": "isarray@~1.0.0",
"resolved": "http://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz"
},
"readable-stream": {
"version": "2.1.4",
"from": "readable-stream@^2.0.0 || ^1.1.13",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.1.4.tgz"
}
}
},
"argparse": {
"version": "1.0.7",
"from": "argparse@>=1.0.7 <2.0.0",
@ -207,6 +229,11 @@
"from": "array-find-index@>=1.0.1 <2.0.0",
"resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.1.tgz"
},
"array-index": {
"version": "1.0.0",
"from": "array-index@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/array-index/-/array-index-1.0.0.tgz"
},
"array-map": {
"version": "0.0.0",
"from": "array-map@>=0.0.0 <0.1.0",
@ -316,6 +343,11 @@
"from": "async@>=2.0.0-rc.2 <3.0.0",
"resolved": "https://registry.npmjs.org/async/-/async-2.0.0.tgz"
},
"async-foreach": {
"version": "0.1.3",
"from": "async-foreach@>=0.1.3 <0.2.0",
"resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz"
},
"autolinker": {
"version": "0.15.3",
"from": "autolinker@>=0.15.0 <0.16.0",
@ -326,6 +358,11 @@
"from": "aws-sign2@>=0.5.0 <0.6.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz"
},
"aws4": {
"version": "1.4.1",
"from": "aws4@>=1.2.1 <2.0.0",
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.4.1.tgz"
},
"balanced-match": {
"version": "0.4.1",
"from": "balanced-match@>=0.4.1 <0.5.0",
@ -346,6 +383,11 @@
"from": "bl@>=0.9.0 <0.10.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-0.9.5.tgz"
},
"block-stream": {
"version": "0.0.9",
"from": "block-stream@*",
"resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz"
},
"bluebird": {
"version": "3.4.1",
"from": "bluebird@>=3.0.5 <4.0.0",
@ -743,6 +785,11 @@
"from": "console-browserify@>=1.1.0 <2.0.0",
"resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz"
},
"console-control-strings": {
"version": "1.1.0",
"from": "console-control-strings@>=1.1.0 <1.2.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz"
},
"constants-browserify": {
"version": "1.0.0",
"from": "constants-browserify@>=1.0.0 <1.1.0",
@ -819,6 +866,11 @@
"from": "create-hmac@>=1.1.0 <2.0.0",
"resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.4.tgz"
},
"cross-spawn": {
"version": "3.0.1",
"from": "cross-spawn@>=3.0.0 <4.0.0",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz"
},
"cross-spawn-async": {
"version": "2.2.4",
"from": "cross-spawn-async@>=2.1.1 <3.0.0",
@ -864,6 +916,18 @@
"from": "d@>=0.1.1 <0.2.0",
"resolved": "https://registry.npmjs.org/d/-/d-0.1.1.tgz"
},
"dashdash": {
"version": "1.14.0",
"from": "dashdash@>=1.12.0 <2.0.0",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.0.tgz",
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"from": "assert-plus@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz"
}
}
},
"date-now": {
"version": "0.1.4",
"from": "date-now@>=0.1.4 <0.2.0",
@ -962,6 +1026,11 @@
"from": "delayed-stream@0.0.5",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz"
},
"delegates": {
"version": "1.0.0",
"from": "delegates@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz"
},
"denymount": {
"version": "2.2.0",
"from": "denymount@>=2.1.0 <3.0.0",
@ -1075,6 +1144,11 @@
}
}
},
"ecc-jsbn": {
"version": "0.1.1",
"from": "ecc-jsbn@>=0.1.1 <0.2.0",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz"
},
"electron-download": {
"version": "1.4.1",
"from": "electron-download@>=1.4.1 <2.0.0",
@ -1280,9 +1354,9 @@
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz"
},
"etcher-image-stream": {
"version": "2.6.1",
"from": "etcher-image-stream@2.6.1",
"resolved": "https://registry.npmjs.org/etcher-image-stream/-/etcher-image-stream-2.6.1.tgz",
"version": "3.0.1",
"from": "etcher-image-stream@3.0.1",
"resolved": "https://registry.npmjs.org/etcher-image-stream/-/etcher-image-stream-3.0.1.tgz",
"dependencies": {
"yauzl": {
"version": "2.6.0",
@ -1370,6 +1444,11 @@
"from": "expand-tilde@>=1.2.0 <2.0.0",
"resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-1.2.2.tgz"
},
"extend": {
"version": "3.0.0",
"from": "extend@>=3.0.0 <3.1.0",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.0.tgz"
},
"extend-shallow": {
"version": "2.0.1",
"from": "extend-shallow@>=2.0.1 <3.0.0",
@ -1412,6 +1491,11 @@
}
}
},
"extsprintf": {
"version": "1.0.2",
"from": "extsprintf@1.0.2",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz"
},
"eyes": {
"version": "0.1.8",
"from": "eyes@>=0.1.0 <0.2.0",
@ -1666,11 +1750,33 @@
"from": "fs.realpath@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
},
"fstream": {
"version": "1.0.10",
"from": "fstream@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.10.tgz",
"dependencies": {
"graceful-fs": {
"version": "4.1.4",
"from": "graceful-fs@^4.1.2",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.4.tgz"
}
}
},
"function-bind": {
"version": "1.1.0",
"from": "function-bind@>=1.0.2 <2.0.0",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.0.tgz"
},
"gauge": {
"version": "2.6.0",
"from": "gauge@>=2.6.0 <2.7.0",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.6.0.tgz"
},
"gaze": {
"version": "1.1.0",
"from": "gaze@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.0.tgz"
},
"generate-function": {
"version": "2.0.0",
"from": "generate-function@>=2.0.0 <3.0.0",
@ -1701,6 +1807,18 @@
"from": "get-value@>=2.0.6 <3.0.0",
"resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz"
},
"getpass": {
"version": "0.1.6",
"from": "getpass@>=0.1.1 <0.2.0",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.6.tgz",
"dependencies": {
"assert-plus": {
"version": "1.0.0",
"from": "assert-plus@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz"
}
}
},
"git-config-path": {
"version": "0.2.0",
"from": "git-config-path@>=0.2.0 <0.3.0",
@ -1767,6 +1885,18 @@
}
}
},
"globule": {
"version": "1.0.0",
"from": "globule@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/globule/-/globule-1.0.0.tgz",
"dependencies": {
"lodash": {
"version": "4.9.0",
"from": "lodash@>=4.9.0 <4.10.0",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.9.0.tgz"
}
}
},
"gm": {
"version": "1.22.0",
"from": "gm@>=1.21.1 <2.0.0",
@ -1848,6 +1978,16 @@
"from": "has-ansi@>=2.0.0 <3.0.0",
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz"
},
"has-color": {
"version": "0.1.7",
"from": "has-color@>=0.1.7 <0.2.0",
"resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz"
},
"has-unicode": {
"version": "2.0.1",
"from": "has-unicode@>=2.0.0 <3.0.0",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz"
},
"has-values": {
"version": "0.1.4",
"from": "has-values@>=0.1.4 <0.2.0",
@ -1974,6 +2114,11 @@
"from": "imurmurhash@>=0.1.4 <0.2.0",
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz"
},
"in-publish": {
"version": "2.0.0",
"from": "in-publish@>=2.0.0 <3.0.0",
"resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz"
},
"indent-string": {
"version": "2.1.0",
"from": "indent-string@>=2.1.0 <3.0.0",
@ -2251,6 +2396,11 @@
"from": "is-stream@>=1.1.0 <2.0.0",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz"
},
"is-typedarray": {
"version": "1.0.0",
"from": "is-typedarray@>=1.0.0 <1.1.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz"
},
"is-unc-path": {
"version": "0.1.1",
"from": "is-unc-path@>=0.1.1 <0.2.0",
@ -2313,6 +2463,11 @@
"from": "jju@>=1.1.0 <2.0.0",
"resolved": "https://registry.npmjs.org/jju/-/jju-1.3.0.tgz"
},
"jodid25519": {
"version": "1.0.2",
"from": "jodid25519@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/jodid25519/-/jodid25519-1.0.2.tgz"
},
"js-tokens": {
"version": "1.0.3",
"from": "js-tokens@>=1.0.1 <2.0.0",
@ -2323,11 +2478,21 @@
"from": "js-yaml@>=3.4.1 <4.0.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.6.1.tgz"
},
"jsbn": {
"version": "0.1.0",
"from": "jsbn@>=0.1.0 <0.2.0",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.0.tgz"
},
"json-parse-helpfulerror": {
"version": "1.0.3",
"from": "json-parse-helpfulerror@>=1.0.2 <2.0.0",
"resolved": "https://registry.npmjs.org/json-parse-helpfulerror/-/json-parse-helpfulerror-1.0.3.tgz"
},
"json-schema": {
"version": "0.2.2",
"from": "json-schema@0.2.2",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.2.tgz"
},
"json-stable-stringify": {
"version": "0.0.1",
"from": "json-stable-stringify@>=0.0.0 <0.1.0",
@ -2363,6 +2528,11 @@
"from": "JSONStream@>=1.0.3 <2.0.0",
"resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.1.2.tgz"
},
"jsprim": {
"version": "1.3.0",
"from": "jsprim@>=1.2.2 <2.0.0",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.3.0.tgz"
},
"kind-of": {
"version": "3.0.3",
"from": "kind-of@>=3.0.2 <4.0.0",
@ -2467,6 +2637,11 @@
"from": "lodash.assigninwith@>=4.0.0 <5.0.0",
"resolved": "https://registry.npmjs.org/lodash.assigninwith/-/lodash.assigninwith-4.0.7.tgz"
},
"lodash.clonedeep": {
"version": "4.4.0",
"from": "lodash.clonedeep@>=4.3.2 <5.0.0",
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.4.0.tgz"
},
"lodash.escape": {
"version": "4.0.0",
"from": "lodash.escape@>=4.0.0 <5.0.0",
@ -3405,6 +3580,18 @@
"from": "nested-error-stacks@>=1.0.1 <2.0.0",
"resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-1.0.2.tgz"
},
"node-gyp": {
"version": "3.4.0",
"from": "node-gyp@>=3.3.1 <4.0.0",
"resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.4.0.tgz",
"dependencies": {
"graceful-fs": {
"version": "4.1.4",
"from": "graceful-fs@^4.1.2",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.4.tgz"
}
}
},
"node-int64": {
"version": "0.4.0",
"from": "node-int64@>=0.4.0 <0.5.0",
@ -3435,6 +3622,18 @@
"from": "npm-run-path@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-1.0.0.tgz"
},
"npmlog": {
"version": "3.1.2",
"from": "npmlog@>=0.0.0 <1.0.0||>=1.0.0 <2.0.0||>=2.0.0 <3.0.0||>=3.0.0 <4.0.0",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-3.1.2.tgz",
"dependencies": {
"set-blocking": {
"version": "2.0.0",
"from": "set-blocking@>=2.0.0 <2.1.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz"
}
}
},
"nugget": {
"version": "1.6.2",
"from": "nugget@>=1.5.1 <2.0.0",
@ -3596,6 +3795,11 @@
"from": "parse-json@>=2.2.0 <3.0.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz"
},
"path-array": {
"version": "1.0.1",
"from": "path-array@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/path-array/-/path-array-1.0.1.tgz"
},
"path-browserify": {
"version": "0.0.0",
"from": "path-browserify@>=0.0.0 <0.1.0",
@ -4129,6 +4333,11 @@
"from": "samsam@1.1.2",
"resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.2.tgz"
},
"sass-graph": {
"version": "2.1.2",
"from": "sass-graph@>=2.1.1 <3.0.0",
"resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.1.2.tgz"
},
"sax": {
"version": "1.2.1",
"from": "sax@>=0.6.0",
@ -4288,6 +4497,23 @@
"from": "sprintf-js@>=1.0.2 <1.1.0",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz"
},
"sshpk": {
"version": "1.8.3",
"from": "sshpk@>=1.7.0 <2.0.0",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.8.3.tgz",
"dependencies": {
"asn1": {
"version": "0.2.3",
"from": "asn1@>=0.2.3 <0.3.0",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz"
},
"assert-plus": {
"version": "1.0.0",
"from": "assert-plus@>=1.0.0 <2.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz"
}
}
},
"stack-trace": {
"version": "0.0.9",
"from": "stack-trace@>=0.0.0 <0.1.0",
@ -4529,6 +4755,11 @@
"from": "tail@>=1.1.0 <2.0.0",
"resolved": "https://registry.npmjs.org/tail/-/tail-1.1.0.tgz"
},
"tar": {
"version": "2.2.1",
"from": "tar@>=2.0.0 <3.0.0",
"resolved": "https://registry.npmjs.org/tar/-/tar-2.2.1.tgz"
},
"tar-stream": {
"version": "1.5.2",
"from": "tar-stream@>=1.5.0 <2.0.0",
@ -4714,6 +4945,11 @@
"from": "tv4@>=1.2.7 <2.0.0",
"resolved": "https://registry.npmjs.org/tv4/-/tv4-1.2.7.tgz"
},
"tweetnacl": {
"version": "0.13.3",
"from": "tweetnacl@>=0.13.0 <0.14.0",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.13.3.tgz"
},
"type-check": {
"version": "0.3.2",
"from": "type-check@>=0.3.2 <0.4.0",
@ -4842,6 +5078,11 @@
}
}
},
"user-home": {
"version": "2.0.0",
"from": "user-home@>=2.0.0 <3.0.0",
"resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz"
},
"username": {
"version": "2.2.2",
"from": "username@>=2.1.0 <3.0.0",
@ -4884,6 +5125,11 @@
"from": "validate-npm-package-license@>=3.0.1 <4.0.0",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz"
},
"verror": {
"version": "1.3.6",
"from": "verror@1.3.6",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz"
},
"vm-browserify": {
"version": "0.0.4",
"from": "vm-browserify@>=0.0.1 <0.1.0",
@ -4899,6 +5145,11 @@
"from": "which@>=1.2.8 <2.0.0",
"resolved": "https://registry.npmjs.org/which/-/which-1.2.10.tgz"
},
"wide-align": {
"version": "1.1.0",
"from": "wide-align@>=1.1.0 <2.0.0",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.0.tgz"
},
"window-size": {
"version": "0.2.0",
"from": "window-size@>=0.2.0 <0.3.0",

View File

@ -65,7 +65,7 @@
"chalk": "^1.1.3",
"drivelist": "^3.2.2",
"electron-is-running-in-asar": "^1.0.0",
"etcher-image-stream": "^2.6.1",
"etcher-image-stream": "^3.0.1",
"etcher-image-write": "^6.0.0",
"etcher-latest-version": "^1.0.0",
"file-tail": "^0.3.0",

View File

@ -47,6 +47,29 @@ describe('Browser: SVGIcon', function() {
m.chai.expect(element.html()).to.equal(iconContents);
});
it('should inline raw svg contents in the element', function() {
const svg = '<svg><text>Raspbian</text></svg>';
const element = $compile(`<svg-icon path="${svg}"></svg-icon>`)($rootScope);
$rootScope.$digest();
m.chai.expect(element.html()).to.equal(svg);
});
it('should react to external updates', function() {
const scope = $rootScope.$new();
scope.name = 'Raspbian';
scope.getSvg = () => {
return `<svg><text>${scope.name}</text></svg>`;
};
const element = $compile('<svg-icon path="{{ getSvg() }}"></svg-icon>')(scope);
$rootScope.$digest();
m.chai.expect(element.html()).to.equal('<svg><text>Raspbian</text></svg>');
scope.name = 'Resin.io';
$rootScope.$digest();
m.chai.expect(element.html()).to.equal('<svg><text>Resin.io</text></svg>');
});
it('should default the size to 40x40 pixels', function() {
const icon = '../../../../../lib/gui/assets/etcher.svg';
const element = $compile(`<svg-icon path="${icon}">Resin.io</svg-icon>`)($rootScope);
@ -55,7 +78,7 @@ describe('Browser: SVGIcon', function() {
m.chai.expect(element.css('height')).to.equal('40px');
});
it('should be able to set a custom height', function() {
it('should be able to set a custom width', function() {
const icon = '../../../../../lib/gui/assets/etcher.svg';
const element = $compile(`<svg-icon path="${icon}" width="20px">Resin.io</svg-icon>`)($rootScope);
$rootScope.$digest();

View File

@ -37,6 +37,18 @@ describe('Browser: SelectionState', function() {
m.chai.expect(SelectionStateModel.getImageSize()).to.be.undefined;
});
it('getImageUrl() should return undefined', function() {
m.chai.expect(SelectionStateModel.getImageUrl()).to.be.undefined;
});
it('getImageName() should return undefined', function() {
m.chai.expect(SelectionStateModel.getImageName()).to.be.undefined;
});
it('getImageLogo() should return undefined', function() {
m.chai.expect(SelectionStateModel.getImageLogo()).to.be.undefined;
});
it('hasDrive() should return false', function() {
const hasDrive = SelectionStateModel.hasDrive();
m.chai.expect(hasDrive).to.be.false;
@ -303,7 +315,10 @@ describe('Browser: SelectionState', function() {
beforeEach(function() {
SelectionStateModel.setImage({
path: 'foo.img',
size: 999999999
size: 999999999,
url: 'https://www.raspbian.org',
name: 'Raspbian',
logo: '<svg><text fill="red">Raspbian</text></svg>'
});
});
@ -425,6 +440,33 @@ describe('Browser: SelectionState', function() {
});
describe('.getImageUrl()', function() {
it('should return the image url', function() {
const imageUrl = SelectionStateModel.getImageUrl();
m.chai.expect(imageUrl).to.equal('https://www.raspbian.org');
});
});
describe('.getImageName()', function() {
it('should return the image name', function() {
const imageName = SelectionStateModel.getImageName();
m.chai.expect(imageName).to.equal('Raspbian');
});
});
describe('.getImageLogo()', function() {
it('should return the image logo', function() {
const imageLogo = SelectionStateModel.getImageLogo();
m.chai.expect(imageLogo).to.equal('<svg><text fill="red">Raspbian</text></svg>');
});
});
describe('.hasImage()', function() {
it('should return true', function() {
@ -529,6 +571,36 @@ describe('Browser: SelectionState', function() {
}).to.throw('Invalid image size: 999999999');
});
it('should throw if url is defined but its not a string', function() {
m.chai.expect(function() {
SelectionStateModel.setImage({
path: 'foo.img',
size: 999999999,
url: 1234
});
}).to.throw('Invalid image url: 1234');
});
it('should throw if name is defined but its not a string', function() {
m.chai.expect(function() {
SelectionStateModel.setImage({
path: 'foo.img',
size: 999999999,
name: 1234
});
}).to.throw('Invalid image name: 1234');
});
it('should throw if logo is defined but its not a string', function() {
m.chai.expect(function() {
SelectionStateModel.setImage({
path: 'foo.img',
size: 999999999,
logo: 1234
});
}).to.throw('Invalid image logo: 1234');
});
});
});

View File

@ -17,7 +17,6 @@
'use strict';
const m = require('mochainon');
const os = require('os');
const angular = require('angular');
const electron = require('electron');
require('angular-mocks');
@ -44,26 +43,13 @@ describe('Browser: OSOpenExternal', function() {
m.chai.expect(element.css('cursor')).to.equal('pointer');
});
describe('given non linux', function() {
beforeEach(function() {
this.osPlatformStub = m.sinon.stub(os, 'platform');
this.osPlatformStub.returns('darwin');
});
afterEach(function() {
this.osPlatformStub.restore();
});
it('should call Electron shell.openExternal with the attribute value', function() {
const shellExternalStub = m.sinon.stub(electron.shell, 'openExternal');
const element = $compile('<span os-open-external="https://resin.io">Resin.io</span>')($rootScope);
element.triggerHandler('click');
$rootScope.$digest();
m.chai.expect(shellExternalStub).to.have.been.calledWith('https://resin.io');
shellExternalStub.restore();
});
it('should call Electron shell.openExternal with the attribute value', function() {
const shellExternalStub = m.sinon.stub(electron.shell, 'openExternal');
const element = $compile('<span os-open-external="https://resin.io">Resin.io</span>')($rootScope);
element.triggerHandler('click');
$rootScope.$digest();
m.chai.expect(shellExternalStub).to.have.been.calledWith('https://resin.io');
shellExternalStub.restore();
});
});