Merge pull request #7350 from atom/ns-assertions

Add assertion mechanism
This commit is contained in:
Nathan Sobo 2015-06-22 12:18:44 -05:00
commit 1fe873740f
4 changed files with 110 additions and 35 deletions

View File

@ -110,7 +110,7 @@
"link": "0.30.0",
"markdown-preview": "0.150.0",
"metrics": "0.51.0",
"notifications": "0.56.0",
"notifications": "0.57.0",
"open-on-github": "0.37.0",
"package-generator": "0.39.0",
"release-notes": "0.53.0",

View File

@ -86,13 +86,13 @@ describe "the `atom` global", ->
error = e
window.onerror.call(window, e.toString(), 'abc', 2, 3, e)
delete willThrowSpy.mostRecentCall.args[0].preventDefault
expect(willThrowSpy).toHaveBeenCalledWith
message: error.toString()
url: 'abc'
line: 2
column: 3
originalError: error
expect(willThrowSpy).toHaveBeenCalledWith(error)
# Deprecated event properties
expect(error.url).toBe 'abc'
expect(error.line).toBe 2
expect(error.column).toBe 3
expect(error.originalError).toBe error
it "will not show the devtools when preventDefault() is called", ->
willThrowSpy.andCallFake (errorObject) -> errorObject.preventDefault()
@ -120,12 +120,60 @@ describe "the `atom` global", ->
catch e
error = e
window.onerror.call(window, e.toString(), 'abc', 2, 3, e)
expect(didThrowSpy).toHaveBeenCalledWith
message: error.toString()
url: 'abc'
line: 2
column: 3
originalError: error
expect(didThrowSpy).toHaveBeenCalledWith(error)
# Deprecated event properties
expect(error.url).toBe 'abc'
expect(error.line).toBe 2
expect(error.column).toBe 3
expect(error.originalError).toBe error
it "will not show the devtools when preventDefault() is called", ->
didThrowSpy.andCallFake (errorObject) -> errorObject.preventDefault()
atom.onDidThrowError(didThrowSpy)
try
a + 1
catch e
window.onerror.call(window, e.toString(), 'abc', 2, 3, e)
expect(didThrowSpy).toHaveBeenCalled()
expect(atom.openDevTools).not.toHaveBeenCalled()
expect(atom.executeJavaScriptInDevTools).not.toHaveBeenCalled()
describe ".assert(condition, message, metadata)", ->
errors = null
beforeEach ->
errors = []
atom.onDidFailAssertion (error) -> errors.push(error)
describe "if the condition is false", ->
it "notifies onDidFailAssertion handlers with an error object based on the call site of the assertion", ->
result = atom.assert(false, "a == b")
expect(result).toBe false
expect(errors.length).toBe 1
expect(errors[0].message).toBe "Assertion failed: a == b"
expect(errors[0].stack).toContain('atom-spec')
describe "if metadata is an object", ->
it "assigns the object on the error as `metadata`", ->
metadata = {foo: 'bar'}
atom.assert(false, "a == b", metadata)
expect(errors[0].metadata).toBe metadata
describe "if metadata is a function", ->
it "assigns the function's return value on the error as `metadata`", ->
metadata = {foo: 'bar'}
atom.assert(false, "a == b", -> metadata)
expect(errors[0].metadata).toBe metadata
describe "if the condition is true", ->
it "does nothing", ->
result = atom.assert(true, "a == b")
expect(result).toBe true
expect(errors).toEqual []
describe "saving and loading", ->
afterEach -> atom.mode = "spec"

View File

@ -198,28 +198,31 @@ class Atom extends Model
initialize: ->
sourceMapCache = {}
window.onerror = =>
@lastUncaughtError = Array::slice.call(arguments)
[message, url, line, column, originalError] = @lastUncaughtError
window.onerror = (message, url, line, column, error) =>
@lastUncaughtError = error
convertedLine = convertLine(url, line, column, sourceMapCache)
{line, column} = convertedLine if convertedLine?
originalError.stack = convertStackTrace(originalError.stack, sourceMapCache) if originalError
eventObject = {message, url, line, column, originalError}
# TODO: These should be deprecated for 2.0 once we transition the
# exception-reporting package to the new API.
error ?= {}
error.message ?= message
error.url = url
error.line = line
error.column = column
error.originalError = error
openDevTools = true
eventObject.preventDefault = -> openDevTools = false
error.preventDefault = -> openDevTools = false
@emitter.emit 'will-throw-error', eventObject
# TODO: Deprecate onWillThrowError once we transition the notifications
# package to use onDidThrowError instead.
@emitter.emit 'will-throw-error', error
@emit 'uncaught-error', arguments... if includeDeprecatedAPIs
@emitter.emit 'did-throw-error', error
if openDevTools
@openDevTools()
@executeJavaScriptInDevTools('DevToolsAPI.showConsole()')
@emit 'uncaught-error', arguments... if includeDeprecatedAPIs
@emitter.emit 'did-throw-error', {message, url, line, column, originalError}
@disposables?.dispose()
@disposables = new CompositeDisposable
@ -321,18 +324,16 @@ class Atom extends Model
# Extended: Invoke the given callback whenever there is an unhandled error.
#
# * `callback` {Function} to be called whenever there is an unhandled error
# * `event` {Object}
# * `originalError` {Object} the original error object
# * `message` {String} the original error object
# * `url` {String} Url to the file where the error originated.
# * `line` {Number}
# * `column` {Number}
# * `callback` {Function} to be called whenever there is an unhandled error.
# * `error` The unhandled {Error} object.
#
# Returns a {Disposable} on which `.dispose()` can be called to unsubscribe.
onDidThrowError: (callback) ->
@emitter.on 'did-throw-error', callback
onDidFailAssertion: (callback) ->
@emitter.on 'did-fail-assertion', callback
###
Section: Atom Details
###
@ -715,6 +716,22 @@ class Atom extends Model
Section: Private
###
assert: (condition, message, metadata) ->
return true if condition
error = new Error("Assertion failed: " + message)
Error.captureStackTrace(error, @assert)
if metadata?
if typeof metadata is 'function'
error.metadata = metadata()
else
error.metadata = metadata
@emitter.emit 'did-fail-assertion', error
false
deserializeProject: ->
Project = require './project'

View File

@ -292,7 +292,17 @@ class TokenizedBuffer extends Model
# Returns a {Boolean} indicating whether the given buffer row starts
# a a foldable row range due to the code's indentation patterns.
isFoldableCodeAtRow: (row) ->
return false if @buffer.isRowBlank(row) or @tokenizedLineForRow(row).isComment()
# Investigating an exception that's occurring here due to the line being
# undefined. This should paper over the problem but we want to figure out
# what is happening:
tokenizedLine = @tokenizedLineForRow(row)
atom.assert tokenizedLine?, "TokenizedLine is defined", =>
metadata:
row: row
rowCount: @tokenizedLines.length
return false unless tokenizedLine?
return false if @buffer.isRowBlank(row) or tokenizedLine.isComment()
nextRow = @buffer.nextNonBlankRow(row)
return false unless nextRow?