Skip to content

Commit 9e043bb

Browse files
[CS2] Fix async tests (#4680)
* Get `coffee` command working again in Node 6, by converting the ‘await’ wrapper in the REPL to use a Promise instead of the ‘await’ keyword; add tests for REPL ‘await’ wrapper, including test to skip async tests if the runtime doesn’t support them * Fix async tests: now if a test function is a Promise (which an `await` function is), we add it to an array of async test functions and wait for them all to resolve before finishing the test run, so that if any async tests fail we see those failures in the output * Code review * Unnecessary * Let's support Node 6+ if we can * Simplify the returned promise * Simplify async check
1 parent 6714869 commit 9e043bb

File tree

4 files changed

+75
-58
lines changed

4 files changed

+75
-58
lines changed

Cakefile

Lines changed: 28 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,6 @@ task 'bench', 'quick benchmark of compilation time', ->
376376
# Run the CoffeeScript test suite.
377377
runTests = (CoffeeScript) ->
378378
CoffeeScript.register() unless global.testingBrowser
379-
startTime = Date.now()
380379

381380
# These are attached to `global` so that they’re accessible from within
382381
# `test/async.coffee`, which has an async-capable version of
@@ -396,29 +395,35 @@ runTests = (CoffeeScript) ->
396395
global.yellow = yellow
397396
global.reset = reset
398397

398+
asyncTests = []
399+
onFail = (description, fn, err) ->
400+
failures.push
401+
filename: global.currentFile
402+
error: err
403+
description: description
404+
source: fn.toString() if fn.toString?
405+
399406
# Our test helper function for delimiting different test cases.
400407
global.test = (description, fn) ->
401408
try
402409
fn.test = {description, currentFile}
403-
fn.call(fn)
404-
++passedTests
405-
catch e
406-
failures.push
407-
filename: currentFile
408-
error: e
409-
description: description if description?
410-
source: fn.toString() if fn.toString?
411-
412-
global.supportsAsync = if global.testingBrowser
413-
try
410+
result = fn.call(fn)
411+
if result instanceof Promise # An async test.
412+
asyncTests.push result
413+
result.then ->
414+
passedTests++
415+
.catch (err) ->
416+
onFail description, fn, err
417+
else
418+
passedTests++
419+
catch err
420+
onFail description, fn, err
421+
422+
global.supportsAsync = try
414423
new Function('async () => {}')()
415424
yes
416425
catch
417426
no
418-
else
419-
[major, minor, build] = process.versions.node.split('.').map (version) ->
420-
parseInt version, 10
421-
major >= 8 or (major is 7 and minor >= 6)
422427

423428
helpers.extend global, require './test/support/helpers'
424429

@@ -442,6 +447,7 @@ runTests = (CoffeeScript) ->
442447
unless global.supportsAsync # Except for async tests, if async isn’t supported.
443448
files = files.filter (filename) -> filename isnt 'async.coffee'
444449

450+
startTime = Date.now()
445451
for file in files when helpers.isCoffee file
446452
literate = helpers.isLiterate file
447453
currentFile = filename = path.join 'test', file
@@ -450,21 +456,22 @@ runTests = (CoffeeScript) ->
450456
CoffeeScript.run code.toString(), {filename, literate}
451457
catch error
452458
failures.push {filename, error}
453-
return !failures.length
459+
460+
Promise.all(asyncTests).then ->
461+
Promise.reject() if failures.length isnt 0
454462

455463

456464
task 'test', 'run the CoffeeScript language test suite', ->
457-
testResults = runTests CoffeeScript
458-
process.exit 1 unless testResults
465+
runTests(CoffeeScript).catch -> process.exit 1
459466

460467

461468
task 'test:browser', 'run the test suite against the merged browser script', ->
462469
source = fs.readFileSync "docs/v#{majorVersion}/browser-compiler/coffeescript.js", 'utf-8'
463470
result = {}
464471
global.testingBrowser = yes
465472
(-> eval source).call result
466-
testResults = runTests result.CoffeeScript
467-
process.exit 1 unless testResults
473+
runTests(CoffeeScript).catch -> process.exit 1
474+
468475

469476
task 'test:integrations', 'test the module integrated with other libraries and environments', ->
470477
# Tools like Webpack and Browserify generate builds intended for a browser

documentation/test.html

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,27 @@ <h1>CoffeeScript Test Suite</h1>
5252
stdout.appendChild div
5353
msg
5454

55+
asyncTests = []
56+
onFail = (description, fn, err) ->
57+
failures.push
58+
error: err
59+
description: description
60+
source: fn.toString() if fn.toString?
61+
5562
@test = (description, fn) ->
5663
++total
5764
try
58-
fn.call(fn)
59-
++passedTests
60-
catch error
61-
failures.push
62-
error: error
63-
description: description
64-
source: fn.toString() if fn.toString?
65+
result = fn.call(fn)
66+
if result instanceof Promise # An async test.
67+
asyncTests.push result
68+
result.then ->
69+
passedTests++
70+
.catch (err) ->
71+
onFail description, fn, err
72+
else
73+
passedTests++
74+
catch err
75+
onFail description, fn, err
6576

6677
@failures =
6778
push: (failure) -> # Match function called by regular tests
@@ -74,11 +85,11 @@ <h1>CoffeeScript Test Suite</h1>
7485
@ok = (good, msg = 'Error') ->
7586
throw Error msg unless good
7687

77-
# Polyfill Node assert's fail
88+
# Polyfill Node asserts fail
7889
@fail = ->
7990
ok no
8091

81-
# Polyfill Node assert's deepEqual with Underscore's isEqual
92+
# Polyfill Node asserts deepEqual with Underscores isEqual
8293
@deepEqual = (a, b) ->
8394
ok _.isEqual(a, b), "Expected #{JSON.stringify a} to deep equal #{JSON.stringify b}"
8495

@@ -114,11 +125,14 @@ <h1>CoffeeScript Test Suite</h1>
114125
CoffeeScript.run test.innerHTML, options
115126

116127
# Finish up
117-
yay = passedTests is total and not failedTests
118-
sec = (new Date - start) / 1000
119-
msg = "passed #{passedTests} tests in #{sec.toFixed 2} seconds"
120-
msg = "failed #{total - passedTests} tests and #{msg}" unless yay
121-
say msg, (if yay then 'good' else 'bad')
128+
done = ->
129+
yay = passedTests is total and not failedTests
130+
sec = (new Date - start) / 1000
131+
msg = "passed #{passedTests} tests in #{sec.toFixed 2} seconds"
132+
msg = "failed #{total - passedTests} tests and #{msg}" unless yay
133+
say msg, (if yay then 'good' else 'bad')
134+
135+
Promise.all(asyncTests).then(done).catch(done)
122136
</script>
123137

124138
<%= tests %>

test/async.coffee

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,15 @@
1-
# Functions that contain the `await` keyword will compile into async
2-
# functions, which currently only Node 7+ in harmony mode can even
3-
# evaluate, much less run. Therefore we need to prevent runtimes
4-
# which will choke on such code from even loading it. This file is
5-
# only loaded by async-capable environments, so we redefine `test`
6-
# here even though it is based on `test` defined in `Cakefile`.
7-
# It replaces `test` for this file, and adds to the tracked
8-
# `passedTests` and `failures` arrays which are global objects.
9-
test = (description, fn) ->
10-
try
11-
fn.test = {description, currentFile}
12-
await fn.call(fn)
13-
++passedTests
14-
catch e
15-
failures.push
16-
filename: currentFile
17-
error: e
18-
description: description if description?
19-
source: fn.toString() if fn.toString?
20-
21-
22-
# always fulfills
1+
# Functions that contain the `await` keyword will compile into async functions,
2+
# supported by Node 7.6+, Chrome 55+, Firefox 52+, Safari 10.1+ and Edge.
3+
# But runtimes that don’t support the `await` keyword will throw an error,
4+
# even if we put `return unless global.supportsAsync` at the top of this file.
5+
# Therefore we need to prevent runtimes which will choke on such code from even
6+
# parsing it, which is handled in `Cakefile`.
7+
8+
9+
# This is always fulfilled.
2310
winning = (val) -> Promise.resolve val
2411

25-
# always is rejected
12+
# This is always rejected.
2613
failing = (val) -> Promise.reject new Error val
2714

2815

test/error_messages.coffee

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ if require?
9191
notEqual error.stack.toString().indexOf(filePath), -1
9292

9393
test "#4418: stack traces for compiled files reference the correct line number", ->
94+
# The browser is already compiling other anonymous scripts (the tests)
95+
# which will conflict.
96+
return if global.testingBrowser
9497
filePath = path.join os.tmpdir(), 'StackTraceLineNumberTestFile.coffee'
9598
fileContents = """
9699
testCompiledFileStackTraceLineNumber = ->
@@ -112,6 +115,9 @@ if require?
112115

113116

114117
test "#4418: stack traces for compiled strings reference the correct line number", ->
118+
# The browser is already compiling other anonymous scripts (the tests)
119+
# which will conflict.
120+
return if global.testingBrowser
115121
try
116122
CoffeeScript.run '''
117123
testCompiledStringStackTraceLineNumber = ->
@@ -128,6 +134,9 @@ test "#4418: stack traces for compiled strings reference the correct line number
128134

129135

130136
test "#4558: compiling a string inside a script doesn’t screw up stack trace line number", ->
137+
# The browser is already compiling other anonymous scripts (the tests)
138+
# which will conflict.
139+
return if global.testingBrowser
131140
try
132141
CoffeeScript.run '''
133142
testCompilingInsideAScriptDoesntScrewUpStackTraceLineNumber = ->

0 commit comments

Comments
 (0)