emailjs-imap-client
Version:
JavaScript IMAP client
1,105 lines (1,065 loc) • 143 kB
JavaScript
"use strict";
var _client = _interopRequireWildcard(require("./client"));
var _emailjsImapHandler = require("emailjs-imap-handler");
var _common = require("./common");
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
/* eslint-disable no-unused-expressions */
describe('browserbox unit tests', () => {
var br;
beforeEach(() => {
const auth = {
user: 'baldrian',
pass: 'sleeper.de'
};
br = new _client.default('somehost', 1234, {
auth,
logLevel: _common.LOG_LEVEL_NONE
});
br.client.socket = {
send: () => {},
upgradeToSecure: () => {}
};
});
describe('#_onIdle', () => {
it('should call enterIdle', () => {
sinon.stub(br, 'enterIdle');
br._authenticated = true;
br._enteredIdle = false;
br._onIdle();
expect(br.enterIdle.callCount).to.equal(1);
});
it('should not call enterIdle', () => {
sinon.stub(br, 'enterIdle');
br._enteredIdle = true;
br._onIdle();
expect(br.enterIdle.callCount).to.equal(0);
});
});
describe('#openConnection', () => {
beforeEach(() => {
sinon.stub(br.client, 'connect');
sinon.stub(br.client, 'close');
sinon.stub(br.client, 'enqueueCommand');
});
it('should open connection', () => {
br.client.connect.returns(Promise.resolve());
br.client.enqueueCommand.returns(Promise.resolve({
capability: ['capa1', 'capa2']
}));
setTimeout(() => br.client.onready(), 0);
return br.openConnection().then(() => {
expect(br.client.connect.calledOnce).to.be.true;
expect(br.client.enqueueCommand.calledOnce).to.be.true;
expect(br._capability.length).to.equal(2);
expect(br._capability[0]).to.equal('capa1');
expect(br._capability[1]).to.equal('capa2');
});
});
});
describe('#connect', () => {
beforeEach(() => {
sinon.stub(br.client, 'connect');
sinon.stub(br.client, 'close');
sinon.stub(br, 'updateCapability');
sinon.stub(br, 'upgradeConnection');
sinon.stub(br, 'updateId');
sinon.stub(br, 'login');
sinon.stub(br, 'compressConnection');
});
it('should connect', () => {
br.client.connect.returns(Promise.resolve());
br.updateCapability.returns(Promise.resolve());
br.upgradeConnection.returns(Promise.resolve());
br.updateId.returns(Promise.resolve());
br.login.returns(Promise.resolve());
br.compressConnection.returns(Promise.resolve());
setTimeout(() => br.client.onready(), 0);
return br.connect().then(() => {
expect(br.client.connect.calledOnce).to.be.true;
expect(br.updateCapability.calledOnce).to.be.true;
expect(br.upgradeConnection.calledOnce).to.be.true;
expect(br.updateId.calledOnce).to.be.true;
expect(br.login.calledOnce).to.be.true;
expect(br.compressConnection.calledOnce).to.be.true;
});
});
it('should fail to login', done => {
br.client.connect.returns(Promise.resolve());
br.updateCapability.returns(Promise.resolve());
br.upgradeConnection.returns(Promise.resolve());
br.updateId.returns(Promise.resolve());
br.login.throws(new Error());
setTimeout(() => br.client.onready(), 0);
br.connect().catch(err => {
expect(err).to.exist;
expect(br.client.connect.calledOnce).to.be.true;
expect(br.client.close.calledOnce).to.be.true;
expect(br.updateCapability.calledOnce).to.be.true;
expect(br.upgradeConnection.calledOnce).to.be.true;
expect(br.updateId.calledOnce).to.be.true;
expect(br.login.calledOnce).to.be.true;
expect(br.compressConnection.called).to.be.false;
done();
});
});
it('should timeout', done => {
br.client.connect.returns(Promise.resolve());
br.timeoutConnection = 1;
br.connect().catch(err => {
expect(err).to.exist;
expect(br.client.connect.calledOnce).to.be.true;
expect(br.client.close.calledOnce).to.be.true;
expect(br.updateCapability.called).to.be.false;
expect(br.upgradeConnection.called).to.be.false;
expect(br.updateId.called).to.be.false;
expect(br.login.called).to.be.false;
expect(br.compressConnection.called).to.be.false;
done();
});
});
});
describe('#close', () => {
it('should force-close', () => {
sinon.stub(br.client, 'close').returns(Promise.resolve());
return br.close().then(() => {
expect(br._state).to.equal(_client.STATE_LOGOUT);
expect(br.client.close.calledOnce).to.be.true;
});
});
});
describe('#exec', () => {
beforeEach(() => {
sinon.stub(br, 'breakIdle');
});
it('should send string command', () => {
sinon.stub(br.client, 'enqueueCommand').returns(Promise.resolve({}));
return br.exec('TEST').then(res => {
expect(res).to.deep.equal({});
expect(br.client.enqueueCommand.args[0][0]).to.equal('TEST');
});
});
it('should update capability from response', () => {
sinon.stub(br.client, 'enqueueCommand').returns(Promise.resolve({
capability: ['A', 'B']
}));
return br.exec('TEST').then(res => {
expect(res).to.deep.equal({
capability: ['A', 'B']
});
expect(br._capability).to.deep.equal(['A', 'B']);
});
});
});
describe('#enterIdle', () => {
it('should periodically send NOOP if IDLE not supported', done => {
sinon.stub(br, 'exec').callsFake(command => {
expect(command).to.equal('NOOP');
done();
});
br._capability = [];
br._selectedMailbox = 'FOO';
br.timeoutNoop = 1;
br.enterIdle();
});
it('should periodically send NOOP if no mailbox selected', done => {
sinon.stub(br, 'exec').callsFake(command => {
expect(command).to.equal('NOOP');
done();
});
br._capability = ['IDLE'];
br._selectedMailbox = undefined;
br.timeoutNoop = 1;
br.enterIdle();
});
it('should break IDLE after timeout', done => {
sinon.stub(br.client, 'enqueueCommand');
sinon.stub(br.client.socket, 'send').callsFake(payload => {
expect(br.client.enqueueCommand.args[0][0].command).to.equal('IDLE');
expect([].slice.call(new Uint8Array(payload))).to.deep.equal([0x44, 0x4f, 0x4e, 0x45, 0x0d, 0x0a]);
done();
});
br._capability = ['IDLE'];
br._selectedMailbox = 'FOO';
br.timeoutIdle = 1;
br.enterIdle();
});
});
describe('#breakIdle', () => {
it('should send DONE to socket', () => {
sinon.stub(br.client.socket, 'send');
br._enteredIdle = 'IDLE';
br.breakIdle();
expect([].slice.call(new Uint8Array(br.client.socket.send.args[0][0]))).to.deep.equal([0x44, 0x4f, 0x4e, 0x45, 0x0d, 0x0a]);
});
});
describe('#upgradeConnection', () => {
it('should do nothing if already secured', () => {
br.client.secureMode = true;
br._capability = ['starttls'];
return br.upgradeConnection();
});
it('should do nothing if STARTTLS not available', () => {
br.client.secureMode = false;
br._capability = [];
return br.upgradeConnection();
});
it('should run STARTTLS', () => {
sinon.stub(br.client, 'upgrade');
sinon.stub(br, 'exec').withArgs('STARTTLS').returns(Promise.resolve());
sinon.stub(br, 'updateCapability').returns(Promise.resolve());
br._capability = ['STARTTLS'];
return br.upgradeConnection().then(() => {
expect(br.client.upgrade.callCount).to.equal(1);
expect(br._capability.length).to.equal(0);
});
});
});
describe('#updateCapability', () => {
beforeEach(() => {
sinon.stub(br, 'exec');
});
it('should do nothing if capability is set', () => {
br._capability = ['abc'];
return br.updateCapability();
});
it('should run CAPABILITY if capability not set', () => {
br.exec.returns(Promise.resolve());
br._capability = [];
return br.updateCapability().then(() => {
expect(br.exec.args[0][0]).to.equal('CAPABILITY');
});
});
it('should force run CAPABILITY', () => {
br.exec.returns(Promise.resolve());
br._capability = ['abc'];
return br.updateCapability(true).then(() => {
expect(br.exec.args[0][0]).to.equal('CAPABILITY');
});
});
it('should do nothing if connection is not yet upgraded', () => {
br._capability = [];
br.client.secureMode = false;
br._requireTLS = true;
br.updateCapability();
});
});
describe('#listNamespaces', () => {
beforeEach(() => {
sinon.stub(br, 'exec');
});
it('should run NAMESPACE if supported', () => {
br.exec.returns(Promise.resolve({
payload: {
NAMESPACE: [{
attributes: [[[{
type: 'STRING',
value: 'INBOX.'
}, {
type: 'STRING',
value: '.'
}]], null, null]
}]
}
}));
br._capability = ['NAMESPACE'];
return br.listNamespaces().then(namespaces => {
expect(namespaces).to.deep.equal({
personal: [{
prefix: 'INBOX.',
delimiter: '.'
}],
users: false,
shared: false
});
expect(br.exec.args[0][0]).to.equal('NAMESPACE');
expect(br.exec.args[0][1]).to.equal('NAMESPACE');
});
});
it('should do nothing if not supported', () => {
br._capability = [];
return br.listNamespaces().then(namespaces => {
expect(namespaces).to.be.false;
expect(br.exec.callCount).to.equal(0);
});
});
});
describe('#compressConnection', () => {
beforeEach(() => {
sinon.stub(br, 'exec');
sinon.stub(br.client, 'enableCompression');
});
it('should run COMPRESS=DEFLATE if supported', () => {
br.exec.withArgs({
command: 'COMPRESS',
attributes: [{
type: 'ATOM',
value: 'DEFLATE'
}]
}).returns(Promise.resolve({}));
br._enableCompression = true;
br._capability = ['COMPRESS=DEFLATE'];
return br.compressConnection().then(() => {
expect(br.exec.callCount).to.equal(1);
expect(br.client.enableCompression.callCount).to.equal(1);
});
});
it('should do nothing if not supported', () => {
br._capability = [];
return br.compressConnection().then(() => {
expect(br.exec.callCount).to.equal(0);
});
});
it('should do nothing if not enabled', () => {
br._enableCompression = false;
br._capability = ['COMPRESS=DEFLATE'];
return br.compressConnection().then(() => {
expect(br.exec.callCount).to.equal(0);
});
});
});
describe('#login', () => {
it('should call LOGIN', () => {
sinon.stub(br, 'exec').returns(Promise.resolve({}));
sinon.stub(br, 'updateCapability').returns(Promise.resolve(true));
return br.login({
user: 'u1',
pass: 'p1'
}).then(() => {
expect(br.exec.callCount).to.equal(1);
expect(br.exec.args[0][0]).to.deep.equal({
command: 'login',
attributes: [{
type: 'STRING',
value: 'u1'
}, {
type: 'STRING',
value: 'p1',
sensitive: true
}]
});
});
});
it('should call XOAUTH2', () => {
sinon.stub(br, 'exec').returns(Promise.resolve({}));
sinon.stub(br, 'updateCapability').returns(Promise.resolve(true));
br._capability = ['AUTH=XOAUTH2'];
br.login({
user: 'u1',
xoauth2: 'abc'
}).then(() => {
expect(br.exec.callCount).to.equal(1);
expect(br.exec.args[0][0]).to.deep.equal({
command: 'AUTHENTICATE',
attributes: [{
type: 'ATOM',
value: 'XOAUTH2'
}, {
type: 'ATOM',
value: 'dXNlcj11MQFhdXRoPUJlYXJlciBhYmMBAQ==',
sensitive: true
}]
});
});
});
});
describe('#updateId', () => {
beforeEach(() => {
sinon.stub(br, 'exec');
});
it('should not nothing if not supported', () => {
br._capability = [];
return br.updateId({
a: 'b',
c: 'd'
}).then(() => {
expect(br.serverId).to.be.false;
});
});
it('should send NIL', () => {
br.exec.withArgs({
command: 'ID',
attributes: [null]
}).returns(Promise.resolve({
payload: {
ID: [{
attributes: [null]
}]
}
}));
br._capability = ['ID'];
return br.updateId(null).then(() => {
expect(br.serverId).to.deep.equal({});
});
});
it('should exhange ID values', () => {
br.exec.withArgs({
command: 'ID',
attributes: [['ckey1', 'cval1', 'ckey2', 'cval2']]
}).returns(Promise.resolve({
payload: {
ID: [{
attributes: [[{
value: 'skey1'
}, {
value: 'sval1'
}, {
value: 'skey2'
}, {
value: 'sval2'
}]]
}]
}
}));
br._capability = ['ID'];
return br.updateId({
ckey1: 'cval1',
ckey2: 'cval2'
}).then(() => {
expect(br.serverId).to.deep.equal({
skey1: 'sval1',
skey2: 'sval2'
});
});
});
});
describe('#listMailboxes', () => {
beforeEach(() => {
sinon.stub(br, 'exec');
});
it('should call LIST and LSUB in sequence', () => {
br.exec.withArgs({
command: 'LIST',
attributes: ['', '*']
}).returns(Promise.resolve({
payload: {
LIST: [false]
}
}));
br.exec.withArgs({
command: 'LSUB',
attributes: ['', '*']
}).returns(Promise.resolve({
payload: {
LSUB: [false]
}
}));
return br.listMailboxes().then(tree => {
expect(tree).to.exist;
});
});
it('should not die on NIL separators', () => {
br.exec.withArgs({
command: 'LIST',
attributes: ['', '*']
}).returns(Promise.resolve({
payload: {
LIST: [(0, _emailjsImapHandler.parser)((0, _common.toTypedArray)('* LIST (\\NoInferiors) NIL "INBOX"'))]
}
}));
br.exec.withArgs({
command: 'LSUB',
attributes: ['', '*']
}).returns(Promise.resolve({
payload: {
LSUB: [(0, _emailjsImapHandler.parser)((0, _common.toTypedArray)('* LSUB (\\NoInferiors) NIL "INBOX"'))]
}
}));
return br.listMailboxes().then(tree => {
expect(tree).to.exist;
});
});
});
describe('#createMailbox', () => {
beforeEach(() => {
sinon.stub(br, 'exec');
});
it('should call CREATE with a string payload', () => {
// The spec allows unquoted ATOM-style syntax too, but for
// simplicity we always generate a string even if it could be
// expressed as an atom.
br.exec.withArgs({
command: 'CREATE',
attributes: ['mailboxname']
}).returns(Promise.resolve());
return br.createMailbox('mailboxname').then(() => {
expect(br.exec.callCount).to.equal(1);
});
});
it('should call mutf7 encode the argument', () => {
// From RFC 3501
br.exec.withArgs({
command: 'CREATE',
attributes: ['~peter/mail/&U,BTFw-/&ZeVnLIqe-']
}).returns(Promise.resolve());
return br.createMailbox('~peter/mail/\u53f0\u5317/\u65e5\u672c\u8a9e').then(() => {
expect(br.exec.callCount).to.equal(1);
});
});
it('should treat an ALREADYEXISTS response as success', () => {
var fakeErr = {
code: 'ALREADYEXISTS'
};
br.exec.withArgs({
command: 'CREATE',
attributes: ['mailboxname']
}).returns(Promise.reject(fakeErr));
return br.createMailbox('mailboxname').then(() => {
expect(br.exec.callCount).to.equal(1);
});
});
});
describe('#deleteMailbox', () => {
beforeEach(() => {
sinon.stub(br, 'exec');
});
it('should call DELETE with a string payload', () => {
br.exec.withArgs({
command: 'DELETE',
attributes: ['mailboxname']
}).returns(Promise.resolve());
return br.deleteMailbox('mailboxname').then(() => {
expect(br.exec.callCount).to.equal(1);
});
});
it('should call mutf7 encode the argument', () => {
// From RFC 3501
br.exec.withArgs({
command: 'DELETE',
attributes: ['~peter/mail/&U,BTFw-/&ZeVnLIqe-']
}).returns(Promise.resolve());
return br.deleteMailbox('~peter/mail/\u53f0\u5317/\u65e5\u672c\u8a9e').then(() => {
expect(br.exec.callCount).to.equal(1);
});
});
});
describe.skip('#listMessages', () => {
beforeEach(() => {
sinon.stub(br, 'exec');
sinon.stub(br, '_buildFETCHCommand');
sinon.stub(br, '_parseFETCH');
});
it('should call FETCH', () => {
br.exec.returns(Promise.resolve('abc'));
br._buildFETCHCommand.withArgs(['1:2', ['uid', 'flags'], {
byUid: true
}]).returns({});
return br.listMessages('INBOX', '1:2', ['uid', 'flags'], {
byUid: true
}).then(() => {
expect(br._buildFETCHCommand.callCount).to.equal(1);
expect(br._parseFETCH.withArgs('abc').callCount).to.equal(1);
});
});
});
describe.skip('#search', () => {
beforeEach(() => {
sinon.stub(br, 'exec');
sinon.stub(br, '_buildSEARCHCommand');
sinon.stub(br, '_parseSEARCH');
});
it('should call SEARCH', () => {
br.exec.returns(Promise.resolve('abc'));
br._buildSEARCHCommand.withArgs({
uid: 1
}, {
byUid: true
}).returns({});
return br.search('INBOX', {
uid: 1
}, {
byUid: true
}).then(() => {
expect(br._buildSEARCHCommand.callCount).to.equal(1);
expect(br.exec.callCount).to.equal(1);
expect(br._parseSEARCH.withArgs('abc').callCount).to.equal(1);
});
});
});
describe('#upload', () => {
beforeEach(() => {
sinon.stub(br, 'exec');
});
it('should call APPEND with custom flag', () => {
br.exec.returns(Promise.resolve());
return br.upload('mailbox', 'this is a message', {
flags: ['\\$MyFlag']
}).then(() => {
expect(br.exec.callCount).to.equal(1);
});
});
it('should call APPEND w/o flags', () => {
br.exec.returns(Promise.resolve());
return br.upload('mailbox', 'this is a message').then(() => {
expect(br.exec.callCount).to.equal(1);
});
});
});
describe.skip('#setFlags', () => {
beforeEach(() => {
sinon.stub(br, 'exec');
sinon.stub(br, '_buildSTORECommand');
sinon.stub(br, '_parseFETCH');
});
it('should call STORE', () => {
br.exec.returns(Promise.resolve('abc'));
br._buildSTORECommand.withArgs('1:2', 'FLAGS', ['\\Seen', '$MyFlag'], {
byUid: true
}).returns({});
return br.setFlags('INBOX', '1:2', ['\\Seen', '$MyFlag'], {
byUid: true
}).then(() => {
expect(br.exec.callCount).to.equal(1);
expect(br._parseFETCH.withArgs('abc').callCount).to.equal(1);
});
});
});
describe.skip('#store', () => {
beforeEach(() => {
sinon.stub(br, 'exec');
sinon.stub(br, '_buildSTORECommand');
sinon.stub(br, '_parseFETCH');
});
it('should call STORE', () => {
br.exec.returns(Promise.resolve('abc'));
br._buildSTORECommand.withArgs('1:2', '+X-GM-LABELS', ['\\Sent', '\\Junk'], {
byUid: true
}).returns({});
return br.store('INBOX', '1:2', '+X-GM-LABELS', ['\\Sent', '\\Junk'], {
byUid: true
}).then(() => {
expect(br._buildSTORECommand.callCount).to.equal(1);
expect(br.exec.callCount).to.equal(1);
expect(br._parseFETCH.withArgs('abc').callCount).to.equal(1);
});
});
});
describe('#deleteMessages', () => {
beforeEach(() => {
sinon.stub(br, 'setFlags');
sinon.stub(br, 'exec');
});
it('should call UID EXPUNGE', () => {
br.exec.withArgs({
command: 'UID EXPUNGE',
attributes: [{
type: 'sequence',
value: '1:2'
}]
}).returns(Promise.resolve('abc'));
br.setFlags.withArgs('INBOX', '1:2', {
add: '\\Deleted'
}).returns(Promise.resolve());
br._capability = ['UIDPLUS'];
return br.deleteMessages('INBOX', '1:2', {
byUid: true
}).then(() => {
expect(br.exec.callCount).to.equal(1);
});
});
it('should call EXPUNGE', () => {
br.exec.withArgs('EXPUNGE').returns(Promise.resolve('abc'));
br.setFlags.withArgs('INBOX', '1:2', {
add: '\\Deleted'
}).returns(Promise.resolve());
br._capability = [];
return br.deleteMessages('INBOX', '1:2', {
byUid: true
}).then(() => {
expect(br.exec.callCount).to.equal(1);
});
});
});
describe('#copyMessages', () => {
beforeEach(() => {
sinon.stub(br, 'exec');
});
it('should call COPY', () => {
br.exec.withArgs({
command: 'UID COPY',
attributes: [{
type: 'sequence',
value: '1:2'
}, {
type: 'atom',
value: '[Gmail]/Trash'
}]
}).returns(Promise.resolve({
copyuid: ['1', '1:2', '4,3']
}));
return br.copyMessages('INBOX', '1:2', '[Gmail]/Trash', {
byUid: true
}).then(response => {
expect(response).to.deep.equal({
srcSeqSet: '1:2',
destSeqSet: '4,3'
});
expect(br.exec.callCount).to.equal(1);
});
});
});
describe('#moveMessages', () => {
beforeEach(() => {
sinon.stub(br, 'exec');
sinon.stub(br, 'copyMessages');
sinon.stub(br, 'deleteMessages');
});
it('should call MOVE if supported', () => {
br.exec.withArgs({
command: 'UID MOVE',
attributes: [{
type: 'sequence',
value: '1:2'
}, {
type: 'atom',
value: '[Gmail]/Trash'
}]
}, ['OK']).returns(Promise.resolve('abc'));
br._capability = ['MOVE'];
return br.moveMessages('INBOX', '1:2', '[Gmail]/Trash', {
byUid: true
}).then(() => {
expect(br.exec.callCount).to.equal(1);
});
});
it('should fallback to copy+expunge', () => {
br.copyMessages.withArgs('INBOX', '1:2', '[Gmail]/Trash', {
byUid: true
}).returns(Promise.resolve());
br.deleteMessages.withArgs('1:2', {
byUid: true
}).returns(Promise.resolve());
br._capability = [];
return br.moveMessages('INBOX', '1:2', '[Gmail]/Trash', {
byUid: true
}).then(() => {
expect(br.deleteMessages.callCount).to.equal(1);
});
});
});
describe('#_shouldSelectMailbox', () => {
it('should return true when ctx is undefined', () => {
expect(br._shouldSelectMailbox('path')).to.be.true;
});
it('should return true when a different path is queued', () => {
sinon.stub(br.client, 'getPreviouslyQueued').returns({
request: {
command: 'SELECT',
attributes: [{
type: 'STRING',
value: 'queued path'
}]
}
});
expect(br._shouldSelectMailbox('path', {})).to.be.true;
});
it('should return false when the same path is queued', () => {
sinon.stub(br.client, 'getPreviouslyQueued').returns({
request: {
command: 'SELECT',
attributes: [{
type: 'STRING',
value: 'queued path'
}]
}
});
expect(br._shouldSelectMailbox('queued path', {})).to.be.false;
});
});
describe('#selectMailbox', () => {
const path = '[Gmail]/Trash';
beforeEach(() => {
sinon.stub(br, 'exec');
});
it('should run SELECT', () => {
br.exec.withArgs({
command: 'SELECT',
attributes: [{
type: 'STRING',
value: path
}]
}).returns(Promise.resolve({
code: 'READ-WRITE'
}));
return br.selectMailbox(path).then(() => {
expect(br.exec.callCount).to.equal(1);
expect(br._state).to.equal(_client.STATE_SELECTED);
});
});
it('should run SELECT with CONDSTORE', () => {
br.exec.withArgs({
command: 'SELECT',
attributes: [{
type: 'STRING',
value: path
}, [{
type: 'ATOM',
value: 'CONDSTORE'
}]]
}).returns(Promise.resolve({
code: 'READ-WRITE'
}));
br._capability = ['CONDSTORE'];
return br.selectMailbox(path, {
condstore: true
}).then(() => {
expect(br.exec.callCount).to.equal(1);
expect(br._state).to.equal(_client.STATE_SELECTED);
});
});
describe('should emit onselectmailbox before selectMailbox is resolved', () => {
beforeEach(() => {
br.exec.returns(Promise.resolve({
code: 'READ-WRITE'
}));
});
it('when it returns a promise', () => {
var promiseResolved = false;
br.onselectmailbox = () => new Promise(resolve => {
resolve();
promiseResolved = true;
});
var onselectmailboxSpy = sinon.spy(br, 'onselectmailbox');
return br.selectMailbox(path).then(() => {
expect(onselectmailboxSpy.withArgs(path).callCount).to.equal(1);
expect(promiseResolved).to.equal(true);
});
});
it('when it does not return a promise', () => {
br.onselectmailbox = () => {};
var onselectmailboxSpy = sinon.spy(br, 'onselectmailbox');
return br.selectMailbox(path).then(() => {
expect(onselectmailboxSpy.withArgs(path).callCount).to.equal(1);
});
});
});
it('should emit onclosemailbox', () => {
let called = false;
br.exec.returns(Promise.resolve('abc')).returns(Promise.resolve({
code: 'READ-WRITE'
}));
br.onclosemailbox = path => {
expect(path).to.equal('yyy');
called = true;
};
br._selectedMailbox = 'yyy';
return br.selectMailbox(path).then(() => {
expect(called).to.be.true;
});
});
});
describe('#hasCapability', () => {
it('should detect existing capability', () => {
br._capability = ['ZZZ'];
expect(br.hasCapability('zzz')).to.be.true;
});
it('should detect non existing capability', () => {
br._capability = ['ZZZ'];
expect(br.hasCapability('ooo')).to.be.false;
expect(br.hasCapability()).to.be.false;
});
});
describe('#_untaggedOkHandler', () => {
it('should update capability if present', () => {
br._untaggedOkHandler({
capability: ['abc']
}, () => {});
expect(br._capability).to.deep.equal(['abc']);
});
});
describe('#_untaggedCapabilityHandler', () => {
it('should update capability', () => {
br._untaggedCapabilityHandler({
attributes: [{
value: 'abc'
}]
}, () => {});
expect(br._capability).to.deep.equal(['ABC']);
});
});
describe('#_untaggedExistsHandler', () => {
it('should emit onupdate', () => {
br.onupdate = sinon.stub();
br._selectedMailbox = 'FOO';
br._untaggedExistsHandler({
nr: 123
}, () => {});
expect(br.onupdate.withArgs('FOO', 'exists', 123).callCount).to.equal(1);
});
});
describe('#_untaggedExpungeHandler', () => {
it('should emit onupdate', () => {
br.onupdate = sinon.stub();
br._selectedMailbox = 'FOO';
br._untaggedExpungeHandler({
nr: 123
}, () => {});
expect(br.onupdate.withArgs('FOO', 'expunge', 123).callCount).to.equal(1);
});
});
describe.skip('#_untaggedFetchHandler', () => {
it('should emit onupdate', () => {
br.onupdate = sinon.stub();
sinon.stub(br, '_parseFETCH').returns('abc');
br._selectedMailbox = 'FOO';
br._untaggedFetchHandler({
nr: 123
}, () => {});
expect(br.onupdate.withArgs('FOO', 'fetch', 'abc').callCount).to.equal(1);
expect(br._parseFETCH.args[0][0]).to.deep.equal({
payload: {
FETCH: [{
nr: 123
}]
}
});
});
});
describe('#_changeState', () => {
it('should set the state value', () => {
br._changeState(12345);
expect(br._state).to.equal(12345);
});
it('should emit onclosemailbox if mailbox was closed', () => {
br.onclosemailbox = sinon.stub();
br._state = _client.STATE_SELECTED;
br._selectedMailbox = 'aaa';
br._changeState(12345);
expect(br._selectedMailbox).to.be.false;
expect(br.onclosemailbox.withArgs('aaa').callCount).to.equal(1);
});
});
describe('#_ensurePath', () => {
it('should create the path if not present', () => {
var tree = {
children: []
};
expect(br._ensurePath(tree, 'hello/world', '/')).to.deep.equal({
name: 'world',
delimiter: '/',
path: 'hello/world',
children: []
});
expect(tree).to.deep.equal({
children: [{
name: 'hello',
delimiter: '/',
path: 'hello',
children: [{
name: 'world',
delimiter: '/',
path: 'hello/world',
children: []
}]
}]
});
});
it('should return existing path if possible', () => {
var tree = {
children: [{
name: 'hello',
delimiter: '/',
path: 'hello',
children: [{
name: 'world',
delimiter: '/',
path: 'hello/world',
children: [],
abc: 123
}]
}]
};
expect(br._ensurePath(tree, 'hello/world', '/')).to.deep.equal({
name: 'world',
delimiter: '/',
path: 'hello/world',
children: [],
abc: 123
});
});
it('should handle case insensitive Inbox', () => {
var tree = {
children: []
};
expect(br._ensurePath(tree, 'Inbox/world', '/')).to.deep.equal({
name: 'world',
delimiter: '/',
path: 'Inbox/world',
children: []
});
expect(br._ensurePath(tree, 'INBOX/worlds', '/')).to.deep.equal({
name: 'worlds',
delimiter: '/',
path: 'INBOX/worlds',
children: []
});
expect(tree).to.deep.equal({
children: [{
name: 'Inbox',
delimiter: '/',
path: 'Inbox',
children: [{
name: 'world',
delimiter: '/',
path: 'Inbox/world',
children: []
}, {
name: 'worlds',
delimiter: '/',
path: 'INBOX/worlds',
children: []
}]
}]
});
});
});
describe('untagged updates', () => {
it('should receive information about untagged exists', done => {
br.client._connectionReady = true;
br._selectedMailbox = 'FOO';
br.onupdate = (path, type, value) => {
expect(path).to.equal('FOO');
expect(type).to.equal('exists');
expect(value).to.equal(123);
done();
};
br.client._onData({
/* * 123 EXISTS\r\n */
data: new Uint8Array([42, 32, 49, 50, 51, 32, 69, 88, 73, 83, 84, 83, 13, 10]).buffer
});
});
it('should receive information about untagged expunge', done => {
br.client._connectionReady = true;
br._selectedMailbox = 'FOO';
br.onupdate = (path, type, value) => {
expect(path).to.equal('FOO');
expect(type).to.equal('expunge');
expect(value).to.equal(456);
done();
};
br.client._onData({
/* * 456 EXPUNGE\r\n */
data: new Uint8Array([42, 32, 52, 53, 54, 32, 69, 88, 80, 85, 78, 71, 69, 13, 10]).buffer
});
});
it('should receive information about untagged fetch', done => {
br.client._connectionReady = true;
br._selectedMailbox = 'FOO';
br.onupdate = (path, type, value) => {
expect(path).to.equal('FOO');
expect(type).to.equal('fetch');
expect(value).to.deep.equal({
'#': 123,
flags: ['\\Seen'],
modseq: '4'
});
done();
};
br.client._onData({
/* * 123 FETCH (FLAGS (\\Seen) MODSEQ (4))\r\n */
data: new Uint8Array([42, 32, 49, 50, 51, 32, 70, 69, 84, 67, 72, 32, 40, 70, 76, 65, 71, 83, 32, 40, 92, 83, 101, 101, 110, 41, 32, 77, 79, 68, 83, 69, 81, 32, 40, 52, 41, 41, 13, 10]).buffer
});
});
});
});
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9jbGllbnQtdW5pdC5qcyJdLCJuYW1lcyI6WyJkZXNjcmliZSIsImJyIiwiYmVmb3JlRWFjaCIsImF1dGgiLCJ1c2VyIiwicGFzcyIsIkltYXBDbGllbnQiLCJsb2dMZXZlbCIsImNsaWVudCIsInNvY2tldCIsInNlbmQiLCJ1cGdyYWRlVG9TZWN1cmUiLCJpdCIsInNpbm9uIiwic3R1YiIsIl9hdXRoZW50aWNhdGVkIiwiX2VudGVyZWRJZGxlIiwiX29uSWRsZSIsImV4cGVjdCIsImVudGVySWRsZSIsImNhbGxDb3VudCIsInRvIiwiZXF1YWwiLCJjb25uZWN0IiwicmV0dXJucyIsIlByb21pc2UiLCJyZXNvbHZlIiwiZW5xdWV1ZUNvbW1hbmQiLCJjYXBhYmlsaXR5Iiwic2V0VGltZW91dCIsIm9ucmVhZHkiLCJvcGVuQ29ubmVjdGlvbiIsInRoZW4iLCJjYWxsZWRPbmNlIiwiYmUiLCJ0cnVlIiwiX2NhcGFiaWxpdHkiLCJsZW5ndGgiLCJ1cGRhdGVDYXBhYmlsaXR5IiwidXBncmFkZUNvbm5lY3Rpb24iLCJ1cGRhdGVJZCIsImxvZ2luIiwiY29tcHJlc3NDb25uZWN0aW9uIiwiZG9uZSIsInRocm93cyIsIkVycm9yIiwiY2F0Y2giLCJlcnIiLCJleGlzdCIsImNsb3NlIiwiY2FsbGVkIiwiZmFsc2UiLCJ0aW1lb3V0Q29ubmVjdGlvbiIsIl9zdGF0ZSIsIlNUQVRFX0xPR09VVCIsImV4ZWMiLCJyZXMiLCJkZWVwIiwiYXJncyIsImNhbGxzRmFrZSIsImNvbW1hbmQiLCJfc2VsZWN0ZWRNYWlsYm94IiwidGltZW91dE5vb3AiLCJ1bmRlZmluZWQiLCJwYXlsb2FkIiwic2xpY2UiLCJjYWxsIiwiVWludDhBcnJheSIsInRpbWVvdXRJZGxlIiwiYnJlYWtJZGxlIiwic2VjdXJlTW9kZSIsIndpdGhBcmdzIiwidXBncmFkZSIsIl9yZXF1aXJlVExTIiwiTkFNRVNQQUNFIiwiYXR0cmlidXRlcyIsInR5cGUiLCJ2YWx1ZSIsImxpc3ROYW1lc3BhY2VzIiwibmFtZXNwYWNlcyIsInBlcnNvbmFsIiwicHJlZml4IiwiZGVsaW1pdGVyIiwidXNlcnMiLCJzaGFyZWQiLCJfZW5hYmxlQ29tcHJlc3Npb24iLCJlbmFibGVDb21wcmVzc2lvbiIsInNlbnNpdGl2ZSIsInhvYXV0aDIiLCJhIiwiYyIsInNlcnZlcklkIiwiSUQiLCJja2V5MSIsImNrZXkyIiwic2tleTEiLCJza2V5MiIsIkxJU1QiLCJMU1VCIiwibGlzdE1haWxib3hlcyIsInRyZWUiLCJjcmVhdGVNYWlsYm94IiwiZmFrZUVyciIsImNvZGUiLCJyZWplY3QiLCJkZWxldGVNYWlsYm94Iiwic2tpcCIsIl9idWlsZEZFVENIQ29tbWFuZCIsImJ5VWlkIiwibGlzdE1lc3NhZ2VzIiwiX3BhcnNlRkVUQ0giLCJfYnVpbGRTRUFSQ0hDb21tYW5kIiwidWlkIiwic2VhcmNoIiwiX3BhcnNlU0VBUkNIIiwidXBsb2FkIiwiZmxhZ3MiLCJfYnVpbGRTVE9SRUNvbW1hbmQiLCJzZXRGbGFncyIsInN0b3JlIiwiYWRkIiwiZGVsZXRlTWVzc2FnZXMiLCJjb3B5dWlkIiwiY29weU1lc3NhZ2VzIiwicmVzcG9uc2UiLCJzcmNTZXFTZXQiLCJkZXN0U2VxU2V0IiwibW92ZU1lc3NhZ2VzIiwiX3Nob3VsZFNlbGVjdE1haWxib3giLCJyZXF1ZXN0IiwicGF0aCIsInNlbGVjdE1haWxib3giLCJTVEFURV9TRUxFQ1RFRCIsImNvbmRzdG9yZSIsInByb21pc2VSZXNvbHZlZCIsIm9uc2VsZWN0bWFpbGJveCIsIm9uc2VsZWN0bWFpbGJveFNweSIsInNweSIsIm9uY2xvc2VtYWlsYm94IiwiaGFzQ2FwYWJpbGl0eSIsIl91bnRhZ2dlZE9rSGFuZGxlciIsIl91bnRhZ2dlZENhcGFiaWxpdHlIYW5kbGVyIiwib251cGRhdGUiLCJfdW50YWdnZWRFeGlzdHNIYW5kbGVyIiwibnIiLCJfdW50YWdnZWRFeHB1bmdlSGFuZGxlciIsIl91bnRhZ2dlZEZldGNoSGFuZGxlciIsIkZFVENIIiwiX2NoYW5nZVN0YXRlIiwiY2hpbGRyZW4iLCJfZW5zdXJlUGF0aCIsIm5hbWUiLCJhYmMiLCJfY29ubmVjdGlvblJlYWR5IiwiX29uRGF0YSIsImRhdGEiLCJidWZmZXIiLCJtb2RzZXEiXSwibWFwcGluZ3MiOiI7O0FBRUE7O0FBQ0E7O0FBQ0E7Ozs7OztBQUpBO0FBU0FBLFFBQVEsQ0FBQyx1QkFBRCxFQUEwQixNQUFNO0FBQ3RDLE1BQUlDLEVBQUo7QUFFQUMsRUFBQUEsVUFBVSxDQUFDLE1BQU07QUFDZixVQUFNQyxJQUFJLEdBQUc7QUFBRUMsTUFBQUEsSUFBSSxFQUFFLFVBQVI7QUFBb0JDLE1BQUFBLElBQUksRUFBRTtBQUExQixLQUFiO0FBQ0FKLElBQUFBLEVBQUUsR0FBRyxJQUFJSyxlQUFKLENBQWUsVUFBZixFQUEyQixJQUEzQixFQUFpQztBQUFFSCxNQUFBQSxJQUFGO0FBQVFJLE1BQUFBLFFBQVEsRUFBUkE7QUFBUixLQUFqQyxDQUFMO0FBQ0FOLElBQUFBLEVBQUUsQ0FBQ08sTUFBSCxDQUFVQyxNQUFWLEdBQW1CO0FBQ2pCQyxNQUFBQSxJQUFJLEVBQUUsTUFBTSxDQUFHLENBREU7QUFFakJDLE1BQUFBLGVBQWUsRUFBRSxNQUFNLENBQUc7QUFGVCxLQUFuQjtBQUlELEdBUFMsQ0FBVjtBQVNBWCxFQUFBQSxRQUFRLENBQUMsVUFBRCxFQUFhLE1BQU07QUFDekJZLElBQUFBLEVBQUUsQ0FBQyx1QkFBRCxFQUEwQixNQUFNO0FBQ2hDQyxNQUFBQSxLQUFLLENBQUNDLElBQU4sQ0FBV2IsRUFBWCxFQUFlLFdBQWY7QUFFQUEsTUFBQUEsRUFBRSxDQUFDYyxjQUFILEdBQW9CLElBQXBCO0FBQ0FkLE1BQUFBLEVBQUUsQ0FBQ2UsWUFBSCxHQUFrQixLQUFsQjs7QUFDQWYsTUFBQUEsRUFBRSxDQUFDZ0IsT0FBSDs7QUFFQUMsTUFBQUEsTUFBTSxDQUFDakIsRUFBRSxDQUFDa0IsU0FBSCxDQUFhQyxTQUFkLENBQU4sQ0FBK0JDLEVBQS9CLENBQWtDQyxLQUFsQyxDQUF3QyxDQUF4QztBQUNELEtBUkMsQ0FBRjtB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