@@ -91,80 +91,129 @@ function matchJestTestSuiteWithCucumberFeature( featureScenariosOrOutline, befor
9191function matchJestTestWithCucumberScenario ( currentScenarioTitle , currentScenarioSteps , testFn , isOutline ) {
9292 testFn ( currentScenarioTitle , ( { given, when, then, and, but } ) => {
9393 currentScenarioSteps . forEach ( ( currentStep ) => {
94- // if( !stepsDefinition[ currentStep.keyword ] )
95- // return
9694
9795 matchJestDefinitionWithCucumberStep ( { given, when, then, and, but } , currentStep . keyword , currentStep . stepText , isOutline )
96+
9897 } )
9998 } )
10099}
101100
102- function matchJestDefinitionWithCucumberStep ( { given, when, then, and, but } , currentStepKeyWork , currentStepText , isOutline ) {
103- const foundMatchingStep = findStep ( currentStepKeyWork , currentStepText , isOutline )
104- if ( ! foundMatchingStep )
105- return
106-
107- switch ( currentStepKeyWork ) {
108- case "given" :
109- given ( foundMatchingStep . stepExpression , foundMatchingStep . stepFn )
110- break
111-
112- case "when" :
113- when ( foundMatchingStep . stepExpression , foundMatchingStep . stepFn )
114- break
101+ function matchJestDefinitionWithCucumberStep ( verbFunction , currentStepKeyword , currentStepText , isOutline ) {
102+
103+ const foundMatchingStep = findMatchingStep ( currentStepKeyword , currentStepText , isOutline )
104+ if ( ! foundMatchingStep ) return
105+
106+ // this will be the "given", "when", "then"...functions
107+ verbFunction [ currentStepKeyword ] ( foundMatchingStep . stepExpression , foundMatchingStep . stepFn )
108+ }
115109
116- case "then" :
117- then ( foundMatchingStep . stepExpression , foundMatchingStep . stepFn )
118- break
119110
120- case "but" :
121- but ( foundMatchingStep . stepExpression , foundMatchingStep . stepFn )
122- break
111+ function findMatchingStep ( scenarioType , scenarioSentence , isOutline ) {
112+ const foundStep = Object . keys ( stepsDefinition [ scenarioType ] )
113+ . find ( ( currentStepDefinitionFunction ) => {
114+ return isFunctionForScenario ( scenarioSentence ,
115+ stepsDefinition [ scenarioType ] [ currentStepDefinitionFunction ] ,
116+ isOutline )
117+ } )
118+ if ( ! foundStep ) return null
119+
120+ return injectVariable ( scenarioType , scenarioSentence , foundStep )
121+ }
123122
124- case "and" :
125- default :
126- and ( foundMatchingStep . stepExpression , foundMatchingStep . stepFn )
127- break
123+ function isFunctionForScenario ( scenarioSentence , stepDefinitionFunction , isOutline ) {
124+ if ( stepDefinitionFunction . stepRegExp ) {
125+ if ( isOutline && / < [ \w ] * > / . test ( scenarioSentence ) ) {
126+ return isPotentialStepFunctionForScenario ( scenarioSentence , stepDefinitionFunction . stepRegExp )
127+ }
128+
129+ else return scenarioSentence . match ( stepDefinitionFunction . stepRegExp )
128130 }
131+
132+ return scenarioSentence === stepDefinitionFunction . stepExpression
129133}
130134
131- function findStep ( scenarioType , scenarioSentence , isOutline ) {
132- // if( !stepsDefinition[ scenarioType ] )
133- // return null
134-
135- const foundStep = Object . keys ( stepsDefinition [ scenarioType ] ) . find ( ( currentSentence ) => {
136- if ( stepsDefinition [ scenarioType ] [ currentSentence ] . stepRegExp ) {
137- if ( isOutline && / < [ \w ] * > / . test ( scenarioSentence ) ) {
138- const cleanedSentence = scenarioSentence . replace ( / < [ \w ] * > / gi, '' )
139- const cleanedRegexp = stepsDefinition [ scenarioType ] [ currentSentence ] . stepRegExp . source
140- . replace ( / ^ \^ / , '' )
141- . replace ( / \\ \( / g, '(' )
142- . replace ( / \\ \) / g, ')' )
143- . replace ( / \\ \^ / g, '^' )
144- . replace ( / \\ \$ / g, '$' )
145- . replace ( / \$ $ / , '' )
146- . replace ( / \( [ . \\ ] + [ s S d D w W b B * ] [ * ? + ] ? \) / g, '' )
147- . replace ( / \( \[ .* \] (?: [ + ? * ] { 1 } | \{ \d \} ) \) / g, '' )
148-
149- // const groupInStepDef = new RegExp( stepsDefinition[ scenarioType ][ currentSentence ].stepRegExp.source + '|' ).exec('')
150- // const numGroupInStepDef = groupInStepDef.length - 1
151- // const groupInSentence = /(<[\w]*>)|/gm.exec( scenarioSentence )
152- // const numGroupInSentence = /(<[\w]*>)|/gm.exec( scenarioSentence ).length - 1
153- //check that we have the same number of capture group than enclosed variables in the expression
154- return cleanedRegexp === cleanedSentence
135+
136+ function isPotentialStepFunctionForScenario ( scenarioDefinition , regStepFunc ) {
137+ //so this one is tricky, to ensure we only find the
138+ // step definition corresponding to actual steps function in the case of outlined gherkin
139+ // we have to "disable" the outlining (since it can replace regular expression
140+ // and then ensure that all "non-outlined" part do respect the regular expression of
141+ // of the step function
142+ // FIRST, we clean the string version of the step definition that has outline variable
143+ const cleanedStepFunc = regStepFunc . source
144+ . replace ( / ^ \^ / , '' )
145+ // .replace( /\\\(/g, '(' )
146+ // .replace( /\\\)/g, ')')
147+ // .replace( /\\\^/g, '^')
148+ // .replace( /\\\$/g, '$')
149+ . replace ( / \$ $ / , '' )
150+ // .replace( /\([.\\]+[sSdDwWbB*][*?+]?\)|\(\[.*\](?:[+?*]{1}|\{\d\})\)/g, '' )
151+
152+ let currentScenarioPart
153+ let currentStepFuncLeft = cleanedStepFunc
154+ let currentScenarioDefLeft = scenarioDefinition
155+
156+ //we step through each of the scenario outline variables
157+ // from there, we will try to detect any regexp present in the
158+ // step definition, so that we can ensure to find the right match
159+ while ( ( currentScenarioPart = / < [ \w ] * > / gi. exec ( currentScenarioDefLeft ) ) != null ) {
160+
161+ let fixedPart = currentScenarioPart . input . substring ( 0 , currentScenarioPart . index )
162+ let idxCutScenarioPart = currentScenarioPart . index + currentScenarioPart [ 0 ] . length
163+
164+ const regEscapedStepFunc = / \( [ . \\ ] + [ s S d D w W b B * ] [ * ? + ] ? \) | \( \[ .* \] (?: [ + ? * ] { 1 } | \{ \d \} ) \) / g. exec ( currentStepFuncLeft . replace ( / \\ \( / g, '(' )
165+ . replace ( / \\ \) / g, ')' )
166+ . replace ( / \\ \^ / g, '^' )
167+ . replace ( / \\ \$ / g, '$' ) )
168+ const regStepFuncLeft = / \( [ . \\ ] + [ s S d D w W b B * ] [ * ? + ] ? \) | \( \[ .* \] (?: [ + ? * ] { 1 } | \{ \d \} ) \) / g. exec ( currentStepFuncLeft )
169+
170+ if ( regStepFuncLeft && regEscapedStepFunc . index == currentScenarioPart . index ) {
171+ //if we have a regex inside our step function definition
172+ // and that regex is at the same position than our Outlined variable
173+ // we just need to check that the sentence match,
174+ // so we can "evaluate" the step function and remove the regex in it
175+ currentStepFuncLeft = regEscapedStepFunc . input . substring ( 0 , regEscapedStepFunc . index )
176+ + currentStepFuncLeft . substring ( regStepFuncLeft . index + regStepFuncLeft [ 0 ] . length )
177+
178+ }
179+ else if ( regStepFuncLeft && regStepFuncLeft . index < currentScenarioPart . index ) {
180+ //if we have a regex inside our step function definition
181+ // but that regex is not at the same position than our outlined variable
182+ // we need to evaluate the regex against the scenario part
183+ const strRegexToEvaluate = regStepFuncLeft . input . substring ( 0 , regStepFuncLeft . index + regStepFuncLeft [ 0 ] . length )
184+ const regexToEvaluate = new RegExp ( strRegexToEvaluate )
185+ const regIntermediatePart = regexToEvaluate . exec ( currentScenarioPart . input )
186+ if ( regIntermediatePart ) {
187+ fixedPart = regStepFuncLeft . input . substring ( 0 , regStepFuncLeft . index + regStepFuncLeft [ 0 ] . length )
188+ idxCutScenarioPart = regIntermediatePart [ 0 ] . length
155189 }
190+ }
191+
192+ const partIndex = currentStepFuncLeft . indexOf ( fixedPart )
193+ if ( partIndex !== - 1 ) {
194+ currentStepFuncLeft = currentStepFuncLeft . substring ( partIndex + fixedPart . length )
195+ currentScenarioDefLeft = currentScenarioDefLeft . substring ( idxCutScenarioPart )
196+ }
197+ else {
198+ return false
199+ }
200+ }
201+
202+ return ( currentScenarioDefLeft === '' && currentStepFuncLeft === '' )
203+ || evaluateStepFuncEndVsScenarioEnd ( currentStepFuncLeft , currentScenarioDefLeft )
204+ }
156205
157- else
158- return scenarioSentence . match ( stepsDefinition [ scenarioType ] [ currentSentence ] . stepRegExp )
206+ function evaluateStepFuncEndVsScenarioEnd ( stepFunctionDef , scenarioDefinition ) {
207+ if ( / \( [ . \\ ] + [ s S d D w W b B * ] [ * ? + ] ? \) | \( \[ .* \] (?: [ + ? * ] { 1 } | \{ \d \} ) \) / g. test ( stepFunctionDef ) ) {
208+ return new RegExp ( stepFunctionDef ) . test ( scenarioDefinition )
209+ }
210+
211+ return stepFunctionDef . endsWith ( scenarioDefinition )
212+ }
159213
160- }
161-
162- return scenarioSentence === stepsDefinition [ scenarioType ] [ currentSentence ] . stepExpression
163- } )
164- if ( ! foundStep )
165- return null
166214
167- const stepObject = stepsDefinition [ scenarioType ] [ foundStep ]
215+ function injectVariable ( scenarioType , scenarioSentence , stepFunctionDefinition ) {
216+ const stepObject = stepsDefinition [ scenarioType ] [ stepFunctionDefinition ]
168217
169218 if ( ! stepObject . stepRegExp )
170219 return {
@@ -186,7 +235,7 @@ function findStep( scenarioType, scenarioSentence, isOutline ) {
186235 const dynamicMatchThatAreVariables = exprMatches //exprMatches.filter( ( currentMatch ) => {
187236 // return foundStep.indexOf( currentMatch ) === -1
188237 // } )
189-
238+
190239 return {
191240 stepExpression : stepObject . stepRegExp ,
192241 stepFn : ( ) => ( stepObject . stepFn ( ...dynamicMatchThatAreVariables ) )
0 commit comments