From 8b2daee21f09635f2f41cf5474eb09616d7af3a1 Mon Sep 17 00:00:00 2001 From: Geoff Doty Date: Sat, 7 Apr 2018 04:07:58 -0400 Subject: [PATCH] reworked assertions to use an expect BDD-like --- README.md | 61 +++++++++++++++++++-------- dist/testit.js | 102 +++++++++++++++++++++++++++++++++++++++++++++ dist/testit.min.js | 2 + src/testit.js | 73 +++++++++++++++++++++++++------- src/testit.min.js | 2 - test/index.spec.js | 54 +++++++++++++----------- 6 files changed, 234 insertions(+), 60 deletions(-) create mode 100644 dist/testit.js create mode 100644 dist/testit.min.js delete mode 100644 src/testit.min.js diff --git a/README.md b/README.md index 09b4a34..b6d6343 100644 --- a/README.md +++ b/README.md @@ -2,17 +2,22 @@ > A minimalistic testing library -**Test.it** is a small testing library for people that want to live in code, not tests. No over engineering here. Inspired by the simplicity of libraries like [Tape](https://github.com/substack/tape), but the implementation ideas of [TinyTest](https://github.com/joewalnes/jstinytest) +**Test.it** is a small testing library for people that want to live in code, not in tests. No over engineering here. Inspired by the simplicity of libraries like [Tape](https://github.com/substack/tape),but the implementation ideas of things like [Expect](https://github.com/Automattic/expect.js) and [TinyTest](https://github.com/joewalnes/jstinytest) + +This is probally not a *cure-all* testing solution, if you want something more robust checkout [Jasmine](), [Tape]() or [Mocha]() -- this is to... + +**Test small things, with small things** ### Features - Works in the Browser - Works with CommonJS (aka NodeJS) -- Less than 100 lines +- Barely over a 100 lines - Single File - No Dependicies - 2kb footprint (*before gzip*) -- Extend with custom reporters +- Extend with custom reporters +- Has an Expect-like style BDD assertions **No Bloat Here!** @@ -25,10 +30,10 @@ By default, you can run your tests like ```js test.it({ 'my passing test': function() { - test.pass(); + test.expects().to.pass(); }, 'my failing test': function() { - test.fail('just wanted to fail fast'); + test.expects().to.fail('just wanted to fail fast'); } }).run(); ``` @@ -46,9 +51,11 @@ test.it({ A `+` will proceed test lines that pass and a `-` for those that fail, the trace back `file:line` is included after the failing test proceeded by `- -` +> NOTE: API still in flux, and may change to closer match TAP + ### Optional Next -`test.it` `.run()` method provides an optional `next` function parameter that will return the results as an `object` for you to process *however* you like +`test.it` `.run()` method provides an optional `next` function parameter that passes the results as an `object` for you to process *however* you like. such as a custom runner For Example... @@ -81,21 +88,39 @@ From this object you can easily find the number of tests ran `pass.length`, numb ## Methods -To stay minimal, `test.it` only provides 7 methods, 5 for assertion, 1 to capture tests -and 1 to run tests +To stay minimal, `test.it` only has 3 core functions: +- `it` to capture your tests +- `run` to execute yours tests +- and `expects` to write your test assertions -| Methods | Description | -| ------------------------------- | --------------------------------------- | -| `test.it(tests)` | captures test object | -| `test.it(tests).run(next)` | runs tests w/ optional processing | -| `test.pass()` | pass test | -| `test.fail(message)` | fails test with message | -| `test.exists(value)` | check if value exists | -| `test.assert(expected, actual)` | evaluates results using `==` | -| `test.equals(expected, actual)` | evaluates results using `===` | +While you can use your own assertion library, the included `expects` provides the following methods for writing your tests: + +| Methods | Description | +| --------------------------------- | --------------------------------------- | +| `.expects(tests).to.exist()` | truthy evalution if value exists | +| `.expects().to.pass()` | pass test | +| `.expects().to.fail(message)` | fails test with message | +| `.expects(this).to.equal(that)` | strictly equal evaluation using `===` | +| `.expects(this).to.be.like(that)` | loose evaluation using `==` | +| `.expects(123).to.be.a('number')` | check typeof value (`.a()` or `.an()`) | > NOTE: wish `eval` was not so evil, `assert(expression, message)` would be ideal +if you want to shorten test typing try + + let expect = test.expects; + +putting that above your tests will allow you to write like + +```js +test.it({ + "my test should work": function() { + expect().to.pass(); + } +}); + +``` + ## Support Please open [an issue](https://github.com/n2geoff/testit/issues/new) for support. @@ -106,5 +131,5 @@ Anyone is welcome to contribute, however, if you decide to get involved, please ## License -[MIT](LICENSE). +[MIT](LICENSE) diff --git a/dist/testit.js b/dist/testit.js new file mode 100644 index 0000000..73029ac --- /dev/null +++ b/dist/testit.js @@ -0,0 +1,102 @@ +/*! Test.it v 0.6.1 | MIT | https://github.com/n2geoff/testit */ +(function (root, factory) { + "use strict"; + if (typeof module === "object" && module.exports) { + module.exports = factory(root.test); + } else { + root.test = factory(root.test); + } +}(this, function () { + "use strict"; + + const test = { + "_tests": {}, + "run": function (next) { + + let tests = this._tests; + let failed = []; + let passed = []; + + Object.keys(tests).forEach(function (name) { + let test = tests[name]; + + try { + test(); + passed.push(`\n+ ${name}`); + } catch (err) { + failed.push(`\n- ${name}`); + console.error(err); + } + }); + + if(typeof next === "function") { + return next({ + pass: passed, + fail: failed + }); + } else { + console.log(...passed, ...failed); + console.log(`\n# tests ${failed.length + passed.length} pass ${passed.length} fail ${failed.length}`); + + return failed.length ? false : true; + } + }, + "it": function (tests) { + this._tests = tests; + return this; + }, + "expects": (val) => { + return { + "to": { + "be": { + "a": (type) => { + return test.expects(val).to.be.an(type); + }, + "an": (type) => { + + if(['array'].indexOf(type) !== -1) { + if(val.constructor.name.toLowerCase() === 'array') { + return true; + } else { + throw new Error(`expected ${typeof val} to be an ${type}`); + } + } + + if(typeof val === type) { + return true; + } else { + throw new Error(`expected ${typeof val} to be an ${type}`); + } + }, + "like": (comp) => { + if(val == comp) { + return true; + } else { + throw new Error(`expected ${val} == ${comp}`); + } + } + }, + "equal": (comp) => { + + if(val === comp) { + return true; + } else { + throw new Error(`expected ${val} === ${comp}`); + } + }, + "exist": () => { + if(val) { + return true; + } else { + throw new Error(`expected ${val} to be truthy`); + } + }, + "pass": () => { return true; }, + "fail": (msg) => { throw new Error(msg); } + } + }; + } + }; + + return test; +})); diff --git a/dist/testit.min.js b/dist/testit.min.js new file mode 100644 index 0000000..240d176 --- /dev/null +++ b/dist/testit.min.js @@ -0,0 +1,2 @@ +/*! Test.it v 0.6.1 | MIT | https://github.com/n2geoff/testit */ +!function(t,e){"use strict";"object"==typeof module&&module.exports?module.exports=e(t.test):t.test=e(t.test)}(this,function(){"use strict";const t={_tests:{},run:function(t){let e=this._tests,r=[],o=[];return Object.keys(e).forEach(function(t){let n=e[t];try{n(),o.push(`\n+ ${t}`)}catch(e){r.push(`\n- ${t}`),console.error(e)}}),"function"==typeof t?t({pass:o,fail:r}):(console.log(...o,...r),console.log(`\n# tests ${r.length+o.length} pass ${o.length} fail ${r.length}`),!r.length)},it:function(t){return this._tests=t,this},expects:e=>({to:{be:{a:r=>t.expects(e).to.be.an(r),an:t=>{if(-1!==["array"].indexOf(t)){if("array"===e.constructor.name.toLowerCase())return!0;throw new Error(`expected ${typeof e} to be an ${t}`)}if(typeof e===t)return!0;throw new Error(`expected ${typeof e} to be an ${t}`)},like:t=>{if(e==t)return!0;throw new Error(`expected ${e} == ${t}`)}},equal:t=>{if(e===t)return!0;throw new Error(`expected ${e} === ${t}`)},exist:()=>{if(e)return!0;throw new Error(`expected ${e} to be truthy`)},pass:()=>!0,fail:t=>{throw new Error(t)}}})};return t}); \ No newline at end of file diff --git a/src/testit.js b/src/testit.js index ebaf272..e89e87e 100644 --- a/src/testit.js +++ b/src/testit.js @@ -1,5 +1,5 @@ -/* Test.it v 0.5.2 | MIT | https://github.com/n2geoff/testit */ -;(function (root, factory) { +/*! Test.it v 0.6.1 | MIT | https://github.com/n2geoff/testit */ +(function (root, factory) { "use strict"; // support browser & commonjs if (typeof module === "object" && module.exports) { @@ -18,26 +18,21 @@ // capture results let failed = []; let passed = []; - + // loop through tests Object.keys(tests).forEach(function (name) { let test = tests[name]; - + // execute try { test(); passed.push(`\n+ ${name}`); } catch (err) { - // TODO: include error stack option - // let back = err.stack.split('\n')[2]; - // let re = /(?<=\()(.*?)(?=\))/g; - // let trace = re.exec(back)[0]; failed.push(`\n- ${name}`); console.error(err); - // failed.push(`\n- ${name} - - ${trace}`); } }); - + // summary if(typeof next === "function") { return next({ @@ -47,7 +42,7 @@ } else { console.log(...passed, ...failed); console.log(`\n# tests ${failed.length + passed.length} pass ${passed.length} fail ${failed.length}`); - + return failed.length ? false : true; } }, @@ -55,11 +50,57 @@ this._tests = tests; return this; }, - "assert": function (e, a) { if (e != a) throw Error(`expected ${e} == ${a}`); }, - "equals": function (e, a) { if (e !== a) throw Error(`expected ${e} === ${a}`); }, - "exists": function (v) { if (!v) throw Error(`exists value ${v}`); }, - "pass": function () { return true; }, - "fail": function (m) { throw Error(m); } + "expects": (val) => { + return { + "to": { + "be": { + "a": (type) => { + return test.expects(val).to.be.an(type); + }, + "an": (type) => { + + if(['array'].indexOf(type) !== -1) { + if(val.constructor.name.toLowerCase() === 'array') { + return true; + } else { + throw new Error(`expected ${typeof val} to be an ${type}`); + } + } + + if(typeof val === type) { + return true; + } else { + throw new Error(`expected ${typeof val} to be an ${type}`); + } + }, + "like": (comp) => { + if(val == comp) { + return true; + } else { + throw new Error(`expected ${val} == ${comp}`); + } + } + }, + "equal": (comp) => { + + if(val === comp) { + return true; + } else { + throw new Error(`expected ${val} === ${comp}`); + } + }, + "exist": () => { + if(val) { + return true; + } else { + throw new Error(`expected ${val} to be truthy`); + } + }, + "pass": () => { return true; }, + "fail": (msg) => { throw new Error(msg); } + } + }; + } }; return test; diff --git a/src/testit.min.js b/src/testit.min.js deleted file mode 100644 index 25f39d0..0000000 --- a/src/testit.min.js +++ /dev/null @@ -1,2 +0,0 @@ -/* Test.it v 0.5.2 | MIT | https://github.com/n2geoff/testit */ -;(function(root,factory){"use strict";if(typeof module==="object"&&module.exports){module.exports=factory(root.test)}else{root.test=factory(root.test)}})(this,function(){"use strict";const test={_tests:{},run:function(next){let tests=this._tests;let failed=[];let passed=[];Object.keys(tests).forEach(function(name){let test=tests[name];try{test();passed.push(`\n+ ${name}`)}catch(err){failed.push(`\n- ${name}`);console.error(err)}});if(typeof next==="function"){return next({pass:passed,fail:failed})}else{console.log(...passed,...failed);console.log(`\n# tests ${failed.length+passed.length} pass ${passed.length} fail ${failed.length}`);return failed.length?false:true}},it:function(tests){this._tests=tests;return this},assert:function(e,a){if(e!=a)throw Error(`expected ${e} == ${a}`)},equals:function(e,a){if(e!==a)throw Error(`expected ${e} === ${a}`)},exists:function(v){if(!v)throw Error(`exists value ${v}`)},pass:function(){return true},fail:function(m){throw Error(m)}};return test}); diff --git a/test/index.spec.js b/test/index.spec.js index 0871018..758e015 100644 --- a/test/index.spec.js +++ b/test/index.spec.js @@ -1,25 +1,31 @@ -test.it({ - "'assert' should exist": function() { - test.exists(test.assert); - }, - "truty assert should work": function() { - test.assert(1, '1'); - test.assert('1', 1); - }, - "'equals' should exist": function() { - test.exists(test.equals); - }, - "'equals' should be exact": function() { - test.equals(1,1); - test.equals('hello', 'hello'); - }, - "'pass' should exist": function() { - test.exists(test.pass); - }, - "'fail' should exist": function() { - test.exists(test.fail); - }, - "'exists' should exist": function() { - test.exists(test.exists); - } +test.it({ + "'like' should do truthy evaluation via ==": function() { + test.expects(1).to.be.like('1'); + test.expects("1").to.be.like(1); + }, + "'equal' should do === evaluation exist": function() { + test.expects(1).to.equal(1); + test.expects('hello').to.equal('hello'); + }, + "you should be able to 'pass' a test": function() { + test.expects().to.pass(); + }, + "you should be able to fail' a test too": function() { + try { + test.expects().to.fail(); + } catch(e) { + test.expects().to.pass(); + } + }, + "you should be albe to see if something 'exists'": function() { + test.expects({}).to.exist(); + }, + "should be able to check types": function() { + test.expects(123).to.be.a('number'); + test.expects([]).to.be.an('array'); + test.expects({}).to.be.a('object'); + test.expects(true).to.be.a('boolean'); + test.expects(false).to.be.a('boolean'); + test.expects(undefined).to.be.a('undefined'); + } }); \ No newline at end of file