mirror of
https://github.com/atom/atom.git
synced 2026-04-06 03:02:13 -04:00
Merge pull request #18140 from atom/migrate-exception-reporting-package
➡️ Migrate core package 'exception-reporting' into ./packages
This commit is contained in:
3
package-lock.json
generated
3
package-lock.json
generated
@@ -2045,8 +2045,7 @@
|
||||
}
|
||||
},
|
||||
"exception-reporting": {
|
||||
"version": "https://www.atom.io/api/packages/exception-reporting/versions/0.43.1/tarball",
|
||||
"integrity": "sha512-IYDPs9MNXcbKJv+G/WH6FqkbikiaP9VBskWatMjCj6ca7aey0bbK/qEQwaMogzEGwh9C+n7f+4fAarpgWNKpsw==",
|
||||
"version": "file:packages/exception-reporting",
|
||||
"requires": {
|
||||
"fs-plus": "^3.0.0",
|
||||
"node-uuid": "~1.4.7",
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
"encoding-selector": "https://www.atom.io/api/packages/encoding-selector/versions/0.23.9/tarball",
|
||||
"etch": "^0.12.6",
|
||||
"event-kit": "^2.5.1",
|
||||
"exception-reporting": "https://www.atom.io/api/packages/exception-reporting/versions/0.43.1/tarball",
|
||||
"exception-reporting": "file:packages/exception-reporting",
|
||||
"find-and-replace": "https://www.atom.io/api/packages/find-and-replace/versions/0.215.14/tarball",
|
||||
"find-parent-dir": "^0.3.0",
|
||||
"first-mate": "7.1.3",
|
||||
@@ -199,7 +199,7 @@
|
||||
"deprecation-cop": "0.56.9",
|
||||
"dev-live-reload": "0.48.1",
|
||||
"encoding-selector": "0.23.9",
|
||||
"exception-reporting": "0.43.1",
|
||||
"exception-reporting": "file:./packages/exception-reporting",
|
||||
"find-and-replace": "0.215.14",
|
||||
"fuzzy-finder": "1.8.2",
|
||||
"github": "0.20.0",
|
||||
|
||||
@@ -8,17 +8,17 @@ See [RFC 003](https://github.com/atom/atom/blob/master/docs/rfcs/003-consolidate
|
||||
|
||||
| Package | Where to find it | Migration issue |
|
||||
|---------|------------------|-----------------|
|
||||
| **about** | [`./packages/about`](./about) | [#17832](https://github.com/atom/atom/issues/17832) |
|
||||
| **atom-dark-syntax** | [`./packages/atom-dark-syntax`][./atom-dark-syntax] | [#17849](https://github.com/atom/atom/issues/17849) |
|
||||
| **atom-dark-ui** | [`./packages/atom-dark-ui`][./atom-dark-ui] | [#17850](https://github.com/atom/atom/issues/17850) |
|
||||
| **atom-light-syntax** | [`./packages/atom-light-syntax`][./atom-light-syntax] | [#17851](https://github.com/atom/atom/issues/17851) |
|
||||
| **atom-light-ui** | [`./packages/atom-light-ui`][./atom-light-ui] | [#17852](https://github.com/atom/atom/issues/17852) |
|
||||
| **about** | [`./about`](./about) | [#17832](https://github.com/atom/atom/issues/17832) |
|
||||
| **atom-dark-syntax** | [`./atom-dark-syntax`](./atom-dark-syntax) | [#17849](https://github.com/atom/atom/issues/17849) |
|
||||
| **atom-dark-ui** | [`./atom-dark-ui`](./atom-dark-ui) | [#17850](https://github.com/atom/atom/issues/17850) |
|
||||
| **atom-light-syntax** | [`./atom-light-syntax`](./atom-light-syntax) | [#17851](https://github.com/atom/atom/issues/17851) |
|
||||
| **atom-light-ui** | [`./atom-light-ui`](./atom-light-ui) | [#17852](https://github.com/atom/atom/issues/17852) |
|
||||
| **autocomplete-atom-api** | [`atom/autocomplete-atom-api`][autocomplete-atom-api] | |
|
||||
| **autocomplete-css** | [`atom/autocomplete-css`][autocomplete-css] | |
|
||||
| **autocomplete-html** | [`atom/autocomplete-html`][autocomplete-html] | |
|
||||
| **autocomplete-plus** | [`atom/autocomplete-plus`][autocomplete-plus] | |
|
||||
| **autocomplete-snippets** | [`atom/autocomplete-snippets`][autocomplete-snippets] | |
|
||||
| **autoflow** | [`atom/autoflow`][./autoflow] | [#17833](https://github.com/atom/atom/issues/17833) |
|
||||
| **autoflow** | [`atom/autoflow`](./autoflow) | [#17833](https://github.com/atom/atom/issues/17833) |
|
||||
| **autosave** | [`atom/autosave`][autosave] | [#17834](https://github.com/atom/atom/issues/17834) |
|
||||
| **background-tips** | [`atom/background-tips`][background-tips] | [#17835](https://github.com/atom/atom/issues/17835) |
|
||||
| **base16-tomorrow-dark-theme** | [`atom/base16-tomorrow-dark-theme`][base16-tomorrow-dark-theme] | [#17836](https://github.com/atom/atom/issues/17836) |
|
||||
@@ -30,15 +30,15 @@ See [RFC 003](https://github.com/atom/atom/blob/master/docs/rfcs/003-consolidate
|
||||
| **deprecation-cop** | [`atom/deprecation-cop`][deprecation-cop] | [#17839](https://github.com/atom/atom/issues/17839) |
|
||||
| **dev-live-reload** | [`atom/dev-live-reload`][dev-live-reload] | [#17840](https://github.com/atom/atom/issues/17840) |
|
||||
| **encoding-selector** | [`atom/encoding-selector`][encoding-selector] | [#17841](https://github.com/atom/atom/issues/17841) |
|
||||
| **exception-reporting** | [`atom/exception-reporting`][exception-reporting] | [#17842](https://github.com/atom/atom/issues/17842) |
|
||||
| **exception-reporting** | [`./exception-reporting`](./exception-reporting) | [#17842](https://github.com/atom/atom/issues/17842) |
|
||||
| **find-and-replace** | [`atom/find-and-replace`][find-and-replace] | |
|
||||
| **fuzzy-finder** | [`atom/fuzzy-finder`][fuzzy-finder] | |
|
||||
| **github** | [`atom/github`][github] | |
|
||||
| **git-diff** | [`./packages/git-diff`](./git-diff) | [#17843](https://github.com/atom/atom/issues/17843) |
|
||||
| **git-diff** | [`./git-diff`](./git-diff) | [#17843](https://github.com/atom/atom/issues/17843) |
|
||||
| **go-to-line** | [`atom/go-to-line`][go-to-line] | [#17844](https://github.com/atom/atom/issues/17844) |
|
||||
| **grammar-selector** | [`atom/grammar-selector`][grammar-selector] | [#17845](https://github.com/atom/atom/issues/17845) |
|
||||
| **image-view** | [`atom/image-view`][image-view] | |
|
||||
| **incompatible-packages** | [`./packages/incompatible-packages`][./incompatible-packages] | [#17846](https://github.com/atom/atom/issues/17846) |
|
||||
| **incompatible-packages** | [`./incompatible-packages`](./incompatible-packages) | [#17846](https://github.com/atom/atom/issues/17846) |
|
||||
| **keybinding-resolver** | [`atom/keybinding-resolver`][keybinding-resolver] | |
|
||||
| **language-c** | [`atom/language-c`][language-c] | |
|
||||
| **language-clojure** | [`atom/language-clojure`][language-clojure] | |
|
||||
@@ -74,14 +74,14 @@ See [RFC 003](https://github.com/atom/atom/blob/master/docs/rfcs/003-consolidate
|
||||
| **language-xml** | [`atom/language-xml`][language-xml] | |
|
||||
| **language-yaml** | [`atom/language-yaml`][language-yaml] | |
|
||||
| **line-ending-selector** | [`atom/line-ending-selector`][line-ending-selector] | [#17847](https://github.com/atom/atom/issues/17847) |
|
||||
| **link** | [`./packages/link`][./link] | [#17848](https://github.com/atom/atom/issues/17848) |
|
||||
| **link** | [`./link`](./link) | [#17848](https://github.com/atom/atom/issues/17848) |
|
||||
| **markdown-preview** | [`atom/markdown-preview`][markdown-preview] | |
|
||||
| **metrics** | [`atom/metrics`][metrics] | |
|
||||
| **notifications** | [`atom/notifications`][notifications] | |
|
||||
| **one-dark-syntax** | [`./packages/one-dark-syntax`](./one-dark-syntax) | [#17853](https://github.com/atom/atom/issues/17853) |
|
||||
| **one-dark-ui** | [`./packages/one-dark-ui`](./one-dark-ui) | [#17854](https://github.com/atom/atom/issues/17854) |
|
||||
| **one-light-syntax** | [`./packages/one-light-syntax`](./one-light-syntax) | [#17855](https://github.com/atom/atom/issues/17855) |
|
||||
| **one-light-ui** | [`./packages/one-light-ui`](./one-light-ui) | [#17856](https://github.com/atom/atom/issues/17856) |
|
||||
| **one-dark-syntax** | [`./one-dark-syntax`](./one-dark-syntax) | [#17853](https://github.com/atom/atom/issues/17853) |
|
||||
| **one-dark-ui** | [`./one-dark-ui`](./one-dark-ui) | [#17854](https://github.com/atom/atom/issues/17854) |
|
||||
| **one-light-syntax** | [`./one-light-syntax`](./one-light-syntax) | [#17855](https://github.com/atom/atom/issues/17855) |
|
||||
| **one-light-ui** | [`./one-light-ui`](./one-light-ui) | [#17856](https://github.com/atom/atom/issues/17856) |
|
||||
| **open-on-github** | [`atom/open-on-github`][open-on-github] | |
|
||||
| **package-generator** | [`atom/package-generator`][package-generator] | |
|
||||
| **settings-view** | [`atom/settings-view`][settings-view] | |
|
||||
@@ -101,10 +101,6 @@ See [RFC 003](https://github.com/atom/atom/blob/master/docs/rfcs/003-consolidate
|
||||
| **wrap-guide** | [`atom/wrap-guide`][wrap-guide] | |
|
||||
|
||||
[archive-view]: https://github.com/atom/archive-view
|
||||
[atom-dark-syntax]: https://github.com/atom/atom-dark-syntax
|
||||
[atom-dark-ui]: https://github.com/atom/atom-dark-ui
|
||||
[atom-light-syntax]: https://github.com/atom/atom-light-syntax
|
||||
[atom-light-ui]: https://github.com/atom/atom-light-ui
|
||||
[autocomplete-atom-api]: https://github.com/atom/autocomplete-atom-api
|
||||
[autocomplete-css]: https://github.com/atom/autocomplete-css
|
||||
[autocomplete-html]: https://github.com/atom/autocomplete-html
|
||||
@@ -121,14 +117,12 @@ See [RFC 003](https://github.com/atom/atom/blob/master/docs/rfcs/003-consolidate
|
||||
[deprecation-cop]: https://github.com/atom/deprecation-cop
|
||||
[dev-live-reload]: https://github.com/atom/dev-live-reload
|
||||
[encoding-selector]: https://github.com/atom/encoding-selector
|
||||
[exception-reporting]: https://github.com/atom/exception-reporting
|
||||
[find-and-replace]: https://github.com/atom/find-and-replace
|
||||
[fuzzy-finder]: https://github.com/atom/fuzzy-finder
|
||||
[github]: https://github.com/atom/github
|
||||
[go-to-line]: https://github.com/atom/go-to-line
|
||||
[grammar-selector]: https://github.com/atom/grammar-selector
|
||||
[image-view]: https://github.com/atom/image-view
|
||||
[incompatible-packages]: https://github.com/atom/incompatible-packages
|
||||
[keybinding-resolver]: https://github.com/atom/keybinding-resolver
|
||||
[language-c]: https://github.com/atom/language-c
|
||||
[language-clojure]: https://github.com/atom/language-clojure
|
||||
@@ -164,7 +158,6 @@ See [RFC 003](https://github.com/atom/atom/blob/master/docs/rfcs/003-consolidate
|
||||
[language-xml]: https://github.com/atom/language-xml
|
||||
[language-yaml]: https://github.com/atom/language-yaml
|
||||
[line-ending-selector]: https://github.com/atom/line-ending-selector
|
||||
[link]: https://github.com/atom/link
|
||||
[markdown-preview]: https://github.com/atom/markdown-preview
|
||||
[metrics]: https://github.com/atom/metrics
|
||||
[notifications]: https://github.com/atom/notifications
|
||||
|
||||
1
packages/exception-reporting/.gitignore
vendored
Normal file
1
packages/exception-reporting/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
node_modules
|
||||
20
packages/exception-reporting/LICENSE.md
Normal file
20
packages/exception-reporting/LICENSE.md
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2014 GitHub Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
3
packages/exception-reporting/README.md
Normal file
3
packages/exception-reporting/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## Exception Reporting package
|
||||
|
||||
Reports uncaught exceptions in Atom to [bugsnag](https://bugsnag.com).
|
||||
49
packages/exception-reporting/lib/main.js
Normal file
49
packages/exception-reporting/lib/main.js
Normal file
@@ -0,0 +1,49 @@
|
||||
/** @babel */
|
||||
|
||||
import {CompositeDisposable} from 'atom'
|
||||
|
||||
let reporter
|
||||
|
||||
function getReporter () {
|
||||
if (!reporter) {
|
||||
const Reporter = require('./reporter')
|
||||
reporter = new Reporter()
|
||||
}
|
||||
return reporter
|
||||
}
|
||||
|
||||
export default {
|
||||
activate() {
|
||||
this.subscriptions = new CompositeDisposable()
|
||||
|
||||
if (!atom.config.get('exception-reporting.userId')) {
|
||||
atom.config.set('exception-reporting.userId', require('node-uuid').v4())
|
||||
}
|
||||
|
||||
this.subscriptions.add(atom.onDidThrowError(({message, url, line, column, originalError}) => {
|
||||
try {
|
||||
getReporter().reportUncaughtException(originalError)
|
||||
} catch (secondaryException) {
|
||||
try {
|
||||
console.error("Error reporting uncaught exception", secondaryException)
|
||||
getReporter().reportUncaughtException(secondaryException)
|
||||
} catch (error) { }
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
if (atom.onDidFailAssertion != null) {
|
||||
this.subscriptions.add(atom.onDidFailAssertion(error => {
|
||||
try {
|
||||
getReporter().reportFailedAssertion(error)
|
||||
} catch (secondaryException) {
|
||||
try {
|
||||
console.error("Error reporting assertion failure", secondaryException)
|
||||
getReporter().reportUncaughtException(secondaryException)
|
||||
} catch (error) {}
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
267
packages/exception-reporting/lib/reporter.js
Normal file
267
packages/exception-reporting/lib/reporter.js
Normal file
@@ -0,0 +1,267 @@
|
||||
/** @babel */
|
||||
|
||||
import _ from 'underscore-plus'
|
||||
import os from 'os'
|
||||
import stackTrace from 'stack-trace'
|
||||
import fs from 'fs-plus'
|
||||
import path from 'path'
|
||||
|
||||
const API_KEY = '7ddca14cb60cbd1cd12d1b252473b076'
|
||||
const LIB_VERSION = require('../package.json')['version']
|
||||
const StackTraceCache = new WeakMap()
|
||||
|
||||
export default class Reporter {
|
||||
constructor (params = {}) {
|
||||
this.request = params.request || window.fetch
|
||||
this.alwaysReport = params.hasOwnProperty('alwaysReport') ? params.alwaysReport : false
|
||||
this.reportPreviousErrors = params.hasOwnProperty('reportPreviousErrors') ? params.reportPreviousErrors : true
|
||||
this.resourcePath = this.normalizePath(params.resourcePath || process.resourcesPath)
|
||||
this.reportedErrors = []
|
||||
this.reportedAssertionFailures = []
|
||||
}
|
||||
|
||||
buildNotificationJSON (error, params) {
|
||||
return {
|
||||
apiKey: API_KEY,
|
||||
notifier: {
|
||||
name: 'Atom',
|
||||
version: LIB_VERSION,
|
||||
url: 'https://www.atom.io'
|
||||
},
|
||||
events: [{
|
||||
payloadVersion: "2",
|
||||
exceptions: [this.buildExceptionJSON(error, params.projectRoot)],
|
||||
severity: params.severity,
|
||||
user: {
|
||||
id: params.userId
|
||||
},
|
||||
app: {
|
||||
version: params.appVersion,
|
||||
releaseStage: params.releaseStage
|
||||
},
|
||||
device: {
|
||||
osVersion: params.osVersion
|
||||
},
|
||||
metaData: error.metadata
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
buildExceptionJSON (error, projectRoot) {
|
||||
return {
|
||||
errorClass: error.constructor.name,
|
||||
message: error.message,
|
||||
stacktrace: this.buildStackTraceJSON(error, projectRoot)
|
||||
}
|
||||
}
|
||||
|
||||
buildStackTraceJSON (error, projectRoot) {
|
||||
return this.parseStackTrace(error).map(callSite => {
|
||||
return {
|
||||
file: this.scrubPath(callSite.getFileName()),
|
||||
method: callSite.getMethodName() || callSite.getFunctionName() || "none",
|
||||
lineNumber: callSite.getLineNumber(),
|
||||
columnNumber: callSite.getColumnNumber(),
|
||||
inProject: !/node_modules/.test(callSite.getFileName())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
normalizePath (pathToNormalize) {
|
||||
return pathToNormalize
|
||||
.replace('file:///', '') // Sometimes it's a uri
|
||||
.replace(/\\/g, '/') // Unify path separators across Win/macOS/Linux
|
||||
}
|
||||
|
||||
scrubPath (pathToScrub) {
|
||||
const absolutePath = this.normalizePath(pathToScrub)
|
||||
|
||||
if (this.isBundledFile(absolutePath)) {
|
||||
return this.normalizePath(path.relative(this.resourcePath, absolutePath))
|
||||
} else {
|
||||
return absolutePath
|
||||
.replace(this.normalizePath(fs.getHomeDirectory()), '~') // Remove users home dir
|
||||
.replace(/.*(\/packages\/.*)/, '$1') // Remove everything before app.asar or packages
|
||||
}
|
||||
}
|
||||
|
||||
getDefaultNotificationParams () {
|
||||
return {
|
||||
userId: atom.config.get('exception-reporting.userId'),
|
||||
appVersion: atom.getVersion(),
|
||||
releaseStage: this.getReleaseChannel(atom.getVersion()),
|
||||
projectRoot: atom.getLoadSettings().resourcePath,
|
||||
osVersion: `${os.platform()}-${os.arch()}-${os.release()}`
|
||||
}
|
||||
}
|
||||
|
||||
getReleaseChannel (version) {
|
||||
return (version.indexOf('beta') > -1)
|
||||
? 'beta'
|
||||
: (version.indexOf('dev') > -1)
|
||||
? 'dev'
|
||||
: 'stable'
|
||||
}
|
||||
|
||||
performRequest (json) {
|
||||
this.request.call(null, 'https://notify.bugsnag.com', {
|
||||
method: 'POST',
|
||||
headers: new Headers({'Content-Type': 'application/json'}),
|
||||
body: JSON.stringify(json)
|
||||
})
|
||||
}
|
||||
|
||||
shouldReport (error) {
|
||||
if (this.alwaysReport) return true // Used in specs
|
||||
if (atom.config.get('core.telemetryConsent') !== 'limited') return false
|
||||
if (atom.inDevMode()) return false
|
||||
|
||||
const topFrame = this.parseStackTrace(error)[0]
|
||||
const fileName = topFrame ? topFrame.getFileName() : null
|
||||
return fileName && (this.isBundledFile(fileName) || this.isTeletypeFile(fileName))
|
||||
}
|
||||
|
||||
parseStackTrace (error) {
|
||||
let callSites = StackTraceCache.get(error)
|
||||
if (callSites) {
|
||||
return callSites
|
||||
} else {
|
||||
callSites = stackTrace.parse(error)
|
||||
StackTraceCache.set(error, callSites)
|
||||
return callSites
|
||||
}
|
||||
}
|
||||
|
||||
requestPrivateMetadataConsent (error, message, reportFn) {
|
||||
let notification, dismissSubscription
|
||||
|
||||
function reportWithoutPrivateMetadata () {
|
||||
if (dismissSubscription) {
|
||||
dismissSubscription.dispose()
|
||||
}
|
||||
delete error.privateMetadata
|
||||
delete error.privateMetadataDescription
|
||||
reportFn(error)
|
||||
if (notification) {
|
||||
notification.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
function reportWithPrivateMetadata () {
|
||||
if (error.metadata == null) {
|
||||
error.metadata = {}
|
||||
}
|
||||
for (let key in error.privateMetadata) {
|
||||
let value = error.privateMetadata[key]
|
||||
error.metadata[key] = value
|
||||
}
|
||||
reportWithoutPrivateMetadata()
|
||||
}
|
||||
|
||||
const name = error.privateMetadataRequestName
|
||||
if (name != null) {
|
||||
if (localStorage.getItem(`private-metadata-request:${name}`)) {
|
||||
return reportWithoutPrivateMetadata(error)
|
||||
} else {
|
||||
localStorage.setItem(`private-metadata-request:${name}`, true)
|
||||
}
|
||||
}
|
||||
|
||||
notification = atom.notifications.addInfo(message, {
|
||||
detail: error.privateMetadataDescription,
|
||||
description: "Are you willing to submit this information to a private server for debugging purposes?",
|
||||
dismissable: true,
|
||||
buttons: [
|
||||
{
|
||||
text: "No",
|
||||
onDidClick: reportWithoutPrivateMetadata
|
||||
},
|
||||
{
|
||||
text: "Yes, Submit for Debugging",
|
||||
onDidClick: reportWithPrivateMetadata
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
dismissSubscription = notification.onDidDismiss(reportWithoutPrivateMetadata)
|
||||
}
|
||||
|
||||
addPackageMetadata (error) {
|
||||
let activePackages = atom.packages.getActivePackages()
|
||||
const availablePackagePaths = atom.packages.getPackageDirPaths()
|
||||
if (activePackages.length > 0) {
|
||||
let userPackages = {}
|
||||
let bundledPackages = {}
|
||||
for (let pack of atom.packages.getActivePackages()) {
|
||||
if (availablePackagePaths.includes(path.dirname(pack.path))) {
|
||||
userPackages[pack.name] = pack.metadata.version
|
||||
} else {
|
||||
bundledPackages[pack.name] = pack.metadata.version
|
||||
}
|
||||
}
|
||||
|
||||
if (error.metadata == null) { error.metadata = {} }
|
||||
error.metadata.bundledPackages = bundledPackages
|
||||
error.metadata.userPackages = userPackages
|
||||
}
|
||||
}
|
||||
|
||||
addPreviousErrorsMetadata (error) {
|
||||
if (!this.reportPreviousErrors) return
|
||||
if (!error.metadata) error.metadata = {}
|
||||
error.metadata.previousErrors = this.reportedErrors.map(error => error.message)
|
||||
error.metadata.previousAssertionFailures = this.reportedAssertionFailures.map(error => error.message)
|
||||
}
|
||||
|
||||
reportUncaughtException (error) {
|
||||
if (!this.shouldReport(error)) return
|
||||
|
||||
this.addPackageMetadata(error)
|
||||
this.addPreviousErrorsMetadata(error)
|
||||
|
||||
if ((error.privateMetadata != null) && (error.privateMetadataDescription != null)) {
|
||||
this.requestPrivateMetadataConsent(error, "The Atom team would like to collect the following information to resolve this error:", error => this.reportUncaughtException(error))
|
||||
return
|
||||
}
|
||||
|
||||
let params = this.getDefaultNotificationParams()
|
||||
params.severity = "error"
|
||||
this.performRequest(this.buildNotificationJSON(error, params))
|
||||
this.reportedErrors.push(error)
|
||||
}
|
||||
|
||||
reportFailedAssertion (error) {
|
||||
if (!this.shouldReport(error)) return
|
||||
|
||||
this.addPackageMetadata(error)
|
||||
this.addPreviousErrorsMetadata(error)
|
||||
|
||||
if ((error.privateMetadata != null) && (error.privateMetadataDescription != null)) {
|
||||
this.requestPrivateMetadataConsent(error, "The Atom team would like to collect some information to resolve an unexpected condition:", error => this.reportFailedAssertion(error))
|
||||
return
|
||||
}
|
||||
|
||||
let params = this.getDefaultNotificationParams()
|
||||
params.severity = "warning"
|
||||
this.performRequest(this.buildNotificationJSON(error, params))
|
||||
this.reportedAssertionFailures.push(error)
|
||||
}
|
||||
|
||||
// Used in specs
|
||||
setRequestFunction (requestFunction) {
|
||||
this.request = requestFunction
|
||||
}
|
||||
|
||||
isBundledFile (fileName) {
|
||||
return this.normalizePath(fileName).indexOf(this.resourcePath) === 0
|
||||
}
|
||||
|
||||
isTeletypeFile (fileName) {
|
||||
const teletypePath = atom.packages.resolvePackagePath('teletype')
|
||||
return teletypePath && this.normalizePath(fileName).indexOf(teletypePath) === 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reporter.API_KEY = API_KEY
|
||||
Reporter.LIB_VERSION = LIB_VERSION
|
||||
20
packages/exception-reporting/package.json
Normal file
20
packages/exception-reporting/package.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "exception-reporting",
|
||||
"main": "./lib/main",
|
||||
"version": "0.43.1",
|
||||
"description": "Reports uncaught Atom exceptions to the Atom team via bugsnag.com",
|
||||
"repository": "https://github.com/atom/atom",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"atom": ">0.48.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"fs-plus": "^3.0.0",
|
||||
"node-uuid": "~1.4.7",
|
||||
"stack-trace": "0.0.9",
|
||||
"underscore-plus": "1.x"
|
||||
},
|
||||
"devDependencies": {
|
||||
"semver": "^5.3.0"
|
||||
}
|
||||
}
|
||||
479
packages/exception-reporting/spec/reporter-spec.js
Normal file
479
packages/exception-reporting/spec/reporter-spec.js
Normal file
@@ -0,0 +1,479 @@
|
||||
const Reporter = require('../lib/reporter')
|
||||
const semver = require('semver')
|
||||
const os = require('os')
|
||||
const path = require('path')
|
||||
const fs = require('fs-plus')
|
||||
let osVersion = `${os.platform()}-${os.arch()}-${os.release()}`
|
||||
|
||||
let getReleaseChannel = version => {
|
||||
return (version.indexOf('beta') > -1)
|
||||
? 'beta'
|
||||
: (version.indexOf('dev') > -1)
|
||||
? 'dev'
|
||||
: 'stable'
|
||||
}
|
||||
|
||||
describe("Reporter", () => {
|
||||
let reporter, requests, initialStackTraceLimit, initialFsGetHomeDirectory, mockActivePackages
|
||||
|
||||
beforeEach(() => {
|
||||
reporter = new Reporter({
|
||||
request: (url, options) => requests.push(Object.assign({url}, options)),
|
||||
alwaysReport: true,
|
||||
reportPreviousErrors: false
|
||||
})
|
||||
requests = []
|
||||
mockActivePackages = []
|
||||
spyOn(atom.packages, 'getActivePackages').andCallFake(() => mockActivePackages)
|
||||
|
||||
initialStackTraceLimit = Error.stackTraceLimit
|
||||
Error.stackTraceLimit = 1
|
||||
|
||||
initialFsGetHomeDirectory = fs.getHomeDirectory
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
fs.getHomeDirectory = initialFsGetHomeDirectory
|
||||
Error.stackTraceLimit = initialStackTraceLimit
|
||||
})
|
||||
|
||||
describe(".reportUncaughtException(error)", () => {
|
||||
it("posts errors originated inside Atom Core to BugSnag", () => {
|
||||
const repositoryRootPath = path.join(__dirname, '..')
|
||||
reporter = new Reporter({
|
||||
request: (url, options) => requests.push(Object.assign({url}, options)),
|
||||
alwaysReport: true,
|
||||
reportPreviousErrors: false,
|
||||
resourcePath: repositoryRootPath
|
||||
})
|
||||
|
||||
let error = new Error()
|
||||
Error.captureStackTrace(error)
|
||||
reporter.reportUncaughtException(error)
|
||||
let [lineNumber, columnNumber] = error.stack.match(/.js:(\d+):(\d+)/).slice(1).map(s => parseInt(s))
|
||||
|
||||
expect(requests.length).toBe(1)
|
||||
let [request] = requests
|
||||
expect(request.method).toBe("POST")
|
||||
expect(request.url).toBe("https://notify.bugsnag.com")
|
||||
expect(request.headers.get("Content-Type")).toBe("application/json")
|
||||
let body = JSON.parse(request.body)
|
||||
// Delete `inProject` field because tests may fail when run as part of Atom core
|
||||
// (i.e. when this test file will be located under `node_modules/exception-reporting/spec`)
|
||||
delete body.events[0].exceptions[0].stacktrace[0].inProject
|
||||
|
||||
expect(body).toEqual({
|
||||
"apiKey": Reporter.API_KEY,
|
||||
"notifier": {
|
||||
"name": "Atom",
|
||||
"version": Reporter.LIB_VERSION,
|
||||
"url": "https://www.atom.io"
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"payloadVersion": "2",
|
||||
"exceptions": [
|
||||
{
|
||||
"errorClass": "Error",
|
||||
"message": "",
|
||||
"stacktrace": [
|
||||
{
|
||||
"method": semver.gt(process.versions.electron, '1.6.0') ? 'Spec.it' : 'it',
|
||||
"file": "spec/reporter-spec.js",
|
||||
"lineNumber": lineNumber,
|
||||
"columnNumber": columnNumber
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"severity": "error",
|
||||
"user": {},
|
||||
"app": {
|
||||
"version": atom.getVersion(),
|
||||
"releaseStage": getReleaseChannel(atom.getVersion())
|
||||
},
|
||||
"device": {
|
||||
"osVersion": osVersion
|
||||
}
|
||||
}
|
||||
]
|
||||
});})
|
||||
|
||||
it("posts errors originated outside Atom Core to BugSnag", () => {
|
||||
fs.getHomeDirectory = () => path.join(__dirname, '..', '..')
|
||||
|
||||
let error = new Error()
|
||||
Error.captureStackTrace(error)
|
||||
reporter.reportUncaughtException(error)
|
||||
let [lineNumber, columnNumber] = error.stack.match(/.js:(\d+):(\d+)/).slice(1).map(s => parseInt(s))
|
||||
|
||||
expect(requests.length).toBe(1)
|
||||
let [request] = requests
|
||||
expect(request.method).toBe("POST")
|
||||
expect(request.url).toBe("https://notify.bugsnag.com")
|
||||
expect(request.headers.get("Content-Type")).toBe("application/json")
|
||||
let body = JSON.parse(request.body)
|
||||
// Delete `inProject` field because tests may fail when run as part of Atom core
|
||||
// (i.e. when this test file will be located under `node_modules/exception-reporting/spec`)
|
||||
delete body.events[0].exceptions[0].stacktrace[0].inProject
|
||||
|
||||
expect(body).toEqual({
|
||||
"apiKey": Reporter.API_KEY,
|
||||
"notifier": {
|
||||
"name": "Atom",
|
||||
"version": Reporter.LIB_VERSION,
|
||||
"url": "https://www.atom.io"
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"payloadVersion": "2",
|
||||
"exceptions": [
|
||||
{
|
||||
"errorClass": "Error",
|
||||
"message": "",
|
||||
"stacktrace": [
|
||||
{
|
||||
"method": semver.gt(process.versions.electron, '1.6.0') ? 'Spec.it' : 'it',
|
||||
"file": '~/exception-reporting/spec/reporter-spec.js',
|
||||
"lineNumber": lineNumber,
|
||||
"columnNumber": columnNumber
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"severity": "error",
|
||||
"user": {},
|
||||
"app": {
|
||||
"version": atom.getVersion(),
|
||||
"releaseStage": getReleaseChannel(atom.getVersion())
|
||||
},
|
||||
"device": {
|
||||
"osVersion": osVersion
|
||||
}
|
||||
}
|
||||
]
|
||||
});})
|
||||
|
||||
describe("when the error object has `privateMetadata` and `privateMetadataDescription` fields", () => {
|
||||
let [error, notification] = []
|
||||
|
||||
beforeEach(() => {
|
||||
atom.notifications.clear()
|
||||
spyOn(atom.notifications, 'addInfo').andCallThrough()
|
||||
|
||||
error = new Error()
|
||||
Error.captureStackTrace(error)
|
||||
|
||||
error.metadata = {foo: "bar"}
|
||||
error.privateMetadata = {baz: "quux"}
|
||||
error.privateMetadataDescription = "The contents of baz"
|
||||
})
|
||||
|
||||
it("posts a notification asking for consent", () => {
|
||||
reporter.reportUncaughtException(error)
|
||||
expect(atom.notifications.addInfo).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("submits the error with the private metadata if the user consents", () => {
|
||||
spyOn(reporter, 'reportUncaughtException').andCallThrough()
|
||||
reporter.reportUncaughtException(error)
|
||||
reporter.reportUncaughtException.reset()
|
||||
|
||||
notification = atom.notifications.getNotifications()[0]
|
||||
|
||||
let notificationOptions = atom.notifications.addInfo.argsForCall[0][1]
|
||||
expect(notificationOptions.buttons[1].text).toMatch(/Yes/)
|
||||
|
||||
notificationOptions.buttons[1].onDidClick()
|
||||
expect(reporter.reportUncaughtException).toHaveBeenCalledWith(error)
|
||||
expect(reporter.reportUncaughtException.callCount).toBe(1)
|
||||
expect(error.privateMetadata).toBeUndefined()
|
||||
expect(error.privateMetadataDescription).toBeUndefined()
|
||||
expect(error.metadata).toEqual({foo: "bar", baz: "quux"})
|
||||
|
||||
expect(notification.isDismissed()).toBe(true)
|
||||
})
|
||||
|
||||
it("submits the error without the private metadata if the user does not consent", () => {
|
||||
spyOn(reporter, 'reportUncaughtException').andCallThrough()
|
||||
reporter.reportUncaughtException(error)
|
||||
reporter.reportUncaughtException.reset()
|
||||
|
||||
notification = atom.notifications.getNotifications()[0]
|
||||
|
||||
let notificationOptions = atom.notifications.addInfo.argsForCall[0][1]
|
||||
expect(notificationOptions.buttons[0].text).toMatch(/No/)
|
||||
|
||||
notificationOptions.buttons[0].onDidClick()
|
||||
expect(reporter.reportUncaughtException).toHaveBeenCalledWith(error)
|
||||
expect(reporter.reportUncaughtException.callCount).toBe(1)
|
||||
expect(error.privateMetadata).toBeUndefined()
|
||||
expect(error.privateMetadataDescription).toBeUndefined()
|
||||
expect(error.metadata).toEqual({foo: "bar"})
|
||||
|
||||
expect(notification.isDismissed()).toBe(true)
|
||||
})
|
||||
|
||||
it("submits the error without the private metadata if the user dismisses the notification", () => {
|
||||
spyOn(reporter, 'reportUncaughtException').andCallThrough()
|
||||
reporter.reportUncaughtException(error)
|
||||
reporter.reportUncaughtException.reset()
|
||||
|
||||
notification = atom.notifications.getNotifications()[0]
|
||||
notification.dismiss()
|
||||
|
||||
expect(reporter.reportUncaughtException).toHaveBeenCalledWith(error)
|
||||
expect(reporter.reportUncaughtException.callCount).toBe(1)
|
||||
expect(error.privateMetadata).toBeUndefined()
|
||||
expect(error.privateMetadataDescription).toBeUndefined()
|
||||
expect(error.metadata).toEqual({foo: "bar"});});})
|
||||
|
||||
it('treats packages located in atom.packages.getPackageDirPaths as user packages', () => {
|
||||
mockActivePackages = [
|
||||
{name: 'user-1', path: '/Users/user/.atom/packages/user-1', metadata: {version: '1.0.0'}},
|
||||
{name: 'user-2', path: '/Users/user/.atom/packages/user-2', metadata: {version: '1.2.0'}},
|
||||
{name: 'bundled-1', path: '/Applications/Atom.app/Contents/Resources/app.asar/node_modules/bundled-1', metadata: {version: '1.0.0'}},
|
||||
{name: 'bundled-2', path: '/Applications/Atom.app/Contents/Resources/app.asar/node_modules/bundled-2', metadata: {version: '1.2.0'}},
|
||||
]
|
||||
|
||||
const packageDirPaths = ['/Users/user/.atom/packages']
|
||||
|
||||
spyOn(atom.packages, 'getPackageDirPaths').andReturn(packageDirPaths)
|
||||
|
||||
let error = new Error()
|
||||
Error.captureStackTrace(error)
|
||||
reporter.reportUncaughtException(error)
|
||||
|
||||
expect(error.metadata.userPackages).toEqual({
|
||||
'user-1': '1.0.0',
|
||||
'user-2': '1.2.0'
|
||||
})
|
||||
expect(error.metadata.bundledPackages).toEqual({
|
||||
'bundled-1': '1.0.0',
|
||||
'bundled-2': '1.2.0'
|
||||
})
|
||||
})
|
||||
|
||||
it('adds previous error messages and assertion failures to the reported metadata', () => {
|
||||
reporter.reportPreviousErrors = true
|
||||
|
||||
reporter.reportUncaughtException(new Error('A'))
|
||||
reporter.reportUncaughtException(new Error('B'))
|
||||
reporter.reportFailedAssertion(new Error('X'))
|
||||
reporter.reportFailedAssertion(new Error('Y'))
|
||||
|
||||
reporter.reportUncaughtException(new Error('C'))
|
||||
|
||||
expect(requests.length).toBe(5)
|
||||
|
||||
const lastRequest = requests[requests.length - 1]
|
||||
const body = JSON.parse(lastRequest.body)
|
||||
|
||||
console.log(body);
|
||||
expect(body.events[0].metaData.previousErrors).toEqual(['A', 'B'])
|
||||
expect(body.events[0].metaData.previousAssertionFailures).toEqual(['X', 'Y'])
|
||||
})
|
||||
})
|
||||
|
||||
describe(".reportFailedAssertion(error)", () => {
|
||||
it("posts warnings to bugsnag", () => {
|
||||
fs.getHomeDirectory = () => path.join(__dirname, '..', '..')
|
||||
|
||||
let error = new Error()
|
||||
Error.captureStackTrace(error)
|
||||
reporter.reportFailedAssertion(error)
|
||||
let [lineNumber, columnNumber] = error.stack.match(/.js:(\d+):(\d+)/).slice(1).map(s => parseInt(s))
|
||||
|
||||
expect(requests.length).toBe(1)
|
||||
let [request] = requests
|
||||
expect(request.method).toBe("POST")
|
||||
expect(request.url).toBe("https://notify.bugsnag.com")
|
||||
expect(request.headers.get("Content-Type")).toBe("application/json")
|
||||
let body = JSON.parse(request.body)
|
||||
// Delete `inProject` field because tests may fail when run as part of Atom core
|
||||
// (i.e. when this test file will be located under `node_modules/exception-reporting/spec`)
|
||||
delete body.events[0].exceptions[0].stacktrace[0].inProject
|
||||
|
||||
expect(body).toEqual({
|
||||
"apiKey": Reporter.API_KEY,
|
||||
"notifier": {
|
||||
"name": "Atom",
|
||||
"version": Reporter.LIB_VERSION,
|
||||
"url": "https://www.atom.io"
|
||||
},
|
||||
"events": [
|
||||
{
|
||||
"payloadVersion": "2",
|
||||
"exceptions": [
|
||||
{
|
||||
"errorClass": "Error",
|
||||
"message": "",
|
||||
"stacktrace": [
|
||||
{
|
||||
"method": semver.gt(process.versions.electron, '1.6.0') ? 'Spec.it' : 'it',
|
||||
"file": '~/exception-reporting/spec/reporter-spec.js',
|
||||
"lineNumber": lineNumber,
|
||||
"columnNumber": columnNumber
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"severity": "warning",
|
||||
"user": {},
|
||||
"app": {
|
||||
"version": atom.getVersion(),
|
||||
"releaseStage": getReleaseChannel(atom.getVersion())
|
||||
},
|
||||
"device": {
|
||||
"osVersion": osVersion
|
||||
}
|
||||
}
|
||||
]
|
||||
});})
|
||||
|
||||
describe("when the error object has `privateMetadata` and `privateMetadataDescription` fields", () => {
|
||||
let [error, notification] = []
|
||||
|
||||
beforeEach(() => {
|
||||
atom.notifications.clear()
|
||||
spyOn(atom.notifications, 'addInfo').andCallThrough()
|
||||
|
||||
error = new Error()
|
||||
Error.captureStackTrace(error)
|
||||
|
||||
error.metadata = {foo: "bar"}
|
||||
error.privateMetadata = {baz: "quux"}
|
||||
error.privateMetadataDescription = "The contents of baz"
|
||||
})
|
||||
|
||||
it("posts a notification asking for consent", () => {
|
||||
reporter.reportFailedAssertion(error)
|
||||
expect(atom.notifications.addInfo).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("submits the error with the private metadata if the user consents", () => {
|
||||
spyOn(reporter, 'reportFailedAssertion').andCallThrough()
|
||||
reporter.reportFailedAssertion(error)
|
||||
reporter.reportFailedAssertion.reset()
|
||||
|
||||
notification = atom.notifications.getNotifications()[0]
|
||||
|
||||
let notificationOptions = atom.notifications.addInfo.argsForCall[0][1]
|
||||
expect(notificationOptions.buttons[1].text).toMatch(/Yes/)
|
||||
|
||||
notificationOptions.buttons[1].onDidClick()
|
||||
expect(reporter.reportFailedAssertion).toHaveBeenCalledWith(error)
|
||||
expect(reporter.reportFailedAssertion.callCount).toBe(1)
|
||||
expect(error.privateMetadata).toBeUndefined()
|
||||
expect(error.privateMetadataDescription).toBeUndefined()
|
||||
expect(error.metadata).toEqual({foo: "bar", baz: "quux"})
|
||||
|
||||
expect(notification.isDismissed()).toBe(true)
|
||||
})
|
||||
|
||||
it("submits the error without the private metadata if the user does not consent", () => {
|
||||
spyOn(reporter, 'reportFailedAssertion').andCallThrough()
|
||||
reporter.reportFailedAssertion(error)
|
||||
reporter.reportFailedAssertion.reset()
|
||||
|
||||
notification = atom.notifications.getNotifications()[0]
|
||||
|
||||
let notificationOptions = atom.notifications.addInfo.argsForCall[0][1]
|
||||
expect(notificationOptions.buttons[0].text).toMatch(/No/)
|
||||
|
||||
notificationOptions.buttons[0].onDidClick()
|
||||
expect(reporter.reportFailedAssertion).toHaveBeenCalledWith(error)
|
||||
expect(reporter.reportFailedAssertion.callCount).toBe(1)
|
||||
expect(error.privateMetadata).toBeUndefined()
|
||||
expect(error.privateMetadataDescription).toBeUndefined()
|
||||
expect(error.metadata).toEqual({foo: "bar"})
|
||||
|
||||
expect(notification.isDismissed()).toBe(true)
|
||||
})
|
||||
|
||||
it("submits the error without the private metadata if the user dismisses the notification", () => {
|
||||
spyOn(reporter, 'reportFailedAssertion').andCallThrough()
|
||||
reporter.reportFailedAssertion(error)
|
||||
reporter.reportFailedAssertion.reset()
|
||||
|
||||
notification = atom.notifications.getNotifications()[0]
|
||||
notification.dismiss()
|
||||
|
||||
expect(reporter.reportFailedAssertion).toHaveBeenCalledWith(error)
|
||||
expect(reporter.reportFailedAssertion.callCount).toBe(1)
|
||||
expect(error.privateMetadata).toBeUndefined()
|
||||
expect(error.privateMetadataDescription).toBeUndefined()
|
||||
expect(error.metadata).toEqual({foo: "bar"})
|
||||
})
|
||||
|
||||
it("only notifies the user once for a given 'privateMetadataRequestName'", () => {
|
||||
let fakeStorage = {}
|
||||
spyOn(global.localStorage, 'setItem').andCallFake((key, value) => fakeStorage[key] = value)
|
||||
spyOn(global.localStorage, 'getItem').andCallFake(key => fakeStorage[key])
|
||||
|
||||
error.privateMetadataRequestName = 'foo'
|
||||
|
||||
reporter.reportFailedAssertion(error)
|
||||
expect(atom.notifications.addInfo).toHaveBeenCalled()
|
||||
atom.notifications.addInfo.reset()
|
||||
|
||||
reporter.reportFailedAssertion(error)
|
||||
expect(atom.notifications.addInfo).not.toHaveBeenCalled()
|
||||
|
||||
let error2 = new Error()
|
||||
Error.captureStackTrace(error2)
|
||||
error2.privateMetadataDescription = 'Something about you'
|
||||
error2.privateMetadata = {baz: 'quux'}
|
||||
error2.privateMetadataRequestName = 'bar'
|
||||
|
||||
reporter.reportFailedAssertion(error2)
|
||||
expect(atom.notifications.addInfo).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
it('treats packages located in atom.packages.getPackageDirPaths as user packages', () => {
|
||||
mockActivePackages = [
|
||||
{name: 'user-1', path: '/Users/user/.atom/packages/user-1', metadata: {version: '1.0.0'}},
|
||||
{name: 'user-2', path: '/Users/user/.atom/packages/user-2', metadata: {version: '1.2.0'}},
|
||||
{name: 'bundled-1', path: '/Applications/Atom.app/Contents/Resources/app.asar/node_modules/bundled-1', metadata: {version: '1.0.0'}},
|
||||
{name: 'bundled-2', path: '/Applications/Atom.app/Contents/Resources/app.asar/node_modules/bundled-2', metadata: {version: '1.2.0'}},
|
||||
]
|
||||
|
||||
const packageDirPaths = ['/Users/user/.atom/packages']
|
||||
|
||||
spyOn(atom.packages, 'getPackageDirPaths').andReturn(packageDirPaths)
|
||||
|
||||
let error = new Error()
|
||||
Error.captureStackTrace(error)
|
||||
reporter.reportFailedAssertion(error)
|
||||
|
||||
expect(error.metadata.userPackages).toEqual({
|
||||
'user-1': '1.0.0',
|
||||
'user-2': '1.2.0'
|
||||
})
|
||||
expect(error.metadata.bundledPackages).toEqual({
|
||||
'bundled-1': '1.0.0',
|
||||
'bundled-2': '1.2.0'
|
||||
})
|
||||
})
|
||||
|
||||
it('adds previous error messages and assertion failures to the reported metadata', () => {
|
||||
reporter.reportPreviousErrors = true
|
||||
|
||||
reporter.reportUncaughtException(new Error('A'))
|
||||
reporter.reportUncaughtException(new Error('B'))
|
||||
reporter.reportFailedAssertion(new Error('X'))
|
||||
reporter.reportFailedAssertion(new Error('Y'))
|
||||
|
||||
reporter.reportFailedAssertion(new Error('C'))
|
||||
|
||||
expect(requests.length).toBe(5)
|
||||
|
||||
const lastRequest = requests[requests.length - 1]
|
||||
const body = JSON.parse(lastRequest.body)
|
||||
|
||||
expect(body.events[0].metaData.previousErrors).toEqual(['A', 'B'])
|
||||
expect(body.events[0].metaData.previousAssertionFailures).toEqual(['X', 'Y'])
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user