Coverage

100%
225
225
0

lib/nixt.js

100%
3
3
0
LineHitsSource
1/**
2 * Primary export.
3 */
4
51module.exports = require('./nixt/runner');
6
7/**
8 * Plugin support.
9 */
10
111module.exports.register = require('./nixt/plugin');
12
13/**
14 * Module version.
15 */
16
171module.exports.version = require('../package.json').version;
18

lib/nixt/batch.js

100%
31
31
0
LineHitsSource
1/**
2 * Batch - maintain the registered middlewares & expectations.
3 *
4 * `Batch` is being used by `Runner`. The main role of it is to
5 * maintain the correct order of the registered middlewares and the expectations.
6 *
7 * In order to support "templates" `Batch` tries to encapsulate the mechanics
8 * behind that. There are a few rules that one should keep in mind:
9 *
10 * - `addBefore` always adds at the end of the 'before' list
11 * - `addAfter` always adds at the end of the 'after' list
12 * - `add` adds either after the before list or before the after list depending if a "main"
13 * function has been registered or not.
14 *
15 * The following example will (hopefully) illustrate how this class works:
16 *
17 * var batch = new Batch;
18 * batch.addBefore(before1) --> execution order [before1]
19 * batch.addBefore(before2) --> execution order [before1, before2]
20 * batch.addAfter(after1) --> execution order [before1, before2, after1]
21 * batch.add(fn1) --> execution order [before1, before2, fn1, after1]
22 * batch.main(main) --> execution order [before1, before2, fn1, main, after1]
23 * batch.add(fn2) --> execution order [before1, before2, fn1, main, fn2, after1]
24 * batch.add(before3) --> execution order [before1, before2, before3, fn1, main, fn2, after1]
25 *
26 *
27 * So why is this even useful? It's useful when you want to implement some sort of a template.
28 * Imagine the following case - you always want to perform "setup" and "teardown" for some
29 * app. In this particular case we'll discuss "todo" (npm install todo). Todo works with a simple
30 * json file which happens to be its database. So if you were testing it you would want to start
31 * with a clean state each and every time. Here is how you could accomplish that:
32 *
33 * var todo = nixt()
34 * .before(createBlankDatabase);
35 * .after(removeTheDatabase);
36 *
37 * Now you can put this in a helper function for your tests:
38 *
39 * function todoApp() {
40 * return todo.clone();
41 * }
42 *
43 * And now every time when you need to create a new instance you can do so by calling the simple
44 * helper method that you have created. Of course there are many ways to accomplish the same, but
45 * nixt gives you the ability to keep everything simple.
46 *
47 * @constructor
48 */
49
50function Batch() {
514 this.before = [];
524 this.afterBefore = [];
534 this.after = [];
544 this.beforeAfter = [];
554 this.fn = null;
56}
57
58/**
59 * Push `fn` into the before list.
60 *
61 * @param {Function} fn
62 * @api public
63 */
64
651Batch.prototype.addBefore = function(fn) {
662 this.before.push(fn);
67};
68
69/**
70 * Push `fn` into the after list.
71 *
72 * @param {Function} fn
73 * @api public
74 */
75
761Batch.prototype.addAfter = function(fn) {
775 this.after.push(fn);
78};
79
80/**
81 * Register a function in either the "after before" list
82 * or in the "before after" list, depending if a "main"
83 * function exists.
84 *
85 * @see Batch#hasMain
86 * @see Batch#main
87 * @param {Function} fn
88 * @api public
89 */
90
911Batch.prototype.add = function(fn) {
9218 (this.hasMain() ? this.beforeAfter : this.afterBefore).push(fn);
93};
94
95/**
96 * Register a "main" function.
97 *
98 * @param {Function} fn
99 * @api public
100 */
101
1021Batch.prototype.main = function(fn) {
10328 this.fn = fn;
104};
105
106/**
107 * Return if there is a main function or not.
108 *
109 * @returns {Boolean}
110 * @api public
111 */
112
1131Batch.prototype.hasMain = function() {
11447 return !!this.fn;
115};
116
117/**
118 * Execute all registered functions. Keep in mind that the result of
119 * the "main" function will be supplied to the last callback.
120 *
121 * @param {Function} last fn to execute
122 * @api public
123 */
124
1251Batch.prototype.run = function(fn) {
12628 var err = null;
12728 var main = this.fn;
12828 var batch = this.before.slice(0).concat(this.afterBefore);
129
13028 batch.push(function(next) {
13184 main(function(e) { err = e; next(); });
132 });
133
13428 batch = batch.concat(this.beforeAfter).concat(this.after);
135
13628 batch.push(function() {
13728 fn(err);
138 });
139
140 function next() {
141109 var fn = batch.shift();
142137 if (!fn) return;
143128 if (fn.length) return fn(next);
14434 fn();
14534 next();
146 }
147
14828 next();
149};
150
151/**
152 * Primary exports.
153 */
154
1551module.exports = Batch;
156

