Skip to content

Commit

Permalink
First test of the CLI with a browser. Ref #95.
Browse files Browse the repository at this point in the history
The CLI test uses hub.phantomContext() to create a browser.

Other changes:

 - Mock streams set isTTY = false to prevent weird handling.

 - Because mock streams are not a TTY, writing keys to them
   using readline.write(null, keyObject) triggers a Node.js crash.
   Wrap the Ctrl+U write in lib/cli with a check to see if the
   readline object is hooked into a terminal.

 - Add pause() and resume() noop methods to mock streams.
   This lets them work in readline.

 - Change a couple process.exit() calls to use self.exit()
   in lib/cli.
  • Loading branch information
reid committed Oct 5, 2012
1 parent 6ac8355 commit 14c7f22
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 19 deletions.
23 changes: 17 additions & 6 deletions lib/cli.js
Expand Up @@ -365,10 +365,21 @@ CLI.prototype.submitBatch = function submitBatch(client, options, cb) {
spins = spin.length - 1,
coverage = coverageProgress();

self.rl.write(null, {
ctrl: true,
name: "u"
});
// If the output of self.rl is NOT a TTY,
// a bug in Node.js readline will cause
// the unused first argument to be treated
// as a Buffer.
//
// https://github.com/joyent/node/blob/6e2055889091a424fbb5c500bc3ab9c05d1c28b4/lib/readline.js#L291
//
// Wrap this call to determine if the rl
// output is a terminal.
if (self.rl.terminal) {
self.rl.write(null, {
ctrl: true,
name: "u"
});
}
self.rl.write("Testing... " +
spin[spinIndex] +
" " + percent.toFixed(0) +
Expand Down Expand Up @@ -448,10 +459,10 @@ CLI.prototype.submitBatch = function submitBatch(client, options, cb) {
if (batchDetails.failed) {
self.puts(color.red("Failures") + ":", batchDetails.failed,
"of", total, "tests failed.", durationString);
process.exit(1);
self.exit(1);
} else {
self.puts(color.green(total + " tests passed!"), durationString);
process.exit(0);
self.exit(0);
}
});
};
Expand Down
96 changes: 84 additions & 12 deletions test/cli.js
Expand Up @@ -4,13 +4,18 @@ var vows = require("vows");
var assert = require("assert");

var streams = require("./lib/streams");
var hub = require("./lib/hub");

var cli = require("../lib/cli");
var YetiCLI = cli.CLI;

function cliTopic(fn) {
return function () {
var topic = {
var vow = this,
context,
topic;

topic = {
fe: null,
stdin: new streams.MockReadableStream(),
stdout: new streams.MockWritableStream(),
Expand All @@ -29,10 +34,35 @@ function cliTopic(fn) {
exitFn: mockExit
});

return fn.call(this, topic);
context = {
callback: function (err, expectedString) {
vow.callback(err, {
output: expectedString,
config: topic
});
}
};

return fn.call(context, topic);
};
}

function expectThenCallback(vow, stream, expectedString, mixins) {
stream.expect(expectedString, function (err, finalString) {
var topic = {
output: finalString
};

if (mixins) {
Object.keys(mixins).forEach(function (key) {
topic[key] = mixins[key];
});
}

vow.callback(err, topic);
});
}

vows.describe("Yeti CLI").addBatch({
"A Yeti CLI without arguments": {
topic: cliTopic(function (topic) {
Expand All @@ -44,10 +74,10 @@ vows.describe("Yeti CLI").addBatch({
]);
}),
"returns usage on stderr": function (topic) {
assert.ok(topic.indexOf("usage:") === 0);
assert.ok(topic.output.indexOf("usage:") === 0);
},
"returns helpful information on stderr": function (topic) {
assert.include(topic, "launch the Yeti server");
assert.include(topic.output, "launch the Yeti server");
}
},
"A Yeti CLI with --server": {
Expand All @@ -61,7 +91,7 @@ vows.describe("Yeti CLI").addBatch({
]);
}),
"returns startup message on stderr": function (topic) {
assert.ok(topic.indexOf("Yeti Hub started") === 0);
assert.ok(topic.output.indexOf("Yeti Hub started") === 0);
}
},
"A Yeti CLI with files": {
Expand All @@ -72,20 +102,62 @@ vows.describe("Yeti CLI").addBatch({
"node",
"cli.js",
"-p", "9011",
"fixture/basic.html",
__dirname + "/fixture/basic.html",
]);
}),
"prints hub creation message on stderr": function (topic) {
assert.ok(topic.indexOf("Creating a Hub.") === 0);
assert.ok(topic.output.indexOf("Creating a Hub.") === 0);
},
"waits for agents to connect on stderr": function (topic) {
assert.include(topic, "Waiting for agents to connect");
assert.include(topic, "also available locally at");
assert.include(topic.output, "Waiting for agents to connect");
assert.include(topic.output, "also available locally at");
},
"prompts on the writableStream": function (topic) {
assert.include(topic, "When ready, press Enter");
}
},
assert.include(topic.output, "When ready, press Enter");
},
"a browser": hub.phantomContext({
"visits Yeti": {
topic: function (browser, cli) {
var vow = this;

expectThenCallback(vow, cli.config.stderr, "Agent connected");

function onPageOpen(err, status) {
if (err) {
vow.callback(err);
}
}

browser.createPage(function (err, page) {
page.open("http://localhost:9011", onPageOpen);
});
},
"is ok": function (topic) {
assert.isUndefined(topic.stack);
},
"the stderr output contains the User-Agent": function (topic) {
assert.include(topic.output, "Mozilla");
},
"when Enter is pressed": {
topic: function (connectionSnapshot, browser, cli) {
expectThenCallback(this, cli.config.stdout, "pass");

cli.config.stdin.write("\n"); // Enter
},
"is ok": function (topic) {
assert.isUndefined(topic.stack);
},
"the stderr output contains the test results": function (topic) {
assert.include(topic.output, "1 tests passed");
},
"the stderr output contains Agent complete": function (topic) {
assert.include(topic.output, "Agent complete");
}
}
}
})
}
}).addBatch({
"parseArgv when given arguments": {
topic: function () {
return cli.parseArgv([
Expand Down
20 changes: 19 additions & 1 deletion test/lib/streams.js
Expand Up @@ -32,6 +32,8 @@ function MockReadableStream() {

util.inherits(MockReadableStream, EventEmitter2);

MockReadableStream.prototype.isTTY = false;

/**
* No-op.
*
Expand All @@ -46,6 +48,20 @@ MockReadableStream.prototype.setEncoding = NOOP;
*/
MockReadableStream.prototype.resume = NOOP;

/**
* No-op.
*
* @method pause
*/
MockReadableStream.prototype.pause = NOOP;

/**
* No-op.
*
* @method destroy
*/
MockReadableStream.prototype.destroy = NOOP;

/**
* Emit the `data` event with first argument
* as a String.
Expand All @@ -66,6 +82,8 @@ function MockWritableStream() {

util.inherits(MockWritableStream, EventEmitter2);

MockWritableStream.prototype.isTTY = false;

/**
* No-op.
*
Expand Down Expand Up @@ -103,7 +121,7 @@ MockWritableStream.prototype.expect = function (expectedString, cb) {

dataEvents.push(data);

if (data.indexOf(expectedString) !== -1) {
if (expectedString && data.indexOf(expectedString) !== -1) {
self.removeListener("data", ondata);
cb(null, dataEvents.join(""));
}
Expand Down

0 comments on commit 14c7f22

Please sign in to comment.