Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is there a way to reuse same steps across multiple scenarios ? #190

Closed
BlayckPanthers opened this issue Jan 28, 2025 · 14 comments · Fixed by #192 or #193
Closed

Is there a way to reuse same steps across multiple scenarios ? #190

BlayckPanthers opened this issue Jan 28, 2025 · 14 comments · Fixed by #192 or #193
Assignees
Labels
enhancement New feature or request feature

Comments

@BlayckPanthers
Copy link

Hello,

first of all, good job for your work on this library !

I have this little question regarding the reusability of the steps, is there a way to reuse the steps between scenarios ?

for example if I have this basic .feature :

`Feature: Simple maths
In order to do maths
As a developer
I want to increment variables

Scenario: Increment a variable
    Given a variable is set to 15
    When I increment this variable by 1
    Then the variable should contain 16

Scenario: Increment a variable by two
    Given a variable is set to 30
    When I increment this variable by 2
    Then the variable should contain 17

`

would it be possible to declare only one Given step and reuse it across the two scenarios in my step-definitions files ?

Given('a variable is set to {number}', (_, value: number) => { result = value })

Thanks in advance for your answer :)

@amiceli
Copy link
Owner

amiceli commented Jan 28, 2025

Hi, thank you for your message ;)

Currently vitest-cucumber doesn't provide a way to reuse same steps.

You can do something like that :

describeFeature(feature, (f) => {
    const defineGiven = (Given: StepCallbackDefinition, data : { value : boolean }) => {
        Given('I like boolean', () => {
            data.value = true
        })
    }

    f.Scenario('first scenario', (s) => {
        let params = { value : false }
        defineGiven(s.Given,  params)
        s.Then('I use boolean', () => {
            expect(params.value).toBe(true)
        })
    })
    f.Scenario('second scenario', (s) => {
        let params = { value : false }
        defineGiven(s.Given,  params)
        s.Then('I prefer number', () => {
            expect(params.value).toBe(true)
        })
    })
})

Maybe I can implement a better solution in vitest-cucumber but I've no idea.

If you have an idea can you show a code example what you'd like to use ?

@Odonno
Copy link
Contributor

Odonno commented Jan 28, 2025

It would be interesting to see if a step function can return a generic value instead of void. This can be particularly helpful for initialization steps.

So, per your previous example, it can look like this:

 describeFeature(feature, (f) => {
    const defineGiven = (Given: StepCallbackDefinition) => {
        return Given('I like boolean', () => { // return is mandatory here, because we won't be able to determine the type
            return true; // return a boolean value
        });
    };

    f.Scenario('first scenario', (s) => {
        const result = defineGiven(s.Given);
        s.Then('I use boolean', () => {
            expect(result).toBe(true);
        });
    });
    f.Scenario('second scenario', (s) => {
        const result = defineGiven(s.Given);
        s.Then('I prefer number', () => {
            expect(result).toBe(true);
        });
    });
});

I am not sure if it feasible though. But if it is, it can be really useful. At least when using with react-testing-library.

@amiceli
Copy link
Owner

amiceli commented Jan 28, 2025

Step functions like Given, Then, etc are vitest test function. A scenario call each step with test.each.

test return nothing, this is why I share variable between steps with params.value.

@Odonno
Copy link
Contributor

Odonno commented Jan 28, 2025

Ah yes, I see. An alternative could be to have a per-scenario context. It could be similar to Test Context but instead of it being alive for the time a single test is run, a Scenario Context would be alive for the time of the entire scenario i.e. all the tests of the scenario. See:

 describeFeature(feature, (f) => {
    const defineGiven = (Given: StepCallbackDefinition, ctx) => {
        Given('I like boolean', () => {
            ctx.value = true; // ctx can be manually typed
        });
    };

    f.Scenario('first scenario', (s) => {
        defineGiven(s.Given, s.ctx); // notice how we can get the context of the current scenario
        s.Then('I use boolean', () => {
            expect(s.ctx.value).toBe(true); // and then reuse it later
        });
    });
    f.Scenario('second scenario', (s) => {
        defineGiven(s.Given, s.ctx);
        s.Then('I prefer number', () => {
            expect(s.ctx.value).toBe(true);
        });
    });
});

I checked in the Vitest docs and this is not an existing but I do not see how difficult it would be to do it this way. A simple way could be:

describe("scenario", () => {
  const scenarioContext = {};

  test.each(...);

  // return this and the scenarioContext
});

@amiceli
Copy link
Owner

amiceli commented Jan 29, 2025

Scenario Context is a good idea ;).

I will check if I can use context provides by vitest for test or suite.
Or I'll see how to implement this in vitest-cucumber.

I will make a PR and publish beta version so you can test it ;)

