Undistraction
Undistraction

Reputation: 43569

How can I test a client-side redirect to a 3rd party site with Cypress?

Cypress offers a simple way to test for server-side redirects using request:

cy.request({
  url: `/dashboard/`,
  followRedirect: false, // turn off following redirects
}).then((resp) => {
  expect(resp.redirectedToUrl).to.eq('http://example.com/session/new')
})

However this doesn't work for client-side redirects because the page is loaded successfully before the redirect happens, meaning the response is for the page, not for the redirect.

How can I test a client-side redirect?

I need a way of catching the redirect and verifying that:

Note:

Upvotes: 9

Views: 3892

Answers (3)

user16003578
user16003578

Reputation:

Gleb Bahmutov has an approach at Deal with window.location.replace, which renames window.location in the source to window.__location which is effectively the stub.

It uses cy.intercept() to modify the loading page before it hits the browser, i.e before window.location is instantiated and becomes a totally immutable/incorruptible object.

it('replaces', () => {

  cy.on('window:before:load', (win) => {
    win.__location = {                           // set up the stub
      replace: cy.stub().as('replace')
    }
  })

  cy.intercept('GET', 'index.html', (req) => {   // catch the page as it loads
    req.continue(res => {
      res.body = res.body.replaceAll(
        'window.location.replace', 'window.__location.replace')
    })
  }).as('index')

  cy.visit('index.html')
  cy.wait('@index')

  cy.contains('h1', 'First page')
  cy.get('@replace').should('have.been.calledOnceWith', 'https://www.cypress.io')
})

This stubs replace, but the same should work for the href setter.

Upvotes: 1

myjobistobehappy
myjobistobehappy

Reputation: 776

Here is what I have found as the only was to detect a successful redirect client side if it is to a third-party website. JavaScript has a handy-dandy function called window.open(). There are many things that you can do with it including redirecting the webpage. This can be done by setting the target to _self or to _top. By using a while loop, you are running code as quickly as you can. Here is a rough-draft of how I would record a client-side redirect.

var redirectURL = 'https://www.example.com/',
redirectWindow = window.open(redirectURL, '_top'),
redirected = false,
count = 0;

while(true) {
    if (redirectWindow.document.readyState === 'complete') {
        redirected = true;
        //Your code here. I am just logging that it is in the process of redirection.
        console.log('redirecting')
        break;
    }
    if (count > 2000) {
        redirected = false;
        break;
    }
    count++
    
}
if (redirected == false) {
    console.log('Error: Redirect Failed');
}

Upvotes: 0

Undistraction
Undistraction

Reputation: 43569

Update: This isn't solid. I've just done some refactoring and it seems that even this solution is flawed. It is possible for the redirect to happen in between cypress visiting the page and triggering the cy.wait so the test ends up waiting for something that has already happened. The below might still work for you depending on when your redirect is triggered, but if it's triggered on initialisation, it appears this will not work.

Not really loving this solution as the redirect still happens (I haven't found a way to cancel it), but it at least tests that the redirect happens, and allows me to check the query:

cy.intercept({ pathname: `/sessions/new` }).as(`loginRedirect`)

cy.visit(`/dashboard/`)

cy.location().then(($location) => {
  cy.wait(`@loginRedirect`).then(($interceptor) => {
    const { query } = urlParse($interceptor.request.url)
    expect(query).to.equal(`?token=true&redirect=${$location.href}`)
  })
})

Note: route2 changed to intercept in v6.

Upvotes: 2

Related Questions