Skip to content

Commit 7ac2803

Browse files
wait for event loop to drain (#1002)
1 parent 367a2ea commit 7ac2803

File tree

10 files changed

+79
-15
lines changed

10 files changed

+79
-15
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ Please see [CONTRIBUTING.md](https://github.com/cucumber/cucumber/blob/master/CO
22

33
### [Unreleased](https://github.com/cucumber/cucumber-js/compare/v3.2.1...master) (In Git)
44

5+
#### BREAKING CHANGES
6+
7+
* cucumber now waits for the event loop to drain before exiting. To exit immediately when the tests finish running use `--exit`. Use of this flag is discouraged. See [here](/docs/cli.md#exiting) for more information
8+
59
### [3.2.1](https://github.com/cucumber/cucumber-js/compare/v3.2.0...v3.2.1) (2017-01-03)
610

711
#### Bug Fixes

docs/cli.md

+8
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ You can pass in format options with `--format-options <JSON>`. The JSON string m
7070

7171
Colors can be disabled with `--format-options '{"colorsEnabled": false}'`
7272

73+
## Exiting
74+
75+
By default, cucumber exits when the event loop drains. Use the `--exit` flag in order to force shutdown of the event loop when the test run has finished. This is discouraged, as fixing the issues that causes the hang is a better long term solution. Some potential resources for that are:
76+
* [Node.js guide to debugging](https://nodejs.org/en/docs/inspector/)
77+
* NPM package [why-is-node-running](https://www.npmjs.com/package/why-is-node-running)
78+
* [Node.js Async Hooks](https://nodejs.org/dist/latest-v8.x/docs/api/async_hooks.html)
79+
* Isolating what scenario or scenarios causes the hang
80+
7381
## Undefined Step Snippets
7482

7583
Undefined steps snippets are printed in javascript using the callback interface by default.

features/exit.feature

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
@spawn
2+
Feature: Exit
3+
4+
Use `--exit` to exit when the test run finishes without
5+
waiting to the even loop to drain
6+
7+
Background:
8+
Given a file named "features/a.feature" with:
9+
"""
10+
Feature:
11+
Scenario:
12+
Given a step
13+
"""
14+
Given a file named "features/step_definitions/cucumber_steps.js" with:
15+
"""
16+
import {defineSupportCode, After} from 'cucumber'
17+
18+
defineSupportCode(({Given}) => {
19+
Given('a step', function() {})
20+
})
21+
22+
After(() => {
23+
setTimeout(() => {
24+
console.log('external process done')
25+
}, 1000)
26+
})
27+
"""
28+
29+
Scenario: by default wait for the event loop to drain
30+
When I run cucumber.js
31+
Then the output contains the text:
32+
"""
33+
external process done
34+
"""
35+
36+
Scenario: exit immediately without waiting for the even loop to drain
37+
When I run cucumber.js with `--exit`
38+
Then the output does not contain the text:
39+
"""
40+
external process done
41+
"""

features/step_definitions/cli_steps.js

+6
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ defineSupportCode(function({ When, Then }) {
4747
expect(actualOutput).to.include(expectedOutput)
4848
})
4949

50+
Then('the output does not contain the text:', function(text) {
51+
const actualOutput = normalizeText(this.lastRun.output)
52+
const expectedOutput = normalizeText(text)
53+
expect(actualOutput).not.to.include(expectedOutput)
54+
})
55+
5056
Then(/^the error output contains the text snippets:$/, function(table) {
5157
const actualOutput = normalizeText(this.lastRun.errorOutput)
5258
table.rows().forEach(row => {

features/support/world.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ class World {
3838
})
3939
let error, stderr
4040
try {
41-
if (!await cli.run()) {
41+
const { success } = await cli.run()
42+
if (!success) {
4243
error = new Error('CLI exited with non-zero')
4344
error.code = 42
4445
}

src/cli/argv_parser.js

+4
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ export default class ArgvParser {
5252
[]
5353
)
5454
.option('-d, --dry-run', 'invoke formatters without executing steps')
55+
.option(
56+
'--exit',
57+
'force shutdown of the event loop when the test run has finished: cucumber will call process.exit'
58+
)
5559
.option('--fail-fast', 'abort the run on first failure')
5660
.option(
5761
'-f, --format <TYPE[:PATH]>',

src/cli/configuration_builder.js

+1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export default class ConfigurationBuilder {
5858
strict: !!this.options.strict,
5959
worldParameters: this.options.worldParameters
6060
},
61+
shouldExitImmediately: !!this.options.exit,
6162
supportCodePaths
6263
}
6364
}

src/cli/configuration_builder_spec.js

+1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ describe('Configuration', function() {
4646
strict: true,
4747
worldParameters: {}
4848
},
49+
shouldExitImmediately: false,
4950
supportCodePaths: []
5051
})
5152
})

src/cli/index.js

+7-4
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,11 @@ export default class Cli {
6767
const configuration = await this.getConfiguration()
6868
if (configuration.listI18nLanguages) {
6969
this.stdout.write(I18n.getLanguages())
70-
return true
70+
return { success: true }
7171
}
7272
if (configuration.listI18nKeywordsFor) {
7373
this.stdout.write(I18n.getKeywords(configuration.listI18nKeywordsFor))
74-
return true
74+
return { success: true }
7575
}
7676
const supportCodeLibrary = this.getSupportCodeLibrary(
7777
configuration.supportCodePaths
@@ -96,8 +96,11 @@ export default class Cli {
9696
supportCodeLibrary,
9797
testCases
9898
})
99-
const result = await runtime.start()
99+
const success = await runtime.start()
100100
await cleanup()
101-
return result
101+
return {
102+
shouldExitImmediately: configuration.shouldExitImmediately,
103+
success
104+
}
102105
}
103106
}

src/cli/run.js

+5-10
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,17 @@ export default async function run() {
1414
stdout: process.stdout
1515
})
1616

17-
let success
17+
let result
1818
try {
19-
success = await cli.run()
19+
result = await cli.run()
2020
} catch (error) {
2121
exitWithError(error)
2222
}
2323

24-
const exitCode = success ? 0 : 1
25-
function exitNow() {
24+
const exitCode = result.success ? 0 : 1
25+
if (result.shouldExitImmediately) {
2626
process.exit(exitCode)
27-
}
28-
29-
// If stdout.write() returned false, kernel buffer is not empty yet
30-
if (process.stdout.write('')) {
31-
exitNow()
3227
} else {
33-
process.stdout.on('drain', exitNow)
28+
process.exitCode = exitCode
3429
}
3530
}

0 commit comments

Comments
 (0)