1
0
mirror of https://github.com/moparisthebest/mail synced 2024-08-13 16:43:47 -04:00

Merge pull request #103 from whiteout-io/dev/WO-515

Dev/wo 515
This commit is contained in:
Tankred Hase 2014-08-05 19:01:35 +02:00
commit 04ed3dab68
9 changed files with 396 additions and 173 deletions

View File

@ -107,7 +107,7 @@ module.exports = function(grunt) {
},
js: {
files: ['src/js/**/*.js'],
tasks: ['copy:js']
tasks: ['copy:js', 'copy:integration']
},
lib: {
files: ['src/lib/**/*.js'],

View File

@ -11,7 +11,7 @@
},
"dependencies": {
"crypto-lib": "~0.2.1",
"imap-client": "~0.3.7",
"imap-client": "~0.4.0",
"mailreader": "~0.3.5",
"pgpmailer": "~0.3.11",
"pgpbuilder": "~0.3.7",
@ -41,4 +41,4 @@
"grunt-contrib-compress": "~0.5.2",
"grunt-node-webkit-builder": "~0.1.17"
}
}
}

View File

@ -92,9 +92,10 @@ define(function(require) {
iconPath: '/img/icon.png',
verificationUrl: '/verify/',
verificationUuidLength: 36,
dbVersion: 4,
dbVersion: 5,
appVersion: appVersion,
outboxMailboxPath: 'OUTBOX',
outboxMailboxName: 'Outbox',
outboxMailboxType: 'Outbox'
};

View File

@ -533,92 +533,25 @@ define(function(require) {
this.cc = [{
address: 'john.doe@gmail.com'
}]; // list of receivers
if (attachments) {
// body structure with three attachments
this.bodystructure = {
"1": {
"part": "1",
"type": "text/plain",
"parameters": {
"charset": "us-ascii"
},
"encoding": "7bit",
"size": 9,
"lines": 2
},
"2": {
"part": "2",
"type": "application/octet-stream",
"parameters": {
"name": "a.md"
},
"encoding": "7bit",
"size": 123,
"disposition": [{
"type": "attachment",
"filename": "a.md"
}]
},
"3": {
"part": "3",
"type": "application/octet-stream",
"parameters": {
"name": "b.md"
},
"encoding": "7bit",
"size": 456,
"disposition": [{
"type": "attachment",
"filename": "b.md"
}]
},
"4": {
"part": "4",
"type": "application/octet-stream",
"parameters": {
"name": "c.md"
},
"encoding": "7bit",
"size": 789,
"disposition": [{
"type": "attachment",
"filename": "c.md"
}]
},
"type": "multipart/mixed"
};
this.attachments = [{
"filename": "a.md",
"filesize": 123,
"mimeType": "text/x-markdown",
"part": "2",
"content": null
}, {
"filename": "b.md",
"filesize": 456,
"mimeType": "text/x-markdown",
"part": "3",
"content": null
}, {
"filename": "c.md",
"filesize": 789,
"mimeType": "text/x-markdown",
"part": "4",
"content": null
}];
} else {
this.bodystructure = {
"part": "1",
"type": "text/plain",
"parameters": {
"charset": "us-ascii"
},
"encoding": "7bit",
"size": 9,
"lines": 2
};
this.attachments = [];
}
this.attachments = attachments ? [{
"filename": "a.md",
"filesize": 123,
"mimeType": "text/x-markdown",
"part": "2",
"content": null
}, {
"filename": "b.md",
"filesize": 456,
"mimeType": "text/x-markdown",
"part": "3",
"content": null
}, {
"filename": "c.md",
"filesize": 789,
"mimeType": "text/x-markdown",
"part": "4",
"content": null
}] : [];
this.unread = unread;
this.answered = answered;
this.sentDate = new Date('Thu Sep 19 2013 20:41:23 GMT+0200 (CEST)');
@ -639,13 +572,14 @@ define(function(require) {
this.decrypted = true;
};
var dummys = [new Email(true, true), new Email(true, false, true), new Email(false, true, true), new Email(false, true)];
for (var i = 0; i < 100; i++) {
dummys.push(new Email(false));
var dummies = [],
i = 100;
while (i--) {
// every second/third/fourth dummy mail with unread/attachments/answered
dummies.push(new Email((i % 2 === 0), (i % 3 === 0), (i % 5 === 0)));
}
return dummys;
return dummies;
}
return MailListCtrl;

View File

@ -6,6 +6,38 @@ define(function(require) {
config = require('js/app-config').config,
str = require('js/app-config').string;
//
//
// Constants
//
//
var FOLDER_DB_TYPE = 'folders';
var SYNC_TYPE_NEW = 'new';
var SYNC_TYPE_DELETED = 'deleted';
var SYNC_TYPE_MSGS = 'messages';
var FOLDER_TYPE_INBOX = 'Inbox';
var FOLDER_TYPE_SENT = 'Sent';
var FOLDER_TYPE_DRAFTS = 'Drafts';
var FOLDER_TYPE_TRASH = 'Trash';
var MSG_ATTR_UID = 'uid';
var MSG_PART_ATTR_CONTENT = 'content';
var MSG_PART_TYPE_ATTACHMENT = 'attachment';
var MSG_PART_TYPE_ENCRYPTED = 'encrypted';
var MSG_PART_TYPE_SIGNED = 'signed';
var MSG_PART_TYPE_TEXT = 'text';
var MSG_PART_TYPE_HTML = 'html';
//
//
// Email Dao
//
//
/**
* High-level data access object that orchestrates everything around the handling of encrypted mails:
* PGP de-/encryption, receiving via IMAP, sending via SMTP, MIME parsing, local db persistence
@ -253,8 +285,8 @@ define(function(require) {
return;
}
var storedUids = _.pluck(storedMessages, 'uid'),
memoryUids = _.pluck(folder.messages, 'uid'),
var storedUids = _.pluck(storedMessages, MSG_ATTR_UID),
memoryUids = _.pluck(folder.messages, MSG_ATTR_UID),
newUids = _.difference(storedUids, memoryUids), // uids of messages that are not yet in memory
removedUids = _.difference(memoryUids, storedUids); // uids of messages that are no longer stored on the disk
@ -370,7 +402,7 @@ define(function(require) {
// this enables us to already show the attachment clip in the message list ui
messages.forEach(function(message) {
message.attachments = message.bodyParts.filter(function(bodyPart) {
return bodyPart.type === 'attachment';
return bodyPart.type === MSG_PART_TYPE_ATTACHMENT;
});
});
@ -401,7 +433,7 @@ define(function(require) {
return;
}
var body = _.pluck(filterBodyParts(parsedBodyParts, 'text'), 'content').join('\n'),
var body = _.pluck(filterBodyParts(parsedBodyParts, MSG_PART_TYPE_TEXT), MSG_PART_ATTR_CONTENT).join('\n'),
verificationUrlPrefix = config.cloudUrl + config.verificationUrl,
uuid = body.split(verificationUrlPrefix).pop().substr(0, config.verificationUuidLength),
uuidRegex = /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/;
@ -645,10 +677,10 @@ define(function(require) {
// we need to fetch the content for non-attachment body parts (encrypted, signed, text, html, resources referenced from the html)
// but we spare the effort and fetch attachment content later upon explicit user request.
var contentParts = localMessage.bodyParts.filter(function(bodyPart) {
return bodyPart.type !== "attachment" || (bodyPart.type === "attachment" && bodyPart.id);
return bodyPart.type !== MSG_PART_TYPE_ATTACHMENT || (bodyPart.type === MSG_PART_TYPE_ATTACHMENT && bodyPart.id);
});
var attachmentParts = localMessage.bodyParts.filter(function(bodyPart) {
return bodyPart.type === "attachment" && !bodyPart.id;
return bodyPart.type === MSG_PART_TYPE_ATTACHMENT && !bodyPart.id;
});
// do we need to fetch content from the imap server?
@ -700,7 +732,7 @@ define(function(require) {
function extractContent() {
if (message.encrypted) {
// show the encrypted message
message.body = filterBodyParts(message.bodyParts, 'encrypted')[0].content;
message.body = filterBodyParts(message.bodyParts, MSG_PART_TYPE_ENCRYPTED)[0].content;
return done();
}
@ -708,13 +740,13 @@ define(function(require) {
if (message.signed) {
// PGP/MIME signed
var signedRoot = filterBodyParts(message.bodyParts, 'signed')[0]; // in case of a signed message, you only want to show the signed content and ignore the rest
var signedRoot = filterBodyParts(message.bodyParts, MSG_PART_TYPE_SIGNED)[0]; // in case of a signed message, you only want to show the signed content and ignore the rest
message.signedMessage = signedRoot.signedMessage;
message.signature = signedRoot.signature;
root = signedRoot.content;
}
var body = _.pluck(filterBodyParts(root, 'text'), 'content').join('\n');
var body = _.pluck(filterBodyParts(root, MSG_PART_TYPE_TEXT), MSG_PART_ATTR_CONTENT).join('\n');
/*
* if the message is plain text and contains pgp/inline, we are only interested in the encrypted
@ -730,7 +762,7 @@ define(function(require) {
// replace the bodyParts info with an artificial bodyPart of type "encrypted"
message.bodyParts = [{
type: 'encrypted',
type: MSG_PART_TYPE_ENCRYPTED,
content: pgpInlineMatch[0],
_isPgpInline: true // used internally to avoid trying to parse non-MIME text with the mailreader
}];
@ -770,8 +802,8 @@ define(function(require) {
function setBody() {
message.body = body;
if (!message.clearSignedMessage) {
message.attachments = filterBodyParts(root, 'attachment');
message.html = _.pluck(filterBodyParts(root, 'html'), 'content').join('\n');
message.attachments = filterBodyParts(root, MSG_PART_TYPE_ATTACHMENT);
message.html = _.pluck(filterBodyParts(root, MSG_PART_TYPE_HTML), MSG_PART_ATTR_CONTENT).join('\n');
inlineExternalImages(message);
}
@ -863,7 +895,7 @@ define(function(require) {
}
// get the receiver's public key to check the message signature
var encryptedNode = filterBodyParts(message.bodyParts, 'encrypted')[0];
var encryptedNode = filterBodyParts(message.bodyParts, MSG_PART_TYPE_ENCRYPTED)[0];
var senderKey = senderPublicKey ? senderPublicKey.publicKey : undefined;
self._pgp.decrypt(encryptedNode.content, senderKey, function(err, decrypted, signaturesValid) {
if (err || !decrypted) {
@ -897,7 +929,7 @@ define(function(require) {
if (!message.signed) {
// message had no signature in the ciphertext, so there's a little extra effort to be done here
// is there a signed MIME node?
var signedRoot = filterBodyParts(root, 'signed')[0];
var signedRoot = filterBodyParts(root, MSG_PART_TYPE_SIGNED)[0];
if (!signedRoot) {
// no signed MIME node, obviously an unsigned PGP/MIME message
return setBody();
@ -927,9 +959,9 @@ define(function(require) {
function setBody() {
// we have successfully interpreted the descrypted message,
// so let's update the views on the message parts
message.body = _.pluck(filterBodyParts(root, 'text'), 'content').join('\n');
message.html = _.pluck(filterBodyParts(root, 'html'), 'content').join('\n');
message.attachments = _.reject(filterBodyParts(root, 'attachment'), function(attmt) {
message.body = _.pluck(filterBodyParts(root, MSG_PART_TYPE_TEXT), MSG_PART_ATTR_CONTENT).join('\n');
message.html = _.pluck(filterBodyParts(root, MSG_PART_TYPE_HTML), MSG_PART_ATTR_CONTENT).join('\n');
message.attachments = _.reject(filterBodyParts(root, MSG_PART_TYPE_ATTACHMENT), function(attmt) {
// remove the pgp-signature from the attachments
return attmt.mimeType === "application/pgp-signature";
});
@ -989,7 +1021,7 @@ define(function(require) {
// upload the sent message to the sent folder if necessary
var sentFolder = _.findWhere(self._account.folders, {
type: 'Sent'
type: FOLDER_TYPE_SENT
});
if (self.ignoreUploadOnSent || !sentFolder || !rfcText) {
@ -1039,7 +1071,7 @@ define(function(require) {
// upload the sent message to the sent folder if necessary
var sentFolder = _.findWhere(self._account.folders, {
type: 'Sent'
type: FOLDER_TYPE_SENT
});
if (self.ignoreUploadOnSent || !sentFolder || !rfcText) {
@ -1130,7 +1162,7 @@ define(function(require) {
var uids, highestModseq, lastUid;
uids = _.pluck(folder.messages, 'uid').sort(function(a, b) {
uids = _.pluck(folder.messages, MSG_ATTR_UID).sort(function(a, b) {
return a - b;
});
lastUid = uids[uids.length - 1];
@ -1153,7 +1185,7 @@ define(function(require) {
// set up the imap client to listen for changes in the inbox
var inbox = _.findWhere(self._account.folders, {
type: 'Inbox'
type: FOLDER_TYPE_INBOX
});
if (!inbox) {
@ -1199,14 +1231,14 @@ define(function(require) {
return;
}
if (options.type === 'new') {
if (options.type === SYNC_TYPE_NEW) {
// new messages available on imap, fetch from imap and store to disk and memory
self.fetchMessages({
folder: folder,
firstUid: Math.min.apply(null, options.list),
lastUid: Math.max.apply(null, options.list)
}, self.onError.bind(self));
} else if (options.type === 'deleted') {
} else if (options.type === SYNC_TYPE_DELETED) {
// messages have been deleted, remove from local storage and memory
options.list.forEach(function(uid) {
var message = _.findWhere(folder.messages, {
@ -1223,7 +1255,7 @@ define(function(require) {
localOnly: true
}, self.onError.bind(self));
});
} else if (options.type === 'messages') {
} else if (options.type === SYNC_TYPE_MSGS) {
// NB! several possible reasons why this could be called.
// if a message in the array has uid value and flag array, it had a possible flag update
options.list.forEach(function(changedMsg) {
@ -1268,13 +1300,12 @@ define(function(require) {
* @param {Function} callback Invoked when the folders are up to date
*/
EmailDAO.prototype._initFoldersFromDisk = function(callback) {
var self = this,
folderDbType = 'folders';
var self = this;
self.busy(); // start the spinner
// fetch list from local cache
self._devicestorage.listItems(folderDbType, 0, null, function(err, stored) {
self._devicestorage.listItems(FOLDER_DB_TYPE, 0, null, function(err, stored) {
if (err) {
return done(err);
}
@ -1297,8 +1328,7 @@ define(function(require) {
* @param {Function} callback Invoked when the folders are up to date
*/
EmailDAO.prototype._initFoldersFromImap = function(callback) {
var self = this,
folderDbType = 'folders';
var self = this;
self.busy(); // start the spinner
@ -1308,57 +1338,73 @@ define(function(require) {
return done(err);
}
// this array is dropped directly into the ui to create the folder list
var folders = [];
if (wellKnownFolders.inbox) {
folders.push(wellKnownFolders.inbox);
}
if (wellKnownFolders.sent) {
folders.push(wellKnownFolders.sent);
}
folders.push({
type: 'Outbox',
// initialize the folders to something meaningful if that hasn't already happened
self._account.folders = self._account.folders || [];
// smuggle the outbox into the well known folders, which is obv not present on imap...
wellKnownFolders[config.outboxMailboxType] = [{
name: config.outboxMailboxName,
type: config.outboxMailboxType,
path: config.outboxMailboxPath
});
if (wellKnownFolders.drafts) {
folders.push(wellKnownFolders.drafts);
}
if (wellKnownFolders.trash) {
folders.push(wellKnownFolders.trash);
}
}];
var foldersChanged = false; // indicates if are there any new/removed folders?
// indicates if we need to persist anything to disk
var foldersChanged = false;
// check for added folders
folders.forEach(function(folder) {
if (!_.findWhere(self._account.folders, {
path: folder.path
})) {
// add the missing folder
self._account.folders.push(folder);
// the folders listed in the navigation pane
[FOLDER_TYPE_INBOX, FOLDER_TYPE_SENT, config.outboxMailboxType, FOLDER_TYPE_DRAFTS, FOLDER_TYPE_TRASH].forEach(function(mbxType) {
var localFolderWithType, imapFolderWithPath;
// check if there is a folder of this type locally available
localFolderWithType = _.findWhere(self._account.folders, {
type: mbxType
});
if (localFolderWithType) {
// we have a local folder available, so let's check if this folder still exists on imap
imapFolderWithPath = _.findWhere(wellKnownFolders[mbxType], {
path: localFolderWithType.path
});
if (imapFolderWithPath) {
// folder present on imap, no need to update.
return;
}
// folder not present on imap, so remove the folder and see if there are any updates for this folder type
self._account.folders.splice(self._account.folders.indexOf(localFolderWithType), 1);
foldersChanged = true;
}
});
// check for deleted folders
self._account.folders.forEach(function(folder) {
if (!_.findWhere(folders, {
path: folder.path
})) {
// remove the obsolete folder
self._account.folders.splice(self._account.folders.indexOf(folder), 1);
foldersChanged = true;
if (!wellKnownFolders[mbxType] || !wellKnownFolders[mbxType].length) {
// no imap folders of the respective mailbox type, so nothing to do here
return;
}
/**
* we have no local folder of the type, so do something intelligent,
* i.e. take the first folder of the respective type
*/
self._account.folders.push(wellKnownFolders[mbxType][0]);
foldersChanged = true;
});
// if folder have changed, we need to persist them to disk.
// if folders have not changed, can fill them with messages directly
if (!foldersChanged) {
return self._initMessagesFromDisk(done);
}
// persist encrypted list in device storage
// NB! persis the array we received from IMAP! do *not* persist self._account.folders with all the messages...
self._devicestorage.storeList([folders], folderDbType, function(err) {
// note: the folders in the ui also include the messages array, so let's create a clean array here
var folders = self._account.folders.map(function(folder) {
return {
name: folder.name,
path: folder.path,
type: folder.type
};
});
self._devicestorage.storeList([folders], FOLDER_DB_TYPE, function(err) {
if (err) {
return done(err);
}
@ -1463,7 +1509,7 @@ define(function(require) {
}
var trash = _.findWhere(this._account.folders, {
type: 'Trash'
type: FOLDER_TYPE_TRASH
});
// there's no known trash folder to move the mail to or we're in the trash folder, so we can purge the message

View File

@ -5,7 +5,8 @@ define(function(require) {
updateV1 = require('js/util/update/update-v1'),
updateV2 = require('js/util/update/update-v2'),
updateV3 = require('js/util/update/update-v3'),
updateV4 = require('js/util/update/update-v4');
updateV4 = require('js/util/update/update-v4'),
updateV5 = require('js/util/update/update-v5');
/**
* Handles database migration
@ -13,7 +14,7 @@ define(function(require) {
var UpdateHandler = function(appConfigStorage, userStorage, auth) {
this._appConfigStorage = appConfigStorage;
this._userStorage = userStorage;
this._updateScripts = [updateV1, updateV2, updateV3, updateV4];
this._updateScripts = [updateV1, updateV2, updateV3, updateV4, updateV5];
this._auth = auth;
};

View File

@ -0,0 +1,56 @@
define(function() {
'use strict';
var FOLDER_TYPE_INBOX = 'Inbox';
var FOLDER_TYPE_SENT = 'Sent';
var FOLDER_TYPE_DRAFTS = 'Drafts';
var FOLDER_TYPE_TRASH = 'Trash';
var FOLDER_DB_TYPE = 'folders';
var VERSION_DB_TYPE = 'dbVersion';
var POST_UPDATE_DB_VERSION = 5;
/**
* Update handler for transition database version 4 -> 5
*
* Due to an overlooked issue, there may be multiple folders, e.g. for sent mails.
* This removes the "duplicate" folders.
*/
function update(options, callback) {
// remove the emails
options.userStorage.listItems(FOLDER_DB_TYPE, 0, null, function(err, stored) {
if (err) {
return callback(err);
}
var folders = stored[0] || [];
[FOLDER_TYPE_INBOX, FOLDER_TYPE_SENT, FOLDER_TYPE_DRAFTS, FOLDER_TYPE_TRASH].forEach(function(mbxType) {
var foldersForType = folders.filter(function(mbx) {
return mbx.type === mbxType;
});
if (foldersForType.length <= 1) {
return; // nothing to do here
}
// remove duplicate folders
for (var i = 1; i < foldersForType.length; i++) {
folders.splice(folders.indexOf(foldersForType[i]), 1);
}
});
options.userStorage.storeList([folders], FOLDER_DB_TYPE, function(err) {
if (err) {
return callback(err);
}
// update the database version to POST_UPDATE_DB_VERSION
options.appConfigStorage.storeList([POST_UPDATE_DB_VERSION], VERSION_DB_TYPE, callback);
});
});
}
return update;
});

View File

@ -40,30 +40,35 @@ define(function(require) {
asymKeySize = 2048;
inboxFolder = {
name: 'Inbox',
type: 'Inbox',
path: 'INBOX',
messages: []
};
sentFolder = {
name: 'Sent',
type: 'Sent',
path: 'SENT',
messages: []
};
draftsFolder = {
name: 'Drafts',
type: 'Drafts',
path: 'DRAFTS',
messages: []
};
outboxFolder = {
name: 'Outbox',
type: 'Outbox',
path: 'OUTBOX',
messages: []
};
trashFolder = {
name: 'Trash',
type: 'Trash',
path: 'TRASH',
messages: []
@ -1969,18 +1974,76 @@ define(function(require) {
it('should initialize from imap if online', function(done) {
account.folders = [];
imapClientStub.listWellKnownFolders.yieldsAsync(null, {
inbox: inboxFolder,
sent: sentFolder,
drafts: draftsFolder,
trash: trashFolder
Inbox: [inboxFolder],
Sent: [sentFolder],
Drafts: [draftsFolder],
Trash: [trashFolder]
});
devicestorageStub.storeList.withArgs(sinon.match(function(arg) {
expect(arg[0][0]).to.deep.equal(inboxFolder);
expect(arg[0][1]).to.deep.equal(sentFolder);
expect(arg[0][0].name).to.deep.equal(inboxFolder.name);
expect(arg[0][0].path).to.deep.equal(inboxFolder.path);
expect(arg[0][0].type).to.deep.equal(inboxFolder.type);
expect(arg[0][1].name).to.deep.equal(sentFolder.name);
expect(arg[0][1].path).to.deep.equal(sentFolder.path);
expect(arg[0][1].type).to.deep.equal(sentFolder.type);
expect(arg[0][2].name).to.deep.equal(outboxFolder.name);
expect(arg[0][2].path).to.deep.equal(outboxFolder.path);
expect(arg[0][2].type).to.deep.equal(outboxFolder.type);
expect(arg[0][3]).to.deep.equal(draftsFolder);
expect(arg[0][4]).to.deep.equal(trashFolder);
expect(arg[0][3].name).to.deep.equal(draftsFolder.name);
expect(arg[0][3].path).to.deep.equal(draftsFolder.path);
expect(arg[0][3].type).to.deep.equal(draftsFolder.type);
expect(arg[0][4].name).to.deep.equal(trashFolder.name);
expect(arg[0][4].path).to.deep.equal(trashFolder.path);
expect(arg[0][4].type).to.deep.equal(trashFolder.type);
return true;
}), 'folders').yieldsAsync();
dao.refreshFolder.yieldsAsync();
dao._initFoldersFromImap(function(err) {
expect(err).to.not.exist;
expect(imapClientStub.listWellKnownFolders.calledOnce).to.be.true;
expect(devicestorageStub.storeList.calledOnce).to.be.true;
done();
});
});
it('should update folders from imap', function(done) {
account.folders = [inboxFolder, outboxFolder, trashFolder, {
name: 'foo',
type: 'Sent',
path: 'bar',
}];
imapClientStub.listWellKnownFolders.yieldsAsync(null, {
Inbox: [inboxFolder],
Sent: [sentFolder],
Drafts: [draftsFolder],
Trash: [trashFolder]
});
devicestorageStub.storeList.withArgs(sinon.match(function(arg) {
expect(arg[0]).to.deep.equal([{
name: inboxFolder.name,
path: inboxFolder.path,
type: inboxFolder.type
}, {
name: outboxFolder.name,
path: outboxFolder.path,
type: outboxFolder.type
}, {
name: trashFolder.name,
path: trashFolder.path,
type: trashFolder.type
}, {
name: sentFolder.name,
path: sentFolder.path,
type: sentFolder.type
}, {
name: draftsFolder.name,
path: draftsFolder.path,
type: draftsFolder.type
}]);
return true;
}), 'folders').yieldsAsync();

View File

@ -277,7 +277,7 @@ define(function(require) {
var SMTP_DB_KEY = 'smtp';
var REALNAME_DB_KEY = 'realname';
var emailaddress = 'bla@blubb.io';
var imap = config.gmail.imap,
smtp = config.gmail.smtp;
@ -347,6 +347,128 @@ define(function(require) {
});
});
});
describe('v4 -> v5', function() {
var FOLDER_TYPE_INBOX = 'Inbox';
var FOLDER_TYPE_SENT = 'Sent';
var FOLDER_TYPE_DRAFTS = 'Drafts';
var FOLDER_TYPE_TRASH = 'Trash';
var FOLDER_DB_TYPE = 'folders';
var VERSION_DB_TYPE = 'dbVersion';
var POST_UPDATE_DB_VERSION = 5;
beforeEach(function() {
cfg.dbVersion = 5; // app requires database version 4
appConfigStorageStub.listItems.withArgs(VERSION_DB_TYPE).yieldsAsync(null, [4]); // database version is 4
});
afterEach(function() {
// database version is only queried for version checking prior to the update script
// so no need to check this in case-specific tests
expect(appConfigStorageStub.listItems.calledOnce).to.be.true;
});
it('should work', function(done) {
userStorageStub.listItems.withArgs(FOLDER_DB_TYPE).yieldsAsync(null, [
[{
name: 'inbox1',
type: FOLDER_TYPE_INBOX
}, {
name: 'inbox2',
type: FOLDER_TYPE_INBOX
}, {
name: 'sent1',
type: FOLDER_TYPE_SENT
}, {
name: 'sent2',
type: FOLDER_TYPE_SENT
}, {
name: 'drafts1',
type: FOLDER_TYPE_DRAFTS
}, {
name: 'drafts2',
type: FOLDER_TYPE_DRAFTS
}, {
name: 'trash1',
type: FOLDER_TYPE_TRASH
}, {
name: 'trash2',
type: FOLDER_TYPE_TRASH
}]
]);
userStorageStub.storeList.withArgs([
[{
name: 'inbox1',
type: FOLDER_TYPE_INBOX
}, {
name: 'sent1',
type: FOLDER_TYPE_SENT
}, {
name: 'drafts1',
type: FOLDER_TYPE_DRAFTS
}, {
name: 'trash1',
type: FOLDER_TYPE_TRASH
}]
], FOLDER_DB_TYPE).yieldsAsync();
appConfigStorageStub.storeList.withArgs([POST_UPDATE_DB_VERSION], VERSION_DB_TYPE).yieldsAsync();
updateHandler.update(function(error) {
expect(error).to.not.exist;
expect(userStorageStub.listItems.calledOnce).to.be.true;
expect(userStorageStub.storeList.calledOnce).to.be.true;
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
done();
});
});
it('should fail when persisting database version fails', function(done) {
userStorageStub.listItems.yieldsAsync(null, []);
userStorageStub.storeList.yieldsAsync();
appConfigStorageStub.storeList.yieldsAsync(new Error());
updateHandler.update(function(error) {
expect(error).to.exist;
expect(userStorageStub.listItems.calledOnce).to.be.true;
expect(userStorageStub.storeList.calledOnce).to.be.true;
expect(appConfigStorageStub.storeList.calledOnce).to.be.true;
done();
});
});
it('should fail when persisting folders fails', function(done) {
userStorageStub.listItems.yieldsAsync(null, []);
userStorageStub.storeList.yieldsAsync(new Error());
updateHandler.update(function(error) {
expect(error).to.exist;
expect(userStorageStub.listItems.calledOnce).to.be.true;
expect(userStorageStub.storeList.calledOnce).to.be.true;
expect(appConfigStorageStub.storeList.called).to.be.false;
done();
});
});
it('should fail when listing folders fails', function(done) {
userStorageStub.listItems.yieldsAsync(new Error());
updateHandler.update(function(error) {
expect(error).to.exist;
expect(userStorageStub.listItems.calledOnce).to.be.true;
expect(userStorageStub.storeList.called).to.be.false;
expect(appConfigStorageStub.storeList.called).to.be.false;
done();
});
});
});
});
});
});