Skip to content

Commit a2aa15c

Browse files
authored
#563: Add Gherkin .feature specification (#565)
1 parent ab6cb9e commit a2aa15c

39 files changed

Lines changed: 2293 additions & 289 deletions

File tree

‎.agents/skills/openfasttrace/SKILL.md‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,28 @@ Multiple coverage:
7171

7272
## Tracing
7373

74+
## Syntax: Gherkin
75+
76+
Gherkin `.feature` files can define OFT scenario items. Put exactly one OFT ID
77+
in the contiguous tag region immediately before a `Scenario` or `Scenario
78+
Outline`. Optional `# Covers:` and `# Needs:` comments belong between the tags
79+
and the scenario header. Multiple `Covers` comments accumulate IDs; `Needs`
80+
may appear once.
81+
82+
```gherkin
83+
@id:scn~user-login~1
84+
# Covers: req~authentication~1
85+
# Needs: dsn, itest
86+
Scenario: User logs in
87+
Given a registered user
88+
When valid credentials are entered
89+
Then access is granted
90+
```
91+
92+
Basic coverage tags are recognized only in Gherkin comments, for example
93+
`# [impl~login~1 -> dsn~authentication~1]`. Executable Gherkin lines are not
94+
evaluated for coverage tags.
95+
7496
Tracing can be performed via CLI, Maven, or Gradle.
7597

7698
### CLI Usage

‎api/src/main/java/org/itsallcode/openfasttrace/api/importer/ImportEventListener.java‎

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public interface ImportEventListener
3535

3636
/**
3737
* The importer found the status of the specification item
38-
*
38+
*
3939
* @param status
4040
* the status
4141
*/
@@ -94,15 +94,15 @@ public interface ImportEventListener
9494

9595
/**
9696
* Add a tag
97-
*
97+
*
9898
* @param tag
9999
* the tag
100100
*/
101101
void addTag(String tag);
102102

103103
/**
104104
* Set the location of the specification item in the imported file
105-
*
105+
*
106106
* @param path
107107
* the path of the imported file
108108
* @param line
@@ -117,19 +117,29 @@ public interface ImportEventListener
117117

118118
/**
119119
* Set the location of the specification item in the imported file
120-
*
120+
*
121121
* @param location
122122
* the location
123123
*/
124124
void setLocation(Location location);
125125

126126
/**
127-
* Set to {@code true} if the specification item forwards needed
128-
* coverage
129-
*
127+
* Set to {@code true} if the specification item forwards needed coverage
128+
*
130129
* @param forwards
131130
* {@code true} if the specification item forwards needed
132131
* coverage
133132
*/
134133
void setForwards(boolean forwards);
134+
135+
/**
136+
* Add a complete specification item to the list of items being built. Use
137+
* this method if the specification item is already complete and does not
138+
* need to be built from events, e.g. when the item is specified in a single
139+
* line and does not span multiple lines in the input file.
140+
*
141+
* @param item
142+
* the complete specification item
143+
*/
144+
void addSpecificationItem(final SpecificationItem item);
135145
}

‎api/src/main/java/org/itsallcode/openfasttrace/api/importer/SpecificationListBuilder.java‎

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ private SpecificationListBuilder(final FilterSettings filterSettings)
2828

