saffron-ai 0.4.2 → 0.5.1
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/CHANGELOG.md +181 -0
- package/README.md +9 -2
- package/dist-pkg/cli.js +101 -100
- package/package.json +4 -2
- package/skills/saffron/references/config.md +1 -1
- package/templates/saucedemo/.env.example +5 -0
- package/templates/saucedemo/features/login.saffron +37 -0
- package/templates/saucedemo/features/shared.steps.saffron +25 -0
- package/templates/saucedemo/features/shopping.saffron +20 -0
- package/templates/saucedemo/saffron.config.json +6 -0
- package/textmate/saffron/syntaxes/saffron.tmLanguage.json +53 -31
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "saffron-ai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Gherkin-native AI-fallback test runner: zero-token cached replay, runtime AI healing, honest reports",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "vitest run",
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"dev": "tsc --watch",
|
|
9
9
|
"e2e": "node scripts/e2e.mjs",
|
|
10
10
|
"prepublishOnly": "npm run build && npm test && npm run bundle",
|
|
11
|
-
"docs": "node scripts/build-docs.mjs",
|
|
11
|
+
"docs": "node scripts/build-docs.mjs && node scripts/build-site-docs.mjs",
|
|
12
12
|
"bundle": "esbuild src/cli/index.ts --bundle --platform=node --format=esm --packages=external --minify --legal-comments=none --outfile=dist-pkg/cli.js"
|
|
13
13
|
},
|
|
14
14
|
"keywords": [
|
|
@@ -56,7 +56,9 @@
|
|
|
56
56
|
"dist-pkg",
|
|
57
57
|
"skills",
|
|
58
58
|
"textmate",
|
|
59
|
+
"templates",
|
|
59
60
|
"README.md",
|
|
61
|
+
"CHANGELOG.md",
|
|
60
62
|
"LICENSE"
|
|
61
63
|
]
|
|
62
64
|
}
|
|
@@ -59,7 +59,7 @@ your-project/
|
|
|
59
59
|
| `saffron steps [search] [--json] [--snippets]` | The step vocabulary with recorded/divergent/unrecorded badges |
|
|
60
60
|
| `saffron author <prose-file>` | Draft a `.saffron` file from plain-paragraph requirements using the project vocabulary |
|
|
61
61
|
| `saffron mcp` / `saffron lsp` | Vocabulary for AI assistants / language server for editors |
|
|
62
|
-
| `saffron init` | Install this skill into the project's agent directories
|
|
62
|
+
| `saffron init [--examples]` | Install this skill into the project's agent directories, register MCP, scaffold config; `--examples` adds the Saucedemo example suite |
|
|
63
63
|
| `saffron report` | Open the latest HTML report |
|
|
64
64
|
|
|
65
65
|
Exit codes: 0 green/yellow, 1 red (or yellow with `--strict`), 2 usage /
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Saucedemo's public demo credentials. Copy this file to .env (git-ignored)
|
|
2
|
+
# or export the variables in your shell. Saffron resolves {env:VAR} tokens
|
|
3
|
+
# at replay and never writes the values into caches, proposals or reports.
|
|
4
|
+
SAUCE_USER=standard_user
|
|
5
|
+
SAUCE_PASSWORD=secret_sauce
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
Feature: Login
|
|
2
|
+
|
|
3
|
+
# Credentials come from the environment ({env:VAR}); they never land in
|
|
4
|
+
# caches, proposals or reports. See .env.example.
|
|
5
|
+
|
|
6
|
+
@smoke @login
|
|
7
|
+
Scenario: Successful login
|
|
8
|
+
Given I am on the login page
|
|
9
|
+
When I enter "{env:SAUCE_USER}" in the username field
|
|
10
|
+
And I enter "{env:SAUCE_PASSWORD}" in the password field
|
|
11
|
+
And I click the "Login" button
|
|
12
|
+
Then I should see the products page
|
|
13
|
+
|
|
14
|
+
@login
|
|
15
|
+
Scenario Outline: Login errors
|
|
16
|
+
Given I am on the login page
|
|
17
|
+
When I enter "<username>" in the username field
|
|
18
|
+
And I enter "<password>" in the password field
|
|
19
|
+
And I click the "Login" button
|
|
20
|
+
Then I should see the error message "<message>"
|
|
21
|
+
|
|
22
|
+
Examples:
|
|
23
|
+
| username | password | message |
|
|
24
|
+
| locked_out_user | secret_sauce | Epic sadface: Sorry, this user has been locked out. |
|
|
25
|
+
| standard_user | wrong | Epic sadface: Username and password do not match |
|
|
26
|
+
|
|
27
|
+
@login @recover
|
|
28
|
+
Scenario: Recover from a locked-out login
|
|
29
|
+
# A StepSet in the middle of a scenario: the set's guard step takes the
|
|
30
|
+
# browser back to the login page before it logs the standard user in.
|
|
31
|
+
Given I am on the login page
|
|
32
|
+
When I enter "locked_out_user" in the username field
|
|
33
|
+
And I enter "secret_sauce" in the password field
|
|
34
|
+
And I click the "Login" button
|
|
35
|
+
Then I should see the error message "Epic sadface: Sorry, this user has been locked out."
|
|
36
|
+
StepSet Log in as the standard user
|
|
37
|
+
Then I should see the products page
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Feature: Shared step sets for the Saucedemo example suite
|
|
2
|
+
|
|
3
|
+
# A sets-only library file: no scenarios, only StepSet definitions. Any
|
|
4
|
+
# .saffron file in the project invokes them with "StepSet <name>", at the
|
|
5
|
+
# start of a scenario or anywhere in the middle. Each set opens with a
|
|
6
|
+
# guard step and closes with an exit assertion, so every invocation checks
|
|
7
|
+
# itself.
|
|
8
|
+
|
|
9
|
+
StepSet: Log in as the standard user
|
|
10
|
+
Given I am on the login page
|
|
11
|
+
When I enter "{env:SAUCE_USER}" in the username field
|
|
12
|
+
And I enter "{env:SAUCE_PASSWORD}" in the password field
|
|
13
|
+
And I click the "Login" button
|
|
14
|
+
Then I should see the products page
|
|
15
|
+
|
|
16
|
+
StepSet: Proceed to checkout with the standard details
|
|
17
|
+
Given I am on the products page
|
|
18
|
+
When I open the cart
|
|
19
|
+
And I click the "Checkout" button
|
|
20
|
+
And I enter the following checkout information
|
|
21
|
+
| firstName | Ada |
|
|
22
|
+
| lastName | Lovelace |
|
|
23
|
+
| postalCode | 10117 |
|
|
24
|
+
And I click the "Continue" button
|
|
25
|
+
Then I should see the checkout overview
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Feature: Shopping
|
|
2
|
+
|
|
3
|
+
# .saffron is Gherkin plus the StepSet keyword. The flows below are
|
|
4
|
+
# defined once in shared.steps.saffron; their steps are inlined at parse
|
|
5
|
+
# time, so they record once and replay everywhere.
|
|
6
|
+
|
|
7
|
+
@smoke @cart
|
|
8
|
+
Scenario: Add a product to the cart
|
|
9
|
+
StepSet Log in as the standard user
|
|
10
|
+
When I add "Sauce Labs Backpack" to the cart
|
|
11
|
+
Then the cart badge should show 1
|
|
12
|
+
|
|
13
|
+
@checkout
|
|
14
|
+
Scenario: Complete a checkout
|
|
15
|
+
StepSet Log in as the standard user
|
|
16
|
+
When I add "Sauce Labs Bike Light" to the cart
|
|
17
|
+
StepSet Proceed to checkout with the standard details
|
|
18
|
+
Then the overview should list "Sauce Labs Bike Light"
|
|
19
|
+
When I click the "Finish" button
|
|
20
|
+
Then I should see "Thank you for your order!"
|
|
@@ -72,56 +72,78 @@
|
|
|
72
72
|
"name": "keyword.control.stepset.saffron"
|
|
73
73
|
},
|
|
74
74
|
"2": {
|
|
75
|
-
"name": "entity.name.function.stepset.saffron"
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
},
|
|
79
|
-
"step": {
|
|
80
|
-
"match": "^\\s*(Given|When|Then|And|But|\\*)\\s(.*)$",
|
|
81
|
-
"captures": {
|
|
82
|
-
"1": {
|
|
83
|
-
"name": "keyword.other.step.saffron"
|
|
84
|
-
},
|
|
85
|
-
"2": {
|
|
86
|
-
"name": "string.unquoted.step-text.saffron",
|
|
75
|
+
"name": "entity.name.function.stepset.saffron",
|
|
87
76
|
"patterns": [
|
|
88
77
|
{
|
|
89
|
-
"
|
|
90
|
-
|
|
78
|
+
"include": "#argument"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"include": "#placeholder"
|
|
91
82
|
},
|
|
92
83
|
{
|
|
93
|
-
"
|
|
94
|
-
"name": "variable.parameter.saffron"
|
|
84
|
+
"include": "#token"
|
|
95
85
|
}
|
|
96
86
|
]
|
|
97
87
|
}
|
|
98
88
|
}
|
|
99
89
|
},
|
|
90
|
+
"step": {
|
|
91
|
+
"begin": "^\\s*(Given|When|Then|And|But|\\*)\\b",
|
|
92
|
+
"beginCaptures": {
|
|
93
|
+
"1": {
|
|
94
|
+
"name": "keyword.other.step.saffron"
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"end": "$",
|
|
98
|
+
"name": "meta.step.saffron",
|
|
99
|
+
"patterns": [
|
|
100
|
+
{
|
|
101
|
+
"include": "#argument"
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"include": "#placeholder"
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"include": "#token"
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
},
|
|
100
111
|
"docstring": {
|
|
101
112
|
"begin": "^\\s*\"\"\"",
|
|
102
113
|
"end": "^\\s*\"\"\"",
|
|
103
114
|
"name": "string.quoted.docstring.saffron"
|
|
104
115
|
},
|
|
105
116
|
"table": {
|
|
106
|
-
"
|
|
117
|
+
"begin": "^\\s*(?=\\|)",
|
|
118
|
+
"end": "$",
|
|
107
119
|
"name": "meta.table.saffron",
|
|
108
|
-
"
|
|
109
|
-
|
|
110
|
-
"
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
]
|
|
120
|
+
"patterns": [
|
|
121
|
+
{
|
|
122
|
+
"match": "\\|",
|
|
123
|
+
"name": "punctuation.separator.table.saffron"
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
"include": "#placeholder"
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"include": "#token"
|
|
120
130
|
}
|
|
121
|
-
|
|
131
|
+
]
|
|
132
|
+
},
|
|
133
|
+
"argument": {
|
|
134
|
+
"match": "\"[^\"]*\"",
|
|
135
|
+
"name": "string.quoted.double.argument.saffron"
|
|
136
|
+
},
|
|
137
|
+
"placeholder": {
|
|
138
|
+
"match": "<[^<>\\s]+>",
|
|
139
|
+
"name": "variable.parameter.placeholder.saffron"
|
|
140
|
+
},
|
|
141
|
+
"token": {
|
|
142
|
+
"match": "\\{(env:[A-Za-z_][A-Za-z0-9_]*|date(?:[+-]\\d+)?(?::[^}]+)?)\\}",
|
|
143
|
+
"name": "constant.other.token.saffron"
|
|
122
144
|
}
|
|
123
145
|
},
|
|
124
146
|
"fileTypes": [
|
|
125
147
|
"saffron"
|
|
126
148
|
]
|
|
127
|
-
}
|
|
149
|
+
}
|