lib/nixt/command.js

100%
19
19
0
LineHitsSource
1/**
2 * Core dependencies.
3 */
4
51var exec = require('child_process').exec;
6
7/**
8 * Internal dependencies.
9 */
10
111var Result = require('./result');
12
13/**
14 * Command to execute.
15 *
16 * This class is a simple wrapper of `child_process#exec`. All it does is
17 * executing the command with the supplied options and then return a new
18 * `Result`. It delegates the formatting of stdout and stderr to `Formatter`.
19 *
20 * @param {Formatter} formatter
21 * @see child_process#exec
22 * @see Formatter
23 * @see Result
24 * @constructor
25 */
26
27function Command(formatter) {
283 this.formatter = formatter;
293 this.envs = {};
30}
31
32/**
33 * Set a command to be executed.
34 *
35 * @param {String} command
36 * @api public
37 */
38
391Command.prototype.set = function(cmd) {
4028 this.cmd = cmd;
41};
42
43/**
44 * Set a command timeout.
45 *
46 * @param {Number}
47 * @api public
48 */
49
501Command.prototype.timeout = function(ms) {
511 this.ms = ms;
52};
53
54/**
55 * Set the current working directory for
56 * the command.
57 *
58 * @param {String} path
59 * @api public
60 */
61
621Command.prototype.cwd = function(cwd) {
633 this.dir = cwd;
64};
65
66/**
67 * Set environemnt variable.
68 *
69 * @param {String} key
70 * @param {String} val
71 * @api public
72 */
73
741Command.prototype.env = function(key, val) {
752 this.envs[key] = val;
76};
77
78/**
79 * Execute the command, load stdout and stderr into
80 * the formatter and return a new `Result`.
81 *
82 * @param {Function} fn
83 * @api public
84 */
85
861Command.prototype.exec = function(fn) {
8728 var self = this;
8828 var options = { cwd: this.dir, timeout: this.ms, env: this.envs };
89
9028 exec(this.cmd, options, function(err, stdout, stderr) {
9128 self.formatter.load(stdout, stderr);
9228 fn(new Result(self.formatter, err, self.cmd));
93 });
94};
95
96/**
97 * Primary export.
98 */
99
1001module.exports = Command;
101

lib/nixt/expectations.js

