Skip to content

#577: Fix Gherkin importer dropping a scenario after a preceding tagged one - #590

Open
adityaanikam wants to merge 2 commits into
itsallcode:mainfrom
adityaanikam:fix-gherkin-consecutive-tagged-scenarios-577
Open

#577: Fix Gherkin importer dropping a scenario after a preceding tagged one#590
adityaanikam wants to merge 2 commits into
itsallcode:mainfrom
adityaanikam:fix-gherkin-consecutive-tagged-scenarios-577

Conversation

@adityaanikam

Copy link
Copy Markdown

Fixes #577

Root cause

GherkinLineConsumer.readLine() only ends the current scenario (and goes back to looking for the next scenario's metadata) when it sees a Scenario/Scenario Outline/Feature/Rule/Background/Examples boundary line. A @id: tag line matches none of those.

So while a scenario's steps are still being streamed into its description, a @id: tag line for the next scenario falls into the same branch as an ordinary step line and gets appended to the current scenario's description instead of being read as metadata:

if (this.importingScenario)
{
    if (!line.trim().startsWith("#") && !line.trim().isEmpty())
    {
        this.listener.appendDescription(line + System.lineSeparator());
    }
    return;
}
readMetadata(lineNumber, line);

The next scenario is then left without a pending id, and is silently skipped when its Scenario: line is reached (beginScenario bails out when pendingId == null). This only shows up when two tagged scenarios are consecutive — i.e. nothing Gherkin considers a block boundary sits between them — which is exactly why the documented workaround (inserting a throwaway Rule: line) works: a Rule: line is such a boundary.

Fix

dsn~gherkin.streaming-import~1 defines the streaming condition as running "until a Gherkin block boundary", and dsn~gherkin.id-detection~1 requires the @id: tag to be the "immediately preceding" tag for a scenario. A tag line is a boundary in exactly that sense — it's where the next scenario's metadata begins. So a tag line encountered while still streaming a scenario should end the current scenario and be handed to the normal metadata reader, instead of being swallowed as description text:

if (this.importingScenario)
{
    if (line.trim().startsWith("@"))
    {
        endScenario();
        readMetadata(lineNumber, line);
        return;
    }
    if (!line.trim().startsWith("#") && !line.trim().isEmpty())
    {
        this.listener.appendDescription(line + System.lineSeparator());
    }
    return;
}
readMetadata(lineNumber, line);

I also checked the more obvious-looking alternative of adding @ to the BOUNDARY regex, since that's the pattern already used for the other boundary keywords. That doesn't actually work here: the BOUNDARY branch calls clearMetadata() and returns immediately, so readMetadata would never run on that same line and the tag line's own @id: would still never get captured — it would just trade "swallowed as description" for "silently dropped". Ending the scenario and then explicitly falling through to readMetadata for that line is what makes the tag itself get read.

Testing

Added testImportsConsecutiveTaggedScenariosWithoutAnInterveningBoundary, reproducing the issue's exact repro shape (two consecutive tagged scenarios, nothing but a blank line between them), asserting both scenarios import with the correct ids and that neither scenario's description is corrupted by a leaked tag line.

I verified this test fails for the right reason against the pre-fix code (temporarily reverting only the new @-line branch, keeping the test): it fails on all three counts — the second scenario is missing from the result, the first scenario's description is "Given a precondition\r\n@id:scn~second~1" (the tag line leaking in exactly as the root-cause analysis above describes), and indexing the missing second item throws. Restoring the fix returns the suite to green (29/29).

AI assistance disclosure

Per CONTRIBUTING.md's AI-assisted-coding policy: I used an AI coding agent (Claude Code) to help find the root cause of this issue — tracing through GherkinLineConsumer's state machine against the exact reported repro to identify the mechanism. All code changes (the fix and the test) were made manually.

@github-project-automation github-project-automation Bot moved this to 📫 Backlog in OpenFastTrace Sep 8, 2026
@redcatbear redcatbear added bug ai-assisted Written with the help of an LLM labels Sep 8, 2026
@redcatbear

Copy link
Copy Markdown
Collaborator

Please add a change log entry.

@redcatbear redcatbear left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a change log entry to 4.10.0.

@github-project-automation github-project-automation Bot moved this from 📫 Backlog to 🔨 In Progress in OpenFastTrace Sep 8, 2026
…eding tagged one

GherkinLineConsumer.readLine() only ends the current scenario (and starts
looking for the next one's metadata) on a Scenario/Feature/Rule/Background/
Examples boundary line. A `@id:` tag line matches none of those, so while a
scenario's steps are still being streamed into its description, an `@id:`
tag for the next scenario falls into the same branch as an ordinary step
line and gets appended as more of the current scenario's description
instead of being read as metadata. The next scenario is then left without
a pending id and is silently skipped on its Scenario: line.

This only shows up when two tagged scenarios are consecutive, i.e. nothing
Gherkin considers a block boundary sits between them - the documented
workaround is inserting a throwaway `Rule:` line, which is exactly such a
boundary.

dsn~gherkin.streaming-import~1 defines "until a Gherkin block boundary" as
the streaming condition, and a tag line is a boundary in that sense: it is
where dsn~gherkin.id-detection~1's "immediately preceding @id: tag" for the
next scenario begins. So a tag line encountered while still streaming a
scenario now ends that scenario and is then handed to the normal metadata
reader, instead of being swallowed as description text. This is why
extending the BOUNDARY regex with `@` doesn't work as a fix on its own:
that branch returns immediately after clearing metadata, so the tag itself
would still never be read as an id.

Adds a regression test reproducing the issue's exact repro shape (two
consecutive tagged scenarios, no boundary between them), asserting both
import with the correct ids and neither description is corrupted by the
leaked tag line.
@adityaanikam
adityaanikam force-pushed the fix-gherkin-consecutive-tagged-scenarios-577 branch from 577ff0e to d3db4c7 Compare September 8, 2026 21:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-assisted Written with the help of an LLM bug

Projects

Status: 🔨 In Progress

Development

Successfully merging this pull request may close these issues.

Using OFT with two valid consecutive tagged gherkin scenarios, only the first scenario is imported;

2 participants