Factory pattern for creating expression instances and argument handling for parameter extraction and transformation.
Factory class that creates appropriate expression instances (Cucumber or Regular) based on input type.
/**
* Factory for creating Expression instances
* Automatically chooses between CucumberExpression and RegularExpression
*/
class ExpressionFactory {
/**
* Create factory with parameter type registry
* @param parameterTypeRegistry - Registry containing parameter type definitions
*/
constructor(parameterTypeRegistry: ParameterTypeRegistry);
/**
* Create appropriate expression instance based on input type
* @param expression - String creates CucumberExpression, RegExp creates RegularExpression
* @returns Expression instance ready for matching
*/
createExpression(expression: string | RegExp): Expression;
}Usage Examples:
import { ExpressionFactory, ParameterTypeRegistry } from "@cucumber/cucumber-expressions";
const registry = new ParameterTypeRegistry();
const factory = new ExpressionFactory(registry);
// Create Cucumber Expression from string
const cucumberExpr = factory.createExpression("I have {int} cucumbers");
// Returns: CucumberExpression instance
// Create Regular Expression from RegExp
const regexExpr = factory.createExpression(/I have (\d+) cucumbers/);
// Returns: RegularExpression instance
// Use both expressions identically
const text = "I have 42 cucumbers";
const cucumberArgs = cucumberExpr.match(text);
const regexArgs = regexExpr.match(text);
// Both return Argument arrays with same interface
if (cucumberArgs && regexArgs) {
const cucumberValue = cucumberArgs[0].getValue<number>(null); // 42 (Number)
const regexValue = regexArgs[0].getValue<string>(null); // "42" (String)
}Represents a matched parameter with its value and type information, providing access to transformed values.
/**
* Represents a matched parameter argument
* Contains the matched text group and parameter type for transformation
*/
class Argument {
/**
* Build array of arguments from matched groups and parameter types
* @param group - Root group containing all matches
* @param parameterTypes - Array of parameter types for transformation
* @returns Array of Argument instances
*/
static build(
group: Group,
parameterTypes: readonly ParameterType<unknown>[]
): readonly Argument[];
/**
* Create argument with group and parameter type
* @param group - Matched text group
* @param parameterType - Parameter type for transformation
*/
constructor(group: Group, parameterType: ParameterType<unknown>);
/**
* Get transformed value using parameter type's transformation function
* @param thisObj - Context object for transformation (usually null)
* @returns Transformed value of the appropriate type
*/
getValue<T>(thisObj: unknown): T | null;
/**
* Get the parameter type used for this argument
* @returns Parameter type instance
*/
getParameterType(): ParameterType<unknown>;
/** Matched text group containing the raw match data */
readonly group: Group;
/** Parameter type used for transformation */
readonly parameterType: ParameterType<unknown>;
}Usage Examples:
import { CucumberExpression, ParameterTypeRegistry } from "@cucumber/cucumber-expressions";
const registry = new ParameterTypeRegistry();
const expression = new CucumberExpression("User {string} has {int} points", registry);
const args = expression.match('User "Alice" has 100 points');
if (args) {
// First argument: string parameter
const username = args[0].getValue<string>(null);
console.log(`Username: ${username}`); // "Alice"
console.log(`Type: ${args[0].getParameterType().name}`); // "string"
// Second argument: int parameter
const points = args[1].getValue<number>(null);
console.log(`Points: ${points}`); // 100
console.log(`Type: ${args[1].getParameterType().name}`); // "int"
// Access raw matched groups
console.log(`Raw username match: ${args[0].group.value}`); // "Alice"
console.log(`Raw points match: ${args[1].group.value}`); // "100"
}Represents a matched text group with position information and child groups for nested matches.
/**
* Represents a matched text group
* Contains the matched text, position, and child groups for complex patterns
*/
class Group {
/**
* Create a group with match information
* @param value - The matched text content
* @param start - Start position in original text (undefined if not tracked)
* @param end - End position in original text (undefined if not tracked)
* @param children - Array of child groups for nested matches
*/
constructor(
value: string,
start: number | undefined,
end: number | undefined,
children: readonly Group[]
);
/**
* Get values from this group and its children
* @returns Array of string values or null if no matches
*/
get values(): string[] | null;
/** The matched text content */
readonly value: string;
/** Start position in original text */
readonly start: number | undefined;
/** End position in original text */
readonly end: number | undefined;
/** Child groups for nested matches */
readonly children: readonly Group[];
}Usage Examples:
import { CucumberExpression, ParameterTypeRegistry, ParameterType } from "@cucumber/cucumber-expressions";
// Custom parameter type with multiple capture groups
class Coordinate {
constructor(public x: number, public y: number) {}
}
const coordinateType = new ParameterType(
'coordinate',
/(-?\d+),(-?\d+)/,
Coordinate,
(x, y) => new Coordinate(parseInt(x), parseInt(y))
);
const registry = new ParameterTypeRegistry();
registry.defineParameterType(coordinateType);
const expression = new CucumberExpression("Move to {coordinate}", registry);
const args = expression.match("Move to 10,-5");
if (args) {
const arg = args[0];
const coordinate = arg.getValue<Coordinate>(null);
console.log(`Coordinate: (${coordinate.x}, ${coordinate.y})`); // (10, -5)
// Access group details
const group = arg.group;
console.log(`Full match: "${group.value}"`); // "10,-5"
console.log(`Child groups: ${group.children.length}`); // 2
console.log(`Child values:`, group.values); // ["10", "-5"]
}Arguments can use custom context objects for transformation:
class MathContext {
constructor(public multiplier: number) {}
}
const scaledNumberType = new ParameterType(
'scaled',
/\d+/,
Number,
function(this: MathContext, value: string) {
return parseInt(value) * this.multiplier;
}
);
const registry = new ParameterTypeRegistry();
registry.defineParameterType(scaledNumberType);
const expression = new CucumberExpression("Scale {scaled}", registry);
const args = expression.match("Scale 5");
if (args) {
const context = new MathContext(10);
const scaledValue = args[0].getValue<number>(context); // 50
}Arguments handle transformation errors gracefully:
const strictNumberType = new ParameterType(
'strict-number',
/\d+/,
Number,
(value: string) => {
const num = parseInt(value);
if (num < 0) {
throw new Error(`Negative numbers not allowed: ${num}`);
}
return num;
}
);
const registry = new ParameterTypeRegistry();
registry.defineParameterType(strictNumberType);
const expression = new CucumberExpression("Process {strict-number}", registry);
try {
const args = expression.match("Process -5");
if (args) {
const value = args[0].getValue<number>(null); // Throws error
}
} catch (error) {
console.log(error.message); // "Negative numbers not allowed: -5"
}The Argument.build method coordinates between matched groups and parameter types:
import { Group, ParameterType } from "@cucumber/cucumber-expressions";
// Manual argument building (usually done internally)
const mockGroup = new Group("42", 0, 2, []);
const intType = new ParameterType('int', /\d+/, Number, (s) => parseInt(s));
const args = Argument.build(mockGroup, [intType]);
console.log(args[0].getValue<number>(null)); // 42import { ExpressionFactory, ParameterTypeRegistry, ParameterType } from "@cucumber/cucumber-expressions";
// Create registry with custom types
const registry = new ParameterTypeRegistry();
registry.defineParameterType(new ParameterType(
'email',
/[^@]+@[^.]+\..+/,
String,
(email) => email.toLowerCase()
));
// Factory uses custom registry
const factory = new ExpressionFactory(registry);
// Custom types available in both expression types
const cucumberExpr = factory.createExpression("Send to {email}");
const regexExpr = factory.createExpression(/Send to ([^@]+@[^.]+\..+)/);
const text = "Send to USER@EXAMPLE.COM";
const cucumberArgs = cucumberExpr.match(text);
const regexArgs = regexExpr.match(text);
if (cucumberArgs && regexArgs) {
// Cucumber expression uses custom transformation
const cucumberEmail = cucumberArgs[0].getValue<string>(null); // "user@example.com"
// Regular expression uses default string transformation
const regexEmail = regexArgs[0].getValue<string>(null); // "USER@EXAMPLE.COM"
}The library provides strong typing for argument values:
import { CucumberExpression, ParameterTypeRegistry } from "@cucumber/cucumber-expressions";
const registry = new ParameterTypeRegistry();
const expression = new CucumberExpression("Transfer {int} to {string}", registry);
const args = expression.match('Transfer 100 to "savings"');
if (args) {
// TypeScript enforces correct types
const amount: number = args[0].getValue<number>(null); // ✓ Correct
const account: string = args[1].getValue<string>(null); // ✓ Correct
// TypeScript catches type mismatches
// const wrongType: string = args[0].getValue<string>(null); // ✗ Type error
}