# cypress-wait-until Add the Cypress waiting power to virtually everything 🎉 [![Build Status](https://travis-ci.com/NoriSte/cypress-wait-until.svg?branch=master)](https://travis-ci.com/NoriSte/cypress-wait-until) [![Build Cron](https://img.shields.io/badge/build%20cron-weekly-44cc11.svg)](https://travis-ci.com/NoriSte/cypress-wait-until) [![Renovate enabled](https://img.shields.io/badge/renovate-enabled-brightgreen.svg)](https://renovatebot.com/) [![Cypress Dashboard](https://img.shields.io/static/v1?label=Cypress&message=Dashboard&color=00BF88)](https://dashboard.cypress.io/#/projects/g21npg/runs) [![NPM downloads](https://img.shields.io/npm/dm/cypress-wait-until?color=CB3836)](https://www.npmjs.com/package/cypress-wait-until)
[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2FNoriSte%2Fcypress-wait-until.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2FNoriSte%2Fcypress-wait-until?ref=badge_shield) [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![TypeScript](https://badges.frapsoft.com/typescript/love/typescript.svg?v=101)](https://github.com/ellerbrock/typescript-badges/) [![Open Source Saturday](https://img.shields.io/badge/%E2%9D%A4%EF%B8%8F-open%20source%20saturday-F64060.svg)](https://www.meetup.com/it-IT/Open-Source-Saturday-Milano/) Use this plugin to wait for everything not expected by [Cypress wait](https://docs.cypress.io/api/commands/wait.html#Syntax). ## Installation ```bash npm i -D cypress-wait-until # or yarn add -D cypress-wait-until ``` ## Usage `cypress-wait-until` extends Cypress' `cy` command. Add this line to your project's `cypress/support/commands.js`: ```javascript import 'cypress-wait-until'; ``` Then, in your test, you can write ```javascript // wait until a cookie is set cy.waitUntil(() => cy.getCookie('token').then(cookie => Boolean(cookie && cookie.value))); // wait until a global variable has an expected value cy.waitUntil(() => cy.window().then(win => win.foo === "bar")); // sync function works too! cy.waitUntil(() => true); // with all the available options cy.waitUntil(() => cy.window().then(win => win.foo === "bar"), { errorMsg: 'This is a custom error message', // overrides the default error message timeout: 2000, // waits up to 2000 ms, default to 5000 interval: 500 // performs the check every 500 ms, default to 200 }); ``` If you return a truthy value, it becomes the subject for the next command. So you can assert about it too ```javascript // wait until the Recaptcha token will be added to the dedicated hidden input field... cy.waitUntil(() => cy.get("input[type=hidden]#recaptchatoken").then($el => $el.val())) // ... then, check that it's valid string asserting about it .then(token => expect(token).to.be.a("string").to.have.length.within(1, 1000)); ``` The `waitUntil` command could be chained to other commands too. As an example, the following codes are equivalent ```javascript cy.waitUntil(() => cy.getCookie('token').then(cookie => cookie.value === '')); // is equivalent to cy.wrap('') .waitUntil((subject) => cy.getCookie('token').then(cookie => cookie.value === subject)); ``` **Please note** - do not expect that the previous command is retried. Only what's inside the `checkFunction` body is retried ```javascript cy.getCookie('token') // will not be retried .waitUntil(cookie => cookie.value === ''); ``` - you cannot put assertions inside `checkFunction`. There is no way to avoid a test failure [if an assertion throws an error](https://stackoverflow.com/questions/56743695/error-handling-using-the-catch-block-in-cypress). You must manually check what the assertions would check for you. The most common case is checking that an element exists or not, instead of using `cy.get('#id').should('exist')` you should check that `Cypress.$('#id').length` is greater than `0`. [Here](https://github.com/NoriSte/cypress-wait-until/issues/75#issuecomment-572685623) you can find a deeper explanation - nested `cy.waitUntil` calls don't respect `timeout` options. `timeout` and `interval` are converted to a number of retries. If the parent `cy.waitUntil` retries 10 times and the nested one 15 times, then the latter will be run 150 times before giving up, resulting in an extremely long wait. See [the discussion](https://github.com/NoriSte/cypress-wait-until/issues/241) for more info ### TypeScript If you use TypeScript you can define the `checkFunction` returning type too. Take a look at the [plugin.spec.ts](./cypress/types/plugin.spec.ts) file to read about the `cy.waitUntil` signature. #### IMPORTANT: 1. Remember to add `cypress-wait-until` to the `cypress/tsconfig.json` file ```javascript { "compilerOptions": { "types": ["cypress", "cypress-wait-until"] } } ``` 2. If you are encountering "cy.waitUntil is not a function" or "cy.waitUntil is undefined", you might need to specify the support file in your `cypress.json` for the import to work correctly: ```javascript { "supportFile": "cypress/support/commands.ts" } ``` ## Arguments - checkFunction A function that must return a truthy value when the wait is over. - options:Object (optional) Pass in an options object to change the default behavior of `cy.waitUntil()`. | Option | Type | Default | Description | | -------------------- | ---------------------- | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | | `errorMsg` | `string` \| `function` | `"Timed out retrying"` | The error message to write. If it's a function, it will receive the last result and the options passed to `cy.waitUntil` | | `timeout` | `number` | `5000` | Time to wait for the `checkFunction` to return a truthy value before throwing an error. | | `interval` | `number` | `200` | Time to wait between the `checkFunction` invocations. | | `description` | `string` | `"waitUntil"` | The name logged into the Cypress Test Runner. | | `logger` | `function` | `Cypress.log` | A custom logger in place of the default [Cypress.log](https://docs.cypress.io/api/cypress-api/cypress-log.html). It's useful just for debugging purposes. | | `log` | `boolean` | `true` | Enable/disable logging. | | `customMessage` | `string` | `undefined` | String logged after the `options.description`. | | `verbose` | `boolean` | `false` | If every single check result must be logged. | | `customCheckMessage` | `string` | `undefined` | Like `customMessage`, but used for every single check. Useless if `verbose` is not set to `true`. | Log options are a lot, take a look at the next screenshot to understand how they are printed ![Plugin log options](assets/log-explained.jpg)

## Could cy.waitUntil avoid failing the test? No. `cy.waitUntil` waits for something that must happen, otherwise the test will fail. [Cypress enforces to avoid conditional testing](https://docs.cypress.io/guides/core-concepts/conditional-testing.html) and the plugin agrees with that. There are cases where conditional testing makes sense but if `cy.waitUntil` would have this capability everyone will use it to create conditional tests, treating the Cypress feature—because avoiding conditional testing is a feature—as "a bug". If you need conditional testing you could write your own recursive promise check function (take a look [here](https://stackoverflow.com/questions/54883861/how-can-i-wait-for-each-element-in-a-list-to-update-to-a-certain-text-using-cypr/55363629#55363629) and [here](https://stackoverflow.com/questions/54732818/how-do-i-wait-until-a-cookie-is-set/54743229#54743229) where it is explained how to do that) where you manage your case. After all, `cy.waitUntil` is just a recursive promise manager 😊 ## Why did we write it? A lot of StackOverflow users had some difficulties in implementing a recursive promise with Cypress just to repeatedly check for something to happen (see two of the various questions about the topic: [How can i wait for each element in a list to update to a certain text?](https://stackoverflow.com/questions/54883861/how-can-i-wait-for-each-element-in-a-list-to-update-to-a-certain-text-using-cypr/55363629#55363629) And [How do I wait until a cookie is set?](https://stackoverflow.com/questions/54732818/how-do-i-wait-until-a-cookie-is-set/54743229#54743229)).
This plugin is dedicated to them ❤️ ## Open Source Saturday This project has been made during one of the Open Source Saturdays, a series of Milan-based events where everyone codes just to spread some Open Source love ❤️ ## Contributing Contributes are welcome, if you need to run the tests through `npm test`, you must update the package.json configuration setting `cypressUploadRecordings` to `false` (or set your own [Cypress recording key](https://docs.cypress.io/guides/guides/command-line.html#Options)). ## Contributors Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):

Stefano Magni

💻 ⚠️ 📖

Tommaso Allevi

💻 ⚠️

brogueady

💻

seeu1

🤔

Sarah Weir

🐛 💻

Lee Alexis

📖

Phil Burrows

🤔 💻 ⚠️

Leo Viezens

📖

Pier-Luc Gendreau

📖
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!