diff --git a/src/code-generation/step-generation.ts b/src/code-generation/step-generation.ts index a71d0b0..4851ca3 100644 --- a/src/code-generation/step-generation.ts +++ b/src/code-generation/step-generation.ts @@ -1,8 +1,14 @@ +import { globalSteps } from '../global-steps'; import { ParsedStep } from '../models'; import { indent } from './utils'; +import { getJestCucumberConfiguration } from '../configuration'; const stepTemplate = (stepKeyword: string, stepMatcher: string, stepArgumentVariables: string[]) => { - return `${stepKeyword}(${stepMatcher}, (${stepArgumentVariables.join(', ')}) => {\n\n});`; + let template = `${stepKeyword}(${stepMatcher}`; + if (!globalSteps.get(stepMatcher)) { + template = `${template}, (${stepArgumentVariables.join(', ')}) => {\n\n}`; + } + return `${template});`; }; const getStepFunctionWrapperName = (stepKeyword: string, stepText: string) => { @@ -11,10 +17,10 @@ const getStepFunctionWrapperName = (stepKeyword: string, stepText: string) => { }; const stepWrapperFunctionTemplate = ( - stepKeyword: string, - stepText: string, - stepMatcher: string, - stepArgumentVariables: string[], + stepKeyword: string, + stepText: string, + stepMatcher: string, + stepArgumentVariables: string[], ) => { // tslint:disable-next-line:max-line-length return `export const ${getStepFunctionWrapperName(stepKeyword, stepText)} = (${stepKeyword}) => {\n${indent(stepTemplate(stepKeyword, stepMatcher, stepArgumentVariables), 1).slice(0, -1)}\n}`; @@ -69,7 +75,7 @@ const getStepArguments = (step: ParsedStep) => { const getStepMatcher = (step: ParsedStep) => { let stepMatcher: string = ''; - if (step.stepText.match(stepTextArgumentRegex)) { + if (step.stepText.match(stepTextArgumentRegex) && !getJestCucumberConfiguration().disableRegexGeneration) { stepMatcher = convertStepTextToRegex(step); } else { stepMatcher = `'${step.stepText.replace(/'+/g, `\\'`)}'`; diff --git a/src/configuration.ts b/src/configuration.ts index bc02499..a2ba55b 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -10,6 +10,7 @@ const defaultErrorSettings = { const defaultConfiguration: Options = { tagFilter: undefined, scenarioNameTemplate: undefined, + disableRegexGeneration: undefined, errors: defaultErrorSettings, }; diff --git a/src/feature-definition-creation.ts b/src/feature-definition-creation.ts index 106c8e4..bfccd6d 100644 --- a/src/feature-definition-creation.ts +++ b/src/feature-definition-creation.ts @@ -11,6 +11,7 @@ import { matchSteps, } from './validation/step-definition-validation'; import { applyTagFilters } from './tag-filtering'; +import { globalSteps } from './global-steps'; export type StepsDefinitionCallbackOptions = { defineStep: DefineStepFunction; @@ -247,12 +248,11 @@ const createDefineScenarioFunctionWithAliases = ( }; const createDefineStepFunction = (scenarioFromStepDefinitions: ScenarioFromStepDefinitions) => { - return (stepMatcher: string | RegExp, stepFunction: () => any) => { + return (stepMatcher: string | RegExp, stepFunction?: () => any) => { const stepDefinition: StepFromStepDefinitions = { stepMatcher, - stepFunction, + stepFunction: stepFunction || globalSteps.get(stepMatcher as string), }; - scenarioFromStepDefinitions.steps.push(stepDefinition); }; }; @@ -270,7 +270,7 @@ export function defineFeature( if ( parsedFeatureWithTagFiltersApplied.scenarios.length === 0 - && parsedFeatureWithTagFiltersApplied.scenarioOutlines.length === 0 + && parsedFeatureWithTagFiltersApplied.scenarioOutlines.length === 0 ) { return; } diff --git a/src/global-steps.ts b/src/global-steps.ts new file mode 100644 index 0000000..1f0c77b --- /dev/null +++ b/src/global-steps.ts @@ -0,0 +1,55 @@ +import { StepFromStepDefinitions } from './models'; + +class Steps { + list: StepFromStepDefinitions[] = []; + + push(step: StepFromStepDefinitions) { + const { stepMatcher } = step; + + const isAlreadyAdded = this.list.some( + step => String(step.stepMatcher) === String(stepMatcher), + ); + + if (isAlreadyAdded) { + throw new Error(`Existing global step with the name ${stepMatcher}`); + } + + this.list.push(step); + } + + get(title: StepFromStepDefinitions["stepMatcher"]) { + let params; + + const found = this.list.find((step) => { + if (typeof title === 'string') { + const matches = title.match(step.stepMatcher); + + if (matches) { + params = matches.slice(1); + return true; + } + } + + if (title instanceof RegExp) { + return String(title) === String(step.stepMatcher); + } + + return false; + }); + + if (!found) { + throw Error(`${title} : was not defined in Steps file`); + } + + return found.stepFunction.bind(this, ...params); + } +} + +export const globalSteps = new Steps(); + +export function defineGlobalStep( + stepMatcher: StepFromStepDefinitions["stepMatcher"], + stepFunction: StepFromStepDefinitions["stepFunction"] +) { + globalSteps.push({ stepMatcher, stepFunction }); +} diff --git a/src/index.ts b/src/index.ts index 7e49d27..2f7e8f3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,3 +5,4 @@ export { generateCodeFromFeature, generateCodeWithSeparateFunctionsFromFeature, } from './code-generation/generate-code-by-line-number'; +export { defineGlobalStep } from './global-steps'; diff --git a/src/models.ts b/src/models.ts index d1c4747..0656edc 100644 --- a/src/models.ts +++ b/src/models.ts @@ -61,6 +61,7 @@ export type ErrorOptions = { export type Options = { loadRelativePath?: boolean; + disableRegexGeneration?: boolean; tagFilter?: string; errors?: ErrorOptions | boolean; scenarioNameTemplate?: (vars: ScenarioNameTemplateVars) => string;