{"id":36,"date":"2023-05-30T17:17:32","date_gmt":"2023-05-30T17:17:32","guid":{"rendered":"https:\/\/danigarcia.org\/?p=36"},"modified":"2023-05-30T17:17:34","modified_gmt":"2023-05-30T17:17:34","slug":"configuration-of-a-new-cypress-project-using-typescript-and-cucumber","status":"publish","type":"post","link":"https:\/\/danigarcia.org\/es\/2023\/05\/30\/configuration-of-a-new-cypress-project-using-typescript-and-cucumber\/","title":{"rendered":"Creaci\u00f3n proyecto en Cypress con Typescript y Cucumber"},"content":{"rendered":"\n<p>I admit it: since I discovered Cypress, test automation has never been the same. Although my origins in this field go back to the combination of Java, Gradle, TestNG, Selenium and Jenkins, I have long since migrated to greener fields with Typescript, GitHub Actions, Cypress and Cucumber. Cypress&#8217;s element location management won me over from the start, so it didn&#8217;t take me long to start moving towards this technology.<\/p>\n\n\n\n<p>While Cypress does not provide direct support for working with BDD, there are a number of packages that provide this versatility, making it possible to write test scenarios using natural language and run them directly in Cypress. This synergy facilitates communication between the different roles and improves test visibility across the team, which in turn promotes transparency and a common understanding of the requirements and functionality of the application. In short: it allows the Product Owner and other team members (technical and non-technical) to understand what functionality the tests are covering.<\/p>\n\n\n\n<p>In this article we are going to learn how to set up the skeleton of a small project that will make use of the following technologies to automate the testing of a website:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Typescript<\/li>\n\n\n\n<li>Cypress<\/li>\n\n\n\n<li>Cucumber<\/li>\n<\/ul>\n\n\n\n<p>For this purpose, we will use <a href=\"https:\/\/the-internet.herokuapp.com\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/the-internet.herokuapp.com<\/a> as the target of our tests, as it offers a number of typical functionalities in the world of automation.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Prerequisites<\/h2>\n\n\n\n<p>To carry out this project, we will need the following:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/\" data-type=\"URL\" data-id=\"https:\/\/github.com\/\" target=\"_blank\">GitHub <\/a>account.<\/li>\n\n\n\n<li>A development IDE. Personally, I recommend <a rel=\"noreferrer noopener\" href=\"https:\/\/code.visualstudio.com\/\" data-type=\"URL\" data-id=\"https:\/\/code.visualstudio.com\/\" target=\"_blank\">Visual Studio Code<\/a>.<\/li>\n\n\n\n<li><a rel=\"noreferrer noopener\" href=\"https:\/\/nodejs.org\/\" data-type=\"URL\" data-id=\"https:\/\/nodejs.org\/\" target=\"_blank\">NodeJS + npm<\/a> installed on your computer. If you don&#8217;t use it yet, I recommend <a href=\"https:\/\/github.com\/nvm-sh\/nvm\" data-type=\"URL\" data-id=\"https:\/\/github.com\/nvm-sh\/nvm\" target=\"_blank\" rel=\"noreferrer noopener\">nvm<\/a> to manage NodeJS versions.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Creating the project on GitHub<\/h2>\n\n\n\n<p>Our first step is to log into our GitHub account and create a new repository. We&#8217;ll give it a representative name, a short description, add a README and specify &#8220;Node&#8221; as the .gitignore template.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"746\" height=\"567\" src=\"https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_create_project_git.png\" alt=\"\" class=\"wp-image-42\" srcset=\"https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_create_project_git.png 746w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_create_project_git-300x228.png 300w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_create_project_git-16x12.png 16w\" sizes=\"auto, (max-width: 746px) 100vw, 746px\" \/><\/figure>\n<\/div>\n\n\n<p>With our project created, it&#8217;s time to clone it in our IDE. To do this, we will deploy the &#8220;&lt;&gt; Code&#8221; menu of the repository and copy the repository address. <\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"420\" height=\"379\" src=\"https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_clone_repository-1.png\" alt=\"\" class=\"wp-image-50\" srcset=\"https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_clone_repository-1.png 420w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_clone_repository-1-300x271.png 300w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_clone_repository-1-13x12.png 13w\" sizes=\"auto, (max-width: 420px) 100vw, 420px\" \/><\/figure>\n<\/div>\n\n\n<p>After doing this, we open a console and go to the directory where we want to locally host the repository and run the following (changing the repository address to the one we just created):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">git clone git@github.com:GarciaDan\/cypress-ts-bdd.git<\/code><\/pre>\n\n\n\n<p>If the repository visibility is public, this will work fine. However, if we want to communicate with GitHub effectively and upload our local changes to the remote repository, we will need to use an ssh key.<\/p>\n\n\n\n<p>To create a private key, run the following in a terminal:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">ssh-keygen -t ed25519 -C \"your_email@example.com\"<\/code><\/pre>\n\n\n\n<p>This will generate a private key and a public key:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">Generating public\/private ed25519 key pair.\nEnter file in which to save the key (\/home\/daniel\/.ssh\/id_ed25519):\nEnter passphrase (empty for no passphrase):\nEnter same passphrase again:\nYour identification has been saved in \/home\/daniel\/.ssh\/id_ed25519\nYour public key has been saved in \/home\/daniel\/.ssh\/id_ed25519.pub\nThe key fingerprint is:\nSHA256:+Fss5a3UMmvRY+t6EjbDJC9kVCnWtP0NxsToFsZAZFY your_email@example.com\nThe key's randomart image is:\n+--[ED25519 256]--+\n|         *O=Eo.  |\n|        +ooo=o.  |\n|       o ..o..+  |\n|       .+ . oo o |\n|      .oS=.o  . .|\n|       ..+Oo+    |\n|        ooB*oo   |\n|         =o=o    |\n|        ..+=.    |\n+----[SHA256]-----+<\/code><\/pre>\n\n\n\n<p>Now,  copy the public key content:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">cat ~\/.ssh\/id_ed25519.pub\nssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMkFnvZBzPiSscOcew1THpzm0URhwmNvP1Tfhlld\/KWQ your_email@example.com<\/code><\/pre>\n\n\n\n<p>In GitHub, click on our Avatar, select &#8220;Settings&#8221; and go to the &#8220;SSH and GPG keys&#8221; section. Once there, click on &#8220;New SSH Key&#8221;.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"864\" height=\"545\" src=\"https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_save_ssh_key_in_github.png\" alt=\"\" class=\"wp-image-39\" srcset=\"https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_save_ssh_key_in_github.png 864w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_save_ssh_key_in_github-300x189.png 300w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_save_ssh_key_in_github-768x484.png 768w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_save_ssh_key_in_github-18x12.png 18w\" sizes=\"auto, (max-width: 864px) 100vw, 864px\" \/><\/figure>\n<\/div>\n\n\n<p>Paste the content of the public key into the text box and save the changes.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"578\" height=\"499\" src=\"https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_save_ssh_key_in_github_2.png\" alt=\"\" class=\"wp-image-41\" srcset=\"https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_save_ssh_key_in_github_2.png 578w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_save_ssh_key_in_github_2-300x259.png 300w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_save_ssh_key_in_github_2-14x12.png 14w\" sizes=\"auto, (max-width: 578px) 100vw, 578px\" \/><\/figure>\n<\/div>\n\n\n<p>With this we will now be able to use our private key to connect to our GitHub account. To do this, we&#8217;ll configure our git client. We open a terminal and type the following (if we haven&#8217;t already done so):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">git config --global user.name \"FIRST_NAME LAST_NAME\"\ngit config --global user.email \"your_email@example.com\"<\/code><\/pre>\n\n\n\n<p>Finally, we run the SSH Agent and add the private key by passing the path to the file where we have generated it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">eval \"$(ssh-agent -s)\"\n&gt; Agent pid 59566\nssh-add ~\/.ssh\/id_ed25519<\/code><\/pre>\n\n\n\n<p>With this, we have communication between our remote repository and our local repository.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Creating the node project<\/h2>\n\n\n\n<p>In our local repository, we initialize a new NPM project.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">npm init<\/code><\/pre>\n\n\n\n<p>Now install the necessary packages for our project:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Cypress<\/li>\n\n\n\n<li>Cucumber Preprocessor<\/li>\n\n\n\n<li>EsBuild bundler Preprocessor<\/li>\n\n\n\n<li>Typescript<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">npm i -D cypress\nnpm i -D @badeball\/cypress-cucumber-preprocessor\nnpm i -D @bahmutov\/cypress-esbuild-preprocessor\nnpm i -D typescript\nnpm i -D @types\/node @types\/cypress-cucumber-preprocessor<\/code><\/pre>\n\n\n\n<p>Our <code>package.json<\/code> file will now look like this:<\/p>\n\n\n\n<pre title=\"package.json\" class=\"wp-block-code\"><code lang=\"javascript\" class=\"language-javascript line-numbers\">{\n  \"name\": \"cypress-ts-bdd\",\n  \"version\": \"1.0.0\",\n  \"description\": \"A simple Cypress+Typescript+BDD scaffolding project\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" &amp;amp;&amp;amp; exit 1\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https:\/\/github.com\/GarciaDan\/cypress-ts-bdd.git\"\n  },\n  \"author\": \"Daniel Garcia\",\n  \"license\": \"ISC\",\n  \"bugs\": {\n    \"url\": \"https:\/\/github.com\/GarciaDan\/cypress-ts-bdd\/issues\"\n  },\n  \"homepage\": \"https:\/\/github.com\/GarciaDan\/cypress-ts-bdd#readme\",\n  \"devDependencies\": {\n    \"@badeball\/cypress-cucumber-preprocessor\": \"^17.1.1\",\n    \"@bahmutov\/cypress-esbuild-preprocessor\": \"^2.2.0\",\n    \"@types\/cypress-cucumber-preprocessor\": \"^4.0.1\",\n    \"@types\/node\": \"^20.2.3\",\n    \"cypress\": \"^12.12.0\",\n    \"typescript\": \"^5.0.4\"\n  }\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Cypress default configuration generation<\/h2>\n\n\n\n<p>Cypress generates a default configuration during the first run. Therefore, let&#8217;s open the Cypress interface:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">npx cypress open<\/code><\/pre>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"720\" src=\"https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_first_execution-1024x720.png\" alt=\"\" class=\"wp-image-43\" srcset=\"https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_first_execution-1024x720.png 1024w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_first_execution-300x211.png 300w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_first_execution-768x540.png 768w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_first_execution-18x12.png 18w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_first_execution-340x240.png 340w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_first_execution.png 1276w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n<\/div>\n\n\n<p>Click on &#8220;Continue &gt;&#8221; and select the &#8220;E2E Testing&#8221; option, which is set to &#8220;Not Configured&#8221;.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"720\" src=\"https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_main-1024x720.png\" alt=\"\" class=\"wp-image-44\" srcset=\"https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_main-1024x720.png 1024w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_main-300x211.png 300w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_main-768x540.png 768w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_main-18x12.png 18w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_main-340x240.png 340w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_main.png 1276w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n<\/div>\n\n\n<p>This should generate the default configuration files:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"720\" src=\"https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_configuration_files-1024x720.png\" alt=\"\" class=\"wp-image-45\" srcset=\"https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_configuration_files-1024x720.png 1024w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_configuration_files-300x211.png 300w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_configuration_files-768x540.png 768w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_configuration_files-18x12.png 18w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_configuration_files-340x240.png 340w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_configuration_files.png 1276w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n<\/div>\n\n\n<p>Now, or project files should look like this:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"240\" height=\"260\" src=\"https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_new_files.png\" alt=\"\" class=\"wp-image-49\" srcset=\"https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_new_files.png 240w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_new_files-11x12.png 11w\" sizes=\"auto, (max-width: 240px) 100vw, 240px\" \/><\/figure>\n<\/div>\n\n\n<p>Done this, we define the place where we will store the step definition (Typescript code). We add the following to our <code>package.json<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"javascript\" class=\"language-javascript\">  \"cypress-cucumber-preprocessor\": {\n    \"stepDefinitions\": [\n      \"cypress\/e2e\/**\/*.ts\"\n    ]\n  }<\/code><\/pre>\n\n\n\n<p>So the file should now look like this:<\/p>\n\n\n\n<pre title=\"package.json\" class=\"wp-block-code\"><code lang=\"javascript\" class=\"language-javascript\">{\n  \"name\": \"cypress-ts-bdd\",\n  \"version\": \"1.0.0\",\n  \"description\": \"A simple Cypress+Typescript+BDD scaffolding project\",\n  \"main\": \"index.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" &amp;&amp; exit 1\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https:\/\/github.com\/GarciaDan\/cypress-ts-bdd.git\"\n  },\n  \"author\": \"Daniel Garcia\",\n  \"license\": \"ISC\",\n  \"bugs\": {\n    \"url\": \"https:\/\/github.com\/GarciaDan\/cypress-ts-bdd\/issues\"\n  },\n  \"homepage\": \"https:\/\/github.com\/GarciaDan\/cypress-ts-bdd#readme\",\n  \"devDependencies\": {\n    \"@badeball\/cypress-cucumber-preprocessor\": \"^17.1.1\",\n    \"@bahmutov\/cypress-esbuild-preprocessor\": \"^2.2.0\",\n    \"@types\/cypress-cucumber-preprocessor\": \"^4.0.1\",\n    \"@types\/node\": \"^20.2.3\",\n    \"cypress\": \"^12.12.0\",\n    \"typescript\": \"^5.0.4\"\n  },\n  \"cypress-cucumber-preprocessor\": {\n    \"stepDefinitions\": [\n      \"cypress\/e2e\/**\/*.ts\"\n    ]\n  }\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Typescript configuration<\/h2>\n\n\n\n<p>We will now configure Typescript by creating the <code>tsconfig.json<\/code> file in the root directory of the project. In it we will include the following:<\/p>\n\n\n\n<pre title=\"tsconfig.json\" class=\"wp-block-code\"><code lang=\"javascript\" class=\"language-javascript line-numbers\">{\n    \"compilerOptions\": {\n        \"target\": \"ES2021\",\n        \"lib\": [\"ES2021\", \"DOM\"],\n        \"types\": [\"cypress\", \"node\"],\n        \"moduleResolution\": \"node16\",\n        \"esModuleInterop\": true,\n        \"allowSyntheticDefaultImports\": true\n    },\n    \"include\": [\"**\/*.ts\"]\n}<\/code><\/pre>\n\n\n\n<p>With these options we will tell typescript to transpile the Javascript code in the ES2021 version of ECMAScript, allowing us to use APIs such as <code>array.include()<\/code>, <code>Object.entries()<\/code>, <code>string.trimEnd()<\/code>, <code>promise.any()<\/code>, \u2026.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>By including the &#8220;DOM&#8221; library we will also be able to access DOM objects such as <code>window<\/code> or <code>document<\/code>.<\/li>\n\n\n\n<li>We also include Cypress and NodeJS types, making the resolution of &#8220;<code>node16<\/code>&#8221; modules to support the latest versions of Typescript (from version 4.7 onwards).<\/li>\n\n\n\n<li>Finally, we included the options <code>esModuleInterop<\/code> and <code>allowSyntheticDefaultImports<\/code> to avoid errors when importing certain modules (such as those that do not have a default export).<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Cypress Configuration<\/h2>\n\n\n\n<p>It&#8217;s time to configure Cypress. Although it is not mandatory, we will start by creating a file to define the Cypress tasks. This way we will avoid declaring them directly in the configuration file and their modification will be much easier. For example, we will create a &#8220;dummy&#8221; task which we will call &#8220;stdout&#8221; and which will display a message on the console:<\/p>\n\n\n\n<pre title=\"cypress\/support\/tasks.ts\" class=\"wp-block-code\"><code lang=\"typescript\" class=\"language-typescript line-numbers\">const tasks = {\n    stdout: (...data: Array&lt;any&gt;) =&gt; {\n        console.log(data)\n    }\n};\n\nexport default tasks;<\/code><\/pre>\n\n\n\n<p>We now edit the configuration file <code>cypress.config.ts<\/code>, which looks like this:<\/p>\n\n\n\n<pre title=\"cypress.config.ts\" class=\"wp-block-code\"><code lang=\"typescript\" class=\"language-typescript line-numbers\">import { defineConfig } from \"cypress\";\n\nexport default defineConfig({\n  e2e: {\n    setupNodeEvents(on, config) {\n      \/\/ implement node event listeners here\n    },\n  },\n});<\/code><\/pre>\n\n\n\n<p>We separately define the <code>setupNodeEvents<\/code> function and add the Cucumber preprocessor to it:<\/p>\n\n\n\n<pre title=\"cypress.config.ts\" class=\"wp-block-code\"><code lang=\"typescript\" class=\"language-typescript line-numbers\">import { defineConfig } from \"cypress\";\nimport { addCucumberPreprocessorPlugin } from \"@badeball\/cypress-cucumber-preprocessor\";\n\nasync function setupNodeEvents(\n  on: Cypress.PluginEvents,\n  config: Cypress.PluginConfigOptions\n): Promise&lt;Cypress.PluginConfigOptions&gt; {\n  await addCucumberPreprocessorPlugin(on, config);\n  return config;\n}\n\nexport default defineConfig({\n  e2e: {\n    setupNodeEvents,\n  },\n});<\/code><\/pre>\n\n\n\n<p>Now add, the esbuild bundler preprocessor:<\/p>\n\n\n\n<pre title=\"cypress.config.ts\" class=\"wp-block-code\"><code lang=\"typescript\" class=\"language-typescript line-numbers\">import { defineConfig } from \"cypress\";\nimport { addCucumberPreprocessorPlugin } from \"@badeball\/cypress-cucumber-preprocessor\";\nimport createEsbuildPlugin from \"@badeball\/cypress-cucumber-preprocessor\/esbuild\";\nimport createBundler from \"@bahmutov\/cypress-esbuild-preprocessor\/src\";\n\nasync function setupNodeEvents(\n  on: Cypress.PluginEvents,\n  config: Cypress.PluginConfigOptions\n): Promise&lt;Cypress.PluginConfigOptions&gt; {\n  await addCucumberPreprocessorPlugin(on, config);\n  on(\n    \"file:preprocessor\",\n    createBundler({ plugins: [createEsbuildPlugin(config)] })\n  );\n  return config;\n}\n\nexport default defineConfig({\n  e2e: {\n    setupNodeEvents,\n  },\n});<\/code><\/pre>\n\n\n\n<p>We define the tasks. As we have created them in an external file, we import them and add them to the <code>setupNodeEvents()<\/code> function:<\/p>\n\n\n\n<pre title=\"cypress.config.ts\" class=\"wp-block-code\"><code lang=\"typescript\" class=\"language-typescript line-numbers\">import { defineConfig } from \"cypress\";\nimport { addCucumberPreprocessorPlugin } from \"@badeball\/cypress-cucumber-preprocessor\";\nimport createEsbuildPlugin from \"@badeball\/cypress-cucumber-preprocessor\/esbuild\";\nimport createBundler from \"@bahmutov\/cypress-esbuild-preprocessor\/src\";\nimport tasks from \".\/cypress\/support\/tasks\";\n\nasync function setupNodeEvents(\n  on: Cypress.PluginEvents,\n  config: Cypress.PluginConfigOptions\n): Promise&lt;Cypress.PluginConfigOptions&gt; {\n  await addCucumberPreprocessorPlugin(on, config);\n  on(\n    \"file:preprocessor\",\n    createBundler({ plugins: [createEsbuildPlugin(config)] })\n  );\n  on(\"task\", tasks);\n  return config;\n}\n\nexport default defineConfig({\n  e2e: {\n    setupNodeEvents,\n  },\n});<\/code><\/pre>\n\n\n\n<p>Finally, we add the following configuration to <code>defineConfig.e2e<\/code> in <code>cypress.config.ts<\/code>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>specPattern<\/code>: tells Cypress where to look for (and with which pattern) the Gherkin language features.<\/li>\n\n\n\n<li><code>supportFile<\/code>: tells Cypress where to find the support file (e2e.ts).<\/li>\n\n\n\n<li><code>baseUrl<\/code>: sets the base URL of the project.<\/li>\n<\/ul>\n\n\n\n<pre title=\"cypress.config.ts\" class=\"wp-block-code\"><code lang=\"typescript\" class=\"language-typescript line-numbers\">import { defineConfig } from \"cypress\";\nimport { addCucumberPreprocessorPlugin } from \"@badeball\/cypress-cucumber-preprocessor\";\nimport createEsbuildPlugin from \"@badeball\/cypress-cucumber-preprocessor\/esbuild\";\nimport createBundler from \"@bahmutov\/cypress-esbuild-preprocessor\/src\";\nimport tasks from \".\/cypress\/support\/tasks\";\n\nasync function setupNodeEvents(\n  on: Cypress.PluginEvents,\n  config: Cypress.PluginConfigOptions\n): Promise&lt;Cypress.PluginConfigOptions&gt; {\n  await addCucumberPreprocessorPlugin(on, config);\n  on(\n    \"file:preprocessor\",\n    createBundler({ plugins: [createEsbuildPlugin(config)] })\n  );\n  on(\"task\", tasks);\n  return config;\n}\n\nexport default defineConfig({\n  e2e: {\n    setupNodeEvents,\n    specPattern: \".\/cypress\/e2e\/**\/*.{feature,features}\",\n    supportFile: \".\/cypress\/support\/e2e.ts\",\n\tbaseUrl: \"https:\/\/the-internet.herokuapp.com\"\n  },\n});<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Environment variables<\/h2>\n\n\n\n<p>Instead of defining the environment variables in the configuration file, it is advisable to generate a <code>cypress.env.json<\/code> file and define them inside it. For example, we will define the username and password:<\/p>\n\n\n\n<pre title=\"cypress.env.json\" class=\"wp-block-code\"><code lang=\"javascript\" class=\"language-javascript line-numbers\">{\n    \"USERNAME\": \"tomsmith\",\n    \"PASSWORD\": \"SuperSecretPassword!\"\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Creating a feature<\/h2>\n\n\n\n<p>Now it&#8217;s time to create our first test. Let&#8217;s start by creating the following folder structure:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>cypress\/e2e\/login\/features<\/li>\n\n\n\n<li>cypress\/e2e\/login\/steps<\/li>\n<\/ul>\n\n\n\n<p>Inside <code>cypress\/e2e\/login\/features<\/code>, we create the login-form.feature file and add the following test:<\/p>\n\n\n\n<pre title=\"cypress\/e2e\/login\/features\/login-form.feature\" class=\"wp-block-code\"><code class=\" line-numbers\">Feature: User is able to sign in the web application\n    Scenario: User logs in successfully\n        Given the user navigates to login page\n        When the user provides valid credentials\n        Then the main page is displayed to the authenticated user<\/code><\/pre>\n\n\n\n<p>The next step is, as we can imagine, to implement the steps. <\/p>\n\n\n\n<p>We create the <code>login-form.cy.ts<\/code> file inside the <code>cypress\/e2e\/login\/steps\/ folder<\/code> and implement the three steps we just used in the feature.<\/p>\n\n\n\n<p>The first step makes us visit the login URL:<\/p>\n\n\n\n<pre title=\"\" class=\"wp-block-code\"><code lang=\"typescript\" class=\"language-typescript line-numbers\">import { Given, When, Then } from \"@badeball\/cypress-cucumber-preprocessor\";\n\nconst LOGIN_PAGE_URL = \"login\";\n\nGiven(\"the user navigates to login page\", () =&gt; {\n  cy.visit(LOGIN_PAGE_URL);\n});<\/code><\/pre>\n\n\n\n<p>The second step retrieves the environment variables <code>USERNAME <\/code>and <code>PASSWORD <\/code>from cypress.env.json and uses them to fill in the login form, to press the login button afterwards:<\/p>\n\n\n\n<pre title=\"\" class=\"wp-block-code\"><code lang=\"typescript\" class=\"language-typescript line-numbers\">When(\"the user provides valid credentials\", () =&gt; {\n  const userName = Cypress.env(\"USERNAME\");\n  const password = Cypress.env(\"PASSWORD\");\n\n  cy.get(\"#username\").clear().type(userName);\n  cy.get(\"#password\").clear().type(password);\n\n  cy.get(\"button\").click();\n});<\/code><\/pre>\n\n\n\n<p>The last step is to perform a couple of checks to make sure that the page is displayed correctly.<\/p>\n\n\n\n<pre title=\"\" class=\"wp-block-code\"><code lang=\"typescript\" class=\"language-typescript line-numbers\">Then(\"the main page is displayed to the authenticated user\", () =&gt; {\n  cy.get('[class=\"flash success\"]').should(\"exist\").and(\"be.visible\");\n  cy.get('a[href=\"\/logout\"]').should(\"exist\").and(\"be.visible\");\n});<\/code><\/pre>\n\n\n\n<p>The complete file would be as follows:<\/p>\n\n\n\n<pre title=\"cypress\/e2e\/login\/steps\/login-form.step\" class=\"wp-block-code\"><code lang=\"typescript\" class=\"language-typescript line-numbers\">import { Given, When, Then } from \"@badeball\/cypress-cucumber-preprocessor\";\n\nconst LOGIN_PAGE_URL = \"login\";\n\nGiven(\"the user navigates to login page\", () =&gt; {\n  cy.visit(LOGIN_PAGE_URL);\n});\n\nWhen(\"the user provides valid credentials\", () =&gt; {\n  const userName = Cypress.env(\"USERNAME\");\n  const password = Cypress.env(\"PASSWORD\");\n\n  cy.get(\"#username\").clear().type(userName);\n  cy.get(\"#password\").clear().type(password);\n\n  cy.get(\"button\").click();\n});\n\nThen(\"the main page is displayed to the authenticated user\", () =&gt; {\n  cy.get('[class=\"flash success\"]').should(\"exist\").and(\"be.visible\");\n  cy.get('a[href=\"\/logout\"]').should(\"exist\").and(\"be.visible\");\n});<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">VSCode configuration with Cucumber support<\/h2>\n\n\n\n<p>If you use Visual Studio Code as an editor, it is highly recommended to support Cucumber with the CucumberAutoComplete extension. This extension allows, among other things, to navigate from step definition to implementation, so it can save a significant amount of time during development. You can get the extension <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=alexkrechik.cucumberautocomplete\" data-type=\"URL\" data-id=\"https:\/\/marketplace.visualstudio.com\/items?itemName=alexkrechik.cucumberautocomplete\" target=\"_blank\" rel=\"noreferrer noopener\">here<\/a>.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"664\" height=\"150\" src=\"https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_cucumber_autocomplete.png\" alt=\"\" class=\"wp-image-48\" srcset=\"https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_cucumber_autocomplete.png 664w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_cucumber_autocomplete-300x68.png 300w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_cucumber_autocomplete-18x4.png 18w\" sizes=\"auto, (max-width: 664px) 100vw, 664px\" \/><\/figure>\n<\/div>\n\n\n<p>Once installed, edit the .<code>vscode\/settings.json<\/code> file and add the following:<\/p>\n\n\n\n<pre title=\".vscode\/settings.json\" class=\"wp-block-code\"><code lang=\"javascript\" class=\"language-javascript line-numbers\">{\n    \"cucumberautocomplete.steps\": [\n        \"cypress\/e2e\/*\/**\/steps\/*.ts\"\n    ],\n    \"cucumberautocomplete.strictGherkinCompletion\": false,\n    \"cucumberautocomplete.smartSnippets\": true\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Test Execution<\/h2>\n\n\n\n<p>Everything should be ready to run our first test using Cypress, Typescript and Cucumber. Let&#8217;s see the result. Open a terminal and run the following:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">npx cypress open<\/code><\/pre>\n\n\n\n<p>Select the option &#8220;E2E Testing&#8221;.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"833\" height=\"525\" src=\"https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_choose_test_type.png\" alt=\"\" class=\"wp-image-52\" srcset=\"https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_choose_test_type.png 833w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_choose_test_type-300x189.png 300w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_choose_test_type-768x484.png 768w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_choose_test_type-18x12.png 18w\" sizes=\"auto, (max-width: 833px) 100vw, 833px\" \/><\/figure>\n<\/div>\n\n\n<p>Choose a browser (e.g. Chrome)<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"674\" src=\"https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_browser_selection-1024x674.png\" alt=\"\" class=\"wp-image-53\" srcset=\"https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_browser_selection-1024x674.png 1024w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_browser_selection-300x197.png 300w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_browser_selection-768x506.png 768w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_browser_selection-18x12.png 18w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_browser_selection.png 1276w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n<\/div>\n\n\n<p>Click on login-form.feature to run the test and wait for the result.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"553\" src=\"https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_select_feature-1024x553.png\" alt=\"\" class=\"wp-image-54\" srcset=\"https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_select_feature-1024x553.png 1024w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_select_feature-300x162.png 300w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_select_feature-768x415.png 768w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_select_feature-18x10.png 18w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_select_feature.png 1366w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n<\/div>\n\n\n<p>Done! With this, our skeleton is complete and we can start adding more tests.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"553\" src=\"https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_success-1024x553.png\" alt=\"\" class=\"wp-image-55\" srcset=\"https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_success-1024x553.png 1024w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_success-300x162.png 300w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_success-768x415.png 768w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_success-18x10.png 18w, https:\/\/danigarcia.org\/wp-content\/uploads\/2023\/05\/230522_cypress_success.png 1366w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n<\/div>\n\n\n<p>You can get the code related to this post in <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/GarciaDan\/cypress-ts-bdd\/tree\/01-project-configuration\" data-type=\"URL\" data-id=\"https:\/\/github.com\/GarciaDan\/cypress-ts-bdd\/tree\/01-project-configuration\" target=\"_blank\">this repo<\/a>. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>I admit it: since I discovered Cypress, test automation has never been the same. Although my origins in this field go back to the combination of Java, Gradle, TestNG, Selenium [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[10,11,8,12,13,14,9],"class_list":["post-36","post","type-post","status-publish","format-standard","hentry","category-automation","tag-bdd","tag-cucumber","tag-cypress","tag-github","tag-howto","tag-scaffolding","tag-typescript"],"gutentor_comment":1387,"_links":{"self":[{"href":"https:\/\/danigarcia.org\/es\/wp-json\/wp\/v2\/posts\/36","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/danigarcia.org\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/danigarcia.org\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/danigarcia.org\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/danigarcia.org\/es\/wp-json\/wp\/v2\/comments?post=36"}],"version-history":[{"count":8,"href":"https:\/\/danigarcia.org\/es\/wp-json\/wp\/v2\/posts\/36\/revisions"}],"predecessor-version":[{"id":59,"href":"https:\/\/danigarcia.org\/es\/wp-json\/wp\/v2\/posts\/36\/revisions\/59"}],"wp:attachment":[{"href":"https:\/\/danigarcia.org\/es\/wp-json\/wp\/v2\/media?parent=36"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/danigarcia.org\/es\/wp-json\/wp\/v2\/categories?post=36"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/danigarcia.org\/es\/wp-json\/wp\/v2\/tags?post=36"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}