Browse Source

[feature] Added support for before and after hooks

0.7.0-breaking-rewrite
Muthu Kumar 6 years ago
parent
commit
f81adb1554
  1. 1
      .eslintrc.js
  2. 56
      gunner/index.js
  3. 72
      gunner/lib/runTests.js
  4. 53
      package.json
  5. 3836
      shrinkwrap.yaml
  6. 4
      util/helpers.js

1
.eslintrc.js

@ -25,5 +25,6 @@ module.exports = {
"linebreak-style": [ "error", "unix" ], "linebreak-style": [ "error", "unix" ],
"semi": [ "error", "always" ], "semi": [ "error", "always" ],
"eqeqeq": [ "error", "always", {"null": "ignore"} ], "eqeqeq": [ "error", "always", {"null": "ignore"} ],
"no-global-assign": 0,
} }
}; };

56
gunner/index.js

@ -1,5 +1,9 @@
'use strict';
const { log } = console; const { log } = console;
Promise = require('bluebird');
const _runTests = require('./lib/runTests'); const _runTests = require('./lib/runTests');
const _expect = require('./lib/expect'); const _expect = require('./lib/expect');
@ -8,12 +12,30 @@ const { stringify, hasProp } = require('../util/helpers');
class Gunner { class Gunner {
constructor (options = {}) { constructor (options = {}) {
this.tests = []; this.__hooks__ = {
before: {
'@start': [],
'@end': [],
'*': [],
},
after: {
'*': [],
},
};
this.__state__ = [];
this.__tests__ = [];
this.name = options.name; this.name = options.name;
} }
test (description, test) { test (description, test) {
this.tests.push({ const existing = (
this.__tests__
.find(x => x.description === description)
);
if (existing)
throw new Error(`Test '${description}' already exists!`);
this.__tests__.push({
description, description,
test: () => { test: () => {
try { try {
@ -24,11 +46,39 @@ class Gunner {
} }
}, },
}); });
return this;
}
before (description, run) {
const hook = {
description,
run,
};
this.__hooks__.before[description]
? this.__hooks__.before[description].push(hook)
: this.__hooks__.before[description] = [ hook ];
return this;
}
after (description, run) {
const hook = {
description,
run,
};
this.__hooks__.after[description]
? this.__hooks__.after[description].push(hook)
: this.__hooks__.after[description] = [ hook ];
return this;
} }
run (options = {}) { run (options = {}) {
const shouldLog = (hasProp(options)('log') && options.log) || !(hasProp(options)('log')); const shouldLog = (hasProp(options)('log') && options.log) || !(hasProp(options)('log'));
return _runTests(this.tests) return _runTests(this)
.then(results => { .then(results => {
if (shouldLog) { if (shouldLog) {
const success = results.filter(r => r.result === 'pass'); const success = results.filter(r => r.result === 'pass');

72
gunner/lib/runTests.js

@ -3,27 +3,65 @@
const { isPromise } = require('../../util/helpers'); const { isPromise } = require('../../util/helpers');
const { pass, fail } = require('./constants'); const { pass, fail } = require('./constants');
const runTests = tests => Promise.all(tests.map(each => { const runTests = instance => {
const pred = each.test(); const beforeAll = () => Promise.map(
instance.__hooks__.before['@start'],
hook => hook.run(),
);
/* There are 4 different cases at play: const beforeEvery = () => Promise.mapSeries(
1. A plain expect() is returned. instance.__hooks__.before['*'] || [],
2. An array of [ expect() ] is returned hook => hook.run(),
3. A plain expect() is wrapped in a promise );
4. An array of [ expect() ] is wrapped in a promise.
Here we normalise all of them into something we can process */
if (!isPromise(pred) && !(pred && isPromise(pred[0]))) const runner = () => Promise.mapSeries(instance.__tests__, each => {
throw new Error(`Malformed test '${each.description}'`);
const toTest = Array.isArray(pred)
? Promise.all(pred)
: pred.then(x => Array.isArray(x) ? Promise.all(x) : x);
return toTest const beforeThis = () => Promise.mapSeries(
.then(() => ({ description: each.description, result: pass })) instance.__hooks__.before[each.description] || [],
.catch(e => ({ description: each.description, result: fail, error: e })); hook => hook.run(),
);
})); const afterThis = () => Promise.mapSeries(
instance.__hooks__.after[each.description] || [],
hook => hook.run(),
);
return beforeEvery().then(() => beforeThis()).then(() => {
const pred = each.test();
/* There are 4 different cases at play:
1. A plain expect() is returned.
2. An array of [ expect() ] is returned
3. A plain expect() is wrapped in a promise
4. An array of [ expect() ] is wrapped in a promise.
Here we normalise all of them into something we can process */
if (!isPromise(pred) && !(pred && isPromise(pred[0])))
throw new Error(`Malformed test '${each.description}'`);
const toTest = Array.isArray(pred)
? Promise.all(pred)
: pred.then(x => Array.isArray(x) ? Promise.all(x) : x);
return toTest
.then(() => ({ description: each.description, result: pass }))
.catch(e => ({ description: each.description, result: fail, error: e }));
})
.then(result => afterThis().then(() => result));
});
const afterAll = () => Promise.mapSeries(
instance.__hooks__.before['@end'],
hook => hook.run(),
);
return beforeAll()
.then(() => runner())
.then(results => afterAll().then(() => results));
};
module.exports = runTests; module.exports = runTests;

53
package.json

@ -1,28 +1,29 @@
{ {
"name": "@klenty/gunner", "name": "@klenty/gunner",
"version": "0.2.5", "version": "0.3.0",
"description": "Zero magic, fast test-runner and assertion framework. No magic globals.", "description": "Zero magic, fast test-runner and assertion framework. No magic globals.",
"main": "index.js", "main": "index.js",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/vengatkrishnaraj/gunner.git" "url": "git+https://github.com/klenty/gunner.git"
}, },
"keywords": [ "keywords": [
"klenty", "klenty",
"gunner", "gunner",
"test", "test",
"tdd", "tdd",
"unit testing" "unit testing"
], ],
"author": "Muthu Kumar <@MKRhere> (https://mkr.pw)", "author": "Muthu Kumar <@MKRhere> (https://mkr.pw)",
"license": "MIT", "license": "MIT",
"bugs": { "bugs": {
"url": "https://github.com/vengatkrishnaraj/testsuite/issues" "url": "https://github.com/klenty/gunner/issues"
}, },
"homepage": "https://github.com/vengatkrishnaraj/testsuite#readme", "homepage": "https://github.com/klenty/gunner#readme",
"dependencies": { "dependencies": {
"@codefeathers/iseq": "^1.2.1", "@codefeathers/iseq": "^1.2.1",
"eslint": "^5.2.0", "bluebird": "^3.5.1",
"fs-extra": "^7.0.0" "eslint": "^5.2.0",
} "json-stringify-safe": "^5.0.1"
}
} }

3836
shrinkwrap.yaml

File diff suppressed because it is too large

4
util/helpers.js

@ -1,3 +1,5 @@
const stringify = require('json-stringify-safe');
module.exports = { module.exports = {
/* Returns true if a promise is passed */ /* Returns true if a promise is passed */
@ -48,7 +50,7 @@ module.exports = {
/* Stringifies object or coerces to string */ /* Stringifies object or coerces to string */
stringify : obj => stringify : obj =>
typeof obj === 'object' typeof obj === 'object'
? (obj.stack || JSON.stringify(obj)) ? (obj.stack || stringify(obj))
: obj, : obj,
/* Short circuits with given value on pred. Else calls function */ /* Short circuits with given value on pred. Else calls function */

Loading…
Cancel
Save