Example #1
0
 it('should add a stacktrace when missing', done => {
   const c = new Client(VALID_NOTIFIER)
   c.delivery(client => ({
     sendReport: (report, cb) => {
       expect(report.events[0].errorMessage).toBe('ENOENT: no such file or directory, open \'does not exist\'')
       expect(report.events[0].stacktrace[0].file).toBe(`${__filename}`)
       cb(null)
     },
     sendSession: () => {}
   }))
   c.setOptions({
     apiKey: 'api_key',
     onUncaughtException: () => {
       done()
     }
   })
   c.configure({
     ...schema,
     onUncaughtException: {
       validate: val => typeof val === 'function',
       message: 'should be a function',
       defaultValue: () => {}
     }
   })
   c.use(plugin)
   const contextualize = c.getPlugin('contextualize')
   contextualize(() => {
     fs.createReadStream('does not exist')
   })
 })
 it('should tolerate delivery errors', done => {
   const c = new Client(VALID_NOTIFIER)
   c.delivery(client => ({
     sendReport: (...args) => args[args.length - 1](new Error('failed')),
     sendSession: (...args) => args[args.length - 1]()
   }))
   c.setOptions({
     apiKey: 'api_key',
     onUncaughtException: (err, report) => {
       expect(err.message).toBe('never gonna catch me')
       expect(report.errorMessage).toBe('never gonna catch me')
       expect(report._handledState.unhandled).toBe(true)
       expect(report._handledState.severity).toBe('error')
       expect(report._handledState.severityReason).toEqual({ type: 'unhandledException' })
       plugin.destroy()
       done()
     }
   })
   c.configure({
     ...schema,
     onUncaughtException: {
       validate: val => typeof val === 'function',
       message: 'should be a function',
       defaultValue: () => {}
     }
   })
   c.use(plugin)
   process.listeners('uncaughtException')[1](new Error('never gonna catch me'))
 })
Example #3
0
 it('should tolerate a failed report', done => {
   const c = new Client(VALID_NOTIFIER)
   c.delivery(client => ({
     sendReport: (report, cb) => {
       cb(new Error('sending failed'))
     },
     sendSession: () => {}
   }))
   c.setOptions({
     apiKey: 'api_key',
     onUncaughtException: (err) => {
       expect(err.message).toBe('no item available')
       done()
     }
   })
   c.configure({
     ...schema,
     onUncaughtException: {
       validate: val => typeof val === 'function',
       message: 'should be a function',
       defaultValue: () => {}
     }
   })
   c.use(plugin)
   const contextualize = c.getPlugin('contextualize')
   contextualize(() => {
     load(8, (err) => {
       if (err) throw err
     })
   })
 })
Example #4
0
 it('tracks handled/unhandled error counts and sends them in error payloads', (done) => {
   const c = new Client(VALID_NOTIFIER)
   c.setOptions({ apiKey: 'API_KEY' })
   c.configure()
   let i = 0
   c.use(plugin)
   c.delivery(client => ({
     sendSession: () => {},
     sendReport: (report, cb) => {
       if (++i < 10) return
       const r = JSON.parse(JSON.stringify(report.events[0]))
       expect(r.session).toBeDefined()
       expect(r.session.events.handled).toBe(6)
       expect(r.session.events.unhandled).toBe(4)
       done()
     }
   }))
   const sessionClient = c.startSession()
   sessionClient.notify(new Error('broke'))
   sessionClient.notify(new c.BugsnagReport('err', 'bad', [], { unhandled: true, severity: 'error', severityReason: { type: 'unhandledException' } }))
   sessionClient.notify(new Error('broke'))
   sessionClient.notify(new Error('broke'))
   sessionClient.notify(new c.BugsnagReport('err', 'bad', [], { unhandled: true, severity: 'error', severityReason: { type: 'unhandledException' } }))
   sessionClient.notify(new Error('broke'))
   sessionClient.notify(new Error('broke'))
   sessionClient.notify(new Error('broke'))
   sessionClient.notify(new c.BugsnagReport('err', 'bad', [], { unhandled: true, severity: 'error', severityReason: { type: 'unhandledException' } }))
   sessionClient.notify(new c.BugsnagReport('err', 'bad', [], { unhandled: true, severity: 'error', severityReason: { type: 'unhandledException' } }))
 })
