executable-stories-jest 7.0.0 → 7.0.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "executable-stories-jest",
3
- "version": "7.0.0",
3
+ "version": "7.0.2",
4
4
  "description": "BDD-style executable stories for Jest with documentation generation",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -25,11 +25,13 @@
25
25
  }
26
26
  },
27
27
  "files": [
28
- "dist"
28
+ "dist",
29
+ "skills",
30
+ "bin"
29
31
  ],
30
32
  "peerDependencies": {
31
33
  "jest": ">=30.2.0",
32
- "executable-stories-formatters": "^0.6.0"
34
+ "executable-stories-formatters": "^0.6.2"
33
35
  },
34
36
  "dependencies": {
35
37
  "fast-glob": "^3.3.3"
@@ -38,12 +40,12 @@
38
40
  "@jest/globals": "^30.2.0",
39
41
  "@opentelemetry/api": "^1.9.0",
40
42
  "@types/jest": "^30.0.0",
41
- "@types/node": "^25.3.0",
43
+ "@types/node": "^25.3.2",
42
44
  "jest": "^30.2.0",
43
45
  "ts-jest": "^29.4.6",
44
46
  "tsup": "^8.5.1",
45
47
  "typescript": "~5.9.3",
46
- "executable-stories-formatters": "0.6.0"
48
+ "executable-stories-formatters": "0.6.2"
47
49
  },
48
50
  "repository": {
49
51
  "type": "git",
@@ -57,6 +59,9 @@
57
59
  "documentation",
58
60
  "executable-stories"
59
61
  ],
