Meteor and Cypress v12 sessions

Hello everyone, Cypress recently released version 12, and with it, made cy.session generally available. The docs to cypress can be found here: session | Cypress Documentation

It promises being able to save time between tests by saving an entire browser session, so that you can recall it in another test or another suite and immediately jump into your test code without having to rerun login for example.

I found a setup that works, but it probably isn’t great yet.

  • The validate() method isn’t really useful, as it should ideally check with the server that the user really exists and is logged in
    • The problem here is that I see no easy way around loading an actual page with Meteor and call a method. This requires an additional visit call which is really slow and adds up on long test suites.

Here’s the code:

import { ADMIN_EMAIL, USER_PASSWORD } from '../e2eConstants';

// The cypress visit command also waits for the page to be interactive,
// but we need to wait for Meteor to be ready
// We first wait on "appReady", which is set after meteor.startup() is called
// We then call a basic meteor method to make sure we're talking with the server
Cypress.Commands.overwrite('visit', (originalFn, url, options) => {
  originalFn(url, options);
  cy.window().should('have.property', 'appReady', true);
  cy.checkConnection();
});

Cypress.Commands.add('createSession', (email, password = USER_PASSWORD) => {
  cy.session(
    [email, cy.id],
    () => {
      cy.visit('/login');
      cy.meteorLogin(email, password);
    },
    {
      validate() {
        cy.window().then(win => {
          const loginToken = win.localStorage.getItem('Meteor.loginToken');
          return !!loginToken;
        });
      },
    },
  );
});

describe('Cypress setup', () => {
  before(() => {
    cy.visit('/setupTest');
    cy.callMethod('resetDatabase'); // As it says
    cy.callMethod('generateScenario', { // Our fixture generator
      scenario: {
        users: [
          {
            _id: 'advisor1',
            emails: [{ address: ADMIN_EMAIL, verified: true }],
          },
        ],
      },
    });
    cy.callMethod('setPassword', { // A bit expensive, so we only run it for users that we login with
      userId: 'advisor1',
      password: USER_PASSWORD,
    });
  });

  beforeEach(() => {
    cy.createSession(ADMIN_EMAIL);
    cy.visit('/'); // Does require login
  });

  it('logs in and saves the session', () => {
    cy.routeTo('/loans'); // Instant react-router routing by using reactRouter.push from the window object
    cy.contains('Loans');
  });

  it('is already logged in', () => {
    cy.routeTo('/users');
    cy.contains('Accounts');
  });
});

I’m curious to know if some of you have experimented with this, and found some more reliable/resilient bits of code to work with Cypress and Meteor?

Why do you need to call createSession in beforEach? If you don’t change the account, I think you can call it once in before() function only.

Because from Cypress 12, every it() is a “hard reset” of your browser. It clears cookies, storage, and goes to a blank page.

If you call createSession in a before hook, and one of your tests fails, it will never login again and all subsequent tests will fail too. So you won’t be able to tell which tests are really failing, and which aren’t :slight_smile:

Interesting. Have you try cacheAcrossSpecs option in cy.session?

Yes, what cacheAcrossSpecs does is it will keep the sessions across different describe() in different files. Otherwise by default sessions can only be reused in a single test suite.

So it won’t affect each individual test really.