Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
addba1b
merge defineFeature and defineRuleBasedFeature
makmu Mar 21, 2021
467f412
fix steps file naming
makmu Mar 22, 2021
642b88e
restore package json
makmu Mar 22, 2021
8750ef1
renaming
makmu Mar 22, 2021
28d32f8
renaming
makmu Mar 22, 2021
6def68b
renaming
makmu Mar 22, 2021
6079b29
renaming
makmu Mar 22, 2021
0ac395e
renaming
makmu Mar 22, 2021
8c61ded
renaming
makmu Mar 22, 2021
62a0479
renaming & linting
makmu Mar 22, 2021
5688655
renaming & linting
makmu Mar 22, 2021
5c8031d
renaming & linting
makmu Mar 22, 2021
5995023
renaming & linting
makmu Mar 22, 2021
0bec5d0
renaming & linting
makmu Mar 22, 2021
274cc63
renaming & linting
makmu Mar 22, 2021
e8ea580
renaming & linting
makmu Mar 22, 2021
cd7ee3d
renaming & linting
makmu Mar 22, 2021
4d56b21
renaming & linting
makmu Mar 22, 2021
5afb53c
renaming & linting
makmu Mar 22, 2021
6b5d87c
renaming & linting
makmu Mar 22, 2021
287cf39
renaming & linting
makmu Mar 22, 2021
a0e0071
renaming & linting
makmu Mar 22, 2021
735eece
renaming & linting
makmu Mar 22, 2021
064c718
renaming & linting
makmu Mar 22, 2021
fad2b08
renaming & linting
makmu Mar 22, 2021
47a8b27
renaming & linting
makmu Mar 22, 2021
667b829
renaming & linting
makmu Mar 22, 2021
3822d74
renaming & linting
makmu Mar 22, 2021
22fff8d
renaming & linting
makmu Mar 22, 2021
1cb7a1c
renaming & linting
makmu Mar 22, 2021
e82180e
renaming & linting
makmu Mar 22, 2021
c43ff66
renaming & linting
makmu Mar 22, 2021
04113c3
renaming & linting
makmu Mar 22, 2021
0ef2e91
renaming & linting
makmu Mar 22, 2021
19ac265
renaming & linting
makmu Mar 22, 2021
c03da2d
renaming & linting
makmu Mar 22, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions examples/typescript/specs/features/tag-filtering.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
@included
Feature: Vending machine

Rule: Dispenses items if correct amount of money is inserted

Scenario: Selecting a snack
Given the vending machine has "Maltesers" in stock
And I have inserted the correct amount of money
When I select "Maltesers"
Then my "Maltesers" should be dispensed

@excluded
Scenario Outline: Selecting a beverage
Given the vending machine has "<beverage>" in stock
And I have inserted the correct amount of money
When I select "<beverage>"
Then my "<beverage>" should be dispensed

Examples:
| beverage |
| Cola |
| Ginger ale |

Rule: Returns my money if item is out of stock

@excluded
Scenario: Selecting a snack
Given the vending machine has no "Maltesers" in stock
And I have inserted the correct amount of money
When I select "Maltesers"
Then my money should be returned

Scenario: Selecting a beverage
Given the vending machine has no "Cola" in stock
And I have inserted the correct amount of money
When I select "Cola"
Then my money should be returned
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { StepDefinitions, loadFeature, autoBindStepsWithRules } from '../../../../src';
import { StepDefinitions, loadFeature, autoBindSteps} from '../../../../src';
import { VendingMachine } from '../../src/vending-machine';

export const vendingMachineSteps: StepDefinitions = ({ given, and, when, then }) => {
Expand Down Expand Up @@ -35,6 +35,9 @@ export const vendingMachineSteps: StepDefinitions = ({ given, and, when, then })
});
};

const feature = loadFeature('./examples/typescript/specs/features/extended-rules-auto-step-binding.feature', {collapseRules: false});
const feature = loadFeature(
'./examples/typescript/specs/features/extended-rules-auto-step-binding.feature', {
collapseRules: false,
});

