executable-stories-formatters 0.1.0
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/README.md +205 -0
- package/dist/adapters.cjs +324 -0
- package/dist/adapters.cjs.map +1 -0
- package/dist/adapters.d.cts +1 -0
- package/dist/adapters.d.ts +1 -0
- package/dist/adapters.js +295 -0
- package/dist/adapters.js.map +1 -0
- package/dist/cli.js +5399 -0
- package/dist/cli.js.map +1 -0
- package/dist/index-DCJ0NvAp.d.cts +378 -0
- package/dist/index-DCJ0NvAp.d.ts +378 -0
- package/dist/index.cjs +5519 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1468 -0
- package/dist/index.d.ts +1468 -0
- package/dist/index.js +5448 -0
- package/dist/index.js.map +1 -0
- package/package.json +77 -0
- package/schemas/README.md +663 -0
- package/schemas/examples/dotnet.json +108 -0
- package/schemas/examples/full.json +254 -0
- package/schemas/examples/go.json +108 -0
- package/schemas/examples/junit5.json +108 -0
- package/schemas/examples/minimal.json +18 -0
- package/schemas/examples/pytest.json +108 -0
- package/schemas/examples/rust.json +108 -0
- package/schemas/raw-run.schema.json +437 -0
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"projectRoot": "/home/ci/myapp",
|
|
4
|
+
"startedAtMs": 1706745600000,
|
|
5
|
+
"finishedAtMs": 1706745614000,
|
|
6
|
+
"meta": {
|
|
7
|
+
"framework": "pytest",
|
|
8
|
+
"pythonVersion": "3.12.1"
|
|
9
|
+
},
|
|
10
|
+
"testCases": [
|
|
11
|
+
{
|
|
12
|
+
"externalId": "tests/auth/test_login.py::test_successful_login",
|
|
13
|
+
"title": "Successful login with valid credentials",
|
|
14
|
+
"titlePath": ["tests", "auth", "test_login", "Successful login with valid credentials"],
|
|
15
|
+
"sourceFile": "tests/auth/test_login.py",
|
|
16
|
+
"sourceLine": 15,
|
|
17
|
+
"status": "pass",
|
|
18
|
+
"durationMs": 342,
|
|
19
|
+
"story": {
|
|
20
|
+
"scenario": "Successful login with valid credentials",
|
|
21
|
+
"steps": [
|
|
22
|
+
{ "keyword": "Given", "text": "a registered user with email 'alice@example.com'" },
|
|
23
|
+
{ "keyword": "When", "text": "the user submits login with valid credentials" },
|
|
24
|
+
{ "keyword": "Then", "text": "the response status is 200" },
|
|
25
|
+
{ "keyword": "And", "text": "a JWT token is returned" }
|
|
26
|
+
],
|
|
27
|
+
"tags": ["smoke", "auth"],
|
|
28
|
+
"tickets": ["AUTH-101"],
|
|
29
|
+
"suitePath": ["tests", "auth", "test_login"],
|
|
30
|
+
"sourceOrder": 0
|
|
31
|
+
},
|
|
32
|
+
"meta": {
|
|
33
|
+
"framework": "pytest",
|
|
34
|
+
"frameworkCaseId": "tests/auth/test_login.py::test_successful_login"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"externalId": "tests/auth/test_login.py::TestLoginValidation::test_invalid_password",
|
|
39
|
+
"title": "Login with invalid password shows error",
|
|
40
|
+
"titlePath": ["tests", "auth", "test_login", "TestLoginValidation", "Login with invalid password shows error"],
|
|
41
|
+
"sourceFile": "tests/auth/test_login.py",
|
|
42
|
+
"sourceLine": 42,
|
|
43
|
+
"status": "fail",
|
|
44
|
+
"durationMs": 215,
|
|
45
|
+
"error": {
|
|
46
|
+
"message": "AssertionError: Expected status 401 but got 500",
|
|
47
|
+
"stack": "Traceback (most recent call last):\n File \"tests/auth/test_login.py\", line 58, in test_invalid_password\n assert response.status_code == 401\nAssertionError: assert 500 == 401"
|
|
48
|
+
},
|
|
49
|
+
"meta": {
|
|
50
|
+
"framework": "pytest",
|
|
51
|
+
"frameworkCaseId": "tests/auth/test_login.py::TestLoginValidation::test_invalid_password"
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"externalId": "tests/auth/test_login.py::test_login_rate_limiting",
|
|
56
|
+
"title": "Login is rate-limited after 5 failed attempts",
|
|
57
|
+
"titlePath": ["tests", "auth", "test_login", "Login is rate-limited after 5 failed attempts"],
|
|
58
|
+
"sourceFile": "tests/auth/test_login.py",
|
|
59
|
+
"sourceLine": 68,
|
|
60
|
+
"status": "skip",
|
|
61
|
+
"durationMs": 0,
|
|
62
|
+
"meta": {
|
|
63
|
+
"framework": "pytest",
|
|
64
|
+
"frameworkCaseId": "tests/auth/test_login.py::test_login_rate_limiting",
|
|
65
|
+
"skipReason": "Rate limit middleware not deployed yet"
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"externalId": "tests/cart/test_checkout.py::test_checkout_flow",
|
|
70
|
+
"title": "Complete checkout with credit card",
|
|
71
|
+
"titlePath": ["tests", "cart", "test_checkout", "Complete checkout with credit card"],
|
|
72
|
+
"sourceFile": "tests/cart/test_checkout.py",
|
|
73
|
+
"sourceLine": 10,
|
|
74
|
+
"status": "pass",
|
|
75
|
+
"durationMs": 890,
|
|
76
|
+
"story": {
|
|
77
|
+
"scenario": "Complete checkout with credit card",
|
|
78
|
+
"steps": [
|
|
79
|
+
{ "keyword": "Given", "text": "a cart with 2 items totaling $59.98" },
|
|
80
|
+
{ "keyword": "When", "text": "the user submits payment with a test credit card" },
|
|
81
|
+
{ "keyword": "Then", "text": "the order is confirmed" },
|
|
82
|
+
{ "keyword": "And", "text": "a confirmation email is queued" }
|
|
83
|
+
],
|
|
84
|
+
"tags": ["checkout", "payments", "e2e"],
|
|
85
|
+
"tickets": ["SHOP-200"],
|
|
86
|
+
"suitePath": ["tests", "cart", "test_checkout"],
|
|
87
|
+
"sourceOrder": 0
|
|
88
|
+
},
|
|
89
|
+
"meta": {
|
|
90
|
+
"framework": "pytest",
|
|
91
|
+
"frameworkCaseId": "tests/cart/test_checkout.py::test_checkout_flow"
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"externalId": "tests/cart/test_checkout.py::test_empty_cart_checkout",
|
|
96
|
+
"title": "Cannot checkout with an empty cart",
|
|
97
|
+
"titlePath": ["tests", "cart", "test_checkout", "Cannot checkout with an empty cart"],
|
|
98
|
+
"sourceFile": "tests/cart/test_checkout.py",
|
|
99
|
+
"sourceLine": 55,
|
|
100
|
+
"status": "pass",
|
|
101
|
+
"durationMs": 32,
|
|
102
|
+
"meta": {
|
|
103
|
+
"framework": "pytest",
|
|
104
|
+
"frameworkCaseId": "tests/cart/test_checkout.py::test_empty_cart_checkout"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
]
|
|
108
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"projectRoot": "/home/ci/myapp",
|
|
4
|
+
"startedAtMs": 1706745600000,
|
|
5
|
+
"finishedAtMs": 1706745607000,
|
|
6
|
+
"meta": {
|
|
7
|
+
"framework": "rust",
|
|
8
|
+
"rustVersion": "1.76.0"
|
|
9
|
+
},
|
|
10
|
+
"testCases": [
|
|
11
|
+
{
|
|
12
|
+
"externalId": "crate::auth::tests::test_successful_login",
|
|
13
|
+
"title": "Successful login with valid credentials",
|
|
14
|
+
"titlePath": ["auth", "tests", "Successful login with valid credentials"],
|
|
15
|
+
"sourceFile": "src/auth.rs",
|
|
16
|
+
"sourceLine": 120,
|
|
17
|
+
"status": "pass",
|
|
18
|
+
"durationMs": 85,
|
|
19
|
+
"story": {
|
|
20
|
+
"scenario": "Successful login with valid credentials",
|
|
21
|
+
"steps": [
|
|
22
|
+
{ "keyword": "Given", "text": "a registered user with email 'alice@example.com'" },
|
|
23
|
+
{ "keyword": "When", "text": "the user submits login with valid credentials" },
|
|
24
|
+
{ "keyword": "Then", "text": "the response status is 200" },
|
|
25
|
+
{ "keyword": "And", "text": "a JWT token is returned" }
|
|
26
|
+
],
|
|
27
|
+
"tags": ["smoke", "auth"],
|
|
28
|
+
"tickets": ["AUTH-101"],
|
|
29
|
+
"suitePath": ["auth", "tests"],
|
|
30
|
+
"sourceOrder": 0
|
|
31
|
+
},
|
|
32
|
+
"meta": {
|
|
33
|
+
"framework": "rust",
|
|
34
|
+
"frameworkCaseId": "crate::auth::tests::test_successful_login"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"externalId": "crate::auth::tests::test_login_invalid_password",
|
|
39
|
+
"title": "Login with invalid password returns 401",
|
|
40
|
+
"titlePath": ["auth", "tests", "Login with invalid password returns 401"],
|
|
41
|
+
"sourceFile": "src/auth.rs",
|
|
42
|
+
"sourceLine": 145,
|
|
43
|
+
"status": "fail",
|
|
44
|
+
"durationMs": 62,
|
|
45
|
+
"error": {
|
|
46
|
+
"message": "assertion `left == right` failed\n left: 500\n right: 401",
|
|
47
|
+
"stack": "thread 'auth::tests::test_login_invalid_password' panicked at src/auth.rs:158:\nassertion `left == right` failed\n left: 500\n right: 401\nnote: run with `RUST_BACKTRACE=1` environment variable to display a backtrace"
|
|
48
|
+
},
|
|
49
|
+
"meta": {
|
|
50
|
+
"framework": "rust",
|
|
51
|
+
"frameworkCaseId": "crate::auth::tests::test_login_invalid_password"
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"externalId": "crate::auth::tests::test_login_rate_limiting",
|
|
56
|
+
"title": "Login is rate-limited after 5 failed attempts",
|
|
57
|
+
"titlePath": ["auth", "tests", "Login is rate-limited after 5 failed attempts"],
|
|
58
|
+
"sourceFile": "src/auth.rs",
|
|
59
|
+
"sourceLine": 170,
|
|
60
|
+
"status": "skip",
|
|
61
|
+
"durationMs": 0,
|
|
62
|
+
"meta": {
|
|
63
|
+
"framework": "rust",
|
|
64
|
+
"frameworkCaseId": "crate::auth::tests::test_login_rate_limiting",
|
|
65
|
+
"skipReason": "#[ignore] Rate limit middleware not deployed yet"
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"externalId": "crate::cart::tests::test_complete_checkout",
|
|
70
|
+
"title": "Complete checkout with credit card",
|
|
71
|
+
"titlePath": ["cart", "tests", "Complete checkout with credit card"],
|
|
72
|
+
"sourceFile": "src/cart.rs",
|
|
73
|
+
"sourceLine": 88,
|
|
74
|
+
"status": "pass",
|
|
75
|
+
"durationMs": 210,
|
|
76
|
+
"story": {
|
|
77
|
+
"scenario": "Complete checkout with credit card",
|
|
78
|
+
"steps": [
|
|
79
|
+
{ "keyword": "Given", "text": "a cart with 2 items totaling $59.98" },
|
|
80
|
+
{ "keyword": "When", "text": "the user submits payment with a test credit card" },
|
|
81
|
+
{ "keyword": "Then", "text": "the order is confirmed" },
|
|
82
|
+
{ "keyword": "And", "text": "a confirmation email is queued" }
|
|
83
|
+
],
|
|
84
|
+
"tags": ["checkout", "payments", "e2e"],
|
|
85
|
+
"tickets": ["SHOP-200"],
|
|
86
|
+
"suitePath": ["cart", "tests"],
|
|
87
|
+
"sourceOrder": 0
|
|
88
|
+
},
|
|
89
|
+
"meta": {
|
|
90
|
+
"framework": "rust",
|
|
91
|
+
"frameworkCaseId": "crate::cart::tests::test_complete_checkout"
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"externalId": "crate::cart::tests::test_empty_cart_checkout",
|
|
96
|
+
"title": "Cannot checkout with an empty cart",
|
|
97
|
+
"titlePath": ["cart", "tests", "Cannot checkout with an empty cart"],
|
|
98
|
+
"sourceFile": "src/cart.rs",
|
|
99
|
+
"sourceLine": 115,
|
|
100
|
+
"status": "pass",
|
|
101
|
+
"durationMs": 8,
|
|
102
|
+
"meta": {
|
|
103
|
+
"framework": "rust",
|
|
104
|
+
"frameworkCaseId": "crate::cart::tests::test_empty_cart_checkout"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
]
|
|
108
|
+
}
|
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://executable-stories.dev/schemas/raw-run.schema.json",
|
|
4
|
+
"title": "RawRun",
|
|
5
|
+
"description": "Raw test run data from any test framework. Feed this to the executable-stories CLI to generate HTML, Markdown, JUnit XML, and Cucumber JSON reports.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"$ref": "#/$defs/RawRun",
|
|
8
|
+
"$defs": {
|
|
9
|
+
"RawRun": {
|
|
10
|
+
"type": "object",
|
|
11
|
+
"description": "Top-level raw run containing all test cases and run metadata.",
|
|
12
|
+
"properties": {
|
|
13
|
+
"schemaVersion": {
|
|
14
|
+
"type": "integer",
|
|
15
|
+
"const": 1,
|
|
16
|
+
"description": "Schema version for forward compatibility. Must be 1."
|
|
17
|
+
},
|
|
18
|
+
"testCases": {
|
|
19
|
+
"type": "array",
|
|
20
|
+
"items": { "$ref": "#/$defs/RawTestCase" },
|
|
21
|
+
"description": "All test cases from the run."
|
|
22
|
+
},
|
|
23
|
+
"startedAtMs": {
|
|
24
|
+
"type": "number",
|
|
25
|
+
"minimum": 0,
|
|
26
|
+
"description": "Run start time as Unix epoch milliseconds."
|
|
27
|
+
},
|
|
28
|
+
"finishedAtMs": {
|
|
29
|
+
"type": "number",
|
|
30
|
+
"minimum": 0,
|
|
31
|
+
"description": "Run finish time as Unix epoch milliseconds."
|
|
32
|
+
},
|
|
33
|
+
"projectRoot": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"minLength": 1,
|
|
36
|
+
"description": "Absolute path to the project root directory. Used for resolving relative attachment paths."
|
|
37
|
+
},
|
|
38
|
+
"packageVersion": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"description": "Version of the package under test."
|
|
41
|
+
},
|
|
42
|
+
"gitSha": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"description": "Git commit SHA at the time of the run."
|
|
45
|
+
},
|
|
46
|
+
"ci": {
|
|
47
|
+
"$ref": "#/$defs/RawCIInfo"
|
|
48
|
+
},
|
|
49
|
+
"meta": {
|
|
50
|
+
"type": "object",
|
|
51
|
+
"description": "Arbitrary runner-specific metadata. Escape hatch for data not covered by the schema."
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"required": ["schemaVersion", "testCases", "projectRoot"],
|
|
55
|
+
"additionalProperties": false
|
|
56
|
+
},
|
|
57
|
+
"RawTestCase": {
|
|
58
|
+
"type": "object",
|
|
59
|
+
"description": "A single test case result. Only 'status' is required — everything else is optional to support plain unit tests without BDD metadata.",
|
|
60
|
+
"properties": {
|
|
61
|
+
"externalId": {
|
|
62
|
+
"type": "string",
|
|
63
|
+
"description": "Framework's native test ID (e.g., Jest test path, Playwright test ID)."
|
|
64
|
+
},
|
|
65
|
+
"title": {
|
|
66
|
+
"type": "string",
|
|
67
|
+
"description": "Human-readable test title/name."
|
|
68
|
+
},
|
|
69
|
+
"titlePath": {
|
|
70
|
+
"type": "array",
|
|
71
|
+
"items": { "type": "string" },
|
|
72
|
+
"description": "Full title path from describe/suite blocks down to the test name."
|
|
73
|
+
},
|
|
74
|
+
"story": {
|
|
75
|
+
"$ref": "#/$defs/StoryMeta"
|
|
76
|
+
},
|
|
77
|
+
"sourceFile": {
|
|
78
|
+
"type": "string",
|
|
79
|
+
"description": "Path to the source file containing this test (relative to projectRoot or absolute)."
|
|
80
|
+
},
|
|
81
|
+
"sourceLine": {
|
|
82
|
+
"type": "integer",
|
|
83
|
+
"minimum": 1,
|
|
84
|
+
"description": "Line number in the source file (1-based)."
|
|
85
|
+
},
|
|
86
|
+
"status": {
|
|
87
|
+
"$ref": "#/$defs/RawStatus"
|
|
88
|
+
},
|
|
89
|
+
"durationMs": {
|
|
90
|
+
"type": "number",
|
|
91
|
+
"minimum": 0,
|
|
92
|
+
"description": "Test duration in milliseconds."
|
|
93
|
+
},
|
|
94
|
+
"error": {
|
|
95
|
+
"type": "object",
|
|
96
|
+
"description": "Error information if the test failed.",
|
|
97
|
+
"properties": {
|
|
98
|
+
"message": {
|
|
99
|
+
"type": "string",
|
|
100
|
+
"description": "Error message."
|
|
101
|
+
},
|
|
102
|
+
"stack": {
|
|
103
|
+
"type": "string",
|
|
104
|
+
"description": "Error stack trace."
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
"additionalProperties": false
|
|
108
|
+
},
|
|
109
|
+
"stepEvents": {
|
|
110
|
+
"type": "array",
|
|
111
|
+
"items": { "$ref": "#/$defs/RawStepEvent" },
|
|
112
|
+
"description": "Step-level execution events from the framework (if available)."
|
|
113
|
+
},
|
|
114
|
+
"attachments": {
|
|
115
|
+
"type": "array",
|
|
116
|
+
"items": { "$ref": "#/$defs/RawAttachment" },
|
|
117
|
+
"description": "Attachments such as screenshots, logs, or other artifacts."
|
|
118
|
+
},
|
|
119
|
+
"meta": {
|
|
120
|
+
"type": "object",
|
|
121
|
+
"description": "Arbitrary framework-specific metadata. Escape hatch for data not covered by the schema."
|
|
122
|
+
},
|
|
123
|
+
"retry": {
|
|
124
|
+
"type": "integer",
|
|
125
|
+
"minimum": 0,
|
|
126
|
+
"description": "Retry attempt number (0-based). 0 means first attempt."
|
|
127
|
+
},
|
|
128
|
+
"retries": {
|
|
129
|
+
"type": "integer",
|
|
130
|
+
"minimum": 0,
|
|
131
|
+
"description": "Total number of retries configured for this test."
|
|
132
|
+
},
|
|
133
|
+
"projectName": {
|
|
134
|
+
"type": "string",
|
|
135
|
+
"description": "Project name (e.g., Playwright project like 'chromium', 'firefox')."
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
"required": ["status"],
|
|
139
|
+
"additionalProperties": false
|
|
140
|
+
},
|
|
141
|
+
"RawStatus": {
|
|
142
|
+
"type": "string",
|
|
143
|
+
"enum": ["pass", "fail", "skip", "todo", "pending", "timeout", "interrupted", "unknown"],
|
|
144
|
+
"description": "Permissive test status from any framework. Mapped to canonical status (passed/failed/skipped/pending) during canonicalization."
|
|
145
|
+
},
|
|
146
|
+
"StoryMeta": {
|
|
147
|
+
"type": "object",
|
|
148
|
+
"description": "BDD story metadata for a test case. Contains the scenario title, steps, tags, and documentation.",
|
|
149
|
+
"properties": {
|
|
150
|
+
"scenario": {
|
|
151
|
+
"type": "string",
|
|
152
|
+
"minLength": 1,
|
|
153
|
+
"description": "The scenario title (typically the test name)."
|
|
154
|
+
},
|
|
155
|
+
"steps": {
|
|
156
|
+
"type": "array",
|
|
157
|
+
"items": { "$ref": "#/$defs/StoryStep" },
|
|
158
|
+
"description": "BDD steps in this scenario. Can be empty or omitted for progressive enrichment."
|
|
159
|
+
},
|
|
160
|
+
"tags": {
|
|
161
|
+
"type": "array",
|
|
162
|
+
"items": { "type": "string" },
|
|
163
|
+
"description": "Tags for filtering and categorization (e.g., ['smoke', 'auth'])."
|
|
164
|
+
},
|
|
165
|
+
"tickets": {
|
|
166
|
+
"type": "array",
|
|
167
|
+
"items": { "type": "string" },
|
|
168
|
+
"description": "Ticket/issue references for requirements traceability (e.g., ['JIRA-123'])."
|
|
169
|
+
},
|
|
170
|
+
"meta": {
|
|
171
|
+
"type": "object",
|
|
172
|
+
"description": "User-defined metadata for this story."
|
|
173
|
+
},
|
|
174
|
+
"suitePath": {
|
|
175
|
+
"type": "array",
|
|
176
|
+
"items": { "type": "string" },
|
|
177
|
+
"description": "Parent describe/suite names for hierarchical grouping."
|
|
178
|
+
},
|
|
179
|
+
"docs": {
|
|
180
|
+
"type": "array",
|
|
181
|
+
"items": { "$ref": "#/$defs/DocEntry" },
|
|
182
|
+
"description": "Story-level documentation entries (before any steps)."
|
|
183
|
+
},
|
|
184
|
+
"sourceOrder": {
|
|
185
|
+
"type": "integer",
|
|
186
|
+
"minimum": 0,
|
|
187
|
+
"description": "Order in which the story was defined in source (for stable sorting)."
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
"required": ["scenario"],
|
|
191
|
+
"additionalProperties": false
|
|
192
|
+
},
|
|
193
|
+
"StoryStep": {
|
|
194
|
+
"type": "object",
|
|
195
|
+
"description": "A single BDD step in a scenario.",
|
|
196
|
+
"properties": {
|
|
197
|
+
"keyword": {
|
|
198
|
+
"$ref": "#/$defs/StepKeyword"
|
|
199
|
+
},
|
|
200
|
+
"text": {
|
|
201
|
+
"type": "string",
|
|
202
|
+
"description": "The step description text."
|
|
203
|
+
},
|
|
204
|
+
"mode": {
|
|
205
|
+
"$ref": "#/$defs/StepMode"
|
|
206
|
+
},
|
|
207
|
+
"docs": {
|
|
208
|
+
"type": "array",
|
|
209
|
+
"items": { "$ref": "#/$defs/DocEntry" },
|
|
210
|
+
"description": "Rich documentation entries attached to this step."
|
|
211
|
+
}
|
|
212
|
+
},
|
|
213
|
+
"required": ["keyword", "text"],
|
|
214
|
+
"additionalProperties": false
|
|
215
|
+
},
|
|
216
|
+
"StepKeyword": {
|
|
217
|
+
"type": "string",
|
|
218
|
+
"description": "BDD step keyword. Case-insensitive — 'given' and 'Given' are both accepted. Normalized to title case during processing.",
|
|
219
|
+
"pattern": "^[Gg]iven$|^[Ww]hen$|^[Tt]hen$|^[Aa]nd$|^[Bb]ut$"
|
|
220
|
+
},
|
|
221
|
+
"StepMode": {
|
|
222
|
+
"type": "string",
|
|
223
|
+
"enum": ["normal", "skip", "only", "todo", "fails", "concurrent"],
|
|
224
|
+
"description": "Step execution mode for documentation rendering."
|
|
225
|
+
},
|
|
226
|
+
"DocPhase": {
|
|
227
|
+
"type": "string",
|
|
228
|
+
"enum": ["static", "runtime"],
|
|
229
|
+
"description": "When the doc entry was added — 'static' at definition time, 'runtime' during execution."
|
|
230
|
+
},
|
|
231
|
+
"DocEntry": {
|
|
232
|
+
"description": "A documentation entry attached to a story or step. Discriminated union on the 'kind' field.",
|
|
233
|
+
"oneOf": [
|
|
234
|
+
{
|
|
235
|
+
"type": "object",
|
|
236
|
+
"description": "Free-text note.",
|
|
237
|
+
"properties": {
|
|
238
|
+
"kind": { "const": "note" },
|
|
239
|
+
"text": { "type": "string" },
|
|
240
|
+
"phase": { "$ref": "#/$defs/DocPhase" }
|
|
241
|
+
},
|
|
242
|
+
"required": ["kind", "text", "phase"],
|
|
243
|
+
"additionalProperties": false
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
"type": "object",
|
|
247
|
+
"description": "Tag(s) for categorization.",
|
|
248
|
+
"properties": {
|
|
249
|
+
"kind": { "const": "tag" },
|
|
250
|
+
"names": {
|
|
251
|
+
"type": "array",
|
|
252
|
+
"items": { "type": "string" }
|
|
253
|
+
},
|
|
254
|
+
"phase": { "$ref": "#/$defs/DocPhase" }
|
|
255
|
+
},
|
|
256
|
+
"required": ["kind", "names", "phase"],
|
|
257
|
+
"additionalProperties": false
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
"type": "object",
|
|
261
|
+
"description": "Key-value pair.",
|
|
262
|
+
"properties": {
|
|
263
|
+
"kind": { "const": "kv" },
|
|
264
|
+
"label": { "type": "string" },
|
|
265
|
+
"value": {},
|
|
266
|
+
"phase": { "$ref": "#/$defs/DocPhase" }
|
|
267
|
+
},
|
|
268
|
+
"required": ["kind", "label", "value", "phase"],
|
|
269
|
+
"additionalProperties": false
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
"type": "object",
|
|
273
|
+
"description": "Code block with optional language.",
|
|
274
|
+
"properties": {
|
|
275
|
+
"kind": { "const": "code" },
|
|
276
|
+
"label": { "type": "string" },
|
|
277
|
+
"content": { "type": "string" },
|
|
278
|
+
"lang": { "type": "string" },
|
|
279
|
+
"phase": { "$ref": "#/$defs/DocPhase" }
|
|
280
|
+
},
|
|
281
|
+
"required": ["kind", "label", "content", "phase"],
|
|
282
|
+
"additionalProperties": false
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
"type": "object",
|
|
286
|
+
"description": "Markdown table.",
|
|
287
|
+
"properties": {
|
|
288
|
+
"kind": { "const": "table" },
|
|
289
|
+
"label": { "type": "string" },
|
|
290
|
+
"columns": {
|
|
291
|
+
"type": "array",
|
|
292
|
+
"items": { "type": "string" }
|
|
293
|
+
},
|
|
294
|
+
"rows": {
|
|
295
|
+
"type": "array",
|
|
296
|
+
"items": {
|
|
297
|
+
"type": "array",
|
|
298
|
+
"items": { "type": "string" }
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
"phase": { "$ref": "#/$defs/DocPhase" }
|
|
302
|
+
},
|
|
303
|
+
"required": ["kind", "label", "columns", "rows", "phase"],
|
|
304
|
+
"additionalProperties": false
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
"type": "object",
|
|
308
|
+
"description": "Hyperlink.",
|
|
309
|
+
"properties": {
|
|
310
|
+
"kind": { "const": "link" },
|
|
311
|
+
"label": { "type": "string" },
|
|
312
|
+
"url": { "type": "string" },
|
|
313
|
+
"phase": { "$ref": "#/$defs/DocPhase" }
|
|
314
|
+
},
|
|
315
|
+
"required": ["kind", "label", "url", "phase"],
|
|
316
|
+
"additionalProperties": false
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
"type": "object",
|
|
320
|
+
"description": "Titled section with markdown content.",
|
|
321
|
+
"properties": {
|
|
322
|
+
"kind": { "const": "section" },
|
|
323
|
+
"title": { "type": "string" },
|
|
324
|
+
"markdown": { "type": "string" },
|
|
325
|
+
"phase": { "$ref": "#/$defs/DocPhase" }
|
|
326
|
+
},
|
|
327
|
+
"required": ["kind", "title", "markdown", "phase"],
|
|
328
|
+
"additionalProperties": false
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
"type": "object",
|
|
332
|
+
"description": "Mermaid diagram.",
|
|
333
|
+
"properties": {
|
|
334
|
+
"kind": { "const": "mermaid" },
|
|
335
|
+
"code": { "type": "string" },
|
|
336
|
+
"title": { "type": "string" },
|
|
337
|
+
"phase": { "$ref": "#/$defs/DocPhase" }
|
|
338
|
+
},
|
|
339
|
+
"required": ["kind", "code", "phase"],
|
|
340
|
+
"additionalProperties": false
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
"type": "object",
|
|
344
|
+
"description": "Screenshot reference.",
|
|
345
|
+
"properties": {
|
|
346
|
+
"kind": { "const": "screenshot" },
|
|
347
|
+
"path": { "type": "string" },
|
|
348
|
+
"alt": { "type": "string" },
|
|
349
|
+
"phase": { "$ref": "#/$defs/DocPhase" }
|
|
350
|
+
},
|
|
351
|
+
"required": ["kind", "path", "phase"],
|
|
352
|
+
"additionalProperties": false
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
"type": "object",
|
|
356
|
+
"description": "Custom documentation entry with arbitrary data.",
|
|
357
|
+
"properties": {
|
|
358
|
+
"kind": { "const": "custom" },
|
|
359
|
+
"type": { "type": "string" },
|
|
360
|
+
"data": {},
|
|
361
|
+
"phase": { "$ref": "#/$defs/DocPhase" }
|
|
362
|
+
},
|
|
363
|
+
"required": ["kind", "type", "data", "phase"],
|
|
364
|
+
"additionalProperties": false
|
|
365
|
+
}
|
|
366
|
+
]
|
|
367
|
+
},
|
|
368
|
+
"RawAttachment": {
|
|
369
|
+
"type": "object",
|
|
370
|
+
"description": "A test attachment (screenshot, log, artifact). In the MVP schema, only path-based attachments are supported (no inline base64).",
|
|
371
|
+
"properties": {
|
|
372
|
+
"name": {
|
|
373
|
+
"type": "string",
|
|
374
|
+
"description": "Human-readable name for the attachment."
|
|
375
|
+
},
|
|
376
|
+
"mediaType": {
|
|
377
|
+
"type": "string",
|
|
378
|
+
"description": "MIME type (e.g., 'image/png', 'text/plain', 'application/json')."
|
|
379
|
+
},
|
|
380
|
+
"path": {
|
|
381
|
+
"type": "string",
|
|
382
|
+
"description": "File path (relative to projectRoot or absolute)."
|
|
383
|
+
}
|
|
384
|
+
},
|
|
385
|
+
"required": ["name", "mediaType", "path"],
|
|
386
|
+
"additionalProperties": false
|
|
387
|
+
},
|
|
388
|
+
"RawStepEvent": {
|
|
389
|
+
"type": "object",
|
|
390
|
+
"description": "Step-level execution event from the framework. Optional — most frameworks don't provide step-level data.",
|
|
391
|
+
"properties": {
|
|
392
|
+
"index": {
|
|
393
|
+
"type": "integer",
|
|
394
|
+
"minimum": 0,
|
|
395
|
+
"description": "Step index (0-based)."
|
|
396
|
+
},
|
|
397
|
+
"title": {
|
|
398
|
+
"type": "string",
|
|
399
|
+
"description": "Step title/description."
|
|
400
|
+
},
|
|
401
|
+
"status": {
|
|
402
|
+
"$ref": "#/$defs/RawStatus"
|
|
403
|
+
},
|
|
404
|
+
"durationMs": {
|
|
405
|
+
"type": "number",
|
|
406
|
+
"minimum": 0,
|
|
407
|
+
"description": "Step duration in milliseconds."
|
|
408
|
+
},
|
|
409
|
+
"errorMessage": {
|
|
410
|
+
"type": "string",
|
|
411
|
+
"description": "Error message if the step failed."
|
|
412
|
+
}
|
|
413
|
+
},
|
|
414
|
+
"additionalProperties": false
|
|
415
|
+
},
|
|
416
|
+
"RawCIInfo": {
|
|
417
|
+
"type": "object",
|
|
418
|
+
"description": "CI environment information.",
|
|
419
|
+
"properties": {
|
|
420
|
+
"name": {
|
|
421
|
+
"type": "string",
|
|
422
|
+
"description": "CI provider name (e.g., 'GitHub Actions', 'Jenkins', 'CircleCI')."
|
|
423
|
+
},
|
|
424
|
+
"url": {
|
|
425
|
+
"type": "string",
|
|
426
|
+
"description": "URL to the CI build/run."
|
|
427
|
+
},
|
|
428
|
+
"buildNumber": {
|
|
429
|
+
"type": "string",
|
|
430
|
+
"description": "CI build number or run ID."
|
|
431
|
+
}
|
|
432
|
+
},
|
|
433
|
+
"required": ["name"],
|
|
434
|
+
"additionalProperties": false
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|