100%
34
34
0
LineHitsSource
1/**
2 * Core dependencies.
3 */
4
51var fs = require('fs');
6
7/**
8 * External dependencies.
9 */
10
111var AssertionError = require('assertion-error');
12
13/**
14 * Return an exit code expectation.
15 *
16 * @param {Number} expected exit code.
17 * @returns {Function}
18 * @api public
19 */
20
211exports.code = function(code) {
224 return function(result) {
234 if (code !== result.code) {
241 return error(result, 'Expected exit code: "' + code + '", actual: "' + result.code + '"');
25 }
26 };
27};
28
29/**
30 * Return no timeout expectation.
31 *
32 * @returns {Function}
33 * @api public
34 */
35
361exports.time = function() {
371 return function(result) {
381 if (result.killed) {
391 return error(result, 'Command execution terminated (timeout)');
40 }
41 };
42};
43
44/**
45 * Return a stderr expectation.
46 *
47 * @param {String|RegExp} expected string or regular express to match
48 * @returns {Function}
49 * @api public
50 */
51
521exports.stderr = function(expected) {
536 return function(result) {
546 return assertOut('stderr', expected, result);
55 };
56};
57
58/**
59 * Return a stdout expectation.
60 *
61 * @param {String|RegExp} expected string or regular express to match
62 * @returns {Function}
63 * @api public
64 */
65
661exports.stdout = function(expected) {
6712 return function(result) {
6812 return assertOut('stdout', expected, result);
69 };
70};
71
72/**
73 * Verify that a `path` exists.
74 *
75 * @param {String} path
76 * @returns {Function}
77 * @api public
78 */
79
801exports.exists = function(path) {
814 return function(result) {
824 if (fs.existsSync(path) !== true) {
832 return error(result, 'Expected "' + path + '" to exist.');
84 }
85 };
86};
87
88/**
89 * Verify that `path`'s data matches `data`.
90 *
91 * @param {String} path
92 * @param {String|RegExp} data
93 * @returns {Function}
94 * @api public
95 */
96
971exports.match = function(path, data) {
983 return function(result) {
993 var contents = fs.readFileSync(path, { encoding: 'utf8' });
1003 var statement = data instanceof RegExp
101 ? data.test(contents)
102 : data === contents;
103
1043 if (statement !== true) {
1051 return error(result, 'Expected "' + path + '" to match "' + data + '", but it was: "' + contents + '"');
106 }
107 };
108};
109
110/**
111 * Assert stdout or stderr.
112 *
113 * @param {String} stdout/stderr
114 * @param {Mixed} expected
115 * @param {Result} result
116 * @returns {AssertionError|null}
117 * @api private
118 */
119
120function assertOut(key, expected, result) {
12118 var actual = result[key];
12218 var statement = expected instanceof RegExp
123 ? expected.test(actual)
124 : expected === actual;
125
12618 if (statement !== true) {
1274 var message = 'Expected ' + key +' to match "' + expected + '". Actual: "' + actual + '"';
1284 return error(result, message);
129 }
130}
131
132/**
133 * Create and return a new `AssertionError`.
134 * It will assign the given `result` to it, it will also prepend the executed command
135 * to the error message.
136 *
137 * Assertion error is a constructor for test and validation frameworks that implements
138 * standardized Assertion Error specification.
139 *
140 * For more info go visit https://github.com/chaijs/assertion-error
141 *
142 * @param {Result} result
143 * @param {String} error message
144 * @returns {AssertionError}
145 * @api private
146 */
147
148function error(result, message) {
1499 var err = new AssertionError('`' + result.cmd + '`: ' + message);
1509 err.result = result;
1519 return err;
152}
153

lib/nixt/formatter.js

100%
14
14
0
LineHitsSource
1/**
2 * Command-line response formatter.
3 *
4 * Options:
5 *
6 * - colors Leave colors, default: true
7 * - newlines Leave newlines, default: true
8 *
9 * Notes:
10 *
11 * - Probably it doesn't make a lot of sense to store the result into
12 * the given instance. It could be just a simple helper, however
13 * by using the current design the communication between `Runner`
14 * `Command` and `Result` is quite simplified.
15 *
16 * @param {Object} options
17 * @constructor
18 */
19
20function Formatter(options) {
214 options = options || {};
224 this.colors = options.colors;
234 this.newlines = options.newlines;
24}
25
26/**
27 * Format the command-line result.
28 *
29 * @param {String} stdout
30 * @param {String} stderr
31 * @api public
32 */
33
341Formatter.prototype.load = function(stdout, stderr) {
3528 this.stdout = this.strip(stdout);
3628 this.stderr = this.strip(stderr);
37};
38
39/**
40 * `Formatter#strip` will do the following:
41 *
42 * - Remove the last new line symbol from the string (always)
43 * - Strip new lines (optional, see `options`)
44 * - Strip colors (optional, see `options`)
45 *
46 * Acknowledgments:
47 *
48 * - StripColorCodes - MIT License
49 *
50 * @param {String} str
51 * @returns {String}
52 * @api private
53 */
54
551Formatter.prototype.strip = function(str) {
5656 str = str.replace(/\r?\n|\r$/, '');
57
5856 if (this.newlines === false) {
592 str = str.replace(/\r?\n|\r/g, '');
60 }
61
6256 if (this.colors === false) {
632 str = str.replace(/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]/g, '');
64 }
65
6656 return str;
67};
68
69/**
70 * Primary exports.
71 */
72
731module.exports = Formatter;
74

lib/nixt/middlewares.js

