Mailspring/spec/tasks/task-spec.coffee
Ben Gotow a3ede94423 feat(offline-status): Show a bar when not connected to the API
Summary:
The TaskQueue does it's own throttling and has it's own processQueue retry timeout, no need for longPollConnected

Remove dead code (OfflineError)

Rename long connection state to status so we don't ask for `state.state`

Remove long poll actions related to online/offline in favor of exposing connection state through NylasSyncStatusStore

Consoliate notifications and account-error-heaer into a single package and organize files into sidebar vs. header.

Update the DeveloperBarStore to query the sync status store for long poll statuses

Test Plan: All existing tests pass

Reviewers: juan, evan

Reviewed By: evan

Differential Revision: https://phab.nylas.com/D2835
2016-04-04 17:11:09 -07:00

221 lines
8.7 KiB
CoffeeScript

Actions = require '../../src/flux/actions'
TaskQueue = require '../../src/flux/stores/task-queue'
Task = require '../../src/flux/tasks/task'
{APIError,
TimeoutError} = require '../../src/flux/errors'
noop = ->
describe "Task", ->
describe "initial state", ->
it "should set up queue state with additional information about local/remote", ->
task = new Task()
expect(task.queueState).toEqual({ isProcessing : false, localError : null, localComplete : false, remoteError : null, remoteAttempts : 0, remoteComplete : false, status: null, debugStatus: Task.DebugStatus.JustConstructed})
describe "runLocal", ->
beforeEach ->
class APITestTask extends Task
performLocal: -> Promise.resolve()
performRemote: -> Promise.resolve(Task.Status.Success)
@task = new APITestTask()
describe "when performLocal is not complete", ->
it "should run performLocal", ->
spyOn(@task, 'performLocal').andCallThrough()
@task.runLocal()
expect(@task.performLocal).toHaveBeenCalled()
describe "when performLocal rejects", ->
beforeEach ->
spyOn(NylasEnv, "reportError")
spyOn(@task, 'performLocal').andCallFake =>
Promise.reject(new Error("Oh no!"))
it "should save the error to the queueState", ->
@task.runLocal().catch(noop)
advanceClock()
expect(@task.performLocal).toHaveBeenCalled()
expect(@task.queueState.localComplete).toBe(false)
expect(@task.queueState.localError.message).toBe("Oh no!")
it "should reject with the error", ->
rejection = null
runs ->
@task.runLocal().catch (err) ->
rejection = err
waitsFor ->
rejection
runs ->
expect(rejection.message).toBe("Oh no!")
describe "when performLocal resolves", ->
beforeEach ->
spyOn(@task, 'performLocal').andCallFake -> Promise.resolve('Hooray')
it "should save that performLocal is complete", ->
@task.runLocal()
advanceClock()
expect(@task.queueState.localComplete).toBe(true)
it "should save that there was no performLocal error", ->
@task.runLocal()
advanceClock()
expect(@task.queueState.localError).toBe(null)
describe "runRemote", ->
beforeEach ->
@task.queueState.localComplete = true
it "should run performRemote", ->
spyOn(@task, 'performRemote').andCallThrough()
@task.runRemote()
advanceClock()
expect(@task.performRemote).toHaveBeenCalled()
it "it should resolve Continue if it already ran", ->
@task.queueState.remoteComplete = true
waitsForPromise =>
@task.runRemote().then (status) =>
expect(@task.queueState.status).toBe Task.Status.Continue
expect(status).toBe Task.Status.Continue
it "marks as complete if the task 'continue's", ->
spyOn(@task, 'performRemote').andCallFake ->
Promise.resolve(Task.Status.Continue)
@task.runRemote()
advanceClock()
expect(@task.performRemote).toHaveBeenCalled()
expect(@task.queueState.remoteError).toBe(null)
expect(@task.queueState.remoteComplete).toBe(true)
expect(@task.queueState.status).toBe(Task.Status.Continue)
it "marks as failed if the task reverts", ->
spyOn(@task, 'performRemote').andCallFake ->
Promise.resolve(Task.Status.Failed)
@task.runRemote()
advanceClock()
expect(@task.performRemote).toHaveBeenCalled()
expect(@task.queueState.remoteError).toBe(null)
expect(@task.queueState.remoteComplete).toBe(true)
expect(@task.queueState.status).toBe(Task.Status.Failed)
describe "when performRemote resolves", ->
beforeEach ->
spyOn(@task, 'performRemote').andCallFake ->
Promise.resolve(Task.Status.Success)
it "should save that performRemote is complete with no errors", ->
@task.runRemote()
advanceClock()
expect(@task.performRemote).toHaveBeenCalled()
expect(@task.queueState.remoteError).toBe(null)
expect(@task.queueState.remoteComplete).toBe(true)
expect(@task.queueState.status).toBe(Task.Status.Success)
it "should only allow the performRemote method to return a Task.Status", ->
result = null
err = null
class OKTask extends Task
performRemote: -> Promise.resolve(Task.Status.Retry)
@ok = new OKTask()
@ok.queueState.localComplete = true
@ok.runRemote().then (r) -> result = r
advanceClock()
expect(@ok.queueState.status).toBe(Task.Status.Retry)
expect(result).toBe(Task.Status.Retry)
class BadTask extends Task
performRemote: -> Promise.resolve('lalal')
@bad = new BadTask()
@bad.queueState.localComplete = true
@bad.runRemote().catch (e) -> err = e
advanceClock()
expect(err.message).toBe('performRemote returned lalal, which is not a Task.Status')
describe "when performRemote rejects multiple times", ->
beforeEach ->
spyOn(@task, 'performRemote').andCallFake =>
Promise.resolve(Task.Status.Failed)
it "should increment the number of attempts", ->
runs ->
@task.runRemote().catch(noop)
waitsFor ->
@task.queueState.remoteAttempts == 1
runs ->
@task.runRemote().catch(noop)
waitsFor ->
@task.queueState.remoteAttempts == 2
describe "when performRemote resolves with Task.Status.Failed", ->
beforeEach ->
spyOn(NylasEnv, "reportError")
@error = new APIError("Oh no!")
spyOn(@task, 'performRemote').andCallFake =>
Promise.resolve(Task.Status.Failed)
it "Should handle the error as a caught Failure", ->
waitsForPromise =>
@task.runRemote().then ->
throw new Error("Should not resolve")
.catch (err) =>
expect(@task.queueState.remoteError instanceof Error).toBe true
expect(@task.queueState.remoteAttempts).toBe(1)
expect(@task.queueState.status).toBe(Task.Status.Failed)
expect(NylasEnv.reportError).not.toHaveBeenCalled()
describe "when performRemote resolves with Task.Status.Failed and an error", ->
beforeEach ->
spyOn(NylasEnv, "reportError")
@error = new APIError("Oh no!")
spyOn(@task, 'performRemote').andCallFake =>
Promise.resolve([Task.Status.Failed, @error])
it "Should handle the error as a caught Failure", ->
waitsForPromise =>
@task.runRemote().then ->
throw new Error("Should not resolve")
.catch (err) =>
expect(@task.queueState.remoteError).toBe(@error)
expect(@task.queueState.remoteAttempts).toBe(1)
expect(@task.queueState.status).toBe(Task.Status.Failed)
expect(NylasEnv.reportError).not.toHaveBeenCalled()
describe "when performRemote rejects with Task.Status.Failed", ->
beforeEach ->
spyOn(NylasEnv, "reportError")
@error = new APIError("Oh no!")
spyOn(@task, 'performRemote').andCallFake =>
Promise.reject([Task.Status.Failed, @error])
it "Should handle the rejection as normal", ->
waitsForPromise =>
@task.runRemote().then ->
throw new Error("Should not resolve")
.catch (err) =>
expect(@task.queueState.remoteError).toBe(@error)
expect(@task.queueState.remoteAttempts).toBe(1)
expect(@task.queueState.status).toBe(Task.Status.Failed)
expect(NylasEnv.reportError).not.toHaveBeenCalled()
describe "when performRemote throws an unknown error", ->
beforeEach ->
spyOn(NylasEnv, "reportError")
@error = new Error("Oh no!")
spyOn(@task, 'performRemote').andCallFake =>
throw @error
it "Should handle the error as an uncaught error", ->
waitsForPromise =>
@task.runRemote().then ->
throw new Error("Should not resolve")
.catch (err) =>
expect(@task.queueState.remoteError).toBe(@error)
expect(@task.queueState.remoteAttempts).toBe(1)
expect(@task.queueState.status).toBe(Task.Status.Failed)
expect(@task.queueState.debugStatus).toBe(Task.DebugStatus.UncaughtError)
expect(NylasEnv.reportError).toHaveBeenCalledWith(@error)