bencompton / jest-cucumber

Execute Gherkin scenarios in Jest
Apache License 2.0
657 stars 115 forks source link

Adding Compatibility of jest-cucumber with Vitest #219

Closed pplancq closed 5 months ago

pplancq commented 5 months ago

I'm delighted to announce the addition of jest-cucumber compatibility with vitest and any other test runner using Jasmine syntax such as Mocha, Jasmine, etc.

Currently, jest-cucumber works with vitest provided that the "globals : true" option is enabled in the vitest configuration. However, by default, this option is set to "false" and some developers prefer not to expose in globals.

With this update, it is now possible to use jest-cucumber with vitest without having to expose globals. We look forward to your feedback on this improvement.

Example to use jest-cucumber with Vitest:

// logging-in.steps.js
import { describe, test, beforeEach } from 'vitest';
import { loadFeature, defineFeature } from 'jest-cucumber';
import { PasswordValidator } from 'src/password-validator';

const feature = loadFeature(
  'specs/features/basic-scenarios.feature',
  {
    runner: {
      describe,
      test,
    }
  },
);

defineFeature(feature, (test) => {
  let passwordValidator = new PasswordValidator();
  let accessGranted = false;

  beforeEach(() => {
    passwordValidator = new PasswordValidator();
  });

  test('Entering a correct password', ({ given, when, then }) => {
    given('I have previously created a password', () => {
      passwordValidator.setPassword('1234');
    });

    when('I enter my password correctly', () => {
      accessGranted = passwordValidator.validatePassword('1234');
    });

    then('I should be granted access', () => {
      expect(accessGranted).toBe(true);
    });
  });
});
bencompton commented 5 months ago

Hey, thanks for this PR! I’m good with adding Vitest support in this repo for now with the intention of moving it later as long as it’s unobtrusive. I’ll make some time over the next few days to thoroughly review this much appreciated addition.

For more context around "moving it later", I’m a huge fan of the Vite ecosystem and obviously made this package loosely coupled with Jest precisely for this type of use case. I had planned to create a GitHub org with separate repos implementing support for different tools and perhaps even different languages. I’ve made something similar to Jest Cucumber for Golang and Ginkgo recently that I haven’t open sourced yet, for example. I’ve noodled with the idea of maybe porting the core logic to WASM, though there are some problems with that idea. Not sure when I'll have time to focus on that, though, and this PR is a good incremental step.

pplancq commented 5 months ago

Hello, thank you for your feedback, I'm glad you like this addition.

I have to admit that I don't really have a strong preference for 'jest' or 'vitest'. But as these two tools have a similar test syntax I thought it would be a shame to have 2 different libraries to do the same thing. Especially as finally other test runners have the same syntax like Mocha or Jasmine and I'm sure that thanks to this update it should also work.

But I confess I haven't tested it, on the other hand I have tested it with playwright and it also works, here's an example:

import { expect, type Page, test } from '@playwright/test';
import { autoBindSteps, loadFeature, StepDefinitionsWithContext } from 'jest-cucumber';

type Context = Partial<{
  page: Page
}>

const localStep: StepDefinitionsWithContext<Context> = ({ given, when, then, context }) => {
  test.beforeEach(async ({ page }) => {
    context.page = page
  })

  test.afterEach(async ({ }) => {
    context.page = undefined
  })

  given(/goto "(.*)"/, async (link) => {
    await context.page.goto(link);
  })

  when(/Search "(.*)"/, async (search: string) => {
    await context.page.getByRole('button', { name: 'Search or jump to...' }).click();
    await context.page.keyboard.type(search);
    await context.page.keyboard.press('Enter');
  })

  then(/title is "(.*)"/, async (title: string) => {
    await expect(context.page).toHaveTitle(title);
  })
}

const feature = loadFeature('./scenario.feature', {
  loadRelativePath: true,
  runner: {
    describe: test.describe,
    test: test,
  }
});

autoBindSteps(feature, [localStep]);

Note: I've also taken care not to introduce any breaking changes to make adoption easier.

bencompton commented 5 months ago

Looks good overall, but I'd prefer to have a bit more separation for the examples to make sure nobody is confused between Jest and Vitest and that Vitest docs are as clear as possible:

Great idea putting the alternative test runner configuration in the settings for loadFeature, and the ability to use setJestCucumberConfiguration to reduce the boilerplate is definitely a big improvement on the current API.

pplancq commented 5 months ago

Hello, thank you for your feedback

I'll make a note of it and I'll put the documentation back in levels and separate the examples better. It's true that it can be confusing for someone new to (or rediscovering) this library.

pplancq commented 5 months ago

hello,

I've just updated the documentation with your comments and I've also added a vitest folder to the examples.

Please let me know if it seems clearer with these changes.

bencompton commented 5 months ago

Appreciate the updates to the docs and examples! LGTM.

github-actions[bot] commented 5 months ago

:tada: This PR is included in version 4.2.0 :tada:

The release is available on:

Your semantic-release bot :package::rocket:

GHEMID-Mohamed commented 1 month ago

Thank you 🥇