-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
f8fe3be
commit 996c44a
Showing
5 changed files
with
327 additions
and
257 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
import Promise from 'promise' | ||
import sinon, { assert, match } from 'sinon' | ||
|
||
import PromiseMiddleware from '../index' | ||
|
||
describe('execute', () => { | ||
let MyWrappedFetcher | ||
const sandbox = sinon.sandbox.create() | ||
|
||
const fetcher = () => new Promise(() => {}) | ||
|
||
beforeAll(() => { | ||
MyWrappedFetcher = new PromiseMiddleware(fetcher) | ||
}) | ||
|
||
afterEach(() => { | ||
sandbox.restore() | ||
}) | ||
|
||
it('calls _onRequest, giving the arguments, a setResponse and a setError function', () => { | ||
sandbox.spy(MyWrappedFetcher, '_onRequest') | ||
let args = [] | ||
MyWrappedFetcher.execute(...args) | ||
assert.calledOnce(MyWrappedFetcher._onRequest) | ||
assert.calledWith(MyWrappedFetcher._onRequest, match({ args })) | ||
}) | ||
|
||
describe('if setResponse is called', () => { | ||
const response = {} | ||
let args = [] | ||
beforeEach(() => { | ||
sandbox.stub(MyWrappedFetcher, '_onRequest').callsFake(({ setResponse }) => { | ||
setResponse(response) | ||
return {} | ||
}) | ||
}) | ||
|
||
afterEach(() => { | ||
sandbox.restore() | ||
}) | ||
|
||
it('calls _onSuccess with the arguments and the provided response', () => { | ||
sandbox.spy(MyWrappedFetcher, '_onSuccess') | ||
|
||
MyWrappedFetcher.execute(...args) | ||
|
||
assert.calledOnce(MyWrappedFetcher._onSuccess) | ||
assert.calledWith(MyWrappedFetcher._onSuccess, { | ||
args, | ||
res: response | ||
}) | ||
}) | ||
|
||
it('does not execute the action', () => { | ||
sandbox.spy(MyWrappedFetcher, '_action') | ||
|
||
MyWrappedFetcher.execute(...args) | ||
|
||
assert.notCalled(MyWrappedFetcher._action) | ||
}) | ||
}) | ||
|
||
describe('if setError is called', () => { | ||
const error = {} | ||
let args = [] | ||
beforeEach(() => { | ||
sandbox.stub(MyWrappedFetcher, '_onRequest').callsFake(({ setError }) => { | ||
setError(error) | ||
return {} | ||
}) | ||
}) | ||
|
||
afterEach(() => { | ||
sandbox.restore() | ||
}) | ||
|
||
it('calls _onError with the arguments and the provided error', () => { | ||
sandbox.spy(MyWrappedFetcher, '_onError') | ||
|
||
MyWrappedFetcher.execute(...args) | ||
|
||
assert.calledOnce(MyWrappedFetcher._onError) | ||
assert.calledWith(MyWrappedFetcher._onError, { | ||
args, | ||
err: error | ||
}) | ||
}) | ||
|
||
it('does not execute the action', () => { | ||
sandbox.spy(MyWrappedFetcher, '_action') | ||
|
||
MyWrappedFetcher.execute(...args) | ||
|
||
assert.notCalled(MyWrappedFetcher._action) | ||
}) | ||
}) | ||
|
||
describe('if _onRequest returns { wasStopped: true }', () => { | ||
let args = [] | ||
beforeEach(() => { | ||
sandbox.stub(MyWrappedFetcher, '_onRequest').returns({ wasStopped: true }) | ||
}) | ||
|
||
afterEach(() => { | ||
sandbox.restore() | ||
}) | ||
|
||
it('does not call _onSuccess', () => { | ||
sandbox.spy(MyWrappedFetcher, '_onSuccess') | ||
MyWrappedFetcher.execute(...args) | ||
assert.notCalled(MyWrappedFetcher._onSuccess) | ||
}) | ||
|
||
it('does not call _onError', () => { | ||
sandbox.spy(MyWrappedFetcher, '_onError') | ||
MyWrappedFetcher.execute(...args) | ||
assert.notCalled(MyWrappedFetcher._onError) | ||
}) | ||
|
||
it('does not call _action', () => { | ||
sandbox.spy(MyWrappedFetcher, '_action') | ||
MyWrappedFetcher.execute(...args) | ||
assert.notCalled(MyWrappedFetcher._action) | ||
}) | ||
}) | ||
|
||
describe('if the action gets called', () => { | ||
let args = [] | ||
|
||
afterEach(() => { | ||
sandbox.restore() | ||
}) | ||
|
||
describe('if it resolves', () => { | ||
it('calls _onSuccess with the arguments and the response', (done) => { | ||
const response = {} | ||
const MyWrappedFetcher = new PromiseMiddleware(() => new Promise(res => { | ||
res(response) | ||
setTimeout(() => { | ||
try { | ||
assert.calledOnce(MyWrappedFetcher._onSuccess) | ||
assert.calledWith(MyWrappedFetcher._onSuccess, { | ||
args, | ||
res: response | ||
}) | ||
done() | ||
} catch (e) { | ||
done(e) | ||
} | ||
}) | ||
})) | ||
|
||
sandbox.spy(MyWrappedFetcher, '_onSuccess') | ||
sandbox.stub(MyWrappedFetcher, '_onRequest').returns({}) | ||
MyWrappedFetcher.execute(...args) | ||
}) | ||
}) | ||
|
||
describe('if it fails', () => { | ||
it('calls _onError with the arguments and the error', (done) => { | ||
const error = {} | ||
const MyWrappedFetcher = new PromiseMiddleware(() => new Promise((res, rej) => { | ||
rej(error) | ||
setTimeout(() => { | ||
try { | ||
assert.calledOnce(MyWrappedFetcher._onError) | ||
assert.calledWith(MyWrappedFetcher._onError, { | ||
args, | ||
err: error | ||
}) | ||
done() | ||
} catch (e) { | ||
done(e) | ||
} | ||
}) | ||
})) | ||
|
||
sandbox.spy(MyWrappedFetcher, '_onError') | ||
sandbox.stub(MyWrappedFetcher, '_onRequest').returns({}) | ||
MyWrappedFetcher.execute(...args) | ||
}) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import Promise from 'promise' | ||
import PromiseMiddleware from '../index' | ||
|
||
|
||
describe('PromiseMiddlware', () => { | ||
const fetcher = () => new Promise(() => {}) | ||
|
||
let MyWrappedFetcher | ||
beforeAll(() => { | ||
MyWrappedFetcher = new PromiseMiddleware(fetcher) | ||
}) | ||
|
||
describe('on construct', () => { | ||
it('stores the given action', () => { | ||
expect(MyWrappedFetcher._action).toEqual(fetcher) | ||
}) | ||
|
||
it('initializes an empty middleware cache', () => { | ||
expect(MyWrappedFetcher._middleware).toEqual({}) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
// @flow | ||
type EventProperties = {| | ||
onRequest: {| args: Array<*>, setResponse: (*) => void, setError: (*) => void |}, | ||
onSuccess: {| args: Array<*>, res: * |}, | ||
onError: {| args: Array<*>, err: * |} | ||
|} | ||
|
||
type MiddlewareTypes = {| | ||
onRequest: (eventProperties: $PropertyType<EventProperties, 'onRequest'>, stop: Stop) => void, | ||
onSuccess: (eventProperties: $PropertyType<EventProperties, 'onSuccess'>, stop: Stop) => void, | ||
onError: (eventProperties: $PropertyType<EventProperties, 'onError'>, stop: Stop) => void, | ||
|} | ||
|
||
type Stop = () => void | ||
|
||
type ActionMiddleware = { | ||
onRequest?: ?Array<$PropertyType<MiddlewareTypes, 'onRequest'>>, | ||
onSuccess?: ?Array<$PropertyType<MiddlewareTypes, 'onSuccess'>>, | ||
onError?: ?Array<$PropertyType<MiddlewareTypes, 'onError'>> | ||
} | ||
|
||
type Action = (*) => Promise<*> | ||
|
||
export default class PromiseMiddleware<T: Action> { | ||
_action: T | ||
_middleware: ActionMiddleware | ||
|
||
constructor (action: T) { | ||
this._action = action | ||
this._middleware = {} | ||
} | ||
|
||
execute (...args: Array<*>) { | ||
let hardCodedResponse = undefined | ||
const setResponse = res => hardCodedResponse = res | ||
|
||
let hardCodedError = undefined | ||
const setError = err => hardCodedError = err | ||
|
||
const requestMiddlewareResult = this._onRequest({ args, setResponse, setError }) | ||
|
||
// if any of the onRequest middleware asked to stop | ||
if (requestMiddlewareResult.wasStopped) { | ||
return | ||
} | ||
|
||
// if any of the onRequest middleware called setResponse we call on sucess middleware | ||
if (hardCodedResponse !== undefined) { | ||
this._onSuccess({ args, res: hardCodedResponse }) | ||
return | ||
} | ||
|
||
// if any of the onRequest middleware called setError we call on error middleware | ||
if (hardCodedError !== undefined) { | ||
this._onError({ args, err: hardCodedError }) | ||
return | ||
} | ||
|
||
// otherwise execute the action and either call onSuccess or onError middleware | ||
// if any of those middleware call stop, no further middleware will be executed | ||
this._action(...args) | ||
.then( | ||
res => { | ||
this._onSuccess({ args, res }) | ||
}, | ||
err => { | ||
this._onError({ args, err }) | ||
} | ||
) | ||
} | ||
|
||
applyOnRequestMiddleware (middleware: $PropertyType<MiddlewareTypes, 'onRequest'>) { | ||
this._middleware.onRequest = this._middleware.onRequest || [] | ||
this._middleware.onRequest.push(middleware) | ||
} | ||
applyOnSuccessMiddleware (middleware: $PropertyType<MiddlewareTypes, 'onSuccess'>) { | ||
this._middleware.onSuccess = this._middleware.onSuccess || [] | ||
this._middleware.onSuccess.push(middleware) | ||
} | ||
applyOnErrorMiddleware (middleware: $PropertyType<MiddlewareTypes, 'onError'>) { | ||
this._middleware.onError = this._middleware.onError || [] | ||
this._middleware.onError.push(middleware) | ||
} | ||
_onRequest (eventProperties: $PropertyType<EventProperties, 'onRequest'>) { | ||
return this._callMiddleware(this._middleware.onRequest, eventProperties) | ||
} | ||
_onSuccess (eventProperties: $PropertyType<EventProperties, 'onSuccess'>) { | ||
return this._callMiddleware(this._middleware.onSuccess, eventProperties) | ||
} | ||
_onError (eventProperties: $PropertyType<EventProperties, 'onError'>) { | ||
return this._callMiddleware(this._middleware.onError, eventProperties) | ||
} | ||
|
||
_callMiddleware (middleware: ?Array<*>, eventProperties: *) { | ||
if (!middleware || middleware.length === 0) { | ||
return { finished: true } | ||
} | ||
|
||
const [ thisMiddleware, ...restMiddleware ] = middleware | ||
|
||
let wasStopped = false | ||
const stop = () => { | ||
wasStopped = true | ||
} | ||
|
||
thisMiddleware(eventProperties, stop) | ||
|
||
if (wasStopped) { | ||
return { wasStopped: true } | ||
} else { | ||
return this._callMiddleware(restMiddleware, eventProperties) | ||
} | ||
} | ||
} |
Oops, something went wrong.