describe "Mercury.uploader", ->
template 'mercury/uploader.html'
beforeEach ->
Mercury.config.uploading.enabled = true
@fileReaderSupport = spyOn(Mercury.uploader, 'fileReaderSupported')
@fileReaderSupport.andCallFake(=> true)
$.fx.off = true
@mockFile = {
size: 1024
fileName: 'image.png'
type: 'image/png'
}
afterEach ->
Mercury.uploader.initialized = false
describe "singleton method", ->
beforeEach ->
@showSpy = spyOn(Mercury.uploader, 'show').andCallFake(=>)
it "calls show", ->
Mercury.uploader(@mockFile)
expect(@showSpy.callCount).toEqual(1)
it "returns the function object", ->
ret = Mercury.uploader(@mockFile)
expect(ret).toEqual(Mercury.uploader)
it "doesn't call show if disabled in configuration", ->
Mercury.config.uploading.enabled = false
Mercury.uploader(@mockFile)
expect(@showSpy.callCount).toEqual(0)
describe "#show", ->
beforeEach ->
@initializeSpy = spyOn(Mercury.uploader, 'initialize').andCallFake(=>)
@appearSpy = spyOn(Mercury.uploader, 'appear').andCallFake(=>)
it "expects a file object", ->
Mercury.uploader.show(@mockFile)
expect(Mercury.uploader.file.name).toEqual(@mockFile.fileName)
it "accepts options", ->
Mercury.uploader.show(@mockFile, {foo: 'bar'})
expect(Mercury.uploader.options).toEqual({foo: 'bar'})
it "creates a file instance from the file", ->
Mercury.uploader.show(@mockFile)
expect(Mercury.uploader.file.name).toEqual(@mockFile.fileName)
expect(Mercury.uploader.file.fullSize).toEqual(1024)
it "alerts and stops if the file has errors (too large or unsupported mimetype)", ->
@mockFile.size = 123123123123
spy = spyOn(window, 'alert').andCallFake(=>)
Mercury.uploader.show(@mockFile)
expect(spy.callCount).toEqual(1)
expect(spy.argsForCall[0]).toEqual(['Error: Too large'])
it "doesn't do anything unless xhr uploading is supported in the browser", ->
spyOn(Mercury.uploader, 'supported').andCallFake(=> false)
Mercury.uploader.show(@mockFile)
expect(@initializeSpy.callCount).toEqual(0)
it "triggers an event to focus the window", ->
spy = spyOn(Mercury, 'trigger').andCallFake(=>)
Mercury.uploader.show(@mockFile)
expect(spy.callCount).toEqual(1)
expect(spy.argsForCall[0]).toEqual(['focus:window'])
it "calls initialize", ->
Mercury.uploader.show(@mockFile)
expect(@initializeSpy.callCount).toEqual(1)
it "calls appear", ->
Mercury.uploader.show(@mockFile)
expect(@appearSpy.callCount).toEqual(1)
describe "#initialize", ->
beforeEach ->
@buildSpy = spyOn(Mercury.uploader, 'build').andCallFake(=>)
@bindEventsSpy = spyOn(Mercury.uploader, 'bindEvents').andCallFake(=>)
it "calls build", ->
Mercury.uploader.initialize()
expect(@buildSpy.callCount).toEqual(1)
it "calls bindEvents", ->
Mercury.uploader.initialize()
expect(@bindEventsSpy.callCount).toEqual(1)
it "only initializes once", ->
Mercury.uploader.initialize()
expect(@buildSpy.callCount).toEqual(1)
Mercury.uploader.initialize()
expect(@buildSpy.callCount).toEqual(1)
describe "#supported", ->
it "prototypes sendAsBinary onto XMLHttpRequest if it's not already there", ->
XMLHttpRequest.prototype.sendAsBinary = null
Mercury.uploader.supported()
expect(XMLHttpRequest.prototype.sendAsBinary).toBeDefined()
it "returns true if everything needed is supported", ->
ret = Mercury.uploader.supported()
expect(ret).toEqual(true)
it "returns false if everything isn't supported", ->
window.Uint8Array = null
ret = Mercury.uploader.supported()
expect(ret).toEqual(true)
describe "#build", ->
beforeEach ->
Mercury.uploader.options = {appendTo: '#test'}
it "builds an element structure", ->
Mercury.uploader.build()
html = $('
').html(Mercury.uploader.element).html()
expect(html).toContain('class="mercury-uploader"')
expect(html).toContain('class="mercury-uploader-preview"')
expect(html).toContain('
')
expect(html).toContain('class="mercury-uploader-details"')
expect(html).toContain('
Processing...')
expect(html).toContain('class="mercury-uploader-indicator"')
expect(html).toContain('
0%
')
it "builds an overlay", ->
Mercury.uploader.build()
html = $('
').html(Mercury.uploader.overlay).html()
expect(html).toContain('class="mercury-uploader-overlay"')
it "appends to any element", ->
Mercury.uploader.options = {appendTo: '#uploader_container'}
Mercury.uploader.build()
expect($('#uploader_container .mercury-uploader').length).toEqual(1)
describe "observed events", ->
describe "custom event: resize", ->
it "calls position", ->
spy = spyOn(Mercury.uploader, 'position').andCallFake(=>)
Mercury.uploader.bindEvents()
Mercury.trigger('resize')
expect(spy.callCount).toEqual(1)
describe "#appear", ->
beforeEach ->
Mercury.uploader.options = {appendTo: '#test'}
Mercury.uploader.build()
@fillDisplaySpy = spyOn(Mercury.uploader, 'fillDisplay').andCallFake(=>)
@positionSpy = spyOn(Mercury.uploader, 'position').andCallFake(=>)
@loadImageSpy = spyOn(Mercury.uploader, 'loadImage').andCallFake(=>)
it "calls fillDisplay", ->
Mercury.uploader.appear()
expect(@fillDisplaySpy.callCount).toEqual(1)
it "calls position", ->
Mercury.uploader.appear()
expect(@positionSpy.callCount).toEqual(1)
it "displays the overlay, and the element", ->
Mercury.uploader.appear()
expect($('#test .mercury-uploader').css('display')).toEqual('block')
expect($('#test .mercury-uploader-overlay').css('display')).toEqual('block')
it "sets visible to true", ->
Mercury.uploader.appear()
expect(Mercury.uploader.visible).toEqual(true)
it "calls loadImage", ->
Mercury.uploader.appear()
expect(@loadImageSpy.callCount).toEqual(1)
describe "#position", ->
beforeEach ->
Mercury.uploader.options = {appendTo: '#test'}
Mercury.uploader.build()
@fillDisplaySpy = spyOn(Mercury.uploader, 'fillDisplay').andCallFake(=>)
@positionSpy = spyOn(Mercury.uploader, 'position').andCallFake(=>)
it "centers the element in the viewport", ->
# todo: this isn't really being tested
Mercury.uploader.element.css({display: 'block'})
Mercury.uploader.position()
@expect($('#test .mercury-uploader').offset()).toEqual({top: 0, left: 0})
describe "#fillDisplay", ->
beforeEach ->
Mercury.uploader.options = {appendTo: '#test'}
Mercury.uploader.file = {name: 'image.png', size: 1024, type: 'image/png'}
Mercury.uploader.build()
it "puts the file details into the element", ->
Mercury.uploader.fillDisplay()
expect($('#test .mercury-uploader-details').html()).toEqual('Name: image.png
Size: undefined
Type: image/png')
describe "#loadImage", ->
beforeEach ->
Mercury.uploader.options = {appendTo: '#test'}
Mercury.uploader.file = new Mercury.uploader.File(@mockFile)
Mercury.uploader.build()
spyOn(FileReader.prototype, 'readAsBinaryString').andCallFake(=>)
@fileReaderSupport.andCallFake(=> true)
@readAsDataURLSpy = spyOn(Mercury.uploader.File.prototype, 'readAsDataURL').andCallFake((callback) => callback('data-url'))
it "calls file.readAsDataURL", ->
Mercury.uploader.loadImage()
expect(@readAsDataURLSpy.callCount).toEqual(1)
it "sets the preview image src to the file contents", ->
Mercury.uploader.loadImage()
expect($('#test .mercury-uploader-preview img').attr('src')).toEqual('data-url')
it "calls upload", ->
spy = spyOn(Mercury.uploader, 'upload').andCallFake(=>)
Mercury.uploader.loadImage()
expect(spy.callCount).toEqual(1)
describe "#loadImage without FileReader", ->
beforeEach ->
Mercury.uploader.options = {appendTo: '#test'}
Mercury.uploader.file = new Mercury.uploader.File(@mockFile)
Mercury.uploader.build()
@fileReaderSupport.andCallFake(=> false)
it "calls upload", ->
spy = spyOn(Mercury.uploader, 'upload').andCallFake(=>)
Mercury.uploader.loadImage()
expect(spy.callCount).toEqual(1)
describe "#upload", ->
# todo: test this
it "should build a multipart form and submit it", ->
describe "#updateStatus", ->
beforeEach ->
Mercury.uploader.options = {appendTo: '#test'}
Mercury.uploader.build()
it "updated the message in the progress display", ->
Mercury.uploader.updateStatus('status message')
expect($('#test .mercury-uploader-progress span').html()).toEqual('status message')
it "updates the progress indicator width", ->
Mercury.uploader.updateStatus('message', 512)
expect($('#test .mercury-uploader-indicator div').css('width')).toEqual('50px')
it "updates the progress indicator value", ->
Mercury.uploader.updateStatus('message', 512)
expect($('#test .mercury-uploader-indicator b').html()).toEqual('50%')
describe "#hide", ->
beforeEach ->
@setTimeoutSpy = spyOn(window, 'setTimeout')
Mercury.uploader.options = {appendTo: '#test'}
Mercury.uploader.build()
it "accepts a delay", ->
@setTimeoutSpy.andCallFake(=>)
Mercury.uploader.hide(1)
expect(@setTimeoutSpy.callCount).toEqual(1)
expect(@setTimeoutSpy.argsForCall[0][0]).toEqual(1000)
it "hides the overlay and element", ->
@setTimeoutSpy.andCallFake((timeout, callback) => callback())
Mercury.uploader.hide()
expect($('#test .mercury-uploader').css('opacity')).toEqual('0')
expect($('#test .mercury-uploader-overlay').css('opacity')).toEqual('0')
it "calls reset", ->
@setTimeoutSpy.andCallFake((timeout, callback) => callback())
spy = spyOn(Mercury.uploader, 'reset').andCallFake(=>)
Mercury.uploader.hide()
expect(spy.callCount).toEqual(1)
it "sets visible to false", ->
@setTimeoutSpy.andCallFake((timeout, callback) => callback())
Mercury.uploader.hide()
expect(Mercury.uploader.visible).toEqual(false)
it "focuses the frame", ->
@setTimeoutSpy.andCallFake((timeout, callback) => callback())
spy = spyOn(Mercury, 'trigger').andCallFake(=>)
Mercury.uploader.hide()
expect(spy.callCount).toEqual(1)
expect(spy.argsForCall[0]).toEqual(['focus:frame'])
describe "#reset", ->
beforeEach ->
Mercury.uploader.options = {appendTo: '#test'}
Mercury.uploader.build()
it "removes the preview image", ->
$('#test .mercury-uploader-indicator div').html('foo')
Mercury.uploader.reset()
expect($('#test .mercury-uploader-preview b').html()).toEqual('')
it "resets the progress back to 0", ->
$('#test .mercury-uploader-indicator div').css({width: '50%'})
$('#test .mercury-uploader-indicator b').html('50%')
Mercury.uploader.reset()
expect($('#test .mercury-uploader-indicator div').css('width')).toEqual('0px')
expect($('#test .mercury-uploader-indicator b').html()).toEqual('0%')
it "sets the status back to 'Processing...' for next time", ->
spy = spyOn(Mercury.uploader, 'updateStatus').andCallFake(=>)
Mercury.uploader.reset()
expect(spy.callCount).toEqual(1)
describe "uploaderEvents", ->
beforeEach ->
Mercury.uploader.file = @mockFile
@updateStatusSpy = spyOn(Mercury.uploader, 'updateStatus').andCallFake(=>)
@hideSpy = spyOn(Mercury.uploader, 'hide').andCallFake(=>)
@events = Mercury.uploader.uploaderEvents
describe ".onloadstart", ->
it "updates the status to 'Uploading...'", ->
@events.onloadstart.call(Mercury.uploader)
expect(@updateStatusSpy.callCount).toEqual(1)
expect(@updateStatusSpy.argsForCall[0]).toEqual(['Uploading...'])
describe ".onprogress", ->
it "updates the status to 'Uploading...' and passes the amount sent so far", ->
@events.onprogress.call(Mercury.uploader, {loaded: 512})
expect(@updateStatusSpy.callCount).toEqual(1)
expect(@updateStatusSpy.argsForCall[0]).toEqual(['Uploading...', 512])
describe ".onabort", ->
it "updates the status to 'Aborted'", ->
@events.onabort.call(Mercury.uploader)
expect(@updateStatusSpy.callCount).toEqual(1)
expect(@updateStatusSpy.argsForCall[0]).toEqual(['Aborted'])
it "calls hide", ->
@events.onabort.call(Mercury.uploader)
expect(@hideSpy.callCount).toEqual(1)
describe ".onload", ->
it "updates the status to 'Successfully uploaded' and passes the total file size", ->
@events.onload.call(Mercury.uploader)
expect(@updateStatusSpy.callCount).toEqual(1)
expect(@updateStatusSpy.argsForCall[0]).toEqual(['Successfully uploaded...', 1024])
describe ".onerror", ->
it "updates the status to 'Error: Unable to upload the file'", ->
@events.onerror.call(Mercury.uploader)
expect(@updateStatusSpy.callCount).toEqual(1)
expect(@updateStatusSpy.argsForCall[0]).toEqual(['Error: Unable to upload the file'])
it "calls hide", ->
@events.onerror.call(Mercury.uploader)
expect(@hideSpy.callCount).toEqual(1)
expect(@hideSpy.argsForCall[0]).toEqual([3])
describe "Mercury.uploader.File", ->
beforeEach ->
@mockFile = {
size: 1024
fileName: 'image.png'
type: 'image/png'
}
afterEach ->
@file = null
delete(@file)
describe "constructor", ->
it "expects a file", ->
@file = new Mercury.uploader.File(@mockFile)
expect(@file.file).toEqual(@mockFile)
it "reads attributes of the file and sets variables", ->
@file = new Mercury.uploader.File(@mockFile)
expect(@file.size).toEqual(1024)
expect(@file.fullSize).toEqual(1024)
expect(@file.readableSize).toEqual('1.00 kb')
expect(@file.name).toEqual('image.png')
expect(@file.type).toEqual('image/png')
it "adds errors if there's any", ->
Mercury.config.uploading.maxFileSize = 100
Mercury.config.uploading.allowedMimeTypes = ['image/foo']
@file = new Mercury.uploader.File(@mockFile)
expect(@file.errors).toEqual('Too large / Unsupported format')
describe "#readAsDataURL", ->
it "it calls readAsDataURL on a FileReader object", ->
spy = spyOn(window.FileReader.prototype, 'readAsDataURL').andCallFake(=>)
@file = new Mercury.uploader.File(@mockFile)
@file.readAsDataURL()
expect(spy.callCount).toEqual(1)
it "calls a callback if one was provided", ->
spyOn(FileReader.prototype, 'readAsDataURL').andCallFake(=>)
FileReader.prototype.result = 'result'
callCount = 0
callback = (r) => callCount += 1
@file = new Mercury.uploader.File(@mockFile)
onload = @file.readAsDataURL(callback)
onload()
expect(callCount).toEqual(1)
describe "#readAsBinaryString", ->
it "it calls readAsBinaryString on a FileReader object", ->
spy = spyOn(window.FileReader.prototype, 'readAsBinaryString').andCallFake(=>)
@file = new Mercury.uploader.File(@mockFile)
@file.readAsBinaryString()
expect(spy.callCount).toEqual(1)
it "calls a callback if one was provided", ->
spyOn(FileReader.prototype, 'readAsBinaryString').andCallFake(=>)
FileReader.prototype.result = 'result'
callCount = 0
callback = (r) => callCount += 1
@file = new Mercury.uploader.File(@mockFile)
onload = @file.readAsBinaryString(callback)
onload()
expect(callCount).toEqual(1)
describe "#updateSize", ->
it "updates the size based on a delta", ->
@file = new Mercury.uploader.File(@mockFile)
@file.updateSize(20)
expect(@file.fullSize).toEqual(1044)
describe "Mercury.uploader.MultiPartPost", ->
beforeEach ->
@mockFile = {
size: 1024
name: 'image.png'
type: 'image/png'
}
afterEach ->
@multiPartPost = null
delete(@multiPartPost)
describe "constructor", ->
it "expects an inputName, file, and file contents", ->
@multiPartPost = new Mercury.uploader.MultiPartPost('foo[bar]', @mockFile, 'file contents')
expect(@multiPartPost.inputName).toEqual('foo[bar]')
expect(@multiPartPost.file).toEqual(@mockFile)
expect(@multiPartPost.contents).toEqual('file contents')
it "accepts a formInputs object", ->
@multiPartPost = new Mercury.uploader.MultiPartPost('foo[bar]', @mockFile, 'file contents', {foo: 'bar'})
expect(@multiPartPost.formInputs).toEqual({foo: 'bar'})
it "defines a boundary string", ->
@multiPartPost = new Mercury.uploader.MultiPartPost('foo[bar]', @mockFile, 'file contents')
expect(@multiPartPost.boundary).toEqual('Boundaryx20072377098235644401115438165x')
it "calls buildBody", ->
spy = spyOn(Mercury.uploader.MultiPartPost.prototype, 'buildBody').andCallFake(=>)
@multiPartPost = new Mercury.uploader.MultiPartPost('foo[bar]', @mockFile, 'file contents')
expect(spy.callCount).toEqual(1)
it "sets a delta based on the body size and file size", ->
@multiPartPost = new Mercury.uploader.MultiPartPost('foo[bar]', @mockFile, 'file contents')
expect(@multiPartPost.delta).toEqual(-790)
describe "#buildBody", ->
it "creates a multipart post body with the file information", ->
@multiPartPost = new Mercury.uploader.MultiPartPost('foo[bar]', @mockFile, 'file contents')
expect(@multiPartPost.body).toContain('--Boundaryx20072377098235644401115438165x')
expect(@multiPartPost.body).toContain('Content-Disposition: form-data; name="foo[bar]"; filename="image.png"')
expect(@multiPartPost.body).toContain('Content-Type: image/png')
expect(@multiPartPost.body).toContain('Content-Transfer-Encoding: binary')
expect(@multiPartPost.body).toContain('file contents')
it "includes form inputs if passed in", ->
@multiPartPost = new Mercury.uploader.MultiPartPost('foo[bar]', @mockFile, 'file contents', {foo: 'bar'})
expect(@multiPartPost.body).toContain('Content-Disposition: form-data; name="foo"\r\n\r\nbar')