cypress-io / cypress

Fast, easy and reliable testing for anything that runs in a browser.
https://cypress.io
MIT License
47.01k stars 3.18k forks source link

Cookie is handled differently when persisted via browser as opposed to POST request #2502

Closed GrayedFox closed 6 years ago

GrayedFox commented 6 years ago

I know this is quite a complex bug, I apologise for the length of this report. Have tried to be specific as possible!

Current behavior:

Persisting a cookie using the support index file whitelist (for authenticated state) exhibits different behaviour if the cookie is saved after logging in via the browser, compared to logging in directly via the API (using a custom command).

Desired behavior:

Persisting a cookie returned by a POST request should behave the same way, when running tests, when that same cookie is persisted via the a browser submitting a form.

Steps to reproduce:

  1. Create a custom login command which sends a POST request with required credentials, against a service you know only requires a signed auth/session cookie in order to be logged in

Something like:

// streamlined version of our login custom command
Cypress.Commands.add('login', (userType, options = {}) => {
  const loginTypes = {
    mgm: {
      body: {
        email: 'testing@comgy.io',
        password: '123456'
      },
      url: '/managers/sign_in',
      form: true
    }
  }

  const client = loginTypes[userType]

  cy.request({
    method: 'POST',
    url: client.url,
    form: client.form,
    body: client.body
  })
})
  1. Persist the cookie you need to using the whitelist functionality of the support/index.js file:
const customCommands = require('./commands.js')

Cypress.Cookies.defaults({
  whitelist: '_some-auth_session' // assuming this is the cookie set from logging in
})

module.exports = {
  commands: customCommands
}
  1. In a before block, log in using your custom command and before trying to access a page that requires authentication:
describe('/device-store', () => {
  before( () => {
    cy.login('mgm')
  })

  beforeEach( () => {
    cy.visit('/booked-devices')  // requires being authenticated
  })

  it('goes to the expected url that requires authentication', () => {
    cy.url().should('eq', `${Cypress.config().baseUrl}/booked-devices`)
  })

This won't work, but if you replace the before block with code that does something like this:

describe('/device-store', () => {
  before( () => {
    cy.fixture('credentials.json').as('users')
    cy.visit('/managers/sign_in')
    cy.get('@users').then( (users) => {
      const user = users[0]
      cy.get('#manager_email').type(user.email)
      cy.get('#manager_password').type(user.password)
      cy.get('[type="submit"].mdl-button').click()
  })

Then the test works as expected.

Basically:

Versions

Cypress: 3.1.0 Electron: 59 Ubuntu: 18.04 LTS

GrayedFox commented 6 years ago

Umm, just to expose a bit more info:

These tests are basically acceptance tests and will be using entirely stubbed responses - except the login. The login is a nice way to check the server is actually alive and besides this I need a valid, signed cookie during the session and I figure trusting the server to do that each time is something I want to test anyway.

So, really, we need to only login once (and not multiple times in the beforeEach block). Hence why I'd like to choose to do this in a before block and then persist the cookie. Login tests clear the cookie in a beforeEach block, so we're good there.

Not sure if this is related to #2390

GrayedFox commented 6 years ago

Closing, jumped the gun here - the cookies being persisted and leaking into other tests caused me some confusion, this is just a duplicate of https://github.com/cypress-io/cypress/issues/781