1

I need to check the status of the email verification token changed after verifying the email.

First I create a user, then I get the email verification URL sent to the user and finally access this link.

When accessing the app link, it sends the auth token to the API to mark the email as verified. I need cypress to wait for the API response, so I can continue testing...

it('ensure create a user and verify the email address', () => {
  // Create the user
  cy.request('POST', 'app/sign-up.json', {
    user: {
      userName: 'new_user',
      email: 'm@d.c',
      password: 'password',
      fullName: 'new user full name'
    }
  }).should((response) => {
    expect(response.status).to.eq(201)
    // Check if user has been created in database
    cy.task('prisma:user:findMany', {}).then(async (result: any) => {
      expect(result.length).to.eq(1)
      // Get email verification link
      cy.task<Array<BeeQueue.Job<IEmail>>>('beeQueue:emailService:getJobs', 'waiting')
        .should('exist')
        .should('have.length', 1)
        .then((result) => {
          if (result[0].data.text !== null && result[0].data.text !== undefined) {
            const tokenUrl = result[0].data.text.substring(result[0].data.text.lastIndexOf('http://'))
            // Intercept API request
            cy.intercept('GET', '/app/verify_email.json?*').as('verifyEmail')
            cy.visit(tokenUrl.substring(tokenUrl.indexOf('/app')))
            //
            // Here the logic doesn't work, cypress doesn't wait for the request response
            //
            cy.wait('@verifyEmail').its('response.statusCode').should('equal', 200)
          }
        })
    })
  })
})

In the image, we can see that the request happens after the .wait() In the image we can see that the request happens after the wait()

In next image I used wait before wait for intercepted .wait(2000)

cy.wait(2000)
cy.wait('@verifyEmail').its('response.statusCode').should('equal', 200)

I don't think this way (using a fixed wait) is the smart way to do it. But if there is only this way, then why use the wait with intercept?

In this image I used wait before intercept wait(2000)

Matheus Toniolli
  • 438
  • 1
  • 7
  • 16

2 Answers2

2

I suspect it's to do with using cy.visit(). Sometimes Cypress will reset the runner when a call is made with visit().

The reason I suspect that is, in the first log the cy.wait('@verifyEmail') has found a request, but the response is undefined as if the request was cancelled.

You may get a better result using cy.request() instead.

it('ensure create a user and verify the email address', () => {
  // Create the user
  cy.request('POST', 'app/sign-up.json', {
    user: {
      userName: 'new_user',
      email: 'm@d.c',
      password: 'password',
      fullName: 'new user full name'
    }
  }).should((response) => {
    expect(response.status).to.eq(201)
    // Check if user has been created in database
    cy.task('prisma:user:findMany', {}).then(async (result: any) => {
      expect(result.length).to.eq(1)
      // Get email verification link
      cy.task<Array<BeeQueue.Job<IEmail>>>('beeQueue:emailService:getJobs', 'waiting')
        .should('exist')
        .should('have.length', 1)
        .then((result) => {
          if (result[0].data.text !== null && result[0].data.text !== undefined) {
            const tokenUrl = result[0].data.text.substring(result[0].data.text.lastIndexOf('http://'))

            cy.request(tokenUrl.substring(tokenUrl.indexOf('/app')))
              .then((response) => {
                expect(response.status).to.equal(200)
              })
          }
        })
    })
  })
})
Fody
  • 23,754
  • 3
  • 20
  • 37
  • But to test the behavior of the app I need `.visit()`, I thought I was doing something wrong..... I'll mark this post in an issue. – Matheus Toniolli Jul 25 '22 at 12:33
  • 1
    There were a couple of questions recently with the same basic problem (but quite different code). One way to do the visit without the janky reset was to wrap it in `cy.origin()`. Although the link being visited may not strictly be a different origin, it does prevent the reset from happening. I suggested `cy.request()` here because the only assertion was checking status, for which cy.request is good enough. – Fody Jul 25 '22 at 20:04
1

(So you're behaving unexpectedly in Cypress)

Before each test, two tasks were done to clean the redis queue and clean the data from the database. I was passing a callBack to the beforeEach function with the argument done.

beforeEach((done) => {
   cy.clearDb().then(() => {
     cy.clearEmailQueueJobs()
     done()
   })
})

At some point one of these tasks was failing and I wasn't catching the error.

As Fody said Cypress was resetting the runner and returning undefined and now we know why.

Looking at the image below you can see an error in Cypress.

Cypress error

The solution was to pass a callback without the done argument and let Cypress handle task errors

beforeEach(() => {
  cy.clearDb()
  cy.clearEmailQueueJobs()
})

Now everything works as expected.

enter image description here

Matheus Toniolli
  • 438
  • 1
  • 7
  • 16