100%
20
20
0
LineHitsSource
1/**
2 * Core dependencies.
3 */
4
51var exec = require('child_process').exec;
61var fs = require('fs');
7
8/**
9 * Asynchronous mkdir(2).
10 *
11 * @param {String} path
12 * @returns {Function} middleware
13 * @see fs#mkdir
14 * @api public
15 */
16
171exports.mkdir = function(path) {
183 return function(next) {
193 fs.mkdir(path, done(next));
20 };
21};
22
23/**
24 * Asynchronously writes data to a file, replacing the file if it already exists.
25 * `data` can be a string or a buffer.
26 *
27 * @param {String} path
28 * @param {Buffer|String} data
29 * @returns {Function} middleware
30 * @see fs#writeFile
31 * @api public
32 */
33
341exports.writeFile = function(path, data) {
356 return function(next) {
366 fs.writeFile(path, data, done(next));
37 };
38};
39
40/**
41 * Asynchronous rmdir(2).
42 *
43 * @param {String} path
44 * @returns {Function} middleware
45 * @see fs#rmdir
46 * @api public
47 */
48
491exports.rmdir = function(path) {
502 return function(next) {
512 fs.rmdir(path, done(next));
52 };
53};
54
55/**
56 * Asynchronous unlink(2).
57 *
58 * @param {String} path
59 * @returns {Function} middleware
60 * @see fs#unlink
61 * @api public
62 */
63
641exports.unlink = function(path) {
656 return function(next) {
666 fs.unlink(path, done(next));
67 };
68};
69
70/**
71 * Run a command in a shell.
72 *
73 * @param {String} the command to run
74 * @returns {Function} middleware
75 * @see child_process#exec
76 * @api public
77 */
78
791exports.exec = function(cmd) {
801 return function(next) {
811 exec(cmd, next);
82 };
83};
84
85/**
86 * Callback generator for middlewares. Throw errors if any.
87 *
88 * @param {Function} next
89 * @returns {Function}
90 * @api public
91 */
92
93function done(next) {
9417 return function(err) {
9517 if (err) throw err;
9617 next();
97 };
98}
99

lib/nixt/plugin.js

100%
9
9
0
LineHitsSource
1/**
2 * Internal dependencies.
3 */
4
51var Runner = require('./runner');
6
7/**
8 * Primitive plugin support.
9 *
10 * It will add the supplied `fn to Runner's prototype.
11 *
12 * Examples:
13 *
14 * Register a single function, could be both middleware or expectation:
15 *
16 * nixt.register('stdoutNotEqual', fn);
17 *
18 * Later on this can be used as you would expect:
19 *
20 * nixt()
21 * .run('ls /tmp')
22 * .stdoutNotEqual('xxx')
23 * .end()
24 *
25 * In case you want to register more than one function at once you may want to pass
26 * an object:
27 *
28 * nixt.register({
29 * name: fn,
30 * otherName: fn2,
31 * etc: etc,
32 * });
33 *
34 * The second example might come handy when developing plugins. Keep in mind that
35 * the plugin system will most certainly change in future version (prior hitting 1.0.0).
36 * The current implementation has some obvious problems like what plugin developers
37 * will do if they happen to use the same function name. Any ideas and suggestions
38 * are more than welcome.
39 *
40 * @param {String|Object} name
41 * @param {Function} fn
42 * @api public
43 */
44
451module.exports = function(name, fn) {
462 var reg = null;
47
482 if (Object(name) !== name) {
491 reg = Object.create(null);
501 reg[name] = fn;
51 } else {
521 reg = name;
53 }
54
552 Object.keys(reg).forEach(function(key) {
563 Runner.prototype[key] = reg[key];
57 });
58};
59

lib/nixt/result.js

100%
7
7
0
LineHitsSource
1/**
2 * Simple value object that contains the result of
3 * `Command`.
4 *
5 * @see Command
6 * @constructor
7 */
8
9function Result(formatter, err, cmd) {
1028 this.err = err;
1128 this.cmd = cmd;
1228 this.stdout = formatter.stdout;
1328 this.stderr = formatter.stderr;
1428 this.code = err ? err.code : 0;
1528 this.killed = err && err.killed;
16}
17
18/**
19 * Primary export.
20 */
21
221module.exports = Result;
23

lib/nixt/runner.js

