Repository: cucumber/godog
Pull request #669 (2024-12-10 14:16:10)

Add details to "step is undefined" error
<!---
Thanks for helping to make Cucumber better! 💖

You can feel free to open a "Draft" status pull request if you're not ready for feedback yet.

Don't worry about getting everything perfect! We're here to help and will coach you through
to getting your pull request ready to merge.

The prompts below are for guidance to help you describe your change in a way that is most 
likely to make sense to other people when they are reviewing it. Still, it's just a guide, 
so feel free to delete anything that doesn't feel appropriate, and add anything additional 
that seems like it would provide useful context. 👏🏻
-->

### 🤔 What's changed?

<!-- Describe your changes in detail -->

Step text is added to the wrapped error and can help to identify the problematic step.

```
=== RUN   TestAPISuiteRun/Foo
    suite.go:634: step is undefined: I should have "foo" responssssse with body
```

### ⚡️ What's your motivation? 


Currently, the error returned for an undefined step lacks details and relies on formatter context to be actionable
```
=== RUN   TestAPISuiteRun/Foo
    suite.go:634: step is undefined
```

This makes it hard to understand which exact step is the issue.


### 🏷️ What kind of change is this?

<!--- Delete any options that are not relevant -->

- :zap: New feature (non-breaking change which adds new behaviour)

### ♻️ Anything particular you want feedback on?

<!-- 
Is there anything in this change you're unsure about, or would 
particularly like reviewers to give you feedback on?
-->
No.

### 📋 Checklist:

<!--- 
This is to help you remember all the little things we often forget to do!

Feel free to delete any tasks that are not relevant, or add new ones.
-->

