Navigation Menu

Skip to content

Commit

Permalink
[api test] Move all outgoing request options into suite.outgoing from…
Browse files Browse the repository at this point in the history
… suite.options. Added before() and unbefore() methods
  • Loading branch information
indexzero committed Mar 22, 2011
1 parent fada29f commit e84e93e
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 35 deletions.
75 changes: 55 additions & 20 deletions lib/rest-easy.js
Expand Up @@ -30,7 +30,10 @@ exports.describe = function (text) {
//
// * `suite`: The underlying vows suite
// * `discussion`: Ordered list containing the set of text to use before each test
// * `options`: Shared options for each test request made by this RESTeasy suite
// * `outgoing`: Shared options to be passed to the `request` module on each test.
// * `before`: Mapping of named functions to execute before each test to modify the
// outgoing request options.
// * `options`: Various configuration options for managing nuances of state / scope.
// * `paths`: The set of paths representing the location of the current resource /
// API method being tested by this object.
// * `batch`: The object literal representing the current batch of vows tests to
Expand All @@ -40,9 +43,11 @@ exports.describe = function (text) {
//
suite: vows.describe(text),
discussion: [],
options: {
outgoing: {
headers: {}
},
before: {},
options: {},
paths: [],
batch: {},
batches: [],
Expand Down Expand Up @@ -102,15 +107,15 @@ exports.describe = function (text) {
// They are designed to mimic the node.js core HTTP APIs.
//
setHeaders: function (headers) {
this.options.headers = headers;
this.outgoing.headers = headers;
return this;
},
setHeader: function (key, value) {
this.options.headers[key] = value;
this.outgoing.headers[key] = value;
return this;
},
removeHeader: function (key, value) {
delete this.options.headers[key];
delete this.outgoing.headers[key];
return this;
},

Expand All @@ -130,7 +135,23 @@ exports.describe = function (text) {
return this;
},
root: function (path) {
this.path = [path];
this.paths = [path];
return this;
},

//
// ### Dynamically set Outgoing Request Options
// Often it is necessary to set some HTTP options conditionally or based on
// results of a dynamic and/or asynchronous operation. A call to `.before()`
// will enqueue a function that will modify the outgoing request options
// before the request is made for all tests on the suite.
//
before: function (name, fn) {
this.before[name] = fn;
return this;
},
unbefore: function (name) {
delete this.before[name];
return this;
},

Expand Down Expand Up @@ -247,7 +268,7 @@ exports.describe = function (text) {
// 3. Create a new empty object literal to use for `this.batch`.
// 4. Reset the context for the `this.current` test.
//
next: function (reset) {
next: function () {
this.suite.addBatch(this.batch);
this.batches.push(this.batch);
this.batch = {};
Expand Down Expand Up @@ -295,14 +316,23 @@ exports.describe = function (text) {
// interested in improving RESTeasy itself.
//
_request: function (/* method [uri, data, params] */) {
var args = Array.prototype.slice.call(arguments),
var self = this,
args = Array.prototype.slice.call(arguments),
method = args.shift(),
uri = typeof args[0] === 'string' && args.shift(),
data = typeof args[0] === 'object' && args.shift(),
params = typeof args[0] === 'object' && args.shift(),
port = this.port && this.port !== 80 ? ':' + this.port : '',
options = clone(this.options),
fullUri, context;
outgoing = clone(this.outgoing),
fullUri, context, before;

// Create an array of the named before functions that should
// be run before the actual request is made. These functions are
// by definition synchronous add vows before a given test if
// this data is fetched asynchronously.
before = Object.keys(this.before).map(function (name) {
return self.before[name];
});

// Update the fullUri for this request with the passed uri
// and the query string parameters (if any).
Expand All @@ -323,14 +353,14 @@ exports.describe = function (text) {
// [request module](http://github.com/mikeal/request)
//
if (data) {
options.body = JSON.stringify(data);
outgoing.body = JSON.stringify(data);
}

// Set the `uri` and `method` properties of the request options
// Set the `uri` and `method` properties of the request options `outgoing`
// using the information provided to this instance and `_request()`.
options.uri = this.secure ? 'https://' : 'http://';
options.uri += this.host + port + fullUri;
options.method = method;
outgoing.uri = this.secure ? 'https://' : 'http://';
outgoing.uri += this.host + port + fullUri;
outgoing.method = method;

//
// Create the description for this test. This is currently static.
Expand All @@ -343,14 +373,19 @@ exports.describe = function (text) {
// batch used by this suite.
context[this.current] = {
topic: function () {
request(options, this.callback);
before.forEach(function (fn) {
outgoing = fn(outgoing);
});

request(outgoing, this.callback);
}
};

// Set the options on the topic. This is used for test assertions and
// the topic itself. This will allow you to customize the options used by
// this topic in a simple pragmatic way.
context[this.current].topic.options = options;
// Set the outgoing request options and set of before functions on the topic.
// This is used for test assertions, general consistency, and basically
// just knowing what every topic does explicitly.
context[this.current].topic.outgoing = outgoing;
context[this.current].topic.before = before
return this;
},

Expand Down
43 changes: 33 additions & 10 deletions test/core-test.js
Expand Up @@ -55,24 +55,24 @@ vows.describe('rest-easy/core').addBatch({
},
"the setHeader() method": {
"should set the header appropriately": function (suite) {
var length = Object.keys(suite.options.headers).length;
var length = Object.keys(suite.outgoing.headers).length;
suite.setHeader('x-test-header', true);
assert.length(Object.keys(suite.options.headers), length + 1);
assert.length(Object.keys(suite.outgoing.headers), length + 1);
}
},
"the removeHeader() method": {
"should remove the header appropriately": function (suite) {
var length = Object.keys(suite.options.headers).length;
var length = Object.keys(suite.outgoing.headers).length;
suite.removeHeader('x-test-header');
assert.length(Object.keys(suite.options.headers), length - 1);
assert.length(Object.keys(suite.outgoing.headers), length - 1);
}
},
"the setHeaders() method": {
"should set all headers appropriately": function (suite) {
suite.setHeader('x-test-header', true);
suite.setHeaders({ 'Content-Type': 'application/json' });
assert.length(Object.keys(suite.options.headers), 1);
assert.equal(suite.options.headers['Content-Type'], 'application/json');
assert.length(Object.keys(suite.outgoing.headers), 1);
assert.equal(suite.outgoing.headers['Content-Type'], 'application/json');
}
},
"the path() method": {
Expand All @@ -90,6 +90,15 @@ vows.describe('rest-easy/core').addBatch({
suite.unpath();
}
},
"the before() method": {
"should append the function to the set of before operations": function (suite) {
suite.before('setAuth', function (outgoing) {
outgoing.headers['x-test-is-authorized'] = true;
});

assert.isFunction(suite.before['setAuth']);
}
},
"a GET test": {
"with no path": {
topic: function (suite) {
Expand All @@ -104,6 +113,7 @@ vows.describe('rest-easy/core').addBatch({
headers: {
'Content-Type': 'application/json'
},
before: 1,
length: 4
})
},
Expand All @@ -116,6 +126,7 @@ vows.describe('rest-easy/core').addBatch({
headers: {
'Content-Type': 'application/json'
},
before: 1,
length: 2
})
},
Expand All @@ -128,10 +139,18 @@ vows.describe('rest-easy/core').addBatch({
headers: {
'Content-Type': 'application/json'
},
before: 1,
length: 2
})
}
},
"the unbefore() method": {
"should remove the function from the set of before operations": function (suite) {
suite.unbefore('setAuth');

assert.length(Object.keys(suite.before), 0);
}
},
"A POST test": {
"with no path": {
topic: function (suite) {
Expand All @@ -143,7 +162,8 @@ vows.describe('rest-easy/core').addBatch({
headers: {
'Content-Type': 'application/json'
},
length: 2
length: 2,
before: 0
})
},
"with no path and a request body": {
Expand All @@ -158,7 +178,8 @@ vows.describe('rest-easy/core').addBatch({
headers: {
'Content-Type': 'application/json'
},
length: 2
length: 2,
before: 0
})
},
"with no path, a request body, and params": {
Expand All @@ -173,7 +194,8 @@ vows.describe('rest-easy/core').addBatch({
headers: {
'Content-Type': 'application/json'
},
length: 2
length: 2,
before: 0
})
},
"with a path, request body, and params": {
Expand All @@ -188,7 +210,8 @@ vows.describe('rest-easy/core').addBatch({
headers: {
'Content-Type': 'application/json'
},
length: 2
length: 2,
before: 0
})
}
}
Expand Down
13 changes: 8 additions & 5 deletions test/helpers.js
Expand Up @@ -13,10 +13,13 @@ var helpers = exports,
reservedOptions = {
'length': function (batch, length) {
assert.length(Object.keys(batch), length);
},
'before': function (batch, length) {
assert.length(batch.topic.before, length);
}
};

helpers.assertOptions = function (scopes, local, options) {
helpers.assertOptions = function (scopes, local, outgoing) {
return function (batch) {
var localScope = scopes.concat(local);

Expand All @@ -26,14 +29,14 @@ helpers.assertOptions = function (scopes, local, options) {
});

assert.isFunction(batch.topic);
assert.isObject(batch.topic.options);
assert.isObject(batch.topic.outgoing);

Object.keys(options).forEach(function (key) {
Object.keys(outgoing).forEach(function (key) {
if (reservedOptions[key]) {
reservedOptions[key](batch, options[key]);
reservedOptions[key](batch, outgoing[key]);
}
else {
assert.deepEqual(batch.topic.options[key], options[key]);
assert.deepEqual(batch.topic.outgoing[key], outgoing[key]);
}
});
};
Expand Down

0 comments on commit e84e93e

Please sign in to comment.