2 function delay(fn) { setTimeout(fn, 10); }
4 var result = (function() {
6 var push = function() { [].push.apply(arr, arguments); };
8 reset: function() { arr = []; },
10 pushTaskname: function() { push(this.name); },
11 get: function() { return arr; },
12 getJoined: function() { return arr.join(''); }
16 var requireTask = require.bind(this, '../../lib/util/task.js');
18 exports['new Task'] = {
19 'create': function(test) {
21 var tasklib = requireTask();
22 test.ok(tasklib.create() instanceof tasklib.Task, 'It should return a Task instance.');
27 exports['Helpers'] = {
28 setUp: function(done) {
29 this.task = requireTask().create();
30 this.fn = function(a, b) { return a + b; };
31 this.task.registerHelper('add', this.fn);
34 'Task#registerHelper': function(test) {
37 test.ok('add' in task._helpers, 'It should register the passed helper.');
40 'Task#helper': function(test) {
43 test.strictEqual(task.helper('add', 1, 2), 3, 'It should receive arguments and return a value.');
44 test.throws(function() { task.helper('nonexistent'); }, 'Attempting to execute unregistered handlers should throw an exception.');
46 error: result.pushTaskname
49 test.doesNotThrow(function() { task.helper('nonexistent'); }, 'It should not throw an exception because an error handler is defined.');
50 test.deepEqual(result.get(), [null], 'Non-nested tasks have a null name.');
53 'Task#renameHelper': function(test) {
56 task.renameHelper('add', 'newadd');
57 test.ok('newadd' in task._helpers, 'It should rename the specified helper.');
58 test.equal('add' in task._helpers, false, 'It should remove the previous helper.');
59 test.doesNotThrow(function() { task.helper('newadd'); }, 'It should be accessible by its new name.');
60 test.throws(function() { task.helper('add'); }, 'It should not be accessible by its previous name.');
65 exports['Directives'] = {
66 setUp: function(done) {
67 this.task = requireTask().create();
68 this.task.registerHelper('add', function(a, b) { return Number(a) + Number(b); });
71 'Task#getDirectiveParts': function(test) {
74 test.deepEqual(task.getDirectiveParts('<add>'), ['add'], 'It should split a directive into parts.');
75 test.deepEqual(task.getDirectiveParts('<add:1>'), ['add', '1'], 'It should split a directive into parts.');
76 test.deepEqual(task.getDirectiveParts('<add:1:2>'), ['add', '1', '2'], 'It should split a directive into parts.');
77 test.deepEqual(task.getDirectiveParts('<foo>'), null, 'It should return null if the directive does not match an existing helper.');
78 test.deepEqual(task.getDirectiveParts('<foo:bar>'), null, 'It should return null if the directive does not match an existing helper.');
79 test.deepEqual(task.getDirectiveParts('x<foo>'), null, 'It should return null otherwise.');
80 test.deepEqual(task.getDirectiveParts('<foo>x'), null, 'It should return null otherwise.');
81 test.deepEqual(task.getDirectiveParts('<--arrow!'), null, 'It should return null otherwise.');
84 'Task#directive': function(test) {
87 var fn = function(val) { return '_' + val + '_'; };
88 test.equal(task.directive('foo'), 'foo', 'If a directive is not passed, it should return the passed value.');
89 test.equal(task.directive('foo', fn), '_foo_', 'If a directive is not passed, the value should be passed through the specified callback.');
90 test.equal(task.directive('<foo>'), '<foo>', 'If a directive is passed but not found, it should return the passed value.');
91 test.equal(task.directive('<foo>', fn), '_<foo>_', 'If a directive is passed but not found, the value should be passed through the specified callback.');
92 test.equal(task.directive('<add:1:2>'), 3, 'If a directive is passed and found, it should call the directive with arguments.');
94 task.registerHelper('call_as_helper', function(a, b) {
95 test.ok(!this.directive, 'should not indicate the helper was called as a directive');
96 test.deepEqual(this.args, [1, 2], 'Should be an array of args.');
97 test.deepEqual(this.flags, {'1': true, '2': true}, 'Should be a map of flags.');
100 test.equal(task.helper('call_as_helper', 1, 2), 3, 'Should receive the proper arguments (and return the proper result).');
102 task.registerHelper('call_as_directive', function(a, b) {
103 test.ok(this.directive, 'should indicate the helper was called as a directive');
104 test.deepEqual(this.args, ['1', '2'], 'Should be an array of args.');
105 test.deepEqual(this.flags, {'1': true, '2': true}, 'Should be a map of flags.');
106 return Number(a) + Number(b);
108 test.equal(task.directive('<call_as_directive:1:2>'), 3, 'Should receive the proper arguments (and return the proper result).');
114 setUp: function(done) {
116 this.task = requireTask().create();
117 var task = this.task;
118 task.registerTask('nothing', 'Do nothing.', function() {});
121 'Task#registerTask': function(test) {
123 var task = this.task;
124 test.ok('nothing' in task._tasks, 'It should register the passed task.');
127 'Task#registerTask (alias)': function(test) {
129 var task = this.task;
130 task.registerTask('a', 'Push task name onto result.', result.pushTaskname);
131 task.registerTask('b', 'Push task name onto result.', result.pushTaskname);
132 task.registerTask('c', 'Push task name onto result.', result.pushTaskname);
133 task.registerTask('x', 'a b c');
134 task.registerTask('y', ['a', 'b', 'c']);
135 task.registerTask('z', 'a b nonexistent c');
138 result.push('!' + this.name);
141 test.strictEqual(result.getJoined(), 'abcabc!z', 'The specified tasks should have run, in-order.');
145 task.run('x y z').start();
147 'Task#isTaskAlias': function(test) {
149 var task = this.task;
150 task.registerTask('a', 'nothing', function() {});
151 task.registerTask('b', 'nothing', function() {});
152 task.registerTask('c', 'a b');
153 test.strictEqual(task.isTaskAlias('a'), false, 'It should not be an alias.');
154 test.strictEqual(task.isTaskAlias('c'), true, 'It should be an alias.');
157 'Task#renameTask': function(test) {
159 var task = this.task;
160 task.renameTask('nothing', 'newnothing');
161 test.ok('newnothing' in task._tasks, 'It should rename the specified task.');
162 test.equal('nothing' in task._tasks, false, 'It should remove the previous task.');
163 test.doesNotThrow(function() { task.run('newnothing'); }, 'It should be accessible by its new name.');
164 test.throws(function() { task.run('nothing'); }, 'It should not be accessible by its previous name and throw an exception.');
167 'Task#run (exception handling)': function(test) {
169 var task = this.task;
170 test.doesNotThrow(function() { task.run('nothing'); }, 'Registered tasks should be runnable.');
171 test.throws(function() { task.run('nonexistent'); }, 'Attempting to run unregistered tasks should throw an exception.');
173 error: result.pushTaskname
175 test.doesNotThrow(function() { task.run('nonexistent'); }, 'It should not throw an exception because an error handler is defined.');
176 test.deepEqual(result.get(), [null], 'Non-nested tasks have a null name.');
179 'Task#run (nested, exception handling)': function(test) {
181 var task = this.task;
182 task.registerTask('yay', 'Run a registered task.', function() {
183 test.doesNotThrow(function() { task.run('nothing'); }, 'Registered tasks should be runnable.');
185 task.registerTask('nay', 'Attempt to run an unregistered task.', function() {
186 test.throws(function() { task.run('nonexistent'); }, 'Attempting to run unregistered tasks should throw an exception.');
191 task.run('yay nay').start();
193 'Task#run (signatures, queue order)': function(test) {
195 var task = this.task;
196 task.registerTask('a', 'Push task name onto result.', result.pushTaskname);
197 task.registerTask('b', 'Push task name onto result.', result.pushTaskname);
198 task.registerTask('c', 'Push task name onto result.', result.pushTaskname);
199 task.registerTask('d', 'Push task name onto result.', result.pushTaskname);
200 task.registerTask('e', 'Push task name onto result.', result.pushTaskname);
201 task.registerTask('f', 'Push task name onto result.', result.pushTaskname);
202 task.registerTask('g', 'Push task name onto result.', result.pushTaskname);
205 test.strictEqual(result.getJoined(), 'abcdefg', 'The specified tasks should have run, in-order.');
209 task.run('a').run('b', 'c').run(['d', 'e']).run('f g').start();
211 'Task#run (colon separated arguments)': function(test) {
213 var task = this.task;
214 task.registerTask('a', 'Push task name and args onto result.', function(x, y) { result.push([1, this.name, this.nameArgs, x, y]); });
215 task.registerTask('a:b', 'Push task name and args onto result.', function(x, y) { result.push([2, this.name, this.nameArgs, x, y]); });
216 task.registerTask('a:b:c', 'Push task name and args onto result.', function(x, y) { result.push([3, this.name, this.nameArgs, x, y]); });
219 test.deepEqual(result.get(), [
220 [1, 'a', 'a', undefined, undefined],
221 [2, 'a:b', 'a:b', undefined, undefined],
222 [3, 'a:b:c', 'a:b:c', undefined, undefined],
223 [1, 'a', 'a:x', 'x', undefined],
224 [1, 'a', 'a:x:y', 'x', 'y'],
225 [2, 'a:b', 'a:b:x', 'x', undefined],
226 [2, 'a:b', 'a:b:x:y', 'x', 'y']
227 ], 'Named tasks should be called as-specified if possible, and arguments should be passed properly.');
231 task.run('a a:b a:b:c a:x a:x:y a:b:x a:b:x:y').start();
233 'Task#run (nested tasks, queue order)': function(test) {
235 var task = this.task;
236 task.registerTask('a', 'Push task name onto result and run other tasks.', function() { result.push(this.name); task.run('b e'); });
237 task.registerTask('b', 'Push task name onto result and run other tasks.', function() { result.push(this.name); task.run('c d'); });
238 task.registerTask('c', 'Push task name onto result.', result.pushTaskname);
239 task.registerTask('d', 'Push task name onto result.', result.pushTaskname);
240 task.registerTask('e', 'Push task name onto result and run other tasks.', function() { result.push(this.name); task.run('f'); });
241 task.registerTask('f', 'Push task name onto result.', result.pushTaskname);
242 task.registerTask('g', 'Push task name onto result.', result.pushTaskname);
245 test.strictEqual(result.getJoined(), 'abcdefg', 'The specified tasks should have run, in-order.');
249 task.run('a g').start();
251 'Task#run (async, nested tasks, queue order)': function(test) {
253 var task = this.task;
254 task.registerTask('a', 'Push task name onto result and run other tasks.', function() { result.push(this.name); task.run('b e'); delay(this.async()); });
255 task.registerTask('b', 'Push task name onto result and run other tasks.', function() { result.push(this.name); delay(this.async()); task.run('c d'); });
256 task.registerTask('c', 'Push task name onto result.', result.pushTaskname);
257 task.registerTask('d', 'Push task name onto result.', result.pushTaskname);
258 task.registerTask('e', 'Push task name onto result and run other tasks.', function() { delay(this.async()); result.push(this.name); task.run('f'); });
259 task.registerTask('f', 'Push task name onto result.', result.pushTaskname);
260 task.registerTask('g', 'Push task name onto result.', result.pushTaskname);
263 test.strictEqual(result.getJoined(), 'abcdefg', 'The specified tasks should have run, in-order.');
267 task.run('a g').start();
269 'Task#current': function(test) {
271 var task = this.task;
272 test.deepEqual(task.current, {}, 'Should start empty.');
273 task.registerTask('a', 'Sample task.', function() {
274 test.equal(task.current, this, 'This and task.current should be the same object.');
275 test.equal(task.current.nameArgs, 'a:b:c', 'Should be task name + args, as-specified.');
276 test.equal(task.current.name, 'a', 'Should be just the task name, no args.');
277 test.equal(typeof task.current.async, 'function', 'Should be a function.');
278 test.deepEqual(task.current.args, ['b', 'c'], 'Should be an array of args.');
279 test.deepEqual(task.current.flags, {b: true, c: true}, 'Should be a map of flags.');
283 test.deepEqual(task.current, {}, 'Should be empty again once tasks are done.');
287 task.run('a:b:c').start();
289 'Task#clearQueue': function(test) {
291 var task = this.task;
292 task.registerTask('a', 'Push task name onto result.', result.pushTaskname);
293 task.registerTask('b', 'Push task name onto result.', result.pushTaskname);
294 task.registerTask('c', 'Clear the queue.', function() {
295 result.push(this.name);
296 task.clearQueue().run('f');
298 task.registerTask('d', 'Push task name onto result.', result.pushTaskname);
299 task.registerTask('e', 'Push task name onto result.', result.pushTaskname);
300 task.registerTask('f', 'Push task name onto result.', result.pushTaskname);
303 test.strictEqual(result.getJoined(), 'abcf', 'The specified tasks should have run, in-order.');
307 task.run('a b c d e').start();
309 'Task#mark': function(test) {
311 var task = this.task;
312 task.registerTask('a', 'Explode.', function() {
313 throw task.taskError('whoops.');
315 task.registerTask('b', 'This task should never run.', result.pushTaskname);
316 task.registerTask('c', 'This task should never run.', result.pushTaskname);
318 task.registerTask('d', 'Push task name onto result.', result.pushTaskname);
319 task.registerTask('e', 'Explode.', function() {
320 throw task.taskError('whoops.');
322 task.registerTask('f', 'This task should never run.', result.pushTaskname);
324 task.registerTask('g', 'Push task name onto result.', result.pushTaskname);
325 task.registerTask('h', 'Push task name onto result.', result.pushTaskname);
326 task.registerTask('i', 'Explode.', function() {
327 throw task.taskError('whoops.');
330 task.registerTask('j', 'Run a task and push task name onto result.', function() {
332 result.push(this.name);
334 task.registerTask('k', 'Explode.', function() {
335 throw task.taskError('whoops.');
337 task.registerTask('l', 'This task should never run.', result.pushTaskname);
339 task.registerTask('m', 'Push task name onto result.', result.pushTaskname);
340 task.registerTask('n', 'Run a task and push task name onto result.', function() {
342 result.push(this.name);
344 task.registerTask('o', 'Explode.', function() {
345 throw task.taskError('whoops.');
348 task.registerTask('p', 'Push task name onto result.', result.pushTaskname);
352 result.push('!' + this.name);
353 task.clearQueue({untilMarker: true});
356 test.strictEqual(result.getJoined(), '!ad!egh!ij!kmn!op', 'The specified tasks should have run, in-order.');
360 task.run('a b c').mark().run('d e f').mark().run('g h i').mark().run('j l').mark().run('m n').mark().run('p').mark().start();
362 'Task#requires': function(test) {
364 var task = this.task;
365 task.registerTask('notrun', 'This task is never run.', function() {});
366 task.registerTask('a', 'Push task name onto result, but fail.', function() {
367 result.push(this.name);
370 task.registerTask('b', 'Push task name onto result, but fail.', function() {
371 delay(this.async().bind(this, false));
372 result.push(this.name);
374 task.registerTask('c', 'Succeed.', result.pushTaskname);
375 task.registerTask('d', 'Succeed.', result.pushTaskname);
376 task.registerTask('e', 'Succeed because all required tasks ran and succeeded.', function() {
377 task.requires('c d');
378 result.push(this.name);
380 task.registerTask('x', 'Fail because a required task never ran.', function() {
381 task.requires('c notrun d');
382 result.push(this.name);
384 task.registerTask('y', 'Fail because a synchronous required task has failed.', function() {
385 task.requires('a c d');
386 result.push(this.name);
388 task.registerTask('z', 'Fail because an asynchronous required task has failed.', function() {
389 task.requires('b c d');
390 result.push(this.name);
394 result.push('!' + this.name);
397 test.strictEqual(result.getJoined(), 'a!ab!bcde!x!y!z', 'Tasks whose requirements have failed or are missing should not run.');
401 task.run('a b c d e x y z').start();
405 exports['Task#parseArgs'] = {
406 setUp: function(done) {
407 var task = requireTask().create();
408 this.parseTest = function() {
409 return task.parseArgs(arguments);
413 'single task string': function(test) {
415 test.deepEqual(this.parseTest('foo'), ['foo'], 'string should be split into array.');
418 'multiple task string': function(test) {
420 test.deepEqual(this.parseTest('foo bar baz'), ['foo', 'bar', 'baz'], 'string should be split into array.');
423 'arguments': function(test) {
425 test.deepEqual(this.parseTest('foo', 'bar', 'baz'), ['foo', 'bar', 'baz'], 'arguments should be converted to array.');
428 'array': function(test) {
430 test.deepEqual(this.parseTest(['foo', 'bar', 'baz']), ['foo', 'bar', 'baz'], 'passed array should be used.');
433 'object': function(test) {
436 test.deepEqual(this.parseTest(obj), [obj], 'single object should be returned as array.');
439 'nothing': function(test) {
441 test.deepEqual(this.parseTest(), [], 'should return an empty array if nothing passed.');