@amiceli amiceli self-assigned this Jan 29, 2025
@amiceli amiceli added enhancement New feature or request feature labels Jan 29, 2025
@Odonno
Copy link
Contributor

Odonno commented Jan 29, 2025

In the same context (no pun intended), you can also implement Feature Context, which will give access to a context that lives the time the feature is running.

I am not sure if that will be useful, but that will exist and will provide a consistent experience/API across the different multi-steps runs. That means for:

  • Feature
  • Scenario
  • ScenarioOutline
  • Background
  • Rule and the related rule scenario

@BlayckPanthers
Copy link
Author

Hello,

Thanks for your feedbacks on the subject.

I was asking for a feature similar to this https://github.com/bencompton/jest-cucumber/blob/2b9c29f510cd6fef93a670f83bfd944cdb1bdfce/docs/AutomaticStepBinding.md

for example I can have many .features files with shared steps that are only defined once.

Do you plan to work on a similar feature anytime soon ?

regards,

@amiceli
Copy link
Owner

amiceli commented Jan 29, 2025

@Odonno Yes, if I find a way to implement scenario context. I will add it too on feature, rule etc.

@BlayckPanthers I'll check jest-cucumber feature and see how to implement something like that on vitest-cucumber.

@amiceli amiceli pinned this issue Jan 29, 2025
@amiceli
Copy link
Owner

amiceli commented Jan 30, 2025

@Odonno I'm working on scenario context. I made something like that.

What do you think about this :

type AwesomeContext = {
    updated: boolean
}

function defineGiven (steppable: StepTest<AwesomeContext>) {
    steppable.Given('I have a scenario context', () => {
        expect(steppable.ctx).toEqual({
            updated: false
        })
    })
}

function defineWhen  (steppable: StepTest<AwesomeContext>) {
    steppable.When('I update scenario context', () => {
        steppable.ctx.updated = true
    })
}

describeFeature(feature, (f) => {
    f.Scenario('use scenario context', (s : StepTest<AwesomeContext>) => {
        s.ctx.updated = false
        defineGiven(s)
        defineWhen(s)
        s.Then('I can check it', () => {
            expect(s.ctx.updated).toBe(true)
        })
    })
})

I see same for feature.ctx, background.ctx, rule.ctx etc.

By default ctx is an empty object.

@Odonno
Copy link
Contributor

Odonno commented Jan 30, 2025

That looks pretty good!

I am not sure if the variable should be named ctx or context though. I don't have a preference so I let you choose the one you think is better.

Well done!

@amiceli
Copy link
Owner

amiceli commented Jan 30, 2025

Same for me ^^

I don't know what is better ctx or context. I will check it.

Thanks for your opinion, I continue this feature ;)

@amiceli amiceli linked a pull request Jan 30, 2025 that will close this issue
@amiceli
Copy link
Owner

amiceli commented Jan 31, 2025

I will a publish a stable version and update doc for scenario, rule context etc. Next I will add some features if needed ;).

@BlayckPanthers Soon I start feature for "common" steps.

@amiceli amiceli reopened this Jan 31, 2025
@amiceli amiceli linked a pull request Feb 1, 2025 that will close this issue
@amiceli
Copy link
Owner

amiceli commented Feb 1, 2025

@BlayckPanthers I'm working on predefined steps feature, so vitest-cucumber will allow to :

  • define steps in Feature
  • define steps in Rule
  • define steps globally

I show an example;)

Feature: Feature.defineSteps
    Scenario: first scenario
        Given I am predefined step
        Then  I am called
    Rule: first rule
        Scenario: second scenario
            Given I am predefined step
            And   I am rule predefined step
            Then  I am called twice

In unit tests :

describeFeature(feature, (f) => {
    f.context.stepCallback = vi.fn()

    f.defineSteps(({ Given }) => {
        Given('I am predefined step', (ctx) => {
            f.context.stepCallback()
        })
    })

    f.Scenario('first scenario', (s) => {
        s.Then('I am called', () => {
            expect(f.context.stepCallback).toHaveBeenCalledTimes(1)
        })
    })

    f.Rule('first rule', (r) => {
        r.defineSteps(({ And }) => {
            And('I am rule predefined step', () => {
                r.context.called = true
            })
        })
        r.RuleScenario('second scenario', (s) => {
            s.Then('I am called twice', () => {
                expect(f.context.stepCallback).toHaveBeenCalledTimes(2)
                expect(r.context.called).toBe(true)
            })
        })
    })
})

Maybe some function names will changed but you see the principle ;).

@amiceli
Copy link
Owner

amiceli commented Feb 3, 2025

@BlayckPanthers defineSteps is now available with v4.6.0 version. Also you can check doc.

@amiceli amiceli unpinned this issue Feb 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request feature
Projects
None yet
3 participants