diff --git a/src/js/controller/write.js b/src/js/controller/write.js index 96236e9..46e5e35 100644 --- a/src/js/controller/write.js +++ b/src/js/controller/write.js @@ -30,7 +30,7 @@ define(function(require) { fillFields(replyTo); $scope.updatePreview(); - verify($scope.to[0]); + $scope.verify($scope.to[0]); }, close: function() { this.open = false; @@ -83,6 +83,9 @@ define(function(require) { // Editing headers // + /** + * This event is fired when editing the email address headers. It checks is space is pressed and if so, creates a new address field. + */ $scope.onAddressUpdate = function(field, index) { var recipient = field[index], address = recipient.address; @@ -97,10 +100,13 @@ define(function(require) { field.splice(field.indexOf(recipient), 1); } - verify(recipient); + $scope.verify(recipient); }; - function verify(recipient) { + /** + * Verify and email address and fetch its public key + */ + $scope.verify = function(recipient) { // set display to insecure while fetching keys recipient.key = undefined; recipient.secure = false; @@ -108,7 +114,7 @@ define(function(require) { // verify email address if (!util.validateEmailAddress(recipient.address)) { recipient.secure = undefined; - checkSendStatus(); + $scope.checkSendStatus(); return; } @@ -125,12 +131,15 @@ define(function(require) { recipient.secure = true; } - checkSendStatus(); + $scope.checkSendStatus(); $scope.$apply(); }); - } + }; - function checkSendStatus() { + /** + * Check if it is ok to send an email depending on the invitation state of the addresses + */ + $scope.checkSendStatus = function() { $scope.okToSend = false; $scope.sendBtnText = undefined; $scope.sendBtnSecure = undefined; @@ -165,7 +174,7 @@ define(function(require) { $scope.okToSend = true; $scope.sendBtnSecure = true; } - } + }; // // Editing email body diff --git a/test/new-unit/write-ctrl-test.js b/test/new-unit/write-ctrl-test.js index 69e3b51..c4490bf 100644 --- a/test/new-unit/write-ctrl-test.js +++ b/test/new-unit/write-ctrl-test.js @@ -47,7 +47,9 @@ define(function(require) { expect(scope.state.writer.open).to.be.false; expect(scope.state.writer.write).to.exist; expect(scope.state.writer.close).to.exist; - expect(scope.verifyTo).to.exist; + expect(scope.verify).to.exist; + expect(scope.onAddressUpdate).to.exist; + expect(scope.checkSendStatus).to.exist; expect(scope.updatePreview).to.exist; expect(scope.sendToOutbox).to.exist; }); @@ -65,22 +67,24 @@ define(function(require) { describe('write', function() { it('should prepare write view', function() { - var verifyToMock = sinon.stub(scope, 'verifyTo'); + var verifyMock = sinon.stub(scope, 'verify'); scope.state.writer.write(); expect(scope.writerTitle).to.equal('New email'); - expect(scope.to).to.equal(''); + expect(scope.to).to.deep.equal([{ + address: '' + }]); expect(scope.subject).to.equal(''); expect(scope.body).to.equal(''); expect(scope.ciphertextPreview).to.equal(''); - expect(verifyToMock.calledOnce).to.be.true; + expect(verifyMock.calledOnce).to.be.true; - scope.verifyTo.restore(); + scope.verify.restore(); }); it('should prefill write view for response', function() { - var verifyToMock = sinon.stub(scope, 'verifyTo'), + var verifyMock = sinon.stub(scope, 'verify'), address = 'pity@dafool', subject = 'Ermahgerd!', body = 'so much body!', @@ -96,61 +100,210 @@ define(function(require) { scope.state.writer.write(re); expect(scope.writerTitle).to.equal('Reply'); - expect(scope.to).to.equal(address); + expect(scope.to).to.deep.equal([{ + address: address, + }, { + address: '' + }]); expect(scope.subject).to.equal('Re: ' + subject); expect(scope.body).to.contain(body); expect(scope.ciphertextPreview).to.not.be.empty; - expect(verifyToMock.calledOnce).to.be.true; + expect(verifyMock.calledOnce).to.be.true; - scope.verifyTo.restore(); + scope.verify.restore(); }); }); - describe('verifyTo', function() { - it('should verify the recipient as secure', function() { - var id = scope.to = 'pity@da.fool'; - keychainMock.getReceiverPublicKey.withArgs(id).yields(null, { - userId: id - }); + describe('onAddressUpdate', function() { + var verifyMock; - scope.verifyTo(); - - expect(scope.toSecure).to.be.true; - expect(scope.sendBtnText).to.equal('Send securely'); + beforeEach(function() { + verifyMock = sinon.stub(scope, 'verify'); }); - it('should verify the recipient as not secure', function(done) { - var id = scope.to = 'pity@da.fool'; - keychainMock.getReceiverPublicKey.withArgs(id).yields({ + afterEach(function() { + scope.verify.restore(); + }); + + it('should add new field item if space is pressed', function() { + var to = [{ + address: 'asdf@asdf.de ' + }]; + scope.onAddressUpdate(to, 0); + + expect(to.length).to.equal(2); + expect(to[0].address).to.equal('asdf@asdf.de'); + expect(to[1].address).to.equal(''); + expect(verifyMock.calledOnce).to.be.true; + }); + + it('should remove field item if address is empty', function() { + var to = [{ + address: 'asdf@asdf.de' + }, { + address: '' + }]; + scope.onAddressUpdate(to, 1); + + expect(to.length).to.equal(1); + expect(to[0].address).to.equal('asdf@asdf.de'); + expect(verifyMock.calledOnce).to.be.true; + }); + + it('should not remove last field item if address is empty', function() { + var to = [{ + address: '' + }]; + scope.onAddressUpdate(to, 0); + + expect(to.length).to.equal(1); + expect(to[0].address).to.equal(''); + expect(verifyMock.calledOnce).to.be.true; + }); + + it('should do nothing for normal address', function() { + var to = [{ + address: 'asdf@asdf.de' + }]; + scope.onAddressUpdate(to, 0); + + expect(to.length).to.equal(1); + expect(to[0].address).to.equal('asdf@asdf.de'); + expect(verifyMock.calledOnce).to.be.true; + }); + }); + + describe('verify', function() { + var checkSendStatusMock; + + beforeEach(function() { + checkSendStatusMock = sinon.stub(scope, 'checkSendStatus'); + }); + + afterEach(function() { + scope.checkSendStatus.restore(); + }); + + it('should not work for invalid email addresses', function() { + var recipient = { + address: '' + }; + + scope.verify(recipient); + + expect(recipient.key).to.be.undefined; + expect(recipient.secure).to.be.undefined; + expect(scope.checkSendStatus.calledOnce).to.be.true; + expect(keychainMock.getReceiverPublicKey.called).to.be.false; + }); + + it('should not work for error in keychain', function(done) { + var recipient = { + address: 'asds@example.com' + }; + + keychainMock.getReceiverPublicKey.withArgs(recipient.address).yields({ errMsg: '404 not found yadda yadda' }); scope.onError = function() { - expect(scope.toSecure).to.be.false; - expect(scope.sendBtnText).to.equal('Invite & send securely'); + expect(recipient.key).to.be.undefined; + expect(recipient.secure).to.be.false; + expect(scope.checkSendStatus.called).to.be.false; + expect(keychainMock.getReceiverPublicKey.calledOnce).to.be.true; done(); }; - scope.verifyTo(); + scope.verify(recipient); }); - it('should reset display if there is no recipient', function() { - scope.to = undefined; - scope.verifyTo(); + it('should work', function(done) { + var recipient = { + address: 'asdf@example.com' + }; + + keychainMock.getReceiverPublicKey.yields(null, { + userId: 'asdf@example.com' + }); + scope.$apply = function() { + expect(recipient.key).to.deep.equal({ + userId: 'asdf@example.com' + }); + expect(recipient.secure).to.be.true; + expect(scope.checkSendStatus.calledOnce).to.be.true; + expect(keychainMock.getReceiverPublicKey.calledOnce).to.be.true; + done(); + }; + + scope.verify(recipient); + }); + }); + + describe('checkSendStatus', function() { + beforeEach(function() { + scope.state.writer.write(); + }); + + afterEach(function() {}); + + it('should not be able to send with no recipients', function() { + scope.checkSendStatus(); + + expect(scope.okToSend).to.be.false; + expect(scope.sendBtnText).to.be.undefined; + expect(scope.sendBtnSecure).to.be.undefined; + }); + + it('should not be to invite 1 user', function() { + scope.to = [{ + address: 'asdf@asdf.de' + }]; + scope.checkSendStatus(); + + expect(scope.okToSend).to.be.true; + expect(scope.sendBtnText).to.equal('Invite & send securely'); + expect(scope.sendBtnSecure).to.be.false; + }); + + it('should not be able to invite multiple recipients', function() { + scope.to = [{ + address: 'asdf@asdf.de' + }, { + address: 'asdf@asdfg.de' + }]; + scope.checkSendStatus(); + + expect(scope.okToSend).to.be.false; + expect(scope.sendBtnText).to.be.undefined; + expect(scope.sendBtnSecure).to.be.undefined; + }); + + it('should be able to send securely to multiple recipients', function() { + scope.to = [{ + address: 'asdf@asdf.de', + secure: true + }, { + address: 'asdf@asdfg.de', + secure: true + }]; + scope.checkSendStatus(); + + expect(scope.okToSend).to.be.true; + expect(scope.sendBtnText).to.equal('Send securely'); + expect(scope.sendBtnSecure).to.be.true; }); }); describe('send to outbox', function() { it('should work when offline', function(done) { - var verifyToSpy = sinon.spy(scope, 'verifyTo'), - re = { - from: [{ - address: 'pity@dafool' - }], - subject: 'Ermahgerd!', - sentDate: new Date(), - body: 'so much body!' - }; + var re = { + from: [{ + address: 'pity@dafool' + }], + subject: 'Ermahgerd!', + sentDate: new Date(), + body: 'so much body!' + }; scope.state.nav = { currentFolder: 'currentFolder' @@ -160,11 +313,9 @@ define(function(require) { scope.onError = function(err) { expect(err).to.not.exist; expect(scope.state.writer.open).to.be.false; - expect(verifyToSpy.calledOnce).to.be.true; expect(emailDaoMock.store.calledOnce).to.be.true; expect(emailDaoMock.sync.calledOnce).to.be.true; - scope.verifyTo.restore(); done(); }; @@ -178,15 +329,14 @@ define(function(require) { }); it('should work', function(done) { - var verifyToSpy = sinon.spy(scope, 'verifyTo'), - re = { - from: [{ - address: 'pity@dafool' - }], - subject: 'Ermahgerd!', - sentDate: new Date(), - body: 'so much body!' - }; + var re = { + from: [{ + address: 'pity@dafool' + }], + subject: 'Ermahgerd!', + sentDate: new Date(), + body: 'so much body!' + }; scope.state.nav = { currentFolder: 'currentFolder' @@ -196,11 +346,9 @@ define(function(require) { scope.onError = function(err) { expect(err).to.not.exist; expect(scope.state.writer.open).to.be.false; - expect(verifyToSpy.calledOnce).to.be.true; expect(emailDaoMock.store.calledOnce).to.be.true; expect(emailDaoMock.sync.calledOnce).to.be.true; - scope.verifyTo.restore(); done(); }; @@ -212,15 +360,14 @@ define(function(require) { }); it('should fail', function(done) { - var verifyToSpy = sinon.spy(scope, 'verifyTo'), - re = { - from: [{ - address: 'pity@dafool' - }], - subject: 'Ermahgerd!', - sentDate: new Date(), - body: 'so much body!' - }; + var re = { + from: [{ + address: 'pity@dafool' + }], + subject: 'Ermahgerd!', + sentDate: new Date(), + body: 'so much body!' + }; scope.state.nav = { currentFolder: 'currentFolder' @@ -230,11 +377,9 @@ define(function(require) { scope.onError = function(err) { expect(err).to.exist; expect(scope.state.writer.open).to.be.false; - expect(verifyToSpy.calledOnce).to.be.true; expect(emailDaoMock.store.calledOnce).to.be.true; expect(emailDaoMock.sync.calledOnce).to.be.true; - scope.verifyTo.restore(); done(); }; @@ -246,14 +391,20 @@ define(function(require) { }); it('should not work and not close the write view', function(done) { - scope.state.writer.open = true; - scope.to = 'a, b, c'; + scope.state.writer.write(); + + scope.to = [{ + address: 'pity@dafool.de', + key: { + publicKey: '----- PGP Stuff -----' + } + }]; scope.body = 'asd'; scope.subject = 'yaddablabla'; scope.toKey = 'Public Key'; emailDaoMock.store.withArgs(sinon.match(function(mail) { - return mail.from[0].address === emailAddress && mail.to.length === 3; + return mail.from[0].address === emailAddress && mail.to.length === 1 && mail.receiverKeys.length === 1; })).yields({ errMsg: 'snafu' }); @@ -264,7 +415,6 @@ define(function(require) { expect(emailDaoMock.store.calledOnce).to.be.true; done(); }; - scope.sendToOutbox(); }); });