- [x] I agree to respect and uphold the [Cucumber Community Code of Conduct](https://github.com/cucumber/.github/tree/main?tab=coc-ov-file)
- [x] I've changed the behaviour of the code
  - [x] I have added/updated tests to cover my changes.
- [ ] My change requires a change to the documentation.
  - [ ] I have updated the documentation accordingly.
- [ ] Users should know about my change
  - [ ] I have added an entry to the "Unreleased" section of the [**CHANGELOG**](../blob/main/CHANGELOG.md), linking to this pull request.

----

*This text was originally generated from a [template](https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/about-issue-and-pull-request-templates), then edited by hand. [You can modify the template here.](https://github.com/cucumber/.github/edit/main/.github/PULL_REQUEST_TEMPLATE.md)*


ahmedaabouzied: LGTM

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5d6c7e1..6be6f8d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,10 @@ This document is formatted according to the principles of [Keep A CHANGELOG](htt
 
 ## Unreleased
 
+### Added
+- Step text is added to "step is undefined" error - ([669](https://github.com/cucumber/godog/pull/669) - [vearutop](https://github.com/vearutop))
+
+### Fixed
 - fix(formatter): On concurrent execution, execute formatter at end of Scenario - ([645](https://github.com/cucumber/godog/pull/645) - [tigh-latte](https://github.com/tigh-latte))
 
 ## [v0.15.0]
diff --git a/suite.go b/suite.go
index 9a38729..b3a87a0 100644
--- a/suite.go
+++ b/suite.go
@@ -270,7 +270,7 @@ func (s *suite) runStep(ctx context.Context, pickle *Scenario, step *Step, scena
 		s.storage.MustInsertPickleStepResult(sr)
 
 		s.fmt.Undefined(pickle, step, match.GetInternalStepDefinition())
-		return ctx, ErrUndefined
+		return ctx, fmt.Errorf("%w: %s", ErrUndefined, step.Text)
 	}
 
 	if scenarioErr != nil {
@@ -461,7 +461,7 @@ func (s *suite) maybeSubSteps(ctx context.Context, result interface{}) (context.
 		}
 
 		if def == nil {
-			return ctx, ErrUndefined
+			return ctx, fmt.Errorf("%w: %s", ErrUndefined, text)
 		} else {
 			ctx, err = s.runSubStep(ctx, text, def)
 			if err != nil {
@@ -609,7 +609,7 @@ func (s *suite) runPickle(pickle *messages.Pickle) (err error) {
 		s.storage.MustInsertPickleResult(pr)
 
 		s.fmt.Pickle(pickle)
-		return ErrUndefined
+		return fmt.Errorf("%w: no steps in scenario", ErrUndefined)
 	}
 
 	// Before scenario hooks are called in context of first evaluated step


Test patch:
diff --git a/internal/formatters/fmt_base_test.go b/internal/formatters/fmt_base_test.go
index ef6e8d2..f14efcf 100644
--- a/internal/formatters/fmt_base_test.go
+++ b/internal/formatters/fmt_base_test.go
@@ -42,7 +42,7 @@ And step failed f2s3:4
 
 Scenario: f2s4
 When step passed f2s4:1
-Then step is undefined f2s4:2
+Then something unknown happens f2s4:2
 And step passed f2s4:3
 `)},
 	)
@@ -116,8 +116,8 @@ scenario "f2s3" passed
 
 step invoked: "f2s4:1", passed
 step "step passed f2s4:1" finished with status passed
-.Ustep "step is undefined f2s4:2" finished with status undefined
-scenario "f2s4" ended with error "step is undefined"
+.Ustep "something unknown happens f2s4:2" finished with status undefined
+scenario "f2s4" ended with error "step is undefined: something unknown happens f2s4:2"
 -step "step passed f2s4:3" finished with status skipped
  13
 
@@ -139,12 +139,12 @@ scenario "f2s4" ended with error "step is undefined"
 
 You can implement step definitions for undefined steps with these snippets:
 
-func stepIsUndefinedFS(arg1, arg2, arg3 int) error {
+func somethingUnknownHappensFS(arg1, arg2, arg3 int) error {
 	return godog.ErrPending
 }
 
 func InitializeScenario(ctx *godog.ScenarioContext) {
-	ctx.Step(`+"`"+`^step is undefined f(\d+)s(\d+):(\d+)$`+"`"+`, stepIsUndefinedFS)
+	ctx.Step(`+"`"+`^something unknown happens f(\d+)s(\d+):(\d+)$`+"`"+`, somethingUnknownHappensFS)
 }
 
 `, out.String())
diff --git a/suite_context_test.go b/suite_context_test.go
index 9b042d8..675389f 100644
--- a/suite_context_test.go
+++ b/suite_context_test.go
@@ -1008,13 +1008,13 @@ func TestTestSuite_Run(t *testing.T) {
 		{
 			name: "undefined_then_pass_no_strict_doesnt_fail_scenario", afterStepCnt: 2, beforeStepCnt: 2, noStrict: true, suitePasses: true,
 			body: `
-					When step is undefined
+					When something unknown happens
 					Then step passes`,
 			log: `
 					>>>> Before suite
 					>> Before scenario "test"
-					Before step "step is undefined"
-					After step "step is undefined", error: step is undefined, status: undefined
+					Before step "something unknown happens"
+					After step "something unknown happens", error: step is undefined: something unknown happens, status: undefined
 					Before step "step passes"
 					After step "step passes", error: <nil>, status: skipped
 					<< After scenario "test", error: <nil>
@@ -1023,14 +1023,14 @@ func TestTestSuite_Run(t *testing.T) {
 		{
 			name: "undefined_then_pass_fails_scenario", afterStepCnt: 2, beforeStepCnt: 2,
 			body: `
-					When step is undefined
+					When something unknown happens
 					Then step passes`,
 			log: `
 					>>>> Before suite
 					>> Before scenario "test"
-					Before step "step is undefined"
-					After step "step is undefined", error: step is undefined, status: undefined
-					<< After scenario "test", error: step is undefined
+					Before step "something unknown happens"
+					After step "something unknown happens", error: step is undefined: something unknown happens, status: undefined
+					<< After scenario "test", error: step is undefined: something unknown happens
 					Before step "step passes"
 					After step "step passes", error: <nil>, status: skipped
 					<<<< After suite`,
@@ -1039,15 +1039,15 @@ func TestTestSuite_Run(t *testing.T) {
 			name: "fail_then_undefined_fails_scenario", afterStepCnt: 2, beforeStepCnt: 2,
 			body: `
 					When step fails
-					Then step is undefined`,
+					Then something unknown happens`,
 			log: `
 					>>>> Before suite
 					>> Before scenario "test"
 					Before step "step fails"
 					After step "step fails", error: oops, status: failed
 					<< After scenario "test", error: oops
-					Before step "step is undefined"
-					After step "step is undefined", error: step is undefined, status: undefined
+					Before step "something unknown happens"
+					After step "something unknown happens", error: step is undefined: something unknown happens, status: undefined
 					<<<< After suite`,
 		},
 		{