简体   繁体   English

如何等待直到设置了cookie?

[英]How do I wait until a cookie is set?

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. 在某些时候,我想仔细检查Cookie的到期时间。

Upon clicking on the "Login" button, a graphql query is sent to my server which responds with a Jwt. 单击“登录”按钮后,graphql查询将发送到我的服务器,该服务器以Jwt进行响应。 Upon reception of the jwt, the application sets the cookie with 收到jwt时,应用程序将cookie设置为

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. 使用cy.get('@graphql') ,我正在等待graphql查询返回响应。 The alias is defined like this: 别名的定义如下:

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

Upon reception, the application sets the cookie. 接收后,应用程序设置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. 没有那个电话,我总是得到一个未定义的cookie。

Is there a way to get that cookie within some time that might be much less than 1000 ms? 有没有办法在可能少于1000毫秒的时间内获得该Cookie? I tried many things without success... 我尝试了很多事情都没有成功...

You must write a recursive promise function, try the following 您必须编写一个递归的Promise函数,尝试以下方法

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) 我没有参数化expectedDuration等。(这超出了向您展示如何执行此操作的范围)
  • 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. 但是它可以工作(我在回复您之前在其他上下文中进行了检查),如果您还有其他麻烦,请共享一个“有效的” GitHub存储库,以便我可以克隆并使用您自己的解决方案进行检查。

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 . 我们( Tommaso )已经写了一个插件来帮助您进行这种检查,它的名称是cypress-wait-until

Please thank the Open Source Saturday community for that, we developed it during one of them Saturdays 😊 请为此感谢Open Source Saturday社区,我们在其中一个星期六(星期六)开发了它。

I dont like the timeout in this i have to say for dom changes. 我不喜欢超时,我不得不为dom更改而说。 I have come up with this solution based on @NoriSte Answer together with DomMutation Observers. 我已经提出了基于@NoriSte Answer和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"]) ); 

Based on @NoriSte's answer, I came up with the following working code: 基于@NoriSte的答案,我想出了以下工作代码:

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: 我将其转换为ES6类,我发现它更加优雅:

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. 这工作得很好,谢谢。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM