Как мне ждать, пока не будет установлен cookie?
Я пишу приемочные тесты для функции входа в мое приложение. В какой-то момент я хочу перепроверить время истечения cookie.
После нажатия на кнопку "Войти" на мой сервер отправляется запрос 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. Псевдоним определяется так:
cy.stub(win, 'fetch', fetch).as('graphql')
При получении приложение устанавливает cookie.
Моя проблема в том, что я не люблю следующий звонок:
cy.wait(1000)
Без этого звонка я всегда получаю неопределенный файл cookie.
Есть ли способ получить этот файл cookie через какое-то время, которое может быть намного меньше 1000 мс? Я попробовал много вещей без успеха...
2 ответа
Вы должны написать рекурсивную функцию обещания, попробуйте следующее
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
и т. д. (это выходит за рамки того, чтобы показать вам, как это сделать) - он ждет вечно без проверки счетчика циклов
Но это работает (я проверял в другом контексте, прежде чем ответить вам), и если у вас есть еще проблемы, пожалуйста, поделитесь "рабочим" репозиторием GitHub, чтобы я мог клонировать и проверить его с вашим собственным решением.
Дайте мне знать, если это не достаточно ясно,
Мне не нравится тайм-аут в этом, я должен сказать, для изменений dom. Я придумал это решение на основе ответа @NoriSte вместе с 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"])
);
Основываясь на ответе @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 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())
})
})
Это работает отлично, спасибо.