@revoloo/cypress6
Version:
Cypress.io end to end testing tool
163 lines (133 loc) • 4.24 kB
text/typescript
import _ from 'lodash'
import {
CyHttpMessages,
SERIALIZABLE_RES_PROPS,
NetEventFrames,
} from '@packages/net-stubbing/lib/types'
import {
validateStaticResponse,
parseStaticResponseShorthand,
STATIC_RESPONSE_KEYS,
getBackendStaticResponse,
} from '../static-response-utils'
import $errUtils from '../../../cypress/error_utils'
import { HandlerFn } from './'
import Bluebird from 'bluebird'
import { parseJsonBody } from './utils'
export const onResponseReceived: HandlerFn<NetEventFrames.HttpResponseReceived> = (Cypress, frame, { getRoute, getRequest, emitNetEvent }) => {
const { res, requestId, routeHandlerId } = frame
const request = getRequest(frame.routeHandlerId, frame.requestId)
parseJsonBody(res)
let sendCalled = false
let resolved = false
if (request) {
request.state = 'ResponseReceived'
request.log.fireChangeEvent()
if (!request.responseHandler) {
// this is notification-only, update the request with the response attributes and end
request.response = res
return
}
}
const continueFrame: NetEventFrames.HttpResponseContinue = {
routeHandlerId,
requestId,
}
const sendContinueFrame = () => {
// copy changeable attributes of userRes to res in frame
// if the user is setting a StaticResponse, use that instead
// @ts-ignore
continueFrame.res = {
..._.pick(continueFrame.staticResponse || userRes, SERIALIZABLE_RES_PROPS),
}
if (request) {
request.response = _.clone(continueFrame.res)
request.state = 'ResponseIntercepted'
request.log.fireChangeEvent()
}
if (_.isObject(continueFrame.res!.body)) {
continueFrame.res!.body = JSON.stringify(continueFrame.res!.body)
}
emitNetEvent('http:response:continue', continueFrame)
}
const userRes: CyHttpMessages.IncomingHttpResponse = {
...res,
send (staticResponse?, maybeBody?, maybeHeaders?) {
if (resolved) {
return $errUtils.throwErrByPath('net_stubbing.response_handling.send_called_after_resolved', { args: { res } })
}
if (sendCalled) {
return $errUtils.throwErrByPath('net_stubbing.response_handling.multiple_send_calls', { args: { res } })
}
sendCalled = true
const shorthand = parseStaticResponseShorthand(staticResponse, maybeBody, maybeHeaders)
if (shorthand) {
staticResponse = shorthand
}
if (staticResponse) {
validateStaticResponse('res.send', staticResponse)
// arguments to res.send() are merged with the existing response
const _staticResponse = _.defaults({}, staticResponse, _.pick(res, STATIC_RESPONSE_KEYS))
_.defaults(_staticResponse.headers, res.headers)
continueFrame.staticResponse = getBackendStaticResponse(_staticResponse)
}
return sendContinueFrame()
},
delay (delay) {
continueFrame.delay = delay
return this
},
throttle (throttleKbps) {
continueFrame.throttleKbps = throttleKbps
return this
},
}
if (!request) {
return sendContinueFrame()
}
const timeout = Cypress.config('defaultCommandTimeout')
const curTest = Cypress.state('test')
return Bluebird.try(() => {
return request.responseHandler!(userRes)
})
.catch((err) => {
$errUtils.throwErrByPath('net_stubbing.response_handling.cb_failed', {
args: {
err,
req: request.request,
route: _.get(getRoute(routeHandlerId), 'options'),
res,
},
errProps: {
appendToStack: {
title: 'From response callback',
content: err.stack,
},
},
})
})
.timeout(timeout)
.catch(Bluebird.TimeoutError, (err) => {
if (Cypress.state('test') !== curTest) {
// active test has changed, ignore the timeout
return
}
$errUtils.throwErrByPath('net_stubbing.response_handling.cb_timeout', {
args: {
timeout,
req: request.request,
route: _.get(getRoute(routeHandlerId), 'options'),
res,
},
})
})
.then(() => {
if (!sendCalled) {
// user did not call send, send response
userRes.send()
}
})
.finally(() => {
resolved = true
})
}