Example #5
0
 it('doesn’t send when releaseStage is not in notifyReleaseStages', (done) => {
   const c = new Client(VALID_NOTIFIER)
   c.setOptions({ apiKey: 'API_KEY', releaseStage: 'foo', notifyReleaseStages: [ 'baz' ] })
   c.configure()
   c.use(plugin)
   c.delivery(client => ({
     sendSession: (session, cb) => {
       expect(true).toBe(false)
     }
   }))
   c.startSession()
   setTimeout(done, 150)
 })
Example #6
0
  it('sets report.request to window.location.href', () => {
    const client = new Client(VALID_NOTIFIER)
    const payloads = []
    client.setOptions({ apiKey: 'API_KEY_YEAH' })
    client.configure()
    client.use(plugin, window)

    client.delivery(client => ({ sendReport: (payload) => payloads.push(payload) }))
    client.notify(new Error('noooo'))

    expect(payloads.length).toEqual(1)
    expect(payloads[0].events[0].request).toEqual({ url: window.location.href })
  })
Example #7
0
 it('correctly infers releaseStage', (done) => {
   const c = new Client(VALID_NOTIFIER)
   c.setOptions({ apiKey: 'API_KEY', releaseStage: 'foo' })
   c.configure()
   c.use(plugin)
   c.delivery(client => ({
     sendSession: (session, cb) => {
       expect(typeof session).toBe('object')
       expect(session.app.releaseStage).toBe('foo')
       done()
     }
   }))
   c.startSession()
 })
Example #8
0
  it('redacts user IP if none is provided', () => {
    const client = new Client(VALID_NOTIFIER)
    const payloads = []
    client.setOptions({ apiKey: 'API_KEY_YEAH', collectUserIp: false })
    client.configure()
    client.use(plugin)

    client.delivery(client => ({ sendReport: (payload) => payloads.push(payload) }))
    client.notify(new Error('noooo'))

    expect(payloads.length).toEqual(1)
    expect(payloads[0].events[0].user).toEqual({ id: '[NOT COLLECTED]' })
    expect(payloads[0].events[0].request).toEqual({ clientIp: '[NOT COLLECTED]' })
  })
Example #9
0
  it('does nothing when collectUserIp=true', () => {
    const client = new Client(VALID_NOTIFIER)
    const payloads = []
    client.setOptions({ apiKey: 'API_KEY_YEAH' })
    client.configure()
    client.use(plugin)

    client.delivery(client => ({ sendReport: (payload) => payloads.push(payload) }))
    client.notify(new Error('noooo'), {
      beforeSend: report => { report.request = { 'some': 'detail' } }
    })

    expect(payloads.length).toEqual(1)
    expect(payloads[0].events[0].request).toEqual({ 'some': 'detail' })
  })
Example #10
0
  it('sets doesn’t overwrite an existing request', () => {
    const client = new Client(VALID_NOTIFIER)
    const payloads = []
    client.setOptions({ apiKey: 'API_KEY_YEAH' })
    client.configure()
    client.use(plugin, window)

    client.request = { url: 'foobar' }

    client.delivery(client => ({ sendReport: (payload) => payloads.push(payload) }))
    client.notify(new Error('noooo'))

    expect(payloads.length).toEqual(1)
    expect(payloads[0].events[0].request).toEqual({ url: 'foobar' })
  })
