executable-stories-cypress 7.0.1 → 7.0.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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "executable-stories-cypress",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.3",
|
|
4
4
|
"description": "BDD-style executable stories for Cypress with documentation generation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -30,11 +30,13 @@
|
|
|
30
30
|
},
|
|
31
31
|
"files": [
|
|
32
32
|
"dist",
|
|
33
|
-
"
|
|
33
|
+
"skills",
|
|
34
|
+
"reporter.cjs",
|
|
35
|
+
"bin"
|
|
34
36
|
],
|
|
35
37
|
"peerDependencies": {
|
|
36
38
|
"cypress": ">=15.11.0",
|
|
37
|
-
"executable-stories-formatters": "^0.6.
|
|
39
|
+
"executable-stories-formatters": "^0.6.2"
|
|
38
40
|
},
|
|
39
41
|
"dependencies": {},
|
|
40
42
|
"devDependencies": {
|
|
@@ -43,7 +45,7 @@
|
|
|
43
45
|
"tsup": "^8.5.1",
|
|
44
46
|
"typescript": "~5.9.3",
|
|
45
47
|
"vitest": "^4.0.18",
|
|
46
|
-
"executable-stories-formatters": "0.6.
|
|
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,136 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cypress-converting-tests
|
|
3
|
+
description: >
|
|
4
|
+
Incrementally adopt executable-stories in Cypress. Add story.init() and
|
|
5
|
+
step markers to existing cy.ts spec files. File naming .story.cy.ts.
|
|
6
|
+
Requires plugin + support file wiring. Progressive enhancement.
|
|
7
|
+
type: lifecycle
|
|
8
|
+
library: executable-stories-cypress
|
|
9
|
+
library_version: "7.0.1"
|
|
10
|
+
requires:
|
|
11
|
+
- cypress-story-api
|
|
12
|
+
sources:
|
|
13
|
+
- "jagreehal/executable-stories:packages/executable-stories-cypress/src/index.ts"
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
This skill builds on cypress-story-api. Read cypress-story-api first.
|
|
17
|
+
|
|
18
|
+
# Converting Existing Cypress Tests
|
|
19
|
+
|
|
20
|
+
## Setup
|
|
21
|
+
|
|
22
|
+
Install and configure the three wiring points:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install -D executable-stories-cypress executable-stories-formatters
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
// cypress.config.ts
|
|
30
|
+
import { defineConfig } from "cypress";
|
|
31
|
+
import { registerExecutableStoriesPlugin } from "executable-stories-cypress/plugin";
|
|
32
|
+
|
|
33
|
+
export default defineConfig({
|
|
34
|
+
e2e: {
|
|
35
|
+
setupNodeEvents(on) {
|
|
36
|
+
registerExecutableStoriesPlugin(on);
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
// cypress/support/e2e.ts
|
|
44
|
+
import "executable-stories-cypress/support";
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Core Patterns
|
|
48
|
+
|
|
49
|
+
### Step 1: Rename the file
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
# Before
|
|
53
|
+
cypress/e2e/login.cy.ts
|
|
54
|
+
|
|
55
|
+
# After
|
|
56
|
+
cypress/e2e/login.story.cy.ts
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Step 2: Add story.init() and step markers
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
// Before
|
|
63
|
+
describe("Login", () => {
|
|
64
|
+
it("logs in successfully", () => {
|
|
65
|
+
cy.visit("/login");
|
|
66
|
+
cy.get("#email").type("user@example.com");
|
|
67
|
+
cy.get("#password").type("secret");
|
|
68
|
+
cy.get("#submit").click();
|
|
69
|
+
cy.get("h1").should("contain", "Dashboard");
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
// After
|
|
76
|
+
import { story } from "executable-stories-cypress";
|
|
77
|
+
|
|
78
|
+
describe("Login", () => {
|
|
79
|
+
it("logs in successfully", () => {
|
|
80
|
+
story.init();
|
|
81
|
+
|
|
82
|
+
story.given("the login page is loaded");
|
|
83
|
+
cy.visit("/login");
|
|
84
|
+
|
|
85
|
+
story.when("valid credentials are entered");
|
|
86
|
+
cy.get("#email").type("user@example.com");
|
|
87
|
+
cy.get("#password").type("secret");
|
|
88
|
+
cy.get("#submit").click();
|
|
89
|
+
|
|
90
|
+
story.then("the dashboard is shown");
|
|
91
|
+
cy.get("h1").should("contain", "Dashboard");
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Step 3: Minimal story
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
it("loads homepage", () => {
|
|
100
|
+
story.init();
|
|
101
|
+
cy.visit("/");
|
|
102
|
+
cy.title().should("eq", "My App");
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Step 4: Add doc entries
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
it("shows product details", () => {
|
|
110
|
+
story.init({ tags: ["e2e", "products"] });
|
|
111
|
+
|
|
112
|
+
story.given("the product page is loaded");
|
|
113
|
+
cy.visit("/products/123");
|
|
114
|
+
|
|
115
|
+
story.then("the product details are shown");
|
|
116
|
+
story.screenshot({ path: "screenshots/product.png", alt: "Product page" });
|
|
117
|
+
story.json({ label: "Product", value: { id: 123, name: "Widget" } });
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Common Mistakes
|
|
122
|
+
|
|
123
|
+
### HIGH Using wrong file extension
|
|
124
|
+
|
|
125
|
+
Wrong: `login.story.test.ts` or `login.story.spec.ts`
|
|
126
|
+
Correct: `login.story.cy.ts`
|
|
127
|
+
|
|
128
|
+
Cypress convention uses `.cy.ts`. The reporter and file matching expect this extension.
|
|
129
|
+
|
|
130
|
+
Source: CLAUDE.md — file naming conventions
|
|
131
|
+
|
|
132
|
+
### CRITICAL Forgetting plugin or support file
|
|
133
|
+
|
|
134
|
+
Both `registerExecutableStoriesPlugin(on)` in cypress.config.ts AND `import "executable-stories-cypress/support"` in e2e.ts are required. Missing either one causes silent failures — stories record in the browser but never reach the reporter.
|
|
135
|
+
|
|
136
|
+
Source: packages/executable-stories-cypress/src/plugin.ts, packages/executable-stories-cypress/src/support.ts
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cypress-reporter-setup
|
|
3
|
+
description: >
|
|
4
|
+
Configure Cypress reporter for executable-stories-cypress. Mocha reporter
|
|
5
|
+
via --reporter flag or cypress.config.ts. Module API for programmatic use
|
|
6
|
+
with buildRawRunFromCypressResult and generateReportsFromRawRun. Output
|
|
7
|
+
formats, directory, naming.
|
|
8
|
+
type: core
|
|
9
|
+
library: executable-stories-cypress
|
|
10
|
+
library_version: "7.0.1"
|
|
11
|
+
sources:
|
|
12
|
+
- "jagreehal/executable-stories:packages/executable-stories-cypress/src/reporter.ts"
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# executable-stories-cypress — Reporter Setup
|
|
16
|
+
|
|
17
|
+
## Setup
|
|
18
|
+
|
|
19
|
+
### Option A: Mocha reporter (CLI)
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
cypress run \
|
|
23
|
+
--reporter executable-stories-cypress/reporter \
|
|
24
|
+
--reporter-options "outputDir=docs,outputName=user-stories,formats=markdown"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Option B: Module API (programmatic)
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
// cypress.config.ts
|
|
31
|
+
import { defineConfig } from "cypress";
|
|
32
|
+
import { registerExecutableStoriesPlugin } from "executable-stories-cypress/plugin";
|
|
33
|
+
import {
|
|
34
|
+
buildRawRunFromCypressResult,
|
|
35
|
+
generateReportsFromRawRun,
|
|
36
|
+
} from "executable-stories-cypress/reporter";
|
|
37
|
+
|
|
38
|
+
export default defineConfig({
|
|
39
|
+
e2e: {
|
|
40
|
+
setupNodeEvents(on) {
|
|
41
|
+
registerExecutableStoriesPlugin(on);
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// After cypress.run() completes:
|
|
47
|
+
const result = await cypress.run();
|
|
48
|
+
const rawRun = buildRawRunFromCypressResult(result, { outputDir: "docs" });
|
|
49
|
+
await generateReportsFromRawRun(rawRun, {
|
|
50
|
+
formats: ["markdown", "html"],
|
|
51
|
+
outputDir: "docs",
|
|
52
|
+
outputName: "user-stories",
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Peer dependency: `executable-stories-formatters` must be installed.
|
|
57
|
+
|
|
58
|
+
## Core Patterns
|
|
59
|
+
|
|
60
|
+
### Mocha reporter with all options
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
cypress run \
|
|
64
|
+
--reporter executable-stories-cypress/reporter \
|
|
65
|
+
--reporter-options "outputDir=reports,outputName=test-results,formats=markdown+html+junit"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Module API for CI pipelines
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import cypress from "cypress";
|
|
72
|
+
import {
|
|
73
|
+
buildRawRunFromCypressResult,
|
|
74
|
+
generateReportsFromRawRun,
|
|
75
|
+
} from "executable-stories-cypress/reporter";
|
|
76
|
+
|
|
77
|
+
const result = await cypress.run({ spec: "cypress/e2e/**/*.story.cy.ts" });
|
|
78
|
+
|
|
79
|
+
if (result.status === "finished") {
|
|
80
|
+
const rawRun = buildRawRunFromCypressResult(result);
|
|
81
|
+
await generateReportsFromRawRun(rawRun, {
|
|
82
|
+
formats: ["markdown"],
|
|
83
|
+
outputDir: "docs",
|
|
84
|
+
outputName: "cypress-stories",
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Common Mistakes
|
|
90
|
+
|
|
91
|
+
### CRITICAL Forgetting plugin + support wiring
|
|
92
|
+
|
|
93
|
+
The reporter only works if both the plugin and support file are configured. Without them, the reporter has no story metadata to process. See cypress-story-api/SKILL.md for the required wiring.
|
|
94
|
+
|
|
95
|
+
### HIGH Using reporter without Module API in afterRun
|
|
96
|
+
|
|
97
|
+
Wrong:
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
// Expecting reporter to auto-generate reports
|
|
101
|
+
export default defineConfig({
|
|
102
|
+
e2e: {
|
|
103
|
+
setupNodeEvents(on) {
|
|
104
|
+
registerExecutableStoriesPlugin(on);
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
reporter: "executable-stories-cypress/reporter",
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The Mocha reporter approach works for CLI usage (`cypress run --reporter`). For programmatic usage with `cypress.run()`, use the Module API (`buildRawRunFromCypressResult` + `generateReportsFromRawRun`).
|
|
112
|
+
|
|
113
|
+
Source: packages/executable-stories-cypress/src/reporter.ts
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cypress-story-api
|
|
3
|
+
description: >
|
|
4
|
+
Write BDD stories in Cypress using executable-stories-cypress. story.init()
|
|
5
|
+
with optional StoryOptions. Steps: story.given, story.when, story.then,
|
|
6
|
+
story.and, story.but. Doc entries: json, kv, code, table, link, section,
|
|
7
|
+
mermaid, screenshot, note, tag. Auto-And keyword conversion. Browser-Node
|
|
8
|
+
bridge via cy.task. Aliases: arrange, act, assert.
|
|
9
|
+
type: core
|
|
10
|
+
library: executable-stories-cypress
|
|
11
|
+
library_version: "7.0.1"
|
|
12
|
+
sources:
|
|
13
|
+
- "jagreehal/executable-stories:packages/executable-stories-cypress/src/story-api.ts"
|
|
14
|
+
- "jagreehal/executable-stories:packages/executable-stories-cypress/src/index.ts"
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# executable-stories-cypress — Story API
|
|
18
|
+
|
|
19
|
+
## Setup
|
|
20
|
+
|
|
21
|
+
Three wiring steps are required:
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
// 1. cypress.config.ts — register the plugin
|
|
25
|
+
import { defineConfig } from "cypress";
|
|
26
|
+
import { registerExecutableStoriesPlugin } from "executable-stories-cypress/plugin";
|
|
27
|
+
|
|
28
|
+
export default defineConfig({
|
|
29
|
+
e2e: {
|
|
30
|
+
setupNodeEvents(on) {
|
|
31
|
+
registerExecutableStoriesPlugin(on);
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
// 2. cypress/support/e2e.ts — import the support file
|
|
39
|
+
import "executable-stories-cypress/support";
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
// 3. cypress/e2e/checkout.story.cy.ts — write a story test
|
|
44
|
+
import { story } from "executable-stories-cypress";
|
|
45
|
+
|
|
46
|
+
describe("Cart checkout", () => {
|
|
47
|
+
it("applies discount code", () => {
|
|
48
|
+
story.init({ tags: ["checkout"], ticket: "CART-42" });
|
|
49
|
+
|
|
50
|
+
story.given("a cart with items totaling $100");
|
|
51
|
+
cy.visit("/cart");
|
|
52
|
+
cy.get("[data-testid=total]").should("contain", "$100");
|
|
53
|
+
|
|
54
|
+
story.when("a 20% discount code is applied");
|
|
55
|
+
cy.get("#discount-code").type("SAVE20");
|
|
56
|
+
cy.get("#apply-discount").click();
|
|
57
|
+
|
|
58
|
+
story.then("the total is $80");
|
|
59
|
+
cy.get("[data-testid=total]").should("contain", "$80");
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
File naming: `*.story.cy.ts`.
|
|
65
|
+
|
|
66
|
+
## Core Patterns
|
|
67
|
+
|
|
68
|
+
### Step markers with Auto-And conversion
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
it("blocks suspended user login", () => {
|
|
72
|
+
story.init();
|
|
73
|
+
|
|
74
|
+
story.given("the user account exists"); // renders "Given"
|
|
75
|
+
story.given("the account is suspended"); // renders "And" (auto-converted)
|
|
76
|
+
story.when("the user submits valid credentials");
|
|
77
|
+
cy.get("#email").type("user@test.com");
|
|
78
|
+
cy.get("#submit").click();
|
|
79
|
+
|
|
80
|
+
story.then("the user sees an error message");
|
|
81
|
+
cy.get(".error").should("be.visible");
|
|
82
|
+
|
|
83
|
+
story.but("the user is not logged in"); // renders "But" (always)
|
|
84
|
+
cy.url().should("include", "/login");
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Doc entries
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
it("processes payment", () => {
|
|
92
|
+
story.init();
|
|
93
|
+
|
|
94
|
+
story.given("a valid payment request");
|
|
95
|
+
story.json({ label: "Payload", value: { amount: 50, currency: "USD" } });
|
|
96
|
+
|
|
97
|
+
story.when("the payment is submitted");
|
|
98
|
+
cy.get("#pay").click();
|
|
99
|
+
|
|
100
|
+
story.then("the order is confirmed");
|
|
101
|
+
story.table({
|
|
102
|
+
label: "Order summary",
|
|
103
|
+
columns: ["Item", "Qty", "Price"],
|
|
104
|
+
rows: [["Widget", "2", "$25"]],
|
|
105
|
+
});
|
|
106
|
+
story.screenshot({ path: "screenshots/confirmation.png", alt: "Confirmation" });
|
|
107
|
+
story.note("Payment processed in sandbox mode");
|
|
108
|
+
});
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Step wrappers
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
const result = story.fn("When", "the calculation runs", () => {
|
|
115
|
+
return calculate(2, 3);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
story.expect("the result is correct", () => {
|
|
119
|
+
expect(result).to.equal(5);
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Common Mistakes
|
|
124
|
+
|
|
125
|
+
### CRITICAL Missing plugin registration
|
|
126
|
+
|
|
127
|
+
Wrong:
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
// cypress.config.ts
|
|
131
|
+
export default defineConfig({
|
|
132
|
+
e2e: {
|
|
133
|
+
setupNodeEvents(on) {
|
|
134
|
+
// No plugin registered
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Correct:
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
import { registerExecutableStoriesPlugin } from "executable-stories-cypress/plugin";
|
|
144
|
+
|
|
145
|
+
export default defineConfig({
|
|
146
|
+
e2e: {
|
|
147
|
+
setupNodeEvents(on) {
|
|
148
|
+
registerExecutableStoriesPlugin(on);
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
The plugin registers `cy.task` handlers for `executableStories:recordMeta`. Without it, the support file's `afterEach` hook fails silently and no story metadata is captured.
|
|
155
|
+
|
|
156
|
+
Source: packages/executable-stories-cypress/src/plugin.ts
|
|
157
|
+
|
|
158
|
+
### CRITICAL Missing support file import
|
|
159
|
+
|
|
160
|
+
Wrong:
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
// cypress/support/e2e.ts
|
|
164
|
+
// (no executable-stories import)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Correct:
|
|
168
|
+
|
|
169
|
+
```typescript
|
|
170
|
+
// cypress/support/e2e.ts
|
|
171
|
+
import "executable-stories-cypress/support";
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
The support file registers an `afterEach` hook that sends story metadata from the browser to Node via `cy.task`. Without it, stories are recorded in the browser but never reach the reporter.
|
|
175
|
+
|
|
176
|
+
Source: packages/executable-stories-cypress/src/support.ts
|
|
177
|
+
|
|
178
|
+
### HIGH Using wrong file extension
|
|
179
|
+
|
|
180
|
+
Wrong:
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
cypress/e2e/checkout.story.test.ts
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
Correct:
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
cypress/e2e/checkout.story.cy.ts
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Cypress uses `.cy.ts` file extensions. The reporter expects `.story.cy.ts` for story test files.
|
|
193
|
+
|
|
194
|
+
Source: CLAUDE.md — "Story test files use .story.cy.ts (cypress)"
|
|
195
|
+
|
|
196
|
+
### MEDIUM Calling steps before story.init()
|
|
197
|
+
|
|
198
|
+
Wrong:
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
it("my test", () => {
|
|
202
|
+
story.given("something");
|
|
203
|
+
story.init();
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
Correct:
|
|
208
|
+
|
|
209
|
+
```typescript
|
|
210
|
+
it("my test", () => {
|
|
211
|
+
story.init();
|
|
212
|
+
story.given("something");
|
|
213
|
+
});
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Steps called before `init()` are silently dropped because no story context exists.
|
|
217
|
+
|
|
218
|
+
Source: packages/executable-stories-cypress/src/story-api.ts
|