Mailspring/spec/stores/file-upload-store-spec.coffee

205 lines
6.9 KiB
CoffeeScript
Raw Normal View History

fs = require 'fs'
{Message,
Actions,
FileUploadStore,
DraftStore} = require 'nylas-exports'
{Upload} = FileUploadStore
msgId = "local-123"
fpath = "/foo/bar/test123.jpg"
2016-01-28 04:33:09 +08:00
fDir = "/foo/bar"
uploadDir = "/uploads"
filename = "test123.jpg"
fix(spec): add support for async specs and disable misbehaving ones More spec fixes replace process.nextTick with setTimeout(fn, 0) for specs Also added an unspy in the afterEach Temporarily disable specs fix(spec): start fixing specs Summary: This is the WIP fix to our spec runner. Several tests have been completely commented out that will require substantially more work to fix. These have been added to our sprint backlog. Other tests have been fixed to update to new APIs or to deal with genuine bugs that were introduced without our knowing! The most common non-trivial change relates to observing the `NylasAPI` and `NylasAPIRequest`. We used to observe the arguments to `makeRequest`. Unfortunately `NylasAPIRequest.run` is argumentless. Instead you can do: `NylasAPIRequest.prototype.run.mostRecentCall.object.options` to get the `options` passed into the object. the `.object` property grabs the context of the spy when it was last called. Fixing these tests uncovered several concerning issues with our test runner. I spent a while tracking down why our participant-text-field-spec was failling every so often. I chose that spec because it was the first spec to likely fail, thereby requiring looking at the least number of preceding files. I tried binary searching, turning on and off, several files beforehand only to realize that the failure rate was not determined by a particular preceding test, but rather the existing and quantity of preceding tests, AND the number of console.log statements I had. There is some processor-dependent race condition going on that needs further investigation. I also discovered an issue with the file-download-spec. We were getting errors about it accessing a file, which was very suspicious given the code stubs out all fs access. This was caused due to a spec that called an async function outside ot a `waitsForPromise` block or a `waitsFor` block. The test completed, the spies were cleaned up, but the downstream async chain was still running. By the time the async chain finished the runner was already working on the next spec and the spies had been restored (causing the real fs access to run). Juan had an idea to kill the specs once one fails to prevent cascading failures. I'll implement this in the next diff update Test Plan: npm test Reviewers: juan, halla, jackie Differential Revision: https://phab.nylas.com/D3501 Disable other specs Disable more broken specs All specs turned off till passing state Use async-safe versions of spec functions Add async test spec Remove unused package code Remove canary spec
2016-12-13 04:12:20 +08:00
xdescribe 'FileUploadStore', ->
beforeEach ->
@draft = new Message()
@session =
changes:
add: jasmine.createSpy('session.changes.add')
commit: ->
draft: => @draft
spyOn(NylasEnv, "isMainWindow").andReturn(true)
spyOn(DraftStore, "sessionForClientId").andReturn(Promise.resolve(@session))
spyOn(FileUploadStore, "_onAttachFileError").andCallFake (msg) ->
throw new Error(msg)
refactor(env): new NylasEnv global Converted all references of global atom to NylasEnv Temporary rename atom.io find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.io/temporaryAtomIoReplacement/g' atom.config to NylasEnv.config find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.config/NylasEnv.config/g' atom.packages -> NylasEnv.packages atom.commands -> NylasEnv.commands atom.getLoadSettings find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.commands/NylasEnv.commands/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.getLoadSettings/NylasEnv.getLoadSettings/g' More common atom methods find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.styles/NylasEnv.styles/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.emitError/NylasEnv.emitError/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.inSpecMode/NylasEnv.inSpecMode/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.inDevMode/NylasEnv.inDevMode/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.getWindowType/NylasEnv.getWindowType/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.displayWindow/NylasEnv.displayWindow/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.quit/NylasEnv.quit/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.close/NylasEnv.close/g' More atom method changes find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.keymaps/NylasEnv.keymaps/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.hide/NylasEnv.hide/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.getCurrentWindow/NylasEnv.getCurrentWindow/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.menu/NylasEnv.menu/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.getConfigDirPath/NylasEnv.getConfigDirPath/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.isMainWindow/NylasEnv.isMainWindow/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.finishUnload/NylasEnv.finishUnload/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.isWorkWindow/NylasEnv.isWorkWindow/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.showSaveDialog/NylasEnv.showSaveDialog/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.append/NylasEnv.append/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.confirm/NylasEnv.confirm/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.clipboard/NylasEnv.clipboard/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.getVersion/NylasEnv.getVersion/g' More atom renaming Rename atom methods More atom methods Fix grunt config variable Change atom.cmd to N1.cmd Rename atom.coffee and atom.js to nylas-env.coffee nylas-env.js Fix atom global reference in specs manually Fix atom requires Change engine from atom to nylas got rid of global/nylas-env rename to nylas-win-bootup Fix onWindowPropsChanged to onWindowPropsReceived fix nylas-workspace atom-text-editor to nylas-theme-wrap atom-text-editor -> nylas-theme-wrap Replacing atom keyword AtomWindow -> NylasWindow Replace Atom -> N1 Rename atom items nylas.asar -> atom.asar Remove more atom references Remove 6to5 references Remove license exception for atom
2015-11-12 02:25:11 +08:00
spyOn(NylasEnv, "showOpenDialog").andCallFake (props, callback) ->
callback(fpath)
2016-01-28 04:33:09 +08:00
describe 'selectAttachment', ->
it "throws if no messageClientId is provided", ->
2016-01-28 04:33:09 +08:00
expect( -> Actions.selectAttachment()).toThrow()
2016-01-28 04:33:09 +08:00
it "throws if messageClientId is blank", ->
expect( -> Actions.selectAttachment("")).toThrow()
it "dispatches action to attach file", ->
spyOn(Actions, "addAttachment")
2016-01-28 04:33:09 +08:00
Actions.selectAttachment(messageClientId: msgId)
refactor(env): new NylasEnv global Converted all references of global atom to NylasEnv Temporary rename atom.io find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.io/temporaryAtomIoReplacement/g' atom.config to NylasEnv.config find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.config/NylasEnv.config/g' atom.packages -> NylasEnv.packages atom.commands -> NylasEnv.commands atom.getLoadSettings find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.commands/NylasEnv.commands/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.getLoadSettings/NylasEnv.getLoadSettings/g' More common atom methods find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.styles/NylasEnv.styles/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.emitError/NylasEnv.emitError/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.inSpecMode/NylasEnv.inSpecMode/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.inDevMode/NylasEnv.inDevMode/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.getWindowType/NylasEnv.getWindowType/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.displayWindow/NylasEnv.displayWindow/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.quit/NylasEnv.quit/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.close/NylasEnv.close/g' More atom method changes find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.keymaps/NylasEnv.keymaps/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.hide/NylasEnv.hide/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.getCurrentWindow/NylasEnv.getCurrentWindow/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.menu/NylasEnv.menu/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.getConfigDirPath/NylasEnv.getConfigDirPath/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.isMainWindow/NylasEnv.isMainWindow/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.finishUnload/NylasEnv.finishUnload/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.isWorkWindow/NylasEnv.isWorkWindow/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.showSaveDialog/NylasEnv.showSaveDialog/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.append/NylasEnv.append/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.confirm/NylasEnv.confirm/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.clipboard/NylasEnv.clipboard/g' find -E . -regex ".*\.(coffee|cjsx|js|md|cmd|es6)" -print0 | xargs -0 sed -i "" 's/atom.getVersion/NylasEnv.getVersion/g' More atom renaming Rename atom methods More atom methods Fix grunt config variable Change atom.cmd to N1.cmd Rename atom.coffee and atom.js to nylas-env.coffee nylas-env.js Fix atom global reference in specs manually Fix atom requires Change engine from atom to nylas got rid of global/nylas-env rename to nylas-win-bootup Fix onWindowPropsChanged to onWindowPropsReceived fix nylas-workspace atom-text-editor to nylas-theme-wrap atom-text-editor -> nylas-theme-wrap Replacing atom keyword AtomWindow -> NylasWindow Replace Atom -> N1 Rename atom items nylas.asar -> atom.asar Remove more atom references Remove 6to5 references Remove license exception for atom
2015-11-12 02:25:11 +08:00
expect(NylasEnv.showOpenDialog).toHaveBeenCalled()
2016-01-28 04:33:09 +08:00
expect(Actions.addAttachment).toHaveBeenCalled()
args = Actions.addAttachment.calls[0].args[0]
expect(args.messageClientId).toBe(msgId)
expect(args.filePath).toBe(fpath)
2016-01-28 04:33:09 +08:00
describe 'addAttachment', ->
beforeEach ->
@stats = {
size: 1234,
isDirectory: -> false,
}
@upload = new Upload({
messageClientId: msgId,
filePath: fpath,
stats: @stats,
id: 'u1',
uploadDir: uploadDir
})
spyOn(FileUploadStore, '_getFileStats').andCallFake => Promise.resolve(@stats)
spyOn(FileUploadStore, '_prepareTargetDir').andCallFake => Promise.resolve()
2016-01-28 04:33:09 +08:00
spyOn(FileUploadStore, '_copyUpload').andCallFake => Promise.resolve(@upload)
spyOn(FileUploadStore, '_applySessionChanges').andCallThrough()
it "throws if no messageClientId or path is provided", ->
expect(-> Actions.addAttachment()).toThrow()
2016-01-28 04:33:09 +08:00
it 'throws if upload is a directory', ->
@stats = {
isDirectory: -> true
}
waitsForPromise ->
FileUploadStore._onAddAttachment({messageClientId: msgId, filePath: fpath})
.then ->
throw new Error('Expected test to land in catch.')
.catch (error) ->
expect(error.message.indexOf(filename + ' is a directory')).not.toBe(-1)
it 'throws if the file is more than 25MB', ->
@stats = {
size: 25*1000000+1,
isDirectory: -> false,
}
waitsForPromise ->
FileUploadStore._onAddAttachment({messageClientId: msgId, filePath: fpath})
.then ->
throw new Error('Expected test to land in catch.')
.catch (error) ->
expect(error.message.indexOf(filename + ' cannot')).not.toBe(-1)
2016-01-28 04:33:09 +08:00
it "executes the required steps and triggers", ->
waitsForPromise ->
FileUploadStore._onAddAttachment({messageClientId: msgId, filePath: fpath})
2016-01-28 04:33:09 +08:00
runs =>
expect(FileUploadStore._getFileStats).toHaveBeenCalled()
expect(FileUploadStore._prepareTargetDir).toHaveBeenCalled()
expect(FileUploadStore._copyUpload).toHaveBeenCalled()
expect(FileUploadStore._applySessionChanges).toHaveBeenCalled()
expect(@session.changes.add).toHaveBeenCalledWith({uploads: [@upload]})
2016-01-28 04:33:09 +08:00
describe 'removeAttachment', ->
beforeEach ->
@upload = new Upload({
messageClientId: msgId,
filePath: fpath,
stats: {
size: 1234,
isDirectory: -> false
},
id: 'u1',
uploadDir: uploadDir
})
2016-01-28 04:33:09 +08:00
spyOn(FileUploadStore, '_deleteUpload').andCallFake => Promise.resolve(@upload)
spyOn(fs, 'rmdir')
it 'removes the upload from the draft', ->
@draft.uploads = [{id: 'u2'}, @upload]
2016-01-28 04:33:09 +08:00
waitsForPromise =>
FileUploadStore._onRemoveAttachment(@upload)
.then =>
expect(@session.changes.add).toHaveBeenCalledWith uploads: [{id: 'u2'}]
2016-01-28 04:33:09 +08:00
expect(fs.rmdir).not.toHaveBeenCalled()
it 'calls deleteUpload to clean up the filesystem', ->
@draft.uploads = [@upload]
2016-01-28 04:33:09 +08:00
waitsForPromise =>
FileUploadStore._onRemoveAttachment(@upload)
.then =>
expect(FileUploadStore._deleteUpload).toHaveBeenCalled()
2016-01-28 04:33:09 +08:00
describe "when a draft is sent", ->
it "should delete its uploads directory", ->
spyOn(FileUploadStore, '_deleteUploadsForClientId')
Actions.ensureMessageInSentSuccess({messageClientId: '123'})
expect(FileUploadStore._deleteUploadsForClientId).toHaveBeenCalledWith('123')
2016-01-28 04:33:09 +08:00
describe '_getFileStats', ->
it 'returns the correct stats', ->
spyOn(fs, 'stat').andCallFake (path, callback) ->
callback(null, {size: 1234, isDirectory: -> false})
2016-01-28 04:33:09 +08:00
waitsForPromise ->
FileUploadStore._getFileStats(fpath)
.then (stats) ->
2016-01-28 04:33:09 +08:00
expect(stats.size).toEqual 1234
expect(stats.isDirectory()).toBe false
it 'throws when there is an error reading the file', ->
spyOn(fs, 'stat').andCallFake (path, callback) ->
2016-01-28 04:33:09 +08:00
callback("Error!", null)
waitsForPromise ->
FileUploadStore._getFileStats(fpath)
2016-01-28 04:33:09 +08:00
.then -> throw new Error('It should fail.')
.catch (error) ->
expect(error.message.indexOf(fpath)).toBe 0
2016-01-28 04:33:09 +08:00
describe '_copyUpload', ->
beforeEach ->
stream = require 'stream'
@upload = new Upload({
messageClientId: msgId,
filePath: fpath,
stats: {
size: 1234,
isDirectory: -> false
},
id: null,
uploadDir: uploadDir
})
2016-01-28 04:33:09 +08:00
@readStream = stream.Readable()
@writeStream = stream.Writable()
spyOn(@readStream, 'pipe')
spyOn(fs, 'createReadStream').andReturn @readStream
spyOn(fs, 'createWriteStream').andReturn @writeStream
it 'copies the file correctly', ->
waitsForPromise =>
promise = FileUploadStore._copyUpload(@upload)
@readStream.emit 'end'
promise.then (up) =>
expect(fs.createReadStream).toHaveBeenCalledWith(fpath)
expect(fs.createWriteStream).toHaveBeenCalledWith(@upload.targetPath)
expect(@readStream.pipe).toHaveBeenCalledWith(@writeStream)
expect(up.id).toEqual @upload.id
it 'throws when there is an error on the read stream', ->
waitsForPromise =>
promise = FileUploadStore._copyUpload(@upload)
@readStream.emit 'error'
promise
.then => throw new Error('It should fail.')
.catch (msg) =>
expect(msg).not.toBeUndefined()
it 'throws when there is an error on the write stream', ->
waitsForPromise =>
promise = FileUploadStore._copyUpload(@upload)
@writeStream.emit 'error'
promise
.then => throw new Error('It should fail.')
.catch (msg) =>
expect(msg).not.toBeUndefined()