9

For one of our applications, I need to set local storage in order to bypass the login page process.

I have the following function that will return the accessToken that I need to set. This function works when running in node.

async function getAccessToken(email, pwd) {
    const form = {email: email, password: pwd};
    let config = {
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
        }
    };
    const accessToken = await axios.post(`${process.env.API_URL}/loginWithToken`, qs.stringify(form), config);
    console.log(accessToken.data.accessToken);
    return accessToken.data.accessToken
}

I'm trying to create a cypress command that will set local storage, then visit the application. When running, I get an error that I returned a promise from a command while also invoking one or more cy commands in that promise.

Cypress.Commands.add("logInAs", async(Useremail, Userpwd, TMURL) => {
    var accessToken = cy.task('getAccessToken', {email: Useremail, pwd: Userpwd
    }).then(Visit =>{
        window.localStorage.setItem("accessToken", accessToken);
        window.localStorage.setItem("refreshToken", accessToken);
        cy.visit(`${process.env.TM_API_URL}/`+TMURL+``);
    });
});

I've also tried the following cypress command

require('dotenv').config();

Cypress.Commands.add('logInAs3', (Useremail, Userpwd, TMURL) => {
        cy.request({
            method: 'POST',
            url: `${process.env.API_URL}/loginWithToken`,
            body: {
                user: {
                    email: Useremail,
                    password: Userpwd,
                }
            }
        })
            .then((resp) => {
                window.localStorage.setItem('accessToken', resp.body.data.data.accessToken);
                window.localStorage.setItem('refreshToken', resp.body.data.data.accessToken);
                cy.visit(`${process.env.TM_API_URL}/`+TMURL+``, {failOnStatusCode: false})
            })
    });

But I get the following error. The URL I need to post to in order to get the access token, is a different domain than the base url. So using the base in the post will not work for me.

cy.request() must be provided a fully qualified url - one that begins with 'http'. By default cy.request() will use either the current window's origin or the 'baseUrl' in cypress.json. Neither of those values were present.

TestRaptor
  • 1,305
  • 8
  • 24
  • 42
  • Please elaborate on `I need to set local storage in order to bypass the login page process`. Do you also control the backend that sets this token? Is that only done in your integration tests? – msanford Jun 03 '19 at 16:39
  • We have a test API that will generate these tokens, so yes, we control it. This app shares some functionality with another app which doesn't allow the typical cypress username/password bypass option to work. I need to grab the token from the response, set the local storage, then navigate to my desired page. – TestRaptor Jun 03 '19 at 18:19

3 Answers3

9

Try this one:

In cypress.json

{
  "env": {
    "EXTERNAL_API": "https://jsonplaceholder.typicode.com/todos/1"
  }
}

In support/commands.js

Cypress.Commands.add('logInAs3', (Useremail, Userpwd, TMURL) => {
  cy.request({
    method: 'POST',
    url: `${Cypress.env('EXTERNAL_API')}/loginWithToken`,
    body: {
      user: {
        email: Useremail,
        password: Userpwd,
      }
    }
  })
    .its('body')
    .then((body) => {
      window.localStorage.setItem('accessToken', body.data.data.accessToken);
      window.localStorage.setItem('refreshToken', body.data.data.accessToken);
    })
});

Inside your test

beforeEach(() => {
  cy.logInAs3()
})

it('check localStorage token', () => {
  cy.visit()
  expect(localStorage.getItem('accessToken')).not.null
  expect(localStorage.getItem('refreshToken')).not.null
})
Danny
  • 682
  • 1
  • 11
  • 21
6

Based on the @Danny answer, you can use the cypress-localstorage-commands package to persist localStorage and reuse same user session for all tests in a block:

In cypress.json

{
  "env": {
    "EXTERNAL_API": "https://jsonplaceholder.typicode.com/"
  }
}

In support/commands.js

import "cypress-localstorage-commands";

Cypress.Commands.add('logInAs', (UserEmail, UserPwd) => {
  cy.request({
    method: 'POST',
    url: `${Cypress.env('EXTERNAL_API')}/loginWithToken`,
    body: {
      user: {
        email: UserEmail,
        password: UserPwd,
      }
    }
  })
    .its('body')
    .then((body) => {
      cy.setLocalStorage("accessToken", body.data.accessToken);
      cy.setLocalStorage("refreshToken", body.data.refreshToken);
    });
});

Inside your tests:

describe("when user FOO is logged in", ()=> {
  before(() => {
    cy.logInAs("foo@foo.com", "fooPassword");
    cy.saveLocalStorage();
  });

  beforeEach(() => {
    cy.restoreLocalStorage();
    cy.visit("");
  });

  it('should exist accessToken in localStorage', () => {
    cy.getLocalStorage("accessToken").should("exist");
  });

  it('should exist refreshToken in localStorage', () => {
    cy.getLocalStorage("refreshToken").should("exist");
  });
});
Javier Brea
  • 1,345
  • 12
  • 16
0

Hitting login API every time a Test Run's cost resources at both(Server and Client)

here is optimized way:

Just store login/auth API Success response in Cypress Env and reuse it in command function

In cypress.json

{
  "myenv": {
    "authResponse": {
      "apiToken": "jwt_token_received_from_server",
      "refreshToken": "refresh_token_received_from_server"
    }
  }
}

In support/commands.js

Cypress.Commands.add('setSession', () => {
  const accessToken = `${Cypress.env('myenv')['authResponse']['apiToken']}`
  const refreshToken = `${Cypress.env('myenv')['authResponse']['refreshToken']}`
  window.localStorage.setItem('accessToken', accessToken);
  window.localStorage.setItem('refreshToken', responseToken);
  })
});

Inside your test

beforeEach(() => {
  cy.setSession();
})

it('Test Ui Components for Authorized User', () => {

})
Jay Soni
  • 461
  • 4
  • 6