Cypress: Is it possible to navigate to a web page and leave it opened for the next tests?

1.9k Views Asked by At

I am planning to update the Cypress version of an existing project from 6.9.1 to 12.6.0.

Currently we are navigating to a web page and logging in with ntlm-auth in a before() hook. After that the web page remains opened and can be used in all tests which are coming next.

In the latest Cypress version it seems that the page is being cleared and closed after each test case, which is the desired behavior to have better test cases as I understand.

But is there a way in the latest Cypress version to navigate to a web page in a before hook or in the first test, leave the page opened, then in the second test case to interact with it and navigate to another sections of the same page, leave the page opened, etc.?

The existing code structure looks like that:

before(() => {

  cy.ntlm(
    ['<url>'],
    <username>,
    <password>
  );

  cy.visit(<url>);

});

it('Test 1', () => {
  cy.contains('something').click();
});

it('Test 2', () => {
  cy.get('#something').type('{enter}');
});

I have tried to save the session with cy.session() in the before hook and my idea was to restore the session/page in the next tests, but I am not sure if this would be the right approach.

2

There are 2 best solutions below

1
agoff On BEST ANSWER

There is a Cypress configuration option to disable test isolation. This can be easily set in your cypress.config.js, in your e2e object.

const { defineConfig } = require('cypress')

module.exports = defineConfig({
  e2e: {
    // e2e options here
    testIsolation: false,
    // more e2e options here
  },
})

By default, testIsolation is set to true, so that should explain the behavior you are currently seeing. Additionally, this Cypress page describes what happens when testIsolation is set to false.

When test isolation is disabled, Cypress will not alter the browser context before the test starts. The page does not clear between tests and cookies, local storage and session storage will be available across tests in that suite. Additionally, the cy.session() command will only clear the current browser context when establishing the browser session - the current page is not cleared.

1
Fody On

You should not turn off test isolation.

Reading through your description, the better approach is to use the cy.session() pattern.

beforeEach(() => {
  cy.session('login', () => {
    cy.ntlm(
      ['<url>'],
      <username>,
      <password>
    );
  })
});

it('Test 1', () => {

  cy.visit('/home-page')

  // test this url
});

it('Test 2', () => {

  cy.visit('/about-page')
  // or 
  cy.visit('/home-page')
  cy.get('menu .about').click()
  
  // test this url
});

What is happening with this pattern

  • beforeEach() is called for every test, making a call to cy.session()

  • cy.session() caches data generated within it's callback, in this case it's login. First test actually does the login. Second test restores the data from previous call - effectively before() logic (i.e login only happens once).

  • cy.visit() is done in each test so that you are at the right page for that test

  • tests remain isolated, you get flaky bugs that are hard to track down.


If you are finding cy.session() difficult to cache everything you need to cache, try cypress-data-session.

Presentation: Flexible Data Setup And Caching For E2E Tests
Repository: bahmutov/cypress-data-session

Example of cypress-data-session

Here is an example of preserving the id from URL across tests (mentioned in comments).

import 'cypress-data-session'

context('data session passing data from one test to another', () => {

  it('test 1 - saves some data to session store', () => {

    // visit a page with an id in the URL
    // note - this is a fake URL, so I'm suppressing the 404 error
    cy.visit('https://example.com/123', {failOnStatusCode:false}) 

    cy.url().then(url => {
      const slug = url.split('/').pop()  // last part of URL

      // save the piece of data I want preserved
      cy.dataSession('slug', () => slug)
    })
  });

  it('test 2 - reads data from context (using function callback)', function() {
    // here we use the variable slug set on the "this" context
    // note - the "function" keyword above is required
    expect(this.slug).to.eq('123')                         // passes
  })

  it('test 3 - reads data from session using getDataSession()', () => {
    // here we use the explict getter for slug
    // which does not require the "function" keyword
    const slug = Cypress.getDataSession('slug')
    expect(slug).to.eq('123')                              // passes
  })
})