How do I wait until a cookie is set?

两盒软妹~` 提交于 2019-12-11 08:07:59

问题


I am writing the acceptance tests for my application's login feature. At some point, I want to double-check the cookie's expiry time.

Upon clicking on the "Login" button, a graphql query is sent to my server which responds with a Jwt. Upon reception of the jwt, the application sets the cookie with

document.cookie = ...

In my Cypress test, I check the token in the following way:

Then("sa session s'ouvre pour {SessionDurationType}", expectedDuration => {
  cy.get('@graphql').then(() => {
    cy.wait(1000)
    cy.getCookie('token').then(cookie => {
      const tokenDuration = getTokenDuration(cookie.value)
     expect(tokenDuration.asSeconds()).to.equal(expectedDuration.asSeconds())
    })
  })
})

With cy.get('@graphql'), I am waiting for the graphql query to return a response. The alias is defined like this:

cy.stub(win, 'fetch', fetch).as('graphql')

Upon reception, the application sets the cookie.

My problem is that I am not fond of the following call:

cy.wait(1000)

Without that call, I always get an undefined cookie.

Is there a way to get that cookie within some time that might be much less than 1000 ms? I tried many things without success...


回答1:


You must write a recursive promise function, try the following

function checkCookie() {
  // cy.getCookie returns a thenebale
  return cy.getCookie('token').then(cookie => {
    const tokenDuration = getTokenDuration(cookie.value);
    // it checks the seconds right now, without unnecessary waitings
    if(tokenDuration.asSeconds() !== expectedDuration.asSeconds()) {
      // waits for a fixed milliseconds amount
      cy.wait(100);
      // returns the same function recursively, the next `.then()` will be the checkCookie function itself
      return checkCookie();
    }
    // only when the condition passes returns a resolving promise
    return Promise.resolve(tokenDuration.asSeconds());
  })
}

Then("sa session s'ouvre pour {SessionDurationType}", expectedDuration => {
  cy.get('@graphql').then(() => {
    checkCookie()
      .then(seconds => {
        expect(seconds).to.equal(expectedDuration.asSeconds())
      })
  })
})

Note that the function must be improved because

  • I didn't parametrize the expectedDuration etc. (it's out of the scope of showing you how to do that)
  • it waits forever without a loop counter check

But it works (I checked in another context before replying to you) and if you have some more troubles please share a "working" GitHub repo so I can clone and check it with your own solution.

Let me know if it isn't enough clear 😉

UPDATE

We (me and Tommaso) have written a plugin to help you with this kind of checks, its name is cypress-wait-until.

Please thank the Open Source Saturday community for that, we developed it during one of them Saturdays 😊




回答2:


I dont like the timeout in this i have to say for dom changes. I have come up with this solution based on @NoriSte Answer together with DomMutation Observers.

   getFileUploadItem().get(".upload-item--state i")
    .should("have.class", "ngx-fileupload-icon--start")
    .then(item => {
        const iconEl = item.get(0);
        const states: string[] = [];

        return new Promise((resolve, reject) => {
          const observer = new MutationObserver((mutations: MutationRecord[]) => {
              const mutationEl = mutations[0].target as HTMLElement;
              const className  = mutationEl.getAttribute("class");

              states.push(className);

              if (className === "ngx-fileupload-icon--uploaded") {
                  resolve(states);
              }
          });

          observer.observe(iconEl, {
              subtree: true,
              attributes: true,
              attributeFilter: ["class"]
          });
        });
    })
    .then((value) => expect(value).to.deep.equal(
      ["ngx-fileupload-icon--progress", "ngx-fileupload-icon--uploaded"])
    );



回答3:


Based on @NoriSte's answer, I came up with the following working code:

function awaitNonNullToken(elapsedTimeInMs = 0) {
  let timeDeltaInMs = 10

  if (elapsedTimeInMs > Cypress.env('timeoutInMs')) {
    return Promise.reject(new Error('Awaiting token timeout'))
  }

  return getTokenCookie().then(cookie => {
    if (cookie === null) {
      cy.wait(timeDeltaInMs)
      elapsedTimeInMs += timeDeltaInMs
      return awaitNonNullToken(elapsedTimeInMs)
    }
    return Promise.resolve(cookie.value)
  })
}

I transformed that into an ES6 class that I find a bit more elegant:

class TokenHandler {
  constructor () {
    this.TIME_DELTA_IN_MS = Cypress.env('timeDeltaInMs')
    this.TIMEOUT_IN_MS = Cypress.env('timeoutInMs')
    this.elapsedTimeInMs = 0
  }

  getToken () {
    if (this.elapsedTimeInMs > this.TIMEOUT_IN_MS) {
      return Promise.reject(new Error('Awaiting token timeout'))
    }
    return getTokenCookie().then(cookie => {
      if (cookie === null) {
        cy.wait(this.TIME_DELTA_IN_MS)
        this.elapsedTimeInMs += this.TIME_DELTA_IN_MS
        return this.getToken()
      }
      return Promise.resolve(cookie.value)
    })
  }
}

and reworked my step like this:

cy.get('@graphql').then(() => {
  const handler = new TokenHandler
  handler.getToken().then(token => {
    const tokenDuration = getTokenDuration(token)
    expect(tokenDuration.asSeconds()).to.equal(expectedDuration.asSeconds())
  })
})

This is working perfectly, thanks.



来源:https://stackoverflow.com/questions/54732818/how-do-i-wait-until-a-cookie-is-set

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!