From 55c500c72ac167591765bcca271a0d6e16cb5af0 Mon Sep 17 00:00:00 2001 From: David Goss Date: Sat, 17 Dec 2022 12:47:03 +0000 Subject: [PATCH 01/10] chore: load json files with fsw --- src/configuration/from_file.ts | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/configuration/from_file.ts b/src/configuration/from_file.ts index 9e4c41701..25442c961 100644 --- a/src/configuration/from_file.ts +++ b/src/configuration/from_file.ts @@ -1,5 +1,7 @@ import stringArgv from 'string-argv' +import fs from 'fs' import path from 'path' +import { promisify } from 'util' import { pathToFileURL } from 'url' import { IConfiguration } from './types' import { mergeConfigurations } from './merge_configurations' @@ -44,15 +46,21 @@ async function loadFile( file: string ): Promise> { const filePath: string = path.join(cwd, file) + const extension = path.extname(filePath) let definitions - try { - // eslint-disable-next-line @typescript-eslint/no-var-requires - definitions = require(filePath) - } catch (error) { - if (error.code === 'ERR_REQUIRE_ESM') { - definitions = await importer(pathToFileURL(filePath)) - } else { - throw error + if (extension === 'json') { + const json = await promisify(fs.readFile)(filePath, { encoding: 'utf-8' }) + definitions = JSON.parse(json) + } else { + try { + // eslint-disable-next-line @typescript-eslint/no-var-requires + definitions = require(filePath) + } catch (error) { + if (error.code === 'ERR_REQUIRE_ESM') { + definitions = await importer(pathToFileURL(filePath)) + } else { + throw error + } } } if (typeof definitions !== 'object') { From 62f3bd9f5507329aa53474747a745bb6d80f2438 Mon Sep 17 00:00:00 2001 From: David Goss Date: Sat, 17 Dec 2022 12:48:33 +0000 Subject: [PATCH 02/10] chore: add yaml dependency --- package-lock.json | 14 ++++++++++++++ package.json | 1 + 2 files changed, 15 insertions(+) diff --git a/package-lock.json b/package-lock.json index f16b0bb60..e620fafc9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,6 +47,7 @@ "util-arity": "^1.1.0", "verror": "^1.10.0", "xmlbuilder": "^15.1.1", + "yaml": "^2.1.3", "yup": "^0.32.11" }, "bin": { @@ -7909,6 +7910,14 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/yaml": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.3.tgz", + "integrity": "sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==", + "engines": { + "node": ">= 14" + } + }, "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -14065,6 +14074,11 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "yaml": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.3.tgz", + "integrity": "sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==" + }, "yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", diff --git a/package.json b/package.json index 3ddf02a6f..394d9ec5b 100644 --- a/package.json +++ b/package.json @@ -234,6 +234,7 @@ "util-arity": "^1.1.0", "verror": "^1.10.0", "xmlbuilder": "^15.1.1", + "yaml": "^2.1.3", "yup": "^0.32.11" }, "devDependencies": { From 2b4168a7bb7617fc0405f088e87dc7b97698f8a2 Mon Sep 17 00:00:00 2001 From: David Goss Date: Sat, 17 Dec 2022 12:51:51 +0000 Subject: [PATCH 03/10] chore: add new featue for yaml --- features/profiles.feature | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/features/profiles.feature b/features/profiles.feature index ba0771843..ce057271e 100644 --- a/features/profiles.feature +++ b/features/profiles.feature @@ -117,3 +117,19 @@ Feature: default command line arguments 1 step (1 skipped) """ + + Scenario: using a YAML file + Given a file named ".cucumber-rc.yaml" with: + """ + default: + dryRun: true + """ + When I run cucumber-js with `--config .cucumber-rc.yaml` + Then it outputs the text: + """ + - + + 1 scenario (1 skipped) + 1 step (1 skipped) + + """ From 4ed9d6edaabb84fffc8ae13067bb49046b1286d4 Mon Sep 17 00:00:00 2001 From: David Goss Date: Sat, 17 Dec 2022 12:58:55 +0000 Subject: [PATCH 04/10] feat: parse yaml files --- src/configuration/from_file.ts | 7 ++++--- src/configuration/from_file_spec.ts | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/configuration/from_file.ts b/src/configuration/from_file.ts index 25442c961..33e34ed71 100644 --- a/src/configuration/from_file.ts +++ b/src/configuration/from_file.ts @@ -1,6 +1,7 @@ import stringArgv from 'string-argv' import fs from 'fs' import path from 'path' +import YAML from 'yaml' import { promisify } from 'util' import { pathToFileURL } from 'url' import { IConfiguration } from './types' @@ -48,9 +49,9 @@ async function loadFile( const filePath: string = path.join(cwd, file) const extension = path.extname(filePath) let definitions - if (extension === 'json') { - const json = await promisify(fs.readFile)(filePath, { encoding: 'utf-8' }) - definitions = JSON.parse(json) + if (extension === '.json' || extension === '.yaml' || extension === '.yml') { + const raw = await promisify(fs.readFile)(filePath, { encoding: 'utf-8' }) + definitions = YAML.parse(raw) } else { try { // eslint-disable-next-line @typescript-eslint/no-var-requires diff --git a/src/configuration/from_file_spec.ts b/src/configuration/from_file_spec.ts index a785b1806..7b75557e8 100644 --- a/src/configuration/from_file_spec.ts +++ b/src/configuration/from_file_spec.ts @@ -99,5 +99,20 @@ describe('fromFile', () => { const result = await fromFile(logger, cwd, 'cucumber.json', ['p1']) expect(result).to.deep.eq({ paths: ['other/path/*.feature'] }) }) + + it('should work with yaml', async () => { + const { logger, cwd } = await setup( + 'cucumber.yaml', + `default: + +p1: + paths: + - "other/path/*.feature" +` + ) + + const result = await fromFile(logger, cwd, 'cucumber.yaml', ['p1']) + expect(result).to.deep.eq({ paths: ['other/path/*.feature'] }) + }) }) }) From 8de178eb306abfb38b582d20cc9cca977730834e Mon Sep 17 00:00:00 2001 From: David Goss Date: Sat, 17 Dec 2022 12:59:19 +0000 Subject: [PATCH 05/10] feat: add yaml extensions to defaults --- src/configuration/locate_file.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/configuration/locate_file.ts b/src/configuration/locate_file.ts index e4897aecf..36c8011e8 100644 --- a/src/configuration/locate_file.ts +++ b/src/configuration/locate_file.ts @@ -6,6 +6,8 @@ const DEFAULT_FILENAMES = [ 'cucumber.cjs', 'cucumber.mjs', 'cucumber.json', + 'cucumber.yaml', + 'cucumber.yml', ] export function locateFile(cwd: string): string | undefined { From cc574a1fe0163d120d141261e9d2a6b080345b8a Mon Sep 17 00:00:00 2001 From: David Goss Date: Sat, 17 Dec 2022 16:12:31 +0000 Subject: [PATCH 06/10] chore: update docs --- docs/configuration.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index ccbce08c9..678801631 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -12,6 +12,8 @@ You can keep your configuration in a file. Cucumber will look for one of these f - `cucumber.cjs` - `cucumber.mjs` - `cucumber.json` +- `cucumber.yaml` +- `cucumber.yml` You can also put your file somewhere else and tell Cucumber via the `--config` CLI option: @@ -45,11 +47,20 @@ And the same in JSON format: { "default": { "parallel": 2, - "format": ["html:cucumber-report.html"] + "format": ["html:cucumber-report.html"] } } ``` +And the same in YAML format: + +```yaml +default: + parallel: 2 + format: + - "html:cucumber-report.html" +``` + Cucumber also supports the configuration being a string of options in the style of the CLI, though this isn't recommended: ```js From bf1fa066a8762d902be85280161be9087fad9853 Mon Sep 17 00:00:00 2001 From: David Goss Date: Sat, 17 Dec 2022 16:19:53 +0000 Subject: [PATCH 07/10] chore: refactor a bit --- src/configuration/from_file.ts | 53 +++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/src/configuration/from_file.ts b/src/configuration/from_file.ts index 33e34ed71..1bcf10707 100644 --- a/src/configuration/from_file.ts +++ b/src/configuration/from_file.ts @@ -1,14 +1,14 @@ -import stringArgv from 'string-argv' -import fs from 'fs' -import path from 'path' -import YAML from 'yaml' -import { promisify } from 'util' -import { pathToFileURL } from 'url' -import { IConfiguration } from './types' -import { mergeConfigurations } from './merge_configurations' -import ArgvParser from './argv_parser' -import { checkSchema } from './check_schema' -import { ILogger } from '../logger' +import stringArgv from "string-argv"; +import fs from "fs"; +import path from "path"; +import YAML from "yaml"; +import { promisify } from "util"; +import { pathToFileURL } from "url"; +import { IConfiguration } from "./types"; +import { mergeConfigurations } from "./merge_configurations"; +import ArgvParser from "./argv_parser"; +import { checkSchema } from "./check_schema"; +import { ILogger } from "../logger"; // eslint-disable-next-line @typescript-eslint/no-var-requires const { importer } = require('../importer') @@ -49,20 +49,25 @@ async function loadFile( const filePath: string = path.join(cwd, file) const extension = path.extname(filePath) let definitions - if (extension === '.json' || extension === '.yaml' || extension === '.yml') { - const raw = await promisify(fs.readFile)(filePath, { encoding: 'utf-8' }) - definitions = YAML.parse(raw) - } else { - try { - // eslint-disable-next-line @typescript-eslint/no-var-requires - definitions = require(filePath) - } catch (error) { - if (error.code === 'ERR_REQUIRE_ESM') { - definitions = await importer(pathToFileURL(filePath)) - } else { - throw error + switch (extension) { + case '.json': + definitions = JSON.parse(await promisify(fs.readFile)(filePath, { encoding: 'utf-8' })) + break + case '.yaml': + case '.yml': + definitions = YAML.parse(await promisify(fs.readFile)(filePath, { encoding: 'utf-8' })) + break + default: + try { + // eslint-disable-next-line @typescript-eslint/no-var-requires + definitions = require(filePath) + } catch (error) { + if (error.code === 'ERR_REQUIRE_ESM') { + definitions = await importer(pathToFileURL(filePath)) + } else { + throw error + } } - } } if (typeof definitions !== 'object') { throw new Error(`Configuration file ${filePath} does not export an object`) From 0478bed623dcf7690b7fa7b807bfca865772055d Mon Sep 17 00:00:00 2001 From: David Goss Date: Sat, 17 Dec 2022 16:32:22 +0000 Subject: [PATCH 08/10] chore: lint issues --- src/configuration/from_file.ts | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/configuration/from_file.ts b/src/configuration/from_file.ts index 1bcf10707..d95ab51e1 100644 --- a/src/configuration/from_file.ts +++ b/src/configuration/from_file.ts @@ -1,14 +1,14 @@ -import stringArgv from "string-argv"; -import fs from "fs"; -import path from "path"; -import YAML from "yaml"; -import { promisify } from "util"; -import { pathToFileURL } from "url"; -import { IConfiguration } from "./types"; -import { mergeConfigurations } from "./merge_configurations"; -import ArgvParser from "./argv_parser"; -import { checkSchema } from "./check_schema"; -import { ILogger } from "../logger"; +import stringArgv from 'string-argv' +import fs from 'fs' +import path from 'path' +import YAML from 'yaml' +import { promisify } from 'util' +import { pathToFileURL } from 'url' +import { IConfiguration } from './types' +import { mergeConfigurations } from './merge_configurations' +import ArgvParser from './argv_parser' +import { checkSchema } from './check_schema' +import { ILogger } from '../logger' // eslint-disable-next-line @typescript-eslint/no-var-requires const { importer } = require('../importer') @@ -51,11 +51,15 @@ async function loadFile( let definitions switch (extension) { case '.json': - definitions = JSON.parse(await promisify(fs.readFile)(filePath, { encoding: 'utf-8' })) + definitions = JSON.parse( + await promisify(fs.readFile)(filePath, { encoding: 'utf-8' }) + ) break case '.yaml': case '.yml': - definitions = YAML.parse(await promisify(fs.readFile)(filePath, { encoding: 'utf-8' })) + definitions = YAML.parse( + await promisify(fs.readFile)(filePath, { encoding: 'utf-8' }) + ) break default: try { From da2875cf7bf96aa8de68e067f54070e28ba15847 Mon Sep 17 00:00:00 2001 From: David Goss Date: Sat, 17 Dec 2022 16:37:39 +0000 Subject: [PATCH 09/10] update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 318547841..ac429c554 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](https://github.com/cucumber/cucumber/blob/master/CONTRIBUTING.md) on how to contribute to Cucumber. ## [Unreleased] +### Added +- Add support for YAML as a configuration file format ([#2199](https://github.com/cucumber/cucumber-js/pull/2199)) ## [8.9.1] - 2022-12-16 ### Fixed From 111e376ea521fbb77a79a8bae8e15680d0beeb8a Mon Sep 17 00:00:00 2001 From: David Goss Date: Sat, 17 Dec 2022 16:46:59 +0000 Subject: [PATCH 10/10] go back to node 12 compatible yaml version --- package-lock.json | 16 ++++++++-------- package.json | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index e620fafc9..15f260be6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,7 +47,7 @@ "util-arity": "^1.1.0", "verror": "^1.10.0", "xmlbuilder": "^15.1.1", - "yaml": "^2.1.3", + "yaml": "1.10.2", "yup": "^0.32.11" }, "bin": { @@ -7911,11 +7911,11 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yaml": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.3.tgz", - "integrity": "sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==", + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", "engines": { - "node": ">= 14" + "node": ">= 6" } }, "node_modules/yargs": { @@ -14075,9 +14075,9 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "yaml": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.3.tgz", - "integrity": "sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==" + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==" }, "yargs": { "version": "16.2.0", diff --git a/package.json b/package.json index 394d9ec5b..374fcf80e 100644 --- a/package.json +++ b/package.json @@ -234,7 +234,7 @@ "util-arity": "^1.1.0", "verror": "^1.10.0", "xmlbuilder": "^15.1.1", - "yaml": "^2.1.3", + "yaml": "1.10.2", "yup": "^0.32.11" }, "devDependencies": {