Skip to content

Commit

Permalink
fix: exception autocapture (#1261)
Browse files Browse the repository at this point in the history
  • Loading branch information
pauldambra authored Jun 25, 2024
1 parent 8ddea5a commit 350176e
Show file tree
Hide file tree
Showing 13 changed files with 426 additions and 246 deletions.
31 changes: 31 additions & 0 deletions cypress/e2e/error-tracking.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { start } from '../support/setup'

describe('Exception autocapture', () => {
beforeEach(() => {
start({
decideResponseOverrides: {
autocaptureExceptions: true,
},
url: './playground/cypress',
})

cy.on('uncaught:exception', () => {
// otherwise the exception we throw on purpose causes the test to fail
return false
})
})

it('captures exceptions', () => {
cy.get('[data-cy-button-throws-error]').click()
cy.phCaptures({ full: true }).then((captures) => {
expect(captures.map((c) => c.event)).to.deep.equal(['$pageview', '$autocapture', '$exception'])
expect(captures[2].event).to.be.eql('$exception')
expect(captures[2].properties.$exception_message).to.be.eql('This is an error')
expect(captures[2].properties.$exception_type).to.be.eql('Error')
expect(captures[2].properties.$exception_source).to.match(/http:\/\/localhost:\d+\/playground\/cypress\//)
expect(captures[2].properties.$exception_personURL).to.match(
/http:\/\/localhost:\d+\/project\/test_token\/person\/[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}/
)
})
})
})
8 changes: 8 additions & 0 deletions cypress/support/e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,12 @@ beforeEach(() => {
cy.readFile('dist/surveys.js.map').then((body) => {
cy.intercept('/static/surveys.js.map', { body })
})

cy.readFile('dist/exception-autocapture.js').then((body) => {
cy.intercept('/static/exception-autocapture.js*', { body })
})

cy.readFile('dist/exception-autocapture.js.map').then((body) => {
cy.intercept('/static/exception-autocapture.js.map', { body })
})
})
4 changes: 4 additions & 0 deletions playground/cypress/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
Sensitive attributes!
</button>

<button data-cy-button-throws-error="true" onclick="throw new Error('This is an error')">
Throw error
</button>

<script>
!function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.async=!0,p.src=s.api_host+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="capture identify alias people.set people.set_once set_config register register_once unregister opt_out_capturing has_opted_out_capturing opt_in_capturing reset isFeatureEnabled onFeatureFlags getSurveys getActiveMatchingSurveys".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
</script>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from '../../../extensions/exception-autocapture/error-conversion'

import { isNull } from '../../../utils/type-utils'
import { expect } from '@jest/globals'

// ugh, jest
// can't reference PromiseRejectionEvent to construct it 🤷
Expand Down Expand Up @@ -36,6 +37,7 @@ describe('Error conversion', () => {
$exception_type: 'InternalError',
$exception_message: 'but somehow still a string',
$exception_is_synthetic: true,
$exception_level: 'error',
}
expect(errorToProperties(['Uncaught exception: InternalError: but somehow still a string'])).toEqual(expected)
})
Expand All @@ -45,6 +47,7 @@ describe('Error conversion', () => {
$exception_type: 'Error',
$exception_message: 'Non-Error exception captured with keys: foo, string',
$exception_is_synthetic: true,
$exception_level: 'error',
}
expect(errorToProperties([{ string: 'candidate', foo: 'bar' } as unknown as Event])).toEqual(expected)
})
Expand All @@ -54,6 +57,7 @@ describe('Error conversion', () => {
$exception_type: 'MouseEvent',
$exception_message: 'Non-Error exception captured with keys: isTrusted',
$exception_is_synthetic: true,
$exception_level: 'error',
}
const event = new MouseEvent('click', { bubbles: true, cancelable: true, composed: true })
expect(errorToProperties([event])).toEqual(expected)
Expand All @@ -67,9 +71,10 @@ describe('Error conversion', () => {
throw new Error("this mustn't be null")
}

expect(Object.keys(errorProperties)).toHaveLength(3)
expect(Object.keys(errorProperties)).toHaveLength(4)
expect(errorProperties.$exception_type).toEqual('Error')
expect(errorProperties.$exception_message).toEqual('oh no an error has happened')
expect(errorProperties.$exception_level).toEqual('error')
// the stack trace changes between runs, so we just check that it's there
expect(errorProperties.$exception_stack_trace_raw).toBeDefined()
expect(errorProperties.$exception_stack_trace_raw).toContain('{"filename')
Expand All @@ -84,6 +89,7 @@ describe('Error conversion', () => {
const expected: ErrorProperties = {
$exception_type: 'DOMError',
$exception_message: 'click: foo',
$exception_level: 'error',
}
const event = new FakeDomError('click', 'foo')
expect(errorToProperties([event as unknown as Event])).toEqual(expected)
Expand All @@ -97,9 +103,10 @@ describe('Error conversion', () => {
throw new Error("this mustn't be null")
}

expect(Object.keys(errorProperties)).toHaveLength(4)
expect(Object.keys(errorProperties)).toHaveLength(5)
expect(errorProperties.$exception_type).toEqual('dom-exception')
expect(errorProperties.$exception_message).toEqual('oh no disaster')
expect(errorProperties.$exception_level).toEqual('error')
// the stack trace changes between runs, so we just check that it's there
expect(errorProperties.$exception_stack_trace_raw).toBeDefined()
expect(errorProperties.$exception_stack_trace_raw).toContain('{"filename')
Expand All @@ -113,9 +120,10 @@ describe('Error conversion', () => {
throw new Error("this mustn't be null")
}

expect(Object.keys(errorProperties)).toHaveLength(3)
expect(Object.keys(errorProperties)).toHaveLength(4)
expect(errorProperties.$exception_type).toEqual('Error')
expect(errorProperties.$exception_message).toEqual('the real error is hidden inside')
expect(errorProperties.$exception_level).toEqual('error')
// the stack trace changes between runs, so we just check that it's there
expect(errorProperties.$exception_stack_trace_raw).toBeDefined()
expect(errorProperties.$exception_stack_trace_raw).toContain('{"filename')
Expand All @@ -129,6 +137,7 @@ describe('Error conversion', () => {
$exception_message: 'string candidate',
$exception_source: 'a source',
$exception_type: 'Error',
$exception_level: 'error',
}
expect(errorToProperties(['string candidate', 'a source', 12, 200])).toEqual(expected)
})
Expand All @@ -143,10 +152,11 @@ describe('Error conversion', () => {
const errorProperties: ErrorProperties = unhandledRejectionToProperties([
ce as unknown as PromiseRejectionEvent,
])
expect(Object.keys(errorProperties)).toHaveLength(4)
expect(Object.keys(errorProperties)).toHaveLength(5)
expect(errorProperties.$exception_type).toEqual('UnhandledRejection')
expect(errorProperties.$exception_message).toEqual('a wrapped rejection event')
expect(errorProperties.$exception_handled).toEqual(false)
expect(errorProperties.$exception_level).toEqual('error')
// the stack trace changes between runs, so we just check that it's there
expect(errorProperties.$exception_stack_trace_raw).toBeDefined()
expect(errorProperties.$exception_stack_trace_raw).toContain('{"filename')
Expand All @@ -160,11 +170,12 @@ describe('Error conversion', () => {
const errorProperties: ErrorProperties = unhandledRejectionToProperties([
pre as unknown as PromiseRejectionEvent,
])
expect(Object.keys(errorProperties)).toHaveLength(3)
expect(Object.keys(errorProperties)).toHaveLength(4)
expect(errorProperties.$exception_type).toEqual('UnhandledRejection')
expect(errorProperties.$exception_message).toEqual(
'Non-Error promise rejection captured with value: My house is on fire'
)
expect(errorProperties.$exception_handled).toEqual(false)
expect(errorProperties.$exception_level).toEqual('error')
})
})
Loading

0 comments on commit 350176e

Please sign in to comment.