diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cb5d0834..dc1df00d4 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 +- Allow `provided` configuration to be a string ([#2373](https://github.com/cucumber/cucumber-js/pull/2373)) ## [10.2.1] - 2024-01-07 ### Fixed diff --git a/exports/api/report.api.md b/exports/api/report.api.md index 585c4e978..199f25001 100644 --- a/exports/api/report.api.md +++ b/exports/api/report.api.md @@ -40,7 +40,7 @@ export interface IConfiguration { export interface ILoadConfigurationOptions { file?: string | false; profiles?: string[]; - provided?: Partial; + provided?: Partial | string; } // @public diff --git a/src/api/load_configuration.ts b/src/api/load_configuration.ts index f5677916a..d1d366c23 100644 --- a/src/api/load_configuration.ts +++ b/src/api/load_configuration.ts @@ -3,8 +3,9 @@ import { DEFAULT_CONFIGURATION, fromFile, mergeConfigurations, + parseConfiguration, + validateConfiguration, } from '../configuration' -import { validateConfiguration } from '../configuration/validate_configuration' import { convertConfiguration } from './convert_configuration' import { mergeEnvironment } from './environment' import { @@ -39,7 +40,7 @@ export async function loadConfiguration( const original = mergeConfigurations( DEFAULT_CONFIGURATION, profileConfiguration, - options.provided + parseConfiguration(logger, 'Provided', options.provided) ) logger.debug('Resolved configuration:', original) validateConfiguration(original, logger) diff --git a/src/api/load_configuration_spec.ts b/src/api/load_configuration_spec.ts index a5df16bd9..e77e0248d 100644 --- a/src/api/load_configuration_spec.ts +++ b/src/api/load_configuration_spec.ts @@ -12,6 +12,15 @@ describe('loadConfiguration', function () { }) afterEach(async () => teardownEnvironment(environment)) + it('should handle configuration directly provided as a string', async () => { + const { useConfiguration } = await loadConfiguration( + { provided: `--world-parameters '{"foo":"bar"}'` }, + environment + ) + + expect(useConfiguration.worldParameters).to.deep.eq({ foo: 'bar' }) + }) + it('should skip trying to resolve from a file if `file=false`', async () => { const { useConfiguration } = await loadConfiguration( { file: false }, diff --git a/src/api/types.ts b/src/api/types.ts index 69aea7ba4..1e2686b8c 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -23,8 +23,16 @@ export interface ILoadConfigurationOptions { /** * Ad-hoc configuration options to be merged over the top of whatever is * loaded from the configuration file/profiles + * @example + * \{ + * failFast: true, + * parallel: 2 + * \} + * @example "--fail-fast --parallel 2" + * @remarks + * This can also be provided as a string of argv-style arguments. */ - provided?: Partial + provided?: Partial | string } /** diff --git a/src/configuration/from_file.ts b/src/configuration/from_file.ts index d52346fcc..7b00d73aa 100644 --- a/src/configuration/from_file.ts +++ b/src/configuration/from_file.ts @@ -2,14 +2,12 @@ import fs from 'node:fs' import path from 'node:path' import { promisify } from 'node:util' import { pathToFileURL } from 'node:url' -import stringArgv from 'string-argv' import YAML from 'yaml' import readPkgUp from 'read-pkg-up' import { ILogger } from '../logger' import { IConfiguration } from './types' import { mergeConfigurations } from './merge_configurations' -import ArgvParser from './argv_parser' -import { checkSchema } from './check_schema' +import { parseConfiguration } from './parse_configuration' export async function fromFile( logger: ILogger, @@ -35,7 +33,11 @@ export async function fromFile( return mergeConfigurations( {}, ...profiles.map((profileKey) => - extractConfiguration(logger, profileKey, definitions[profileKey]) + parseConfiguration( + logger, + `Profile "${profileKey}"`, + definitions[profileKey] + ) ) ) } @@ -109,28 +111,3 @@ async function readPackageJson(filePath: string) { const parentPackage = await readPkgUp({ cwd: path.dirname(filePath) }) return parentPackage?.packageJson } - -function extractConfiguration( - logger: ILogger, - name: string, - definition: any -): Partial { - if (typeof definition === 'string') { - logger.debug(`Profile "${name}" value is a string; parsing as argv`) - const { configuration } = ArgvParser.parse([ - 'node', - 'cucumber-js', - ...stringArgv(definition), - ]) - return configuration - } - try { - return checkSchema(definition) - } catch (error) { - throw new Error( - `Requested profile "${name}" failed schema validation: ${error.errors.join( - ' ' - )}` - ) - } -} diff --git a/src/configuration/from_file_spec.ts b/src/configuration/from_file_spec.ts index 7d27fd21c..f12290040 100644 --- a/src/configuration/from_file_spec.ts +++ b/src/configuration/from_file_spec.ts @@ -82,7 +82,7 @@ describe('fromFile', () => { expect.fail('should have thrown') } catch (error) { expect(error.message).to.eq( - 'Requested profile "p1" failed schema validation: paths must be a `array` type, but the final value was: `4`. requireModule must be a `array` type, but the final value was: `"aardvark"`.' + 'Profile "p1" configuration value failed schema validation: paths must be a `array` type, but the final value was: `4`. requireModule must be a `array` type, but the final value was: `"aardvark"`.' ) } }) diff --git a/src/configuration/index.ts b/src/configuration/index.ts index f8d91c2b8..09bd57759 100644 --- a/src/configuration/index.ts +++ b/src/configuration/index.ts @@ -3,5 +3,7 @@ export * from './default_configuration' export * from './from_file' export * from './helpers' export * from './merge_configurations' +export * from './parse_configuration' export * from './split_format_descriptor' export * from './types' +export * from './validate_configuration' diff --git a/src/configuration/parse_configuration.ts b/src/configuration/parse_configuration.ts new file mode 100644 index 000000000..e52577ea6 --- /dev/null +++ b/src/configuration/parse_configuration.ts @@ -0,0 +1,33 @@ +import stringArgv from 'string-argv' +import { ILogger } from '../logger' +import { IConfiguration } from './types' +import ArgvParser from './argv_parser' +import { checkSchema } from './check_schema' + +export function parseConfiguration( + logger: ILogger, + source: string, + definition: Partial | string | undefined +): Partial { + if (!definition) { + return {} + } + if (typeof definition === 'string') { + logger.debug(`${source} configuration value is a string; parsing as argv`) + const { configuration } = ArgvParser.parse([ + 'node', + 'cucumber-js', + ...stringArgv(definition), + ]) + return configuration + } + try { + return checkSchema(definition) + } catch (error) { + throw new Error( + `${source} configuration value failed schema validation: ${error.errors.join( + ' ' + )}` + ) + } +}