Skip to content

Commit 8498d3f

Browse files
committed
merge defineFeature and defineRuleBasedFeature
1 parent cb7c122 commit 8498d3f

18 files changed

+502
-489
lines changed

.vscode/launch.json

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@
44
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
55
"version": "0.2.0",
66
"configurations": [
7+
{
8+
"name": "Launch via NPM",
9+
"type": "pwa-node",
10+
"request": "launch",
11+
"cwd": "${workspaceRoot}",
12+
"runtimeExecutable": "npm",
13+
"runtimeArgs": [
14+
"run-script", "test", "--", "/home/markus/git/jest-cucumber/examples/typescript/specs/step-definitions/tag-filter.steps.ts"
15+
],
16+
},
717
{
818
"type": "node",
919
"request": "launch",
@@ -12,10 +22,10 @@
1222
"program": "${workspaceFolder}/dist/code-generation-test.js"
1323
},
1424
{
15-
"type": "node",
25+
"type": "pwa-node",
1626
"request": "launch",
1727
"name": "TypeScript examples",
18-
"program": "${workspaceRoot}/node_modules/jest/bin/jest.js",
28+
"program": "npm${workspaceRoot}/node_modules/jest/bin/jest.js",
1929
"args": [
2030
"-i"
2131
],
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
@included
2+
Feature: Vending machine
3+
4+
Rule: Dispenses items if correct amount of money is inserted
5+
6+
Scenario: Selecting a snack
7+
Given the vending machine has "Maltesers" in stock
8+
And I have inserted the correct amount of money
9+
When I select "Maltesers"
10+
Then my "Maltesers" should be dispensed
11+
12+
@excluded
13+
Scenario Outline: Selecting a beverage
14+
Given the vending machine has "<beverage>" in stock
15+
And I have inserted the correct amount of money
16+
When I select "<beverage>"
17+
Then my "<beverage>" should be dispensed
18+
19+
Examples:
20+
| beverage |
21+
| Cola |
22+
| Ginger ale |
23+
24+
Rule: Returns my money if item is out of stock
25+
26+
@excluded
27+
Scenario: Selecting a snack
28+
Given the vending machine has no "Maltesers" in stock
29+
And I have inserted the correct amount of money
30+
When I select "Maltesers"
31+
Then my money should be returned
32+
33+
Scenario: Selecting a beverage
34+
Given the vending machine has no "Cola" in stock
35+
And I have inserted the correct amount of money
36+
When I select "Cola"
37+
Then my money should be returned

examples/typescript/specs/step-definitions/extended-rules-auto-step-binding.steps.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { StepDefinitions, loadFeature, autoBindStepsWithRules } from '../../../../src';
1+
import { StepDefinitions, loadFeature, autoBindSteps} from '../../../../src';
22
import { VendingMachine } from '../../src/vending-machine';
33

44
export const vendingMachineSteps: StepDefinitions = ({ given, and, when, then }) => {
@@ -37,4 +37,4 @@ export const vendingMachineSteps: StepDefinitions = ({ given, and, when, then })
3737

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

40-
autoBindStepsWithRules([feature], [ vendingMachineSteps ]);
40+
autoBindSteps([feature], [ vendingMachineSteps ]);

examples/typescript/specs/step-definitions/extended-rules-definition.steps.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,48 @@
1-
import { loadFeature, defineRuleBasedFeature } from '../../../../src';
2-
import { DefineStepFunction } from '../../../../src/feature-definition-creation';
1+
import { loadFeature, defineFeature} from '../../../../src';
2+
import { StepDefinitionFunction } from '../../../../src/feature-definition-creation';
33
import { VendingMachine } from '../../src/vending-machine';
44

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

7-
defineRuleBasedFeature(feature, (rule) => {
7+
defineFeature(feature, ({rule}) => {
88
let vendingMachine: VendingMachine;
99

1010
const myMoney = 0.50;
1111

12-
const givenTheVendingMachineHasXInStock = (given: DefineStepFunction) => {
12+
const givenTheVendingMachineHasXInStock = (given: StepDefinitionFunction) => {
1313
given(/^the vending machine has "([^"]*)" in stock$/, (itemName: string) => {
1414
vendingMachine = new VendingMachine();
1515
vendingMachine.stockItem(itemName, 1);
1616
});
1717
};
1818

19-
const givenTheVendingMachineHasNoXInStock = (given: DefineStepFunction) => {
19+
const givenTheVendingMachineHasNoXInStock = (given: StepDefinitionFunction) => {
2020
given(/^the vending machine has no "([^"]*)" in stock$/, (itemName: string) => {
2121
vendingMachine = new VendingMachine();
2222
vendingMachine.stockItem(itemName, 0);
2323
});
2424
}
2525

26-
const givenIHaveInsertedTheCorrectAmountOfMoney = (given: DefineStepFunction) => {
26+
const givenIHaveInsertedTheCorrectAmountOfMoney = (given: StepDefinitionFunction) => {
2727
given('I have inserted the correct amount of money', () => {
2828
vendingMachine.insertMoney(myMoney);
2929
});
3030
};
3131

32-
const whenISelectX = (when: DefineStepFunction) => {
32+
const whenISelectX = (when: StepDefinitionFunction) => {
3333
when(/^I select "(.*)"$/, (itemName: string) => {
3434
vendingMachine.dispenseItem(itemName);
3535
});
3636
};
3737

38-
const thenXShouldBeDespensed = (then: DefineStepFunction) => {
38+
const thenXShouldBeDespensed = (then: StepDefinitionFunction) => {
3939
then(/^my "(.*)" should be dispensed$/, (itemName: string) => {
4040
const inventoryAmount = vendingMachine.items[itemName];
4141
expect(inventoryAmount).toBe(0);
4242
});
4343
}
4444

45-
const thenMyMoneyShouldBeReturned = (then: DefineStepFunction) => {
45+
const thenMyMoneyShouldBeReturned = (then: StepDefinitionFunction) => {
4646
then(/^my money should be returned$/, () => {
4747
const returnedMoney = vendingMachine.moneyReturnSlot;
4848
expect(returnedMoney).toBe(myMoney);
@@ -51,14 +51,14 @@ defineRuleBasedFeature(feature, (rule) => {
5151

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

54-
test('Selecting a snack', ({ given, and, when, then }) => {
54+
test('Selecting a snack', ({ given, when, then }) => {
5555
givenTheVendingMachineHasXInStock(given);
5656
givenIHaveInsertedTheCorrectAmountOfMoney(given);
5757
whenISelectX(when);
5858
thenXShouldBeDespensed(then);
5959
});
6060

61-
test('Selecting a beverage', ({ given, and, when, then }) => {
61+
test('Selecting a beverage', ({ given, when, then }) => {
6262
givenTheVendingMachineHasXInStock(given);
6363
givenIHaveInsertedTheCorrectAmountOfMoney(given);
6464
whenISelectX(when);
@@ -68,14 +68,14 @@ defineRuleBasedFeature(feature, (rule) => {
6868

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

71-
test('Selecting a snack', ({ given, and, when, then }) => {
71+
test('Selecting a snack', ({ given, when, then }) => {
7272
givenTheVendingMachineHasNoXInStock(given);
7373
givenIHaveInsertedTheCorrectAmountOfMoney(given);
7474
whenISelectX(when);
7575
thenMyMoneyShouldBeReturned(then);
7676
});
7777

78-
test('Selecting a beverage', ({ given, and, when, then }) => {
78+
test('Selecting a beverage', ({ given, when, then }) => {
7979
givenTheVendingMachineHasNoXInStock(given);
8080
givenIHaveInsertedTheCorrectAmountOfMoney(given);
8181
whenISelectX(when);
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { loadFeature, defineFeature} from '../../../../src';
2+
import { StepDefinitionFunction } from '../../../../src/feature-definition-creation';
3+
import { VendingMachine } from '../../src/vending-machine';
4+
5+
const feature = loadFeature('./examples/typescript/specs/features/tag-filtering.feature', {
6+
collapseRules: false,
7+
tagFilter: "@included and not @excluded"
8+
});
9+
10+
defineFeature(feature, ({rule}) => {
11+
let vendingMachine: VendingMachine;
12+
13+
const myMoney = 0.50;
14+
15+
const givenTheVendingMachineHasXInStock = (given: StepDefinitionFunction) => {
16+
given(/^the vending machine has "([^"]*)" in stock$/, (itemName: string) => {
17+
vendingMachine = new VendingMachine();
18+
vendingMachine.stockItem(itemName, 1);
19+
});
20+
};
21+
22+
const givenTheVendingMachineHasNoXInStock = (given: StepDefinitionFunction) => {
23+
given(/^the vending machine has no "([^"]*)" in stock$/, (itemName: string) => {
24+
vendingMachine = new VendingMachine();
25+
vendingMachine.stockItem(itemName, 0);
26+
});
27+
}
28+
29+
const givenIHaveInsertedTheCorrectAmountOfMoney = (given: StepDefinitionFunction) => {
30+
given('I have inserted the correct amount of money', () => {
31+
vendingMachine.insertMoney(myMoney);
32+
});
33+
};
34+
35+
const whenISelectX = (when: StepDefinitionFunction) => {
36+
when(/^I select "(.*)"$/, (itemName: string) => {
37+
vendingMachine.dispenseItem(itemName);
38+
});
39+
};
40+
41+
const thenXShouldBeDespensed = (then: StepDefinitionFunction) => {
42+
then(/^my "(.*)" should be dispensed$/, (itemName: string) => {
43+
const inventoryAmount = vendingMachine.items[itemName];
44+
expect(inventoryAmount).toBe(0);
45+
});
46+
}
47+
48+
const thenMyMoneyShouldBeReturned = (then: StepDefinitionFunction) => {
49+
then(/^my money should be returned$/, () => {
50+
const returnedMoney = vendingMachine.moneyReturnSlot;
51+
expect(returnedMoney).toBe(myMoney);
52+
});
53+
}
54+
55+
rule("Dispenses items if correct amount of money is inserted", (test) => {
56+
57+
test('Selecting a snack', ({ given, when, then }) => {
58+
givenTheVendingMachineHasXInStock(given);
59+
givenIHaveInsertedTheCorrectAmountOfMoney(given);
60+
whenISelectX(when);
61+
thenXShouldBeDespensed(then);
62+
});
63+
});
64+
65+
rule("Returns my money if item is out of stock", (test) => {
66+
67+
test('Selecting a beverage', ({ given, when, then }) => {
68+
givenTheVendingMachineHasNoXInStock(given);
69+
givenIHaveInsertedTheCorrectAmountOfMoney(given);
70+
whenISelectX(when);
71+
thenMyMoneyShouldBeReturned(then);
72+
});
73+
});
74+
});

examples/typescript/specs/step-definitions/using-latest-gherkin-keywords.steps.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@ defineFeature(feature, (test) => {
3737
};
3838

3939
const thenTheOutputOfXShouldBeDisplayed = (then: DefineStepFunction) => {
40-
then(/^the output of "(\d+)" should be displayed$/, (expectedOutput: string) => {
40+
then(/^the output of "(\d+|undefined)" should be displayed$/, (expectedOutput: string) => {
4141
if (!expectedOutput) {
4242
expect(output).toBeFalsy();
4343
} else {
44-
expect(output).toBe(parseFloat(expectedOutput));
44+
expect(output).toBe(expectedOutput === "undefined" ? undefined: parseFloat(expectedOutput));
4545
}
4646
});
4747
};

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"scripts": {
88
"build": "tsc",
99
"jest": "jest --verbose",
10-
"test": "npm run build & npm run lint & jest --color",
10+
"test": "npm run build & jest --color",
1111
"lint": "tslint --project ./"
1212
},
1313
"repository": {

src/automatic-step-binding.ts

Lines changed: 16 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { ParsedFeature, ScenarioGroup } from './models';
1+
import { Feature, Rule } from './models';
22
import { matchSteps } from './validation/step-definition-validation';
33
import {
44
StepsDefinitionCallbackFunction,
55
defineFeature,
6-
defineRuleBasedFeature,
7-
DefineScenarioFunctionWithAliases
6+
FeatureDefinitionFunctions,
7+
ScenarioDefinitionFunctionWithAliases,
88
} from './feature-definition-creation';
99
import { generateStepCode } from './code-generation/step-generation';
1010

@@ -24,11 +24,11 @@ const registerSteps = (stepDefinitionCallback: StepsDefinitionCallbackFunction)
2424
but: registerStep,
2525
pending: () => {
2626
// Nothing to do
27-
}
27+
},
2828
});
2929
};
3030

31-
const matchAndDefineSteps = (group: ScenarioGroup, test: DefineScenarioFunctionWithAliases, errors: string[]) => {
31+
const matchAndDefineSteps = (group: Rule, test: ScenarioDefinitionFunctionWithAliases, errors: string[]) => {
3232
const scenarioOutlineScenarios = group.scenarioOutlines.map((scenarioOutline) => scenarioOutline.scenarios[0]);
3333

3434
const scenarios = [ ...group.scenarios, ...scenarioOutlineScenarios ];
@@ -43,10 +43,10 @@ const matchAndDefineSteps = (group: ScenarioGroup, test: DefineScenarioFunctionW
4343

4444
options.defineStep(match.stepMatcher, match.stepFunction);
4545
} else if (matches.length === 0) {
46-
const stepCode = generateStepCode(scenario.steps, stepIndex, false);
46+
const stepCode = generateStepCode(scenario.steps[stepIndex], false);
4747
// tslint:disable-next-line:max-line-length
4848
errors.push(
49-
`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}`
49+
`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}`,
5050
);
5151
} else {
5252
const matchingCode = matches.map(
@@ -55,49 +55,31 @@ const matchAndDefineSteps = (group: ScenarioGroup, test: DefineScenarioFunctionW
5555
errors.push(
5656
`${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(
5757
'\n\n'
58-
)}`
58+
)}`,
5959
);
6060
}
6161
});
6262
});
6363
});
6464
};
6565

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

6969
const errors: string[] = [];
7070

7171
features.forEach((feature) => {
72-
defineFeature(feature, (test) => {
72+
defineFeature(feature, ({test, rule}) => {
7373
matchAndDefineSteps(feature, test, errors);
74+
feature.rules.forEach(r => {
75+
rule(r.title, (test) => {
76+
matchAndDefineSteps(r, test, errors)
77+
})
78+
})
7479
});
7580
});
7681

7782
if (errors.length) {
7883
throw new Error(errors.join('\n\n'));
7984
}
80-
};
81-
82-
export const autoBindStepsWithRules = (
83-
features: ParsedFeature[],
84-
stepDefinitions: StepsDefinitionCallbackFunction[]
85-
) => {
86-
stepDefinitions.forEach(registerSteps);
87-
88-
const errors: string[] = [];
89-
90-
features.forEach((feature) => {
91-
defineRuleBasedFeature(feature, (ruleDefinition) => {
92-
feature.rules.forEach((rule) => {
93-
ruleDefinition(rule.title, (test) => {
94-
matchAndDefineSteps(rule, test, errors);
95-
});
96-
});
97-
});
98-
});
99-
100-
if (errors.length) {
101-
throw new Error(errors.join('\n\n'));
102-
}
103-
};
85+
};

0 commit comments

Comments
 (0)