Skip to content

Commit

Permalink
continue building execute spec
Browse files Browse the repository at this point in the history
  • Loading branch information
Ricardo-Marques committed Apr 8, 2018
1 parent f8fe3be commit 996c44a
Show file tree
Hide file tree
Showing 5 changed files with 327 additions and 257 deletions.
184 changes: 184 additions & 0 deletions src/PromiseMiddleware/__spec/execute.spec.js
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)
})
})
})
})
22 changes: 22 additions & 0 deletions src/PromiseMiddleware/__spec/index.spec.js
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({})
})
})
})
119 changes: 119 additions & 0 deletions src/PromiseMiddleware/index.js
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)
}
}
}
Loading

0 comments on commit 996c44a

Please sign in to comment.