2929
/**
3030
* Creates a new {@link SpecificationListBuilder}.
31-
*
31+
*
3232
* @return a new {@link SpecificationListBuilder}.
3333
*/
3434
public static SpecificationListBuilder create()
@@ -39,7 +39,7 @@ public static SpecificationListBuilder create()
3939
/**
4040
* Creates a new {@link SpecificationListBuilder} with the given
4141
* {@link FilterSettings}.
42-
*
42+
*
4343
* @param filterSettings
4444
* the filter settings for the new builder.
4545
* @return a new {@link SpecificationListBuilder}.
@@ -139,7 +139,7 @@ public void addTag(final String tag)
139139
public List<SpecificationItem> build()
140140
{
141141
this.endSpecificationItem();
142-
return Collections.unmodifiableList(this.items) ;
142+
return Collections.unmodifiableList(this.items);
143143
}
144144

145145
/**
@@ -177,14 +177,20 @@ public void endSpecificationItem()
177177
{
178178
final SpecificationItem item = createNewSpecificationItem();
179179
// [impl->dsn~filtering-by-artifact-types-during-import~1]
180-
if (isAccepted(item))
181-
{
182-
addNewItemToList(item);
183-
}
180+
addSpecificationItem(item);
184181
}
185182
resetState();
186183
}
187184

185+
@Override
186+
public void addSpecificationItem(final SpecificationItem item)
187+
{
188+
if (isAccepted(item))
189+
{
190+
addNewItemToList(item);
191+
}
192+
}
193+
188194
// [impl->dsn~cleaning-imported-multi-line-text-elements~1]
189195
private SpecificationItem createNewSpecificationItem()
190196
{

‎api/src/test/java/org/itsallcode/openfasttrace/api/importer/TestSpecificationListBuilder.java‎

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
package org.itsallcode.openfasttrace.api.importer;
22

33
import static org.hamcrest.MatcherAssert.assertThat;
4-
import static org.hamcrest.Matchers.containsInAnyOrder;
5-
import static org.hamcrest.Matchers.equalTo;
4+
import static org.hamcrest.Matchers.*;
65
import static org.junit.jupiter.api.Assertions.assertAll;
76

87
import java.util.*;
@@ -57,6 +56,15 @@ void testBuildWithTags()
5756
assertThat(items.get(0).getTags(), containsInAnyOrder("foo", "bar"));
5857
}
5958

59+
@Test
60+
void testAddSpecificationItem()
61+
{
62+
final SpecificationItem item = SpecificationItem.builder().id(ID).build();
63+
final SpecificationListBuilder builder = SpecificationListBuilder.create();
64+
builder.addSpecificationItem(item);
65+
assertThat(builder.build(), contains(item));
66+
}
67+
6068
// [utest->dsn~filtering-by-artifact-types-during-import~1]
6169
@Test
6270
void testFilterArtifactOfType()
@@ -153,18 +161,17 @@ void testDuplicateIdNotIgnored()
153161
@Test
154162
void testFilterSpecificationItemsByStatus()
155163
{
156-
final Set<ItemStatus> wantedStatuses = new HashSet<>();
157-
wantedStatuses.add(ItemStatus.DRAFT);
158-
final FilterSettings filterSettings = FilterSettings.builder() //
159-
.wantedStatuses(wantedStatuses) //
164+
final FilterSettings filterSettings = FilterSettings.builder()
165+
.wantedStatuses(Set.of(ItemStatus.DRAFT))
160166
.build();
161167
final SpecificationListBuilder builder = SpecificationListBuilder
162168
.createWithFilter(filterSettings);
163169
addItemWithStatus(builder, "in-A", ItemStatus.DRAFT);
164170
addItemWithStatus(builder, "out-B", ItemStatus.APPROVED);
165171
addItemWithStatus(builder, "out-C", ItemStatus.PROPOSED);
166172
addItemWithStatus(builder, "out-D", ItemStatus.REJECTED);
167-
addItemWithStatus(builder, "out-E", null); // becomes APPROVED by default
173+
// out-E becomes APPROVED by default
174+
addItemWithStatus(builder, "out-E", null);
168175
final List<SpecificationItem> items = builder.build();
169176
assertThat(items.stream().map(SpecificationItem::getName).toList(),
170177
containsInAnyOrder("in-A"));
@@ -255,7 +262,6 @@ void testMultilineTextFieldsGetTrimmed()
255262
assertAll(
256263
() -> assertThat(item.getComment(), equalTo("a comment")),
257264
() -> assertThat(item.getDescription(), equalTo("a description")),
258-
() -> assertThat(item.getRationale(), equalTo("a rationale"))
259-
);
265+
() -> assertThat(item.getRationale(), equalTo("a rationale")));
260266
}
261267
}

‎doc/changes/changes.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Changes
22

3+
* [4.7.0](changes_4.7.0.md)
34
* [4.6.0](changes_4.6.0.md)
45
* [4.5.0](changes_4.5.0.md)
56
* [4.4.0](changes_4.4.0.md)

‎doc/changes/changes_4.7.0.md‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# OpenFastTrace 4.7.0, released 2026-07-??
2+
3+
Code name: Gherkin Importer
4+
5+
## Summary
6+
7+
OpenFastTrace can now import traced specifications directly from Gherkin
8+
`.feature` files. Annotate scenarios or scenario outlines with an OFT ID and
9+
optional `Covers` and `Needs` metadata; existing coverage tags remain supported
10+
when written in Gherkin comments.
11+
12+
## New Features
13+
14+
* #563: Added Gherkin `.feature` specification import for annotated scenarios and scenario outlines.

‎doc/changesets/563-support-gherkin-feature-specification-documents.md‎

Lines changed: 0 additions & 156 deletions
This file was deleted.

0 commit comments

Comments
 (0)