File = require '../../src/flux/models/file' Thread = require '../../src/flux/models/thread' Message = require '../../src/flux/models/message' Contact = require '../../src/flux/models/contact' ModelQuery = require '../../src/flux/models/query' AccountStore = require '../../src/flux/stores/account-store' DatabaseStore = require '../../src/flux/stores/database-store' DraftStore = require '../../src/flux/stores/draft-store' DraftStoreExtension = require '../../src/flux/stores/draft-store-extension' SendDraftTask = require '../../src/flux/tasks/send-draft' DestroyDraftTask = require '../../src/flux/tasks/destroy-draft' Actions = require '../../src/flux/actions' Utils = require '../../src/flux/models/utils' ipc = require 'ipc' _ = require 'underscore' fakeThread = null fakeMessage1 = null fakeMessage2 = null msgFromMe = null msgWithReplyTo = null msgWithReplyToDuplicates = null messageWithStyleTags = null fakeMessages = null fakeMessageWithFiles = null class TestExtension extends DraftStoreExtension @prepareNewDraft: (draft) -> draft.body = "Edited by TestExtension!" + draft.body describe "DraftStore", -> beforeEach -> spyOn(atom, 'newWindow').andCallFake -> describe "creating drafts", -> beforeEach -> spyOn(DraftStore, "_sanitizeBody").andCallThrough() spyOn(DraftStore, "_onInlineStylesResult").andCallThrough() spyOn(DraftStore, "_convertToInlineStyles").andCallThrough() spyOn(ipc, "send").andCallFake (message, body) -> if message is "inline-style-parse" # There needs to be a defer block in here so the promise # responsible for handling the `inline-style-parse` can be # properly set. If the whole path is synchronous instead of # asynchrounous, the promise is not cleared properly. Doing this # requires us to add `advanceClock` blocks. _.defer -> DraftStore._onInlineStylesResult(body) fakeThread = new Thread id: 'fake-thread-id' subject: 'Fake Subject' fakeMessage1 = new Message id: 'fake-message-1' to: [new Contact(email: 'ben@nylas.com'), new Contact(email: 'evan@nylas.com')] cc: [new Contact(email: 'mg@nylas.com'), new Contact(email: AccountStore.current().me().email)] bcc: [new Contact(email: 'recruiting@nylas.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) fakeMessage2 = new Message id: 'fake-message-2' to: [new Contact(email: 'customer@example.com')] from: [new Contact(email: 'ben@nylas.com')] threadId: 'fake-thread-id' body: 'Fake Message 2' subject: 'Re: Fake Subject' date: new Date(1415814587) fakeMessageWithFiles = new Message id: 'fake-message-with-files' to: [new Contact(email: 'ben@nylas.com'), new Contact(email: 'evan@nylas.com')] cc: [new Contact(email: 'mg@nylas.com'), new Contact(email: AccountStore.current().me().email)] bcc: [new Contact(email: 'recruiting@nylas.com')] from: [new Contact(email: 'customer@example.com', name: 'Customer')] files: [new File(filename: "test.jpg"), new File(filename: "test.pdj")] threadId: 'fake-thread-id' body: 'Fake Message 1' subject: 'Fake Subject' date: new Date(1415814587) msgFromMe = new Message id: 'fake-message-3' 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: [new Contact(email: AccountStore.current().me().email)] threadId: 'fake-thread-id' body: 'Fake Message 2' subject: 'Re: Fake Subject' date: new Date(1415814587) msgWithReplyTo = new Message id: 'fake-message-reply-to' 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) msgWithReplyToDuplicates = new Message id: 'fake-message-reply-to-duplicates' 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) messageWithStyleTags = new Message id: 'message-with-style-tags' to: [new Contact(email: 'ben@nylas.com'), new Contact(email: 'evan@nylas.com')] cc: [new Contact(email: 'mg@nylas.com'), new Contact(email: AccountStore.current().me().email)] bcc: [new Contact(email: 'recruiting@nylas.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) fakeMessages = 'fake-message-1': fakeMessage1 'fake-message-3': msgFromMe 'fake-message-2': fakeMessage2 'fake-message-reply-to': msgWithReplyTo 'fake-message-with-files': fakeMessageWithFiles 'fake-message-reply-to-duplicates': msgWithReplyToDuplicates 'message-with-style-tags': messageWithStyleTags spyOn(DatabaseStore, 'find').andCallFake (klass, id) -> query = new ModelQuery(klass, {id}) spyOn(query, 'then').andCallFake (fn) -> return fn(fakeThread) if klass is Thread return fn(fakeMessages[id]) if klass is Message return fn(new Error('Not Stubbed')) query spyOn(DatabaseStore, 'run').andCallFake (query) -> return Promise.resolve(fakeMessage2) if query._klass is Message return Promise.reject(new Error('Not Stubbed')) spyOn(DatabaseStore, 'persistModel').andCallFake -> Promise.resolve() afterEach -> # Have to cleanup the DraftStoreProxy objects or we'll get a memory # leak error for id, session of DraftStore._draftSessions DraftStore._doneWithSession(session) describe "onComposeReply", -> beforeEach -> runs -> DraftStore._onComposeReply({threadId: fakeThread.id, messageId: fakeMessage1.id}) waitsFor -> DatabaseStore.persistModel.callCount > 0 runs -> @model = DatabaseStore.persistModel.mostRecentCall.args[0] it "should include quoted text", -> expect(@model.body.indexOf('blockquote') > 0).toBe(true) expect(@model.body.indexOf(fakeMessage1.body) > 0).toBe(true) it "should address the message to the previous message's sender", -> expect(@model.to).toEqual(fakeMessage1.from) it "should set the replyToMessageId to the previous message's ids", -> expect(@model.replyToMessageId).toEqual(fakeMessage1.id) it "should sanitize the HTML", -> expect(DraftStore._sanitizeBody).toHaveBeenCalled() it "should not call the style inliner when there are no style tags", -> expect(DraftStore._convertToInlineStyles).not.toHaveBeenCalled() expect(DraftStore._onInlineStylesResult).not.toHaveBeenCalled() describe "onComposeReply", -> 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", -> runs -> DraftStore._onComposeReply({threadId: fakeThread.id, messageId: msgWithReplyTo.id}) waitsFor -> DatabaseStore.persistModel.callCount > 0 runs -> @model = DatabaseStore.persistModel.mostRecentCall.args[0] expect(@model.to).toEqual(msgWithReplyTo.replyTo) expect(@model.cc.length).toBe 0 expect(@model.bcc.length).toBe 0 describe "onComposeReply", -> 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", -> runs -> DraftStore._onComposeReply({threadId: fakeThread.id, messageId: msgFromMe.id}) waitsFor -> DatabaseStore.persistModel.callCount > 0 runs -> @model = DatabaseStore.persistModel.mostRecentCall.args[0] expect(@model.to).toEqual(msgFromMe.to) expect(@model.cc.length).toBe 0 expect(@model.bcc.length).toBe 0 describe "onComposeReplyAll", -> beforeEach -> runs -> DraftStore._onComposeReplyAll({threadId: fakeThread.id, messageId: fakeMessage1.id}) waitsFor -> DatabaseStore.persistModel.callCount > 0 runs -> @model = DatabaseStore.persistModel.mostRecentCall.args[0] it "should include quoted text", -> expect(@model.body.indexOf('blockquote') > 0).toBe(true) expect(@model.body.indexOf(fakeMessage1.body) > 0).toBe(true) it "should address the message to the previous message's sender", -> expect(@model.to).toEqual(fakeMessage1.from) it "should cc everyone who was on the previous message in to or cc", -> ccEmails = @model.cc.map (cc) -> cc.email expect(ccEmails.sort()).toEqual([ 'ben@nylas.com', 'evan@nylas.com', 'mg@nylas.com']) it "should not include people who were bcc'd on the previous message", -> expect(@model.bcc).toEqual([]) expect(@model.cc.indexOf(fakeMessage1.bcc[0])).toEqual(-1) it "should not include you when you were cc'd on the previous message", -> ccEmails = @model.cc.map (cc) -> cc.email expect(ccEmails.indexOf(AccountStore.current().me().email)).toEqual(-1) it "should set the replyToMessageId to the previous message's ids", -> expect(@model.replyToMessageId).toEqual(fakeMessage1.id) it "should sanitize the HTML", -> expect(DraftStore._sanitizeBody).toHaveBeenCalled() it "should not call the style inliner when there are no style tags", -> expect(DraftStore._convertToInlineStyles).not.toHaveBeenCalled() expect(DraftStore._onInlineStylesResult).not.toHaveBeenCalled() describe "onComposeReplyAll", -> describe "when the message provided as context has one or more 'ReplyTo' recipients", -> beforeEach -> runs -> DraftStore._onComposeReply({threadId: fakeThread.id, messageId: msgWithReplyTo.id}) waitsFor -> DatabaseStore.persistModel.callCount > 0 runs -> @model = DatabaseStore.persistModel.mostRecentCall.args[0] it "addresses the draft to all of the message's 'ReplyTo' recipients", -> expect(@model.to).toEqual(msgWithReplyTo.replyTo) it "should not include the message's 'From' recipient in any field", -> all = [].concat(@model.to, @model.cc, @model.bcc) match = _.find all, (c) -> c.email is msgWithReplyTo.from[0].email expect(match).toEqual(undefined) describe "onComposeReplyAll", -> 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", -> runs -> DraftStore._onComposeReplyAll({threadId: fakeThread.id, messageId: msgWithReplyToDuplicates.id}) waitsFor -> DatabaseStore.persistModel.callCount > 0 runs -> model = DatabaseStore.persistModel.mostRecentCall.args[0] ccEmails = model.cc.map (cc) -> cc.email expect(ccEmails.sort()).toEqual(['1@1.com', '2@2.com', '4@4.com']) toEmails = model.to.map (to) -> to.email expect(toEmails.sort()).toEqual(['reply-to@5.com']) describe "onComposeReplyAll", -> 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", -> runs -> DraftStore._onComposeReplyAll({threadId: fakeThread.id, messageId: msgFromMe.id}) waitsFor -> DatabaseStore.persistModel.callCount > 0 runs -> @model = DatabaseStore.persistModel.mostRecentCall.args[0] expect(@model.to).toEqual(msgFromMe.to) expect(@model.cc).toEqual(msgFromMe.cc) expect(@model.bcc.length).toBe 0 describe "forwarding with attachments", -> it "should include the attached files", -> runs -> DraftStore._onComposeForward({threadId: fakeThread.id, messageId: fakeMessageWithFiles.id}) waitsFor -> DatabaseStore.persistModel.callCount > 0 runs -> @model = DatabaseStore.persistModel.mostRecentCall.args[0] expect(@model.files.length).toBe 2 expect(@model.files[0].filename).toBe "test.jpg" describe "onComposeForward", -> beforeEach -> runs -> DraftStore._onComposeForward({threadId: fakeThread.id, messageId: fakeMessage1.id}) waitsFor -> DatabaseStore.persistModel.callCount > 0 runs -> @model = DatabaseStore.persistModel.mostRecentCall.args[0] it "should include quoted text", -> expect(@model.body.indexOf('blockquote') > 0).toBe(true) expect(@model.body.indexOf(fakeMessage1.body) > 0).toBe(true) it "should not address the message to anyone", -> expect(@model.to).toEqual([]) expect(@model.cc).toEqual([]) expect(@model.bcc).toEqual([]) it "should not set the replyToMessageId", -> expect(@model.replyToMessageId).toEqual(undefined) it "should sanitize the HTML", -> expect(DraftStore._sanitizeBody).toHaveBeenCalled() it "should not call the style inliner when there are no style tags", -> expect(DraftStore._convertToInlineStyles).not.toHaveBeenCalled() expect(DraftStore._onInlineStylesResult).not.toHaveBeenCalled() describe "inlining