diff --git a/CHANGELOG.md b/CHANGELOG.md index aebb8fcfe..d7ad98754 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/). Please see [CONTRIBUTING.md](./CONTRIBUTING.md) on how to contribute to Cucumber. ## [Unreleased] +### Added +- Support array notation of formats with path in configuration files ([#2318](https://github.com/cucumber/cucumber-js/pull/2318)) ## [9.4.0] - 2023-08-12 ### Fixed diff --git a/cucumber.json b/cucumber.json index 401715dd1..a8cd30254 100644 --- a/cucumber.json +++ b/cucumber.json @@ -4,11 +4,11 @@ "require": ["features/**/*.ts"], "format": [ "progress-bar", - "rerun:@rerun.txt", - "usage:reports/usage.txt", - "message:reports/messages.ndjson", - "junit:reports/junit.xml", - "html:reports/html-formatter.html" + ["rerun", "@rerun.txt"], + ["usage", "reports/usage.txt"], + ["message", "reports/messages.ndjson"], + ["junit", "reports/junit.xml"], + ["html", "reports/html-formatter.html"] ], "retry": 2, "retryTagFilter": "@flaky" diff --git a/docs/formatters.md b/docs/formatters.md index 213952c70..be559c77d 100644 --- a/docs/formatters.md +++ b/docs/formatters.md @@ -6,22 +6,19 @@ cucumber-js provides many built-in Formatters, plus building blocks with which y You can specify one or more formats via the `format` configuration option: -- In a configuration file `{ format: [''] }` -- On the CLI `$ cucumber-js --format ` +- In a configuration file `{ format: ['progress-bar', ['html', 'cucumber-report.html']] }` +- On the CLI `$ cucumber-js --format progress-bar --format html:cucumber-report.html` -For each value you provide, `TYPE` should be one of: +For each format you specify, you have to provide one or two values. The first (required) is to identify the formatter. It can take a few forms: -* The name of one of the built-in formatters (below) e.g. `progress` +* The name of one of the built-in formatters (below) e.g. `progress-bar` * A module/package name e.g. `@cucumber/pretty-formatter` * A relative path to a local formatter implementation e.g. `./my-customer-formatter.js` * An absolute path to a local formatter implementation in the form of a `file://` URL -If `PATH` is supplied, the formatter prints to the given file, otherwise it prints to `stdout`. If the path includes directories that do not yet exist they will be created. +Without a second value, the formatter will print to `stdout`. The second value, if present, is a path to where the formatter output should be written. If the path includes directories that do not yet exist, they will be created. -For example, this configuration would give you a progress bar as you run, plus JSON and HTML report files: - -- In a configuration file `{ format: ['progress-bar', 'json:cucumber-report.json', 'html:cucumber-report.html'] }` -- On the CLI `$ cucumber-js --format progress-bar --format json:cucumber-report.json --format html:cucumber-report.html` +On the CLI, when specifying both a name and path, you'll need to use `:` as a delimiter. In a configuration file you can do this too, but you can also provide an array with the two values as separate strings, which is recommended. Some notes on specifying Formatters: diff --git a/package-lock.json b/package-lock.json index 814397884..fd6f54431 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,7 +48,7 @@ "verror": "^1.10.0", "xmlbuilder": "^15.1.1", "yaml": "^2.2.2", - "yup": "^0.32.11" + "yup": "1.2.0" }, "bin": { "cucumber-js": "bin/cucumber.js" @@ -458,17 +458,6 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/runtime": { - "version": "7.21.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz", - "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==", - "dependencies": { - "regenerator-runtime": "^0.13.11" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/template": { "version": "7.20.7", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz", @@ -1454,7 +1443,8 @@ "node_modules/@types/lodash": { "version": "4.14.194", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.194.tgz", - "integrity": "sha512-r22s9tAS7imvBt2lyHC9B8AGwWnXaYb1tY09oyLkXDs4vArpYJzw09nj8MLx5VfciBPGIb+ZwG0ssYnEPJxn/g==" + "integrity": "sha512-r22s9tAS7imvBt2lyHC9B8AGwWnXaYb1tY09oyLkXDs4vArpYJzw09nj8MLx5VfciBPGIb+ZwG0ssYnEPJxn/g==", + "dev": true }, "node_modules/@types/lodash.merge": { "version": "4.6.7", @@ -5145,12 +5135,8 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" - }, - "node_modules/lodash-es": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", - "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true }, "node_modules/lodash.flattendeep": { "version": "4.4.0", @@ -5596,11 +5582,6 @@ "thenify-all": "^1.0.0" } }, - "node_modules/nanoclone": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz", - "integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==" - }, "node_modules/nanoid": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", @@ -6597,11 +6578,6 @@ "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" }, - "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==" - }, "node_modules/regexp-match-indices": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regexp-match-indices/-/regexp-match-indices-1.0.2.tgz", @@ -7384,6 +7360,11 @@ "node": ">=0.8" } }, + "node_modules/tiny-case": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-case/-/tiny-case-1.0.3.tgz", + "integrity": "sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==" + }, "node_modules/tmp": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", @@ -8084,20 +8065,25 @@ } }, "node_modules/yup": { - "version": "0.32.11", - "resolved": "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz", - "integrity": "sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/yup/-/yup-1.2.0.tgz", + "integrity": "sha512-PPqYKSAXjpRCgLgLKVGPA33v5c/WgEx3wi6NFjIiegz90zSwyMpvTFp/uGcVnnbx6to28pgnzp/q8ih3QRjLMQ==", "dependencies": { - "@babel/runtime": "^7.15.4", - "@types/lodash": "^4.14.175", - "lodash": "^4.17.21", - "lodash-es": "^4.17.21", - "nanoclone": "^0.2.1", - "property-expr": "^2.0.4", - "toposort": "^2.0.2" - }, + "property-expr": "^2.0.5", + "tiny-case": "^1.0.3", + "toposort": "^2.0.2", + "type-fest": "^2.19.0" + } + }, + "node_modules/yup/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", "engines": { - "node": ">=10" + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/z-schema": { diff --git a/package.json b/package.json index 6905ab3bd..434eb47db 100644 --- a/package.json +++ b/package.json @@ -246,7 +246,7 @@ "verror": "^1.10.0", "xmlbuilder": "^15.1.1", "yaml": "^2.2.2", - "yup": "^0.32.11" + "yup": "1.2.0" }, "devDependencies": { "@cucumber/compatibility-kit": "^12.0.0", diff --git a/src/api/convert_configuration.ts b/src/api/convert_configuration.ts index ddc7c3cae..588f40204 100644 --- a/src/api/convert_configuration.ts +++ b/src/api/convert_configuration.ts @@ -42,7 +42,7 @@ function convertFormats( env: NodeJS.ProcessEnv ) { const splitFormats: string[][] = flatConfiguration.format.map((item) => - OptionSplitter.split(item) + Array.isArray(item) ? item : OptionSplitter.split(item) ) return { stdout: diff --git a/src/api/convert_configuration_spec.ts b/src/api/convert_configuration_spec.ts index a864291f1..2cbe918b6 100644 --- a/src/api/convert_configuration_spec.ts +++ b/src/api/convert_configuration_spec.ts @@ -39,7 +39,7 @@ describe('convertConfiguration', () => { }) }) - it('should map multiple formatters', async () => { + it('should map multiple formatters with string and array notations', async () => { const result = await convertConfiguration( { ...DEFAULT_CONFIGURATION, @@ -47,7 +47,7 @@ describe('convertConfiguration', () => { 'summary', 'message', 'json:./report.json', - 'html:./report.html', + ['html', './report.html'], ], }, {} diff --git a/src/configuration/check_schema.ts b/src/configuration/check_schema.ts index 758704eba..9538d7b18 100644 --- a/src/configuration/check_schema.ts +++ b/src/configuration/check_schema.ts @@ -7,7 +7,15 @@ const schema = yup.object().shape({ dryRun: yup.boolean(), exit: yup.boolean(), failFast: yup.boolean(), - format: yup.array().of(yup.string()), + format: yup + .array() + .of( + yup.lazy((val) => + Array.isArray(val) + ? yup.array().of(yup.string()).min(2).max(2) + : yup.string() + ) + ), formatOptions: yup.object(), import: yup.array().of(yup.string()), language: yup.string().oneOf(Object.keys(dialects)), diff --git a/src/configuration/types.ts b/src/configuration/types.ts index cdc75b1d7..ad43d96f9 100644 --- a/src/configuration/types.ts +++ b/src/configuration/types.ts @@ -1,12 +1,14 @@ import { FormatOptions } from '../formatter' import { PickleOrder } from '../models/pickle_order' +type FormatsConfiguration = Array + export interface IConfiguration { backtrace: boolean dryRun: boolean forceExit: boolean failFast: boolean - format: string[] + format: FormatsConfiguration formatOptions: FormatOptions import: string[] language: string