Skip to content

Commit

Permalink
use import by default for support code (#2337)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidjgoss committed Oct 9, 2023
1 parent ddb3312 commit 5224de2
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 49 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Please see [CONTRIBUTING.md](./CONTRIBUTING.md) on how to contribute to Cucumber
### Changed
- BREAKING CHANGE: Use appropriate module loading mechanism for configuration files ([#2334](https://github.com/cucumber/cucumber-js/pull/2334))
- BREAKING CHANGE: Use `await import()` to load all custom formatters and snippet syntaxes ([#2334](https://github.com/cucumber/cucumber-js/pull/2334))
- BREAKING CHANGE: Use `await import()` for default support code loading ([#2337](https://github.com/cucumber/cucumber-js/pull/2337))

### Removed
- BREAKING CHANGE: Drop support for Node.js 14, 16 and 19 ([#2331](https://github.com/cucumber/cucumber-js/pull/2331))
Expand Down
66 changes: 33 additions & 33 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,40 +8,20 @@ _**You are reading the documentation in the `main` development branch, which mig

You can keep your configuration in a file. Cucumber will look for one of these files in the root of your project, and use the first one it finds:

- `cucumber.js`
- `cucumber.cjs`
- `cucumber.mjs`
- `cucumber.json`
- `cucumber.yaml`
- `cucumber.yml`
- `cucumber.js`
- `cucumber.cjs`
- `cucumber.mjs`

You can also put your file somewhere else and tell Cucumber via the `--config` CLI option:

```shell
$ cucumber-js --config config/cucumber.js
```

Here's a concise example of a configuration file in CommonJS format:

```js
module.exports = {
default: {
parallel: 2,
format: ['html:cucumber-report.html']
}
}
```

And the same in ESM format:

```js
export default {
parallel: 2,
format: ['html:cucumber-report.html']
}
$ cucumber-js --config config/cucumber.json
```

And the same in JSON format:
Here's a concise example of a configuration file in JSON format:

```json
{
Expand All @@ -61,6 +41,26 @@ default:
- "html:cucumber-report.html"
```

And the same in JavaScript (ESM) format:

```js
export default {
parallel: 2,
format: ['html:cucumber-report.html']
}
```

And the same in JavaScript (CommonJS) format:

```js
module.exports = {
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
Expand All @@ -87,7 +87,7 @@ These options can be used in a configuration file (see [above](#files)) or on th
| `failFast` | `boolean` | No | `--fail-fast` | Stop running tests when a test fails - see [Fail Fast](./fail_fast.md) | false |
| `format` | `string[]` | Yes | `--format`, `-f` | Name/path and (optionally) output file path of each formatter to use - see [Formatters](./formatters.md) | [] |
| `formatOptions` | `object` | Yes | `--format-options` | Options to be provided to formatters - see [Formatters](./formatters.md) | {} |
| `import` | `string[]` | Yes | `--import`, `-i` | Paths to where your support code is, for ESM - see [ESM](./esm.md) | [] |
| `import` | `string[]` | Yes | `--import`, `-i` | Paths to where your support code is | [] |
| `language` | `string` | No | `--language` | Default language for your feature files | en |
| `name` | `string` | No | `--name` | Regular expressions of which scenario names should match one of to be run - see [Filtering](./filtering.md#names) | [] |
| `order` | `string` | No | `--order` | Run in the order defined, or in a random order | defined |
Expand Down Expand Up @@ -123,15 +123,15 @@ For more granular options to control _which scenarios_ from your features should
By default, Cucumber finds support code files with this logic:

* If the features live in a `features` directory (at any level)
* `features/**/*.(js)`
* `features/**/*.@(js|cjs|mjs)`
* Otherwise
* `<DIR>/**/*.(js)` for each directory containing the selected features
* `<DIR>/**/*.@(js|cjs|mjs)` for each directory containing the selected features

If your files are somewhere else, you can override this by proving your own [glob](https://github.com/isaacs/node-glob), directory or file path to the `require` configuration option:
If your files are somewhere else, you can override this by proving your own [glob](https://github.com/isaacs/node-glob), directory or file path to the `import` configuration option:

- In a configuration file `{ require: ['somewhere-else/support/*.js'] }`
- On the CLI `$ cucumber-js --require somewhere-else/support/*.js`
- In a configuration file `{ import: ['somewhere-else/support/*.js'] }`
- On the CLI `$ cucumber-js --import somewhere-else/support/*.js`

Once you specify any `require` options, the defaults described above are no longer applied. The option is repeatable, so you can provide several values and they'll be combined, meaning you can load files from multiple locations.
Once you specify any `import` options, the defaults described above are no longer applied. The option is repeatable, so you can provide several values and they'll be combined, meaning you can load files from multiple locations.

The default behaviour and the `require` option both use the [legacy CommonJS modules API](https://nodejs.org/api/modules.html) to load your files. If your files are native ES modules, you'll need to use the `import` option instead in the same way, and they'll be loaded with the [new ES modules API](https://nodejs.org/api/esm.html). See [ES Modules](./esm.md) for more on using Cucumber in an ESM project.
The default behaviour and the `import` option both use the [new ES modules API](https://nodejs.org/api/esm.html) to load your files. This should work fine for the majority of cases, but sometimes (e.g. when transpiling with the `require-module` option), you'll need to use the `require` option instead in the same way, and they'll be loaded with the [legacy CommonJS modules API](https://nodejs.org/api/modules.html).
5 changes: 2 additions & 3 deletions src/api/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,8 @@ async function deriveSupportPaths(
unexpandedImportPaths.length === 0
) {
const defaultPaths = getFeatureDirectoryPaths(cwd, featurePaths)
const requirePaths = await expandPaths(cwd, defaultPaths, '.js')
const importPaths = await expandPaths(cwd, defaultPaths, '.mjs')
return { requirePaths, importPaths }
const importPaths = await expandPaths(cwd, defaultPaths, '.@(js|cjs|mjs)')
return { requirePaths: [], importPaths }
}
const requirePaths =
unexpandedRequirePaths.length > 0
Expand Down
26 changes: 13 additions & 13 deletions src/api/paths_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ describe('resolvePaths', () => {
// Assert
expect(featurePaths).to.eql([featurePath])
expect(unexpandedFeaturePaths).to.eql([relativeFeaturePath])
expect(requirePaths).to.eql([jsSupportCodePath])
expect(importPaths).to.eql([esmSupportCodePath])
expect(requirePaths).to.eql([])
expect(importPaths).to.eql([jsSupportCodePath, esmSupportCodePath])
})

it('deduplicates features based on overlapping expressions', async function () {
Expand Down Expand Up @@ -113,7 +113,7 @@ describe('resolvePaths', () => {
await fsExtra.outputFile(supportCodePath, '')

// Act
const { featurePaths, unexpandedFeaturePaths, requirePaths } =
const { featurePaths, unexpandedFeaturePaths, importPaths } =
await resolvePaths(
new FakeLogger(),
cwd,
Expand All @@ -130,7 +130,7 @@ describe('resolvePaths', () => {
// Assert
expect(featurePaths).to.eql([featurePath])
expect(unexpandedFeaturePaths).to.eql([relativeFeaturePath])
expect(requirePaths).to.eql([supportCodePath])
expect(importPaths).to.eql([supportCodePath])
})
})

Expand All @@ -145,7 +145,7 @@ describe('resolvePaths', () => {
await fsExtra.outputFile(supportCodePath, '')

// Act
const { featurePaths, unexpandedFeaturePaths, requirePaths } =
const { featurePaths, unexpandedFeaturePaths, importPaths } =
await resolvePaths(
new FakeLogger(),
cwd,
Expand All @@ -162,7 +162,7 @@ describe('resolvePaths', () => {
// Assert
expect(featurePaths).to.eql([featurePath])
expect(unexpandedFeaturePaths).to.eql([relativeFeaturePath])
expect(requirePaths).to.eql([supportCodePath])
expect(importPaths).to.eql([supportCodePath])
})

it('returns the appropriate .md and support code paths', async function () {
Expand All @@ -179,7 +179,7 @@ describe('resolvePaths', () => {
await fsExtra.outputFile(supportCodePath, '')

// Act
const { featurePaths, unexpandedFeaturePaths, requirePaths } =
const { featurePaths, unexpandedFeaturePaths, importPaths } =
await resolvePaths(
new FakeLogger(),
cwd,
Expand All @@ -196,7 +196,7 @@ describe('resolvePaths', () => {
// Assert
expect(featurePaths).to.eql([featurePath])
expect(unexpandedFeaturePaths).to.eql([relativeFeaturePath])
expect(requirePaths).to.eql([supportCodePath])
expect(importPaths).to.eql([supportCodePath])
})
})

Expand Down Expand Up @@ -297,8 +297,8 @@ describe('resolvePaths', () => {
const relativeFeaturePath = path.join('features', 'a.feature')
const featurePath = path.join(cwd, relativeFeaturePath)
await fsExtra.outputFile(featurePath, '')
const jsSupportCodePath = path.join(cwd, 'features', 'a.js')
await fsExtra.outputFile(jsSupportCodePath, '')
const cjsSupportCodePath = path.join(cwd, 'features', 'a.cjs')
await fsExtra.outputFile(cjsSupportCodePath, '')
const esmSupportCodePath = path.join(cwd, 'features', 'a.mjs')
await fsExtra.outputFile(esmSupportCodePath, '')

Expand All @@ -311,8 +311,8 @@ describe('resolvePaths', () => {
},
{
requireModules: [],
requirePaths: [],
importPaths: [],
requirePaths: [cjsSupportCodePath],
importPaths: [esmSupportCodePath],
}
)

Expand All @@ -323,7 +323,7 @@ describe('resolvePaths', () => {
)
expect(logger.debug).to.have.been.calledWith(
'Found support files to load via `require` based on configuration:',
[jsSupportCodePath]
[cjsSupportCodePath]
)
expect(logger.debug).to.have.been.calledWith(
'Found support files to load via `import` based on configuration:',
Expand Down
5 changes: 5 additions & 0 deletions src/configuration/validate_configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@ export function validateConfiguration(
'`publishQuiet` option is no longer needed, you can remove it from your configuration; see https://github.com/cucumber/cucumber-js/blob/main/docs/deprecations.md'
)
}
if (configuration.requireModule.length && !configuration.require.length) {
logger.warn(
'Use of `require-module` option normally means you should specify your support code paths with `require`; see https://github.com/cucumber/cucumber-js/blob/main/docs/configuration.md#finding-your-code'
)
}
if (configuration.retryTagFilter && !configuration.retry) {
throw new Error(
'a positive `retry` count must be specified when setting `retryTagFilter`'
Expand Down

0 comments on commit 5224de2

Please sign in to comment.