@pollyjs/core
Advanced tools
Comparing version 2.3.0 to 2.4.0
@@ -6,2 +6,13 @@ # Change Log | ||
# [2.4.0](https://github.com/netflix/pollyjs/tree/master/packages/@pollyjs/core/compare/v2.3.2...v2.4.0) (2019-04-27) | ||
### Features | ||
* **core:** Improved control flow with `times` and `stopPropagation` ([#202](https://github.com/netflix/pollyjs/tree/master/packages/[@pollyjs](https://github.com/pollyjs)/core/issues/202)) ([2c8231e](https://github.com/netflix/pollyjs/tree/master/packages/@pollyjs/core/commit/2c8231e)) | ||
# [2.3.0](https://github.com/netflix/pollyjs/tree/master/packages/@pollyjs/core/compare/v2.2.0...v2.3.0) (2019-02-27) | ||
@@ -8,0 +19,0 @@ |
{ | ||
"name": "@pollyjs/core", | ||
"version": "2.3.0", | ||
"version": "2.4.0", | ||
"description": "Record, replay, and stub HTTP Interactions", | ||
@@ -53,7 +53,7 @@ "main": "dist/cjs/pollyjs-core.js", | ||
"devDependencies": { | ||
"@pollyjs/adapter": "^2.2.0", | ||
"@pollyjs/persister": "^2.1.0", | ||
"@pollyjs/adapter": "^2.4.0", | ||
"@pollyjs/persister": "*", | ||
"rollup": "^0.67.0" | ||
}, | ||
"gitHead": "3c10e74b2f7fb7bdb9d9f63d5038e090a1431ac7" | ||
"gitHead": "87e305e1e13c89f5a88ce4cc5210408a5eef2ae1" | ||
} |
import { assert } from '@pollyjs/utils'; | ||
import isObjectLike from 'lodash-es/isObjectLike'; | ||
import cancelFnAfterNTimes from '../utils/cancel-fn-after-n-times'; | ||
import { validateTimesOption } from '../utils/validators'; | ||
import Event from './event'; | ||
const EVENTS = Symbol(); | ||
@@ -67,5 +72,7 @@ const EVENT_NAMES = Symbol(); | ||
* @param {Function} listener - The callback function | ||
* @param {Object} [options={}] | ||
* @param {Number} options.times - listener will be cancelled after this many times | ||
* @returns {EventEmitter} | ||
*/ | ||
on(eventName, listener) { | ||
on(eventName, listener, options = {}) { | ||
assertEventName(eventName, this[EVENT_NAMES]); | ||
@@ -75,2 +82,3 @@ assertListener(listener); | ||
const events = this[EVENTS]; | ||
const { times } = options; | ||
@@ -81,2 +89,27 @@ if (!events.has(eventName)) { | ||
if (times) { | ||
validateTimesOption(times); | ||
const tempListener = cancelFnAfterNTimes(listener, times, () => | ||
this.off(eventName, tempListener) | ||
); | ||
/* | ||
Remove any existing listener or tempListener that match this one. | ||
For example, if the following would get called: | ||
this.on('request', listener); | ||
this.on('request', listener, { times: 1 }); | ||
We want to make sure that there is only one instance of the given | ||
listener for the given event. | ||
*/ | ||
this.off(eventName, listener); | ||
// Save the original listener on the temp one so we can easily match it | ||
// given the original. | ||
tempListener.listener = listener; | ||
listener = tempListener; | ||
} | ||
events.get(eventName).add(listener); | ||
@@ -94,16 +127,8 @@ | ||
* @param {Function} listener - The callback function | ||
* @param {Object} [options={}] | ||
* @returns {EventEmitter} | ||
*/ | ||
once(eventName, listener) { | ||
assertEventName(eventName, this[EVENT_NAMES]); | ||
assertListener(listener); | ||
once(eventName, listener, options = {}) { | ||
this.on(eventName, listener, { ...options, times: 1 }); | ||
const once = (...args) => { | ||
this.off(eventName, once); | ||
return listener(...args); | ||
}; | ||
this.on(eventName, once); | ||
return this; | ||
@@ -129,2 +154,9 @@ } | ||
events.get(eventName).delete(listener); | ||
// Remove any temp listeners that use the provided listener | ||
this.listeners(eventName).forEach(l => { | ||
if (l.listener === listener) { | ||
events.get(eventName).delete(l); | ||
} | ||
}); | ||
} else { | ||
@@ -170,4 +202,4 @@ events.get(eventName).clear(eventName); | ||
* | ||
* Returns a promise that will resolve to `true` if the event had listeners, | ||
* `false` otherwise. | ||
* Returns a promise that will resolve to `false` if a listener stopped | ||
* propagation, `true` otherwise. | ||
* | ||
@@ -182,11 +214,13 @@ * @async | ||
if (this.hasListeners(eventName)) { | ||
for (const listener of this.listeners(eventName)) { | ||
await listener(...args); | ||
const event = new Event(eventName); | ||
for (const listener of this.listeners(eventName)) { | ||
await listener(...args, event); | ||
if (event.shouldStopPropagating) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
return false; | ||
return true; | ||
} | ||
@@ -199,4 +233,4 @@ | ||
* | ||
* Returns a promise that will resolve to `true` if the event had listeners, | ||
* `false` otherwise. | ||
* Returns a promise that will resolve to `false` if a listener stopped | ||
* propagation, `true` otherwise. | ||
* | ||
@@ -211,11 +245,13 @@ * @async | ||
if (this.hasListeners(eventName)) { | ||
await Promise.all( | ||
this.listeners(eventName).map(listener => listener(...args)) | ||
); | ||
const event = new Event(eventName); | ||
return true; | ||
await Promise.all( | ||
this.listeners(eventName).map(listener => listener(...args, event)) | ||
); | ||
if (event.shouldStopPropagating) { | ||
return false; | ||
} | ||
return false; | ||
return true; | ||
} | ||
@@ -230,3 +266,3 @@ | ||
* | ||
* Returns `true` if the event had listeners, `false` otherwise. | ||
* Returns`false` if a listener stopped propagation, `true` otherwise. | ||
* | ||
@@ -240,17 +276,19 @@ * @param {String} eventName - The name of the event | ||
if (this.hasListeners(eventName)) { | ||
this.listeners(eventName).forEach(listener => { | ||
const returnValue = listener(...args); | ||
const event = new Event(eventName); | ||
assert( | ||
`Attempted to emit a synchronous event "${eventName}" but an asynchronous listener was called.`, | ||
!(isObjectLike(returnValue) && typeof returnValue.then === 'function') | ||
); | ||
}); | ||
for (const listener of this.listeners(eventName)) { | ||
const returnValue = listener(...args, event); | ||
return true; | ||
assert( | ||
`Attempted to emit a synchronous event "${eventName}" but an asynchronous listener was called.`, | ||
!(isObjectLike(returnValue) && typeof returnValue.then === 'function') | ||
); | ||
if (event.shouldStopPropagating) { | ||
return false; | ||
} | ||
} | ||
return false; | ||
return true; | ||
} | ||
} |
@@ -19,2 +19,3 @@ import md5 from 'blueimp-md5'; | ||
import EventEmitter from './event-emitter'; | ||
import Interceptor from './interceptor'; | ||
@@ -58,2 +59,5 @@ const { keys, freeze } = Object; | ||
// Interceptor instance to be passed to each of the intercept handlers | ||
this._interceptor = new Interceptor(); | ||
// Lookup the associated route for this request | ||
@@ -60,0 +64,0 @@ this[ROUTE] = polly.server.lookup(this.method, this.url); |
import { assert } from '@pollyjs/utils'; | ||
import EventEmitter from '../-private/event-emitter'; | ||
import cancelFnAfterNTimes from '../utils/cancel-fn-after-n-times'; | ||
import { | ||
validateTimesOption, | ||
validateRecordingName, | ||
@@ -14,3 +16,5 @@ validateRequestConfig | ||
this.set('config', {}); | ||
this.set('defaultOptions', {}); | ||
this.set('filters', new Set()); | ||
this._eventEmitter = new EventEmitter({ | ||
@@ -28,4 +32,7 @@ eventNames: [ | ||
on(eventName, listener) { | ||
this._eventEmitter.on(eventName, listener); | ||
on(eventName, listener, options = {}) { | ||
this._eventEmitter.on(eventName, listener, { | ||
...this.get('defaultOptions'), | ||
...options | ||
}); | ||
@@ -57,3 +64,3 @@ return this; | ||
intercept(fn) { | ||
intercept(fn, options = {}) { | ||
assert( | ||
@@ -64,2 +71,11 @@ `Invalid intercept handler provided. Expected function, received: "${typeof fn}".`, | ||
options = { ...this.get('defaultOptions'), ...options }; | ||
if ('times' in options) { | ||
validateTimesOption(options.times); | ||
fn = cancelFnAfterNTimes(fn, options.times, () => | ||
this.delete('intercept') | ||
); | ||
} | ||
this.set('intercept', fn); | ||
@@ -98,2 +114,13 @@ this.passthrough(false); | ||
} | ||
times(n) { | ||
if (!n && typeof n !== 'number') { | ||
delete this.get('defaultOptions').times; | ||
} else { | ||
validateTimesOption(n); | ||
this.get('defaultOptions').times = n; | ||
} | ||
return this; | ||
} | ||
} |
@@ -82,3 +82,7 @@ import mergeConfigs from '../utils/merge-configs'; | ||
for (const { route, handler } of this[HANDLERS]) { | ||
if (handler.has('intercept') && interceptor.shouldIntercept) { | ||
if (!interceptor.shouldIntercept || interceptor.shouldStopPropagating) { | ||
return; | ||
} | ||
if (handler.has('intercept')) { | ||
await handler.get('intercept')( | ||
@@ -101,6 +105,10 @@ requestWithParams(req, route), | ||
for (const { route, handler } of this[HANDLERS]) { | ||
const listeners = handler._eventEmitter.listeners(eventName); | ||
const shouldContinue = await handler._eventEmitter.emit( | ||
eventName, | ||
requestWithParams(req, route), | ||
...args | ||
); | ||
for (const listener of listeners) { | ||
await listener(requestWithParams(req, route), ...args); | ||
if (!shouldContinue) { | ||
return; | ||
} | ||
@@ -107,0 +115,0 @@ } |
@@ -19,3 +19,3 @@ import isObjectLike from 'lodash-es/isObjectLike'; | ||
`Invalid config provided. Expected object, received: "${typeof config}".`, | ||
isObjectLike(config) | ||
isObjectLike(config) && !Array.isArray(config) | ||
); | ||
@@ -37,1 +37,13 @@ | ||
} | ||
export function validateTimesOption(times) { | ||
assert( | ||
`Invalid number provided. Expected number, received: "${typeof times}".`, | ||
typeof times === 'number' | ||
); | ||
assert( | ||
`Invalid number provided. The number must be greater than 0, received "${typeof times}".`, | ||
times > 0 | ||
); | ||
} |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
3856584
44
25103