Actions = require '../../src/flux/actions' SyncbackDraftTask = require '../../src/flux/tasks/syncback-draft' SendDraftTask = require '../../src/flux/tasks/send-draft' DatabaseStore = require '../../src/flux/stores/database-store' {generateTempId} = require '../../src/flux/models/utils' Message = require '../../src/flux/models/message' TaskQueue = require '../../src/flux/stores/task-queue' _ = require 'underscore-plus' describe "SendDraftTask", -> describe "shouldWaitForTask", -> it "should return any SyncbackDraftTasks for the same draft", -> @draftA = new Message version: '1' id: '1233123AEDF1' namespaceId: 'A12ADE' subject: 'New Draft' draft: true to: name: 'Dummy' email: 'dummy@inboxapp.com' @draftB = new Message version: '1' id: '1233OTHERDRAFT' namespaceId: 'A12ADE' subject: 'New Draft' draft: true to: name: 'Dummy' email: 'dummy@inboxapp.com' @saveA = new SyncbackDraftTask('localid-A') @saveB = new SyncbackDraftTask('localid-B') @sendA = new SendDraftTask('localid-A') expect(@sendA.shouldWaitForTask(@saveA)).toBe(true) describe "When on the TaskQueue", -> beforeEach -> TaskQueue._queue = [] TaskQueue._completed = [] @saveTask = new SyncbackDraftTask('localid-A') @saveTaskB = new SyncbackDraftTask('localid-B') @sendTask = new SendDraftTask('localid-A') @tasks = [@saveTask, @saveTaskB, @sendTask] describe "when tasks succeed", -> beforeEach -> for task in @tasks spyOn(task, "performLocal").andCallFake -> Promise.resolve() spyOn(task, "performRemote").andCallFake -> Promise.resolve() runs -> TaskQueue.enqueue(@saveTask, silent: true) TaskQueue.enqueue(@saveTaskB, silent: true) TaskQueue.enqueue(@sendTask) waitsFor -> @sendTask.queueState.performedRemote isnt false it "processes all of the items", -> runs -> expect(TaskQueue._queue.length).toBe 0 expect(TaskQueue._completed.length).toBe 3 it "all of the tasks", -> runs -> expect(@saveTask.performRemote).toHaveBeenCalled() expect(@saveTaskB.performRemote).toHaveBeenCalled() expect(@sendTask.performRemote).toHaveBeenCalled() it "finishes the save before sending", -> runs -> save = @saveTask.queueState.performedRemote send = @sendTask.queueState.performedRemote expect(save).toBeGreaterThan 0 expect(send).toBeGreaterThan 0 expect(save <= send).toBe true describe "performLocal", -> it "should throw an exception if the first parameter is not a localId", -> badTasks = [new SendDraftTask()] goodTasks = [new SendDraftTask('localid-a')] caught = [] succeeded = [] runs -> [].concat(badTasks, goodTasks).forEach (task) -> task.performLocal() .then -> succeeded.push(task) .catch (err) -> caught.push(task) waitsFor -> succeeded.length + caught.length == badTasks.length + goodTasks.length runs -> expect(caught).toEqual(badTasks) expect(succeeded).toEqual(goodTasks) describe "performRemote", -> beforeEach -> @draft = new Message version: '1' id: '1233123AEDF1' namespaceId: 'A12ADE' subject: 'New Draft' draft: true body: 'hello world' to: name: 'Dummy' email: 'dummy@inboxapp.com' @draftLocalId = "local-123" @task = new SendDraftTask(@draftLocalId) spyOn(atom.inbox, 'makeRequest').andCallFake (options) -> options.success() if options.success spyOn(DatabaseStore, 'findByLocalId').andCallFake (klass, localId) => Promise.resolve(@draft) spyOn(DatabaseStore, 'unpersistModel').andCallFake (draft) -> Promise.resolve() spyOn(atom, "playSound") spyOn(Actions, "postNotification") spyOn(Actions, "sendDraftSuccess") it "should unpersist when successfully sent", -> waitsForPromise => @task.performRemote().then => expect(DatabaseStore.unpersistModel).toHaveBeenCalledWith(@draft) it "should notify the draft was sent", -> waitsForPromise => @task.performRemote().then => expect(Actions.sendDraftSuccess).toHaveBeenCalledWith(@draftLocalId) it "should play a sound", -> waitsForPromise => @task.performRemote().then -> expect(atom.playSound).toHaveBeenCalledWith("mail_sent.ogg") it "post a notification", -> waitsForPromise => @task.performRemote().then -> expect(Actions.postNotification).toHaveBeenCalled() it "should start an API request to /send", -> waitsForPromise => @task.performRemote().then => expect(atom.inbox.makeRequest.calls.length).toBe(1) options = atom.inbox.makeRequest.mostRecentCall.args[0] expect(options.path).toBe("/n/#{@draft.namespaceId}/send") expect(options.method).toBe('POST') describe "when the draft has been saved", -> it "should send the draft ID and version", -> waitsForPromise => @task.performRemote().then => expect(atom.inbox.makeRequest.calls.length).toBe(1) options = atom.inbox.makeRequest.mostRecentCall.args[0] expect(options.body.version).toBe(@draft.version) expect(options.body.draft_id).toBe(@draft.id) describe "when the draft has not been saved", -> beforeEach -> @draft = new Message id: generateTempId() namespaceId: 'A12ADE' subject: 'New Draft' draft: true body: 'hello world' to: name: 'Dummy' email: 'dummy@inboxapp.com' @task = new SendDraftTask(@draftLocalId) it "should send the draft JSON", -> waitsForPromise => @task.performRemote().then => expect(atom.inbox.makeRequest.calls.length).toBe(1) options = atom.inbox.makeRequest.mostRecentCall.args[0] expect(options.body).toEqual(@draft.toJSON(joined: true)) it "should pass returnsModel:true so that the draft is saved to the data store when returned", -> waitsForPromise => @task.performRemote().then -> expect(atom.inbox.makeRequest.calls.length).toBe(1) options = atom.inbox.makeRequest.mostRecentCall.args[0] expect(options.returnsModel).toBe(true) describe "failing performRemote", -> beforeEach -> @draft = new Message version: '1' id: '1233123AEDF1' namespaceId: 'A12ADE' subject: 'New Draft' draft: true to: name: 'Dummy' email: 'dummy@inboxapp.com' @task = new SendDraftTask(@draft.id) spyOn(Actions, "dequeueTask") spyOn(Actions, "sendDraftError") it "throws an error if the draft can't be found", -> spyOn(DatabaseStore, 'findByLocalId').andCallFake (klass, localId) -> Promise.resolve() waitsForPromise => @task.performRemote().catch (error) -> expect(error.message).toBeDefined() it "throws an error if the draft isn't saved", -> spyOn(DatabaseStore, 'findByLocalId').andCallFake (klass, localId) -> Promise.resolve(isSaved: false) waitsForPromise => @task.performRemote().catch (error) -> expect(error.message).toBeDefined() it "throws an error if the DB store has issues", -> spyOn(DatabaseStore, 'findByLocalId').andCallFake (klass, localId) -> Promise.reject("DB error") waitsForPromise => @task.performRemote().catch (error) -> expect(error).toBe "DB error" checkError = -> expect(Actions.sendDraftError).toHaveBeenCalled() args = Actions.sendDraftError.calls[0].args expect(args[0]).toBe @draft.id expect(args[1].length).toBeGreaterThan 0 it "onAPIError notifies of the error", -> @task.onAPIError(message: "oh no") checkError.call(@) it "onOtherError notifies of the error", -> @task.onOtherError() checkError.call(@) it "onTimeoutError notifies of the error", -> @task.onTimeoutError() checkError.call(@) it "onOfflineError notifies of the error and dequeues", -> @task.onOfflineError() checkError.call(@) expect(Actions.dequeueTask).toHaveBeenCalledWith(@task)