Skip to content

Instantly share code, notes, and snippets.

@lorenzofox3
Created October 29, 2019 13:59
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lorenzofox3/a8e6a7b080bcccde0045153ba3c42341 to your computer and use it in GitHub Desktop.
Save lorenzofox3/a8e6a7b080bcccde0045153ba3c42341 to your computer and use it in GitHub Desktop.
a small zora like test runner for nodejs
module.exports = testResult => {
const isFailed = testResult.pass === false;
console.log(`${!isFailed ? 'ok' : 'no ok'} - ${testResult.description}`);
if (testResult.error) {
console.log(testResult.error.stack);
if (testResult.error.operator) {
console.log(`operator: ${testResult.error.operator}`);
}
if (testResult.error.expected) {
console.log(`expected: \n ${JSON.stringify(testResult.error.expected, null, 4)}`);
}
if (testResult.error.actual) {
console.log(`actual: \n ${JSON.stringify(testResult.error.actual, null, 4)}`);
}
}
};
const testFunction = require('./test.js');
const reporter = require('./reporter.js');
const createHarness = () => {
const testList = [];
const test = (description, spec) => testFunction(description, spec, testList);
return {
test,
async report() {
for (const t of testList) {
for await (const a of t) {
reporter(a);
}
}
}
};
};
const defaultTestHarness = createHarness();
process.nextTick(() => defaultTestHarness.report());
module.exports = defaultTestHarness;
const testFunction = module.exports = (description, specFunction, testList) => {
let error = null;
let passing = true;
const subTestList = [];
// we return the routine so we can explicitly wait for it to complete (serial tests)
const subTest = (description, fn) => testFunction(description, fn, subTestList).execRoutine;
// eagerly run the test as soon as testFunction is called
const execRoutine = (async function () {
try {
await specFunction({test: subTest});
} catch (e) {
passing = false;
error = e;
}
})();
const testObject = Object.defineProperties({
// we **report** test result with async iterators... in a non blocking way
[Symbol.asyncIterator]: async function* () {
await execRoutine;
for await (const t of subTestList) {
yield* t;// report sub test
passing = passing && t.pass; // mark parent test as failing in case a subtest fails (but don't bubble the error)
}
yield this; // report this test
}
}, {
execRoutine: {value: execRoutine},
error: {
get() {
return error;
}
},
description: {
value: description
},
pass: {
get() {
return passing;
}
}
});
// collect the test in the parent's test list
testList.push(testObject);
return testObject;
};
const assert = require('assert').strict;
const {test} = require('./run.js');
const wait = (time = 1000) => new Promise(resolve => setTimeout(() => resolve(), time));
test(`some failing test`, t => {
assert.deepEqual([1, 2, 3], [1, 2, 4], `array should be equivalent`);
});
test(`some nested test`, t => {
t.test(`inside`, () => {
assert.deepEqual({foo: 'bar'}, {foo: 'bar'}, `should match`);
});
t.test(`failing inside`, () => {
assert.ok(false, `oh noooo`);
});
});
test(`some async test that shows concurrency`, async t => {
let foo = 'bar';
t.test(`nested async`, async t => {
await wait(100);
assert.equal(foo, 'baz', 'see changed value although started before');
foo = 'whatever';
});
t.test(`change foo faster`, t => {
assert.equal(foo, 'bar');
foo = 'baz';
});
});
test(`some serial test`, async t => {
let foo = 'bar';
await t.test('nested inside', async t => {
await wait(100);
assert.equal(foo, 'bar', 'see the initial value of foo');
foo = 'whatever';
});
t.test('run only once "nested inside" has finished', () => {
assert.equal(foo, 'whatever', 'see the changed value');
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment