@step-forge/step-forge 0.0.7-beta.7 → 0.0.8

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 (69) hide show
  1. package/.claude/settings.local.json +17 -0
  2. package/.eslintignore +6 -0
  3. package/.eslintrc +18 -0
  4. package/.prettierignore +6 -0
  5. package/.prettierrc +15 -0
  6. package/CHANGELOG.md +28 -0
  7. package/CLAUDE.md +115 -0
  8. package/README.md +45 -39
  9. package/cucumber.mjs +39 -0
  10. package/dist/step-forge.cjs +1 -1
  11. package/dist/step-forge.js +143 -120
  12. package/dist/types/src/builderTypeUtils.d.ts +14 -0
  13. package/dist/types/src/common.d.ts +32 -0
  14. package/dist/types/src/given.d.ts +73 -0
  15. package/dist/types/src/index.d.ts +5 -0
  16. package/dist/types/src/then.d.ts +73 -0
  17. package/dist/types/src/utils.d.ts +5 -0
  18. package/dist/types/src/when.d.ts +72 -0
  19. package/dist/types/src/world.d.ts +21 -0
  20. package/docs/assets/state_deps.gif +0 -0
  21. package/dts-bundle-generator.config.cjs +19 -0
  22. package/features/TESTING.md +100 -0
  23. package/features/analyzer/analyzer.feature +83 -0
  24. package/features/analyzer/fixtures/ambiguous-step.feature +5 -0
  25. package/features/analyzer/fixtures/missing-given-dep.feature +5 -0
  26. package/features/analyzer/fixtures/missing-multiple-deps.feature +6 -0
  27. package/features/analyzer/fixtures/missing-when-dep.feature +5 -0
  28. package/features/analyzer/fixtures/steps.ts +113 -0
  29. package/features/analyzer/fixtures/undefined-step.feature +5 -0
  30. package/features/analyzer/fixtures/undefined-with-missing-dep.feature +5 -0
  31. package/features/analyzer/fixtures/valid-and-but-keywords.feature +8 -0
  32. package/features/analyzer/fixtures/valid-deps.feature +6 -0
  33. package/features/analyzer/fixtures/valid-no-deps.feature +6 -0
  34. package/features/analyzer/fixtures/valid-optional-deps.feature +6 -0
  35. package/features/analyzer/fixtures/valid-variables.feature +6 -0
  36. package/features/basic.feature +21 -0
  37. package/features/exported.feature +6 -0
  38. package/features/steps/analyzerSteps.ts +53 -0
  39. package/features/steps/commonSteps.ts +93 -0
  40. package/features/steps/exportedSteps.ts +42 -0
  41. package/features/steps/world.ts +29 -0
  42. package/package.json +41 -26
  43. package/rolldown.config.mjs +45 -0
  44. package/src/analyzer/cli.ts +96 -0
  45. package/src/analyzer/gherkinParser.ts +175 -0
  46. package/src/analyzer/index.ts +89 -0
  47. package/src/analyzer/rules/ambiguousStepRule.ts +36 -0
  48. package/src/analyzer/rules/dependencyRule.ts +71 -0
  49. package/src/analyzer/rules/index.ts +23 -0
  50. package/src/analyzer/rules/undefinedStepRule.ts +31 -0
  51. package/src/analyzer/stepExtractor.ts +432 -0
  52. package/src/analyzer/stepMatcher.ts +85 -0
  53. package/src/analyzer/types.ts +55 -0
  54. package/src/builderTypeUtils.ts +27 -0
  55. package/src/common.ts +138 -0
  56. package/src/given.ts +102 -0
  57. package/src/index.ts +46 -0
  58. package/src/then.ts +128 -0
  59. package/src/utils.ts +74 -0
  60. package/src/when.ts +118 -0
  61. package/src/world.ts +90 -0
  62. package/test/givenCompilationTests.ts +130 -0
  63. package/test/testUtils.ts +30 -0
  64. package/test/thenCompilationTests.ts +207 -0
  65. package/test/whenCompilationTests.ts +157 -0
  66. package/ts-node-esm-register.js +8 -0
  67. package/tsconfig.cucumber.json +14 -0
  68. package/tsconfig.json +29 -0
  69. package/dist/types/index.d.ts +0 -301
