Search code examples
javascriptcypresstoken

Is it possible to set variable in a before, and reference in a beforeEach in a Cypress test


I have a scenario I'm writing an automated test for. Currently the backend for a new api is not built out, but modules for the front end of the new experience are functioning. I want to write a test for the new experience so it's ready when we are switching to the new api. The new api has enough function to be authenticated via jwt token, and show a validation in console. The problem I'm having is that it works, but is not efficient.

Currently, I am using a beforeEach() block to get the authorized jwt token via POST request. The access token taken from there is used to go to a url, then set the token in session storage. After which, the page with expected content loads.

In context blocks, I validate the expectation of each page (There are 3 for this specific test flow). I would like to make it so that I only hit the API and return the jwt token once, then can call that jwt token for each time the browser is loaded.

Things I have tried:

  • making a before() section that creates the jwt token. Referenced in the beforeEach section. Result is referenceError, oc_token is not defined
  • trying let instead of const; same result as above
  • instead of const; using response.body.access_token.invoke('oc_token').as('oc_token'), and calling it in the beforeEach as this.oc_token; response was typeError - response.body.access.invoke is not a function
  • setting variable as cy.wrap(response.body.access_token).as('oc_token'); calling as this.oc_token; response was TypeErrorcannot read properties of undefined (reading 'oc_token')
  • setting variable as cy.fixture(response.body.access_token).as('oc_token'); calling as either this.oc_token, or just oc_token. result is Error ENAMETOOLING: name too long

Current structure of test file:

describe('New experience test', () => {
  beforeEach(() => {
    cy.interceptFeatureFlags({
      'new-experience-feature-flags': true
    })

    cy.visit(Cypress.config('baseUrl'));
    cy.window().then(w => {
      cy.request({
        method: 'POST',
        url: 'yadda',
        headers: 'stuff',
        body: { payload }
      }).then((response) => {
        expect.(response).property('status').to.equal(200)
        const oc_token = response.body.access_token;
        w.sessionStorage.setItem(
          'jwt',
          oc_token
        )
        w.sessionStorage.setItem(
          'other_jwt',
          oc_token
        )
      })
    });
    cy.fixture('new_experience_context.json').then((context) => {
      button(cy).click();
    });
  });

  context('first page', () => {
    it('shows condition', () => {
      thing.should('exist');
    });

  context('second page', () => {
    beforeEach(() => {
      //steps that get to the second page
    });

    it('shows condition', () => {
      thing.should('exist');
    });
  });

  context('third page', () => {
    beforeEach(() => {
      //steps that get to the second page
      //steps that get to the third page
    });

    it('shows condition', () => {
      thing.should('exist');
    });
  });
});

//functions that identify things in the DOM
   

Structure I've tried:

describe('New experience test', () => {

  before(() => {
    cy.request({
    method: 'POST',
    url: 'yadda',
    headers: 'stuff',
    body: { payload }
   }).then((response) => {
    expect.(response).property('status').to.equal(200)
    const oc_token = response.body.access_token;
  });

  beforeEach(() => {
    cy.interceptFeatureFlags({
      'new-experience-feature-flags': true
    })

    cy.visit(Cypress.config('baseUrl'));
    cy.window().then(w => {
        w.sessionStorage.setItem(
          'jwt',
          oc_token
        )
        w.sessionStorage.setItem(
          'other_jwt',
          oc_token
        )
      })
    });
    cy.fixture('new_experience_context.json').then((context) => {
      button(cy).click();
    });
  });

Solution

  • If I'm reading this correctly, you can do this by declaring the variable in an outer context, the variable will be a "closure" variable.

    describe('New experience test', () => {
    
      let oc_token;
    
      before(() => {
        cy.request({...}).then((response) => {
          oc_token = response.body.access_token; // set outer ("closure") variable
        })
      });
    
      beforeEach(() => {
        cy.interceptFeatureFlags({...})
    
        cy.visit('/')                 // uses baseUrl from config
          .then(w => {
            w.sessionStorage.setItem('jwt', oc_token)
            w.sessionStorage.setItem('other_jwt', oc_token)
          })
        ...
      })
    })
    

    The only problem you might have is if Cypress resets the browser, but it's unlikely if you are visiting baseUrl set in configuration.

    There are other methods such as alias

    describe('New experience test', () => {
    
      before(() => {
        cy.request({...}).then((response) => {
          cy.wrap(response.body.access_token).as('oc_token');  // alias variable
        })
      });
    
      beforeEach(() => {
        cy.interceptFeatureFlags({...})
    
        cy.visit('/')                 // uses baseUrl from config
          .then(w => {
            cy.get('@oc_token').then(oc_token => {     // retrieve alias value
              w.sessionStorage.setItem('jwt', oc_token)
              w.sessionStorage.setItem('other_jwt', oc_token)
            })
          })
        ...
      })
    })
    

    or environment variable

    describe('New experience test', () => {
    
      before(() => {
        cy.request({...}).then((response) => {
          Cypress.env('oc_token', response.body.access_token); // env variable
        })
      });
    
      beforeEach(() => {
        cy.interceptFeatureFlags({...})
    
        cy.visit('/')                 // uses baseUrl from config
          .then(w => {
            w.sessionStorage.setItem('jwt', Cypress.env('oc_token'))
            w.sessionStorage.setItem('other_jwt', Cypress.env('oc_token'))
          })
        ...
      })
    })