Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stubbing not working for 3xx redirect situations due to "Location" header #3190

Closed
tlpbu opened this issue Jan 23, 2019 · 16 comments
Closed
Labels
stale no activity on this issue for a long period type: bug

Comments

@tlpbu
Copy link

tlpbu commented Jan 23, 2019

Current behavior:

When "Location" header is stubbed (common case for 301 and 302 redirects), cypress actually does not set stubbed response status code and header.

cy.route({
    method: 'GET',
    url: '**/profile',
    response: '',
    status: 302,
    headers: {
        'Location': '/web/redirect/target'
  }
})

While Chrome devtools network tab shows response status as it was stubbed, application under test and Cypress itself interprets response with status code 200 and without "Location" header.
See image

Same works with any other header I tried. Wild guess is that "Location" header is internally messing with Cypress' own cy.location() method or window.location.

Desired behavior:

Response should have 302 status code and "Location" header.

Versions

Cypress 3.1.3,
MacOS 10.14.2
Chrome 71

@tlpbu tlpbu changed the title Stubbing not working for 301 and 302 redirect situations due to "Location" header Stubbing not working for 3xx redirect situations due to "Location" header Jan 23, 2019
@jennifer-shehane jennifer-shehane added type: bug stage: needs investigating Someone from Cypress needs to look at this labels Jan 23, 2019
@jennifer-shehane
Copy link
Member

jennifer-shehane commented Jan 23, 2019

It doesn't seem to matter whether you set the 'Location' in the headers, stubbing the response as 302 response responds with 200 status code.

Reproducible code

it('302 route', () => {
  cy.visit('http://example.cypress.io/commands/network-requests')
  cy.server()

  cy.route({
    method: 'GET',
    url: 'comments/*',
    status: 302,
    response: '',
    headers: {
      'Location': 'https://example.cypress.io/'
    }
  }).as('getComments')

  cy.get('.network-btn').click()
  cy.wait('@getComments').its('status').should('eq', 302)
})

screen shot 2019-01-23 at 4 03 14 pm

Edited with correct reproducible example

@tlpbu
Copy link
Author

tlpbu commented Jan 23, 2019

cy.wait('@getcomments').its('status').should('eq', 200)

should be cy.wait('@getComments').its('status').should('eq', 302) and then it passes.

But this would fail and redirect would never occur:

it('302 route', () => {
  cy.visit('http://example.cypress.io/commands/network-requests')
  cy.server()

  cy.route({
      method: 'GET',
      url: 'comments/*',
      status: 302,
      response: '',
      headers: {
          'Location': 'https://example.cypress.io/'
      }
  }).as('getComments')

  cy.get('.network-btn').click()
  cy.wait('@getComments').its('status').should('eq', 302)
})

@tlpbu
Copy link
Author

tlpbu commented Jan 23, 2019

Essentially stubbing works with any other header set in route options:

it('302 route something else', () => {
    cy.visit('http://example.cypress.io/commands/network-requests')
    cy.server()

    cy.route({
        method: 'GET',
        url: 'comments/*',
        status: 302,
        response: '',
        headers: {
            'X-something-else': 'https://example.cypress.io/'
        }
    }).as('getComments')

    cy.get('.network-btn').click()
    
    cy.get('@getComments').then(res => {
        expect(res.status).to.eq(302)
        expect(res.response.headers).to.include.keys('X-something-else'.toLowerCase())
    })
})

and same scenario fails with fails with "location" header in route options:

it('302 route location', () => {
  cy.visit('http://example.cypress.io/commands/network-requests')
  cy.server()

  cy.route({
      method: 'GET',
      url: 'comments/*',
      status: 302,
      response: '',
      headers: {
          'Location': 'https://example.cypress.io/'
      }
  }).as('getComments')

  cy.get('.network-btn').click()
  cy.get('@getComments').then(res => {
      expect(res.status).to.eq(302)
      expect(res.response.headers).to.include.keys('Location'.toLowerCase())
  })
})

Spinning icon in Cypress UI it looks like stubbing is still in process.
screenshot 2019-01-23 at 12 41 42