Example #11
0
  it('overwrites a user id if it is explicitly `undefined`', () => {
    const client = new Client(VALID_NOTIFIER)
    const payloads = []
    client.setOptions({ apiKey: 'API_KEY_YEAH', collectUserIp: false })
    client.configure()
    client.use(plugin)

    client.user = { id: undefined }

    client.delivery(client => ({ sendReport: (payload) => payloads.push(payload) }))
    client.notify(new Error('noooo'))

    expect(payloads.length).toEqual(1)
    expect(payloads[0].events[0].user).toEqual({ id: '[NOT COLLECTED]' })
    expect(payloads[0].events[0].request).toEqual({ clientIp: '[NOT COLLECTED]' })
  })
Example #12
0
 it('should call the onUnhandledException callback when an error is captured', done => {
   const c = new Client(VALID_NOTIFIER)
   c.delivery(client => ({
     sendReport: (report, cb) => {
       expect(report.events[0].errorMessage).toBe('no item available')
       expect(report.events[0].severity).toBe('warning')
       expect(report.events[0].user).toEqual({
         id: '1a2c3cd4',
         name: 'Ben Gourley',
         email: '*****@*****.**'
       })
       cb(null)
     },
     sendSession: () => {}
   }))
   c.setOptions({
     apiKey: 'api_key',
     onUncaughtException: (err) => {
       expect(err.message).toBe('no item available')
       done()
     }
   })
   c.configure({
     ...schema,
     onUncaughtException: {
       validate: val => typeof val === 'function',
       message: 'should be a function',
       defaultValue: () => {}
     }
   })
   c.use(plugin)
   const contextualize = c.getPlugin('contextualize')
   contextualize(() => {
     load(8, (err) => {
       if (err) throw err
     })
   }, {
     user: {
       id: '1a2c3cd4',
       name: 'Ben Gourley',
       email: '*****@*****.**'
     },
     severity: 'warning'
   })
 })
Example #13
0
 it('notifies the session endpoint', (done) => {
   const c = new Client(VALID_NOTIFIER)
   c.setOptions({ apiKey: 'API_KEY' })
   c.configure()
   c.use(plugin)
   c.delivery(client => ({
     sendSession: (session, cb) => {
       expect(typeof session).toBe('object')
       expect(session.notifier).toEqual(VALID_NOTIFIER)
       expect(session.sessions.length).toBe(1)
       expect(session.sessions[0].id).toBeTruthy()
       expect(session.sessions[0].id.length).toBeGreaterThan(10)
       expect(session.sessions[0].startedAt).toBeTruthy()
       done()
     }
   }))
   c.startSession()
 })
