saffron-ai 0.5.0 → 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 +17 -0
- package/README.md +5 -4
- package/package.json +1 -1
- package/templates/saucedemo/features/{login.feature → login.saffron} +12 -0
- package/templates/saucedemo/features/shared.steps.saffron +16 -2
- package/templates/saucedemo/features/shopping.saffron +6 -11
- package/textmate/saffron/syntaxes/saffron.tmLanguage.json +53 -31
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,23 @@ All notable changes to Saffron, in one place: the `saffron-ai` runner (npm),
|
|
|
4
4
|
the VS Code extension (`ChathurangaJayasinghe.saffron-vscode`) and the
|
|
5
5
|
JetBrains plugin (`ai.saffron.jetbrains`). Dates are publish dates.
|
|
6
6
|
|
|
7
|
+
## 0.5.1
|
|
8
|
+
|
|
9
|
+
Released 2026-09-14.
|
|
10
|
+
|
|
11
|
+
Runner
|
|
12
|
+
|
|
13
|
+
- Example suite: `.saffron` files only, with StepSets invoked both at the
|
|
14
|
+
start of scenarios and in the middle ("Recover from a locked-out
|
|
15
|
+
login", and a checkout set that carries a data table).
|
|
16
|
+
|
|
17
|
+
Editors
|
|
18
|
+
|
|
19
|
+
- Grammar: quoted arguments, `<placeholders>` and `{env:…}` / `{date…}`
|
|
20
|
+
tokens get their own scopes, so editors colour them like Cucumber
|
|
21
|
+
parameters instead of the whole step line. Ships in the VS Code
|
|
22
|
+
extension, the JetBrains plugin and the package's TextMate bundle.
|
|
23
|
+
|
|
7
24
|
## 0.5.0
|
|
8
25
|
|
|
9
26
|
Released 2026-09-14.
|
package/README.md
CHANGED
|
@@ -37,10 +37,11 @@ npx saffron init # config + agent skill; offers the Saucedemo example sui
|
|
|
37
37
|
npx saffron run
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
No app of your own yet? `npx saffron init --examples` installs
|
|
41
|
-
scenarios against the public demo shop at saucedemo.com
|
|
42
|
-
|
|
43
|
-
replays something
|
|
40
|
+
No app of your own yet? `npx saffron init --examples` installs a handful
|
|
41
|
+
of short `.saffron` scenarios against the public demo shop at saucedemo.com
|
|
42
|
+
(login, a Scenario Outline, cart, checkout, StepSets at the start and in
|
|
43
|
+
the middle of scenarios) so the first run records and replays something
|
|
44
|
+
real.
|
|
44
45
|
|
|
45
46
|
**Built for the AI era:** the package ships an [Agent Skill](https://agentskills.io) that teaches Claude Code, Codex, Cursor, Copilot and any skill-aware agent to write Saffron tests efficiently. Reuse recorded wordings, keep assertions sacred, secrets as `{env:VAR}`, so the tests your agents write replay at zero tokens instead of paying for near-duplicate recordings.
|
|
46
47
|
|
package/package.json
CHANGED
|
@@ -23,3 +23,15 @@ Feature: Login
|
|
|
23
23
|
| username | password | message |
|
|
24
24
|
| locked_out_user | secret_sauce | Epic sadface: Sorry, this user has been locked out. |
|
|
25
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
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
Feature: Shared step sets for the Saucedemo example suite
|
|
2
2
|
|
|
3
|
-
# A sets-only library file: no scenarios, only StepSet definitions
|
|
4
|
-
# .saffron file in the project
|
|
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.
|
|
5
8
|
|
|
6
9
|
StepSet: Log in as the standard user
|
|
7
10
|
Given I am on the login page
|
|
@@ -9,3 +12,14 @@ StepSet: Log in as the standard user
|
|
|
9
12
|
And I enter "{env:SAUCE_PASSWORD}" in the password field
|
|
10
13
|
And I click the "Login" button
|
|
11
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
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Feature: Shopping
|
|
2
2
|
|
|
3
|
-
# .saffron is Gherkin plus the StepSet keyword. The
|
|
4
|
-
# once in shared.steps.saffron
|
|
5
|
-
#
|
|
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
6
|
|
|
7
7
|
@smoke @cart
|
|
8
8
|
Scenario: Add a product to the cart
|
|
@@ -14,12 +14,7 @@ Feature: Shopping
|
|
|
14
14
|
Scenario: Complete a checkout
|
|
15
15
|
StepSet Log in as the standard user
|
|
16
16
|
When I add "Sauce Labs Bike Light" to the cart
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
| firstName | Ada |
|
|
21
|
-
| lastName | Lovelace |
|
|
22
|
-
| postalCode | 10117 |
|
|
23
|
-
And I click the "Continue" button
|
|
24
|
-
And I click the "Finish" button
|
|
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
|
|
25
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
|
+
}
|