Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # The second way is recommended, which is flexible, and also work for non jQuery ajax library.
- # Setup sinon sandbox
- beforeEach ->
- @sinon = sinon.sandbox.create()
- afterEach ->
- @sinon.restore()
- jasmine.Spec::spy = (args...) ->
- @sinon.spy args...
- jasmine.Spec::stub = (args...) ->
- @sinon.stub args...
- # no mock, tests the real code
- # ==================================================
- # 1. Stub out ajax using Deferred #
- #
- # Use deferred object to trigger callback, so testing is synchronous. The
- # resolve or reject are invoked, all requests are executed and returned.
- #
- # It also can be applied to any other library based on $.Deferred
- jasmine.Spec::stubAjax = (object = $) ->
- @stub object, 'ajax', (options) ->
- # returns a new Deferred object so it supports all deferred methods, also invokes the callbacks in options
- dfd = $.Deferred()
- dfd.done(options.done) if options.done
- dfd.done(options.success) if options.success
- dfd.fail(options.fail) if options.fail
- dfd.fail(options.error) if options.error
- dfd.always(options.always) if options.always
- dfd.always(options.complete) if options.complete
- dfd.success = dfd.done
- dfd.error = dfd.fail
- dfd.complete = dfd.always
- dfd
- describe 'Stup Ajax', ->
- beforeEach ->
- @ajaxStub = @stubAjax()
- @ajax = $.ajax
- url: '/test'
- success: @success = @spy()
- error: @error = @spy()
- complete: @complete = @spy()
- it 'tests options', ->
- # accept options can be checked in stub
- expect(@ajaxStub.args[0][0].url).toEqual('/test')
- it 'tests success', ->
- # resolve with success arguments, should match the arguments for ajax success callback
- @ajax.resolve(name: 'abc')
- expect(@success.callCount).toBe(1)
- expect(@success.args[0]).toEqual([name: 'abc'])
- # or
- spy = @spy()
- @ajax.done(spy)
- expect(spy.callCount).toBe(1)
- expect(spy.args[0]).toEqual([name: 'abc'])
- it 'tests failure', ->
- # to test failure, should match the arguments for ajax error callback
- @ajax.reject()
- expect(@error.callCount).toBe(1)
- expect(@error.args[0]).toEqual([])
- # or
- spy = @spy()
- @ajax.fail(spy)
- expect(spy.callCount).toBe(1)
- expect(spy.args[0]).toEqual([])
- it 'tests complete', ->
- # both resolve and reject will trigger complete/always
- @ajax.resolve()
- # or ajax.reject()
- expect(@complete.callCount).toBe(1)
- expect(@complete.args[0]).toEqual([])
- # ==================================================
- # 2. Stub out jqxhr using sinon server (RECOMMENDED)
- #
- # Use respond to trigger callback, so testing is synchronous.
- #
- # This way you can get real AJAX response.
- #
- # It is also works for any ajax libraries.
- # setup sinon server sandbox
- afterEach ->
- @server?.restore()
- jasmine.Spec::fakeServer = ->
- @server ?= sinon.fakeServer.create()
- # ease respondWith
- # The matching is processed in FIFO sequence, to add a catch all response, but that can be overrided
- # later, set urlOrRegExp to null
- jasmine.Spec::respondWith = (urlOrRegExp, options = {}) ->
- type = options.type
- code = options.code ? 200
- headers = options.headers ? {}
- data = options.data ? ''
- contentType = options.contentType ? headers['Content-Type']
- contentType = 'application/x-www-form-urlencoded' if contentType is 'form'
- headers['Content-Type'] = contentType if contentType
- unless type of data is 'string'
- contentType ?= 'application/json'
- headers['Content-Type'] = contentType
- if /json$/.test(contentType)
- data = JSON.stringify(data)
- else if /x-www-form-urlencoded$/.test(contentType)
- data = $.param(data)
- if urlOrRegExp
- if type
- @fakeServer().respondWith(type, urlOrRegExp, [code, headers, data])
- else
- @fakeServer().respondWith(urlOrRegExp, [code, headers, data])
- else
- # if urlOrRegExp is falsy, use as default response, a.k.a, when no response matches, returns this
- @fakeServer().respondWith([code, headers, data])
- # All ajax requests are returned after call respond
- jasmine.Spec::respond = -> @server?.respond()
- describe 'Stup jqxhr', ->
- beforeEach ->
- @fakeServer()
- @ajaxSpy = @spy($, 'ajax')
- @ajax = $.ajax
- url: '/test'
- success: @success = @spy()
- error: @error = @spy()
- complete: @complete = @spy()
- it 'tests options', ->
- @respond()
- # accept options can be checked in spy
- expect(@ajaxSpy.args[0][0].url).toEqual('/test')
- it 'tests success', ->
- # simulate a server response
- @respondWith '/test',
- data:
- name: 'abc'
- # use respond to synchronize
- @respond()
- expect(@success.callCount).toBe(1)
- expect(@success.args[0][0]).toEqual(name: 'abc')
- # or
- spy = @spy()
- @ajax.done(spy)
- expect(spy.callCount).toBe(1)
- expect(spy.args[0][0]).toEqual(name: 'abc')
- it 'tests failure', ->
- # to test failure, should match the arguments for ajax error callback
- @respondWith '/test',
- code: 500
- data:
- error: 'abc'
- @respond()
- expect(@error.callCount).toBe(1)
- expect(@error.args[0][0].status).toBe(500)
- expect(@error.args[0][0].responseText).toEqual('{"error":"abc"}')
- # or
- spy = @spy()
- @ajax.fail(spy)
- expect(spy.callCount).toBe(1)
- expect(spy.args[0][0].status).toBe(500)
- it 'tests complete', ->
- # both failure and success trigger complete/always
- @respondWith '/test',
- code: 500
- data:
- error: 'abc'
- @respond()
- expect(@complete.callCount).toBe(1)
- # ==================================================
- # 3. Really want to test with real server?
- #
- # Since ajax requests are asynchronuse, use Deferred pipe and runs/waitsFor to
- # synchronize testing.
- #
- # This is also work for any asynchronize library that written based on Deferred.
- #
- # Setup a resolved deferred as pipe start point
- beforeEach ->
- @pipePromise = $.Deferred().resolve().promise()
- # See jQuery Deferred.pipe
- #
- # Deferred status is filerted.
- jasmine.Spec::pipe = (success, error) ->
- # wrap callbacks with current context
- context = @
- if success
- successWrapper = -> success.call(context, arguments...)
- if error
- errorWrapper = -> error.call(context, arguments...)
- # setup args to ease test
- chained = @pipePromise.pipe(successWrapper, errorWrapper)
- chained.always (args...) -> chained.args = args
- @pipePromise = chained
- jasmine.Spec::waitsForPipe = (message = 'Waits for Spec pipe', timeout = 5000) ->
- waitsFor ->
- @pipePromise.state() in ['rejected', 'resolved']
- , message, timeout
- jasmine.Spec::expectPipeResolved = ->
- runs ->
- expect(@pipePromise.state()).toEqual('resolved')
- jasmine.Spec::expectPipeResolvedWith = (args...) ->
- runs ->
- expect(@pipePromise.state()).toEqual('resolved')
- expect(@pipePromise.args).toEqual(args)
- jasmine.Spec::expectPipeRejected = ->
- runs ->
- expect(@pipePromise.state()).toEqual('rejected')
- jasmine.Spec::expectPipeRejectedWith = (args...) ->
- runs ->
- expect(@pipePromise.state()).toEqual('rejected')
- expect(@pipePromise.args).toEqual(args)
- describe 'Synchronize real ajax', ->
- it 'tests 404', ->
- @pipe -> $.ajax '/not_found_page.html'
- # must wait for all deferred in pipe finished
- @waitsForPipe()
- @expectPipeRejected()
- # code that executed after pipe is finished must be contained in runs block
- runs ->
- expect(@pipePromise.args[0].status).toBe(404)
- it 'tests success', ->
- @pipe -> $.ajax './'
- @waitsForPipe()
- @expectPipeResolved()
- # code that executed after pipe is finished must be contained in runs block
- runs ->
- expect(@pipePromise.args[2].status).toBe(200)
- it 'multiple requests', ->
- step1 = @pipe -> $.ajax './'
- # execute when former request is success
- @pipe -> $.ajax '/not_found_page.html'
- @waitsForPipe()
- @expectPipeRejected()
- # code that executed after pipe is finished must be contained in runs block
- runs ->
- expect(step1.state()).toBe('resolved')
- expect(step1.args[0]).toContain('<html')
- expect(step1.args[2].status).toBe(200)
- expect(@pipePromise.args[0].status).toBe(404)
- it 'pipe error', ->
- step1 = @pipe -> $.ajax '/not_found_page.html'
- # to pipe when former request is failed, use the second argument
- @pipe null, -> $.ajax './'
- @waitsForPipe()
- @expectPipeResolved()
- # code that executed after pipe is finished must be contained in runs block
- runs ->
- expect(step1.state()).toBe('rejected')
- expect(step1.args[0].status).toBe(404)
- expect(@pipePromise.args[2].status).toBe(200)
Add Comment
Please, Sign In to add comment