└── README.md /README.md: -------------------------------------------------------------------------------- 1 | # Cypress cheat sheet 2 | 3 | ``` 4 | These notes were written in mid-2019. 5 | It is possible that the information is no longer up to date. 6 | Please check the official Cypress documentation. 7 | ``` 8 | 9 | - [Setup](#setup) 10 | - [Configuration](#configuration) 11 | - [Aliases](#aliases) 12 | - [Select Elements](#select-elements) 13 | - [Stub XHR Requests](#stub-xhr-requests) 14 | - [Environment Variables](#environment-variables) 15 | - [TypeScript Support](#typescript-support) 16 | - [Create Reports](#create-reports) 17 | - [Recommendations](#Recommendations) 18 | - [Links & Resources](#resources) 19 | - [Links](#links) 20 | - [Online Courses](#online-courses) 21 | - [Plugins](#plugins) 22 | 23 | --- 24 | 25 | ## Setup 26 | 27 | Add Cypress via `npm install cypress --save-dev` or `yarn add cypress --dev` to your project and open the UI after installation with `cypress open`. 28 | You may extend you script section inside your package.json like this: 29 | 30 | ```javascript 31 | "cypress:open": "cypress open", 32 | "cypress:run": "cypress run", 33 | ``` 34 | 35 | The first command will opeh the Cypress UI. The second will run Cypress in headless mode. 36 | 37 | ## Configuration 38 | 39 | The minimum configuration inside the `cypress.json` should contain the baseUrl. 40 | If you set the baseUrl you can omit passing the URL in methods like `cy.visit()`. It may also improve the startup time. 41 | 42 | If you want to block requests from other hosts (e.g. from Google Analytics) add all these hosts to the `blacklistHosts` property. 43 | 44 | ```javascript 45 | // cypress.json 46 | { 47 | "baseUrl": "http://localhost:4200/", 48 | "blacklistHosts": "www.google-analytics.com", 49 | } 50 | ``` 51 | 52 | ## Aliases 53 | 54 | You can define an alias for elements inside `beforeEach()` if you want to select an element in multiple tests. 55 | 56 | ```javascript 57 | beforeEach(() => { 58 | cy.get(".element").as("myElement"); 59 | }); 60 | ``` 61 | 62 | Now you can use the alias inside a test, prefixed with an `@`, like this: 63 | 64 | ```javascript 65 | it("my test", () => { 66 | cy.get("@myElement").click(); 67 | }); 68 | ``` 69 | 70 | ## Select Elements 71 | 72 | Instead of using a CSS Selector to select an element it is considered as best practice to use data-\* attributes for selecting elements. Using data-\* attributes is a more robust solution, because, unlike CSS classes, these do not normally change during development. 73 | 74 | ```html 75 |
Hello world!
76 | ``` 77 | 78 | ```javascript 79 | cy.get("[data-test='myElement']"); 80 | ``` 81 | 82 | You may create a Custom Command for selecting elements and use it inside your tests. 83 | 84 | ```javascript 85 | // Custom Command 86 | Cypress.Commands.add("getElByDataId", (id) => { 87 | cy.get(`[data-test='${id}']`); 88 | }); 89 | 90 | // Usage 91 | cy.getElByDataId("myElement"); 92 | ``` 93 | 94 | ## Stub XHR Requests 95 | 96 | To intercept XHR-Request you can stub calls like this. 97 | 98 | ```javascript 99 | // method, url, response 100 | cy.route("GET", "/myRoute", []); 101 | ``` 102 | 103 | Every call that goes against `/myRoute` will return an empty array as response, as the third parameter describes the response. 104 | 105 | Be aware that Cypress can only intercept XMLHttpRequests. 106 | 107 | ## Environment Variables 108 | 109 | Use environment variables to avoid hard coded string (e.g. for API endpoints) inside your tests, so that you can easily change configurations. 110 | 111 | Either your provide them via CLI 112 | 113 | ```javascript 114 | cypress run --env host=myHost.local,apiEndpoint=http://localhost:8888/api/v1 115 | ``` 116 | 117 | or move them in an environment file: 118 | 119 | ```javascript 120 | // cypress.env.json 121 | { 122 | "host": "myHost.local", 123 | "apiEndpoint": "http://localhost:8888/api/v1" 124 | } 125 | ``` 126 | 127 | You may even combine both approaches and load a dedicated configuration file that you pass via CLI. That is useful if you run your E2E tests inside a CI/CD pipeline and want to change settings depending on the branch. 128 | 129 | First of all you need to write a helper function which is placed inside your plugin file. 130 | 131 | ```javascript 132 | // cypress/plugins/index.js 133 | const fs = require("fs-extra"); 134 | const path = require("path"); 135 | 136 | // get config file and return it 137 | function getConfigurationByFile(file) { 138 | const pathToConfigFile = path.resolve( 139 | __dirname, 140 | "../config/stages", 141 | `${file}.json` 142 | ); 143 | 144 | console.log(`${pathToConfigFile} is used for overriding configurations.`); 145 | return fs.readJson(pathToConfigFile); 146 | } 147 | 148 | module.exports = (on, config) => { 149 | // `on` is used to hook into various events Cypress emits 150 | // `config` is the resolved Cypress config 151 | 152 | // All configs that might be okay 153 | // This is relevant if you resolve config files based on your current git branch. 154 | const allowedConfigs = ["develop", "master"]; 155 | 156 | // Passed config file 157 | const configFile = config.env.configFile; 158 | 159 | // accept a configFile value or use develop by default 160 | const file = allowedConfigs.includes(configFile) ? configFile : "develop"; 161 | return getConfigurationByFile(file); 162 | }; 163 | ``` 164 | 165 | After that you need to define possible configurations. 166 | 167 | ```javascript 168 | // cypress/config/stages/develop.json 169 | { 170 | "host": "dev.mydomain.com", 171 | "apiEndpoint": "http://api.dev.mydomain.com/api/v1" 172 | } 173 | 174 | // cypress/config/stages/master.json 175 | { 176 | "host": "mydomain.com", 177 | "apiEndpoint": "http://api.mydomain.com/api/v1" 178 | } 179 | ``` 180 | 181 | Now you can run Cypress like that: 182 | 183 | ```bash 184 | # Run headless Cypress 185 | cypress run --env configFile=myConfigFile 186 | 187 | # Open Cypress UI 188 | cypress open --env configFile=develop 189 | ``` 190 | 191 | Or even integrate it into your CI/CD pipeline like this: 192 | 193 | ```yaml 194 | # The branch or tag name for which project is built 195 | cypress run --env configFile=$CI_COMMIT_REF_NAME 196 | ``` 197 | 198 | ## TypeScript Support 199 | 200 | If you want to use TypeScript, the existing code base must be adapted in some places. 201 | 202 | Install Cypress Webpack Preprocessor as devDependency with `npm install --save-dev @cypress/webpack-preprocessor` or `yarn add --dev @cypress/webpack-preprocessor`. 203 | 204 | Remove the existing files from your `cypress/` folder and create following files. 205 | 206 | ```javascript 207 | // cypress/plugins/cy-ts-preprocessor.js 208 | const wp = require("@cypress/webpack-preprocessor"); 209 | const webpackOptions = { 210 | resolve: { 211 | extensions: [".ts", ".js"], 212 | }, 213 | module: { 214 | rules: [ 215 | { 216 | test: /\.ts$/, 217 | loaders: ["ts-loader"], 218 | exclude: [/node_modules/], 219 | }, 220 | { 221 | test: /\.(html|css)$/, 222 | loader: "raw-loader", 223 | exclude: /\.async\.(html|css)$/, 224 | }, 225 | { 226 | test: /\.async\.(html|css)$/, 227 | loaders: ["file?name=[name].[hash].[ext]", "extract"], 228 | }, 229 | ], 230 | }, 231 | }; 232 | 233 | const options = { 234 | webpackOptions, 235 | }; 236 | 237 | module.exports = wp(options); 238 | ``` 239 | 240 | ```javascript 241 | // cypress/plugins/index.js 242 | const cypressTypeScriptPreprocessor = require("./cy-ts-preprocessor"); 243 | 244 | module.exports = (on, config) => { 245 | // `on` is used to hook into various events Cypress emits 246 | // `config` is the resolved Cypress config 247 | 248 | // Enable TypeScript. 249 | on("file:preprocessor", cypressTypeScriptPreprocessor); 250 | }; 251 | ``` 252 | 253 | ```javascript 254 | // cypress/support/index.ts 255 | import "./commands"; 256 | ``` 257 | 258 | ```javascript 259 | // cypress/support/commands.ts 260 | export function myFunction(): void { 261 | window.console.log("Hello World!"); 262 | } 263 | Cypress.Commands.add("myFunction", myFunction); 264 | 265 | // Overwrite the given namespace to make the custom commands available. 266 | declare global { 267 | namespace Cypress { 268 | interface Chainable { 269 | myFunction(): typeof myFunction; 270 | } 271 | } 272 | } 273 | ``` 274 | 275 | ```javascript 276 | // cypress/tsconfig.json 277 | { 278 | "compilerOptions": { 279 | "strict": true, 280 | "baseUrl": "../node_modules", 281 | "target": "es2015", 282 | "lib": ["es2015", "dom"], 283 | "types": ["cypress"] 284 | }, 285 | "include": [ 286 | "**/*.ts" 287 | ] 288 | } 289 | ``` 290 | 291 | Now your environment is ready and you can write your tests with TypeScript. 292 | 293 | ## Create Reports 294 | 295 | As Cypress is based on Mocha we can generate reports e.g. with [mochawesome-report-generator](mochawesome-report-generator). 296 | 297 | Install the necessary devDependencies with `npm install --save-dev mocha@5.2.0 mochawesome mochawesome-merge mochawesome-report-generator` or `yarn add --dev mocha@5.2.0 mochawesome mochawesome-merge mochawesome-report-generator`. 298 | Note: It seems that using mocha^6.0.0 throws a TypeError and fails if a report should be generated. Therefore we use mocha@5.2.0 ([GitHub issue](https://github.com/cypress-io/cypress/issues/3537)). 299 | 300 | Extend your cypress.json to define the reporter options. 301 | 302 | ```javascript 303 | { 304 | "reporter": "mochawesome", 305 | "reporterOptions": { 306 | "overwrite": false, 307 | "html": false, 308 | "json": true, 309 | "reportDir": "cypress/reports" 310 | } 311 | } 312 | ``` 313 | 314 | You can generate the reports manually. 315 | 316 | ```bash 317 | # Merge all .json files inside cypress/reports/ into one file named cypress/reports/reports.json. 318 | # After that create a report of this file with mochawesome-report-generator. 319 | mochawesome-merge --reportDir cypress/reports/ > cypress/reports/reports.json && marge cypress/reports/reports.json --reportDir cypress/reports/ 320 | ``` 321 | 322 | If you do not specify the reporter inside the `cypress.json` you can pass the reporter like this: 323 | 324 | ```bash 325 | cypress run --reporter mochawesome 326 | ``` 327 | 328 | Modify the scripts section inside your package.json like this: 329 | 330 | ```javascript 331 | "cypress:run": "cypress run --reporter mochawesome", 332 | "cypress:createReport": "mochawesome-merge --reportDir cypress/reports/ > cypress/reports/reports.json && marge cypress/reports/reports.json --reportDir cypress/reports/" 333 | ``` 334 | 335 | Now you can call `npm run cypress:run && npm run cypress:createReport` or `yarn cypress:run && yarn cypress:createReport` to run the tests and create the report. 336 | You'll find the generated report in `cypress/reports/report.html`. 337 | 338 | ## Recommendations 339 | 340 | - Split application and test logic. Either using Page Objects or Custom Commands. 341 | - Separate test logic and test data. Save all your test data inside `fixtures/` and load them inside the test using `cy.fixture('myFile')`. 342 | - Focus on UI testing (with stubbed XHR) and only test the critical paths E2E. 343 | - Do not slow down your tests with unnecessary sleeping, because Cypress automatically waits. 344 | - Test the application the way a user would use it. If possible use ARIA or data- attributes instead of CSS Selecors. 345 | - Assert frequently so you can reproduce the problem easier & faster. 346 | 347 | ## Resources 348 | 349 | ### Links 350 | 351 | [Documentation](https://docs.cypress.io/guides/overview/why-cypress.html) 352 | 353 | [Gleb Bahmutov](https://glebbahmutov.com/blog/tags/cypress/) 354 | 355 | ### Online Courses 356 | 357 | [Test Production Ready Apps with Cypress](https://egghead.io/courses/test-production-ready-apps-with-cypress) 358 | 359 | [End to End testing with Cypress](https://egghead.io/courses/end-to-end-testing-with-cypress) 360 | 361 | ### Plugins 362 | 363 | [Cypress Image Snapshot](https://www.npmjs.com/package/cypress-image-snapshot) 364 | 365 | [Percy (Visual Testing & Review)](https://docs.percy.io/docs/cypress) 366 | 367 | [cypress-skip-and-only-ui](https://www.npmjs.com/package/cypress-skip-and-only-ui) 368 | 369 | [cypress-watch-and-reload](https://www.npmjs.com/package/cypress-watch-and-reload) 370 | 371 | [cypress-testing-library](https://www.npmjs.com/package/@testing-library/cypress) 372 | --------------------------------------------------------------------------------