├── LICENSE └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Daniil Shapovalov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cypress101-tips-and-tricks 2 | Welcome to a curated list of **101 Cypress tips and tricks** gathered from my own experience in UI and API automation. This guide is perfect for QA Automation engineers building scalable and maintainable Cypress frameworks 3 | 4 | --- 5 | 6 | ## ✅ Tips 1 - 7: Core Cypress practices 7 | 1. Integrate custom accessibility plugins for your framework, for instance `WICK-A11Y` Cypress open-source Accessibility plugin 8 | 2. Use IDE extensions (e.g., `VS Code Cypress Snippets`) 9 | 3. Use Cypress Cloud for analytics, reporting, and parallel runs, UI Coverage metrics, AI tool 10 | 11 | ### UI elements manipulation 12 | 4. Assert element state by verifying partial class name if nothing else available: `[class*="disabled-true"]` or `[class*="isError-true"]` 13 | 5. Find parent locator of a child element: `cy.get('#element').parent()` 14 | 6. Get element if it is out of visible screen zone: `cy.get('#element').scrollIntoView().click()` 15 | 7. Use `.should()` for UI web elements assertions: `cy.get('#element').should('have.attr', 'data-state', 'off')` 16 | 17 | ## ⚙️ Tips 8 - 30: Advanced Cypress practices 18 | 19 | ### Alert handling 20 | 21 | 8. Handle `window:alert` using `cy.on('window:alert')` for confirm action in browser alert window 22 | ```js 23 | cy.on('window:confirm', (text) => { 24 | expect(text).to.equal('Are you sure?'); 25 | return true; // Simulates clicking "OK" 26 | }); 27 | ``` 28 | 29 | 9. Handle `window:alert` using `cy.on('window:alert')` for abort action in browser alert window 30 | ```js 31 | cy.on('window:confirm', (text) => { 32 | expect(text).to.equal('Are you sure?'); 33 | return false; // Simulates clicking "Cancel" 34 | }); 35 | ``` 36 | 37 | ### Input and assertion tricks 38 | 39 | 10. Use `.clear({ force: true })` for clear input if it is not active input `cy.get('input[name="email"]').clear({ force: true }).type('test@example.com');` 40 | 11. Use RegEx in assertions for reliable text validation, e.g., price ranges - `expect(invokedNormalizedText).to.match(/Min:\s*\d+\sUSD\s*-\s*Max:\s*\d+\sUSD/);` 41 | 42 | ### Test tags and events 43 | 44 | 12. Use test tags to group test suites: 45 | ```js 46 | ///testSuite.spec.js 47 | 48 | describe("Regression Tests", { tags: ["regression"] }, () => { 49 | it("Should test something", () => { 50 | //Test code 51 | }); 52 | }); 53 | ``` 54 | 55 | 13. Use `.trigger()` to simulate browser events. Helps when you need to simulate events like drag-and-drop or custom actions that aren’t directly available through Cypress commands 56 | ```js 57 | cy.get('#paswordInput') 58 | .clear({ force: true }) 59 | .type(phoneNumber) 60 | .trigger("input") 61 | .trigger("change"); 62 | ``` 63 | 64 | ### Configuration and environment setup 65 | 66 | 14. Modify `defaultCommandTimeout` in config for better test control: `defaultCommandTimeout: 40000` 67 | 15. Modify `requestTimeout` / `responseTimeout` in config for network-heavy web applications: `requestTimeout: 20000` or `responseTimeout: 20000` 68 | 16. Create multiple config files for testing different states/environments `cypress.stage.config.js` then run it with `npx cypress run --config-file cypress.stage.config.js --headless` 69 | 17. Create separate file with environment specific URLs for testing different environments by using CLI flag `--env url=stage` 70 | 18. Use `cy.viewport()` for testing mobile responsiveness - `cy.viewport('iphone-16')` 71 | 19. Use `Cypress.env()` to manage environment-specific variables with combination of environment specific URLs 72 | 73 | ```js 74 | // environmentURLs.js 75 | 76 | const URL = { 77 | stage: { 78 | homePage: "https://myhomepage.stage.com", 79 | }, 80 | dev: { 81 | homePage: "https://myhomepage.dev.com", 82 | } 83 | } 84 | 85 | export default URL; 86 | ``` 87 | 88 | ```js 89 | // Get environment variable and default to 'stage' if none is provided 90 | const environment = Cypress.env("url") || "stage"; 91 | // Construct environment specific URL object 92 | const envConfig = URL[environment]; 93 | // Construct environment specific the API URL based on the environment 94 | const homePageURL = envConfig.homePage; 95 | ``` 96 | 97 | 20. Use npm scripts to run Cypress tests efficiently in CLI `npm run cypress:tests:cloud:stage` 98 | ```js 99 | // package.json 100 | "scripts": { 101 | "cypress:tests:cloud:stage": "npx cypress run --config-file cypress.stage.config.js --headless --record --key {{CYPRESS_CLOUD_PROJECT_KEY}} --env url=stage", 102 | }, 103 | ``` 104 | 105 | ## 🔄 Tips 31 - 38: Advanced command chaining & flow control 106 | 107 | 21. Use `cy.should('be.visible').and('include.text', 'Success')` chaining for multiple assertions 108 | 22. Use `cy.clock()` to control time-based logic (timers, intervals, polling logic) 109 | 23. Use `cy.tick()` to simulate time passing instantly 110 | ```js 111 | cy.clock(); 112 | cy.get('#toast-btn').click(); 113 | cy.tick(3000); // Simulates the 3 seconds instantly 114 | cy.get('.toast').should('not.exist'); 115 | ``` 116 | 117 | 24. Chain `.then()` to access values dynamically 118 | ```js 119 | cy.get('#user-email').invoke('text').then((email) => { 120 | expect(email).to.include('@'); 121 | }); 122 | ``` 123 | 124 | 25. Iterate over DOM with `.each()` 125 | ```js 126 | cy.get('.product-item').each(($el) => { 127 | cy.wrap($el).should('be.visible'); 128 | }); 129 | ``` 130 | 131 | 26. Wrap external values using `cy.wrap()` 132 | ```js 133 | const user = { name: 'John' }; 134 | cy.wrap(user).its('name').should('eq', 'John'); 135 | ``` 136 | 137 | 27. Use `.spread()` for multiple aliases 138 | ```js 139 | cy.get('.user-name').as('name'); 140 | cy.get('.user-age').as('age'); 141 | 142 | cy.get('@name').then((nameElement) => { 143 | cy.get('@age').then((ageElement) => { 144 | cy.wrap([nameElement.text(), ageElement.text()]).spread((name, age) => { 145 | expect(name).to.not.be.empty; 146 | expect(Number(age)).to.be.greaterThan(0); 147 | }); 148 | }); 149 | }); 150 | ``` 151 | 152 | 28. Invoke jQuery functions with `.invoke()` 153 | ```js 154 | cy.get('#element').invoke('val').should('eq', 'expectedValue'); 155 | ``` 156 | 157 | 29. Use `.its()` to access nested properties 158 | ```js 159 | cy.window().its('navigator.language').should('eq', 'en-US'); 160 | ``` 161 | 162 | 30. Alias values with `.as()` to get them later in your test 163 | ```js 164 | cy.get('#user-profile').as('profile'); 165 | cy.get('@profile').should('be.visible'); 166 | ``` 167 | 168 | 31. Use `Cypress._` for a helpful JavaScript library - Lodash that makes it easier to work with arrays, objects, and data. 169 | ```js 170 | const items = [1, 2, 3]; 171 | const doubled = Cypress._.map(items, (i) => i * 2); 172 | expect(doubled).to.deep.equal([2, 4, 6]); 173 | ``` 174 | 175 | 32. Use `Cypress.Promise` if application or test logic requires asynchronous behavior 176 | ```js 177 | return new Cypress.Promise((resolve) => { 178 | setTimeout(() => { 179 | resolve('done'); 180 | }, 1000); 181 | }); 182 | ``` 183 | 184 | 33. Use `.retry()` plugin for custom retry logic 185 | ```js 186 | cy.get('.status').then(($el) => { 187 | const checkStatus = () => { 188 | if ($el.text() !== 'Ready') { 189 | setTimeout(checkStatus, 500); 190 | } else { 191 | expect($el.text()).to.equal('Ready'); 192 | } 193 | }; 194 | checkStatus(); 195 | }); 196 | ``` 197 | 198 | 34. Manually retry assertions within `.then()` 199 | ```js 200 | cy.get('#user').then(($el) => { 201 | cy.log('User element found:', $el.text()); 202 | }); 203 | ``` 204 | 205 | 35. Use `cy.origin()` for cross-origin testing (Cypress 12+): login on external auth pages, interact with payment gateways, work with widgets hosted on other domains 206 | ```js 207 | // Start from your main app 208 | cy.visit('http://localhost:3000'); 209 | // Click the login button, which redirects to a different domain 210 | cy.get('#login-button').click(); 211 | // Now we enter the new origin block 212 | cy.origin('https://auth.example.com', () => { 213 | // These commands run inside the login page domain 214 | cy.get('#username').type('testuser'); 215 | cy.get('#password').type('testpass'); 216 | cy.get('#submit').click(); 217 | }); 218 | // After login, you're back on your main app domain 219 | cy.url().should('include', 'dashboard'); 220 | ``` 221 | 222 | 36. Scope actions with `.within()`. Inside `.within()` all `cy.get()` commands will search only inner elements, not the whole page. It is faster. 223 | ```js 224 | cy.get('#login-form').within(() => { 225 | cy.get('input[name="email"]').type('user@example.com'); 226 | cy.get('input[name="password"]').type('123456'); 227 | }); 228 | ``` 229 | 230 | 37. Avoid fixed waits `cy.wait(1000)` with `.wait('@alias')` - network request/response to finish. 231 | ```js 232 | cy.intercept('/api/user').as('getUser'); 233 | cy.visit('/dashboard'); 234 | cy.wait('@getUser'); // ✅ wait only until the request finishes 235 | ``` 236 | 237 | 38. Use `cypress-if` plugin for conditional logic 238 | ```js 239 | cy.get('button#subscribe') 240 | .if('exists') // Check if the button exists 241 | .click(); // Click only if it does 242 | ``` 243 | 244 | --- 245 | 246 | ## 🧪 Tips 39 - 56: API testing techniques 247 | 39. Intercept API calls and wait for API response to assert UI element without `cy.wait(ms)`, `cy.wait("@aliasName')` 248 | 40. Use `expect` for API request/response assertions `expect(response.statusCode).to.eq(200);` 249 | 41. Use `cy.task` to operate with Node (e.g., store or fetch values): `cy.task("getItem", "bearerUserToken");` 250 | 42. Pass headers in `cy.request()` when needed: 251 | 252 | ```js 253 | cy.request({ 254 | method: GET, 255 | url: yourURL, 256 | headers: { 257 | Authorization: `Bearer ${bearerUserToken}`, 258 | }, 259 | }); 260 | ``` 261 | 262 | 43. Write custom commands for REST API calls and then use that as a templates for REST API testing 263 | 44. Auto-generate GraphQL commands via introspection. Write custom commands for GraphQL queries and (or) mutations and then use that as a templates for GraphQL testing 264 | 45. Manage tokens for user sessions (e.g., login via API) 265 | 46. Test real-time events using WebSockets (e.g., SignalR) 266 | 47. Validate schemas with Cypress plugin [cypress-ajv-schema-validator](https://github.com/sclavijosuero/cypress-ajv-schema-validator) or `zod` 267 | 48. Chain `cy.request()` calls for API workflows 268 | 49. Assert cookies, headers, and status codes 269 | 50. Prepare data or cleanup using API 270 | 51. Chain UI and API tests for full coverage 271 | 52. Mock API failures (401, 500) to test error states 272 | 53. Use `cy.log()` inside commands to improve readability 273 | 54. Extract dynamic values from API responses 274 | ```js 275 | cy.request('/api/users').then((response) => { 276 | const userId = response.body.data[0].id; 277 | expect(userId).to.exist; 278 | }); 279 | ``` 280 | 281 | 55. Load mock data using `.fixture()` 282 | ```js 283 | cy.fixture('user.json').then((user) => { 284 | expect(user.name).to.equal('Jane'); 285 | }); 286 | ``` 287 | 288 | 56. Use `Wiremock` for API mocking 289 | 290 | --- 291 | 292 | ## 🧠 Tips 57 - 66: Debugging & testing strategy 293 | 294 | 57. Use `debug()` and `pause()`, `cy.stop()` to investigate issues 295 | 58. Use `console.log()` inside `.then()` 296 | 59. Set retries using `Cypress.config('retries', 2)` 297 | 60. Follow Arrange → Act → Assert structure 298 | 61. Test file upload with `.selectFile()` 299 | 62. Use `cy.viewportPreset()` or custom sizes 300 | 63. Use page object pattern for test architecture 301 | 64. Group tests using `describe()` and `.only` 302 | 65. Use `.skip()` or conditional `it()` 303 | 66. Use `beforeEach()` to handle shared setup 304 | 305 | --- 306 | 307 | ## 📦 Tips 67 - 77: DOM, forms, and user interaction 308 | 309 | 67. Simulate typing using `.type({ delay: 100 })` 310 | 68. Test downloads using `cypress-downloadfile` plugin 311 | 69. Use `should('be.visible')` vs `should('exist')` 312 | 70. Force click hidden elements: `.click({ force: true })` 313 | 71. Use `.scrollTo('top'/'bottom')` to navigate view 314 | 72. Use `.check()` and `.uncheck()` for checkboxes 315 | 73. Use `.select()` for dropdowns 316 | 74. Assert form validations dynamically 317 | 75. Use `cy.clock()` and `.type()` for date pickers 318 | 76. Iterate and click multiple elements with `.each()` 319 | 77. Use `cy.submit()` for forms 320 | 321 | --- 322 | 323 | ## 🛡️ Tips 76 – 85: Auth & session management techniques 324 | 325 | 78. Use `cy.session()` to cache login sessions 326 | 79. Login via API and store token in `localStorage` 327 | 80. Clear `localStorage` / `sessionStorage` before each test. By default, Cypress built-in mechanism does it 328 | 81. Use `cy.setCookie()` / `cy.clearCookies()` for cookie handling 329 | 82. Validate cookies with `cy.getCookie()` 330 | 83. Test multi-role access by switching tokens 331 | 84. Verify unauthenticated redirects 332 | 85. Simulate session with `cy.window().then(win => win.sessionStorage.setItem(...))` 333 | 86. Store credentials securely with `Cypress.env()` 334 | 87. Skip login UI flow entirely using `cy.request()` 335 | 336 | --- 337 | 338 | ## 📊 Tips 88 – 96: CI, cloud & reporting 339 | 340 | 88. Use `mochawesome` for advanced reporting 341 | 89. Enable video recording for debugging 342 | 90. Use any CI/CD provider to run your tests automatically: GitHub Actions, GitLab CI, Jenkins, Argo Workflows, etc. 343 | 91. Filter tests using `cypress-grep` plugind by tag or regex 344 | 92. Send test status to Slack via webhook or via Slack integration in the Cypress Cloud 345 | 93. Capture custom screenshots on failure only 346 | 94. Run tests in parallel with Cypress Cloud 347 | 95. Set `baseUrl` in config or CLI for multi-env testing 348 | 96. Use `start-server-and-test` for one-liner dev+test runs 349 | 350 | --- 351 | 352 | ## 🧩 Tips 97 – 101: Final touches 353 | 354 | 97. Test localizations with query param or language switch 355 | 98. Simulate feature flags and toggle behavior in tests 356 | 99. Integrate with Percy or Applitools for visual testing 357 | 100. Create Cypress boilerplate repo for team use 358 | 101. Keep changelog for Cypress updates & breaking changes. And always read the [Cypress Changelog](https://docs.cypress.io/guides/references/changelog) to stay up to date 359 | 360 | --- 361 | 362 | ## 💬 Bonus: Slack notification integration (Cypress Cloud subscription required) 363 | 364 | ### Report Cypress test results to Slack in 4 simple Steps: 365 | 366 | 1. **Create a Cypress Cloud Project** 367 | - Go to [Cypress Dashboard](https://cloud.cypress.io) and create a project. 368 | 369 | 2. **Update `cypress.config.js` with projectId:** 370 | ```js 371 | const { defineConfig } = require('cypress'); 372 | 373 | module.exports = defineConfig({ 374 | projectId: 'your-project-id', 375 | }); 376 | ``` 377 | 378 | 3. **Create a Slack channel** 379 | - Create a dedicated channel (e.g., `#cypress-tests`) in Slack. 380 | 381 | 4. **Connect Slack via Cypress Cloud Integrations** 382 | - In Cypress Cloud: Go to your project → `Settings` → `Integrations` → Add Slack → Select your channel. 383 | 384 | Done! You’ll now receive test run results right in Slack 🚀 385 | 386 | --- 387 | 388 | Contributions welcome! Feel free to suggest new tips via issues or pull requests 🤝 389 | --------------------------------------------------------------------------------