diff --git a/appveyor.yml b/appveyor.yml index 45afa94..04d60fc 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -42,7 +42,11 @@ on_success: https://git.nfp.is/api/v1/repos/$APPVEYOR_REPO_NAME/releases \ -d "{\"tag_name\":\"v${CURR_VER}\",\"name\":\"v${CURR_VER}\",\"body\":\"Automatic release from Appveyor from ${APPVEYOR_REPO_COMMIT} :\n\n${APPVEYOR_REPO_COMMIT_MESSAGE}\"}") RELEASE_ID=$(echo $RELEASE_RESULT | jq -r .id) - echo "Created release ${RELEASE_ID}" + if [ "$RELEASE_ID" == "null" ]; then + echo $RELEASE_RESULT + else + echo "Created release ${RELEASE_ID}" + fi echo '//registry.npmjs.org/:_authToken=${npmtoken}' > ~/.npmrc echo "Publishing new version to npm" npm publish diff --git a/lib/callback.mjs b/lib/callback.mjs new file mode 100644 index 0000000..d525573 --- /dev/null +++ b/lib/callback.mjs @@ -0,0 +1,33 @@ +export function runWithCallbackSafe(test) { + return new Promise(function(res, rej) { + try { + let cb = function(err) { + if (err) { + return rej(err) + } + res() + } + let safeWrap = function(finish) { + // return a safe wrap support + return function(fun) { + return function(a, b, c) { + try { + fun(a, b, c) + if (finish) { + res() + } + } + catch (err) { + return rej(err) + } + } + } + } + cb.wrap = safeWrap(false) + cb.finish = safeWrap(true) + test.func(cb) + } catch (err) { + rej(err) + } + }) +} \ No newline at end of file diff --git a/lib/eltro.mjs b/lib/eltro.mjs index fdfabfa..688c420 100644 --- a/lib/eltro.mjs +++ b/lib/eltro.mjs @@ -1,4 +1,5 @@ import * as readline from 'readline' +import { runWithCallbackSafe } from './callback.mjs' import { printError } from './cli.mjs' function Group(e, name) { @@ -155,14 +156,7 @@ Eltro.prototype.__runTest = async function(stats, test, prefix = 'Test', child = // If the test requires callback, wrap it in a promise where callback // either resolves or rejects that promise if (checkIsCallback) { - promise = new Promise(function(res, rej) { - test.func(function(err) { - if (err) { - return rej(err) - } - res() - }) - }) + promise = runWithCallbackSafe(test) } else { // Function doesn't require a callback, run it directly promise = test.func() diff --git a/package.json b/package.json index 8f8dd8b..a6dae7f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eltro", - "version": "1.3.1", + "version": "1.3.2", "description": "Eltro is a tiny no-dependancy test framework for node", "main": "index.mjs", "scripts": { diff --git a/test/callback.test.mjs b/test/callback.test.mjs new file mode 100644 index 0000000..697e56f --- /dev/null +++ b/test/callback.test.mjs @@ -0,0 +1,217 @@ +import { runWithCallbackSafe } from '../lib/callback.mjs' +import assert from '../lib/assert.mjs' + +import t from '../lib/eltro.mjs' + +t.describe('runCb()', function() { + t.test('cb() should work normally', async function() { + let called = false + let test = { func: function(cb) { called = true; cb() } } + + await runWithCallbackSafe(test) + + assert.strictEqual(called, true) + }) + + t.test('cb() should work with timeout', async function() { + let called = false + let test = { func: function(cb) { + setImmediate(function() { + called = true; + cb(); + }) + } } + + await runWithCallbackSafe(test) + + assert.strictEqual(called, true) + }) + + t.test('cb() should capture throws outside', async function() { + const assertError = new Error('a') + let test = { func: function(cb) { throw assertError } } + + let err = await assert.isRejected(runWithCallbackSafe(test)) + + assert.strictEqual(err, assertError) + }) + + t.test('cb() should support callback error', async function() { + const assertError = new Error('a') + let test = { func: function(cb) { cb(assertError) } } + + let err = await assert.isRejected(runWithCallbackSafe(test)) + + assert.strictEqual(err, assertError) + }) + + t.test('cb() should support callback error in immediate', async function() { + const assertError = new Error('a') + let test = { func: function(cb) { + setImmediate(function() { + cb(assertError) + }) + } } + + let err = await assert.isRejected(runWithCallbackSafe(test)) + + assert.strictEqual(err, assertError) + }) + + t.test('cb.wrap() should return function and work with timeout', async function() { + let calledFirst = false + let calledSecond = false + let test = { func: function(cb) { + let fun = cb.wrap(function() { + calledSecond = true + cb() + }) + + setImmediate(function() { + calledFirst = true; + fun() + }) + } } + + await runWithCallbackSafe(test) + + assert.strictEqual(calledFirst, true) + assert.strictEqual(calledSecond, true) + }) + + t.test('cb.wrap() should pass arguments correctly', async function() { + let a = 0 + let b = 0 + let c = 0 + let called = false + + let test = { func: function(cb) { + let fun = cb.wrap(function(ina, inb, inc) { + a = ina + b = inb + c = inc + cb() + }) + + setImmediate(function() { + called = true + fun(1, 2, 3) + }) + } } + + await runWithCallbackSafe(test) + + assert.strictEqual(called, true) + assert.strictEqual(a, 1) + assert.strictEqual(b, 2) + assert.strictEqual(c, 3) + + await runWithCallbackSafe(test) + }) + + t.test('cb.wrap() should throw', async function() { + const assertError = new Error('a') + let test = { func: function(cb) { + setImmediate(cb.wrap(function() { + throw assertError + })) + } } + + let err = await assert.isRejected(runWithCallbackSafe(test)) + + assert.strictEqual(err, assertError) + }) + + t.test('cb.wrap() should support nested calls', async function() { + const assertError = new Error('a') + let test = { func: function(cb) { + setImmediate(function() { + setImmediate(cb.wrap(function() { + throw assertError + })) + }) + } } + + let err = await assert.isRejected(runWithCallbackSafe(test)) + + assert.strictEqual(err, assertError) + }) + + + t.test('cb.finish() should return function and work with timeout and finish', async function() { + let calledFirst = false + let calledSecond = false + let test = { func: function(cb) { + let fun = cb.finish(function() { + calledSecond = true + }) + + setImmediate(function() { + calledFirst = true; + fun() + }) + } } + + await runWithCallbackSafe(test) + + assert.strictEqual(calledFirst, true) + assert.strictEqual(calledSecond, true) + }) + + t.test('cb.finish() should pass arguments correctly', async function() { + let a = 0 + let b = 0 + let c = 0 + let called = false + + let test = { func: function(cb) { + let fun = cb.finish(function(ina, inb, inc) { + a = ina + b = inb + c = inc + }) + + setImmediate(function() { + called = true + fun(1, 2, 3) + }) + } } + + await runWithCallbackSafe(test) + + assert.strictEqual(called, true) + assert.strictEqual(a, 1) + assert.strictEqual(b, 2) + assert.strictEqual(c, 3) + + await runWithCallbackSafe(test) + }) + + t.test('cb.finish() should support throw', async function() { + const assertError = new Error('a') + let test = { func: function(cb) { + setImmediate(cb.finish(function() { + throw assertError + })) + } } + + let err = await assert.isRejected(runWithCallbackSafe(test)) + + assert.strictEqual(err, assertError) + }) + + t.test('cb.finish() should support nested throw calls', async function() { + const assertError = new Error('a') + let test = { func: function(cb) { + setImmediate(function() { + setImmediate(cb.finish(function() { + throw assertError + })) + }) + } } + + let err = await assert.isRejected(runWithCallbackSafe(test)) + + assert.strictEqual(err, assertError) + }) +}) \ No newline at end of file diff --git a/test/eltro.test.mjs b/test/eltro.test.mjs index 7c298a9..3c07a45 100644 --- a/test/eltro.test.mjs +++ b/test/eltro.test.mjs @@ -147,6 +147,38 @@ e.test('Eltro should support callback', async function() { assert.strictEqual(assertIsTrue, true) }) +e.test('Eltro should support custom cb with finish wrap', async function() { + let actualRan = 0 + const t = CreateT() + t.begin() + t.describe('', function() { + t.test('a', function(cb) { + setImmediate(() => { + setImmediate(cb.finish(() => { actualRan++; })) + }) + }) + + t.test('b', function(cb) { + setImmediate(() => { + setImmediate(cb.finish(() => { actualRan++; })) + }) + }) + t.test('c', function(cb) { + setImmediate(() => { + setImmediate(cb.finish(() => { actualRan++; })) + }) + }) + t.test('d', function(cb) { + setImmediate(() => { + setImmediate(cb.finish(() => { actualRan++; })) + }) + }) + }) + await t.run() + assert.strictEqual(t.failedTests.length, 0) + assert.strictEqual(actualRan, 4) +}) + e.test('Eltro should support directly thrown errors', async function() { testsWereRun = true const assertError = new Error() diff --git a/test/failure.test.mjs b/test/failure.test.mjs index 2a9fadf..4627d2e 100644 --- a/test/failure.test.mjs +++ b/test/failure.test.mjs @@ -49,3 +49,41 @@ e.test('Eltro should support any value in throws', async function() { assert.ok(t.failedTests[x].error.stack) } }) + +e.test('Eltro should support custom cb with safe wrap', async function() { + let actualRan = 0 + const t = CreateT() + t.begin() + t.describe('', function() { + t.test('a', function(cb) { + setImmediate(() => { + setImmediate(cb.wrap(() => { actualRan++; throw null })) + }) + }) + + t.test('b', function(cb) { + setImmediate(() => { + setImmediate(cb.wrap(() => { actualRan++; throw {} })) + }) + }) + t.test('c', function(cb) { + setImmediate(() => { + setImmediate(cb.wrap(() => { actualRan++; throw { message: 'test' } })) + }) + }) + t.test('d', function(cb) { + setImmediate(() => { + setImmediate(cb.wrap(() => { actualRan++; throw 1234 })) + }) + }) + }) + await t.run() + assert.strictEqual(t.failedTests.length, 4) + + for (let x = 0; x < t.failedTests.length; x++) { + assert.strictEqual(typeof(t.failedTests[x].error), 'object') + assert.ok(t.failedTests[x].error.message) + assert.ok(t.failedTests[x].error.stack) + } + assert.strictEqual(actualRan, t.failedTests.length) +})