Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .agents/skills/openfasttrace/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,28 @@ Multiple coverage:

## Tracing

## Syntax: Gherkin

Gherkin `.feature` files can define OFT scenario items. Put exactly one OFT ID
in the contiguous tag region immediately before a `Scenario` or `Scenario
Outline`. Optional `# Covers:` and `# Needs:` comments belong between the tags
and the scenario header. Multiple `Covers` comments accumulate IDs; `Needs`
may appear once.

```gherkin
@id:scn~user-login~1
# Covers: req~authentication~1
# Needs: dsn, itest
Scenario: User logs in
Given a registered user
When valid credentials are entered
Then access is granted
```

Legacy coverage tags are recognized only in Gherkin comments, for example
`# [impl~login~1 -> dsn~authentication~1]`. Executable Gherkin lines are not
evaluated for coverage tags.

Tracing can be performed via CLI, Maven, or Gradle.

### CLI Usage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public interface ImportEventListener

/**
* The importer found the status of the specification item
*
*
* @param status
* the status
*/
Expand Down Expand Up @@ -94,15 +94,15 @@ public interface ImportEventListener

/**
* Add a tag
*
*
* @param tag
* the tag
*/
void addTag(String tag);

/**
* Set the location of the specification item in the imported file
*
*
* @param path
* the path of the imported file
* @param line
Expand All @@ -117,19 +117,40 @@ public interface ImportEventListener

/**
* Set the location of the specification item in the imported file
*
*
* @param location
* the location
*/
void setLocation(Location location);

/**
* Set to {@code true} if the specification item forwards needed
* coverage
*
* Set to {@code true} if the specification item forwards needed coverage
*
* @param forwards
* {@code true} if the specification item forwards needed
* coverage
*/
void setForwards(boolean forwards);

/**
* Add a complete specification item to the list of items being built. Use
* this method if the specification item is already complete and does not
* need to be built from events, e.g. when the item is specified in a single
* line and does not span multiple lines in the input file.
* <p>
* This is equivalent to the following sequence of method calls:
* <ul>
* <li>{@link #beginSpecificationItem()}</li>
* <li>{@link #setId(SpecificationItemId)}</li>
* <li>{@link #addCoveredId(SpecificationItemId)}</li>
* <li>{@link #addNeededArtifactType(String)}</li>
* <li>{@link #setLocation(String, int)}</li>
* <li>...</li>
* <li>{@link #endSpecificationItem()}</li>
* </ul>
*
* @param item
* the complete specification item
*/
void addSpecificationItem(final SpecificationItem item);
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ private SpecificationListBuilder(final FilterSettings filterSettings)

/**
* Creates a new {@link SpecificationListBuilder}.
*
*
* @return a new {@link SpecificationListBuilder}.
*/
public static SpecificationListBuilder create()
Expand All @@ -39,7 +39,7 @@ public static SpecificationListBuilder create()
/**
* Creates a new {@link SpecificationListBuilder} with the given
* {@link FilterSettings}.
*
*
* @param filterSettings
* the filter settings for the new builder.
* @return a new {@link SpecificationListBuilder}.
Expand Down Expand Up @@ -139,7 +139,7 @@ public void addTag(final String tag)
public List<SpecificationItem> build()
{
this.endSpecificationItem();
return Collections.unmodifiableList(this.items) ;
return Collections.unmodifiableList(this.items);
}

/**
Expand Down Expand Up @@ -177,14 +177,20 @@ public void endSpecificationItem()
{
final SpecificationItem item = createNewSpecificationItem();
// [impl->dsn~filtering-by-artifact-types-during-import~1]
if (isAccepted(item))
{
addNewItemToList(item);
}
addSpecificationItem(item);
}
resetState();
}

@Override
public void addSpecificationItem(final SpecificationItem item)
{
if (isAccepted(item))
{
addNewItemToList(item);
}
}

// [impl->dsn~cleaning-imported-multi-line-text-elements~1]
private SpecificationItem createNewSpecificationItem()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package org.itsallcode.openfasttrace.api.importer;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.*;
import static org.junit.jupiter.api.Assertions.assertAll;

import java.util.*;
Expand Down Expand Up @@ -57,6 +56,17 @@ void testBuildWithTags()
assertThat(items.get(0).getTags(), containsInAnyOrder("foo", "bar"));
}

@Test
void testAddSpecificationItem()
{
final SpecificationItem item = SpecificationItem.builder().id(ID).build();
final SpecificationListBuilder builder = SpecificationListBuilder.create();

builder.addSpecificationItem(item);

assertThat(builder.build(), contains(item));
}

// [utest->dsn~filtering-by-artifact-types-during-import~1]
@Test
void testFilterArtifactOfType()
Expand Down Expand Up @@ -153,18 +163,17 @@ void testDuplicateIdNotIgnored()
@Test
void testFilterSpecificationItemsByStatus()
{
final Set<ItemStatus> wantedStatuses = new HashSet<>();
wantedStatuses.add(ItemStatus.DRAFT);
final FilterSettings filterSettings = FilterSettings.builder() //
.wantedStatuses(wantedStatuses) //
final FilterSettings filterSettings = FilterSettings.builder()
.wantedStatuses(Set.of(ItemStatus.DRAFT))
.build();
final SpecificationListBuilder builder = SpecificationListBuilder
.createWithFilter(filterSettings);
addItemWithStatus(builder, "in-A", ItemStatus.DRAFT);
addItemWithStatus(builder, "out-B", ItemStatus.APPROVED);
addItemWithStatus(builder, "out-C", ItemStatus.PROPOSED);
addItemWithStatus(builder, "out-D", ItemStatus.REJECTED);
addItemWithStatus(builder, "out-E", null); // becomes APPROVED by default
// out-E becomes APPROVED by default
addItemWithStatus(builder, "out-E", null);
final List<SpecificationItem> items = builder.build();
assertThat(items.stream().map(SpecificationItem::getName).toList(),
containsInAnyOrder("in-A"));
Expand Down Expand Up @@ -255,7 +264,6 @@ void testMultilineTextFieldsGetTrimmed()
assertAll(
() -> assertThat(item.getComment(), equalTo("a comment")),
() -> assertThat(item.getDescription(), equalTo("a description")),
() -> assertThat(item.getRationale(), equalTo("a rationale"))
);
() -> assertThat(item.getRationale(), equalTo("a rationale")));
}
}
1 change: 1 addition & 0 deletions doc/changes/changes.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Changes

* [4.7.0](changes_4.7.0.md)
* [4.6.0](changes_4.6.0.md)
* [4.5.0](changes_4.5.0.md)
* [4.4.0](changes_4.4.0.md)
Expand Down
14 changes: 14 additions & 0 deletions doc/changes/changes_4.7.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# OpenFastTrace 4.7.0, released 2026-07-??

Code name: Gherkin Importer

## Summary

OpenFastTrace can now import traced specifications directly from Gherkin
`.feature` files. Annotate scenarios or scenario outlines with an OFT ID and
optional `Covers` and `Needs` metadata; existing coverage tags remain supported
when written in Gherkin comments.

## New Features

* #563: Added Gherkin `.feature` specification import for annotated scenarios and scenario outlines.
156 changes: 0 additions & 156 deletions doc/changesets/563-support-gherkin-feature-specification-documents.md

This file was deleted.

Loading
Loading