cucumber-expressions
Version:
Cucumber Expressions - a simpler alternative to Regular Expressions
128 lines (109 loc) • 4.41 kB
text/typescript
import ParameterTypeRegistry from './ParameterTypeRegistry'
import ParameterTypeMatcher from './ParameterTypeMatcher'
import ParameterType from './ParameterType'
import util from 'util'
import CombinatorialGeneratedExpressionFactory from './CombinatorialGeneratedExpressionFactory'
import GeneratedExpression from './GeneratedExpression'
export default class CucumberExpressionGenerator {
constructor(private readonly parameterTypeRegistry: ParameterTypeRegistry) {}
public generateExpressions(text: string): GeneratedExpression[] {
const parameterTypeCombinations: Array<Array<ParameterType<any>>> = []
const parameterTypeMatchers = this.createParameterTypeMatchers(text)
let expressionTemplate = ''
let pos = 0
// eslint-disable-next-line no-constant-condition
while (true) {
let matchingParameterTypeMatchers = []
for (const parameterTypeMatcher of parameterTypeMatchers) {
const advancedParameterTypeMatcher = parameterTypeMatcher.advanceTo(pos)
if (advancedParameterTypeMatcher.find) {
matchingParameterTypeMatchers.push(advancedParameterTypeMatcher)
}
}
if (matchingParameterTypeMatchers.length > 0) {
matchingParameterTypeMatchers = matchingParameterTypeMatchers.sort(
ParameterTypeMatcher.compare
)
// Find all the best parameter type matchers, they are all candidates.
const bestParameterTypeMatcher = matchingParameterTypeMatchers[0]
const bestParameterTypeMatchers = matchingParameterTypeMatchers.filter(
m => ParameterTypeMatcher.compare(m, bestParameterTypeMatcher) === 0
)
// Build a list of parameter types without duplicates. The reason there
// might be duplicates is that some parameter types have more than one regexp,
// which means multiple ParameterTypeMatcher objects will have a reference to the
// same ParameterType.
// We're sorting the list so preferential parameter types are listed first.
// Users are most likely to want these, so they should be listed at the top.
let parameterTypes = []
for (const parameterTypeMatcher of bestParameterTypeMatchers) {
if (
parameterTypes.indexOf(parameterTypeMatcher.parameterType) === -1
) {
parameterTypes.push(parameterTypeMatcher.parameterType)
}
}
parameterTypes = parameterTypes.sort(ParameterType.compare)
parameterTypeCombinations.push(parameterTypes)
expressionTemplate += escape(
text.slice(pos, bestParameterTypeMatcher.start)
)
expressionTemplate += '{%s}'
pos =
bestParameterTypeMatcher.start + bestParameterTypeMatcher.group.length
} else {
break
}
if (pos >= text.length) {
break
}
}
expressionTemplate += escape(text.slice(pos))
return new CombinatorialGeneratedExpressionFactory(
expressionTemplate,
parameterTypeCombinations
).generateExpressions()
}
/**
* @deprecated
*/
public generateExpression(text: string): GeneratedExpression {
return util.deprecate(
() => this.generateExpressions(text)[0],
'CucumberExpressionGenerator.generateExpression: Use CucumberExpressionGenerator.generateExpressions instead'
)()
}
private createParameterTypeMatchers(text: string): ParameterTypeMatcher[] {
let parameterMatchers: ParameterTypeMatcher[] = []
for (const parameterType of this.parameterTypeRegistry.parameterTypes) {
if (parameterType.useForSnippets) {
parameterMatchers = parameterMatchers.concat(
CucumberExpressionGenerator.createParameterTypeMatchers2(
parameterType,
text
)
)
}
}
return parameterMatchers
}
private static createParameterTypeMatchers2(
parameterType: ParameterType<any>,
text: string
): ParameterTypeMatcher[] {
// TODO: [].map
const result = []
for (const regexp of parameterType.regexpStrings) {
result.push(new ParameterTypeMatcher(parameterType, regexp, text))
}
return result
}
}
function escape(s: string): string {
return s
.replace(/%/g, '%%') // for util.format
.replace(/\(/g, '\\(')
.replace(/{/g, '\\{')
.replace(/\//g, '\\/')
}
module.exports = CucumberExpressionGenerator