Search code examples
cucumbercypresscypress-cucumber-preprocessor

BeforeEach step is repeated with cy.session using cypress-cucumber-preprocessor


I have a Cypress project where I use the Cypress session API to maintain a session throughout features.

Now I try switching from the deprecated Klaveness Cypress Cucumber Preprocessor to the replacement, Badeball's Cypress Cucumber Preprocessor. But I am running into an issue; the beforeEach() step where my authentication takes place gets repeated several times before the tests start. Eventually, Cypress "snaps out of it" and starts running the actual tests - but obviously this is very resource and time intensive, something is going wrong.

My setup:

Dependencies:

    "cypress": "^9.6.1",
    "@badeball/cypress-cucumber-preprocessor": "^9.1.3",

index.ts:

beforeEach(() => {
  let isAuthInitialized = false;
  function spyOnAuthInitialized(window: Window) {
    window.addEventListener('react:authIsInitialized', () => {
      isAuthInitialized = true;
    });
  }

  login();
  cy.visit('/', { onBeforeLoad: spyOnAuthInitialized });
  cy.waitUntil(() => isAuthInitialized, { timeout: 30000 });
});

login() function:

export function login() {
  cy.session('auth', () => {
    cy.authenticate();
  });
}

As far as I can see, I follow the docs for cy.session almost literally.

My authenticate command has only application specific steps, it does include a cy.visit('/') - after which my application is redirected to a login service (different domain) and then continues.

The problem

cy.session works OK, it creates a session on the first try - then each subsequent time it logs a succesful restore of a valid session. But this happens a number of times, it seems to get stuck in a loop.

Screenshot: Repeated before logs

It looks to me like cy.visit() is somehow triggering the beforeEach() again. Perhaps clearing some session data (localstorage?) that causes my authentication redirect to happen again - or somehow makes Cypress think the test starts fresh. But of course beforeEach() should only happen once per feature.

I am looking at a diff of my code changes, and the only difference except the preprocessor change is:

  • my .cypress-cucumber-preprocessorrc.json (which I set up according to the docs
  • typing changes, this preprocessor is stricter about typings
  • plugins/index.ts file, also set up according to the docs

Am I looking at a bug in the preprocessor? Did I make a mistake? Or something else?


Solution

  • There are two aspects of Cypress + Cucumber with preprocessor that make this potentially confusing

    1. Cypress >10 "Run all specs" behaviour

    As demonstrated in Gleb Bahmutov PhD's great blog post, if you don't configure Cypress to do otherwise, running all specs runs each hook before each test. His proposed solution is to not use the "run all specs" button, which I find excessive - because there are ways around this; see below for a working solution with the Cucumber preprocessor.

    Note: as of Cypress 10, "run all specs" is no longer supported (for reasons related to this unclarity).

    1. Cucumber preprocessor config

    The Cypress Cucumber preprocessor recommends to not use the config option nonGlobalStepDefinitions, but instead configure specific paths like (source):

      "stepDefinitions": [
        "cypress/integration/[filepath]/**/*.{js,ts}",
        "cypress/integration/[filepath].{js,ts}",
        "cypress/support/step_definitions/**/*.{js,ts}",
      ]
    }
    

    What it doesn't explicitly state though, is that the file which includes your hooks (in my case index.ts) should be excluded from these paths if you don't want them to run for each test! I could see how one might think this is obvious, but it's easy to accidentally include your hooks' file in this filepath config.

    TLDR: If I exclude my index.ts file which includes my hooks from my stepDefinitions config, I can use "run all specs" as intended - with beforeEach() running only once before each test.