vitest-cucumber-plugin 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/CONTRIBUTING.md +42 -0
  2. package/LICENSE +21 -0
  3. package/README.md +113 -0
  4. package/RELEASE_NOTES.md +9 -0
  5. package/package.json +34 -0
  6. package/rollup-nearley.js +10 -0
  7. package/run-tests +8 -0
  8. package/src/generate/example.js +20 -0
  9. package/src/generate/examples.js +47 -0
  10. package/src/generate/feature.js +45 -0
  11. package/src/generate/index.js +13 -0
  12. package/src/generate/scenarioOutline.js +18 -0
  13. package/src/generate/tests.js +23 -0
  14. package/src/generate/util.js +10 -0
  15. package/src/gherkin.js +171 -0
  16. package/src/gherkin.ne +130 -0
  17. package/src/gherkin.umd.js +118 -0
  18. package/src/index.js +65 -0
  19. package/src/logger.js +7 -0
  20. package/src/parameterize.js +7 -0
  21. package/src/parse.js +21 -0
  22. package/src/steps.js +42 -0
  23. package/tests/background/features/background.feature +25 -0
  24. package/tests/background/features/step_definitions/steps.js +30 -0
  25. package/tests/background/package-lock.json +1967 -0
  26. package/tests/background/package.json +11 -0
  27. package/tests/background/vite.config.js +9 -0
  28. package/tests/comments/features/is-it-friday.feature +10 -0
  29. package/tests/comments/features/step_definitions/stepdefs.js +15 -0
  30. package/tests/comments/package-lock.json +1967 -0
  31. package/tests/comments/package.json +11 -0
  32. package/tests/comments/vite.config.js +9 -0
  33. package/tests/data-tables/features/data-tables.feature +25 -0
  34. package/tests/data-tables/features/step_definitions/data-tables.js +21 -0
  35. package/tests/data-tables/package-lock.json +1967 -0
  36. package/tests/data-tables/package.json +11 -0
  37. package/tests/data-tables/vite.config.js +9 -0
  38. package/tests/data-tables/vite.config.js.timestamp-1682359000824-3876ac2e9095b.mjs +13 -0
  39. package/tests/is-it-friday/features/is-it-friday.feature +7 -0
  40. package/tests/is-it-friday/features/step_definitions/stepdefs.js +15 -0
  41. package/tests/is-it-friday/package-lock.json +1967 -0
  42. package/tests/is-it-friday/package.json +11 -0
  43. package/tests/is-it-friday/vite.config.js +9 -0
  44. package/tests/is-it-friday-scenario-outline/features/is-it-friday.feature +13 -0
  45. package/tests/is-it-friday-scenario-outline/features/step_definitions/stepdefs.js +15 -0
  46. package/tests/is-it-friday-scenario-outline/package-lock.json +1967 -0
  47. package/tests/is-it-friday-scenario-outline/package.json +11 -0
  48. package/tests/is-it-friday-scenario-outline/vite.config.js +9 -0
  49. package/tests/is-it-friday-two-scenarios/features/is-it-friday.feature +12 -0
  50. package/tests/is-it-friday-two-scenarios/features/step_definitions/stepdefs.js +19 -0
  51. package/tests/is-it-friday-two-scenarios/package-lock.json +1967 -0
  52. package/tests/is-it-friday-two-scenarios/package.json +11 -0
  53. package/tests/is-it-friday-two-scenarios/vite.config.js +9 -0
  54. package/tests/is-it-friday-two-scenarios-multiple-feature-files/features/friday.feature +7 -0
  55. package/tests/is-it-friday-two-scenarios-multiple-feature-files/features/step_definitions/stepdefs.js +19 -0
  56. package/tests/is-it-friday-two-scenarios-multiple-feature-files/features/sunday.feature +8 -0
  57. package/tests/is-it-friday-two-scenarios-multiple-feature-files/package-lock.json +1967 -0
  58. package/tests/is-it-friday-two-scenarios-multiple-feature-files/package.json +11 -0
  59. package/tests/is-it-friday-two-scenarios-multiple-feature-files/vite.config.js +9 -0
  60. package/tests/keyword-aliases/features/scenario-outline.feature +20 -0
  61. package/tests/keyword-aliases/features/scenario.feature +14 -0
  62. package/tests/keyword-aliases/features/step_definitions/steps.js +30 -0
  63. package/tests/keyword-aliases/features/steps.feature +28 -0
  64. package/tests/keyword-aliases/package-lock.json +1967 -0
  65. package/tests/keyword-aliases/package.json +11 -0
  66. package/tests/keyword-aliases/vite.config.js +9 -0
  67. package/tests/tags/features/skip.feature +9 -0
  68. package/tests/tags/features/step_definitions/steps.js +30 -0
  69. package/tests/tags/features/tags-scenario-outline.feature +30 -0
  70. package/tests/tags/features/tags.feature +26 -0
  71. package/tests/tags/package-lock.json +1967 -0
  72. package/tests/tags/package.json +11 -0
  73. package/tests/tags/vite.config.js +12 -0