autoBindStepsWithRules([feature], [ vendingMachineSteps ]);
autoBindSteps([feature], [ vendingMachineSteps ]);
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { loadFeature, defineRuleBasedFeature } from '../../../../src';
import { loadFeature, defineFeature} from '../../../../src';
import { DefineStepFunction } from '../../../../src/feature-definition-creation';
import { VendingMachine } from '../../src/vending-machine';

const feature = loadFeature('./examples/typescript/specs/features/extended-rules-definition.feature', {collapseRules: false});
const feature = loadFeature(
'./examples/typescript/specs/features/extended-rules-definition.feature', {
collapseRules: false,
});

defineRuleBasedFeature(feature, (rule) => {
defineFeature(feature, ({rule}) => {
let vendingMachine: VendingMachine;

const myMoney = 0.50;
Expand All @@ -21,7 +24,7 @@ defineRuleBasedFeature(feature, (rule) => {
vendingMachine = new VendingMachine();
vendingMachine.stockItem(itemName, 0);
});
}
};

const givenIHaveInsertedTheCorrectAmountOfMoney = (given: DefineStepFunction) => {
given('I have inserted the correct amount of money', () => {
Expand All @@ -40,42 +43,42 @@ defineRuleBasedFeature(feature, (rule) => {
const inventoryAmount = vendingMachine.items[itemName];
expect(inventoryAmount).toBe(0);
});
}
};

const thenMyMoneyShouldBeReturned = (then: DefineStepFunction) => {
then(/^my money should be returned$/, () => {
const returnedMoney = vendingMachine.moneyReturnSlot;
expect(returnedMoney).toBe(myMoney);
});
}
};

rule("Dispenses items if correct amount of money is inserted", (test) => {
rule('Dispenses items if correct amount of money is inserted', (test) => {

test('Selecting a snack', ({ given, and, when, then }) => {
test('Selecting a snack', ({ given, when, then }) => {
givenTheVendingMachineHasXInStock(given);
givenIHaveInsertedTheCorrectAmountOfMoney(given);
whenISelectX(when);
thenXShouldBeDespensed(then);
});

test('Selecting a beverage', ({ given, and, when, then }) => {
test('Selecting a beverage', ({ given, when, then }) => {
givenTheVendingMachineHasXInStock(given);
givenIHaveInsertedTheCorrectAmountOfMoney(given);
whenISelectX(when);
thenXShouldBeDespensed(then);
});
});

rule("Returns my money if item is out of stock", (test) => {
rule('Returns my money if item is out of stock', (test) => {

test('Selecting a snack', ({ given, and, when, then }) => {
test('Selecting a snack', ({ given, when, then }) => {
givenTheVendingMachineHasNoXInStock(given);
givenIHaveInsertedTheCorrectAmountOfMoney(given);
whenISelectX(when);
thenMyMoneyShouldBeReturned(then);
});

test('Selecting a beverage', ({ given, and, when, then }) => {
test('Selecting a beverage', ({ given, when, then }) => {
givenTheVendingMachineHasNoXInStock(given);
givenIHaveInsertedTheCorrectAmountOfMoney(given);
whenISelectX(when);
Expand Down
74 changes: 74 additions & 0 deletions examples/typescript/specs/step-definitions/tag-filtering.steps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { loadFeature, defineFeature} from '../../../../src';
import { DefineStepFunction } from '../../../../src/feature-definition-creation';
import { VendingMachine } from '../../src/vending-machine';

const feature = loadFeature('./examples/typescript/specs/features/tag-filtering.feature', {
collapseRules: false,
tagFilter: '@included and not @excluded',
});

defineFeature(feature, ({rule}) => {
let vendingMachine: VendingMachine;

const myMoney = 0.50;

const givenTheVendingMachineHasXInStock = (given: DefineStepFunction) => {
given(/^the vending machine has "([^"]*)" in stock$/, (itemName: string) => {
vendingMachine = new VendingMachine();
vendingMachine.stockItem(itemName, 1);
});
};

const givenTheVendingMachineHasNoXInStock = (given: DefineStepFunction) => {
given(/^the vending machine has no "([^"]*)" in stock$/, (itemName: string) => {
vendingMachine = new VendingMachine();
vendingMachine.stockItem(itemName, 0);
});
};

const givenIHaveInsertedTheCorrectAmountOfMoney = (given: DefineStepFunction) => {
given('I have inserted the correct amount of money', () => {
vendingMachine.insertMoney(myMoney);
});
};

const whenISelectX = (when: DefineStepFunction) => {
when(/^I select "(.*)"$/, (itemName: string) => {
vendingMachine.dispenseItem(itemName);
});
};

const thenXShouldBeDespensed = (then: DefineStepFunction) => {
then(/^my "(.*)" should be dispensed$/, (itemName: string) => {
const inventoryAmount = vendingMachine.items[itemName];
expect(inventoryAmount).toBe(0);
});
};

const thenMyMoneyShouldBeReturned = (then: DefineStepFunction) => {
then(/^my money should be returned$/, () => {
const returnedMoney = vendingMachine.moneyReturnSlot;
expect(returnedMoney).toBe(myMoney);
});
};

rule('Dispenses items if correct amount of money is inserted', (test) => {

test('Selecting a snack', ({ given, when, then }) => {
givenTheVendingMachineHasXInStock(given);
givenIHaveInsertedTheCorrectAmountOfMoney(given);
whenISelectX(when);
thenXShouldBeDespensed(then);
});
});

rule('Returns my money if item is out of stock', (test) => {

test('Selecting a beverage', ({ given, when, then }) => {
givenTheVendingMachineHasNoXInStock(given);
givenIHaveInsertedTheCorrectAmountOfMoney(given);
whenISelectX(when);
thenMyMoneyShouldBeReturned(then);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ defineFeature(feature, (test) => {
};

const thenTheOutputOfXShouldBeDisplayed = (then: DefineStepFunction) => {
then(/^the output of "(\d+)" should be displayed$/, (expectedOutput: string) => {
then(/^the output of "(\d+|undefined)" should be displayed$/, (expectedOutput: string) => {
if (!expectedOutput) {
expect(output).toBeFalsy();
} else {
expect(output).toBe(parseFloat(expectedOutput));
expect(output).toBe(expectedOutput === 'undefined' ? undefined : parseFloat(expectedOutput));
}
});
};
Expand Down
2 changes: 1 addition & 1 deletion examples/typescript/src/vending-machine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export class VendingMachine {
}

public dispenseItem(itemName: string) {
if(this.items[itemName] === 0) {
if (this.items[itemName] === 0) {
this.moneyReturnSlot = this.balance;
this.balance = 0;
}
Expand Down
49 changes: 15 additions & 34 deletions src/automatic-step-binding.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { ParsedFeature, ScenarioGroup } from './models';
import { Feature, Rule } from './models';
import { matchSteps } from './validation/step-definition-validation';
import {
StepsDefinitionCallbackFunction,
defineFeature,
defineRuleBasedFeature,
DefineScenarioFunctionWithAliases
DefineScenarioFunctionWithAliases,
} from './feature-definition-creation';
import { generateStepCode } from './code-generation/step-generation';

Expand All @@ -24,11 +23,11 @@ const registerSteps = (stepDefinitionCallback: StepsDefinitionCallbackFunction)
but: registerStep,
pending: () => {
// Nothing to do
}
},
});
};

const matchAndDefineSteps = (group: ScenarioGroup, test: DefineScenarioFunctionWithAliases, errors: string[]) => {
const matchAndDefineSteps = (group: Rule | Feature, test: DefineScenarioFunctionWithAliases, errors: string[]) => {
const scenarioOutlineScenarios = group.scenarioOutlines.map((scenarioOutline) => scenarioOutline.scenarios[0]);

const scenarios = [ ...group.scenarios, ...scenarioOutlineScenarios ];
Expand All @@ -43,56 +42,38 @@ const matchAndDefineSteps = (group: ScenarioGroup, test: DefineScenarioFunctionW

options.defineStep(match.stepMatcher, match.stepFunction);
} else if (matches.length === 0) {
const stepCode = generateStepCode(scenario.steps, stepIndex, false);
const stepCode = generateStepCode(scenario.steps[stepIndex], false);
// tslint:disable-next-line:max-line-length
errors.push(
`No matching step found for step "${step.stepText}" in scenario "${scenario.title}" in feature "${group.title}". Please add the following step code: \n\n${stepCode}`
`No matching step found for step "${step.stepText}" in scenario "${scenario.title}" in feature "${group.title}". Please add the following step code: \n\n${stepCode}`,
);
} else {
const matchingCode = matches.map(
(match) => `${match.stepMatcher.toString()}\n\n${match.stepFunction.toString()}`
(match) => `${match.stepMatcher.toString()}\n\n${match.stepFunction.toString()}`,
);
errors.push(
`${matches.length} step definition matches were found for step "${step.stepText}" in scenario "${scenario.title}" in feature "${group.title}". Each step can only have one matching step definition. The following step definition matches were found:\n\n${matchingCode.join(
'\n\n'
)}`
'\n\n',
)}`,
);
}
});
});
});
};

export const autoBindSteps = (features: ParsedFeature[], stepDefinitions: StepsDefinitionCallbackFunction[]) => {
export const autoBindSteps = (features: Feature[], stepDefinitions: StepsDefinitionCallbackFunction[]) => {
stepDefinitions.forEach(registerSteps);

const errors: string[] = [];

features.forEach((feature) => {
defineFeature(feature, (test) => {
defineFeature(feature, ({test, rule}) => {
matchAndDefineSteps(feature, test, errors);
});
});

if (errors.length) {
throw new Error(errors.join('\n\n'));
}
};

export const autoBindStepsWithRules = (
features: ParsedFeature[],
stepDefinitions: StepsDefinitionCallbackFunction[]
) => {
stepDefinitions.forEach(registerSteps);

const errors: string[] = [];

features.forEach((feature) => {
defineRuleBasedFeature(feature, (ruleDefinition) => {
feature.rules.forEach((rule) => {
ruleDefinition(rule.title, (test) => {
matchAndDefineSteps(rule, test, errors);
});
feature.rules.forEach((parsedRule) => {
rule(parsedRule.title, (ruleTest) => {
matchAndDefineSteps(parsedRule, ruleTest, errors);
});
});
});
});
Expand Down
12 changes: 6 additions & 6 deletions src/code-generation/generate-code-by-line-number.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ParsedFeature } from '../models';
import { Feature } from '../models';
import { generateScenarioCodeWithSeparateStepFunctions, generateScenarioCode } from './scenario-generation';
import { generateStepCode } from './step-generation';

Expand All @@ -9,7 +9,7 @@ export enum ObjectTypeEnum {
}

const findObjectByLineNumber = (
feature: ParsedFeature,
feature: Feature,
lineNumber: number,
): { object: any, type: ObjectTypeEnum } | null => {
let found: any = null;
Expand Down Expand Up @@ -47,7 +47,7 @@ const findObjectByLineNumber = (
};

export const generateCodeFromFeature = (
feature: ParsedFeature,
feature: Feature,
lineNumber: number,
) => {
const objectAtLine = findObjectByLineNumber(feature, lineNumber);
Expand All @@ -60,13 +60,13 @@ export const generateCodeFromFeature = (
case ObjectTypeEnum.scenarioOutline:
return generateScenarioCode(objectAtLine.object);
case ObjectTypeEnum.step:
return generateStepCode(objectAtLine.object.steps, objectAtLine.object.index, false);
return generateStepCode(objectAtLine.object.steps[objectAtLine.object.index], false);
}
}
};

export const generateCodeWithSeparateFunctionsFromFeature = (
feature: ParsedFeature,
feature: Feature,
lineNumber: number,
) => {
const objectAtLine = findObjectByLineNumber(feature, lineNumber);
Expand All @@ -79,7 +79,7 @@ export const generateCodeWithSeparateFunctionsFromFeature = (
case ObjectTypeEnum.scenarioOutline:
return generateScenarioCodeWithSeparateStepFunctions(objectAtLine.object);
case ObjectTypeEnum.step:
return generateStepCode(objectAtLine.object.steps, objectAtLine.object.index, true);
return generateStepCode(objectAtLine.object.steps[objectAtLine.object.index], true);
}
}
};
Loading