mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-11-14 05:41:05 +08:00
6695de4187
Summary: 1. **Generic CUD Tasks**: There is now a generic `CreateModelTask`, `UpdateModelTask`, and `DestroyModelTask`. These can either be used as-is or trivially overridden to easily update simple objects. Hopefully all of the boilerplate rollback, error handling, and undo logic won't have to be re-duplicated on every task. There are also tests for these tasks. We use them to perform mutating actions on `Metadata` objects. 1. **Failing on Promise Rejects**: Turns out that if a Promise rejected due to an error or `Promise.reject` we were ignoring it and letting tests pass. Now, tests will Fail if any unhandled promise rejects. This uncovered a variety of errors throughout the test suite that had to be fixed. The most significant one was during the `theme-manager` tests when all packages (and their stores with async DB requests) was loaded. Long after the `theme-manager` specs finished, those DB requests were (somtimes) silently failing. 1. **Globally stub `DatabaseStore._query`**: All tests shouldn't actually make queries on the database. Furthremore, the `inTransaction` block doesn't resolve at all unless `_query` is stubbed. Instead of manually remembering to do this in every test that touches the DB, it's now mocked in `spec_helper`. This broke a handful of tests that needed to be manually fixed. 1. **ESLint Fixes**: Some minor fixes to the linter config to prevent yelling about minor ES6 things and ensuring we have the correct parser. Test Plan: new tests Reviewers: bengotow, juan, drew Differential Revision: https://phab.nylas.com/D2419 Remove cloudState and N1-Send-Later
225 lines
9.1 KiB
CoffeeScript
225 lines
9.1 KiB
CoffeeScript
_ = require 'underscore'
|
|
fs = require 'fs'
|
|
Actions = require '../src/flux/actions'
|
|
NylasAPI = require '../src/flux/nylas-api'
|
|
Thread = require '../src/flux/models/thread'
|
|
DatabaseStore = require '../src/flux/stores/database-store'
|
|
DatabaseTransaction = require '../src/flux/stores/database-transaction'
|
|
|
|
describe "NylasAPI", ->
|
|
describe "handleModel404", ->
|
|
it "should unpersist the model from the cache that was requested", ->
|
|
model = new Thread(id: 'threadidhere')
|
|
spyOn(DatabaseTransaction.prototype, 'unpersistModel')
|
|
spyOn(DatabaseStore, 'find').andCallFake (klass, id) =>
|
|
return Promise.resolve(model)
|
|
NylasAPI._handleModel404("/threads/#{model.id}")
|
|
advanceClock()
|
|
expect(DatabaseStore.find).toHaveBeenCalledWith(Thread, model.id)
|
|
expect(DatabaseTransaction.prototype.unpersistModel).toHaveBeenCalledWith(model)
|
|
|
|
it "should not do anything if the model is not in the cache", ->
|
|
spyOn(DatabaseTransaction.prototype, 'unpersistModel')
|
|
spyOn(DatabaseStore, 'find').andCallFake (klass, id) =>
|
|
return Promise.resolve(null)
|
|
NylasAPI._handleModel404("/threads/1234")
|
|
advanceClock()
|
|
expect(DatabaseStore.find).toHaveBeenCalledWith(Thread, '1234')
|
|
expect(DatabaseTransaction.prototype.unpersistModel).not.toHaveBeenCalledWith()
|
|
|
|
it "should not do anything bad if it doesn't recognize the class", ->
|
|
spyOn(DatabaseStore, 'find')
|
|
spyOn(DatabaseTransaction.prototype, 'unpersistModel')
|
|
waitsForPromise ->
|
|
NylasAPI._handleModel404("/asdasdasd/1234")
|
|
runs ->
|
|
expect(DatabaseStore.find).not.toHaveBeenCalled()
|
|
expect(DatabaseTransaction.prototype.unpersistModel).not.toHaveBeenCalled()
|
|
|
|
it "should not do anything bad if the endpoint only has a single segment", ->
|
|
spyOn(DatabaseStore, 'find')
|
|
spyOn(DatabaseTransaction.prototype, 'unpersistModel')
|
|
waitsForPromise ->
|
|
NylasAPI._handleModel404("/account")
|
|
runs ->
|
|
expect(DatabaseStore.find).not.toHaveBeenCalled()
|
|
expect(DatabaseTransaction.prototype.unpersistModel).not.toHaveBeenCalled()
|
|
|
|
describe "handle401", ->
|
|
it "should post a notification", ->
|
|
spyOn(Actions, 'postNotification')
|
|
NylasAPI._handle401('/threads/1234')
|
|
expect(Actions.postNotification).toHaveBeenCalled()
|
|
expect(Actions.postNotification.mostRecentCall.args[0].message).toEqual("Nylas can no longer authenticate with your mail provider. You will not be able to send or receive mail. Please unlink your account and sign in again.")
|
|
|
|
describe "handleModelResponse", ->
|
|
beforeEach ->
|
|
spyOn(DatabaseTransaction.prototype, "persistModels").andCallFake (models) ->
|
|
Promise.resolve(models)
|
|
|
|
stubDB = ({models, testClass, testMatcher}) ->
|
|
spyOn(DatabaseStore, "findAll").andCallFake (klass) ->
|
|
testClass?(klass)
|
|
where: (matcher) ->
|
|
testMatcher?(matcher)
|
|
Promise.resolve(models)
|
|
|
|
it "should reject if no JSON is provided", ->
|
|
waitsForPromise ->
|
|
NylasAPI._handleModelResponse()
|
|
.then -> throw new Error("Should reject!")
|
|
.catch (err) ->
|
|
expect(err.message).toEqual "handleModelResponse with no JSON provided"
|
|
|
|
it "should resolve if an empty JSON array is provided", ->
|
|
waitsForPromise ->
|
|
NylasAPI._handleModelResponse([])
|
|
.then (resp) ->
|
|
expect(resp).toEqual []
|
|
|
|
describe "if JSON contains objects which are of unknown types", ->
|
|
it "should warn and resolve", ->
|
|
spyOn(console, "warn")
|
|
waitsForPromise ->
|
|
NylasAPI._handleModelResponse([{id: 'a', object: 'unknown'}])
|
|
.then (resp) ->
|
|
expect(resp).toEqual []
|
|
expect(console.warn).toHaveBeenCalled()
|
|
expect(console.warn.calls.length).toBe 1
|
|
|
|
describe "if JSON contains the same object more than once", ->
|
|
beforeEach ->
|
|
stubDB(models: [])
|
|
spyOn(console, "warn")
|
|
@dupes = [
|
|
{id: 'a', object: 'thread'}
|
|
{id: 'a', object: 'thread'}
|
|
{id: 'b', object: 'thread'}
|
|
]
|
|
|
|
it "should warn", ->
|
|
waitsForPromise =>
|
|
NylasAPI._handleModelResponse(@dupes)
|
|
.then ->
|
|
expect(console.warn).toHaveBeenCalled()
|
|
expect(console.warn.calls.length).toBe 1
|
|
|
|
it "should omit duplicates", ->
|
|
waitsForPromise =>
|
|
NylasAPI._handleModelResponse(@dupes)
|
|
.then ->
|
|
models = DatabaseTransaction.prototype.persistModels.calls[0].args[0]
|
|
expect(models.length).toBe 2
|
|
expect(models[0].id).toBe 'a'
|
|
expect(models[1].id).toBe 'b'
|
|
|
|
describe "when locked by the optimistic change tracker", ->
|
|
it "should remove locked models from the set", ->
|
|
json = [
|
|
{id: 'a', object: 'thread'}
|
|
{id: 'b', object: 'thread'}
|
|
]
|
|
spyOn(NylasAPI._optimisticChangeTracker, "acceptRemoteChangesTo").andCallFake (klass, id) ->
|
|
if id is "a" then return false
|
|
|
|
stubDB models: [new Thread(json[1])], testMatcher: (whereMatcher) ->
|
|
expect(whereMatcher.val).toEqual ['b']
|
|
|
|
waitsForPromise =>
|
|
NylasAPI._handleModelResponse(json)
|
|
.then (models) ->
|
|
expect(models.length).toBe 1
|
|
models = DatabaseTransaction.prototype.persistModels.calls[0].args[0]
|
|
expect(models.length).toBe 1
|
|
expect(models[0].id).toBe 'b'
|
|
|
|
describe "when updating models", ->
|
|
Message = require '../src/flux/models/message'
|
|
beforeEach ->
|
|
@json = [
|
|
{id: 'a', object: 'draft', unread: true}
|
|
{id: 'b', object: 'draft', starred: true}
|
|
]
|
|
@existing = new Message(id: 'b', unread: true)
|
|
stubDB models: [@existing]
|
|
|
|
verifyUpdateHappened = (responseModels) ->
|
|
changedModels = DatabaseTransaction.prototype.persistModels.calls[0].args[0]
|
|
expect(changedModels.length).toBe 2
|
|
expect(changedModels[1].id).toBe 'b'
|
|
expect(changedModels[1].starred).toBe true
|
|
# Doesn't override existing values
|
|
expect(changedModels[1].unread).toBe true
|
|
expect(responseModels.length).toBe 2
|
|
expect(responseModels[0].id).toBe 'a'
|
|
expect(responseModels[0].unread).toBe true
|
|
|
|
it "updates found models with new data", ->
|
|
waitsForPromise =>
|
|
NylasAPI._handleModelResponse(@json).then verifyUpdateHappened
|
|
|
|
it "updates if the json version is newer", ->
|
|
@existing.version = 9
|
|
@json[1].version = 10
|
|
waitsForPromise =>
|
|
NylasAPI._handleModelResponse(@json).then verifyUpdateHappened
|
|
|
|
verifyUpdateStopped = (responseModels) ->
|
|
changedModels = DatabaseTransaction.prototype.persistModels.calls[0].args[0]
|
|
expect(changedModels.length).toBe 1
|
|
expect(changedModels[0].id).toBe 'a'
|
|
expect(changedModels[0].unread).toBe true
|
|
expect(responseModels.length).toBe 2
|
|
expect(responseModels[1].id).toBe 'b'
|
|
expect(responseModels[1].starred).toBeUndefined()
|
|
|
|
it "doesn't update if the json version is older", ->
|
|
@existing.version = 10
|
|
@json[1].version = 9
|
|
waitsForPromise =>
|
|
NylasAPI._handleModelResponse(@json).then verifyUpdateStopped
|
|
|
|
it "doesn't update if it's already sent", ->
|
|
@existing.draft = false
|
|
@json[1].draft = true
|
|
waitsForPromise =>
|
|
NylasAPI._handleModelResponse(@json).then verifyUpdateStopped
|
|
|
|
describe "handling all types of objects", ->
|
|
apiObjectToClassMap =
|
|
"file": require('../src/flux/models/file')
|
|
"event": require('../src/flux/models/event')
|
|
"label": require('../src/flux/models/label')
|
|
"folder": require('../src/flux/models/folder')
|
|
"thread": require('../src/flux/models/thread')
|
|
"draft": require('../src/flux/models/message')
|
|
"account": require('../src/flux/models/account')
|
|
"message": require('../src/flux/models/message')
|
|
"contact": require('../src/flux/models/contact')
|
|
"calendar": require('../src/flux/models/calendar')
|
|
"metadata": require('../src/flux/models/metadata')
|
|
|
|
verifyUpdateHappened = (klass, responseModels) ->
|
|
changedModels = DatabaseTransaction.prototype.persistModels.calls[0].args[0]
|
|
expect(changedModels.length).toBe 2
|
|
expect(changedModels[0].id).toBe 'a'
|
|
expect(changedModels[1].id).toBe 'b'
|
|
expect(changedModels[0] instanceof klass).toBe true
|
|
expect(changedModels[1] instanceof klass).toBe true
|
|
expect(responseModels.length).toBe 2
|
|
expect(responseModels[0].id).toBe 'a'
|
|
expect(responseModels[1].id).toBe 'b'
|
|
expect(responseModels[0] instanceof klass).toBe true
|
|
expect(responseModels[1] instanceof klass).toBe true
|
|
|
|
_.forEach apiObjectToClassMap, (klass, type) ->
|
|
it "properly handle the '#{type}' type", ->
|
|
json = [
|
|
{id: 'a', object: type}
|
|
{id: 'b', object: type}
|
|
]
|
|
stubDB models: [new klass(id: 'b')]
|
|
|
|
verifyUpdate = _.partial(verifyUpdateHappened, klass)
|
|
waitsForPromise =>
|
|
NylasAPI._handleModelResponse(json).then verifyUpdate
|