@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.
- package/.claude/settings.local.json +17 -0
- package/.eslintignore +6 -0
- package/.eslintrc +18 -0
- package/.prettierignore +6 -0
- package/.prettierrc +15 -0
- package/CHANGELOG.md +28 -0
- package/CLAUDE.md +115 -0
- package/README.md +45 -39
- package/cucumber.mjs +39 -0
- package/dist/step-forge.cjs +1 -1
- package/dist/step-forge.js +143 -120
- package/dist/types/src/builderTypeUtils.d.ts +14 -0
- package/dist/types/src/common.d.ts +32 -0
- package/dist/types/src/given.d.ts +73 -0
- package/dist/types/src/index.d.ts +5 -0
- package/dist/types/src/then.d.ts +73 -0
- package/dist/types/src/utils.d.ts +5 -0
- package/dist/types/src/when.d.ts +72 -0
- package/dist/types/src/world.d.ts +21 -0
- package/docs/assets/state_deps.gif +0 -0
- package/dts-bundle-generator.config.cjs +19 -0
- package/features/TESTING.md +100 -0
- package/features/analyzer/analyzer.feature +83 -0
- package/features/analyzer/fixtures/ambiguous-step.feature +5 -0
- package/features/analyzer/fixtures/missing-given-dep.feature +5 -0
- package/features/analyzer/fixtures/missing-multiple-deps.feature +6 -0
- package/features/analyzer/fixtures/missing-when-dep.feature +5 -0
- package/features/analyzer/fixtures/steps.ts +113 -0
- package/features/analyzer/fixtures/undefined-step.feature +5 -0
- package/features/analyzer/fixtures/undefined-with-missing-dep.feature +5 -0
- package/features/analyzer/fixtures/valid-and-but-keywords.feature +8 -0
- package/features/analyzer/fixtures/valid-deps.feature +6 -0
- package/features/analyzer/fixtures/valid-no-deps.feature +6 -0
- package/features/analyzer/fixtures/valid-optional-deps.feature +6 -0
- package/features/analyzer/fixtures/valid-variables.feature +6 -0
- package/features/basic.feature +21 -0
- package/features/exported.feature +6 -0
- package/features/steps/analyzerSteps.ts +53 -0
- package/features/steps/commonSteps.ts +93 -0
- package/features/steps/exportedSteps.ts +42 -0
- package/features/steps/world.ts +29 -0
- package/package.json +41 -26
- package/rolldown.config.mjs +45 -0
- package/src/analyzer/cli.ts +96 -0
- package/src/analyzer/gherkinParser.ts +175 -0
- package/src/analyzer/index.ts +89 -0
- package/src/analyzer/rules/ambiguousStepRule.ts +36 -0
- package/src/analyzer/rules/dependencyRule.ts +71 -0
- package/src/analyzer/rules/index.ts +23 -0
- package/src/analyzer/rules/undefinedStepRule.ts +31 -0
- package/src/analyzer/stepExtractor.ts +432 -0
- package/src/analyzer/stepMatcher.ts +85 -0
- package/src/analyzer/types.ts +55 -0
- package/src/builderTypeUtils.ts +27 -0
- package/src/common.ts +138 -0
- package/src/given.ts +102 -0
- package/src/index.ts +46 -0
- package/src/then.ts +128 -0
- package/src/utils.ts +74 -0
- package/src/when.ts +118 -0
- package/src/world.ts +90 -0
- package/test/givenCompilationTests.ts +130 -0
- package/test/testUtils.ts +30 -0
- package/test/thenCompilationTests.ts +207 -0
- package/test/whenCompilationTests.ts +157 -0
- package/ts-node-esm-register.js +8 -0
- package/tsconfig.cucumber.json +14 -0
- package/tsconfig.json +29 -0
- 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
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
|
+
}
|
package/.prettierignore
ADDED
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: [
|
|
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: [
|
|
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
|
-
|
|
230
|
-
|
|
231
|
-
|
|
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: [
|
|
259
|
-
|
|
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
|
-
|
|
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
|

|
|
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;
|
package/dist/step-forge.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var
|
|
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;
|