domsuite
Advanced tools
Comparing version 0.3.1 to 0.4.0
@@ -16,2 +16,6 @@ 'use strict'; | ||
var _isString = require('lodash/isString'); | ||
var _isString2 = _interopRequireDefault(_isString); | ||
var _sinon = require('sinon'); | ||
@@ -43,6 +47,20 @@ | ||
var FetchServer = exports.FetchServer = function () { | ||
/** | ||
* Create a FetchServer instance. | ||
* | ||
* @param {object} handlers : map of urls to response objects (or functions) | ||
* @param {object} [options={}] | ||
* @param {boolean} [options.debug=false] : output debug logging to console | ||
* @param {function|null} [options.errorCallback=null] : custom error handler | ||
* | ||
* errorCallback param allows hooking test framework error handling utils | ||
* into FetchServer so that tests may be reliably failed on error. | ||
*/ | ||
function FetchServer(handlers) { | ||
var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}, | ||
_ref$debug = _ref.debug, | ||
debug = _ref$debug === undefined ? false : _ref$debug; | ||
debug = _ref$debug === undefined ? false : _ref$debug, | ||
_ref$errorCallback = _ref.errorCallback, | ||
errorCallback = _ref$errorCallback === undefined ? null : _ref$errorCallback; | ||
@@ -53,3 +71,3 @@ _classCallCheck(this, FetchServer); | ||
this.debug = debug; | ||
this.handle = this.handle.bind(this); | ||
this.errorCallback = errorCallback; | ||
} | ||
@@ -60,2 +78,23 @@ | ||
value: function handle(url, params) { | ||
// Order routes by url length (descending). | ||
// This ensures that when route overlap occurs (e.g. /foo/bar and /foo/bar/baz) | ||
// the more-specific route (/foo/bar/baz) is checked first. Otherwise, some | ||
// routes will be silently ignored when more-generic (shorter) routes exist. | ||
var routes = Object.entries(this.handlers).sort(function (_ref2, _ref3) { | ||
var _ref5 = _slicedToArray(_ref2, 1), | ||
aRoute = _ref5[0]; | ||
var _ref4 = _slicedToArray(_ref3, 1), | ||
bRoute = _ref4[0]; | ||
// If route lengths are the same, sort alphabetically; | ||
// otherwise, sort by route string length (descending). | ||
// Route length + alpha makes sort fully deterministic. | ||
if (bRoute.length === aRoute.length) { | ||
return bRoute.toLowerCase() < aRoute.toLowerCase() ? 1 : -1; | ||
} else { | ||
return bRoute.length - aRoute.length; | ||
} | ||
}); | ||
var _iteratorNormalCompletion = true; | ||
@@ -66,15 +105,25 @@ var _didIteratorError = false; | ||
try { | ||
for (var _iterator = Object.entries(this.handlers)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
var _ref2 = _step.value; | ||
for (var _iterator = routes[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
var _ref6 = _step.value; | ||
var _ref3 = _slicedToArray(_ref2, 2); | ||
var _ref7 = _slicedToArray(_ref6, 2); | ||
var route = _ref3[0]; | ||
var handler = _ref3[1]; | ||
var route = _ref7[0]; | ||
var handler = _ref7[1]; | ||
if (url.includes(route)) { | ||
if (params && params.body) { | ||
params = JSON.parse(params.body); | ||
if ((0, _isString2.default)(params.body)) { | ||
// Allow string or JSON params.body; if valid JSON, | ||
// parse body to JS object before passing to handler function. | ||
try { | ||
params = JSON.parse(params.body); | ||
} catch (err) { | ||
params = params.body; | ||
} | ||
} else { | ||
throw new Error('Invalid body: ' + params.body); | ||
} | ||
} | ||
var response = (0, _isFunction2.default)(handler) ? handler(url, params) : handler; | ||
@@ -115,16 +164,56 @@ | ||
} | ||
/** | ||
* Start the FetchServer, sinon.stub'ing `fetch` with a fake version. | ||
*/ | ||
}, { | ||
key: 'start', | ||
value: function start() { | ||
if (window.fetch.hasOwnProperty('restore')) { | ||
window.fetch.restore(); | ||
var _this = this; | ||
this.restore(); // clean up prior FetchServer stubs (if necessary) | ||
var fakeFetch = function fakeFetch(url, params) { | ||
try { | ||
return _this.handle(url, params); | ||
} catch (err) { | ||
// errorCallback allows hooking test framework error handling utils | ||
// into FetchServer so that tests may be reliably failed on error: | ||
(0, _isFunction2.default)(_this.errorCallback) && _this.errorCallback(err); | ||
throw err; // rethrow error | ||
} | ||
}; | ||
// stub fetch in browser environment: | ||
if (typeof window !== 'undefined' && window.fetch) { | ||
sinon.stub(window, 'fetch').callsFake(fakeFetch); | ||
} | ||
sinon.stub(window, 'fetch').callsFake(this.handle); | ||
// stub fetch in NodeJS environment: | ||
if (typeof global !== 'undefined' && global.fetch) { | ||
sinon.stub(global, 'fetch').callsFake(fakeFetch); | ||
} | ||
} | ||
/** | ||
* Restore sinon.stub'ed `fetch` to its original state. | ||
*/ | ||
}, { | ||
key: 'restore', | ||
/** | ||
* Restore sinon.stub'ed `fetch` to its original state. | ||
* (convenience method for calling from class instance) | ||
*/ | ||
value: function restore() { | ||
FetchServer.restore(); | ||
} | ||
}, { | ||
key: 'debugLog', | ||
value: function debugLog(_ref4) { | ||
var url = _ref4.url, | ||
params = _ref4.params, | ||
response = _ref4.response; | ||
value: function debugLog(_ref8) { | ||
var url = _ref8.url, | ||
params = _ref8.params, | ||
response = _ref8.response; | ||
@@ -138,6 +227,6 @@ if (this.debug) { | ||
}); | ||
Promise.all([responseObjectPromise, responseBodyPromise]).then(function (_ref5) { | ||
var _ref6 = _slicedToArray(_ref5, 2), | ||
response = _ref6[0], | ||
responseBody = _ref6[1]; | ||
Promise.all([responseObjectPromise, responseBodyPromise]).then(function (_ref9) { | ||
var _ref10 = _slicedToArray(_ref9, 2), | ||
response = _ref10[0], | ||
responseBody = _ref10[1]; | ||
@@ -158,2 +247,14 @@ console.groupCollapsed('[fetch-server] ' + url); | ||
} | ||
}], [{ | ||
key: 'restore', | ||
value: function restore() { | ||
// restore fetch in browser environment: | ||
if (typeof window !== 'undefined' && window.fetch && Object.hasOwnProperty.call(window.fetch, 'restore')) { | ||
window.fetch.restore(); | ||
} | ||
// restore fetch in NodeJS environment: | ||
if (typeof global !== 'undefined' && global.fetch && Object.hasOwnProperty.call(global.fetch, 'restore')) { | ||
global.fetch.restore(); | ||
} | ||
} | ||
}]); | ||
@@ -160,0 +261,0 @@ |
import isFunction from 'lodash/isFunction'; | ||
import isString from 'lodash/isString'; | ||
import * as sinon from 'sinon'; | ||
@@ -20,15 +21,52 @@ | ||
export class FetchServer { | ||
constructor(handlers, {debug = false} = {}) { | ||
/** | ||
* Create a FetchServer instance. | ||
* | ||
* @param {object} handlers : map of urls to response objects (or functions) | ||
* @param {object} [options={}] | ||
* @param {boolean} [options.debug=false] : output debug logging to console | ||
* @param {function|null} [options.errorCallback=null] : custom error handler | ||
* | ||
* errorCallback param allows hooking test framework error handling utils | ||
* into FetchServer so that tests may be reliably failed on error. | ||
*/ | ||
constructor(handlers, {debug = false, errorCallback = null} = {}) { | ||
this.handlers = handlers; | ||
this.debug = debug; | ||
this.handle = this.handle.bind(this); | ||
this.errorCallback = errorCallback; | ||
} | ||
handle(url, params) { | ||
for (const [route, handler] of Object.entries(this.handlers)) { | ||
// Order routes by url length (descending). | ||
// This ensures that when route overlap occurs (e.g. /foo/bar and /foo/bar/baz) | ||
// the more-specific route (/foo/bar/baz) is checked first. Otherwise, some | ||
// routes will be silently ignored when more-generic (shorter) routes exist. | ||
const routes = Object.entries(this.handlers).sort(([aRoute], [bRoute]) => { | ||
// If route lengths are the same, sort alphabetically; | ||
// otherwise, sort by route string length (descending). | ||
// Route length + alpha makes sort fully deterministic. | ||
if (bRoute.length === aRoute.length) { | ||
return bRoute.toLowerCase() < aRoute.toLowerCase() ? 1 : -1; | ||
} else { | ||
return bRoute.length - aRoute.length; | ||
} | ||
}); | ||
for (const [route, handler] of routes) { | ||
if (url.includes(route)) { | ||
if (params && params.body) { | ||
params = JSON.parse(params.body); | ||
if (isString(params.body)) { | ||
// Allow string or JSON params.body; if valid JSON, | ||
// parse body to JS object before passing to handler function. | ||
try { | ||
params = JSON.parse(params.body); | ||
} catch (err) { | ||
params = params.body; | ||
} | ||
} else { | ||
throw new Error(`Invalid body: ${params.body}`); | ||
} | ||
} | ||
let response = isFunction(handler) ? handler(url, params) : handler; | ||
@@ -57,9 +95,57 @@ | ||
/** | ||
* Start the FetchServer, sinon.stub'ing `fetch` with a fake version. | ||
*/ | ||
start() { | ||
if (window.fetch.hasOwnProperty(`restore`)) { | ||
this.restore(); // clean up prior FetchServer stubs (if necessary) | ||
const fakeFetch = (url, params) => { | ||
try { | ||
return this.handle(url, params); | ||
} catch (err) { | ||
// errorCallback allows hooking test framework error handling utils | ||
// into FetchServer so that tests may be reliably failed on error: | ||
isFunction(this.errorCallback) && this.errorCallback(err); | ||
throw err; // rethrow error | ||
} | ||
}; | ||
// stub fetch in browser environment: | ||
if (typeof window !== `undefined` && window.fetch) { | ||
sinon.stub(window, `fetch`).callsFake(fakeFetch); | ||
} | ||
// stub fetch in NodeJS environment: | ||
if (typeof global !== `undefined` && global.fetch) { | ||
sinon.stub(global, `fetch`).callsFake(fakeFetch); | ||
} | ||
} | ||
/** | ||
* Restore sinon.stub'ed `fetch` to its original state. | ||
*/ | ||
static restore() { | ||
// restore fetch in browser environment: | ||
if ( | ||
typeof window !== `undefined` && window.fetch && | ||
Object.hasOwnProperty.call(window.fetch, `restore`) | ||
) { | ||
window.fetch.restore(); | ||
} | ||
sinon.stub(window, `fetch`).callsFake(this.handle); | ||
// restore fetch in NodeJS environment: | ||
if ( | ||
typeof global !== `undefined` && global.fetch && | ||
Object.hasOwnProperty.call(global.fetch, `restore`) | ||
) { | ||
global.fetch.restore(); | ||
} | ||
} | ||
/** | ||
* Restore sinon.stub'ed `fetch` to its original state. | ||
* (convenience method for calling from class instance) | ||
*/ | ||
restore() { | ||
FetchServer.restore(); | ||
} | ||
debugLog({url, params, response}) { | ||
@@ -66,0 +152,0 @@ if (this.debug) { |
{ | ||
"name": "domsuite", | ||
"version": "0.3.1", | ||
"version": "0.4.0", | ||
"description": "Browser testing/automation utilities with async/await", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
40858
978