Gitlab CI/CD: TypeScript/Cypress/Cucumber

  1. Generate new project in your workspace

  2. Make sure that Node.js is installed

  3. Install Cypress with TypeScript: “npm install --save-dev typescript” it will result in the following structure:

Copy the following dependencies on your package.json:

"dependencies": {
 "@badeball/cypress-cucumber-preprocessor": "latest",
 "@cypress/webpack-preprocessor": "latest",
 "cypress": "latest",
 "ts-loader": "latest",
 "typescript": "latest"
}

Run npm install to install the latest versions of the dependencies.

Write a test case using Gherkin syntax and create additional file with Steps Definition. Place both files under /Cypress/e2e/ project folder:

For automation purpose let's assume that the .feature name is based on the actual aqua Test Case Id: e.g in the case above we updated aqua Test Case with Id=270.

Our simple 270.feature file looks as follows:

Feature: Test Case 270
 Scenario: visiting the frontpage
   Given I am on web page "https://example.cypress.io"
   When I click on type
   Then URL should include "/commands/actions"


   When I click on type
   Then URL should include "/commands/type"

Steps Definition 270.ts file looks as follows:

import { Given, When, Then } from "@badeball/cypress-cucumber-preprocessor";


Given("I am on web page {string}", (webpage) => {
 cy.visit(`${webpage}`)
});


When("I click on type", ()=>{
 cy.contains('type').click()
})


When("I type {string} to locator {string}", (msg,locator)=>{
 cy.get(`${locator}`).type(`${msg}`)
})


Then("URL should include {string}", (pattern)=>{
 cy.url().should('include', `${pattern}`);
});

Navigate to your cypress.config.ts file and put the following content into it:

import { defineConfig } from "cypress";
import * as webpack from "@cypress/webpack-preprocessor";
import { addCucumberPreprocessorPlugin } from "@badeball/cypress-cucumber-preprocessor";


async function setupNodeEvents(
 on: Cypress.PluginEvents,
 config: Cypress.PluginConfigOptions
): Promise<Cypress.PluginConfigOptions> {
 // This is required for the preprocessor to be able to generate JSON reports after each run, and more,
 await addCucumberPreprocessorPlugin(on, config);


 on(
   "file:preprocessor",
   webpack({
     webpackOptions: {
       resolve: {
         extensions: [".ts", ".js"],
       },
       module: {
         rules: [
           {
             test: /\.ts$/,
             exclude: [/node_modules/],
             use: [
               {
                 loader: "ts-loader",
               },
             ],
           },
           {
             test: /\.feature$/,
             use: [
               {
                 loader: "@badeball/cypress-cucumber-preprocessor/webpack",
                 options: config,
               },
             ],
           },
         ],
       },
     },
   })
 );


 // Make sure to return the config object as it might have been modified by the plugin.
 return config;
}


export default defineConfig({
 e2e: {
   baseUrl: "https://duckduckgo.com",
   specPattern: "**/*.feature",
   chromeWebSecurity: false,
   setupNodeEvents,
 },
});


Now we need to update aqua Test Case Execution based on the result of the Cypress/Cucumber feature run. To make it general and executable for all cases we will add “AfterEach” hook block in support file: …/cypress/support/e2e.ts:

import { EnvAquaRestConfig } from '../../aquaClient/envAquaRestConfig';
import { ApiTestExecutionNew, ApiTestStepExecutionStepType, ApiTestStepExecutionUpdateStatus, TestExecutionClient } from '../../aquaClient/src/api/aqua';
import './commands'
import fetch from "node-fetch";


// Alternatively you can use CommonJS syntax:
// require('./commands')


beforeEach (() => {


})


afterEach(async function () {


   const restConfig = new EnvAquaRestConfig();
   const client = new TestExecutionClient(restConfig.url, { fetch });
   const testCaseId = Number(Cypress.spec.fileName);


   cy.log(Cypress.spec.fileName);
   let stepStatus = ApiTestStepExecutionUpdateStatus.Pass;
   if (this.currentTest.state === 'failed') {
     stepStatus = ApiTestStepExecutionUpdateStatus.Failed;
   } else if (this.currentTest.state != 'passed') {
     throw new Error('no such status for test case execution');
   }
  
   const executionData = {
     Guid: undefined,
     TestCaseId: testCaseId,
     TestCaseName: undefined,
     Finalize: false,
     ValueSetName: undefined,
     TestScenarioInfo: undefined,
     Steps: [
       {
         Index: 1,
         Name: 'Step 1',
         StepType: ApiTestStepExecutionStepType.Step,
         Status: stepStatus,
       },
     ],
     TestedVersion: undefined,
     ExecutionDuration: undefined,
     AttachedLabels: undefined,
     CustomFields: undefined,
     Attachments: undefined
   } as unknown as ApiTestExecutionNew;
    await client.create([executionData]);
  
 })

For the above API request we need to know test case execution status from Cypress. This is being done by “cy.currentTest.state”.

To update aqua with Test Case Execution info we also need to provide aqua Test Case Id: in the above example test case id is extracted from “Number(Cypress.spec.fileName)”.

Classes we use for API calls can be generated from JSON schema available here: https://app.aqua-cloud.io/aquaWebNG/swagger/v1/swagger.json

After successful JUnit test execution we should see corresponding single Test Case execution in aqua

To be able to run the above Cypress/Cucumber Project using GitLab CI/CD we need to create corresponding workflow file .gitlab-ci.yml. Simple Example of .gitlab-ci.yml script is shown below:

stages:
 - test


test:
 image: cypress/browsers
 stage: test
 script:
   # install dependencies
   - npm ci
   # run Cypress tests
   - npx cypress run --browser chrome

Once a GitLab test runner is configured and connected to your GitLab projects we can Run GitLab Pipeline using the above workflow:

Link to GitLab Repository - https://gitlab.com/aqua3191704/cypress-typescript-gitlab.git (branch ‘Cucumber’)

Last updated