@jennifer-shehane
Copy link
Member

Oops, my bad, copy pasted too quickly. Thanks for clarifying. I'll update my example.

@tlpbu
Copy link
Author

tlpbu commented Jan 25, 2019

@jennifer-shehane please let me know if some additional info is required from me in order get past needs investigating stage.

@tlpbu
Copy link
Author

tlpbu commented Feb 1, 2019

Any progress with this issue?

@jennifer-shehane jennifer-shehane added stage: ready for work The issue is reproducible and in scope and removed stage: needs investigating Someone from Cypress needs to look at this labels Feb 5, 2019
@jennifer-shehane
Copy link
Member

No work has been done on this feature. We will update when work is done.

@jennifer-shehane
Copy link
Member

There was some discussion that this may not be a bug, but reflecting the behavior of how 302 redirects work - by essentially returning a 200 after redirection.

I haven't really found the evidence to support this from the spec or any other docs. @brian-mann

@tlpbu
Copy link
Author

tlpbu commented Feb 12, 2019

behavior of how 302 redirects work - by essentially returning a 200 after redirection

This is apparently how it works in Cypress under the hood right now but IMHO stubbed endpoint should work as described in options and different URI from location header should be the one that should return 200 status code.

Currently it is impossible to write tests that verify
a) redirection works and is not for example blocked by CORS when redirected to SAML site (usually different domain) when session has expired;
b) does not throw unhandled exception due to inability to parse response body when fetching endpoint which usually returns 2xx with body but then suddenly returns 302 without body.

@tlpbu
Copy link
Author

tlpbu commented Apr 17, 2019

@jennifer-shehane any news about when this could be fixed?

@jennifer-shehane
Copy link
Member

This issue still occurs in 9.5.3.

@Gora2ks
Copy link

Gora2ks commented Apr 11, 2022

Yes, true.
Issue still presented.
Could somebody fix it?
Thank you in advance.
Br,
Igor

@cypress-bot cypress-bot bot added stage: icebox and removed stage: ready for work The issue is reproducible and in scope labels Apr 28, 2022
@bradsobie
Copy link

bradsobie commented Sep 9, 2022

I'm building an authentication app which relies on 302 redirects for api calls, and am running into a similar issue.

Example: user enters credentials, I make a fetch POST request with creds which will return a 302 redirect if authentication was successful. Since it's a fetch api call that is returning a 302, the user won't automatically be navigated to the url in the Location header. To fix that I send the request with redirect: "manual", which tells the browser to not actually follow the redirect through, and allows me to intercept the redirect and actually do a window.location.href page change myself given the url from the Location header.

Unfortunately when trying to stub the Location header in cypress, cypress doesn't seem to pass this Location header url along when using redirect: "manual". So when running in cypress my app is redirecting to the original url (the api endpoint in this case), which is not where the user is supposed to go.

I can't share my code directly but the cypress redirect looks something like this:

cy.intercept("POST", "/login", { // login request is made with "redirect: manual"
  statusCode: 302,
  type: "opaqueredirect",
  headers: {
    Location: "http://location.the.user.is.supposed.to.go", // app navigates to /login instead
  },
});

@bradsobie
Copy link

To be clear, I understand that opaqueredirect does not give you access to the headers at all for security reasons, so I'm not attempting to access the redirect via the Location header directly, but rather through the response.url property. When running in a real app/browser, the fetch response.url becomes the value that was in the Location header. In cypress the response.url remains the original url.

@cypress-app-bot
Copy link
Collaborator

This issue has not had any activity in 180 days. Cypress evolves quickly and the reported behavior should be tested on the latest version of Cypress to verify the behavior is still occurring. It will be closed in 14 days if no updates are provided.

@cypress-app-bot cypress-app-bot added the stale no activity on this issue for a long period label May 18, 2023
@cypress-app-bot
Copy link
Collaborator

This issue has been closed due to inactivity.

@cypress-app-bot cypress-app-bot closed this as not planned Won't fix, can't repro, duplicate, stale Jun 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stale no activity on this issue for a long period type: bug
Projects
None yet
Development

No branches or pull requests

5 participants