如何等到设置 cookie?
How do I wait until a cookie is set?
我正在为我的应用程序的登录功能编写验收测试。在某些时候,我想仔细检查 cookie 的过期时间。
单击 "Login" 按钮后,graphql 查询将发送到我的服务器,该服务器以 Jwt 响应。收到 jwt 后,应用程序将 cookie 设置为
document.cookie = ...
在我的 Cypress 测试中,我通过以下方式检查令牌:
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())
})
})
})
使用 cy.get('@graphql')
,我正在等待 graphql 查询对 return 的响应。别名定义如下:
cy.stub(win, 'fetch', fetch).as('graphql')
收到后,应用程序设置 cookie。
我的问题是我不喜欢下面的电话:
cy.wait(1000)
没有那个电话,我总是得到一个未定义的 cookie。
有没有办法在可能远小于 1000 毫秒的时间内获取该 cookie?我尝试了很多东西都没有成功...
你必须写一个递归的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())
})
})
})
注意功能一定要完善因为
- 我没有参数化
expectedDuration
等(这超出了向您展示如何操作的范围)
- 它会一直等待而不进行循环计数器检查
但它有效(我在回复你之前检查了另一个上下文)如果你有更多的麻烦请分享一个 "working" GitHub 回购所以我可以克隆并用你自己的检查解决方案。
如果不够清楚请告诉我
更新
我们 (me and Tommaso) have written a plugin to help you with this kind of checks, its name is cypress-wait-until.
请为此感谢 Open Source Saturday 社区,我们在其中一个星期六开发了它
根据@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)
})
}
我将其转换为 ES6 class,我发现它更优雅一些:
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)
})
}
}
然后像这样修改了我的步骤:
cy.get('@graphql').then(() => {
const handler = new TokenHandler
handler.getToken().then(token => {
const tokenDuration = getTokenDuration(token)
expect(tokenDuration.asSeconds()).to.equal(expectedDuration.asSeconds())
})
})
这很好用,谢谢。
我不喜欢这里的超时,我不得不说 dom 更改。我根据@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"])
);
我正在为我的应用程序的登录功能编写验收测试。在某些时候,我想仔细检查 cookie 的过期时间。
单击 "Login" 按钮后,graphql 查询将发送到我的服务器,该服务器以 Jwt 响应。收到 jwt 后,应用程序将 cookie 设置为
document.cookie = ...
在我的 Cypress 测试中,我通过以下方式检查令牌:
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())
})
})
})
使用 cy.get('@graphql')
,我正在等待 graphql 查询对 return 的响应。别名定义如下:
cy.stub(win, 'fetch', fetch).as('graphql')
收到后,应用程序设置 cookie。
我的问题是我不喜欢下面的电话:
cy.wait(1000)
没有那个电话,我总是得到一个未定义的 cookie。
有没有办法在可能远小于 1000 毫秒的时间内获取该 cookie?我尝试了很多东西都没有成功...
你必须写一个递归的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())
})
})
})
注意功能一定要完善因为
- 我没有参数化
expectedDuration
等(这超出了向您展示如何操作的范围) - 它会一直等待而不进行循环计数器检查
但它有效(我在回复你之前检查了另一个上下文)如果你有更多的麻烦请分享一个 "working" GitHub 回购所以我可以克隆并用你自己的检查解决方案。
如果不够清楚请告诉我
更新
我们 (me and Tommaso) have written a plugin to help you with this kind of checks, its name is cypress-wait-until.
请为此感谢 Open Source Saturday 社区,我们在其中一个星期六开发了它
根据@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)
})
}
我将其转换为 ES6 class,我发现它更优雅一些:
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)
})
}
}
然后像这样修改了我的步骤:
cy.get('@graphql').then(() => {
const handler = new TokenHandler
handler.getToken().then(token => {
const tokenDuration = getTokenDuration(token)
expect(tokenDuration.asSeconds()).to.equal(expectedDuration.asSeconds())
})
})
这很好用,谢谢。
我不喜欢这里的超时,我不得不说 dom 更改。我根据@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"])
);