Some random thoughts on what a decent test environment would need for us to have some kick ass testing.
Request objects are immutable. All functions that take a value to mutate a setting return a new copy of the object. But they all return a new copy so we can do method chaining:
var req = Request.create();
req = req.method("POST").json_body({docs: [{"foo": "bar"}]});
# This last line is just illustrative. json_body would obviously
# set this correctly, though we could override it to something else.
req = req.headers("Content-Type", "application/json");
In addition there will be a handful of helper functions that allow clients to construct a base request and fire a handful of requests with simple modifications:
var req0 = Request.create("GET", HOST + "/path");
var req1 = req.get(HOST + "/other_path");
var req2 = req.get(HOST + "/my_path", {"Accept", "application/xml"});
The constructor takes up to four values:
var req = Request.create(method, url, headers, options);
method, url, and headers are self explanatory. Headers is an object with string or array of string values. The options object has these parameters:
version = [positive_integer, positive_integer]
connection = "close" | "keep-alive" | falsy means default for version
auth = ["user", "password"] # maybe?
followRedirect = true | false
maxRedirects = positive_integer
timeout = positive_integer milliseconds
raw_body = true | false # optionally disable chunk parsing
writer = true | false # maybe add a thing that allows writing
# a request body in chunks?
resp = Response object # A way to easily create requests that
# use cache info from a previous resp.
# and more that we need
Request global settings:
Request.base_url = "http://127.0.0.1:5984/";
This would set the base URL so that all URL's that are bare paths would
be interpreted as relative to this URL. Or I could set this from the
URL in the constructor. The port will be random though, so we'll need
to control for that.
Sending the request and getting a response:
var resp = req.go();
Response objects will have read only access to the various attributes:
status = HTTP response status (200, 404, 500, etc)
mesg = HTTP response message (OK, Not Found, Server Error, etc)
version = [positive_integer, positive_integer]
headers = object with string keys and string values
mheaders = object with string keys and arrays of string values.
body = object that can be read from like a file (or maybe just a string?)
json_body = parse the body as JSON
By default request objects will dispatch their HTTP request synchronously and return the response directly. Alternatively, clients can elect to have their request fired asynchronously (not XHR style) in the background and continue to do other work. This would look like such:
var resp = req.dispatch();
for(var i = 0; i < 10; i++) {
var info = req.get("/path/" + i).go();
assert(info.status == 200);
}
resp.wait();
tap.is(resp.status, 404, "Pithy note.");
Response.wait() would also return the status, so this would work:
tap.isnt(resp.wait().status, 200, "Not ok!");
This part I'm not sure about, but it might be useful to be able to dispatch multiple HTTP requests and then wait for the first to respond:
var hub = Response.hub();
var req = Request.create("GET", "/", "foo");
for(var i = 0; i < 10; i++) {
resps[resps.length] = req.get("/" + i).dispatch(hub);
}
var resp = hub.next();
tap.is(resp.status, 200, "First request was ok.");
That sort of thing.
There'd be a global tap object. It'd have methods based on what exists in the Erlang etap that make sense for JavaScript.
As part of this, setup and teardown functions could come in quite handy here. Not sure if this should be part of tap or on a CommonJS module.
tap.assert(value) - This would behave like assert in C where execution would halt or optionally drop into the REPL.
Nothing too fancy, but a log.(info|error|debug|spew) thing that we can control with command line switches or environment variables. This way we can be quite liberal with log functions and then when an error occurs we would be able to track it down much faster. Messages would just go to the tap output stream.
Each log level would also affect how verbosely HTTP request are logged. INFO would be nothing, debug would print some basic access type logs. Spew would dump the entire request/response byte streams. HTTP requests that aren't displayed on a single line will have nonce values for easy pairing of output.
I wonder if we should use commonjs modules to load library code. This seems like it'd be quite useful when we get more and more complicated tests.
Maybe support Surefire or something that also seems to be popular for test results.
Apparently lots of people use a debugger thing in the browser. I don't think we should get too fancy here, but we could support a "break to REPL on exception" or something similar.