_ = require 'underscore' Folder = require '../../src/flux/models/folder' Thread = require '../../src/flux/models/thread' Message = require '../../src/flux/models/message' Actions = require '../../src/flux/actions' NylasAPI = require '../../src/flux/nylas-api' DatabaseStore = require '../../src/flux/stores/database-store' ChangeFolderTask = require '../../src/flux/tasks/change-folder-task' {APIError} = require '../../src/flux/errors' {Utils} = require '../../src/flux/models/utils' testFolders = {} testThreads = {} testMessages = {} describe "ChangeFolderTask", -> beforeEach -> @_findFunction = (klass, id) => if klass is Thread Promise.resolve(testThreads[id]) else if klass is Message Promise.resolve(testMessages[id]) else if klass is Folder Promise.resolve(testFolders[id]) else throw new Error("Not stubbed!") spyOn(DatabaseStore, 'persistModel').andCallFake -> Promise.resolve() spyOn(DatabaseStore, 'persistModels').andCallFake -> Promise.resolve() spyOn(DatabaseStore, 'find').andCallFake (klass, id) => @_findFunction(klass, id) spyOn(DatabaseStore, 'findAll').andCallFake (klass, finder) => if klass is Message Promise.resolve(_.values(testMessages)) else if klass is Thread Promise.resolve(_.values(testThreads)) else if klass is Folder Promise.resolve(_.values(testFolders)) else throw new Error("Not stubbed!") testFolders = @testFolders = "f1": new Folder({name: 'inbox', id: 'f1', displayName: "INBOX"}), "f2": new Folder({name: 'drafts', id: 'f2', displayName: "MyDrafts"}) "f3": new Folder({name: null, id: 'f3', displayName: "My Folder"}) testThreads = @testThreads = 't1': new Thread(id: 't1', folders: [@testFolders['f1']]) 't2': new Thread(id: 't2', folders: _.values(@testFolders)) 't3': new Thread(id: 't3', folders: [@testFolders['f2'], @testFolders['f3']]) testMessages = @testMessages = 'm1': new Message(id: 'm1', folder: @testFolders['f1']) 'm2': new Message(id: 'm2', folder: @testFolders['f2']) 'm3': new Message(id: 'm3', folder: @testFolders['f3']) @basicThreadTask = new ChangeFolderTask folderOrId: "f1" threadIds: ['t1'] @basicMessageTask = new ChangeFolderTask folderOrId: @testFolders['f2'] messageIds: ['m1'] describe "description", -> it "should include the folder name if folderOrId is a folder", -> taskWithFolderId = new ChangeFolderTask folderOrId: 'f2' messageIds: ['m1'] expect(taskWithFolderId.description()).toEqual("Moved 1 message") taskWithFolder = new ChangeFolderTask folderOrId: @testFolders['f2'] messageIds: ['m1'] expect(taskWithFolder.description()).toEqual("Moved 1 message to MyDrafts") it "should correctly mention threads and messages", -> taskWithFolderId = new ChangeFolderTask folderOrId: 'f2' messageIds: ['m1'] expect(@basicThreadTask.description()).toEqual("Moved 1 thread") taskWithFolder = new ChangeFolderTask folderOrId: @testFolders['f2'] messageIds: ['m1'] expect(@basicMessageTask.description()).toEqual("Moved 1 message to MyDrafts") describe "shouldWaitForTask", -> it "should return true if another, older ChangeFolderTask involves the same threads", -> a = new ChangeFolderTask(threadIds: ['t1', 't2', 't3']) a.creationDate = new Date(1000) b = new ChangeFolderTask(threadIds: ['t3', 't4', 't7']) b.creationDate = new Date(2000) c = new ChangeFolderTask(threadIds: ['t0', 't7']) c.creationDate = new Date(3000) expect(a.shouldWaitForTask(b)).toEqual(false) expect(a.shouldWaitForTask(c)).toEqual(false) expect(b.shouldWaitForTask(a)).toEqual(true) expect(c.shouldWaitForTask(a)).toEqual(false) expect(c.shouldWaitForTask(b)).toEqual(true) describe "performLocal", -> it "should throw an exception if task has not been given a folder", -> badTasks = [ new ChangeFolderTask(), new ChangeFolderTask(threadIds: [123]), new ChangeFolderTask(threadIds: [123], messageIds: ["foo"]), new ChangeFolderTask(threadIds: "Thread"), ] goodTasks = [ new ChangeFolderTask( folderOrId: 'f2' threadIds: ['t1', 't2'] ) new ChangeFolderTask( folderOrId: @testFolders['f2'] messageIds: ['m1'] ) ] caught = [] succeeded = [] runs -> [].concat(badTasks, goodTasks).forEach (task) -> task.performLocal() .then -> succeeded.push(task) .catch (err) -> caught.push(task) waitsFor -> succeeded.length + caught.length == 6 runs -> expect(caught.length).toEqual(badTasks.length) expect(succeeded.length).toEqual(goodTasks.length) it "throws an error if an undo task isn't passed undo data", -> t = new ChangeFolderTask folderOrId: 'f1' threadIds: ['t1', 't2'] t._isUndoTask = true waitsForPromise -> t.performLocal().catch (error) -> expect(error.message).toBe "Must pass an `undoData` to rollback folder changes" it "throws an error if an undo task is passed an empty hash of undo data", -> t = new ChangeFolderTask folderOrId: 'f1' undoData: {} threadIds: ['t1', 't2'] t._isUndoTask = true waitsForPromise -> t.performLocal().catch (error) -> expect(error.message).toBe "Must pass an `undoData` to rollback folder changes" it 'finds the folder to add by id', -> waitsForPromise => @basicThreadTask.collectCategories().then (categories) => expect(categories.folder).toEqual @testFolders['f1'] it 'finds the folder to add by folder object', -> waitsForPromise => @basicMessageTask.collectCategories().then (categories) => expect(categories.folder).toEqual @testFolders['f2'] it 'increments optimistic changes', -> spyOn(@basicThreadTask, "localUpdateThread").andReturn Promise.resolve() spyOn(NylasAPI, "incrementOptimisticChangeCount") waitsForPromise => @basicThreadTask.performLocal().then -> expect(NylasAPI.incrementOptimisticChangeCount) .toHaveBeenCalledWith(Thread, 't1') it 'removes the objectId from the set if the object cannot be found', -> spyOn(@basicThreadTask, "localUpdateThread").andReturn Promise.resolve() spyOn(NylasAPI, "incrementOptimisticChangeCount") @_findFunction = (klass, id) => if klass is Thread Promise.resolve(null) expect(@basicThreadTask.objectIds).toEqual(['t1']) waitsForPromise => @basicThreadTask.performLocal().then => expect(NylasAPI.incrementOptimisticChangeCount).not.toHaveBeenCalled() expect(@basicThreadTask.objectIds).toEqual([]) it 'decrements optimistic changes if reverting', -> spyOn(@basicThreadTask, "localUpdateThread").andReturn Promise.resolve() spyOn(NylasAPI, "decrementOptimisticChangeCount") waitsForPromise => @basicThreadTask.performLocal(reverting: true).then -> expect(NylasAPI.decrementOptimisticChangeCount) .toHaveBeenCalledWith(Thread, 't1') describe "When it's a Regular Task", -> it 'sets undo data and ignores messages that already have the folder we want', -> @basicThreadTask.performLocal().then => expectedData = originalMessageFolder: m2: @testFolders['f2'] m3: @testFolders['f3'] originalThreadFolders: t1: [@testFolders['f1']] expect(expectedData).toEqual @basicThreadTask.undoData it 'updates a thread with the new folder', -> waitsForPromise => @basicThreadTask.performLocal().then => thread = DatabaseStore.persistModel.calls[0].args[0] expect(thread.folders).toEqual [@testFolders['f1']] it "updates a thread's messages with the new folder and ignores messages that already have the same folder", -> # Our stub of DatabaseStore.findAll ignores the scoping parameter. # We simply return all messages. expectedFolder = @testFolders['f1'] waitsForPromise => @basicThreadTask.performLocal().then -> messages = DatabaseStore.persistModels.calls[0].args[0] # We expect 2 because 1 of our 3 messages already has the folder # we want. expect(messages.length).toBe 2 for message in messages expect(message.folder).toEqual expectedFolder ## MORE TESTS COMING SOON # describe "When it's an Undo Task", -> # # xit "doesn't botter updating the message if it already has the correct folder", -> # @testMessages['m4'] = # new Message(id: 'm4', folder: [@testFolders['f1'], @testFolders['f2']]) # @testMessages['m5'] = # new Message(id: 'm5', folder: []) # # expectedFolder = [@testFolders['f1'], @testFolders['f2']] # @basicThreadTask.performLocal().then => # messages = DatabaseStore.persistModels.calls[0].args[0] # expect(messages.length).toBe 4 # for message in messages # expect(message.folder).toEqual expectedFolder # expect(@testMessages['m4'] not in messages).toBe true # # xit 'updates a message with the new folder on a message task', -> # expectedFolder = [@testFolders['f1'], @testFolders['f2']] # @basicMessageTask.performLocal().then -> # thread = DatabaseStore.persistModel.calls[0].args[0] # expect(thread.folder).toEqual expectedFolder # # xit 'saves the new folder set to an instance variable on the task so performRemote can access it later', -> # expectedFolder = [@testFolders['f1'], @testFolders['f2']] # @basicThreadTask.performLocal().then => # expect(@basicThreadTask._newFolder['t1']).toEqual expectedFolder # # xdescribe 'performRemote', -> # beforeEach -> # spyOn(NylasAPI, "makeRequest").andCallFake (options) -> # options.beforeProcessing?(options.body) # return Promise.resolve() # # @multiThreadTask = new ChangeFolderTask # folderOrId: ["f1", "f2"] # folderToRemove: ["f3"] # threadIds: ['t1', 't2'] # # @multiMessageTask = new ChangeFolderTask # folderOrId: ["f1", "f2"] # folderToRemove: ["f3"] # messageIds: ['m1', 'm2'] # # expectedFolder = [@testFolders['f1'], @testFolders['f2']] # @multiThreadTask._newFolder['t1'] = expectedFolder # @multiThreadTask._newFolder['t2'] = expectedFolder # @multiMessageTask._newFolder['m1'] = expectedFolder # @multiMessageTask._newFolder['m2'] = expectedFolder # # it 'makes a new request object for each object', -> # @multiThreadTask.performRemote().then -> # expect(NylasAPI.makeRequest.calls.length).toBe 2 # # it 'decrements the optimistic change count on each request', -> # spyOn(NylasAPI, "decrementOptimisticChangeCount") # @multiThreadTask.performRemote().then -> # klass = NylasAPI.decrementOptimisticChangeCount.calls[0].args[0] # expect(NylasAPI.decrementOptimisticChangeCount.calls.length).toBe 2 # expect(klass).toBe Thread # # it 'decrements the optimistic change for messages too', -> # spyOn(NylasAPI, "decrementOptimisticChangeCount") # @multiMessageTask.performRemote().then -> # klass = NylasAPI.decrementOptimisticChangeCount.calls[0].args[0] # expect(NylasAPI.decrementOptimisticChangeCount.calls.length).toBe 2 # expect(klass).toBe Message # # it 'properly passes the folder IDs to the body', -> # @multiThreadTask.performRemote().then -> # opts = NylasAPI.makeRequest.calls[0].args[0] # expect(opts.body).toEqual folder: ['f1', 'f2'] # # it 'gets the correct endpoint for the thread tasks', -> # @multiThreadTask.performRemote().then -> # opts = NylasAPI.makeRequest.calls[0].args[0] # expect(opts.path).toEqual "/n/nsid/threads/t1" # # it 'gets the correct endpoint for the message tasks', -> # @multiMessageTask.performRemote().then -> # opts = NylasAPI.makeRequest.calls[0].args[0] # expect(opts.path).toEqual "/n/nsid/messages/m1"