Last active
August 29, 2015 14:06
-
-
Save dellermann/df2710ab03c9698c561c to your computer and use it in GitHub Desktop.
Deferreds in test cases. This gist shows how to use `Deferred` or `Promise` in QUnit test cases where a chain of actions and assertions are needed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This function returns a `Promise` that is resolved when a particular | |
# event on the given element occurs. | |
newEventPromise = (elem, event, action) -> | |
$ = jQuery | |
that = this | |
# First, we create a new `Deferred` object. | |
deferred = $.Deferred() | |
# Then, we register a handle for the given event type (e. g. "click"). | |
# If the event is triggered the `Deferred` object is resolved. | |
$(elem).one event, (ev) -> deferred.resolve [ev] | |
# If there is an action it is called here. Note the order of registering the | |
# event listener and calling the action. If it was swapped the action would | |
# be called first, and the event would be triggered before registering the | |
# `Deferred` with the `resolve` method, effectively causing the event comes | |
# to nothing. | |
action.call that if action? | |
# Now we obtain and return a `Promise` object from the `Deferred` object to | |
# prevent resolving or rejecting in the caller code. | |
deferred.promise() | |
# A sample use case may be defined as follows. It creates a chain of functions | |
# that are called in a particular order and reflect the actions and asserts. | |
$btn = $('#btn1') | |
# First, we obtain a `Promise` that is resolved if the button is clicked. After | |
# that, a click on the button is simulated. | |
newEventPromise($btn, 'click', -> $btn.click()) | |
.then( -> | |
# This function is called after the button has been clicked for the | |
# first time. Here we can assert something, and, finally create a | |
# new `Promise` and click the button again. | |
console.log 'Clicked 1st time' | |
newEventPromise $btn, 'click', -> $btn.click() | |
) | |
.then( -> | |
# As above, this function is called when the button has been clicked | |
# for the second time. Also, we click the button again. | |
console.log 'Clicked 2nd time' | |
newEventPromise $btn, 'click', -> $btn.click() | |
) | |
.then( -> | |
# Same as above... | |
console.log 'Clicked 3rd time' | |
newEventPromise $btn, 'click', -> $btn.click() | |
) | |
# Finally, the button will be click no more and there is no event | |
# listener any longer. | |
.done -> console.log 'Clicked last time' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# First, I define a class that allows to use a Promise that is resolved when a | |
# particular trigger is called. | |
class TriggerChain | |
constructor: -> | |
# The triggerFunc variable contains a function that is called when | |
# triggered. Initially, we set it to `null` because it isn't initialized | |
# yet. | |
@triggerFunc = null | |
newPromise: (action) -> | |
$ = jQuery | |
that = this | |
# Here, we create a new `Deferred` object and register the trigger function. | |
# When it is called it resolves the `Deferred` object causing the chain to | |
# be executed. | |
deferred = $.Deferred() | |
@triggerFunc = -> deferred.resolveWith that, $.makeArray arguments | |
# Here we call an action function, if any. Note the order of defining the | |
# trigger function and calling the action. If it was swapped the action | |
# would be called first, and the trigger would be called before registering | |
# the `Deferred` with the `resolveWith` method, effectively causing the | |
# trigger comes to nothing. | |
action.call that if action? | |
# Now we obtain and return a `Promise` object from the `Deferred` object to | |
# prevent resolving or rejecting in the caller code. | |
deferred.promise() | |
trigger: -> | |
# Here, we call the trigger function if it is defined. | |
f = @triggerFunc | |
f.apply this, arguments if f | |
# A sample use case may be defined as follows. It creates a document list | |
# widget on element `#dl1` within a QUnit test case. The aspiration is to | |
# have a chain of functions that reflect the actions and asserts. | |
chain = new TriggerChain() | |
$('#dl1').documentlist | |
init: -> | |
# Initially, the document list loaded and the content of path `/` is | |
# displayed. | |
console.log 'should be in /' | |
$this = $(this) | |
# Now I want to define a chain of actions and asserts using `Promise` | |
# objects. First, we create a `Promise` and click on second folder | |
# in the list. | |
chain.newPromise( -> $this.find('> ul > li:nth-child(2)').click()) | |
.then( (path) -> | |
# Here, the path has changed, we are in `/foo`, and the content | |
# of this folder has been loaded successfully. | |
console.log "should be in /foo :: /#{path}" | |
# TODO asserts here... | |
# We create a new promise and, as an action, we click the second link | |
# (the first folder, because the first link is a back link to `/`). | |
@newPromise -> $this.find('> ul > li:nth-child(2)').click() | |
) | |
.then( (path) -> | |
# The same case as in the `then` step above, except now we are in path | |
# `/foo/wheezy` and click the first link (the back button). | |
console.log "should be in /foo/wheezy :: /#{path}" | |
# TODO asserts here... | |
@newPromise -> $this.find('> ul > li:first-child').click() | |
) | |
.then( (path) -> | |
# Now, we are in path `/foo` again and click the back button. | |
console.log "should be in /foo :: /#{path}" | |
# TODO asserts here... | |
@newPromise -> $this.find('> ul > li:first-child').click() | |
) | |
.done( (path) -> | |
# Finally, we are in path `/` again where our journey ends. | |
console.log "should be in / :: /#{path}" | |
# TODO asserts here... | |
) | |
.fail( -> | |
# This function is called if anything in the previous steps went | |
# wrong. | |
console.log 'Anything went wrong...' | |
) | |
# This triggers if the path has changed and the content of the current | |
# folder has been loaded successfully and displayed. Each time the | |
# trigger is called the next step in the chain above is executed. | |
pathChanged: (path) -> chain.trigger path |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment