mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-01-01 13:14:16 +08:00
895 lines
34 KiB
TypeScript
895 lines
34 KiB
TypeScript
import _ from 'underscore';
|
|
|
|
import {
|
|
File,
|
|
Actions,
|
|
Thread,
|
|
Contact,
|
|
Message,
|
|
DraftStore,
|
|
AccountStore,
|
|
DatabaseStore,
|
|
AttachmentStore,
|
|
SanitizeTransformer,
|
|
InlineStyleTransformer,
|
|
} from 'mailspring-exports';
|
|
|
|
import DraftFactory from '../../src/flux/stores/draft-factory';
|
|
|
|
let msgFromMe = null;
|
|
let fakeThread = null;
|
|
let fakeMessage1 = null;
|
|
let msgWithReplyTo = null;
|
|
let fakeMessageWithFiles = null;
|
|
let msgWithReplyToDuplicates = null;
|
|
let msgWithReplyToFromMe = null;
|
|
let account = null;
|
|
const downloadData = {};
|
|
|
|
const expectContactsEqual = (a, b) => {
|
|
expect(a.map(c => c.email).sort()).toEqual(b.map(c => c.email).sort());
|
|
};
|
|
|
|
describe('DraftFactory', function draftFactory() {
|
|
beforeEach(() => {
|
|
// Out of the scope of these specs
|
|
spyOn(InlineStyleTransformer, 'run').andCallFake(input => Promise.resolve(input));
|
|
spyOn(SanitizeTransformer, 'run').andCallFake(input => Promise.resolve(input));
|
|
spyOn(AttachmentStore, 'getDownloadDataForFile').andCallFake(fid => {
|
|
return downloadData[fid];
|
|
});
|
|
|
|
account = AccountStore.accounts()[0];
|
|
const files = [
|
|
new File({ filename: 'test.jpg', accountId: account.id, id: 'a' }),
|
|
new File({ filename: 'test.pdj', accountId: account.id, id: 'b' }),
|
|
];
|
|
files.forEach(file => {
|
|
downloadData[file.id] = {
|
|
fileId: file.id,
|
|
filename: file.filename,
|
|
};
|
|
});
|
|
|
|
fakeThread = new Thread({
|
|
id: 'fake-thread-id',
|
|
accountId: account.id,
|
|
subject: 'Fake Subject',
|
|
});
|
|
|
|
fakeMessage1 = new Message({
|
|
id: 'fake-message-1',
|
|
headerMessageId: 'fake-message-1@localhost',
|
|
accountId: account.id,
|
|
to: [
|
|
new Contact({ email: 'ben@mailspring.com' }),
|
|
new Contact({ email: 'evan@mailspring.com' }),
|
|
],
|
|
cc: [new Contact({ email: 'mg@mailspring.com' }), account.me()],
|
|
bcc: [new Contact({ email: 'recruiting@mailspring.com' })],
|
|
from: [new Contact({ email: 'customer@example.com', name: 'Customer' })],
|
|
threadId: 'fake-thread-id',
|
|
body: 'Fake Message 1',
|
|
subject: 'Fake Subject',
|
|
date: new Date(1415814587),
|
|
});
|
|
|
|
fakeMessageWithFiles = new Message({
|
|
id: 'fake-message-with-files',
|
|
headerMessageId: 'fake-message-with-files@localhost',
|
|
accountId: account.id,
|
|
to: [
|
|
new Contact({ email: 'ben@mailspring.com' }),
|
|
new Contact({ email: 'evan@mailspring.com' }),
|
|
],
|
|
cc: [new Contact({ email: 'mg@mailspring.com' }), account.me()],
|
|
bcc: [new Contact({ email: 'recruiting@mailspring.com' })],
|
|
from: [new Contact({ email: 'customer@example.com', name: 'Customer' })],
|
|
files: files,
|
|
threadId: 'fake-thread-id',
|
|
body: 'Fake Message 1',
|
|
subject: 'Fake Subject',
|
|
date: new Date(1415814587),
|
|
});
|
|
|
|
msgFromMe = new Message({
|
|
id: 'fake-message-3',
|
|
headerMessageId: 'fake-message-3@localhost',
|
|
accountId: account.id,
|
|
to: [new Contact({ email: '1@1.com' }), new Contact({ email: '2@2.com' })],
|
|
cc: [new Contact({ email: '3@3.com' }), new Contact({ email: '4@4.com' })],
|
|
bcc: [new Contact({ email: '5@5.com' }), new Contact({ email: '6@6.com' })],
|
|
from: [account.me()],
|
|
threadId: 'fake-thread-id',
|
|
body: 'Fake Message 2',
|
|
subject: 'Re: Fake Subject',
|
|
date: new Date(1415814587),
|
|
});
|
|
|
|
msgWithReplyTo = new Message({
|
|
id: 'fake-message-reply-to',
|
|
headerMessageId: 'fake-message-reply-to@localhost',
|
|
accountId: account.id,
|
|
to: [new Contact({ email: '1@1.com' }), new Contact({ email: '2@2.com' })],
|
|
cc: [new Contact({ email: '3@3.com' }), new Contact({ email: '4@4.com' })],
|
|
bcc: [new Contact({ email: '5@5.com' }), new Contact({ email: '6@6.com' })],
|
|
replyTo: [new Contact({ email: 'reply-to@5.com' }), new Contact({ email: 'reply-to@6.com' })],
|
|
from: [new Contact({ email: 'from@5.com' })],
|
|
threadId: 'fake-thread-id',
|
|
body: 'Fake Message 2',
|
|
subject: 'Re: Fake Subject',
|
|
date: new Date(1415814587),
|
|
});
|
|
|
|
msgWithReplyToFromMe = new Message({
|
|
accountId: account.id,
|
|
threadId: 'fake-thread-id',
|
|
from: [account.me()],
|
|
to: [new Contact({ email: 'tiffany@popular.com', name: 'Tiffany' })],
|
|
replyTo: [new Contact({ email: 'danco@gmail.com', name: 'danco@gmail.com' })],
|
|
});
|
|
|
|
msgWithReplyToDuplicates = new Message({
|
|
id: 'fake-message-reply-to-duplicates',
|
|
headerMessageId: 'fake-message-reply-to-duplicates@localhost',
|
|
accountId: account.id,
|
|
to: [new Contact({ email: '1@1.com' }), new Contact({ email: '2@2.com' })],
|
|
cc: [new Contact({ email: '1@1.com' }), new Contact({ email: '4@4.com' })],
|
|
from: [new Contact({ email: 'reply-to@5.com' })],
|
|
replyTo: [new Contact({ email: 'reply-to@5.com' })],
|
|
threadId: 'fake-thread-id',
|
|
body: 'Fake Message Duplicates',
|
|
subject: 'Re: Fake Subject',
|
|
date: new Date(1415814587),
|
|
});
|
|
});
|
|
|
|
describe('creating drafts', () => {
|
|
describe('createDraftForReply', () => {
|
|
it('should include a quoted text block', () => {
|
|
waitsForPromise(() => {
|
|
return DraftFactory.createDraftForReply({
|
|
thread: fakeThread,
|
|
message: fakeMessage1,
|
|
type: 'reply',
|
|
}).then(draft => {
|
|
expect(draft.body.includes(`div class="gmail_quote`)).toBe(true);
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should address the message to the previous message's sender", () => {
|
|
waitsForPromise(() => {
|
|
return DraftFactory.createDraftForReply({
|
|
thread: fakeThread,
|
|
message: fakeMessage1,
|
|
type: 'reply',
|
|
}).then(draft => {
|
|
expect(draft.to).toEqual(fakeMessage1.from);
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should set the replyToHeaderMessageId to the previous message's ids", () => {
|
|
waitsForPromise(() => {
|
|
return DraftFactory.createDraftForReply({
|
|
thread: fakeThread,
|
|
message: fakeMessage1,
|
|
type: 'reply',
|
|
}).then(draft => {
|
|
expect(draft.replyToHeaderMessageId).toEqual(fakeMessage1.headerMessageId);
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should set the accountId and from address based on the message', () => {
|
|
waitsForPromise(() => {
|
|
const secondAccount = AccountStore.accounts()[1];
|
|
fakeMessage1.to = [
|
|
new Contact({ email: secondAccount.emailAddress }),
|
|
new Contact({ email: 'evan@mailspring.com' }),
|
|
];
|
|
fakeMessage1.accountId = secondAccount.id;
|
|
fakeThread.accountId = secondAccount.id;
|
|
|
|
return DraftFactory.createDraftForReply({
|
|
thread: fakeThread,
|
|
message: fakeMessage1,
|
|
type: 'reply',
|
|
}).then(draft => {
|
|
expect(draft.accountId).toEqual(secondAccount.id);
|
|
expect(draft.from[0].email).toEqual(secondAccount.defaultMe().email);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('when the email is TO an alias', () => {
|
|
it('should use the alias as the from address', () => {
|
|
waitsForPromise(() => {
|
|
fakeMessage1.to = [
|
|
new Contact({ email: TEST_ACCOUNT_ALIAS_EMAIL }),
|
|
new Contact({ email: 'evan@mailspring.com' }),
|
|
];
|
|
|
|
return DraftFactory.createDraftForReply({
|
|
thread: fakeThread,
|
|
message: fakeMessage1,
|
|
type: 'reply',
|
|
}).then(draft => {
|
|
expect(draft.accountId).toEqual(TEST_ACCOUNT_ID);
|
|
expect(draft.from[0].email).toEqual(TEST_ACCOUNT_ALIAS_EMAIL);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("when the email is CC'd to an alias", () => {
|
|
it('should use the alias as the from address', () => {
|
|
waitsForPromise(() => {
|
|
fakeMessage1.to = [new Contact({ email: 'juan@mailspring.com' })];
|
|
fakeMessage1.cc = [
|
|
new Contact({ email: TEST_ACCOUNT_ALIAS_EMAIL }),
|
|
new Contact({ email: 'evan@mailspring.com' }),
|
|
];
|
|
|
|
return DraftFactory.createDraftForReply({
|
|
thread: fakeThread,
|
|
message: fakeMessage1,
|
|
type: 'reply',
|
|
}).then(draft => {
|
|
expect(draft.accountId).toEqual(TEST_ACCOUNT_ID);
|
|
expect(draft.from[0].email).toEqual(TEST_ACCOUNT_ALIAS_EMAIL);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should make the subject the subject of the message, not the thread', () => {
|
|
fakeMessage1.subject = 'OLD SUBJECT';
|
|
waitsForPromise(() => {
|
|
return DraftFactory.createDraftForReply({
|
|
thread: fakeThread,
|
|
message: fakeMessage1,
|
|
type: 'reply',
|
|
}).then(draft => {
|
|
expect(draft.subject).toEqual('Re: OLD SUBJECT');
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should change the subject from Fwd: back to Re: if necessary', () => {
|
|
fakeMessage1.subject = 'Fwd: This is my DRAFT';
|
|
waitsForPromise(() => {
|
|
return DraftFactory.createDraftForReply({
|
|
thread: fakeThread,
|
|
message: fakeMessage1,
|
|
type: 'reply',
|
|
}).then(draft => {
|
|
expect(draft.subject).toEqual('Re: This is my DRAFT');
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('type: reply', () => {
|
|
describe("when the message provided as context has one or more 'ReplyTo' recipients", () => {
|
|
it("addresses the draft to all of the message's 'ReplyTo' recipients", () => {
|
|
waitsForPromise(() => {
|
|
return DraftFactory.createDraftForReply({
|
|
thread: fakeThread,
|
|
message: msgWithReplyTo,
|
|
type: 'reply',
|
|
}).then(draft => {
|
|
expect(draft.to).toEqual(msgWithReplyTo.replyTo);
|
|
expect(draft.cc.length).toBe(0);
|
|
expect(draft.bcc.length).toBe(0);
|
|
});
|
|
});
|
|
});
|
|
|
|
it("addresses the draft to all of the message's 'ReplyTo' recipients, even if the message is 'From' you", () => {
|
|
waitsForPromise(() => {
|
|
return DraftFactory.createDraftForReply({
|
|
thread: fakeThread,
|
|
message: msgWithReplyToFromMe,
|
|
type: 'reply',
|
|
}).then(draft => {
|
|
expect(draft.to).toEqual(msgWithReplyToFromMe.replyTo);
|
|
expect(draft.cc.length).toBe(0);
|
|
expect(draft.bcc.length).toBe(0);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('when the message provided as context was sent by the current user', () => {
|
|
it("addresses the draft to all of the last messages's 'To' recipients", () => {
|
|
waitsForPromise(() => {
|
|
return DraftFactory.createDraftForReply({
|
|
thread: fakeThread,
|
|
message: msgFromMe,
|
|
type: 'reply',
|
|
}).then(draft => {
|
|
expect(draft.to).toEqual(msgFromMe.to);
|
|
expect(draft.cc.length).toBe(0);
|
|
expect(draft.bcc.length).toBe(0);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('type: reply-all', () => {
|
|
it('should include people in the cc field', () => {
|
|
waitsForPromise(() => {
|
|
return DraftFactory.createDraftForReply({
|
|
thread: fakeThread,
|
|
message: fakeMessage1,
|
|
type: 'reply-all',
|
|
}).then(draft => {
|
|
const ccEmails = draft.cc.map(cc => cc.email);
|
|
expect(ccEmails.sort()).toEqual([
|
|
'ben@mailspring.com',
|
|
'evan@mailspring.com',
|
|
'mg@mailspring.com',
|
|
]);
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should not include people who were bcc'd on the previous message", () => {
|
|
waitsForPromise(() => {
|
|
return DraftFactory.createDraftForReply({
|
|
thread: fakeThread,
|
|
message: fakeMessage1,
|
|
type: 'reply-all',
|
|
}).then(draft => {
|
|
expect(draft.bcc).toEqual([]);
|
|
expect(draft.cc.indexOf(fakeMessage1.bcc[0])).toEqual(-1);
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should not include you when you were cc'd on the previous message", () => {
|
|
waitsForPromise(() => {
|
|
return DraftFactory.createDraftForReply({
|
|
thread: fakeThread,
|
|
message: fakeMessage1,
|
|
type: 'reply-all',
|
|
}).then(draft => {
|
|
const ccEmails = draft.cc.map(cc => cc.email);
|
|
expect(ccEmails.indexOf(account.me().email)).toEqual(-1);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("when the message provided as context has one or more 'ReplyTo' recipients", () => {
|
|
it("addresses the draft to all of the message's 'ReplyTo' recipients", () => {
|
|
waitsForPromise(() => {
|
|
return DraftFactory.createDraftForReply({
|
|
thread: fakeThread,
|
|
message: msgWithReplyTo,
|
|
type: 'reply-all',
|
|
}).then(draft => {
|
|
expect(draft.to).toEqual(msgWithReplyTo.replyTo);
|
|
});
|
|
});
|
|
});
|
|
|
|
it("addresses the draft to all of the message's 'ReplyTo' recipients, even if the message is 'From' you", () => {
|
|
waitsForPromise(() => {
|
|
return DraftFactory.createDraftForReply({
|
|
thread: fakeThread,
|
|
message: msgWithReplyToFromMe,
|
|
type: 'reply-all',
|
|
}).then(draft => {
|
|
expect(draft.to).toEqual(msgWithReplyToFromMe.replyTo);
|
|
});
|
|
});
|
|
});
|
|
|
|
it("should not include the message's 'From' recipient in any field", () => {
|
|
waitsForPromise(() => {
|
|
return DraftFactory.createDraftForReply({
|
|
thread: fakeThread,
|
|
message: msgWithReplyTo,
|
|
type: 'reply-all',
|
|
}).then(draft => {
|
|
const all = [...draft.to, ...draft.cc, ...draft.bcc];
|
|
const match = _.find(all, c => c.email === msgWithReplyTo.from[0].email);
|
|
expect(match).toEqual(undefined);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("when the message provided has one or more 'ReplyTo' recipients and duplicates in the To/Cc fields", () => {
|
|
it('should unique the to/cc fields', () => {
|
|
waitsForPromise(() => {
|
|
return DraftFactory.createDraftForReply({
|
|
thread: fakeThread,
|
|
message: msgWithReplyToDuplicates,
|
|
type: 'reply-all',
|
|
}).then(draft => {
|
|
const ccEmails = draft.cc.map(cc => cc.email);
|
|
expect(ccEmails.sort()).toEqual(['1@1.com', '2@2.com', '4@4.com']);
|
|
const toEmails = draft.to.map(to => to.email);
|
|
expect(toEmails.sort()).toEqual(['reply-to@5.com']);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('when the message provided as context was sent by the current user', () => {
|
|
it("addresses the draft to all of the last messages's recipients", () => {
|
|
waitsForPromise(() => {
|
|
return DraftFactory.createDraftForReply({
|
|
thread: fakeThread,
|
|
message: msgFromMe,
|
|
type: 'reply-all',
|
|
}).then(draft => {
|
|
expect(draft.to).toEqual(msgFromMe.to);
|
|
expect(draft.cc).toEqual(msgFromMe.cc);
|
|
expect(draft.bcc.length).toBe(0);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('onComposeForward', () => {
|
|
beforeEach(() => {
|
|
waitsForPromise(() => {
|
|
return DraftFactory.createDraftForForward({
|
|
thread: fakeThread,
|
|
message: fakeMessage1,
|
|
}).then(draft => {
|
|
this.model = draft;
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should include forwarded message text, in a div rather than a blockquote', () => {
|
|
expect(this.model.body.indexOf('gmail_quote') > 0).toBe(true);
|
|
expect(this.model.body.indexOf('blockquote') > 0).toBe(false);
|
|
expect(this.model.body.indexOf(fakeMessage1.body) > 0).toBe(true);
|
|
expect(this.model.body.indexOf('---------- Forwarded Message ---------') > 0).toBe(true);
|
|
expect(this.model.body.indexOf('From: Customer <customer@example.com>') > 0).toBe(
|
|
true
|
|
);
|
|
expect(this.model.body.indexOf('Subject: Fake Subject') > 0).toBe(true);
|
|
expect(this.model.body.indexOf('To: ben@mailspring.com, evan@mailspring.com') > 0).toBe(
|
|
true
|
|
);
|
|
expect(
|
|
this.model.body.indexOf(
|
|
'Cc: mg@mailspring.com, Mailspring Test <tester@mailspring.com>'
|
|
) > 0
|
|
).toBe(true);
|
|
});
|
|
|
|
it("should not mention BCC'd recipients in the forwarded message header", () => {
|
|
expect(this.model.body.indexOf('recruiting@mailspring.com') > 0).toBe(false);
|
|
});
|
|
it('should not address the message to anyone', () => {
|
|
expect(this.model.to).toEqual([]);
|
|
expect(this.model.cc).toEqual([]);
|
|
expect(this.model.bcc).toEqual([]);
|
|
});
|
|
|
|
it('should not set the replyToHeaderMessageId', () => {
|
|
expect(this.model.replyToHeaderMessageId).toEqual(undefined);
|
|
});
|
|
|
|
it('should sanitize the HTML', () => {
|
|
expect(InlineStyleTransformer.run).toHaveBeenCalled();
|
|
expect(SanitizeTransformer.run).toHaveBeenCalled();
|
|
});
|
|
|
|
it('should include the attached files as files', () => {
|
|
waitsForPromise(() => {
|
|
return DraftFactory.createDraftForForward({
|
|
thread: fakeThread,
|
|
message: fakeMessageWithFiles,
|
|
}).then(draft => {
|
|
expect(draft.files.length).toBe(2);
|
|
expect(draft.files[0].filename).toBe('test.jpg');
|
|
expect(draft.files[1].filename).toBe('test.pdj');
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should make the subject the subject of the message, not the thread', () => {
|
|
fakeMessage1.subject = 'OLD SUBJECT';
|
|
waitsForPromise(() => {
|
|
return DraftFactory.createDraftForForward({
|
|
thread: fakeThread,
|
|
message: fakeMessage1,
|
|
}).then(draft => {
|
|
expect(draft.subject).toEqual('Fwd: OLD SUBJECT');
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should change the subject from Re: back to Fwd: if necessary', () => {
|
|
fakeMessage1.subject = 'Re: This is my DRAFT';
|
|
waitsForPromise(() => {
|
|
return DraftFactory.createDraftForForward({
|
|
thread: fakeThread,
|
|
message: fakeMessage1,
|
|
}).then(draft => {
|
|
expect(draft.subject).toEqual('Fwd: This is my DRAFT');
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('createOrUpdateDraftForReply', () => {
|
|
it('should throw an exception unless you provide `reply` or `reply-all`', () => {
|
|
waitsForPromise(() =>
|
|
DraftFactory.createOrUpdateDraftForReply({
|
|
thread: fakeThread,
|
|
message: fakeMessage1,
|
|
type: 'wrong' as any,
|
|
behavior: 'prefer-existing',
|
|
})
|
|
.then(() => {
|
|
expect(false).toEqual(true);
|
|
})
|
|
.catch(() => {
|
|
expect(true).toEqual(true);
|
|
})
|
|
);
|
|
});
|
|
|
|
describe('when there is already a draft in reply to the same message the thread', () => {
|
|
beforeEach(() => {
|
|
this.existingDraft = new Message({
|
|
id: 'asd',
|
|
accountId: TEST_ACCOUNT_ID,
|
|
replyToHeaderMessageId: fakeMessage1.headerMessageId,
|
|
threadId: fakeMessage1.threadId,
|
|
draft: true,
|
|
});
|
|
this.sessionStub = {
|
|
changes: {
|
|
add: jasmine.createSpy('add'),
|
|
},
|
|
};
|
|
spyOn(DatabaseStore, 'run').andReturn(Promise.resolve([fakeMessage1, this.existingDraft]));
|
|
spyOn(DraftStore, 'sessionForClientId').andReturn(Promise.resolve(this.sessionStub));
|
|
});
|
|
|
|
describe('when reply-all is passed', () => {
|
|
it('should add missing participants', async () => {
|
|
this.existingDraft.to = fakeMessage1.participantsForReply().to;
|
|
this.existingDraft.cc = fakeMessage1.participantsForReply().cc;
|
|
const { to, cc } = await DraftFactory.createOrUpdateDraftForReply({
|
|
thread: fakeThread,
|
|
message: fakeMessage1,
|
|
type: 'reply-all',
|
|
behavior: 'prefer-existing',
|
|
});
|
|
expectContactsEqual(to, fakeMessage1.participantsForReplyAll().to);
|
|
expectContactsEqual(cc, fakeMessage1.participantsForReplyAll().cc);
|
|
});
|
|
|
|
it('should not blow away other participants who have been added to the draft', async () => {
|
|
const randomA = new Contact({ email: 'other-guy-a@gmail.com' });
|
|
const randomB = new Contact({ email: 'other-guy-b@gmail.com' });
|
|
this.existingDraft.to = fakeMessage1.participantsForReply().to.concat([randomA]);
|
|
this.existingDraft.cc = fakeMessage1.participantsForReply().cc.concat([randomB]);
|
|
const { to, cc } = await DraftFactory.createOrUpdateDraftForReply({
|
|
thread: fakeThread,
|
|
message: fakeMessage1,
|
|
type: 'reply-all',
|
|
behavior: 'prefer-existing',
|
|
});
|
|
expectContactsEqual(to, fakeMessage1.participantsForReplyAll().to.concat([randomA]));
|
|
expectContactsEqual(cc, fakeMessage1.participantsForReplyAll().cc.concat([randomB]));
|
|
});
|
|
});
|
|
|
|
describe('when reply is passed', () => {
|
|
it('should remove participants present in the reply-all participant set and not in the reply set', async () => {
|
|
this.existingDraft.to = fakeMessage1.participantsForReplyAll().to;
|
|
this.existingDraft.cc = fakeMessage1.participantsForReplyAll().cc;
|
|
const { to, cc } = await DraftFactory.createOrUpdateDraftForReply({
|
|
thread: fakeThread,
|
|
message: fakeMessage1,
|
|
type: 'reply',
|
|
behavior: 'prefer-existing',
|
|
});
|
|
expectContactsEqual(to, fakeMessage1.participantsForReply().to);
|
|
expectContactsEqual(cc, fakeMessage1.participantsForReply().cc);
|
|
});
|
|
|
|
it('should not blow away other participants who have been added to the draft', async () => {
|
|
const randomA = new Contact({ email: 'other-guy-a@gmail.com' });
|
|
const randomB = new Contact({ email: 'other-guy-b@gmail.com' });
|
|
this.existingDraft.to = fakeMessage1.participantsForReplyAll().to.concat([randomA]);
|
|
this.existingDraft.cc = fakeMessage1.participantsForReplyAll().cc.concat([randomB]);
|
|
const { to, cc } = await DraftFactory.createOrUpdateDraftForReply({
|
|
thread: fakeThread,
|
|
message: fakeMessage1,
|
|
type: 'reply',
|
|
behavior: 'prefer-existing',
|
|
});
|
|
expectContactsEqual(to, fakeMessage1.participantsForReply().to.concat([randomA]));
|
|
expectContactsEqual(cc, fakeMessage1.participantsForReply().cc.concat([randomB]));
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('when there is not an existing draft at the bottom of the thread', () => {
|
|
beforeEach(() => {
|
|
spyOn(DatabaseStore, 'run').andCallFake(async () => [fakeMessage1]);
|
|
spyOn(DraftFactory, 'createDraftForReply');
|
|
});
|
|
|
|
it('should call through to createDraftForReply', async () => {
|
|
await DraftFactory.createOrUpdateDraftForReply({
|
|
thread: fakeThread,
|
|
message: fakeMessage1,
|
|
type: 'reply-all',
|
|
behavior: 'prefer-existing',
|
|
});
|
|
expect(DraftFactory.createDraftForReply).toHaveBeenCalledWith({
|
|
thread: fakeThread,
|
|
message: fakeMessage1,
|
|
type: 'reply-all',
|
|
});
|
|
|
|
await DraftFactory.createOrUpdateDraftForReply({
|
|
thread: fakeThread,
|
|
message: fakeMessage1,
|
|
type: 'reply',
|
|
behavior: 'prefer-existing',
|
|
});
|
|
expect(DraftFactory.createDraftForReply).toHaveBeenCalledWith({
|
|
thread: fakeThread,
|
|
message: fakeMessage1,
|
|
type: 'reply',
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('_fromContactForReply', () => {
|
|
it('should work correctly in a range of test cases', () => {
|
|
// Note: These specs are based on the second account hard-coded in SpecHelper
|
|
account = AccountStore.accounts()[1];
|
|
const cases = [
|
|
{
|
|
to: [new Contact({ name: 'Ben', email: 'ben@mailspring.com' })], // user is not present, must have been BCC'd
|
|
cc: [],
|
|
expected: account.defaultMe(),
|
|
},
|
|
{
|
|
to: [new Contact({ name: 'Second Support', email: 'second@gmail.com' })], // only name identifies alias
|
|
cc: [],
|
|
expected: new Contact({ name: 'Second Support', email: 'second@gmail.com' }),
|
|
},
|
|
{
|
|
to: [new Contact({ name: 'Second Wrong!', email: 'second+alternate@gmail.com' })], // only email identifies alias, name wrong
|
|
cc: [],
|
|
expected: new Contact({ name: 'Second Alternate', email: 'second+alternate@gmail.com' }),
|
|
},
|
|
{
|
|
to: [new Contact({ name: 'Second Alternate', email: 'second+alternate@gmail.com' })], // exact alias match
|
|
cc: [],
|
|
expected: new Contact({ name: 'Second Alternate', email: 'second+alternate@gmail.com' }),
|
|
},
|
|
{
|
|
to: [new Contact({ email: 'second+third@gmail.com' })], // exact alias match, name not present
|
|
cc: [],
|
|
expected: new Contact({ name: 'Second', email: 'second+third@gmail.com' }),
|
|
},
|
|
{
|
|
to: [new Contact({ email: 'ben@mailspring.com' })],
|
|
cc: [new Contact({ email: 'second+third@gmail.com' })], // exact alias match, but in CC
|
|
expected: new Contact({ name: 'Second', email: 'second+third@gmail.com' }),
|
|
},
|
|
];
|
|
cases.forEach(({ to, cc, expected }) => {
|
|
const contact = DraftFactory._fromContactForReply(
|
|
new Message({
|
|
accountId: account.id,
|
|
to: to,
|
|
cc: cc,
|
|
})
|
|
);
|
|
expect(contact.name).toEqual(expected.name);
|
|
expect(contact.email).toEqual(expected.email);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('createDraftForMailto', () => {
|
|
describe('parameters in the URL', () => {
|
|
let expected = null;
|
|
beforeEach(() => {
|
|
expected = 'EmailSubjectLOLOL';
|
|
});
|
|
|
|
it('works for lowercase', () => {
|
|
waitsForPromise(() => {
|
|
return DraftFactory.createDraftForMailto(`mailto:asdf@asdf.com?subject=${expected}`).then(
|
|
draft => {
|
|
expect(draft.subject).toBe(expected);
|
|
}
|
|
);
|
|
});
|
|
});
|
|
|
|
it('works for title case', () => {
|
|
waitsForPromise(() => {
|
|
return DraftFactory.createDraftForMailto(`mailto:asdf@asdf.com?Subject=${expected}`).then(
|
|
draft => {
|
|
expect(draft.subject).toBe(expected);
|
|
}
|
|
);
|
|
});
|
|
});
|
|
|
|
it('works for uppercase', () => {
|
|
waitsForPromise(() => {
|
|
return DraftFactory.createDraftForMailto(`mailto:asdf@asdf.com?SUBJECT=${expected}`).then(
|
|
draft => {
|
|
expect(draft.subject).toBe(expected);
|
|
}
|
|
);
|
|
});
|
|
});
|
|
['mailto', 'mail', ''].forEach(url => {
|
|
it(`rejects gracefully on super mangled mailto link: ${url}`, () => {
|
|
waitsForPromise(() => {
|
|
return DraftFactory.createDraftForMailto(url)
|
|
.then(() => {
|
|
expect('resolved').toBe(false);
|
|
})
|
|
.catch(() => {});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('should correctly instantiate drafts for a wide range of mailto URLs', () => {
|
|
const links = [
|
|
'mailto:',
|
|
'mailto://bengotow@gmail.com',
|
|
'mailto:bengotow@gmail.com',
|
|
'mailto:mg%40mailspring.com',
|
|
'mailto:?subject=%1z2a', // fails uriDecode
|
|
'mailto:?subject=%52z2a', // passes uriDecode
|
|
'mailto:?subject=Martha Stewart',
|
|
'mailto:?subject=Martha Stewart&cc=cc@mailspring.com',
|
|
'mailto:?subject=Martha Stewart&cc=cc@mailspring.com;bengotow@gmail.com',
|
|
'mailto:bengotow@gmail.com&subject=Martha Stewart&cc=cc@mailspring.com',
|
|
'mailto:bengotow@gmail.com?subject=Martha%20Stewart&cc=cc@mailspring.com&bcc=bcc@mailspring.com',
|
|
'mailto:bengotow@gmail.com?subject=Martha%20Stewart&cc=cc@mailspring.com&bcc=Ben <bcc@mailspring.com>',
|
|
'mailto:bengotow@gmail.com?subject=Martha%20Stewart&cc=cc@mailspring.com&bcc=Ben <bcc@mailspring.com>;Shawn <shawn@mailspring.com>',
|
|
'mailto:Ben Gotow <bengotow@gmail.com>,Shawn <shawn@mailspring.com>?subject=Yes this is really valid',
|
|
'mailto:Ben%20Gotow%20<bengotow@gmail.com>,Shawn%20<shawn@mailspring.com>?subject=Yes%20this%20is%20really%20valid',
|
|
'mailto:Reply <d+AORGpRdj0KXKUPBE1LoI0a30F10Ahj3wu3olS-aDk5_7K5Wu6WqqqG8t1HxxhlZ4KEEw3WmrSdtobgUq57SkwsYAH6tG57IrNqcQR0K6XaqLM2nGNZ22D2k@docs.google.com>?subject=Nilas%20Message%20to%20Customers',
|
|
'mailto:email@address.com?&subject=test&body=type%20your%0Amessage%20here',
|
|
'mailto:?body=type%20your%0D%0Amessage%0D%0Ahere',
|
|
'mailto:?subject=Issues%20%C2%B7%20atom/electron%20%C2%B7%20GitHub&body=https://github.com/atom/electron/issues?utf8=&q=is%253Aissue+is%253Aopen+123%0A%0A',
|
|
];
|
|
const expected = [
|
|
new Message(),
|
|
new Message({
|
|
to: [new Contact({ name: 'bengotow@gmail.com', email: 'bengotow@gmail.com' })],
|
|
}),
|
|
new Message({
|
|
to: [new Contact({ name: 'bengotow@gmail.com', email: 'bengotow@gmail.com' })],
|
|
}),
|
|
new Message({
|
|
to: [new Contact({ name: 'mg@mailspring.com', email: 'mg@mailspring.com' })],
|
|
}),
|
|
new Message({ subject: '%1z2a' }),
|
|
new Message({ subject: 'Rz2a' }),
|
|
new Message({ subject: 'Martha Stewart' }),
|
|
new Message({
|
|
cc: [new Contact({ name: 'cc@mailspring.com', email: 'cc@mailspring.com' })],
|
|
subject: 'Martha Stewart',
|
|
}),
|
|
new Message({
|
|
cc: [
|
|
new Contact({ name: 'cc@mailspring.com', email: 'cc@mailspring.com' }),
|
|
new Contact({ name: 'bengotow@gmail.com', email: 'bengotow@gmail.com' }),
|
|
],
|
|
subject: 'Martha Stewart',
|
|
}),
|
|
new Message({
|
|
to: [new Contact({ name: 'bengotow@gmail.com', email: 'bengotow@gmail.com' })],
|
|
cc: [new Contact({ name: 'cc@mailspring.com', email: 'cc@mailspring.com' })],
|
|
subject: 'Martha Stewart',
|
|
}),
|
|
new Message({
|
|
to: [new Contact({ name: 'bengotow@gmail.com', email: 'bengotow@gmail.com' })],
|
|
cc: [new Contact({ name: 'cc@mailspring.com', email: 'cc@mailspring.com' })],
|
|
bcc: [new Contact({ name: 'bcc@mailspring.com', email: 'bcc@mailspring.com' })],
|
|
subject: 'Martha Stewart',
|
|
}),
|
|
new Message({
|
|
to: [new Contact({ name: 'bengotow@gmail.com', email: 'bengotow@gmail.com' })],
|
|
cc: [new Contact({ name: 'cc@mailspring.com', email: 'cc@mailspring.com' })],
|
|
bcc: [new Contact({ name: 'Ben', email: 'bcc@mailspring.com' })],
|
|
subject: 'Martha Stewart',
|
|
}),
|
|
new Message({
|
|
to: [new Contact({ name: 'bengotow@gmail.com', email: 'bengotow@gmail.com' })],
|
|
cc: [new Contact({ name: 'cc@mailspring.com', email: 'cc@mailspring.com' })],
|
|
bcc: [
|
|
new Contact({ name: 'Ben', email: 'bcc@mailspring.com' }),
|
|
new Contact({ name: 'Shawn', email: 'shawn@mailspring.com' }),
|
|
],
|
|
subject: 'Martha Stewart',
|
|
}),
|
|
new Message({
|
|
to: [
|
|
new Contact({ name: 'Ben Gotow', email: 'bengotow@gmail.com' }),
|
|
new Contact({ name: 'Shawn', email: 'shawn@mailspring.com' }),
|
|
],
|
|
subject: 'Yes this is really valid',
|
|
}),
|
|
new Message({
|
|
to: [
|
|
new Contact({ name: 'Ben Gotow', email: 'bengotow@gmail.com' }),
|
|
new Contact({ name: 'Shawn', email: 'shawn@mailspring.com' }),
|
|
],
|
|
subject: 'Yes this is really valid',
|
|
}),
|
|
new Message({
|
|
to: [
|
|
new Contact({
|
|
name: 'Reply',
|
|
email:
|
|
'd+AORGpRdj0KXKUPBE1LoI0a30F10Ahj3wu3olS-aDk5_7K5Wu6WqqqG8t1HxxhlZ4KEEw3WmrSdtobgUq57SkwsYAH6tG57IrNqcQR0K6XaqLM2nGNZ22D2k@docs.google.com',
|
|
}),
|
|
],
|
|
subject: 'Nilas Message to Customers',
|
|
}),
|
|
new Message({
|
|
to: [new Contact({ name: 'email@address.com', email: 'email@address.com' })],
|
|
subject: 'test',
|
|
body: 'type your<br/>message here',
|
|
}),
|
|
new Message({
|
|
to: [],
|
|
body: 'type your<br/><br/>message<br/><br/>here',
|
|
}),
|
|
new Message({
|
|
to: [],
|
|
subject: 'Issues · atom/electron · GitHub',
|
|
body:
|
|
'https://github.com/atom/electron/issues?utf8=&q=is%3Aissue+is%3Aopen+123<br/><br/>',
|
|
}),
|
|
];
|
|
|
|
links.forEach((link, idx) => {
|
|
it(`works for ${link}`, () => {
|
|
waitsForPromise(() => {
|
|
return DraftFactory.createDraftForMailto(link).then(draft => {
|
|
const expectedDraft = expected[idx];
|
|
expect(draft.subject).toEqual(expectedDraft.subject);
|
|
if (expectedDraft.body) {
|
|
expect(draft.body).toEqual(expectedDraft.body);
|
|
}
|
|
['to', 'cc', 'bcc'].forEach(attr => {
|
|
expectedDraft[attr].forEach((expectedContact, jdx) => {
|
|
const actual = draft[attr][jdx];
|
|
expect(actual instanceof Contact).toBe(true);
|
|
expect(actual.email).toEqual(expectedContact.email);
|
|
expect(actual.name).toEqual(expectedContact.name);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|