Commit 4ffbf5e8 authored by Daniel Shumway's avatar Daniel Shumway

test: refactor and expand tests

- will be useful for writing documentation in the future
- doesn't completely update tape tests
parent ff93f601
Pipeline #10218845 passed with stage
in 28 seconds
var Distilled = require('Distilled');
/*
* Attach singleton since that's all we need
* In larger libraries, you'd probably want to build a more complicated harness
*/
Distilled.suite = new Distilled();
console.log('Distilled self-test: ');
require('./distilled/test.js');
require('./distilled/then.js');
require('./distilled/callback.js');
require('./distilled/assertions.js');
require('./distilled/recursion.js');
require('./distilled/reporter.js');
process.on('exit', function (err) {
console.log();
if (process.exitCode === 1) {
console.log('Done: test suite failed!');
} else {
console.log('Done: test suite passed');
}
console.log();
});
var Distilled = require('Distilled');
var assert = require('assert');
var suite = Distilled.suite;
function deferred () {
var result = {};
result.promise = new Promise(function (res, rej) {
result.resolve = res;
result.reject = rej;
var show = require('../helpers/show.js');
show('The `assert` method: ', function (console) {
var suite = new Distilled(function () {});
console.log('Tests/suites expose an `assert` method: ', typeof suite.assert === 'function');
console.log('Calling assert returns a promise: ', suite.assert() instanceof Promise);
});
show('Basic assertion types: ', function (console) {
var suite = new Distilled(function () {});
suite.assert(null).then(function () {
console.log('Asserting `null` passes.');
});
return result;
}
/*
* BASIC TEST RESOLUTION
*----------------------------------------------------------
*
* This checks that logic for tests passing/failing works correctly
* It also does shallow tests for recursion
*
* Ways to resolve a test include:
* - passing nothing
* - passing a (failed) promise
* - passing false
*
* If a function or a resolved promise are passed:
* - the returned/resolved result is recursively evaluated with the above rules
*/
suite.test('basic types', function () {
var def = deferred();
var _suite = new Distilled(function(test) {
if (test === tests.reject) {
def.resolve(assertion);
}
suite.assert(undefined).then(function () {
console.log('Asserting `undefined` passes.');
});
var tests = {
null: _suite.test('null', null),
undefined: _suite.test('undefined', undefined),
true: _suite.test('true', true),
false: _suite.test('false', false),
reject: _suite.test('reject', Promise.reject('error'))
};
function assertion () {
/**
* ``undefined`` or ``null``:
*
* ```js
* suite.test('', null); //passes
* suite.test('', undefined); //passes
* suite.test(''); //passes
* ```
*/
this.test('undefined', function () {
assert.strictEqual(tests.null.status, Distilled.STATUS.PASSED, 'Accept `null`');
assert.strictEqual(tests.undefined.status, Distilled.STATUS.PASSED, 'Accept `undefined`');
});
suite.assert(true).then(function () {
console.log('Asserting `true` passes.');
});
/**
* A Boolean:
*
* ```js
* suite.test('', true); //passes
* suite.test('', false); //fails
* ```
*/
this.test('boolean', function () {
assert.strictEqual(tests.true.status, Distilled.STATUS.PASSED, 'Accept `true`');
assert.strictEqual(tests.false.status, Distilled.STATUS.FAILED, 'Reject `false`');
});
suite.assert(false).catch(function () {
console.log('Asserting `false` fails.');
});
});
/**
* A Promise:
*
* ```js
* suite.test('', Promise.reject('error')); //fails (with correct error)
*```
*/
this.test('rejected promises', function () {
assert.strictEqual(tests.reject.status, Distilled.STATUS.FAILED, 'Reject rejected promises');
assert.strictEqual(tests.reject.error, 'error', '`test.error` is the rejected promise\'s error');
});
}
show('Recursion using functions: ', function (console) {
var suite = new Distilled(function () {});
return def.promise;
});
suite.assert(function () {
console.log('functions passed into assert are called.');
}).then(function () {
console.log('Because the function returned `undefined`, the assertion passed.');
});
suite.assert(function () {
return false;
}).catch(function () {
console.log('Because the function returned `false`, it failed.');
});
suite.test('advanced recursion', function () {
var def = deferred();
var err = new Error('An exception!');
suite.assert(function () {
throw err;
}).catch(function (result) {
console.log('If an exception is thrown, it gets intercepted as a failure: ', result === err);
});
var _suite = new Distilled(function (test) {
if (test === _func_exception) {
def.resolve(assertion);
}
}, { attachPostResolve : true });
suite.assert(function (test) {
console.log('Assertion context is passed into functions: ', test === suite);
console.log('Assertion context is set on `this`: ', this === suite);
});
});
var _res_undefined = _suite.test('res undefined', Promise.resolve()),
_res_false = _suite.test('res false', Promise.resolve(false)),
show('Recursion using promises: ', function (console) {
var suite = new Distilled(function () {});
_func_undefined = _suite.test('func undefined', function () {}),
_func_false = _suite.test('func false', function () { return false; }),
_func_exception = _suite.test('func exception', function () { throw 'error'; });
suite.assert(Promise.resolve()).then(function () {
console.log('Because the promise resolved to `undefined`, the assertion passed.');
});
function assertion () {
this.test('promises', function () {
assert.strictEqual(_res_undefined.status, Distilled.STATUS.PASSED, 'Promise can resolve to undefined');
assert.strictEqual(_res_false.status, Distilled.STATUS.FAILED, 'Promise can resolve to boolean');
suite.assert(Promise.reject()).catch(function () {
console.log('Because the promise was rejected, the assertion failed.');
});
this.test('functions', function () {
var def = deferred();
suite.assert(Promise.resolve(false)).catch(function () {
console.log('Because the promise resolved to `false` the assertion failed.');
});
});
var _res_function = _suite.test('res function', function (_test) {
assert.strictEqual(_test, _res_function, 'resulting function is passed test');
assert.strictEqual(this, _res_function, '`this` is set to the resulting test/suite');
show('Advanced recursion: ', function (console) {
var suite = new Distilled(function () {});
def.resolve();
});
suite.assert(function () {
return Promise.resolve(function () {
return Promise.resolve(function (test) {
console.log('Promises and functions can be infinitely nested.');
console.log('Nested functions still have the correct context.', test === suite && this === suite);
return def.promise;
return Promise.resolve(false);
});
});
}).catch(function () {
console.log('Because the nested promises and functions eventually returned false, the test failed.');
});
this.test('functions', function () {
assert.strictEqual(_func_undefined.status, Distilled.STATUS.PASSED, 'Function can resolve to undefined');
assert.strictEqual(_func_false.status, Distilled.STATUS.FAILED, 'Function can resolve to boolean');
assert.strictEqual(_func_exception.status, Distilled.STATUS.FAILED, 'Throwing an error causes the test to fail');
assert.strictEqual(_func_exception.error, 'error', '`test.error` is set to the thrown error');
this.test('context', function () {
var def = deferred();
suite.assert(function () {
return function () {
return function () {
return Promise.resolve(
Promise.resolve(function () {
return Promise.resolve(function () {
console.log('Promises can be nested in promises and functions in functions.');
});
})
);
};
};
}).then(function () {
console.log('Because the assertion eventually resolved to `undefined`, it passed.');
});
var _ctx = _suite.test('ctx', function (_test) {
assert.strictEqual(_test, _ctx, 'function is passed test');
assert.strictEqual(this, _ctx, '`this` is set to the resulting test/suite');
});
def.resolve();
});
show('Bug #9: ', function (console) {
var suite = new Distilled(function () {});
return def;
});
var error = new Error('error');
suite.assert(function () {
return Promise.resolve(function () {
throw error;
});
}
return def.promise;
}).catch(function (err) {
console.log('Exceptions from recursively called functions are still thrown: ', err === error);
});
});
......@@ -12,6 +12,10 @@ function deferred () {
return result;
}
var Distilled = require('Distilled');
var show = require('../helpers/show.js');
/*
* CALLBACK TESTS
*-----------------------------------------------------------
......@@ -25,144 +29,54 @@ function deferred () {
* - but, if you do understand Promises, that will probably make you better
* - tests are executed as promises, and when they resolve, the callback is called
*/
suite.test('Basic callback', function (test) {
var def = deferred();
var _suite = new Distilled(function (_test) {
def.resolve(assertion.bind(this, _test));
show('The callback', function (console) {
var suite = new Distilled(function (test) {
console.log('Callback is passed the finished test/suite as a parameter:', test === suite);
console.log('`this` is set to the finished test/suite:', this === suite);
});
function assertion (_test) {
var that = this;
test.test('Callback references', function () {
assert.strictEqual(_test, _suite, 'Callback is passed the resulting test/suite as a parameter');
assert.strictEqual(that, _suite, '`this` is set to the resulting test/suite');
});
}
return def.promise;
});
suite.test('Multiple tests', function () {
var def = deferred(),
calls = [],
tests = [];
show('Callback on multiple/chained tests', function (console) {
var calls = [];
var suite = new Distilled(function (test) {
calls.push(test.label);
var _suite = new Distilled(function () {
calls.push(this);
if (calls.length === tests.length) {
def.resolve(assertion);
if (test === suite) {
console.log('All tests/suites called in the correct order:', '_a-_b-a-b-A-B-' === calls.join('-'));
}
});
tests.push(_suite.test('A'));
tests.push(_suite.test('B'));
tests.push(_suite);
function assertion () {
this.test('Correct test/suite passed into each callback', function () {
assert.deepEqual(calls, tests);
});
}
return def.promise;
suite.test('A').test('a').test('_a');
suite.test('B').test('b').test('_b');
});
suite.test('Nested tests', function () {
var def = deferred(),
calls = [],
tests = [];
var _suite = new Distilled(function () {
calls.push(this);
if (calls.length === tests.length) {
def.resolve(assertion);
show('Test status within callbacks', function (console) {
var suite = new Distilled(function (test) {
if (test === passed) {
console.log('Labels set correctly for passed tests:', test.label === 'passed');
console.log('`status` is set to `STATUS.PASSED`:', test.status === Distilled.STATUS.PASSED);
console.log('there is no error:', test.error == null);
}
});
var A = _suite.test('A'),
B = A.test('B'),
C = B.test('C');
tests.push(C);
tests.push(B);
tests.push(A);
tests.push(_suite);
function assertion () {
this.test('Correct test/suite passed into each callback', function () {
assert.deepEqual(calls, tests);
});
}
return def.promise;
});
suite.test('Test statuses are set correctly', function () {
this.test('for passed tests', function () {
var def = deferred(),
pass;
var _suite = new Distilled(function () {
if (this === pass) {
def.resolve();
}
});
pass = _suite.test('A');
function assertion () {
assert.strictEqual(pass.label, 'A');
assert.strictEqual(pass.status, Distilled.STATUS.PASSED, '`test.status` is set to `STATUS.PASSED`');
assert.equal(pass.error, null);
if (test === failed) {
console.log('Labels set correctly for failed tests:', test.label === 'failed');
console.log('`status is set to `STATUS.FAILED`:', test.status === Distilled.STATUS.FAILED);
console.log('Error is set:', test.error === error);
}
return def.promise;
});
this.test('for failed tests', function () {
var def = deferred(),
fail,
error = new Error('error');
var _suite = new Distilled(function () {
if (this === fail) {
def.resolve(assertion);
}
});
fail = _suite.test('A', Promise.reject(error));
function assertion () {
assert.strictEqual(fail.label, 'A');
assert.strictEqual(fail.status, Distilled.STATUS.FAILED, '`test.status` is set to `STATUS.FAILED`');
assert.strictEqual(fail.error, error, '`test.error` is set to the error the test failed with');
}
return def.promise;
});
var passed = suite.test('passed', true);
var error = new Error('error');
var failed = suite.test('failed', Promise.reject(error));
});
suite.test('Include skipped tests', function () {
var def = deferred(),
calls = [];
var _suite = new Distilled(function () {
calls.push(this);
if (this === _b) {
def.resolve(assertion);
show('Skipped tests are logged (if already attached)', function (console) {
var suite = new Distilled(function (test) {
if (test === a) {
console.log('Status is set to `STATUS.SKIPPED`:', test.status === Distilled.STATUS.SKIPPED);
}
});
var _a = _suite.test('A', false).test('a').test('_a'),
_b = _suite.test('B').test('b').test('_b');
function assertion () {
assert.strictEqual(calls.length, 7, 'reporter fires for all tests');
}
return def.promise;
var A = suite.test('A', false),
a = A.test('a');
});
var Distilled = require('Distilled');
var assert = require('assert');
var suite = Distilled.suite;
function deferred () {
var result = {};
result.promise = new Promise(function (res, rej) {
result.resolve = res;
result.reject = rej;
});
return result;
}
/*
* RESOLUTION RECURSION
*-------------------------------------------------
* Short tests to provide some evidence that resolution recursion is actually infinite
*/
suite.test('Recursively resolving function results', function () {
var def = deferred();
var _suite = new Distilled(function () {
if (this === _b) {
def.resolve(assertion);
}
});
var _a = _suite.test('A', function () {
return function () {
return function () {
return function () {
return function () {
return true;
};
};
};
};
});
var _b = _suite.test('B', function () {
return function () {
return function () {
return function () {
return function () {
return false;
};
};
};
};
});
function assertion () {
assert.strictEqual(_a.status, Distilled.STATUS.PASSED, 'nested functions pass correctly');
assert.strictEqual(_b.status, Distilled.STATUS.FAILED, 'nested functions fail correctly');
}
return def.promise;
});
suite.test('Recursively resolving promise results', function (t) {
var def = deferred();
var _suite = new Distilled(function () {
if (this === _b) {
def.resolve(assertion);
}
});
var _a = _suite.test('A', Promise.resolve(
Promise.resolve(
Promise.resolve(
Promise.resolve(
Promise.resolve(true)
)
)
)
));
var _b = _suite.test('B', Promise.resolve(
Promise.resolve(
Promise.resolve(
Promise.resolve(
Promise.resolve(false)
)
)
)
));
function assertion () {
assert.strictEqual(_a.status, Distilled.STATUS.PASSED, 'nested promises pass correctly');
assert.strictEqual(_b.status, Distilled.STATUS.FAILED, 'nested promises fail correctly');
}
return def.promise;
});
suite.test('Recursively resolving "weird" results', function () {
var def = deferred();
var _suite = new Distilled(function () {
if (this === _b) {
def.resolve(assertion);
}
});
var _a = _suite.test('A', function () {
return Promise.resolve(function () {
return Promise.resolve(function () {
return true;
});
});
});
var _b = _suite.test('B', function () {
return Promise.resolve(function () {
return Promise.resolve(function () {
return false;
});
});
});
function assertion () {
assert.strictEqual(_a.status, Distilled.STATUS.PASSED, 'nested promises pass correctly');
assert.strictEqual(_b.status, Distilled.STATUS.FAILED, 'nested promises fail correctly');
}
return def.promise;
});
suite.test('Exceptions from functions within promises (#9)', function () {
var def = deferred();
var _suite = new Distilled(function () {
if (this === _a) {
def.resolve(assertion);
}
});
var _a = _suite.test('A', function () {
return Promise.resolve(function () {
throw 'error';
});
});
function assertion () {
assert.strictEqual(_a.status, Distilled.STATUS.FAILED, 'Exceptions from recursively called functions are caught correctly');
}
return def.promise;
});
var Distilled = require('Distilled');
var assert = require('assert');
var suite = Distilled.suite;
var reporter = Distilled.REPORTERS.default;
function deferred () {
var result = {};
result.promise = new Promise(function (res, rej) {
result.resolve = res;
result.reject = rej;
});
return result;
}
var output;
function mock (setup, callback) {
return function (t) {
output = ''; //Mock
var write = process.stdout.write;
process.stdout.write = function (message) {
output = output + message;
};
var processCode = process.exitCode;
setup.call(this, t);
process.stdout.write = write; //Unmock
callback.call(this, t);
process.exitCode = processCode; //Final unmock
};
};
/*
* DEFAULT REPORTER
*------------------------------------
* A limited test of the default reporter (only deals with node side of things)
*
* - temporarily suppresses output while supporter is running (console.log will not work)
* - these tests don't go super in-depth, because honestly there's not a ton of risk here.
*/
suite.test('Root test is skipped', mock(function () {
var test = {};
reporter.call(test, test);
}, function () {
assert.strictEqual(output, '', 'Root test is skipped');
}));
suite.test('Passed tests', mock(function () {
var test = {
status : Distilled.STATUS.PASSED,
parent : {},
};
reporter.call(test, test);
}, function () {
assert.strictEqual(output, '.', 'Passed tests are logged as `.`');
}));
suite.test('Failed tests', mock (function () {
var test = {
status : Distilled.STATUS.FAILED,
label : 'child',
error : {
expected : 'expected',
actual : 'actual',
stack : 'stack'
},
parent : {
label : 'parent',
parent : {}
}
};
reporter.call(test, test);
}, function () {
assert.strictEqual(output,
'X\n\n()(parent)(child) failed!\nExpected: expected\nActual: actual\nstack\n\n',
'Failed tests write the full error');
assert.strictEqual(process.exitCode, 1, 'Failed tests set `process.exitCode` to 1');
}));