UNPKG

emailjs-imap-client

Version:
1,105 lines (1,065 loc) 143 kB
"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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