[TIMOB-7749] Drillbit: Add support for creating a sequence of asynchronous operations in a test
| GitHub Issue | n/a |
|---|---|
| Type | New Feature |
| Priority | High |
| Status | Closed |
| Resolution | Fixed |
| Resolution Date | 2012-02-29T09:07:24.000+0000 |
| Affected Version/s | n/a |
| Fix Version/s | Sprint 2012-05, Release 2.0.0 |
| Components | Drillbit |
| Labels | drillbit, feature, tooling |
| Reporter | Jeff English |
| Assignee | Jeff English |
| Created | 2012-02-21T18:08:18.000+0000 |
| Updated | 2012-03-19T06:23:06.000+0000 |
Description
It would be nice to be able to define a sequence of asynchronous functions that should be executed as part of a single test. For example,
1. Login to a service and verify result (asynchronously)
2. Query data and verify result (asynchronously)
3. Update data and verify result (asynchronously)
4. Logout of service and verify result (asynchronously)
This is especially important for validating REST-based services. While this could be done by putting each step into a separate test, it would be good to be able to define these in-line in a single test to avoid issues that could occur if a developer inserted or moved tests around in the test file.
Example of utilizing this functionality:
// Cloud.Objects.remove deleteAllCars: asyncTest({ start: function(callback) { var carIds = []; this.sequence.push(function() { Cloud.Objects.query('cars', this.async(function(e) { valueOf(e.success).shouldBeTrue(); valueOf(e.error).shouldBeFalse(); for (var i=0; i<e.cars.length; i++) { carIds.push(e.cars[i].id); } }) )}); this.sequence.push(function() { Cloud.Objects.remove('cars', { ids: carIds.toString() }, this.async(function(e) { valueOf(e.success).shouldBeTrue(); valueOf(e.error).shouldBeFalse(); }) )}); this.sequence.push(function() { Cloud.Objects.query('cars', this.async(function(e) { valueOf(e.success).shouldBeTrue(); valueOf(e.error).shouldBeFalse(); valueOf(e.cars.length).shouldBe(0); valueOf(e.meta.total_results).shouldBe(0); }) )}); }, timeout: 5000, timeoutError: 'Timed out waiting for remove response' }),You can even push an
asyncfunction on the sequence if an operation does not involve the use of a callback function (see the last operation in this example):// Register for push notifications pushTest: asyncTest({ start: function(callback) { var deviceToken; this.sequence.push(function() { Ti.Network.registerForPushNotifications({ types: [ Ti.Network.NOTIFICATION_TYPE_BADGE, Ti.Network.NOTIFICATION_TYPE_ALERT, Ti.Network.NOTIFICATION_TYPE_SOUND ], success: this.async(function (e) { deviceToken = e.deviceToken; }), error: this.async(function(e) { callback.failed(e); }), callback: this.async(function(e) { // Do nothing -- the next function in the sequence will automatically // trigger if we get the push }) }); }); this.sequence.push(function() { Cloud.subscribe({ channel: 'test', device_token: deviceToken }, this.async(function(e) { valueOf(e.success).shouldBeTrue(); valueOf(e.error).shouldBeFalse(); }) )}); this.sequence.push(function() { Cloud.notify({ channel: 'test', payload: 'Hello World' }, this.async(function(e) { valueOf(e.success).shouldBeTrue(); valueOf(e.error).shouldBeFalse(); }) )}); this.sequence.push(function() { // The sequencer will wait after this method returns until something triggers // it to go to the next sequence. Hopefully that will be the receipt of the // push notification. }); this.sequence.push(function() { Cloud.unsubscribe({ channel: 'test', device_token: deviceToken }, this.async(function(e) { valueOf(e.success).shouldBeTrue(); valueOf(e.error).shouldBeFalse(); }) )}); this.sequence.push(this.async(function() { Ti.Network.unregisterForPushNotifications(); })); }, timeout: 10000, timeoutError: 'Timed out waiting for push notification' }),PR submitted: https://github.com/appcelerator/titanium_mobile/pull/1479
Here is an example that demonstrates that the sequence can be modified while running. In this case, we need to query the remote database to get a list of existing entries to be deleted. Upon receipt of the list we need to issue a 'remove' request for each entry that is returned. Since we don't know ahead of time how many entries there will be, we query the server and insert additional entries into the sequence.
cloudPlacesQueryAndDeleteAll: asyncTest({ start: function(callback) { var ids = []; this.sequence.push(function() { Cloud.Places.query( this.async(function(e) { valueOf(e.success).shouldBeTrue(); valueOf(e.error).shouldBeFalse(); for (var i=0; i<e.places.length; i++) { ids.push(e.places[i].id); this.sequence.unshift(function() { Cloud.Places.remove({ place_id: ids.pop() }, this.async(function(e) { valueOf(e.success).shouldBeTrue(); valueOf(e.error).shouldBeFalse(); }) )}); } }) )}); this.sequence.push(function() { Cloud.Places.query( this.async(function(e) { valueOf(e.success).shouldBeTrue(); valueOf(e.error).shouldBeFalse(); valueOf(e.places.length).shouldBe(0); valueOf(e.meta.total_results).shouldBe(0); }) )}); }, timeout: 5000, timeoutError: 'Timed out waiting for remove response' }),