package/src/gherkin.ne ADDED
@@ -0,0 +1,130 @@
1
+ @{%
2
+ const fp = require('lodash/fp.js');
3
+ const moo = require('moo');
4
+ const log = require('./logger.js').log;
5
+ const lexer = moo.compile({
6
+ emptyLine : { match: /^[ \t]*(?:\#[^\n]+)?\n/, lineBreaks : true },
7
+ newline : { match : '\n', lineBreaks : true },
8
+ ws : /[ \t]+/,
9
+ at : '@',
10
+ colon : ':',
11
+ step : '*',
12
+ pipe : '|',
13
+ backSlash : '\\',
14
+ scenarioOutline : ['Scenario Outline','Scenario Template'],
15
+ word : {
16
+ match : /[^ \t\n:|\\]+/,
17
+ type : moo.keywords({
18
+ feature : 'Feature',
19
+ examples : ['Examples','Scenarios'],
20
+ step : ['Given','When','Then','And','But'],
21
+ example : ['Example','Scenario'],
22
+ background : 'Background',
23
+ }),
24
+ },
25
+ });
26
+ %}
27
+
28
+ @lexer lexer
29
+
30
+ main -> emptyLines tags feature {% data => fp.set('tags',data[1],data[2]) %}
31
+
32
+ feature -> featureStatement freeform background statements {%
33
+ (data) => fp.assign(data[0],{ description : data[1].trim(), background : data[2], statements : data[3] })
34
+ %}
35
+ featureStatement -> _ %feature _ %colon text %newline {%
36
+ (data) => { return { type : { type : 'feature', name : data[1].value.trim() }, name : data[4].trim() } }
37
+ %}
38
+
39
+ tags -> null {% data => [] %}
40
+ | _ tag tagList %newline {% data => fp.concat(data[1],data[2]) %}
41
+
42
+ tagList -> null {% data => [] %}
43
+ | tagList %ws tag {% data => fp.concat(data[0],data[2]) %}
44
+
45
+ tag -> %at %word {% data => data[1].value.trim() %}
46
+
47
+ background -> null {% data => null %}
48
+ | backgroundStatement freeform steps {%
49
+ data => fp.assign(data[0],{ description : data[1].trim(), steps : data[2] })
50
+ %}
51
+
52
+ backgroundStatement -> _ %background _ %colon text %newline {%
53
+ (data) => { return { type : { type : 'background', name : data[1].value }, name : data[4].trim() } }
54
+ %}
55
+
56
+ statement -> example {% data => data[0] %}
57
+ | scenarioOutline {% data => data[0] %}
58
+
59
+ statements -> null {% data => [] %}
60
+ | statements statement {% data => fp.concat(data[0],data[1]) %}
61
+
62
+ example -> tags exampleStatement steps {% (data) => fp.assign(data[1],{ tags : data[0], steps : data[2] }) %}
63
+ exampleStatement -> _ exampleKeyword _ %colon text %newline {%
64
+ (data) => { return { type : { type : 'example', name : data[1] }, name : data[4].trim() } }
65
+ %}
66
+ exampleKeyword -> %example {% data => data[0].value %}
67
+
68
+ scenarioOutline -> tags scenarioOutlineStatement steps examplesList {%
69
+ data => fp.assign(data[1],{ tags : data[0], steps : data[2], examples : data[3] })
70
+ %}
71
+ scenarioOutlineStatement -> _ scenarioOutlineKeyword _ %colon text %newline {%
72
+ (data) => { return { type : { type : 'scenarioOutline', name : data[1] }, name : data[4].trim() } }
73
+ %}
74
+ scenarioOutlineKeyword -> %scenarioOutline {% data => data[0].value %}
75
+
76
+ examplesList -> null {% data => [] %}
77
+ | examplesList examples {% data => fp.concat(data[0],data[1]) %}
78
+
79
+ examples -> tags examplesStatement dataTable emptyLines {%
80
+ data => fp.assign(data[1],{ tags : data[0], dataTable : data[2] })
81
+ %}
82
+ examplesStatement -> _ examplesKeyword _ %colon text %newline {%
83
+ (data) => { return { type : { type : 'examples', name : data[1] }, name : data[4] } }
84
+ %}
85
+ examplesKeyword -> %examples {% data => data[0].value %}
86
+
87
+ dataTable -> null {% data => [] %}
88
+ | dataTable dataTableRow {% data => fp.concat(data[0],[data[1]]) %}
89
+
90
+ dataTableRow -> _ %pipe dataTableColumns %newline {% data => data[2] %}
91
+
92
+ dataTableColumns -> null {% data => [] %}
93
+ | dataTableColumns text %pipe {% data => fp.concat(data[0],data[1].trim()) %}
94
+
95
+ steps -> stepAndTable moreSteps {% data => fp.concat(data[0],data[1]) %}
96
+
97
+ moreSteps -> null {% data => [] %}
98
+ | moreSteps stepAndTable {% data => fp.concat(data[0],data[1]) %}
99
+ | moreSteps %emptyLine {% data => data[0] %}
100
+
101
+ step -> _ stepKeyword text %newline {% data => { return { type : data[1], text : data[2].trim() } } %}
102
+ stepAndTable -> step dataTable {% data => fp.set('dataTable',data[1],data[0]) %}
103
+
104
+ stepKeyword -> %step {% (data) => { return { type : 'step', name : data[0].value } } %}
105
+
106
+ text -> null {% data => '' %}
107
+ | text %word {% data => data[0]+data[1].value %}
108
+ | text %ws {% data => data[0]+data[1].value %}
109
+ | text %step {% data => data[0]+data[1].value %}
110
+ | text %colon {% data => data[0]+data[1].value %}
111
+ | text %example {% data => data[0]+data[1].value %}
112
+ | text %examples {% data => data[0]+data[1].value %}
113
+ | text %scenarioOutline {% data => data[0]+data[1].value %}
114
+ | text %background {% data => data[0]+data[1].value %}
115
+
116
+ bolText -> %ws %word {% data => data[1].value %}
117
+ | %word {% data => data[0].value %}
118
+
119
+ freeform -> null {% data => '' %}
120
+ | freeform bolText text %newline {% (data) => {
121
+ return data[0]+data[1]+data[2]+'\n'
122
+ }
123
+ %}
124
+ | freeform %emptyLine {% data => data[0]+'\n' %}
125
+
126
+ _ -> null {% data => '' %}
127
+ | %ws {% data => data[0].value %}
128
+
129
+ emptyLines -> null {% data => '' %}
130
+ | emptyLines %emptyLine {% data => data[0]+'\n' %}
@@ -0,0 +1,118 @@
1
+ // Generated automatically by nearley, version 2.20.1
2
+ // http://github.com/Hardmath123/nearley
3
+ (function () {
4
+ function id(x) { return x[0]; }
5
+
6
+ const fp = require('lodash/fp.js');
7
+ const moo = require('moo');
8
+ const log = require('./logger.js').log;
9
+ const lexer = moo.compile({
10
+ emptyLine : { match: /^[ \t]*(?:\#[^\n]+)?\n/, lineBreaks : true },
11
+ newline : { match : '\n', lineBreaks : true },
12
+ ws : /[ \t]+/,
13
+ at : '@',
14
+ colon : ':',
15
+ step : '*',
16
+ pipe : '|',
17
+ backSlash : '\\',
18
+ scenarioOutline : ['Scenario Outline','Scenario Template'],
19
+ word : {
20
+ match : /[^ \t\n:|\\]+/,
21
+ type : moo.keywords({
22
+ feature : 'Feature',
23
+ examples : ['Examples','Scenarios'],
24
+ step : ['Given','When','Then','And','But'],
25
+ example : ['Example','Scenario'],
26
+ background : 'Background',
27
+ }),
28
+ },
29
+ });
30
+ var grammar = {
31
+ Lexer: lexer,
32
+ ParserRules: [
33
+ {"name": "main", "symbols": ["emptyLines", "tags", "feature"], "postprocess": data => fp.set('tags',data[1],data[2])},
34
+ {"name": "feature", "symbols": ["featureStatement", "freeform", "background", "statements"], "postprocess":
35
+ (data) => fp.assign(data[0],{ description : data[1].trim(), background : data[2], statements : data[3] })
36
+ },
37
+ {"name": "featureStatement", "symbols": ["_", (lexer.has("feature") ? {type: "feature"} : feature), "_", (lexer.has("colon") ? {type: "colon"} : colon), "text", (lexer.has("newline") ? {type: "newline"} : newline)], "postprocess":
38
+ (data) => { return { type : { type : 'feature', name : data[1].value.trim() }, name : data[4].trim() } }
39
+ },
40
+ {"name": "tags", "symbols": [], "postprocess": data => []},
41
+ {"name": "tags", "symbols": ["_", "tag", "tagList", (lexer.has("newline") ? {type: "newline"} : newline)], "postprocess": data => fp.concat(data[1],data[2])},
42
+ {"name": "tagList", "symbols": [], "postprocess": data => []},
43
+ {"name": "tagList", "symbols": ["tagList", (lexer.has("ws") ? {type: "ws"} : ws), "tag"], "postprocess": data => fp.concat(data[0],data[2])},
44
+ {"name": "tag", "symbols": [(lexer.has("at") ? {type: "at"} : at), (lexer.has("word") ? {type: "word"} : word)], "postprocess": data => data[1].value.trim()},
45
+ {"name": "background", "symbols": [], "postprocess": data => null},
46
+ {"name": "background", "symbols": ["backgroundStatement", "freeform", "steps"], "postprocess":
47
+ data => fp.assign(data[0],{ description : data[1].trim(), steps : data[2] })
48
+ },
49
+ {"name": "backgroundStatement", "symbols": ["_", (lexer.has("background") ? {type: "background"} : background), "_", (lexer.has("colon") ? {type: "colon"} : colon), "text", (lexer.has("newline") ? {type: "newline"} : newline)], "postprocess":
50
+ (data) => { return { type : { type : 'background', name : data[1].value }, name : data[4].trim() } }
51
+ },
52
+ {"name": "statement", "symbols": ["example"], "postprocess": data => data[0]},
53
+ {"name": "statement", "symbols": ["scenarioOutline"], "postprocess": data => data[0]},
54
+ {"name": "statements", "symbols": [], "postprocess": data => []},
55
+ {"name": "statements", "symbols": ["statements", "statement"], "postprocess": data => fp.concat(data[0],data[1])},
56
+ {"name": "example", "symbols": ["tags", "exampleStatement", "steps"], "postprocess": (data) => fp.assign(data[1],{ tags : data[0], steps : data[2] })},
57
+ {"name": "exampleStatement", "symbols": ["_", "exampleKeyword", "_", (lexer.has("colon") ? {type: "colon"} : colon), "text", (lexer.has("newline") ? {type: "newline"} : newline)], "postprocess":
58
+ (data) => { return { type : { type : 'example', name : data[1] }, name : data[4].trim() } }
59
+ },
60
+ {"name": "exampleKeyword", "symbols": [(lexer.has("example") ? {type: "example"} : example)], "postprocess": data => data[0].value},
61
+ {"name": "scenarioOutline", "symbols": ["tags", "scenarioOutlineStatement", "steps", "examplesList"], "postprocess":
62
+ data => fp.assign(data[1],{ tags : data[0], steps : data[2], examples : data[3] })
63
+ },
64
+ {"name": "scenarioOutlineStatement", "symbols": ["_", "scenarioOutlineKeyword", "_", (lexer.has("colon") ? {type: "colon"} : colon), "text", (lexer.has("newline") ? {type: "newline"} : newline)], "postprocess":
65
+ (data) => { return { type : { type : 'scenarioOutline', name : data[1] }, name : data[4].trim() } }
66
+ },
67
+ {"name": "scenarioOutlineKeyword", "symbols": [(lexer.has("scenarioOutline") ? {type: "scenarioOutline"} : scenarioOutline)], "postprocess": data => data[0].value},
68
+ {"name": "examplesList", "symbols": [], "postprocess": data => []},
69
+ {"name": "examplesList", "symbols": ["examplesList", "examples"], "postprocess": data => fp.concat(data[0],data[1])},
70
+ {"name": "examples", "symbols": ["tags", "examplesStatement", "dataTable", "emptyLines"], "postprocess":
71
+ data => fp.assign(data[1],{ tags : data[0], dataTable : data[2] })
72
+ },
73
+ {"name": "examplesStatement", "symbols": ["_", "examplesKeyword", "_", (lexer.has("colon") ? {type: "colon"} : colon), "text", (lexer.has("newline") ? {type: "newline"} : newline)], "postprocess":
74
+ (data) => { return { type : { type : 'examples', name : data[1] }, name : data[4] } }
75
+ },
76
+ {"name": "examplesKeyword", "symbols": [(lexer.has("examples") ? {type: "examples"} : examples)], "postprocess": data => data[0].value},
77
+ {"name": "dataTable", "symbols": [], "postprocess": data => []},
78
+ {"name": "dataTable", "symbols": ["dataTable", "dataTableRow"], "postprocess": data => fp.concat(data[0],[data[1]])},
79
+ {"name": "dataTableRow", "symbols": ["_", (lexer.has("pipe") ? {type: "pipe"} : pipe), "dataTableColumns", (lexer.has("newline") ? {type: "newline"} : newline)], "postprocess": data => data[2]},
80
+ {"name": "dataTableColumns", "symbols": [], "postprocess": data => []},
81
+ {"name": "dataTableColumns", "symbols": ["dataTableColumns", "text", (lexer.has("pipe") ? {type: "pipe"} : pipe)], "postprocess": data => fp.concat(data[0],data[1].trim())},
82
+ {"name": "steps", "symbols": ["stepAndTable", "moreSteps"], "postprocess": data => fp.concat(data[0],data[1])},
83
+ {"name": "moreSteps", "symbols": [], "postprocess": data => []},
84
+ {"name": "moreSteps", "symbols": ["moreSteps", "stepAndTable"], "postprocess": data => fp.concat(data[0],data[1])},
85
+ {"name": "moreSteps", "symbols": ["moreSteps", (lexer.has("emptyLine") ? {type: "emptyLine"} : emptyLine)], "postprocess": data => data[0]},
86
+ {"name": "step", "symbols": ["_", "stepKeyword", "text", (lexer.has("newline") ? {type: "newline"} : newline)], "postprocess": data => { return { type : data[1], text : data[2].trim() } }},
87
+ {"name": "stepAndTable", "symbols": ["step", "dataTable"], "postprocess": data => fp.set('dataTable',data[1],data[0])},
88
+ {"name": "stepKeyword", "symbols": [(lexer.has("step") ? {type: "step"} : step)], "postprocess": (data) => { return { type : 'step', name : data[0].value } }},
89
+ {"name": "text", "symbols": [], "postprocess": data => ''},
90
+ {"name": "text", "symbols": ["text", (lexer.has("word") ? {type: "word"} : word)], "postprocess": data => data[0]+data[1].value},
91
+ {"name": "text", "symbols": ["text", (lexer.has("ws") ? {type: "ws"} : ws)], "postprocess": data => data[0]+data[1].value},
92
+ {"name": "text", "symbols": ["text", (lexer.has("step") ? {type: "step"} : step)], "postprocess": data => data[0]+data[1].value},
93
+ {"name": "text", "symbols": ["text", (lexer.has("colon") ? {type: "colon"} : colon)], "postprocess": data => data[0]+data[1].value},
94
+ {"name": "text", "symbols": ["text", (lexer.has("example") ? {type: "example"} : example)], "postprocess": data => data[0]+data[1].value},
95
+ {"name": "text", "symbols": ["text", (lexer.has("examples") ? {type: "examples"} : examples)], "postprocess": data => data[0]+data[1].value},
96
+ {"name": "text", "symbols": ["text", (lexer.has("scenarioOutline") ? {type: "scenarioOutline"} : scenarioOutline)], "postprocess": data => data[0]+data[1].value},
97
+ {"name": "text", "symbols": ["text", (lexer.has("background") ? {type: "background"} : background)], "postprocess": data => data[0]+data[1].value},
98
+ {"name": "bolText", "symbols": [(lexer.has("ws") ? {type: "ws"} : ws), (lexer.has("word") ? {type: "word"} : word)], "postprocess": data => data[1].value},
99
+ {"name": "bolText", "symbols": [(lexer.has("word") ? {type: "word"} : word)], "postprocess": data => data[0].value},
100
+ {"name": "freeform", "symbols": [], "postprocess": data => ''},
101
+ {"name": "freeform", "symbols": ["freeform", "bolText", "text", (lexer.has("newline") ? {type: "newline"} : newline)], "postprocess": (data) => {
102
+ return data[0]+data[1]+data[2]+'\n'
103
+ }
104
+ },
105
+ {"name": "freeform", "symbols": ["freeform", (lexer.has("emptyLine") ? {type: "emptyLine"} : emptyLine)], "postprocess": data => data[0]+'\n'},
106
+ {"name": "_", "symbols": [], "postprocess": data => ''},
107
+ {"name": "_", "symbols": [(lexer.has("ws") ? {type: "ws"} : ws)], "postprocess": data => data[0].value},
108
+ {"name": "emptyLines", "symbols": [], "postprocess": data => ''},
109
+ {"name": "emptyLines", "symbols": ["emptyLines", (lexer.has("emptyLine") ? {type: "emptyLine"} : emptyLine)], "postprocess": data => data[0]+'\n'}
110
+ ]
111
+ , ParserStart: "main"
112
+ }
113
+ if (typeof module !== 'undefined'&& typeof module.exports !== 'undefined') {
114
+ module.exports = grammar;
115
+ } else {
116
+ window.grammar = grammar;
117
+ }
118
+ })();
package/src/index.js ADDED
@@ -0,0 +1,65 @@
1
+ import _ from 'lodash/fp.js';
2
+ import { addStepDefinition, findStepDefinitionMatch } from './steps.js';
3
+ import { parameterizeText } from './parameterize.js';
4
+ import { generateFeature } from './generate/index.js';
5
+ import { log, setLogLevel } from './logger.js';
6
+ import { parse } from './parse.js';
7
+
8
+ const featureRegex = /\.feature$/;
9
+
10
+ const compileFeatureToJS = (config,featureSrc) => {
11
+ const feature = parse(featureSrc);
12
+
13
+ const code = generateFeature(config,feature);
14
+
15
+ log.debug(code);
16
+
17
+ return code;
18
+ }
19
+
20
+ export const Given = addStepDefinition;
21
+ export const When = addStepDefinition;
22
+ export const Then = addStepDefinition;
23
+
24
+ export const Test = (state,step) => {
25
+ log.debug('Test step: '+JSON.stringify(step)+' state:'+JSON.stringify(state));
26
+ const stepDefinitionMatch = findStepDefinitionMatch(step);
27
+
28
+ const newState = stepDefinitionMatch.stepDefinition.f(state,stepDefinitionMatch.parameters,step.dataTable);
29
+ log.debug('Test newState: '+JSON.stringify(newState));
30
+
31
+ return newState;
32
+ };
33
+
34
+ export const DataTable = (dataTable) => {
35
+ const parameters = _.first(dataTable);
36
+ const rows = _.tail(dataTable);
37
+
38
+ return _.map((row) => _.zipObject(parameters,row))(rows);
39
+ }
40
+
41
+ export default function vitestCucumberPlugin() {
42
+ let config;
43
+
44
+ return {
45
+ name : 'vitest-cucumber-transform',
46
+ configResolved : (resolvedConfig) => {
47
+ if (_.has('test.cucumber.logLevel',resolvedConfig)) {
48
+ setLogLevel(resolvedConfig.test.cucumber.logLevel);
49
+ }
50
+ log.debug('config: resolvedConfig:'+JSON.stringify(resolvedConfig,null,2));
51
+ config = _.get('test.cucumber',resolvedConfig);
52
+ config = _.set('root',resolvedConfig.root,config);
53
+ log.debug('config: '+JSON.stringify(config));
54
+ },
55
+ transform : async (src,id) => {
56
+ if (featureRegex.test(id)) {
57
+ const code = compileFeatureToJS(config,src);
58
+
59
+ return {
60
+ code
61
+ }
62
+ }
63
+ }
64
+ }
65
+ }
package/src/logger.js ADDED
@@ -0,0 +1,7 @@
1
+ import pino from 'pino';
2
+
3
+ export const log = pino();
4
+
5
+ log.level = 'warn';
6
+
7
+ export const setLogLevel = (logLevel) => { log.level = logLevel };
@@ -0,0 +1,7 @@
1
+ import _ from 'lodash/fp.js';
2
+
3
+ export const parameterizeText = (text,parameterMap) => {
4
+ return _.reduce((text,parameter) => {
5
+ return text.replaceAll('<'+parameter+'>',parameterMap[parameter]);
6
+ },text)(_.keys(parameterMap));
7
+ };
package/src/parse.js ADDED
@@ -0,0 +1,21 @@
1
+ import nearley from 'nearley';
2
+ import gherkin from './gherkin.js';
3
+ import { log } from './logger.js';
4
+
5
+ export const parse = (src) => {
6
+ const parser = new nearley.Parser(nearley.Grammar.fromCompiled(gherkin));
7
+
8
+ log.debug('parsing src: '+src);
9
+ parser.feed(src);
10
+
11
+ if (parser.results.length == 0) {
12
+ throw new Error('Unexpected end of file');
13
+ }
14
+ log.debug('parsing result: '+JSON.stringify(parser.results,null,2));
15
+ if (parser.results.length > 1) {
16
+ throw new Error('Ambiguous parsing: '+parser.results.length);
17
+ }
18
+
19
+ const results = parser.results;
20
+ return results[0];
21
+ }
package/src/steps.js ADDED
@@ -0,0 +1,42 @@
1
+ import { ExpressionFactory, ParameterTypeRegistry } from '@cucumber/cucumber-expressions';
2
+ import _ from 'lodash/fp.js';
3
+ import { log } from './logger.js';
4
+
5
+ var steps = [];
6
+
7
+ const expressionFactory = new ExpressionFactory(new ParameterTypeRegistry());
8
+
9
+ export const addStepDefinition = (expression,f) => {
10
+ log.debug('addStepDefinition expression: '+JSON.stringify(expression));
11
+ const cucumberExpression = expressionFactory.createExpression(expression);
12
+ steps = _.concat(steps,{ expression, f, cucumberExpression });
13
+ }
14
+
15
+ const findStepDefinitionMatches = (step) => {
16
+ const matchesMapper = _.map((match) => match.getValue());
17
+ const reducer = _.reduce((accumulator,stepDefinition) => {
18
+ const matches = stepDefinition.cucumberExpression.match(step.text);
19
+ if (matches) {
20
+ //console.log(accumulator,stepDefinition,matches);
21
+ return _.concat(accumulator,{ stepDefinition, parameters : matchesMapper(matches) });
22
+ } else {
23
+ return accumulator;
24
+ }
25
+ });
26
+
27
+ return reducer([])(steps);
28
+ };
29
+
30
+ export const findStepDefinitionMatch = (step) => {
31
+ const stepDefinitionMatches = findStepDefinitionMatches(step);
32
+
33
+ if (!stepDefinitionMatches || (stepDefinitionMatches.length == 0)) {
34
+ throw new Error('Step not defined: \''+step.type.name+' '+step.text+'\'');
35
+ }
36
+
37
+ if (stepDefinitionMatches.length > 1) {
38
+ throw new Error('More than one step which matches: \''+step.type.name+' '+step.text+'\'');
39
+ }
40
+
41
+ return stepDefinitionMatches[0];
42
+ };
@@ -0,0 +1,25 @@
1
+ Feature: The Background keyword behaviors
2
+ The Background keyword allow you to add context to scenarios that follow it.
3
+
4
+ # This is the background.
5
+ Background: Set up some background data
6
+ It's the cool thing to do.
7
+
8
+ Given I have a "orange"
9
+ And I have a "grape"
10
+
11
+ Scenario: What's the scenario?
12
+ Given I have a "apple"
13
+ When I get a "pea"
14
+ Then I have the following items:
15
+ | orange |
16
+ | grape |
17
+ | apple |
18
+ | pea |
19
+
20
+ Scenario: It's another scenario!
21
+ When I get a "pea"
22
+ Then I have the following items:
23
+ | orange |
24
+ | grape |
25
+ | pea |
@@ -0,0 +1,30 @@
1
+ import { Given, When, Then, DataTable } from 'vitest-cucumber-plugin';
2
+ import { expect } from 'vitest'
3
+ import _ from 'lodash/fp';
4
+
5
+ const addItem = (state,[ item ],data) => { return { items : _.concat(state.items ? state.items : [],item) } };
6
+
7
+ Given('I have a {string}',addItem);
8
+ Then('I get a {string}',addItem);
9
+ Then('I have the following items:',(state,params,data) => {
10
+ const items = _.flatten(data);
11
+ expect(state.items).toEqual(items);
12
+ return state;
13
+ });
14
+ Then('I don\'t have a {string}',(state,[ item ],data) => {
15
+ expect(state.items).not.toContain(item);
16
+ return state;
17
+ });
18
+
19
+ Given('there are {int} cucumbers',(state,[ count ],data) => {
20
+ return _.set('cucumbers',count,state);
21
+ });
22
+
23
+ When('I eat {int} cucumbers',(state,[ count ],data) => {
24
+ return _.set('cucumbers',state.cucumbers - count,state);
25
+ });
26
+
27
+ Then('I should have {int} cucumbers',(state,[ count ],data) => {
28
+ expect(state.cucumbers).toEqual(count);
29
+ return state;
30
+ });