diff --git a/CHANGELOG.md b/CHANGELOG.md index b6806047e..88c30ba93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ Please see [CONTRIBUTING.md](https://github.com/cucumber/cucumber/blob/master/CO ### Added +* Experimental support for native ES modules via the [`--esm` flag](./docs/cli.md#es-modules-experimental-nodejs-12) ([#1589](https://github.com/cucumber/cucumber-js/pull/1589)) + ### Changed ### Deprecated diff --git a/dependency-lint.yml b/dependency-lint.yml index d85654c33..a4c469fc7 100644 --- a/dependency-lint.yml +++ b/dependency-lint.yml @@ -43,6 +43,7 @@ requiredModules: - 'dist/**/*' - 'lib/**/*' - 'node_modules/**/*' + - 'src/importers.js' - 'tmp/**/*' root: '**/*.{js,ts}' stripLoaders: false diff --git a/docs/cli.md b/docs/cli.md index 9b8a1acd1..2f2d86258 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -81,6 +81,22 @@ You can pass in format options with `--format-options `. The JSON string m * Suggested use: add with profiles so you can define an object and use `JSON.stringify` instead of writing `JSON` manually. +## ES Modules (experimental) (Node.js 12+) + +You can optionally write your support code (steps, hooks, etc) with native ES modules syntax - i.e. using `import` and `export` statements without transpiling. + +To enable this, run with the `--esm` flag. + +This will also expand the default glob for support files to include the `.mjs` file extension. + +As well as support code, these things can also be in ES modules syntax: + +- Custom formatters +- Custom snippets +- Your `cucumber.js` config file + +You can use ES modules selectively/incrementally - the module loading strategy that the `--esm` flag activates supports both ES modules and CommonJS. + ## Colors Colors can be disabled with `--format-options '{"colorsEnabled": false}'` diff --git a/features/esm.feature b/features/esm.feature new file mode 100644 index 000000000..96397709c --- /dev/null +++ b/features/esm.feature @@ -0,0 +1,80 @@ +Feature: ES modules support + + cucumber-js works with native ES modules, via a Cli flag `--esm` + + @esm + Scenario Outline: native module syntax works when using --esm + Given a file named "features/a.feature" with: + """ + Feature: + Scenario: one + Given a step passes + + Scenario: two + Given a step passes + """ + And a file named "features/step_definitions/cucumber_steps.js" with: + """ + import {Given} from '@cucumber/cucumber' + + Given(/^a step passes$/, function() {}); + """ + And a file named "cucumber.js" with: + """ + export default { + 'default': '--format message:messages.ndjson', + } + """ + And a file named "custom-formatter.js" with: + """ + import {SummaryFormatter} from '@cucumber/cucumber' + + export default class CustomFormatter extends SummaryFormatter {} + """ + And a file named "custom-snippet-syntax.js" with: + """ + export default class CustomSnippetSyntax { + build(opts) { + return 'hello world' + } + } + """ + When I run cucumber-js with ` --format ./custom-formatter.js --format-options '{"snippetSyntax": "./custom-snippet-syntax.js"}'` + Then it passes + Examples: + | options | + | --esm | + | --esm --parallel 2 | + + @esm + Scenario: .mjs support code files are matched by default when using --esm + Given a file named "features/a.feature" with: + """ + Feature: + Scenario: + Given a step passes + """ + And a file named "features/step_definitions/cucumber_steps.mjs" with: + """ + import {Given} from '@cucumber/cucumber' + + Given(/^a step passes$/, function() {}); + """ + When I run cucumber-js with `--esm` + Then it passes + + Scenario: native module syntax doesn't work without --esm + Given a file named "features/a.feature" with: + """ + Feature: + Scenario: + Given a step passes + """ + And a file named "features/step_definitions/cucumber_steps.js" with: + """ + import {Given} from '@cucumber/cucumber' + + Given(/^a step passes$/, function() {}); + """ + When I run cucumber-js + Then it fails \ No newline at end of file diff --git a/features/support/hooks.ts b/features/support/hooks.ts index 04d5bee06..a5f056d55 100644 --- a/features/support/hooks.ts +++ b/features/support/hooks.ts @@ -13,7 +13,7 @@ Before('@debug', function (this: World) { this.debug = true }) -Before('@spawn', function (this: World) { +Before('@spawn or @esm', function (this: World) { this.spawn = true }) @@ -43,6 +43,18 @@ Before(function ( this.localExecutablePath = path.join(projectPath, 'bin', 'cucumber-js') }) +Before('@esm', function (this: World) { + const [majorVersion] = process.versions.node.split('.') + if (Number(majorVersion) < 12) { + return 'skipped' + } + fsExtra.writeJSONSync(path.join(this.tmpDir, 'package.json'), { + name: 'feature-test-pickle', + type: 'module', + }) + return undefined +}) + Before('@global-install', function (this: World) { const tmpObject = tmp.dirSync({ unsafeCleanup: true }) diff --git a/package.json b/package.json index f8841dc82..2adeeb709 100644 --- a/package.json +++ b/package.json @@ -163,6 +163,10 @@ "lib": "./lib" }, "main": "./lib/index.js", + "exports": { + "import": "./lib/wrapper.mjs", + "require": "./lib/index.js" + }, "types": "./lib/index.d.ts", "engines": { "node": ">=10" @@ -182,7 +186,6 @@ "cli-table3": "^0.6.0", "colors": "^1.4.0", "commander": "^7.0.0", - "create-require": "^1.1.1", "duration": "^0.2.2", "durations": "^3.4.2", "figures": "^3.2.0", @@ -257,7 +260,7 @@ "typescript": "4.2.3" }, "scripts": { - "build-local": "tsc -p tsconfig.node.json", + "build-local": "tsc -p tsconfig.node.json && cp src/importers.js lib/ && cp src/wrapper.mjs lib/", "cck-test": "mocha 'compatibility/**/*_spec.ts'", "feature-test": "node ./bin/cucumber-js", "html-formatter": "node ./bin/cucumber-js --profile htmlFormatter", diff --git a/src/cli/argv_parser.ts b/src/cli/argv_parser.ts index 1a80381fc..94e828009 100644 --- a/src/cli/argv_parser.ts +++ b/src/cli/argv_parser.ts @@ -22,6 +22,7 @@ export interface IParsedArgvFormatOptions { export interface IParsedArgvOptions { backtrace: boolean dryRun: boolean + esm: boolean exit: boolean failFast: boolean format: string[] @@ -112,6 +113,7 @@ const ArgvParser = { 'invoke formatters without executing steps', false ) + .option('--esm', 'import support code via ES module imports', false) .option( '--exit', 'force shutdown of the event loop when the test run has finished: cucumber will call process.exit', diff --git a/src/cli/configuration_builder.ts b/src/cli/configuration_builder.ts index e830b11c5..7c86484af 100644 --- a/src/cli/configuration_builder.ts +++ b/src/cli/configuration_builder.ts @@ -19,6 +19,7 @@ export interface IConfigurationFormat { } export interface IConfiguration { + esm: boolean featureDefaultLanguage: string featurePaths: string[] formats: IConfigurationFormat[] @@ -80,10 +81,11 @@ export default class ConfigurationBuilder { } supportCodePaths = await this.expandPaths( unexpandedSupportCodePaths, - '.js' + this.options.esm ? '.@(js|mjs)' : '.js' ) } return { + esm: this.options.esm, featureDefaultLanguage: this.options.language, featurePaths, formats: this.getFormats(), diff --git a/src/cli/configuration_builder_spec.ts b/src/cli/configuration_builder_spec.ts index 46138c50e..61cda90c1 100644 --- a/src/cli/configuration_builder_spec.ts +++ b/src/cli/configuration_builder_spec.ts @@ -29,6 +29,7 @@ describe('Configuration', () => { // Assert expect(result).to.eql({ + esm: false, featureDefaultLanguage: 'en', featurePaths: [], formatOptions: {}, @@ -65,27 +66,79 @@ describe('Configuration', () => { }) describe('path to a feature', () => { - it('returns the appropriate feature and support code paths', async function () { - // Arrange - const cwd = await buildTestWorkingDirectory() - const relativeFeaturePath = path.join('features', 'a.feature') - const featurePath = path.join(cwd, relativeFeaturePath) - await fsExtra.outputFile(featurePath, '') - const supportCodePath = path.join(cwd, 'features', 'a.js') - await fsExtra.outputFile(supportCodePath, '') - const argv = baseArgv.concat([relativeFeaturePath]) + describe('without esm', () => { + it('returns the appropriate feature and support code paths', async function () { + // Arrange + const cwd = await buildTestWorkingDirectory() + const relativeFeaturePath = path.join('features', 'a.feature') + const featurePath = path.join(cwd, relativeFeaturePath) + await fsExtra.outputFile(featurePath, '') + const supportCodePath = path.join(cwd, 'features', 'a.js') + await fsExtra.outputFile(supportCodePath, '') + const argv = baseArgv.concat([relativeFeaturePath]) + + // Act + const { + featurePaths, + pickleFilterOptions, + supportCodePaths, + } = await ConfigurationBuilder.build({ argv, cwd }) + + // Assert + expect(featurePaths).to.eql([featurePath]) + expect(pickleFilterOptions.featurePaths).to.eql([relativeFeaturePath]) + expect(supportCodePaths).to.eql([supportCodePath]) + }) + }) - // Act - const { - featurePaths, - pickleFilterOptions, - supportCodePaths, - } = await ConfigurationBuilder.build({ argv, cwd }) + describe('with esm and js support files', () => { + it('returns the appropriate feature and support code paths', async function () { + // Arrange + const cwd = await buildTestWorkingDirectory() + const relativeFeaturePath = path.join('features', 'a.feature') + const featurePath = path.join(cwd, relativeFeaturePath) + await fsExtra.outputFile(featurePath, '') + const supportCodePath = path.join(cwd, 'features', 'a.js') + await fsExtra.outputFile(supportCodePath, '') + const argv = baseArgv.concat([relativeFeaturePath, '--esm']) + + // Act + const { + featurePaths, + pickleFilterOptions, + supportCodePaths, + } = await ConfigurationBuilder.build({ argv, cwd }) + + // Assert + expect(featurePaths).to.eql([featurePath]) + expect(pickleFilterOptions.featurePaths).to.eql([relativeFeaturePath]) + expect(supportCodePaths).to.eql([supportCodePath]) + }) + }) - // Assert - expect(featurePaths).to.eql([featurePath]) - expect(pickleFilterOptions.featurePaths).to.eql([relativeFeaturePath]) - expect(supportCodePaths).to.eql([supportCodePath]) + describe('with esm and mjs support files', () => { + it('returns the appropriate feature and support code paths', async function () { + // Arrange + const cwd = await buildTestWorkingDirectory() + const relativeFeaturePath = path.join('features', 'a.feature') + const featurePath = path.join(cwd, relativeFeaturePath) + await fsExtra.outputFile(featurePath, '') + const supportCodePath = path.join(cwd, 'features', 'a.mjs') + await fsExtra.outputFile(supportCodePath, '') + const argv = baseArgv.concat([relativeFeaturePath, '--esm']) + + // Act + const { + featurePaths, + pickleFilterOptions, + supportCodePaths, + } = await ConfigurationBuilder.build({ argv, cwd }) + + // Assert + expect(featurePaths).to.eql([featurePath]) + expect(pickleFilterOptions.featurePaths).to.eql([relativeFeaturePath]) + expect(supportCodePaths).to.eql([supportCodePath]) + }) }) }) diff --git a/src/cli/helpers.ts b/src/cli/helpers.ts index c56414f22..514b22460 100644 --- a/src/cli/helpers.ts +++ b/src/cli/helpers.ts @@ -16,6 +16,9 @@ import TestCaseHookDefinition from '../models/test_case_hook_definition' import TestRunHookDefinition from '../models/test_run_hook_definition' import { builtinParameterTypes } from '../support_code_library_builder' +// eslint-disable-next-line @typescript-eslint/no-var-requires +const importers = require('../importers') + const StepDefinitionPatternType = messages.StepDefinition.StepDefinitionPattern.StepDefinitionPatternType @@ -29,8 +32,11 @@ export async function getExpandedArgv({ cwd, }: IGetExpandedArgvRequest): Promise { const { options } = ArgvParser.parse(argv) + const importer = options.esm ? importers.esm : importers.legacy let fullArgv = argv - const profileArgv = await new ProfileLoader(cwd).getArgv(options.profile) + const profileArgv = await new ProfileLoader(cwd, importer).getArgv( + options.profile + ) if (profileArgv.length > 0) { fullArgv = _.concat(argv.slice(0, 2), profileArgv, argv.slice(2)) } diff --git a/src/cli/index.ts b/src/cli/index.ts index 25020be63..a8e5f3672 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -30,6 +30,8 @@ import { IParsedArgvFormatOptions } from './argv_parser' import HttpStream from '../formatter/http_stream' import { Writable } from 'stream' +// eslint-disable-next-line @typescript-eslint/no-var-requires +const importers = require('../importers') const { incrementing, uuid } = IdGenerator export interface ICliRunResult { @@ -51,10 +53,16 @@ interface IGetSupportCodeLibraryRequest { supportCodePaths: string[] } +export type IUserCodeImporter = ( + path: string, + isFilePath?: boolean +) => Promise + export default class Cli { private readonly argv: string[] private readonly cwd: string private readonly stdout: IFormatterStream + private importer: IUserCodeImporter = importers.legacy constructor({ argv, @@ -125,6 +133,7 @@ export default class Cli { eventDataCollector, log: stream.write.bind(stream), parsedArgvOptions: formatOptions, + importer: this.importer, stream, cleanup: stream === this.stdout @@ -142,7 +151,7 @@ export default class Cli { ) type = 'progress' } - return FormatterBuilder.build(type, typeOptions) + return await FormatterBuilder.build(type, typeOptions) } ) return async function () { @@ -152,14 +161,18 @@ export default class Cli { } } - getSupportCodeLibrary({ + async getSupportCodeLibrary({ newId, supportCodeRequiredModules, supportCodePaths, - }: IGetSupportCodeLibraryRequest): ISupportCodeLibrary { - supportCodeRequiredModules.map((module) => require(module)) + }: IGetSupportCodeLibraryRequest): Promise { + for (const requiredModule of supportCodeRequiredModules) { + await this.importer(requiredModule) + } supportCodeLibraryBuilder.reset(this.cwd, newId) - supportCodePaths.forEach((codePath) => require(codePath)) + for (const codePath of supportCodePaths) { + await this.importer(codePath, true) + } return supportCodeLibraryBuilder.finalize() } @@ -178,7 +191,10 @@ export default class Cli { configuration.predictableIds && configuration.parallel <= 1 ? incrementing() : uuid() - const supportCodeLibrary = this.getSupportCodeLibrary({ + if (configuration.esm) { + this.importer = importers.esm + } + const supportCodeLibrary = await this.getSupportCodeLibrary({ newId, supportCodePaths: configuration.supportCodePaths, supportCodeRequiredModules: configuration.supportCodeRequiredModules, diff --git a/src/cli/profile_loader.ts b/src/cli/profile_loader.ts index 0dd9b6344..893fe32b1 100644 --- a/src/cli/profile_loader.ts +++ b/src/cli/profile_loader.ts @@ -3,13 +3,13 @@ import fs from 'mz/fs' import path from 'path' import stringArgv from 'string-argv' import { doesHaveValue, doesNotHaveValue } from '../value_checker' +import { IUserCodeImporter } from './index' export default class ProfileLoader { - private readonly directory: string - - constructor(directory: string) { - this.directory = directory - } + constructor( + private readonly directory: string, + private readonly importer: IUserCodeImporter + ) {} async getDefinitions(): Promise> { const definitionsFilePath = path.join(this.directory, 'cucumber.js') @@ -17,7 +17,7 @@ export default class ProfileLoader { if (!exists) { return {} } - const definitions = require(definitionsFilePath) // eslint-disable-line @typescript-eslint/no-var-requires + const definitions = await this.importer(definitionsFilePath, true) if (typeof definitions !== 'object') { throw new Error(`${definitionsFilePath} does not export an object`) } diff --git a/src/cli/profile_loader_spec.ts b/src/cli/profile_loader_spec.ts index a57d52ccd..d8813fcc6 100644 --- a/src/cli/profile_loader_spec.ts +++ b/src/cli/profile_loader_spec.ts @@ -7,6 +7,9 @@ import tmp, { DirOptions } from 'tmp' import { promisify } from 'util' import { doesHaveValue, valueOrDefault } from '../value_checker' +// eslint-disable-next-line @typescript-eslint/no-var-requires +const importers = require('../importers') + interface TestProfileLoaderOptions { definitionsFileContent?: string profiles?: string[] @@ -24,7 +27,7 @@ async function testProfileLoader( opts.definitionsFileContent ) } - const profileLoader = new ProfileLoader(cwd) + const profileLoader = new ProfileLoader(cwd, importers.legacy) return await profileLoader.getArgv(valueOrDefault(opts.profiles, [])) } diff --git a/src/formatter/builder.ts b/src/formatter/builder.ts index 27da244eb..4f6d2b10d 100644 --- a/src/formatter/builder.ts +++ b/src/formatter/builder.ts @@ -20,10 +20,11 @@ import { Writable as WritableStream } from 'stream' import { IParsedArgvFormatOptions } from '../cli/argv_parser' import { SnippetInterface } from './step_definition_snippet_builder/snippet_syntax' import HtmlFormatter from './html_formatter' -import createRequire from 'create-require' +import { IUserCodeImporter } from '../cli' interface IGetStepDefinitionSnippetBuilderOptions { cwd: string + importer: IUserCodeImporter snippetInterface?: SnippetInterface snippetSyntax?: string supportCodeLibrary: ISupportCodeLibrary @@ -35,24 +36,29 @@ export interface IBuildOptions { eventDataCollector: EventDataCollector log: IFormatterLogFn parsedArgvOptions: IParsedArgvFormatOptions + importer: IUserCodeImporter stream: WritableStream cleanup: IFormatterCleanupFn supportCodeLibrary: ISupportCodeLibrary } const FormatterBuilder = { - build(type: string, options: IBuildOptions): Formatter { - const FormatterConstructor = FormatterBuilder.getConstructorByType( + async build(type: string, options: IBuildOptions): Promise { + const FormatterConstructor = await FormatterBuilder.getConstructorByType( type, - options.cwd + options.cwd, + options.importer ) const colorFns = getColorFns(options.parsedArgvOptions.colorsEnabled) - const snippetBuilder = FormatterBuilder.getStepDefinitionSnippetBuilder({ - cwd: options.cwd, - snippetInterface: options.parsedArgvOptions.snippetInterface, - snippetSyntax: options.parsedArgvOptions.snippetSyntax, - supportCodeLibrary: options.supportCodeLibrary, - }) + const snippetBuilder = await FormatterBuilder.getStepDefinitionSnippetBuilder( + { + cwd: options.cwd, + importer: options.importer, + snippetInterface: options.parsedArgvOptions.snippetInterface, + snippetSyntax: options.parsedArgvOptions.snippetSyntax, + supportCodeLibrary: options.supportCodeLibrary, + } + ) return new FormatterConstructor({ colorFns, snippetBuilder, @@ -60,7 +66,11 @@ const FormatterBuilder = { }) }, - getConstructorByType(type: string, cwd: string): typeof Formatter { + async getConstructorByType( + type: string, + cwd: string, + importer: IUserCodeImporter + ): Promise { switch (type) { case 'json': return JsonFormatter @@ -83,12 +93,13 @@ const FormatterBuilder = { case 'usage-json': return UsageJsonFormatter default: - return FormatterBuilder.loadCustomFormatter(type, cwd) + return await FormatterBuilder.loadCustomFormatter(type, cwd, importer) } }, - getStepDefinitionSnippetBuilder({ + async getStepDefinitionSnippetBuilder({ cwd, + importer, snippetInterface, snippetSyntax, supportCodeLibrary, @@ -99,7 +110,8 @@ const FormatterBuilder = { let Syntax = JavascriptSnippetSyntax if (doesHaveValue(snippetSyntax)) { const fullSyntaxPath = path.resolve(cwd, snippetSyntax) - Syntax = require(fullSyntaxPath) // eslint-disable-line @typescript-eslint/no-var-requires + Syntax = await importer(fullSyntaxPath, true) + Syntax = FormatterBuilder.resolveConstructor(Syntax) } return new StepDefinitionSnippetBuilder({ snippetSyntax: new Syntax(snippetInterface), @@ -107,20 +119,34 @@ const FormatterBuilder = { }) }, - loadCustomFormatter(customFormatterPath: string, cwd: string) { - const CustomFormatter = createRequire(cwd)(customFormatterPath) - - if (typeof CustomFormatter === 'function') { + async loadCustomFormatter( + customFormatterPath: string, + cwd: string, + importer: IUserCodeImporter + ) { + let CustomFormatter = customFormatterPath.startsWith(`.`) + ? await importer(path.resolve(cwd, customFormatterPath), true) + : await importer(customFormatterPath) + CustomFormatter = FormatterBuilder.resolveConstructor(CustomFormatter) + if (doesHaveValue(CustomFormatter)) { return CustomFormatter + } else { + throw new Error( + `Custom formatter (${customFormatterPath}) does not export a function` + ) + } + }, + + resolveConstructor(ImportedCode: any) { + if (typeof ImportedCode === 'function') { + return ImportedCode } else if ( - doesHaveValue(CustomFormatter) && - typeof CustomFormatter.default === 'function' + doesHaveValue(ImportedCode) && + typeof ImportedCode.default === 'function' ) { - return CustomFormatter.default + return ImportedCode.default } - throw new Error( - `Custom formatter (${customFormatterPath}) does not export a function` - ) + return null }, } diff --git a/src/formatter/helpers/issue_helpers_spec.ts b/src/formatter/helpers/issue_helpers_spec.ts index ddae29f74..5afa258a6 100644 --- a/src/formatter/helpers/issue_helpers_spec.ts +++ b/src/formatter/helpers/issue_helpers_spec.ts @@ -23,8 +23,9 @@ async function testFormatIssue(sourceData: string): Promise { cwd: 'project/', colorFns: getColorFns(false), number: 1, - snippetBuilder: FormatterBuilder.getStepDefinitionSnippetBuilder({ + snippetBuilder: await FormatterBuilder.getStepDefinitionSnippetBuilder({ cwd: 'project/', + importer: async (path) => await import(path), supportCodeLibrary, }), supportCodeLibrary, diff --git a/src/formatter/progress_bar_formatter_spec.ts b/src/formatter/progress_bar_formatter_spec.ts index 9ff6712f4..deff57861 100644 --- a/src/formatter/progress_bar_formatter_spec.ts +++ b/src/formatter/progress_bar_formatter_spec.ts @@ -55,16 +55,17 @@ async function testProgressBarFormatter({ output += data } const passThrough = new PassThrough() - const progressBarFormatter = FormatterBuilder.build('progress-bar', { + const progressBarFormatter = (await FormatterBuilder.build('progress-bar', { cwd: '', eventBroadcaster, eventDataCollector: new EventDataCollector(eventBroadcaster), log: logFn, parsedArgvOptions: {}, + importer: async (path) => await import(path), stream: passThrough, cleanup: bluebird.promisify(passThrough.end.bind(passThrough)), supportCodeLibrary, - }) as ProgressBarFormatter + })) as ProgressBarFormatter let mocked = false for (const envelope of envelopes) { eventBroadcaster.emit('envelope', envelope) diff --git a/src/importers.js b/src/importers.js new file mode 100644 index 000000000..0a125fdec --- /dev/null +++ b/src/importers.js @@ -0,0 +1,17 @@ +/* +Provides the async `import()` function to source code that needs it, +without having it transpiled down to commonjs `require()` by TypeScript. +When we drop Node 10 support, we'll stop transpiling to commonjs and remove this. + */ + +const { pathToFileURL } = require('url') + +module.exports = { + legacy: async (descriptor) => await Promise.resolve(require(descriptor)), + esm: async (descriptor, isFilePath) => { + if (isFilePath) { + descriptor = pathToFileURL(descriptor).toString() + } + return await import(descriptor) + }, +} diff --git a/src/wrapper.mjs b/src/wrapper.mjs new file mode 100644 index 000000000..360d5eee7 --- /dev/null +++ b/src/wrapper.mjs @@ -0,0 +1,38 @@ +import cucumber from './index.js' + +export const Cli = cucumber.Cli +export const parseGherkinMessageStream = cucumber.parseGherkinMessageStream +export const PickleFilter = cucumber.PickleFilter +export const Runtime = cucumber.Runtime +export const supportCodeLibraryBuilder = cucumber.supportCodeLibraryBuilder +export const Status = cucumber.Status +export const DataTable = cucumber.DataTable + +export const Formatter = cucumber.Formatter +export const FormatterBuilder = cucumber.FormatterBuilder +export const JsonFormatter = cucumber.JsonFormatter +export const ProgressFormatter = cucumber.ProgressFormatter +export const RerunFormatter = cucumber.RerunFormatter +export const SnippetsFormatter = cucumber.SnippetsFormatter +export const SummaryFormatter = cucumber.SummaryFormatter +export const UsageFormatter = cucumber.UsageFormatter +export const UsageJsonFormatter = cucumber.UsageJsonFormatter +export const formatterHelpers = cucumber.formatterHelpers + +export const After = cucumber.After +export const AfterAll = cucumber.AfterAll +export const AfterStep = cucumber.AfterStep +export const Before = cucumber.Before +export const BeforeAll = cucumber.BeforeAll +export const BeforeStep = cucumber.BeforeStep +export const defineParameterType = cucumber.defineParameterType +export const defineStep = cucumber.defineStep +export const Given = cucumber.Given +export const setDefaultTimeout = cucumber.setDefaultTimeout +export const setDefinitionFunctionWrapper = cucumber.setDefinitionFunctionWrapper +export const setWorldConstructor = cucumber.setWorldConstructor +export const Then = cucumber.Then +export const When = cucumber.When + +export const World = cucumber.World + diff --git a/test/formatter_helpers.ts b/test/formatter_helpers.ts index b12246b6b..2b791daf0 100644 --- a/test/formatter_helpers.ts +++ b/test/formatter_helpers.ts @@ -59,12 +59,13 @@ export async function testFormatter({ output += data } const passThrough = new PassThrough() - FormatterBuilder.build(type, { + await FormatterBuilder.build(type, { cwd: '', eventBroadcaster, eventDataCollector, log: logFn, parsedArgvOptions, + importer: async (path) => await import(path), stream: passThrough, cleanup: bluebird.promisify(passThrough.end.bind(passThrough)), supportCodeLibrary, diff --git a/tsconfig.json b/tsconfig.json index fdb94c5fc..c3204a3cf 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "esModuleInterop": true, - "lib": ["es2017"], + "lib": ["es2018"], "module": "commonjs", "noImplicitAny": true, "noImplicitReturns": true, @@ -9,7 +9,7 @@ "resolveJsonModule": true, "sourceMap": true, "inlineSources": true, - "target": "es2017", + "target": "es2018", "typeRoots": [ "./node_modules/@types", "./src/types" diff --git a/yarn.lock b/yarn.lock index ce9e2cb80..a17b699e7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1359,7 +1359,7 @@ core-util-is@1.0.2: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= -create-require@^1.1.0, create-require@^1.1.1: +create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==