diff --git a/.travis.yml b/.travis.yml
index 4ddb3a185..5c858b87f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,12 +1,13 @@
language: node_js
node_js:
- 'node' # Latest stable Node.js release
+cache:
+ directories:
+ - src/node_modules/
env:
- ON_TRAVIS=true
before_install: cd src
before_script:
- - npm install -g grunt
- - npm install -g grunt-cli
- npm install
script:
- npm run test
diff --git a/src/app/components/login/login.js b/src/app/components/login/login.js
index 980bc0037..7504e9205 100644
--- a/src/app/components/login/login.js
+++ b/src/app/components/login/login.js
@@ -21,7 +21,7 @@ app.component('login', {
this.$mdMedia = $mdMedia;
this.$cookies = $cookies;
- this.$scope.$watch('$ctrl.input_passphrase', this.isValid.bind(this));
+ this.$scope.$watch('$ctrl.input_passphrase', this.isValidPassphrase.bind(this));
this.$timeout(this.devTestAccount.bind(this), 200);
this.$scope.$watch(() => this.$mdMedia('xs') || this.$mdMedia('sm'), (wantsFullScreen) => {
@@ -35,20 +35,20 @@ app.component('login', {
this.seed = login.emptyBytes().map(() => '00');
}
- stop() {
- this.random = false;
+ stopNewPassphraseGeneration() {
+ this.generatingNewPassphrase = false;
this.$document.unbind('mousemove', this.listener);
}
- go() {
- this.passphrase = login.fix(this.input_passphrase);
+ doTheLogin() {
+ this.passphrase = login.fixCaseAndWhitespace(this.input_passphrase);
this.reset();
this.$timeout(this.onLogin);
}
- isValid(value) {
- const fixedValue = login.fix(value);
+ isValidPassphrase(value) {
+ const fixedValue = login.fixCaseAndWhitespace(value);
if (fixedValue === '') {
this.valid = 2;
@@ -59,10 +59,10 @@ app.component('login', {
}
}
- start() {
+ startGenratingNewPassphrase() {
this.reset();
- this.random = true;
+ this.generatingNewPassphrase = true;
let last = [0, 0];
let used = login.emptyBytes();
@@ -108,8 +108,8 @@ app.component('login', {
}
if (count >= total) {
- this.stop();
- this.setNew();
+ this.stopNewPassphraseGeneration();
+ this.setNewPassphrase(this.seed);
return;
}
}
@@ -119,15 +119,15 @@ app.component('login', {
this.$timeout(() => this.$document.mousemove(this.listener), 300);
}
- asd() {
+ simulateMousemove() {
this.$document.mousemove();
}
- setNew() {
- const passphrase = (new mnemonic(new Buffer(this.seed.join(''), 'hex'))).toString();
+ setNewPassphrase(seed) {
+ const passphrase = (new mnemonic(new Buffer(seed.join(''), 'hex'))).toString();
const ok = () => {
this.input_passphrase = passphrase;
- this.$timeout(this.go.bind(this), 100);
+ this.$timeout(this.doTheLogin.bind(this), 100);
};
this.$mdDialog.show({
@@ -172,11 +172,11 @@ app.component('login', {
const passphrase = this.$cookies.get('passphrase');
if (passphrase) {
this.input_passphrase = passphrase;
- this.$timeout(this.go.bind(this), 10);
+ this.$timeout(this.doTheLogin.bind(this), 10);
}
}
- static fix(v) {
+ static fixCaseAndWhitespace(v) {
return (v || '').replace(/ +/g, ' ').trim().toLowerCase();
}
diff --git a/src/app/components/login/login.pug b/src/app/components/login/login.pug
index d23faae4c..a630457db 100644
--- a/src/app/components/login/login.pug
+++ b/src/app/components/login/login.pug
@@ -6,17 +6,17 @@ md-card
form(ng-submit='$ctrl.go()')
md-input-container.md-block(md-is-error='$ctrl.valid === 0')
label Enter your passphrase
- input(type="{{ $ctrl.show_passphrase ? 'text' : 'password' }}", ng-model='$ctrl.input_passphrase', ng-disabled='$ctrl.random', autofocus)
+ input(type="{{ $ctrl.show_passphrase ? 'text' : 'password' }}", ng-model='$ctrl.input_passphrase', ng-disabled='$ctrl.generatingNewPassphrase', autofocus)
md-input-container.md-block
md-checkbox.md-primary(ng-model="$ctrl.show_passphrase", aria-label="Show passphrase") Show passphrase
md-content(layout='row', layout-align='center center')
- // md-button(ng-disabled='$ctrl.random', ng-click='$ctrl.devTestAccount()') Dev Test Account
- md-button.md-primary(ng-disabled='$ctrl.random', ng-click='$ctrl.start()') NEW ACCOUNT
+ // md-button(ng-disabled='$ctrl.generatingNewPassphrase', ng-click='$ctrl.devTestAccount()') Dev Test Account
+ md-button.md-primary(ng-disabled='$ctrl.random', ng-click='$ctrl.startGenratingNewPassphrase()') NEW ACCOUNT
md-button.md-raised.md-primary(md-autofocus, ng-disabled='$ctrl.valid !== 1', ng-click='$ctrl.go()') Login
- md-content(layout-padding, layout='column', layout-align='center center', ng-show='$ctrl.random')
+ md-content(layout-padding, layout='column', layout-align='center center', ng-show='$ctrl.generatingNewPassphrase')
h4.move(ng-show='$ctrl.mobileAndTabletcheck()') Enter text below to generate random bytes
h4.move(ng-hide='$ctrl.mobileAndTabletcheck()') Move your mouse to generate random bytes
- input.random-input(type="text", ng-keydown='$ctrl.asd()', ng-show='$ctrl.mobileAndTabletcheck()')
+ input.random-input(type="text", ng-keydown='$ctrl.simulateMousemove()', ng-show='$ctrl.mobileAndTabletcheck()')
md-progress-linear(md-mode='determinate', value='{{ $ctrl.progress }}')
md-content.bytes
span.byte(ng-repeat='byte in $ctrl.seed track by $index', ng-bind='byte', animate-on-change='byte')
diff --git a/src/karma.conf.js b/src/karma.conf.js
index 66a1f776c..c5ab74762 100644
--- a/src/karma.conf.js
+++ b/src/karma.conf.js
@@ -13,6 +13,7 @@ preprocessors[test] = ['webpack'];
var opts = {
onTravis: process.env.ON_TRAVIS,
+ live: process.env.LIVE,
};
module.exports = function(config) {
@@ -57,7 +58,7 @@ module.exports = function(config) {
// enable / disable watching file and executing tests whenever any file changes
- autoWatch: false,
+ autoWatch: opts.live,
ngHtml2JsPreprocessor: {
stripPrefix: 'app/components/',
@@ -77,7 +78,7 @@ module.exports = function(config) {
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
- singleRun: true,
+ singleRun: !opts.live,
client: {
mocha: {
opts: 'test/mocha.opts' // You can set opts to equal true then plugin will load opts from default location 'test/mocha.opts'
diff --git a/src/package.json b/src/package.json
index 619804cac..ea3f67858 100644
--- a/src/package.json
+++ b/src/package.json
@@ -5,7 +5,8 @@
"scripts": {
"build": "webpack --profile --progress --display-modules --display-exclude --display-chunks --display-cached --display-cached-assets",
"dev": "webpack-dev-server --host 0.0.0.0 --profile --progress",
- "test": "export NODE_ENV=test && karma start"
+ "test": "export NODE_ENV=test && karma start",
+ "test-live": "export NODE_ENV=test && export LIVE=true && karma start"
},
"dependencies": {
"angular": "=1.5.8",
@@ -70,6 +71,8 @@
"pug-loader": "=2.3.0",
"raw-loader": "=0.5.1",
"should": "=11.2.0",
+ "sinon": "=2.0.0",
+ "sinon-chai": "=2.8.0",
"style-loader": "=0.13.1",
"url-loader": "=0.5.7",
"webpack": "=1.13.1",
diff --git a/src/test/components/login/login.spec.js b/src/test/components/login/login.spec.js
index ae31f9289..bc3068dcd 100644
--- a/src/test/components/login/login.spec.js
+++ b/src/test/components/login/login.spec.js
@@ -1,23 +1,118 @@
+var sinon = require('sinon');
+var sinonChai = require('sinon-chai');
+var expect = chai.expect;
+chai.use(sinonChai);
+
describe('Login component', function() {
var $compile,
- $rootScope;
+ $rootScope,
+ element;
// Load the myApp module, which contains the directive
beforeEach(angular.mock.module("app"));
// Store references to $rootScope and $compile
// so they are available to all tests in this describe block
- beforeEach(inject(function(_$compile_, _$rootScope_){
+ beforeEach(inject(function(_$compile_, _$rootScope_) {
// The injector unwraps the underscores (_) from around the parameter names when matching
$compile = _$compile_;
$rootScope = _$rootScope_;
}));
- it('should contain header', function() {
+ beforeEach(function() {
// Compile a piece of HTML containing the directive
- var element = $compile("")($rootScope);
+ element = $compile('')($rootScope);
$rootScope.$digest();
- expect(element.html()).to.contain("Sign In");
+ });
+
+ var HEADER_TEXT = 'Sign In';
+ it('should contain header saying "' + HEADER_TEXT + '"', function() {
+ expect(element.find('.md-title').text()).to.equal(HEADER_TEXT);
+ });
+
+ var LABEL_TEXT = 'Enter your passphrase';
+ it('should contain a form with label saying "' + LABEL_TEXT + '"', function() {
+ expect(element.find('form label').text()).to.equal(LABEL_TEXT);
+ });
+
+ it('should contain an input field', function() {
+ expect(element.find('form input').html()).to.equal('');
+ });
+
+ var LOGIN_BUTTON_TEXT = 'Login';
+ it('should contain a button saying "' + LOGIN_BUTTON_TEXT + '"', function() {
+ expect(element.find('.md-raised').text()).to.equal(LOGIN_BUTTON_TEXT);
});
});
+describe('Login controller', function() {
+ beforeEach(angular.mock.module('app'));
+
+ var $controller,
+ $rootScope;
+
+ beforeEach(inject(function(_$componentController_, _$rootScope_) {
+ $componentController = _$componentController_;
+ $rootScope = _$rootScope_;
+ }));
+
+ describe('$scope.reset()', function() {
+ var $scope,
+ controller;
+
+ beforeEach(function() {
+ $scope = $rootScope.$new();
+ controller = $componentController('login', $scope, {});
+ });
+
+ it('makes input_passphrase empty', function() {
+ passphrase = 'TEST';
+ controller.input_passphrase = passphrase;
+ expect(controller.input_passphrase).to.equal(passphrase);
+ controller.reset();
+ expect(controller.input_passphrase).to.equal('');
+ });
+ });
+
+ describe('$scope.setNewPassphrase()', function() {
+ var $scope,
+ controller;
+
+ beforeEach(function() {
+ $scope = $rootScope.$new();
+ controller = $componentController('login', $scope, {});
+ });
+
+ it('opens a material design dialog', function() {
+ var seed = ['23', '34', '34', '34', '34', '34', '34', '34'];
+ var dialogSpy = sinon.spy(controller.$mdDialog, 'show');
+ controller.setNewPassphrase(seed);
+ expect(dialogSpy).to.have.been.calledWith();
+ });
+ });
+
+ describe('$scope.isValidPassphrase(value)', function() {
+ var $scope,
+ controller;
+
+ beforeEach(function() {
+ $scope = $rootScope.$new();
+ controller = $componentController('login', $scope, {});
+ });
+
+ it('sets $scope.valid = 2 if value is empty', function() {
+ controller.isValidPassphrase('');
+ expect(controller.valid).to.equal(2);
+ });
+
+ it('sets $scope.valid = 1 if value is valid', function() {
+ controller.isValidPassphrase('ability theme abandon abandon abandon abandon abandon abandon abandon abandon abandon absorb');
+ expect(controller.valid).to.equal(1);
+ });
+
+ it('sets $scope.valid = 0 if value is invalid', function() {
+ controller.isValidPassphrase('INVALID VALUE');
+ expect(controller.valid).to.equal(0);
+ });
+ });
+});
diff --git a/src/test/components/timestamp/timestamp.spec.js b/src/test/components/timestamp/timestamp.spec.js
new file mode 100644
index 000000000..cee468f8c
--- /dev/null
+++ b/src/test/components/timestamp/timestamp.spec.js
@@ -0,0 +1,28 @@
+describe('timestamp component', function() {
+ var $compile,
+ $rootScope,
+ element;
+
+ // Load the myApp module, which contains the directive
+ beforeEach(angular.mock.module('app'));
+
+ // Store references to $rootScope and $compile
+ // so they are available to all tests in this describe block
+ beforeEach(inject(function(_$compile_, _$rootScope_) {
+ // The injector unwraps the underscores (_) from around the parameter names when matching
+ $compile = _$compile_;
+ $rootScope = _$rootScope_;
+ }));
+
+ beforeEach(function() {
+ var liskEpoch = Date.UTC(2016, 4, 24, 17, 0, 0, 0);
+ $rootScope.currentTimestamp = Math.floor((new Date().valueOf() - liskEpoch) / 1000);
+
+ element = $compile('')($rootScope);
+ $rootScope.$digest();
+ });
+
+ it('should contain a timeago of the date', function() {
+ expect(element.text()).to.equal('a few seconds');
+ });
+});
diff --git a/src/test/components/top/top.spec.js b/src/test/components/top/top.spec.js
index 6a0cebd6f..e41995d32 100644
--- a/src/test/components/top/top.spec.js
+++ b/src/test/components/top/top.spec.js
@@ -1,14 +1,13 @@
-
describe('Top component', function() {
var $compile,
$rootScope;
// Load the myApp module, which contains the directive
- beforeEach(angular.mock.module("app"));
+ beforeEach(angular.mock.module('app'));
// Store references to $rootScope and $compile
// so they are available to all tests in this describe block
- beforeEach(inject(function(_$compile_, _$rootScope_){
+ beforeEach(inject(function(_$compile_, _$rootScope_) {
// The injector unwraps the underscores (_) from around the parameter names when matching
$compile = _$compile_;
$rootScope = _$rootScope_;
@@ -16,10 +15,10 @@ describe('Top component', function() {
it('should contain address', function() {
// Compile a piece of HTML containing the directive
- var element = $compile("")($rootScope);
+ var element = $compile('')($rootScope);
// fire all the watches, so the scope expression {{1 + 1}} will be evaluated
$rootScope.$digest();
// Check that the compiled element contains the templated content
- expect(element.html()).to.contain("address");
+ expect(element.html()).to.contain('address');
});
});
diff --git a/src/test/test.js b/src/test/test.js
index 39d45b3f6..8e9a4d053 100644
--- a/src/test/test.js
+++ b/src/test/test.js
@@ -4,4 +4,5 @@ require('chai');
require('./components/login/login.spec');
require('./components/top/top.spec');
+require('./components/timestamp/timestamp.spec');