Mailspring/spec/tasks/send-draft-spec.coffee

465 lines
19 KiB
CoffeeScript
Raw Normal View History

_ = require 'underscore'
feat(transactions): Explicit (and faster) database transactions Summary: Until now, we've been hiding transactions beneath the surface. When you call persistModel, you're implicitly creating a transaction. You could explicitly create them with `atomically`..., but there were several critical problems that are fixed in this diff: - Calling persistModel / unpersistModel within a transaction could cause the DatabaseStore to trigger. This could result in other parts of the app making queries /during/ the transaction, potentially before the COMMIT occurred and saved the changes. The new, explicit inTransaction syntax holds all changes until after COMMIT and then triggers. - Calling atomically and then calling persistModel inside that resulted in us having to check whether a transaction was present and was gross. - Many parts of the code ran extensive logic inside a promise chained within `atomically`: BAD: ``` DatabaseStore.atomically => DatabaseStore.persistModel(draft) => GoMakeANetworkRequestThatReturnsAPromise ``` OVERWHELMINGLY BETTER: ``` DatabaseStore.inTransaction (t) => t.persistModel(draft) .then => GoMakeANetworkRequestThatReturnsAPromise ``` Having explicit transactions also puts us on equal footing with Sequelize and other ORMs. Note that you /have/ to call DatabaseStore.inTransaction (t) =>. There is no other way to access the methods that let you alter the database. :-) Other changes: - This diff removes Message.labels and the Message-Labels table. We weren't using Message-level labels anywhere, and the table could grow very large. - This diff changes the page size during initial sync from 250 => 200 in an effort to make transactions a bit faster. Test Plan: Run tests! Reviewers: juan, evan Reviewed By: juan, evan Differential Revision: https://phab.nylas.com/D2353
2015-12-18 03:46:05 +08:00
{APIError,
Actions,
DatabaseStore,
DatabaseTransaction,
Message,
Task,
TaskQueue,
SendDraftTask,
SyncbackDraftTask,
NylasAPI,
SoundRegistry} = require 'nylas-exports'
DBt = DatabaseTransaction.prototype
fix(drafts): Various improvements and fixes to drafts, draft state management Summary: This diff contains a few major changes: 1. Scribe is no longer used for the text editor. It's just a plain contenteditable region. The toolbar items (bold, italic, underline) still work. Scribe was causing React inconcistency issues in the following scenario: - View thread with draft, edit draft - Move to another thread - Move back to thread with draft - Move to another thread. Notice that one or more messages from thread with draft are still there. There may be a way to fix this, but I tried for hours and there are Github Issues open on it's repository asking for React compatibility, so it may be fixed soon. For now contenteditable is working great. 2. Action.saveDraft() is no longer debounced in the DraftStore. Instead, firing that action causes the save to happen immediately, and the DraftStoreProxy has a new "DraftChangeSet" class which is responsbile for batching saves as the user interacts with the ComposerView. There are a couple big wins here: - In the future, we may want to be able to call Action.saveDraft() in other situations and it should behave like a normal action. We may also want to expose the DraftStoreProxy as an easy way of backing interactive draft UI. - Previously, when you added a contact to To/CC/BCC, this happened: <input> -> Action.saveDraft -> (delay!!) -> Database -> DraftStore -> DraftStoreProxy -> View Updates Increasing the delay to something reasonable like 200msec meant there was 200msec of lag before you saw the new view state. To fix this, I created a new class called DraftChangeSet which is responsible for accumulating changes as they're made and firing Action.saveDraft. "Adding" a change to the change set also causes the Draft provided by the DraftStoreProxy to change immediately (the changes are a temporary layer on top of the database object). This means no delay while changes are being applied. There's a better explanation in the source! This diff includes a few minor fixes as well: 1. Draft.state is gone—use Message.object = draft instead 2. String model attributes should never be null 3. Pre-send checks that can cancel draft send 4. Put the entire curl history and task queue into feedback reports 5. Cache localIds for extra speed 6. Move us up to latest React Test Plan: No new tests - once we lock down this new design I'll write tests for the DraftChangeSet Reviewers: evan Reviewed By: evan Differential Revision: https://review.inboxapp.com/D1125
2015-02-04 08:24:31 +08:00
describe "SendDraftTask", ->
fix(tasks): don't continue if dependent task fails Summary: Fixes T4291 If I made a final edit to a pre-existing draft and sent, we'd queue a `SyncbackDraftTask` before a `SendDraftTask`. This is important because since we have a valid draft `server_id`, the `SendDraftTask` will send by server_id, not by POSTing the whole body. If the `SyncbackDraftTask` fails, then we had a very serious issue whereby the `SendDraftTask` would keep on sending. Unfortunately the server never got the latest changes and sent the wrong version of the draft. This incorrect version would show up later when the `/send` endpoint returned the message that got actually sent. The solution was to make any queued `SendDraftTask` fail if a dependent `SyncbackDraftTask` failed. This meant we needed to make the requirements for `shouldWaitForTask` stricter, and block if tasks failed. Unfortunatley there was no infrastructure in place to do this. The first change was to change `shouldWaitForTask` to `isDependentTask`. If we're going to fail when a dependent task fails, I wanted the method name to reflect this. Now, if a dependent task fails, we recursively check the dependency tree (and check for cycles) and `dequeue` anything that needed that to succeed. I chose `dequeue` as the default action because it seemed as though all current uses of `shouldWaitForTask` really should bail if their dependencies fail. It's possible you don't want your task dequeued in this dependency case. You can return the special `Task.DO_NOT_DEQUEUE_ME` constant from the `onDependentTaskError` method. When a task gets dequeued because of the reason above, the `onDependentTaskError` callback gets fired. This gives tasks like the `SendDraftTask` a chance to notify the user that it bailed. Not all tasks need to notify. The next big issue was a better way to determine if a task truely errored to the point that we need to dequeue dependencies. In the Developer Status area we were showing tasks that had errored as "Green" because we caught the error and resolved with `Task.Status.Finished`. This used to be fine since nothing life-or-death cared if a task errored or not. Now that it might cause abortions down the line, we needed a more robust method then this. For one I changed `Task.Status.Finished` to a variety of finish types including `Task.Status.Success`. The way you "error" out is to `throw` or `Promise.reject` an `Error` object from the `performRemote` method. This allows us to propagate API errors up, and acts as a safety net that can catch any malformed code or unexpected responses. The developer bar now shows a much richer set of statuses instead of a binary one, which was REALLY helpful in debugging this. We also record when a Task got dequeued because of the conditions introduced here. Once all this was working we still had an issue of sending old drafts. If after a `SyncbackDraftTask` failed, now we'd block the send and notify the users as such. However, if we tried to send again, there was a separate issue whereby we wouldn't queue another `SyncbackDraftTask` to update the server with the latest information. Since our changes were persisted to the DB, we thought we had no changes, and therefore didn't need to queue a `SyncbackDraftTask`. The fix to this is to always force the creation of a `SyncbackDraftTask` before send regardless of the state of the `DraftStoreProxy`. Test Plan: new tests. Lots of manual testing Reviewers: bengotow Reviewed By: bengotow Subscribers: mg Maniphest Tasks: T4291 Differential Revision: https://phab.nylas.com/D2156
2015-10-22 01:33:43 +08:00
describe "isDependentTask", ->
it "is not dependent on any pending SyncbackDraftTasks", ->
fix(drafts): Various improvements and fixes to drafts, draft state management Summary: This diff contains a few major changes: 1. Scribe is no longer used for the text editor. It's just a plain contenteditable region. The toolbar items (bold, italic, underline) still work. Scribe was causing React inconcistency issues in the following scenario: - View thread with draft, edit draft - Move to another thread - Move back to thread with draft - Move to another thread. Notice that one or more messages from thread with draft are still there. There may be a way to fix this, but I tried for hours and there are Github Issues open on it's repository asking for React compatibility, so it may be fixed soon. For now contenteditable is working great. 2. Action.saveDraft() is no longer debounced in the DraftStore. Instead, firing that action causes the save to happen immediately, and the DraftStoreProxy has a new "DraftChangeSet" class which is responsbile for batching saves as the user interacts with the ComposerView. There are a couple big wins here: - In the future, we may want to be able to call Action.saveDraft() in other situations and it should behave like a normal action. We may also want to expose the DraftStoreProxy as an easy way of backing interactive draft UI. - Previously, when you added a contact to To/CC/BCC, this happened: <input> -> Action.saveDraft -> (delay!!) -> Database -> DraftStore -> DraftStoreProxy -> View Updates Increasing the delay to something reasonable like 200msec meant there was 200msec of lag before you saw the new view state. To fix this, I created a new class called DraftChangeSet which is responsible for accumulating changes as they're made and firing Action.saveDraft. "Adding" a change to the change set also causes the Draft provided by the DraftStoreProxy to change immediately (the changes are a temporary layer on top of the database object). This means no delay while changes are being applied. There's a better explanation in the source! This diff includes a few minor fixes as well: 1. Draft.state is gone—use Message.object = draft instead 2. String model attributes should never be null 3. Pre-send checks that can cancel draft send 4. Put the entire curl history and task queue into feedback reports 5. Cache localIds for extra speed 6. Move us up to latest React Test Plan: No new tests - once we lock down this new design I'll write tests for the DraftChangeSet Reviewers: evan Reviewed By: evan Differential Revision: https://review.inboxapp.com/D1125
2015-02-04 08:24:31 +08:00
@draftA = new Message
version: '1'
clientId: 'localid-A'
serverId: '1233123AEDF1'
accountId: 'A12ADE'
fix(drafts): Various improvements and fixes to drafts, draft state management Summary: This diff contains a few major changes: 1. Scribe is no longer used for the text editor. It's just a plain contenteditable region. The toolbar items (bold, italic, underline) still work. Scribe was causing React inconcistency issues in the following scenario: - View thread with draft, edit draft - Move to another thread - Move back to thread with draft - Move to another thread. Notice that one or more messages from thread with draft are still there. There may be a way to fix this, but I tried for hours and there are Github Issues open on it's repository asking for React compatibility, so it may be fixed soon. For now contenteditable is working great. 2. Action.saveDraft() is no longer debounced in the DraftStore. Instead, firing that action causes the save to happen immediately, and the DraftStoreProxy has a new "DraftChangeSet" class which is responsbile for batching saves as the user interacts with the ComposerView. There are a couple big wins here: - In the future, we may want to be able to call Action.saveDraft() in other situations and it should behave like a normal action. We may also want to expose the DraftStoreProxy as an easy way of backing interactive draft UI. - Previously, when you added a contact to To/CC/BCC, this happened: <input> -> Action.saveDraft -> (delay!!) -> Database -> DraftStore -> DraftStoreProxy -> View Updates Increasing the delay to something reasonable like 200msec meant there was 200msec of lag before you saw the new view state. To fix this, I created a new class called DraftChangeSet which is responsible for accumulating changes as they're made and firing Action.saveDraft. "Adding" a change to the change set also causes the Draft provided by the DraftStoreProxy to change immediately (the changes are a temporary layer on top of the database object). This means no delay while changes are being applied. There's a better explanation in the source! This diff includes a few minor fixes as well: 1. Draft.state is gone—use Message.object = draft instead 2. String model attributes should never be null 3. Pre-send checks that can cancel draft send 4. Put the entire curl history and task queue into feedback reports 5. Cache localIds for extra speed 6. Move us up to latest React Test Plan: No new tests - once we lock down this new design I'll write tests for the DraftChangeSet Reviewers: evan Reviewed By: evan Differential Revision: https://review.inboxapp.com/D1125
2015-02-04 08:24:31 +08:00
subject: 'New Draft'
draft: true
fix(drafts): Various improvements and fixes to drafts, draft state management Summary: This diff contains a few major changes: 1. Scribe is no longer used for the text editor. It's just a plain contenteditable region. The toolbar items (bold, italic, underline) still work. Scribe was causing React inconcistency issues in the following scenario: - View thread with draft, edit draft - Move to another thread - Move back to thread with draft - Move to another thread. Notice that one or more messages from thread with draft are still there. There may be a way to fix this, but I tried for hours and there are Github Issues open on it's repository asking for React compatibility, so it may be fixed soon. For now contenteditable is working great. 2. Action.saveDraft() is no longer debounced in the DraftStore. Instead, firing that action causes the save to happen immediately, and the DraftStoreProxy has a new "DraftChangeSet" class which is responsbile for batching saves as the user interacts with the ComposerView. There are a couple big wins here: - In the future, we may want to be able to call Action.saveDraft() in other situations and it should behave like a normal action. We may also want to expose the DraftStoreProxy as an easy way of backing interactive draft UI. - Previously, when you added a contact to To/CC/BCC, this happened: <input> -> Action.saveDraft -> (delay!!) -> Database -> DraftStore -> DraftStoreProxy -> View Updates Increasing the delay to something reasonable like 200msec meant there was 200msec of lag before you saw the new view state. To fix this, I created a new class called DraftChangeSet which is responsible for accumulating changes as they're made and firing Action.saveDraft. "Adding" a change to the change set also causes the Draft provided by the DraftStoreProxy to change immediately (the changes are a temporary layer on top of the database object). This means no delay while changes are being applied. There's a better explanation in the source! This diff includes a few minor fixes as well: 1. Draft.state is gone—use Message.object = draft instead 2. String model attributes should never be null 3. Pre-send checks that can cancel draft send 4. Put the entire curl history and task queue into feedback reports 5. Cache localIds for extra speed 6. Move us up to latest React Test Plan: No new tests - once we lock down this new design I'll write tests for the DraftChangeSet Reviewers: evan Reviewed By: evan Differential Revision: https://review.inboxapp.com/D1125
2015-02-04 08:24:31 +08:00
to:
name: 'Dummy'
email: 'dummy@nylas.com'
fix(drafts): Various improvements and fixes to drafts, draft state management Summary: This diff contains a few major changes: 1. Scribe is no longer used for the text editor. It's just a plain contenteditable region. The toolbar items (bold, italic, underline) still work. Scribe was causing React inconcistency issues in the following scenario: - View thread with draft, edit draft - Move to another thread - Move back to thread with draft - Move to another thread. Notice that one or more messages from thread with draft are still there. There may be a way to fix this, but I tried for hours and there are Github Issues open on it's repository asking for React compatibility, so it may be fixed soon. For now contenteditable is working great. 2. Action.saveDraft() is no longer debounced in the DraftStore. Instead, firing that action causes the save to happen immediately, and the DraftStoreProxy has a new "DraftChangeSet" class which is responsbile for batching saves as the user interacts with the ComposerView. There are a couple big wins here: - In the future, we may want to be able to call Action.saveDraft() in other situations and it should behave like a normal action. We may also want to expose the DraftStoreProxy as an easy way of backing interactive draft UI. - Previously, when you added a contact to To/CC/BCC, this happened: <input> -> Action.saveDraft -> (delay!!) -> Database -> DraftStore -> DraftStoreProxy -> View Updates Increasing the delay to something reasonable like 200msec meant there was 200msec of lag before you saw the new view state. To fix this, I created a new class called DraftChangeSet which is responsible for accumulating changes as they're made and firing Action.saveDraft. "Adding" a change to the change set also causes the Draft provided by the DraftStoreProxy to change immediately (the changes are a temporary layer on top of the database object). This means no delay while changes are being applied. There's a better explanation in the source! This diff includes a few minor fixes as well: 1. Draft.state is gone—use Message.object = draft instead 2. String model attributes should never be null 3. Pre-send checks that can cancel draft send 4. Put the entire curl history and task queue into feedback reports 5. Cache localIds for extra speed 6. Move us up to latest React Test Plan: No new tests - once we lock down this new design I'll write tests for the DraftChangeSet Reviewers: evan Reviewed By: evan Differential Revision: https://review.inboxapp.com/D1125
2015-02-04 08:24:31 +08:00
@draftB = new Message
version: '1'
clientId: 'localid-B'
serverId: '1233OTHERDRAFT'
accountId: 'A12ADE'
fix(drafts): Various improvements and fixes to drafts, draft state management Summary: This diff contains a few major changes: 1. Scribe is no longer used for the text editor. It's just a plain contenteditable region. The toolbar items (bold, italic, underline) still work. Scribe was causing React inconcistency issues in the following scenario: - View thread with draft, edit draft - Move to another thread - Move back to thread with draft - Move to another thread. Notice that one or more messages from thread with draft are still there. There may be a way to fix this, but I tried for hours and there are Github Issues open on it's repository asking for React compatibility, so it may be fixed soon. For now contenteditable is working great. 2. Action.saveDraft() is no longer debounced in the DraftStore. Instead, firing that action causes the save to happen immediately, and the DraftStoreProxy has a new "DraftChangeSet" class which is responsbile for batching saves as the user interacts with the ComposerView. There are a couple big wins here: - In the future, we may want to be able to call Action.saveDraft() in other situations and it should behave like a normal action. We may also want to expose the DraftStoreProxy as an easy way of backing interactive draft UI. - Previously, when you added a contact to To/CC/BCC, this happened: <input> -> Action.saveDraft -> (delay!!) -> Database -> DraftStore -> DraftStoreProxy -> View Updates Increasing the delay to something reasonable like 200msec meant there was 200msec of lag before you saw the new view state. To fix this, I created a new class called DraftChangeSet which is responsible for accumulating changes as they're made and firing Action.saveDraft. "Adding" a change to the change set also causes the Draft provided by the DraftStoreProxy to change immediately (the changes are a temporary layer on top of the database object). This means no delay while changes are being applied. There's a better explanation in the source! This diff includes a few minor fixes as well: 1. Draft.state is gone—use Message.object = draft instead 2. String model attributes should never be null 3. Pre-send checks that can cancel draft send 4. Put the entire curl history and task queue into feedback reports 5. Cache localIds for extra speed 6. Move us up to latest React Test Plan: No new tests - once we lock down this new design I'll write tests for the DraftChangeSet Reviewers: evan Reviewed By: evan Differential Revision: https://review.inboxapp.com/D1125
2015-02-04 08:24:31 +08:00
subject: 'New Draft'
draft: true
fix(drafts): Various improvements and fixes to drafts, draft state management Summary: This diff contains a few major changes: 1. Scribe is no longer used for the text editor. It's just a plain contenteditable region. The toolbar items (bold, italic, underline) still work. Scribe was causing React inconcistency issues in the following scenario: - View thread with draft, edit draft - Move to another thread - Move back to thread with draft - Move to another thread. Notice that one or more messages from thread with draft are still there. There may be a way to fix this, but I tried for hours and there are Github Issues open on it's repository asking for React compatibility, so it may be fixed soon. For now contenteditable is working great. 2. Action.saveDraft() is no longer debounced in the DraftStore. Instead, firing that action causes the save to happen immediately, and the DraftStoreProxy has a new "DraftChangeSet" class which is responsbile for batching saves as the user interacts with the ComposerView. There are a couple big wins here: - In the future, we may want to be able to call Action.saveDraft() in other situations and it should behave like a normal action. We may also want to expose the DraftStoreProxy as an easy way of backing interactive draft UI. - Previously, when you added a contact to To/CC/BCC, this happened: <input> -> Action.saveDraft -> (delay!!) -> Database -> DraftStore -> DraftStoreProxy -> View Updates Increasing the delay to something reasonable like 200msec meant there was 200msec of lag before you saw the new view state. To fix this, I created a new class called DraftChangeSet which is responsible for accumulating changes as they're made and firing Action.saveDraft. "Adding" a change to the change set also causes the Draft provided by the DraftStoreProxy to change immediately (the changes are a temporary layer on top of the database object). This means no delay while changes are being applied. There's a better explanation in the source! This diff includes a few minor fixes as well: 1. Draft.state is gone—use Message.object = draft instead 2. String model attributes should never be null 3. Pre-send checks that can cancel draft send 4. Put the entire curl history and task queue into feedback reports 5. Cache localIds for extra speed 6. Move us up to latest React Test Plan: No new tests - once we lock down this new design I'll write tests for the DraftChangeSet Reviewers: evan Reviewed By: evan Differential Revision: https://review.inboxapp.com/D1125
2015-02-04 08:24:31 +08:00
to:
name: 'Dummy'
email: 'dummy@nylas.com'
fix(drafts): Various improvements and fixes to drafts, draft state management Summary: This diff contains a few major changes: 1. Scribe is no longer used for the text editor. It's just a plain contenteditable region. The toolbar items (bold, italic, underline) still work. Scribe was causing React inconcistency issues in the following scenario: - View thread with draft, edit draft - Move to another thread - Move back to thread with draft - Move to another thread. Notice that one or more messages from thread with draft are still there. There may be a way to fix this, but I tried for hours and there are Github Issues open on it's repository asking for React compatibility, so it may be fixed soon. For now contenteditable is working great. 2. Action.saveDraft() is no longer debounced in the DraftStore. Instead, firing that action causes the save to happen immediately, and the DraftStoreProxy has a new "DraftChangeSet" class which is responsbile for batching saves as the user interacts with the ComposerView. There are a couple big wins here: - In the future, we may want to be able to call Action.saveDraft() in other situations and it should behave like a normal action. We may also want to expose the DraftStoreProxy as an easy way of backing interactive draft UI. - Previously, when you added a contact to To/CC/BCC, this happened: <input> -> Action.saveDraft -> (delay!!) -> Database -> DraftStore -> DraftStoreProxy -> View Updates Increasing the delay to something reasonable like 200msec meant there was 200msec of lag before you saw the new view state. To fix this, I created a new class called DraftChangeSet which is responsible for accumulating changes as they're made and firing Action.saveDraft. "Adding" a change to the change set also causes the Draft provided by the DraftStoreProxy to change immediately (the changes are a temporary layer on top of the database object). This means no delay while changes are being applied. There's a better explanation in the source! This diff includes a few minor fixes as well: 1. Draft.state is gone—use Message.object = draft instead 2. String model attributes should never be null 3. Pre-send checks that can cancel draft send 4. Put the entire curl history and task queue into feedback reports 5. Cache localIds for extra speed 6. Move us up to latest React Test Plan: No new tests - once we lock down this new design I'll write tests for the DraftChangeSet Reviewers: evan Reviewed By: evan Differential Revision: https://review.inboxapp.com/D1125
2015-02-04 08:24:31 +08:00
@saveA = new SyncbackDraftTask('localid-A')
@saveB = new SyncbackDraftTask('localid-B')
fix(drafts): Various improvements and fixes to drafts, draft state management Summary: This diff contains a few major changes: 1. Scribe is no longer used for the text editor. It's just a plain contenteditable region. The toolbar items (bold, italic, underline) still work. Scribe was causing React inconcistency issues in the following scenario: - View thread with draft, edit draft - Move to another thread - Move back to thread with draft - Move to another thread. Notice that one or more messages from thread with draft are still there. There may be a way to fix this, but I tried for hours and there are Github Issues open on it's repository asking for React compatibility, so it may be fixed soon. For now contenteditable is working great. 2. Action.saveDraft() is no longer debounced in the DraftStore. Instead, firing that action causes the save to happen immediately, and the DraftStoreProxy has a new "DraftChangeSet" class which is responsbile for batching saves as the user interacts with the ComposerView. There are a couple big wins here: - In the future, we may want to be able to call Action.saveDraft() in other situations and it should behave like a normal action. We may also want to expose the DraftStoreProxy as an easy way of backing interactive draft UI. - Previously, when you added a contact to To/CC/BCC, this happened: <input> -> Action.saveDraft -> (delay!!) -> Database -> DraftStore -> DraftStoreProxy -> View Updates Increasing the delay to something reasonable like 200msec meant there was 200msec of lag before you saw the new view state. To fix this, I created a new class called DraftChangeSet which is responsible for accumulating changes as they're made and firing Action.saveDraft. "Adding" a change to the change set also causes the Draft provided by the DraftStoreProxy to change immediately (the changes are a temporary layer on top of the database object). This means no delay while changes are being applied. There's a better explanation in the source! This diff includes a few minor fixes as well: 1. Draft.state is gone—use Message.object = draft instead 2. String model attributes should never be null 3. Pre-send checks that can cancel draft send 4. Put the entire curl history and task queue into feedback reports 5. Cache localIds for extra speed 6. Move us up to latest React Test Plan: No new tests - once we lock down this new design I'll write tests for the DraftChangeSet Reviewers: evan Reviewed By: evan Differential Revision: https://review.inboxapp.com/D1125
2015-02-04 08:24:31 +08:00
@sendA = new SendDraftTask('localid-A')
expect(@sendA.isDependentTask(@saveA)).toBe(false)
fix(drafts): Various improvements and fixes to drafts, draft state management Summary: This diff contains a few major changes: 1. Scribe is no longer used for the text editor. It's just a plain contenteditable region. The toolbar items (bold, italic, underline) still work. Scribe was causing React inconcistency issues in the following scenario: - View thread with draft, edit draft - Move to another thread - Move back to thread with draft - Move to another thread. Notice that one or more messages from thread with draft are still there. There may be a way to fix this, but I tried for hours and there are Github Issues open on it's repository asking for React compatibility, so it may be fixed soon. For now contenteditable is working great. 2. Action.saveDraft() is no longer debounced in the DraftStore. Instead, firing that action causes the save to happen immediately, and the DraftStoreProxy has a new "DraftChangeSet" class which is responsbile for batching saves as the user interacts with the ComposerView. There are a couple big wins here: - In the future, we may want to be able to call Action.saveDraft() in other situations and it should behave like a normal action. We may also want to expose the DraftStoreProxy as an easy way of backing interactive draft UI. - Previously, when you added a contact to To/CC/BCC, this happened: <input> -> Action.saveDraft -> (delay!!) -> Database -> DraftStore -> DraftStoreProxy -> View Updates Increasing the delay to something reasonable like 200msec meant there was 200msec of lag before you saw the new view state. To fix this, I created a new class called DraftChangeSet which is responsible for accumulating changes as they're made and firing Action.saveDraft. "Adding" a change to the change set also causes the Draft provided by the DraftStoreProxy to change immediately (the changes are a temporary layer on top of the database object). This means no delay while changes are being applied. There's a better explanation in the source! This diff includes a few minor fixes as well: 1. Draft.state is gone—use Message.object = draft instead 2. String model attributes should never be null 3. Pre-send checks that can cancel draft send 4. Put the entire curl history and task queue into feedback reports 5. Cache localIds for extra speed 6. Move us up to latest React Test Plan: No new tests - once we lock down this new design I'll write tests for the DraftChangeSet Reviewers: evan Reviewed By: evan Differential Revision: https://review.inboxapp.com/D1125
2015-02-04 08:24:31 +08:00
describe "performLocal", ->
it "throws an error if we we don't pass a draftClientId", ->
badTask = new SendDraftTask()
badTask.performLocal()
.then ->
throw new Error("Shouldn't succeed")
.catch (err) ->
expect(err.message).toBe "Attempt to call SendDraftTask.performLocal without @draftClientId."
it "finds the message and saves a backup copy of it", ->
draft = new Message
clientId: "local-123"
serverId: "server-123"
draft: true
fix(drafts): Various improvements and fixes to drafts, draft state management Summary: This diff contains a few major changes: 1. Scribe is no longer used for the text editor. It's just a plain contenteditable region. The toolbar items (bold, italic, underline) still work. Scribe was causing React inconcistency issues in the following scenario: - View thread with draft, edit draft - Move to another thread - Move back to thread with draft - Move to another thread. Notice that one or more messages from thread with draft are still there. There may be a way to fix this, but I tried for hours and there are Github Issues open on it's repository asking for React compatibility, so it may be fixed soon. For now contenteditable is working great. 2. Action.saveDraft() is no longer debounced in the DraftStore. Instead, firing that action causes the save to happen immediately, and the DraftStoreProxy has a new "DraftChangeSet" class which is responsbile for batching saves as the user interacts with the ComposerView. There are a couple big wins here: - In the future, we may want to be able to call Action.saveDraft() in other situations and it should behave like a normal action. We may also want to expose the DraftStoreProxy as an easy way of backing interactive draft UI. - Previously, when you added a contact to To/CC/BCC, this happened: <input> -> Action.saveDraft -> (delay!!) -> Database -> DraftStore -> DraftStoreProxy -> View Updates Increasing the delay to something reasonable like 200msec meant there was 200msec of lag before you saw the new view state. To fix this, I created a new class called DraftChangeSet which is responsible for accumulating changes as they're made and firing Action.saveDraft. "Adding" a change to the change set also causes the Draft provided by the DraftStoreProxy to change immediately (the changes are a temporary layer on top of the database object). This means no delay while changes are being applied. There's a better explanation in the source! This diff includes a few minor fixes as well: 1. Draft.state is gone—use Message.object = draft instead 2. String model attributes should never be null 3. Pre-send checks that can cancel draft send 4. Put the entire curl history and task queue into feedback reports 5. Cache localIds for extra speed 6. Move us up to latest React Test Plan: No new tests - once we lock down this new design I'll write tests for the DraftChangeSet Reviewers: evan Reviewed By: evan Differential Revision: https://review.inboxapp.com/D1125
2015-02-04 08:24:31 +08:00
calledBody = "ERROR: The body wasn't included!"
spyOn(DatabaseStore, "findBy").andCallFake ->
then: -> throw new Error("You must include the body!")
include: (body) ->
calledBody = body
return Promise.resolve(draft)
task = new SendDraftTask('local-123')
waitsForPromise => task.performLocal().then =>
expect(task.backupDraft).toBeDefined()
expect(task.backupDraft.clientId).toBe "local-123"
expect(task.backupDraft.serverId).toBe "server-123"
expect(task.backupDraft).not.toBe draft # It's a clone
expect(task.replyToMessageId).not.toBeDefined()
expect(task.threadId).not.toBeDefined()
expect(calledBody).toBe Message.attributes.body
fix(drafts): Various improvements and fixes to drafts, draft state management Summary: This diff contains a few major changes: 1. Scribe is no longer used for the text editor. It's just a plain contenteditable region. The toolbar items (bold, italic, underline) still work. Scribe was causing React inconcistency issues in the following scenario: - View thread with draft, edit draft - Move to another thread - Move back to thread with draft - Move to another thread. Notice that one or more messages from thread with draft are still there. There may be a way to fix this, but I tried for hours and there are Github Issues open on it's repository asking for React compatibility, so it may be fixed soon. For now contenteditable is working great. 2. Action.saveDraft() is no longer debounced in the DraftStore. Instead, firing that action causes the save to happen immediately, and the DraftStoreProxy has a new "DraftChangeSet" class which is responsbile for batching saves as the user interacts with the ComposerView. There are a couple big wins here: - In the future, we may want to be able to call Action.saveDraft() in other situations and it should behave like a normal action. We may also want to expose the DraftStoreProxy as an easy way of backing interactive draft UI. - Previously, when you added a contact to To/CC/BCC, this happened: <input> -> Action.saveDraft -> (delay!!) -> Database -> DraftStore -> DraftStoreProxy -> View Updates Increasing the delay to something reasonable like 200msec meant there was 200msec of lag before you saw the new view state. To fix this, I created a new class called DraftChangeSet which is responsible for accumulating changes as they're made and firing Action.saveDraft. "Adding" a change to the change set also causes the Draft provided by the DraftStoreProxy to change immediately (the changes are a temporary layer on top of the database object). This means no delay while changes are being applied. There's a better explanation in the source! This diff includes a few minor fixes as well: 1. Draft.state is gone—use Message.object = draft instead 2. String model attributes should never be null 3. Pre-send checks that can cancel draft send 4. Put the entire curl history and task queue into feedback reports 5. Cache localIds for extra speed 6. Move us up to latest React Test Plan: No new tests - once we lock down this new design I'll write tests for the DraftChangeSet Reviewers: evan Reviewed By: evan Differential Revision: https://review.inboxapp.com/D1125
2015-02-04 08:24:31 +08:00
describe "performRemote", ->
beforeEach ->
@accountId = "a123"
@draftClientId = "local-123"
@serverMessageId = '1233123AEDF1'
@response =
version: 2
id: @serverMessageId
account_id: @accountId
subject: 'New Draft'
body: 'hello world'
to:
name: 'Dummy'
email: 'dummy@nylas.com'
spyOn(NylasAPI, 'makeRequest').andCallFake (options) =>
options.success?(@response)
Promise.resolve(@response)
spyOn(DBt, 'unpersistModel').andCallFake (draft) ->
fix(drafts): Various improvements and fixes to drafts, draft state management Summary: This diff contains a few major changes: 1. Scribe is no longer used for the text editor. It's just a plain contenteditable region. The toolbar items (bold, italic, underline) still work. Scribe was causing React inconcistency issues in the following scenario: - View thread with draft, edit draft - Move to another thread - Move back to thread with draft - Move to another thread. Notice that one or more messages from thread with draft are still there. There may be a way to fix this, but I tried for hours and there are Github Issues open on it's repository asking for React compatibility, so it may be fixed soon. For now contenteditable is working great. 2. Action.saveDraft() is no longer debounced in the DraftStore. Instead, firing that action causes the save to happen immediately, and the DraftStoreProxy has a new "DraftChangeSet" class which is responsbile for batching saves as the user interacts with the ComposerView. There are a couple big wins here: - In the future, we may want to be able to call Action.saveDraft() in other situations and it should behave like a normal action. We may also want to expose the DraftStoreProxy as an easy way of backing interactive draft UI. - Previously, when you added a contact to To/CC/BCC, this happened: <input> -> Action.saveDraft -> (delay!!) -> Database -> DraftStore -> DraftStoreProxy -> View Updates Increasing the delay to something reasonable like 200msec meant there was 200msec of lag before you saw the new view state. To fix this, I created a new class called DraftChangeSet which is responsible for accumulating changes as they're made and firing Action.saveDraft. "Adding" a change to the change set also causes the Draft provided by the DraftStoreProxy to change immediately (the changes are a temporary layer on top of the database object). This means no delay while changes are being applied. There's a better explanation in the source! This diff includes a few minor fixes as well: 1. Draft.state is gone—use Message.object = draft instead 2. String model attributes should never be null 3. Pre-send checks that can cancel draft send 4. Put the entire curl history and task queue into feedback reports 5. Cache localIds for extra speed 6. Move us up to latest React Test Plan: No new tests - once we lock down this new design I'll write tests for the DraftChangeSet Reviewers: evan Reviewed By: evan Differential Revision: https://review.inboxapp.com/D1125
2015-02-04 08:24:31 +08:00
Promise.resolve()
spyOn(DBt, 'persistModel').andCallFake (draft) ->
Promise.resolve()
2015-10-02 10:24:06 +08:00
spyOn(SoundRegistry, "playSound")
spyOn(Actions, "postNotification")
spyOn(Actions, "sendDraftSuccess")
spyOn(NylasEnv, "emitError")
runFetchLatestDraftTests = ->
it "fetches the draft object from the DB", ->
waitsForPromise => @task._fetchLatestDraft().then (model) =>
expect(model).toBe @draft
expect(@task.draftAccountId).toBe @draft.accountId
expect(@task.draftServerId).toBe @draft.serverId
expect(@task.draftVersion).toBe @draft.version
it "throws a `NotFoundError` if the model is blank", ->
spyOn(@task, "_notifyUserOfError")
spyOn(@task, "_permanentError").andCallThrough()
@draftResolver = -> Promise.resolve(null)
waitsForPromise => @task.performRemote().then =>
expect(DBt.persistModel.callCount).toBe 1
expect(DBt.persistModel).toHaveBeenCalledWith(@backupDraft)
expect(@task._permanentError).toHaveBeenCalled()
it "throws a `NotFoundError` if findBy fails", ->
spyOn(@task, "_notifyUserOfError")
spyOn(@task, "_permanentError").andCallThrough()
@draftResolver = -> Promise.reject(new Error("Test Problem"))
waitsForPromise => @task.performRemote().then =>
expect(DBt.persistModel.callCount).toBe 1
expect(DBt.persistModel).toHaveBeenCalledWith(@backupDraft)
expect(@task._permanentError).toHaveBeenCalled()
# All of these are run in both the context of a saved draft and a new
# draft.
runMakeSendRequestTests = ->
it "makes a send request with the correct data", ->
@task.draftAccountId = @accountId
waitsForPromise => @task._makeSendRequest(@draft).then =>
expect(NylasAPI.makeRequest).toHaveBeenCalled()
reqArgs = NylasAPI.makeRequest.calls[0].args[0]
expect(reqArgs.accountId).toBe @accountId
expect(reqArgs.body).toEqual @draft.toJSON()
it "should pass returnsModel:false", ->
waitsForPromise => @task._makeSendRequest(@draft).then ->
expect(NylasAPI.makeRequest.calls.length).toBe(1)
options = NylasAPI.makeRequest.mostRecentCall.args[0]
expect(options.returnsModel).toBe(false)
it "should always send the draft body in the request body (joined attribute check)", ->
waitsForPromise =>
@task._makeSendRequest(@draft).then =>
expect(NylasAPI.makeRequest.calls.length).toBe(1)
options = NylasAPI.makeRequest.mostRecentCall.args[0]
expect(options.body.body).toBe('hello world')
it "should start an API request to /send", -> waitsForPromise =>
@task._makeSendRequest(@draft).then =>
expect(NylasAPI.makeRequest.calls.length).toBe(1)
options = NylasAPI.makeRequest.mostRecentCall.args[0]
expect(options.path).toBe("/send")
fix(drafts): Various improvements and fixes to drafts, draft state management Summary: This diff contains a few major changes: 1. Scribe is no longer used for the text editor. It's just a plain contenteditable region. The toolbar items (bold, italic, underline) still work. Scribe was causing React inconcistency issues in the following scenario: - View thread with draft, edit draft - Move to another thread - Move back to thread with draft - Move to another thread. Notice that one or more messages from thread with draft are still there. There may be a way to fix this, but I tried for hours and there are Github Issues open on it's repository asking for React compatibility, so it may be fixed soon. For now contenteditable is working great. 2. Action.saveDraft() is no longer debounced in the DraftStore. Instead, firing that action causes the save to happen immediately, and the DraftStoreProxy has a new "DraftChangeSet" class which is responsbile for batching saves as the user interacts with the ComposerView. There are a couple big wins here: - In the future, we may want to be able to call Action.saveDraft() in other situations and it should behave like a normal action. We may also want to expose the DraftStoreProxy as an easy way of backing interactive draft UI. - Previously, when you added a contact to To/CC/BCC, this happened: <input> -> Action.saveDraft -> (delay!!) -> Database -> DraftStore -> DraftStoreProxy -> View Updates Increasing the delay to something reasonable like 200msec meant there was 200msec of lag before you saw the new view state. To fix this, I created a new class called DraftChangeSet which is responsible for accumulating changes as they're made and firing Action.saveDraft. "Adding" a change to the change set also causes the Draft provided by the DraftStoreProxy to change immediately (the changes are a temporary layer on top of the database object). This means no delay while changes are being applied. There's a better explanation in the source! This diff includes a few minor fixes as well: 1. Draft.state is gone—use Message.object = draft instead 2. String model attributes should never be null 3. Pre-send checks that can cancel draft send 4. Put the entire curl history and task queue into feedback reports 5. Cache localIds for extra speed 6. Move us up to latest React Test Plan: No new tests - once we lock down this new design I'll write tests for the DraftChangeSet Reviewers: evan Reviewed By: evan Differential Revision: https://review.inboxapp.com/D1125
2015-02-04 08:24:31 +08:00
expect(options.method).toBe('POST')
it "retries the task if 'Invalid message public id'", ->
jasmine.unspy(NylasAPI, "makeRequest")
spyOn(NylasAPI, 'makeRequest').andCallFake (options) =>
if options.body.reply_to_message_id
err = new APIError(body: "Invalid message public id")
Promise.reject(err)
else
options.success?(@response)
Promise.resolve(@response)
@draft.replyToMessageId = "reply-123"
@draft.threadId = "thread-123"
waitsForPromise => @task._makeSendRequest(@draft).then =>
expect(NylasAPI.makeRequest).toHaveBeenCalled()
expect(NylasAPI.makeRequest.callCount).toEqual 2
req1 = NylasAPI.makeRequest.calls[0].args[0]
req2 = NylasAPI.makeRequest.calls[1].args[0]
expect(req1.body.reply_to_message_id).toBe "reply-123"
expect(req1.body.thread_id).toBe "thread-123"
expect(req2.body.reply_to_message_id).toBe null
expect(req2.body.thread_id).toBe "thread-123"
it "retries the task if 'Invalid message public id'", ->
jasmine.unspy(NylasAPI, "makeRequest")
spyOn(NylasAPI, 'makeRequest').andCallFake (options) =>
if options.body.reply_to_message_id
err = new APIError(body: "Invalid thread")
Promise.reject(err)
else
options.success?(@response)
Promise.resolve(@response)
@draft.replyToMessageId = "reply-123"
@draft.threadId = "thread-123"
waitsForPromise => @task._makeSendRequest(@draft).then =>
expect(NylasAPI.makeRequest).toHaveBeenCalled()
expect(NylasAPI.makeRequest.callCount).toEqual 2
req1 = NylasAPI.makeRequest.calls[0].args[0]
req2 = NylasAPI.makeRequest.calls[1].args[0]
expect(req1.body.reply_to_message_id).toBe "reply-123"
expect(req1.body.thread_id).toBe "thread-123"
expect(req2.body.reply_to_message_id).toBe null
expect(req2.body.thread_id).toBe null
runSaveNewMessageTests = ->
it "should write the saved message to the database with the same client ID", ->
waitsForPromise =>
@task._saveNewMessage(@response).then =>
expect(DBt.persistModel).toHaveBeenCalled()
expect(DBt.persistModel.mostRecentCall.args[0].clientId).toEqual(@draftClientId)
expect(DBt.persistModel.mostRecentCall.args[0].serverId).toEqual(@serverMessageId)
expect(DBt.persistModel.mostRecentCall.args[0].draft).toEqual(false)
runNotifySuccess = ->
it "should notify the draft was sent", ->
waitsForPromise => @task.performRemote().then =>
args = Actions.sendDraftSuccess.calls[0].args[0]
expect(args.draftClientId).toBe @draftClientId
it "get an object back on success", ->
waitsForPromise => @task.performRemote().then =>
args = Actions.sendDraftSuccess.calls[0].args[0]
expect(args.newMessage.id).toBe @serverMessageId
it "should play a sound", ->
spyOn(NylasEnv.config, "get").andReturn true
waitsForPromise => @task.performRemote().then ->
expect(NylasEnv.config.get).toHaveBeenCalledWith("core.sending.sounds")
expect(SoundRegistry.playSound).toHaveBeenCalledWith("send")
it "shouldn't play a sound if the config is disabled", ->
spyOn(NylasEnv.config, "get").andReturn false
waitsForPromise => @task.performRemote().then ->
expect(NylasEnv.config.get).toHaveBeenCalledWith("core.sending.sounds")
expect(SoundRegistry.playSound).not.toHaveBeenCalled()
runIntegrativeWithErrors = ->
describe "when there are errors", ->
beforeEach ->
spyOn(@task, "_notifyUserOfError")
jasmine.unspy(NylasAPI, "makeRequest")
it "notifies of a permanent error of misc error types", ->
## DB error
thrownError = null
jasmine.unspy(DBt, "persistModel")
spyOn(DBt, "persistModel").andCallFake =>
thrownError = new Error('db error')
throw thrownError
waitsForPromise =>
@task.performRemote().then (status) =>
expect(status[0]).toBe Task.Status.Failed
expect(status[1]).toBe thrownError
expect(@task._notifyUserOfError).toHaveBeenCalled()
expect(NylasEnv.emitError).toHaveBeenCalled()
it "notifies of a permanent error on 500 errors", ->
thrownError = new APIError(statusCode: 500, body: "err")
spyOn(NylasAPI, 'makeRequest').andCallFake (options) =>
Promise.reject(thrownError)
waitsForPromise => @task.performRemote().then (status) =>
expect(status[0]).toBe Task.Status.Failed
expect(status[1]).toBe thrownError
expect(@task._notifyUserOfError).toHaveBeenCalled()
expect(NylasEnv.emitError).not.toHaveBeenCalled()
it "notifies us and users of a permanent error on 400 errors", ->
thrownError = new APIError(statusCode: 400, body: "err")
spyOn(NylasAPI, 'makeRequest').andCallFake (options) =>
Promise.reject(thrownError)
waitsForPromise => @task.performRemote().then (status) =>
expect(status[0]).toBe Task.Status.Failed
expect(status[1]).toBe thrownError
expect(@task._notifyUserOfError).toHaveBeenCalled()
expect(NylasEnv.emitError).toHaveBeenCalled()
it "notifies of a permanent error on timeouts", ->
thrownError = new APIError(statusCode: NylasAPI.TimeoutErrorCode, body: "err")
spyOn(NylasAPI, 'makeRequest').andCallFake (options) =>
Promise.reject(thrownError)
waitsForPromise => @task.performRemote().then (status) =>
expect(status[0]).toBe Task.Status.Failed
expect(status[1]).toBe thrownError
expect(@task._notifyUserOfError).toHaveBeenCalled()
expect(NylasEnv.emitError).not.toHaveBeenCalled()
it "retries for other error types", ->
thrownError = new APIError(statusCode: 402, body: "err")
spyOn(NylasAPI, 'makeRequest').andCallFake (options) =>
Promise.reject(thrownError)
waitsForPromise => @task.performRemote().then (status) =>
expect(status).toBe Task.Status.Retry
expect(@task._notifyUserOfError).not.toHaveBeenCalled()
expect(NylasEnv.emitError).not.toHaveBeenCalled()
describe "checking the promise chain halts on errors", ->
beforeEach ->
spyOn(@task,"_makeSendRequest").andCallThrough()
spyOn(@task,"_saveNewMessage").andCallThrough()
spyOn(@task,"_deleteRemoteDraft").andCallThrough()
spyOn(@task,"_notifySuccess").andCallThrough()
spyOn(@task,"_onError").andCallThrough()
@expectBlockedChain = =>
expect(@task._makeSendRequest).toHaveBeenCalled()
expect(@task._saveNewMessage).not.toHaveBeenCalled()
expect(@task._deleteRemoteDraft).not.toHaveBeenCalled()
expect(@task._notifySuccess).not.toHaveBeenCalled()
expect(@task._onError).toHaveBeenCalled()
it "halts on 500s", ->
thrownError = new APIError(statusCode: 500, body: "err")
spyOn(NylasAPI, 'makeRequest').andCallFake (options) =>
Promise.reject(thrownError)
waitsForPromise => @task.performRemote().then (status) =>
@expectBlockedChain()
it "halts on 400s", ->
thrownError = new APIError(statusCode: 400, body: "err")
spyOn(NylasAPI, 'makeRequest').andCallFake (options) =>
Promise.reject(thrownError)
waitsForPromise => @task.performRemote().then (status) =>
@expectBlockedChain()
it "halts on other errors", ->
thrownError = new Error("oh no")
spyOn(NylasAPI, 'makeRequest').andCallFake (options) =>
Promise.reject(thrownError)
waitsForPromise => @task.performRemote().then (status) =>
@expectBlockedChain()
it "dosn't halt on success", ->
spyOn(NylasAPI, 'makeRequest').andCallFake (options) =>
options.success?(@response)
Promise.resolve(@response)
waitsForPromise => @task.performRemote().then (status) =>
expect(@task._makeSendRequest).toHaveBeenCalled()
expect(@task._saveNewMessage).toHaveBeenCalled()
expect(@task._deleteRemoteDraft).toHaveBeenCalled()
expect(@task._notifySuccess).toHaveBeenCalled()
expect(@task._onError).not.toHaveBeenCalled()
describe "with a new draft", ->
beforeEach ->
@draft = new Message
version: 1
clientId: @draftClientId
accountId: @accountId
subject: 'New Draft'
draft: true
body: 'hello world'
@task = new SendDraftTask(@draftClientId)
@backupDraft = @draft.clone()
@task.backupDraft = @backupDraft # Since performLocal doesn't run
@draftResolver = -> Promise.resolve(@draft)
@calledBody = "ERROR: The body wasn't included!"
spyOn(DatabaseStore, "findBy").andCallFake =>
include: (body) =>
@calledBody = body
return @draftResolver()
it "can complete a full performRemote", -> waitsForPromise =>
@task.performRemote().then (status) ->
expect(status).toBe Task.Status.Success
runFetchLatestDraftTests.call(@)
runMakeSendRequestTests.call(@)
runSaveNewMessageTests.call(@)
it "shouldn't attempt to delete a draft", -> waitsForPromise =>
expect(@task.draftServerId).not.toBeDefined()
@task._deleteRemoteDraft().then =>
expect(NylasAPI.makeRequest).not.toHaveBeenCalled()
runNotifySuccess.call(@)
runIntegrativeWithErrors.call(@)
it "should locally convert the draft to a message on send", ->
expect(@draft.clientId).toBe @draftClientId
expect(@draft.serverId).toBeUndefined()
waitsForPromise => @task.performRemote().then =>
expect(DBt.persistModel).toHaveBeenCalled()
model = DBt.persistModel.calls[0].args[0]
expect(model.clientId).toBe @draftClientId
expect(model.serverId).toBe @serverMessageId
expect(model.draft).toBe false
describe "with an existing persisted draft", ->
beforeEach ->
@draftServerId = 'server-123'
@draft = new Message
version: 1
clientId: @draftClientId
serverId: @draftServerId
accountId: @accountId
subject: 'New Draft'
draft: true
body: 'hello world'
to:
name: 'Dummy'
email: 'dummy@nylas.com'
@task = new SendDraftTask(@draftClientId)
@backupDraft = @draft.clone()
@task.backupDraft = @backupDraft # Since performLocal doesn't run
@draftResolver = -> Promise.resolve(@draft)
@calledBody = "ERROR: The body wasn't included!"
spyOn(DatabaseStore, "findBy").andCallFake =>
then: -> throw new Error("You must include the body!")
include: (body) =>
@calledBody = body
return @draftResolver()
it "can complete a full performRemote", -> waitsForPromise =>
@task.performRemote().then (status) ->
expect(status).toBe Task.Status.Success
runFetchLatestDraftTests.call(@)
runMakeSendRequestTests.call(@)
runSaveNewMessageTests.call(@)
it "should make a request to delete a draft", ->
waitsForPromise => @task._fetchLatestDraft().then(@task._deleteRemoteDraft).then =>
expect(@task.draftServerId).toBe @draftServerId
expect(NylasAPI.makeRequest).toHaveBeenCalled()
expect(NylasAPI.makeRequest.callCount).toBe 1
req = NylasAPI.makeRequest.calls[0].args[0]
expect(req.path).toBe "/drafts/#{@draftServerId}"
expect(req.accountId).toBe @accountId
expect(req.method).toBe "DELETE"
expect(req.returnsModel).toBe false
it "should continue if the request failes", ->
jasmine.unspy(NylasAPI, "makeRequest")
spyOn(console, "error")
spyOn(NylasAPI, 'makeRequest').andCallFake (options) =>
err = new APIError(body: "Boo", statusCode: 500)
Promise.reject(err)
waitsForPromise => @task._fetchLatestDraft().then(@task._deleteRemoteDraft).then =>
expect(NylasAPI.makeRequest).toHaveBeenCalled()
expect(NylasAPI.makeRequest.callCount).toBe 1
expect(console.error).toHaveBeenCalled()
.catch =>
throw new Error("Shouldn't fail the promise")
runNotifySuccess.call(@)
runIntegrativeWithErrors.call(@)
it "should locally convert the existing draft to a message on send", ->
expect(@draft.clientId).toBe @draftClientId
expect(@draft.serverId).toBe "server-123"
waitsForPromise => @task.performRemote().then =>
expect(DBt.persistModel).toHaveBeenCalled()
model = DBt.persistModel.calls[0].args[0]
expect(model.clientId).toBe @draftClientId
expect(model.serverId).toBe @serverMessageId
expect(model.draft).toBe false