100%
88
88
0
LineHitsSource
1/**
2 * External dependencies.
3 */
4
51var clone = require('clone');
6
7/**
8 * Internal dependencies.
9 */
10
111var Batch = require('./batch');
121var Command = require('./command');
131var Formatter = require('./formatter');
141var expect = require('./expectations');
151var middlewares = require('./middlewares');
16
17/**
18 * The primary entry point for every Nixt test.
19 * It provides public interface that the users will interact with.
20 * Every `Runner` instance can be cloned and this way one can build
21 * the so called "templates".
22 *
23 * Options:
24 *
25 * - colors: default - true, Strip colors from stdout and stderr when `false`
26 * - newlines: default - true, Strip new lines from stdout and stderr when `false`
27 *
28 * Examples:
29 *
30 * Instantiating the class:
31 *
32 * nixt() // -> Runner
33 * new nixt // -> Runner
34 *
35 * Simple stdout assertion:
36 *
37 * nixt({ colors: false, newlines: false })
38 * .exec('todo clear')
39 * .exec('todo Buy milk')
40 * .run('todo ls')
41 * .stdout('Buy milk')
42 * .end(fn);
43 *
44 * Stdout assertion:
45 *
46 * nixt({ colors: false, newlines: false })
47 * .exec('todo clear')
48 * .run('todo')
49 * .stderr('Please enter a todo')
50 * .end(fn);
51 *
52 * So repeating "todo clear" is simply ugly. You can avoid this by
53 * creating a "template".
54 *
55 * var todo = nixt().before(clearTodos);
56 *
57 * Later on:
58 *
59 * todo.clone().exec...
60 *
61 * For more examples check the "README" file.
62 *
63 * @see Batch
64 * @see Formatter
65 * @see Command
66 * @param {Object} options
67 * @constructor
68 */
69
70function Runner(options) {
7112 if (!(this instanceof Runner)) return new Runner(options);
724 options = options || {};
734 this.options = options;
744 this.formatter = new Formatter({ colors: options.colors, newlines: options.newlines });
754 this.expectations = [];
764 this.batch = new Batch;
774 this.baseCmd = '';
78}
79
80/**
81 * Register a before filter.
82 *
83 * @param {Function} fn
84 * @returns {Runner} for chaining
85 * @see Batch#addBefore
86 * @api public
87 */
88
891Runner.prototype.before = function(fn) {
902 this.batch.addBefore(fn);
912 return this;
92};
93
94/**
95 * Register an after filter.
96 *
97 * @param {Function} fn
98 * @returns {Runner} for chaining
99 * @see Batch#addAfter
100 * @api public
101 */
102
1031Runner.prototype.after = function(fn) {
1045 this.batch.addAfter(fn);
1055 return this;
106};
107
108/**
109 * Set the current working directory for
110 * the command that will be executed.
111 *
112 * @param {String} path
113 * @returns {Runner} for chaining
114 * @api public
115 */
116
1171Runner.prototype.cwd = function(path) {
1183 this.command().cwd(path);
1193 return this;
120};
121
122/**
123 * Specify a base command.
124 *
125 * Very convenient when testing the same executable
126 * again and again.
127 *
128 * @param {String} command
129 * @returns {Runner} for chaining
130 * @api public
131 */
132
1331Runner.prototype.base = function(cmd) {
1341 this.baseCmd = cmd;
1351 return this;
136};
137
138/**
139 * Set environment variable.
140 *
141 * @param {String} key
142 * @param {String} value
143 * @returns {Runner} for chaining
144 * @see Command#env
145 * @api public
146 */
147
1481Runner.prototype.env = function(key, val) {
1492 this.command().env(key, val);
1502 return this;
151};
152
153/**
154 * Specify a command to run.
155 *
156 * @param {String} command
157 * @returns {Runner} for chaining
158 * @see Batch#main
159 * @api public
160 */
161
1621Runner.prototype.run = function(cmd, fn) {
16328 this.command().set(this.baseCmd + cmd);
16428 this.batch.main(this.execFn());
16528 if (fn) this.end(fn);
16628 return this;
167};
168
169/**
170 * Force an execution timeout.
171 *
172 * @param {Number} ms
173 * @returns {Runner} for chaining
174 * @api public
175 */
176
1771Runner.prototype.timeout = function(ms) {
1781 this.command().timeout(ms);
1791 this.expect(expect.time(ms));
1801 return this;
181};
182
183/**
184 * Register a "stdout" expectation.
185 *
186 * @param {Regex|String} pattern
187 * @returns {Runner} for chaining
188 * @api public
189 */
190
1911Runner.prototype.stdout = function(pattern) {
19212 this.expect(expect.stdout(pattern));
19312 return this;
194};
195
196/**
197 * Register a "stderr" expectation.
198 *
199 * @param {Regex|String} pattern
200 * @returns {Runner} for chaining
201 * @api public
202 */
203
2041Runner.prototype.stderr = function(pattern) {
2056 this.expect(expect.stderr(pattern));
2066 return this;
207};
208
209/**
210 * Register an exit code expectation.
211 *
212 * @param {Number} code
213 * @returns {Runner} for chaining
214 * @api public
215 */
216
2171Runner.prototype.code = function(code) {
2184 this.expect(expect.code(code));
2194 return this;
220};
221
222/**
223 * Check if a file or a directory exists.
224 *
225 * @param {String} path
226 * @returns {Runner} for chaining
227 * @api public
228 */
229
2301Runner.prototype.exist = function(path) {
2314 this.expect(expect.exists(path));
2324 return this;
233};
234
235/**
236 * Match the content of a file.
237 *
238 * @param {Regex|String} pattern
239 * @returns {Runner} for chaining
240 * @api public
241 */
242
2431Runner.prototype.match = function(file, pattern) {
2443 this.expect(expect.match(file, pattern));
2453 return this;
246};
247
248/**
249 * Create a new directory.
250 *
251 * @param {String} path
252 * @returns {Runner} for chaining
253 * @api public
254 */
255
2561Runner.prototype.mkdir = function(path) {
2573 this.batch.add(middlewares.mkdir(path));
2583 return this;
259};
260
261/**
262 * Execute a command.
263 *
264 * @param {String} command
265 * @returns {Runner} for chaining
266 * @api public
267 */
268
2691Runner.prototype.exec = function(cmd) {
2701 this.batch.add(middlewares.exec(cmd));
2711 return this;
272};
273
274/**
275 * Create a new file with the given `content`.
276 *
277 * @param {String} path
278 * @param {String} data [optional]
279 * @returns {Runner} for chaining
280 * @api public
281 */
282
2831Runner.prototype.writeFile = function(path, data) {
2846 this.batch.add(middlewares.writeFile(path, data));
2856 return this;
286};
287
288/**
289 * Remove a directory.
290 *
291 * @param {String} path
292 * @returns {Runner} for chaining
293 * @api public
294 */
295
2961Runner.prototype.rmdir = function(path) {
2972 this.batch.add(middlewares.rmdir(path));
2982 return this;
299};
300
301/**
302 * Remove a file.
303 *
304 * @param {String} path
305 * @returns {Runner} for chaining
306 * @api public
307 */
308
3091Runner.prototype.unlink = function(path) {
3106 this.batch.add(middlewares.unlink(path));
3116 return this;
312};
313
314/**
315 * Run the test.
316 *
317 * @param {Function} fn
318 * @returns {Runner} for chaining
319 * @api public
320 */
321
3221Runner.prototype.end = function(fn) {
32330 if (!this.batch.hasMain()) throw new Error('Please provide a command to run. Hint: `nixt#run`');
32428 this.batch.run(fn);
325};
326
327/**
328 * Clone the runner. Give basic support for templates.
329 *
330 * @returns {Runner} clone of the current instance
331 * @api public
332 */
333
3341Runner.prototype.clone = function() {
33526 return clone(this, false);
336};
337
338/**
339 * Register an expectation.
340 *
341 * @param {Function} fn
342 * @api public
343 */
344
3451Runner.prototype.expect = function(fn) {
34630 this.expectations.push(fn);
34730 return this;
348};
349
350/**
351 * Command factory.
352 *
353 * Return the current command (or create a new one
354 * if none exists yet).
355 *
356 * @returns {Command}
357 * @api private
358 */
359
3601Runner.prototype.command = function() {
36162 this._command = this._command || new Command(this.formatter);
36262 return this._command;
363};
364
365/**
366 * Return a function that will execute
367 * the command.
368 *
369 * @returns {Function}
370 * @api private
371 */
372
3731Runner.prototype.execFn = function() {
37428 var self = this;
375
37628 return function(fn) {
37728 self.command().exec(function(result) {
37828 var err = null;
37928 for (var i = 0, len = self.expectations.length; i < len; i++) {
38030 err = self.expectations[i](result);
38139 if (err) break;
382 }
38328 fn(err);
384 });
385 };
386};
387
388/**
389 * Primary export.
390 */
391
3921module.exports = Runner;
393