Titanium JIRA Archive
Titanium SDK/CLI (TIMOB)

[TIMOB-7749] Drillbit: Add support for creating a sequence of asynchronous operations in a test

GitHub Issuen/a
TypeNew Feature
PriorityHigh
StatusClosed
ResolutionFixed
Resolution Date2012-02-29T09:07:24.000+0000
Affected Version/sn/a
Fix Version/sSprint 2012-05, Release 2.0.0
ComponentsDrillbit
Labelsdrillbit, feature, tooling
ReporterJeff English
AssigneeJeff English
Created2012-02-21T18:08:18.000+0000
Updated2012-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.

Comments

  1. Jeff English 2012-02-21

    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'
          	}),
       	
       
  2. Jeff English 2012-02-21

    You can even push an async function 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'
          	}),
       
  3. Jeff English 2012-02-21

    PR submitted: https://github.com/appcelerator/titanium_mobile/pull/1479
  4. Jeff English 2012-02-27

    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'
           }),
       

JSON Source