0
votes

I have react app testing with cypress. I am trying to create custom login command to use it throughout the testing.

The issue is that cy.request('POST', '/api/admin/user/register', {userInfo}) creates actual data in database which I absolutely don't want.

another issue is that 'login' button does not change to 'logout'. On the test, request returns 200, token exist in localStorage. I guess 'isLoggedIn' state (which is saved in redux storage on login success) need to be set in cypress.

How can I work around ?

commands.js

Cypress.Commands.add('login', userType => {
  const types = {
    admin: {
      username,
      companyName,
      password,
      contactNo,
      bankAccountId,
      email,
      mealPrice,
      businessType,
      admin: true,
    },
    user: {
      username,
      companyName,
      password,
      contactNo,
      bankAccountId,
      email,
      mealPrice,
      businessType,
      admin: false,
    },
  };

  const userInfo = types[userType];

  return cy
    .request('POST', '/api/admin/user/register', { userInfo })
.then(()=>{
  cy.route({
    url: '/api/auth/login',
    method: 'POST',
    body: {
      username: userInfo.username,
      password: userInfo.password,
    },
  }).then(({ body }) => window.localStorage.setItem('token', body.token));
})
});

user.spec.js

describe('user', () => {
  beforeEach(() => {});
  it('can visit the app', () => {
    cy.login('user').then(() => {
      expect(window.localStorage.getItem('token')).to.exist;
      cy.visit('/');
      cy.contains('๋กœ๊ทธ์•„์›ƒ');
    });
  });
});

navBar.js

const Navbar = ({ isLoggedIn, handleUserLogout }) => (
  <div className="flex justify-between">
    {/* calling isLoggedIn() directly from localStoragy does not re-render the component on router history change. */}
    {isLoggedIn ? (
      <button
        type="button"
        className="nav--login-btn td-none c-text br f-mini"
        onClick={handleUserLogout}
      >
        logout
      </button>
    ) : (
      <Link className="nav--login-btn td-none c-text br f-mini" to="/login">
        login
      </Link>
    )}
  </div>
);

enter image description here

2

2 Answers

2
votes

I assume the reason why you don't want to write to the DB is because your tests are using the same database/schema that you use in your manual development and you don't want to pollute it every time the tests run.

But if you don't write to DB, how will you be able to test your app? I assume when accessing user data during test lifecycle you'll need an actual user in a database to retrieve data from.

In any case, the real problem isn't data being written to db during tests, the real problem is DB pollution.

In that case, there are two solutions:

  1. Clean up afterwards --- truncate tables, or remove specific rows that were created during tests (e.g. by timestamp).
  2. (Recommended) use a different database/schema for tests, and truncate after, or drop and recreate on each test run.

As for your second problem (which you really should create a new question for): you're storing the token into localStorage on Cypress runner window object, not your app's (AUT) window object.

You'd need to do something like:

/* ... */
.then(({ body }) => {
  cy.window().then( win => {
    win.localStorage.setItem('token', body.token);
  });
});

But it won't solve the UI-not-rerendering issue because this workflow of logging in (I assume) bypasses your normal app log-in workflow (normally, you'd log in using the UI directly, but this may not be a good solution for every test).

0
votes
  1. use Docker PostgreSQL for testing.
  2. login command
   Cypress.Commands.add('login', userType => {

     // visit login page
     cy.visit('/login');

     const types = {
       admin: {
      ...
       },
       user: {
      ...
       },
     };

     // grab the user
     const userInfo = types[userType];
     // login
     cy.get(`[data-testid="username"]`).type(userInfo.username);
     cy.get(`[data-testid="password"]`).type(userInfo.password);
     cy.get('button[type=submit]').click();

     cy.request({
       url: '/api/auth/login',
       method: 'POST',
       body: {
         username: userInfo.username,
         password: userInfo.password,
       },
     }).then(({ body }) =>
       // save token
       cy.window().then(win => {
         win.localStorage.setItem('token', body.token);
         win.sessionStorage.setItem('keepUserLoggedIn', false);
       }),
     );
   });