Example #14
0
module.exports = (opts) => {
  // handle very simple use case where user supplies just the api key as a string
  if (typeof opts === 'string') opts = { apiKey: opts }

  // ensure opts is actually an object (at this point it
  // could be null, undefined, a number, a boolean etc.)
  opts = { ...opts }

  // attempt to fetch apiKey from app.json if we didn't get one explicitly passed
  if (!opts.apiKey &&
    Constants.manifest &&
    Constants.manifest.extra &&
    Constants.manifest.extra.bugsnag &&
    Constants.manifest.extra.bugsnag.apiKey) {
    opts.apiKey = Constants.manifest.extra.bugsnag.apiKey
  }

  const bugsnag = new Client({ name, version, url })

  bugsnag.delivery(delivery)
  bugsnag.setOptions(opts)
  bugsnag.configure(schema)

  plugins.forEach(pl => {
    switch (pl.name) {
      case 'networkBreadcrumbs':
        bugsnag.use(pl, () => [
          bugsnag.config.endpoints.notify,
          bugsnag.config.endpoints.sessions,
          Constants.manifest.logUrl
        ])
        break
      default:
        bugsnag.use(pl)
    }
  })
  bugsnag.use(bugsnagReact, React)

  bugsnag._logger.debug(`Loaded!`)

  return bugsnag.config.autoCaptureSessions
    ? bugsnag.startSession()
    : bugsnag
}
  it('should not remove a matching substring if it is not at the start', done => {
    const client = new Client(VALID_NOTIFIER)

    client.delivery(client => ({
      sendReport: (report) => {
        const evt = report.events[0]
        expect(evt.stacktrace[0].file).toBe(join('/var', 'lib', '01.js'))
        expect(evt.stacktrace[1].file).toBe(join('/foo', 'lib', '02.js'))
        expect(evt.stacktrace[2].file).toBe(join('/tmp', 'lib', '03.js'))
        done()
      },
      sendSession: () => {}
    }))

    client.setOptions({ apiKey: 'api_key', projectRoot: '/app' })
    client.configure({
      ...schema,
      projectRoot: {
        validate: () => true,
        defaultValue: () => '',
        message: ''
      }
    })
    client.use(plugin)

    client.notify(new Report('Error', 'strip project root test', [
      {
        lineNumber: 22,
        columnNumber: 18,
        fileName: join('/var', 'lib', '01.js')
      }, {
        lineNumber: 31,
        columnNumber: 7,
        fileName: join('/foo', 'lib', '02.js')
      }, {
        lineNumber: 118,
        columnNumber: 28,
        fileName: join('/tmp', 'lib', '03.js')
      }
    ]))
  })
  it('should tolerate stackframe.file not being a string', done => {
    const client = new Client(VALID_NOTIFIER)

    client.delivery(client => ({
      sendReport: (report) => {
        const evt = report.events[0]
        expect(evt.stacktrace[0].file).toBe('global code')
        expect(evt.stacktrace[1].file).toBe('global code')
        expect(evt.stacktrace[2].file).toEqual({})
        done()
      },
      sendSession: () => {}
    }))

    client.setOptions({ apiKey: 'api_key', projectRoot: '/app' })
    client.configure({
      ...schema,
      projectRoot: {
        validate: () => true,
        defaultValue: () => '',
        message: ''
      }
    })
    client.use(plugin)

    client.notify(new Report('Error', 'strip project root test', [
      {
        lineNumber: 22,
        columnNumber: 18,
        fileName: undefined
      }, {
        lineNumber: 31,
        columnNumber: 7,
        fileName: null
      }, {
        lineNumber: 31,
        columnNumber: 7,
        fileName: {}
      }
    ]))
  })
Example #17
0
 it('logs a warning when no session endpoint is set', (done) => {
   const c = new Client(VALID_NOTIFIER)
   c.setOptions({
     apiKey: 'API_KEY',
     releaseStage: 'foo',
     endpoints: { notify: '/foo' },
     autoCaptureSessions: false
   })
   c.configure()
   c.use(plugin)
   c.logger({
     warn: msg => {
       expect(msg).toMatch(/session not sent/i)
       done()
     }
   })
   c.delivery(client => ({
     sendSession: (session, cb) => {
       expect(true).toBe(false)
     }
   }))
   c.startSession()
 })
  it('should work with node_modules and node internals', done => {
    const client = new Client(VALID_NOTIFIER)

    client.delivery(client => ({
      sendReport: (report) => {
        const evt = report.events[0]
        expect(evt.stacktrace[0].file).toBe('_module.js')
        expect(evt.stacktrace[1].file).toBe(join('node_modules', 'bugsnag-example', 'index.js'))
        done()
      },
      sendSession: () => {}
    }))

    client.setOptions({ apiKey: 'api_key', projectRoot: '/app' })
    client.configure({
      ...schema,
      projectRoot: {
        validate: () => true,
        defaultValue: () => '',
        message: ''
      }
    })
    client.use(plugin)

    client.notify(new Report('Error', 'strip project root test', [
      {
        lineNumber: 22,
        columnNumber: 18,
        fileName: '_module.js'
      }, {
        lineNumber: 31,
        columnNumber: 7,
        fileName: join('/app', 'node_modules', 'bugsnag-example', 'index.js')
      }
    ]))
  })