From cd54b10128270c5d515cfc3740131d5b6c6e8885 Mon Sep 17 00:00:00 2001 From: Muthu Kumar Date: Sat, 11 Aug 2018 03:27:31 +0530 Subject: [PATCH] [docs] Update docs for before, after, and state --- README.md | 176 ++++++++++++++++++++++++++++++++++++++++++++++---- hello.txt | 1 - sample.test.js | 86 ------------------------ sample/hello.txt | 1 + sample/sample.test.js | 86 ++++++++++++++++++++++++ 5 files changed, 250 insertions(+), 100 deletions(-) delete mode 100644 hello.txt delete mode 100644 sample.test.js create mode 100644 sample/hello.txt create mode 100644 sample/sample.test.js diff --git a/README.md b/README.md index 30f48a5..aa0c641 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,9 @@ ## Requirements & Usage -`Gunner` uses very modern JavaScript, and hence requires node 10+ currently. +`Gunner` uses very modern JavaScript, and hence requires **node 10+** ❗️⚠️ currently. -Create a new `Gunner` instance and simply write your tests. The assertion methods are passed in as the callback to the test function. +Create a new `Gunner` instance and simply write your tests. The assertion methods are passed in as the callback as an `expect` object to the test function. ```JavaScript const gunner = new Gunner(); @@ -22,14 +22,28 @@ gunner.test('arrays are equal', expect => { gunner.run(); ``` -## API +## Documentation Index + +- ### `Class`: + - #### [`Gunner.constructor`](#new-gunner-options) + +- ### `Methods`: + - #### [`Gunner#test`](#gunnertest-title-implementation) + - #### [`Gunner#before`](#gunnerbefore-title-implementation) + - #### [`Gunner#after`](#gunnerafter-title-implementation) + - #### [`Gunner#run`](#gunnerrun-options) + +- ### `Constants`: + - #### `[Gunner.Start]` + - #### `[Gunner.End]` + +- ### [`State and Advanced Usage`](#state) -#### [Gunner.constructor](#new-gunner) -#### [Gunner#test(title, implementation)](#gunnertest) -#### [Gunner#run(options)](#gunnerrun) --- -### new Gunner +## API + +### new Gunner (options) Creates a new Gunner instance. @@ -37,17 +51,21 @@ Creates a new Gunner instance. - **`name`** [default: undefined]: A name for this Gunner instance. -#### Usage +#### Example ```JavaScript const gunner = new Gunner(options); ``` -### Gunner#test +[`INDEX`](#index) + +### Gunner#test (title, implementation) + +Registers a new test. An `expect` object is passed into the implementation callback as the first argument. A test can have multiple expect statements. They should be returned as an array. The first expect to fail will cause the test to fail. -Registers a new test. A test can have multiple expect statements. They should be returned as an array. The first expect to fail will cause the test to fail. +The `expect` object is passed in as first argument, but any assertion module may be used, as long it either throws an error, or rejects. If you use a different assert module such as `chai`, remember to return Promises properly, else some Promises will be lost, just like in regular JavaScript. -#### Usage +#### Example ```JavaScript gunner.test('sum should equal 3', expect => { @@ -86,7 +104,75 @@ gunner.test('asynchronous test', async expect => { }) ``` -### Gunner#run +[`INDEX`](#index) + +### Gunner#before (title, implementation) + +Registers a new `before` hook. `before` hooks run before the selected test(s). The implementation callback is similar to that of a test, with the exception that no expect object will be passed. + +The first argument can be one of: + +- title of a test, which causes the hook to run once before the mentioned test + +- `'*'`, which causes the hook to run once before _every_ test + +- either of the constants: `Gunner.Start`, and `Gunner.End`. + +`gunner.before(Gunner.Start, () => {})` will run once before Gunner starts running any tests. The `Gunner.End` equivalent will run once after running all tests (before ending). + +#### Example + +```JavaScript +gunner.before('insert to db should not error', () => { + + // Clear db before test + return db.remove('users', { username: 'mkrhere' }); + +}); + +gunner.test('insert to db should not error', expect => { + + const user = await db.insert({ + username: 'mkrhere', + firstname: 'muthu', + }); + return expect(user).hasPair('firstname', 'muthu'); + +}); +``` + +[`INDEX`](#index) + +### Gunner#after (title, implementation) + +Registers a new `after` hook. `after` hooks run after the corresponding test(s). The implementation callback is similar to that of a test, with the exception that no expect object will be passed. + +The first argument is similar to `Gunner#before`, but does not accept `Gunner.Start` and `Gunner.End` constants, only `'*'` or test description. + +#### Example + +```JavaScript +gunner.test('insert to db should not error', expect => { + + const user = await db.insert({ + username: 'mkrhere', + firstname: 'muthu', + }); + return expect(user).hasPair('firstname', 'muthu'); + +}); + +gunner.after('insert to db should not error', () => { + + // Clear db after test + return db.remove('users', { username: 'mkrhere' }); + +}); +``` + +[`INDEX`](#index) + +### Gunner#run (options) Starts running Gunner tests. Takes an options object as optional parameter. @@ -95,9 +181,73 @@ Starts running Gunner tests. Takes an options object as optional parameter. - **`log`** [default: true]: Turn logs on or off (returns array of results) - **`trace`** [default: false]: Turn stack traces on or off -#### Usage +#### Example ```JavaScript const options = { logs: true, trace: true }; gunner.run(options); ``` + +[`INDEX`](#index) + +### State + +> `[ADVANCED]` + +Additionally, `before` hooks create state objects from returned values that will be passed down hierarchically to other `before` and `after` hooks, and their matching tests. The state object is passed as second argument to tests. Hooks will also receive as the first argument state from hooks above itself. + +This has four levels: + +- `'@start'` (from the `Gunner.Start` hooks). +- `'@every'` (from the `'*'` hooks). +- `'@this'` (from the hook registered to this test). +- `'@results'` (results from all tests, passed only to the `Gunner.End` hook). + +#### Example + +```JavaScript +gunner.before(Gunner.Start, () => { + const db = DBModule.createDbConnection(); + return db; +}); + +gunner.before('test user should exist in db', state => { + + // Receives '@start' and '@every' states if exists + const db = state['@start'][0]; + + const testUser = await db.insert('users', { + username: 'mkrhere', + firstname: 'muthu', + }); + return testUser.username; + +}); + +gunner.test('test user should exist in db', (expect, state) => { + + // Receives '@start', '@every', and '@this' states + // Each state level is an array because multiple hooks may exist per level + const db = state['@start'][0]; + const username = state['@this'][0]; + + const user = await db.find('users', { username }); + return expect(user).hasPair('firstname', 'muthu'); + +}); + +gunner.after('test user should exist in db', state => { + + const db = state['@start'][0]; + const db = state['@this'][0]; + + return db.remove('users', { username }); + +}); +``` + +[`INDEX`](#index) + +## Credits + +`Gunner` was built at [Klenty](https://klenty.com), a sales automation startup, by Muthu Kumar [(@MKRhere)](https://github.com/MKRhere). diff --git a/hello.txt b/hello.txt deleted file mode 100644 index b6fc4c6..0000000 --- a/hello.txt +++ /dev/null @@ -1 +0,0 @@ -hello \ No newline at end of file diff --git a/sample.test.js b/sample.test.js deleted file mode 100644 index 73fef4b..0000000 --- a/sample.test.js +++ /dev/null @@ -1,86 +0,0 @@ -/** - * This file contains random tests - * used during development - */ - -const Gunner = require('./index.js'); -const gunner = new Gunner({ name: 'sample tests' }); -const a = 1; - -gunner.before(Gunner.Start, () => console.log('Started tests!')); -gunner.before(Gunner.End, () => console.log('Ended tests!')); -let runCount = 1; -gunner.before('*', () => console.log(`Running test ${runCount++}`)); - -gunner.test('should automatically pass', expect => expect().done()); -gunner.test(`should be equal`, expect => expect(1).equal(1)); -gunner.test(`objects are deep equal`, expect => expect({ a: 1 }).deepEqual({ a: 1 })); -gunner.test('expression should be true', expect => expect(a === 1).isTrue()); - -gunner.test('should be a Promise (resolved)', expect => - expect(Promise.resolve()).isPromise()); - -gunner.test('should be a Promise (rejected)', expect => - expect(Promise.reject()).isPromise()); - -gunner.test('should resolve to 5', expect => - expect(Promise.resolve(5)).resolvesTo(5)); - -gunner.before( - 'file must have hello as content', - () => console.log('>> starting test! file must have hello as content'), -); - -gunner.after( - 'file must have hello as content', - () => console.log('>> finished test! file must have hello as content'), -); - -gunner.test('file must have hello as content', async expect => { - const { readFile } = require('fs').promises; - const file = await readFile('./hello.txt', { encoding: 'utf8' }); - return [ - expect(file).equal('hello'), - expect(file.length).equal(5), - ]; -}); - -gunner.test('(should fail) Value is not a Promise', expect => - expect(5).isPromise()); - -gunner.test('(should fail) Error is not a Promise', expect => - expect(flamethrower()).isPromise()); - -gunner.test(`(should fail) objects aren't deeply equal`, expect => expect({a : 1}).deepEqual({ a: 2 })); - -gunner.test('(should fail) promise must reject', expect => - expect(Promise.reject(new Error('Promise Rejected'))).equal('no rejection')); - -gunner.test('(should fail) multiple expect', expect => { - - const a = { }; - a.b = 1; - a.c = 2; - - return [ - expect(a).hasProp('b'), - expect(a).hasPair('c', 3), - ]; - -}); - -const flamethrower = () => { - throw new Error('This burns!'); -}; - -gunner.test('(should fail) should catch error', expect => { - return expect(flamethrower()).equal(5); -}); - -gunner.test('(should fail) should not resolve to 5', expect => - expect(Promise.resolve()).resolvesTo(5)); - -const trace = process.argv.slice(2).indexOf('--trace') !== -1; -const log = process.argv.slice(2).indexOf('--log') !== -1; - -gunner.run({ trace, log }); diff --git a/sample/hello.txt b/sample/hello.txt new file mode 100644 index 0000000..b6fc4c6 --- /dev/null +++ b/sample/hello.txt @@ -0,0 +1 @@ +hello \ No newline at end of file diff --git a/sample/sample.test.js b/sample/sample.test.js new file mode 100644 index 0000000..9d83ef2 --- /dev/null +++ b/sample/sample.test.js @@ -0,0 +1,86 @@ +/** + * This file contains random tests + * used during development + */ + +const Gunner = require('../index.js'); +const gunner = new Gunner({ name: 'sample tests' }); +const a = 1; + +gunner.before(Gunner.Start, () => console.log('Started tests!')); +gunner.before(Gunner.End, () => console.log('Ended tests!')); +let runCount = 1; +gunner.before('*', () => console.log(`Running test ${runCount++}`)); + +gunner.test('should automatically pass', expect => expect().done()); +gunner.test(`should be equal`, expect => expect(1).equal(1)); +gunner.test(`objects are deep equal`, expect => expect({ a: 1 }).deepEqual({ a: 1 })); +gunner.test('expression should be true', expect => expect(a === 1).isTrue()); + +gunner.test('should be a Promise (resolved)', expect => + expect(Promise.resolve()).isPromise()); + +gunner.test('should be a Promise (rejected)', expect => + expect(Promise.reject()).isPromise()); + +gunner.test('should resolve to 5', expect => + expect(Promise.resolve(5)).resolvesTo(5)); + +gunner.before( + 'file must have hello as content', + () => console.log('>> starting test! file must have hello as content'), +); + +gunner.after( + 'file must have hello as content', + () => console.log('>> finished test! file must have hello as content'), +); + +gunner.test('file must have hello as content', async expect => { + const { readFile } = require('fs').promises; + const file = await readFile(__dirname + '/hello.txt', { encoding: 'utf8' }); + return [ + expect(file).equal('hello'), + expect(file.length).equal(5), + ]; +}); + +gunner.test('(should fail) Value is not a Promise', expect => + expect(5).isPromise()); + +gunner.test('(should fail) Error is not a Promise', expect => + expect(flamethrower()).isPromise()); + +gunner.test(`(should fail) objects aren't deeply equal`, expect => expect({a : 1}).deepEqual({ a: 2 })); + +gunner.test('(should fail) promise must reject', expect => + expect(Promise.reject(new Error('Promise Rejected'))).equal('no rejection')); + +gunner.test('(should fail) multiple expect', expect => { + + const a = { }; + a.b = 1; + a.c = 2; + + return [ + expect(a).hasProp('b'), + expect(a).hasPair('c', 3), + ]; + +}); + +const flamethrower = () => { + throw new Error('This burns!'); +}; + +gunner.test('(should fail) should catch error', expect => { + return expect(flamethrower()).equal(5); +}); + +gunner.test('(should fail) should not resolve to 5', expect => + expect(Promise.resolve()).resolvesTo(5)); + +const trace = process.argv.slice(2).indexOf('--trace') !== -1; +const log = process.argv.slice(2).indexOf('--log') !== -1; + +gunner.run({ trace, log });