diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 1a8af3190..ab1f1683b 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -29,7 +29,7 @@ jobs: uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - - run: npm i -g npm@7 + - run: npm i -g npm@8 - run: npm install-test coverage: diff --git a/CHANGELOG.md b/CHANGELOG.md index 274a10a13..1e2d0e506 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,13 +8,15 @@ and this project adheres to [Semantic Versioning](http://semver.org/). Please see [CONTRIBUTING.md](https://github.com/cucumber/cucumber/blob/master/CONTRIBUTING.md) on how to contribute to Cucumber. ## [Unreleased] - ### Added - Add `willBeRetried` to the parameter passed to `After` hook functions ([#2045](https://github.com/cucumber/cucumber-js/pull/2045)) ### Changed - `defineStep` is now deprecated and will eventually be removed; use the appropriate Given/When/Then keyword to define your step ([#2044](https://github.com/cucumber/cucumber-js/pull/2044)) +### Fixed +- Prevent outputting ANSI escapes to `stderr` if it can't display them ([#2035](https://github.com/cucumber/cucumber-js/pull/2035)) + ## [8.2.2] - 2022-05-27 ### Changed - Use latest HTML formatter with better handling for scenario outlines diff --git a/package-lock.json b/package-lock.json index 869e48fe4..e18b65294 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,7 @@ "durations": "^3.4.2", "figures": "^3.2.0", "glob": "^7.1.6", + "has-ansi": "^4.0.1", "indent-string": "^4.0.0", "is-stream": "^2.0.0", "knuth-shuffle-seeded": "^1.0.6", @@ -40,6 +41,7 @@ "semver": "7.3.7", "stack-chain": "^2.0.0", "string-argv": "^0.3.1", + "strip-ansi": "6.0.1", "supports-color": "^8.1.1", "tmp": "^0.2.1", "util-arity": "^1.1.0", @@ -91,7 +93,6 @@ "express": "4.18.1", "fs-extra": "10.1.0", "genversion": "3.1.1", - "has-ansi": "^4.0.1", "mocha": "10.0.0", "mustache": "4.2.0", "nyc": "15.1.0", @@ -103,7 +104,6 @@ "sinon-chai": "3.7.0", "stream-buffers": "3.0.2", "stream-to-string": "1.2.0", - "strip-ansi": "6.0.1", "ts-node": "10.8.0", "tsd": "0.20.0", "typescript": "4.7.2" @@ -4035,7 +4035,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-4.0.1.tgz", "integrity": "sha512-Qr4RtTm30xvEdqUXbSBVWDu+PrTokJOwe/FU+VdfJPk+MXAPoeOzKpRyrDTnZIJwAkQ4oBLTU53nu0HrkF/Z2A==", - "dev": true, "dependencies": { "ansi-regex": "^4.1.0" }, @@ -4047,7 +4046,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true, "engines": { "node": ">=6" } @@ -10877,7 +10875,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-4.0.1.tgz", "integrity": "sha512-Qr4RtTm30xvEdqUXbSBVWDu+PrTokJOwe/FU+VdfJPk+MXAPoeOzKpRyrDTnZIJwAkQ4oBLTU53nu0HrkF/Z2A==", - "dev": true, "requires": { "ansi-regex": "^4.1.0" }, @@ -10885,8 +10882,7 @@ "ansi-regex": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", - "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", - "dev": true + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==" } } }, diff --git a/package.json b/package.json index aa73d96d8..f936d18bc 100644 --- a/package.json +++ b/package.json @@ -213,6 +213,7 @@ "durations": "^3.4.2", "figures": "^3.2.0", "glob": "^7.1.6", + "has-ansi": "^4.0.1", "indent-string": "^4.0.0", "is-stream": "^2.0.0", "knuth-shuffle-seeded": "^1.0.6", @@ -225,6 +226,7 @@ "semver": "7.3.7", "stack-chain": "^2.0.0", "string-argv": "^0.3.1", + "strip-ansi": "6.0.1", "supports-color": "^8.1.1", "tmp": "^0.2.1", "util-arity": "^1.1.0", @@ -273,7 +275,6 @@ "express": "4.18.1", "fs-extra": "10.1.0", "genversion": "3.1.1", - "has-ansi": "^4.0.1", "mocha": "10.0.0", "mustache": "4.2.0", "nyc": "15.1.0", @@ -285,7 +286,6 @@ "sinon-chai": "3.7.0", "stream-buffers": "3.0.2", "stream-to-string": "1.2.0", - "strip-ansi": "6.0.1", "ts-node": "10.8.0", "tsd": "0.20.0", "typescript": "4.7.2" diff --git a/src/api/formatters.ts b/src/api/formatters.ts index f01a16a10..ab83c05cb 100644 --- a/src/api/formatters.ts +++ b/src/api/formatters.ts @@ -10,12 +10,16 @@ import path from 'path' import { DEFAULT_CUCUMBER_PUBLISH_URL } from '../formatter/publish' import HttpStream from '../formatter/http_stream' import { Writable } from 'stream' +import { supportsColor } from 'supports-color' import { IRunOptionsFormats } from './types' +import hasAnsi from 'has-ansi' +import stripAnsi from 'strip-ansi' export async function initializeFormatters({ env, cwd, stdout, + stderr, logger, onStreamError, eventBroadcaster, @@ -26,6 +30,7 @@ export async function initializeFormatters({ env: NodeJS.ProcessEnv cwd: string stdout: IFormatterStream + stderr: IFormatterStream logger: Console onStreamError: () => void eventBroadcaster: EventEmitter @@ -88,7 +93,7 @@ export async function initializeFormatters({ const readerStream = new Writable({ objectMode: true, write: function (responseBody: string, encoding, writeCallback) { - logger.error(responseBody) + logger.error(sanitisePublishOutput(responseBody, stderr)) writeCallback() }, }) @@ -100,3 +105,16 @@ export async function initializeFormatters({ await Promise.all(formatters.map(async (f) => await f.finished())) } } + +/* +This is because the Cucumber Reports service returns a pre-formatted console message +including ANSI escapes, so if our stderr stream doesn't support those we need to +strip them back out. Ideally we should get structured data from the service and +compose the console message on this end. + */ +function sanitisePublishOutput(raw: string, stderr: IFormatterStream) { + if (!supportsColor(stderr) && hasAnsi(raw)) { + return stripAnsi(raw) + } + return raw +} diff --git a/src/api/run_cucumber.ts b/src/api/run_cucumber.ts index 096d728f1..05791acbb 100644 --- a/src/api/run_cucumber.ts +++ b/src/api/run_cucumber.ts @@ -58,6 +58,7 @@ export async function runCucumber( env, cwd, stdout, + stderr, logger, onStreamError: () => (formatterStreamError = true), eventBroadcaster, diff --git a/src/cli/publish_banner.ts b/src/cli/publish_banner.ts index 123bcb96b..890ddff0b 100644 --- a/src/cli/publish_banner.ts +++ b/src/cli/publish_banner.ts @@ -1,12 +1,16 @@ import chalk from 'chalk' import Table from 'cli-table3' +const chalkInstance = chalk.stderr + const underlineBoldCyan = (x: string): string => - chalk.underline(chalk.bold(chalk.cyan(x))) + chalkInstance.underline(chalkInstance.bold(chalkInstance.cyan(x))) const formattedReportUrl = underlineBoldCyan('https://reports.cucumber.io') const formattedEnv = - chalk.cyan('CUCUMBER_PUBLISH_ENABLED') + '=' + chalk.cyan('true') + chalkInstance.cyan('CUCUMBER_PUBLISH_ENABLED') + + '=' + + chalkInstance.cyan('true') const formattedMoreInfoUrl = underlineBoldCyan( 'https://cucumber.io/docs/cucumber/environment-variables/' ) @@ -14,18 +18,20 @@ const formattedMoreInfoUrl = underlineBoldCyan( const text = `\ Share your Cucumber Report with your team at ${formattedReportUrl} -Command line option: ${chalk.cyan('--publish')} +Command line option: ${chalkInstance.cyan('--publish')} Environment variable: ${formattedEnv} More information at ${formattedMoreInfoUrl} -To disable this message, add this to your ${chalk.bold('./cucumber.js')}: -${chalk.bold("module.exports = { default: '--publish-quiet' }")}` +To disable this message, add this to your ${chalkInstance.bold( + './cucumber.js' +)}: +${chalkInstance.bold("module.exports = { default: '--publish-quiet' }")}` const table = new Table({ style: { head: [], - border: ['green'], + border: chalkInstance.supportsColor ? ['green'] : [], }, })