I am dealing with multiple nested try catch statements due to different wrappers and things passed to one another in a pipeline.
I am trying to get test coverage on a catch block for a function with the name of put by mocking the response and throwing an error when data is eventually passed somewhere invoking .json() on it and throwing an error of the shape defined below:
it('hits the catch', async () => {
fetch.mockImplementation(() =>
Promise.resolve({
ok: true,
json: () => {
throw { type: 'error', message: 'hit the catch' }
},
})
)
expect(
await put({
path: '/script/1',
body: {
name: 'newName',
},
})
).toThrow({
message: 'hit the catch',
type: 'error',
})
})
This function in turn calls a client function:
export const put = async <I, R>({ path, body }: { path: string; body: I }) => {
try {
return client<I>(getServiceUrl(path), 'PUT', getServiceHeaders(token), body)
} catch (e) {
throw handleClientError(e)
// return handleClientError(e)
}
}
The client function is a wrapper for fetch:
export const client = async <I>(
url: string,
method: 'POST' | 'GET' | 'PUT' | 'PATCH' | 'DELETE',
headers: HeadersInit,
body?: I
): Promise<ServiceResonse<I>> => {
try {
const res: Response = await fetch(url, {
method,
headers,
body: JSON.stringify(body),
})
return getServiceResponse(res)
} catch (e) {
throw handleClientError(e)
// return handleClientError(e)
}
}
And here are the various helper functions called as well:
export const getServiceHeaders = (token: string = ''): HeadersInit => {
try {
if (token) {
return {
'Content-Type': 'application/json',
Authorization: 'Bearer ' + token,
}
} else {
return {
'Content-Type': 'application/json',
}
}
} catch (e) {
const msg = 'Error getting service headers ' + JSON.stringify(e)
console.error(msg) //shouldn't ever happen
throw new Error(msg)
}
}
export const getServiceResponse = async <T>(
response: Response
): Promise<ServiceResonse<T>> => {
try {
const data = await response.json()
if (!response.ok) {
let error = {
code: (data && data.code) || 'error',
message:
(data && data.message) ||
response.statusText ||
'Unkown Error x03391aa00',
invalid: null,
}
switch (response.status) {
case 400:
if (error.code == 'invalid') {
return (error.invalid = ((data && data.errors) || []).reduce(
(items: RequestErrorResponseFields, i: APIResponseErrorItem) => {
items[i.field] = i.message
return items
},
{}
))
}
break
case 401:
// TODO handle error)
return { type: 'error', fields: {}, message: 'Invalid Token' }
break
}
return Promise.reject(error)
} else {
return data
}
} catch (e) {
throw e
}
}
export const getServiceUrl = (
path: string,
options: { [k: string]: string | number } = {}
): string => {
if (typeof path !== 'string') throw new Error('path must be of type string')
if (typeof options !== 'object')
throw new Error('options must be of type object')
const query = Object.keys(options)
.map(k => k + '=' + options[k])
.join('&')
//todo move to general configuration file, still use process.env
const baseUrl = process.env.API_URL || 'http://localhost/ipa'
return `${baseUrl}${path}${query.length ? '?' + query : ''}`
}
export const handleClientError = (
err: { message?: string } | RequestErrorResponse
) => {
if ('type' in err) {
throw err
// return Promise.reject(err)
} else {
throw {
code: 'error',
message: err.message || 'Unkown - 0x39aef3991',
errors: [],
}
// return Promise.reject({
// code: 'error',
// message: err.message || 'Unkown - 0x39aef3991',
// errors: [],
// })
}
}
I am trying to get testing coverage on the catch block in the put function by forcing an exception and passing it up to the catch but I can't seem to get it to reach. I am not sure at what point to force and exception and then how to pass it up and test against it. Does anyone have any ideas on how I should approach this or what I am missing?