62
+ "bin": {
63
+ "intent": "./bin/intent.js"
64
+ },
60
65
  "scripts": {
61
66
  "build": "tsup",
62
67
  "type-check": "tsc --noEmit",
@@ -0,0 +1,142 @@
1
+ ---
2
+ name: jest-converting-tests
3
+ description: >
4
+ Incrementally adopt executable-stories in Jest. Add story.init() and
5
+ top-level step imports to existing test blocks. No task argument needed.
6
+ File naming .story.test.ts. Progressive enhancement without rewriting.
7
+ type: lifecycle
8
+ library: executable-stories-jest
9
+ library_version: "7.0.1"
10
+ requires:
11
+ - jest-story-api
12
+ sources:
13
+ - "jagreehal/executable-stories:apps/docs-site/src/content/docs/guides/converting-jest.md"
14
+ ---
15
+
16
+ This skill builds on jest-story-api. Read jest-story-api first.
17
+
18
+ # Converting Existing Jest Tests
19
+
20
+ ## Setup
21
+
22
+ Install the packages and configure:
23
+
24
+ ```bash
25
+ npm install -D executable-stories-jest executable-stories-formatters
26
+ ```
27
+
28
+ ```javascript
29
+ // jest.config.mjs
30
+ export default {
31
+ setupFilesAfterEnv: ["executable-stories-jest/setup"],
32
+ reporters: [
33
+ "default",
34
+ ["executable-stories-jest/reporter", { formats: ["markdown"] }],
35
+ ],
36
+ };
37
+ ```
38
+
39
+ ## Core Patterns
40
+
41
+ ### Step 1: Rename the file
42
+
43
+ ```
44
+ # Before
45
+ test/calculator.test.ts
46
+
47
+ # After
48
+ test/calculator.story.test.ts
49
+ ```
50
+
51
+ ### Step 2: Add story.init() and step markers
52
+
53
+ ```typescript
54
+ // Before
55
+ import { describe, expect, it } from "@jest/globals";
56
+
57
+ describe("Calculator", () => {
58
+ it("adds two numbers", () => {
59
+ const result = add(2, 3);
60
+ expect(result).toBe(5);
61
+ });
62
+ });
63
+ ```
64
+
65
+ ```typescript
66
+ // After
67
+ import { describe, expect, it } from "@jest/globals";
68
+ import { story, given, when, then } from "executable-stories-jest";
69
+
70
+ describe("Calculator", () => {
71
+ it("adds two numbers", () => {
72
+ story.init();
73
+
74
+ given("two numbers 2 and 3");
75
+ const a = 2, b = 3;
76
+
77
+ when("they are added");
78
+ const result = add(a, b);
79
+
80
+ then("the result is 5");
81
+ expect(result).toBe(5);
82
+ });
83
+ });
84
+ ```
85
+
86
+ Key difference from Vitest: no `({ task })` needed. `story.init()` takes no arguments.
87
+
88
+ ### Step 3: Minimal story
89
+
90
+ ```typescript
91
+ it("subtracts two numbers", () => {
92
+ story.init();
93
+ expect(subtract(10, 4)).toBe(6);
94
+ });
95
+ ```
96
+
97
+ ### Step 4: Add doc entries
98
+
99
+ ```typescript
100
+ it("handles division by zero", () => {
101
+ story.init({ tags: ["edge-case"] });
102
+
103
+ given("a divisor of zero");
104
+ story.note("This tests the error handling path");
105
+
106
+ when("division is attempted");
107
+ const fn = () => divide(10, 0);
108
+
109
+ then("an error is thrown");
110
+ expect(fn).toThrow("Division by zero");
111
+ });
112
+ ```
113
+
114
+ ## Common Mistakes
115
+
116
+ ### HIGH Adding ({ task }) like Vitest
117
+
118
+ Wrong:
119
+
120
+ ```typescript
121
+ it("my test", ({ task }) => {
122
+ story.init(task);
123
+ });
124
+ ```
125
+
126
+ Correct:
127
+
128
+ ```typescript
129
+ it("my test", () => {
130
+ story.init();
131
+ });
132
+ ```
133
+
134
+ Jest does not provide a `task` object. `story.init()` reads the test name from `expect.getState().currentTestName`.
135
+
136
+ Source: packages/executable-stories-jest/src/story-api.ts
137
+
138
+ ### HIGH Forgetting setupFilesAfterEnv
139
+
140
+ Without `setupFilesAfterEnv: ["executable-stories-jest/setup"]` in jest.config, stories are never flushed to disk and the reporter produces empty output.
141
+
142
+ Source: packages/executable-stories-jest/src/reporter.ts
@@ -0,0 +1,150 @@
1
+ ---
2
+ name: jest-reporter-setup
3
+ description: >
4
+ Configure Jest custom reporter for executable-stories-jest. jest.config
5
+ reporters array with options. setupFilesAfterEnv for story flushing.
6
+ Output formats, directory, naming. Aggregated and colocated modes.
7
+ type: core
8
+ library: executable-stories-jest
9
+ library_version: "7.0.1"
10
+ sources:
11
+ - "jagreehal/executable-stories:packages/executable-stories-jest/src/reporter.ts"
12
+ ---
13
+
14
+ # executable-stories-jest — Reporter Setup
15
+
16
+ ## Setup
17
+
18
+ ```javascript
19
+ // jest.config.mjs
20
+ export default {
21
+ setupFilesAfterEnv: ["executable-stories-jest/setup"],
22
+ reporters: [
23
+ "default",
24
+ [
25
+ "executable-stories-jest/reporter",
26
+ {
27
+ formats: ["markdown", "html"],
28
+ outputDir: "docs",
29
+ outputName: "user-stories",
30
+ },
31
+ ],
32
+ ],
33
+ };
34
+ ```
35
+
36
+ Both the `setup` file and the `reporter` entry are required. Peer dependency: `executable-stories-formatters` must be installed.
37
+
38
+ ## Core Patterns
39
+
40
+ ### Minimal config
41
+
42
+ ```javascript
43
+ export default {
44
+ setupFilesAfterEnv: ["executable-stories-jest/setup"],
45
+ reporters: [
46
+ "default",
47
+ ["executable-stories-jest/reporter", { formats: ["markdown"] }],
48
+ ],
49
+ };
50
+ ```
51
+
52
+ ### Full options
53
+
54
+ ```javascript
55
+ export default {
56
+ setupFilesAfterEnv: ["executable-stories-jest/setup"],
57
+ reporters: [
58
+ "default",
59
+ [
60
+ "executable-stories-jest/reporter",
61
+ {
62
+ formats: ["markdown", "html", "junit", "cucumber-json"],
63
+ outputDir: "reports",
64
+ outputName: "test-results",
65
+ output: {
66
+ mode: "aggregated",
67
+ // mode: "colocated",
68
+ // colocatedStyle: "mirrored",
69
+ },
70
+ markdown: {
71
+ title: "User Stories",
72
+ includeStatusIcons: true,
73
+ includeErrors: true,
74
+ includeMetadata: true,
75
+ sortScenarios: "source",
76
+ ticketUrlTemplate: "https://jira.example.com/browse/{ticket}",
77
+ },
78
+ html: {
79
+ title: "Test Report",
80
+ darkMode: true,
81
+ searchable: true,
82
+ },
83
+ rawRunPath: "reports/raw-run.json",
84
+ },
85
+ ],
86
+ ],
87
+ };
88
+ ```
89
+
90
+ ### File-based communication
91
+
92
+ Jest uses worker processes. Stories are written to `.jest-executable-stories/worker-{id}/*.json` during execution. The reporter aggregates these files in `onRunComplete`. The `JEST_STORY_DOCS_DIR` env var overrides the temp directory.
93
+
94
+ ## Common Mistakes
95
+
96
+ ### CRITICAL Missing setupFilesAfterEnv entry
97
+
98
+ Wrong:
99
+
100
+ ```javascript
101
+ export default {
102
+ reporters: [
103
+ "default",
104
+ ["executable-stories-jest/reporter", { formats: ["markdown"] }],
105
+ ],
106
+ };
107
+ ```
108
+
109
+ Correct:
110
+
111
+ ```javascript
112
+ export default {
113
+ setupFilesAfterEnv: ["executable-stories-jest/setup"],
114
+ reporters: [
115
+ "default",
116
+ ["executable-stories-jest/reporter", { formats: ["markdown"] }],
117
+ ],
118
+ };
119
+ ```
120
+
121
+ The setup file registers an `afterAll` hook that flushes story metadata to disk at the end of each test file. Without it, the reporter receives no data and produces empty output.
122
+
123
+ Source: packages/executable-stories-jest/src/reporter.ts
124
+
125
+ ### HIGH Using the reporter path as a string instead of tuple
126
+
127
+ Wrong:
128
+
129
+ ```javascript
130
+ reporters: ["default", "executable-stories-jest/reporter"]
131
+ ```
132
+
133
+ Correct:
134
+
135
+ ```javascript
136
+ reporters: [
137
+ "default",
138
+ ["executable-stories-jest/reporter", { formats: ["markdown"] }],
139
+ ]
140
+ ```
141
+
142
+ Without options, the default format is `["cucumber-json"]`. Use the tuple form `[path, options]` to specify formats and output directory.
143
+
144
+ Source: packages/executable-stories-jest/src/reporter.ts
145
+
146
+ ### MEDIUM Default format is cucumber-json, not markdown
147
+
148
+ The default `formats` is `["cucumber-json"]`. Always specify `formats: ["markdown"]` (or other desired formats) explicitly.
149
+
150
+ Source: packages/executable-stories-jest/src/reporter.ts
@@ -0,0 +1,199 @@
1
+ ---
2
+ name: jest-story-api
3
+ description: >
4
+ Write BDD stories in Jest using executable-stories-jest. Top-level exports:
5
+ import { given, when, then, and, but } from executable-stories-jest.
6
+ story.init() takes no arguments. Auto-And keyword conversion. Suite path
7
+ from expect.getState().currentTestName. Aliases: arrange, act, assert.
8
+ Doc entries: json, kv, code, table, link, section, mermaid, note, tag.
9
+ type: core
10
+ library: executable-stories-jest
11
+ library_version: "7.0.1"
12
+ sources:
13
+ - "jagreehal/executable-stories:packages/executable-stories-jest/src/index.ts"
14
+ - "jagreehal/executable-stories:packages/executable-stories-jest/src/story-api.ts"
15
+ ---
16
+
17
+ # executable-stories-jest — Story API
18
+
19
+ ## Setup
20
+
21
+ ```typescript
22
+ import { describe, expect, it } from "@jest/globals";
23
+ import { story, given, when, then } from "executable-stories-jest";
24
+
25
+ describe("Cart checkout", () => {
26
+ it("applies discount code", () => {
27
+ story.init({ tags: ["checkout"], ticket: "CART-42" });
28
+
29
+ given("a cart with items totaling $100");
30
+ const cart = createCart([{ name: "Shirt", price: 100 }]);
31
+
32
+ when("a 20% discount code is applied");
33
+ applyDiscount(cart, "SAVE20");
34
+
35
+ then("the total is $80");
36
+ expect(cart.total).toBe(80);
37
+ });
38
+ });
39
+ ```
40
+
41
+ File naming: `*.story.test.ts`.
42
+
43
+ Jest uses top-level step exports. `story.init()` takes no arguments — the test name is derived from `expect.getState().currentTestName`.
44
+
45
+ ## Core Patterns
46
+
47
+ ### Top-level step exports
48
+
49
+ ```typescript
50
+ import { story, given, when, then, and, but } from "executable-stories-jest";
51
+
52
+ it("blocks suspended user login", () => {
53
+ story.init();
54
+
55
+ given("the user account exists"); // renders "Given"
56
+ given("the account is suspended"); // renders "And" (auto-converted)
57
+ when("the user submits valid credentials");
58
+ then("the user sees an error message");
59
+ but("the user is not logged in"); // renders "But" (always)
60
+ });
61
+ ```
62
+
63
+ ### Doc entries
64
+
65
+ ```typescript
66
+ import { story, given, when, then } from "executable-stories-jest";
67
+
68
+ it("processes payment", () => {
69
+ story.init();
70
+
71
+ given("a valid payment request");
72
+ story.json({ label: "Payload", value: { amount: 50, currency: "USD" } });
73
+
74
+ when("the payment is submitted");
75
+ story.code({ label: "Response", content: '{ "status": "ok" }', lang: "json" });
76
+
77
+ then("the order is confirmed");
78
+ story.table({
79
+ label: "Order summary",
80
+ columns: ["Item", "Qty", "Price"],
81
+ rows: [["Widget", "2", "$25"]],
82
+ });
83
+ story.note("Payment processed in sandbox mode");
84
+ });
85
+ ```
86
+
87
+ ### Step wrappers with timing
88
+
89
+ ```typescript
90
+ const profile = await story.fn("When", "the profile is fetched", async () => {
91
+ return fetchProfile(userId);
92
+ });
93
+
94
+ await story.expect("the profile contains the correct name", () => {
95
+ expect(profile.name).toBe("Alice");
96
+ });
97
+ ```
98
+
99
+ ### Inline docs via second argument
100
+
101
+ ```typescript
102
+ given("valid credentials", {
103
+ json: { label: "Credentials", value: { user: "alice", role: "admin" } },
104
+ note: "Password masked for security",
105
+ });
106
+ ```
107
+
108
+ ## Common Mistakes
109
+
110
+ ### CRITICAL Calling story.init() with a task argument
111
+
112
+ Wrong:
113
+
114
+ ```typescript
115
+ it("my test", ({ task }) => {
116
+ story.init(task); // Jest does not provide task
117
+ });
118
+ ```
119
+
120
+ Correct:
121
+
122
+ ```typescript
123
+ it("my test", () => {
124
+ story.init(); // No arguments needed
125
+ });
126
+ ```
127
+
128
+ Jest's `story.init()` takes no arguments. It reads the test name from `expect.getState().currentTestName`. Passing an argument is a Vitest pattern that does not apply to Jest.
129
+
130
+ Source: packages/executable-stories-jest/src/story-api.ts
131
+
132
+ ### HIGH Suite path not detected in docs
133
+
134
+ Wrong assumption:
135
+
136
+ ```typescript
137
+ // Expecting "Calculator" to appear as a ## heading in docs
138
+ describe("Calculator", () => {
139
+ it("adds numbers", () => {
140
+ story.init();
141
+ // ...
142
+ });
143
+ });
144
+ // Jest's currentTestName format is "Calculator adds numbers" (space-separated)
145
+ // Suite path is usually undefined → docs are flat
146
+ ```
147
+
148
+ This is a known limitation. Jest's `expect.getState().currentTestName` uses space-separated format (`"Describe title test name"`), not `" > "` separated. Suite path extraction only works when `" > "` is present. In the default Jest setup, docs are flat without `## Suite name` headings.
149
+
150
+ Source: CLAUDE.md — "Doc heading and describe in generated docs"
151
+
152
+ ### HIGH Missing setup file in jest.config
153
+
154
+ Wrong:
155
+
156
+ ```javascript
157
+ // jest.config.mjs
158
+ export default {
159
+ reporters: ["default", "executable-stories-jest/reporter"],
160
+ };
161
+ ```
162
+
163
+ Correct:
164
+
165
+ ```javascript
166
+ // jest.config.mjs
167
+ export default {
168
+ setupFilesAfterEnv: ["executable-stories-jest/setup"],
169
+ reporters: ["default", "executable-stories-jest/reporter"],
170
+ };
171
+ ```
172
+
173
+ The setup file registers an `afterAll` hook that flushes story metadata to disk. Without it, the reporter has no data to process.
174
+
175
+ Source: packages/executable-stories-jest/src/reporter.ts
176
+
177
+ ### MEDIUM Calling steps before story.init()
178
+
179
+ Wrong:
180
+
181
+ ```typescript
182
+ it("my test", () => {
183
+ given("something");
184
+ story.init();
185
+ });
186
+ ```
187
+
188
+ Correct:
189
+
190
+ ```typescript
191
+ it("my test", () => {
192
+ story.init();
193
+ given("something");
194
+ });
195
+ ```
196
+
197
+ Steps called before `init()` are silently dropped because no story context exists.
198
+
199
+ Source: packages/eslint-plugin-executable-stories-jest/src/rules/require-story-context-for-steps.ts