From 088184bb17787adf027e43b93f8f68f61ca3506e Mon Sep 17 00:00:00 2001 From: Guillaume ARM Date: Thu, 29 Mar 2018 17:26:13 +0200 Subject: [PATCH] feat: createTestHandler constructor can be extended (#134) - expose createTestHandler - add constructor parameter to createTestHandler - write example-readme-custom-testhandler tests - write documentation about createTestHandler Closes #133 Ref #134 --- README.md | 39 ++++++ .../example-readme-custom-testhandler.js | 128 ++++++++++++++++++ __bundle_tests__/index.js | 1 + __tests__/testHandler.js | 4 +- src/index.js | 2 +- src/testHandler.js | 20 +-- 6 files changed, 181 insertions(+), 13 deletions(-) create mode 100644 __bundle_tests__/example-readme-custom-testhandler.js diff --git a/README.md b/README.md index 21148c4..7084028 100644 --- a/README.md +++ b/README.md @@ -268,5 +268,44 @@ testHandler(myHandler()) .run(); ``` +### Custom testHandler + +A custom `testHandler` can be created using `createTestHandler`. + +**e.g.** + +```js +import { io, createTestHandler } from 'handle-io'; + + +const createCustomTestHandler = (h, mockedIOs, expectedRetValue, assertRet, constructor = createCustomTestHandler) => { + return { + ...createTestHandler(h, mockedIOs, expectedRetValue, assertRet, constructor), + matchLog: (arg, ret) => constructor( + h, + [...mockedIOs, [io(console.log)(arg), ret]], + expectedRetValue, + assertRet, + constructor, + ), + }; +}; + +const customTestHandler = h => createCustomTestHandler(h); + +const log = io(console.log); +const myHandler = handler(function*(value) { + yield log(value); + yield log(value); + return 42; +}); + +customTestHandler(myHandler('hello world')) + .shouldReturn(42) + .matchLog('hello world') + .matchLog('hello world') + .run() +``` + ## License [MIT](https://github.com/guillaumearm/handle-io/blob/master/LICENSE) diff --git a/__bundle_tests__/example-readme-custom-testhandler.js b/__bundle_tests__/example-readme-custom-testhandler.js new file mode 100644 index 0000000..bd76f17 --- /dev/null +++ b/__bundle_tests__/example-readme-custom-testhandler.js @@ -0,0 +1,128 @@ +/* eslint-disable no-console */ + +import expect from 'expect.js'; +import { test, describe } from 'async-describe'; + +module.exports = ({ io, handler, testHandler, createTestHandler }) => ( + describe('[README.md] Custom testHandler example', async () => { + const log = io(console.log); + + const createCustomTestHandler = (h, mockedIOs = [], expectedRetValue, assertRet = false, constructor = createCustomTestHandler) => { + return { + ...createTestHandler(h, mockedIOs, expectedRetValue, assertRet, constructor), + matchLog: (arg, ret) => constructor( + h, + [...mockedIOs, [io(console.log)(arg), ret]], + expectedRetValue, + assertRet, + constructor, + ), + }; + }; + const customTestHandler = h => createCustomTestHandler(h); + + const myHandler = handler(function*(value) { + yield log(value); + yield log(value); + return 42; + }); + + await describe('whith testHandler', async () => { + await test('logs "hello world" twice and return 42', () => { + testHandler(myHandler('hello world')) + .matchIo(log('hello world')) + .matchIo(log('hello world')) + .shouldReturn(42) + .run() + }); + }); + + await describe('with customTestHandler (success)', async () => { + await test('logs "hello world" twice and return 42', async () => { + customTestHandler(myHandler('hello world')) + .matchLog('hello world') + .matchLog('hello world') + .shouldReturn(42) + .run(); + + customTestHandler(myHandler('hello world')) + .shouldReturn(42) + .matchLog('hello world') + .matchIo(log('hello world')) + .run(); + + customTestHandler(myHandler('hello world')) + .shouldReturn(42) + .matchIo(log('hello world')) + .matchLog('hello world') + .run(); + }); + }); + + await describe('with customTestHandler (failure)', async () => { + await test('throws an error when arguments are invalid', async () => { + expect(() => { + customTestHandler(myHandler('hello world')) + .matchLog('hello world 1') + .matchLog('hello world') + .shouldReturn(42) + .run(); + }).to.throwError('Invalid IO#0 function arguments') + expect(() => { + customTestHandler(myHandler('hello world')) + .matchLog('hello world') + .matchLog('hello world 2') + .shouldReturn(42) + .run(); + }).to.throwError('Invalid IO#1 function arguments') + }); + + await test('throws an error when io functions are wrong', async () => { + expect(() => { + customTestHandler(myHandler('hello world')) + .matchIo(io(() => {})('hello world')) + .matchLog('hello world') + .shouldReturn(42) + .run(); + }).to.throwError('Invalid IO#0 function'); + expect(() => { + customTestHandler(myHandler('hello world')) + .matchLog('hello world') + .matchIo(io(() => {})('hello world')) + .shouldReturn(42) + .run(); + }).to.throwError('Invalid IO#1 function'); + }); + + await test('throws an error when returned value is wrong', async () => { + expect(() => { + customTestHandler(myHandler('hello world')) + .matchLog('hello world') + .matchLog('hello world') + .shouldReturn(0) + .run(); + }).to.throwError('Invalid returned value'); + }); + + await test('throws an error when too much io ran', async () => { + expect(() => { + customTestHandler(myHandler('hello world')) + .matchLog('hello world') + .shouldReturn(42) + .run(); + }).to.throwError('Too much io ran'); + }); + + await test('throws an error when not enough io ran', async () => { + expect(() => { + customTestHandler(myHandler('hello world')) + .matchLog('hello world') + .matchLog('hello world') + .matchLog('hello world') + .shouldReturn(42) + .run(); + }).to.throwError('Not enough io ran'); + }); + }); + }) +) diff --git a/__bundle_tests__/index.js b/__bundle_tests__/index.js index df8ca32..f5038c6 100644 --- a/__bundle_tests__/index.js +++ b/__bundle_tests__/index.js @@ -13,6 +13,7 @@ const testSuites = [ require('./example-readme-addvalues'), require('./example-readme-promises'), require('./example-readme-errors'), + require('./example-readme-custom-testhandler'), ]; const bundlePrefix = 'bundle-' diff --git a/__tests__/testHandler.js b/__tests__/testHandler.js index c2f9b25..071a68d 100644 --- a/__tests__/testHandler.js +++ b/__tests__/testHandler.js @@ -153,7 +153,7 @@ describe('handle-io/testHandler', () => { .matchIo({ f: f1, args: args2 }, 'b') .matchIo({ f: f2, args: args1 }, 'c') .run() - ).toThrow('Too much runned io'); + ).toThrow('Too much io ran'); }); test('not enough io ran', () => { @@ -165,7 +165,7 @@ describe('handle-io/testHandler', () => { .matchIo({ f: f2, args: args2 }, 'd') .matchIo({ f: () => {}, args: [] }, 'x') .run() - ).toThrow('Not enough runned io'); + ).toThrow('Not enough io ran'); }); test('invalid io function', () => { diff --git a/src/index.js b/src/index.js index 36bb328..376bfb0 100644 --- a/src/index.js +++ b/src/index.js @@ -1,5 +1,5 @@ export { default as io } from './io'; export { default as handler } from './handler'; -export { default as testHandler } from './testHandler'; +export { default as testHandler, createTestHandler } from './testHandler'; export { default as catchError } from './catchError'; export { default as simulateThrow } from './simulateThrow'; diff --git a/src/testHandler.js b/src/testHandler.js index 6fc34b6..258604e 100644 --- a/src/testHandler.js +++ b/src/testHandler.js @@ -3,33 +3,34 @@ import SimulatedThrow from './internal/SimulatedThrow'; import isEqual from 'lodash.isequal'; import { stringify } from './internal/utils'; -const createTestHandler = (h, mockedIOs = [], expectedRetValue, assertRet = false) => { +export const createTestHandler = (h, mockedIOs = [], expectedRetValue, assertRet = false, constructor = createTestHandler) => { if (typeof h !== 'function') { throw new Error('Handler should be a function') } return { - matchIo: (io, ret) => createTestHandler( + matchIo: (io, ret) => constructor( h, [...mockedIOs, [io, ret]], expectedRetValue, - assertRet + assertRet, + constructor ), - shouldReturn: (expected) => createTestHandler(h, mockedIOs, expected, true), + shouldReturn: (expected) => constructor(h, mockedIOs, expected, true, constructor), run: () => { - // 1. run handler using a custom runner to get a retValue + // run handler using a custom runner to get a retValue let mockIndex = 0; const retValue = h.run((io) => { if (!mockedIOs[mockIndex]) { - throw new BypassHandlerError('Too much runned io') + throw new BypassHandlerError('Too much io ran'); } const [expectedIO, mockedRetValue] = mockedIOs[mockIndex]; if (!isEqual(io.f, expectedIO.f)) { - throw new BypassHandlerError(`Invalid IO#${mockIndex} function`) + throw new BypassHandlerError(`Invalid IO#${mockIndex} function`); } if (!isEqual(io.args, expectedIO.args)) { const expectedArgs = stringify(expectedIO.args); const ioArgs = stringify(io.args); - throw new BypassHandlerError(`Invalid IO#${mockIndex} function arguments: expected \n${expectedArgs}\nbut got \n${ioArgs}`) + throw new BypassHandlerError(`Invalid IO#${mockIndex} function arguments: expected \n${expectedArgs}\nbut got \n${ioArgs}`); } mockIndex += 1; if (mockedRetValue instanceof SimulatedThrow) { @@ -39,10 +40,9 @@ const createTestHandler = (h, mockedIOs = [], expectedRetValue, assertRet = fals }); if (mockIndex < mockedIOs.length) { - throw new Error('Not enough runned io'); + throw new Error('Not enough io ran'); } - // 3. expectedRetValue and retValue should be equal if (assertRet && !isEqual(expectedRetValue, retValue)) { throw new Error(`Invalid returned value : expected ${expectedRetValue} but got ${retValue}`) }