diff --git a/Cakefile b/Cakefile
index 01fdd1e2e8..a43239a5f7 100644
--- a/Cakefile
+++ b/Cakefile
@@ -376,7 +376,6 @@ task 'bench', 'quick benchmark of compilation time', ->
# Run the CoffeeScript test suite.
runTests = (CoffeeScript) ->
CoffeeScript.register() unless global.testingBrowser
- startTime = Date.now()
# These are attached to `global` so that they’re accessible from within
# `test/async.coffee`, which has an async-capable version of
@@ -396,29 +395,35 @@ runTests = (CoffeeScript) ->
global.yellow = yellow
global.reset = reset
+ asyncTests = []
+ onFail = (description, fn, err) ->
+ failures.push
+ filename: global.currentFile
+ error: err
+ description: description
+ source: fn.toString() if fn.toString?
+
# Our test helper function for delimiting different test cases.
global.test = (description, fn) ->
try
fn.test = {description, currentFile}
- fn.call(fn)
- ++passedTests
- catch e
- failures.push
- filename: currentFile
- error: e
- description: description if description?
- source: fn.toString() if fn.toString?
-
- global.supportsAsync = if global.testingBrowser
- try
+ result = fn.call(fn)
+ if result instanceof Promise # An async test.
+ asyncTests.push result
+ result.then ->
+ passedTests++
+ .catch (err) ->
+ onFail description, fn, err
+ else
+ passedTests++
+ catch err
+ onFail description, fn, err
+
+ global.supportsAsync = try
new Function('async () => {}')()
yes
catch
no
- else
- [major, minor, build] = process.versions.node.split('.').map (version) ->
- parseInt version, 10
- major >= 8 or (major is 7 and minor >= 6)
helpers.extend global, require './test/support/helpers'
@@ -442,6 +447,7 @@ runTests = (CoffeeScript) ->
unless global.supportsAsync # Except for async tests, if async isn’t supported.
files = files.filter (filename) -> filename isnt 'async.coffee'
+ startTime = Date.now()
for file in files when helpers.isCoffee file
literate = helpers.isLiterate file
currentFile = filename = path.join 'test', file
@@ -450,12 +456,13 @@ runTests = (CoffeeScript) ->
CoffeeScript.run code.toString(), {filename, literate}
catch error
failures.push {filename, error}
- return !failures.length
+
+ Promise.all(asyncTests).then ->
+ Promise.reject() if failures.length isnt 0
task 'test', 'run the CoffeeScript language test suite', ->
- testResults = runTests CoffeeScript
- process.exit 1 unless testResults
+ runTests(CoffeeScript).catch -> process.exit 1
task 'test:browser', 'run the test suite against the merged browser script', ->
@@ -463,8 +470,8 @@ task 'test:browser', 'run the test suite against the merged browser script', ->
result = {}
global.testingBrowser = yes
(-> eval source).call result
- testResults = runTests result.CoffeeScript
- process.exit 1 unless testResults
+ runTests(CoffeeScript).catch -> process.exit 1
+
task 'test:integrations', 'test the module integrated with other libraries and environments', ->
# Tools like Webpack and Browserify generate builds intended for a browser
diff --git a/documentation/test.html b/documentation/test.html
index 3254e97574..dc2ad5abef 100644
--- a/documentation/test.html
+++ b/documentation/test.html
@@ -52,16 +52,27 @@
CoffeeScript Test Suite
stdout.appendChild div
msg
+asyncTests = []
+onFail = (description, fn, err) ->
+ failures.push
+ error: err
+ description: description
+ source: fn.toString() if fn.toString?
+
@test = (description, fn) ->
++total
try
- fn.call(fn)
- ++passedTests
- catch error
- failures.push
- error: error
- description: description
- source: fn.toString() if fn.toString?
+ result = fn.call(fn)
+ if result instanceof Promise # An async test.
+ asyncTests.push result
+ result.then ->
+ passedTests++
+ .catch (err) ->
+ onFail description, fn, err
+ else
+ passedTests++
+ catch err
+ onFail description, fn, err
@failures =
push: (failure) -> # Match function called by regular tests
@@ -74,11 +85,11 @@ CoffeeScript Test Suite
@ok = (good, msg = 'Error') ->
throw Error msg unless good
-# Polyfill Node assert's fail
+# Polyfill Node assert’s fail
@fail = ->
ok no
-# Polyfill Node assert's deepEqual with Underscore's isEqual
+# Polyfill Node assert’s deepEqual with Underscore’s isEqual
@deepEqual = (a, b) ->
ok _.isEqual(a, b), "Expected #{JSON.stringify a} to deep equal #{JSON.stringify b}"
@@ -114,11 +125,14 @@ CoffeeScript Test Suite
CoffeeScript.run test.innerHTML, options
# Finish up
-yay = passedTests is total and not failedTests
-sec = (new Date - start) / 1000
-msg = "passed #{passedTests} tests in #{sec.toFixed 2} seconds"
-msg = "failed #{total - passedTests} tests and #{msg}" unless yay
-say msg, (if yay then 'good' else 'bad')
+done = ->
+ yay = passedTests is total and not failedTests
+ sec = (new Date - start) / 1000
+ msg = "passed #{passedTests} tests in #{sec.toFixed 2} seconds"
+ msg = "failed #{total - passedTests} tests and #{msg}" unless yay
+ say msg, (if yay then 'good' else 'bad')
+
+Promise.all(asyncTests).then(done).catch(done)
<%= tests %>
diff --git a/test/async.coffee b/test/async.coffee
index cb71d2f1bd..be1994c589 100644
--- a/test/async.coffee
+++ b/test/async.coffee
@@ -1,28 +1,15 @@
-# Functions that contain the `await` keyword will compile into async
-# functions, which currently only Node 7+ in harmony mode can even
-# evaluate, much less run. Therefore we need to prevent runtimes
-# which will choke on such code from even loading it. This file is
-# only loaded by async-capable environments, so we redefine `test`
-# here even though it is based on `test` defined in `Cakefile`.
-# It replaces `test` for this file, and adds to the tracked
-# `passedTests` and `failures` arrays which are global objects.
-test = (description, fn) ->
- try
- fn.test = {description, currentFile}
- await fn.call(fn)
- ++passedTests
- catch e
- failures.push
- filename: currentFile
- error: e
- description: description if description?
- source: fn.toString() if fn.toString?
-
-
-# always fulfills
+# Functions that contain the `await` keyword will compile into async functions,
+# supported by Node 7.6+, Chrome 55+, Firefox 52+, Safari 10.1+ and Edge.
+# But runtimes that don’t support the `await` keyword will throw an error,
+# even if we put `return unless global.supportsAsync` at the top of this file.
+# Therefore we need to prevent runtimes which will choke on such code from even
+# parsing it, which is handled in `Cakefile`.
+
+
+# This is always fulfilled.
winning = (val) -> Promise.resolve val
-# always is rejected
+# This is always rejected.
failing = (val) -> Promise.reject new Error val
diff --git a/test/error_messages.coffee b/test/error_messages.coffee
index 640e46c432..ca583e888a 100644
--- a/test/error_messages.coffee
+++ b/test/error_messages.coffee
@@ -91,6 +91,9 @@ if require?
notEqual error.stack.toString().indexOf(filePath), -1
test "#4418: stack traces for compiled files reference the correct line number", ->
+ # The browser is already compiling other anonymous scripts (the tests)
+ # which will conflict.
+ return if global.testingBrowser
filePath = path.join os.tmpdir(), 'StackTraceLineNumberTestFile.coffee'
fileContents = """
testCompiledFileStackTraceLineNumber = ->
@@ -112,6 +115,9 @@ if require?
test "#4418: stack traces for compiled strings reference the correct line number", ->
+ # The browser is already compiling other anonymous scripts (the tests)
+ # which will conflict.
+ return if global.testingBrowser
try
CoffeeScript.run '''
testCompiledStringStackTraceLineNumber = ->
@@ -128,6 +134,9 @@ test "#4418: stack traces for compiled strings reference the correct line number
test "#4558: compiling a string inside a script doesn’t screw up stack trace line number", ->
+ # The browser is already compiling other anonymous scripts (the tests)
+ # which will conflict.
+ return if global.testingBrowser
try
CoffeeScript.run '''
testCompilingInsideAScriptDoesntScrewUpStackTraceLineNumber = ->