Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions bin/run
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ governing permissions and limitations under the License.
process.stderr.setMaxListeners(30)
process.stdout.setMaxListeners(30)

// `@oclif/core` v4 exposes `handle` and `flush` as named exports on their
// respective subpath modules. Passing the module object (instead of the
// function) to `Promise#catch` / `Promise#then` silently drops the
// handler: errors escape to Node's default uncaught-exception printer
// instead of oclif's renderer, and `flush()` is never invoked.
// Destructure the functions explicitly so both paths run as intended.
const { handle } = require('@oclif/core/handle')
const { flush } = require('@oclif/core/flush')

require('../src/').run()
.then(() => require('@oclif/core/flush'))
.catch(require('@oclif/core/handle'))
.then(flush)
.catch(handle)
30 changes: 30 additions & 0 deletions e2e/e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,45 @@ governing permissions and limitations under the License.
*/

const execa = require('execa')
const path = require('path')
const fs = jest.requireActual('fs')
const util = require('util')
const fse = {
mkdir: util.promisify(fs.mkdir),
rm: util.promisify(fs.rm)
}

const BIN_RUN = path.resolve(__dirname, '../bin/run')

jest.setTimeout(120000)

test('errors render through oclif, not Node\'s uncaught-exception printer', async () => {
// Spawns the real bin/run with an argv guaranteed to trigger a flag
// parse error from `@oclif/core`. When the bin's `.catch` handler is
// wired correctly, oclif's renderer produces a clean single-line
// `Error: …` message. When the handler is dropped (regression
// tracked in adobe/aio-cli#829 / ACNA-4659), the rejection escapes
// to Node and the output instead contains a source frame, a caret,
// a stack trace, and a `Node.js vXX` footer. Asserting on the
// absence of those Node-specific markers detects the regression
// without coupling to oclif's exact prefix.
const result = await execa('node', [BIN_RUN, 'app', 'use', '--no-such-flag'],
{ reject: false })

// Non-zero exit on a parse error is part of the contract.
expect(result.exitCode).not.toBe(0)

// The user-facing message must be present so the customer knows what
// went wrong; the framework renders it on stderr.
expect(result.stderr).toMatch(/Nonexistent flag.*--no-such-flag/)

// None of these substrings should appear: each is a fingerprint of
// Node's default unhandled-rejection / uncaught-exception output.
expect(result.stderr).not.toMatch(/at processTicksAndRejections/)
expect(result.stderr).not.toMatch(/^Node\.js v\d+/m)
expect(result.stderr).not.toMatch(/^\s+\^\s*$/m)
})

test('cli init test', async () => {
const testFolder = 'e2e_test_run'

Expand Down
Loading