@@ -0,0 +1,17 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(NODE_ENV=test NODE_OPTIONS=\"--experimental-specifier-resolution=node --loader ts-node/esm\" TS_NODE_PROJECT=./tsconfig.cucumber.json cucumber-js:*)",
5
+ "Bash(NODE_ENV=test NODE_OPTIONS=\"--experimental-specifier-resolution=node --loader ts-node/esm\" TS_NODE_PROJECT=./tsconfig.cucumber.json npx cucumber-js:*)",
6
+ "Bash(npm run test:cucumber:*)",
7
+ "Bash(npx tsc:*)",
8
+ "Bash(npm run build:*)",
9
+ "Bash(git stash:*)",
10
+ "Bash(npm install:*)",
11
+ "Bash(npm run test:debug:*)",
12
+ "Bash(npm test:*)",
13
+ "Bash(git -C /Users/ellisande/dev/step-forge log --oneline -10)",
14
+ "Bash(git -C /Users/ellisande/dev/step-forge branch:*)"
15
+ ]
16
+ }
17
+ }
package/.eslintignore ADDED
@@ -0,0 +1,6 @@
1
+ .history
2
+ .husky
3
+ .vscode
4
+ coverage
5
+ dist
6
+ node_modules
package/.eslintrc ADDED
@@ -0,0 +1,18 @@
1
+ {
2
+ "root": true,
3
+ "parser": "@typescript-eslint/parser",
4
+ "plugins": ["@typescript-eslint", "prettier"],
5
+ "extends": [
6
+ "eslint:recommended",
7
+ "plugin:@typescript-eslint/eslint-recommended",
8
+ "plugin:@typescript-eslint/recommended",
9
+ "prettier"
10
+ ],
11
+ "env": {
12
+ "browser": true,
13
+ "node": true
14
+ },
15
+ "rules": {
16
+ "prettier/prettier": "error"
17
+ }
18
+ }
@@ -0,0 +1,6 @@
1
+ .history
2
+ .husky
3
+ .vscode
4
+ coverage
5
+ dist
6
+ node_modules
package/.prettierrc ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "printWidth": 80,
3
+ "tabWidth": 2,
4
+ "singleQuote": false,
5
+ "trailingComma": "es5",
6
+ "arrowParens": "avoid",
7
+ "bracketSpacing": true,
8
+ "useTabs": false,
9
+ "endOfLine": "auto",
10
+ "singleAttributePerLine": false,
11
+ "bracketSameLine": false,
12
+ "jsxSingleQuote": false,
13
+ "quoteProps": "as-needed",
14
+ "semi": true
15
+ }
package/CHANGELOG.md ADDED
@@ -0,0 +1,28 @@
1
+ # Change Log
2
+
3
+ All notable changes to the "@step-forge/step-forge" project will be documented in this file.
4
+
5
+ ## [0.0.5] - 2024-12-31
6
+
7
+ - Add support for exporting steps
8
+
9
+ ## [0.0.4] - 2024-07-30
10
+
11
+ - Update all packages to the latest versions
12
+ - Remove support for CommonJS modules
13
+
14
+ ## [0.0.3] - 2024-01-23
15
+
16
+ - Update all packages to the latest versions (update to vite 5.x)
17
+
18
+ ## [0.0.2] - 2023-01-18
19
+
20
+ - Update all packages to the latest versions (update to vite 4.x)
21
+
22
+ ## [0.0.1] - 2022-09-08
23
+
24
+ - Update all packages to the latest versions (update to vite 3.x)
25
+
26
+ ## [0.0.0] - 2022-03-28
27
+
28
+ - Initial release
package/CLAUDE.md ADDED
@@ -0,0 +1,115 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Commands
6
+
7
+ ```bash
8
+ npm test # Run all tests (Cucumber.js, suppresses stderr)
9
+ npm run test:debug # Run all tests with full output (use when debugging failures)
10
+ npm run test:cucumber # Run with default Cucumber profile
11
+ npm run test:ci # Run with CI profile
12
+ npm run build # Full build: clean → tsc → rolldown → dts-bundle-generator → copy
13
+ npm run lint # ESLint
14
+ npm run format # Prettier
15
+ ```
16
+
17
+ ## Architecture
18
+
19
+ Step Forge is a TypeScript library that wraps Cucumber.js with a type-safe builder pattern for step definitions.
20
+
21
+ ### Builder Chain
22
+
23
+ Each Gherkin phase (given/when/then) has a builder that follows this chain:
24
+
25
+ ```
26
+ builder<State>().statement(str | fn) → .dependencies?(deps) → .step(fn) → .register()
27
+ ```
28
+
29
+ - **Statement**: A string or function. Functions define variables via parameters: `(name: string) => \`a user named ${name}\`` — each parameter becomes a `{string}` placeholder in the Gherkin expression.
30
+ - **Dependencies**: Declare which keys from other phases' state this step needs. Keys are marked `"required"` or `"optional"`. Required deps are validated at runtime; optional ones may be `undefined`.
31
+ - **Step function**: Receives `{ variables, given, when, then }` — only the phases allowed by the builder type are accessible (given steps can't access when/then state).
32
+ - **Register**: Calls the corresponding Cucumber.js `Given`/`When`/`Then` to wire everything up.
33
+
34
+ ### Phase Restrictions
35
+
36
+ - `givenBuilder` — dependencies on `given` only, returns `Partial<GivenState>`
37
+ - `whenBuilder` — dependencies on `given` and `when`, returns `Partial<WhenState>`
38
+ - `thenBuilder` — dependencies on all three phases, returns `Partial<ThenState> | void`
39
+
40
+ ### Key Source Files
41
+
42
+ - `src/common.ts` — `addStep()`: core registration that wires parsers, dependency validation, and state merging into Cucumber
43
+ - `src/given.ts`, `src/when.ts`, `src/then.ts` — Builder implementations with phase-specific type constraints
44
+ - `src/world.ts` — `BasicWorld<Given, When, Then>` with `MergeableWorldState` (lodash deep merge, arrays concatenate)
45
+ - `src/builderTypeUtils.ts` — TypeScript utility types driving the builder's type safety
46
+ - `src/utils.ts` — `typeCoercer()` for string→typed conversion, `requireFrom{Given,When,Then}()` for runtime dep validation
47
+
48
+ ### Testing
49
+
50
+ Tests use Cucumber.js itself (self-testing). Feature files in `features/` with step definitions in `features/steps/` exercise the library. Type-safety tests in `test/` use `@ts-expect-error` annotations — they validate at `tsc` compile time, not at runtime.
51
+
52
+ #### Test Scripts
53
+
54
+ | Script | Profile | Description |
55
+ |---|---|---|
56
+ | `npm test` | `all` | Run all tests. Suppresses stderr (`2> /dev/null`) for clean output. Use this for normal development. |
57
+ | `npm run test:debug` | `all` | Run all tests with full output (stderr included). Use this when a test fails and you need stack traces or error details. |
58
+ | `npm run test:cucumber` | `default` | Run tests using the `default` Cucumber profile. Same paths as `all`, but does not set `PORT` or `LOG_LEVEL`. |
59
+ | `npm run test:ci` | `ci` | CI-oriented profile. Does not import `src/**/*.ts` (only step defs). Enables `publish`. |
60
+
61
+ All profiles run with `parallel: 1` and use `tsx` as the TypeScript loader via `NODE_OPTIONS='--import tsx'`.
62
+
63
+ There is no way to run a single test file. To run a subset, use Cucumber tags or modify the feature files temporarily.
64
+
65
+ #### Analyzer Tests
66
+
67
+ The analyzer has its own test suite under `features/analyzer/` that tests the `analyze()` API against fixture files. The fixtures are **data** — they are read by the analyzer's extractor and parser, not executed by Cucumber.
68
+
69
+ ```
70
+ features/analyzer/
71
+ analyzer.feature ← Test scenarios (run by Cucumber)
72
+ fixtures/
73
+ steps.ts ← Fixture step definitions (read by extractor as data)
74
+ valid-no-deps.feature ← Fixture feature files (read by parser as data)
75
+ valid-deps.feature
76
+ missing-given-dep.feature
77
+ ...
78
+ ```
79
+
80
+ The `cucumber.mjs` config uses non-recursive path globs (`./features/*.feature`, `./features/analyzer/*.feature`) to ensure fixture files in `features/analyzer/fixtures/` are never executed as tests.
81
+
82
+ Step definitions in `features/steps/analyzerSteps.ts` provide these steps:
83
+
84
+ - `Given step definitions from {string}` — sets the fixture step file (relative to `fixtures/`)
85
+ - `Given a feature file {string}` — sets the fixture feature file (relative to `fixtures/`)
86
+ - `When I analyze the files` — calls `analyze()` with the configured files
87
+ - `Then there should be no errors` — asserts zero errors
88
+ - `Then there should be {int} error/errors` — asserts exact error count
89
+ - `Then an error should mention {string}` — asserts an error message contains the substring
90
+
91
+ #### Adding New Analyzer Tests
92
+
93
+ 1. **If you need new step definition patterns**, add builder calls to `features/analyzer/fixtures/steps.ts`. This file is only parsed by the TypeScript AST extractor — it is never executed, but it must be valid TypeScript that compiles.
94
+
95
+ 2. **Create a fixture feature file** in `features/analyzer/fixtures/` that uses the step expressions defined in `steps.ts`. This file is parsed by the Gherkin parser as data — Cucumber never runs it.
96
+
97
+ 3. **Add a scenario** to `features/analyzer/analyzer.feature`:
98
+ ```gherkin
99
+ Scenario: Description of what you're testing
100
+ Given step definitions from "steps.ts"
101
+ Given a feature file "your-new-fixture.feature"
102
+ When I analyze the files
103
+ Then there should be no errors
104
+ ```
105
+ The Background already provides `step definitions from "steps.ts"`, so you only need the `Given a feature file` line in each scenario.
106
+
107
+ 4. **Run `npm run test:debug`** to verify. Use `test:debug` instead of `npm test` so you can see error details if something fails.
108
+
109
+ ### Build Output
110
+
111
+ Vite produces ESM (`dist/step-forge.js`) and `dts-bundle-generator` creates a single `dist/index.d.ts`. The `build/` directory contains the publishable package.
112
+
113
+ ## Exports
114
+
115
+ `givenBuilder`, `whenBuilder`, `thenBuilder`, `BasicWorld` — all from `src/index.ts`.
package/README.md CHANGED
@@ -2,14 +2,16 @@
2
2
 
3
3
  Step Forge is a typed wrapper around the Cucumber library. It provides an opinionated way to define steps, work with scenario state, and enforce dependencies between steps.
4
4
 
5
+ This is just a primer, see the [official documentation site](https://step-forge.com) for more information.
6
+
5
7
  ## Installation
6
8
 
7
9
  ```bash
8
10
  npm install @step-forge/step-forge
9
11
  ```
10
12
 
11
-
12
13
  ## Getting Started
14
+
13
15
  ### Setting Up World State
14
16
 
15
17
  ```ts
@@ -53,6 +55,7 @@ givenBuilder<GivenState>()
53
55
  ```
54
56
 
55
57
  This creates a cucumber step that conceptually looks like this:
58
+
56
59
  ```ts
57
60
  Given("a user", function (this: World) {
58
61
  this.given.merge({
@@ -77,7 +80,7 @@ type GivenState = {
77
80
 
78
81
  givenBuilder<GivenState>()
79
82
  .statement((userName: string) => `a user named ${userName}`)
80
- .step(({ variables: [ userName ] }) => {
83
+ .step(({ variables: [userName] }) => {
81
84
  return {
82
85
  user: userName,
83
86
  };
@@ -90,6 +93,7 @@ Here you can see that you can pass a template function to the statement that wil
90
93
  > Note: Right now only `string` variables are supported, but we're working to improve this.
91
94
 
92
95
  This creates a cucumber step that conceptually looks like this:
96
+
93
97
  ```ts
94
98
  Given("a user named {name}", function (this: World, name: string) {
95
99
  this.given.merge({
@@ -111,15 +115,15 @@ type GivenState = {
111
115
  user: {
112
116
  name: string;
113
117
  realName: string;
114
- }
118
+ };
115
119
  };
116
120
 
117
121
  givenBuilder<GivenState>()
118
122
  .statement((userName: string) => `a user named ${userName}`)
119
123
  // Since this is a given step, only allow dependencies on given state
120
124
  // Strongly typed to ensure you can't depend on something not in given state
121
- .dependencies({ given: { realName: "required"}})
122
- .step(({ variables: [ userName ], given: { realName } }) => {
125
+ .dependencies({ given: { realName: "required" } })
126
+ .step(({ variables: [userName], given: { realName } }) => {
123
127
  return {
124
128
  user: {
125
129
  name: userName,
@@ -134,6 +138,7 @@ givenBuilder<GivenState>()
134
138
  Now this is getting more interesting. We've added a dependency on the `realName` part of given state. Step Forge strongly types dependencies based on your world state, and properly restricts access based on the step type. Additionally, since `realName` is required Step Forge will fail the scenario if it doesn't exist when the step is run.
135
139
 
136
140
  This creates a cucumber step that conceptually looks like this:
141
+
137
142
  ```ts
138
143
  Given("a user named {name}", function (this: World, name: string) {
139
144
  if (!this.given.realName) {
@@ -174,34 +179,38 @@ type GivenState = {
174
179
  user: {
175
180
  name: string;
176
181
  realName: string;
177
- }
178
- }
182
+ };
183
+ };
179
184
 
180
185
  type WhenState = {
181
186
  user: {
182
187
  name: string;
183
188
  realName: string;
184
- }
185
- }
189
+ };
190
+ };
186
191
 
187
- type ThenState = {}
192
+ type ThenState = {};
188
193
 
189
194
  thenBuilder<GivenState, WhenState, ThenState>()
190
195
  .statement("the user's real name should not change")
191
196
  .dependencies({
192
197
  given: { realName: "required" },
193
- when: { user: "required" }
194
- })
195
- .step(({ given: { realName: expectedRealName }, when: { user: updatedUser } }) => {
196
- expect(updatedUser.realName).toEqual(expectedRealName);
197
- // Then steps can return void if they are not adding anything to `then` state
198
+ when: { user: "required" },
198
199
  })
200
+ .step(
201
+ ({
202
+ given: { realName: expectedRealName },
203
+ when: { user: updatedUser },
204
+ }) => {
205
+ expect(updatedUser.realName).toEqual(expectedRealName);
206
+ // Then steps can return void if they are not adding anything to `then` state
207
+ }
208
+ )
199
209
  .register();
200
210
  ```
201
211
 
202
212
  Even though we have complex dependencies across multiple parts of state, Step Forge ensures that the dependencies are properly enforced. It also provides type safety ensuring that the step can't depend on something from state that isn't explicitly declared.
203
213
 
204
-
205
214
  ### Simpler Step Definitions
206
215
 
207
216
  > Note: Defining steps this way currently doesn't work with the vs code extension. We're working to improve this.
@@ -224,15 +233,15 @@ import { Given } from "./common";
224
233
 
225
234
  // You can now start your definition with the statement directly, without having to provide GivenState or chain through the builder.
226
235
  Given("a user")
227
- .dependencies({ given: { realName: "required" } })
228
- .step(({ given: { realName } }) => {
229
- return {
230
- user: {
231
- realName,
232
- },
233
- };
234
- })
235
- .register();
236
+ .dependencies({ given: { realName: "required" } })
237
+ .step(({ given: { realName } }) => {
238
+ return {
239
+ user: {
240
+ realName,
241
+ },
242
+ };
243
+ })
244
+ .register();
236
245
  ```
237
246
 
238
247
  ### Compile Time Gherkin Analysis
@@ -251,21 +260,21 @@ type GivenState = {
251
260
  name: string;
252
261
  user: {
253
262
  name: string;
254
- }
255
- }
263
+ };
264
+ };
256
265
 
257
266
  Given((name: string) => `a user named ${name}`)
258
- .step(({ variables: [ name ] }) => {
259
- return { name }
260
- })
261
- .register();
267
+ .step(({ variables: [name] }) => {
268
+ return { name };
269
+ })
270
+ .register();
262
271
 
263
272
  When("I save the user")
264
- .dependencies({ given: { user: "required" } })
265
- .step(({ given: { user } }) => {
266
- // save the user
267
- })
268
- .register();
273
+ .dependencies({ given: { user: "required" } })
274
+ .step(({ given: { user } }) => {
275
+ // save the user
276
+ })
277
+ .register();
269
278
  ```
270
279
 
271
280
  ```gherkin
@@ -284,6 +293,3 @@ Feature: Updating a user's username
284
293
  Additionally when adding a step to a feature file, if the step has unfulfilled dependencies, the extension will show a list of steps that can be used to fulfill the dependencies.
285
294
 
286
295
  ![Step Forge Autocomplete Demo](./docs/assets/state_deps.gif)
287
-
288
-
289
-
package/cucumber.mjs ADDED
@@ -0,0 +1,39 @@
1
+ const defaultProfile = {
2
+ format: [
3
+ process.env.CI || !process.stdout.isTTY ? "progress" : "progress-bar",
4
+ "json:./reports/cucumber-json-reports/report.json",
5
+ "rerun:./reports/cucumber/@rerun.txt",
6
+ "usage:./reports/cucumber/usage.txt",
7
+ ],
8
+ parallel: 1,
9
+ paths: [
10
+ "./features/*.feature",
11
+ "./features/analyzer/*.feature",
12
+ ],
13
+ import: [
14
+ "./features/steps/**/*.ts",
15
+ "./features/steps/*.ts",
16
+ "./src/**/*.ts",
17
+ ],
18
+ strict: false,
19
+ };
20
+
21
+ const ciProfile = {
22
+ format: [
23
+ process.env.CI || !process.stdout.isTTY ? "progress" : "progress-bar",
24
+ "json:./reports/cucumber-json-reports/report.json",
25
+ "rerun:./reports/cucumber/@rerun.txt",
26
+ "usage:./reports/cucumber/usage.txt",
27
+ ],
28
+ parallel: 1,
29
+ import: ["./features/steps/**/*.ts", "./features/steps/*.ts"],
30
+ strict: false,
31
+ publish: true,
32
+ };
33
+
34
+ const all = {
35
+ ...defaultProfile,
36
+ };
37
+
38
+ export { ciProfile as ci, all };
39
+ export default defaultProfile;
@@ -1 +1 @@
1
- "use strict";var M=Object.defineProperty;var $=(e,n,t)=>n in e?M(e,n,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[n]=t;var h=(e,n,t)=>$(e,typeof n!="symbol"?n+"":n,t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("@cucumber/cucumber"),z=require("lodash"),j=e=>e&&e.__esModule?e:{default:e},c=j(z),d=e=>typeof e=="string",A=(e,n)=>(e.forEach(t=>{if(!n.given[t])throw new Error(`Key ${String(t)} is required in given state`)}),e.reduce((t,r)=>({...t,[r]:n.given[r]}),{})),C=(e,n)=>(e.forEach(t=>{if(!n.when[t])throw new Error(`Key ${String(t)} is required in when state`)}),e.reduce((t,r)=>({...t,[r]:n.when[r]}),{})),P=(e,n)=>(e.forEach(t=>{if(!n.then[t])throw new Error(`Key ${String(t)} is required in then state`)}),e.reduce((t,r)=>({...t,[r]:n.then[r]}),{})),N=e=>{const n=parseInt(e,10);return isNaN(n)?e==="true"?!0:e==="false"?!1:e:n},I={given:o.Given,when:o.When,then:o.Then},u=(e,n,t={given:{},when:{},then:{}})=>r=>{const s=e,{given:g,when:l,then:f}=t;return{statement:e,dependencies:t,stepType:n,stepFunction:r,register:()=>{const w=s.length,v=Array.from({length:w},()=>"{string}"),m=s(...v),S=Object.defineProperty(async function(...q){const D=q.map(N),F=Object.entries(g??{}).filter(([,i])=>i==="required").map(([i])=>i),O=A(F,this),b={...c.default.pick(this.given,Object.keys(g??{})),...O},B=Object.entries(l??{}).filter(([,i])=>i==="required").map(([i])=>i),E=C(B,this),W={...c.default.pick(this.when,Object.keys(l??{})),...E},_=Object.entries(f??{}).filter(([,i])=>i==="required").map(([i])=>i),y=P(_,this),K={...c.default.pick(this.then,Object.keys(f??{})),...y},G=await r({variables:D,given:b,when:W,then:K});this[n].merge({...G})},"length",{value:w,configurable:!0}),p=I[n];return p(m,S),{stepType:n,dependencies:t,statement:s,stepFunction:r}}}},x=(e,n)=>t=>{const r={...t,when:{},then:{}};return{step:u(e,n,r)}},H=e=>n=>{let t;d(n)?t=()=>n:t=n;const r=x(t,e),s=u(t,e);return{dependencies:r,step:s}},J=()=>({statement:H("given")}),L=(e,n)=>t=>{const r={given:t.given??{},when:t.when??{},then:{}};return{step:u(e,n,r)}},Q=e=>n=>{let t;d(n)?t=()=>n:t=n;const r=L(t,e),s=u(t,e);return{dependencies:r,step:s}},R=()=>({statement:Q("when")}),T=(e,n)=>t=>{const r={given:t.given??{},when:t.when??{},then:t.then??{}};return{step:u(e,n,r)}},U=e=>n=>{let t;d(n)?t=()=>n:t=n;const r=T(t,e),s=u(t,e);return{dependencies:r,step:s}},X=()=>({statement:U("then")});function a(e,n){if(c.default.isArray(e))return e.concat(n);if(e&&!c.default.isPlainObject(e)&&e!==n)throw new Error(`Merge would have destroyed previous value ${e} with ${n}`);return e}class Y{constructor(){h(this,"givenState",{});h(this,"whenState",{});h(this,"thenState",{})}get given(){return{...this.givenState,merge:n=>{this.givenState=c.default.merge({...this.givenState},n,a)}}}get when(){return{...this.whenState,merge:n=>{this.whenState=c.default.merge({...this.whenState},n,a)}}}get then(){return{...this.thenState,merge:n=>{this.thenState=c.default.merge({...this.thenState},n,a)}}}}exports.BasicWorld=Y;exports.givenBuilder=J;exports.thenBuilder=X;exports.whenBuilder=R;
1
+ "use strict";var C=Object.defineProperty;var I=(e,n,t)=>n in e?C(e,n,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[n]=t;var u=(e,n,t)=>I(e,typeof n!="symbol"?n+"":n,t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const g=require("@cucumber/cucumber"),x=require("lodash"),H=e=>e&&e.__esModule?e:{default:e},o=H(x),f=e=>typeof e=="string",J=(e,n)=>(e.forEach(t=>{if(!n.given[t])throw new Error(`Key ${String(t)} is required in given state`)}),e.reduce((t,r)=>({...t,[r]:n.given[r]}),{})),L=(e,n)=>(e.forEach(t=>{if(!n.when[t])throw new Error(`Key ${String(t)} is required in when state`)}),e.reduce((t,r)=>({...t,[r]:n.when[r]}),{})),N=(e,n)=>(e.forEach(t=>{if(!n.then[t])throw new Error(`Key ${String(t)} is required in then state`)}),e.reduce((t,r)=>({...t,[r]:n.then[r]}),{})),q={parse:e=>e,gherkin:"{string}"},Q={parse:e=>parseInt(e,10),gherkin:"{int}"},R={parse:e=>parseFloat(e),gherkin:"{double}"},U={parse:e=>e==="true",gherkin:"{string}"},X={given:g.Given,when:g.When,then:g.Then},c=(e,n,t={given:{},when:{},then:{}},r)=>i=>{const h=e,{given:v,when:w,then:m}=t;return{statement:e,dependencies:t,stepType:n,stepFunction:i,register:()=>{const a=h.length,S=r??Array.from({length:a},()=>q),D=Array.from({length:a},(p,d)=>S[d].gherkin),O=h(...D),B=Object.defineProperty(async function(...p){const d=p.filter(s=>typeof s!="function").map((s,y)=>S[y].parse(s)),E=Object.entries(v??{}).filter(([,s])=>s==="required").map(([s])=>s),W=J(E,this),K={...o.default.pick(this.given,Object.keys(v??{})),...W},G=Object.entries(w??{}).filter(([,s])=>s==="required").map(([s])=>s),M=L(G,this),$={...o.default.pick(this.when,Object.keys(w??{})),...M},z=Object.entries(m??{}).filter(([,s])=>s==="required").map(([s])=>s),A=N(z,this),j={...o.default.pick(this.then,Object.keys(m??{})),...A},k=await i({variables:d,given:K,when:$,then:j});this[n].merge({...k})},"length",{value:a,configurable:!0}),_=X[n];return _(O,B),{stepType:n,dependencies:t,statement:h,stepFunction:i}}}},P=(e,n,t)=>r=>{const i={...r,when:{},then:{}};return{step:c(e,n,i,t)}},Y=(e,n)=>t=>({dependencies:P(e,n,t),step:c(e,n,void 0,t)}),Z=e=>n=>{let t;return f(n)?t=()=>n:t=n,{dependencies:P(t,e),parsers:Y(t,e),step:c(t,e)}},V=()=>({statement:Z("given")}),F=(e,n,t)=>r=>{const i={given:r.given??{},when:r.when??{},then:{}};return{step:c(e,n,i,t)}},T=(e,n)=>t=>({dependencies:F(e,n,t),step:c(e,n,void 0,t)}),ee=e=>n=>{let t;f(n)?t=()=>n:t=n;const r=F(t,e),i=T(t,e),h=c(t,e);return{dependencies:r,parsers:i,step:h}},ne=()=>({statement:ee("when")}),b=(e,n,t)=>r=>{const i={given:r.given??{},when:r.when??{},then:r.then??{}};return{step:c(e,n,i,t)}},te=(e,n)=>t=>({dependencies:b(e,n,t),step:c(e,n,void 0,t)}),re=e=>n=>{let t;f(n)?t=()=>n:t=n;const r=b(t,e),i=c(t,e),h=te(t,e);return{dependencies:r,parsers:h,step:i}},se=()=>({statement:re("then")});function l(e,n){if(o.default.isArray(e))return e.concat(n);if(e&&!o.default.isPlainObject(e)&&e!==n)throw new Error(`Merge would have destroyed previous value ${e} with ${n}`);return e}class ie{constructor(){u(this,"givenState",{});u(this,"whenState",{});u(this,"thenState",{})}get given(){return{...this.givenState,merge:n=>{this.givenState=o.default.merge({...this.givenState},n,l)}}}get when(){return{...this.whenState,merge:n=>{this.whenState=o.default.merge({...this.whenState},n,l)}}}get then(){return{...this.thenState,merge:n=>{this.thenState=o.default.merge({...this.thenState},n,l)}}}}exports.BasicWorld=ie;exports.booleanParser=U;exports.givenBuilder=V;exports.intParser=Q;exports.numberParser=R;exports.stringParser=q;exports.thenBuilder=se;exports.whenBuilder=ne;