Skip to content

Commit

Permalink
feat: ✨ add new reportUnhandledPromiseRejectionsAsHandled config option
Browse files Browse the repository at this point in the history
  • Loading branch information
gingerbenw committed Oct 16, 2024
1 parent 50b432a commit 90a0fe9
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 4 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## [Unreleased]

### Added

- Add new `reportUnhandledPromiseRejectionsAsHandled` config option

## [8.0.0] - 2024-08-29

### Summary
Expand Down
5 changes: 5 additions & 0 deletions packages/core/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,5 +168,10 @@ module.exports.schema = {
isArray(value) && value.length === filter(value, feature =>
feature && typeof feature === 'object' && typeof feature.name === 'string'
).length
},
reportUnhandledPromiseRejectionsAsHandled: {
defaultValue: () => false,
message: 'should be true|false',
validate: value => value === true || value === false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,34 @@ describe('plugin: node unhandled rejection handler', () => {
process.listeners('unhandledRejection')[0](new Error('never gonna catch me'), Promise.resolve())
})

it('should report unhandledRejection events as handled when reportUnhandledPromiseRejectionsAsHandled is true', (done) => {
const c = new Client({
apiKey: 'api_key',
reportUnhandledPromiseRejectionsAsHandled: true,
onUnhandledRejection: (err: Error, event: EventWithInternals) => {
expect(err.message).toBe('never gonna catch me')
expect(event._handledState.unhandled).toBe(false)
expect(event._handledState.severity).toBe('error')
expect(event._handledState.severityReason).toEqual({ type: 'unhandledPromiseRejection' })
plugin.destroy()
done()
},
plugins: [plugin]
}, {
...schema,
onUnhandledRejection: {
validate: (val: unknown) => typeof val === 'function',
message: 'should be a function',
defaultValue: () => {}
}
})
c._setDelivery(client => ({
sendEvent: (payload, cb) => cb(),
sendSession: (payload, cb) => cb()
}))
process.listeners('unhandledRejection')[0](new Error('never gonna catch me'), Promise.resolve())
})

it('should tolerate delivery errors', done => {
const c = new Client({
apiKey: 'api_key',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ module.exports = {
const ctx = client._clientContext && client._clientContext.getStore()
const c = ctx || client

// Report unhandled promise rejections as handled if the user has configured it
const unhandled = !client._config.reportUnhandledPromiseRejectionsAsHandled

const event = c.Event.create(err, false, {
severity: 'error',
unhandled: true,
unhandled,
severityReason: { type: 'unhandledPromiseRejection' }
}, 'unhandledRejection handler', 1)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ module.exports = {
// Do not attach any listeners if autoDetectErrors is disabled or unhandledRejections are not an enabled error type
if (!client._config.autoDetectErrors || !client._config.enabledErrorTypes.unhandledRejections) return () => { }

// Report unhandled promise rejections as handled if the user has configured it
const unhandled = !client._config.reportUnhandledPromiseRejectionsAsHandled

// Check if Hermes is available and is being used for promises
// React Native v0.63 and v0.64 include global.HermesInternal but not 'hasPromise'
if (global?.HermesInternal?.hasPromise?.() && global.HermesInternal.enablePromiseRejectionTracker) {
Expand All @@ -19,7 +22,7 @@ module.exports = {
onUnhandled: (id, rejection = {}) => {
const event = client.Event.create(rejection, false, {
severity: 'error',
unhandled: true,
unhandled,
severityReason: { type: 'unhandledPromiseRejection' }
}, 'promise rejection tracking', 1)

Expand All @@ -39,7 +42,7 @@ module.exports = {
onUnhandled: (id, error) => {
const event = client.Event.create(error, false, {
severity: 'error',
unhandled: true,
unhandled,
severityReason: { type: 'unhandledPromiseRejection' }
}, 'promise rejection tracking', 1)
client._notify(event)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,30 @@ describe('plugin: react native rejection handler', () => {
stop()
})

it('should report unhandledRejection events as handled when reportUnhandledPromiseRejectionsAsHandled is true', (done) => {
expect.assertions(1)

const c = new Client({ apiKey: 'api_key', reportUnhandledPromiseRejectionsAsHandled: true })
c._setDelivery(client => ({
sendEvent: (payload) => {
const r = JSON.parse(JSON.stringify(payload))
expect(r.events[0].unhandled).toBe(false)
done()
},
sendSession: () => { }
}))
const stop = plugin.load(c)
// in the interests of keeping the tests quick, TypeErrors get rejected quicker
// see: https://github.com/then/promise/blob/d980ed01b7a383bfec416c96095e2f40fd18ab34/src/rejection-tracking.js#L48-L54
try {
// @ts-ignore
String.floop()
} catch (e) {
RnPromise.reject(e)
}
stop()
})

it('should hook in to the hermes promise rejection tracker', (done) => {
// @ts-ignore
global.HermesInternal = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,30 @@ describe('plugin: unhandled rejection', () => {
handler({ reason: new Error('BAD_PROMISE') })
})

it('should report unhandledRejection events as handled when reportUnhandledPromiseRejectionsAsHandled is true', (done) => {
const p = plugin(window)
const client = new Client({
apiKey: 'API_KEY_YEAH',
reportUnhandledPromiseRejectionsAsHandled: true,
plugins: [p]
})

client._setDelivery(client => ({
sendEvent: (payload) => {
const event = payload.events[0].toJSON()
expect(event.unhandled).toBe(false)
expect(event.severityReason).toEqual({ type: 'unhandledPromiseRejection' })
// @ts-ignore
p.destroy(window)
done()
},
sendSession: () => {}
}))

// simulate an UnhandledRejection event
getUnhandledRejectionHandler()({ reason: new Error('BAD_PROMISE') })
})

it('handles bad user input', done => {
expect.assertions(6)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@ module.exports = (win = window) => {
}
} catch (e) {}

// Report unhandled promise rejections as handled if the user has configured it
const unhandled = !client._config.reportUnhandledPromiseRejectionsAsHandled

const event = client.Event.create(error, false, {
severity: 'error',
unhandled: true,
unhandled,
severityReason: { type: 'unhandledPromiseRejection' }
}, 'unhandledrejection handler', 1, client._logger)

Expand Down

0 comments on commit 90a0fe9

Please sign in to comment.